@effect/ai-openrouter 0.8.2 → 4.0.0-beta.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/dist/Generated.d.ts +19505 -0
- package/dist/Generated.d.ts.map +1 -0
- package/dist/Generated.js +5115 -0
- package/dist/Generated.js.map +1 -0
- package/dist/OpenRouterClient.d.ts +116 -0
- package/dist/OpenRouterClient.d.ts.map +1 -0
- package/dist/OpenRouterClient.js +120 -0
- package/dist/OpenRouterClient.js.map +1 -0
- package/dist/{dts/OpenRouterConfig.d.ts → OpenRouterConfig.d.ts} +9 -9
- package/dist/OpenRouterConfig.d.ts.map +1 -0
- package/dist/{esm/OpenRouterConfig.js → OpenRouterConfig.js} +8 -5
- package/dist/OpenRouterConfig.js.map +1 -0
- package/dist/OpenRouterError.d.ts +83 -0
- package/dist/OpenRouterError.d.ts.map +1 -0
- package/dist/OpenRouterError.js +10 -0
- package/dist/OpenRouterError.js.map +1 -0
- package/dist/OpenRouterLanguageModel.d.ts +285 -0
- package/dist/OpenRouterLanguageModel.d.ts.map +1 -0
- package/dist/OpenRouterLanguageModel.js +1210 -0
- package/dist/OpenRouterLanguageModel.js.map +1 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +30 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/errors.d.ts +2 -0
- package/dist/internal/errors.d.ts.map +1 -0
- package/dist/internal/errors.js +347 -0
- package/dist/internal/errors.js.map +1 -0
- package/dist/{dts/internal → internal}/utilities.d.ts.map +1 -1
- package/dist/internal/utilities.js +77 -0
- package/dist/internal/utilities.js.map +1 -0
- package/package.json +45 -62
- package/src/Generated.ts +9312 -5435
- package/src/OpenRouterClient.ts +223 -304
- package/src/OpenRouterConfig.ts +14 -14
- package/src/OpenRouterError.ts +92 -0
- package/src/OpenRouterLanguageModel.ts +941 -572
- package/src/index.ts +20 -4
- package/src/internal/errors.ts +373 -0
- package/src/internal/utilities.ts +78 -11
- package/Generated/package.json +0 -6
- package/OpenRouterClient/package.json +0 -6
- package/OpenRouterConfig/package.json +0 -6
- package/OpenRouterLanguageModel/package.json +0 -6
- package/README.md +0 -5
- package/dist/cjs/Generated.js +0 -5813
- package/dist/cjs/Generated.js.map +0 -1
- package/dist/cjs/OpenRouterClient.js +0 -229
- package/dist/cjs/OpenRouterClient.js.map +0 -1
- package/dist/cjs/OpenRouterConfig.js +0 -30
- package/dist/cjs/OpenRouterConfig.js.map +0 -1
- package/dist/cjs/OpenRouterLanguageModel.js +0 -826
- package/dist/cjs/OpenRouterLanguageModel.js.map +0 -1
- package/dist/cjs/index.js +0 -16
- package/dist/cjs/index.js.map +0 -1
- package/dist/cjs/internal/utilities.js +0 -29
- package/dist/cjs/internal/utilities.js.map +0 -1
- package/dist/dts/Generated.d.ts +0 -11026
- package/dist/dts/Generated.d.ts.map +0 -1
- package/dist/dts/OpenRouterClient.d.ts +0 -407
- package/dist/dts/OpenRouterClient.d.ts.map +0 -1
- package/dist/dts/OpenRouterConfig.d.ts.map +0 -1
- package/dist/dts/OpenRouterLanguageModel.d.ts +0 -215
- package/dist/dts/OpenRouterLanguageModel.d.ts.map +0 -1
- package/dist/dts/index.d.ts +0 -17
- package/dist/dts/index.d.ts.map +0 -1
- package/dist/esm/Generated.js +0 -5457
- package/dist/esm/Generated.js.map +0 -1
- package/dist/esm/OpenRouterClient.js +0 -214
- package/dist/esm/OpenRouterClient.js.map +0 -1
- package/dist/esm/OpenRouterConfig.js.map +0 -1
- package/dist/esm/OpenRouterLanguageModel.js +0 -815
- package/dist/esm/OpenRouterLanguageModel.js.map +0 -1
- package/dist/esm/index.js +0 -17
- package/dist/esm/index.js.map +0 -1
- package/dist/esm/internal/utilities.js +0 -21
- package/dist/esm/internal/utilities.js.map +0 -1
- package/dist/esm/package.json +0 -4
- package/index/package.json +0 -6
- /package/dist/{dts/internal → internal}/utilities.d.ts +0 -0
|
@@ -1,826 +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) && delta.reasoning.length > 0) {
|
|
546
|
-
emitReasoningPart(delta.reasoning);
|
|
547
|
-
}
|
|
548
|
-
if (Predicate.isNotNullable(delta.reasoning_details) && delta.reasoning_details.length > 0) {
|
|
549
|
-
for (const detail of delta.reasoning_details) {
|
|
550
|
-
switch (detail.type) {
|
|
551
|
-
case "reasoning.summary":
|
|
552
|
-
{
|
|
553
|
-
if (Predicate.isNotUndefined(detail.summary) && detail.summary.length > 0) {
|
|
554
|
-
emitReasoningPart(detail.summary);
|
|
555
|
-
}
|
|
556
|
-
break;
|
|
557
|
-
}
|
|
558
|
-
case "reasoning.encrypted":
|
|
559
|
-
{
|
|
560
|
-
if (Predicate.isNotUndefined(detail.data) && detail.data.length > 0) {
|
|
561
|
-
emitReasoningPart("", {
|
|
562
|
-
type: "encrypted_reasoning",
|
|
563
|
-
format: detail.format,
|
|
564
|
-
redactedData: detail.data
|
|
565
|
-
});
|
|
566
|
-
}
|
|
567
|
-
break;
|
|
568
|
-
}
|
|
569
|
-
case "reasoning.text":
|
|
570
|
-
{
|
|
571
|
-
if (Predicate.isNotUndefined(detail.text) && detail.text.length > 0) {
|
|
572
|
-
emitReasoningPart(detail.text, {
|
|
573
|
-
type: "reasoning",
|
|
574
|
-
signature: detail.signature
|
|
575
|
-
});
|
|
576
|
-
}
|
|
577
|
-
break;
|
|
578
|
-
}
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
|
-
}
|
|
582
|
-
// Text Parts
|
|
583
|
-
if (Predicate.isNotNullable(delta.content) && delta.content.length > 0) {
|
|
584
|
-
// End in-progress reasoning part if present before starting text
|
|
585
|
-
if (Predicate.isNotUndefined(activeReasoningId)) {
|
|
586
|
-
parts.push({
|
|
587
|
-
type: "reasoning-end",
|
|
588
|
-
id: activeReasoningId
|
|
589
|
-
});
|
|
590
|
-
activeReasoningId = undefined;
|
|
591
|
-
}
|
|
592
|
-
// Start a new text part if necessary
|
|
593
|
-
if (Predicate.isUndefined(activeTextId)) {
|
|
594
|
-
activeTextId = (idCounter++).toString();
|
|
595
|
-
parts.push({
|
|
596
|
-
type: "text-start",
|
|
597
|
-
id: activeTextId
|
|
598
|
-
});
|
|
599
|
-
}
|
|
600
|
-
// Emit the text delta
|
|
601
|
-
parts.push({
|
|
602
|
-
type: "text-delta",
|
|
603
|
-
id: activeTextId,
|
|
604
|
-
delta: delta.content
|
|
605
|
-
});
|
|
606
|
-
}
|
|
607
|
-
// Source Parts
|
|
608
|
-
if (Predicate.isNotNullable(delta.annotations)) {
|
|
609
|
-
for (const annotation of delta.annotations) {
|
|
610
|
-
if (annotation.type === "url_citation") {
|
|
611
|
-
parts.push({
|
|
612
|
-
type: "source",
|
|
613
|
-
sourceType: "url",
|
|
614
|
-
id: annotation.url_citation.url,
|
|
615
|
-
url: annotation.url_citation.url,
|
|
616
|
-
title: annotation.url_citation.title,
|
|
617
|
-
metadata: {
|
|
618
|
-
openrouter: {
|
|
619
|
-
content: annotation.url_citation.content
|
|
620
|
-
}
|
|
621
|
-
}
|
|
622
|
-
});
|
|
623
|
-
}
|
|
624
|
-
}
|
|
625
|
-
}
|
|
626
|
-
// Tool Call Parts
|
|
627
|
-
if (Predicate.isNotNullable(delta.tool_calls) && delta.tool_calls.length > 0) {
|
|
628
|
-
for (const toolCall of delta.tool_calls) {
|
|
629
|
-
// Get the active tool call, if present
|
|
630
|
-
let activeToolCall = activeToolCalls[toolCall.index];
|
|
631
|
-
// If no active tool call was found, start a new active tool call
|
|
632
|
-
if (Predicate.isUndefined(activeToolCall)) {
|
|
633
|
-
// The tool call id and function name always come back with the
|
|
634
|
-
// first tool call delta
|
|
635
|
-
activeToolCall = {
|
|
636
|
-
index: toolCall.index,
|
|
637
|
-
id: toolCall.id,
|
|
638
|
-
name: toolCall.function.name,
|
|
639
|
-
params: toolCall.function.arguments ?? ""
|
|
640
|
-
};
|
|
641
|
-
activeToolCalls[toolCall.index] = activeToolCall;
|
|
642
|
-
parts.push({
|
|
643
|
-
type: "tool-params-start",
|
|
644
|
-
id: activeToolCall.id,
|
|
645
|
-
name: activeToolCall.name
|
|
646
|
-
});
|
|
647
|
-
// Emit a tool call delta part if parameters were also sent
|
|
648
|
-
if (activeToolCall.params.length > 0) {
|
|
649
|
-
parts.push({
|
|
650
|
-
type: "tool-params-delta",
|
|
651
|
-
id: activeToolCall.id,
|
|
652
|
-
delta: activeToolCall.params
|
|
653
|
-
});
|
|
654
|
-
}
|
|
655
|
-
} else {
|
|
656
|
-
// If an active tool call was found, update and emit the delta for
|
|
657
|
-
// the tool call's parameters
|
|
658
|
-
activeToolCall.params += toolCall.function.arguments;
|
|
659
|
-
parts.push({
|
|
660
|
-
type: "tool-params-delta",
|
|
661
|
-
id: activeToolCall.id,
|
|
662
|
-
delta: activeToolCall.params
|
|
663
|
-
});
|
|
664
|
-
}
|
|
665
|
-
// Check if the tool call is complete
|
|
666
|
-
try {
|
|
667
|
-
const params = Tool.unsafeSecureJsonParse(activeToolCall.params);
|
|
668
|
-
parts.push({
|
|
669
|
-
type: "tool-params-end",
|
|
670
|
-
id: activeToolCall.id
|
|
671
|
-
});
|
|
672
|
-
parts.push({
|
|
673
|
-
type: "tool-call",
|
|
674
|
-
id: activeToolCall.id,
|
|
675
|
-
name: activeToolCall.name,
|
|
676
|
-
params
|
|
677
|
-
});
|
|
678
|
-
delete activeToolCalls[toolCall.index];
|
|
679
|
-
} catch {
|
|
680
|
-
// Tool call incomplete, continue parsing
|
|
681
|
-
continue;
|
|
682
|
-
}
|
|
683
|
-
}
|
|
684
|
-
}
|
|
685
|
-
// File Parts
|
|
686
|
-
if (Predicate.isNotNullable(delta.images)) {
|
|
687
|
-
for (const image of delta.images) {
|
|
688
|
-
parts.push({
|
|
689
|
-
type: "file",
|
|
690
|
-
mediaType: getMediaType(image.image_url.url) ?? "image/jpeg",
|
|
691
|
-
data: getBase64FromDataUrl(image.image_url.url)
|
|
692
|
-
});
|
|
693
|
-
}
|
|
694
|
-
}
|
|
695
|
-
// Finish Parts
|
|
696
|
-
if (Predicate.isNotNullable(choice.finish_reason)) {
|
|
697
|
-
finishReason = InternalUtilities.resolveFinishReason(choice.finish_reason);
|
|
698
|
-
}
|
|
699
|
-
// Usage is only emitted by the last part of the stream, so we need to
|
|
700
|
-
// handle flushing any remaining text / reasoning / tool calls
|
|
701
|
-
if (Predicate.isNotUndefined(event.usage)) {
|
|
702
|
-
// Complete any remaining tool calls if the finish reason is tool-calls
|
|
703
|
-
if (finishReason === "tool-calls") {
|
|
704
|
-
for (const toolCall of Object.values(activeToolCalls)) {
|
|
705
|
-
// Coerce invalid tool call parameters to an empty object
|
|
706
|
-
const params = yield* Effect.try(() => Tool.unsafeSecureJsonParse(toolCall.params)).pipe(Effect.catchAll(() => Effect.succeed({})));
|
|
707
|
-
parts.push({
|
|
708
|
-
type: "tool-params-end",
|
|
709
|
-
id: toolCall.id
|
|
710
|
-
});
|
|
711
|
-
parts.push({
|
|
712
|
-
type: "tool-call",
|
|
713
|
-
id: toolCall.id,
|
|
714
|
-
name: toolCall.name,
|
|
715
|
-
params
|
|
716
|
-
});
|
|
717
|
-
delete activeToolCalls[toolCall.index];
|
|
718
|
-
}
|
|
719
|
-
}
|
|
720
|
-
// Flush remaining reasoning parts
|
|
721
|
-
if (Predicate.isNotUndefined(activeReasoningId)) {
|
|
722
|
-
parts.push({
|
|
723
|
-
type: "reasoning-end",
|
|
724
|
-
id: activeReasoningId
|
|
725
|
-
});
|
|
726
|
-
activeReasoningId = undefined;
|
|
727
|
-
}
|
|
728
|
-
// Flush remaining text parts
|
|
729
|
-
if (Predicate.isNotUndefined(activeTextId)) {
|
|
730
|
-
parts.push({
|
|
731
|
-
type: "text-end",
|
|
732
|
-
id: activeTextId
|
|
733
|
-
});
|
|
734
|
-
activeTextId = undefined;
|
|
735
|
-
}
|
|
736
|
-
parts.push({
|
|
737
|
-
type: "finish",
|
|
738
|
-
reason: finishReason,
|
|
739
|
-
usage: {
|
|
740
|
-
inputTokens: event.usage?.prompt_tokens,
|
|
741
|
-
outputTokens: event.usage?.completion_tokens,
|
|
742
|
-
totalTokens: event.usage?.total_tokens,
|
|
743
|
-
reasoningTokens: event.usage?.completion_tokens_details?.reasoning_tokens,
|
|
744
|
-
cachedInputTokens: event.usage?.prompt_tokens_details?.cached_tokens
|
|
745
|
-
},
|
|
746
|
-
metadata: {
|
|
747
|
-
openrouter: {
|
|
748
|
-
provider: event.provider,
|
|
749
|
-
usage: {
|
|
750
|
-
cost: event.usage?.cost,
|
|
751
|
-
promptTokensDetails: event.usage?.prompt_tokens_details,
|
|
752
|
-
completionTokensDetails: event.usage?.completion_tokens_details,
|
|
753
|
-
costDetails: event.usage?.cost_details
|
|
754
|
-
}
|
|
755
|
-
}
|
|
756
|
-
}
|
|
757
|
-
});
|
|
758
|
-
}
|
|
759
|
-
return parts;
|
|
760
|
-
})), Stream.flattenIterables);
|
|
761
|
-
});
|
|
762
|
-
// =============================================================================
|
|
763
|
-
// Telemetry
|
|
764
|
-
// =============================================================================
|
|
765
|
-
const annotateRequest = (span, request) => {
|
|
766
|
-
(0, _Telemetry.addGenAIAnnotations)(span, {
|
|
767
|
-
system: "openrouter",
|
|
768
|
-
operation: {
|
|
769
|
-
name: "chat"
|
|
770
|
-
},
|
|
771
|
-
request: {
|
|
772
|
-
model: request.model,
|
|
773
|
-
temperature: request.temperature,
|
|
774
|
-
topP: request.top_p,
|
|
775
|
-
maxTokens: request.max_tokens,
|
|
776
|
-
stopSequences: Arr.ensure(request.stop).filter(Predicate.isNotNullable)
|
|
777
|
-
}
|
|
778
|
-
});
|
|
779
|
-
};
|
|
780
|
-
const annotateResponse = (span, response) => {
|
|
781
|
-
(0, _Telemetry.addGenAIAnnotations)(span, {
|
|
782
|
-
response: {
|
|
783
|
-
id: response.id,
|
|
784
|
-
model: response.model,
|
|
785
|
-
finishReasons: response.choices.map(choice => choice.finish_reason).filter(Predicate.isNotNullable)
|
|
786
|
-
},
|
|
787
|
-
usage: {
|
|
788
|
-
inputTokens: response.usage?.prompt_tokens,
|
|
789
|
-
outputTokens: response.usage?.completion_tokens
|
|
790
|
-
}
|
|
791
|
-
});
|
|
792
|
-
};
|
|
793
|
-
const annotateStreamResponse = (span, part) => {
|
|
794
|
-
if (part.type === "response-metadata") {
|
|
795
|
-
(0, _Telemetry.addGenAIAnnotations)(span, {
|
|
796
|
-
response: {
|
|
797
|
-
id: part.id,
|
|
798
|
-
model: part.modelId
|
|
799
|
-
}
|
|
800
|
-
});
|
|
801
|
-
}
|
|
802
|
-
if (part.type === "finish") {
|
|
803
|
-
(0, _Telemetry.addGenAIAnnotations)(span, {
|
|
804
|
-
response: {
|
|
805
|
-
finishReasons: [part.reason]
|
|
806
|
-
},
|
|
807
|
-
usage: {
|
|
808
|
-
inputTokens: part.usage.inputTokens,
|
|
809
|
-
outputTokens: part.usage.outputTokens
|
|
810
|
-
}
|
|
811
|
-
});
|
|
812
|
-
}
|
|
813
|
-
};
|
|
814
|
-
// =============================================================================
|
|
815
|
-
// Utilities
|
|
816
|
-
// =============================================================================
|
|
817
|
-
const getCacheControl = part => part.options.openrouter?.cacheControl;
|
|
818
|
-
const getMediaType = dataUrl => {
|
|
819
|
-
const match = dataUrl.match(/^data:([^;]+)/);
|
|
820
|
-
return match ? match[1] : undefined;
|
|
821
|
-
};
|
|
822
|
-
const getBase64FromDataUrl = dataUrl => {
|
|
823
|
-
const match = dataUrl.match(/^data:[^;]*;base64,(.+)$/);
|
|
824
|
-
return match ? match[1] : dataUrl;
|
|
825
|
-
};
|
|
826
|
-
//# sourceMappingURL=OpenRouterLanguageModel.js.map
|