Relay API reference: TypeSpec→AsyncAPI 3.0 tooling + full Relay surface + Fern docs#423
Draft
Devon-White wants to merge 74 commits into
Draft
Relay API reference: TypeSpec→AsyncAPI 3.0 tooling + full Relay surface + Fern docs#423Devon-White wants to merge 74 commits into
Devon-White wants to merge 74 commits into
Conversation
Also fixes test harness: import the library and use the full SignalWire.AsyncAPI namespace path so decorators resolve.
…uth security Avoids depending on @typespec/http (whose lib tsp won't load in the hoisted standalone tester); custom @bearerAuth keeps the emitter self-contained.
…dModel, inline-param docs Addresses adversarial review: numeric enum/discriminator type inference (latent invalid-schema bug), Array.isArray guard, isNamedModel helper, inline-parameter description extraction, and a multi-method/multi-event collision-coverage test. String-discriminator output is unchanged (golden snapshot + Relay artifact in sync).
@minValue/@maxValue/exclusive, @minLength/@maxlength, @minItems/@Maxitems, @pattern, @Format, @secret→format:password, property defaults, and @example now map to the corresponding JSON-Schema keywords (valid per AsyncAPI 3.0's Draft-07 superset). Relay timeout now models its non-negative/default-30 semantics.
…odedname Completes the standard property-decorator surface: @encode decays to its wire type with a JSON-Schema format (e.g. utcDateTime+rfc3339 → string/date-time), @Encodedname remaps the emitted JSON property key. No standard property decorator is silently dropped now.
…r assertions, dup-method diagnostic Addresses the remaining adversarial-review quality findings: - Replace SchemaObject=Record<string,unknown> with a structured AsyncAPISchema interface (correct AsyncAPI/Draft-07 dialect: numeric exclusiveMin/Max, string discriminator, no nullable) — removes the as-Record casts. Not reusing @typespec/openapi3's OpenAPI3Schema, which models a different dialect. - RefFn returns SchemaOrRef (= AsyncAPISchema | AsyncAPIRef); unionInline typed to Union (removes the as-any casts). - emitRpcMethods/emitEvents take a typed EmitTarget context instead of drilling doc!.components! — removes non-null assertions. - Add duplicate-@rpcMethod diagnostic + test.
…lwire + calling core
…s across 6 services
…p invented /api/relay/wss path)
Verified against the TS + Python SDK clients: both connect to `wss://${host}` with
DEFAULT_RELAY_HOST = relay.signalwire.com and reject a host containing a path. The
service is selected by the JSON-RPC method in the payload, not a URL path.
With address null, Fern composes the channel URL as wss://<host>/<channelId>, showing a misleading per-service path (e.g. wss://relay.signalwire.com/messaging). Relay multiplexes every service over one root connection and routes by the JSON-RPC method, so emit the root address "/" — Fern then renders wss://relay.signalwire.com/. Verified live for the messaging and calling channels.
…pecs The emitter's dist/ is gitignored and nothing built it in CI, so build:relay failed with import-not-found on ../dist/src/index.js. build:relay now runs the emitter's tsc build first (verified against a clean dist/).
The Relay API was rendering in the same flat sidebar as REST. Convert the apis product nav to tabs (matching the compatibility-api pattern): a REST tab and a Relay tab. URLs are preserved — Core stays at /docs/apis/<page>, REST at /docs/apis/rest/..., Relay at /docs/apis/relay/... (tab carries the slug, the API uses skip-slug). Verified live: both tabs render and all existing URLs 200.
…mples Correctness fixes from the one-to-one spec audit (6 services / 71 methods / 26 events): - pay: input enum speech->voice; add PayPrompt.attempt + require_matching_inputs; actions optional; numeric/bool params retyped to wire strings per pay_schema.md - calling: join_conference recording_status_callback_event enum->string; hold/unhold result state literals; broaden TapCodec + detector-event enums - signalwire: ConnectResult.ice_servers optional; strip internal jargon (BLADE/etc.) - tasking: drop carrier fields (timestamp/space_id/project_id) the emitter synthesizes - add missing customer-facing @doc across event/refer models Examples: @opExample on every method + @example on every event; discriminator-union variant models carry schema-level @example where an op frame can't narrow the base. All 6 specs validate 0 errors via the official AsyncAPI CLI; fern check clean.
…_transcribe/translate Verified the SWML models against the mod_openai/mod_infrastructure C engine (96/96 AIParams fields parsed by the engine; SWAIG schema shared via one process_swaig_function; live_transcribe/translate hit the same parse_transcribe_json). Replicated the engine-confirmed shapes into the Relay specs (cross-import blocked by @jsonSchema/SWMLVar), stripping ~72 markup arms: - calling.ai/amazon_bedrock: params Record<unknown> -> typed AiBehaviorParams (91 fields) - SWAIG: native_functions enum, web_hook_auth_* + meta_data on function/defaults/includes, mcp_servers; kept parameters/data_map/internal_fillers loose (@OneOf, validate-first) - live_transcribe/translate: start/summarize/inject Record<unknown> -> typed action models - added @opExample to all 7 AI ops calling validates 0 errors (AsyncAPI CLI); fern check clean.
Replace the hand-replicated AiBehaviorParams (91 fields) with a direct spread of
the engine-verified SWML AIParams model, stripping its markup-only `| SWMLVar`
template-variable arms at emit time so they never reach the live JSON-RPC wire.
- typespec-emit-filter: standalone, generic @excludeFromEmit(...types) decorator
package (TypeSpec's shared-decorator-library pattern).
- asyncapi emitter: honor @excludeFromEmit on models/unions/properties — drop the
named arms and collapse the union (0 -> {}, 1 -> bare, all-string -> enum, else
oneOf). Applied on both the component and inline-model paths (open models with a
`...Record<unknown>` index signature always inline).
- asyncapi emitter: namespace-qualified component names via getTypeName so reused
cross-namespace SWML types stay collision-free (service-local types stay bare);
add a duplicate-type-name diagnostic backstop.
- relay calling: AiBehaviorParams now spreads SWML.Calling.AIParams, keeping the 2
deprecated eleven_labs_* fields.
Verified: 32 emitter tests pass; AsyncAPI CLI 0 errors; fern check clean (only the
known FDR 403); parity diff vs 158d9c1 shows only additive/authoritative deltas
(added defaults/examples, SWML constraints, reused SWML.Calling.ConversationMessage,
+2 deprecated fields, int32->integer format drops); the other 5 relay specs are
byte-identical; SWML's own json-schema emit still includes SWMLVar.
…zon_bedrock Extend the SWML reuse from `params` (AIParams) to the entire AI agent config object, which mod_openai's create_app_from_json parses identically for the SWML `ai` verb and Relay calling.ai (verified in C source). The Relay-local Ai* sub-models were incomplete projections of one shared backend object; replace them with direct references to the engine-verified SWML models. - AiParams/AmazonBedrockParams: prompt -> AIPrompt, post_prompt -> AIPostPrompt, pronounce -> Pronounce, hints -> (string|Hint)[], languages -> Languages, SWAIG -> SWAIG (incl. the full function/parameters/data_map tree and the SWMLAction that returns a full SWML document), global_data -> GlobalData. Delete the 11 hand-rolled Ai* sub-models (-288 lines net). - Exclude ONLY the markup-only `SWMLVar` template-variable arms via @excludeFromEmit on the entry models (resolved at SWML execution, never on the live JSON-RPC wire). Every other surface SWML accepts is emitted as-is, including the recursive SWML document a SWAIG data_map action can return (handled natively via $ref). - Reused SWML as-is; SWML's current gaps vs Relay (missing web_hook_auth_* on SWAIG, Hint requiring pattern/replace, AIPrompt missing barge_confidence/model) are logged in SWML-REUSE-GAPS.md for a later SWML-truing pass. Verified: 0 SWMLVar leak; valid AsyncAPI (fern check clean); fern docs dev renders the calling AI reference incl. the recursive SWML `$ref`; other 5 relay specs byte-identical; SWML's own emit unaffected.
…en) on signalwire.connect; drop inaccurate HTTP bearer scheme; note per-service client/server audience
…nalwire/docs into Devon/relay-asyncapi-tooling
…visioning, webrtc, signalwire
- Fix unterminated doc code span in conference status_callback_event - Reword @server description to drop in-band/transport jargon - Regenerate relay.yaml and relay-single.yaml from updated specs
Replace @rpcMethod + @channelPerCommand with a single per-operation @channel decorator: each operation gets its own root-addressed AsyncAPI 3.0 channel. Drop @channelPerCommand; channel-mode is now multi|single. Server-pushed events not returned by any op get their own receive-only channel (uniform output). Verified: relay-single.yaml byte-identical; relay.yaml components (schemas + messages), signalwire, and calling channels/operations byte-identical; 243 operations preserved; only messaging/tasking/provisioning/webrtc regroup into per-op + per-event channels. Emitter tests 50/50 green.
…ire/tasking/provisioning/webrtc (Part B, 5/6 services)
Reorganize each non-calling service into <service>/main.tsp + models/core.tsp +
<feature>/main.tsp + <feature>/models/{requests,responses}.tsp + events/<event>.tsp.
Output byte-identical (relay.yaml + relay-single.yaml unchanged).
…6/6 services)
Split the 8 grab-bag method files + common.tsp + media-1/media-2 event files into
34 per-operation feature directories (<feature>/main.tsp + models/{requests,responses}.tsp),
shared models/, and meaningfully-named events/ files. Operation declaration order
preserved; relay.yaml + relay-single.yaml byte-identical.
5 responses.tsp files spread ...Result without importing their service's models/core.tsp; signalwire/receive referenced Acknowledgement and Relay.Calling.CallReceiveEvent without imports. These resolved only via the root main.tsp's transitive imports, so the language server flagged them per-file. Add the direct imports; every spec file now compiles standalone with zero invalid-ref errors. Output byte-identical.
…elative import paths Move every operation feature directory into <service>/operations/, leaving models/ (shared) and events/ (received events) cleanly separated at the service top level. Update main.tsp feature imports and the relative paths in moved files (service-level models/events and cross-service/swml refs gain one ../). Output byte-identical; every file compiles standalone with zero invalid-ref.
Source-verified against mod_infrastructure (C), mod_openai (C), and prime-rails (Ruby). Params-level type/enum/required/missing-param/event-field/example fixes, e.g.: SipCodec +AMR-WB, ToneName +10 codes, ConferenceRegion +ch, ConferenceCallbackEventType +laml, TapCodec closed enum, tap direction required, record.audio +max_length, collect +model, missing dial/connect/answer scalars, event fields (direction/end_reason/failed_reason/recording_id/...), and corrected @opExample reply strings. Layering-sensitive findings (node_id envelope, fabric naming, wire method names) deferred as FLAG. Build + 50/50 emitter tests + AsyncAPI conformance pass.
Source-wins fixes: receive event call_state uses real CallState values (drop phantom ReceiveCallState); remove never-emitted created_by; messaging ReceiveEvent.media nullable; drop never-emitted error variants from detect/fax events. Result envelope: add call_id?/control_id? to shared RelayResult (dedup 14 results). Add source-confirmed customer params: conference acl, SIP encryption, typed confirm (url|object), queue wait_url/wait_time/execute_after_queue/whisper_url. Build + dangling-ref scan + AsyncAPI conformance pass.
…ice_id) Resolved open FLAGs via prime-rails + customer-SDK source: - Envelope (node_id) and user_event(event:string) CONFIRMED correct via SDKs — no change. - Conference event (built in C, tapped by prime-rails): add record-*/stream-* statuses, region/size/call_id_to_coach fields, node_id optional. - Messaging: backend emits tags:[] (fix examples); add 'read' delivery state. - Bedrock voice_id moved from prompt to AmazonBedrockObject root with the real 5-voice enum + tiffany default (bedrock.c:5511,129-136); fixes silent-ignore bug for SWML + Relay reuse. Build:relay + build:schema + scan + 50/50 tests pass.
…it/source-verified) Git history (commit #669 deleted old conference_controller.c, added new_conference_controller.c; recent commits touch only the new one) confirms the live controller emits start_on_join/end_on_leave/call_id_ending_conf (new_conference_controller.c:688/689/497) — the spec's start_on_enter/end_on_exit/ call_ending_conference came from stale docs. Removed phantom participant_call_status and reason_participant_left (emitted nowhere in source). Source > docs.
The spec modeled the post-gateway blade method 'message' (copied from a doc that
states 'all methods are blade.execute'). The customer-facing v4 browser SDK sends
'webrtc.verto' with a required callID and reads {code, result, node_id}
(webrtc.c:14779 = node 'message'; v4 SDK = webrtc.verto). Rename @channel to
webrtc.verto, add required callID, reshape MessageResult to {code, result?,
node_id?}. Build + scan pass.
Verified against @signalwire/realtime-api@4.2.1: Task.send() POSTs to /api/relay/rest/tasks via node:https (HTTP 204) — there is no WebSocket send method. Drop the deliver operation (it belongs in the REST spec) and keep only the inbound queuing.relay.tasks event in this AsyncAPI spec; namespace doc now points delivery to the REST endpoint. Removed unused Result model.
Verified: no SDK sends conference.list (v4 Call Fabric + typescript-web + realtime Node all have zero references; v4 lists fabric addresses, not conferences), and C does not register it as an RPC — webrtc.c:14779 registers only message/detach/ reattach/bootstrap. The only conference.list in C is a Kafka event 'source' tag (conference_events.c:907). The spec had modeled an internal blade.execute/event artifact as a request/reply op (same provenance error as message). Removed the operation + ConferenceListParams/Conference/ConferenceListResult + unused Result. Build + scan + 50/50 tests pass.
Remove the 'Relay (Single Channel)' tab and the 'Playground' page (which rendered relay-single) from the Relay tab. Also fix the Webrtc section's stale referenced-packages (webrtc/message/conferenceList -> webrtcVerto/webrtcMessage) left over from the webrtc.verto rename + conference.list removal.
The relay-single (single-channel) spec is no longer used in the docs (tab + Playground page removed). Drop its generation entirely: remove the build:relay-single-spec step from build:relay, delete specs/relay/tspconfig.single.yaml and the generated fern/apis/relay-single/ output. Only the one multi-channel Relay AsyncAPI spec (relay.yaml) remains. The emitter's channel-mode:single capability and its unit test are unchanged (still a supported feature, just unused by the build).
…nhance event models - Introduced JsonRpcRequest and JsonRpcResponse models for standardized request/response handling across Relay services. - Updated transfer and user-event operations to utilize new request/response models. - Refactored messaging events (receive, state) to align with new event model structure. - Enhanced connect and disconnect operations with new request/response models. - Added new documentation pages for Relay authentication and error handling. - Improved overall structure and clarity of Relay API specifications.
…tooling # Conflicts: # yarn.lock
… Address terminology + field order)
- emitter: a parameterless @channel op now emits a receive-only channel (channel + receive ops, no request frame, no send op); add the reply-without-request diagnostic; correct the stale channel-mode doc - tasking: model queuing.relay.tasks as a receive-only channel and move TasksEvent off signalwire.receive (de-dup) with a pointer note on receive - reorg every operation to models/{send,reply,events}.tsp (REST-aligned, AsyncAPI-named); split state.tsp into per-op events plus calling/events/shared.tsp; co-locate every per-op event with its op - apis.yml: fix relay nav refs (messagingSend, provisioningConfigure, queuingRelayTasks; drop invalid tasking/webrtcMessage) - rebuild all specs The reorg is output-neutral (relay.yaml byte-identical).
- Removed redundant comments and documentation sections across various models in the Relay Calling API specifications to enhance clarity and reduce clutter. - Streamlined model definitions by eliminating unnecessary comments related to shared structures and enums. - Updated event and operation models to focus on essential documentation, improving readability and maintainability. - Ensured consistency in documentation style across all models, enhancing the overall coherence of the API specifications.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Type of Change
Related Issues
Testing
Checklist
Additional Notes