@@ -13,7 +13,11 @@ import type {
1313} from './tool-types.js' ;
1414
1515import { betaResponsesSend } from '../funcs/betaResponsesSend.js' ;
16- import { hasAsyncFunctions , resolveAsyncFunctions } from './async-params.js' ;
16+ import {
17+ hasAsyncFunctions ,
18+ resolveAsyncFunctions ,
19+ type ResolvedCallModelInput ,
20+ } from './async-params.js' ;
1721import { ReusableReadableStream } from './reusable-stream.js' ;
1822import {
1923 buildResponsesMessageStream ,
@@ -83,9 +87,8 @@ function hasTypeProperty(item: unknown): item is {
8387}
8488
8589export interface GetResponseOptions {
86- // Request can be a mix of sync and async fields
87- // The actual type will be narrowed during async function resolution
88- request : models . OpenResponsesRequest | CallModelInput | Record < string , unknown > ;
90+ // Request can have async functions that will be resolved before sending to API
91+ request : CallModelInput ;
8992 client : OpenRouterCore ;
9093 options ?: RequestOptions ;
9194 tools ?: Tool [ ] ;
@@ -122,6 +125,8 @@ export class ModelResult {
122125 toolCalls : ParsedToolCall [ ] ;
123126 response : models . OpenResponsesNonStreamingResponse ;
124127 } > = [ ] ;
128+ // Track resolved request after async function resolution
129+ private resolvedRequest : models . OpenResponsesRequest | null = null ;
125130
126131 constructor ( options : GetResponseOptions ) {
127132 this . options = options ;
@@ -160,20 +165,30 @@ export class ModelResult {
160165 } ;
161166
162167 // Resolve any async functions first
168+ let baseRequest : ResolvedCallModelInput ;
163169 if ( hasAsyncFunctions ( this . options . request ) ) {
164- const resolved = await resolveAsyncFunctions (
165- this . options . request as CallModelInput ,
170+ baseRequest = await resolveAsyncFunctions (
171+ this . options . request ,
166172 initialContext ,
167173 ) ;
168- this . options . request = resolved as models . OpenResponsesRequest ;
174+ } else {
175+ // Already resolved, extract non-function fields
176+ // Since request is CallModelInput, we need to filter out tools/stopWhen
177+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
178+ const { tools, stopWhen, ...rest } = this . options . request ;
179+ // Use satisfies to validate type compatibility while maintaining the target type
180+ baseRequest = rest as ResolvedCallModelInput satisfies ResolvedCallModelInput ;
169181 }
170182
171- // Force stream mode
172- const request = {
173- ...( this . options . request as models . OpenResponsesRequest ) ,
183+ // Store resolved request with stream mode
184+ this . resolvedRequest = {
185+ ...baseRequest ,
174186 stream : true as const ,
175187 } ;
176188
189+ // Force stream mode for initial request
190+ const request = this . resolvedRequest ;
191+
177192 // Create the stream promise
178193 this . streamPromise = betaResponsesSend (
179194 this . options . client ,
@@ -183,7 +198,10 @@ export class ModelResult {
183198 if ( ! result . ok ) {
184199 throw result . error ;
185200 }
186- return result . value ;
201+ // When stream: true, the API returns EventStream
202+ // TypeScript can't narrow the union type based on runtime parameter values,
203+ // so we assert the type here based on our knowledge that stream=true
204+ return result . value as EventStream < models . OpenResponsesStreamEvent > ;
187205 } ) ;
188206
189207 // Wait for the stream and create the reusable stream
@@ -277,10 +295,14 @@ export class ModelResult {
277295 // Resolve async functions for this turn
278296 if ( hasAsyncFunctions ( this . options . request ) ) {
279297 const resolved = await resolveAsyncFunctions (
280- this . options . request as CallModelInput ,
298+ this . options . request ,
281299 turnContext ,
282300 ) ;
283- this . options . request = resolved as models . OpenResponsesRequest ;
301+ // Update resolved request with new values
302+ this . resolvedRequest = {
303+ ...resolved ,
304+ stream : false , // Tool execution turns don't need streaming
305+ } ;
284306 }
285307
286308 // Execute all tool calls
@@ -314,16 +336,20 @@ export class ModelResult {
314336
315337 // Execute nextTurnParams functions for tools that were called
316338 if ( this . options . tools && currentToolCalls . length > 0 ) {
339+ if ( ! this . resolvedRequest ) {
340+ throw new Error ( 'Request not initialized' ) ;
341+ }
342+
317343 const computedParams = await executeNextTurnParamsFunctions (
318344 currentToolCalls ,
319345 this . options . tools ,
320- this . options . request as models . OpenResponsesRequest
346+ this . resolvedRequest
321347 ) ;
322348
323- // Apply computed parameters to the request for next turn
349+ // Apply computed parameters to the resolved request for next turn
324350 if ( Object . keys ( computedParams ) . length > 0 ) {
325- this . options . request = applyNextTurnParamsToRequest (
326- this . options . request as models . OpenResponsesRequest ,
351+ this . resolvedRequest = applyNextTurnParamsToRequest (
352+ this . resolvedRequest ,
327353 computedParams
328354 ) ;
329355 }
@@ -341,8 +367,12 @@ export class ModelResult {
341367 ] ;
342368
343369 // Make new request with tool results
370+ if ( ! this . resolvedRequest ) {
371+ throw new Error ( 'Request not initialized' ) ;
372+ }
373+
344374 const newRequest : models . OpenResponsesRequest = {
345- ...( this . options . request as models . OpenResponsesRequest ) ,
375+ ...this . resolvedRequest ,
346376 input : newInput ,
347377 stream : false ,
348378 } ;
0 commit comments