@jaypie/llm 1.1.30-rc.0 → 1.1.30

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