@@ -5,6 +5,110 @@ import * as models from "../models/index.js";
55import { EnhancedTool , MaxToolRounds } from "../lib/tool-types.js" ;
66import { convertEnhancedToolsToAPIFormat } from "../lib/tool-executor.js" ;
77
8+ /**
9+ * Input type that accepts both chat-style messages and responses-style input
10+ */
11+ export type CallModelInput =
12+ | models . OpenResponsesInput
13+ | models . Message [ ] ;
14+
15+ /**
16+ * Tool type that accepts chat-style, responses-style, or enhanced tools
17+ */
18+ export type CallModelTools =
19+ | EnhancedTool [ ]
20+ | models . ToolDefinitionJson [ ]
21+ | models . OpenResponsesRequest [ "tools" ] ;
22+
23+ /**
24+ * Check if input is chat-style messages (Message[])
25+ */
26+ function isChatStyleMessages ( input : CallModelInput ) : input is models . Message [ ] {
27+ if ( ! Array . isArray ( input ) ) return false ;
28+ if ( input . length === 0 ) return false ;
29+
30+ const first = input [ 0 ] as any ;
31+ // Chat-style messages have role but no 'type' field at top level
32+ // Responses-style items have 'type' field (like 'message', 'function_call', etc.)
33+ return first && 'role' in first && ! ( 'type' in first ) ;
34+ }
35+
36+ /**
37+ * Check if tools are chat-style (ToolDefinitionJson[])
38+ */
39+ function isChatStyleTools ( tools : CallModelTools ) : tools is models . ToolDefinitionJson [ ] {
40+ if ( ! Array . isArray ( tools ) ) return false ;
41+ if ( tools . length === 0 ) return false ;
42+
43+ const first = tools [ 0 ] as any ;
44+ // Chat-style tools have nested 'function' property with 'name' inside
45+ // Enhanced tools have 'function' with 'inputSchema'
46+ // Responses-style tools have 'name' at top level
47+ return first && 'function' in first && first . function && 'name' in first . function && ! ( 'inputSchema' in first . function ) ;
48+ }
49+
50+ /**
51+ * Convert chat-style tools to responses-style
52+ */
53+ function convertChatToResponsesTools ( tools : models . ToolDefinitionJson [ ] ) : models . OpenResponsesRequest [ "tools" ] {
54+ return tools . map ( ( tool ) : models . OpenResponsesRequestToolFunction => ( {
55+ type : "function" ,
56+ name : tool . function . name ,
57+ description : tool . function . description ?? null ,
58+ strict : tool . function . strict ?? null ,
59+ parameters : tool . function . parameters ?? null ,
60+ } ) ) ;
61+ }
62+
63+ /**
64+ * Convert chat-style messages to responses-style input
65+ */
66+ function convertChatToResponsesInput ( messages : models . Message [ ] ) : models . OpenResponsesInput {
67+ return messages . map ( ( msg ) : models . OpenResponsesEasyInputMessage | models . OpenResponsesFunctionCallOutput => {
68+ // Extract extra fields like cache_control
69+ const { role, content, ...extraFields } = msg as any ;
70+
71+ if ( role === "tool" ) {
72+ const toolMsg = msg as models . ToolResponseMessage ;
73+ return {
74+ type : "function_call_output" ,
75+ callId : toolMsg . toolCallId ,
76+ output : typeof toolMsg . content === "string" ? toolMsg . content : JSON . stringify ( toolMsg . content ) ,
77+ ...extraFields ,
78+ } as models . OpenResponsesFunctionCallOutput ;
79+ }
80+
81+ // Handle assistant messages with tool calls
82+ if ( role === "assistant" ) {
83+ const assistantMsg = msg as models . AssistantMessage ;
84+ // If it has tool calls, we need to convert them
85+ // For now, just convert the content part
86+ return {
87+ role : "assistant" ,
88+ content : typeof assistantMsg . content === "string"
89+ ? assistantMsg . content
90+ : assistantMsg . content === null
91+ ? ""
92+ : JSON . stringify ( assistantMsg . content ) ,
93+ ...extraFields ,
94+ } as models . OpenResponsesEasyInputMessage ;
95+ }
96+
97+ // System, user, developer messages
98+ const convertedContent = typeof content === "string"
99+ ? content
100+ : content === null || content === undefined
101+ ? ""
102+ : JSON . stringify ( content ) ;
103+
104+ return {
105+ role : role as "user" | "system" | "developer" ,
106+ content : convertedContent ,
107+ ...extraFields ,
108+ } as models . OpenResponsesEasyInputMessage ;
109+ } ) as models . OpenResponsesInput ;
110+ }
111+
8112/**
9113 * Get a response with multiple consumption patterns
10114 *
@@ -75,24 +179,46 @@ import { convertEnhancedToolsToAPIFormat } from "../lib/tool-executor.js";
75179 */
76180export function callModel (
77181 client : OpenRouterCore ,
78- request : Omit < models . OpenResponsesRequest , "stream" | "tools" > & {
79- tools ?: EnhancedTool [ ] | models . OpenResponsesRequest [ "tools" ] ;
182+ request : Omit < models . OpenResponsesRequest , "stream" | "tools" | "input" > & {
183+ input ?: CallModelInput ;
184+ tools ?: CallModelTools ;
80185 maxToolRounds ?: MaxToolRounds ;
81186 } ,
82187 options ?: RequestOptions ,
83188) : ResponseWrapper {
84- const { tools, maxToolRounds, ...apiRequest } = request ;
189+ const { tools, maxToolRounds, input, ...restRequest } = request ;
190+
191+ // Convert chat-style messages to responses-style input if needed
192+ const convertedInput = input && isChatStyleMessages ( input )
193+ ? convertChatToResponsesInput ( input )
194+ : input ;
195+
196+ const apiRequest = {
197+ ...restRequest ,
198+ input : convertedInput ,
199+ } ;
85200
86- // Separate enhanced tools from API tools
201+ // Determine tool type and convert as needed
87202 let isEnhancedTools = false ;
88- if ( tools && tools . length > 0 ) {
203+ let isChatTools = false ;
204+
205+ if ( tools && Array . isArray ( tools ) && tools . length > 0 ) {
89206 const firstTool = tools [ 0 ] as any ;
90207 isEnhancedTools = "function" in firstTool && firstTool . function && "inputSchema" in firstTool . function ;
208+ isChatTools = ! isEnhancedTools && isChatStyleTools ( tools ) ;
91209 }
210+
92211 const enhancedTools = isEnhancedTools ? ( tools as EnhancedTool [ ] ) : undefined ;
93212
94- // Convert enhanced tools to API format if provided, otherwise use tools as-is
95- const apiTools = enhancedTools ? convertEnhancedToolsToAPIFormat ( enhancedTools ) : ( tools as models . OpenResponsesRequest [ "tools" ] ) ;
213+ // Convert tools to API format based on their type
214+ let apiTools : models . OpenResponsesRequest [ "tools" ] ;
215+ if ( enhancedTools ) {
216+ apiTools = convertEnhancedToolsToAPIFormat ( enhancedTools ) ;
217+ } else if ( isChatTools ) {
218+ apiTools = convertChatToResponsesTools ( tools as models . ToolDefinitionJson [ ] ) ;
219+ } else {
220+ apiTools = tools as models . OpenResponsesRequest [ "tools" ] ;
221+ }
96222
97223 // Build the request with converted tools
98224 const finalRequest : models . OpenResponsesRequest = {
0 commit comments