@rimelabs/sdk 0.1.0-alpha.24 → 0.1.0-alpha.25

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/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.1.0-alpha.25](https://github.com/rimelabs/rime-sdk/compare/typescript-v0.1.0-alpha.24...typescript-v0.1.0-alpha.25) (2026-10-28)
4
+
5
+
6
+ ### Features
7
+
8
+ * expose Prism reply labels and response data ([#114](https://github.com/rimelabs/rime-sdk/issues/114)) ([4a303a1](https://github.com/rimelabs/rime-sdk/commit/4a303a1b545019dea389f30d0c696e8f7e6cc6b1))
9
+
3
10
  ## [0.1.0-alpha.24](https://github.com/rimelabs/rime-sdk/compare/typescript-v0.1.0-alpha.23...typescript-v0.1.0-alpha.24) (2026-10-27)
4
11
 
5
12
 
@@ -26,6 +26,8 @@ export interface SessionSettings {
26
26
  }
27
27
  /** A copy of the client's labels for every reply: keys to strings. */
28
28
  export declare function responseMetadata(value: unknown): t.Labels;
29
+ /** Copy caller labels before combining them with the SDK's protocol fields. */
30
+ export declare function requestMetadata(value: t.Labels | null | undefined, reserved: Record<string, string>): Record<string, string>;
29
31
  export interface SessionPatch {
30
32
  truncation?: SessionSettings["truncation"];
31
33
  voice?: string;
@@ -76,11 +78,15 @@ export interface ItemDeleted {
76
78
  export interface AudioCleared {
77
79
  kind: "audio.cleared";
78
80
  }
81
+ export interface SessionIdleTimeout {
82
+ kind: "session.idle_timeout";
83
+ idleTimeoutS: number;
84
+ }
79
85
  export interface ResponseEnded extends t.ResponseEnded {
80
86
  drainToken: string | null;
81
87
  }
82
88
  export type Acknowledgment = SessionUpdated | HistoryListed | ItemDeleted | ItemCreated | ToolResultCreated | AudioCleared | t.ItemTruncated | t.ResponseStarted | ResponseEnded;
83
- export type ServerPayload = SessionCreated | Acknowledgment | t.InputEvent<t.NormalizedRealtimeFault> | t.MessageStarted | t.TextDelta | t.TextDone | t.AudioDelta | t.AudioDone | t.ToolCall | t.FaultEvent<t.NormalizedRealtimeFault>;
89
+ export type ServerPayload = SessionCreated | Acknowledgment | t.InputEvent<t.NormalizedRealtimeFault> | t.MessageStarted | t.TextDelta | t.TextDone | t.AudioDelta | t.AudioDone | t.ToolCall | t.FaultEvent<t.NormalizedRealtimeFault> | SessionIdleTimeout;
84
90
  export interface ServerEvent {
85
91
  eventId: string;
86
92
  requestId: string | null;
@@ -12,6 +12,13 @@ export function responseMetadata(value) {
12
12
  throw new RimeInputError("responseMetadata must map keys to strings");
13
13
  return Object.freeze({ ...value });
14
14
  }
15
+ /** Copy caller labels before combining them with the SDK's protocol fields. */
16
+ export function requestMetadata(value, reserved) {
17
+ const labels = value == null ? {} : responseMetadata(value);
18
+ if (Object.keys(labels).some((key) => key.startsWith("prism_")))
19
+ throw new RimeInputError("request metadata keys cannot start with prism_");
20
+ return { ...labels, ...reserved };
21
+ }
15
22
  /** A request carries its result type and checks it before exposing the result. */
16
23
  export class Request {
17
24
  kind;
@@ -67,6 +74,9 @@ function id(value, field) {
67
74
  function optionalString(value, field) {
68
75
  return value == null ? null : string(value, field);
69
76
  }
77
+ function optionalId(value, field) {
78
+ return value == null ? null : id(value, field);
79
+ }
70
80
  function integer(value, field) {
71
81
  if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0)
72
82
  return invalid(field);
@@ -91,6 +101,17 @@ function responseTruncation(response) {
91
101
  : string(value.first_kept_item_id, "prism_truncation.first_kept_item_id"),
92
102
  });
93
103
  }
104
+ function responseUsage(response) {
105
+ if (response.usage == null)
106
+ return null;
107
+ const usage = object(response.usage, "response.usage");
108
+ return Object.freeze({
109
+ inputTokens: integer(usage.input_tokens, "response.usage.input_tokens"),
110
+ outputTokens: integer(usage.output_tokens, "response.usage.output_tokens"),
111
+ totalTokens: integer(usage.total_tokens, "response.usage.total_tokens"),
112
+ contextWindow: integer(usage.context_window, "response.usage.context_window"),
113
+ });
114
+ }
94
115
  function duration(value, field) {
95
116
  if (value == null)
96
117
  return null;
@@ -287,6 +308,15 @@ function decodeValue(value, sessionId) {
287
308
  case "conversation.item.deleted":
288
309
  payload = { kind: "item.deleted", itemId: id(event.item_id, "item_id") };
289
310
  break;
311
+ case "session.idle_timeout": {
312
+ if (event.reason !== "idle_timeout")
313
+ return invalid("reason");
314
+ const timeout = duration(event.idle_timeout_s, "idle_timeout_s");
315
+ if (timeout === null || timeout < 1)
316
+ return invalid("idle_timeout_s");
317
+ payload = { kind: "session.idle_timeout", idleTimeoutS: timeout };
318
+ break;
319
+ }
290
320
  case "input_audio_buffer.cleared":
291
321
  payload = { kind: "audio.cleared" };
292
322
  break;
@@ -398,6 +428,7 @@ function decodeValue(value, sessionId) {
398
428
  : "unknown",
399
429
  parent: parent === null ? null : ref(parent),
400
430
  inputItemId,
431
+ instructionItemId: optionalId(metadata.prism_instruction_item_id, "prism_instruction_item_id"),
401
432
  metadata: clientMetadata(metadata),
402
433
  inputItemIds: Object.freeze(inputItemIds.map((value) => id(value, "prism_input_item_ids"))),
403
434
  };
@@ -415,6 +446,7 @@ function decodeValue(value, sessionId) {
415
446
  kind: "response.ended",
416
447
  settingsRevision: revision(response),
417
448
  truncation: responseTruncation(response),
449
+ usage: responseUsage(response),
418
450
  response: ref(response.id),
419
451
  status: status === "completed" ||
420
452
  status === "cancelled" ||
@@ -56,7 +56,7 @@ export declare class RealtimeSession {
56
56
  * complete list. Replies already started keep their original settings.
57
57
  * Cancellation after submission does not undo an accepted update. */
58
58
  update(options?: t.RealtimeUpdateOptions): Promise<t.SessionInfo>;
59
- sendText(value: string, options?: t.RealtimeOperationOptions): Promise<t.ResponseRef>;
59
+ sendText(value: string, options?: t.ReplyRequestOptions): Promise<t.ResponseRef>;
60
60
  requestReply(options?: t.ReplyOptions): Promise<t.ResponseRef>;
61
61
  /** Read the items the next reply would see, in server order. */
62
62
  getHistory(options?: t.RealtimeOperationOptions): Promise<readonly t.HistoryItem[]>;
@@ -66,7 +66,7 @@ export declare class RealtimeSession {
66
66
  createItem(item: t.HistoryItem, options?: t.HistoryCreateOptions): Promise<t.ItemRef>;
67
67
  addMessage(role: "user" | "assistant", value: string, options?: t.RealtimeOperationOptions): Promise<t.ItemRef>;
68
68
  submitToolResult(call: t.ToolCallRef, output: string, options?: t.RealtimeOperationOptions): Promise<void>;
69
- continueReply(parent: t.ResponseRef, options?: t.RealtimeOperationOptions): Promise<t.ResponseRef>;
69
+ continueReply(parent: t.ResponseRef, options?: t.ReplyRequestOptions): Promise<t.ResponseRef>;
70
70
  cancel(response: t.ResponseRef, options?: t.RealtimeOperationOptions): Promise<void>;
71
71
  /** Replaces the labels later replies carry; a reply already started keeps
72
72
  * its own. Resolves once Prism confirms the change. */
@@ -403,11 +403,19 @@ export class RealtimeSession {
403
403
  }
404
404
  async sendText(value, options = {}) {
405
405
  text(value);
406
- return this.operation(options, (signal) => this.create({ prism_input_text: value, metadata: { prism_cause: "user_text" } }, signal, p.requests.text));
406
+ const metadata = p.requestMetadata(options.metadata, {
407
+ prism_cause: "user_text",
408
+ });
409
+ return this.operation(options, (signal) => this.create({
410
+ prism_input_text: value,
411
+ metadata,
412
+ }, signal, p.requests.text));
407
413
  }
408
414
  async requestReply(options = {}) {
409
415
  const body = {
410
- metadata: { prism_cause: "proactive" },
416
+ metadata: p.requestMetadata(options.metadata, {
417
+ prism_cause: "proactive",
418
+ }),
411
419
  };
412
420
  if (options.instruction != null) {
413
421
  text(options.instruction);
@@ -498,6 +506,10 @@ export class RealtimeSession {
498
506
  }
499
507
  async continueReply(parent, options = {}) {
500
508
  const ref = { ...parent };
509
+ const metadata = p.requestMetadata(options.metadata, {
510
+ prism_cause: "tool_continuation",
511
+ prism_parent_response_id: ref.responseId,
512
+ });
501
513
  const state = this.response(ref);
502
514
  return this.operation(options, async (signal) => {
503
515
  const ready = scope([signal], this.timeouts.readyS, new t.RealtimeAdmissionTimeout("Tool results or parent completion did not arrive before readyS expired"));
@@ -522,10 +534,7 @@ export class RealtimeSession {
522
534
  }
523
535
  return (await this.request(p.requests.create, {
524
536
  response: {
525
- metadata: {
526
- prism_cause: "tool_continuation",
527
- prism_parent_response_id: ref.responseId,
528
- },
537
+ metadata,
529
538
  },
530
539
  }, signal, undefined, state)).response;
531
540
  });
@@ -677,6 +686,9 @@ export class RealtimeSession {
677
686
  throw new RimeStreamError("Tool result acknowledgment names another call");
678
687
  }
679
688
  switch (payload.kind) {
689
+ case "session.idle_timeout":
690
+ this.fail(new t.RealtimeIdleTimeout(payload.idleTimeoutS));
691
+ return;
680
692
  case "session.created":
681
693
  if (this.sessionId === null) {
682
694
  this.sessionId = payload.session.id;
@@ -87,7 +87,11 @@ export interface RealtimeUpdateOptions extends RealtimeOperationOptions {
87
87
  readonly voice?: string;
88
88
  readonly interruptOnSpeech?: boolean;
89
89
  }
90
- export interface ReplyOptions extends RealtimeOperationOptions {
90
+ export interface ReplyRequestOptions extends RealtimeOperationOptions {
91
+ /** Labels for this reply. Request labels override matching session labels. */
92
+ readonly metadata?: Labels | null;
93
+ }
94
+ export interface ReplyOptions extends ReplyRequestOptions {
91
95
  readonly instruction?: string | null;
92
96
  readonly toolCall?: ToolCallRef | null;
93
97
  }
@@ -178,6 +182,11 @@ export interface NormalizedRealtimeFault extends RealtimeFault {
178
182
  }
179
183
  export declare class RealtimeAdmissionTimeout extends RimeTimeoutError {
180
184
  }
185
+ /** Prism closed the conversation after this many idle seconds. */
186
+ export declare class RealtimeIdleTimeout extends RimeTimeoutError {
187
+ readonly idleTimeoutS: number;
188
+ constructor(idleTimeoutS: number);
189
+ }
181
190
  export declare class RimeRealtimeError extends RimeError {
182
191
  readonly fault: NormalizedRealtimeFault;
183
192
  constructor(fault: RealtimeFault);
@@ -244,6 +253,8 @@ export interface ResponseStarted {
244
253
  readonly cause: "speech" | "text" | "proactive" | "tools" | "unknown";
245
254
  readonly parent: ResponseRef | null;
246
255
  readonly inputItemId: string | null;
256
+ /** Saved nudge instruction; pass this ID to deleteItem to remove it. */
257
+ readonly instructionItemId?: string | null;
247
258
  /** The session's labels when the reply began. Prism's `prism_` keys are
248
259
  * left out. */
249
260
  readonly metadata: Labels;
@@ -295,6 +306,12 @@ export interface ResponseTruncation {
295
306
  /** Oldest retained item; null when no earlier item was retained. */
296
307
  readonly firstKeptItemId: string | null;
297
308
  }
309
+ export interface ResponseUsage {
310
+ readonly inputTokens: number;
311
+ readonly outputTokens: number;
312
+ readonly totalTokens: number;
313
+ readonly contextWindow: number;
314
+ }
298
315
  export interface ResponseEnded {
299
316
  /** Settings revision published by the server; null when unavailable.
300
317
  * Optional on caller-created values for compatibility. */
@@ -302,6 +319,8 @@ export interface ResponseEnded {
302
319
  /** Null when the server reports no exclusions or the information is unavailable.
303
320
  * Optional on caller-created values for compatibility. */
304
321
  readonly truncation?: ResponseTruncation | null;
322
+ /** Null when Prism did not count tokens. */
323
+ readonly usage?: ResponseUsage | null;
305
324
  readonly kind: "response.ended";
306
325
  readonly response: ResponseRef;
307
326
  readonly status: "completed" | "cancelled" | "failed" | "unknown";
@@ -2,6 +2,14 @@ import { RimeError, RimeTimeoutError } from "../errors.js";
2
2
  import { normalizeFault } from "./faults.js";
3
3
  export class RealtimeAdmissionTimeout extends RimeTimeoutError {
4
4
  }
5
+ /** Prism closed the conversation after this many idle seconds. */
6
+ export class RealtimeIdleTimeout extends RimeTimeoutError {
7
+ idleTimeoutS;
8
+ constructor(idleTimeoutS) {
9
+ super(`Realtime session idle for ${idleTimeoutS} seconds`);
10
+ this.idleTimeoutS = idleTimeoutS;
11
+ }
12
+ }
5
13
  export class RimeRealtimeError extends RimeError {
6
14
  fault;
7
15
  constructor(fault) {
package/docs/realtime.md CHANGED
@@ -294,6 +294,12 @@ instruction, tool call, and signal in one object. `update` returns
294
294
  `getHistory` returns `Promise<readonly HistoryItem[]>`. The other methods return
295
295
  `Promise<void>`.
296
296
 
297
+ Pass `metadata: { step: "3" }` in the options for `sendText`, `requestReply`, or
298
+ `continueReply` to label only that reply. Request labels replace matching
299
+ session labels in Prism's response. The SDK adds the required `prism_` fields;
300
+ caller keys cannot use that prefix. Omitting `metadata` keeps the existing
301
+ request shape. Replies to speech use the session labels.
302
+
297
303
  `sendText` and `addMessage` require nonblank text of at most 4,000 Unicode
298
304
  characters. `addMessage` accepts `"user"` and `"assistant"` roles. `createItem`
299
305
  also accepts `"system"` messages and allows blank message text, with the same
@@ -397,12 +403,12 @@ are exported from `@rimelabs/sdk`.
397
403
  | `input.committed` | `itemId` |
398
404
  | `transcript.delta`, `transcript.final` | `itemId` and `delta` or `text` |
399
405
  | `transcript.failed` | `itemId`, `error` |
400
- | `response.started` | `response`, `cause`, nullable `parent` and `inputItemId`, `inputItemIds` (the user items it answers), `metadata` (your own keys) |
406
+ | `response.started` | `response`, `cause`, nullable `parent`, `inputItemId`, and `instructionItemId`, `inputItemIds` (the user items it answers), `metadata` (your own keys) |
401
407
  | `message.started` | `output` |
402
408
  | `text.delta`, `text.done` | `output` and `delta` or `text` |
403
409
  | `audio.delta`, `audio.done` | `output`; a delta also has `audio.data` and a complete `audio.format` |
404
410
  | `tool.call` | `call`, `itemId`, `name`, parsed `arguments` |
405
- | `response.ended` | `response`, `status`, nullable `reason` |
411
+ | `response.ended` | `response`, `status`, nullable `reason` and `usage` |
406
412
  | `response.abandoned` | `response`; the SDK requests cancellation after the requesting caller aborted. |
407
413
  | `error` | `error`, a `RealtimeFault` |
408
414
 
@@ -460,6 +466,10 @@ the session remains usable. An unknown request outcome closes the connection and
460
466
  is never replayed. The SDK retries typed-turn admission only after named refusals
461
467
  that confirm the request made no change.
462
468
 
469
+ `RealtimeIdleTimeout` means Prism closed an idle session. Its `idleTimeoutS`
470
+ is the limit reported by Prism. Pending methods and the event iterator reject
471
+ with this error. Reconnect to start a new conversation.
472
+
463
473
  Protocol faults appear as `error` events and can also reject an affected method
464
474
  with `RimeRealtimeError`. Its `.fault` has `code`, `message`, `scope`, nullable
465
475
  `requestId`, `responseId`, `itemId`, `correlationId`, `parameter`, and a `callIds`
@@ -616,6 +626,14 @@ This event can arrive after `response.ended`. It has no response reference and d
616
626
  not require the SDK to retain that response. `text.done` still reports generated
617
627
  text. Add `item.truncated` to exhaustive switches on `SessionEvent.payload.kind`.
618
628
 
629
+ ## Response usage and saved nudge instructions
630
+
631
+ `ResponseEnded.usage` contains `inputTokens`, `outputTokens`, `totalTokens`,
632
+ and `contextWindow` when Prism counted the reply's tokens. It is `null` when
633
+ the count is unavailable. For a nudge with an instruction,
634
+ `ResponseStarted.instructionItemId` names the saved instruction. Pass that ID
635
+ to `deleteItem` to remove it from later history.
636
+
619
637
  ## History excluded from a reply
620
638
 
621
639
  Available in the next SDK release. `ResponseEnded.truncation` exposes the server's
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rimelabs/sdk",
3
- "version": "0.1.0-alpha.24",
3
+ "version": "0.1.0-alpha.25",
4
4
  "description": "Rime streaming TTS, STT and Prism realtime SDK for Node.js",
5
5
  "license": "MIT",
6
6
  "type": "module",