Skip to content

fix(minimax/speech): place output_format at body root, not under audi… - #2

Merged
Misaka43fd merged 4 commits into
openmule:mainfrom
nosuchperson:mc-auto/sess-1e6d7f1f
May 25, 2026
Merged

fix(minimax/speech): place output_format at body root, not under audi…#2
Misaka43fd merged 4 commits into
openmule:mainfrom
nosuchperson:mc-auto/sess-1e6d7f1f

Conversation

@nosuchperson

Copy link
Copy Markdown
Contributor

…o_setting

Upstream ExternalSpeechGenerationRequest schema (mule-router tasks/handlers/minimax/models/external.py:254) declares output_format at the top of the request body. The CLI's buildSpeechRequestBody was nesting it inside audio_setting, where upstream's AudioSetting model silently drops it during pydantic validation. As a result, every --output-format url invocation was effectively ignored and the upstream default (HEX) returned.

Verified end-to-end against the mule-run review-app:

  • speech-2.8-turbo, no flag → audios[0] starts 49443304 (ID3 hex) ✅
  • speech-2.8-turbo, output-format url → audios[0] starts https://... ✅
  • speech-2.8-hd, output-format url → audios[0] starts https://... ✅

Move the field to the body root and add a regression test asserting audio_setting does not contain output_format. Update the existing cli/tests/run.test.ts case which had memorized the buggy placement. Strengthen the parameter description to explain url vs hex schemas and call out the upstream HEX default. Bump cli + core to 0.4.1.

Reported by: github-mulerouter-skill harness session d20acc8b.

nosuchperson and others added 4 commits May 25, 2026 19:19
…o_setting

Upstream ExternalSpeechGenerationRequest schema (mule-router
tasks/handlers/minimax/models/external.py:254) declares output_format
at the top of the request body. The CLI's buildSpeechRequestBody was
nesting it inside audio_setting, where upstream's AudioSetting model
silently drops it during pydantic validation. As a result, every
`--output-format url` invocation was effectively ignored and the
upstream default (HEX) returned.

Verified end-to-end against the mule-run review-app:
- speech-2.8-turbo, no flag       → audios[0] starts 49443304 (ID3 hex)  ✅
- speech-2.8-turbo, output-format url → audios[0] starts https://...     ✅
- speech-2.8-hd,    output-format url → audios[0] starts https://...     ✅

Move the field to the body root and add a regression test asserting
audio_setting does not contain output_format. Update the existing
cli/tests/run.test.ts case which had memorized the buggy placement.
Strengthen the parameter description to explain url vs hex schemas
and call out the upstream HEX default. Bump cli + core to 0.4.1.

Reported by: github-mulerouter-skill harness session d20acc8b.

Co-Authored-By: Claude <noreply@anthropic.com>
…nion schema

The endpoint declaration has been wrong since the initial commit
(82822db) and never touched. End-to-end validation by the
github-mulerouter-skill harness (session d20acc8b) caught two
independent breakages on the same upstream layer:

  1. --function reference_generation -> 400 error_code 2001:
     "function expected 'image_reference','video_repainting','video_edit',
     'video_extension','video_outpainting', got 'reference_generation'"
  2. --ref-images-url ... -> 400:
     "Invalid parameters: 'images' expected to be provided, got None"

Both are pydantic failures against VideoEditPayload (mule-router
payloads/video.py:607). The public mule-router API for
/vendors/alibaba/v1/wan2.1-vace-plus/generation validates the body
against this discriminated union; only afterwards does the
bailian-channel request_builder internally rename images ->
ref_images_url (request_builder.py:408). CLI must conform to
VideoEditPayload, not the downstream bailian schema.

Why the existing test didn't catch this:
  tests/models.test.ts:147 only asserted the (wrong) declared enum
  contained the strings "outpainting" and "inpainting" -- which are
  literally present in the wrong 5-value enum by naming coincidence.
  The test was registry self-consistency only; it never validated
  against upstream and could not detect CLI/upstream drift. This is
  the class of bug that SOP Layer 4 (manual E2E) exists to catch.
  The new tests in this commit are still self-consistency only, so
  they can prevent regression from the correct state but cannot
  guarantee long-term alignment if upstream changes again -- Layer 4
  E2E remains the authoritative gate.

Changes:
- function: replace enum with VideoEditPayload discriminator values
  (image_reference, video_repainting, video_edit, video_extension,
  video_outpainting). Was: outpainting, video_extend,
  reference_generation, interpolation, inpainting.
- ref_images_url -> images: rename to match upstream. (Type was
  already array; only the name changed.)
- obj_or_bg: string -> array of "obj"|"bg" (parallel to images, 'bg'
  at most once, required when images length > 1).
- mask_frame_id: string -> integer (upstream Field(ge=1)).
- negative_prompt: remove. Not in VideoEditBasePayload or any
  function subtype.
- Add upstream enum constraints to size, mask_type, expand_mode.
  Note: expand_mode keeps the upstream typo "orginal" verbatim.
- Per-field descriptions now call out which function each sub-param
  applies to, so `mulerouter params` is self-documenting.
- Rewrite tests/models.test.ts vace block to assert against the new
  correct schema.

E2E verified against review-app endpoint (api-r-main-6wkxvg.mule.run):
- image_reference with --images: mule-router accepts body (task_id
  created); downstream bailian then returns code 3005 for unrelated
  reason (CLI body shape is no longer the blocker).
- video_outpainting with --video-url + scales --no-wait: task created
  status=pending (body schema accepted).
Pre-fix, both would have failed at mule-router pydantic with 400.

Bump cli + core to 0.4.2.

Co-Authored-By: Claude <noreply@anthropic.com>
…even for single image

Full end-to-end validation against api-r-main-6wkxvg.mule.run review-app
revealed: with --images of length 1 and --obj-or-bg omitted, the request
passes mule-router's pydantic (VideoEditValidators.validate_obj_or_bg
allows None when len(images)==1) but Alibaba Bailian rejects upstream
with code InvalidParameter:

    "ref_images_url and obj_or_bg must be the same length"

(captured in mule-router primary pod task_executor.py:221 log, task
b2700315-cc50-4d03-ab1d-b970d101955b's external task a2f8a088-...)

After adding --obj-or-bg '["obj"]' to the same request, the task
completed successfully and returned a video URL (~3.5 min wall, $0.10
USD/s × 5s = 50 credits). CLI body shape is correct end-to-end; the
gap is a documentation issue only.

This is also a latent mule-router bug (its validator is more lenient
than Bailian's actual requirement), but rather than wait on upstream,
surface the requirement in the CLI param description so
`mulerouter params` is self-documenting.

Co-Authored-By: Claude <noreply@anthropic.com>
… + tests

CI on PR openmule#2 failed at `bun run lint` (biome check) with:
- packages/core/tests/models.test.ts:145 — `endpoint!.parameters`
  triggers lint/style/noNonNullAssertion
- packages/core/tests/models.test.ts:291 — `Object.keys(audioSetting!)`
  same
- packages/core/src/models/alibaba/wan2.1-vace-plus.ts:131 — long
  description line exceeds biome's formatter wrap threshold

Fixes:
- Replace `!` with explicit `if (!endpoint) return;` early-return after
  `expect(endpoint).toBeDefined()` so the type narrows naturally.
- Replace `Object.keys(audioSetting!)` with `Object.keys(audioSetting ?? {})`;
  the assertion above ensures it's defined at runtime, this just keeps the
  type checker happy without a non-null assertion.
- Apply biome's auto-format suggestion to wan2.1-vace-plus.ts (descriptions
  split onto their own lines).

172/172 tests still green.

Co-Authored-By: Claude <noreply@anthropic.com>
@Misaka43fd
Misaka43fd merged commit e078f24 into openmule:main May 25, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants