@jaypie/llm 1.1.30-rc.0 → 1.2.0-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/constants.d.ts +17 -1
- package/dist/cjs/index.cjs +725 -109
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.ts +1 -0
- package/dist/cjs/operate/adapters/AnthropicAdapter.d.ts +1 -1
- 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/anthropic/utils.d.ts +3 -37
- package/dist/cjs/providers/gemini/utils.d.ts +3 -37
- package/dist/cjs/providers/openai/utils.d.ts +1 -36
- 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 +15 -0
- package/dist/cjs/util/logger.d.ts +2 -71
- package/dist/esm/constants.d.ts +17 -1
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.js +709 -94
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/operate/adapters/AnthropicAdapter.d.ts +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/anthropic/utils.d.ts +3 -37
- package/dist/esm/providers/gemini/utils.d.ts +3 -37
- package/dist/esm/providers/openai/utils.d.ts +1 -36
- 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 +15 -0
- package/dist/esm/util/logger.d.ts +2 -71
- package/package.json +24 -7
- 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
|
@@ -1,17 +1,36 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var errors = require('@jaypie/errors');
|
|
4
|
-
var Anthropic = require('@anthropic-ai/sdk');
|
|
5
4
|
var v4 = require('zod/v4');
|
|
6
|
-
var
|
|
5
|
+
var kit = require('@jaypie/kit');
|
|
6
|
+
var logger = require('@jaypie/logger');
|
|
7
7
|
var RandomLib = require('random');
|
|
8
8
|
var openai = require('openai');
|
|
9
9
|
var zod = require('openai/helpers/zod');
|
|
10
10
|
var aws = require('@jaypie/aws');
|
|
11
|
-
var genai = require('@google/genai');
|
|
12
11
|
var openmeteo = require('openmeteo');
|
|
13
12
|
|
|
14
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
|
+
},
|
|
15
34
|
GEMINI: {
|
|
16
35
|
// https://ai.google.dev/gemini-api/docs/models
|
|
17
36
|
MODEL: {
|
|
@@ -158,6 +177,12 @@ function determineModelProvider(input) {
|
|
|
158
177
|
provider: PROVIDER.OPENAI.NAME,
|
|
159
178
|
};
|
|
160
179
|
}
|
|
180
|
+
if (input === PROVIDER.OPENROUTER.NAME) {
|
|
181
|
+
return {
|
|
182
|
+
model: PROVIDER.OPENROUTER.MODEL.DEFAULT,
|
|
183
|
+
provider: PROVIDER.OPENROUTER.NAME,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
161
186
|
// Check if input matches an Anthropic model exactly
|
|
162
187
|
for (const [, modelValue] of Object.entries(PROVIDER.ANTHROPIC.MODEL)) {
|
|
163
188
|
if (input === modelValue) {
|
|
@@ -185,6 +210,15 @@ function determineModelProvider(input) {
|
|
|
185
210
|
};
|
|
186
211
|
}
|
|
187
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
|
+
}
|
|
188
222
|
// Check Anthropic match words
|
|
189
223
|
const lowerInput = input.toLowerCase();
|
|
190
224
|
for (const matchWord of PROVIDER.ANTHROPIC.MODEL_MATCH_WORDS) {
|
|
@@ -223,6 +257,15 @@ function determineModelProvider(input) {
|
|
|
223
257
|
}
|
|
224
258
|
}
|
|
225
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
|
+
}
|
|
226
269
|
// Default fallback if model not recognized
|
|
227
270
|
return {
|
|
228
271
|
model: input,
|
|
@@ -304,7 +347,7 @@ var LlmResponseStatus;
|
|
|
304
347
|
* @returns LlmInputMessage
|
|
305
348
|
*/
|
|
306
349
|
function formatOperateMessage(input, options) {
|
|
307
|
-
const content = options?.data ?
|
|
350
|
+
const content = options?.data ? kit.placeholders(input, options.data) : input;
|
|
308
351
|
return {
|
|
309
352
|
content,
|
|
310
353
|
role: options?.role || exports.LlmMessageRole.User,
|
|
@@ -342,8 +385,8 @@ function formatOperateInput(input, options) {
|
|
|
342
385
|
return [input];
|
|
343
386
|
}
|
|
344
387
|
|
|
345
|
-
const getLogger$
|
|
346
|
-
const log$1 = getLogger$
|
|
388
|
+
const getLogger$4 = () => logger.log.lib({ lib: kit.JAYPIE.LIB.LLM });
|
|
389
|
+
const log$1 = getLogger$4();
|
|
347
390
|
|
|
348
391
|
// Turn policy constants
|
|
349
392
|
const MAX_TURNS_ABSOLUTE_LIMIT = 72;
|
|
@@ -646,17 +689,18 @@ var ErrorCategory;
|
|
|
646
689
|
//
|
|
647
690
|
// Constants
|
|
648
691
|
//
|
|
649
|
-
const STRUCTURED_OUTPUT_TOOL_NAME$
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
692
|
+
const STRUCTURED_OUTPUT_TOOL_NAME$2 = "structured_output";
|
|
693
|
+
// Error names for classification (using string names since SDK is optional)
|
|
694
|
+
const RETRYABLE_ERROR_NAMES = [
|
|
695
|
+
"APIConnectionError",
|
|
696
|
+
"APIConnectionTimeoutError",
|
|
697
|
+
"InternalServerError",
|
|
654
698
|
];
|
|
655
|
-
const
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
699
|
+
const NOT_RETRYABLE_ERROR_NAMES = [
|
|
700
|
+
"AuthenticationError",
|
|
701
|
+
"BadRequestError",
|
|
702
|
+
"NotFoundError",
|
|
703
|
+
"PermissionDeniedError",
|
|
660
704
|
];
|
|
661
705
|
//
|
|
662
706
|
//
|
|
@@ -711,7 +755,7 @@ class AnthropicAdapter extends BaseProviderAdapter {
|
|
|
711
755
|
type: "custom",
|
|
712
756
|
}));
|
|
713
757
|
// Determine tool choice based on whether structured output is requested
|
|
714
|
-
const hasStructuredOutput = request.tools.some((t) => t.name === STRUCTURED_OUTPUT_TOOL_NAME$
|
|
758
|
+
const hasStructuredOutput = request.tools.some((t) => t.name === STRUCTURED_OUTPUT_TOOL_NAME$2);
|
|
715
759
|
anthropicRequest.tool_choice = {
|
|
716
760
|
type: hasStructuredOutput ? "any" : "auto",
|
|
717
761
|
};
|
|
@@ -733,7 +777,7 @@ class AnthropicAdapter extends BaseProviderAdapter {
|
|
|
733
777
|
// Add structured output tool if schema is provided
|
|
734
778
|
if (outputSchema) {
|
|
735
779
|
tools.push({
|
|
736
|
-
name: STRUCTURED_OUTPUT_TOOL_NAME$
|
|
780
|
+
name: STRUCTURED_OUTPUT_TOOL_NAME$2,
|
|
737
781
|
description: "Output a structured JSON object, " +
|
|
738
782
|
"use this before your final response to give structured outputs to the user",
|
|
739
783
|
parameters: outputSchema,
|
|
@@ -864,8 +908,9 @@ class AnthropicAdapter extends BaseProviderAdapter {
|
|
|
864
908
|
// Error Classification
|
|
865
909
|
//
|
|
866
910
|
classifyError(error) {
|
|
911
|
+
const errorName = error?.constructor?.name;
|
|
867
912
|
// Check for rate limit error
|
|
868
|
-
if (
|
|
913
|
+
if (errorName === "RateLimitError") {
|
|
869
914
|
return {
|
|
870
915
|
error,
|
|
871
916
|
category: ErrorCategory.RateLimit,
|
|
@@ -874,24 +919,20 @@ class AnthropicAdapter extends BaseProviderAdapter {
|
|
|
874
919
|
};
|
|
875
920
|
}
|
|
876
921
|
// Check for retryable errors
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
};
|
|
884
|
-
}
|
|
922
|
+
if (RETRYABLE_ERROR_NAMES.includes(errorName)) {
|
|
923
|
+
return {
|
|
924
|
+
error,
|
|
925
|
+
category: ErrorCategory.Retryable,
|
|
926
|
+
shouldRetry: true,
|
|
927
|
+
};
|
|
885
928
|
}
|
|
886
929
|
// Check for non-retryable errors
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
};
|
|
894
|
-
}
|
|
930
|
+
if (NOT_RETRYABLE_ERROR_NAMES.includes(errorName)) {
|
|
931
|
+
return {
|
|
932
|
+
error,
|
|
933
|
+
category: ErrorCategory.Unrecoverable,
|
|
934
|
+
shouldRetry: false,
|
|
935
|
+
};
|
|
895
936
|
}
|
|
896
937
|
// Unknown error - treat as potentially retryable
|
|
897
938
|
return {
|
|
@@ -912,13 +953,13 @@ class AnthropicAdapter extends BaseProviderAdapter {
|
|
|
912
953
|
// Check if the last content block is a tool_use with structured_output
|
|
913
954
|
const lastBlock = anthropicResponse.content[anthropicResponse.content.length - 1];
|
|
914
955
|
return (lastBlock?.type === "tool_use" &&
|
|
915
|
-
lastBlock.name === STRUCTURED_OUTPUT_TOOL_NAME$
|
|
956
|
+
lastBlock.name === STRUCTURED_OUTPUT_TOOL_NAME$2);
|
|
916
957
|
}
|
|
917
958
|
extractStructuredOutput(response) {
|
|
918
959
|
const anthropicResponse = response;
|
|
919
960
|
const lastBlock = anthropicResponse.content[anthropicResponse.content.length - 1];
|
|
920
961
|
if (lastBlock?.type === "tool_use" &&
|
|
921
|
-
lastBlock.name === STRUCTURED_OUTPUT_TOOL_NAME$
|
|
962
|
+
lastBlock.name === STRUCTURED_OUTPUT_TOOL_NAME$2) {
|
|
922
963
|
return lastBlock.input;
|
|
923
964
|
}
|
|
924
965
|
return undefined;
|
|
@@ -943,10 +984,10 @@ const anthropicAdapter = new AnthropicAdapter();
|
|
|
943
984
|
//
|
|
944
985
|
// Constants
|
|
945
986
|
//
|
|
946
|
-
const STRUCTURED_OUTPUT_TOOL_NAME = "structured_output";
|
|
987
|
+
const STRUCTURED_OUTPUT_TOOL_NAME$1 = "structured_output";
|
|
947
988
|
// Gemini uses HTTP status codes for error classification
|
|
948
989
|
// Documented at: https://ai.google.dev/api/rest/v1beta/Status
|
|
949
|
-
const RETRYABLE_STATUS_CODES = [
|
|
990
|
+
const RETRYABLE_STATUS_CODES$1 = [
|
|
950
991
|
408, // Request Timeout
|
|
951
992
|
429, // Too Many Requests (Rate Limit)
|
|
952
993
|
500, // Internal Server Error
|
|
@@ -1060,7 +1101,7 @@ class GeminiAdapter extends BaseProviderAdapter {
|
|
|
1060
1101
|
// Add structured output tool if schema is provided
|
|
1061
1102
|
if (outputSchema) {
|
|
1062
1103
|
tools.push({
|
|
1063
|
-
name: STRUCTURED_OUTPUT_TOOL_NAME,
|
|
1104
|
+
name: STRUCTURED_OUTPUT_TOOL_NAME$1,
|
|
1064
1105
|
description: "Output a structured JSON object, " +
|
|
1065
1106
|
"use this before your final response to give structured outputs to the user",
|
|
1066
1107
|
parameters: outputSchema,
|
|
@@ -1261,7 +1302,7 @@ class GeminiAdapter extends BaseProviderAdapter {
|
|
|
1261
1302
|
}
|
|
1262
1303
|
// Check for retryable errors
|
|
1263
1304
|
if (typeof statusCode === "number" &&
|
|
1264
|
-
RETRYABLE_STATUS_CODES.includes(statusCode)) {
|
|
1305
|
+
RETRYABLE_STATUS_CODES$1.includes(statusCode)) {
|
|
1265
1306
|
return {
|
|
1266
1307
|
error,
|
|
1267
1308
|
category: ErrorCategory.Retryable,
|
|
@@ -1325,7 +1366,7 @@ class GeminiAdapter extends BaseProviderAdapter {
|
|
|
1325
1366
|
}
|
|
1326
1367
|
// Check if the last part is a function call with structured_output
|
|
1327
1368
|
const lastPart = candidate.content.parts[candidate.content.parts.length - 1];
|
|
1328
|
-
return lastPart?.functionCall?.name === STRUCTURED_OUTPUT_TOOL_NAME;
|
|
1369
|
+
return lastPart?.functionCall?.name === STRUCTURED_OUTPUT_TOOL_NAME$1;
|
|
1329
1370
|
}
|
|
1330
1371
|
extractStructuredOutput(response) {
|
|
1331
1372
|
const geminiResponse = response;
|
|
@@ -1334,7 +1375,7 @@ class GeminiAdapter extends BaseProviderAdapter {
|
|
|
1334
1375
|
return undefined;
|
|
1335
1376
|
}
|
|
1336
1377
|
const lastPart = candidate.content.parts[candidate.content.parts.length - 1];
|
|
1337
|
-
if (lastPart?.functionCall?.name === STRUCTURED_OUTPUT_TOOL_NAME) {
|
|
1378
|
+
if (lastPart?.functionCall?.name === STRUCTURED_OUTPUT_TOOL_NAME$1) {
|
|
1338
1379
|
return lastPart.functionCall.args;
|
|
1339
1380
|
}
|
|
1340
1381
|
return undefined;
|
|
@@ -1764,8 +1805,411 @@ class OpenAiAdapter extends BaseProviderAdapter {
|
|
|
1764
1805
|
// Export singleton instance
|
|
1765
1806
|
const openAiAdapter = new OpenAiAdapter();
|
|
1766
1807
|
|
|
1808
|
+
//
|
|
1809
|
+
//
|
|
1810
|
+
// Constants
|
|
1811
|
+
//
|
|
1812
|
+
const STRUCTURED_OUTPUT_TOOL_NAME = "structured_output";
|
|
1813
|
+
// OpenRouter SDK error types based on HTTP status codes
|
|
1814
|
+
const RETRYABLE_STATUS_CODES = [408, 500, 502, 503, 524, 529];
|
|
1815
|
+
const RATE_LIMIT_STATUS_CODE = 429;
|
|
1816
|
+
//
|
|
1817
|
+
//
|
|
1818
|
+
// Main
|
|
1819
|
+
//
|
|
1820
|
+
/**
|
|
1821
|
+
* OpenRouterAdapter implements the ProviderAdapter interface for OpenRouter's API.
|
|
1822
|
+
* OpenRouter provides a unified API to access hundreds of AI models through OpenAI-compatible endpoints.
|
|
1823
|
+
* It handles request building, response parsing, and error classification
|
|
1824
|
+
* specific to OpenRouter's Chat Completions API.
|
|
1825
|
+
*/
|
|
1826
|
+
class OpenRouterAdapter extends BaseProviderAdapter {
|
|
1827
|
+
constructor() {
|
|
1828
|
+
super(...arguments);
|
|
1829
|
+
this.name = PROVIDER.OPENROUTER.NAME;
|
|
1830
|
+
this.defaultModel = PROVIDER.OPENROUTER.MODEL.DEFAULT;
|
|
1831
|
+
}
|
|
1832
|
+
//
|
|
1833
|
+
// Request Building
|
|
1834
|
+
//
|
|
1835
|
+
buildRequest(request) {
|
|
1836
|
+
// Convert messages to OpenRouter format (OpenAI-compatible)
|
|
1837
|
+
const messages = this.convertMessagesToOpenRouter(request.messages, request.system);
|
|
1838
|
+
// Append instructions to last message if provided
|
|
1839
|
+
if (request.instructions && messages.length > 0) {
|
|
1840
|
+
const lastMsg = messages[messages.length - 1];
|
|
1841
|
+
if (lastMsg.content && typeof lastMsg.content === "string") {
|
|
1842
|
+
lastMsg.content = lastMsg.content + "\n\n" + request.instructions;
|
|
1843
|
+
}
|
|
1844
|
+
}
|
|
1845
|
+
const openRouterRequest = {
|
|
1846
|
+
model: request.model || this.defaultModel,
|
|
1847
|
+
messages,
|
|
1848
|
+
};
|
|
1849
|
+
if (request.user) {
|
|
1850
|
+
openRouterRequest.user = request.user;
|
|
1851
|
+
}
|
|
1852
|
+
if (request.tools && request.tools.length > 0) {
|
|
1853
|
+
openRouterRequest.tools = request.tools.map((tool) => ({
|
|
1854
|
+
type: "function",
|
|
1855
|
+
function: {
|
|
1856
|
+
name: tool.name,
|
|
1857
|
+
description: tool.description,
|
|
1858
|
+
parameters: tool.parameters,
|
|
1859
|
+
},
|
|
1860
|
+
}));
|
|
1861
|
+
// Determine tool choice based on whether structured output is requested
|
|
1862
|
+
const hasStructuredOutput = request.tools.some((t) => t.name === STRUCTURED_OUTPUT_TOOL_NAME);
|
|
1863
|
+
openRouterRequest.tool_choice = hasStructuredOutput ? "required" : "auto";
|
|
1864
|
+
}
|
|
1865
|
+
if (request.providerOptions) {
|
|
1866
|
+
Object.assign(openRouterRequest, request.providerOptions);
|
|
1867
|
+
}
|
|
1868
|
+
return openRouterRequest;
|
|
1869
|
+
}
|
|
1870
|
+
formatTools(toolkit, outputSchema) {
|
|
1871
|
+
const tools = toolkit.tools.map((tool) => ({
|
|
1872
|
+
name: tool.name,
|
|
1873
|
+
description: tool.description,
|
|
1874
|
+
parameters: tool.parameters,
|
|
1875
|
+
}));
|
|
1876
|
+
// Add structured output tool if schema is provided
|
|
1877
|
+
// (OpenRouter doesn't have native structured output like OpenAI, so use tool approach)
|
|
1878
|
+
if (outputSchema) {
|
|
1879
|
+
tools.push({
|
|
1880
|
+
name: STRUCTURED_OUTPUT_TOOL_NAME,
|
|
1881
|
+
description: "REQUIRED: You MUST call this tool to provide your final response. " +
|
|
1882
|
+
"After gathering all necessary information (including results from other tools), " +
|
|
1883
|
+
"call this tool with the structured data to complete the request.",
|
|
1884
|
+
parameters: outputSchema,
|
|
1885
|
+
});
|
|
1886
|
+
}
|
|
1887
|
+
return tools;
|
|
1888
|
+
}
|
|
1889
|
+
formatOutputSchema(schema) {
|
|
1890
|
+
let jsonSchema;
|
|
1891
|
+
// Check if schema is already a JsonObject with type "json_schema"
|
|
1892
|
+
if (typeof schema === "object" &&
|
|
1893
|
+
schema !== null &&
|
|
1894
|
+
!Array.isArray(schema) &&
|
|
1895
|
+
schema.type === "json_schema") {
|
|
1896
|
+
jsonSchema = structuredClone(schema);
|
|
1897
|
+
jsonSchema.type = "object"; // Normalize type
|
|
1898
|
+
}
|
|
1899
|
+
else {
|
|
1900
|
+
// Convert NaturalSchema to JSON schema through Zod
|
|
1901
|
+
const zodSchema = schema instanceof v4.z.ZodType
|
|
1902
|
+
? schema
|
|
1903
|
+
: naturalZodSchema(schema);
|
|
1904
|
+
jsonSchema = v4.z.toJSONSchema(zodSchema);
|
|
1905
|
+
}
|
|
1906
|
+
// Remove $schema property (can cause issues with some providers)
|
|
1907
|
+
if (jsonSchema.$schema) {
|
|
1908
|
+
delete jsonSchema.$schema;
|
|
1909
|
+
}
|
|
1910
|
+
return jsonSchema;
|
|
1911
|
+
}
|
|
1912
|
+
//
|
|
1913
|
+
// API Execution
|
|
1914
|
+
//
|
|
1915
|
+
async executeRequest(client, request) {
|
|
1916
|
+
const openRouter = client;
|
|
1917
|
+
const openRouterRequest = request;
|
|
1918
|
+
const response = await openRouter.chat.send({
|
|
1919
|
+
model: openRouterRequest.model,
|
|
1920
|
+
messages: openRouterRequest.messages,
|
|
1921
|
+
tools: openRouterRequest.tools,
|
|
1922
|
+
toolChoice: openRouterRequest.tool_choice,
|
|
1923
|
+
user: openRouterRequest.user,
|
|
1924
|
+
});
|
|
1925
|
+
return response;
|
|
1926
|
+
}
|
|
1927
|
+
//
|
|
1928
|
+
// Response Parsing
|
|
1929
|
+
//
|
|
1930
|
+
parseResponse(response, _options) {
|
|
1931
|
+
const openRouterResponse = response;
|
|
1932
|
+
const choice = openRouterResponse.choices[0];
|
|
1933
|
+
const content = this.extractContent(openRouterResponse);
|
|
1934
|
+
const hasToolCalls = this.hasToolCalls(openRouterResponse);
|
|
1935
|
+
// SDK returns camelCase (finishReason), but support snake_case as fallback
|
|
1936
|
+
const stopReason = choice?.finishReason ?? choice?.finish_reason ?? undefined;
|
|
1937
|
+
return {
|
|
1938
|
+
content,
|
|
1939
|
+
hasToolCalls,
|
|
1940
|
+
stopReason,
|
|
1941
|
+
usage: this.extractUsage(openRouterResponse, openRouterResponse.model),
|
|
1942
|
+
raw: openRouterResponse,
|
|
1943
|
+
};
|
|
1944
|
+
}
|
|
1945
|
+
extractToolCalls(response) {
|
|
1946
|
+
const openRouterResponse = response;
|
|
1947
|
+
const toolCalls = [];
|
|
1948
|
+
const choice = openRouterResponse.choices[0];
|
|
1949
|
+
// SDK returns camelCase (toolCalls)
|
|
1950
|
+
if (!choice?.message?.toolCalls) {
|
|
1951
|
+
return toolCalls;
|
|
1952
|
+
}
|
|
1953
|
+
for (const toolCall of choice.message.toolCalls) {
|
|
1954
|
+
toolCalls.push({
|
|
1955
|
+
callId: toolCall.id,
|
|
1956
|
+
name: toolCall.function.name,
|
|
1957
|
+
arguments: toolCall.function.arguments,
|
|
1958
|
+
raw: toolCall,
|
|
1959
|
+
});
|
|
1960
|
+
}
|
|
1961
|
+
return toolCalls;
|
|
1962
|
+
}
|
|
1963
|
+
extractUsage(response, model) {
|
|
1964
|
+
const openRouterResponse = response;
|
|
1965
|
+
if (!openRouterResponse.usage) {
|
|
1966
|
+
return {
|
|
1967
|
+
input: 0,
|
|
1968
|
+
output: 0,
|
|
1969
|
+
reasoning: 0,
|
|
1970
|
+
total: 0,
|
|
1971
|
+
provider: this.name,
|
|
1972
|
+
model,
|
|
1973
|
+
};
|
|
1974
|
+
}
|
|
1975
|
+
// SDK returns camelCase, but support snake_case as fallback
|
|
1976
|
+
const usage = openRouterResponse.usage;
|
|
1977
|
+
return {
|
|
1978
|
+
input: usage.promptTokens || usage.prompt_tokens || 0,
|
|
1979
|
+
output: usage.completionTokens || usage.completion_tokens || 0,
|
|
1980
|
+
reasoning: 0, // OpenRouter doesn't expose reasoning tokens in standard format
|
|
1981
|
+
total: usage.totalTokens || usage.total_tokens || 0,
|
|
1982
|
+
provider: this.name,
|
|
1983
|
+
model,
|
|
1984
|
+
};
|
|
1985
|
+
}
|
|
1986
|
+
//
|
|
1987
|
+
// Tool Result Handling
|
|
1988
|
+
//
|
|
1989
|
+
formatToolResult(toolCall, result) {
|
|
1990
|
+
return {
|
|
1991
|
+
role: "tool",
|
|
1992
|
+
toolCallId: toolCall.callId,
|
|
1993
|
+
content: result.output,
|
|
1994
|
+
};
|
|
1995
|
+
}
|
|
1996
|
+
appendToolResult(request, toolCall, result) {
|
|
1997
|
+
const openRouterRequest = request;
|
|
1998
|
+
const toolCallRaw = toolCall.raw;
|
|
1999
|
+
// Add assistant message with the tool call (SDK uses camelCase)
|
|
2000
|
+
openRouterRequest.messages.push({
|
|
2001
|
+
role: "assistant",
|
|
2002
|
+
content: null,
|
|
2003
|
+
toolCalls: [toolCallRaw],
|
|
2004
|
+
});
|
|
2005
|
+
// Add tool result message
|
|
2006
|
+
openRouterRequest.messages.push(this.formatToolResult(toolCall, result));
|
|
2007
|
+
return openRouterRequest;
|
|
2008
|
+
}
|
|
2009
|
+
//
|
|
2010
|
+
// History Management
|
|
2011
|
+
//
|
|
2012
|
+
responseToHistoryItems(response) {
|
|
2013
|
+
const openRouterResponse = response;
|
|
2014
|
+
const historyItems = [];
|
|
2015
|
+
const choice = openRouterResponse.choices[0];
|
|
2016
|
+
if (!choice?.message) {
|
|
2017
|
+
return historyItems;
|
|
2018
|
+
}
|
|
2019
|
+
// Check if this is a tool use response (SDK returns camelCase)
|
|
2020
|
+
if (choice.message.toolCalls && choice.message.toolCalls.length > 0) {
|
|
2021
|
+
// Don't add to history yet - will be added after tool execution
|
|
2022
|
+
return historyItems;
|
|
2023
|
+
}
|
|
2024
|
+
// Extract text content for non-tool responses
|
|
2025
|
+
if (choice.message.content) {
|
|
2026
|
+
historyItems.push({
|
|
2027
|
+
content: choice.message.content,
|
|
2028
|
+
role: exports.LlmMessageRole.Assistant,
|
|
2029
|
+
type: exports.LlmMessageType.Message,
|
|
2030
|
+
});
|
|
2031
|
+
}
|
|
2032
|
+
return historyItems;
|
|
2033
|
+
}
|
|
2034
|
+
//
|
|
2035
|
+
// Error Classification
|
|
2036
|
+
//
|
|
2037
|
+
classifyError(error) {
|
|
2038
|
+
// Check if error has a status code (HTTP error)
|
|
2039
|
+
const errorWithStatus = error;
|
|
2040
|
+
const statusCode = errorWithStatus.status || errorWithStatus.statusCode;
|
|
2041
|
+
if (statusCode) {
|
|
2042
|
+
// Rate limit error
|
|
2043
|
+
if (statusCode === RATE_LIMIT_STATUS_CODE) {
|
|
2044
|
+
return {
|
|
2045
|
+
error,
|
|
2046
|
+
category: ErrorCategory.RateLimit,
|
|
2047
|
+
shouldRetry: false,
|
|
2048
|
+
suggestedDelayMs: 60000,
|
|
2049
|
+
};
|
|
2050
|
+
}
|
|
2051
|
+
// Retryable errors (server errors, timeouts, etc.)
|
|
2052
|
+
if (RETRYABLE_STATUS_CODES.includes(statusCode)) {
|
|
2053
|
+
return {
|
|
2054
|
+
error,
|
|
2055
|
+
category: ErrorCategory.Retryable,
|
|
2056
|
+
shouldRetry: true,
|
|
2057
|
+
};
|
|
2058
|
+
}
|
|
2059
|
+
// Client errors (4xx except 429) are unrecoverable
|
|
2060
|
+
if (statusCode >= 400 && statusCode < 500) {
|
|
2061
|
+
return {
|
|
2062
|
+
error,
|
|
2063
|
+
category: ErrorCategory.Unrecoverable,
|
|
2064
|
+
shouldRetry: false,
|
|
2065
|
+
};
|
|
2066
|
+
}
|
|
2067
|
+
}
|
|
2068
|
+
// Check error message for rate limit indicators
|
|
2069
|
+
const errorMessage = error instanceof Error ? error.message.toLowerCase() : "";
|
|
2070
|
+
if (errorMessage.includes("rate limit") ||
|
|
2071
|
+
errorMessage.includes("too many requests")) {
|
|
2072
|
+
return {
|
|
2073
|
+
error,
|
|
2074
|
+
category: ErrorCategory.RateLimit,
|
|
2075
|
+
shouldRetry: false,
|
|
2076
|
+
suggestedDelayMs: 60000,
|
|
2077
|
+
};
|
|
2078
|
+
}
|
|
2079
|
+
// Unknown error - treat as potentially retryable
|
|
2080
|
+
return {
|
|
2081
|
+
error,
|
|
2082
|
+
category: ErrorCategory.Unknown,
|
|
2083
|
+
shouldRetry: true,
|
|
2084
|
+
};
|
|
2085
|
+
}
|
|
2086
|
+
//
|
|
2087
|
+
// Provider-Specific Features
|
|
2088
|
+
//
|
|
2089
|
+
isComplete(response) {
|
|
2090
|
+
const openRouterResponse = response;
|
|
2091
|
+
const choice = openRouterResponse.choices[0];
|
|
2092
|
+
// Complete if no tool calls (SDK returns camelCase)
|
|
2093
|
+
if (!choice?.message?.toolCalls?.length) {
|
|
2094
|
+
return true;
|
|
2095
|
+
}
|
|
2096
|
+
return false;
|
|
2097
|
+
}
|
|
2098
|
+
hasStructuredOutput(response) {
|
|
2099
|
+
const openRouterResponse = response;
|
|
2100
|
+
const choice = openRouterResponse.choices[0];
|
|
2101
|
+
// SDK returns camelCase (toolCalls)
|
|
2102
|
+
if (!choice?.message?.toolCalls?.length) {
|
|
2103
|
+
return false;
|
|
2104
|
+
}
|
|
2105
|
+
// Check if the last tool call is structured_output
|
|
2106
|
+
const lastToolCall = choice.message.toolCalls[choice.message.toolCalls.length - 1];
|
|
2107
|
+
return lastToolCall?.function?.name === STRUCTURED_OUTPUT_TOOL_NAME;
|
|
2108
|
+
}
|
|
2109
|
+
extractStructuredOutput(response) {
|
|
2110
|
+
const openRouterResponse = response;
|
|
2111
|
+
const choice = openRouterResponse.choices[0];
|
|
2112
|
+
// SDK returns camelCase (toolCalls)
|
|
2113
|
+
if (!choice?.message?.toolCalls?.length) {
|
|
2114
|
+
return undefined;
|
|
2115
|
+
}
|
|
2116
|
+
const lastToolCall = choice.message.toolCalls[choice.message.toolCalls.length - 1];
|
|
2117
|
+
if (lastToolCall?.function?.name === STRUCTURED_OUTPUT_TOOL_NAME) {
|
|
2118
|
+
try {
|
|
2119
|
+
return JSON.parse(lastToolCall.function.arguments);
|
|
2120
|
+
}
|
|
2121
|
+
catch {
|
|
2122
|
+
return undefined;
|
|
2123
|
+
}
|
|
2124
|
+
}
|
|
2125
|
+
return undefined;
|
|
2126
|
+
}
|
|
2127
|
+
//
|
|
2128
|
+
// Private Helpers
|
|
2129
|
+
//
|
|
2130
|
+
hasToolCalls(response) {
|
|
2131
|
+
const choice = response.choices[0];
|
|
2132
|
+
// SDK returns camelCase (toolCalls)
|
|
2133
|
+
return (choice?.message?.toolCalls?.length ?? 0) > 0;
|
|
2134
|
+
}
|
|
2135
|
+
extractContent(response) {
|
|
2136
|
+
// Check for structured output first
|
|
2137
|
+
if (this.hasStructuredOutput(response)) {
|
|
2138
|
+
return this.extractStructuredOutput(response);
|
|
2139
|
+
}
|
|
2140
|
+
const choice = response.choices[0];
|
|
2141
|
+
return choice?.message?.content ?? undefined;
|
|
2142
|
+
}
|
|
2143
|
+
convertMessagesToOpenRouter(messages, system) {
|
|
2144
|
+
const openRouterMessages = [];
|
|
2145
|
+
// Add system message if provided
|
|
2146
|
+
if (system) {
|
|
2147
|
+
openRouterMessages.push({
|
|
2148
|
+
role: "system",
|
|
2149
|
+
content: system,
|
|
2150
|
+
});
|
|
2151
|
+
}
|
|
2152
|
+
for (const msg of messages) {
|
|
2153
|
+
const message = msg;
|
|
2154
|
+
// Handle different message types
|
|
2155
|
+
if (message.role === "system") {
|
|
2156
|
+
openRouterMessages.push({
|
|
2157
|
+
role: "system",
|
|
2158
|
+
content: message.content,
|
|
2159
|
+
});
|
|
2160
|
+
}
|
|
2161
|
+
else if (message.role === "user") {
|
|
2162
|
+
openRouterMessages.push({
|
|
2163
|
+
role: "user",
|
|
2164
|
+
content: message.content,
|
|
2165
|
+
});
|
|
2166
|
+
}
|
|
2167
|
+
else if (message.role === "assistant") {
|
|
2168
|
+
const assistantMsg = {
|
|
2169
|
+
role: "assistant",
|
|
2170
|
+
content: message.content || null,
|
|
2171
|
+
};
|
|
2172
|
+
// Include toolCalls if present (check both camelCase and snake_case for compatibility)
|
|
2173
|
+
if (message.toolCalls) {
|
|
2174
|
+
assistantMsg.toolCalls = message.toolCalls;
|
|
2175
|
+
}
|
|
2176
|
+
else if (message.tool_calls) {
|
|
2177
|
+
assistantMsg.toolCalls = message.tool_calls;
|
|
2178
|
+
}
|
|
2179
|
+
openRouterMessages.push(assistantMsg);
|
|
2180
|
+
}
|
|
2181
|
+
else if (message.role === "tool") {
|
|
2182
|
+
openRouterMessages.push({
|
|
2183
|
+
role: "tool",
|
|
2184
|
+
toolCallId: message.toolCallId || message.tool_call_id,
|
|
2185
|
+
content: message.content,
|
|
2186
|
+
});
|
|
2187
|
+
}
|
|
2188
|
+
else if (message.type === exports.LlmMessageType.Message) {
|
|
2189
|
+
// Handle internal message format
|
|
2190
|
+
const role = message.role?.toLowerCase();
|
|
2191
|
+
if (role === "assistant") {
|
|
2192
|
+
openRouterMessages.push({
|
|
2193
|
+
role: "assistant",
|
|
2194
|
+
content: message.content,
|
|
2195
|
+
});
|
|
2196
|
+
}
|
|
2197
|
+
else {
|
|
2198
|
+
openRouterMessages.push({
|
|
2199
|
+
role: "user",
|
|
2200
|
+
content: message.content,
|
|
2201
|
+
});
|
|
2202
|
+
}
|
|
2203
|
+
}
|
|
2204
|
+
}
|
|
2205
|
+
return openRouterMessages;
|
|
2206
|
+
}
|
|
2207
|
+
}
|
|
2208
|
+
// Export singleton instance
|
|
2209
|
+
const openRouterAdapter = new OpenRouterAdapter();
|
|
2210
|
+
|
|
1767
2211
|
const DEFAULT_TOOL_TYPE = "function";
|
|
1768
|
-
const log =
|
|
2212
|
+
const log = logger.log.lib({ lib: kit.JAYPIE.LIB.LLM });
|
|
1769
2213
|
function logToolMessage(message, context) {
|
|
1770
2214
|
log.trace.var({ [context.name]: message });
|
|
1771
2215
|
}
|
|
@@ -1831,7 +2275,7 @@ class Toolkit {
|
|
|
1831
2275
|
else if (typeof tool.message === "function") {
|
|
1832
2276
|
log.trace("[Toolkit] Tool provided function message");
|
|
1833
2277
|
log.trace("[Toolkit] Resolving message result");
|
|
1834
|
-
message = await
|
|
2278
|
+
message = await kit.resolveValue(tool.message(parsedArgs, { name }));
|
|
1835
2279
|
}
|
|
1836
2280
|
else {
|
|
1837
2281
|
log.warn("[Toolkit] Tool provided unknown message type");
|
|
@@ -1844,7 +2288,7 @@ class Toolkit {
|
|
|
1844
2288
|
}
|
|
1845
2289
|
if (typeof this.log === "function") {
|
|
1846
2290
|
log.trace("[Toolkit] Log tool call with custom logger");
|
|
1847
|
-
await
|
|
2291
|
+
await kit.resolveValue(this.log(message, context));
|
|
1848
2292
|
}
|
|
1849
2293
|
else {
|
|
1850
2294
|
log.trace("[Toolkit] Log tool call with default logger");
|
|
@@ -1857,7 +2301,7 @@ class Toolkit {
|
|
|
1857
2301
|
log.debug("[Toolkit] Continuing...");
|
|
1858
2302
|
}
|
|
1859
2303
|
}
|
|
1860
|
-
return await
|
|
2304
|
+
return await kit.resolveValue(tool.call(parsedArgs));
|
|
1861
2305
|
}
|
|
1862
2306
|
extend(tools, options = {}) {
|
|
1863
2307
|
for (const tool of tools) {
|
|
@@ -1905,7 +2349,7 @@ class HookRunner {
|
|
|
1905
2349
|
*/
|
|
1906
2350
|
async runBeforeModelRequest(hooks, context) {
|
|
1907
2351
|
if (hooks?.beforeEachModelRequest) {
|
|
1908
|
-
await
|
|
2352
|
+
await kit.resolveValue(hooks.beforeEachModelRequest(context));
|
|
1909
2353
|
}
|
|
1910
2354
|
}
|
|
1911
2355
|
/**
|
|
@@ -1913,7 +2357,7 @@ class HookRunner {
|
|
|
1913
2357
|
*/
|
|
1914
2358
|
async runAfterModelResponse(hooks, context) {
|
|
1915
2359
|
if (hooks?.afterEachModelResponse) {
|
|
1916
|
-
await
|
|
2360
|
+
await kit.resolveValue(hooks.afterEachModelResponse(context));
|
|
1917
2361
|
}
|
|
1918
2362
|
}
|
|
1919
2363
|
/**
|
|
@@ -1921,7 +2365,7 @@ class HookRunner {
|
|
|
1921
2365
|
*/
|
|
1922
2366
|
async runBeforeTool(hooks, context) {
|
|
1923
2367
|
if (hooks?.beforeEachTool) {
|
|
1924
|
-
await
|
|
2368
|
+
await kit.resolveValue(hooks.beforeEachTool(context));
|
|
1925
2369
|
}
|
|
1926
2370
|
}
|
|
1927
2371
|
/**
|
|
@@ -1929,7 +2373,7 @@ class HookRunner {
|
|
|
1929
2373
|
*/
|
|
1930
2374
|
async runAfterTool(hooks, context) {
|
|
1931
2375
|
if (hooks?.afterEachTool) {
|
|
1932
|
-
await
|
|
2376
|
+
await kit.resolveValue(hooks.afterEachTool(context));
|
|
1933
2377
|
}
|
|
1934
2378
|
}
|
|
1935
2379
|
/**
|
|
@@ -1937,7 +2381,7 @@ class HookRunner {
|
|
|
1937
2381
|
*/
|
|
1938
2382
|
async runOnToolError(hooks, context) {
|
|
1939
2383
|
if (hooks?.onToolError) {
|
|
1940
|
-
await
|
|
2384
|
+
await kit.resolveValue(hooks.onToolError(context));
|
|
1941
2385
|
}
|
|
1942
2386
|
}
|
|
1943
2387
|
/**
|
|
@@ -1945,7 +2389,7 @@ class HookRunner {
|
|
|
1945
2389
|
*/
|
|
1946
2390
|
async runOnRetryableError(hooks, context) {
|
|
1947
2391
|
if (hooks?.onRetryableModelError) {
|
|
1948
|
-
await
|
|
2392
|
+
await kit.resolveValue(hooks.onRetryableModelError(context));
|
|
1949
2393
|
}
|
|
1950
2394
|
}
|
|
1951
2395
|
/**
|
|
@@ -1953,7 +2397,7 @@ class HookRunner {
|
|
|
1953
2397
|
*/
|
|
1954
2398
|
async runOnUnrecoverableError(hooks, context) {
|
|
1955
2399
|
if (hooks?.onUnrecoverableModelError) {
|
|
1956
|
-
await
|
|
2400
|
+
await kit.resolveValue(hooks.onUnrecoverableModelError(context));
|
|
1957
2401
|
}
|
|
1958
2402
|
}
|
|
1959
2403
|
}
|
|
@@ -2019,7 +2463,7 @@ class InputProcessor {
|
|
|
2019
2463
|
if (options.data &&
|
|
2020
2464
|
(options.placeholders?.instructions === undefined ||
|
|
2021
2465
|
options.placeholders?.instructions)) {
|
|
2022
|
-
return
|
|
2466
|
+
return kit.placeholders(options.instructions, options.data);
|
|
2023
2467
|
}
|
|
2024
2468
|
return options.instructions;
|
|
2025
2469
|
}
|
|
@@ -2032,7 +2476,7 @@ class InputProcessor {
|
|
|
2032
2476
|
}
|
|
2033
2477
|
// Apply placeholders to system if data is provided and placeholders.system is not false
|
|
2034
2478
|
if (options.data && options.placeholders?.system !== false) {
|
|
2035
|
-
return
|
|
2479
|
+
return kit.placeholders(options.system, options.data);
|
|
2036
2480
|
}
|
|
2037
2481
|
return options.system;
|
|
2038
2482
|
}
|
|
@@ -2316,7 +2760,7 @@ class RetryExecutor {
|
|
|
2316
2760
|
providerRequest: options.context.providerRequest,
|
|
2317
2761
|
error,
|
|
2318
2762
|
});
|
|
2319
|
-
await
|
|
2763
|
+
await kit.sleep(delay);
|
|
2320
2764
|
attempt++;
|
|
2321
2765
|
}
|
|
2322
2766
|
}
|
|
@@ -2707,46 +3151,60 @@ function createOperateLoop(config) {
|
|
|
2707
3151
|
return new OperateLoop(config);
|
|
2708
3152
|
}
|
|
2709
3153
|
|
|
3154
|
+
// SDK loader with caching
|
|
3155
|
+
let cachedSdk$2 = null;
|
|
3156
|
+
async function loadSdk$2() {
|
|
3157
|
+
if (cachedSdk$2)
|
|
3158
|
+
return cachedSdk$2;
|
|
3159
|
+
try {
|
|
3160
|
+
cachedSdk$2 = await import('@anthropic-ai/sdk');
|
|
3161
|
+
return cachedSdk$2;
|
|
3162
|
+
}
|
|
3163
|
+
catch {
|
|
3164
|
+
throw new errors.ConfigurationError("@anthropic-ai/sdk is required but not installed. Run: npm install @anthropic-ai/sdk");
|
|
3165
|
+
}
|
|
3166
|
+
}
|
|
2710
3167
|
// Logger
|
|
2711
|
-
const getLogger$
|
|
3168
|
+
const getLogger$3 = () => logger.log.lib({ lib: kit.JAYPIE.LIB.LLM });
|
|
2712
3169
|
// Client initialization
|
|
2713
|
-
async function initializeClient$
|
|
2714
|
-
const logger = getLogger$
|
|
3170
|
+
async function initializeClient$3({ apiKey, } = {}) {
|
|
3171
|
+
const logger = getLogger$3();
|
|
2715
3172
|
const resolvedApiKey = apiKey || (await aws.getEnvSecret("ANTHROPIC_API_KEY"));
|
|
2716
3173
|
if (!resolvedApiKey) {
|
|
2717
|
-
throw new
|
|
3174
|
+
throw new errors.ConfigurationError("The application could not resolve the required API key: ANTHROPIC_API_KEY");
|
|
2718
3175
|
}
|
|
2719
|
-
const
|
|
3176
|
+
const sdk = await loadSdk$2();
|
|
3177
|
+
const client = new sdk.default({ apiKey: resolvedApiKey });
|
|
2720
3178
|
logger.trace("Initialized Anthropic client");
|
|
2721
3179
|
return client;
|
|
2722
3180
|
}
|
|
2723
3181
|
// Message formatting functions
|
|
2724
|
-
function formatSystemMessage$
|
|
3182
|
+
function formatSystemMessage$2(systemPrompt, { data, placeholders } = {}) {
|
|
2725
3183
|
return placeholders?.system === false
|
|
2726
3184
|
? systemPrompt
|
|
2727
|
-
:
|
|
3185
|
+
: kit.placeholders(systemPrompt, data);
|
|
2728
3186
|
}
|
|
2729
|
-
function formatUserMessage$
|
|
3187
|
+
function formatUserMessage$3(message, { data, placeholders } = {}) {
|
|
2730
3188
|
const content = placeholders?.message === false
|
|
2731
3189
|
? message
|
|
2732
|
-
:
|
|
3190
|
+
: kit.placeholders(message, data);
|
|
2733
3191
|
return {
|
|
2734
3192
|
role: PROVIDER.ANTHROPIC.ROLE.USER,
|
|
2735
3193
|
content,
|
|
2736
3194
|
};
|
|
2737
3195
|
}
|
|
2738
|
-
function prepareMessages$
|
|
2739
|
-
const logger = getLogger$
|
|
3196
|
+
function prepareMessages$3(message, { data, placeholders } = {}) {
|
|
3197
|
+
const logger = getLogger$3();
|
|
2740
3198
|
const messages = [];
|
|
2741
3199
|
// Add user message (necessary for all requests)
|
|
2742
|
-
const userMessage = formatUserMessage$
|
|
3200
|
+
const userMessage = formatUserMessage$3(message, { data, placeholders });
|
|
2743
3201
|
messages.push(userMessage);
|
|
2744
3202
|
logger.trace(`User message: ${userMessage.content.length} characters`);
|
|
2745
3203
|
return messages;
|
|
2746
3204
|
}
|
|
2747
3205
|
// Basic text completion
|
|
2748
3206
|
async function createTextCompletion$1(client, messages, model, systemMessage) {
|
|
2749
|
-
|
|
3207
|
+
logger.log.trace("Using text output (unstructured)");
|
|
2750
3208
|
const params = {
|
|
2751
3209
|
model,
|
|
2752
3210
|
messages,
|
|
@@ -2755,17 +3213,17 @@ async function createTextCompletion$1(client, messages, model, systemMessage) {
|
|
|
2755
3213
|
// Add system instruction if provided
|
|
2756
3214
|
if (systemMessage) {
|
|
2757
3215
|
params.system = systemMessage;
|
|
2758
|
-
|
|
3216
|
+
logger.log.trace(`System message: ${systemMessage.length} characters`);
|
|
2759
3217
|
}
|
|
2760
3218
|
const response = await client.messages.create(params);
|
|
2761
3219
|
const firstContent = response.content[0];
|
|
2762
3220
|
const text = firstContent && "text" in firstContent ? firstContent.text : "";
|
|
2763
|
-
|
|
3221
|
+
logger.log.trace(`Assistant reply: ${text.length} characters`);
|
|
2764
3222
|
return text;
|
|
2765
3223
|
}
|
|
2766
3224
|
// Structured output completion
|
|
2767
3225
|
async function createStructuredCompletion$1(client, messages, model, responseSchema, systemMessage) {
|
|
2768
|
-
|
|
3226
|
+
logger.log.trace("Using structured output");
|
|
2769
3227
|
// Get the JSON schema for the response
|
|
2770
3228
|
const schema = responseSchema instanceof v4.z.ZodType
|
|
2771
3229
|
? responseSchema
|
|
@@ -2798,7 +3256,7 @@ async function createStructuredCompletion$1(client, messages, model, responseSch
|
|
|
2798
3256
|
if (!schema.parse(result)) {
|
|
2799
3257
|
throw new Error(`JSON response from Anthropic does not match schema: ${responseText}`);
|
|
2800
3258
|
}
|
|
2801
|
-
|
|
3259
|
+
logger.log.trace("Received structured response", { result });
|
|
2802
3260
|
return result;
|
|
2803
3261
|
}
|
|
2804
3262
|
catch {
|
|
@@ -2809,7 +3267,7 @@ async function createStructuredCompletion$1(client, messages, model, responseSch
|
|
|
2809
3267
|
throw new Error("Failed to parse structured response from Anthropic");
|
|
2810
3268
|
}
|
|
2811
3269
|
catch (error) {
|
|
2812
|
-
|
|
3270
|
+
logger.log.error("Error creating structured completion", { error });
|
|
2813
3271
|
throw error;
|
|
2814
3272
|
}
|
|
2815
3273
|
}
|
|
@@ -2825,7 +3283,7 @@ async function createStructuredCompletion$1(client, messages, model, responseSch
|
|
|
2825
3283
|
// Main class implementation
|
|
2826
3284
|
class AnthropicProvider {
|
|
2827
3285
|
constructor(model = PROVIDER.ANTHROPIC.MODEL.DEFAULT, { apiKey } = {}) {
|
|
2828
|
-
this.log = getLogger$
|
|
3286
|
+
this.log = getLogger$3();
|
|
2829
3287
|
this.conversationHistory = [];
|
|
2830
3288
|
this.model = model;
|
|
2831
3289
|
this.apiKey = apiKey;
|
|
@@ -2834,7 +3292,7 @@ class AnthropicProvider {
|
|
|
2834
3292
|
if (this._client) {
|
|
2835
3293
|
return this._client;
|
|
2836
3294
|
}
|
|
2837
|
-
this._client = await initializeClient$
|
|
3295
|
+
this._client = await initializeClient$3({ apiKey: this.apiKey });
|
|
2838
3296
|
return this._client;
|
|
2839
3297
|
}
|
|
2840
3298
|
async getOperateLoop() {
|
|
@@ -2851,12 +3309,12 @@ class AnthropicProvider {
|
|
|
2851
3309
|
// Main send method
|
|
2852
3310
|
async send(message, options) {
|
|
2853
3311
|
const client = await this.getClient();
|
|
2854
|
-
const messages = prepareMessages$
|
|
3312
|
+
const messages = prepareMessages$3(message, options || {});
|
|
2855
3313
|
const modelToUse = options?.model || this.model;
|
|
2856
3314
|
// Process system message if provided
|
|
2857
3315
|
let systemMessage;
|
|
2858
3316
|
if (options?.system) {
|
|
2859
|
-
systemMessage = formatSystemMessage$
|
|
3317
|
+
systemMessage = formatSystemMessage$2(options.system, {
|
|
2860
3318
|
data: options.data,
|
|
2861
3319
|
placeholders: options.placeholders,
|
|
2862
3320
|
});
|
|
@@ -2886,36 +3344,50 @@ class AnthropicProvider {
|
|
|
2886
3344
|
}
|
|
2887
3345
|
}
|
|
2888
3346
|
|
|
3347
|
+
// SDK loader with caching
|
|
3348
|
+
let cachedSdk$1 = null;
|
|
3349
|
+
async function loadSdk$1() {
|
|
3350
|
+
if (cachedSdk$1)
|
|
3351
|
+
return cachedSdk$1;
|
|
3352
|
+
try {
|
|
3353
|
+
cachedSdk$1 = await import('@google/genai');
|
|
3354
|
+
return cachedSdk$1;
|
|
3355
|
+
}
|
|
3356
|
+
catch {
|
|
3357
|
+
throw new errors.ConfigurationError("@google/genai is required but not installed. Run: npm install @google/genai");
|
|
3358
|
+
}
|
|
3359
|
+
}
|
|
2889
3360
|
// Logger
|
|
2890
|
-
const getLogger$
|
|
3361
|
+
const getLogger$2 = () => logger.log.lib({ lib: kit.JAYPIE.LIB.LLM });
|
|
2891
3362
|
// Client initialization
|
|
2892
|
-
async function initializeClient$
|
|
2893
|
-
const logger = getLogger$
|
|
3363
|
+
async function initializeClient$2({ apiKey, } = {}) {
|
|
3364
|
+
const logger = getLogger$2();
|
|
2894
3365
|
const resolvedApiKey = apiKey || (await aws.getEnvSecret("GEMINI_API_KEY"));
|
|
2895
3366
|
if (!resolvedApiKey) {
|
|
2896
|
-
throw new
|
|
3367
|
+
throw new errors.ConfigurationError("The application could not resolve the requested keys");
|
|
2897
3368
|
}
|
|
2898
|
-
const
|
|
3369
|
+
const sdk = await loadSdk$1();
|
|
3370
|
+
const client = new sdk.GoogleGenAI({ apiKey: resolvedApiKey });
|
|
2899
3371
|
logger.trace("Initialized Gemini client");
|
|
2900
3372
|
return client;
|
|
2901
3373
|
}
|
|
2902
|
-
function formatUserMessage$
|
|
3374
|
+
function formatUserMessage$2(message, { data, placeholders } = {}) {
|
|
2903
3375
|
const content = placeholders?.message === false
|
|
2904
3376
|
? message
|
|
2905
|
-
:
|
|
3377
|
+
: kit.placeholders(message, data);
|
|
2906
3378
|
return {
|
|
2907
3379
|
role: "user",
|
|
2908
3380
|
content,
|
|
2909
3381
|
};
|
|
2910
3382
|
}
|
|
2911
|
-
function prepareMessages$
|
|
2912
|
-
const logger = getLogger$
|
|
3383
|
+
function prepareMessages$2(message, { data, placeholders } = {}) {
|
|
3384
|
+
const logger = getLogger$2();
|
|
2913
3385
|
const messages = [];
|
|
2914
3386
|
let systemInstruction;
|
|
2915
3387
|
// Note: Gemini handles system prompts differently via systemInstruction config
|
|
2916
3388
|
// This function is kept for compatibility but system prompts should be passed
|
|
2917
3389
|
// via the systemInstruction parameter in generateContent
|
|
2918
|
-
const userMessage = formatUserMessage$
|
|
3390
|
+
const userMessage = formatUserMessage$2(message, { data, placeholders });
|
|
2919
3391
|
messages.push(userMessage);
|
|
2920
3392
|
logger.trace(`User message: ${userMessage.content?.length} characters`);
|
|
2921
3393
|
return { messages, systemInstruction };
|
|
@@ -2923,7 +3395,7 @@ function prepareMessages$1(message, { data, placeholders } = {}) {
|
|
|
2923
3395
|
|
|
2924
3396
|
class GeminiProvider {
|
|
2925
3397
|
constructor(model = PROVIDER.GEMINI.MODEL.DEFAULT, { apiKey } = {}) {
|
|
2926
|
-
this.log = getLogger$
|
|
3398
|
+
this.log = getLogger$2();
|
|
2927
3399
|
this.conversationHistory = [];
|
|
2928
3400
|
this.model = model;
|
|
2929
3401
|
this.apiKey = apiKey;
|
|
@@ -2932,7 +3404,7 @@ class GeminiProvider {
|
|
|
2932
3404
|
if (this._client) {
|
|
2933
3405
|
return this._client;
|
|
2934
3406
|
}
|
|
2935
|
-
this._client = await initializeClient$
|
|
3407
|
+
this._client = await initializeClient$2({ apiKey: this.apiKey });
|
|
2936
3408
|
return this._client;
|
|
2937
3409
|
}
|
|
2938
3410
|
async getOperateLoop() {
|
|
@@ -2948,7 +3420,7 @@ class GeminiProvider {
|
|
|
2948
3420
|
}
|
|
2949
3421
|
async send(message, options) {
|
|
2950
3422
|
const client = await this.getClient();
|
|
2951
|
-
const { messages } = prepareMessages$
|
|
3423
|
+
const { messages } = prepareMessages$2(message, options);
|
|
2952
3424
|
const modelToUse = options?.model || this.model;
|
|
2953
3425
|
// Build the request config
|
|
2954
3426
|
const config = {};
|
|
@@ -3003,52 +3475,52 @@ class GeminiProvider {
|
|
|
3003
3475
|
}
|
|
3004
3476
|
|
|
3005
3477
|
// Logger
|
|
3006
|
-
const getLogger = () =>
|
|
3478
|
+
const getLogger$1 = () => logger.log.lib({ lib: kit.JAYPIE.LIB.LLM });
|
|
3007
3479
|
// Client initialization
|
|
3008
|
-
async function initializeClient({ apiKey, } = {}) {
|
|
3009
|
-
const logger = getLogger();
|
|
3480
|
+
async function initializeClient$1({ apiKey, } = {}) {
|
|
3481
|
+
const logger = getLogger$1();
|
|
3010
3482
|
const resolvedApiKey = apiKey || (await aws.getEnvSecret("OPENAI_API_KEY"));
|
|
3011
3483
|
if (!resolvedApiKey) {
|
|
3012
|
-
throw new
|
|
3484
|
+
throw new errors.ConfigurationError("The application could not resolve the requested keys");
|
|
3013
3485
|
}
|
|
3014
3486
|
const client = new openai.OpenAI({ apiKey: resolvedApiKey });
|
|
3015
3487
|
logger.trace("Initialized OpenAI client");
|
|
3016
3488
|
return client;
|
|
3017
3489
|
}
|
|
3018
|
-
function formatSystemMessage(systemPrompt, { data, placeholders } = {}) {
|
|
3490
|
+
function formatSystemMessage$1(systemPrompt, { data, placeholders } = {}) {
|
|
3019
3491
|
const content = placeholders?.system === false
|
|
3020
3492
|
? systemPrompt
|
|
3021
|
-
:
|
|
3493
|
+
: kit.placeholders(systemPrompt, data);
|
|
3022
3494
|
return {
|
|
3023
3495
|
role: "developer",
|
|
3024
3496
|
content,
|
|
3025
3497
|
};
|
|
3026
3498
|
}
|
|
3027
|
-
function formatUserMessage(message, { data, placeholders } = {}) {
|
|
3499
|
+
function formatUserMessage$1(message, { data, placeholders } = {}) {
|
|
3028
3500
|
const content = placeholders?.message === false
|
|
3029
3501
|
? message
|
|
3030
|
-
:
|
|
3502
|
+
: kit.placeholders(message, data);
|
|
3031
3503
|
return {
|
|
3032
3504
|
role: "user",
|
|
3033
3505
|
content,
|
|
3034
3506
|
};
|
|
3035
3507
|
}
|
|
3036
|
-
function prepareMessages(message, { system, data, placeholders } = {}) {
|
|
3037
|
-
const logger = getLogger();
|
|
3508
|
+
function prepareMessages$1(message, { system, data, placeholders } = {}) {
|
|
3509
|
+
const logger = getLogger$1();
|
|
3038
3510
|
const messages = [];
|
|
3039
3511
|
if (system) {
|
|
3040
|
-
const systemMessage = formatSystemMessage(system, { data, placeholders });
|
|
3512
|
+
const systemMessage = formatSystemMessage$1(system, { data, placeholders });
|
|
3041
3513
|
messages.push(systemMessage);
|
|
3042
3514
|
logger.trace(`System message: ${systemMessage.content?.length} characters`);
|
|
3043
3515
|
}
|
|
3044
|
-
const userMessage = formatUserMessage(message, { data, placeholders });
|
|
3516
|
+
const userMessage = formatUserMessage$1(message, { data, placeholders });
|
|
3045
3517
|
messages.push(userMessage);
|
|
3046
3518
|
logger.trace(`User message: ${userMessage.content?.length} characters`);
|
|
3047
3519
|
return messages;
|
|
3048
3520
|
}
|
|
3049
3521
|
// Completion requests
|
|
3050
3522
|
async function createStructuredCompletion(client, { messages, responseSchema, model, }) {
|
|
3051
|
-
const logger = getLogger();
|
|
3523
|
+
const logger = getLogger$1();
|
|
3052
3524
|
logger.trace("Using structured output");
|
|
3053
3525
|
const zodSchema = responseSchema instanceof v4.z.ZodType
|
|
3054
3526
|
? responseSchema
|
|
@@ -3079,7 +3551,7 @@ async function createStructuredCompletion(client, { messages, responseSchema, mo
|
|
|
3079
3551
|
return completion.choices[0].message.parsed;
|
|
3080
3552
|
}
|
|
3081
3553
|
async function createTextCompletion(client, { messages, model, }) {
|
|
3082
|
-
const logger = getLogger();
|
|
3554
|
+
const logger = getLogger$1();
|
|
3083
3555
|
logger.trace("Using text output (unstructured)");
|
|
3084
3556
|
const completion = await client.chat.completions.create({
|
|
3085
3557
|
messages,
|
|
@@ -3091,7 +3563,7 @@ async function createTextCompletion(client, { messages, model, }) {
|
|
|
3091
3563
|
|
|
3092
3564
|
class OpenAiProvider {
|
|
3093
3565
|
constructor(model = PROVIDER.OPENAI.MODEL.DEFAULT, { apiKey } = {}) {
|
|
3094
|
-
this.log = getLogger();
|
|
3566
|
+
this.log = getLogger$1();
|
|
3095
3567
|
this.conversationHistory = [];
|
|
3096
3568
|
this.model = model;
|
|
3097
3569
|
this.apiKey = apiKey;
|
|
@@ -3100,7 +3572,7 @@ class OpenAiProvider {
|
|
|
3100
3572
|
if (this._client) {
|
|
3101
3573
|
return this._client;
|
|
3102
3574
|
}
|
|
3103
|
-
this._client = await initializeClient({ apiKey: this.apiKey });
|
|
3575
|
+
this._client = await initializeClient$1({ apiKey: this.apiKey });
|
|
3104
3576
|
return this._client;
|
|
3105
3577
|
}
|
|
3106
3578
|
async getOperateLoop() {
|
|
@@ -3116,7 +3588,7 @@ class OpenAiProvider {
|
|
|
3116
3588
|
}
|
|
3117
3589
|
async send(message, options) {
|
|
3118
3590
|
const client = await this.getClient();
|
|
3119
|
-
const messages = prepareMessages(message, options || {});
|
|
3591
|
+
const messages = prepareMessages$1(message, options || {});
|
|
3120
3592
|
const modelToUse = options?.model || this.model;
|
|
3121
3593
|
if (options?.response) {
|
|
3122
3594
|
return createStructuredCompletion(client, {
|
|
@@ -3150,6 +3622,145 @@ class OpenAiProvider {
|
|
|
3150
3622
|
}
|
|
3151
3623
|
}
|
|
3152
3624
|
|
|
3625
|
+
// SDK loader with caching
|
|
3626
|
+
let cachedSdk = null;
|
|
3627
|
+
async function loadSdk() {
|
|
3628
|
+
if (cachedSdk)
|
|
3629
|
+
return cachedSdk;
|
|
3630
|
+
try {
|
|
3631
|
+
cachedSdk = await import('@openrouter/sdk');
|
|
3632
|
+
return cachedSdk;
|
|
3633
|
+
}
|
|
3634
|
+
catch {
|
|
3635
|
+
throw new errors.ConfigurationError("@openrouter/sdk is required but not installed. Run: npm install @openrouter/sdk");
|
|
3636
|
+
}
|
|
3637
|
+
}
|
|
3638
|
+
// Logger
|
|
3639
|
+
const getLogger = () => logger.log.lib({ lib: kit.JAYPIE.LIB.LLM });
|
|
3640
|
+
// Client initialization
|
|
3641
|
+
async function initializeClient({ apiKey, } = {}) {
|
|
3642
|
+
const logger = getLogger();
|
|
3643
|
+
const resolvedApiKey = apiKey || (await aws.getEnvSecret("OPENROUTER_API_KEY"));
|
|
3644
|
+
if (!resolvedApiKey) {
|
|
3645
|
+
throw new errors.ConfigurationError("The application could not resolve the requested keys");
|
|
3646
|
+
}
|
|
3647
|
+
const sdk = await loadSdk();
|
|
3648
|
+
const client = new sdk.OpenRouter({ apiKey: resolvedApiKey });
|
|
3649
|
+
logger.trace("Initialized OpenRouter client");
|
|
3650
|
+
return client;
|
|
3651
|
+
}
|
|
3652
|
+
// Get default model from environment or constants
|
|
3653
|
+
function getDefaultModel() {
|
|
3654
|
+
return process.env.OPENROUTER_MODEL || PROVIDER.OPENROUTER.MODEL.DEFAULT;
|
|
3655
|
+
}
|
|
3656
|
+
function formatSystemMessage(systemPrompt, { data, placeholders } = {}) {
|
|
3657
|
+
const content = placeholders?.system === false
|
|
3658
|
+
? systemPrompt
|
|
3659
|
+
: kit.placeholders(systemPrompt, data);
|
|
3660
|
+
return {
|
|
3661
|
+
role: "system",
|
|
3662
|
+
content,
|
|
3663
|
+
};
|
|
3664
|
+
}
|
|
3665
|
+
function formatUserMessage(message, { data, placeholders } = {}) {
|
|
3666
|
+
const content = placeholders?.message === false
|
|
3667
|
+
? message
|
|
3668
|
+
: kit.placeholders(message, data);
|
|
3669
|
+
return {
|
|
3670
|
+
role: "user",
|
|
3671
|
+
content,
|
|
3672
|
+
};
|
|
3673
|
+
}
|
|
3674
|
+
function prepareMessages(message, { system, data, placeholders } = {}) {
|
|
3675
|
+
const logger = getLogger();
|
|
3676
|
+
const messages = [];
|
|
3677
|
+
if (system) {
|
|
3678
|
+
const systemMessage = formatSystemMessage(system, { data, placeholders });
|
|
3679
|
+
messages.push(systemMessage);
|
|
3680
|
+
logger.trace(`System message: ${systemMessage.content?.length} characters`);
|
|
3681
|
+
}
|
|
3682
|
+
const userMessage = formatUserMessage(message, { data, placeholders });
|
|
3683
|
+
messages.push(userMessage);
|
|
3684
|
+
logger.trace(`User message: ${userMessage.content?.length} characters`);
|
|
3685
|
+
return messages;
|
|
3686
|
+
}
|
|
3687
|
+
|
|
3688
|
+
class OpenRouterProvider {
|
|
3689
|
+
constructor(model = getDefaultModel(), { apiKey } = {}) {
|
|
3690
|
+
this.log = getLogger();
|
|
3691
|
+
this.conversationHistory = [];
|
|
3692
|
+
this.model = model;
|
|
3693
|
+
this.apiKey = apiKey;
|
|
3694
|
+
}
|
|
3695
|
+
async getClient() {
|
|
3696
|
+
if (this._client) {
|
|
3697
|
+
return this._client;
|
|
3698
|
+
}
|
|
3699
|
+
this._client = await initializeClient({ apiKey: this.apiKey });
|
|
3700
|
+
return this._client;
|
|
3701
|
+
}
|
|
3702
|
+
async getOperateLoop() {
|
|
3703
|
+
if (this._operateLoop) {
|
|
3704
|
+
return this._operateLoop;
|
|
3705
|
+
}
|
|
3706
|
+
const client = await this.getClient();
|
|
3707
|
+
this._operateLoop = createOperateLoop({
|
|
3708
|
+
adapter: openRouterAdapter,
|
|
3709
|
+
client,
|
|
3710
|
+
});
|
|
3711
|
+
return this._operateLoop;
|
|
3712
|
+
}
|
|
3713
|
+
async send(message, options) {
|
|
3714
|
+
const client = await this.getClient();
|
|
3715
|
+
const messages = prepareMessages(message, options);
|
|
3716
|
+
const modelToUse = options?.model || this.model;
|
|
3717
|
+
// Build the request
|
|
3718
|
+
const response = await client.chat.send({
|
|
3719
|
+
model: modelToUse,
|
|
3720
|
+
messages: messages,
|
|
3721
|
+
});
|
|
3722
|
+
const rawContent = response.choices?.[0]?.message?.content;
|
|
3723
|
+
// Extract text content - content could be string or array of content items
|
|
3724
|
+
const content = typeof rawContent === "string"
|
|
3725
|
+
? rawContent
|
|
3726
|
+
: Array.isArray(rawContent)
|
|
3727
|
+
? rawContent
|
|
3728
|
+
.filter((item) => item.type === "text")
|
|
3729
|
+
.map((item) => item.text)
|
|
3730
|
+
.join("")
|
|
3731
|
+
: "";
|
|
3732
|
+
this.log.trace(`Assistant reply: ${content?.length || 0} characters`);
|
|
3733
|
+
// If structured output was requested, try to parse the response
|
|
3734
|
+
if (options?.response && content) {
|
|
3735
|
+
try {
|
|
3736
|
+
return JSON.parse(content);
|
|
3737
|
+
}
|
|
3738
|
+
catch {
|
|
3739
|
+
return content || "";
|
|
3740
|
+
}
|
|
3741
|
+
}
|
|
3742
|
+
return content || "";
|
|
3743
|
+
}
|
|
3744
|
+
async operate(input, options = {}) {
|
|
3745
|
+
const operateLoop = await this.getOperateLoop();
|
|
3746
|
+
const mergedOptions = { ...options, model: options.model ?? this.model };
|
|
3747
|
+
// Create a merged history including both the tracked history and any explicitly provided history
|
|
3748
|
+
if (this.conversationHistory.length > 0) {
|
|
3749
|
+
// If options.history exists, merge with instance history, otherwise use instance history
|
|
3750
|
+
mergedOptions.history = options.history
|
|
3751
|
+
? [...this.conversationHistory, ...options.history]
|
|
3752
|
+
: [...this.conversationHistory];
|
|
3753
|
+
}
|
|
3754
|
+
// Execute operate loop
|
|
3755
|
+
const response = await operateLoop.execute(input, mergedOptions);
|
|
3756
|
+
// Update conversation history with the new history from the response
|
|
3757
|
+
if (response.history && response.history.length > 0) {
|
|
3758
|
+
this.conversationHistory = response.history;
|
|
3759
|
+
}
|
|
3760
|
+
return response;
|
|
3761
|
+
}
|
|
3762
|
+
}
|
|
3763
|
+
|
|
3153
3764
|
class Llm {
|
|
3154
3765
|
constructor(providerName = DEFAULT.PROVIDER.NAME, options = {}) {
|
|
3155
3766
|
const { model } = options;
|
|
@@ -3198,6 +3809,10 @@ class Llm {
|
|
|
3198
3809
|
return new OpenAiProvider(model || PROVIDER.OPENAI.MODEL.DEFAULT, {
|
|
3199
3810
|
apiKey,
|
|
3200
3811
|
});
|
|
3812
|
+
case PROVIDER.OPENROUTER.NAME:
|
|
3813
|
+
return new OpenRouterProvider(model || PROVIDER.OPENROUTER.MODEL.DEFAULT, {
|
|
3814
|
+
apiKey,
|
|
3815
|
+
});
|
|
3201
3816
|
default:
|
|
3202
3817
|
throw new errors.ConfigurationError(`Unsupported provider: ${providerName}`);
|
|
3203
3818
|
}
|
|
@@ -3533,6 +4148,7 @@ exports.GeminiProvider = GeminiProvider;
|
|
|
3533
4148
|
exports.JaypieToolkit = JaypieToolkit;
|
|
3534
4149
|
exports.LLM = constants;
|
|
3535
4150
|
exports.Llm = Llm;
|
|
4151
|
+
exports.OpenRouterProvider = OpenRouterProvider;
|
|
3536
4152
|
exports.Toolkit = Toolkit;
|
|
3537
4153
|
exports.toolkit = toolkit;
|
|
3538
4154
|
exports.tools = tools;
|