@runappsai/sdk 0.7.0 → 0.7.1

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.1",
4
4
  "description": "JavaScript / TypeScript client for the RunApps AI Gateway",
5
5
  "license": "MIT",
6
6
  "author": "RunApps",
package/src/realtime.ts CHANGED
@@ -50,8 +50,9 @@ export interface RealtimeUsage {
50
50
 
51
51
  /** How a session ended. */
52
52
  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. */
53
+ /** WebSocket close code: 1000 when the session completed; 4402 when the
54
+ * spending cap was reached (`maxCost`, or the account's balance); 4000+N
55
+ * for an upstream HTTP error N (e.g. 4429); 4408 idle or over-long session. */
55
56
  code: number;
56
57
  reason: string;
57
58
  /** Null when the session ended before the cost could be reported. */
@@ -78,6 +79,12 @@ export interface RealtimeConnectOptions {
78
79
  session?: Record<string, unknown>;
79
80
  /** Model-specific extension block (Doubao: `{ asr, tts, dialog }`). */
80
81
  extension?: Record<string, unknown>;
82
+ /**
83
+ * End the session (close code 4402) once it has cost this many USD. The
84
+ * account's remaining balance always caps a session; this sets a lower
85
+ * cap. Checked after each turn, so the last turn may go slightly over.
86
+ */
87
+ maxCost?: number;
81
88
  /** Extra query parameters for the handshake. */
82
89
  query?: Record<string, string>;
83
90
  /**
@@ -336,7 +343,10 @@ export class RealtimeService {
336
343
  ws.send(JSON.stringify(openingEvent(options)));
337
344
  });
338
345
  void session.closed.then((info) => {
339
- fail(new Error(`runapps: realtime connection closed (${info.code}${info.reason ? " " + info.reason : ""})`));
346
+ // A refused handshake (bad key, unknown model, low balance) reaches
347
+ // a WebSocket client only as 1006 — the HTTP status is hidden.
348
+ const hint = info.code === 1006 ? " — check the API key, the model name and the account balance" : "";
349
+ fail(new Error(`runapps: realtime connection closed (${info.code}${info.reason ? " " + info.reason : ""})${hint}`));
340
350
  });
341
351
  });
342
352
  } catch (err) {
@@ -380,6 +390,7 @@ function realtimeURL(baseURL: string, options: RealtimeConnectOptions): string {
380
390
  u.protocol = u.protocol === "http:" ? "ws:" : "wss:";
381
391
  for (const [k, v] of Object.entries(options.query ?? {})) u.searchParams.set(k, v);
382
392
  if (options.model) u.searchParams.set("model", options.model);
393
+ if (options.maxCost !== undefined && options.maxCost > 0) u.searchParams.set("max_cost", String(options.maxCost));
383
394
  return u.toString();
384
395
  }
385
396