@jaypie/llm 1.1.30-rc.0 → 1.1.30

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