@openrouter/sdk 0.9.11 → 0.10.2
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/FUNCTIONS.md +2 -1
- package/_speakeasy/.github/action-inputs-config.json +4 -0
- package/esm/funcs/analyticsGetUserActivity.js +2 -1
- package/esm/funcs/apiKeysCreate.js +2 -1
- package/esm/funcs/apiKeysDelete.js +2 -1
- package/esm/funcs/apiKeysGet.js +2 -1
- package/esm/funcs/apiKeysGetCurrentKeyMetadata.js +2 -1
- package/esm/funcs/apiKeysList.js +2 -1
- package/esm/funcs/apiKeysUpdate.js +2 -1
- package/esm/funcs/betaResponsesSend.js +2 -1
- package/esm/funcs/call-model.d.ts +19 -70
- package/esm/funcs/call-model.js +29 -71
- package/esm/funcs/chatSend.js +2 -1
- package/esm/funcs/creditsCreateCoinbaseCharge.js +2 -1
- package/esm/funcs/creditsGetCredits.js +2 -1
- package/esm/funcs/embeddingsGenerate.js +2 -1
- package/esm/funcs/embeddingsListModels.js +2 -1
- package/esm/funcs/endpointsList.js +2 -1
- package/esm/funcs/endpointsListZdrEndpoints.js +2 -1
- package/esm/funcs/generationsGetGeneration.js +2 -1
- package/esm/funcs/guardrailsBulkAssignKeys.js +2 -1
- package/esm/funcs/guardrailsBulkAssignMembers.js +2 -1
- package/esm/funcs/guardrailsBulkUnassignKeys.js +2 -1
- package/esm/funcs/guardrailsBulkUnassignMembers.js +2 -1
- package/esm/funcs/guardrailsCreate.js +2 -1
- package/esm/funcs/guardrailsDelete.js +2 -1
- package/esm/funcs/guardrailsGet.js +2 -1
- package/esm/funcs/guardrailsList.js +2 -1
- package/esm/funcs/guardrailsListGuardrailKeyAssignments.js +2 -1
- package/esm/funcs/guardrailsListGuardrailMemberAssignments.js +2 -1
- package/esm/funcs/guardrailsListKeyAssignments.js +2 -1
- package/esm/funcs/guardrailsListMemberAssignments.js +2 -1
- package/esm/funcs/guardrailsUpdate.js +2 -1
- package/esm/funcs/modelsCount.d.ts +1 -1
- package/esm/funcs/modelsCount.js +9 -4
- package/esm/funcs/modelsList.js +3 -1
- package/esm/funcs/modelsListForUser.js +2 -1
- package/esm/funcs/oAuthCreateAuthCode.d.ts +1 -1
- package/esm/funcs/oAuthCreateAuthCode.js +4 -3
- package/esm/funcs/oAuthExchangeAuthCodeForAPIKey.js +2 -1
- package/esm/funcs/providersList.js +2 -1
- package/esm/index.d.ts +22 -23
- package/esm/index.js +20 -17
- package/esm/lib/anthropic-compat.d.ts +2 -1
- package/esm/lib/anthropic-compat.js +1 -2
- package/esm/lib/async-params.d.ts +19 -5
- package/esm/lib/async-params.js +5 -1
- package/esm/lib/chat-compat.d.ts +1 -1
- package/esm/lib/chat-compat.js +1 -2
- package/esm/lib/claude-type-guards.d.ts +2 -2
- package/esm/lib/config.d.ts +8 -10
- package/esm/lib/config.js +2 -2
- package/esm/lib/conversation-state.d.ts +1 -1
- package/esm/lib/conversation-state.js +35 -32
- package/esm/lib/env.d.ts +6 -2
- package/esm/lib/env.js +7 -3
- package/esm/lib/model-result.d.ts +73 -17
- package/esm/lib/model-result.js +329 -125
- package/esm/lib/sdks.d.ts +1 -1
- package/esm/lib/sdks.js +8 -28
- package/esm/lib/stream-transformers.d.ts +5 -4
- package/esm/lib/stream-transformers.js +24 -29
- package/esm/lib/stream-type-guards.js +8 -32
- package/esm/lib/tool-context.d.ts +68 -0
- package/esm/lib/tool-context.js +175 -0
- package/esm/lib/tool-executor.d.ts +9 -5
- package/esm/lib/tool-executor.js +26 -16
- package/esm/lib/tool-orchestrator.d.ts +2 -2
- package/esm/lib/tool-types.d.ts +101 -14
- package/esm/lib/tool-types.js +17 -0
- package/esm/lib/tool.d.ts +49 -60
- package/esm/lib/tool.js +15 -19
- package/esm/lib/turn-context.d.ts +1 -1
- package/esm/models/assistantmessage.d.ts +6 -0
- package/esm/models/assistantmessage.js +3 -0
- package/esm/models/chatcompletionaudiooutput.d.ts +38 -0
- package/esm/models/chatcompletionaudiooutput.js +36 -0
- package/esm/models/chatgenerationparams.d.ts +75 -6
- package/esm/models/chatgenerationparams.js +42 -0
- package/esm/models/chatmessagecontentitem.d.ts +3 -2
- package/esm/models/chatmessagecontentitem.js +3 -0
- package/esm/models/chatmessagecontentitemcachecontrol.d.ts +5 -5
- package/esm/models/chatmessagecontentitemcachecontrol.js +5 -7
- package/esm/models/chatmessagecontentitemfile.d.ts +57 -0
- package/esm/models/chatmessagecontentitemfile.js +59 -0
- package/esm/models/chatmessagetokenlogprobs.d.ts +1 -1
- package/esm/models/chatmessagetokenlogprobs.js +2 -1
- package/esm/models/chatresponse.d.ts +1 -1
- package/esm/models/chatresponse.js +1 -1
- package/esm/models/chatstreamingmessagechunk.d.ts +2 -0
- package/esm/models/chatstreamingmessagechunk.js +2 -0
- package/esm/models/chatstreamingresponsechunk.d.ts +1 -1
- package/esm/models/chatstreamingresponsechunk.js +1 -1
- package/esm/models/compoundfilter.d.ts +36 -0
- package/esm/models/compoundfilter.js +32 -0
- package/esm/models/conflictresponseerrordata.d.ts +17 -0
- package/esm/models/conflictresponseerrordata.js +16 -0
- package/esm/models/contextcompressionengine.d.ts +15 -0
- package/esm/models/contextcompressionengine.js +14 -0
- package/esm/models/datetimeservertool.d.ts +30 -0
- package/esm/models/datetimeservertool.js +22 -0
- package/esm/models/defaultparameters.d.ts +3 -0
- package/esm/models/defaultparameters.js +6 -0
- package/esm/models/errors/conflictresponseerror.d.ts +33 -0
- package/esm/models/errors/conflictresponseerror.js +42 -0
- package/esm/models/errors/index.d.ts +1 -0
- package/esm/models/errors/index.js +1 -0
- package/esm/models/index.d.ts +23 -4
- package/esm/models/index.js +23 -4
- package/esm/models/openairesponsesinputunion.d.ts +32 -1
- package/esm/models/openairesponsesinputunion.js +53 -1
- package/esm/models/openresponsesapplypatchtool.d.ts +20 -0
- package/esm/models/openresponsesapplypatchtool.js +21 -0
- package/esm/models/openresponsescodeinterpretertool.d.ts +68 -0
- package/esm/models/openresponsescodeinterpretertool.js +85 -0
- package/esm/models/openresponsescomputertool.d.ts +39 -0
- package/esm/models/openresponsescomputertool.js +50 -0
- package/esm/models/openresponsescustomtool.d.ts +75 -0
- package/esm/models/openresponsescustomtool.js +95 -0
- package/esm/models/openresponseseasyinputmessage.d.ts +30 -4
- package/esm/models/openresponseseasyinputmessage.js +28 -2
- package/esm/models/openresponsesfilesearchtool.d.ts +111 -0
- package/esm/models/openresponsesfilesearchtool.js +180 -0
- package/esm/models/openresponsesfunctioncalloutput.d.ts +46 -7
- package/esm/models/openresponsesfunctioncalloutput.js +56 -4
- package/esm/models/openresponsesfunctionshelltool.d.ts +20 -0
- package/esm/models/openresponsesfunctionshelltool.js +21 -0
- package/esm/models/openresponsesimagegenerationtool.d.ts +125 -0
- package/esm/models/openresponsesimagegenerationtool.js +153 -0
- package/esm/models/openresponsesinputmessageitem.d.ts +2 -2
- package/esm/models/openresponsesinputmessageitem.js +2 -2
- package/esm/models/openresponsesinputunion.d.ts +207 -0
- package/esm/models/openresponsesinputunion.js +225 -0
- package/esm/models/openresponseslocalshelltool.d.ts +20 -0
- package/esm/models/openresponseslocalshelltool.js +21 -0
- package/esm/models/openresponsesmcptool.d.ts +128 -0
- package/esm/models/openresponsesmcptool.js +173 -0
- package/esm/models/openresponsesnonstreamingresponse.d.ts +11 -2
- package/esm/models/openresponsesnonstreamingresponse.js +27 -0
- package/esm/models/openresponsesrequest.d.ts +53 -11
- package/esm/models/openresponsesrequest.js +51 -2
- package/esm/models/openresponsesstreamevent.d.ts +52 -4
- package/esm/models/openresponsesstreamevent.js +54 -3
- package/esm/models/openresponseswebsearch20250826tool.d.ts +29 -0
- package/esm/models/openresponseswebsearch20250826tool.js +23 -0
- package/esm/models/openresponseswebsearchcallcompleted.d.ts +16 -0
- package/esm/models/openresponseswebsearchcallcompleted.js +24 -0
- package/esm/models/openresponseswebsearchcallinprogress.d.ts +16 -0
- package/esm/models/openresponseswebsearchcallinprogress.js +24 -0
- package/esm/models/openresponseswebsearchcallsearching.d.ts +16 -0
- package/esm/models/openresponseswebsearchcallsearching.js +24 -0
- package/esm/models/openresponseswebsearchpreview20250311tool.d.ts +44 -0
- package/esm/models/openresponseswebsearchpreview20250311tool.js +51 -0
- package/esm/models/openresponseswebsearchpreviewtool.d.ts +44 -0
- package/esm/models/openresponseswebsearchpreviewtool.js +47 -0
- package/esm/models/openresponseswebsearchtool.d.ts +29 -0
- package/esm/models/openresponseswebsearchtool.js +23 -0
- package/esm/models/operations/bulkassignkeystoguardrail.d.ts +16 -3
- package/esm/models/operations/bulkassignkeystoguardrail.js +2 -2
- package/esm/models/operations/bulkassignmemberstoguardrail.d.ts +16 -3
- package/esm/models/operations/bulkassignmemberstoguardrail.js +2 -2
- package/esm/models/operations/bulkunassignkeysfromguardrail.d.ts +16 -3
- package/esm/models/operations/bulkunassignkeysfromguardrail.js +2 -2
- package/esm/models/operations/bulkunassignmembersfromguardrail.d.ts +16 -3
- package/esm/models/operations/bulkunassignmembersfromguardrail.js +2 -2
- package/esm/models/operations/createauthkeyscode.d.ts +16 -3
- package/esm/models/operations/createauthkeyscode.js +2 -2
- package/esm/models/operations/createcoinbasecharge.d.ts +16 -3
- package/esm/models/operations/createcoinbasecharge.js +2 -2
- package/esm/models/operations/createembeddings.d.ts +16 -3
- package/esm/models/operations/createembeddings.js +2 -2
- package/esm/models/operations/createguardrail.d.ts +25 -3
- package/esm/models/operations/createguardrail.js +6 -2
- package/esm/models/operations/createkeys.d.ts +16 -3
- package/esm/models/operations/createkeys.js +2 -2
- package/esm/models/operations/createresponses.d.ts +16 -3
- package/esm/models/operations/createresponses.js +2 -2
- package/esm/models/operations/deleteguardrail.d.ts +16 -3
- package/esm/models/operations/deleteguardrail.js +2 -2
- package/esm/models/operations/deletekeys.d.ts +16 -3
- package/esm/models/operations/deletekeys.js +2 -2
- package/esm/models/operations/exchangeauthcodeforapikey.d.ts +16 -3
- package/esm/models/operations/exchangeauthcodeforapikey.js +2 -2
- package/esm/models/operations/getcredits.d.ts +16 -3
- package/esm/models/operations/getcredits.js +2 -2
- package/esm/models/operations/getcurrentkey.d.ts +16 -3
- package/esm/models/operations/getcurrentkey.js +2 -2
- package/esm/models/operations/getgeneration.d.ts +25 -3
- package/esm/models/operations/getgeneration.js +7 -2
- package/esm/models/operations/getguardrail.d.ts +20 -3
- package/esm/models/operations/getguardrail.js +4 -2
- package/esm/models/operations/getkey.d.ts +16 -3
- package/esm/models/operations/getkey.js +2 -2
- package/esm/models/operations/getmodels.d.ts +21 -3
- package/esm/models/operations/getmodels.js +4 -2
- package/esm/models/operations/getuseractivity.d.ts +16 -3
- package/esm/models/operations/getuseractivity.js +2 -2
- package/esm/models/operations/list.d.ts +16 -3
- package/esm/models/operations/list.js +2 -2
- package/esm/models/operations/listembeddingsmodels.d.ts +16 -3
- package/esm/models/operations/listembeddingsmodels.js +2 -2
- package/esm/models/operations/listendpoints.d.ts +16 -3
- package/esm/models/operations/listendpoints.js +2 -2
- package/esm/models/operations/listendpointszdr.d.ts +16 -3
- package/esm/models/operations/listendpointszdr.js +2 -2
- package/esm/models/operations/listguardrailkeyassignments.d.ts +16 -3
- package/esm/models/operations/listguardrailkeyassignments.js +2 -2
- package/esm/models/operations/listguardrailmemberassignments.d.ts +16 -3
- package/esm/models/operations/listguardrailmemberassignments.js +2 -2
- package/esm/models/operations/listguardrails.d.ts +20 -3
- package/esm/models/operations/listguardrails.js +4 -2
- package/esm/models/operations/listkeyassignments.d.ts +16 -3
- package/esm/models/operations/listkeyassignments.js +2 -2
- package/esm/models/operations/listmemberassignments.d.ts +16 -3
- package/esm/models/operations/listmemberassignments.js +2 -2
- package/esm/models/operations/listmodelscount.d.ts +21 -3
- package/esm/models/operations/listmodelscount.js +4 -2
- package/esm/models/operations/listmodelsuser.d.ts +16 -3
- package/esm/models/operations/listmodelsuser.js +2 -2
- package/esm/models/operations/listproviders.d.ts +16 -3
- package/esm/models/operations/listproviders.js +2 -2
- package/esm/models/operations/sendchatcompletionrequest.d.ts +16 -3
- package/esm/models/operations/sendchatcompletionrequest.js +2 -2
- package/esm/models/operations/updateguardrail.d.ts +25 -3
- package/esm/models/operations/updateguardrail.js +6 -2
- package/esm/models/operations/updatekeys.d.ts +16 -3
- package/esm/models/operations/updatekeys.js +2 -2
- package/esm/models/outputmessage.d.ts +23 -0
- package/esm/models/outputmessage.js +24 -0
- package/esm/models/outputmodality.d.ts +1 -0
- package/esm/models/outputmodality.js +1 -0
- package/esm/models/providername.d.ts +1 -0
- package/esm/models/providername.js +1 -0
- package/esm/models/providerpreferences.d.ts +4 -0
- package/esm/models/providerpreferences.js +4 -0
- package/esm/models/providersort.d.ts +1 -0
- package/esm/models/providersort.js +1 -0
- package/esm/models/providersortconfig.d.ts +1 -0
- package/esm/models/providersortconfig.js +1 -0
- package/esm/models/responseoutputtext.d.ts +8 -8
- package/esm/models/responseoutputtext.js +10 -9
- package/esm/models/responsesoutputitem.d.ts +2 -13
- package/esm/models/responsesoutputitem.js +8 -6
- package/esm/models/responsesoutputitemreasoning.d.ts +2 -31
- package/esm/models/responsesoutputitemreasoning.js +2 -44
- package/esm/models/responsesoutputmessage.d.ts +24 -32
- package/esm/models/responsesoutputmessage.js +26 -47
- package/esm/models/responsesservertooloutput.d.ts +42 -0
- package/esm/models/responsesservertooloutput.js +44 -0
- package/esm/models/responseswebsearchcalloutput.d.ts +90 -5
- package/esm/models/responseswebsearchcalloutput.js +113 -5
- package/esm/models/responseswebsearchservertool.d.ts +35 -0
- package/esm/models/responseswebsearchservertool.js +28 -0
- package/esm/models/tooldefinitionjson.d.ts +38 -20
- package/esm/models/tooldefinitionjson.js +22 -11
- package/esm/models/websearchengine.d.ts +2 -0
- package/esm/models/websearchengine.js +2 -0
- package/esm/models/websearchservertool.d.ts +116 -0
- package/esm/models/websearchservertool.js +77 -0
- package/esm/models/websearchshorthand.d.ts +215 -0
- package/esm/models/websearchshorthand.js +138 -0
- package/esm/sdk/sdk.d.ts +4 -1
- package/esm/sdk/sdk.js +0 -1
- package/esm/types/index.d.ts +0 -2
- package/esm/types/index.js +0 -1
- package/jsr.json +1 -1
- package/package.json +11 -6
- package/turbo.json +10 -0
- package/esm/models/openresponsesinput.d.ts +0 -28
- package/esm/models/openresponsesinput.js +0 -54
- package/esm/models/openresponseslogprobs.d.ts +0 -16
- package/esm/models/openresponseslogprobs.js +0 -22
- package/esm/models/openresponsestoplogprobs.d.ts +0 -14
- package/esm/models/openresponsestoplogprobs.js +0 -15
package/esm/lib/model-result.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ToolEventBroadcaster } from './tool-event-broadcaster.js';
|
|
2
|
+
import { ToolContextStore, resolveContext } from './tool-context.js';
|
|
2
3
|
import { betaResponsesSend } from '../funcs/betaResponsesSend.js';
|
|
3
4
|
import { hasAsyncFunctions, resolveAsyncFunctions, } from './async-params.js';
|
|
4
5
|
import { appendToMessages, createInitialState, createRejectedResult, createUnsentResult, extractTextFromResponse as extractTextFromResponseState, partitionToolCalls, unsentResultsToAPIFormat, updateState, } from './conversation-state.js';
|
|
@@ -8,7 +9,7 @@ import { executeTool } from './tool-executor.js';
|
|
|
8
9
|
import { executeNextTurnParamsFunctions, applyNextTurnParamsToRequest } from './next-turn-params.js';
|
|
9
10
|
import { hasExecuteFunction } from './tool-types.js';
|
|
10
11
|
import { isStopConditionMet, stepCountIs } from './stop-conditions.js';
|
|
11
|
-
import { isOutputMessage, isFunctionCallItem, isReasoningOutputItem, isWebSearchCallOutputItem, isFileSearchCallOutputItem, isImageGenerationCallOutputItem, hasTypeProperty, } from './stream-type-guards.js';
|
|
12
|
+
import { isOutputMessage, isFunctionCallItem, isReasoningOutputItem, isWebSearchCallOutputItem, isFileSearchCallOutputItem, isImageGenerationCallOutputItem, isResponseCompletedEvent, isResponseFailedEvent, isResponseIncompleteEvent, isOutputTextDeltaEvent, isReasoningDeltaEvent, hasTypeProperty, } from './stream-type-guards.js';
|
|
12
13
|
/**
|
|
13
14
|
* Default maximum number of tool execution steps if no stopWhen is specified.
|
|
14
15
|
* This prevents infinite loops in tool execution.
|
|
@@ -49,6 +50,7 @@ function isEventStream(value) {
|
|
|
49
50
|
* ReusableReadableStream implementation.
|
|
50
51
|
*
|
|
51
52
|
* @template TTools - The tools array type to enable typed tool calls and results
|
|
53
|
+
* @template TShared - The shape of the shared context (inferred from sharedContextSchema)
|
|
52
54
|
*/
|
|
53
55
|
export class ModelResult {
|
|
54
56
|
constructor(options) {
|
|
@@ -68,6 +70,12 @@ export class ModelResult {
|
|
|
68
70
|
this.approvedToolCalls = [];
|
|
69
71
|
this.rejectedToolCalls = [];
|
|
70
72
|
this.isResumingFromApproval = false;
|
|
73
|
+
// Unified turn broadcaster for multi-turn streaming
|
|
74
|
+
this.turnBroadcaster = null;
|
|
75
|
+
this.initialStreamPipeStarted = false;
|
|
76
|
+
this.initialPipePromise = null;
|
|
77
|
+
// Context store for typed tool context (persists across turns)
|
|
78
|
+
this.contextStore = null;
|
|
71
79
|
this.options = options;
|
|
72
80
|
// Runtime validation: approval decisions require state
|
|
73
81
|
const hasApprovalDecisions = (options.approveToolCalls && options.approveToolCalls.length > 0) ||
|
|
@@ -83,15 +91,139 @@ export class ModelResult {
|
|
|
83
91
|
this.rejectedToolCalls = options.rejectToolCalls ?? [];
|
|
84
92
|
}
|
|
85
93
|
/**
|
|
86
|
-
* Get or create the
|
|
87
|
-
*
|
|
88
|
-
* Broadcasts both preliminary results and final tool results.
|
|
94
|
+
* Get or create the unified turn broadcaster (lazy initialization).
|
|
95
|
+
* Broadcasts all API stream events, tool events, and turn delimiters across turns.
|
|
89
96
|
*/
|
|
90
|
-
|
|
91
|
-
if (!this.
|
|
92
|
-
this.
|
|
97
|
+
ensureTurnBroadcaster() {
|
|
98
|
+
if (!this.turnBroadcaster) {
|
|
99
|
+
this.turnBroadcaster = new ToolEventBroadcaster();
|
|
93
100
|
}
|
|
94
|
-
return this.
|
|
101
|
+
return this.turnBroadcaster;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Start piping the initial stream into the turn broadcaster.
|
|
105
|
+
* Idempotent — only starts once even if called multiple times.
|
|
106
|
+
* Wraps the initial stream events with turn.start(0) / turn.end(0) delimiters.
|
|
107
|
+
*/
|
|
108
|
+
startInitialStreamPipe() {
|
|
109
|
+
if (this.initialStreamPipeStarted)
|
|
110
|
+
return;
|
|
111
|
+
this.initialStreamPipeStarted = true;
|
|
112
|
+
const broadcaster = this.ensureTurnBroadcaster();
|
|
113
|
+
if (!this.reusableStream) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
const stream = this.reusableStream;
|
|
117
|
+
this.initialPipePromise = (async () => {
|
|
118
|
+
broadcaster.push({
|
|
119
|
+
type: 'turn.start',
|
|
120
|
+
turnNumber: 0,
|
|
121
|
+
timestamp: Date.now(),
|
|
122
|
+
});
|
|
123
|
+
const consumer = stream.createConsumer();
|
|
124
|
+
for await (const event of consumer) {
|
|
125
|
+
broadcaster.push(event);
|
|
126
|
+
}
|
|
127
|
+
broadcaster.push({
|
|
128
|
+
type: 'turn.end',
|
|
129
|
+
turnNumber: 0,
|
|
130
|
+
timestamp: Date.now(),
|
|
131
|
+
});
|
|
132
|
+
})().catch((error) => {
|
|
133
|
+
broadcaster.complete(error instanceof Error ? error : new Error(String(error)));
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Pipe a follow-up stream into the turn broadcaster and capture the completed response.
|
|
138
|
+
* Emits turn.start / turn.end delimiters around the stream events.
|
|
139
|
+
*/
|
|
140
|
+
async pipeAndConsumeStream(stream, turnNumber) {
|
|
141
|
+
const broadcaster = this.turnBroadcaster;
|
|
142
|
+
broadcaster.push({
|
|
143
|
+
type: 'turn.start',
|
|
144
|
+
turnNumber,
|
|
145
|
+
timestamp: Date.now(),
|
|
146
|
+
});
|
|
147
|
+
const consumer = stream.createConsumer();
|
|
148
|
+
let completedResponse = null;
|
|
149
|
+
for await (const event of consumer) {
|
|
150
|
+
broadcaster.push(event);
|
|
151
|
+
if (isResponseCompletedEvent(event)) {
|
|
152
|
+
completedResponse = event.response;
|
|
153
|
+
}
|
|
154
|
+
if (isResponseFailedEvent(event)) {
|
|
155
|
+
const errorMsg = 'message' in event ? String(event.message) : 'Response failed';
|
|
156
|
+
throw new Error(errorMsg);
|
|
157
|
+
}
|
|
158
|
+
if (isResponseIncompleteEvent(event)) {
|
|
159
|
+
completedResponse = event.response;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
broadcaster.push({
|
|
163
|
+
type: 'turn.end',
|
|
164
|
+
turnNumber,
|
|
165
|
+
timestamp: Date.now(),
|
|
166
|
+
});
|
|
167
|
+
if (!completedResponse) {
|
|
168
|
+
throw new Error('Follow-up stream ended without a completed response');
|
|
169
|
+
}
|
|
170
|
+
return completedResponse;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Push a tool result event to both the legacy tool event broadcaster
|
|
174
|
+
* and the unified turn broadcaster.
|
|
175
|
+
*/
|
|
176
|
+
broadcastToolResult(toolCallId, result, preliminaryResults) {
|
|
177
|
+
this.toolEventBroadcaster?.push({
|
|
178
|
+
type: 'tool_result',
|
|
179
|
+
toolCallId,
|
|
180
|
+
result,
|
|
181
|
+
...(preliminaryResults?.length && { preliminaryResults }),
|
|
182
|
+
});
|
|
183
|
+
this.turnBroadcaster?.push({
|
|
184
|
+
type: 'tool.result',
|
|
185
|
+
toolCallId,
|
|
186
|
+
result,
|
|
187
|
+
timestamp: Date.now(),
|
|
188
|
+
...(preliminaryResults?.length && { preliminaryResults }),
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Push a preliminary result event to both the legacy tool event broadcaster
|
|
193
|
+
* and the unified turn broadcaster.
|
|
194
|
+
*/
|
|
195
|
+
broadcastPreliminaryResult(toolCallId, result) {
|
|
196
|
+
this.toolEventBroadcaster?.push({
|
|
197
|
+
type: 'preliminary_result',
|
|
198
|
+
toolCallId,
|
|
199
|
+
result,
|
|
200
|
+
});
|
|
201
|
+
this.turnBroadcaster?.push({
|
|
202
|
+
type: 'tool.preliminary_result',
|
|
203
|
+
toolCallId,
|
|
204
|
+
result,
|
|
205
|
+
timestamp: Date.now(),
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Set up the turn broadcaster with tool execution and return the consumer.
|
|
210
|
+
* Used by stream methods that need to iterate over all turns.
|
|
211
|
+
*/
|
|
212
|
+
startTurnBroadcasterExecution() {
|
|
213
|
+
const broadcaster = this.ensureTurnBroadcaster();
|
|
214
|
+
this.startInitialStreamPipe();
|
|
215
|
+
const consumer = broadcaster.createConsumer();
|
|
216
|
+
const executionPromise = this.executeToolsIfNeeded().finally(async () => {
|
|
217
|
+
// Wait for the initial stream pipe to finish pushing all events
|
|
218
|
+
// (including turn.end) before marking the broadcaster as complete.
|
|
219
|
+
// Without this, turn.end can be silently dropped if the pipe hasn't
|
|
220
|
+
// finished when executeToolsIfNeeded completes.
|
|
221
|
+
if (this.initialPipePromise) {
|
|
222
|
+
await this.initialPipePromise;
|
|
223
|
+
}
|
|
224
|
+
broadcaster.complete();
|
|
225
|
+
});
|
|
226
|
+
return { consumer, executionPromise };
|
|
95
227
|
}
|
|
96
228
|
/**
|
|
97
229
|
* Type guard to check if a value is a non-streaming response
|
|
@@ -208,7 +340,7 @@ export class ModelResult {
|
|
|
208
340
|
toolResults: round.toolResults.map((tr) => ({
|
|
209
341
|
toolCallId: tr.callId,
|
|
210
342
|
toolName: round.toolCalls.find((tc) => tc.id === tr.callId)?.name ?? '',
|
|
211
|
-
result: JSON.parse(tr.output),
|
|
343
|
+
result: typeof tr.output === 'string' ? JSON.parse(tr.output) : tr.output,
|
|
212
344
|
})),
|
|
213
345
|
response: round.response,
|
|
214
346
|
usage: round.response.usage,
|
|
@@ -242,7 +374,7 @@ export class ModelResult {
|
|
|
242
374
|
if (!tool || !hasExecuteFunction(tool)) {
|
|
243
375
|
return null;
|
|
244
376
|
}
|
|
245
|
-
const result = await executeTool(tool, tc, turnContext);
|
|
377
|
+
const result = await executeTool(tool, tc, turnContext, undefined, this.contextStore ?? undefined, this.options.sharedContextSchema);
|
|
246
378
|
if (result.error) {
|
|
247
379
|
return createRejectedResult(tc.id, String(tc.name), result.error.message);
|
|
248
380
|
}
|
|
@@ -281,7 +413,10 @@ export class ModelResult {
|
|
|
281
413
|
async handleApprovalCheck(toolCalls, currentRound, currentResponse) {
|
|
282
414
|
if (!this.options.tools)
|
|
283
415
|
return false;
|
|
284
|
-
const turnContext = {
|
|
416
|
+
const turnContext = {
|
|
417
|
+
numberOfTurns: currentRound,
|
|
418
|
+
// context is handled via contextStore, not on TurnContext
|
|
419
|
+
};
|
|
285
420
|
const { requiresApproval: needsApproval, autoExecute } = await partitionToolCalls(toolCalls, this.options.tools, turnContext, this.requireApprovalFn ?? undefined);
|
|
286
421
|
if (needsApproval.length === 0)
|
|
287
422
|
return false;
|
|
@@ -326,13 +461,7 @@ export class ModelResult {
|
|
|
326
461
|
const errorMessage = `Failed to parse tool call arguments for "${toolCall.name}": The model provided invalid JSON. ` +
|
|
327
462
|
`Raw arguments received: "${rawArgs}". ` +
|
|
328
463
|
`Please provide valid JSON arguments for this tool call.`;
|
|
329
|
-
|
|
330
|
-
this.toolEventBroadcaster.push({
|
|
331
|
-
type: 'tool_result',
|
|
332
|
-
toolCallId: toolCall.id,
|
|
333
|
-
result: { error: errorMessage },
|
|
334
|
-
});
|
|
335
|
-
}
|
|
464
|
+
this.broadcastToolResult(toolCall.id, { error: errorMessage });
|
|
336
465
|
return {
|
|
337
466
|
type: 'parse_error',
|
|
338
467
|
toolCall,
|
|
@@ -345,18 +474,15 @@ export class ModelResult {
|
|
|
345
474
|
};
|
|
346
475
|
}
|
|
347
476
|
const preliminaryResultsForCall = [];
|
|
348
|
-
const
|
|
477
|
+
const hasBroadcaster = this.toolEventBroadcaster || this.turnBroadcaster;
|
|
478
|
+
const onPreliminaryResult = hasBroadcaster
|
|
349
479
|
? (callId, resultValue) => {
|
|
350
480
|
const typedResult = resultValue;
|
|
351
481
|
preliminaryResultsForCall.push(typedResult);
|
|
352
|
-
this.
|
|
353
|
-
type: 'preliminary_result',
|
|
354
|
-
toolCallId: callId,
|
|
355
|
-
result: typedResult,
|
|
356
|
-
});
|
|
482
|
+
this.broadcastPreliminaryResult(callId, typedResult);
|
|
357
483
|
}
|
|
358
484
|
: undefined;
|
|
359
|
-
const result = await executeTool(tool, toolCall, turnContext, onPreliminaryResult);
|
|
485
|
+
const result = await executeTool(tool, toolCall, turnContext, onPreliminaryResult, this.contextStore ?? undefined, this.options.sharedContextSchema);
|
|
360
486
|
return {
|
|
361
487
|
type: 'execution',
|
|
362
488
|
toolCall,
|
|
@@ -376,13 +502,7 @@ export class ModelResult {
|
|
|
376
502
|
const errorMessage = settled.reason instanceof Error
|
|
377
503
|
? settled.reason.message
|
|
378
504
|
: String(settled.reason);
|
|
379
|
-
|
|
380
|
-
this.toolEventBroadcaster.push({
|
|
381
|
-
type: 'tool_result',
|
|
382
|
-
toolCallId: originalToolCall.id,
|
|
383
|
-
result: { error: errorMessage },
|
|
384
|
-
});
|
|
385
|
-
}
|
|
505
|
+
this.broadcastToolResult(originalToolCall.id, { error: errorMessage });
|
|
386
506
|
toolResults.push({
|
|
387
507
|
type: 'function_call_output',
|
|
388
508
|
id: `output_${originalToolCall.id}`,
|
|
@@ -398,18 +518,10 @@ export class ModelResult {
|
|
|
398
518
|
toolResults.push(value.output);
|
|
399
519
|
continue;
|
|
400
520
|
}
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
result: (value.result.error
|
|
406
|
-
? { error: value.result.error.message }
|
|
407
|
-
: value.result.result),
|
|
408
|
-
...(value.preliminaryResultsForCall.length > 0 && {
|
|
409
|
-
preliminaryResults: value.preliminaryResultsForCall,
|
|
410
|
-
}),
|
|
411
|
-
});
|
|
412
|
-
}
|
|
521
|
+
const toolResult = (value.result.error
|
|
522
|
+
? { error: value.result.error.message }
|
|
523
|
+
: value.result.result);
|
|
524
|
+
this.broadcastToolResult(value.toolCall.id, toolResult, value.preliminaryResultsForCall.length > 0 ? value.preliminaryResultsForCall : undefined);
|
|
413
525
|
toolResults.push({
|
|
414
526
|
type: 'function_call_output',
|
|
415
527
|
id: `output_${value.toolCall.id}`,
|
|
@@ -432,9 +544,10 @@ export class ModelResult {
|
|
|
432
544
|
const resolved = await resolveAsyncFunctions(this.options.request, turnContext);
|
|
433
545
|
// Preserve accumulated input from previous turns
|
|
434
546
|
const preservedInput = this.resolvedRequest?.input;
|
|
547
|
+
const preservedStream = this.resolvedRequest?.stream;
|
|
435
548
|
this.resolvedRequest = {
|
|
436
549
|
...resolved,
|
|
437
|
-
stream:
|
|
550
|
+
stream: preservedStream ?? true,
|
|
438
551
|
...(preservedInput !== undefined && { input: preservedInput }),
|
|
439
552
|
};
|
|
440
553
|
}
|
|
@@ -456,13 +569,14 @@ export class ModelResult {
|
|
|
456
569
|
}
|
|
457
570
|
/**
|
|
458
571
|
* Make a follow-up API request with tool results.
|
|
459
|
-
*
|
|
572
|
+
* Uses streaming and pipes events through the turn broadcaster when available.
|
|
460
573
|
*
|
|
461
574
|
* @param currentResponse - The response that contained tool calls
|
|
462
575
|
* @param toolResults - The results from executing those tools
|
|
576
|
+
* @param turnNumber - The turn number for this follow-up request
|
|
463
577
|
* @returns The new response from the API
|
|
464
578
|
*/
|
|
465
|
-
async makeFollowupRequest(currentResponse, toolResults) {
|
|
579
|
+
async makeFollowupRequest(currentResponse, toolResults, turnNumber) {
|
|
466
580
|
// Build new input preserving original conversation + tool results
|
|
467
581
|
const originalInput = this.resolvedRequest?.input;
|
|
468
582
|
const normalizedOriginalInput = Array.isArray(originalInput)
|
|
@@ -487,7 +601,7 @@ export class ModelResult {
|
|
|
487
601
|
};
|
|
488
602
|
const newRequest = {
|
|
489
603
|
...this.resolvedRequest,
|
|
490
|
-
stream:
|
|
604
|
+
stream: true,
|
|
491
605
|
};
|
|
492
606
|
const newResult = await betaResponsesSend(this.options.client, { openResponsesRequest: newRequest }, this.options.options);
|
|
493
607
|
if (!newResult.ok) {
|
|
@@ -496,8 +610,11 @@ export class ModelResult {
|
|
|
496
610
|
// Handle streaming or non-streaming response
|
|
497
611
|
const value = newResult.value;
|
|
498
612
|
if (isEventStream(value)) {
|
|
499
|
-
const
|
|
500
|
-
|
|
613
|
+
const followUpStream = new ReusableReadableStream(value);
|
|
614
|
+
if (this.turnBroadcaster) {
|
|
615
|
+
return this.pipeAndConsumeStream(followUpStream, turnNumber);
|
|
616
|
+
}
|
|
617
|
+
return consumeStreamForCompletion(followUpStream);
|
|
501
618
|
}
|
|
502
619
|
else if (this.isNonStreamingResponse(value)) {
|
|
503
620
|
return value;
|
|
@@ -533,7 +650,7 @@ export class ModelResult {
|
|
|
533
650
|
}
|
|
534
651
|
// Already resolved, extract non-function fields
|
|
535
652
|
// Filter out stopWhen and state-related fields that aren't part of the API request
|
|
536
|
-
const { stopWhen: _, state: _s, requireApproval: _r, approveToolCalls: _a, rejectToolCalls: _rj, ...rest } = this.options.request;
|
|
653
|
+
const { stopWhen: _, state: _s, requireApproval: _r, approveToolCalls: _a, rejectToolCalls: _rj, context: _c, ...rest } = this.options.request;
|
|
537
654
|
return rest;
|
|
538
655
|
}
|
|
539
656
|
/**
|
|
@@ -590,6 +707,12 @@ export class ModelResult {
|
|
|
590
707
|
// Check if we're resuming from awaiting_approval with decisions
|
|
591
708
|
if (loadedState.status === 'awaiting_approval' &&
|
|
592
709
|
(this.approvedToolCalls.length > 0 || this.rejectedToolCalls.length > 0)) {
|
|
710
|
+
// Initialize context store before resuming so tools have access
|
|
711
|
+
if (this.options.context !== undefined) {
|
|
712
|
+
const approvalContext = { numberOfTurns: 0 };
|
|
713
|
+
const resolvedCtx = await resolveContext(this.options.context, approvalContext);
|
|
714
|
+
this.contextStore = new ToolContextStore(resolvedCtx);
|
|
715
|
+
}
|
|
593
716
|
this.isResumingFromApproval = true;
|
|
594
717
|
await this.processApprovalDecisions();
|
|
595
718
|
return; // Skip normal initialization, we're resuming
|
|
@@ -610,9 +733,12 @@ export class ModelResult {
|
|
|
610
733
|
}
|
|
611
734
|
// Resolve async functions before initial request
|
|
612
735
|
// Build initial turn context (turn 0 for initial request)
|
|
613
|
-
const initialContext = {
|
|
614
|
-
|
|
615
|
-
|
|
736
|
+
const initialContext = { numberOfTurns: 0 };
|
|
737
|
+
// Initialize context store from the context option
|
|
738
|
+
if (this.options.context !== undefined) {
|
|
739
|
+
const resolvedCtx = await resolveContext(this.options.context, initialContext);
|
|
740
|
+
this.contextStore = new ToolContextStore(resolvedCtx);
|
|
741
|
+
}
|
|
616
742
|
// Resolve any async functions first
|
|
617
743
|
let baseRequest = await this.resolveRequestForContext(initialContext);
|
|
618
744
|
// If we have state with existing messages, use those as input
|
|
@@ -673,6 +799,7 @@ export class ModelResult {
|
|
|
673
799
|
// Build turn context - numberOfTurns represents the current turn (1-indexed after initial)
|
|
674
800
|
const turnContext = {
|
|
675
801
|
numberOfTurns: this.allToolExecutionRounds.length + 1,
|
|
802
|
+
// context is handled via contextStore, not on TurnContext
|
|
676
803
|
};
|
|
677
804
|
// Process approvals - execute the approved tools
|
|
678
805
|
for (const callId of this.approvedToolCalls) {
|
|
@@ -685,7 +812,7 @@ export class ModelResult {
|
|
|
685
812
|
unsentResults.push(createRejectedResult(callId, String(toolCall.name), 'Tool not found or not executable'));
|
|
686
813
|
continue;
|
|
687
814
|
}
|
|
688
|
-
const result = await executeTool(tool, toolCall, turnContext);
|
|
815
|
+
const result = await executeTool(tool, toolCall, turnContext, undefined, this.contextStore ?? undefined, this.options.sharedContextSchema);
|
|
689
816
|
if (result.error) {
|
|
690
817
|
unsentResults.push(createRejectedResult(callId, String(toolCall.name), result.error.message));
|
|
691
818
|
}
|
|
@@ -839,7 +966,9 @@ export class ModelResult {
|
|
|
839
966
|
break;
|
|
840
967
|
}
|
|
841
968
|
// Build turn context
|
|
842
|
-
const
|
|
969
|
+
const turnNumber = currentRound + 1;
|
|
970
|
+
const turnContext = { numberOfTurns: turnNumber };
|
|
971
|
+
await this.options.onTurnStart?.(turnContext);
|
|
843
972
|
// Resolve async functions for this turn
|
|
844
973
|
await this.resolveAsyncFunctionsForTurn(turnContext);
|
|
845
974
|
// Execute tools
|
|
@@ -855,8 +984,8 @@ export class ModelResult {
|
|
|
855
984
|
await this.saveToolResultsToState(toolResults);
|
|
856
985
|
// Apply nextTurnParams
|
|
857
986
|
await this.applyNextTurnParams(currentToolCalls);
|
|
858
|
-
|
|
859
|
-
|
|
987
|
+
currentResponse = await this.makeFollowupRequest(currentResponse, toolResults, turnNumber);
|
|
988
|
+
await this.options.onTurnEnd?.(turnContext, currentResponse);
|
|
860
989
|
// Save new response to state
|
|
861
990
|
await this.saveResponseToState(currentResponse);
|
|
862
991
|
currentRound++;
|
|
@@ -902,63 +1031,56 @@ export class ModelResult {
|
|
|
902
1031
|
return this.finalResponse;
|
|
903
1032
|
}
|
|
904
1033
|
/**
|
|
905
|
-
* Stream all response events as they arrive.
|
|
1034
|
+
* Stream all response events as they arrive across all turns.
|
|
906
1035
|
* Multiple consumers can iterate over this stream concurrently.
|
|
907
|
-
*
|
|
1036
|
+
* Includes API events, tool events, and turn.start/turn.end delimiters.
|
|
908
1037
|
*/
|
|
909
1038
|
getFullResponsesStream() {
|
|
910
1039
|
return async function* () {
|
|
911
1040
|
await this.initStream();
|
|
912
|
-
if (!this.reusableStream) {
|
|
1041
|
+
if (!this.reusableStream && !this.finalResponse) {
|
|
913
1042
|
throw new Error('Stream not initialized');
|
|
914
1043
|
}
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
1044
|
+
if (!this.options.tools?.length) {
|
|
1045
|
+
if (this.reusableStream) {
|
|
1046
|
+
const consumer = this.reusableStream.createConsumer();
|
|
1047
|
+
for await (const event of consumer) {
|
|
1048
|
+
yield event;
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
return;
|
|
1052
|
+
}
|
|
1053
|
+
const { consumer, executionPromise } = this.startTurnBroadcasterExecution();
|
|
924
1054
|
for await (const event of consumer) {
|
|
925
1055
|
yield event;
|
|
926
1056
|
}
|
|
927
|
-
// Yield tool events as they arrive (real-time!)
|
|
928
|
-
for await (const event of toolEventConsumer) {
|
|
929
|
-
if (event.type === 'preliminary_result') {
|
|
930
|
-
yield {
|
|
931
|
-
type: 'tool.preliminary_result',
|
|
932
|
-
toolCallId: event.toolCallId,
|
|
933
|
-
result: event.result,
|
|
934
|
-
timestamp: Date.now(),
|
|
935
|
-
};
|
|
936
|
-
}
|
|
937
|
-
else if (event.type === 'tool_result') {
|
|
938
|
-
yield {
|
|
939
|
-
type: 'tool.result',
|
|
940
|
-
toolCallId: event.toolCallId,
|
|
941
|
-
result: event.result,
|
|
942
|
-
timestamp: Date.now(),
|
|
943
|
-
...(event.preliminaryResults && { preliminaryResults: event.preliminaryResults }),
|
|
944
|
-
};
|
|
945
|
-
}
|
|
946
|
-
}
|
|
947
|
-
// Ensure execution completed (handles errors)
|
|
948
1057
|
await executionPromise;
|
|
949
1058
|
}.call(this);
|
|
950
1059
|
}
|
|
951
1060
|
/**
|
|
952
|
-
* Stream only text deltas as they arrive.
|
|
953
|
-
* This filters the full event stream to only yield text content
|
|
1061
|
+
* Stream only text deltas as they arrive from all turns.
|
|
1062
|
+
* This filters the full event stream to only yield text content,
|
|
1063
|
+
* including text from follow-up responses in multi-turn tool loops.
|
|
954
1064
|
*/
|
|
955
1065
|
getTextStream() {
|
|
956
1066
|
return async function* () {
|
|
957
1067
|
await this.initStream();
|
|
958
|
-
if (!this.reusableStream) {
|
|
1068
|
+
if (!this.reusableStream && !this.finalResponse) {
|
|
959
1069
|
throw new Error('Stream not initialized');
|
|
960
1070
|
}
|
|
961
|
-
|
|
1071
|
+
if (!this.options.tools?.length) {
|
|
1072
|
+
if (this.reusableStream) {
|
|
1073
|
+
yield* extractTextDeltas(this.reusableStream);
|
|
1074
|
+
}
|
|
1075
|
+
return;
|
|
1076
|
+
}
|
|
1077
|
+
const { consumer, executionPromise } = this.startTurnBroadcasterExecution();
|
|
1078
|
+
for await (const event of consumer) {
|
|
1079
|
+
if (isOutputTextDeltaEvent(event)) {
|
|
1080
|
+
yield event.delta;
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
await executionPromise;
|
|
962
1084
|
}.call(this);
|
|
963
1085
|
}
|
|
964
1086
|
/**
|
|
@@ -978,11 +1100,13 @@ export class ModelResult {
|
|
|
978
1100
|
getItemsStream() {
|
|
979
1101
|
return async function* () {
|
|
980
1102
|
await this.initStream();
|
|
981
|
-
if (!this.reusableStream) {
|
|
1103
|
+
if (!this.reusableStream && !this.finalResponse) {
|
|
982
1104
|
throw new Error('Stream not initialized');
|
|
983
1105
|
}
|
|
984
1106
|
// Stream all items from the API response cumulatively
|
|
985
|
-
|
|
1107
|
+
if (this.reusableStream) {
|
|
1108
|
+
yield* buildItemsStream(this.reusableStream);
|
|
1109
|
+
}
|
|
986
1110
|
// Execute tools if needed
|
|
987
1111
|
await this.executeToolsIfNeeded();
|
|
988
1112
|
// Yield function calls and outputs for each tool round
|
|
@@ -1028,11 +1152,13 @@ export class ModelResult {
|
|
|
1028
1152
|
getNewMessagesStream() {
|
|
1029
1153
|
return async function* () {
|
|
1030
1154
|
await this.initStream();
|
|
1031
|
-
if (!this.reusableStream) {
|
|
1155
|
+
if (!this.reusableStream && !this.finalResponse) {
|
|
1032
1156
|
throw new Error('Stream not initialized');
|
|
1033
1157
|
}
|
|
1034
1158
|
// First yield messages from the stream in responses format
|
|
1035
|
-
|
|
1159
|
+
if (this.reusableStream) {
|
|
1160
|
+
yield* buildResponsesMessageStream(this.reusableStream);
|
|
1161
|
+
}
|
|
1036
1162
|
// Execute tools if needed
|
|
1037
1163
|
await this.executeToolsIfNeeded();
|
|
1038
1164
|
// Yield function calls and their outputs for each executed tool
|
|
@@ -1059,20 +1185,33 @@ export class ModelResult {
|
|
|
1059
1185
|
}.call(this);
|
|
1060
1186
|
}
|
|
1061
1187
|
/**
|
|
1062
|
-
* Stream only reasoning deltas as they arrive.
|
|
1063
|
-
* This filters the full event stream to only yield reasoning content
|
|
1188
|
+
* Stream only reasoning deltas as they arrive from all turns.
|
|
1189
|
+
* This filters the full event stream to only yield reasoning content,
|
|
1190
|
+
* including reasoning from follow-up responses in multi-turn tool loops.
|
|
1064
1191
|
*/
|
|
1065
1192
|
getReasoningStream() {
|
|
1066
1193
|
return async function* () {
|
|
1067
1194
|
await this.initStream();
|
|
1068
|
-
if (!this.reusableStream) {
|
|
1195
|
+
if (!this.reusableStream && !this.finalResponse) {
|
|
1069
1196
|
throw new Error('Stream not initialized');
|
|
1070
1197
|
}
|
|
1071
|
-
|
|
1198
|
+
if (!this.options.tools?.length) {
|
|
1199
|
+
if (this.reusableStream) {
|
|
1200
|
+
yield* extractReasoningDeltas(this.reusableStream);
|
|
1201
|
+
}
|
|
1202
|
+
return;
|
|
1203
|
+
}
|
|
1204
|
+
const { consumer, executionPromise } = this.startTurnBroadcasterExecution();
|
|
1205
|
+
for await (const event of consumer) {
|
|
1206
|
+
if (isReasoningDeltaEvent(event)) {
|
|
1207
|
+
yield event.delta;
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
await executionPromise;
|
|
1072
1211
|
}.call(this);
|
|
1073
1212
|
}
|
|
1074
1213
|
/**
|
|
1075
|
-
* Stream tool call argument deltas and preliminary results.
|
|
1214
|
+
* Stream tool call argument deltas and preliminary results from all turns.
|
|
1076
1215
|
* Preliminary results are streamed in REAL-TIME as generator tools yield.
|
|
1077
1216
|
* - Tool call argument deltas as { type: "delta", content: string }
|
|
1078
1217
|
* - Preliminary results as { type: "preliminary_result", toolCallId, result }
|
|
@@ -1080,30 +1219,31 @@ export class ModelResult {
|
|
|
1080
1219
|
getToolStream() {
|
|
1081
1220
|
return async function* () {
|
|
1082
1221
|
await this.initStream();
|
|
1083
|
-
if (!this.reusableStream) {
|
|
1222
|
+
if (!this.reusableStream && !this.finalResponse) {
|
|
1084
1223
|
throw new Error('Stream not initialized');
|
|
1085
1224
|
}
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
// Yield tool deltas from API stream
|
|
1094
|
-
for await (const delta of extractToolDeltas(this.reusableStream)) {
|
|
1095
|
-
yield {
|
|
1096
|
-
type: 'delta',
|
|
1097
|
-
content: delta,
|
|
1098
|
-
};
|
|
1225
|
+
if (!this.options.tools?.length) {
|
|
1226
|
+
if (this.reusableStream) {
|
|
1227
|
+
for await (const delta of extractToolDeltas(this.reusableStream)) {
|
|
1228
|
+
yield { type: 'delta', content: delta };
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1231
|
+
return;
|
|
1099
1232
|
}
|
|
1100
|
-
|
|
1101
|
-
for await (const event of
|
|
1102
|
-
if (event.type === '
|
|
1103
|
-
yield event;
|
|
1233
|
+
const { consumer, executionPromise } = this.startTurnBroadcasterExecution();
|
|
1234
|
+
for await (const event of consumer) {
|
|
1235
|
+
if (event.type === 'response.function_call_arguments.delta') {
|
|
1236
|
+
yield { type: 'delta', content: event.delta };
|
|
1237
|
+
continue;
|
|
1238
|
+
}
|
|
1239
|
+
if (event.type === 'tool.preliminary_result') {
|
|
1240
|
+
yield {
|
|
1241
|
+
type: 'preliminary_result',
|
|
1242
|
+
toolCallId: event.toolCallId,
|
|
1243
|
+
result: event.result,
|
|
1244
|
+
};
|
|
1104
1245
|
}
|
|
1105
1246
|
}
|
|
1106
|
-
// Ensure execution completed (handles errors)
|
|
1107
1247
|
await executionPromise;
|
|
1108
1248
|
}.call(this);
|
|
1109
1249
|
}
|
|
@@ -1132,12 +1272,76 @@ export class ModelResult {
|
|
|
1132
1272
|
getToolCallsStream() {
|
|
1133
1273
|
return async function* () {
|
|
1134
1274
|
await this.initStream();
|
|
1135
|
-
if (!this.reusableStream) {
|
|
1275
|
+
if (!this.reusableStream && !this.finalResponse) {
|
|
1136
1276
|
throw new Error('Stream not initialized');
|
|
1137
1277
|
}
|
|
1138
|
-
|
|
1278
|
+
if (this.reusableStream) {
|
|
1279
|
+
yield* buildToolCallStream(this.reusableStream);
|
|
1280
|
+
}
|
|
1139
1281
|
}.call(this);
|
|
1140
1282
|
}
|
|
1283
|
+
/**
|
|
1284
|
+
* Returns an async iterable that emits a full context snapshot every time
|
|
1285
|
+
* any tool calls ctx.update(). Can be consumed concurrently with getText(),
|
|
1286
|
+
* getToolStream(), etc.
|
|
1287
|
+
*
|
|
1288
|
+
* @example
|
|
1289
|
+
* ```typescript
|
|
1290
|
+
* for await (const snapshot of result.getContextUpdates()) {
|
|
1291
|
+
* console.log('Context changed:', snapshot);
|
|
1292
|
+
* }
|
|
1293
|
+
* ```
|
|
1294
|
+
*/
|
|
1295
|
+
async *getContextUpdates() {
|
|
1296
|
+
// Ensure stream is initialized (which creates the context store)
|
|
1297
|
+
await this.initStream();
|
|
1298
|
+
if (!this.contextStore) {
|
|
1299
|
+
return;
|
|
1300
|
+
}
|
|
1301
|
+
const store = this.contextStore;
|
|
1302
|
+
const queue = [];
|
|
1303
|
+
let resolve = null;
|
|
1304
|
+
let done = false;
|
|
1305
|
+
const unsubscribe = store.subscribe((snapshot) => {
|
|
1306
|
+
queue.push(snapshot);
|
|
1307
|
+
if (resolve) {
|
|
1308
|
+
resolve();
|
|
1309
|
+
resolve = null;
|
|
1310
|
+
}
|
|
1311
|
+
});
|
|
1312
|
+
// Signal completion when tool execution finishes
|
|
1313
|
+
this.executeToolsIfNeeded().then(() => {
|
|
1314
|
+
done = true;
|
|
1315
|
+
if (resolve) {
|
|
1316
|
+
resolve();
|
|
1317
|
+
resolve = null;
|
|
1318
|
+
}
|
|
1319
|
+
}, () => {
|
|
1320
|
+
done = true;
|
|
1321
|
+
if (resolve) {
|
|
1322
|
+
resolve();
|
|
1323
|
+
resolve = null;
|
|
1324
|
+
}
|
|
1325
|
+
});
|
|
1326
|
+
try {
|
|
1327
|
+
while (!done) {
|
|
1328
|
+
if (queue.length > 0) {
|
|
1329
|
+
yield queue.shift();
|
|
1330
|
+
}
|
|
1331
|
+
else {
|
|
1332
|
+
// Wait for next update or completion
|
|
1333
|
+
await new Promise((r) => { resolve = r; });
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
// Drain any remaining queued snapshots
|
|
1337
|
+
while (queue.length > 0) {
|
|
1338
|
+
yield queue.shift();
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
finally {
|
|
1342
|
+
unsubscribe();
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1141
1345
|
/**
|
|
1142
1346
|
* Cancel the underlying stream and all consumers
|
|
1143
1347
|
*/
|