@jaypie/llm 1.1.30-rc.0 → 1.1.30
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/LICENSE.txt +21 -0
- package/dist/cjs/constants.d.ts +17 -1
- package/dist/cjs/index.cjs +627 -49
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.ts +1 -0
- package/dist/cjs/operate/adapters/OpenRouterAdapter.d.ts +96 -0
- package/dist/cjs/operate/adapters/index.d.ts +1 -0
- package/dist/cjs/operate/index.d.ts +1 -1
- package/dist/cjs/providers/openrouter/OpenRouterProvider.class.d.ts +17 -0
- package/dist/cjs/providers/openrouter/index.d.ts +2 -0
- package/dist/cjs/providers/openrouter/utils.d.ts +49 -0
- package/dist/esm/constants.d.ts +17 -1
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.js +627 -50
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/operate/adapters/OpenRouterAdapter.d.ts +96 -0
- package/dist/esm/operate/adapters/index.d.ts +1 -0
- package/dist/esm/operate/index.d.ts +1 -1
- package/dist/esm/providers/openrouter/OpenRouterProvider.class.d.ts +17 -0
- package/dist/esm/providers/openrouter/index.d.ts +2 -0
- package/dist/esm/providers/openrouter/utils.d.ts +49 -0
- package/package.json +4 -2
- package/dist/cjs/providers/anthropic/operate.d.ts +0 -16
- package/dist/cjs/providers/openai/operate.d.ts +0 -26
- package/dist/esm/providers/anthropic/operate.d.ts +0 -16
- package/dist/esm/providers/openai/operate.d.ts +0 -26
package/dist/cjs/index.cjs
CHANGED
|
@@ -9,9 +9,30 @@ var openai = require('openai');
|
|
|
9
9
|
var zod = require('openai/helpers/zod');
|
|
10
10
|
var aws = require('@jaypie/aws');
|
|
11
11
|
var genai = require('@google/genai');
|
|
12
|
+
var sdk = require('@openrouter/sdk');
|
|
12
13
|
var openmeteo = require('openmeteo');
|
|
13
14
|
|
|
14
15
|
const PROVIDER = {
|
|
16
|
+
OPENROUTER: {
|
|
17
|
+
// https://openrouter.ai/models
|
|
18
|
+
// OpenRouter provides access to hundreds of models from various providers
|
|
19
|
+
// The model format is: provider/model-name (e.g., "openai/gpt-4", "anthropic/claude-3-opus")
|
|
20
|
+
MODEL: {
|
|
21
|
+
// Default uses env var OPENROUTER_MODEL if set, otherwise a reasonable default
|
|
22
|
+
DEFAULT: "openai/gpt-4o",
|
|
23
|
+
SMALL: "openai/gpt-4o-mini",
|
|
24
|
+
LARGE: "anthropic/claude-3-opus",
|
|
25
|
+
TINY: "openai/gpt-4o-mini",
|
|
26
|
+
},
|
|
27
|
+
MODEL_MATCH_WORDS: ["openrouter"],
|
|
28
|
+
NAME: "openrouter",
|
|
29
|
+
ROLE: {
|
|
30
|
+
ASSISTANT: "assistant",
|
|
31
|
+
SYSTEM: "system",
|
|
32
|
+
TOOL: "tool",
|
|
33
|
+
USER: "user",
|
|
34
|
+
},
|
|
35
|
+
},
|
|
15
36
|
GEMINI: {
|
|
16
37
|
// https://ai.google.dev/gemini-api/docs/models
|
|
17
38
|
MODEL: {
|
|
@@ -158,6 +179,12 @@ function determineModelProvider(input) {
|
|
|
158
179
|
provider: PROVIDER.OPENAI.NAME,
|
|
159
180
|
};
|
|
160
181
|
}
|
|
182
|
+
if (input === PROVIDER.OPENROUTER.NAME) {
|
|
183
|
+
return {
|
|
184
|
+
model: PROVIDER.OPENROUTER.MODEL.DEFAULT,
|
|
185
|
+
provider: PROVIDER.OPENROUTER.NAME,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
161
188
|
// Check if input matches an Anthropic model exactly
|
|
162
189
|
for (const [, modelValue] of Object.entries(PROVIDER.ANTHROPIC.MODEL)) {
|
|
163
190
|
if (input === modelValue) {
|
|
@@ -185,6 +212,15 @@ function determineModelProvider(input) {
|
|
|
185
212
|
};
|
|
186
213
|
}
|
|
187
214
|
}
|
|
215
|
+
// Check if input matches an OpenRouter model exactly
|
|
216
|
+
for (const [, modelValue] of Object.entries(PROVIDER.OPENROUTER.MODEL)) {
|
|
217
|
+
if (input === modelValue) {
|
|
218
|
+
return {
|
|
219
|
+
model: input,
|
|
220
|
+
provider: PROVIDER.OPENROUTER.NAME,
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
}
|
|
188
224
|
// Check Anthropic match words
|
|
189
225
|
const lowerInput = input.toLowerCase();
|
|
190
226
|
for (const matchWord of PROVIDER.ANTHROPIC.MODEL_MATCH_WORDS) {
|
|
@@ -223,6 +259,15 @@ function determineModelProvider(input) {
|
|
|
223
259
|
}
|
|
224
260
|
}
|
|
225
261
|
}
|
|
262
|
+
// Check OpenRouter match words
|
|
263
|
+
for (const matchWord of PROVIDER.OPENROUTER.MODEL_MATCH_WORDS) {
|
|
264
|
+
if (lowerInput.includes(matchWord)) {
|
|
265
|
+
return {
|
|
266
|
+
model: input,
|
|
267
|
+
provider: PROVIDER.OPENROUTER.NAME,
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
}
|
|
226
271
|
// Default fallback if model not recognized
|
|
227
272
|
return {
|
|
228
273
|
model: input,
|
|
@@ -342,8 +387,8 @@ function formatOperateInput(input, options) {
|
|
|
342
387
|
return [input];
|
|
343
388
|
}
|
|
344
389
|
|
|
345
|
-
const getLogger$
|
|
346
|
-
const log$1 = getLogger$
|
|
390
|
+
const getLogger$4 = () => core.log.lib({ lib: core.JAYPIE.LIB.LLM });
|
|
391
|
+
const log$1 = getLogger$4();
|
|
347
392
|
|
|
348
393
|
// Turn policy constants
|
|
349
394
|
const MAX_TURNS_ABSOLUTE_LIMIT = 72;
|
|
@@ -646,7 +691,7 @@ var ErrorCategory;
|
|
|
646
691
|
//
|
|
647
692
|
// Constants
|
|
648
693
|
//
|
|
649
|
-
const STRUCTURED_OUTPUT_TOOL_NAME$
|
|
694
|
+
const STRUCTURED_OUTPUT_TOOL_NAME$2 = "structured_output";
|
|
650
695
|
const RETRYABLE_ERROR_TYPES$1 = [
|
|
651
696
|
Anthropic.APIConnectionError,
|
|
652
697
|
Anthropic.APIConnectionTimeoutError,
|
|
@@ -711,7 +756,7 @@ class AnthropicAdapter extends BaseProviderAdapter {
|
|
|
711
756
|
type: "custom",
|
|
712
757
|
}));
|
|
713
758
|
// Determine tool choice based on whether structured output is requested
|
|
714
|
-
const hasStructuredOutput = request.tools.some((t) => t.name === STRUCTURED_OUTPUT_TOOL_NAME$
|
|
759
|
+
const hasStructuredOutput = request.tools.some((t) => t.name === STRUCTURED_OUTPUT_TOOL_NAME$2);
|
|
715
760
|
anthropicRequest.tool_choice = {
|
|
716
761
|
type: hasStructuredOutput ? "any" : "auto",
|
|
717
762
|
};
|
|
@@ -733,7 +778,7 @@ class AnthropicAdapter extends BaseProviderAdapter {
|
|
|
733
778
|
// Add structured output tool if schema is provided
|
|
734
779
|
if (outputSchema) {
|
|
735
780
|
tools.push({
|
|
736
|
-
name: STRUCTURED_OUTPUT_TOOL_NAME$
|
|
781
|
+
name: STRUCTURED_OUTPUT_TOOL_NAME$2,
|
|
737
782
|
description: "Output a structured JSON object, " +
|
|
738
783
|
"use this before your final response to give structured outputs to the user",
|
|
739
784
|
parameters: outputSchema,
|
|
@@ -912,13 +957,13 @@ class AnthropicAdapter extends BaseProviderAdapter {
|
|
|
912
957
|
// Check if the last content block is a tool_use with structured_output
|
|
913
958
|
const lastBlock = anthropicResponse.content[anthropicResponse.content.length - 1];
|
|
914
959
|
return (lastBlock?.type === "tool_use" &&
|
|
915
|
-
lastBlock.name === STRUCTURED_OUTPUT_TOOL_NAME$
|
|
960
|
+
lastBlock.name === STRUCTURED_OUTPUT_TOOL_NAME$2);
|
|
916
961
|
}
|
|
917
962
|
extractStructuredOutput(response) {
|
|
918
963
|
const anthropicResponse = response;
|
|
919
964
|
const lastBlock = anthropicResponse.content[anthropicResponse.content.length - 1];
|
|
920
965
|
if (lastBlock?.type === "tool_use" &&
|
|
921
|
-
lastBlock.name === STRUCTURED_OUTPUT_TOOL_NAME$
|
|
966
|
+
lastBlock.name === STRUCTURED_OUTPUT_TOOL_NAME$2) {
|
|
922
967
|
return lastBlock.input;
|
|
923
968
|
}
|
|
924
969
|
return undefined;
|
|
@@ -943,10 +988,10 @@ const anthropicAdapter = new AnthropicAdapter();
|
|
|
943
988
|
//
|
|
944
989
|
// Constants
|
|
945
990
|
//
|
|
946
|
-
const STRUCTURED_OUTPUT_TOOL_NAME = "structured_output";
|
|
991
|
+
const STRUCTURED_OUTPUT_TOOL_NAME$1 = "structured_output";
|
|
947
992
|
// Gemini uses HTTP status codes for error classification
|
|
948
993
|
// Documented at: https://ai.google.dev/api/rest/v1beta/Status
|
|
949
|
-
const RETRYABLE_STATUS_CODES = [
|
|
994
|
+
const RETRYABLE_STATUS_CODES$1 = [
|
|
950
995
|
408, // Request Timeout
|
|
951
996
|
429, // Too Many Requests (Rate Limit)
|
|
952
997
|
500, // Internal Server Error
|
|
@@ -1060,7 +1105,7 @@ class GeminiAdapter extends BaseProviderAdapter {
|
|
|
1060
1105
|
// Add structured output tool if schema is provided
|
|
1061
1106
|
if (outputSchema) {
|
|
1062
1107
|
tools.push({
|
|
1063
|
-
name: STRUCTURED_OUTPUT_TOOL_NAME,
|
|
1108
|
+
name: STRUCTURED_OUTPUT_TOOL_NAME$1,
|
|
1064
1109
|
description: "Output a structured JSON object, " +
|
|
1065
1110
|
"use this before your final response to give structured outputs to the user",
|
|
1066
1111
|
parameters: outputSchema,
|
|
@@ -1261,7 +1306,7 @@ class GeminiAdapter extends BaseProviderAdapter {
|
|
|
1261
1306
|
}
|
|
1262
1307
|
// Check for retryable errors
|
|
1263
1308
|
if (typeof statusCode === "number" &&
|
|
1264
|
-
RETRYABLE_STATUS_CODES.includes(statusCode)) {
|
|
1309
|
+
RETRYABLE_STATUS_CODES$1.includes(statusCode)) {
|
|
1265
1310
|
return {
|
|
1266
1311
|
error,
|
|
1267
1312
|
category: ErrorCategory.Retryable,
|
|
@@ -1325,7 +1370,7 @@ class GeminiAdapter extends BaseProviderAdapter {
|
|
|
1325
1370
|
}
|
|
1326
1371
|
// Check if the last part is a function call with structured_output
|
|
1327
1372
|
const lastPart = candidate.content.parts[candidate.content.parts.length - 1];
|
|
1328
|
-
return lastPart?.functionCall?.name === STRUCTURED_OUTPUT_TOOL_NAME;
|
|
1373
|
+
return lastPart?.functionCall?.name === STRUCTURED_OUTPUT_TOOL_NAME$1;
|
|
1329
1374
|
}
|
|
1330
1375
|
extractStructuredOutput(response) {
|
|
1331
1376
|
const geminiResponse = response;
|
|
@@ -1334,7 +1379,7 @@ class GeminiAdapter extends BaseProviderAdapter {
|
|
|
1334
1379
|
return undefined;
|
|
1335
1380
|
}
|
|
1336
1381
|
const lastPart = candidate.content.parts[candidate.content.parts.length - 1];
|
|
1337
|
-
if (lastPart?.functionCall?.name === STRUCTURED_OUTPUT_TOOL_NAME) {
|
|
1382
|
+
if (lastPart?.functionCall?.name === STRUCTURED_OUTPUT_TOOL_NAME$1) {
|
|
1338
1383
|
return lastPart.functionCall.args;
|
|
1339
1384
|
}
|
|
1340
1385
|
return undefined;
|
|
@@ -1764,6 +1809,409 @@ class OpenAiAdapter extends BaseProviderAdapter {
|
|
|
1764
1809
|
// Export singleton instance
|
|
1765
1810
|
const openAiAdapter = new OpenAiAdapter();
|
|
1766
1811
|
|
|
1812
|
+
//
|
|
1813
|
+
//
|
|
1814
|
+
// Constants
|
|
1815
|
+
//
|
|
1816
|
+
const STRUCTURED_OUTPUT_TOOL_NAME = "structured_output";
|
|
1817
|
+
// OpenRouter SDK error types based on HTTP status codes
|
|
1818
|
+
const RETRYABLE_STATUS_CODES = [408, 500, 502, 503, 524, 529];
|
|
1819
|
+
const RATE_LIMIT_STATUS_CODE = 429;
|
|
1820
|
+
//
|
|
1821
|
+
//
|
|
1822
|
+
// Main
|
|
1823
|
+
//
|
|
1824
|
+
/**
|
|
1825
|
+
* OpenRouterAdapter implements the ProviderAdapter interface for OpenRouter's API.
|
|
1826
|
+
* OpenRouter provides a unified API to access hundreds of AI models through OpenAI-compatible endpoints.
|
|
1827
|
+
* It handles request building, response parsing, and error classification
|
|
1828
|
+
* specific to OpenRouter's Chat Completions API.
|
|
1829
|
+
*/
|
|
1830
|
+
class OpenRouterAdapter extends BaseProviderAdapter {
|
|
1831
|
+
constructor() {
|
|
1832
|
+
super(...arguments);
|
|
1833
|
+
this.name = PROVIDER.OPENROUTER.NAME;
|
|
1834
|
+
this.defaultModel = PROVIDER.OPENROUTER.MODEL.DEFAULT;
|
|
1835
|
+
}
|
|
1836
|
+
//
|
|
1837
|
+
// Request Building
|
|
1838
|
+
//
|
|
1839
|
+
buildRequest(request) {
|
|
1840
|
+
// Convert messages to OpenRouter format (OpenAI-compatible)
|
|
1841
|
+
const messages = this.convertMessagesToOpenRouter(request.messages, request.system);
|
|
1842
|
+
// Append instructions to last message if provided
|
|
1843
|
+
if (request.instructions && messages.length > 0) {
|
|
1844
|
+
const lastMsg = messages[messages.length - 1];
|
|
1845
|
+
if (lastMsg.content && typeof lastMsg.content === "string") {
|
|
1846
|
+
lastMsg.content = lastMsg.content + "\n\n" + request.instructions;
|
|
1847
|
+
}
|
|
1848
|
+
}
|
|
1849
|
+
const openRouterRequest = {
|
|
1850
|
+
model: request.model || this.defaultModel,
|
|
1851
|
+
messages,
|
|
1852
|
+
};
|
|
1853
|
+
if (request.user) {
|
|
1854
|
+
openRouterRequest.user = request.user;
|
|
1855
|
+
}
|
|
1856
|
+
if (request.tools && request.tools.length > 0) {
|
|
1857
|
+
openRouterRequest.tools = request.tools.map((tool) => ({
|
|
1858
|
+
type: "function",
|
|
1859
|
+
function: {
|
|
1860
|
+
name: tool.name,
|
|
1861
|
+
description: tool.description,
|
|
1862
|
+
parameters: tool.parameters,
|
|
1863
|
+
},
|
|
1864
|
+
}));
|
|
1865
|
+
// Determine tool choice based on whether structured output is requested
|
|
1866
|
+
const hasStructuredOutput = request.tools.some((t) => t.name === STRUCTURED_OUTPUT_TOOL_NAME);
|
|
1867
|
+
openRouterRequest.tool_choice = hasStructuredOutput ? "required" : "auto";
|
|
1868
|
+
}
|
|
1869
|
+
if (request.providerOptions) {
|
|
1870
|
+
Object.assign(openRouterRequest, request.providerOptions);
|
|
1871
|
+
}
|
|
1872
|
+
return openRouterRequest;
|
|
1873
|
+
}
|
|
1874
|
+
formatTools(toolkit, outputSchema) {
|
|
1875
|
+
const tools = toolkit.tools.map((tool) => ({
|
|
1876
|
+
name: tool.name,
|
|
1877
|
+
description: tool.description,
|
|
1878
|
+
parameters: tool.parameters,
|
|
1879
|
+
}));
|
|
1880
|
+
// Add structured output tool if schema is provided
|
|
1881
|
+
// (OpenRouter doesn't have native structured output like OpenAI, so use tool approach)
|
|
1882
|
+
if (outputSchema) {
|
|
1883
|
+
tools.push({
|
|
1884
|
+
name: STRUCTURED_OUTPUT_TOOL_NAME,
|
|
1885
|
+
description: "REQUIRED: You MUST call this tool to provide your final response. " +
|
|
1886
|
+
"After gathering all necessary information (including results from other tools), " +
|
|
1887
|
+
"call this tool with the structured data to complete the request.",
|
|
1888
|
+
parameters: outputSchema,
|
|
1889
|
+
});
|
|
1890
|
+
}
|
|
1891
|
+
return tools;
|
|
1892
|
+
}
|
|
1893
|
+
formatOutputSchema(schema) {
|
|
1894
|
+
let jsonSchema;
|
|
1895
|
+
// Check if schema is already a JsonObject with type "json_schema"
|
|
1896
|
+
if (typeof schema === "object" &&
|
|
1897
|
+
schema !== null &&
|
|
1898
|
+
!Array.isArray(schema) &&
|
|
1899
|
+
schema.type === "json_schema") {
|
|
1900
|
+
jsonSchema = structuredClone(schema);
|
|
1901
|
+
jsonSchema.type = "object"; // Normalize type
|
|
1902
|
+
}
|
|
1903
|
+
else {
|
|
1904
|
+
// Convert NaturalSchema to JSON schema through Zod
|
|
1905
|
+
const zodSchema = schema instanceof v4.z.ZodType
|
|
1906
|
+
? schema
|
|
1907
|
+
: naturalZodSchema(schema);
|
|
1908
|
+
jsonSchema = v4.z.toJSONSchema(zodSchema);
|
|
1909
|
+
}
|
|
1910
|
+
// Remove $schema property (can cause issues with some providers)
|
|
1911
|
+
if (jsonSchema.$schema) {
|
|
1912
|
+
delete jsonSchema.$schema;
|
|
1913
|
+
}
|
|
1914
|
+
return jsonSchema;
|
|
1915
|
+
}
|
|
1916
|
+
//
|
|
1917
|
+
// API Execution
|
|
1918
|
+
//
|
|
1919
|
+
async executeRequest(client, request) {
|
|
1920
|
+
const openRouter = client;
|
|
1921
|
+
const openRouterRequest = request;
|
|
1922
|
+
const response = await openRouter.chat.send({
|
|
1923
|
+
model: openRouterRequest.model,
|
|
1924
|
+
messages: openRouterRequest.messages,
|
|
1925
|
+
tools: openRouterRequest.tools,
|
|
1926
|
+
toolChoice: openRouterRequest.tool_choice,
|
|
1927
|
+
user: openRouterRequest.user,
|
|
1928
|
+
});
|
|
1929
|
+
return response;
|
|
1930
|
+
}
|
|
1931
|
+
//
|
|
1932
|
+
// Response Parsing
|
|
1933
|
+
//
|
|
1934
|
+
parseResponse(response, _options) {
|
|
1935
|
+
const openRouterResponse = response;
|
|
1936
|
+
const choice = openRouterResponse.choices[0];
|
|
1937
|
+
const content = this.extractContent(openRouterResponse);
|
|
1938
|
+
const hasToolCalls = this.hasToolCalls(openRouterResponse);
|
|
1939
|
+
// SDK returns camelCase (finishReason), but support snake_case as fallback
|
|
1940
|
+
const stopReason = choice?.finishReason ?? choice?.finish_reason ?? undefined;
|
|
1941
|
+
return {
|
|
1942
|
+
content,
|
|
1943
|
+
hasToolCalls,
|
|
1944
|
+
stopReason,
|
|
1945
|
+
usage: this.extractUsage(openRouterResponse, openRouterResponse.model),
|
|
1946
|
+
raw: openRouterResponse,
|
|
1947
|
+
};
|
|
1948
|
+
}
|
|
1949
|
+
extractToolCalls(response) {
|
|
1950
|
+
const openRouterResponse = response;
|
|
1951
|
+
const toolCalls = [];
|
|
1952
|
+
const choice = openRouterResponse.choices[0];
|
|
1953
|
+
// SDK returns camelCase (toolCalls)
|
|
1954
|
+
if (!choice?.message?.toolCalls) {
|
|
1955
|
+
return toolCalls;
|
|
1956
|
+
}
|
|
1957
|
+
for (const toolCall of choice.message.toolCalls) {
|
|
1958
|
+
toolCalls.push({
|
|
1959
|
+
callId: toolCall.id,
|
|
1960
|
+
name: toolCall.function.name,
|
|
1961
|
+
arguments: toolCall.function.arguments,
|
|
1962
|
+
raw: toolCall,
|
|
1963
|
+
});
|
|
1964
|
+
}
|
|
1965
|
+
return toolCalls;
|
|
1966
|
+
}
|
|
1967
|
+
extractUsage(response, model) {
|
|
1968
|
+
const openRouterResponse = response;
|
|
1969
|
+
if (!openRouterResponse.usage) {
|
|
1970
|
+
return {
|
|
1971
|
+
input: 0,
|
|
1972
|
+
output: 0,
|
|
1973
|
+
reasoning: 0,
|
|
1974
|
+
total: 0,
|
|
1975
|
+
provider: this.name,
|
|
1976
|
+
model,
|
|
1977
|
+
};
|
|
1978
|
+
}
|
|
1979
|
+
// SDK returns camelCase, but support snake_case as fallback
|
|
1980
|
+
const usage = openRouterResponse.usage;
|
|
1981
|
+
return {
|
|
1982
|
+
input: usage.promptTokens || usage.prompt_tokens || 0,
|
|
1983
|
+
output: usage.completionTokens || usage.completion_tokens || 0,
|
|
1984
|
+
reasoning: 0, // OpenRouter doesn't expose reasoning tokens in standard format
|
|
1985
|
+
total: usage.totalTokens || usage.total_tokens || 0,
|
|
1986
|
+
provider: this.name,
|
|
1987
|
+
model,
|
|
1988
|
+
};
|
|
1989
|
+
}
|
|
1990
|
+
//
|
|
1991
|
+
// Tool Result Handling
|
|
1992
|
+
//
|
|
1993
|
+
formatToolResult(toolCall, result) {
|
|
1994
|
+
return {
|
|
1995
|
+
role: "tool",
|
|
1996
|
+
toolCallId: toolCall.callId,
|
|
1997
|
+
content: result.output,
|
|
1998
|
+
};
|
|
1999
|
+
}
|
|
2000
|
+
appendToolResult(request, toolCall, result) {
|
|
2001
|
+
const openRouterRequest = request;
|
|
2002
|
+
const toolCallRaw = toolCall.raw;
|
|
2003
|
+
// Add assistant message with the tool call (SDK uses camelCase)
|
|
2004
|
+
openRouterRequest.messages.push({
|
|
2005
|
+
role: "assistant",
|
|
2006
|
+
content: null,
|
|
2007
|
+
toolCalls: [toolCallRaw],
|
|
2008
|
+
});
|
|
2009
|
+
// Add tool result message
|
|
2010
|
+
openRouterRequest.messages.push(this.formatToolResult(toolCall, result));
|
|
2011
|
+
return openRouterRequest;
|
|
2012
|
+
}
|
|
2013
|
+
//
|
|
2014
|
+
// History Management
|
|
2015
|
+
//
|
|
2016
|
+
responseToHistoryItems(response) {
|
|
2017
|
+
const openRouterResponse = response;
|
|
2018
|
+
const historyItems = [];
|
|
2019
|
+
const choice = openRouterResponse.choices[0];
|
|
2020
|
+
if (!choice?.message) {
|
|
2021
|
+
return historyItems;
|
|
2022
|
+
}
|
|
2023
|
+
// Check if this is a tool use response (SDK returns camelCase)
|
|
2024
|
+
if (choice.message.toolCalls && choice.message.toolCalls.length > 0) {
|
|
2025
|
+
// Don't add to history yet - will be added after tool execution
|
|
2026
|
+
return historyItems;
|
|
2027
|
+
}
|
|
2028
|
+
// Extract text content for non-tool responses
|
|
2029
|
+
if (choice.message.content) {
|
|
2030
|
+
historyItems.push({
|
|
2031
|
+
content: choice.message.content,
|
|
2032
|
+
role: exports.LlmMessageRole.Assistant,
|
|
2033
|
+
type: exports.LlmMessageType.Message,
|
|
2034
|
+
});
|
|
2035
|
+
}
|
|
2036
|
+
return historyItems;
|
|
2037
|
+
}
|
|
2038
|
+
//
|
|
2039
|
+
// Error Classification
|
|
2040
|
+
//
|
|
2041
|
+
classifyError(error) {
|
|
2042
|
+
// Check if error has a status code (HTTP error)
|
|
2043
|
+
const errorWithStatus = error;
|
|
2044
|
+
const statusCode = errorWithStatus.status || errorWithStatus.statusCode;
|
|
2045
|
+
if (statusCode) {
|
|
2046
|
+
// Rate limit error
|
|
2047
|
+
if (statusCode === RATE_LIMIT_STATUS_CODE) {
|
|
2048
|
+
return {
|
|
2049
|
+
error,
|
|
2050
|
+
category: ErrorCategory.RateLimit,
|
|
2051
|
+
shouldRetry: false,
|
|
2052
|
+
suggestedDelayMs: 60000,
|
|
2053
|
+
};
|
|
2054
|
+
}
|
|
2055
|
+
// Retryable errors (server errors, timeouts, etc.)
|
|
2056
|
+
if (RETRYABLE_STATUS_CODES.includes(statusCode)) {
|
|
2057
|
+
return {
|
|
2058
|
+
error,
|
|
2059
|
+
category: ErrorCategory.Retryable,
|
|
2060
|
+
shouldRetry: true,
|
|
2061
|
+
};
|
|
2062
|
+
}
|
|
2063
|
+
// Client errors (4xx except 429) are unrecoverable
|
|
2064
|
+
if (statusCode >= 400 && statusCode < 500) {
|
|
2065
|
+
return {
|
|
2066
|
+
error,
|
|
2067
|
+
category: ErrorCategory.Unrecoverable,
|
|
2068
|
+
shouldRetry: false,
|
|
2069
|
+
};
|
|
2070
|
+
}
|
|
2071
|
+
}
|
|
2072
|
+
// Check error message for rate limit indicators
|
|
2073
|
+
const errorMessage = error instanceof Error ? error.message.toLowerCase() : "";
|
|
2074
|
+
if (errorMessage.includes("rate limit") ||
|
|
2075
|
+
errorMessage.includes("too many requests")) {
|
|
2076
|
+
return {
|
|
2077
|
+
error,
|
|
2078
|
+
category: ErrorCategory.RateLimit,
|
|
2079
|
+
shouldRetry: false,
|
|
2080
|
+
suggestedDelayMs: 60000,
|
|
2081
|
+
};
|
|
2082
|
+
}
|
|
2083
|
+
// Unknown error - treat as potentially retryable
|
|
2084
|
+
return {
|
|
2085
|
+
error,
|
|
2086
|
+
category: ErrorCategory.Unknown,
|
|
2087
|
+
shouldRetry: true,
|
|
2088
|
+
};
|
|
2089
|
+
}
|
|
2090
|
+
//
|
|
2091
|
+
// Provider-Specific Features
|
|
2092
|
+
//
|
|
2093
|
+
isComplete(response) {
|
|
2094
|
+
const openRouterResponse = response;
|
|
2095
|
+
const choice = openRouterResponse.choices[0];
|
|
2096
|
+
// Complete if no tool calls (SDK returns camelCase)
|
|
2097
|
+
if (!choice?.message?.toolCalls?.length) {
|
|
2098
|
+
return true;
|
|
2099
|
+
}
|
|
2100
|
+
return false;
|
|
2101
|
+
}
|
|
2102
|
+
hasStructuredOutput(response) {
|
|
2103
|
+
const openRouterResponse = response;
|
|
2104
|
+
const choice = openRouterResponse.choices[0];
|
|
2105
|
+
// SDK returns camelCase (toolCalls)
|
|
2106
|
+
if (!choice?.message?.toolCalls?.length) {
|
|
2107
|
+
return false;
|
|
2108
|
+
}
|
|
2109
|
+
// Check if the last tool call is structured_output
|
|
2110
|
+
const lastToolCall = choice.message.toolCalls[choice.message.toolCalls.length - 1];
|
|
2111
|
+
return lastToolCall?.function?.name === STRUCTURED_OUTPUT_TOOL_NAME;
|
|
2112
|
+
}
|
|
2113
|
+
extractStructuredOutput(response) {
|
|
2114
|
+
const openRouterResponse = response;
|
|
2115
|
+
const choice = openRouterResponse.choices[0];
|
|
2116
|
+
// SDK returns camelCase (toolCalls)
|
|
2117
|
+
if (!choice?.message?.toolCalls?.length) {
|
|
2118
|
+
return undefined;
|
|
2119
|
+
}
|
|
2120
|
+
const lastToolCall = choice.message.toolCalls[choice.message.toolCalls.length - 1];
|
|
2121
|
+
if (lastToolCall?.function?.name === STRUCTURED_OUTPUT_TOOL_NAME) {
|
|
2122
|
+
try {
|
|
2123
|
+
return JSON.parse(lastToolCall.function.arguments);
|
|
2124
|
+
}
|
|
2125
|
+
catch {
|
|
2126
|
+
return undefined;
|
|
2127
|
+
}
|
|
2128
|
+
}
|
|
2129
|
+
return undefined;
|
|
2130
|
+
}
|
|
2131
|
+
//
|
|
2132
|
+
// Private Helpers
|
|
2133
|
+
//
|
|
2134
|
+
hasToolCalls(response) {
|
|
2135
|
+
const choice = response.choices[0];
|
|
2136
|
+
// SDK returns camelCase (toolCalls)
|
|
2137
|
+
return (choice?.message?.toolCalls?.length ?? 0) > 0;
|
|
2138
|
+
}
|
|
2139
|
+
extractContent(response) {
|
|
2140
|
+
// Check for structured output first
|
|
2141
|
+
if (this.hasStructuredOutput(response)) {
|
|
2142
|
+
return this.extractStructuredOutput(response);
|
|
2143
|
+
}
|
|
2144
|
+
const choice = response.choices[0];
|
|
2145
|
+
return choice?.message?.content ?? undefined;
|
|
2146
|
+
}
|
|
2147
|
+
convertMessagesToOpenRouter(messages, system) {
|
|
2148
|
+
const openRouterMessages = [];
|
|
2149
|
+
// Add system message if provided
|
|
2150
|
+
if (system) {
|
|
2151
|
+
openRouterMessages.push({
|
|
2152
|
+
role: "system",
|
|
2153
|
+
content: system,
|
|
2154
|
+
});
|
|
2155
|
+
}
|
|
2156
|
+
for (const msg of messages) {
|
|
2157
|
+
const message = msg;
|
|
2158
|
+
// Handle different message types
|
|
2159
|
+
if (message.role === "system") {
|
|
2160
|
+
openRouterMessages.push({
|
|
2161
|
+
role: "system",
|
|
2162
|
+
content: message.content,
|
|
2163
|
+
});
|
|
2164
|
+
}
|
|
2165
|
+
else if (message.role === "user") {
|
|
2166
|
+
openRouterMessages.push({
|
|
2167
|
+
role: "user",
|
|
2168
|
+
content: message.content,
|
|
2169
|
+
});
|
|
2170
|
+
}
|
|
2171
|
+
else if (message.role === "assistant") {
|
|
2172
|
+
const assistantMsg = {
|
|
2173
|
+
role: "assistant",
|
|
2174
|
+
content: message.content || null,
|
|
2175
|
+
};
|
|
2176
|
+
// Include toolCalls if present (check both camelCase and snake_case for compatibility)
|
|
2177
|
+
if (message.toolCalls) {
|
|
2178
|
+
assistantMsg.toolCalls = message.toolCalls;
|
|
2179
|
+
}
|
|
2180
|
+
else if (message.tool_calls) {
|
|
2181
|
+
assistantMsg.toolCalls = message.tool_calls;
|
|
2182
|
+
}
|
|
2183
|
+
openRouterMessages.push(assistantMsg);
|
|
2184
|
+
}
|
|
2185
|
+
else if (message.role === "tool") {
|
|
2186
|
+
openRouterMessages.push({
|
|
2187
|
+
role: "tool",
|
|
2188
|
+
toolCallId: message.toolCallId || message.tool_call_id,
|
|
2189
|
+
content: message.content,
|
|
2190
|
+
});
|
|
2191
|
+
}
|
|
2192
|
+
else if (message.type === exports.LlmMessageType.Message) {
|
|
2193
|
+
// Handle internal message format
|
|
2194
|
+
const role = message.role?.toLowerCase();
|
|
2195
|
+
if (role === "assistant") {
|
|
2196
|
+
openRouterMessages.push({
|
|
2197
|
+
role: "assistant",
|
|
2198
|
+
content: message.content,
|
|
2199
|
+
});
|
|
2200
|
+
}
|
|
2201
|
+
else {
|
|
2202
|
+
openRouterMessages.push({
|
|
2203
|
+
role: "user",
|
|
2204
|
+
content: message.content,
|
|
2205
|
+
});
|
|
2206
|
+
}
|
|
2207
|
+
}
|
|
2208
|
+
}
|
|
2209
|
+
return openRouterMessages;
|
|
2210
|
+
}
|
|
2211
|
+
}
|
|
2212
|
+
// Export singleton instance
|
|
2213
|
+
const openRouterAdapter = new OpenRouterAdapter();
|
|
2214
|
+
|
|
1767
2215
|
const DEFAULT_TOOL_TYPE = "function";
|
|
1768
2216
|
const log = core.log.lib({ lib: core.JAYPIE.LIB.LLM });
|
|
1769
2217
|
function logToolMessage(message, context) {
|
|
@@ -2708,10 +3156,10 @@ function createOperateLoop(config) {
|
|
|
2708
3156
|
}
|
|
2709
3157
|
|
|
2710
3158
|
// Logger
|
|
2711
|
-
const getLogger$
|
|
3159
|
+
const getLogger$3 = () => core.log.lib({ lib: core.JAYPIE.LIB.LLM });
|
|
2712
3160
|
// Client initialization
|
|
2713
|
-
async function initializeClient$
|
|
2714
|
-
const logger = getLogger$
|
|
3161
|
+
async function initializeClient$3({ apiKey, } = {}) {
|
|
3162
|
+
const logger = getLogger$3();
|
|
2715
3163
|
const resolvedApiKey = apiKey || (await aws.getEnvSecret("ANTHROPIC_API_KEY"));
|
|
2716
3164
|
if (!resolvedApiKey) {
|
|
2717
3165
|
throw new core.ConfigurationError("The application could not resolve the required API key: ANTHROPIC_API_KEY");
|
|
@@ -2721,12 +3169,12 @@ async function initializeClient$2({ apiKey, } = {}) {
|
|
|
2721
3169
|
return client;
|
|
2722
3170
|
}
|
|
2723
3171
|
// Message formatting functions
|
|
2724
|
-
function formatSystemMessage$
|
|
3172
|
+
function formatSystemMessage$2(systemPrompt, { data, placeholders } = {}) {
|
|
2725
3173
|
return placeholders?.system === false
|
|
2726
3174
|
? systemPrompt
|
|
2727
3175
|
: core.placeholders(systemPrompt, data);
|
|
2728
3176
|
}
|
|
2729
|
-
function formatUserMessage$
|
|
3177
|
+
function formatUserMessage$3(message, { data, placeholders } = {}) {
|
|
2730
3178
|
const content = placeholders?.message === false
|
|
2731
3179
|
? message
|
|
2732
3180
|
: core.placeholders(message, data);
|
|
@@ -2735,11 +3183,11 @@ function formatUserMessage$2(message, { data, placeholders } = {}) {
|
|
|
2735
3183
|
content,
|
|
2736
3184
|
};
|
|
2737
3185
|
}
|
|
2738
|
-
function prepareMessages$
|
|
2739
|
-
const logger = getLogger$
|
|
3186
|
+
function prepareMessages$3(message, { data, placeholders } = {}) {
|
|
3187
|
+
const logger = getLogger$3();
|
|
2740
3188
|
const messages = [];
|
|
2741
3189
|
// Add user message (necessary for all requests)
|
|
2742
|
-
const userMessage = formatUserMessage$
|
|
3190
|
+
const userMessage = formatUserMessage$3(message, { data, placeholders });
|
|
2743
3191
|
messages.push(userMessage);
|
|
2744
3192
|
logger.trace(`User message: ${userMessage.content.length} characters`);
|
|
2745
3193
|
return messages;
|
|
@@ -2825,7 +3273,7 @@ async function createStructuredCompletion$1(client, messages, model, responseSch
|
|
|
2825
3273
|
// Main class implementation
|
|
2826
3274
|
class AnthropicProvider {
|
|
2827
3275
|
constructor(model = PROVIDER.ANTHROPIC.MODEL.DEFAULT, { apiKey } = {}) {
|
|
2828
|
-
this.log = getLogger$
|
|
3276
|
+
this.log = getLogger$3();
|
|
2829
3277
|
this.conversationHistory = [];
|
|
2830
3278
|
this.model = model;
|
|
2831
3279
|
this.apiKey = apiKey;
|
|
@@ -2834,7 +3282,7 @@ class AnthropicProvider {
|
|
|
2834
3282
|
if (this._client) {
|
|
2835
3283
|
return this._client;
|
|
2836
3284
|
}
|
|
2837
|
-
this._client = await initializeClient$
|
|
3285
|
+
this._client = await initializeClient$3({ apiKey: this.apiKey });
|
|
2838
3286
|
return this._client;
|
|
2839
3287
|
}
|
|
2840
3288
|
async getOperateLoop() {
|
|
@@ -2851,12 +3299,12 @@ class AnthropicProvider {
|
|
|
2851
3299
|
// Main send method
|
|
2852
3300
|
async send(message, options) {
|
|
2853
3301
|
const client = await this.getClient();
|
|
2854
|
-
const messages = prepareMessages$
|
|
3302
|
+
const messages = prepareMessages$3(message, options || {});
|
|
2855
3303
|
const modelToUse = options?.model || this.model;
|
|
2856
3304
|
// Process system message if provided
|
|
2857
3305
|
let systemMessage;
|
|
2858
3306
|
if (options?.system) {
|
|
2859
|
-
systemMessage = formatSystemMessage$
|
|
3307
|
+
systemMessage = formatSystemMessage$2(options.system, {
|
|
2860
3308
|
data: options.data,
|
|
2861
3309
|
placeholders: options.placeholders,
|
|
2862
3310
|
});
|
|
@@ -2887,10 +3335,10 @@ class AnthropicProvider {
|
|
|
2887
3335
|
}
|
|
2888
3336
|
|
|
2889
3337
|
// Logger
|
|
2890
|
-
const getLogger$
|
|
3338
|
+
const getLogger$2 = () => core.log.lib({ lib: core.JAYPIE.LIB.LLM });
|
|
2891
3339
|
// Client initialization
|
|
2892
|
-
async function initializeClient$
|
|
2893
|
-
const logger = getLogger$
|
|
3340
|
+
async function initializeClient$2({ apiKey, } = {}) {
|
|
3341
|
+
const logger = getLogger$2();
|
|
2894
3342
|
const resolvedApiKey = apiKey || (await aws.getEnvSecret("GEMINI_API_KEY"));
|
|
2895
3343
|
if (!resolvedApiKey) {
|
|
2896
3344
|
throw new core.ConfigurationError("The application could not resolve the requested keys");
|
|
@@ -2899,7 +3347,7 @@ async function initializeClient$1({ apiKey, } = {}) {
|
|
|
2899
3347
|
logger.trace("Initialized Gemini client");
|
|
2900
3348
|
return client;
|
|
2901
3349
|
}
|
|
2902
|
-
function formatUserMessage$
|
|
3350
|
+
function formatUserMessage$2(message, { data, placeholders } = {}) {
|
|
2903
3351
|
const content = placeholders?.message === false
|
|
2904
3352
|
? message
|
|
2905
3353
|
: core.placeholders(message, data);
|
|
@@ -2908,14 +3356,14 @@ function formatUserMessage$1(message, { data, placeholders } = {}) {
|
|
|
2908
3356
|
content,
|
|
2909
3357
|
};
|
|
2910
3358
|
}
|
|
2911
|
-
function prepareMessages$
|
|
2912
|
-
const logger = getLogger$
|
|
3359
|
+
function prepareMessages$2(message, { data, placeholders } = {}) {
|
|
3360
|
+
const logger = getLogger$2();
|
|
2913
3361
|
const messages = [];
|
|
2914
3362
|
let systemInstruction;
|
|
2915
3363
|
// Note: Gemini handles system prompts differently via systemInstruction config
|
|
2916
3364
|
// This function is kept for compatibility but system prompts should be passed
|
|
2917
3365
|
// via the systemInstruction parameter in generateContent
|
|
2918
|
-
const userMessage = formatUserMessage$
|
|
3366
|
+
const userMessage = formatUserMessage$2(message, { data, placeholders });
|
|
2919
3367
|
messages.push(userMessage);
|
|
2920
3368
|
logger.trace(`User message: ${userMessage.content?.length} characters`);
|
|
2921
3369
|
return { messages, systemInstruction };
|
|
@@ -2923,7 +3371,7 @@ function prepareMessages$1(message, { data, placeholders } = {}) {
|
|
|
2923
3371
|
|
|
2924
3372
|
class GeminiProvider {
|
|
2925
3373
|
constructor(model = PROVIDER.GEMINI.MODEL.DEFAULT, { apiKey } = {}) {
|
|
2926
|
-
this.log = getLogger$
|
|
3374
|
+
this.log = getLogger$2();
|
|
2927
3375
|
this.conversationHistory = [];
|
|
2928
3376
|
this.model = model;
|
|
2929
3377
|
this.apiKey = apiKey;
|
|
@@ -2932,7 +3380,7 @@ class GeminiProvider {
|
|
|
2932
3380
|
if (this._client) {
|
|
2933
3381
|
return this._client;
|
|
2934
3382
|
}
|
|
2935
|
-
this._client = await initializeClient$
|
|
3383
|
+
this._client = await initializeClient$2({ apiKey: this.apiKey });
|
|
2936
3384
|
return this._client;
|
|
2937
3385
|
}
|
|
2938
3386
|
async getOperateLoop() {
|
|
@@ -2948,7 +3396,7 @@ class GeminiProvider {
|
|
|
2948
3396
|
}
|
|
2949
3397
|
async send(message, options) {
|
|
2950
3398
|
const client = await this.getClient();
|
|
2951
|
-
const { messages } = prepareMessages$
|
|
3399
|
+
const { messages } = prepareMessages$2(message, options);
|
|
2952
3400
|
const modelToUse = options?.model || this.model;
|
|
2953
3401
|
// Build the request config
|
|
2954
3402
|
const config = {};
|
|
@@ -3003,10 +3451,10 @@ class GeminiProvider {
|
|
|
3003
3451
|
}
|
|
3004
3452
|
|
|
3005
3453
|
// Logger
|
|
3006
|
-
const getLogger = () => core.log.lib({ lib: core.JAYPIE.LIB.LLM });
|
|
3454
|
+
const getLogger$1 = () => core.log.lib({ lib: core.JAYPIE.LIB.LLM });
|
|
3007
3455
|
// Client initialization
|
|
3008
|
-
async function initializeClient({ apiKey, } = {}) {
|
|
3009
|
-
const logger = getLogger();
|
|
3456
|
+
async function initializeClient$1({ apiKey, } = {}) {
|
|
3457
|
+
const logger = getLogger$1();
|
|
3010
3458
|
const resolvedApiKey = apiKey || (await aws.getEnvSecret("OPENAI_API_KEY"));
|
|
3011
3459
|
if (!resolvedApiKey) {
|
|
3012
3460
|
throw new core.ConfigurationError("The application could not resolve the requested keys");
|
|
@@ -3015,7 +3463,7 @@ async function initializeClient({ apiKey, } = {}) {
|
|
|
3015
3463
|
logger.trace("Initialized OpenAI client");
|
|
3016
3464
|
return client;
|
|
3017
3465
|
}
|
|
3018
|
-
function formatSystemMessage(systemPrompt, { data, placeholders } = {}) {
|
|
3466
|
+
function formatSystemMessage$1(systemPrompt, { data, placeholders } = {}) {
|
|
3019
3467
|
const content = placeholders?.system === false
|
|
3020
3468
|
? systemPrompt
|
|
3021
3469
|
: core.placeholders(systemPrompt, data);
|
|
@@ -3024,7 +3472,7 @@ function formatSystemMessage(systemPrompt, { data, placeholders } = {}) {
|
|
|
3024
3472
|
content,
|
|
3025
3473
|
};
|
|
3026
3474
|
}
|
|
3027
|
-
function formatUserMessage(message, { data, placeholders } = {}) {
|
|
3475
|
+
function formatUserMessage$1(message, { data, placeholders } = {}) {
|
|
3028
3476
|
const content = placeholders?.message === false
|
|
3029
3477
|
? message
|
|
3030
3478
|
: core.placeholders(message, data);
|
|
@@ -3033,22 +3481,22 @@ function formatUserMessage(message, { data, placeholders } = {}) {
|
|
|
3033
3481
|
content,
|
|
3034
3482
|
};
|
|
3035
3483
|
}
|
|
3036
|
-
function prepareMessages(message, { system, data, placeholders } = {}) {
|
|
3037
|
-
const logger = getLogger();
|
|
3484
|
+
function prepareMessages$1(message, { system, data, placeholders } = {}) {
|
|
3485
|
+
const logger = getLogger$1();
|
|
3038
3486
|
const messages = [];
|
|
3039
3487
|
if (system) {
|
|
3040
|
-
const systemMessage = formatSystemMessage(system, { data, placeholders });
|
|
3488
|
+
const systemMessage = formatSystemMessage$1(system, { data, placeholders });
|
|
3041
3489
|
messages.push(systemMessage);
|
|
3042
3490
|
logger.trace(`System message: ${systemMessage.content?.length} characters`);
|
|
3043
3491
|
}
|
|
3044
|
-
const userMessage = formatUserMessage(message, { data, placeholders });
|
|
3492
|
+
const userMessage = formatUserMessage$1(message, { data, placeholders });
|
|
3045
3493
|
messages.push(userMessage);
|
|
3046
3494
|
logger.trace(`User message: ${userMessage.content?.length} characters`);
|
|
3047
3495
|
return messages;
|
|
3048
3496
|
}
|
|
3049
3497
|
// Completion requests
|
|
3050
3498
|
async function createStructuredCompletion(client, { messages, responseSchema, model, }) {
|
|
3051
|
-
const logger = getLogger();
|
|
3499
|
+
const logger = getLogger$1();
|
|
3052
3500
|
logger.trace("Using structured output");
|
|
3053
3501
|
const zodSchema = responseSchema instanceof v4.z.ZodType
|
|
3054
3502
|
? responseSchema
|
|
@@ -3079,7 +3527,7 @@ async function createStructuredCompletion(client, { messages, responseSchema, mo
|
|
|
3079
3527
|
return completion.choices[0].message.parsed;
|
|
3080
3528
|
}
|
|
3081
3529
|
async function createTextCompletion(client, { messages, model, }) {
|
|
3082
|
-
const logger = getLogger();
|
|
3530
|
+
const logger = getLogger$1();
|
|
3083
3531
|
logger.trace("Using text output (unstructured)");
|
|
3084
3532
|
const completion = await client.chat.completions.create({
|
|
3085
3533
|
messages,
|
|
@@ -3091,7 +3539,7 @@ async function createTextCompletion(client, { messages, model, }) {
|
|
|
3091
3539
|
|
|
3092
3540
|
class OpenAiProvider {
|
|
3093
3541
|
constructor(model = PROVIDER.OPENAI.MODEL.DEFAULT, { apiKey } = {}) {
|
|
3094
|
-
this.log = getLogger();
|
|
3542
|
+
this.log = getLogger$1();
|
|
3095
3543
|
this.conversationHistory = [];
|
|
3096
3544
|
this.model = model;
|
|
3097
3545
|
this.apiKey = apiKey;
|
|
@@ -3100,7 +3548,7 @@ class OpenAiProvider {
|
|
|
3100
3548
|
if (this._client) {
|
|
3101
3549
|
return this._client;
|
|
3102
3550
|
}
|
|
3103
|
-
this._client = await initializeClient({ apiKey: this.apiKey });
|
|
3551
|
+
this._client = await initializeClient$1({ apiKey: this.apiKey });
|
|
3104
3552
|
return this._client;
|
|
3105
3553
|
}
|
|
3106
3554
|
async getOperateLoop() {
|
|
@@ -3116,7 +3564,7 @@ class OpenAiProvider {
|
|
|
3116
3564
|
}
|
|
3117
3565
|
async send(message, options) {
|
|
3118
3566
|
const client = await this.getClient();
|
|
3119
|
-
const messages = prepareMessages(message, options || {});
|
|
3567
|
+
const messages = prepareMessages$1(message, options || {});
|
|
3120
3568
|
const modelToUse = options?.model || this.model;
|
|
3121
3569
|
if (options?.response) {
|
|
3122
3570
|
return createStructuredCompletion(client, {
|
|
@@ -3150,6 +3598,131 @@ class OpenAiProvider {
|
|
|
3150
3598
|
}
|
|
3151
3599
|
}
|
|
3152
3600
|
|
|
3601
|
+
// Logger
|
|
3602
|
+
const getLogger = () => core.log.lib({ lib: core.JAYPIE.LIB.LLM });
|
|
3603
|
+
// Client initialization
|
|
3604
|
+
async function initializeClient({ apiKey, } = {}) {
|
|
3605
|
+
const logger = getLogger();
|
|
3606
|
+
const resolvedApiKey = apiKey || (await aws.getEnvSecret("OPENROUTER_API_KEY"));
|
|
3607
|
+
if (!resolvedApiKey) {
|
|
3608
|
+
throw new core.ConfigurationError("The application could not resolve the requested keys");
|
|
3609
|
+
}
|
|
3610
|
+
const client = new sdk.OpenRouter({ apiKey: resolvedApiKey });
|
|
3611
|
+
logger.trace("Initialized OpenRouter client");
|
|
3612
|
+
return client;
|
|
3613
|
+
}
|
|
3614
|
+
// Get default model from environment or constants
|
|
3615
|
+
function getDefaultModel() {
|
|
3616
|
+
return process.env.OPENROUTER_MODEL || PROVIDER.OPENROUTER.MODEL.DEFAULT;
|
|
3617
|
+
}
|
|
3618
|
+
function formatSystemMessage(systemPrompt, { data, placeholders } = {}) {
|
|
3619
|
+
const content = placeholders?.system === false
|
|
3620
|
+
? systemPrompt
|
|
3621
|
+
: core.placeholders(systemPrompt, data);
|
|
3622
|
+
return {
|
|
3623
|
+
role: "system",
|
|
3624
|
+
content,
|
|
3625
|
+
};
|
|
3626
|
+
}
|
|
3627
|
+
function formatUserMessage(message, { data, placeholders } = {}) {
|
|
3628
|
+
const content = placeholders?.message === false
|
|
3629
|
+
? message
|
|
3630
|
+
: core.placeholders(message, data);
|
|
3631
|
+
return {
|
|
3632
|
+
role: "user",
|
|
3633
|
+
content,
|
|
3634
|
+
};
|
|
3635
|
+
}
|
|
3636
|
+
function prepareMessages(message, { system, data, placeholders } = {}) {
|
|
3637
|
+
const logger = getLogger();
|
|
3638
|
+
const messages = [];
|
|
3639
|
+
if (system) {
|
|
3640
|
+
const systemMessage = formatSystemMessage(system, { data, placeholders });
|
|
3641
|
+
messages.push(systemMessage);
|
|
3642
|
+
logger.trace(`System message: ${systemMessage.content?.length} characters`);
|
|
3643
|
+
}
|
|
3644
|
+
const userMessage = formatUserMessage(message, { data, placeholders });
|
|
3645
|
+
messages.push(userMessage);
|
|
3646
|
+
logger.trace(`User message: ${userMessage.content?.length} characters`);
|
|
3647
|
+
return messages;
|
|
3648
|
+
}
|
|
3649
|
+
|
|
3650
|
+
class OpenRouterProvider {
|
|
3651
|
+
constructor(model = getDefaultModel(), { apiKey } = {}) {
|
|
3652
|
+
this.log = getLogger();
|
|
3653
|
+
this.conversationHistory = [];
|
|
3654
|
+
this.model = model;
|
|
3655
|
+
this.apiKey = apiKey;
|
|
3656
|
+
}
|
|
3657
|
+
async getClient() {
|
|
3658
|
+
if (this._client) {
|
|
3659
|
+
return this._client;
|
|
3660
|
+
}
|
|
3661
|
+
this._client = await initializeClient({ apiKey: this.apiKey });
|
|
3662
|
+
return this._client;
|
|
3663
|
+
}
|
|
3664
|
+
async getOperateLoop() {
|
|
3665
|
+
if (this._operateLoop) {
|
|
3666
|
+
return this._operateLoop;
|
|
3667
|
+
}
|
|
3668
|
+
const client = await this.getClient();
|
|
3669
|
+
this._operateLoop = createOperateLoop({
|
|
3670
|
+
adapter: openRouterAdapter,
|
|
3671
|
+
client,
|
|
3672
|
+
});
|
|
3673
|
+
return this._operateLoop;
|
|
3674
|
+
}
|
|
3675
|
+
async send(message, options) {
|
|
3676
|
+
const client = await this.getClient();
|
|
3677
|
+
const messages = prepareMessages(message, options);
|
|
3678
|
+
const modelToUse = options?.model || this.model;
|
|
3679
|
+
// Build the request
|
|
3680
|
+
const response = await client.chat.send({
|
|
3681
|
+
model: modelToUse,
|
|
3682
|
+
messages: messages,
|
|
3683
|
+
});
|
|
3684
|
+
const rawContent = response.choices?.[0]?.message?.content;
|
|
3685
|
+
// Extract text content - content could be string or array of content items
|
|
3686
|
+
const content = typeof rawContent === "string"
|
|
3687
|
+
? rawContent
|
|
3688
|
+
: Array.isArray(rawContent)
|
|
3689
|
+
? rawContent
|
|
3690
|
+
.filter((item) => item.type === "text")
|
|
3691
|
+
.map((item) => item.text)
|
|
3692
|
+
.join("")
|
|
3693
|
+
: "";
|
|
3694
|
+
this.log.trace(`Assistant reply: ${content?.length || 0} characters`);
|
|
3695
|
+
// If structured output was requested, try to parse the response
|
|
3696
|
+
if (options?.response && content) {
|
|
3697
|
+
try {
|
|
3698
|
+
return JSON.parse(content);
|
|
3699
|
+
}
|
|
3700
|
+
catch {
|
|
3701
|
+
return content || "";
|
|
3702
|
+
}
|
|
3703
|
+
}
|
|
3704
|
+
return content || "";
|
|
3705
|
+
}
|
|
3706
|
+
async operate(input, options = {}) {
|
|
3707
|
+
const operateLoop = await this.getOperateLoop();
|
|
3708
|
+
const mergedOptions = { ...options, model: options.model ?? this.model };
|
|
3709
|
+
// Create a merged history including both the tracked history and any explicitly provided history
|
|
3710
|
+
if (this.conversationHistory.length > 0) {
|
|
3711
|
+
// If options.history exists, merge with instance history, otherwise use instance history
|
|
3712
|
+
mergedOptions.history = options.history
|
|
3713
|
+
? [...this.conversationHistory, ...options.history]
|
|
3714
|
+
: [...this.conversationHistory];
|
|
3715
|
+
}
|
|
3716
|
+
// Execute operate loop
|
|
3717
|
+
const response = await operateLoop.execute(input, mergedOptions);
|
|
3718
|
+
// Update conversation history with the new history from the response
|
|
3719
|
+
if (response.history && response.history.length > 0) {
|
|
3720
|
+
this.conversationHistory = response.history;
|
|
3721
|
+
}
|
|
3722
|
+
return response;
|
|
3723
|
+
}
|
|
3724
|
+
}
|
|
3725
|
+
|
|
3153
3726
|
class Llm {
|
|
3154
3727
|
constructor(providerName = DEFAULT.PROVIDER.NAME, options = {}) {
|
|
3155
3728
|
const { model } = options;
|
|
@@ -3198,6 +3771,10 @@ class Llm {
|
|
|
3198
3771
|
return new OpenAiProvider(model || PROVIDER.OPENAI.MODEL.DEFAULT, {
|
|
3199
3772
|
apiKey,
|
|
3200
3773
|
});
|
|
3774
|
+
case PROVIDER.OPENROUTER.NAME:
|
|
3775
|
+
return new OpenRouterProvider(model || PROVIDER.OPENROUTER.MODEL.DEFAULT, {
|
|
3776
|
+
apiKey,
|
|
3777
|
+
});
|
|
3201
3778
|
default:
|
|
3202
3779
|
throw new errors.ConfigurationError(`Unsupported provider: ${providerName}`);
|
|
3203
3780
|
}
|
|
@@ -3533,6 +4110,7 @@ exports.GeminiProvider = GeminiProvider;
|
|
|
3533
4110
|
exports.JaypieToolkit = JaypieToolkit;
|
|
3534
4111
|
exports.LLM = constants;
|
|
3535
4112
|
exports.Llm = Llm;
|
|
4113
|
+
exports.OpenRouterProvider = OpenRouterProvider;
|
|
3536
4114
|
exports.Toolkit = Toolkit;
|
|
3537
4115
|
exports.toolkit = toolkit;
|
|
3538
4116
|
exports.tools = tools;
|