@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.
Files changed (35) hide show
  1. package/dist/cjs/constants.d.ts +17 -1
  2. package/dist/cjs/index.cjs +725 -109
  3. package/dist/cjs/index.cjs.map +1 -1
  4. package/dist/cjs/index.d.ts +1 -0
  5. package/dist/cjs/operate/adapters/AnthropicAdapter.d.ts +1 -1
  6. package/dist/cjs/operate/adapters/OpenRouterAdapter.d.ts +96 -0
  7. package/dist/cjs/operate/adapters/index.d.ts +1 -0
  8. package/dist/cjs/operate/index.d.ts +1 -1
  9. package/dist/cjs/providers/anthropic/utils.d.ts +3 -37
  10. package/dist/cjs/providers/gemini/utils.d.ts +3 -37
  11. package/dist/cjs/providers/openai/utils.d.ts +1 -36
  12. package/dist/cjs/providers/openrouter/OpenRouterProvider.class.d.ts +17 -0
  13. package/dist/cjs/providers/openrouter/index.d.ts +2 -0
  14. package/dist/cjs/providers/openrouter/utils.d.ts +15 -0
  15. package/dist/cjs/util/logger.d.ts +2 -71
  16. package/dist/esm/constants.d.ts +17 -1
  17. package/dist/esm/index.d.ts +1 -0
  18. package/dist/esm/index.js +709 -94
  19. package/dist/esm/index.js.map +1 -1
  20. package/dist/esm/operate/adapters/AnthropicAdapter.d.ts +1 -1
  21. package/dist/esm/operate/adapters/OpenRouterAdapter.d.ts +96 -0
  22. package/dist/esm/operate/adapters/index.d.ts +1 -0
  23. package/dist/esm/operate/index.d.ts +1 -1
  24. package/dist/esm/providers/anthropic/utils.d.ts +3 -37
  25. package/dist/esm/providers/gemini/utils.d.ts +3 -37
  26. package/dist/esm/providers/openai/utils.d.ts +1 -36
  27. package/dist/esm/providers/openrouter/OpenRouterProvider.class.d.ts +17 -0
  28. package/dist/esm/providers/openrouter/index.d.ts +2 -0
  29. package/dist/esm/providers/openrouter/utils.d.ts +15 -0
  30. package/dist/esm/util/logger.d.ts +2 -71
  31. package/package.json +24 -7
  32. package/dist/cjs/providers/anthropic/operate.d.ts +0 -16
  33. package/dist/cjs/providers/openai/operate.d.ts +0 -26
  34. package/dist/esm/providers/anthropic/operate.d.ts +0 -16
  35. package/dist/esm/providers/openai/operate.d.ts +0 -26
@@ -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 core = require('@jaypie/core');
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 ? core.placeholders(input, options.data) : input;
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$3 = () => core.log.lib({ lib: core.JAYPIE.LIB.LLM });
346
- const log$1 = getLogger$3();
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$1 = "structured_output";
650
- const RETRYABLE_ERROR_TYPES$1 = [
651
- Anthropic.APIConnectionError,
652
- Anthropic.APIConnectionTimeoutError,
653
- Anthropic.InternalServerError,
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 NOT_RETRYABLE_ERROR_TYPES$1 = [
656
- Anthropic.AuthenticationError,
657
- Anthropic.BadRequestError,
658
- Anthropic.NotFoundError,
659
- Anthropic.PermissionDeniedError,
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$1);
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$1,
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 (error instanceof Anthropic.RateLimitError) {
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
- for (const ErrorType of RETRYABLE_ERROR_TYPES$1) {
878
- if (error instanceof ErrorType) {
879
- return {
880
- error,
881
- category: ErrorCategory.Retryable,
882
- shouldRetry: true,
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
- for (const ErrorType of NOT_RETRYABLE_ERROR_TYPES$1) {
888
- if (error instanceof ErrorType) {
889
- return {
890
- error,
891
- category: ErrorCategory.Unrecoverable,
892
- shouldRetry: false,
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$1);
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$1) {
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 = core.log.lib({ lib: core.JAYPIE.LIB.LLM });
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 core.resolveValue(tool.message(parsedArgs, { name }));
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 core.resolveValue(this.log(message, context));
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 core.resolveValue(tool.call(parsedArgs));
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 core.resolveValue(hooks.beforeEachModelRequest(context));
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 core.resolveValue(hooks.afterEachModelResponse(context));
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 core.resolveValue(hooks.beforeEachTool(context));
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 core.resolveValue(hooks.afterEachTool(context));
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 core.resolveValue(hooks.onToolError(context));
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 core.resolveValue(hooks.onRetryableModelError(context));
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 core.resolveValue(hooks.onUnrecoverableModelError(context));
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 core.placeholders(options.instructions, options.data);
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 core.placeholders(options.system, options.data);
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 core.sleep(delay);
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$2 = () => core.log.lib({ lib: core.JAYPIE.LIB.LLM });
3168
+ const getLogger$3 = () => logger.log.lib({ lib: kit.JAYPIE.LIB.LLM });
2712
3169
  // Client initialization
2713
- async function initializeClient$2({ apiKey, } = {}) {
2714
- const logger = getLogger$2();
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 core.ConfigurationError("The application could not resolve the required API key: ANTHROPIC_API_KEY");
3174
+ throw new errors.ConfigurationError("The application could not resolve the required API key: ANTHROPIC_API_KEY");
2718
3175
  }
2719
- const client = new Anthropic({ apiKey: resolvedApiKey });
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$1(systemPrompt, { data, placeholders } = {}) {
3182
+ function formatSystemMessage$2(systemPrompt, { data, placeholders } = {}) {
2725
3183
  return placeholders?.system === false
2726
3184
  ? systemPrompt
2727
- : core.placeholders(systemPrompt, data);
3185
+ : kit.placeholders(systemPrompt, data);
2728
3186
  }
2729
- function formatUserMessage$2(message, { data, placeholders } = {}) {
3187
+ function formatUserMessage$3(message, { data, placeholders } = {}) {
2730
3188
  const content = placeholders?.message === false
2731
3189
  ? message
2732
- : core.placeholders(message, data);
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$2(message, { data, placeholders } = {}) {
2739
- const logger = getLogger$2();
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$2(message, { data, placeholders });
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
- core.log.trace("Using text output (unstructured)");
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
- core.log.trace(`System message: ${systemMessage.length} characters`);
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
- core.log.trace(`Assistant reply: ${text.length} characters`);
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
- core.log.trace("Using structured output");
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
- core.log.trace("Received structured response", { result });
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
- core.log.error("Error creating structured completion", { error });
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$2();
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$2({ apiKey: this.apiKey });
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$2(message, options || {});
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$1(options.system, {
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$1 = () => core.log.lib({ lib: core.JAYPIE.LIB.LLM });
3361
+ const getLogger$2 = () => logger.log.lib({ lib: kit.JAYPIE.LIB.LLM });
2891
3362
  // Client initialization
2892
- async function initializeClient$1({ apiKey, } = {}) {
2893
- const logger = getLogger$1();
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 core.ConfigurationError("The application could not resolve the requested keys");
3367
+ throw new errors.ConfigurationError("The application could not resolve the requested keys");
2897
3368
  }
2898
- const client = new genai.GoogleGenAI({ apiKey: resolvedApiKey });
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$1(message, { data, placeholders } = {}) {
3374
+ function formatUserMessage$2(message, { data, placeholders } = {}) {
2903
3375
  const content = placeholders?.message === false
2904
3376
  ? message
2905
- : core.placeholders(message, data);
3377
+ : kit.placeholders(message, data);
2906
3378
  return {
2907
3379
  role: "user",
2908
3380
  content,
2909
3381
  };
2910
3382
  }
2911
- function prepareMessages$1(message, { data, placeholders } = {}) {
2912
- const logger = getLogger$1();
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$1(message, { data, placeholders });
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$1();
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$1({ apiKey: this.apiKey });
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$1(message, options);
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 = () => core.log.lib({ lib: core.JAYPIE.LIB.LLM });
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 core.ConfigurationError("The application could not resolve the requested keys");
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
- : core.placeholders(systemPrompt, data);
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
- : core.placeholders(message, data);
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;