@effect/ai-openrouter 0.8.3 → 4.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/dist/Generated.d.ts +19505 -0
  2. package/dist/Generated.d.ts.map +1 -0
  3. package/dist/Generated.js +5115 -0
  4. package/dist/Generated.js.map +1 -0
  5. package/dist/OpenRouterClient.d.ts +116 -0
  6. package/dist/OpenRouterClient.d.ts.map +1 -0
  7. package/dist/OpenRouterClient.js +120 -0
  8. package/dist/OpenRouterClient.js.map +1 -0
  9. package/dist/{dts/OpenRouterConfig.d.ts → OpenRouterConfig.d.ts} +9 -9
  10. package/dist/OpenRouterConfig.d.ts.map +1 -0
  11. package/dist/{esm/OpenRouterConfig.js → OpenRouterConfig.js} +8 -5
  12. package/dist/OpenRouterConfig.js.map +1 -0
  13. package/dist/OpenRouterError.d.ts +83 -0
  14. package/dist/OpenRouterError.d.ts.map +1 -0
  15. package/dist/OpenRouterError.js +10 -0
  16. package/dist/OpenRouterError.js.map +1 -0
  17. package/dist/OpenRouterLanguageModel.d.ts +285 -0
  18. package/dist/OpenRouterLanguageModel.d.ts.map +1 -0
  19. package/dist/OpenRouterLanguageModel.js +1210 -0
  20. package/dist/OpenRouterLanguageModel.js.map +1 -0
  21. package/dist/index.d.ts +29 -0
  22. package/dist/index.d.ts.map +1 -0
  23. package/dist/index.js +30 -0
  24. package/dist/index.js.map +1 -0
  25. package/dist/internal/errors.d.ts +2 -0
  26. package/dist/internal/errors.d.ts.map +1 -0
  27. package/dist/internal/errors.js +347 -0
  28. package/dist/internal/errors.js.map +1 -0
  29. package/dist/{dts/internal → internal}/utilities.d.ts.map +1 -1
  30. package/dist/internal/utilities.js +77 -0
  31. package/dist/internal/utilities.js.map +1 -0
  32. package/package.json +45 -62
  33. package/src/Generated.ts +9312 -5435
  34. package/src/OpenRouterClient.ts +223 -304
  35. package/src/OpenRouterConfig.ts +14 -14
  36. package/src/OpenRouterError.ts +92 -0
  37. package/src/OpenRouterLanguageModel.ts +941 -570
  38. package/src/index.ts +20 -4
  39. package/src/internal/errors.ts +373 -0
  40. package/src/internal/utilities.ts +78 -11
  41. package/Generated/package.json +0 -6
  42. package/OpenRouterClient/package.json +0 -6
  43. package/OpenRouterConfig/package.json +0 -6
  44. package/OpenRouterLanguageModel/package.json +0 -6
  45. package/README.md +0 -5
  46. package/dist/cjs/Generated.js +0 -5813
  47. package/dist/cjs/Generated.js.map +0 -1
  48. package/dist/cjs/OpenRouterClient.js +0 -229
  49. package/dist/cjs/OpenRouterClient.js.map +0 -1
  50. package/dist/cjs/OpenRouterConfig.js +0 -30
  51. package/dist/cjs/OpenRouterConfig.js.map +0 -1
  52. package/dist/cjs/OpenRouterLanguageModel.js +0 -825
  53. package/dist/cjs/OpenRouterLanguageModel.js.map +0 -1
  54. package/dist/cjs/index.js +0 -16
  55. package/dist/cjs/index.js.map +0 -1
  56. package/dist/cjs/internal/utilities.js +0 -29
  57. package/dist/cjs/internal/utilities.js.map +0 -1
  58. package/dist/dts/Generated.d.ts +0 -11026
  59. package/dist/dts/Generated.d.ts.map +0 -1
  60. package/dist/dts/OpenRouterClient.d.ts +0 -407
  61. package/dist/dts/OpenRouterClient.d.ts.map +0 -1
  62. package/dist/dts/OpenRouterConfig.d.ts.map +0 -1
  63. package/dist/dts/OpenRouterLanguageModel.d.ts +0 -215
  64. package/dist/dts/OpenRouterLanguageModel.d.ts.map +0 -1
  65. package/dist/dts/index.d.ts +0 -17
  66. package/dist/dts/index.d.ts.map +0 -1
  67. package/dist/esm/Generated.js +0 -5457
  68. package/dist/esm/Generated.js.map +0 -1
  69. package/dist/esm/OpenRouterClient.js +0 -214
  70. package/dist/esm/OpenRouterClient.js.map +0 -1
  71. package/dist/esm/OpenRouterConfig.js.map +0 -1
  72. package/dist/esm/OpenRouterLanguageModel.js +0 -814
  73. package/dist/esm/OpenRouterLanguageModel.js.map +0 -1
  74. package/dist/esm/index.js +0 -17
  75. package/dist/esm/index.js.map +0 -1
  76. package/dist/esm/internal/utilities.js +0 -21
  77. package/dist/esm/internal/utilities.js.map +0 -1
  78. package/dist/esm/package.json +0 -4
  79. package/index/package.json +0 -6
  80. /package/dist/{dts/internal → internal}/utilities.d.ts +0 -0
@@ -1,825 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.withConfigOverride = exports.model = exports.make = exports.layer = exports.Config = void 0;
7
- var AiError = _interopRequireWildcard(require("@effect/ai/AiError"));
8
- var LanguageModel = _interopRequireWildcard(require("@effect/ai/LanguageModel"));
9
- var AiModel = _interopRequireWildcard(require("@effect/ai/Model"));
10
- var _Telemetry = require("@effect/ai/Telemetry");
11
- var Tool = _interopRequireWildcard(require("@effect/ai/Tool"));
12
- var Arr = _interopRequireWildcard(require("effect/Array"));
13
- var Context = _interopRequireWildcard(require("effect/Context"));
14
- var DateTime = _interopRequireWildcard(require("effect/DateTime"));
15
- var Effect = _interopRequireWildcard(require("effect/Effect"));
16
- var Encoding = _interopRequireWildcard(require("effect/Encoding"));
17
- var _Function = require("effect/Function");
18
- var Layer = _interopRequireWildcard(require("effect/Layer"));
19
- var Predicate = _interopRequireWildcard(require("effect/Predicate"));
20
- var Stream = _interopRequireWildcard(require("effect/Stream"));
21
- var InternalUtilities = _interopRequireWildcard(require("./internal/utilities.js"));
22
- var _OpenRouterClient = require("./OpenRouterClient.js");
23
- function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
24
- /**
25
- * @since 1.0.0
26
- */
27
-
28
- // =============================================================================
29
- // Configuration
30
- // =============================================================================
31
- /**
32
- * @since 1.0.0
33
- * @category Context
34
- */
35
- class Config extends /*#__PURE__*/Context.Tag("@effect/ai-openrouter/OpenRouterLanguageModel/Config")() {
36
- /**
37
- * @since 1.0.0
38
- */
39
- static getOrUndefined = /*#__PURE__*/Effect.map(/*#__PURE__*/Effect.context(), context => context.unsafeMap.get(Config.key));
40
- }
41
- // =============================================================================
42
- // OpenRouter Language Model
43
- // =============================================================================
44
- /**
45
- * @since 1.0.0
46
- * @category Ai Models
47
- */
48
- exports.Config = Config;
49
- const model = (model, config) => AiModel.make("openrouter", layer({
50
- model,
51
- config
52
- }));
53
- /**
54
- * @since 1.0.0
55
- * @category Constructors
56
- */
57
- exports.model = model;
58
- const make = exports.make = /*#__PURE__*/Effect.fnUntraced(function* (options) {
59
- const client = yield* _OpenRouterClient.OpenRouterClient;
60
- const makeRequest = Effect.fnUntraced(function* (providerOptions) {
61
- const context = yield* Effect.context();
62
- const config = {
63
- model: options.model,
64
- ...options.config,
65
- ...context.unsafeMap.get(Config.key)
66
- };
67
- const messages = yield* prepareMessages(providerOptions);
68
- const {
69
- toolChoice,
70
- tools
71
- } = yield* prepareTools(providerOptions);
72
- const responseFormat = providerOptions.responseFormat;
73
- const request = {
74
- ...config,
75
- messages,
76
- tools,
77
- tool_choice: toolChoice,
78
- response_format: responseFormat.type === "text" ? undefined : {
79
- type: "json_schema",
80
- json_schema: {
81
- name: responseFormat.objectName,
82
- description: Tool.getDescriptionFromSchemaAst(responseFormat.schema.ast) ?? "Respond with a JSON object",
83
- schema: Tool.getJsonSchemaFromSchemaAst(responseFormat.schema.ast),
84
- strict: true
85
- }
86
- }
87
- };
88
- return request;
89
- });
90
- return yield* LanguageModel.make({
91
- generateText: Effect.fnUntraced(function* (options) {
92
- const request = yield* makeRequest(options);
93
- annotateRequest(options.span, request);
94
- const rawResponse = yield* client.createChatCompletion(request);
95
- annotateResponse(options.span, rawResponse);
96
- return yield* makeResponse(rawResponse);
97
- }),
98
- streamText: Effect.fnUntraced(function* (options) {
99
- const request = yield* makeRequest(options);
100
- annotateRequest(options.span, request);
101
- return client.createChatCompletionStream(request);
102
- }, (effect, options) => effect.pipe(Effect.flatMap(stream => makeStreamResponse(stream)), Stream.unwrap, Stream.map(response => {
103
- annotateStreamResponse(options.span, response);
104
- return response;
105
- })))
106
- });
107
- });
108
- /**
109
- * @since 1.0.0
110
- * @category Layers
111
- */
112
- const layer = options => Layer.effect(LanguageModel.LanguageModel, make({
113
- model: options.model,
114
- config: options.config
115
- }));
116
- /**
117
- * @since 1.0.0
118
- * @category Configuration
119
- */
120
- exports.layer = layer;
121
- const withConfigOverride = exports.withConfigOverride = /*#__PURE__*/(0, _Function.dual)(2, (self, overrides) => Effect.flatMap(Config.getOrUndefined, config => Effect.provideService(self, Config, {
122
- ...config,
123
- ...overrides
124
- })));
125
- // =============================================================================
126
- // Prompt Conversion
127
- // =============================================================================
128
- const prepareMessages = /*#__PURE__*/Effect.fnUntraced(function* (options) {
129
- const messages = [];
130
- for (const message of options.prompt.content) {
131
- switch (message.role) {
132
- case "system":
133
- {
134
- messages.push({
135
- role: "system",
136
- content: message.content,
137
- cache_control: getCacheControl(message)
138
- });
139
- break;
140
- }
141
- case "user":
142
- {
143
- if (message.content.length === 1 && message.content[0].type === "text") {
144
- const part = message.content[0];
145
- const cacheControl = getCacheControl(message) ?? getCacheControl(part);
146
- messages.push({
147
- role: "user",
148
- content: Predicate.isNotUndefined(cacheControl) ? [{
149
- type: "text",
150
- text: part.text,
151
- cache_control: cacheControl
152
- }] : part.text
153
- });
154
- } else {
155
- const content = [];
156
- const messageCacheControl = getCacheControl(message);
157
- for (const part of message.content) {
158
- const partCacheControl = getCacheControl(part);
159
- const cacheControl = partCacheControl ?? messageCacheControl;
160
- switch (part.type) {
161
- case "text":
162
- {
163
- content.push({
164
- type: "text",
165
- text: part.text,
166
- cache_control: cacheControl
167
- });
168
- break;
169
- }
170
- case "file":
171
- {
172
- if (part.mediaType.startsWith("image/")) {
173
- const mediaType = part.mediaType === "image/*" ? "image/jpeg" : part.mediaType;
174
- content.push({
175
- type: "image_url",
176
- image_url: {
177
- url: part.data instanceof URL ? part.data.toString() : part.data instanceof Uint8Array ? `data:${mediaType};base64,${Encoding.encodeBase64(part.data)}` : part.data
178
- },
179
- cache_control: cacheControl
180
- });
181
- } else {
182
- const options = part.options.openrouter;
183
- const fileName = options?.fileName ?? part.fileName ?? "";
184
- content.push({
185
- type: "file",
186
- file: {
187
- filename: fileName,
188
- file_data: part.data instanceof URL ? part.data.toString() : part.data instanceof Uint8Array ? `data:${part.mediaType};base64,${Encoding.encodeBase64(part.data)}` : part.data
189
- },
190
- cache_control: part.data instanceof URL ? cacheControl : undefined
191
- });
192
- }
193
- break;
194
- }
195
- }
196
- }
197
- messages.push({
198
- role: "user",
199
- content
200
- });
201
- }
202
- break;
203
- }
204
- case "assistant":
205
- {
206
- let text = "";
207
- let reasoning = "";
208
- const reasoningDetails = [];
209
- const toolCalls = [];
210
- const cacheControl = getCacheControl(message);
211
- for (const part of message.content) {
212
- switch (part.type) {
213
- case "text":
214
- {
215
- text += part.text;
216
- break;
217
- }
218
- case "reasoning":
219
- {
220
- reasoning += part.text;
221
- reasoningDetails.push({
222
- type: "reasoning.text",
223
- text: part.text
224
- });
225
- break;
226
- }
227
- case "tool-call":
228
- {
229
- toolCalls.push({
230
- id: part.id,
231
- type: "function",
232
- function: {
233
- name: part.name,
234
- arguments: JSON.stringify(part.params)
235
- }
236
- });
237
- break;
238
- }
239
- }
240
- }
241
- messages.push({
242
- role: "assistant",
243
- content: text,
244
- tool_calls: toolCalls.length > 0 ? toolCalls : undefined,
245
- reasoning: reasoning.length > 0 ? reasoning : undefined,
246
- reasoning_details: reasoningDetails.length > 0 ? reasoningDetails : undefined,
247
- cache_control: cacheControl
248
- });
249
- break;
250
- }
251
- case "tool":
252
- {
253
- const cacheControl = getCacheControl(message);
254
- for (const part of message.content) {
255
- messages.push({
256
- role: "tool",
257
- tool_call_id: part.id,
258
- content: JSON.stringify(part.result),
259
- cache_control: cacheControl
260
- });
261
- }
262
- break;
263
- }
264
- }
265
- }
266
- return messages;
267
- });
268
- // =============================================================================
269
- // Tool Conversion
270
- // =============================================================================
271
- const prepareTools = /*#__PURE__*/Effect.fnUntraced(function* (options) {
272
- if (options.tools.length === 0) {
273
- return {
274
- tools: undefined,
275
- toolChoice: undefined
276
- };
277
- }
278
- const hasProviderDefinedTools = options.tools.some(tool => Tool.isProviderDefined(tool));
279
- if (hasProviderDefinedTools) {
280
- return yield* new AiError.MalformedInput({
281
- module: "OpenRouterLanguageModel",
282
- method: "prepareTools",
283
- description: "Provider-defined tools are unsupported by the OpenRouter " + "provider integration at this time"
284
- });
285
- }
286
- let tools = [];
287
- let toolChoice = undefined;
288
- for (const tool of options.tools) {
289
- tools.push({
290
- type: "function",
291
- function: {
292
- name: tool.name,
293
- description: Tool.getDescription(tool),
294
- parameters: Tool.getJsonSchema(tool),
295
- strict: true
296
- }
297
- });
298
- }
299
- if (options.toolChoice === "none") {
300
- toolChoice = "none";
301
- } else if (options.toolChoice === "auto") {
302
- toolChoice = "auto";
303
- } else if (options.toolChoice === "required") {
304
- toolChoice = "required";
305
- } else if ("tool" in options.toolChoice) {
306
- toolChoice = {
307
- type: "function",
308
- function: {
309
- name: options.toolChoice.tool
310
- }
311
- };
312
- } else {
313
- const allowedTools = new Set(options.toolChoice.oneOf);
314
- tools = tools.filter(tool => allowedTools.has(tool.function.name));
315
- toolChoice = options.toolChoice.mode === "auto" ? "auto" : "required";
316
- }
317
- return {
318
- tools,
319
- toolChoice
320
- };
321
- });
322
- // =============================================================================
323
- // Response Conversion
324
- // =============================================================================
325
- const makeResponse = /*#__PURE__*/Effect.fnUntraced(function* (response) {
326
- const choice = response.choices[0];
327
- if (Predicate.isUndefined(choice)) {
328
- return yield* new AiError.MalformedOutput({
329
- module: "OpenRouterLanguageModel",
330
- method: "makeResponse",
331
- description: "Received response with no valid choices"
332
- });
333
- }
334
- const parts = [];
335
- const message = choice.message;
336
- const createdAt = new Date(response.created * 1000);
337
- parts.push({
338
- type: "response-metadata",
339
- id: response.id,
340
- modelId: response.model,
341
- timestamp: DateTime.formatIso(DateTime.unsafeFromDate(createdAt))
342
- });
343
- if (Predicate.isNotNullable(message.reasoning) && message.reasoning.length > 0) {
344
- parts.push({
345
- type: "reasoning",
346
- text: message.reasoning
347
- });
348
- }
349
- if (Predicate.isNotNullable(message.reasoning_details) && message.reasoning_details.length > 0) {
350
- for (const detail of message.reasoning_details) {
351
- switch (detail.type) {
352
- case "reasoning.summary":
353
- {
354
- if (Predicate.isNotUndefined(detail.summary) && detail.summary.length > 0) {
355
- parts.push({
356
- type: "reasoning",
357
- text: detail.summary
358
- });
359
- }
360
- break;
361
- }
362
- case "reasoning.encrypted":
363
- {
364
- if (Predicate.isNotUndefined(detail.data) && detail.data.length > 0) {
365
- parts.push({
366
- type: "reasoning",
367
- text: "",
368
- metadata: {
369
- openrouter: {
370
- type: "encrypted_reasoning",
371
- format: detail.format,
372
- redactedData: detail.data
373
- }
374
- }
375
- });
376
- }
377
- break;
378
- }
379
- case "reasoning.text":
380
- {
381
- if (Predicate.isNotUndefined(detail.text) && detail.text.length > 0) {
382
- parts.push({
383
- type: "reasoning",
384
- text: detail.text,
385
- metadata: {
386
- openrouter: {
387
- type: "reasoning",
388
- signature: detail.signature
389
- }
390
- }
391
- });
392
- }
393
- break;
394
- }
395
- }
396
- }
397
- }
398
- if (Predicate.isNotNullable(message.content) && message.content.length > 0) {
399
- parts.push({
400
- type: "text",
401
- text: message.content
402
- });
403
- }
404
- if (Predicate.isNotNullable(message.tool_calls)) {
405
- for (const toolCall of message.tool_calls) {
406
- const toolName = toolCall.function.name;
407
- const toolParams = toolCall.function.arguments;
408
- const params = yield* Effect.try({
409
- try: () => Tool.unsafeSecureJsonParse(toolParams),
410
- catch: cause => new AiError.MalformedOutput({
411
- module: "OpenRouterLanguageModel",
412
- method: "makeResponse",
413
- description: "Failed to securely parse tool call parameters " + `for tool '${toolName}':\nParameters: ${toolParams}`,
414
- cause
415
- })
416
- });
417
- parts.push({
418
- type: "tool-call",
419
- id: toolCall.id,
420
- name: toolName,
421
- params
422
- });
423
- }
424
- }
425
- if (Predicate.isNotNullable(message.annotations)) {
426
- for (const annotation of message.annotations) {
427
- if (annotation.type === "url_citation") {
428
- parts.push({
429
- type: "source",
430
- sourceType: "url",
431
- id: annotation.url_citation.url,
432
- url: annotation.url_citation.url,
433
- title: annotation.url_citation.title,
434
- metadata: {
435
- openrouter: {
436
- content: annotation.url_citation.content
437
- }
438
- }
439
- });
440
- }
441
- }
442
- }
443
- if (Predicate.isNotNullable(message.images)) {
444
- for (const image of message.images) {
445
- parts.push({
446
- type: "file",
447
- mediaType: getMediaType(image.image_url.url) ?? "image/jpeg",
448
- data: getBase64FromDataUrl(image.image_url.url)
449
- });
450
- }
451
- }
452
- parts.push({
453
- type: "finish",
454
- reason: InternalUtilities.resolveFinishReason(choice.finish_reason),
455
- usage: {
456
- inputTokens: response.usage?.prompt_tokens,
457
- outputTokens: response.usage?.completion_tokens,
458
- totalTokens: response.usage?.total_tokens,
459
- reasoningTokens: response.usage?.completion_tokens_details?.reasoning_tokens,
460
- cachedInputTokens: response.usage?.prompt_tokens_details?.cached_tokens
461
- },
462
- metadata: {
463
- openrouter: {
464
- provider: response.provider,
465
- usage: {
466
- cost: response.usage?.cost,
467
- promptTokensDetails: response.usage?.prompt_tokens_details,
468
- completionTokensDetails: response.usage?.completion_tokens_details,
469
- costDetails: response.usage?.cost_details
470
- }
471
- }
472
- }
473
- });
474
- return parts;
475
- });
476
- const makeStreamResponse = /*#__PURE__*/Effect.fnUntraced(function* (stream) {
477
- let idCounter = 0;
478
- let activeTextId = undefined;
479
- let activeReasoningId = undefined;
480
- let finishReason = "unknown";
481
- let responseMetadataEmitted = false;
482
- const activeToolCalls = {};
483
- return stream.pipe(Stream.mapEffect(Effect.fnUntraced(function* (event) {
484
- const parts = [];
485
- if ("error" in event) {
486
- parts.push({
487
- type: "error",
488
- error: event.error
489
- });
490
- return parts;
491
- }
492
- // Response Metadata
493
- if (Predicate.isNotUndefined(event.id) && !responseMetadataEmitted) {
494
- parts.push({
495
- type: "response-metadata",
496
- id: event.id,
497
- modelId: event.model,
498
- timestamp: DateTime.formatIso(yield* DateTime.now)
499
- });
500
- responseMetadataEmitted = true;
501
- }
502
- const choice = event.choices[0];
503
- if (Predicate.isUndefined(choice)) {
504
- return yield* new AiError.MalformedOutput({
505
- module: "OpenRouterLanguageModel",
506
- method: "makeResponse",
507
- description: "Received response with no valid choices"
508
- });
509
- }
510
- const delta = choice.delta;
511
- if (Predicate.isUndefined(delta)) {
512
- return parts;
513
- }
514
- // Reasoning Parts
515
- const emitReasoningPart = (delta, metadata = undefined) => {
516
- // End in-progress text part if present before starting reasoning
517
- if (Predicate.isNotUndefined(activeTextId)) {
518
- parts.push({
519
- type: "text-end",
520
- id: activeTextId
521
- });
522
- activeTextId = undefined;
523
- }
524
- // Start a new reasoning part if necessary
525
- if (Predicate.isUndefined(activeReasoningId)) {
526
- activeReasoningId = (idCounter++).toString();
527
- parts.push({
528
- type: "reasoning-start",
529
- id: activeReasoningId,
530
- metadata: {
531
- openrouter: metadata
532
- }
533
- });
534
- }
535
- // Emit the reasoning delta
536
- parts.push({
537
- type: "reasoning-delta",
538
- id: activeReasoningId,
539
- delta,
540
- metadata: {
541
- openrouter: metadata
542
- }
543
- });
544
- };
545
- if (Predicate.isNotNullable(delta.reasoning_details) && delta.reasoning_details.length > 0) {
546
- for (const detail of delta.reasoning_details) {
547
- switch (detail.type) {
548
- case "reasoning.summary":
549
- {
550
- if (Predicate.isNotUndefined(detail.summary) && detail.summary.length > 0) {
551
- emitReasoningPart(detail.summary);
552
- }
553
- break;
554
- }
555
- case "reasoning.encrypted":
556
- {
557
- if (Predicate.isNotUndefined(detail.data) && detail.data.length > 0) {
558
- emitReasoningPart("", {
559
- type: "encrypted_reasoning",
560
- format: detail.format,
561
- redactedData: detail.data
562
- });
563
- }
564
- break;
565
- }
566
- case "reasoning.text":
567
- {
568
- if (Predicate.isNotUndefined(detail.text) && detail.text.length > 0) {
569
- emitReasoningPart(detail.text, {
570
- type: "reasoning",
571
- signature: detail.signature
572
- });
573
- }
574
- break;
575
- }
576
- }
577
- }
578
- } else if (Predicate.isNotNullable(delta.reasoning) && delta.reasoning.length > 0) {
579
- emitReasoningPart(delta.reasoning);
580
- }
581
- // Text Parts
582
- if (Predicate.isNotNullable(delta.content) && delta.content.length > 0) {
583
- // End in-progress reasoning part if present before starting text
584
- if (Predicate.isNotUndefined(activeReasoningId)) {
585
- parts.push({
586
- type: "reasoning-end",
587
- id: activeReasoningId
588
- });
589
- activeReasoningId = undefined;
590
- }
591
- // Start a new text part if necessary
592
- if (Predicate.isUndefined(activeTextId)) {
593
- activeTextId = (idCounter++).toString();
594
- parts.push({
595
- type: "text-start",
596
- id: activeTextId
597
- });
598
- }
599
- // Emit the text delta
600
- parts.push({
601
- type: "text-delta",
602
- id: activeTextId,
603
- delta: delta.content
604
- });
605
- }
606
- // Source Parts
607
- if (Predicate.isNotNullable(delta.annotations)) {
608
- for (const annotation of delta.annotations) {
609
- if (annotation.type === "url_citation") {
610
- parts.push({
611
- type: "source",
612
- sourceType: "url",
613
- id: annotation.url_citation.url,
614
- url: annotation.url_citation.url,
615
- title: annotation.url_citation.title,
616
- metadata: {
617
- openrouter: {
618
- content: annotation.url_citation.content
619
- }
620
- }
621
- });
622
- }
623
- }
624
- }
625
- // Tool Call Parts
626
- if (Predicate.isNotNullable(delta.tool_calls) && delta.tool_calls.length > 0) {
627
- for (const toolCall of delta.tool_calls) {
628
- // Get the active tool call, if present
629
- let activeToolCall = activeToolCalls[toolCall.index];
630
- // If no active tool call was found, start a new active tool call
631
- if (Predicate.isUndefined(activeToolCall)) {
632
- // The tool call id and function name always come back with the
633
- // first tool call delta
634
- activeToolCall = {
635
- index: toolCall.index,
636
- id: toolCall.id,
637
- name: toolCall.function.name,
638
- params: toolCall.function.arguments ?? ""
639
- };
640
- activeToolCalls[toolCall.index] = activeToolCall;
641
- parts.push({
642
- type: "tool-params-start",
643
- id: activeToolCall.id,
644
- name: activeToolCall.name
645
- });
646
- // Emit a tool call delta part if parameters were also sent
647
- if (activeToolCall.params.length > 0) {
648
- parts.push({
649
- type: "tool-params-delta",
650
- id: activeToolCall.id,
651
- delta: activeToolCall.params
652
- });
653
- }
654
- } else {
655
- // If an active tool call was found, update and emit the delta for
656
- // the tool call's parameters
657
- activeToolCall.params += toolCall.function.arguments;
658
- parts.push({
659
- type: "tool-params-delta",
660
- id: activeToolCall.id,
661
- delta: activeToolCall.params
662
- });
663
- }
664
- // Check if the tool call is complete
665
- try {
666
- const params = Tool.unsafeSecureJsonParse(activeToolCall.params);
667
- parts.push({
668
- type: "tool-params-end",
669
- id: activeToolCall.id
670
- });
671
- parts.push({
672
- type: "tool-call",
673
- id: activeToolCall.id,
674
- name: activeToolCall.name,
675
- params
676
- });
677
- delete activeToolCalls[toolCall.index];
678
- } catch {
679
- // Tool call incomplete, continue parsing
680
- continue;
681
- }
682
- }
683
- }
684
- // File Parts
685
- if (Predicate.isNotNullable(delta.images)) {
686
- for (const image of delta.images) {
687
- parts.push({
688
- type: "file",
689
- mediaType: getMediaType(image.image_url.url) ?? "image/jpeg",
690
- data: getBase64FromDataUrl(image.image_url.url)
691
- });
692
- }
693
- }
694
- // Finish Parts
695
- if (Predicate.isNotNullable(choice.finish_reason)) {
696
- finishReason = InternalUtilities.resolveFinishReason(choice.finish_reason);
697
- }
698
- // Usage is only emitted by the last part of the stream, so we need to
699
- // handle flushing any remaining text / reasoning / tool calls
700
- if (Predicate.isNotUndefined(event.usage)) {
701
- // Complete any remaining tool calls if the finish reason is tool-calls
702
- if (finishReason === "tool-calls") {
703
- for (const toolCall of Object.values(activeToolCalls)) {
704
- // Coerce invalid tool call parameters to an empty object
705
- const params = yield* Effect.try(() => Tool.unsafeSecureJsonParse(toolCall.params)).pipe(Effect.catchAll(() => Effect.succeed({})));
706
- parts.push({
707
- type: "tool-params-end",
708
- id: toolCall.id
709
- });
710
- parts.push({
711
- type: "tool-call",
712
- id: toolCall.id,
713
- name: toolCall.name,
714
- params
715
- });
716
- delete activeToolCalls[toolCall.index];
717
- }
718
- }
719
- // Flush remaining reasoning parts
720
- if (Predicate.isNotUndefined(activeReasoningId)) {
721
- parts.push({
722
- type: "reasoning-end",
723
- id: activeReasoningId
724
- });
725
- activeReasoningId = undefined;
726
- }
727
- // Flush remaining text parts
728
- if (Predicate.isNotUndefined(activeTextId)) {
729
- parts.push({
730
- type: "text-end",
731
- id: activeTextId
732
- });
733
- activeTextId = undefined;
734
- }
735
- parts.push({
736
- type: "finish",
737
- reason: finishReason,
738
- usage: {
739
- inputTokens: event.usage?.prompt_tokens,
740
- outputTokens: event.usage?.completion_tokens,
741
- totalTokens: event.usage?.total_tokens,
742
- reasoningTokens: event.usage?.completion_tokens_details?.reasoning_tokens,
743
- cachedInputTokens: event.usage?.prompt_tokens_details?.cached_tokens
744
- },
745
- metadata: {
746
- openrouter: {
747
- provider: event.provider,
748
- usage: {
749
- cost: event.usage?.cost,
750
- promptTokensDetails: event.usage?.prompt_tokens_details,
751
- completionTokensDetails: event.usage?.completion_tokens_details,
752
- costDetails: event.usage?.cost_details
753
- }
754
- }
755
- }
756
- });
757
- }
758
- return parts;
759
- })), Stream.flattenIterables);
760
- });
761
- // =============================================================================
762
- // Telemetry
763
- // =============================================================================
764
- const annotateRequest = (span, request) => {
765
- (0, _Telemetry.addGenAIAnnotations)(span, {
766
- system: "openrouter",
767
- operation: {
768
- name: "chat"
769
- },
770
- request: {
771
- model: request.model,
772
- temperature: request.temperature,
773
- topP: request.top_p,
774
- maxTokens: request.max_tokens,
775
- stopSequences: Arr.ensure(request.stop).filter(Predicate.isNotNullable)
776
- }
777
- });
778
- };
779
- const annotateResponse = (span, response) => {
780
- (0, _Telemetry.addGenAIAnnotations)(span, {
781
- response: {
782
- id: response.id,
783
- model: response.model,
784
- finishReasons: response.choices.map(choice => choice.finish_reason).filter(Predicate.isNotNullable)
785
- },
786
- usage: {
787
- inputTokens: response.usage?.prompt_tokens,
788
- outputTokens: response.usage?.completion_tokens
789
- }
790
- });
791
- };
792
- const annotateStreamResponse = (span, part) => {
793
- if (part.type === "response-metadata") {
794
- (0, _Telemetry.addGenAIAnnotations)(span, {
795
- response: {
796
- id: part.id,
797
- model: part.modelId
798
- }
799
- });
800
- }
801
- if (part.type === "finish") {
802
- (0, _Telemetry.addGenAIAnnotations)(span, {
803
- response: {
804
- finishReasons: [part.reason]
805
- },
806
- usage: {
807
- inputTokens: part.usage.inputTokens,
808
- outputTokens: part.usage.outputTokens
809
- }
810
- });
811
- }
812
- };
813
- // =============================================================================
814
- // Utilities
815
- // =============================================================================
816
- const getCacheControl = part => part.options.openrouter?.cacheControl;
817
- const getMediaType = dataUrl => {
818
- const match = dataUrl.match(/^data:([^;]+)/);
819
- return match ? match[1] : undefined;
820
- };
821
- const getBase64FromDataUrl = dataUrl => {
822
- const match = dataUrl.match(/^data:[^;]*;base64,(.+)$/);
823
- return match ? match[1] : dataUrl;
824
- };
825
- //# sourceMappingURL=OpenRouterLanguageModel.js.map