Skip to content

fix: align Clip + Voice contracts with the live gateway - #48

Open
yakimoto wants to merge 4 commits into
mainfrom
fix/live-gateway-contract
Open

fix: align Clip + Voice contracts with the live gateway#48
yakimoto wants to merge 4 commits into
mainfrom
fix/live-gateway-contract

Conversation

@yakimoto

@yakimoto yakimoto commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

The api-spec was out of sync with the live gateway (verified today by probing api.wave.online):

  • ClipCreate previously required the rejected numeric videoId/startTime/endTime shape. The gateway accepts { source: "<recording-id>", in: "5s", out: "10s" }.
  • The voice path is POST /voice (not /voice/generate), requiring only text (voiceId optional, also sent as voice_id). The primary 200 returns raw audio/mpeg bytes.

CHANGELOG updated.


View with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is enabled.


Open in Devin Review

Review in cubic

Note

Align Clip and Voice API contracts with live gateway

  • Renames the Voice synthesis path from POST /voice/generate to POST /voice; the 200 response now returns raw audio/mpeg bytes by default, with JSON only when opting into timestamps or async engines.
  • ClipCreate now requires source, in, and out (relative time strings e.g. 5s, 2m30s) instead of videoId, startTime, and endTime (numeric).
  • VoiceGenerateRequest now only requires text; voiceId and voice_id are optional snake/camel aliases but mutually exclusive — providing both is invalid.
  • Risk: all three changes are breaking; clients using /voice/generate, numeric clip times, or passing voiceId as required must update.

Macroscope summarized 16af177.

Verified by probing api.wave.online: ClipCreate previously required the
rejected numeric videoId/startTime/endTime shape; the gateway accepts
{ source: "<recording-id>", in: "5s", out: "10s" }. The voice path is
POST /voice (not /voice/generate), requiring only text, and the primary
200 returns raw audio/mpeg bytes.

- ClipCreate: source (recording id string) + in/out relative time strings.
- /voice/generate -> /voice; VoiceGenerateRequest.text required, voiceId
  optional (also sent as voice_id).
@cursor

cursor Bot commented Aug 11, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_4433be5b-ea99-4ac6-9085-eb4bb1bdfe31)

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 3 minutes

Your organization has reached its usage spending cap. Adjust your spending cap in the billing tab.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: c1db1d9f-8c32-45ec-942b-a937a89584a0

📥 Commits

Reviewing files that changed from the base of the PR and between 6649096 and 16af177.

📒 Files selected for processing (2)
  • CHANGELOG.md
  • openapi.yaml

Comment @coderabbitai help to get the list of available commands.

@macroscopeapp

macroscopeapp Bot commented Aug 11, 2026

Copy link
Copy Markdown

Approvability

Verdict: Needs human review

This PR introduces explicitly labeled BREAKING changes to API schemas (endpoint rename, field type changes) that will break existing SDK clients. An unresolved comment about the missing version bump for semver compliance, combined with a Medium-severity finding about the voiceId/voice_id alias constraint, warrants human review.

You can customize Macroscope's approvability policy. Learn more.

macroscopeapp[bot]
macroscopeapp Bot previously approved these changes Aug 11, 2026
@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Fix OpenAPI ClipCreate + Voice endpoints to match live gateway

🐞 Bug fix 📝 Documentation 🕐 10-20 Minutes

Grey Divider

AI Description

• Update ClipCreate request schema to source + in/out time strings accepted by the live
 gateway.
• Correct Voice generation endpoint to POST /voice and document primary audio/mpeg byte
 response.
• Record the contract alignment in the Unreleased changelog for downstream SDK/spec consumers.
Diagram

graph TD
  A["SDK / API Client"] --> B["OpenAPI spec"] --> C["Live Gateway"]
  B --> D["ClipCreate schema"] --> G["source + in/out"]
  B --> E["POST /voice"] --> F["200 audio/mpeg"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Add gateway contract checks in CI
  • ➕ Detects spec drift automatically before release
  • ➕ Prevents future mismatches like endpoint paths and required fields
  • ➖ Requires stable test credentials / environment access
  • ➖ Can introduce flaky CI if the live gateway is unstable
2. Generate OpenAPI from the gateway source of truth
  • ➕ Eliminates manual spec maintenance for these endpoints
  • ➕ Reduces risk of breaking SDKs due to spec drift
  • ➖ May be non-trivial if gateway isn’t instrumented for spec generation
  • ➖ Can constrain gateway implementation choices to tooling limitations
3. Versioned endpoint aliases (/voice and /voice/generate)
  • ➕ Backwards compatibility for existing clients pinned to old paths
  • ➕ Allows gradual migration with deprecation notices
  • ➖ Adds maintenance burden and ambiguity about the canonical path
  • ➖ Doesn’t address request/response schema drift by itself

Recommendation: The PR’s approach (manually correcting the OpenAPI contract to match verified live behavior) is the right immediate fix because it unblocks SDK/spec consumers quickly. Follow up with CI-level contract probing (or spec generation if feasible) to prevent recurring drift, since these changes indicate the spec is not currently the source of truth.

Files changed (2) +28 / -14

Bug fix (1) +19 / -14
openapi.yamlFix ClipCreate schema and Voice endpoint/response to match gateway +19/-14

Fix ClipCreate schema and Voice endpoint/response to match gateway

• Updates Voice generation path from '/voice/generate' to '/voice' and clarifies that the primary 200 response returns raw 'audio/mpeg' bytes. Adjusts 'ClipCreate' to require 'source', 'in', and 'out' (string offsets) and makes 'VoiceGenerateRequest.text' the only required field with optional 'voiceId'.

openapi.yaml

Documentation (1) +9 / -0
CHANGELOG.mdDocument live-gateway contract alignment for Clip and Voice +9/-0

Document live-gateway contract alignment for Clip and Voice

• Adds an Unreleased 'Changed' entry describing the ClipCreate schema update and Voice endpoint correction. Notes the live verification and the primary 'audio/mpeg' response behavior for Voice.

CHANGELOG.md

@cubic-dev-ai

cubic-dev-ai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Running ultrareview automatically — This PR makes significant changes to the Clip and Voice API contracts—replacing required fields, altering endpoint paths, and updating response types—which risk breaking client integrations if any detail is misaligned with the live gateway.. I'll post findings when complete.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 3 potential issues.

Open in Devin Review

Comment thread openapi.yaml
Comment on lines +2241 to +2251
required: [source, in, out]
properties:
videoId:
source:
type: string
startTime:
type: number
endTime:
type: number
description: Recording id the clip is cut from (e.g. `rec_abc123`). Verified against the live gateway — the older `{ type, id, start_time, end_time }` source object is rejected.
in:
type: string
description: Start offset as a relative time string, e.g. `5s` or `2m`.
out:
type: string
description: End offset as a relative time string, e.g. `10s` or `1m30s`.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 Clip response and update schemas still use the old numeric time contract

ClipCreate was migrated to { source, in, out } string offsets, but Clip (openapi.yaml:2201-2237) and ClipUpdate (openapi.yaml:2256-2266) still declare videoId, startTime, endTime as numbers. If the live gateway really rejects the numeric shape on create, PATCH /clips/{clipId} (which sends startTime/endTime numbers) and the documented clip response shape are likely also out of sync. Worth probing the gateway for the read/update shapes so the whole Clips surface is consistent, otherwise generated SDKs will still produce rejected PATCH bodies.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only the ClipCreate request shape was verified against the live gateway; rewriting the Clip response and ClipUpdate schemas without probing the read/update endpoints (which requires gateway credentials unavailable here) would risk documenting an unverified contract.

Comment thread openapi.yaml
Comment on lines +2246 to +2248
in:
type: string
description: Start offset as a relative time string, e.g. `5s` or `2m`.

@devin-ai-integration devin-ai-integration Bot Aug 11, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 Property named in may collide with reserved words in generated SDKs

in/out are fine in JSON Schema, but in is a reserved keyword in several target languages used by openapi-generator (Python/JS in operator contexts, Go in is fine, C#/Java in). Generators usually escape it, but the resulting SDK field names may be mangled (e.g. _in, varIn). Worth a quick generation check with the typescript-fetch generator documented in README before publishing.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in/out are the live gateway's wire field names, so the spec must keep them; openapi-generator escapes reserved keywords in generated SDKs without affecting the wire format, making this a generator-side cosmetic concern, not a spec defect.

Comment thread openapi.yaml
$ref: '#/components/schemas/Voice'

/voice/generate:
/voice:

@devin-ai-integration devin-ai-integration Bot Aug 11, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 Breaking path rename with no version bump or deprecated alias

/voice/generate/voice removes the old path entirely (CHANGELOG explains the gateway does not serve it), but info.version remains 1.0.0 (openapi.yaml:24) despite the CHANGELOG labelling this a BREAKING change and the project claiming SemVer adherence. Existing generated clients calling generateSpeech will 404 with no transition period; consider bumping the spec version so downstream consumers detect the break.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@cubic-dev-ai

cubic-dev-ai Bot commented Aug 11, 2026

Copy link
Copy Markdown

I can't run this ultrareview because your workspace has reached its monthly review limit. cubic has reviewed 100,145 of the 100,000 allowed lines of code this month. Reviews resume on 4 September 2026 (in 25 days). Enable flex capacity to cover overages automatically and resume reviews now. Learn how flex capacity works.

To help optimise your usage, you can tune cubic to get the most out of your usage limits:

Learn more →

Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>
@qodo-code-review

qodo-code-review Bot commented Aug 11, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (1) 📜 Skill insights (0)

Grey Divider


Remediation recommended

1. voice_id not in schema ✓ Resolved 🐞 Bug ⚙ Maintainability
Description
VoiceGenerateRequest’s prose claims the SDK also sends voiceId as voice_id, but the schema only
defines voiceId and does not define a voice_id property. This is an internal contract
inconsistency that can mislead consumers and break schema-driven validation for callers that follow
the documented alias.
Code

openapi.yaml[R2408-2411]

+          description: Text to convert to speech.
+        voiceId:
+          type: string
+          description: Voice id to use. Optional — the gateway picks a default when omitted. The SDK also sends this as `voice_id`.
Evidence
The schema defines voiceId but its description states it is also sent as voice_id, creating a
mismatch between declared properties and documented request shape.

openapi.yaml[2402-2411]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`VoiceGenerateRequest.voiceId` is documented as “also sent as `voice_id` by the SDK”, but `voice_id` is not represented in the schema. This makes the OpenAPI contract internally inconsistent and may mislead integrators who implement requests based on the spec text.

### Issue Context
If `voice_id` is a wire-level alias accepted by the gateway, it should be explicitly modeled (or otherwise formally documented). If it’s purely an internal SDK serialization detail, the OpenAPI schema should not claim it as a request-field alias.

### Fix Focus Areas
- Decide which is true and update the spec accordingly:
 - **Wire accepts both**: add a `voice_id` property (potentially marked `deprecated: true`), and explain precedence between `voiceId` and `voice_id` if both are provided.
 - **SDK-only serialization**: remove the `voice_id` claim from the schema description and document serialization behavior elsewhere (e.g., SDK docs), keeping the OpenAPI contract strictly about wire fields.

### Fix Focus Areas (code references)
- openapi.yaml[2402-2411]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Voice 200 negotiation unclear ✓ Resolved 🐞 Bug ≡ Correctness
Description
The /voice 200 response description now claims the primary path returns raw audio/mpeg bytes, but
the spec still advertises application/json alongside it without clearly specifying when each media
type is returned or how clients should request JSON vs MP3. This creates an ambiguous contract for
SDKs and callers about response handling/content negotiation.
Code

openapi.yaml[R407-410]

+            Speech generated. Verified against the live gateway: the primary path returns the raw
+            audio bytes (`audio/mpeg`) in the response body — POST `/v1/voice` with `{ text }`
+            returns the MP3 directly. Engines that request timestamps may instead return an inline
+            JSON payload with base64 audio + character `alignment`, or an async job to poll.
Evidence
The updated /voice 200 description says the primary response is raw MP3 bytes, but the response
still declares both application/json and audio/mpeg without specifying the selection condition.
Elsewhere in the same spec, /render documents the exact Accept-driven behavior for its
binary-vs-JSON 200 response, highlighting the lack of equivalent specificity for /voice.

openapi.yaml[393-420]
openapi.yaml[111-142]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`POST /voice` now documents that the primary 200 returns raw `audio/mpeg`, but the OpenAPI response still lists both `application/json` and `audio/mpeg` without defining the selection rule (e.g., `timestamps: true` vs `Accept: application/json`). This leaves clients uncertain about what they’ll receive and how to request the JSON shapes.

### Issue Context
There is already a precedent in this spec for “binary by default, JSON when Accept is application/json” (`/render`), which makes the current `/voice` wording/content less precise by comparison.

### Fix Focus Areas
- Define an explicit rule in the `/voice` 200 response docs, e.g.:
 - `timestamps: true` → `application/json` (inline alignment/job)
 - otherwise → `audio/mpeg` (binary MP3)
 - or explicitly tie JSON to `Accept: application/json` (and mention default behavior)
- Consider aligning the presentation with `/render` (documenting the Accept header behavior) and, if helpful for consumers/tools, order the default/binary media type first.

### Fix Focus Areas (code references)
- openapi.yaml[393-420]
- openapi.yaml[111-142]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. openapi.yaml exceeds 500 lines 📘 Rule violation ⚙ Maintainability
Description
openapi.yaml is a touched file and is well over the 500-line limit (it contains content past line
2400). This makes the spec harder to maintain and violates the maximum file-length requirement.
Code

openapi.yaml[R2404-2407]

+      required: [text]
      properties:
-        voiceId:
-          type: string
        text:
          type: string
Evidence
Compliance ID 2497975 requires each touched source file to be <=500 lines of code. The modified
openapi.yaml includes schema definitions at/after line 2402 in the PR branch, proving the file
length is far beyond 500 lines.

Rule 2497975: Limit source file length to 500 lines of code
openapi.yaml[2402-2411]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The PR modifies `openapi.yaml`, which is far larger than the 500-line maximum required by the compliance checklist.

## Issue Context
The file contains content beyond line 2400, indicating it significantly exceeds the 500-line cap.

## Fix Focus Areas
- openapi.yaml[393-410]
- openapi.yaml[2241-2251]
- openapi.yaml[2404-2411]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context
✅ Compliance rules (platform): 1 rule
✅ REVIEW.md
Review mode: ⚖️ Balanced: This changes public OpenAPI contracts and endpoint/response semantics across Clip and Voice paths; it carries real compatibility risk but is not dense enough in independent logic to warrant extended review.

Grey Divider

Tip of the day
💡 Did you know, you can group findings by type and pick your Finding display, from Minimal to Full

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread openapi.yaml
Comment on lines +2404 to 2407
required: [text]
properties:
voiceId:
type: string
text:
type: string

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

1. openapi.yaml exceeds 500 lines 📘 Rule violation ⚙ Maintainability

openapi.yaml is a touched file and is well over the 500-line limit (it contains content past line
2400). This makes the spec harder to maintain and violates the maximum file-length requirement.
Agent Prompt
## Issue description
The PR modifies `openapi.yaml`, which is far larger than the 500-line maximum required by the compliance checklist.

## Issue Context
The file contains content beyond line 2400, indicating it significantly exceeds the 500-line cap.

## Fix Focus Areas
- openapi.yaml[393-410]
- openapi.yaml[2241-2251]
- openapi.yaml[2404-2411]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

openapi.yaml is a long-standing monolithic spec (2400+ lines before this PR); splitting it to meet the 500-line rule is a large restructuring outside the scope of this contract-alignment PR.

Comment thread openapi.yaml Outdated
Comment thread openapi.yaml Outdated
@qodo-code-review

Copy link
Copy Markdown

Qodo Fixer

No findings are within the configured fix scope. To change which findings are fixed, adjust the setting on your Qodo configuration page.

Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>
devin-ai-integration[bot]

This comment was marked as resolved.

Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>
@wave-bugbot

wave-bugbot Bot commented Aug 12, 2026

Copy link
Copy Markdown

🟢 WAVE BugBot — clear

No confident findings on the changed lines.

Local review · $0 inference · wave-dispatch

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.

1 participant