@discomedia/utils 1.0.24 → 1.0.26
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/index-frontend.cjs +194 -127
- package/dist/index-frontend.cjs.map +1 -1
- package/dist/index-frontend.mjs +194 -128
- package/dist/index-frontend.mjs.map +1 -1
- package/dist/index.cjs +323 -137
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +323 -138
- package/dist/index.mjs.map +1 -1
- package/dist/package.json +2 -2
- package/dist/test.js +5343 -1060
- package/dist/test.js.map +1 -1
- package/dist/types/index-frontend.d.ts +1 -1
- package/dist/types/index.d.ts +3 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/json-tools.d.ts +1 -3
- package/dist/types/json-tools.d.ts.map +1 -1
- package/dist/types/llm-config.d.ts.map +1 -1
- package/dist/types/llm-deepseek.d.ts +1 -1
- package/dist/types/llm-deepseek.d.ts.map +1 -1
- package/dist/types/llm-images.d.ts.map +1 -1
- package/dist/types/llm-openai.d.ts +8 -2
- package/dist/types/llm-openai.d.ts.map +1 -1
- package/dist/types/llm-openrouter.d.ts +28 -0
- package/dist/types/llm-openrouter.d.ts.map +1 -0
- package/dist/types/misc-utils.d.ts.map +1 -1
- package/dist/types/types/llm-types.d.ts +27 -4
- package/dist/types/types/llm-types.d.ts.map +1 -1
- package/dist/types/types/logging-types.d.ts +1 -1
- package/dist/types/types/logging-types.d.ts.map +1 -1
- package/dist/types-frontend/index-frontend.d.ts +1 -1
- package/dist/types-frontend/index.d.ts +3 -1
- package/dist/types-frontend/index.d.ts.map +1 -1
- package/dist/types-frontend/json-tools.d.ts +1 -3
- package/dist/types-frontend/json-tools.d.ts.map +1 -1
- package/dist/types-frontend/llm-config.d.ts.map +1 -1
- package/dist/types-frontend/llm-deepseek.d.ts +1 -1
- package/dist/types-frontend/llm-deepseek.d.ts.map +1 -1
- package/dist/types-frontend/llm-images.d.ts.map +1 -1
- package/dist/types-frontend/llm-openai.d.ts +8 -2
- package/dist/types-frontend/llm-openai.d.ts.map +1 -1
- package/dist/types-frontend/llm-openrouter.d.ts +28 -0
- package/dist/types-frontend/llm-openrouter.d.ts.map +1 -0
- package/dist/types-frontend/misc-utils.d.ts.map +1 -1
- package/dist/types-frontend/types/llm-types.d.ts +27 -4
- package/dist/types-frontend/types/llm-types.d.ts.map +1 -1
- package/dist/types-frontend/types/logging-types.d.ts +1 -1
- package/dist/types-frontend/types/logging-types.d.ts.map +1 -1
- package/package.json +2 -2
- package/dist/types/old-test.d.ts +0 -2
- package/dist/types/old-test.d.ts.map +0 -1
- package/dist/types-frontend/old-test.d.ts +0 -2
- package/dist/types-frontend/old-test.d.ts.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -1019,8 +1019,28 @@ function dateTimeForGS(date) {
|
|
|
1019
1019
|
.replace(/\./g, '/');
|
|
1020
1020
|
}
|
|
1021
1021
|
|
|
1022
|
+
/**
|
|
1023
|
+
* Type guard to check if a model is an OpenRouter model
|
|
1024
|
+
*/
|
|
1025
|
+
function isOpenRouterModel(model) {
|
|
1026
|
+
const openRouterModels = [
|
|
1027
|
+
'openai/gpt-5',
|
|
1028
|
+
'openai/gpt-5-mini',
|
|
1029
|
+
'openai/gpt-5-nano',
|
|
1030
|
+
'openai/gpt-oss-120b',
|
|
1031
|
+
'z.ai/glm-4.5',
|
|
1032
|
+
'z.ai/glm-4.5-air',
|
|
1033
|
+
'google/gemini-2.5-flash',
|
|
1034
|
+
'google/gemini-2.5-flash-lite',
|
|
1035
|
+
'deepseek/deepseek-r1-0528',
|
|
1036
|
+
'deepseek/deepseek-chat-v3-0324',
|
|
1037
|
+
];
|
|
1038
|
+
return openRouterModels.includes(model);
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1022
1041
|
var Types = /*#__PURE__*/Object.freeze({
|
|
1023
|
-
__proto__: null
|
|
1042
|
+
__proto__: null,
|
|
1043
|
+
isOpenRouterModel: isOpenRouterModel
|
|
1024
1044
|
});
|
|
1025
1045
|
|
|
1026
1046
|
// Utility function for debug logging
|
|
@@ -1112,29 +1132,31 @@ function hideApiKeyFromurl(url) {
|
|
|
1112
1132
|
* @returns Structured error details.
|
|
1113
1133
|
*/
|
|
1114
1134
|
function extractErrorDetails(error, response) {
|
|
1115
|
-
|
|
1135
|
+
const errMsg = error instanceof Error ? error.message : String(error);
|
|
1136
|
+
const errName = error instanceof Error ? error.name : 'Error';
|
|
1137
|
+
if (errName === 'TypeError' && errMsg.includes('fetch')) {
|
|
1116
1138
|
return { type: 'NETWORK_ERROR', reason: 'Network connectivity issue', status: null };
|
|
1117
1139
|
}
|
|
1118
|
-
if (
|
|
1119
|
-
const match =
|
|
1140
|
+
if (errMsg.includes('HTTP error: 429')) {
|
|
1141
|
+
const match = errMsg.match(/RATE_LIMIT: 429:(\d+)/);
|
|
1120
1142
|
const retryAfter = match ? parseInt(match[1]) : undefined;
|
|
1121
1143
|
return { type: 'RATE_LIMIT', reason: 'Rate limit exceeded', status: 429, retryAfter };
|
|
1122
1144
|
}
|
|
1123
|
-
if (
|
|
1145
|
+
if (errMsg.includes('HTTP error: 401') || errMsg.includes('AUTH_ERROR: 401')) {
|
|
1124
1146
|
return { type: 'AUTH_ERROR', reason: 'Authentication failed - invalid API key', status: 401 };
|
|
1125
1147
|
}
|
|
1126
|
-
if (
|
|
1148
|
+
if (errMsg.includes('HTTP error: 403') || errMsg.includes('AUTH_ERROR: 403')) {
|
|
1127
1149
|
return { type: 'AUTH_ERROR', reason: 'Access forbidden - insufficient permissions', status: 403 };
|
|
1128
1150
|
}
|
|
1129
|
-
if (
|
|
1130
|
-
const status = parseInt(
|
|
1151
|
+
if (errMsg.includes('SERVER_ERROR:')) {
|
|
1152
|
+
const status = parseInt(errMsg.split('SERVER_ERROR: ')[1]) || 500;
|
|
1131
1153
|
return { type: 'SERVER_ERROR', reason: `Server error (${status})`, status };
|
|
1132
1154
|
}
|
|
1133
|
-
if (
|
|
1134
|
-
const status = parseInt(
|
|
1155
|
+
if (errMsg.includes('CLIENT_ERROR:')) {
|
|
1156
|
+
const status = parseInt(errMsg.split('CLIENT_ERROR: ')[1]) || 400;
|
|
1135
1157
|
return { type: 'CLIENT_ERROR', reason: `Client error (${status})`, status };
|
|
1136
1158
|
}
|
|
1137
|
-
return { type: 'UNKNOWN', reason:
|
|
1159
|
+
return { type: 'UNKNOWN', reason: errMsg || 'Unknown error', status: null };
|
|
1138
1160
|
}
|
|
1139
1161
|
/**
|
|
1140
1162
|
* Fetches a resource with intelligent retry logic for handling transient errors.
|
|
@@ -2368,7 +2390,7 @@ const safeJSON = (text) => {
|
|
|
2368
2390
|
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
|
|
2369
2391
|
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
2370
2392
|
|
|
2371
|
-
const VERSION = '5.12.
|
|
2393
|
+
const VERSION = '5.12.2'; // x-release-please-version
|
|
2372
2394
|
|
|
2373
2395
|
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
|
|
2374
2396
|
const isRunningInBrowser = () => {
|
|
@@ -4001,8 +4023,119 @@ let Messages$1 = class Messages extends APIResource {
|
|
|
4001
4023
|
}
|
|
4002
4024
|
};
|
|
4003
4025
|
|
|
4004
|
-
function
|
|
4005
|
-
return
|
|
4026
|
+
function isChatCompletionFunctionTool(tool) {
|
|
4027
|
+
return tool !== undefined && 'function' in tool && tool.function !== undefined;
|
|
4028
|
+
}
|
|
4029
|
+
function isAutoParsableResponseFormat(response_format) {
|
|
4030
|
+
return response_format?.['$brand'] === 'auto-parseable-response-format';
|
|
4031
|
+
}
|
|
4032
|
+
function isAutoParsableTool$1(tool) {
|
|
4033
|
+
return tool?.['$brand'] === 'auto-parseable-tool';
|
|
4034
|
+
}
|
|
4035
|
+
function maybeParseChatCompletion(completion, params) {
|
|
4036
|
+
if (!params || !hasAutoParseableInput$1(params)) {
|
|
4037
|
+
return {
|
|
4038
|
+
...completion,
|
|
4039
|
+
choices: completion.choices.map((choice) => {
|
|
4040
|
+
assertToolCallsAreChatCompletionFunctionToolCalls(choice.message.tool_calls);
|
|
4041
|
+
return {
|
|
4042
|
+
...choice,
|
|
4043
|
+
message: {
|
|
4044
|
+
...choice.message,
|
|
4045
|
+
parsed: null,
|
|
4046
|
+
...(choice.message.tool_calls ?
|
|
4047
|
+
{
|
|
4048
|
+
tool_calls: choice.message.tool_calls,
|
|
4049
|
+
}
|
|
4050
|
+
: undefined),
|
|
4051
|
+
},
|
|
4052
|
+
};
|
|
4053
|
+
}),
|
|
4054
|
+
};
|
|
4055
|
+
}
|
|
4056
|
+
return parseChatCompletion(completion, params);
|
|
4057
|
+
}
|
|
4058
|
+
function parseChatCompletion(completion, params) {
|
|
4059
|
+
const choices = completion.choices.map((choice) => {
|
|
4060
|
+
if (choice.finish_reason === 'length') {
|
|
4061
|
+
throw new LengthFinishReasonError();
|
|
4062
|
+
}
|
|
4063
|
+
if (choice.finish_reason === 'content_filter') {
|
|
4064
|
+
throw new ContentFilterFinishReasonError();
|
|
4065
|
+
}
|
|
4066
|
+
assertToolCallsAreChatCompletionFunctionToolCalls(choice.message.tool_calls);
|
|
4067
|
+
return {
|
|
4068
|
+
...choice,
|
|
4069
|
+
message: {
|
|
4070
|
+
...choice.message,
|
|
4071
|
+
...(choice.message.tool_calls ?
|
|
4072
|
+
{
|
|
4073
|
+
tool_calls: choice.message.tool_calls?.map((toolCall) => parseToolCall$1(params, toolCall)) ?? undefined,
|
|
4074
|
+
}
|
|
4075
|
+
: undefined),
|
|
4076
|
+
parsed: choice.message.content && !choice.message.refusal ?
|
|
4077
|
+
parseResponseFormat(params, choice.message.content)
|
|
4078
|
+
: null,
|
|
4079
|
+
},
|
|
4080
|
+
};
|
|
4081
|
+
});
|
|
4082
|
+
return { ...completion, choices };
|
|
4083
|
+
}
|
|
4084
|
+
function parseResponseFormat(params, content) {
|
|
4085
|
+
if (params.response_format?.type !== 'json_schema') {
|
|
4086
|
+
return null;
|
|
4087
|
+
}
|
|
4088
|
+
if (params.response_format?.type === 'json_schema') {
|
|
4089
|
+
if ('$parseRaw' in params.response_format) {
|
|
4090
|
+
const response_format = params.response_format;
|
|
4091
|
+
return response_format.$parseRaw(content);
|
|
4092
|
+
}
|
|
4093
|
+
return JSON.parse(content);
|
|
4094
|
+
}
|
|
4095
|
+
return null;
|
|
4096
|
+
}
|
|
4097
|
+
function parseToolCall$1(params, toolCall) {
|
|
4098
|
+
const inputTool = params.tools?.find((inputTool) => isChatCompletionFunctionTool(inputTool) && inputTool.function?.name === toolCall.function.name); // TS doesn't narrow based on isChatCompletionTool
|
|
4099
|
+
return {
|
|
4100
|
+
...toolCall,
|
|
4101
|
+
function: {
|
|
4102
|
+
...toolCall.function,
|
|
4103
|
+
parsed_arguments: isAutoParsableTool$1(inputTool) ? inputTool.$parseRaw(toolCall.function.arguments)
|
|
4104
|
+
: inputTool?.function.strict ? JSON.parse(toolCall.function.arguments)
|
|
4105
|
+
: null,
|
|
4106
|
+
},
|
|
4107
|
+
};
|
|
4108
|
+
}
|
|
4109
|
+
function shouldParseToolCall(params, toolCall) {
|
|
4110
|
+
if (!params || !('tools' in params) || !params.tools) {
|
|
4111
|
+
return false;
|
|
4112
|
+
}
|
|
4113
|
+
const inputTool = params.tools?.find((inputTool) => isChatCompletionFunctionTool(inputTool) && inputTool.function?.name === toolCall.function.name);
|
|
4114
|
+
return (isChatCompletionFunctionTool(inputTool) &&
|
|
4115
|
+
(isAutoParsableTool$1(inputTool) || inputTool?.function.strict || false));
|
|
4116
|
+
}
|
|
4117
|
+
function hasAutoParseableInput$1(params) {
|
|
4118
|
+
if (isAutoParsableResponseFormat(params.response_format)) {
|
|
4119
|
+
return true;
|
|
4120
|
+
}
|
|
4121
|
+
return (params.tools?.some((t) => isAutoParsableTool$1(t) || (t.type === 'function' && t.function.strict === true)) ?? false);
|
|
4122
|
+
}
|
|
4123
|
+
function assertToolCallsAreChatCompletionFunctionToolCalls(toolCalls) {
|
|
4124
|
+
for (const toolCall of toolCalls || []) {
|
|
4125
|
+
if (toolCall.type !== 'function') {
|
|
4126
|
+
throw new OpenAIError(`Currently only \`function\` tool calls are supported; Received \`${toolCall.type}\``);
|
|
4127
|
+
}
|
|
4128
|
+
}
|
|
4129
|
+
}
|
|
4130
|
+
function validateInputTools(tools) {
|
|
4131
|
+
for (const tool of tools ?? []) {
|
|
4132
|
+
if (tool.type !== 'function') {
|
|
4133
|
+
throw new OpenAIError(`Currently only \`function\` tool types support auto-parsing; Received \`${tool.type}\``);
|
|
4134
|
+
}
|
|
4135
|
+
if (tool.function.strict !== true) {
|
|
4136
|
+
throw new OpenAIError(`The \`${tool.function.name}\` tool is not marked with \`strict: true\`. Only strict function tools can be auto-parsed`);
|
|
4137
|
+
}
|
|
4138
|
+
}
|
|
4006
4139
|
}
|
|
4007
4140
|
|
|
4008
4141
|
const isAssistantMessage = (message) => {
|
|
@@ -4196,104 +4329,8 @@ _EventStream_connectedPromise = new WeakMap(), _EventStream_resolveConnectedProm
|
|
|
4196
4329
|
return this._emit('error', new OpenAIError(String(error)));
|
|
4197
4330
|
};
|
|
4198
4331
|
|
|
4199
|
-
function
|
|
4200
|
-
return
|
|
4201
|
-
}
|
|
4202
|
-
function isAutoParsableTool$1(tool) {
|
|
4203
|
-
return tool?.['$brand'] === 'auto-parseable-tool';
|
|
4204
|
-
}
|
|
4205
|
-
function maybeParseChatCompletion(completion, params) {
|
|
4206
|
-
if (!params || !hasAutoParseableInput$1(params)) {
|
|
4207
|
-
return {
|
|
4208
|
-
...completion,
|
|
4209
|
-
choices: completion.choices.map((choice) => ({
|
|
4210
|
-
...choice,
|
|
4211
|
-
message: {
|
|
4212
|
-
...choice.message,
|
|
4213
|
-
parsed: null,
|
|
4214
|
-
...(choice.message.tool_calls ?
|
|
4215
|
-
{
|
|
4216
|
-
tool_calls: choice.message.tool_calls,
|
|
4217
|
-
}
|
|
4218
|
-
: undefined),
|
|
4219
|
-
},
|
|
4220
|
-
})),
|
|
4221
|
-
};
|
|
4222
|
-
}
|
|
4223
|
-
return parseChatCompletion(completion, params);
|
|
4224
|
-
}
|
|
4225
|
-
function parseChatCompletion(completion, params) {
|
|
4226
|
-
const choices = completion.choices.map((choice) => {
|
|
4227
|
-
if (choice.finish_reason === 'length') {
|
|
4228
|
-
throw new LengthFinishReasonError();
|
|
4229
|
-
}
|
|
4230
|
-
if (choice.finish_reason === 'content_filter') {
|
|
4231
|
-
throw new ContentFilterFinishReasonError();
|
|
4232
|
-
}
|
|
4233
|
-
return {
|
|
4234
|
-
...choice,
|
|
4235
|
-
message: {
|
|
4236
|
-
...choice.message,
|
|
4237
|
-
...(choice.message.tool_calls ?
|
|
4238
|
-
{
|
|
4239
|
-
tool_calls: choice.message.tool_calls?.map((toolCall) => parseToolCall$1(params, toolCall)) ?? undefined,
|
|
4240
|
-
}
|
|
4241
|
-
: undefined),
|
|
4242
|
-
parsed: choice.message.content && !choice.message.refusal ?
|
|
4243
|
-
parseResponseFormat(params, choice.message.content)
|
|
4244
|
-
: null,
|
|
4245
|
-
},
|
|
4246
|
-
};
|
|
4247
|
-
});
|
|
4248
|
-
return { ...completion, choices };
|
|
4249
|
-
}
|
|
4250
|
-
function parseResponseFormat(params, content) {
|
|
4251
|
-
if (params.response_format?.type !== 'json_schema') {
|
|
4252
|
-
return null;
|
|
4253
|
-
}
|
|
4254
|
-
if (params.response_format?.type === 'json_schema') {
|
|
4255
|
-
if ('$parseRaw' in params.response_format) {
|
|
4256
|
-
const response_format = params.response_format;
|
|
4257
|
-
return response_format.$parseRaw(content);
|
|
4258
|
-
}
|
|
4259
|
-
return JSON.parse(content);
|
|
4260
|
-
}
|
|
4261
|
-
return null;
|
|
4262
|
-
}
|
|
4263
|
-
function parseToolCall$1(params, toolCall) {
|
|
4264
|
-
const inputTool = params.tools?.find((inputTool) => inputTool.function?.name === toolCall.function.name);
|
|
4265
|
-
return {
|
|
4266
|
-
...toolCall,
|
|
4267
|
-
function: {
|
|
4268
|
-
...toolCall.function,
|
|
4269
|
-
parsed_arguments: isAutoParsableTool$1(inputTool) ? inputTool.$parseRaw(toolCall.function.arguments)
|
|
4270
|
-
: inputTool?.function.strict ? JSON.parse(toolCall.function.arguments)
|
|
4271
|
-
: null,
|
|
4272
|
-
},
|
|
4273
|
-
};
|
|
4274
|
-
}
|
|
4275
|
-
function shouldParseToolCall(params, toolCall) {
|
|
4276
|
-
if (!params) {
|
|
4277
|
-
return false;
|
|
4278
|
-
}
|
|
4279
|
-
const inputTool = params.tools?.find((inputTool) => inputTool.function?.name === toolCall.function.name);
|
|
4280
|
-
return isAutoParsableTool$1(inputTool) || inputTool?.function.strict || false;
|
|
4281
|
-
}
|
|
4282
|
-
function hasAutoParseableInput$1(params) {
|
|
4283
|
-
if (isAutoParsableResponseFormat(params.response_format)) {
|
|
4284
|
-
return true;
|
|
4285
|
-
}
|
|
4286
|
-
return (params.tools?.some((t) => isAutoParsableTool$1(t) || (t.type === 'function' && t.function.strict === true)) ?? false);
|
|
4287
|
-
}
|
|
4288
|
-
function validateInputTools(tools) {
|
|
4289
|
-
for (const tool of tools ?? []) {
|
|
4290
|
-
if (tool.type !== 'function') {
|
|
4291
|
-
throw new OpenAIError(`Currently only \`function\` tool types support auto-parsing; Received \`${tool.type}\``);
|
|
4292
|
-
}
|
|
4293
|
-
if (tool.function.strict !== true) {
|
|
4294
|
-
throw new OpenAIError(`The \`${tool.function.name}\` tool is not marked with \`strict: true\`. Only strict function tools can be auto-parsed`);
|
|
4295
|
-
}
|
|
4296
|
-
}
|
|
4332
|
+
function isRunnableFunctionWithParse(fn) {
|
|
4333
|
+
return typeof fn.parse === 'function';
|
|
4297
4334
|
}
|
|
4298
4335
|
|
|
4299
4336
|
var _AbstractChatCompletionRunner_instances, _AbstractChatCompletionRunner_getFinalContent, _AbstractChatCompletionRunner_getFinalMessage, _AbstractChatCompletionRunner_getFinalFunctionToolCall, _AbstractChatCompletionRunner_getFinalFunctionToolCallResult, _AbstractChatCompletionRunner_calculateTotalUsage, _AbstractChatCompletionRunner_validateParams, _AbstractChatCompletionRunner_stringifyFunctionCallResult;
|
|
@@ -4419,7 +4456,7 @@ class AbstractChatCompletionRunner extends EventStream {
|
|
|
4419
4456
|
async _runTools(client, params, options) {
|
|
4420
4457
|
const role = 'tool';
|
|
4421
4458
|
const { tool_choice = 'auto', stream, ...restParams } = params;
|
|
4422
|
-
const singleFunctionToCall = typeof tool_choice !== 'string' && tool_choice?.function?.name;
|
|
4459
|
+
const singleFunctionToCall = typeof tool_choice !== 'string' && tool_choice.type === 'function' && tool_choice?.function?.name;
|
|
4423
4460
|
const { maxChatCompletions = DEFAULT_MAX_CHAT_COMPLETIONS } = options || {};
|
|
4424
4461
|
// TODO(someday): clean this logic up
|
|
4425
4462
|
const inputTools = params.tools.map((tool) => {
|
|
@@ -4537,7 +4574,7 @@ _AbstractChatCompletionRunner_instances = new WeakSet(), _AbstractChatCompletion
|
|
|
4537
4574
|
for (let i = this.messages.length - 1; i >= 0; i--) {
|
|
4538
4575
|
const message = this.messages[i];
|
|
4539
4576
|
if (isAssistantMessage(message) && message?.tool_calls?.length) {
|
|
4540
|
-
return message.tool_calls.at(-1)?.function;
|
|
4577
|
+
return message.tool_calls.filter((x) => x.type === 'function').at(-1)?.function;
|
|
4541
4578
|
}
|
|
4542
4579
|
}
|
|
4543
4580
|
return;
|
|
@@ -5015,7 +5052,7 @@ class ChatCompletionStream extends AbstractChatCompletionRunner {
|
|
|
5015
5052
|
throw new Error('tool call snapshot missing `type`');
|
|
5016
5053
|
}
|
|
5017
5054
|
if (toolCallSnapshot.type === 'function') {
|
|
5018
|
-
const inputTool = __classPrivateFieldGet(this, _ChatCompletionStream_params, "f")?.tools?.find((tool) => tool
|
|
5055
|
+
const inputTool = __classPrivateFieldGet(this, _ChatCompletionStream_params, "f")?.tools?.find((tool) => isChatCompletionFunctionTool(tool) && tool.function.name === toolCallSnapshot.function.name); // TS doesn't narrow based on isChatCompletionTool
|
|
5019
5056
|
this._emit('tool_calls.function.arguments.done', {
|
|
5020
5057
|
name: toolCallSnapshot.function.name,
|
|
5021
5058
|
index: toolCallIndex,
|
|
@@ -8825,7 +8862,7 @@ OpenAI.Evals = Evals;
|
|
|
8825
8862
|
OpenAI.Containers = Containers;
|
|
8826
8863
|
|
|
8827
8864
|
// llm-openai-config.ts
|
|
8828
|
-
const DEFAULT_MODEL
|
|
8865
|
+
const DEFAULT_MODEL = 'gpt-4.1-mini';
|
|
8829
8866
|
/** Token costs in USD per 1M tokens. Last updated Feb 2025. */
|
|
8830
8867
|
const openAiModelCosts = {
|
|
8831
8868
|
'gpt-4o': {
|
|
@@ -8864,6 +8901,18 @@ const openAiModelCosts = {
|
|
|
8864
8901
|
inputCost: 0.1 / 1_000_000,
|
|
8865
8902
|
outputCost: 0.4 / 1_000_000,
|
|
8866
8903
|
},
|
|
8904
|
+
'gpt-5': {
|
|
8905
|
+
inputCost: 1.25 / 1_000_000,
|
|
8906
|
+
outputCost: 10 / 1_000_000,
|
|
8907
|
+
},
|
|
8908
|
+
'gpt-5-mini': {
|
|
8909
|
+
inputCost: 0.25 / 1_000_000,
|
|
8910
|
+
outputCost: 2 / 1_000_000,
|
|
8911
|
+
},
|
|
8912
|
+
'gpt-5-nano': {
|
|
8913
|
+
inputCost: 0.05 / 1_000_000,
|
|
8914
|
+
outputCost: 0.4 / 1_000_000,
|
|
8915
|
+
},
|
|
8867
8916
|
'o4-mini': {
|
|
8868
8917
|
inputCost: 1.1 / 1_000_000,
|
|
8869
8918
|
outputCost: 4.4 / 1_000_000,
|
|
@@ -8931,7 +8980,6 @@ function calculateCost(provider, model, inputTokens, outputTokens, reasoningToke
|
|
|
8931
8980
|
return inputCost + outputCost + reasoningCost;
|
|
8932
8981
|
}
|
|
8933
8982
|
|
|
8934
|
-
const DEFAULT_MODEL = 'gpt-4.1-mini';
|
|
8935
8983
|
/**
|
|
8936
8984
|
* Fix a broken JSON string by attempting to extract and parse valid JSON content. This function is very lenient and will attempt to fix many types of JSON errors, including unbalanced brackets, missing or extra commas, improperly escaped $ signs, unquoted strings, trailing commas, missing closing brackets or braces, etc.
|
|
8937
8985
|
* @param {string} jsonStr - The broken JSON string to fix
|
|
@@ -9137,7 +9185,8 @@ function fixBrokenJson(jsonStr) {
|
|
|
9137
9185
|
return parse();
|
|
9138
9186
|
}
|
|
9139
9187
|
catch (error) {
|
|
9140
|
-
|
|
9188
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
9189
|
+
console.error(`Error parsing JSON at position ${index}: ${msg}`);
|
|
9141
9190
|
return null;
|
|
9142
9191
|
}
|
|
9143
9192
|
}
|
|
@@ -9176,9 +9225,7 @@ function initializeOpenAI(apiKey) {
|
|
|
9176
9225
|
});
|
|
9177
9226
|
}
|
|
9178
9227
|
/**
|
|
9179
|
-
* Fixes broken JSON by sending it to
|
|
9180
|
-
* The GPT-4.1-mini model is a large language model that can understand and generate code,
|
|
9181
|
-
* including JSON. The returned JSON is the fixed version of the input JSON.
|
|
9228
|
+
* Fixes broken JSON by sending it to OpenAI to fix it.
|
|
9182
9229
|
* If the model fails to return valid JSON, an error is thrown.
|
|
9183
9230
|
* @param jsonStr - the broken JSON to fix
|
|
9184
9231
|
* @param apiKey - the OpenAI API key to use, or undefined to use the value of the OPENAI_API_KEY environment variable
|
|
@@ -9322,8 +9369,11 @@ const isSupportedModel = (model) => {
|
|
|
9322
9369
|
'o3-mini',
|
|
9323
9370
|
'gpt-4.1',
|
|
9324
9371
|
'gpt-4.1-mini',
|
|
9325
|
-
'o4-mini',
|
|
9326
9372
|
'gpt-4.1-nano',
|
|
9373
|
+
'gpt-5',
|
|
9374
|
+
'gpt-5-mini',
|
|
9375
|
+
'gpt-5-nano',
|
|
9376
|
+
'o4-mini',
|
|
9327
9377
|
'o3',
|
|
9328
9378
|
].includes(model);
|
|
9329
9379
|
};
|
|
@@ -9334,8 +9384,9 @@ const isSupportedModel = (model) => {
|
|
|
9334
9384
|
*/
|
|
9335
9385
|
function supportsTemperature(model) {
|
|
9336
9386
|
// Reasoning models don't support temperature
|
|
9337
|
-
|
|
9338
|
-
|
|
9387
|
+
// GPT-5 models also do not support temperature
|
|
9388
|
+
const reasoningAndGPT5Models = ['o1', 'o1-mini', 'o3-mini', 'o4-mini', 'o3', 'gpt-5', 'gpt-5-mini', 'gpt-5-nano'];
|
|
9389
|
+
return !reasoningAndGPT5Models.includes(model);
|
|
9339
9390
|
}
|
|
9340
9391
|
/**
|
|
9341
9392
|
* Checks if the given model is a reasoning model. Reasoning models have different tool choice constraints.
|
|
@@ -9346,6 +9397,15 @@ function isReasoningModel(model) {
|
|
|
9346
9397
|
const reasoningModels = ['o1', 'o1-mini', 'o3-mini', 'o4-mini', 'o3'];
|
|
9347
9398
|
return reasoningModels.includes(model);
|
|
9348
9399
|
}
|
|
9400
|
+
/**
|
|
9401
|
+
* Checks if the given model is a GPT-5 model. GPT-5 models don't support tool_choice other than 'auto'.
|
|
9402
|
+
* @param model The model to check.
|
|
9403
|
+
* @returns True if the model is a GPT-5 model, false otherwise.
|
|
9404
|
+
*/
|
|
9405
|
+
function isGPT5Model(model) {
|
|
9406
|
+
const gpt5Models = ['gpt-5', 'gpt-5-mini', 'gpt-5-nano'];
|
|
9407
|
+
return gpt5Models.includes(model);
|
|
9408
|
+
}
|
|
9349
9409
|
/**
|
|
9350
9410
|
* Makes a call to OpenAI's Responses API for more advanced use cases with built-in tools.
|
|
9351
9411
|
*
|
|
@@ -9373,7 +9433,7 @@ function isReasoningModel(model) {
|
|
|
9373
9433
|
* @throws Error if the API call fails
|
|
9374
9434
|
*/
|
|
9375
9435
|
const makeResponsesAPICall = async (input, options = {}) => {
|
|
9376
|
-
const normalizedModel = normalizeModelName(options.model || DEFAULT_MODEL
|
|
9436
|
+
const normalizedModel = normalizeModelName(options.model || DEFAULT_MODEL);
|
|
9377
9437
|
const apiKey = options.apiKey || process.env.OPENAI_API_KEY;
|
|
9378
9438
|
if (!apiKey) {
|
|
9379
9439
|
throw new Error('OpenAI API key is not provided and OPENAI_API_KEY environment variable is not set');
|
|
@@ -9484,7 +9544,7 @@ const makeResponsesAPICall = async (input, options = {}) => {
|
|
|
9484
9544
|
* });
|
|
9485
9545
|
*/
|
|
9486
9546
|
async function makeLLMCall(input, options = {}) {
|
|
9487
|
-
const { apiKey, model = DEFAULT_MODEL
|
|
9547
|
+
const { apiKey, model = DEFAULT_MODEL, responseFormat = 'text', tools, useCodeInterpreter = false, useWebSearch = false, imageBase64, imageDetail = 'high', context, } = options;
|
|
9488
9548
|
// Validate model
|
|
9489
9549
|
const normalizedModel = normalizeModelName(model);
|
|
9490
9550
|
if (!isSupportedModel(normalizedModel)) {
|
|
@@ -9576,8 +9636,8 @@ async function makeLLMCall(input, options = {}) {
|
|
|
9576
9636
|
}
|
|
9577
9637
|
if (useWebSearch) {
|
|
9578
9638
|
responsesOptions.tools = [{ type: 'web_search_preview' }];
|
|
9579
|
-
// For reasoning models, we can't force tool choice - they only support 'auto'
|
|
9580
|
-
if (!isReasoningModel(normalizedModel)) {
|
|
9639
|
+
// For reasoning models and GPT-5 models, we can't force tool choice - they only support 'auto'
|
|
9640
|
+
if (!isReasoningModel(normalizedModel) && !isGPT5Model(normalizedModel)) {
|
|
9581
9641
|
responsesOptions.tool_choice = { type: 'web_search_preview' };
|
|
9582
9642
|
}
|
|
9583
9643
|
}
|
|
@@ -9670,7 +9730,13 @@ async function makeImagesCall(prompt, options = {}) {
|
|
|
9670
9730
|
const enhancedResponse = {
|
|
9671
9731
|
...response,
|
|
9672
9732
|
usage: {
|
|
9673
|
-
|
|
9733
|
+
// OpenAI Images response may not include usage details per image; preserve if present
|
|
9734
|
+
...(response.usage ?? {
|
|
9735
|
+
input_tokens: 0,
|
|
9736
|
+
input_tokens_details: { image_tokens: 0, text_tokens: 0 },
|
|
9737
|
+
output_tokens: 0,
|
|
9738
|
+
total_tokens: 0,
|
|
9739
|
+
}),
|
|
9674
9740
|
provider: 'openai',
|
|
9675
9741
|
model: 'gpt-image-1',
|
|
9676
9742
|
cost,
|
|
@@ -9679,7 +9745,8 @@ async function makeImagesCall(prompt, options = {}) {
|
|
|
9679
9745
|
return enhancedResponse;
|
|
9680
9746
|
}
|
|
9681
9747
|
catch (error) {
|
|
9682
|
-
|
|
9748
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
9749
|
+
throw new Error(`OpenAI Images API call failed: ${message}`);
|
|
9683
9750
|
}
|
|
9684
9751
|
}
|
|
9685
9752
|
|
|
@@ -9904,14 +9971,15 @@ const makeDeepseekCall = async (content, responseFormat = 'json', options = {})
|
|
|
9904
9971
|
const completion = await createDeepseekCompletion(content, responseFormat, mergedOptions);
|
|
9905
9972
|
// Handle tool calls similarly to OpenAI
|
|
9906
9973
|
if (completion.tool_calls && completion.tool_calls.length > 0) {
|
|
9974
|
+
const fnCalls = completion.tool_calls
|
|
9975
|
+
.filter((tc) => tc.type === 'function')
|
|
9976
|
+
.map((tc) => ({
|
|
9977
|
+
id: tc.id,
|
|
9978
|
+
name: tc.function.name,
|
|
9979
|
+
arguments: JSON.parse(tc.function.arguments),
|
|
9980
|
+
}));
|
|
9907
9981
|
return {
|
|
9908
|
-
response: {
|
|
9909
|
-
tool_calls: completion.tool_calls.map((tc) => ({
|
|
9910
|
-
id: tc.id,
|
|
9911
|
-
name: tc.function.name,
|
|
9912
|
-
arguments: JSON.parse(tc.function.arguments),
|
|
9913
|
-
})),
|
|
9914
|
-
},
|
|
9982
|
+
response: { tool_calls: fnCalls },
|
|
9915
9983
|
usage: {
|
|
9916
9984
|
prompt_tokens: completion.usage.prompt_tokens,
|
|
9917
9985
|
completion_tokens: completion.usage.completion_tokens,
|
|
@@ -9966,6 +10034,122 @@ const makeDeepseekCall = async (content, responseFormat = 'json', options = {})
|
|
|
9966
10034
|
}
|
|
9967
10035
|
};
|
|
9968
10036
|
|
|
10037
|
+
// llm-openrouter.ts
|
|
10038
|
+
// Map our ContextMessage to OpenAI chat message
|
|
10039
|
+
function mapContextToMessages(context) {
|
|
10040
|
+
return context.map((msg) => {
|
|
10041
|
+
const role = msg.role === 'developer' ? 'system' : msg.role;
|
|
10042
|
+
return { role, content: msg.content };
|
|
10043
|
+
});
|
|
10044
|
+
}
|
|
10045
|
+
function toOpenRouterModel(model) {
|
|
10046
|
+
if (model && model.includes('/'))
|
|
10047
|
+
return model;
|
|
10048
|
+
const base = normalizeModelName(model || DEFAULT_MODEL);
|
|
10049
|
+
return `openai/${base}`;
|
|
10050
|
+
}
|
|
10051
|
+
// Normalize model name for pricing
|
|
10052
|
+
function normalizeModelForPricing(model) {
|
|
10053
|
+
if (!model)
|
|
10054
|
+
return { provider: 'openai', coreModel: normalizeModelName(DEFAULT_MODEL) };
|
|
10055
|
+
const [maybeProvider, maybeModel] = model.includes('/') ? model.split('/') : ['openai', model];
|
|
10056
|
+
const provider = (maybeProvider === 'deepseek' ? 'deepseek' : 'openai');
|
|
10057
|
+
const coreModel = normalizeModelName(maybeModel || model);
|
|
10058
|
+
return { provider, coreModel };
|
|
10059
|
+
}
|
|
10060
|
+
/**
|
|
10061
|
+
* Make a call through OpenRouter using the OpenAI Chat Completions-compatible API.
|
|
10062
|
+
* Supports: JSON mode, model selection, message history, and tools.
|
|
10063
|
+
*/
|
|
10064
|
+
async function makeOpenRouterCall(input, options = {}) {
|
|
10065
|
+
const { apiKey = process.env.OPENROUTER_API_KEY, model, responseFormat = 'text', tools, toolChoice, context, developerPrompt, temperature = 0.2, max_tokens, top_p, frequency_penalty, presence_penalty, stop, seed, referer = process.env.OPENROUTER_SITE_URL, title = process.env.OPENROUTER_SITE_NAME, } = options;
|
|
10066
|
+
if (!apiKey) {
|
|
10067
|
+
throw new Error('OpenRouter API key is not provided and OPENROUTER_API_KEY is not set');
|
|
10068
|
+
}
|
|
10069
|
+
const client = new OpenAI({
|
|
10070
|
+
apiKey,
|
|
10071
|
+
baseURL: 'https://openrouter.ai/api/v1',
|
|
10072
|
+
defaultHeaders: {
|
|
10073
|
+
...(referer ? { 'HTTP-Referer': referer } : {}),
|
|
10074
|
+
...(title ? { 'X-Title': title } : {}),
|
|
10075
|
+
},
|
|
10076
|
+
});
|
|
10077
|
+
const messages = [];
|
|
10078
|
+
if (developerPrompt && developerPrompt.trim()) {
|
|
10079
|
+
messages.push({ role: 'system', content: developerPrompt });
|
|
10080
|
+
}
|
|
10081
|
+
if (context && context.length > 0) {
|
|
10082
|
+
messages.push(...mapContextToMessages(context));
|
|
10083
|
+
}
|
|
10084
|
+
messages.push({ role: 'user', content: input });
|
|
10085
|
+
// Configure response_format
|
|
10086
|
+
let response_format;
|
|
10087
|
+
let parsingFormat = 'text';
|
|
10088
|
+
if (responseFormat === 'json') {
|
|
10089
|
+
response_format = { type: 'json_object' };
|
|
10090
|
+
parsingFormat = 'json';
|
|
10091
|
+
}
|
|
10092
|
+
else if (typeof responseFormat === 'object') {
|
|
10093
|
+
response_format = { type: 'json_object' };
|
|
10094
|
+
parsingFormat = responseFormat;
|
|
10095
|
+
}
|
|
10096
|
+
const modelId = toOpenRouterModel(model);
|
|
10097
|
+
const completion = await client.chat.completions.create({
|
|
10098
|
+
model: modelId,
|
|
10099
|
+
messages,
|
|
10100
|
+
response_format,
|
|
10101
|
+
tools,
|
|
10102
|
+
tool_choice: toolChoice,
|
|
10103
|
+
temperature,
|
|
10104
|
+
max_tokens,
|
|
10105
|
+
top_p,
|
|
10106
|
+
frequency_penalty,
|
|
10107
|
+
presence_penalty,
|
|
10108
|
+
stop,
|
|
10109
|
+
seed,
|
|
10110
|
+
});
|
|
10111
|
+
const choice = completion.choices && completion.choices.length > 0 ? completion.choices[0] : undefined;
|
|
10112
|
+
const message = (choice && 'message' in choice ? choice.message : undefined);
|
|
10113
|
+
const { provider: pricingProvider, coreModel } = normalizeModelForPricing(modelId);
|
|
10114
|
+
const promptTokens = completion.usage?.prompt_tokens ?? 0;
|
|
10115
|
+
const completionTokens = completion.usage?.completion_tokens ?? 0;
|
|
10116
|
+
const cost = calculateCost(pricingProvider, coreModel, promptTokens, completionTokens);
|
|
10117
|
+
// Tool calls branch: return empty string response and expose tool_calls on LLMResponse
|
|
10118
|
+
const hasToolCalls = Array.isArray(message?.tool_calls) && message.tool_calls.length > 0;
|
|
10119
|
+
if (hasToolCalls) {
|
|
10120
|
+
const usageModel = isOpenRouterModel(modelId) ? modelId : DEFAULT_MODEL;
|
|
10121
|
+
return {
|
|
10122
|
+
response: '',
|
|
10123
|
+
usage: {
|
|
10124
|
+
prompt_tokens: promptTokens,
|
|
10125
|
+
completion_tokens: completionTokens,
|
|
10126
|
+
provider: 'openrouter',
|
|
10127
|
+
model: usageModel,
|
|
10128
|
+
cost,
|
|
10129
|
+
},
|
|
10130
|
+
tool_calls: message.tool_calls,
|
|
10131
|
+
};
|
|
10132
|
+
}
|
|
10133
|
+
const rawText = typeof message?.content === 'string' ? message.content : '';
|
|
10134
|
+
const parsed = await parseResponse(rawText, parsingFormat);
|
|
10135
|
+
if (parsed === null) {
|
|
10136
|
+
throw new Error('Failed to parse OpenRouter response');
|
|
10137
|
+
}
|
|
10138
|
+
// Ensure the model value conforms to LLMModel; otherwise fall back to DEFAULT_MODEL
|
|
10139
|
+
const usageModel = isOpenRouterModel(modelId) ? modelId : DEFAULT_MODEL;
|
|
10140
|
+
return {
|
|
10141
|
+
response: parsed,
|
|
10142
|
+
usage: {
|
|
10143
|
+
prompt_tokens: promptTokens,
|
|
10144
|
+
completion_tokens: completionTokens,
|
|
10145
|
+
provider: 'openrouter',
|
|
10146
|
+
model: usageModel,
|
|
10147
|
+
cost,
|
|
10148
|
+
},
|
|
10149
|
+
...(hasToolCalls ? { tool_calls: message.tool_calls } : {}),
|
|
10150
|
+
};
|
|
10151
|
+
}
|
|
10152
|
+
|
|
9969
10153
|
/**
|
|
9970
10154
|
* A class to measure performance of code execution.
|
|
9971
10155
|
*
|
|
@@ -18275,6 +18459,7 @@ const disco = {
|
|
|
18275
18459
|
call: makeLLMCall,
|
|
18276
18460
|
seek: makeDeepseekCall,
|
|
18277
18461
|
images: makeImagesCall,
|
|
18462
|
+
open: makeOpenRouterCall,
|
|
18278
18463
|
},
|
|
18279
18464
|
polygon: {
|
|
18280
18465
|
fetchTickerInfo: fetchTickerInfo,
|
|
@@ -18320,5 +18505,5 @@ const disco = {
|
|
|
18320
18505
|
},
|
|
18321
18506
|
};
|
|
18322
18507
|
|
|
18323
|
-
export { AlpacaMarketDataAPI, AlpacaTradingAPI, disco };
|
|
18508
|
+
export { AlpacaMarketDataAPI, AlpacaTradingAPI, disco, isOpenRouterModel };
|
|
18324
18509
|
//# sourceMappingURL=index.mjs.map
|