@modelrelay/sdk 0.14.1 → 0.17.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 +79 -3
- package/dist/index.cjs +291 -72
- package/dist/index.d.cts +82 -43
- package/dist/index.d.ts +82 -43
- package/dist/index.js +288 -68
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -82,11 +82,87 @@ const stream = await mr.chat.completions.create(
|
|
|
82
82
|
);
|
|
83
83
|
```
|
|
84
84
|
|
|
85
|
-
### Typed models
|
|
85
|
+
### Typed models and stop reasons
|
|
86
86
|
|
|
87
|
-
- Models
|
|
87
|
+
- Models are plain strings (e.g., `"gpt-4o"`), so new models do not require SDK updates.
|
|
88
88
|
- Stop reasons are parsed into the `StopReason` union (e.g., `StopReasons.EndTurn`); unknown values surface as `{ other: "<raw>" }`.
|
|
89
|
-
- Usage backfills `totalTokens` when
|
|
89
|
+
- Usage backfills `totalTokens` when the backend omits it, ensuring consistent accounting.
|
|
90
|
+
|
|
91
|
+
### Structured outputs (`response_format`)
|
|
92
|
+
|
|
93
|
+
Request structured JSON instead of free-form text when the backend supports it:
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
import { ModelRelay, type ResponseFormat } from "@modelrelay/sdk";
|
|
97
|
+
|
|
98
|
+
const mr = new ModelRelay({ key: "mr_sk_..." });
|
|
99
|
+
|
|
100
|
+
const format: ResponseFormat = {
|
|
101
|
+
type: "json_schema",
|
|
102
|
+
json_schema: {
|
|
103
|
+
name: "summary",
|
|
104
|
+
schema: {
|
|
105
|
+
type: "object",
|
|
106
|
+
properties: { headline: { type: "string" } },
|
|
107
|
+
additionalProperties: false,
|
|
108
|
+
},
|
|
109
|
+
strict: true,
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const completion = await mr.chat.completions.create(
|
|
114
|
+
{
|
|
115
|
+
model: "gpt-4o-mini",
|
|
116
|
+
messages: [{ role: "user", content: "Summarize ModelRelay" }],
|
|
117
|
+
responseFormat: format,
|
|
118
|
+
stream: false,
|
|
119
|
+
},
|
|
120
|
+
{ stream: false },
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
console.log(completion.content[0]); // JSON string matching your schema
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Structured streaming (NDJSON + response_format)
|
|
127
|
+
|
|
128
|
+
Use the structured streaming contract for `/llm/proxy` to stream schema-valid
|
|
129
|
+
JSON payloads over NDJSON:
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
type Item = { id: string; label: string };
|
|
133
|
+
type RecommendationPayload = { items: Item[] };
|
|
134
|
+
|
|
135
|
+
const format: ResponseFormat = {
|
|
136
|
+
type: "json_schema",
|
|
137
|
+
json_schema: {
|
|
138
|
+
name: "recommendations",
|
|
139
|
+
schema: {
|
|
140
|
+
type: "object",
|
|
141
|
+
properties: { items: { type: "array", items: { type: "object" } } },
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
const stream = await mr.chat.completions.streamJSON<RecommendationPayload>({
|
|
147
|
+
model: "grok-4-1-fast",
|
|
148
|
+
messages: [{ role: "user", content: "Recommend items for my user" }],
|
|
149
|
+
responseFormat: format,
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
for await (const evt of stream) {
|
|
153
|
+
if (evt.type === "update") {
|
|
154
|
+
// Progressive UI: evt.payload is a partial but schema-valid payload.
|
|
155
|
+
renderPartial(evt.payload.items);
|
|
156
|
+
}
|
|
157
|
+
if (evt.type === "completion") {
|
|
158
|
+
renderFinal(evt.payload.items);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Prefer a single blocking result but still want structured validation?
|
|
163
|
+
const final = await stream.collect();
|
|
164
|
+
console.log(final.items.length);
|
|
165
|
+
```
|
|
90
166
|
|
|
91
167
|
### Telemetry & metrics hooks
|
|
92
168
|
|
package/dist/index.cjs
CHANGED
|
@@ -33,10 +33,10 @@ __export(index_exports, {
|
|
|
33
33
|
ErrorCodes: () => ErrorCodes,
|
|
34
34
|
ModelRelay: () => ModelRelay,
|
|
35
35
|
ModelRelayError: () => ModelRelayError,
|
|
36
|
-
|
|
37
|
-
Providers: () => Providers,
|
|
36
|
+
ResponseFormatTypes: () => ResponseFormatTypes,
|
|
38
37
|
SDK_VERSION: () => SDK_VERSION,
|
|
39
38
|
StopReasons: () => StopReasons,
|
|
39
|
+
StructuredJSONStream: () => StructuredJSONStream,
|
|
40
40
|
TiersClient: () => TiersClient,
|
|
41
41
|
ToolArgsError: () => ToolArgsError,
|
|
42
42
|
ToolCallAccumulator: () => ToolCallAccumulator,
|
|
@@ -44,6 +44,7 @@ __export(index_exports, {
|
|
|
44
44
|
ToolRegistry: () => ToolRegistry,
|
|
45
45
|
ToolTypes: () => ToolTypes,
|
|
46
46
|
TransportError: () => TransportError,
|
|
47
|
+
WebToolModes: () => WebToolModes,
|
|
47
48
|
assistantMessageWithToolCalls: () => assistantMessageWithToolCalls,
|
|
48
49
|
createAccessTokenAuth: () => createAccessTokenAuth,
|
|
49
50
|
createApiKeyAuth: () => createApiKeyAuth,
|
|
@@ -68,12 +69,10 @@ __export(index_exports, {
|
|
|
68
69
|
mergeTrace: () => mergeTrace,
|
|
69
70
|
modelToString: () => modelToString,
|
|
70
71
|
normalizeModelId: () => normalizeModelId,
|
|
71
|
-
normalizeProvider: () => normalizeProvider,
|
|
72
72
|
normalizeStopReason: () => normalizeStopReason,
|
|
73
73
|
parseErrorResponse: () => parseErrorResponse,
|
|
74
74
|
parseToolArgs: () => parseToolArgs,
|
|
75
75
|
parseToolArgsRaw: () => parseToolArgsRaw,
|
|
76
|
-
providerToString: () => providerToString,
|
|
77
76
|
respondToToolCall: () => respondToToolCall,
|
|
78
77
|
stopReasonToString: () => stopReasonToString,
|
|
79
78
|
toolChoiceAuto: () => toolChoiceAuto,
|
|
@@ -348,7 +347,7 @@ function isTokenReusable(token) {
|
|
|
348
347
|
// package.json
|
|
349
348
|
var package_default = {
|
|
350
349
|
name: "@modelrelay/sdk",
|
|
351
|
-
version: "0.
|
|
350
|
+
version: "0.17.0",
|
|
352
351
|
description: "TypeScript SDK for the ModelRelay API",
|
|
353
352
|
type: "module",
|
|
354
353
|
main: "dist/index.cjs",
|
|
@@ -407,30 +406,6 @@ var StopReasons = {
|
|
|
407
406
|
Incomplete: "incomplete",
|
|
408
407
|
Unknown: "unknown"
|
|
409
408
|
};
|
|
410
|
-
var Providers = {
|
|
411
|
-
OpenAI: "openai",
|
|
412
|
-
Anthropic: "anthropic",
|
|
413
|
-
XAI: "xai",
|
|
414
|
-
GoogleAIStudio: "google-ai-studio",
|
|
415
|
-
Echo: "echo"
|
|
416
|
-
};
|
|
417
|
-
var Models = {
|
|
418
|
-
// OpenAI models (provider-agnostic identifiers)
|
|
419
|
-
Gpt4o: "gpt-4o",
|
|
420
|
-
Gpt4oMini: "gpt-4o-mini",
|
|
421
|
-
Gpt51: "gpt-5.1",
|
|
422
|
-
// Anthropic models (provider-agnostic identifiers)
|
|
423
|
-
Claude35HaikuLatest: "claude-3-5-haiku-latest",
|
|
424
|
-
Claude35SonnetLatest: "claude-3-5-sonnet-latest",
|
|
425
|
-
ClaudeOpus45: "claude-opus-4-5",
|
|
426
|
-
Claude35Haiku: "claude-3.5-haiku",
|
|
427
|
-
// xAI / Grok models
|
|
428
|
-
Grok2: "grok-2",
|
|
429
|
-
Grok4_1FastNonReasoning: "grok-4-1-fast-non-reasoning",
|
|
430
|
-
Grok4_1FastReasoning: "grok-4-1-fast-reasoning",
|
|
431
|
-
// Internal echo model for testing.
|
|
432
|
-
Echo1: "echo-1"
|
|
433
|
-
};
|
|
434
409
|
function createUsage(inputTokens, outputTokens, totalTokens) {
|
|
435
410
|
return {
|
|
436
411
|
inputTokens,
|
|
@@ -441,14 +416,24 @@ function createUsage(inputTokens, outputTokens, totalTokens) {
|
|
|
441
416
|
var ToolTypes = {
|
|
442
417
|
Function: "function",
|
|
443
418
|
Web: "web",
|
|
419
|
+
WebSearch: "web_search",
|
|
444
420
|
XSearch: "x_search",
|
|
445
421
|
CodeExecution: "code_execution"
|
|
446
422
|
};
|
|
423
|
+
var WebToolModes = {
|
|
424
|
+
Search: "search",
|
|
425
|
+
Browse: "browse"
|
|
426
|
+
};
|
|
447
427
|
var ToolChoiceTypes = {
|
|
448
428
|
Auto: "auto",
|
|
449
429
|
Required: "required",
|
|
450
430
|
None: "none"
|
|
451
431
|
};
|
|
432
|
+
var ResponseFormatTypes = {
|
|
433
|
+
Text: "text",
|
|
434
|
+
JsonObject: "json_object",
|
|
435
|
+
JsonSchema: "json_schema"
|
|
436
|
+
};
|
|
452
437
|
function mergeMetrics(base, override) {
|
|
453
438
|
if (!base && !override) return void 0;
|
|
454
439
|
return {
|
|
@@ -482,34 +467,14 @@ function stopReasonToString(value) {
|
|
|
482
467
|
if (typeof value === "string") return value;
|
|
483
468
|
return value.other?.trim() || void 0;
|
|
484
469
|
}
|
|
485
|
-
function normalizeProvider(value) {
|
|
486
|
-
if (value === void 0 || value === null) return void 0;
|
|
487
|
-
const str = String(value).trim();
|
|
488
|
-
if (!str) return void 0;
|
|
489
|
-
const lower = str.toLowerCase();
|
|
490
|
-
for (const p of Object.values(Providers)) {
|
|
491
|
-
if (lower === p) return p;
|
|
492
|
-
}
|
|
493
|
-
return { other: str };
|
|
494
|
-
}
|
|
495
|
-
function providerToString(value) {
|
|
496
|
-
if (!value) return void 0;
|
|
497
|
-
if (typeof value === "string") return value;
|
|
498
|
-
return value.other?.trim() || void 0;
|
|
499
|
-
}
|
|
500
470
|
function normalizeModelId(value) {
|
|
501
471
|
if (value === void 0 || value === null) return void 0;
|
|
502
472
|
const str = String(value).trim();
|
|
503
473
|
if (!str) return void 0;
|
|
504
|
-
|
|
505
|
-
for (const m of Object.values(Models)) {
|
|
506
|
-
if (lower === m) return m;
|
|
507
|
-
}
|
|
508
|
-
return { other: str };
|
|
474
|
+
return str;
|
|
509
475
|
}
|
|
510
476
|
function modelToString(value) {
|
|
511
|
-
|
|
512
|
-
return value.other?.trim() || "";
|
|
477
|
+
return String(value).trim();
|
|
513
478
|
}
|
|
514
479
|
|
|
515
480
|
// src/tools.ts
|
|
@@ -1167,7 +1132,6 @@ var ChatCompletionsClient = class {
|
|
|
1167
1132
|
if (!hasUserMessage(params.messages)) {
|
|
1168
1133
|
throw new ConfigError("at least one user message is required");
|
|
1169
1134
|
}
|
|
1170
|
-
validateRequestModel(params.model);
|
|
1171
1135
|
const authHeaders = await this.auth.authForChat(params.customerId);
|
|
1172
1136
|
const body = buildProxyBody(
|
|
1173
1137
|
params,
|
|
@@ -1181,7 +1145,6 @@ var ChatCompletionsClient = class {
|
|
|
1181
1145
|
const baseContext = {
|
|
1182
1146
|
method: "POST",
|
|
1183
1147
|
path: "/llm/proxy",
|
|
1184
|
-
provider: params.provider,
|
|
1185
1148
|
model: params.model,
|
|
1186
1149
|
requestId
|
|
1187
1150
|
};
|
|
@@ -1231,6 +1194,80 @@ var ChatCompletionsClient = class {
|
|
|
1231
1194
|
trace
|
|
1232
1195
|
);
|
|
1233
1196
|
}
|
|
1197
|
+
/**
|
|
1198
|
+
* Stream structured JSON responses using the NDJSON contract defined for
|
|
1199
|
+
* /llm/proxy. The request must include a structured responseFormat.
|
|
1200
|
+
*/
|
|
1201
|
+
async streamJSON(params, options = {}) {
|
|
1202
|
+
const metrics = mergeMetrics(this.metrics, options.metrics);
|
|
1203
|
+
const trace = mergeTrace(this.trace, options.trace);
|
|
1204
|
+
if (!params?.messages?.length) {
|
|
1205
|
+
throw new ConfigError("at least one message is required");
|
|
1206
|
+
}
|
|
1207
|
+
if (!hasUserMessage(params.messages)) {
|
|
1208
|
+
throw new ConfigError("at least one user message is required");
|
|
1209
|
+
}
|
|
1210
|
+
if (!params.responseFormat || params.responseFormat.type !== "json_object" && params.responseFormat.type !== "json_schema") {
|
|
1211
|
+
throw new ConfigError(
|
|
1212
|
+
"responseFormat with type=json_object or json_schema is required for structured streaming"
|
|
1213
|
+
);
|
|
1214
|
+
}
|
|
1215
|
+
const authHeaders = await this.auth.authForChat(params.customerId);
|
|
1216
|
+
const body = buildProxyBody(
|
|
1217
|
+
params,
|
|
1218
|
+
mergeMetadata(this.defaultMetadata, params.metadata, options.metadata)
|
|
1219
|
+
);
|
|
1220
|
+
const requestId = params.requestId || options.requestId;
|
|
1221
|
+
const headers = { ...options.headers || {} };
|
|
1222
|
+
if (requestId) {
|
|
1223
|
+
headers[REQUEST_ID_HEADER] = requestId;
|
|
1224
|
+
}
|
|
1225
|
+
const baseContext = {
|
|
1226
|
+
method: "POST",
|
|
1227
|
+
path: "/llm/proxy",
|
|
1228
|
+
model: params.model,
|
|
1229
|
+
requestId
|
|
1230
|
+
};
|
|
1231
|
+
const response = await this.http.request("/llm/proxy", {
|
|
1232
|
+
method: "POST",
|
|
1233
|
+
body,
|
|
1234
|
+
headers,
|
|
1235
|
+
apiKey: authHeaders.apiKey,
|
|
1236
|
+
accessToken: authHeaders.accessToken,
|
|
1237
|
+
accept: "application/x-ndjson",
|
|
1238
|
+
raw: true,
|
|
1239
|
+
signal: options.signal,
|
|
1240
|
+
timeoutMs: options.timeoutMs ?? 0,
|
|
1241
|
+
useDefaultTimeout: false,
|
|
1242
|
+
connectTimeoutMs: options.connectTimeoutMs,
|
|
1243
|
+
retry: options.retry,
|
|
1244
|
+
metrics,
|
|
1245
|
+
trace,
|
|
1246
|
+
context: baseContext
|
|
1247
|
+
});
|
|
1248
|
+
const resolvedRequestId = requestIdFromHeaders(response.headers) || requestId || void 0;
|
|
1249
|
+
if (!response.ok) {
|
|
1250
|
+
throw await parseErrorResponse(response);
|
|
1251
|
+
}
|
|
1252
|
+
const contentType = response.headers.get("Content-Type") || "";
|
|
1253
|
+
if (!/application\/(x-)?ndjson/i.test(contentType)) {
|
|
1254
|
+
throw new TransportError(
|
|
1255
|
+
`expected NDJSON structured stream, got Content-Type ${contentType || "missing"}`,
|
|
1256
|
+
{ kind: "request" }
|
|
1257
|
+
);
|
|
1258
|
+
}
|
|
1259
|
+
const streamContext = {
|
|
1260
|
+
...baseContext,
|
|
1261
|
+
requestId: resolvedRequestId ?? baseContext.requestId
|
|
1262
|
+
};
|
|
1263
|
+
return new StructuredJSONStream(
|
|
1264
|
+
response,
|
|
1265
|
+
resolvedRequestId,
|
|
1266
|
+
streamContext,
|
|
1267
|
+
metrics,
|
|
1268
|
+
trace
|
|
1269
|
+
);
|
|
1270
|
+
}
|
|
1234
1271
|
};
|
|
1235
1272
|
var ChatCompletionsStream = class {
|
|
1236
1273
|
constructor(response, requestId, context, metrics, trace) {
|
|
@@ -1332,6 +1369,164 @@ var ChatCompletionsStream = class {
|
|
|
1332
1369
|
});
|
|
1333
1370
|
}
|
|
1334
1371
|
};
|
|
1372
|
+
var StructuredJSONStream = class {
|
|
1373
|
+
constructor(response, requestId, context, metrics, trace) {
|
|
1374
|
+
this.closed = false;
|
|
1375
|
+
this.sawTerminal = false;
|
|
1376
|
+
if (!response.body) {
|
|
1377
|
+
throw new ConfigError("streaming response is missing a body");
|
|
1378
|
+
}
|
|
1379
|
+
this.response = response;
|
|
1380
|
+
this.requestId = requestId;
|
|
1381
|
+
this.context = context;
|
|
1382
|
+
this.metrics = metrics;
|
|
1383
|
+
this.trace = trace;
|
|
1384
|
+
}
|
|
1385
|
+
async cancel(reason) {
|
|
1386
|
+
this.closed = true;
|
|
1387
|
+
try {
|
|
1388
|
+
await this.response.body?.cancel(reason);
|
|
1389
|
+
} catch {
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
async *[Symbol.asyncIterator]() {
|
|
1393
|
+
if (this.closed) {
|
|
1394
|
+
return;
|
|
1395
|
+
}
|
|
1396
|
+
const body = this.response.body;
|
|
1397
|
+
if (!body) {
|
|
1398
|
+
throw new ConfigError("streaming response is missing a body");
|
|
1399
|
+
}
|
|
1400
|
+
const reader = body.getReader();
|
|
1401
|
+
const decoder = new TextDecoder();
|
|
1402
|
+
let buffer = "";
|
|
1403
|
+
try {
|
|
1404
|
+
while (true) {
|
|
1405
|
+
if (this.closed) {
|
|
1406
|
+
await reader.cancel();
|
|
1407
|
+
return;
|
|
1408
|
+
}
|
|
1409
|
+
const { value, done } = await reader.read();
|
|
1410
|
+
if (done) {
|
|
1411
|
+
const { records: records2 } = consumeNDJSONBuffer(buffer, true);
|
|
1412
|
+
for (const line of records2) {
|
|
1413
|
+
const evt = this.parseRecord(line);
|
|
1414
|
+
if (evt) {
|
|
1415
|
+
this.traceStructuredEvent(evt, line);
|
|
1416
|
+
yield evt;
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
if (!this.sawTerminal) {
|
|
1420
|
+
throw new TransportError(
|
|
1421
|
+
"structured stream ended without completion or error",
|
|
1422
|
+
{ kind: "request" }
|
|
1423
|
+
);
|
|
1424
|
+
}
|
|
1425
|
+
return;
|
|
1426
|
+
}
|
|
1427
|
+
buffer += decoder.decode(value, { stream: true });
|
|
1428
|
+
const { records, remainder } = consumeNDJSONBuffer(buffer);
|
|
1429
|
+
buffer = remainder;
|
|
1430
|
+
for (const line of records) {
|
|
1431
|
+
const evt = this.parseRecord(line);
|
|
1432
|
+
if (evt) {
|
|
1433
|
+
this.traceStructuredEvent(evt, line);
|
|
1434
|
+
yield evt;
|
|
1435
|
+
}
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
} catch (err) {
|
|
1439
|
+
this.trace?.streamError?.({ context: this.context, error: err });
|
|
1440
|
+
throw err;
|
|
1441
|
+
} finally {
|
|
1442
|
+
this.closed = true;
|
|
1443
|
+
reader.releaseLock();
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1446
|
+
async collect() {
|
|
1447
|
+
let last;
|
|
1448
|
+
for await (const evt of this) {
|
|
1449
|
+
last = evt;
|
|
1450
|
+
if (evt.type === "completion") {
|
|
1451
|
+
return evt.payload;
|
|
1452
|
+
}
|
|
1453
|
+
}
|
|
1454
|
+
throw new TransportError(
|
|
1455
|
+
"structured stream ended without completion or error",
|
|
1456
|
+
{ kind: "request" }
|
|
1457
|
+
);
|
|
1458
|
+
}
|
|
1459
|
+
parseRecord(line) {
|
|
1460
|
+
let parsed;
|
|
1461
|
+
try {
|
|
1462
|
+
parsed = JSON.parse(line);
|
|
1463
|
+
} catch (err) {
|
|
1464
|
+
throw new TransportError("invalid JSON in structured stream", {
|
|
1465
|
+
kind: "request",
|
|
1466
|
+
cause: err
|
|
1467
|
+
});
|
|
1468
|
+
}
|
|
1469
|
+
if (!parsed || typeof parsed !== "object") {
|
|
1470
|
+
throw new TransportError("structured stream record is not an object", {
|
|
1471
|
+
kind: "request"
|
|
1472
|
+
});
|
|
1473
|
+
}
|
|
1474
|
+
const obj = parsed;
|
|
1475
|
+
const rawType = String(obj.type || "").trim().toLowerCase();
|
|
1476
|
+
if (!rawType) return null;
|
|
1477
|
+
if (rawType === "start") {
|
|
1478
|
+
return null;
|
|
1479
|
+
}
|
|
1480
|
+
if (rawType === "error") {
|
|
1481
|
+
this.sawTerminal = true;
|
|
1482
|
+
const status = typeof obj.status === "number" && obj.status > 0 ? obj.status : 500;
|
|
1483
|
+
const message = typeof obj.message === "string" && obj.message.trim() ? obj.message : "structured stream error";
|
|
1484
|
+
const code = typeof obj.code === "string" && obj.code.trim() ? obj.code : void 0;
|
|
1485
|
+
throw new APIError(message, {
|
|
1486
|
+
status,
|
|
1487
|
+
code,
|
|
1488
|
+
requestId: this.requestId
|
|
1489
|
+
});
|
|
1490
|
+
}
|
|
1491
|
+
if (rawType !== "update" && rawType !== "completion") {
|
|
1492
|
+
return null;
|
|
1493
|
+
}
|
|
1494
|
+
if (obj.payload === void 0 || obj.payload === null) {
|
|
1495
|
+
throw new TransportError(
|
|
1496
|
+
"structured stream record missing payload",
|
|
1497
|
+
{ kind: "request" }
|
|
1498
|
+
);
|
|
1499
|
+
}
|
|
1500
|
+
if (rawType === "completion") {
|
|
1501
|
+
this.sawTerminal = true;
|
|
1502
|
+
}
|
|
1503
|
+
const event = {
|
|
1504
|
+
type: rawType,
|
|
1505
|
+
// biome-ignore lint/suspicious/noExplicitAny: payload is untyped json
|
|
1506
|
+
payload: obj.payload,
|
|
1507
|
+
requestId: this.requestId
|
|
1508
|
+
};
|
|
1509
|
+
return event;
|
|
1510
|
+
}
|
|
1511
|
+
traceStructuredEvent(evt, raw) {
|
|
1512
|
+
if (!this.trace?.streamEvent) return;
|
|
1513
|
+
const event = {
|
|
1514
|
+
type: "custom",
|
|
1515
|
+
event: "structured",
|
|
1516
|
+
data: { type: evt.type, payload: evt.payload },
|
|
1517
|
+
textDelta: void 0,
|
|
1518
|
+
toolCallDelta: void 0,
|
|
1519
|
+
toolCalls: void 0,
|
|
1520
|
+
responseId: void 0,
|
|
1521
|
+
model: void 0,
|
|
1522
|
+
stopReason: void 0,
|
|
1523
|
+
usage: void 0,
|
|
1524
|
+
requestId: this.requestId,
|
|
1525
|
+
raw
|
|
1526
|
+
};
|
|
1527
|
+
this.trace.streamEvent({ context: this.context, event });
|
|
1528
|
+
}
|
|
1529
|
+
};
|
|
1335
1530
|
function consumeSSEBuffer(buffer, flush = false) {
|
|
1336
1531
|
const events = [];
|
|
1337
1532
|
let eventName = "";
|
|
@@ -1374,6 +1569,19 @@ function consumeSSEBuffer(buffer, flush = false) {
|
|
|
1374
1569
|
}
|
|
1375
1570
|
return { events, remainder };
|
|
1376
1571
|
}
|
|
1572
|
+
function consumeNDJSONBuffer(buffer, flush = false) {
|
|
1573
|
+
const lines = buffer.split(/\r?\n/);
|
|
1574
|
+
const records = [];
|
|
1575
|
+
const lastIndex = lines.length - 1;
|
|
1576
|
+
const limit = flush ? lines.length : Math.max(0, lastIndex);
|
|
1577
|
+
for (let i = 0; i < limit; i++) {
|
|
1578
|
+
const line = lines[i]?.trim();
|
|
1579
|
+
if (!line) continue;
|
|
1580
|
+
records.push(line);
|
|
1581
|
+
}
|
|
1582
|
+
const remainder = flush ? "" : lines[lastIndex] ?? "";
|
|
1583
|
+
return { records, remainder };
|
|
1584
|
+
}
|
|
1377
1585
|
function mapChatEvent(raw, requestId) {
|
|
1378
1586
|
let parsed = raw.data;
|
|
1379
1587
|
if (raw.data) {
|
|
@@ -1502,7 +1710,6 @@ function normalizeChatResponse(payload, requestId) {
|
|
|
1502
1710
|
const p = payload;
|
|
1503
1711
|
const response = {
|
|
1504
1712
|
id: p?.id,
|
|
1505
|
-
provider: normalizeProvider(p?.provider),
|
|
1506
1713
|
content: Array.isArray(p?.content) ? p.content : p?.content ? [String(p.content)] : [],
|
|
1507
1714
|
stopReason: normalizeStopReason(p?.stop_reason),
|
|
1508
1715
|
model: normalizeModelId(p?.model),
|
|
@@ -1533,19 +1740,6 @@ function normalizeUsage(payload) {
|
|
|
1533
1740
|
const totalTokens = Number(payload.total_tokens ?? 0);
|
|
1534
1741
|
return createUsage(inputTokens, outputTokens, totalTokens || void 0);
|
|
1535
1742
|
}
|
|
1536
|
-
function validateRequestModel(model) {
|
|
1537
|
-
if (model === void 0 || model === null) return;
|
|
1538
|
-
const value = modelToString(model).trim();
|
|
1539
|
-
if (!value) {
|
|
1540
|
-
throw new ConfigError("model id must be a non-empty string when provided");
|
|
1541
|
-
}
|
|
1542
|
-
const knownModels = Object.values(Models);
|
|
1543
|
-
if (!knownModels.includes(value)) {
|
|
1544
|
-
throw new ConfigError(
|
|
1545
|
-
`unsupported model id "${value}". Use one of the SDK Models.* constants or omit model to use the tier's default model.`
|
|
1546
|
-
);
|
|
1547
|
-
}
|
|
1548
|
-
}
|
|
1549
1743
|
function buildProxyBody(params, metadata) {
|
|
1550
1744
|
const modelValue = params.model ? modelToString(params.model).trim() : "";
|
|
1551
1745
|
const body = {
|
|
@@ -1555,7 +1749,6 @@ function buildProxyBody(params, metadata) {
|
|
|
1555
1749
|
body.model = modelValue;
|
|
1556
1750
|
}
|
|
1557
1751
|
if (typeof params.maxTokens === "number") body.max_tokens = params.maxTokens;
|
|
1558
|
-
if (params.provider) body.provider = providerToString(params.provider);
|
|
1559
1752
|
if (typeof params.temperature === "number")
|
|
1560
1753
|
body.temperature = params.temperature;
|
|
1561
1754
|
if (metadata && Object.keys(metadata).length > 0) body.metadata = metadata;
|
|
@@ -1563,6 +1756,7 @@ function buildProxyBody(params, metadata) {
|
|
|
1563
1756
|
if (params.stopSequences?.length) body.stop_sequences = params.stopSequences;
|
|
1564
1757
|
if (params.tools?.length) body.tools = normalizeTools(params.tools);
|
|
1565
1758
|
if (params.toolChoice) body.tool_choice = normalizeToolChoice(params.toolChoice);
|
|
1759
|
+
if (params.responseFormat) body.response_format = params.responseFormat;
|
|
1566
1760
|
return body;
|
|
1567
1761
|
}
|
|
1568
1762
|
function normalizeMessages(messages) {
|
|
@@ -1742,6 +1936,32 @@ var CustomersClient = class {
|
|
|
1742
1936
|
});
|
|
1743
1937
|
return response.customer;
|
|
1744
1938
|
}
|
|
1939
|
+
/**
|
|
1940
|
+
* Claim a customer by email, setting their external_id.
|
|
1941
|
+
* Used when a customer subscribes via Stripe Checkout (email only) and later
|
|
1942
|
+
* authenticates to the app, needing to link their identity.
|
|
1943
|
+
*
|
|
1944
|
+
* @throws {APIError} with status 404 if customer not found by email
|
|
1945
|
+
* @throws {APIError} with status 409 if customer already claimed or external_id in use
|
|
1946
|
+
*/
|
|
1947
|
+
async claim(request) {
|
|
1948
|
+
this.ensureSecretKey();
|
|
1949
|
+
if (!request.email?.trim()) {
|
|
1950
|
+
throw new ConfigError("email is required");
|
|
1951
|
+
}
|
|
1952
|
+
if (!isValidEmail(request.email)) {
|
|
1953
|
+
throw new ConfigError("invalid email format");
|
|
1954
|
+
}
|
|
1955
|
+
if (!request.external_id?.trim()) {
|
|
1956
|
+
throw new ConfigError("external_id is required");
|
|
1957
|
+
}
|
|
1958
|
+
const response = await this.http.json("/customers/claim", {
|
|
1959
|
+
method: "POST",
|
|
1960
|
+
body: request,
|
|
1961
|
+
apiKey: this.apiKey
|
|
1962
|
+
});
|
|
1963
|
+
return response.customer;
|
|
1964
|
+
}
|
|
1745
1965
|
/**
|
|
1746
1966
|
* Delete a customer by ID.
|
|
1747
1967
|
*/
|
|
@@ -2219,10 +2439,10 @@ function resolveBaseUrl(override) {
|
|
|
2219
2439
|
ErrorCodes,
|
|
2220
2440
|
ModelRelay,
|
|
2221
2441
|
ModelRelayError,
|
|
2222
|
-
|
|
2223
|
-
Providers,
|
|
2442
|
+
ResponseFormatTypes,
|
|
2224
2443
|
SDK_VERSION,
|
|
2225
2444
|
StopReasons,
|
|
2445
|
+
StructuredJSONStream,
|
|
2226
2446
|
TiersClient,
|
|
2227
2447
|
ToolArgsError,
|
|
2228
2448
|
ToolCallAccumulator,
|
|
@@ -2230,6 +2450,7 @@ function resolveBaseUrl(override) {
|
|
|
2230
2450
|
ToolRegistry,
|
|
2231
2451
|
ToolTypes,
|
|
2232
2452
|
TransportError,
|
|
2453
|
+
WebToolModes,
|
|
2233
2454
|
assistantMessageWithToolCalls,
|
|
2234
2455
|
createAccessTokenAuth,
|
|
2235
2456
|
createApiKeyAuth,
|
|
@@ -2254,12 +2475,10 @@ function resolveBaseUrl(override) {
|
|
|
2254
2475
|
mergeTrace,
|
|
2255
2476
|
modelToString,
|
|
2256
2477
|
normalizeModelId,
|
|
2257
|
-
normalizeProvider,
|
|
2258
2478
|
normalizeStopReason,
|
|
2259
2479
|
parseErrorResponse,
|
|
2260
2480
|
parseToolArgs,
|
|
2261
2481
|
parseToolArgsRaw,
|
|
2262
|
-
providerToString,
|
|
2263
2482
|
respondToToolCall,
|
|
2264
2483
|
stopReasonToString,
|
|
2265
2484
|
toolChoiceAuto,
|