@discomedia/utils 1.0.25 → 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 +42 -12
- package/dist/index-frontend.cjs.map +1 -1
- package/dist/index-frontend.mjs +42 -13
- package/dist/index-frontend.mjs.map +1 -1
- package/dist/index.cjs +171 -22
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +171 -23
- package/dist/index.mjs.map +1 -1
- package/dist/package.json +2 -2
- package/dist/test.js +250 -63
- 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.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 +2 -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 +26 -3
- 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.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 +2 -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 +26 -3
- 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/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 = () => {
|
|
@@ -9163,7 +9185,8 @@ function fixBrokenJson(jsonStr) {
|
|
|
9163
9185
|
return parse();
|
|
9164
9186
|
}
|
|
9165
9187
|
catch (error) {
|
|
9166
|
-
|
|
9188
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
9189
|
+
console.error(`Error parsing JSON at position ${index}: ${msg}`);
|
|
9167
9190
|
return null;
|
|
9168
9191
|
}
|
|
9169
9192
|
}
|
|
@@ -9707,7 +9730,13 @@ async function makeImagesCall(prompt, options = {}) {
|
|
|
9707
9730
|
const enhancedResponse = {
|
|
9708
9731
|
...response,
|
|
9709
9732
|
usage: {
|
|
9710
|
-
|
|
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
|
+
}),
|
|
9711
9740
|
provider: 'openai',
|
|
9712
9741
|
model: 'gpt-image-1',
|
|
9713
9742
|
cost,
|
|
@@ -9716,7 +9745,8 @@ async function makeImagesCall(prompt, options = {}) {
|
|
|
9716
9745
|
return enhancedResponse;
|
|
9717
9746
|
}
|
|
9718
9747
|
catch (error) {
|
|
9719
|
-
|
|
9748
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
9749
|
+
throw new Error(`OpenAI Images API call failed: ${message}`);
|
|
9720
9750
|
}
|
|
9721
9751
|
}
|
|
9722
9752
|
|
|
@@ -9941,14 +9971,15 @@ const makeDeepseekCall = async (content, responseFormat = 'json', options = {})
|
|
|
9941
9971
|
const completion = await createDeepseekCompletion(content, responseFormat, mergedOptions);
|
|
9942
9972
|
// Handle tool calls similarly to OpenAI
|
|
9943
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
|
+
}));
|
|
9944
9981
|
return {
|
|
9945
|
-
response: {
|
|
9946
|
-
tool_calls: completion.tool_calls.map((tc) => ({
|
|
9947
|
-
id: tc.id,
|
|
9948
|
-
name: tc.function.name,
|
|
9949
|
-
arguments: JSON.parse(tc.function.arguments),
|
|
9950
|
-
})),
|
|
9951
|
-
},
|
|
9982
|
+
response: { tool_calls: fnCalls },
|
|
9952
9983
|
usage: {
|
|
9953
9984
|
prompt_tokens: completion.usage.prompt_tokens,
|
|
9954
9985
|
completion_tokens: completion.usage.completion_tokens,
|
|
@@ -10003,6 +10034,122 @@ const makeDeepseekCall = async (content, responseFormat = 'json', options = {})
|
|
|
10003
10034
|
}
|
|
10004
10035
|
};
|
|
10005
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
|
+
|
|
10006
10153
|
/**
|
|
10007
10154
|
* A class to measure performance of code execution.
|
|
10008
10155
|
*
|
|
@@ -18312,6 +18459,7 @@ const disco = {
|
|
|
18312
18459
|
call: makeLLMCall,
|
|
18313
18460
|
seek: makeDeepseekCall,
|
|
18314
18461
|
images: makeImagesCall,
|
|
18462
|
+
open: makeOpenRouterCall,
|
|
18315
18463
|
},
|
|
18316
18464
|
polygon: {
|
|
18317
18465
|
fetchTickerInfo: fetchTickerInfo,
|
|
@@ -18357,5 +18505,5 @@ const disco = {
|
|
|
18357
18505
|
},
|
|
18358
18506
|
};
|
|
18359
18507
|
|
|
18360
|
-
export { AlpacaMarketDataAPI, AlpacaTradingAPI, disco };
|
|
18508
|
+
export { AlpacaMarketDataAPI, AlpacaTradingAPI, disco, isOpenRouterModel };
|
|
18361
18509
|
//# sourceMappingURL=index.mjs.map
|