@modelrelay/sdk 0.25.1 → 0.30.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 +54 -373
- package/dist/index.cjs +86 -120
- package/dist/index.d.cts +24 -6
- package/dist/index.d.ts +24 -6
- package/dist/index.js +86 -120
- package/package.json +1 -1
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
|
-
##
|
|
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
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
225
|
-
|
|
226
|
-
|
|
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(
|
|
235
|
-
console.log(`Succeeded on attempt ${result.attempts}`);
|
|
41
|
+
console.log(result.value); // { name: "John Doe", age: 30 }
|
|
236
42
|
```
|
|
237
43
|
|
|
238
|
-
|
|
44
|
+
## Streaming Structured Outputs
|
|
239
45
|
|
|
240
|
-
|
|
46
|
+
Build progressive UIs that render fields as they complete:
|
|
241
47
|
|
|
242
48
|
```ts
|
|
243
|
-
const
|
|
244
|
-
|
|
245
|
-
|
|
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
|
-
|
|
268
|
-
|
|
269
|
-
|
|
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
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
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
|
-
|
|
339
|
-
if (
|
|
340
|
-
|
|
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
|
-
|
|
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
|
-
|
|
78
|
+
For metered billing, use `forCustomer()` — the customer's tier determines the model:
|
|
360
79
|
|
|
361
80
|
```ts
|
|
362
|
-
const
|
|
363
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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: "
|
|
409
|
-
external_id: "
|
|
91
|
+
tier_id: "tier-uuid",
|
|
92
|
+
external_id: "your-user-id",
|
|
410
93
|
email: "user@example.com",
|
|
411
94
|
});
|
|
412
95
|
|
|
413
|
-
//
|
|
414
|
-
const
|
|
415
|
-
|
|
416
|
-
|
|
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(
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
103
|
+
const status = await mr.customers.getSubscription(customer.id);
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Configuration
|
|
431
107
|
|
|
432
|
-
|
|
433
|
-
|
|
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.
|
|
440
|
+
version: "0.30.0",
|
|
441
441
|
description: "TypeScript SDK for the ModelRelay API",
|
|
442
442
|
type: "module",
|
|
443
443
|
main: "dist/index.cjs",
|
|
@@ -527,7 +527,6 @@ var ToolChoiceTypes = {
|
|
|
527
527
|
};
|
|
528
528
|
var ResponseFormatTypes = {
|
|
529
529
|
Text: "text",
|
|
530
|
-
JsonObject: "json_object",
|
|
531
530
|
JsonSchema: "json_schema"
|
|
532
531
|
};
|
|
533
532
|
function mergeMetrics(base, override) {
|
|
@@ -1332,7 +1331,7 @@ var ChatCompletionsClient = class {
|
|
|
1332
1331
|
headers,
|
|
1333
1332
|
apiKey: authHeaders.apiKey,
|
|
1334
1333
|
accessToken: authHeaders.accessToken,
|
|
1335
|
-
accept: stream ? "
|
|
1334
|
+
accept: stream ? "application/x-ndjson" : "application/json",
|
|
1336
1335
|
raw: true,
|
|
1337
1336
|
signal: options.signal,
|
|
1338
1337
|
timeoutMs: options.timeoutMs ?? (stream ? 0 : void 0),
|
|
@@ -1385,9 +1384,9 @@ var ChatCompletionsClient = class {
|
|
|
1385
1384
|
if (!hasUserMessage(params.messages)) {
|
|
1386
1385
|
throw new ConfigError("at least one user message is required");
|
|
1387
1386
|
}
|
|
1388
|
-
if (!params.responseFormat || params.responseFormat.type !== "
|
|
1387
|
+
if (!params.responseFormat || params.responseFormat.type !== "json_schema") {
|
|
1389
1388
|
throw new ConfigError(
|
|
1390
|
-
"responseFormat with type=
|
|
1389
|
+
"responseFormat with type=json_schema is required for structured streaming"
|
|
1391
1390
|
);
|
|
1392
1391
|
}
|
|
1393
1392
|
const authHeaders = await this.auth.authForChat();
|
|
@@ -1644,7 +1643,7 @@ var CustomerChatClient = class {
|
|
|
1644
1643
|
headers,
|
|
1645
1644
|
apiKey: authHeaders.apiKey,
|
|
1646
1645
|
accessToken: authHeaders.accessToken,
|
|
1647
|
-
accept: stream ? "
|
|
1646
|
+
accept: stream ? "application/x-ndjson" : "application/json",
|
|
1648
1647
|
raw: true,
|
|
1649
1648
|
signal: options.signal,
|
|
1650
1649
|
timeoutMs: options.timeoutMs ?? (stream ? 0 : void 0),
|
|
@@ -1697,9 +1696,9 @@ var CustomerChatClient = class {
|
|
|
1697
1696
|
if (!hasUserMessage(params.messages)) {
|
|
1698
1697
|
throw new ConfigError("at least one user message is required");
|
|
1699
1698
|
}
|
|
1700
|
-
if (!params.responseFormat || params.responseFormat.type !== "
|
|
1699
|
+
if (!params.responseFormat || params.responseFormat.type !== "json_schema") {
|
|
1701
1700
|
throw new ConfigError(
|
|
1702
|
-
"responseFormat with type=
|
|
1701
|
+
"responseFormat with type=json_schema is required for structured streaming"
|
|
1703
1702
|
);
|
|
1704
1703
|
}
|
|
1705
1704
|
const authHeaders = await this.auth.authForChat(this.customerId);
|
|
@@ -1926,9 +1925,9 @@ var ChatCompletionsStream = class {
|
|
|
1926
1925
|
}
|
|
1927
1926
|
const { value, done } = await reader.read();
|
|
1928
1927
|
if (done) {
|
|
1929
|
-
const {
|
|
1930
|
-
for (const
|
|
1931
|
-
const parsed =
|
|
1928
|
+
const { records: records2 } = consumeNDJSONBuffer(buffer, true);
|
|
1929
|
+
for (const line of records2) {
|
|
1930
|
+
const parsed = mapNDJSONChatEvent(line, this.requestId);
|
|
1932
1931
|
if (parsed) {
|
|
1933
1932
|
this.handleStreamEvent(parsed);
|
|
1934
1933
|
yield parsed;
|
|
@@ -1937,10 +1936,10 @@ var ChatCompletionsStream = class {
|
|
|
1937
1936
|
return;
|
|
1938
1937
|
}
|
|
1939
1938
|
buffer += decoder.decode(value, { stream: true });
|
|
1940
|
-
const {
|
|
1939
|
+
const { records, remainder } = consumeNDJSONBuffer(buffer);
|
|
1941
1940
|
buffer = remainder;
|
|
1942
|
-
for (const
|
|
1943
|
-
const parsed =
|
|
1941
|
+
for (const line of records) {
|
|
1942
|
+
const parsed = mapNDJSONChatEvent(line, this.requestId);
|
|
1944
1943
|
if (parsed) {
|
|
1945
1944
|
this.handleStreamEvent(parsed);
|
|
1946
1945
|
yield parsed;
|
|
@@ -2123,11 +2122,13 @@ var StructuredJSONStream = class {
|
|
|
2123
2122
|
if (rawType === "completion") {
|
|
2124
2123
|
this.sawTerminal = true;
|
|
2125
2124
|
}
|
|
2125
|
+
const completeFieldsArray = Array.isArray(obj.complete_fields) ? obj.complete_fields.filter((f) => typeof f === "string") : [];
|
|
2126
2126
|
const event = {
|
|
2127
2127
|
type: rawType,
|
|
2128
2128
|
// biome-ignore lint/suspicious/noExplicitAny: payload is untyped json
|
|
2129
2129
|
payload: obj.payload,
|
|
2130
|
-
requestId: this.requestId
|
|
2130
|
+
requestId: this.requestId,
|
|
2131
|
+
completeFields: new Set(completeFieldsArray)
|
|
2131
2132
|
};
|
|
2132
2133
|
return event;
|
|
2133
2134
|
}
|
|
@@ -2150,48 +2151,6 @@ var StructuredJSONStream = class {
|
|
|
2150
2151
|
this.trace.streamEvent({ context: this.context, event });
|
|
2151
2152
|
}
|
|
2152
2153
|
};
|
|
2153
|
-
function consumeSSEBuffer(buffer, flush = false) {
|
|
2154
|
-
const events = [];
|
|
2155
|
-
let eventName = "";
|
|
2156
|
-
let dataLines = [];
|
|
2157
|
-
let remainder = "";
|
|
2158
|
-
const lines = buffer.split(/\r?\n/);
|
|
2159
|
-
const lastIndex = lines.length - 1;
|
|
2160
|
-
const limit = flush ? lines.length : Math.max(0, lastIndex);
|
|
2161
|
-
const pushEvent = () => {
|
|
2162
|
-
if (!eventName && dataLines.length === 0) {
|
|
2163
|
-
return;
|
|
2164
|
-
}
|
|
2165
|
-
events.push({
|
|
2166
|
-
event: eventName || "message",
|
|
2167
|
-
data: dataLines.join("\n")
|
|
2168
|
-
});
|
|
2169
|
-
eventName = "";
|
|
2170
|
-
dataLines = [];
|
|
2171
|
-
};
|
|
2172
|
-
for (let i = 0; i < limit; i++) {
|
|
2173
|
-
const line = lines[i];
|
|
2174
|
-
if (line === "") {
|
|
2175
|
-
pushEvent();
|
|
2176
|
-
continue;
|
|
2177
|
-
}
|
|
2178
|
-
if (line.startsWith(":")) {
|
|
2179
|
-
continue;
|
|
2180
|
-
}
|
|
2181
|
-
if (line.startsWith("event:")) {
|
|
2182
|
-
eventName = line.slice(6).trim();
|
|
2183
|
-
} else if (line.startsWith("data:")) {
|
|
2184
|
-
dataLines.push(line.slice(5).trimStart());
|
|
2185
|
-
}
|
|
2186
|
-
}
|
|
2187
|
-
if (flush) {
|
|
2188
|
-
pushEvent();
|
|
2189
|
-
remainder = "";
|
|
2190
|
-
} else {
|
|
2191
|
-
remainder = lines[lastIndex] ?? "";
|
|
2192
|
-
}
|
|
2193
|
-
return { events, remainder };
|
|
2194
|
-
}
|
|
2195
2154
|
function consumeNDJSONBuffer(buffer, flush = false) {
|
|
2196
2155
|
const lines = buffer.split(/\r?\n/);
|
|
2197
2156
|
const records = [];
|
|
@@ -2205,29 +2164,79 @@ function consumeNDJSONBuffer(buffer, flush = false) {
|
|
|
2205
2164
|
const remainder = flush ? "" : lines[lastIndex] ?? "";
|
|
2206
2165
|
return { records, remainder };
|
|
2207
2166
|
}
|
|
2208
|
-
function
|
|
2209
|
-
let parsed
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2167
|
+
function mapNDJSONChatEvent(line, requestId) {
|
|
2168
|
+
let parsed;
|
|
2169
|
+
try {
|
|
2170
|
+
parsed = JSON.parse(line);
|
|
2171
|
+
} catch (err) {
|
|
2172
|
+
console.warn(
|
|
2173
|
+
`[ModelRelay SDK] Failed to parse NDJSON line: ${err instanceof Error ? err.message : String(err)}`,
|
|
2174
|
+
{ line: line.substring(0, 200), requestId }
|
|
2175
|
+
);
|
|
2176
|
+
return null;
|
|
2177
|
+
}
|
|
2178
|
+
if (!parsed || typeof parsed !== "object") {
|
|
2179
|
+
console.warn("[ModelRelay SDK] NDJSON record is not an object", {
|
|
2180
|
+
parsed,
|
|
2181
|
+
requestId
|
|
2182
|
+
});
|
|
2183
|
+
return null;
|
|
2184
|
+
}
|
|
2185
|
+
const obj = parsed;
|
|
2186
|
+
const recordType = String(obj.type || "").trim().toLowerCase();
|
|
2187
|
+
if (recordType === "keepalive") {
|
|
2188
|
+
return null;
|
|
2189
|
+
}
|
|
2190
|
+
if (!recordType) {
|
|
2191
|
+
console.warn("[ModelRelay SDK] NDJSON record missing 'type' field", {
|
|
2192
|
+
obj,
|
|
2193
|
+
requestId
|
|
2194
|
+
});
|
|
2195
|
+
return null;
|
|
2196
|
+
}
|
|
2197
|
+
let type;
|
|
2198
|
+
switch (recordType) {
|
|
2199
|
+
case "start":
|
|
2200
|
+
type = "message_start";
|
|
2201
|
+
break;
|
|
2202
|
+
case "update":
|
|
2203
|
+
type = "message_delta";
|
|
2204
|
+
break;
|
|
2205
|
+
case "completion":
|
|
2206
|
+
type = "message_stop";
|
|
2207
|
+
break;
|
|
2208
|
+
case "error":
|
|
2209
|
+
type = "custom";
|
|
2210
|
+
break;
|
|
2211
|
+
// Tool use event types
|
|
2212
|
+
case "tool_use_start":
|
|
2213
|
+
type = "tool_use_start";
|
|
2214
|
+
break;
|
|
2215
|
+
case "tool_use_delta":
|
|
2216
|
+
type = "tool_use_delta";
|
|
2217
|
+
break;
|
|
2218
|
+
case "tool_use_stop":
|
|
2219
|
+
type = "tool_use_stop";
|
|
2220
|
+
break;
|
|
2221
|
+
default:
|
|
2222
|
+
type = "custom";
|
|
2223
|
+
}
|
|
2224
|
+
const usage = normalizeUsage(obj.usage);
|
|
2225
|
+
const responseId = obj.request_id;
|
|
2226
|
+
const model = normalizeModelId(obj.model);
|
|
2227
|
+
const stopReason = normalizeStopReason(obj.stop_reason);
|
|
2228
|
+
let textDelta;
|
|
2229
|
+
if (obj.payload && typeof obj.payload === "object") {
|
|
2230
|
+
if (typeof obj.payload.content === "string") {
|
|
2231
|
+
textDelta = obj.payload.content;
|
|
2215
2232
|
}
|
|
2216
2233
|
}
|
|
2217
|
-
const
|
|
2218
|
-
const
|
|
2219
|
-
const type = normalizeEventType(raw.event, p);
|
|
2220
|
-
const usage = normalizeUsage(p.usage);
|
|
2221
|
-
const responseId = p.response_id || p.id || p?.message?.id;
|
|
2222
|
-
const model = normalizeModelId(p.model || p?.message?.model);
|
|
2223
|
-
const stopReason = normalizeStopReason(p.stop_reason);
|
|
2224
|
-
const textDelta = extractTextDelta(p);
|
|
2225
|
-
const toolCallDelta = extractToolCallDelta(p, type);
|
|
2226
|
-
const toolCalls = extractToolCalls(p, type);
|
|
2234
|
+
const toolCallDelta = extractToolCallDelta(obj, type);
|
|
2235
|
+
const toolCalls = extractToolCalls(obj, type);
|
|
2227
2236
|
return {
|
|
2228
2237
|
type,
|
|
2229
|
-
event:
|
|
2230
|
-
data:
|
|
2238
|
+
event: recordType,
|
|
2239
|
+
data: obj,
|
|
2231
2240
|
textDelta,
|
|
2232
2241
|
toolCallDelta,
|
|
2233
2242
|
toolCalls,
|
|
@@ -2236,52 +2245,9 @@ function mapChatEvent(raw, requestId) {
|
|
|
2236
2245
|
stopReason,
|
|
2237
2246
|
usage,
|
|
2238
2247
|
requestId,
|
|
2239
|
-
raw:
|
|
2248
|
+
raw: line
|
|
2240
2249
|
};
|
|
2241
2250
|
}
|
|
2242
|
-
function normalizeEventType(eventName, payload) {
|
|
2243
|
-
const hint = String(
|
|
2244
|
-
payload?.type || payload?.event || eventName || ""
|
|
2245
|
-
).trim();
|
|
2246
|
-
switch (hint) {
|
|
2247
|
-
case "message_start":
|
|
2248
|
-
return "message_start";
|
|
2249
|
-
case "message_delta":
|
|
2250
|
-
return "message_delta";
|
|
2251
|
-
case "message_stop":
|
|
2252
|
-
return "message_stop";
|
|
2253
|
-
case "tool_use_start":
|
|
2254
|
-
return "tool_use_start";
|
|
2255
|
-
case "tool_use_delta":
|
|
2256
|
-
return "tool_use_delta";
|
|
2257
|
-
case "tool_use_stop":
|
|
2258
|
-
return "tool_use_stop";
|
|
2259
|
-
case "ping":
|
|
2260
|
-
return "ping";
|
|
2261
|
-
default:
|
|
2262
|
-
return "custom";
|
|
2263
|
-
}
|
|
2264
|
-
}
|
|
2265
|
-
function extractTextDelta(payload) {
|
|
2266
|
-
if (!payload || typeof payload !== "object") {
|
|
2267
|
-
return void 0;
|
|
2268
|
-
}
|
|
2269
|
-
if (typeof payload.text_delta === "string" && payload.text_delta !== "") {
|
|
2270
|
-
return payload.text_delta;
|
|
2271
|
-
}
|
|
2272
|
-
if (typeof payload.delta === "string") {
|
|
2273
|
-
return payload.delta;
|
|
2274
|
-
}
|
|
2275
|
-
if (payload.delta && typeof payload.delta === "object") {
|
|
2276
|
-
if (typeof payload.delta.text === "string") {
|
|
2277
|
-
return payload.delta.text;
|
|
2278
|
-
}
|
|
2279
|
-
if (typeof payload.delta.content === "string") {
|
|
2280
|
-
return payload.delta.content;
|
|
2281
|
-
}
|
|
2282
|
-
}
|
|
2283
|
-
return void 0;
|
|
2284
|
-
}
|
|
2285
2251
|
function extractToolCallDelta(payload, type) {
|
|
2286
2252
|
if (!payload || typeof payload !== "object") {
|
|
2287
2253
|
return void 0;
|
package/dist/index.d.cts
CHANGED
|
@@ -350,7 +350,6 @@ interface ToolCall {
|
|
|
350
350
|
}
|
|
351
351
|
declare const ResponseFormatTypes: {
|
|
352
352
|
readonly Text: "text";
|
|
353
|
-
readonly JsonObject: "json_object";
|
|
354
353
|
readonly JsonSchema: "json_schema";
|
|
355
354
|
};
|
|
356
355
|
type ResponseFormatType = (typeof ResponseFormatTypes)[keyof typeof ResponseFormatTypes];
|
|
@@ -385,8 +384,8 @@ interface ChatCompletionCreateParams {
|
|
|
385
384
|
*/
|
|
386
385
|
toolChoice?: ToolChoice;
|
|
387
386
|
/**
|
|
388
|
-
* Structured outputs configuration. When set with type `
|
|
389
|
-
*
|
|
387
|
+
* Structured outputs configuration. When set with type `json_schema`,
|
|
388
|
+
* the backend validates and returns structured JSON.
|
|
390
389
|
*/
|
|
391
390
|
responseFormat?: ResponseFormat;
|
|
392
391
|
/**
|
|
@@ -418,8 +417,8 @@ interface CustomerChatParams {
|
|
|
418
417
|
*/
|
|
419
418
|
toolChoice?: ToolChoice;
|
|
420
419
|
/**
|
|
421
|
-
* Structured outputs configuration. When set with type `
|
|
422
|
-
*
|
|
420
|
+
* Structured outputs configuration. When set with type `json_schema`,
|
|
421
|
+
* the backend validates and returns structured JSON.
|
|
423
422
|
*/
|
|
424
423
|
responseFormat?: ResponseFormat;
|
|
425
424
|
/**
|
|
@@ -560,10 +559,29 @@ interface ChatCompletionEvent<T = unknown> {
|
|
|
560
559
|
raw: string;
|
|
561
560
|
}
|
|
562
561
|
type StructuredJSONRecordType = "start" | "update" | "completion" | "error";
|
|
562
|
+
/**
|
|
563
|
+
* Recursively makes all properties optional.
|
|
564
|
+
* Useful for typing partial payloads during progressive streaming before
|
|
565
|
+
* all fields are complete.
|
|
566
|
+
*
|
|
567
|
+
* @example
|
|
568
|
+
* interface Article { title: string; body: string; }
|
|
569
|
+
* type PartialArticle = DeepPartial<Article>;
|
|
570
|
+
* // { title?: string; body?: string; }
|
|
571
|
+
*/
|
|
572
|
+
type DeepPartial<T> = T extends object ? {
|
|
573
|
+
[P in keyof T]?: DeepPartial<T[P]>;
|
|
574
|
+
} : T;
|
|
563
575
|
interface StructuredJSONEvent<T> {
|
|
564
576
|
type: "update" | "completion";
|
|
565
577
|
payload: T;
|
|
566
578
|
requestId?: string;
|
|
579
|
+
/**
|
|
580
|
+
* Set of field paths that are complete (have their closing delimiter).
|
|
581
|
+
* Use dot notation for nested fields (e.g., "metadata.author").
|
|
582
|
+
* Check with completeFields.has("fieldName").
|
|
583
|
+
*/
|
|
584
|
+
completeFields: Set<string>;
|
|
567
585
|
}
|
|
568
586
|
interface APIFrontendToken {
|
|
569
587
|
token: string;
|
|
@@ -1884,4 +1902,4 @@ declare class ModelRelay {
|
|
|
1884
1902
|
constructor(options: ModelRelayOptions);
|
|
1885
1903
|
}
|
|
1886
1904
|
|
|
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 };
|
|
1905
|
+
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
|
@@ -350,7 +350,6 @@ interface ToolCall {
|
|
|
350
350
|
}
|
|
351
351
|
declare const ResponseFormatTypes: {
|
|
352
352
|
readonly Text: "text";
|
|
353
|
-
readonly JsonObject: "json_object";
|
|
354
353
|
readonly JsonSchema: "json_schema";
|
|
355
354
|
};
|
|
356
355
|
type ResponseFormatType = (typeof ResponseFormatTypes)[keyof typeof ResponseFormatTypes];
|
|
@@ -385,8 +384,8 @@ interface ChatCompletionCreateParams {
|
|
|
385
384
|
*/
|
|
386
385
|
toolChoice?: ToolChoice;
|
|
387
386
|
/**
|
|
388
|
-
* Structured outputs configuration. When set with type `
|
|
389
|
-
*
|
|
387
|
+
* Structured outputs configuration. When set with type `json_schema`,
|
|
388
|
+
* the backend validates and returns structured JSON.
|
|
390
389
|
*/
|
|
391
390
|
responseFormat?: ResponseFormat;
|
|
392
391
|
/**
|
|
@@ -418,8 +417,8 @@ interface CustomerChatParams {
|
|
|
418
417
|
*/
|
|
419
418
|
toolChoice?: ToolChoice;
|
|
420
419
|
/**
|
|
421
|
-
* Structured outputs configuration. When set with type `
|
|
422
|
-
*
|
|
420
|
+
* Structured outputs configuration. When set with type `json_schema`,
|
|
421
|
+
* the backend validates and returns structured JSON.
|
|
423
422
|
*/
|
|
424
423
|
responseFormat?: ResponseFormat;
|
|
425
424
|
/**
|
|
@@ -560,10 +559,29 @@ interface ChatCompletionEvent<T = unknown> {
|
|
|
560
559
|
raw: string;
|
|
561
560
|
}
|
|
562
561
|
type StructuredJSONRecordType = "start" | "update" | "completion" | "error";
|
|
562
|
+
/**
|
|
563
|
+
* Recursively makes all properties optional.
|
|
564
|
+
* Useful for typing partial payloads during progressive streaming before
|
|
565
|
+
* all fields are complete.
|
|
566
|
+
*
|
|
567
|
+
* @example
|
|
568
|
+
* interface Article { title: string; body: string; }
|
|
569
|
+
* type PartialArticle = DeepPartial<Article>;
|
|
570
|
+
* // { title?: string; body?: string; }
|
|
571
|
+
*/
|
|
572
|
+
type DeepPartial<T> = T extends object ? {
|
|
573
|
+
[P in keyof T]?: DeepPartial<T[P]>;
|
|
574
|
+
} : T;
|
|
563
575
|
interface StructuredJSONEvent<T> {
|
|
564
576
|
type: "update" | "completion";
|
|
565
577
|
payload: T;
|
|
566
578
|
requestId?: string;
|
|
579
|
+
/**
|
|
580
|
+
* Set of field paths that are complete (have their closing delimiter).
|
|
581
|
+
* Use dot notation for nested fields (e.g., "metadata.author").
|
|
582
|
+
* Check with completeFields.has("fieldName").
|
|
583
|
+
*/
|
|
584
|
+
completeFields: Set<string>;
|
|
567
585
|
}
|
|
568
586
|
interface APIFrontendToken {
|
|
569
587
|
token: string;
|
|
@@ -1884,4 +1902,4 @@ declare class ModelRelay {
|
|
|
1884
1902
|
constructor(options: ModelRelayOptions);
|
|
1885
1903
|
}
|
|
1886
1904
|
|
|
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 };
|
|
1905
|
+
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.
|
|
343
|
+
version: "0.30.0",
|
|
344
344
|
description: "TypeScript SDK for the ModelRelay API",
|
|
345
345
|
type: "module",
|
|
346
346
|
main: "dist/index.cjs",
|
|
@@ -430,7 +430,6 @@ var ToolChoiceTypes = {
|
|
|
430
430
|
};
|
|
431
431
|
var ResponseFormatTypes = {
|
|
432
432
|
Text: "text",
|
|
433
|
-
JsonObject: "json_object",
|
|
434
433
|
JsonSchema: "json_schema"
|
|
435
434
|
};
|
|
436
435
|
function mergeMetrics(base, override) {
|
|
@@ -1235,7 +1234,7 @@ var ChatCompletionsClient = class {
|
|
|
1235
1234
|
headers,
|
|
1236
1235
|
apiKey: authHeaders.apiKey,
|
|
1237
1236
|
accessToken: authHeaders.accessToken,
|
|
1238
|
-
accept: stream ? "
|
|
1237
|
+
accept: stream ? "application/x-ndjson" : "application/json",
|
|
1239
1238
|
raw: true,
|
|
1240
1239
|
signal: options.signal,
|
|
1241
1240
|
timeoutMs: options.timeoutMs ?? (stream ? 0 : void 0),
|
|
@@ -1288,9 +1287,9 @@ var ChatCompletionsClient = class {
|
|
|
1288
1287
|
if (!hasUserMessage(params.messages)) {
|
|
1289
1288
|
throw new ConfigError("at least one user message is required");
|
|
1290
1289
|
}
|
|
1291
|
-
if (!params.responseFormat || params.responseFormat.type !== "
|
|
1290
|
+
if (!params.responseFormat || params.responseFormat.type !== "json_schema") {
|
|
1292
1291
|
throw new ConfigError(
|
|
1293
|
-
"responseFormat with type=
|
|
1292
|
+
"responseFormat with type=json_schema is required for structured streaming"
|
|
1294
1293
|
);
|
|
1295
1294
|
}
|
|
1296
1295
|
const authHeaders = await this.auth.authForChat();
|
|
@@ -1547,7 +1546,7 @@ var CustomerChatClient = class {
|
|
|
1547
1546
|
headers,
|
|
1548
1547
|
apiKey: authHeaders.apiKey,
|
|
1549
1548
|
accessToken: authHeaders.accessToken,
|
|
1550
|
-
accept: stream ? "
|
|
1549
|
+
accept: stream ? "application/x-ndjson" : "application/json",
|
|
1551
1550
|
raw: true,
|
|
1552
1551
|
signal: options.signal,
|
|
1553
1552
|
timeoutMs: options.timeoutMs ?? (stream ? 0 : void 0),
|
|
@@ -1600,9 +1599,9 @@ var CustomerChatClient = class {
|
|
|
1600
1599
|
if (!hasUserMessage(params.messages)) {
|
|
1601
1600
|
throw new ConfigError("at least one user message is required");
|
|
1602
1601
|
}
|
|
1603
|
-
if (!params.responseFormat || params.responseFormat.type !== "
|
|
1602
|
+
if (!params.responseFormat || params.responseFormat.type !== "json_schema") {
|
|
1604
1603
|
throw new ConfigError(
|
|
1605
|
-
"responseFormat with type=
|
|
1604
|
+
"responseFormat with type=json_schema is required for structured streaming"
|
|
1606
1605
|
);
|
|
1607
1606
|
}
|
|
1608
1607
|
const authHeaders = await this.auth.authForChat(this.customerId);
|
|
@@ -1829,9 +1828,9 @@ var ChatCompletionsStream = class {
|
|
|
1829
1828
|
}
|
|
1830
1829
|
const { value, done } = await reader.read();
|
|
1831
1830
|
if (done) {
|
|
1832
|
-
const {
|
|
1833
|
-
for (const
|
|
1834
|
-
const parsed =
|
|
1831
|
+
const { records: records2 } = consumeNDJSONBuffer(buffer, true);
|
|
1832
|
+
for (const line of records2) {
|
|
1833
|
+
const parsed = mapNDJSONChatEvent(line, this.requestId);
|
|
1835
1834
|
if (parsed) {
|
|
1836
1835
|
this.handleStreamEvent(parsed);
|
|
1837
1836
|
yield parsed;
|
|
@@ -1840,10 +1839,10 @@ var ChatCompletionsStream = class {
|
|
|
1840
1839
|
return;
|
|
1841
1840
|
}
|
|
1842
1841
|
buffer += decoder.decode(value, { stream: true });
|
|
1843
|
-
const {
|
|
1842
|
+
const { records, remainder } = consumeNDJSONBuffer(buffer);
|
|
1844
1843
|
buffer = remainder;
|
|
1845
|
-
for (const
|
|
1846
|
-
const parsed =
|
|
1844
|
+
for (const line of records) {
|
|
1845
|
+
const parsed = mapNDJSONChatEvent(line, this.requestId);
|
|
1847
1846
|
if (parsed) {
|
|
1848
1847
|
this.handleStreamEvent(parsed);
|
|
1849
1848
|
yield parsed;
|
|
@@ -2026,11 +2025,13 @@ var StructuredJSONStream = class {
|
|
|
2026
2025
|
if (rawType === "completion") {
|
|
2027
2026
|
this.sawTerminal = true;
|
|
2028
2027
|
}
|
|
2028
|
+
const completeFieldsArray = Array.isArray(obj.complete_fields) ? obj.complete_fields.filter((f) => typeof f === "string") : [];
|
|
2029
2029
|
const event = {
|
|
2030
2030
|
type: rawType,
|
|
2031
2031
|
// biome-ignore lint/suspicious/noExplicitAny: payload is untyped json
|
|
2032
2032
|
payload: obj.payload,
|
|
2033
|
-
requestId: this.requestId
|
|
2033
|
+
requestId: this.requestId,
|
|
2034
|
+
completeFields: new Set(completeFieldsArray)
|
|
2034
2035
|
};
|
|
2035
2036
|
return event;
|
|
2036
2037
|
}
|
|
@@ -2053,48 +2054,6 @@ var StructuredJSONStream = class {
|
|
|
2053
2054
|
this.trace.streamEvent({ context: this.context, event });
|
|
2054
2055
|
}
|
|
2055
2056
|
};
|
|
2056
|
-
function consumeSSEBuffer(buffer, flush = false) {
|
|
2057
|
-
const events = [];
|
|
2058
|
-
let eventName = "";
|
|
2059
|
-
let dataLines = [];
|
|
2060
|
-
let remainder = "";
|
|
2061
|
-
const lines = buffer.split(/\r?\n/);
|
|
2062
|
-
const lastIndex = lines.length - 1;
|
|
2063
|
-
const limit = flush ? lines.length : Math.max(0, lastIndex);
|
|
2064
|
-
const pushEvent = () => {
|
|
2065
|
-
if (!eventName && dataLines.length === 0) {
|
|
2066
|
-
return;
|
|
2067
|
-
}
|
|
2068
|
-
events.push({
|
|
2069
|
-
event: eventName || "message",
|
|
2070
|
-
data: dataLines.join("\n")
|
|
2071
|
-
});
|
|
2072
|
-
eventName = "";
|
|
2073
|
-
dataLines = [];
|
|
2074
|
-
};
|
|
2075
|
-
for (let i = 0; i < limit; i++) {
|
|
2076
|
-
const line = lines[i];
|
|
2077
|
-
if (line === "") {
|
|
2078
|
-
pushEvent();
|
|
2079
|
-
continue;
|
|
2080
|
-
}
|
|
2081
|
-
if (line.startsWith(":")) {
|
|
2082
|
-
continue;
|
|
2083
|
-
}
|
|
2084
|
-
if (line.startsWith("event:")) {
|
|
2085
|
-
eventName = line.slice(6).trim();
|
|
2086
|
-
} else if (line.startsWith("data:")) {
|
|
2087
|
-
dataLines.push(line.slice(5).trimStart());
|
|
2088
|
-
}
|
|
2089
|
-
}
|
|
2090
|
-
if (flush) {
|
|
2091
|
-
pushEvent();
|
|
2092
|
-
remainder = "";
|
|
2093
|
-
} else {
|
|
2094
|
-
remainder = lines[lastIndex] ?? "";
|
|
2095
|
-
}
|
|
2096
|
-
return { events, remainder };
|
|
2097
|
-
}
|
|
2098
2057
|
function consumeNDJSONBuffer(buffer, flush = false) {
|
|
2099
2058
|
const lines = buffer.split(/\r?\n/);
|
|
2100
2059
|
const records = [];
|
|
@@ -2108,29 +2067,79 @@ function consumeNDJSONBuffer(buffer, flush = false) {
|
|
|
2108
2067
|
const remainder = flush ? "" : lines[lastIndex] ?? "";
|
|
2109
2068
|
return { records, remainder };
|
|
2110
2069
|
}
|
|
2111
|
-
function
|
|
2112
|
-
let parsed
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2070
|
+
function mapNDJSONChatEvent(line, requestId) {
|
|
2071
|
+
let parsed;
|
|
2072
|
+
try {
|
|
2073
|
+
parsed = JSON.parse(line);
|
|
2074
|
+
} catch (err) {
|
|
2075
|
+
console.warn(
|
|
2076
|
+
`[ModelRelay SDK] Failed to parse NDJSON line: ${err instanceof Error ? err.message : String(err)}`,
|
|
2077
|
+
{ line: line.substring(0, 200), requestId }
|
|
2078
|
+
);
|
|
2079
|
+
return null;
|
|
2080
|
+
}
|
|
2081
|
+
if (!parsed || typeof parsed !== "object") {
|
|
2082
|
+
console.warn("[ModelRelay SDK] NDJSON record is not an object", {
|
|
2083
|
+
parsed,
|
|
2084
|
+
requestId
|
|
2085
|
+
});
|
|
2086
|
+
return null;
|
|
2087
|
+
}
|
|
2088
|
+
const obj = parsed;
|
|
2089
|
+
const recordType = String(obj.type || "").trim().toLowerCase();
|
|
2090
|
+
if (recordType === "keepalive") {
|
|
2091
|
+
return null;
|
|
2092
|
+
}
|
|
2093
|
+
if (!recordType) {
|
|
2094
|
+
console.warn("[ModelRelay SDK] NDJSON record missing 'type' field", {
|
|
2095
|
+
obj,
|
|
2096
|
+
requestId
|
|
2097
|
+
});
|
|
2098
|
+
return null;
|
|
2099
|
+
}
|
|
2100
|
+
let type;
|
|
2101
|
+
switch (recordType) {
|
|
2102
|
+
case "start":
|
|
2103
|
+
type = "message_start";
|
|
2104
|
+
break;
|
|
2105
|
+
case "update":
|
|
2106
|
+
type = "message_delta";
|
|
2107
|
+
break;
|
|
2108
|
+
case "completion":
|
|
2109
|
+
type = "message_stop";
|
|
2110
|
+
break;
|
|
2111
|
+
case "error":
|
|
2112
|
+
type = "custom";
|
|
2113
|
+
break;
|
|
2114
|
+
// Tool use event types
|
|
2115
|
+
case "tool_use_start":
|
|
2116
|
+
type = "tool_use_start";
|
|
2117
|
+
break;
|
|
2118
|
+
case "tool_use_delta":
|
|
2119
|
+
type = "tool_use_delta";
|
|
2120
|
+
break;
|
|
2121
|
+
case "tool_use_stop":
|
|
2122
|
+
type = "tool_use_stop";
|
|
2123
|
+
break;
|
|
2124
|
+
default:
|
|
2125
|
+
type = "custom";
|
|
2126
|
+
}
|
|
2127
|
+
const usage = normalizeUsage(obj.usage);
|
|
2128
|
+
const responseId = obj.request_id;
|
|
2129
|
+
const model = normalizeModelId(obj.model);
|
|
2130
|
+
const stopReason = normalizeStopReason(obj.stop_reason);
|
|
2131
|
+
let textDelta;
|
|
2132
|
+
if (obj.payload && typeof obj.payload === "object") {
|
|
2133
|
+
if (typeof obj.payload.content === "string") {
|
|
2134
|
+
textDelta = obj.payload.content;
|
|
2118
2135
|
}
|
|
2119
2136
|
}
|
|
2120
|
-
const
|
|
2121
|
-
const
|
|
2122
|
-
const type = normalizeEventType(raw.event, p);
|
|
2123
|
-
const usage = normalizeUsage(p.usage);
|
|
2124
|
-
const responseId = p.response_id || p.id || p?.message?.id;
|
|
2125
|
-
const model = normalizeModelId(p.model || p?.message?.model);
|
|
2126
|
-
const stopReason = normalizeStopReason(p.stop_reason);
|
|
2127
|
-
const textDelta = extractTextDelta(p);
|
|
2128
|
-
const toolCallDelta = extractToolCallDelta(p, type);
|
|
2129
|
-
const toolCalls = extractToolCalls(p, type);
|
|
2137
|
+
const toolCallDelta = extractToolCallDelta(obj, type);
|
|
2138
|
+
const toolCalls = extractToolCalls(obj, type);
|
|
2130
2139
|
return {
|
|
2131
2140
|
type,
|
|
2132
|
-
event:
|
|
2133
|
-
data:
|
|
2141
|
+
event: recordType,
|
|
2142
|
+
data: obj,
|
|
2134
2143
|
textDelta,
|
|
2135
2144
|
toolCallDelta,
|
|
2136
2145
|
toolCalls,
|
|
@@ -2139,52 +2148,9 @@ function mapChatEvent(raw, requestId) {
|
|
|
2139
2148
|
stopReason,
|
|
2140
2149
|
usage,
|
|
2141
2150
|
requestId,
|
|
2142
|
-
raw:
|
|
2151
|
+
raw: line
|
|
2143
2152
|
};
|
|
2144
2153
|
}
|
|
2145
|
-
function normalizeEventType(eventName, payload) {
|
|
2146
|
-
const hint = String(
|
|
2147
|
-
payload?.type || payload?.event || eventName || ""
|
|
2148
|
-
).trim();
|
|
2149
|
-
switch (hint) {
|
|
2150
|
-
case "message_start":
|
|
2151
|
-
return "message_start";
|
|
2152
|
-
case "message_delta":
|
|
2153
|
-
return "message_delta";
|
|
2154
|
-
case "message_stop":
|
|
2155
|
-
return "message_stop";
|
|
2156
|
-
case "tool_use_start":
|
|
2157
|
-
return "tool_use_start";
|
|
2158
|
-
case "tool_use_delta":
|
|
2159
|
-
return "tool_use_delta";
|
|
2160
|
-
case "tool_use_stop":
|
|
2161
|
-
return "tool_use_stop";
|
|
2162
|
-
case "ping":
|
|
2163
|
-
return "ping";
|
|
2164
|
-
default:
|
|
2165
|
-
return "custom";
|
|
2166
|
-
}
|
|
2167
|
-
}
|
|
2168
|
-
function extractTextDelta(payload) {
|
|
2169
|
-
if (!payload || typeof payload !== "object") {
|
|
2170
|
-
return void 0;
|
|
2171
|
-
}
|
|
2172
|
-
if (typeof payload.text_delta === "string" && payload.text_delta !== "") {
|
|
2173
|
-
return payload.text_delta;
|
|
2174
|
-
}
|
|
2175
|
-
if (typeof payload.delta === "string") {
|
|
2176
|
-
return payload.delta;
|
|
2177
|
-
}
|
|
2178
|
-
if (payload.delta && typeof payload.delta === "object") {
|
|
2179
|
-
if (typeof payload.delta.text === "string") {
|
|
2180
|
-
return payload.delta.text;
|
|
2181
|
-
}
|
|
2182
|
-
if (typeof payload.delta.content === "string") {
|
|
2183
|
-
return payload.delta.content;
|
|
2184
|
-
}
|
|
2185
|
-
}
|
|
2186
|
-
return void 0;
|
|
2187
|
-
}
|
|
2188
2154
|
function extractToolCallDelta(payload, type) {
|
|
2189
2155
|
if (!payload || typeof payload !== "object") {
|
|
2190
2156
|
return void 0;
|