@runappsai/sdk 0.7.0 → 0.7.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@runappsai/sdk",
3
- "version": "0.7.0",
3
+ "version": "0.7.3",
4
4
  "description": "JavaScript / TypeScript client for the RunApps AI Gateway",
5
5
  "license": "MIT",
6
6
  "author": "RunApps",
package/src/audio.ts CHANGED
@@ -90,7 +90,10 @@ export interface TranscribeParams {
90
90
  */
91
91
  url?: string;
92
92
  language?: string;
93
- prompt?: string;
93
+
94
+ /** A vocabulary hint for the model, as text or a private prompt
95
+ * `{ id, vars }` (or a list; see `PromptInput`). */
96
+ prompt?: string | PromptInput;
94
97
  response_format?: string;
95
98
  timestamp_granularities?: ("segment" | "word")[];
96
99
  user?: string;
@@ -406,7 +409,7 @@ function buildTranscribeForm(model: string, params: TranscribeParams): FormData
406
409
  const form = new FormData();
407
410
  form.set("model", model);
408
411
  if (params.language) form.set("language", params.language);
409
- if (params.prompt) form.set("prompt", params.prompt);
412
+ if (params.prompt) form.set("prompt", typeof params.prompt === "string" ? params.prompt : JSON.stringify(params.prompt));
410
413
  if (params.response_format) form.set("response_format", params.response_format);
411
414
  if (params.timestamp_granularities) {
412
415
  for (const g of params.timestamp_granularities) {
package/src/realtime.ts CHANGED
@@ -30,6 +30,15 @@ import type { Transport } from "./transport.js";
30
30
  * Audio input must arrive at real-time pace (send ~20 ms chunks as they
31
31
  * are captured); the model also expects a steady stream, so keep sending
32
32
  * silence — or `mute()` — while the microphone is off.
33
+ *
34
+ * Transcription models (e.g. "Doubao ASR 2.0 Streaming") use the same
35
+ * session for live speech-to-text: `session.created` carries
36
+ * `session.mode: "transcription"`, `session.hotwords` (array or
37
+ * comma-separated string) favours words, and only the
38
+ * `…input_audio_transcription.started / .delta / .completed` events come
39
+ * back — `.delta` holds the sentence so far, `.completed` a finished
40
+ * sentence with `start_ms` / `end_ms`. `commitAudio()` or `close()` ends
41
+ * the audio; the last sentence settles before the session closes.
33
42
  */
34
43
 
35
44
  /** One protocol event. `type` names it; the rest depends on the event. */
@@ -50,8 +59,9 @@ export interface RealtimeUsage {
50
59
 
51
60
  /** How a session ended. */
52
61
  export interface RealtimeCloseInfo {
53
- /** WebSocket close code: 1000 when the session completed; 4000+N for an
54
- * upstream HTTP error N (e.g. 4429); 4408 idle or over-long session. */
62
+ /** WebSocket close code: 1000 when the session completed; 4402 when the
63
+ * spending cap was reached (`maxCost`, or the account's balance); 4000+N
64
+ * for an upstream HTTP error N (e.g. 4429); 4408 idle or over-long session. */
55
65
  code: number;
56
66
  reason: string;
57
67
  /** Null when the session ended before the cost could be reported. */
@@ -69,7 +79,8 @@ export interface WebSocketLike {
69
79
  export type WebSocketConstructor = new (url: string, protocols?: string | string[]) => WebSocketLike;
70
80
 
71
81
  export interface RealtimeConnectOptions {
72
- /** Realtime model name (e.g. "Doubao Realtime Voice"); omit for the platform default. */
82
+ /** Realtime model name (e.g. "Doubao Realtime Voice", or "Doubao ASR 2.0
83
+ * Streaming" for live speech-to-text); omit for the platform default. */
73
84
  model?: string;
74
85
  /**
75
86
  * Session settings sent in the opening `session.create`, e.g.
@@ -78,6 +89,12 @@ export interface RealtimeConnectOptions {
78
89
  session?: Record<string, unknown>;
79
90
  /** Model-specific extension block (Doubao: `{ asr, tts, dialog }`). */
80
91
  extension?: Record<string, unknown>;
92
+ /**
93
+ * End the session (close code 4402) once it has cost this many USD. The
94
+ * account's remaining balance always caps a session; this sets a lower
95
+ * cap. Checked after each turn, so the last turn may go slightly over.
96
+ */
97
+ maxCost?: number;
81
98
  /** Extra query parameters for the handshake. */
82
99
  query?: Record<string, string>;
83
100
  /**
@@ -174,7 +191,9 @@ export class RealtimeSession {
174
191
  this.send({ type: "input_audio_buffer.append", audio: bytesToBase64(asBytes(pcm16)) });
175
192
  }
176
193
 
177
- /** Tell the model the user has finished speaking (skips end-of-speech detection). */
194
+ /** Tell the model the user has finished speaking (skips end-of-speech
195
+ * detection). For a transcription model this ends the audio: the last
196
+ * sentence settles, then the session closes. */
178
197
  commitAudio(): void {
179
198
  this.send({ type: "input_audio_buffer.commit" });
180
199
  }
@@ -336,7 +355,10 @@ export class RealtimeService {
336
355
  ws.send(JSON.stringify(openingEvent(options)));
337
356
  });
338
357
  void session.closed.then((info) => {
339
- fail(new Error(`runapps: realtime connection closed (${info.code}${info.reason ? " " + info.reason : ""})`));
358
+ // A refused handshake (bad key, unknown model, low balance) reaches
359
+ // a WebSocket client only as 1006 — the HTTP status is hidden.
360
+ const hint = info.code === 1006 ? " — check the API key, the model name and the account balance" : "";
361
+ fail(new Error(`runapps: realtime connection closed (${info.code}${info.reason ? " " + info.reason : ""})${hint}`));
340
362
  });
341
363
  });
342
364
  } catch (err) {
@@ -380,6 +402,7 @@ function realtimeURL(baseURL: string, options: RealtimeConnectOptions): string {
380
402
  u.protocol = u.protocol === "http:" ? "ws:" : "wss:";
381
403
  for (const [k, v] of Object.entries(options.query ?? {})) u.searchParams.set(k, v);
382
404
  if (options.model) u.searchParams.set("model", options.model);
405
+ if (options.maxCost !== undefined && options.maxCost > 0) u.searchParams.set("max_cost", String(options.maxCost));
383
406
  return u.toString();
384
407
  }
385
408