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