Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,17 @@ All notable changes to this project are documented here. The format is based on

## [Unreleased]

### Changed

- **BREAKING: Clip + Voice contracts aligned to the LIVE gateway** (verified by probing
`api.wave.online`). SDK/CLI clients generated from the previous spec will break:
- `ClipCreate` now requires `source` (recording id string) + `in`/`out` relative time strings
(`5s`, `2m`), replacing the rejected `videoId`/`startTime`/`endTime` numeric shape.
- `POST /voice/generate` → `POST /voice` (the live path); `VoiceGenerateRequest` requires only
`text` (`voiceId` optional, also sent as `voice_id` by the SDK). The 200 response now documents
that the primary path returns raw `audio/mpeg` bytes directly. The old `/voice/generate` path
is not kept as a deprecated alias because the live gateway does not serve it.

### Added

- **MoQ join-token mint surface** (`openapi.yaml`) — the Media over QUIC product had no spec at
Expand Down
47 changes: 30 additions & 17 deletions openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,7 @@ paths:
items:
$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.

post:
tags: [Voice]
summary: Generate speech from text
Expand All @@ -404,20 +404,22 @@ paths:
responses:
'200':
description: >
Speech generated. The gateway returns one of three shapes depending on
the engine path: an inline JSON payload with base64 audio + character
`alignment` (single round-trip, carries word timestamps), an async job
to poll, or raw audio bytes when timestamps were not requested.
Speech generated. Returns the raw MP3 bytes (`audio/mpeg`) by default (verified
against the live gateway: POST `/voice` with `{ text }` returns the MP3 directly).
The `application/json` shapes are returned only when the request opts out of the
default: requests that ask for character timestamps receive a `VoiceSynthesisInline`
payload (base64 audio + `alignment`), and engines that process asynchronously return
a `VoiceGeneration` job to poll.
content:
audio/mpeg:
schema:
type: string
format: binary
application/json:
schema:
oneOf:
- $ref: '#/components/schemas/VoiceSynthesisInline'
- $ref: '#/components/schemas/VoiceGeneration'
audio/mpeg:
schema:
type: string
format: binary
'401':
$ref: '#/components/responses/Unauthorized'
'403':
Expand Down Expand Up @@ -2238,14 +2240,17 @@ components:

ClipCreate:
type: object
required: [videoId, startTime, endTime]
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`.
Comment on lines +2248 to +2250

@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.

out:
type: string
description: End offset as a relative time string, e.g. `10s` or `1m30s`.
Comment on lines +2243 to +2253

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.

title:
type: string
description:
Expand Down Expand Up @@ -2398,12 +2403,20 @@ components:

VoiceGenerateRequest:
type: object
required: [voiceId, text]
required: [text]
# `voiceId` and `voice_id` are wire aliases for the same setting; supplying both is invalid.
not:
required: [voiceId, voice_id]
properties:
text:
type: string
Comment on lines +2406 to 2412

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.

description: Text to convert to speech.
voiceId:
type: string
text:
description: Voice id to use. Optional; the gateway picks a default when omitted. Also accepted on the wire as the `voice_id` alias.
voice_id:
type: string
description: Snake_case wire alias for `voiceId` (the form the SDK sends). Provide either `voiceId` or `voice_id`, not both.
Comment thread
devin-ai-integration[bot] marked this conversation as resolved.
stability:
type: number
minimum: 0
Expand Down
Loading