@modelrelay/sdk 0.25.1 → 0.27.0

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/README.md CHANGED
@@ -1,434 +1,115 @@
1
1
  # ModelRelay TypeScript SDK
2
2
 
3
- Typed client for Node.js that wraps the ModelRelay API for **consuming** LLM/usage endpoints. Use secret API keys or bearer tokens issued by your backend; publishable-key frontend token flows have been removed.
4
-
5
- ## Installation
6
-
7
3
  ```bash
8
4
  bun add @modelrelay/sdk
9
- # or: npm install @modelrelay/sdk
10
5
  ```
11
6
 
12
- ## Quick Start
7
+ ## Streaming Chat
13
8
 
14
9
  ```ts
15
10
  import { ModelRelay } from "@modelrelay/sdk";
16
11
 
17
- // Use a secret key or bearer token from your backend.
18
- const mr = new ModelRelay({
19
- key: "mr_sk_..."
20
- });
21
-
22
- // Stream chat completions.
23
- const stream = await mr.chat.completions.create({
24
- model: "grok-4-1-fast-reasoning",
25
- messages: [{ role: "user", content: "Hello" }]
26
- });
27
-
28
- for await (const event of stream) {
29
- if (event.type === "message_delta" && event.textDelta) {
30
- console.log(event.textDelta);
31
- }
32
- }
33
- ```
34
-
35
- ### Server-side usage
36
-
37
- Provide a secret API key or bearer token:
38
-
39
- ```ts
40
12
  const mr = new ModelRelay({ key: "mr_sk_..." });
41
- const completion = await mr.chat.completions.create(
42
- { model: "grok-4-1-fast-reasoning", messages: [{ role: "user", content: "Hi" }], stream: false }
43
- );
44
- console.log(completion.content.join(""));
45
- ```
46
-
47
- ## Scripts (run with Bun)
48
-
49
- - `bun run build` — bundle CJS + ESM outputs with type declarations.
50
- - `bun run test` — run unit tests.
51
- - `bun run lint` — typecheck the source without emitting files.
52
-
53
- ## Configuration
54
-
55
- - **Environments**: `environment: "production" | "staging" | "sandbox"` or override `baseUrl`.
56
- - **Auth**: pass a secret/publishable `key` or a bearer `token`. Publishable keys mint frontend tokens automatically.
57
- - **Timeouts & retries**: `connectTimeoutMs` (default 5s per attempt) and `timeoutMs` (default 60s overall; set `0` to disable). Per-call overrides available on `chat.completions.create`. `retry` config (`{ maxAttempts, baseBackoffMs, maxBackoffMs, retryPost }` or `false`) controls exponential backoff with jitter.
58
- - **Headers & metadata**: `defaultHeaders` are sent with every request; `defaultMetadata` merges into every chat request and can be overridden per-call via `metadata`.
59
- - **Client header**: set `clientHeader` to override the telemetry header (defaults to `modelrelay-ts/<version>`).
60
-
61
- ### Timeouts & retry examples
62
-
63
- ```ts
64
- // Shorten connect + request timeouts globally
65
- const mr = new ModelRelay({
66
- key: "mr_sk_...",
67
- connectTimeoutMs: 3_000,
68
- timeoutMs: 20_000,
69
- retry: { maxAttempts: 4, baseBackoffMs: 200, maxBackoffMs: 2_000 }
70
- });
71
-
72
- // Per-call overrides (blocking)
73
- await mr.chat.completions.create(
74
- { model: "grok-4-1-fast-reasoning", messages: [{ role: "user", content: "Hi" }], stream: false },
75
- { timeoutMs: 5_000, retry: false }
76
- );
77
-
78
- // Streaming: keep connect timeout but disable request timeout
79
- const stream = await mr.chat.completions.create(
80
- { model: "grok-4-1-fast-reasoning", messages: [{ role: "user", content: "Hi" }] },
81
- { connectTimeoutMs: 2_000 } // request timeout is already disabled for streams by default
82
- );
83
- ```
84
13
 
85
- ### Typed models, stop reasons, and message roles
86
-
87
- - Models are plain strings (e.g., `"gpt-4o"`), so new models do not require SDK updates.
88
- - Stop reasons are parsed into the `StopReason` union (e.g., `StopReasons.EndTurn`); unknown values surface as `{ other: "<raw>" }`.
89
- - Message roles use a typed union (`MessageRole`) with constants available via `MessageRoles`.
90
- - Usage backfills `totalTokens` when the backend omits it, ensuring consistent accounting.
91
-
92
- ```ts
93
- import { MessageRoles } from "@modelrelay/sdk";
94
-
95
- // Use typed role constants
96
- const messages = [
97
- { role: MessageRoles.System, content: "You are helpful." },
98
- { role: MessageRoles.User, content: "Hello!" },
99
- ];
100
-
101
- // Available roles: User, Assistant, System, Tool
102
- ```
103
-
104
- ### Customer-attributed requests
105
-
106
- For customer-attributed requests, the customer's tier determines which model to use.
107
- Use `forCustomer()` instead of providing a model:
108
-
109
- ```ts
110
- // Customer-attributed: tier determines model, no model parameter needed
111
- const stream = await mr.chat.forCustomer("customer-123").create({
112
- messages: [{ role: "user", content: "Hello!" }]
14
+ const stream = await mr.chat.completions.create({
15
+ model: "claude-sonnet-4-20250514",
16
+ messages: [{ role: "user", content: "Hello" }],
113
17
  });
114
18
 
115
19
  for await (const event of stream) {
116
20
  if (event.type === "message_delta" && event.textDelta) {
117
- console.log(event.textDelta);
21
+ process.stdout.write(event.textDelta);
118
22
  }
119
23
  }
120
-
121
- // Non-streaming
122
- const completion = await mr.chat.forCustomer("customer-123").create(
123
- { messages: [{ role: "user", content: "Hello!" }] },
124
- { stream: false }
125
- );
126
- ```
127
-
128
- This provides compile-time separation between:
129
- - **Direct/PAYGO requests** (`chat.completions.create({ model, ... })`) — model is required
130
- - **Customer-attributed requests** (`chat.forCustomer(id).create(...)`) — tier determines model
131
-
132
- ### Structured outputs (`response_format`)
133
-
134
- Request structured JSON instead of free-form text when the backend supports it:
135
-
136
- ```ts
137
- import { ModelRelay, type ResponseFormat } from "@modelrelay/sdk";
138
-
139
- const mr = new ModelRelay({ key: "mr_sk_..." });
140
-
141
- const format: ResponseFormat = {
142
- type: "json_schema",
143
- json_schema: {
144
- name: "summary",
145
- schema: {
146
- type: "object",
147
- properties: { headline: { type: "string" } },
148
- additionalProperties: false,
149
- },
150
- strict: true,
151
- },
152
- };
153
-
154
- const completion = await mr.chat.completions.create(
155
- {
156
- model: "gpt-4o-mini",
157
- messages: [{ role: "user", content: "Summarize ModelRelay" }],
158
- responseFormat: format,
159
- stream: false,
160
- },
161
- { stream: false },
162
- );
163
-
164
- console.log(completion.content[0]); // JSON string matching your schema
165
24
  ```
166
25
 
167
- ### Structured streaming (NDJSON + response_format)
168
-
169
- Use the structured streaming contract for `/llm/proxy` to stream schema-valid
170
- JSON payloads over NDJSON:
26
+ ## Structured Outputs with Zod
171
27
 
172
28
  ```ts
173
- type Item = { id: string; label: string };
174
- type RecommendationPayload = { items: Item[] };
175
-
176
- const format: ResponseFormat = {
177
- type: "json_schema",
178
- json_schema: {
179
- name: "recommendations",
180
- schema: {
181
- type: "object",
182
- properties: { items: { type: "array", items: { type: "object" } } },
183
- },
184
- },
185
- };
186
-
187
- const stream = await mr.chat.completions.streamJSON<RecommendationPayload>({
188
- model: "grok-4-1-fast",
189
- messages: [{ role: "user", content: "Recommend items for my user" }],
190
- responseFormat: format,
191
- });
192
-
193
- for await (const evt of stream) {
194
- if (evt.type === "update") {
195
- // Progressive UI: evt.payload is a partial but schema-valid payload.
196
- renderPartial(evt.payload.items);
197
- }
198
- if (evt.type === "completion") {
199
- renderFinal(evt.payload.items);
200
- }
201
- }
202
-
203
- // Prefer a single blocking result but still want structured validation?
204
- const final = await stream.collect();
205
- console.log(final.items.length);
206
- ```
207
-
208
- ### Type-safe structured outputs with Zod schemas
209
-
210
- For automatic schema generation and validation, use `structured()` with Zod:
211
-
212
- ```ts
213
- import { ModelRelay } from "@modelrelay/sdk";
214
29
  import { z } from "zod";
215
30
 
216
- const mr = new ModelRelay({ key: "mr_sk_..." });
217
-
218
- // Define your output type with Zod
219
- const PersonSchema = z.object({
31
+ const Person = z.object({
220
32
  name: z.string(),
221
33
  age: z.number(),
222
34
  });
223
35
 
224
- // structured() auto-generates JSON schema and validates responses
225
- const result = await mr.chat.completions.structured(
226
- PersonSchema,
227
- {
228
- model: "claude-sonnet-4-20250514",
229
- messages: [{ role: "user", content: "Extract: John Doe is 30 years old" }],
230
- },
231
- { maxRetries: 2 } // Retry on validation failures
232
- );
36
+ const result = await mr.chat.completions.structured(Person, {
37
+ model: "claude-sonnet-4-20250514",
38
+ messages: [{ role: "user", content: "Extract: John Doe is 30" }],
39
+ });
233
40
 
234
- console.log(`Name: ${result.value.name}, Age: ${result.value.age}`);
235
- console.log(`Succeeded on attempt ${result.attempts}`);
41
+ console.log(result.value); // { name: "John Doe", age: 30 }
236
42
  ```
237
43
 
238
- #### Schema features
44
+ ## Streaming Structured Outputs
239
45
 
240
- Zod schemas map to JSON Schema properties:
46
+ Build progressive UIs that render fields as they complete:
241
47
 
242
48
  ```ts
243
- const StatusSchema = z.object({
244
- // Required string field
245
- code: z.string(),
246
-
247
- // Optional field (not in "required" array)
248
- notes: z.string().optional(),
249
-
250
- // Description for documentation
251
- email: z.string().email().describe("User's email address"),
252
-
253
- // Enum constraint
254
- priority: z.enum(["low", "medium", "high"]),
255
-
256
- // Nested objects are fully supported
257
- address: z.object({
258
- city: z.string(),
259
- country: z.string(),
260
- }),
261
-
262
- // Arrays
263
- tags: z.array(z.string()),
49
+ const Article = z.object({
50
+ title: z.string(),
51
+ summary: z.string(),
52
+ body: z.string(),
264
53
  });
265
- ```
266
54
 
267
- #### Handling validation errors
268
-
269
- When validation fails after all retries:
270
-
271
- ```ts
272
- import { StructuredExhaustedError } from "@modelrelay/sdk";
55
+ const stream = await mr.chat.completions.streamStructured(Article, {
56
+ model: "claude-sonnet-4-20250514",
57
+ messages: [{ role: "user", content: "Write an article about TypeScript" }],
58
+ });
273
59
 
274
- try {
275
- const result = await mr.chat.completions.structured(
276
- PersonSchema,
277
- { model: "claude-sonnet-4-20250514", messages },
278
- { maxRetries: 2 }
279
- );
280
- } catch (err) {
281
- if (err instanceof StructuredExhaustedError) {
282
- console.log(`Failed after ${err.allAttempts.length} attempts`);
283
- for (const attempt of err.allAttempts) {
284
- console.log(`Attempt ${attempt.attempt}: ${attempt.rawJson}`);
285
- if (attempt.error.kind === "validation" && attempt.error.issues) {
286
- for (const issue of attempt.error.issues) {
287
- console.log(` - ${issue.path ?? "root"}: ${issue.message}`);
288
- }
289
- } else if (attempt.error.kind === "decode") {
290
- console.log(` Decode error: ${attempt.error.message}`);
291
- }
292
- }
60
+ for await (const event of stream) {
61
+ // Render fields as soon as they're complete
62
+ if (event.completeFields.has("title")) {
63
+ renderTitle(event.payload.title); // Safe to display
293
64
  }
294
- }
295
- ```
296
-
297
- #### Custom retry handlers
298
-
299
- Customize retry behavior:
300
-
301
- ```ts
302
- import type { RetryHandler } from "@modelrelay/sdk";
303
-
304
- const customHandler: RetryHandler = {
305
- onValidationError(attempt, rawJson, error, messages) {
306
- if (attempt >= 3) {
307
- return null; // Stop retrying
308
- }
309
- return [
310
- {
311
- role: "user",
312
- content: `Invalid response. Issues: ${JSON.stringify(error.issues)}. Try again.`,
313
- },
314
- ];
315
- },
316
- };
317
-
318
- const result = await mr.chat.completions.structured(
319
- PersonSchema,
320
- { model: "claude-sonnet-4-20250514", messages },
321
- { maxRetries: 3, retryHandler: customHandler }
322
- );
323
- ```
324
-
325
- #### Streaming structured outputs
326
-
327
- For streaming with Zod schema (no retries):
328
-
329
- ```ts
330
- const stream = await mr.chat.completions.streamStructured(
331
- PersonSchema,
332
- {
333
- model: "claude-sonnet-4-20250514",
334
- messages: [{ role: "user", content: "Extract: Jane, 25" }],
65
+ if (event.completeFields.has("summary")) {
66
+ renderSummary(event.payload.summary);
335
67
  }
336
- );
337
68
 
338
- for await (const evt of stream) {
339
- if (evt.type === "completion") {
340
- console.log("Final:", evt.payload);
69
+ // Show streaming preview of incomplete fields
70
+ if (!event.completeFields.has("body")) {
71
+ renderBodyPreview(event.payload.body + "");
341
72
  }
342
73
  }
343
74
  ```
344
75
 
345
- #### Customer-attributed structured outputs
346
-
347
- Works with customer-attributed requests too:
348
-
349
- ```ts
350
- const result = await mr.chat.forCustomer("customer-123").structured(
351
- PersonSchema,
352
- { messages: [{ role: "user", content: "Extract: John, 30" }] },
353
- { maxRetries: 2 }
354
- );
355
- ```
356
-
357
- ### Telemetry & metrics hooks
76
+ ## Customer-Attributed Requests
358
77
 
359
- Provide lightweight callbacks to observe latency and usage without extra deps:
78
+ For metered billing, use `forCustomer()` the customer's tier determines the model:
360
79
 
361
80
  ```ts
362
- const calls: string[] = [];
363
- const mr = new ModelRelay({
364
- key: "mr_sk_...",
365
- metrics: {
366
- httpRequest: (m) => calls.push(`http ${m.context.path} ${m.status} ${m.latencyMs}ms`),
367
- streamFirstToken: (m) => calls.push(`first-token ${m.latencyMs}ms`),
368
- usage: (m) => calls.push(`usage ${m.usage.totalTokens}`)
369
- },
370
- trace: {
371
- streamEvent: ({ event }) => calls.push(`event ${event.type}`),
372
- requestFinish: ({ status, latencyMs }) => calls.push(`finished ${status} in ${latencyMs}`)
373
- }
81
+ const stream = await mr.chat.forCustomer("customer-123").create({
82
+ messages: [{ role: "user", content: "Hello" }],
374
83
  });
375
-
376
- // Per-call overrides
377
- await mr.chat.completions.create(
378
- { model: "echo-1", messages: [{ role: "user", content: "hi" }] },
379
- { metrics: { usage: console.log }, trace: { streamEvent: console.debug } }
380
- );
381
84
  ```
382
85
 
383
- ### Error categories
384
-
385
- - **ConfigError**: missing key/token, invalid base URL, or request validation issues.
386
- - **TransportError**: network/connect/request/timeout failures (`kind` is one of `connect | timeout | request | other`), includes retry metadata when retries were attempted.
387
- - **APIError**: Non-2xx responses with `status`, `code`, `fields`, `requestId`, and optional `retries` metadata.
388
-
389
- ## API surface
390
-
391
- - `chat.completions.create(params, options?)`
392
- - Supports streaming (default) or blocking JSON (`stream: false`).
393
- - Accepts per-call `requestId`, `headers`, `metadata`, `timeoutMs`, and `retry` overrides.
394
- - `apiKeys.list() | create() | delete(id)` — manage API keys when using secret keys or bearer tokens.
395
- - `customers` — manage customers with a secret key (see below).
396
-
397
- ## Backend Customer Management
398
-
399
- Use a secret key (`mr_sk_*`) to manage customers from your backend:
86
+ ## Customer Management (Backend)
400
87
 
401
88
  ```ts
402
- import { ModelRelay } from "@modelrelay/sdk";
403
-
404
- const mr = new ModelRelay({ key: "mr_sk_..." });
405
-
406
- // Create or update a customer (upsert by external_id)
89
+ // Create/update customer
407
90
  const customer = await mr.customers.upsert({
408
- tier_id: "your-tier-uuid",
409
- external_id: "github-user-12345", // your app's user ID
91
+ tier_id: "tier-uuid",
92
+ external_id: "your-user-id",
410
93
  email: "user@example.com",
411
94
  });
412
95
 
413
- // List all customers
414
- const customers = await mr.customers.list();
415
-
416
- // Get a specific customer
417
- const customer = await mr.customers.get("customer-uuid");
418
-
419
- // Create a checkout session for subscription billing
420
- const session = await mr.customers.createCheckoutSession("customer-uuid", {
421
- success_url: "https://myapp.com/billing/success",
422
- cancel_url: "https://myapp.com/billing/cancel",
96
+ // Create checkout session for subscription billing
97
+ const session = await mr.customers.createCheckoutSession(customer.id, {
98
+ success_url: "https://myapp.com/success",
99
+ cancel_url: "https://myapp.com/cancel",
423
100
  });
424
- // Redirect user to session.url to complete payment
425
101
 
426
102
  // Check subscription status
427
- const status = await mr.customers.getSubscription("customer-uuid");
428
- if (status.active) {
429
- // Grant access
430
- }
103
+ const status = await mr.customers.getSubscription(customer.id);
104
+ ```
105
+
106
+ ## Configuration
431
107
 
432
- // Delete a customer
433
- await mr.customers.delete("customer-uuid");
108
+ ```ts
109
+ const mr = new ModelRelay({
110
+ key: "mr_sk_...",
111
+ environment: "production", // or "staging", "sandbox"
112
+ timeoutMs: 30_000,
113
+ retry: { maxAttempts: 3 },
114
+ });
434
115
  ```
package/dist/index.cjs CHANGED
@@ -437,7 +437,7 @@ function isTokenReusable(token) {
437
437
  // package.json
438
438
  var package_default = {
439
439
  name: "@modelrelay/sdk",
440
- version: "0.25.1",
440
+ version: "0.27.0",
441
441
  description: "TypeScript SDK for the ModelRelay API",
442
442
  type: "module",
443
443
  main: "dist/index.cjs",
@@ -2123,11 +2123,13 @@ var StructuredJSONStream = class {
2123
2123
  if (rawType === "completion") {
2124
2124
  this.sawTerminal = true;
2125
2125
  }
2126
+ const completeFieldsArray = Array.isArray(obj.complete_fields) ? obj.complete_fields.filter((f) => typeof f === "string") : [];
2126
2127
  const event = {
2127
2128
  type: rawType,
2128
2129
  // biome-ignore lint/suspicious/noExplicitAny: payload is untyped json
2129
2130
  payload: obj.payload,
2130
- requestId: this.requestId
2131
+ requestId: this.requestId,
2132
+ completeFields: new Set(completeFieldsArray)
2131
2133
  };
2132
2134
  return event;
2133
2135
  }
package/dist/index.d.cts CHANGED
@@ -560,10 +560,29 @@ interface ChatCompletionEvent<T = unknown> {
560
560
  raw: string;
561
561
  }
562
562
  type StructuredJSONRecordType = "start" | "update" | "completion" | "error";
563
+ /**
564
+ * Recursively makes all properties optional.
565
+ * Useful for typing partial payloads during progressive streaming before
566
+ * all fields are complete.
567
+ *
568
+ * @example
569
+ * interface Article { title: string; body: string; }
570
+ * type PartialArticle = DeepPartial<Article>;
571
+ * // { title?: string; body?: string; }
572
+ */
573
+ type DeepPartial<T> = T extends object ? {
574
+ [P in keyof T]?: DeepPartial<T[P]>;
575
+ } : T;
563
576
  interface StructuredJSONEvent<T> {
564
577
  type: "update" | "completion";
565
578
  payload: T;
566
579
  requestId?: string;
580
+ /**
581
+ * Set of field paths that are complete (have their closing delimiter).
582
+ * Use dot notation for nested fields (e.g., "metadata.author").
583
+ * Check with completeFields.has("fieldName").
584
+ */
585
+ completeFields: Set<string>;
567
586
  }
568
587
  interface APIFrontendToken {
569
588
  token: string;
@@ -1884,4 +1903,4 @@ declare class ModelRelay {
1884
1903
  constructor(options: ModelRelayOptions);
1885
1904
  }
1886
1905
 
1887
- export { type APIChatResponse, type APIChatUsage, type APICheckoutSession, type APICustomerRef, APIError, type APIFrontendToken, type APIKey, type AttemptRecord, AuthClient, type AuthHeaders, ChatClient, type ChatCompletionCreateParams, type ChatCompletionEvent, type ChatCompletionResponse, ChatCompletionsStream, type ChatEventType, type ChatMessage, type CheckoutSession, type CheckoutSessionRequest, type CodeExecConfig, ConfigError, type Customer, CustomerChatClient, type CustomerChatParams, type CustomerClaimRequest, type CustomerCreateRequest, type CustomerMetadata, type CustomerUpsertRequest, CustomersClient, DEFAULT_BASE_URL, DEFAULT_CLIENT_HEADER, DEFAULT_CONNECT_TIMEOUT_MS, DEFAULT_REQUEST_TIMEOUT_MS, type ErrorCategory, type ErrorCode, ErrorCodes, type FieldError, type FrontendCustomer, type FrontendToken, type FrontendTokenAutoProvisionRequest, type FrontendTokenRequest, type FunctionCall, type FunctionCallDelta, type FunctionTool, type HttpRequestMetrics, type JsonSchemaOptions, type KnownStopReason, type MessageDeltaData, type MessageRole, MessageRoles, type MessageStartData, type MessageStopData, type MetricsCallbacks, type ModelId, ModelRelay, type ModelRelayBaseOptions, ModelRelayError, type ModelRelayKeyOptions, type ModelRelayOptions, type ModelRelayOptionsLegacy, type ModelRelayTokenOptions, type NonEmptyArray, type PriceInterval, type Project, type ProviderId, type RequestContext, type ResponseFormat, type ResponseFormatType, ResponseFormatTypes, type ResponseJSONSchemaFormat, type RetryConfig, type RetryHandler, type RetryMetadata, type RetryOptions, SDK_VERSION, type Schema, type StopReason, StopReasons, type StreamFirstTokenMetrics, StructuredDecodeError, type StructuredErrorKind, StructuredExhaustedError, type StructuredJSONEvent, type StructuredJSONRecordType, StructuredJSONStream, type StructuredOptions, type StructuredResult, type SubscriptionStatus, type Tier, type TierCheckoutRequest, type TierCheckoutSession, TiersClient, type TokenType, type TokenUsageMetrics, type Tool, ToolArgsError, type ToolCall, ToolCallAccumulator, type ToolCallDelta, type ToolChoice, type ToolChoiceType, ToolChoiceTypes, type ToolExecutionResult, type ToolHandler, ToolRegistry, type ToolType, ToolTypes, type TraceCallbacks, TransportError, type TransportErrorKind, type Usage, type UsageSummary, type ValidationIssue, type WebSearchConfig, type WebToolMode, WebToolModes, type XSearchConfig, type ZodLikeSchema, assistantMessageWithToolCalls, createAccessTokenAuth, createApiKeyAuth, createAssistantMessage, createFunctionCall, createFunctionTool, createFunctionToolFromSchema, createRetryMessages, createSystemMessage, createToolCall, createUsage, createUserMessage, createWebTool, defaultRetryHandler, executeWithRetry, firstToolCall, formatToolErrorForModel, getRetryableErrors, hasRetryableErrors, hasToolCalls, isEmailRequired, isNoFreeTier, isNoTiers, isProvisioningError, isPublishableKey, mergeMetrics, mergeTrace, modelToString, normalizeModelId, normalizeStopReason, parseErrorResponse, parseToolArgs, parseToolArgsRaw, respondToToolCall, responseFormatFromZod, stopReasonToString, toolChoiceAuto, toolChoiceNone, toolChoiceRequired, toolResultMessage, tryParseToolArgs, validateWithZod, zodToJsonSchema };
1906
+ export { type APIChatResponse, type APIChatUsage, type APICheckoutSession, type APICustomerRef, APIError, type APIFrontendToken, type APIKey, type AttemptRecord, AuthClient, type AuthHeaders, ChatClient, type ChatCompletionCreateParams, type ChatCompletionEvent, type ChatCompletionResponse, ChatCompletionsStream, type ChatEventType, type ChatMessage, type CheckoutSession, type CheckoutSessionRequest, type CodeExecConfig, ConfigError, type Customer, CustomerChatClient, type CustomerChatParams, type CustomerClaimRequest, type CustomerCreateRequest, type CustomerMetadata, type CustomerUpsertRequest, CustomersClient, DEFAULT_BASE_URL, DEFAULT_CLIENT_HEADER, DEFAULT_CONNECT_TIMEOUT_MS, DEFAULT_REQUEST_TIMEOUT_MS, type DeepPartial, type ErrorCategory, type ErrorCode, ErrorCodes, type FieldError, type FrontendCustomer, type FrontendToken, type FrontendTokenAutoProvisionRequest, type FrontendTokenRequest, type FunctionCall, type FunctionCallDelta, type FunctionTool, type HttpRequestMetrics, type JsonSchemaOptions, type KnownStopReason, type MessageDeltaData, type MessageRole, MessageRoles, type MessageStartData, type MessageStopData, type MetricsCallbacks, type ModelId, ModelRelay, type ModelRelayBaseOptions, ModelRelayError, type ModelRelayKeyOptions, type ModelRelayOptions, type ModelRelayOptionsLegacy, type ModelRelayTokenOptions, type NonEmptyArray, type PriceInterval, type Project, type ProviderId, type RequestContext, type ResponseFormat, type ResponseFormatType, ResponseFormatTypes, type ResponseJSONSchemaFormat, type RetryConfig, type RetryHandler, type RetryMetadata, type RetryOptions, SDK_VERSION, type Schema, type StopReason, StopReasons, type StreamFirstTokenMetrics, StructuredDecodeError, type StructuredErrorKind, StructuredExhaustedError, type StructuredJSONEvent, type StructuredJSONRecordType, StructuredJSONStream, type StructuredOptions, type StructuredResult, type SubscriptionStatus, type Tier, type TierCheckoutRequest, type TierCheckoutSession, TiersClient, type TokenType, type TokenUsageMetrics, type Tool, ToolArgsError, type ToolCall, ToolCallAccumulator, type ToolCallDelta, type ToolChoice, type ToolChoiceType, ToolChoiceTypes, type ToolExecutionResult, type ToolHandler, ToolRegistry, type ToolType, ToolTypes, type TraceCallbacks, TransportError, type TransportErrorKind, type Usage, type UsageSummary, type ValidationIssue, type WebSearchConfig, type WebToolMode, WebToolModes, type XSearchConfig, type ZodLikeSchema, assistantMessageWithToolCalls, createAccessTokenAuth, createApiKeyAuth, createAssistantMessage, createFunctionCall, createFunctionTool, createFunctionToolFromSchema, createRetryMessages, createSystemMessage, createToolCall, createUsage, createUserMessage, createWebTool, defaultRetryHandler, executeWithRetry, firstToolCall, formatToolErrorForModel, getRetryableErrors, hasRetryableErrors, hasToolCalls, isEmailRequired, isNoFreeTier, isNoTiers, isProvisioningError, isPublishableKey, mergeMetrics, mergeTrace, modelToString, normalizeModelId, normalizeStopReason, parseErrorResponse, parseToolArgs, parseToolArgsRaw, respondToToolCall, responseFormatFromZod, stopReasonToString, toolChoiceAuto, toolChoiceNone, toolChoiceRequired, toolResultMessage, tryParseToolArgs, validateWithZod, zodToJsonSchema };
package/dist/index.d.ts CHANGED
@@ -560,10 +560,29 @@ interface ChatCompletionEvent<T = unknown> {
560
560
  raw: string;
561
561
  }
562
562
  type StructuredJSONRecordType = "start" | "update" | "completion" | "error";
563
+ /**
564
+ * Recursively makes all properties optional.
565
+ * Useful for typing partial payloads during progressive streaming before
566
+ * all fields are complete.
567
+ *
568
+ * @example
569
+ * interface Article { title: string; body: string; }
570
+ * type PartialArticle = DeepPartial<Article>;
571
+ * // { title?: string; body?: string; }
572
+ */
573
+ type DeepPartial<T> = T extends object ? {
574
+ [P in keyof T]?: DeepPartial<T[P]>;
575
+ } : T;
563
576
  interface StructuredJSONEvent<T> {
564
577
  type: "update" | "completion";
565
578
  payload: T;
566
579
  requestId?: string;
580
+ /**
581
+ * Set of field paths that are complete (have their closing delimiter).
582
+ * Use dot notation for nested fields (e.g., "metadata.author").
583
+ * Check with completeFields.has("fieldName").
584
+ */
585
+ completeFields: Set<string>;
567
586
  }
568
587
  interface APIFrontendToken {
569
588
  token: string;
@@ -1884,4 +1903,4 @@ declare class ModelRelay {
1884
1903
  constructor(options: ModelRelayOptions);
1885
1904
  }
1886
1905
 
1887
- export { type APIChatResponse, type APIChatUsage, type APICheckoutSession, type APICustomerRef, APIError, type APIFrontendToken, type APIKey, type AttemptRecord, AuthClient, type AuthHeaders, ChatClient, type ChatCompletionCreateParams, type ChatCompletionEvent, type ChatCompletionResponse, ChatCompletionsStream, type ChatEventType, type ChatMessage, type CheckoutSession, type CheckoutSessionRequest, type CodeExecConfig, ConfigError, type Customer, CustomerChatClient, type CustomerChatParams, type CustomerClaimRequest, type CustomerCreateRequest, type CustomerMetadata, type CustomerUpsertRequest, CustomersClient, DEFAULT_BASE_URL, DEFAULT_CLIENT_HEADER, DEFAULT_CONNECT_TIMEOUT_MS, DEFAULT_REQUEST_TIMEOUT_MS, type ErrorCategory, type ErrorCode, ErrorCodes, type FieldError, type FrontendCustomer, type FrontendToken, type FrontendTokenAutoProvisionRequest, type FrontendTokenRequest, type FunctionCall, type FunctionCallDelta, type FunctionTool, type HttpRequestMetrics, type JsonSchemaOptions, type KnownStopReason, type MessageDeltaData, type MessageRole, MessageRoles, type MessageStartData, type MessageStopData, type MetricsCallbacks, type ModelId, ModelRelay, type ModelRelayBaseOptions, ModelRelayError, type ModelRelayKeyOptions, type ModelRelayOptions, type ModelRelayOptionsLegacy, type ModelRelayTokenOptions, type NonEmptyArray, type PriceInterval, type Project, type ProviderId, type RequestContext, type ResponseFormat, type ResponseFormatType, ResponseFormatTypes, type ResponseJSONSchemaFormat, type RetryConfig, type RetryHandler, type RetryMetadata, type RetryOptions, SDK_VERSION, type Schema, type StopReason, StopReasons, type StreamFirstTokenMetrics, StructuredDecodeError, type StructuredErrorKind, StructuredExhaustedError, type StructuredJSONEvent, type StructuredJSONRecordType, StructuredJSONStream, type StructuredOptions, type StructuredResult, type SubscriptionStatus, type Tier, type TierCheckoutRequest, type TierCheckoutSession, TiersClient, type TokenType, type TokenUsageMetrics, type Tool, ToolArgsError, type ToolCall, ToolCallAccumulator, type ToolCallDelta, type ToolChoice, type ToolChoiceType, ToolChoiceTypes, type ToolExecutionResult, type ToolHandler, ToolRegistry, type ToolType, ToolTypes, type TraceCallbacks, TransportError, type TransportErrorKind, type Usage, type UsageSummary, type ValidationIssue, type WebSearchConfig, type WebToolMode, WebToolModes, type XSearchConfig, type ZodLikeSchema, assistantMessageWithToolCalls, createAccessTokenAuth, createApiKeyAuth, createAssistantMessage, createFunctionCall, createFunctionTool, createFunctionToolFromSchema, createRetryMessages, createSystemMessage, createToolCall, createUsage, createUserMessage, createWebTool, defaultRetryHandler, executeWithRetry, firstToolCall, formatToolErrorForModel, getRetryableErrors, hasRetryableErrors, hasToolCalls, isEmailRequired, isNoFreeTier, isNoTiers, isProvisioningError, isPublishableKey, mergeMetrics, mergeTrace, modelToString, normalizeModelId, normalizeStopReason, parseErrorResponse, parseToolArgs, parseToolArgsRaw, respondToToolCall, responseFormatFromZod, stopReasonToString, toolChoiceAuto, toolChoiceNone, toolChoiceRequired, toolResultMessage, tryParseToolArgs, validateWithZod, zodToJsonSchema };
1906
+ export { type APIChatResponse, type APIChatUsage, type APICheckoutSession, type APICustomerRef, APIError, type APIFrontendToken, type APIKey, type AttemptRecord, AuthClient, type AuthHeaders, ChatClient, type ChatCompletionCreateParams, type ChatCompletionEvent, type ChatCompletionResponse, ChatCompletionsStream, type ChatEventType, type ChatMessage, type CheckoutSession, type CheckoutSessionRequest, type CodeExecConfig, ConfigError, type Customer, CustomerChatClient, type CustomerChatParams, type CustomerClaimRequest, type CustomerCreateRequest, type CustomerMetadata, type CustomerUpsertRequest, CustomersClient, DEFAULT_BASE_URL, DEFAULT_CLIENT_HEADER, DEFAULT_CONNECT_TIMEOUT_MS, DEFAULT_REQUEST_TIMEOUT_MS, type DeepPartial, type ErrorCategory, type ErrorCode, ErrorCodes, type FieldError, type FrontendCustomer, type FrontendToken, type FrontendTokenAutoProvisionRequest, type FrontendTokenRequest, type FunctionCall, type FunctionCallDelta, type FunctionTool, type HttpRequestMetrics, type JsonSchemaOptions, type KnownStopReason, type MessageDeltaData, type MessageRole, MessageRoles, type MessageStartData, type MessageStopData, type MetricsCallbacks, type ModelId, ModelRelay, type ModelRelayBaseOptions, ModelRelayError, type ModelRelayKeyOptions, type ModelRelayOptions, type ModelRelayOptionsLegacy, type ModelRelayTokenOptions, type NonEmptyArray, type PriceInterval, type Project, type ProviderId, type RequestContext, type ResponseFormat, type ResponseFormatType, ResponseFormatTypes, type ResponseJSONSchemaFormat, type RetryConfig, type RetryHandler, type RetryMetadata, type RetryOptions, SDK_VERSION, type Schema, type StopReason, StopReasons, type StreamFirstTokenMetrics, StructuredDecodeError, type StructuredErrorKind, StructuredExhaustedError, type StructuredJSONEvent, type StructuredJSONRecordType, StructuredJSONStream, type StructuredOptions, type StructuredResult, type SubscriptionStatus, type Tier, type TierCheckoutRequest, type TierCheckoutSession, TiersClient, type TokenType, type TokenUsageMetrics, type Tool, ToolArgsError, type ToolCall, ToolCallAccumulator, type ToolCallDelta, type ToolChoice, type ToolChoiceType, ToolChoiceTypes, type ToolExecutionResult, type ToolHandler, ToolRegistry, type ToolType, ToolTypes, type TraceCallbacks, TransportError, type TransportErrorKind, type Usage, type UsageSummary, type ValidationIssue, type WebSearchConfig, type WebToolMode, WebToolModes, type XSearchConfig, type ZodLikeSchema, assistantMessageWithToolCalls, createAccessTokenAuth, createApiKeyAuth, createAssistantMessage, createFunctionCall, createFunctionTool, createFunctionToolFromSchema, createRetryMessages, createSystemMessage, createToolCall, createUsage, createUserMessage, createWebTool, defaultRetryHandler, executeWithRetry, firstToolCall, formatToolErrorForModel, getRetryableErrors, hasRetryableErrors, hasToolCalls, isEmailRequired, isNoFreeTier, isNoTiers, isProvisioningError, isPublishableKey, mergeMetrics, mergeTrace, modelToString, normalizeModelId, normalizeStopReason, parseErrorResponse, parseToolArgs, parseToolArgsRaw, respondToToolCall, responseFormatFromZod, stopReasonToString, toolChoiceAuto, toolChoiceNone, toolChoiceRequired, toolResultMessage, tryParseToolArgs, validateWithZod, zodToJsonSchema };
package/dist/index.js CHANGED
@@ -340,7 +340,7 @@ function isTokenReusable(token) {
340
340
  // package.json
341
341
  var package_default = {
342
342
  name: "@modelrelay/sdk",
343
- version: "0.25.1",
343
+ version: "0.27.0",
344
344
  description: "TypeScript SDK for the ModelRelay API",
345
345
  type: "module",
346
346
  main: "dist/index.cjs",
@@ -2026,11 +2026,13 @@ var StructuredJSONStream = class {
2026
2026
  if (rawType === "completion") {
2027
2027
  this.sawTerminal = true;
2028
2028
  }
2029
+ const completeFieldsArray = Array.isArray(obj.complete_fields) ? obj.complete_fields.filter((f) => typeof f === "string") : [];
2029
2030
  const event = {
2030
2031
  type: rawType,
2031
2032
  // biome-ignore lint/suspicious/noExplicitAny: payload is untyped json
2032
2033
  payload: obj.payload,
2033
- requestId: this.requestId
2034
+ requestId: this.requestId,
2035
+ completeFields: new Set(completeFieldsArray)
2034
2036
  };
2035
2037
  return event;
2036
2038
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@modelrelay/sdk",
3
- "version": "0.25.1",
3
+ "version": "0.27.0",
4
4
  "description": "TypeScript SDK for the ModelRelay API",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",