@fgv/ts-extras 5.1.0-32 → 5.1.0-34
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/packlets/ai-assist/apiClient.js +4 -4
- package/dist/packlets/ai-assist/apiClient.js.map +1 -1
- package/dist/packlets/ai-assist/chatRequestBuilders.js +86 -3
- package/dist/packlets/ai-assist/chatRequestBuilders.js.map +1 -1
- package/dist/packlets/ai-assist/converters.js +31 -1
- package/dist/packlets/ai-assist/converters.js.map +1 -1
- package/dist/packlets/ai-assist/index.js +3 -2
- package/dist/packlets/ai-assist/index.js.map +1 -1
- package/dist/packlets/ai-assist/model.js.map +1 -1
- package/dist/packlets/ai-assist/streamingAdapters/anthropic.js +176 -32
- package/dist/packlets/ai-assist/streamingAdapters/anthropic.js.map +1 -1
- package/dist/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.js +511 -0
- package/dist/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.js.map +1 -0
- package/dist/packlets/ai-assist/streamingAdapters/common.js +95 -0
- package/dist/packlets/ai-assist/streamingAdapters/common.js.map +1 -1
- package/dist/packlets/ai-assist/streamingAdapters/gemini.js +34 -10
- package/dist/packlets/ai-assist/streamingAdapters/gemini.js.map +1 -1
- package/dist/packlets/ai-assist/streamingAdapters/openaiResponses.js +215 -15
- package/dist/packlets/ai-assist/streamingAdapters/openaiResponses.js.map +1 -1
- package/dist/packlets/ai-assist/streamingClient.js +18 -0
- package/dist/packlets/ai-assist/streamingClient.js.map +1 -1
- package/dist/packlets/ai-assist/thinkingOptionsResolver.js +23 -0
- package/dist/packlets/ai-assist/thinkingOptionsResolver.js.map +1 -1
- package/dist/packlets/ai-assist/toolFormats.js +106 -10
- package/dist/packlets/ai-assist/toolFormats.js.map +1 -1
- package/dist/packlets/crypto-utils/index.browser.js +3 -2
- package/dist/packlets/crypto-utils/index.browser.js.map +1 -1
- package/dist/packlets/crypto-utils/keystore/encryptedFilePrivateKeyStorage.js +287 -0
- package/dist/packlets/crypto-utils/keystore/encryptedFilePrivateKeyStorage.js.map +1 -0
- package/dist/packlets/crypto-utils/keystore/index.browser.js +36 -0
- package/dist/packlets/crypto-utils/keystore/index.browser.js.map +1 -0
- package/dist/packlets/crypto-utils/keystore/index.js +2 -0
- package/dist/packlets/crypto-utils/keystore/index.js.map +1 -1
- package/dist/packlets/crypto-utils/keystore/privateKeyStorage.js.map +1 -1
- package/dist/ts-extras.d.ts +492 -6
- package/lib/packlets/ai-assist/apiClient.d.ts.map +1 -1
- package/lib/packlets/ai-assist/apiClient.js +3 -3
- package/lib/packlets/ai-assist/apiClient.js.map +1 -1
- package/lib/packlets/ai-assist/chatRequestBuilders.d.ts +29 -5
- package/lib/packlets/ai-assist/chatRequestBuilders.d.ts.map +1 -1
- package/lib/packlets/ai-assist/chatRequestBuilders.js +86 -3
- package/lib/packlets/ai-assist/chatRequestBuilders.js.map +1 -1
- package/lib/packlets/ai-assist/converters.d.ts +9 -1
- package/lib/packlets/ai-assist/converters.d.ts.map +1 -1
- package/lib/packlets/ai-assist/converters.js +31 -1
- package/lib/packlets/ai-assist/converters.js.map +1 -1
- package/lib/packlets/ai-assist/index.d.ts +4 -3
- package/lib/packlets/ai-assist/index.d.ts.map +1 -1
- package/lib/packlets/ai-assist/index.js +5 -1
- package/lib/packlets/ai-assist/index.js.map +1 -1
- package/lib/packlets/ai-assist/model.d.ts +183 -3
- package/lib/packlets/ai-assist/model.d.ts.map +1 -1
- package/lib/packlets/ai-assist/model.js.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/anthropic.d.ts +58 -5
- package/lib/packlets/ai-assist/streamingAdapters/anthropic.d.ts.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/anthropic.js +175 -31
- package/lib/packlets/ai-assist/streamingAdapters/anthropic.js.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.d.ts +158 -0
- package/lib/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.d.ts.map +1 -0
- package/lib/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.js +517 -0
- package/lib/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.js.map +1 -0
- package/lib/packlets/ai-assist/streamingAdapters/common.d.ts +51 -0
- package/lib/packlets/ai-assist/streamingAdapters/common.d.ts.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/common.js +97 -0
- package/lib/packlets/ai-assist/streamingAdapters/common.js.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/gemini.d.ts +16 -2
- package/lib/packlets/ai-assist/streamingAdapters/gemini.d.ts.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/gemini.js +34 -10
- package/lib/packlets/ai-assist/streamingAdapters/gemini.js.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/openaiResponses.d.ts +15 -2
- package/lib/packlets/ai-assist/streamingAdapters/openaiResponses.d.ts.map +1 -1
- package/lib/packlets/ai-assist/streamingAdapters/openaiResponses.js +214 -14
- package/lib/packlets/ai-assist/streamingAdapters/openaiResponses.js.map +1 -1
- package/lib/packlets/ai-assist/streamingClient.d.ts +17 -0
- package/lib/packlets/ai-assist/streamingClient.d.ts.map +1 -1
- package/lib/packlets/ai-assist/streamingClient.js +20 -1
- package/lib/packlets/ai-assist/streamingClient.js.map +1 -1
- package/lib/packlets/ai-assist/thinkingOptionsResolver.d.ts +18 -2
- package/lib/packlets/ai-assist/thinkingOptionsResolver.d.ts.map +1 -1
- package/lib/packlets/ai-assist/thinkingOptionsResolver.js +24 -0
- package/lib/packlets/ai-assist/thinkingOptionsResolver.js.map +1 -1
- package/lib/packlets/ai-assist/toolFormats.d.ts +40 -9
- package/lib/packlets/ai-assist/toolFormats.d.ts.map +1 -1
- package/lib/packlets/ai-assist/toolFormats.js +107 -10
- package/lib/packlets/ai-assist/toolFormats.js.map +1 -1
- package/lib/packlets/crypto-utils/index.browser.d.ts +1 -1
- package/lib/packlets/crypto-utils/index.browser.d.ts.map +1 -1
- package/lib/packlets/crypto-utils/index.browser.js +3 -2
- package/lib/packlets/crypto-utils/index.browser.js.map +1 -1
- package/lib/packlets/crypto-utils/keystore/encryptedFilePrivateKeyStorage.d.ts +148 -0
- package/lib/packlets/crypto-utils/keystore/encryptedFilePrivateKeyStorage.d.ts.map +1 -0
- package/lib/packlets/crypto-utils/keystore/encryptedFilePrivateKeyStorage.js +324 -0
- package/lib/packlets/crypto-utils/keystore/encryptedFilePrivateKeyStorage.js.map +1 -0
- package/lib/packlets/crypto-utils/keystore/index.browser.d.ts +10 -0
- package/lib/packlets/crypto-utils/keystore/index.browser.d.ts.map +1 -0
- package/lib/packlets/crypto-utils/keystore/index.browser.js +76 -0
- package/lib/packlets/crypto-utils/keystore/index.browser.js.map +1 -0
- package/lib/packlets/crypto-utils/keystore/index.d.ts +1 -0
- package/lib/packlets/crypto-utils/keystore/index.d.ts.map +1 -1
- package/lib/packlets/crypto-utils/keystore/index.js +4 -1
- package/lib/packlets/crypto-utils/keystore/index.js.map +1 -1
- package/lib/packlets/crypto-utils/keystore/privateKeyStorage.d.ts +6 -3
- package/lib/packlets/crypto-utils/keystore/privateKeyStorage.d.ts.map +1 -1
- package/lib/packlets/crypto-utils/keystore/privateKeyStorage.js.map +1 -1
- package/package.json +15 -10
|
@@ -46,6 +46,9 @@ exports.callOpenAiResponsesStream = callOpenAiResponsesStream;
|
|
|
46
46
|
* Completions doesn't support tool progress events, but the Responses API
|
|
47
47
|
* does.
|
|
48
48
|
*
|
|
49
|
+
* Client-defined tools (`function_call` type) are accumulated per call ID
|
|
50
|
+
* and emitted as `client-tool-call-done` events when complete.
|
|
51
|
+
*
|
|
49
52
|
* @packageDocumentation
|
|
50
53
|
*/
|
|
51
54
|
const ts_utils_1 = require("@fgv/ts-utils");
|
|
@@ -57,8 +60,27 @@ const common_1 = require("./common");
|
|
|
57
60
|
const responsesDeltaPayload = ts_utils_1.Validators.object({
|
|
58
61
|
delta: ts_utils_1.Validators.string
|
|
59
62
|
});
|
|
63
|
+
const responsesOutputItemInner = ts_utils_1.Validators.object({
|
|
64
|
+
type: ts_utils_1.Validators.string.optional(),
|
|
65
|
+
id: ts_utils_1.Validators.string.optional(),
|
|
66
|
+
name: ts_utils_1.Validators.string.optional(),
|
|
67
|
+
call_id: ts_utils_1.Validators.string.optional()
|
|
68
|
+
}, { options: { optionalFields: ['type', 'id', 'name', 'call_id'] } });
|
|
69
|
+
const responsesOutputItemAddedPayload = ts_utils_1.Validators.object({ item: responsesOutputItemInner });
|
|
70
|
+
const responsesFunctionCallArgsDeltaPayload = ts_utils_1.Validators.object({
|
|
71
|
+
item_id: ts_utils_1.Validators.string,
|
|
72
|
+
delta: ts_utils_1.Validators.string
|
|
73
|
+
});
|
|
74
|
+
const responsesFunctionCallArgsDonePayload = ts_utils_1.Validators.object({
|
|
75
|
+
item_id: ts_utils_1.Validators.string,
|
|
76
|
+
arguments: ts_utils_1.Validators.string
|
|
77
|
+
});
|
|
78
|
+
const responsesIncompleteDetails = ts_utils_1.Validators.object({ reason: ts_utils_1.Validators.string.optional() }, { options: { optionalFields: ['reason'] } });
|
|
60
79
|
const responsesCompletedPayload = ts_utils_1.Validators.object({
|
|
61
|
-
response: ts_utils_1.Validators.object({
|
|
80
|
+
response: ts_utils_1.Validators.object({
|
|
81
|
+
status: ts_utils_1.Validators.string.optional(),
|
|
82
|
+
incomplete_details: responsesIncompleteDetails.optional()
|
|
83
|
+
}, { options: { optionalFields: ['status', 'incomplete_details'] } })
|
|
62
84
|
});
|
|
63
85
|
const responsesErrorInner = ts_utils_1.Validators.object({ message: ts_utils_1.Validators.string.optional() }, { options: { optionalFields: ['message'] } });
|
|
64
86
|
const responsesErrorPayload = ts_utils_1.Validators.object({
|
|
@@ -68,26 +90,117 @@ const responsesErrorPayload = ts_utils_1.Validators.object({
|
|
|
68
90
|
// ============================================================================
|
|
69
91
|
// Stream translator
|
|
70
92
|
// ============================================================================
|
|
93
|
+
/**
|
|
94
|
+
* Recognized OpenAI / xAI Responses API SSE event names. An event in this set is either
|
|
95
|
+
* handled below or intentionally ignored (lifecycle / reasoning content discarded by design /
|
|
96
|
+
* server-tool channels not yet surfaced). Any event whose name is NOT in this set surfaces
|
|
97
|
+
* a one-time `logger.warn` per stream — making provider drift (new event types from a model
|
|
98
|
+
* update) visible empirically instead of silently no-op'd.
|
|
99
|
+
*
|
|
100
|
+
* Source: official `openai-node` SDK source (`src/resources/responses/responses.ts`)
|
|
101
|
+
* event-type literal-string sweep, plus the xAI Responses superset. Add to this set when a
|
|
102
|
+
* new event type is observed and confirmed safe to ignore.
|
|
103
|
+
*
|
|
104
|
+
* @internal
|
|
105
|
+
*/
|
|
106
|
+
const RECOGNIZED_OPENAI_RESPONSES_EVENTS = new Set([
|
|
107
|
+
// ---- handled by the translator ----
|
|
108
|
+
'response.output_text.delta',
|
|
109
|
+
'response.web_search_call.in_progress',
|
|
110
|
+
'response.web_search_call.completed',
|
|
111
|
+
'response.output_item.added',
|
|
112
|
+
'response.function_call_arguments.delta',
|
|
113
|
+
'response.function_call_arguments.done',
|
|
114
|
+
'response.completed',
|
|
115
|
+
'response.failed',
|
|
116
|
+
'error',
|
|
117
|
+
// ---- lifecycle events: intentionally silent ----
|
|
118
|
+
'response.created',
|
|
119
|
+
'response.in_progress',
|
|
120
|
+
'response.queued',
|
|
121
|
+
'response.incomplete',
|
|
122
|
+
// ---- text content lifecycle: handled implicitly via delta accumulation ----
|
|
123
|
+
'response.output_text.done',
|
|
124
|
+
'response.output_text.annotation.added',
|
|
125
|
+
'response.content_part.added',
|
|
126
|
+
'response.content_part.done',
|
|
127
|
+
'response.output_item.done',
|
|
128
|
+
// ---- reasoning content: discarded by design (see ai-assist-thinking-events follow-on) ----
|
|
129
|
+
'response.reasoning_summary_part.added',
|
|
130
|
+
'response.reasoning_summary_part.done',
|
|
131
|
+
'response.reasoning_summary_text.delta',
|
|
132
|
+
'response.reasoning_summary_text.done',
|
|
133
|
+
'response.reasoning_text.delta',
|
|
134
|
+
'response.reasoning_text.done',
|
|
135
|
+
// ---- refusals: caller currently sees these via the model's final text ----
|
|
136
|
+
'response.refusal.delta',
|
|
137
|
+
'response.refusal.done',
|
|
138
|
+
// ---- server-tool channels not surfaced as tool-event yet ----
|
|
139
|
+
'response.web_search_call.searching',
|
|
140
|
+
'response.file_search_call.in_progress',
|
|
141
|
+
'response.file_search_call.searching',
|
|
142
|
+
'response.file_search_call.completed',
|
|
143
|
+
'response.code_interpreter_call.in_progress',
|
|
144
|
+
'response.code_interpreter_call.interpreting',
|
|
145
|
+
'response.code_interpreter_call.completed',
|
|
146
|
+
'response.code_interpreter_call_code.delta',
|
|
147
|
+
'response.code_interpreter_call_code.done',
|
|
148
|
+
'response.image_generation_call.in_progress',
|
|
149
|
+
'response.image_generation_call.generating',
|
|
150
|
+
'response.image_generation_call.completed',
|
|
151
|
+
'response.image_generation_call.partial_image',
|
|
152
|
+
'response.mcp_call.in_progress',
|
|
153
|
+
'response.mcp_call.completed',
|
|
154
|
+
'response.mcp_call.failed',
|
|
155
|
+
'response.mcp_call_arguments.delta',
|
|
156
|
+
'response.mcp_call_arguments.done',
|
|
157
|
+
'response.mcp_list_tools.in_progress',
|
|
158
|
+
'response.mcp_list_tools.completed',
|
|
159
|
+
'response.mcp_list_tools.failed',
|
|
160
|
+
'response.custom_tool_call_input.delta',
|
|
161
|
+
'response.custom_tool_call_input.done',
|
|
162
|
+
// ---- audio (not used in chat/tool flows here) ----
|
|
163
|
+
'response.audio.delta',
|
|
164
|
+
'response.audio.done',
|
|
165
|
+
'response.audio.transcript.delta',
|
|
166
|
+
'response.audio.transcript.done'
|
|
167
|
+
]);
|
|
71
168
|
/**
|
|
72
169
|
* Translates an OpenAI Responses API SSE stream into unified events.
|
|
73
170
|
*
|
|
171
|
+
* Maintains a per-call-ID accumulation map for client-defined function calls.
|
|
172
|
+
* The map is exposed via the passed-in `functionCallMap` parameter for the
|
|
173
|
+
* C3 continuation builder to read after the stream completes.
|
|
174
|
+
*
|
|
175
|
+
* Unrecognized event names are reported once per stream via `logger?.warn` —
|
|
176
|
+
* see {@link RECOGNIZED_OPENAI_RESPONSES_EVENTS}.
|
|
177
|
+
*
|
|
74
178
|
* @internal
|
|
75
179
|
*/
|
|
76
|
-
function translateOpenAiResponsesStream(response) {
|
|
180
|
+
function translateOpenAiResponsesStream(response, functionCallMap, logger) {
|
|
77
181
|
return __asyncGenerator(this, arguments, function* translateOpenAiResponsesStream_1() {
|
|
78
182
|
var _a, e_1, _b, _c;
|
|
79
|
-
var _d, _e, _f;
|
|
183
|
+
var _d, _e, _f, _g, _h;
|
|
80
184
|
let fullText = '';
|
|
81
185
|
let truncated = false;
|
|
82
186
|
let completed = false;
|
|
187
|
+
let incompleteReason;
|
|
188
|
+
// OpenAI / xAI Responses API emits function_call_arguments.{delta,done} events keyed by
|
|
189
|
+
// `item_id` (the fc_* output-item id). The harness and continuation builder key by
|
|
190
|
+
// `call_id` (the call_* id). This map correlates the two — populated when the
|
|
191
|
+
// function_call item is announced in `response.output_item.added`.
|
|
192
|
+
const itemIdToCallId = new Map();
|
|
193
|
+
// Track unrecognized event names we have already warned about, so a hot stream of
|
|
194
|
+
// an unknown event type produces exactly one log line per name per stream.
|
|
195
|
+
const warnedEvents = new Set();
|
|
83
196
|
try {
|
|
84
197
|
/* c8 ignore next - body is non-null at this point per openSseConnection */
|
|
85
198
|
if (!response.body)
|
|
86
199
|
return yield __await(void 0);
|
|
87
200
|
try {
|
|
88
|
-
for (var
|
|
89
|
-
_c =
|
|
90
|
-
|
|
201
|
+
for (var _j = true, _k = __asyncValues((0, sseParser_1.readSseEvents)(response.body)), _l; _l = yield __await(_k.next()), _a = _l.done, !_a; _j = true) {
|
|
202
|
+
_c = _l.value;
|
|
203
|
+
_j = false;
|
|
91
204
|
const message = _c;
|
|
92
205
|
const eventName = message.event;
|
|
93
206
|
if (eventName === 'response.output_text.delta') {
|
|
@@ -105,26 +218,111 @@ function translateOpenAiResponsesStream(response) {
|
|
|
105
218
|
else if (eventName === 'response.web_search_call.completed') {
|
|
106
219
|
yield yield __await({ type: 'tool-event', toolType: 'web_search', phase: 'completed' });
|
|
107
220
|
}
|
|
221
|
+
else if (eventName === 'response.output_item.added') {
|
|
222
|
+
const payload = (0, common_1.validateEventPayload)((0, sseParser_1.parseSseEventJson)(message.data), responsesOutputItemAddedPayload);
|
|
223
|
+
/* c8 ignore next 1 - defensive: payload null branch unreachable after validation */
|
|
224
|
+
const item = payload === null || payload === void 0 ? void 0 : payload.item;
|
|
225
|
+
/* c8 ignore next 1 - defensive: falsy call_id/name branches are protocol violations */
|
|
226
|
+
if ((item === null || item === void 0 ? void 0 : item.type) === 'function_call' && item.call_id && item.name) {
|
|
227
|
+
functionCallMap.set(item.call_id, { id: item.call_id, name: item.name, argsBuffer: '' });
|
|
228
|
+
// Reasoning models (and the standard flow) reference this function_call in subsequent
|
|
229
|
+
// arguments.{delta,done} events by `item_id`, never by `call_id`. Record the mapping
|
|
230
|
+
// so the arg-accumulation handlers can resolve the call.
|
|
231
|
+
if (item.id) {
|
|
232
|
+
itemIdToCallId.set(item.id, item.call_id);
|
|
233
|
+
}
|
|
234
|
+
yield yield __await({ type: 'client-tool-call-start', toolName: item.name, callId: item.call_id });
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
else if (eventName === 'response.function_call_arguments.delta') {
|
|
238
|
+
const payload = (0, common_1.validateEventPayload)((0, sseParser_1.parseSseEventJson)(message.data), responsesFunctionCallArgsDeltaPayload);
|
|
239
|
+
/* c8 ignore next 1 - defensive: payload null branch unreachable after validation */
|
|
240
|
+
const itemId = payload === null || payload === void 0 ? void 0 : payload.item_id;
|
|
241
|
+
if (itemId !== undefined) {
|
|
242
|
+
const callId = itemIdToCallId.get(itemId);
|
|
243
|
+
/* c8 ignore next 1 - defensive: orphan delta (no preceding output_item.added) is a protocol violation */
|
|
244
|
+
const call = callId !== undefined ? functionCallMap.get(callId) : undefined;
|
|
245
|
+
/* c8 ignore next 1 - defensive: call always present and delta always string in valid stream */
|
|
246
|
+
if (call && typeof (payload === null || payload === void 0 ? void 0 : payload.delta) === 'string') {
|
|
247
|
+
call.argsBuffer += payload.delta;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
else if (eventName === 'response.function_call_arguments.done') {
|
|
252
|
+
const payload = (0, common_1.validateEventPayload)((0, sseParser_1.parseSseEventJson)(message.data), responsesFunctionCallArgsDonePayload);
|
|
253
|
+
/* c8 ignore next 1 - defensive: payload null branch unreachable after validation */
|
|
254
|
+
const itemId = payload === null || payload === void 0 ? void 0 : payload.item_id;
|
|
255
|
+
if (itemId !== undefined) {
|
|
256
|
+
const callId = itemIdToCallId.get(itemId);
|
|
257
|
+
/* c8 ignore next 1 - defensive: orphan done (no preceding output_item.added) is a protocol violation */
|
|
258
|
+
const call = callId !== undefined ? functionCallMap.get(callId) : undefined;
|
|
259
|
+
if (call && callId !== undefined) {
|
|
260
|
+
/* c8 ignore next 1 - defensive: payload?.arguments null branch unreachable after validation */
|
|
261
|
+
const canonicalArgs = (_d = payload === null || payload === void 0 ? void 0 : payload.arguments) !== null && _d !== void 0 ? _d : '{}';
|
|
262
|
+
// Sync the accumulation entry with the canonical arguments from the .done event.
|
|
263
|
+
// Delta events may carry partial/empty payloads; the .done event carries the
|
|
264
|
+
// authoritative final arguments string used by the continuation builder.
|
|
265
|
+
call.argsBuffer = canonicalArgs;
|
|
266
|
+
let args;
|
|
267
|
+
try {
|
|
268
|
+
const parsed = JSON.parse(canonicalArgs);
|
|
269
|
+
/* c8 ignore start - defensive: non-object/malformed parse defaults to empty object */
|
|
270
|
+
args =
|
|
271
|
+
parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed)
|
|
272
|
+
? parsed
|
|
273
|
+
: {};
|
|
274
|
+
}
|
|
275
|
+
catch (_m) {
|
|
276
|
+
args = {};
|
|
277
|
+
}
|
|
278
|
+
/* c8 ignore stop */
|
|
279
|
+
yield yield __await({ type: 'client-tool-call-done', toolName: call.name, callId, args });
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
108
283
|
else if (eventName === 'response.completed') {
|
|
109
284
|
const payload = (0, common_1.validateEventPayload)((0, sseParser_1.parseSseEventJson)(message.data), responsesCompletedPayload);
|
|
110
|
-
/* c8 ignore next 1 - defensive: payload
|
|
111
|
-
|
|
285
|
+
/* c8 ignore next 1 - defensive: payload null branch unreachable after validation */
|
|
286
|
+
if (payload) {
|
|
287
|
+
truncated = payload.response.status === 'incomplete';
|
|
288
|
+
// Per IAiStreamDone.incompleteReason's contract, the reason is meaningful only
|
|
289
|
+
// when the response was truncated. Move both fields together on every completed
|
|
290
|
+
// event so a stray incomplete_details on a non-incomplete payload never leaks
|
|
291
|
+
// through, and a later (defensive) completed event can't leave a stale reason.
|
|
292
|
+
incompleteReason = truncated ? (_e = payload.response.incomplete_details) === null || _e === void 0 ? void 0 : _e.reason : undefined;
|
|
293
|
+
}
|
|
112
294
|
completed = true;
|
|
113
295
|
/* c8 ignore next 1 - defensive: eventName === 'error' alternative not exercised in tests */
|
|
114
296
|
}
|
|
115
297
|
else if (eventName === 'response.failed' || eventName === 'error') {
|
|
116
298
|
const payload = (0, common_1.validateEventPayload)((0, sseParser_1.parseSseEventJson)(message.data), responsesErrorPayload);
|
|
117
299
|
/* c8 ignore next 1 - defensive: payload?.error and payload?.message null branches unreachable */
|
|
118
|
-
const errMsg = (
|
|
300
|
+
const errMsg = (_h = (_g = (_f = payload === null || payload === void 0 ? void 0 : payload.error) === null || _f === void 0 ? void 0 : _f.message) !== null && _g !== void 0 ? _g : payload === null || payload === void 0 ? void 0 : payload.message) !== null && _h !== void 0 ? _h : 'Responses API stream failed';
|
|
119
301
|
yield yield __await({ type: 'error', message: errMsg });
|
|
120
302
|
return yield __await(void 0);
|
|
121
303
|
}
|
|
304
|
+
else if (typeof eventName === 'string' &&
|
|
305
|
+
!RECOGNIZED_OPENAI_RESPONSES_EVENTS.has(eventName) &&
|
|
306
|
+
!warnedEvents.has(eventName)) {
|
|
307
|
+
// Empirical drift instrument: an unrecognized SSE event from the Responses API
|
|
308
|
+
// surfaces as a one-time warning per stream. If a provider adds a new event type
|
|
309
|
+
// (or changes an existing event's name), the warning fires the first time the
|
|
310
|
+
// event arrives. Update RECOGNIZED_OPENAI_RESPONSES_EVENTS once the new event is
|
|
311
|
+
// either handled or confirmed safe to ignore.
|
|
312
|
+
warnedEvents.add(eventName);
|
|
313
|
+
logger === null || logger === void 0 ? void 0 : logger.warn(`${common_1.UNRECOGNIZED_EVENT_WARN_TAG} OpenAI Responses adapter: unrecognized SSE event ` +
|
|
314
|
+
`'${eventName}'. ` +
|
|
315
|
+
`payload preview: ${(0, common_1.formatUnrecognizedEventPayloadPreview)(message.data)}. ` +
|
|
316
|
+
`This may indicate provider drift — if the new event carries data the adapter ` +
|
|
317
|
+
`should surface, add a handler; otherwise add the name to ` +
|
|
318
|
+
`RECOGNIZED_OPENAI_RESPONSES_EVENTS.`);
|
|
319
|
+
}
|
|
122
320
|
}
|
|
123
321
|
}
|
|
124
322
|
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
125
323
|
finally {
|
|
126
324
|
try {
|
|
127
|
-
if (!
|
|
325
|
+
if (!_j && !_a && (_b = _k.return)) yield __await(_b.call(_k));
|
|
128
326
|
}
|
|
129
327
|
finally { if (e_1) throw e_1.error; }
|
|
130
328
|
}
|
|
@@ -134,7 +332,7 @@ function translateOpenAiResponsesStream(response) {
|
|
|
134
332
|
return yield __await(void 0);
|
|
135
333
|
} /* c8 ignore stop */
|
|
136
334
|
if (completed) {
|
|
137
|
-
yield yield __await({ type: 'done', truncated, fullText });
|
|
335
|
+
yield yield __await({ type: 'done', truncated, fullText, incompleteReason });
|
|
138
336
|
}
|
|
139
337
|
else {
|
|
140
338
|
yield yield __await({ type: 'error', message: 'Responses API stream ended without a completed event' });
|
|
@@ -150,11 +348,12 @@ function translateOpenAiResponsesStream(response) {
|
|
|
150
348
|
*
|
|
151
349
|
* @internal
|
|
152
350
|
*/
|
|
153
|
-
async function callOpenAiResponsesStream(config, prompt, tools, messagesBefore, temperature, logger, signal, resolvedThinking) {
|
|
351
|
+
async function callOpenAiResponsesStream(config, prompt, tools, messagesBefore, temperature, logger, signal, resolvedThinking, functionCallMap, continuationMessages) {
|
|
154
352
|
var _a;
|
|
155
353
|
const url = `${config.baseUrl}/responses`;
|
|
156
354
|
const input = (0, chatRequestBuilders_1.buildMessages)(prompt.system, (0, chatRequestBuilders_1.buildOpenAiResponsesUserContent)(prompt), {
|
|
157
|
-
head: messagesBefore
|
|
355
|
+
head: messagesBefore,
|
|
356
|
+
rawTail: continuationMessages
|
|
158
357
|
});
|
|
159
358
|
const effort = (_a = resolvedThinking === null || resolvedThinking === void 0 ? void 0 : resolvedThinking.openAiEffort) !== null && _a !== void 0 ? _a : resolvedThinking === null || resolvedThinking === void 0 ? void 0 : resolvedThinking.xaiEffort;
|
|
160
359
|
const supportsReasoning = config.model !== 'grok-4';
|
|
@@ -176,7 +375,8 @@ async function callOpenAiResponsesStream(config, prompt, tools, messagesBefore,
|
|
|
176
375
|
const headers = (0, endpoint_1.bearerAuthHeader)(config.apiKey);
|
|
177
376
|
/* c8 ignore next 3 - optional logger */
|
|
178
377
|
logger === null || logger === void 0 ? void 0 : logger.info(`OpenAI Responses streaming: model=${config.model}, tools=${tools.map((t) => t.type).join(',')}`);
|
|
378
|
+
const callMap = functionCallMap !== null && functionCallMap !== void 0 ? functionCallMap : new Map();
|
|
179
379
|
const conn = await (0, common_1.openSseConnection)(url, headers, body, logger, signal);
|
|
180
|
-
return conn.onSuccess((response) => (0, ts_utils_1.succeed)(translateOpenAiResponsesStream(response)));
|
|
380
|
+
return conn.onSuccess((response) => (0, ts_utils_1.succeed)(translateOpenAiResponsesStream(response, callMap, logger)));
|
|
181
381
|
}
|
|
182
382
|
//# sourceMappingURL=openaiResponses.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openaiResponses.js","sourceRoot":"","sources":["../../../../src/packlets/ai-assist/streamingAdapters/openaiResponses.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) 2026 Erik Fortune\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\n/**\n * Streaming adapter for the OpenAI / xAI Responses API. This is the format\n * used when server-side tools (e.g. web_search) are requested — Chat\n * Completions doesn't support tool progress events, but the Responses API\n * does.\n *\n * @packageDocumentation\n */\n\nimport { type Logging, Result, succeed, type Validator, Validators } from '@fgv/ts-utils';\n\nimport { buildMessages, buildOpenAiResponsesUserContent } from '../chatRequestBuilders';\nimport { bearerAuthHeader } from '../endpoint';\nimport { AiPrompt, type AiServerToolConfig, type IAiStreamEvent, type IChatMessage } from '../model';\nimport { parseSseEventJson, readSseEvents } from '../sseParser';\nimport { toResponsesApiTools } from '../toolFormats';\nimport { type IResolvedThinkingConfig } from '../thinkingOptionsResolver';\nimport { IStreamApiConfig, openSseConnection, validateEventPayload } from './common';\n\n// ============================================================================\n// Event payload shapes\n// ============================================================================\n\n/**\n * Payload of a `response.output_text.delta` SSE event.\n *\n * @internal\n */\ninterface IResponsesDeltaPayload {\n readonly delta: string;\n}\n\n/**\n * Payload of a `response.completed` SSE event. `status === 'incomplete'`\n * signals the stream was cut short (max output tokens, etc.).\n *\n * @internal\n */\ninterface IResponsesCompletedPayload {\n readonly response: { readonly status?: string };\n}\n\n/**\n * Payload of a `response.failed` or `error` SSE event. Both shapes appear\n * in the wild — sometimes `error.message`, sometimes a top-level `message`.\n *\n * @internal\n */\ninterface IResponsesErrorPayload {\n readonly error?: { readonly message?: string };\n readonly message?: string;\n}\n\nconst responsesDeltaPayload: Validator<IResponsesDeltaPayload> = Validators.object<IResponsesDeltaPayload>({\n delta: Validators.string\n});\n\nconst responsesCompletedPayload: Validator<IResponsesCompletedPayload> =\n Validators.object<IResponsesCompletedPayload>({\n response: Validators.object<{ status?: string }>(\n { status: Validators.string.optional() },\n { options: { optionalFields: ['status'] } }\n )\n });\n\nconst responsesErrorInner: Validator<{ message?: string }> = Validators.object<{ message?: string }>(\n { message: Validators.string.optional() },\n { options: { optionalFields: ['message'] } }\n);\n\nconst responsesErrorPayload: Validator<IResponsesErrorPayload> = Validators.object<IResponsesErrorPayload>(\n {\n error: responsesErrorInner.optional(),\n message: Validators.string.optional()\n },\n { options: { optionalFields: ['error', 'message'] } }\n);\n\n// ============================================================================\n// Stream translator\n// ============================================================================\n\n/**\n * Translates an OpenAI Responses API SSE stream into unified events.\n *\n * @internal\n */\nasync function* translateOpenAiResponsesStream(response: Response): AsyncGenerator<IAiStreamEvent> {\n let fullText = '';\n let truncated = false;\n let completed = false;\n\n try {\n /* c8 ignore next - body is non-null at this point per openSseConnection */\n if (!response.body) return;\n for await (const message of readSseEvents(response.body)) {\n const eventName = message.event;\n if (eventName === 'response.output_text.delta') {\n const payload = validateEventPayload(parseSseEventJson(message.data), responsesDeltaPayload);\n /* c8 ignore next 1 - defensive: payload?.delta null branch unreachable after validation */\n const delta = payload?.delta;\n if (typeof delta === 'string' && delta.length > 0) {\n fullText += delta;\n yield { type: 'text-delta', delta };\n }\n } else if (eventName === 'response.web_search_call.in_progress') {\n yield { type: 'tool-event', toolType: 'web_search', phase: 'started' };\n } else if (eventName === 'response.web_search_call.completed') {\n yield { type: 'tool-event', toolType: 'web_search', phase: 'completed' };\n } else if (eventName === 'response.completed') {\n const payload = validateEventPayload(parseSseEventJson(message.data), responsesCompletedPayload);\n /* c8 ignore next 1 - defensive: payload?.response null branch unreachable after validation */\n truncated = payload?.response.status === 'incomplete';\n completed = true;\n /* c8 ignore next 1 - defensive: eventName === 'error' alternative not exercised in tests */\n } else if (eventName === 'response.failed' || eventName === 'error') {\n const payload = validateEventPayload(parseSseEventJson(message.data), responsesErrorPayload);\n /* c8 ignore next 1 - defensive: payload?.error and payload?.message null branches unreachable */\n const errMsg = payload?.error?.message ?? payload?.message ?? 'Responses API stream failed';\n yield { type: 'error', message: errMsg };\n return;\n }\n }\n } catch (err: unknown) /* c8 ignore start - defensive: stream errors are always Error instances */ {\n yield { type: 'error', message: err instanceof Error ? err.message : String(err) };\n return;\n } /* c8 ignore stop */\n\n if (completed) {\n yield { type: 'done', truncated, fullText };\n } else {\n yield { type: 'error', message: 'Responses API stream ended without a completed event' };\n }\n}\n\n// ============================================================================\n// Per-format request caller\n// ============================================================================\n\n/**\n * Issues a streaming Responses API request (with tools) and returns the\n * unified-event iterable on success.\n *\n * @internal\n */\nexport async function callOpenAiResponsesStream(\n config: IStreamApiConfig,\n prompt: AiPrompt,\n tools: ReadonlyArray<AiServerToolConfig>,\n messagesBefore: ReadonlyArray<IChatMessage> | undefined,\n temperature: number,\n logger?: Logging.ILogger,\n signal?: AbortSignal,\n resolvedThinking?: IResolvedThinkingConfig\n): Promise<Result<AsyncIterable<IAiStreamEvent>>> {\n const url = `${config.baseUrl}/responses`;\n const input = buildMessages(prompt.system, buildOpenAiResponsesUserContent(prompt), {\n head: messagesBefore\n });\n const effort = resolvedThinking?.openAiEffort ?? resolvedThinking?.xaiEffort;\n const supportsReasoning = config.model !== 'grok-4';\n const body: Record<string, unknown> = {\n model: config.model,\n input,\n tools: toResponsesApiTools(tools),\n stream: true\n };\n if (effort !== undefined && supportsReasoning) {\n body.reasoning = { effort };\n }\n if (effort === undefined || effort === 'none') {\n body.temperature = temperature;\n }\n if (resolvedThinking?.otherParams !== undefined) {\n Object.assign(body, resolvedThinking.otherParams);\n }\n const headers: Record<string, string> = bearerAuthHeader(config.apiKey);\n /* c8 ignore next 3 - optional logger */\n logger?.info(\n `OpenAI Responses streaming: model=${config.model}, tools=${tools.map((t) => t.type).join(',')}`\n );\n const conn = await openSseConnection(url, headers, body, logger, signal);\n return conn.onSuccess((response) => succeed(translateOpenAiResponsesStream(response)));\n}\n"]}
|
|
1
|
+
{"version":3,"file":"openaiResponses.js","sourceRoot":"","sources":["../../../../src/packlets/ai-assist/streamingAdapters/openaiResponses.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) 2026 Erik Fortune\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\n/**\n * Streaming adapter for the OpenAI / xAI Responses API. This is the format\n * used when server-side tools (e.g. web_search) are requested — Chat\n * Completions doesn't support tool progress events, but the Responses API\n * does.\n *\n * Client-defined tools (`function_call` type) are accumulated per call ID\n * and emitted as `client-tool-call-done` events when complete.\n *\n * @packageDocumentation\n */\n\nimport { type Logging, Result, succeed, type Validator, Validators } from '@fgv/ts-utils';\nimport { type JsonObject } from '@fgv/ts-json-base';\n\nimport { buildMessages, buildOpenAiResponsesUserContent } from '../chatRequestBuilders';\nimport { bearerAuthHeader } from '../endpoint';\nimport { AiPrompt, type AiToolConfig, type IAiStreamEvent, type IChatMessage } from '../model';\nimport { parseSseEventJson, readSseEvents } from '../sseParser';\nimport { toResponsesApiTools } from '../toolFormats';\nimport { type IResolvedThinkingConfig } from '../thinkingOptionsResolver';\nimport {\n IStreamApiConfig,\n UNRECOGNIZED_EVENT_WARN_TAG,\n formatUnrecognizedEventPayloadPreview,\n openSseConnection,\n validateEventPayload\n} from './common';\n\n// ============================================================================\n// Accumulated call state (internal — used by C3 continuation builder)\n// ============================================================================\n\n/**\n * Accumulated state for a single function_call in the OpenAI Responses API stream.\n * @internal\n */\nexport interface IAccumulatedFunctionCall {\n readonly id: string;\n readonly name: string;\n argsBuffer: string;\n}\n\n// ============================================================================\n// Event payload shapes\n// ============================================================================\n\n/**\n * Payload of a `response.output_text.delta` SSE event.\n * @internal\n */\ninterface IResponsesDeltaPayload {\n readonly delta: string;\n}\n\n/**\n * Payload of a `response.output_item.added` SSE event.\n * @internal\n */\ninterface IResponsesOutputItemAddedPayload {\n readonly item: {\n readonly type?: string;\n readonly id?: string;\n readonly name?: string;\n readonly call_id?: string;\n };\n}\n\n/**\n * Payload of a `response.function_call_arguments.delta` SSE event.\n *\n * The live OpenAI / xAI Responses API emits these events keyed by `item_id` (the\n * `fc_*` output-item id), NOT by `call_id` (the `call_*` id used in continuation\n * input items). The adapter correlates `item_id` → `call_id` via the\n * `response.output_item.added` event that introduced the function_call item.\n *\n * @internal\n */\ninterface IResponsesFunctionCallArgsDeltaPayload {\n readonly item_id: string;\n readonly delta: string;\n}\n\n/**\n * Payload of a `response.function_call_arguments.done` SSE event.\n *\n * Keyed by `item_id` like the delta event — see {@link IResponsesFunctionCallArgsDeltaPayload}.\n *\n * @internal\n */\ninterface IResponsesFunctionCallArgsDonePayload {\n readonly item_id: string;\n readonly arguments: string;\n}\n\n/**\n * Payload of a `response.completed` SSE event.\n * @internal\n */\ninterface IResponsesCompletedPayload {\n readonly response: {\n readonly status?: string;\n readonly incomplete_details?: { readonly reason?: string };\n };\n}\n\n/**\n * Payload of a `response.failed` or `error` SSE event.\n * @internal\n */\ninterface IResponsesErrorPayload {\n readonly error?: { readonly message?: string };\n readonly message?: string;\n}\n\nconst responsesDeltaPayload: Validator<IResponsesDeltaPayload> = Validators.object<IResponsesDeltaPayload>({\n delta: Validators.string\n});\n\nconst responsesOutputItemInner: Validator<{\n type?: string;\n id?: string;\n name?: string;\n call_id?: string;\n}> = Validators.object<{ type?: string; id?: string; name?: string; call_id?: string }>(\n {\n type: Validators.string.optional(),\n id: Validators.string.optional(),\n name: Validators.string.optional(),\n call_id: Validators.string.optional()\n },\n { options: { optionalFields: ['type', 'id', 'name', 'call_id'] } }\n);\n\nconst responsesOutputItemAddedPayload: Validator<IResponsesOutputItemAddedPayload> =\n Validators.object<IResponsesOutputItemAddedPayload>({ item: responsesOutputItemInner });\n\nconst responsesFunctionCallArgsDeltaPayload: Validator<IResponsesFunctionCallArgsDeltaPayload> =\n Validators.object<IResponsesFunctionCallArgsDeltaPayload>({\n item_id: Validators.string,\n delta: Validators.string\n });\n\nconst responsesFunctionCallArgsDonePayload: Validator<IResponsesFunctionCallArgsDonePayload> =\n Validators.object<IResponsesFunctionCallArgsDonePayload>({\n item_id: Validators.string,\n arguments: Validators.string\n });\n\nconst responsesIncompleteDetails: Validator<{ reason?: string }> = Validators.object<{ reason?: string }>(\n { reason: Validators.string.optional() },\n { options: { optionalFields: ['reason'] } }\n);\n\nconst responsesCompletedPayload: Validator<IResponsesCompletedPayload> =\n Validators.object<IResponsesCompletedPayload>({\n response: Validators.object<{ status?: string; incomplete_details?: { reason?: string } }>(\n {\n status: Validators.string.optional(),\n incomplete_details: responsesIncompleteDetails.optional()\n },\n { options: { optionalFields: ['status', 'incomplete_details'] } }\n )\n });\n\nconst responsesErrorInner: Validator<{ message?: string }> = Validators.object<{ message?: string }>(\n { message: Validators.string.optional() },\n { options: { optionalFields: ['message'] } }\n);\n\nconst responsesErrorPayload: Validator<IResponsesErrorPayload> = Validators.object<IResponsesErrorPayload>(\n {\n error: responsesErrorInner.optional(),\n message: Validators.string.optional()\n },\n { options: { optionalFields: ['error', 'message'] } }\n);\n\n// ============================================================================\n// Stream translator\n// ============================================================================\n\n/**\n * Recognized OpenAI / xAI Responses API SSE event names. An event in this set is either\n * handled below or intentionally ignored (lifecycle / reasoning content discarded by design /\n * server-tool channels not yet surfaced). Any event whose name is NOT in this set surfaces\n * a one-time `logger.warn` per stream — making provider drift (new event types from a model\n * update) visible empirically instead of silently no-op'd.\n *\n * Source: official `openai-node` SDK source (`src/resources/responses/responses.ts`)\n * event-type literal-string sweep, plus the xAI Responses superset. Add to this set when a\n * new event type is observed and confirmed safe to ignore.\n *\n * @internal\n */\nconst RECOGNIZED_OPENAI_RESPONSES_EVENTS: ReadonlySet<string> = new Set<string>([\n // ---- handled by the translator ----\n 'response.output_text.delta',\n 'response.web_search_call.in_progress',\n 'response.web_search_call.completed',\n 'response.output_item.added',\n 'response.function_call_arguments.delta',\n 'response.function_call_arguments.done',\n 'response.completed',\n 'response.failed',\n 'error',\n // ---- lifecycle events: intentionally silent ----\n 'response.created',\n 'response.in_progress',\n 'response.queued',\n 'response.incomplete',\n // ---- text content lifecycle: handled implicitly via delta accumulation ----\n 'response.output_text.done',\n 'response.output_text.annotation.added',\n 'response.content_part.added',\n 'response.content_part.done',\n 'response.output_item.done',\n // ---- reasoning content: discarded by design (see ai-assist-thinking-events follow-on) ----\n 'response.reasoning_summary_part.added',\n 'response.reasoning_summary_part.done',\n 'response.reasoning_summary_text.delta',\n 'response.reasoning_summary_text.done',\n 'response.reasoning_text.delta',\n 'response.reasoning_text.done',\n // ---- refusals: caller currently sees these via the model's final text ----\n 'response.refusal.delta',\n 'response.refusal.done',\n // ---- server-tool channels not surfaced as tool-event yet ----\n 'response.web_search_call.searching',\n 'response.file_search_call.in_progress',\n 'response.file_search_call.searching',\n 'response.file_search_call.completed',\n 'response.code_interpreter_call.in_progress',\n 'response.code_interpreter_call.interpreting',\n 'response.code_interpreter_call.completed',\n 'response.code_interpreter_call_code.delta',\n 'response.code_interpreter_call_code.done',\n 'response.image_generation_call.in_progress',\n 'response.image_generation_call.generating',\n 'response.image_generation_call.completed',\n 'response.image_generation_call.partial_image',\n 'response.mcp_call.in_progress',\n 'response.mcp_call.completed',\n 'response.mcp_call.failed',\n 'response.mcp_call_arguments.delta',\n 'response.mcp_call_arguments.done',\n 'response.mcp_list_tools.in_progress',\n 'response.mcp_list_tools.completed',\n 'response.mcp_list_tools.failed',\n 'response.custom_tool_call_input.delta',\n 'response.custom_tool_call_input.done',\n // ---- audio (not used in chat/tool flows here) ----\n 'response.audio.delta',\n 'response.audio.done',\n 'response.audio.transcript.delta',\n 'response.audio.transcript.done'\n]);\n\n/**\n * Translates an OpenAI Responses API SSE stream into unified events.\n *\n * Maintains a per-call-ID accumulation map for client-defined function calls.\n * The map is exposed via the passed-in `functionCallMap` parameter for the\n * C3 continuation builder to read after the stream completes.\n *\n * Unrecognized event names are reported once per stream via `logger?.warn` —\n * see {@link RECOGNIZED_OPENAI_RESPONSES_EVENTS}.\n *\n * @internal\n */\nasync function* translateOpenAiResponsesStream(\n response: Response,\n functionCallMap: Map<string, IAccumulatedFunctionCall>,\n logger?: Logging.ILogger\n): AsyncGenerator<IAiStreamEvent> {\n let fullText = '';\n let truncated = false;\n let completed = false;\n let incompleteReason: string | undefined;\n // OpenAI / xAI Responses API emits function_call_arguments.{delta,done} events keyed by\n // `item_id` (the fc_* output-item id). The harness and continuation builder key by\n // `call_id` (the call_* id). This map correlates the two — populated when the\n // function_call item is announced in `response.output_item.added`.\n const itemIdToCallId = new Map<string, string>();\n // Track unrecognized event names we have already warned about, so a hot stream of\n // an unknown event type produces exactly one log line per name per stream.\n const warnedEvents = new Set<string>();\n\n try {\n /* c8 ignore next - body is non-null at this point per openSseConnection */\n if (!response.body) return;\n for await (const message of readSseEvents(response.body)) {\n const eventName = message.event;\n if (eventName === 'response.output_text.delta') {\n const payload = validateEventPayload(parseSseEventJson(message.data), responsesDeltaPayload);\n /* c8 ignore next 1 - defensive: payload?.delta null branch unreachable after validation */\n const delta = payload?.delta;\n if (typeof delta === 'string' && delta.length > 0) {\n fullText += delta;\n yield { type: 'text-delta', delta };\n }\n } else if (eventName === 'response.web_search_call.in_progress') {\n yield { type: 'tool-event', toolType: 'web_search', phase: 'started' };\n } else if (eventName === 'response.web_search_call.completed') {\n yield { type: 'tool-event', toolType: 'web_search', phase: 'completed' };\n } else if (eventName === 'response.output_item.added') {\n const payload = validateEventPayload(\n parseSseEventJson(message.data),\n responsesOutputItemAddedPayload\n );\n /* c8 ignore next 1 - defensive: payload null branch unreachable after validation */\n const item = payload?.item;\n /* c8 ignore next 1 - defensive: falsy call_id/name branches are protocol violations */\n if (item?.type === 'function_call' && item.call_id && item.name) {\n functionCallMap.set(item.call_id, { id: item.call_id, name: item.name, argsBuffer: '' });\n // Reasoning models (and the standard flow) reference this function_call in subsequent\n // arguments.{delta,done} events by `item_id`, never by `call_id`. Record the mapping\n // so the arg-accumulation handlers can resolve the call.\n if (item.id) {\n itemIdToCallId.set(item.id, item.call_id);\n }\n yield { type: 'client-tool-call-start', toolName: item.name, callId: item.call_id };\n }\n } else if (eventName === 'response.function_call_arguments.delta') {\n const payload = validateEventPayload(\n parseSseEventJson(message.data),\n responsesFunctionCallArgsDeltaPayload\n );\n /* c8 ignore next 1 - defensive: payload null branch unreachable after validation */\n const itemId = payload?.item_id;\n if (itemId !== undefined) {\n const callId = itemIdToCallId.get(itemId);\n /* c8 ignore next 1 - defensive: orphan delta (no preceding output_item.added) is a protocol violation */\n const call = callId !== undefined ? functionCallMap.get(callId) : undefined;\n /* c8 ignore next 1 - defensive: call always present and delta always string in valid stream */\n if (call && typeof payload?.delta === 'string') {\n call.argsBuffer += payload.delta;\n }\n }\n } else if (eventName === 'response.function_call_arguments.done') {\n const payload = validateEventPayload(\n parseSseEventJson(message.data),\n responsesFunctionCallArgsDonePayload\n );\n /* c8 ignore next 1 - defensive: payload null branch unreachable after validation */\n const itemId = payload?.item_id;\n if (itemId !== undefined) {\n const callId = itemIdToCallId.get(itemId);\n /* c8 ignore next 1 - defensive: orphan done (no preceding output_item.added) is a protocol violation */\n const call = callId !== undefined ? functionCallMap.get(callId) : undefined;\n if (call && callId !== undefined) {\n /* c8 ignore next 1 - defensive: payload?.arguments null branch unreachable after validation */\n const canonicalArgs = payload?.arguments ?? '{}';\n // Sync the accumulation entry with the canonical arguments from the .done event.\n // Delta events may carry partial/empty payloads; the .done event carries the\n // authoritative final arguments string used by the continuation builder.\n call.argsBuffer = canonicalArgs;\n let args: JsonObject;\n try {\n const parsed = JSON.parse(canonicalArgs) as unknown;\n /* c8 ignore start - defensive: non-object/malformed parse defaults to empty object */\n args =\n parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed)\n ? (parsed as JsonObject)\n : {};\n } catch {\n args = {};\n }\n /* c8 ignore stop */\n yield { type: 'client-tool-call-done', toolName: call.name, callId, args };\n }\n }\n } else if (eventName === 'response.completed') {\n const payload = validateEventPayload(parseSseEventJson(message.data), responsesCompletedPayload);\n /* c8 ignore next 1 - defensive: payload null branch unreachable after validation */\n if (payload) {\n truncated = payload.response.status === 'incomplete';\n // Per IAiStreamDone.incompleteReason's contract, the reason is meaningful only\n // when the response was truncated. Move both fields together on every completed\n // event so a stray incomplete_details on a non-incomplete payload never leaks\n // through, and a later (defensive) completed event can't leave a stale reason.\n incompleteReason = truncated ? payload.response.incomplete_details?.reason : undefined;\n }\n completed = true;\n /* c8 ignore next 1 - defensive: eventName === 'error' alternative not exercised in tests */\n } else if (eventName === 'response.failed' || eventName === 'error') {\n const payload = validateEventPayload(parseSseEventJson(message.data), responsesErrorPayload);\n /* c8 ignore next 1 - defensive: payload?.error and payload?.message null branches unreachable */\n const errMsg = payload?.error?.message ?? payload?.message ?? 'Responses API stream failed';\n yield { type: 'error', message: errMsg };\n return;\n } else if (\n typeof eventName === 'string' &&\n !RECOGNIZED_OPENAI_RESPONSES_EVENTS.has(eventName) &&\n !warnedEvents.has(eventName)\n ) {\n // Empirical drift instrument: an unrecognized SSE event from the Responses API\n // surfaces as a one-time warning per stream. If a provider adds a new event type\n // (or changes an existing event's name), the warning fires the first time the\n // event arrives. Update RECOGNIZED_OPENAI_RESPONSES_EVENTS once the new event is\n // either handled or confirmed safe to ignore.\n warnedEvents.add(eventName);\n logger?.warn(\n `${UNRECOGNIZED_EVENT_WARN_TAG} OpenAI Responses adapter: unrecognized SSE event ` +\n `'${eventName}'. ` +\n `payload preview: ${formatUnrecognizedEventPayloadPreview(message.data)}. ` +\n `This may indicate provider drift — if the new event carries data the adapter ` +\n `should surface, add a handler; otherwise add the name to ` +\n `RECOGNIZED_OPENAI_RESPONSES_EVENTS.`\n );\n }\n }\n } catch (err: unknown) /* c8 ignore start - defensive: stream errors are always Error instances */ {\n yield { type: 'error', message: err instanceof Error ? err.message : String(err) };\n return;\n } /* c8 ignore stop */\n\n if (completed) {\n yield { type: 'done', truncated, fullText, incompleteReason };\n } else {\n yield { type: 'error', message: 'Responses API stream ended without a completed event' };\n }\n}\n\n// ============================================================================\n// Per-format request caller\n// ============================================================================\n\n/**\n * Issues a streaming Responses API request (with tools) and returns the\n * unified-event iterable on success.\n *\n * @internal\n */\nexport async function callOpenAiResponsesStream(\n config: IStreamApiConfig,\n prompt: AiPrompt,\n tools: ReadonlyArray<AiToolConfig>,\n messagesBefore: ReadonlyArray<IChatMessage> | undefined,\n temperature: number,\n logger?: Logging.ILogger,\n signal?: AbortSignal,\n resolvedThinking?: IResolvedThinkingConfig,\n functionCallMap?: Map<string, IAccumulatedFunctionCall>,\n continuationMessages?: ReadonlyArray<JsonObject>\n): Promise<Result<AsyncIterable<IAiStreamEvent>>> {\n const url = `${config.baseUrl}/responses`;\n const input = buildMessages(prompt.system, buildOpenAiResponsesUserContent(prompt), {\n head: messagesBefore,\n rawTail: continuationMessages\n });\n const effort = resolvedThinking?.openAiEffort ?? resolvedThinking?.xaiEffort;\n const supportsReasoning = config.model !== 'grok-4';\n const body: Record<string, unknown> = {\n model: config.model,\n input,\n tools: toResponsesApiTools(tools),\n stream: true\n };\n if (effort !== undefined && supportsReasoning) {\n body.reasoning = { effort };\n }\n if (effort === undefined || effort === 'none') {\n body.temperature = temperature;\n }\n if (resolvedThinking?.otherParams !== undefined) {\n Object.assign(body, resolvedThinking.otherParams);\n }\n const headers: Record<string, string> = bearerAuthHeader(config.apiKey);\n /* c8 ignore next 3 - optional logger */\n logger?.info(\n `OpenAI Responses streaming: model=${config.model}, tools=${tools.map((t) => t.type).join(',')}`\n );\n const callMap = functionCallMap ?? new Map<string, IAccumulatedFunctionCall>();\n const conn = await openSseConnection(url, headers, body, logger, signal);\n return conn.onSuccess((response) => succeed(translateOpenAiResponsesStream(response, callMap, logger)));\n}\n"]}
|
|
@@ -11,6 +11,23 @@ import { type IAiStreamEvent } from './model';
|
|
|
11
11
|
import { type IProviderCompletionStreamParams } from './streamingAdapters/common';
|
|
12
12
|
export { callProxiedCompletionStream } from './streamingAdapters/proxy';
|
|
13
13
|
export type { IProviderCompletionStreamParams } from './streamingAdapters/common';
|
|
14
|
+
/**
|
|
15
|
+
* Re-export `executeClientToolTurn` from the continuation builder so callers
|
|
16
|
+
* can import it directly from the streaming-client surface.
|
|
17
|
+
*
|
|
18
|
+
* @remarks
|
|
19
|
+
* **Why `clientTools` does not flow through `callProviderCompletionStream`:**
|
|
20
|
+
* Client tools require a stateful per-stream accumulation buffer (for thinking
|
|
21
|
+
* blocks, tool-use args, Gemini functionCall accumulation) that is owned by the
|
|
22
|
+
* continuation builder's execution context. Passing `clientTools` through the
|
|
23
|
+
* generic `callProviderCompletionStream` entry would require the caller to manage
|
|
24
|
+
* the accumulation buffer externally. `executeClientToolTurn` is therefore the
|
|
25
|
+
* canonical (and only) call path for client tools in Phase C. Callers that need
|
|
26
|
+
* both server tools and client tools in the same request should use
|
|
27
|
+
* `executeClientToolTurn` with both `tools` (server) and `clientTools` present;
|
|
28
|
+
* `callProviderCompletionStream` is for server-tools-only or no-tools requests.
|
|
29
|
+
*/
|
|
30
|
+
export { executeClientToolTurn, type IExecuteClientToolTurnParams, type IExecuteClientToolTurnResult } from './streamingAdapters/clientToolContinuationBuilder';
|
|
14
31
|
/**
|
|
15
32
|
* Calls the appropriate streaming chat completion API for a given provider.
|
|
16
33
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"streamingClient.d.ts","sourceRoot":"","sources":["../../../src/packlets/ai-assist/streamingClient.ts"],"names":[],"mappings":"AAoBA;;;;;;;GAOG;AAEH,OAAO,EAAQ,MAAM,EAAE,MAAM,eAAe,CAAC;AAG7C,OAAO,EAAE,KAAK,cAAc,EAAgB,MAAM,SAAS,CAAC;AAE5D,OAAO,EAAE,KAAK,+BAA+B,EAAyB,MAAM,4BAA4B,CAAC;AAWzG,OAAO,EAAE,2BAA2B,EAAE,MAAM,2BAA2B,CAAC;AACxE,YAAY,EAAE,+BAA+B,EAAE,MAAM,4BAA4B,CAAC;AAElF;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,4BAA4B,CAChD,MAAM,EAAE,+BAA+B,GACtC,OAAO,CAAC,MAAM,CAAC,aAAa,CAAC,cAAc,CAAC,CAAC,CAAC,CAoHhD"}
|
|
1
|
+
{"version":3,"file":"streamingClient.d.ts","sourceRoot":"","sources":["../../../src/packlets/ai-assist/streamingClient.ts"],"names":[],"mappings":"AAoBA;;;;;;;GAOG;AAEH,OAAO,EAAQ,MAAM,EAAE,MAAM,eAAe,CAAC;AAG7C,OAAO,EAAE,KAAK,cAAc,EAAgB,MAAM,SAAS,CAAC;AAE5D,OAAO,EAAE,KAAK,+BAA+B,EAAyB,MAAM,4BAA4B,CAAC;AAWzG,OAAO,EAAE,2BAA2B,EAAE,MAAM,2BAA2B,CAAC;AACxE,YAAY,EAAE,+BAA+B,EAAE,MAAM,4BAA4B,CAAC;AAElF;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EACL,qBAAqB,EACrB,KAAK,4BAA4B,EACjC,KAAK,4BAA4B,EAClC,MAAM,mDAAmD,CAAC;AAE3D;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,4BAA4B,CAChD,MAAM,EAAE,+BAA+B,GACtC,OAAO,CAAC,MAAM,CAAC,aAAa,CAAC,cAAc,CAAC,CAAC,CAAC,CAoHhD"}
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
20
20
|
// SOFTWARE.
|
|
21
21
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
-
exports.callProxiedCompletionStream = void 0;
|
|
22
|
+
exports.executeClientToolTurn = exports.callProxiedCompletionStream = void 0;
|
|
23
23
|
exports.callProviderCompletionStream = callProviderCompletionStream;
|
|
24
24
|
/**
|
|
25
25
|
* Streaming chat completion client. Public façade over the per-format
|
|
@@ -39,6 +39,25 @@ const openaiResponses_1 = require("./streamingAdapters/openaiResponses");
|
|
|
39
39
|
const thinkingOptionsResolver_1 = require("./thinkingOptionsResolver");
|
|
40
40
|
var proxy_1 = require("./streamingAdapters/proxy");
|
|
41
41
|
Object.defineProperty(exports, "callProxiedCompletionStream", { enumerable: true, get: function () { return proxy_1.callProxiedCompletionStream; } });
|
|
42
|
+
/**
|
|
43
|
+
* Re-export `executeClientToolTurn` from the continuation builder so callers
|
|
44
|
+
* can import it directly from the streaming-client surface.
|
|
45
|
+
*
|
|
46
|
+
* @remarks
|
|
47
|
+
* **Why `clientTools` does not flow through `callProviderCompletionStream`:**
|
|
48
|
+
* Client tools require a stateful per-stream accumulation buffer (for thinking
|
|
49
|
+
* blocks, tool-use args, Gemini functionCall accumulation) that is owned by the
|
|
50
|
+
* continuation builder's execution context. Passing `clientTools` through the
|
|
51
|
+
* generic `callProviderCompletionStream` entry would require the caller to manage
|
|
52
|
+
* the accumulation buffer externally. `executeClientToolTurn` is therefore the
|
|
53
|
+
* canonical (and only) call path for client tools in Phase C. Callers that need
|
|
54
|
+
* both server tools and client tools in the same request should use
|
|
55
|
+
* `executeClientToolTurn` with both `tools` (server) and `clientTools` present;
|
|
56
|
+
* `callProviderCompletionStream` is for server-tools-only or no-tools requests.
|
|
57
|
+
*/
|
|
58
|
+
/* c8 ignore next 5 - barrel re-export; function is covered by clientToolContinuationBuilder tests */
|
|
59
|
+
var clientToolContinuationBuilder_1 = require("./streamingAdapters/clientToolContinuationBuilder");
|
|
60
|
+
Object.defineProperty(exports, "executeClientToolTurn", { enumerable: true, get: function () { return clientToolContinuationBuilder_1.executeClientToolTurn; } });
|
|
42
61
|
/**
|
|
43
62
|
* Calls the appropriate streaming chat completion API for a given provider.
|
|
44
63
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"streamingClient.js","sourceRoot":"","sources":["../../../src/packlets/ai-assist/streamingClient.ts"],"names":[],"mappings":";AAAA,kCAAkC;AAClC,EAAE;AACF,+EAA+E;AAC/E,gFAAgF;AAChF,+EAA+E;AAC/E,4EAA4E;AAC5E,wEAAwE;AACxE,2DAA2D;AAC3D,EAAE;AACF,iFAAiF;AACjF,kDAAkD;AAClD,EAAE;AACF,6EAA6E;AAC7E,2EAA2E;AAC3E,8EAA8E;AAC9E,yEAAyE;AACzE,gFAAgF;AAChF,gFAAgF;AAChF,YAAY;;;AAgDZ,oEAsHC;AApKD;;;;;;;GAOG;AAEH,4CAA6C;AAE7C,yCAAqD;AACrD,mCAA4D;AAC5D,6DAAoE;AAEpE,uDAA8D;AAC9D,+DAAsE;AACtE,yEAAgF;AAChF,uEAKmC;AAEnC,mDAAwE;AAA/D,oHAAA,2BAA2B,OAAA;AAGpC;;;;;;;;;;;;;;;;;GAiBG;AACI,KAAK,UAAU,4BAA4B,CAChD,MAAuC;;IAEvC,MAAM,EACJ,UAAU,EACV,MAAM,EACN,MAAM,EACN,cAAc,EACd,WAAW,EACX,aAAa,EACb,MAAM,EACN,KAAK,EACL,MAAM,EACN,QAAQ,EACR,QAAQ,EACT,GAAG,MAAM,CAAC;IAEX,MAAM,aAAa,GAAG,IAAA,kCAAuB,EAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;IACpE,IAAI,aAAa,CAAC,SAAS,EAAE,EAAE,CAAC;QAC9B,OAAO,IAAA,eAAI,EAAC,aAAa,CAAC,OAAO,CAAC,CAAC;IACrC,CAAC;IACD,IAAI,UAAU,CAAC,uBAAuB,EAAE,CAAC;QACvC,OAAO,IAAA,eAAI,EAAC,aAAa,UAAU,CAAC,EAAE,sDAAsD,CAAC,CAAC;IAChG,CAAC;IACD,IAAI,MAAM,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,iBAAiB,EAAE,CAAC;QACnE,OAAO,IAAA,eAAI,EAAC,aAAa,UAAU,CAAC,EAAE,+BAA+B,CAAC,CAAC;IACzE,CAAC;IAED,MAAM,QAAQ,GAAG,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;IACzD,MAAM,aAAa,GAAG,IAAA,oDAA0B,EAAC,UAAU,CAAC,EAAE,CAAC,CAAC;IAChE,MAAM,iBAAiB,GACrB,aAAa,KAAK,SAAS;QAC3B,CAAC,CAAA,QAAQ,aAAR,QAAQ,uBAAR,QAAQ,CAAE,MAAM,MAAK,SAAS;YAC7B,CAAA,MAAA,QAAQ,aAAR,QAAQ,uBAAR,QAAQ,CAAE,SAAS,0CAAE,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,OAAO,IAAI,CAAC,CAAC,QAAQ,KAAK,aAAa,CAAC,MAAK,IAAI,CAAC,CAAC;IACvG,MAAM,YAAY,GAAG,iBAAiB,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;IAErF,MAAM,KAAK,GAAG,IAAA,oBAAY,EAAC,aAAa,aAAb,aAAa,cAAb,aAAa,GAAI,UAAU,CAAC,YAAY,EAAE,YAAY,CAAC,CAAC;IACnF,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,IAAA,eAAI,EACT,aAAa,UAAU,CAAC,EAAE,yEAAyE,CACpG,CAAC;IACJ,CAAC;IAED,IAAI,gBAAqD,CAAC;IAC1D,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC3B,IAAI,aAAa,KAAK,SAAS,EAAE,CAAC;YAChC,MAAM,WAAW,GAAG,IAAA,6CAAmB,EAAC,QAAQ,EAAE,KAAK,EAAE,aAAa,CAAC,CAAC;YACxE,6EAA6E;YAC7E,IAAI,WAAW,CAAC,SAAS,EAAE,EAAE,CAAC;gBAC5B,OAAO,IAAA,eAAI,EAAC,WAAW,CAAC,OAAO,CAAC,CAAC;YACnC,CAAC;YACD,gBAAgB,GAAG,WAAW,CAAC,KAAK,CAAC;YACrC,MAAM,cAAc,GAAG,IAAA,kDAAwB,EAAC,gBAAgB,EAAE,aAAa,EAAE,WAAW,CAAC,CAAC;YAC9F,IAAI,cAAc,CAAC,SAAS,EAAE,EAAE,CAAC;gBAC/B,OAAO,IAAA,eAAI,EAAC,cAAc,CAAC,OAAO,CAAC,CAAC;YACtC,CAAC;QACH,CAAC;IACH,CAAC;IAED,MAAM,oBAAoB,GAAG,WAAW,aAAX,WAAW,cAAX,WAAW,GAAI,GAAG,CAAC;IAEhD,MAAM,MAAM,GAAqB;QAC/B,OAAO,EAAE,aAAa,CAAC,KAAK;QAC5B,MAAM;QACN,KAAK;KACN,CAAC;IAEF,QAAQ,UAAU,CAAC,SAAS,EAAE,CAAC;QAC7B,KAAK,QAAQ;YACX,IAAI,QAAQ,EAAE,CAAC;gBACb,OAAO,IAAA,2CAAyB,EAC9B,MAAM,EACN,MAAM,EACN,KAAK,EACL,cAAc,EACd,oBAAoB,EACpB,MAAM,EACN,MAAM,EACN,gBAAgB,CACjB,CAAC;YACJ,CAAC;YACD,OAAO,IAAA,iCAAoB,EACzB,MAAM,EACN,MAAM,EACN,cAAc,EACd,oBAAoB,EACpB,MAAM,EACN,MAAM,EACN,gBAAgB,CACjB,CAAC;QACJ,KAAK,WAAW;YACd,OAAO,IAAA,+BAAmB,EACxB,MAAM,EACN,MAAM,EACN,cAAc,EACd,oBAAoB,EACpB,KAAK,EACL,MAAM,EACN,MAAM,EACN,gBAAgB,CACjB,CAAC;QACJ,KAAK,QAAQ;YACX,OAAO,IAAA,yBAAgB,EACrB,MAAM,EACN,MAAM,EACN,cAAc,EACd,oBAAoB,EACpB,KAAK,EACL,MAAM,EACN,MAAM,EACN,gBAAgB,CACjB,CAAC;QACJ,qFAAqF;QACrF,OAAO,CAAC,CAAC,CAAC;YACR,MAAM,WAAW,GAAU,UAAU,CAAC,SAAS,CAAC;YAChD,OAAO,IAAA,eAAI,EAAC,2BAA2B,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;QAChE,CAAC;IACH,CAAC;AACH,CAAC","sourcesContent":["// Copyright (c) 2026 Erik Fortune\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\n/**\n * Streaming chat completion client. Public façade over the per-format\n * adapters under `streamingAdapters/`: provider dispatch and pre-flight\n * validation live here; format-specific request/translation logic and the\n * typed event-payload validators live with each adapter.\n *\n * @packageDocumentation\n */\n\nimport { fail, Result } from '@fgv/ts-utils';\n\nimport { resolveEffectiveBaseUrl } from './endpoint';\nimport { type IAiStreamEvent, resolveModel } from './model';\nimport { callAnthropicStream } from './streamingAdapters/anthropic';\nimport { type IProviderCompletionStreamParams, type IStreamApiConfig } from './streamingAdapters/common';\nimport { callGeminiStream } from './streamingAdapters/gemini';\nimport { callOpenAiChatStream } from './streamingAdapters/openaiChat';\nimport { callOpenAiResponsesStream } from './streamingAdapters/openaiResponses';\nimport {\n checkTemperatureConflict,\n mergeThinkingConfig,\n providerDiscriminatorForId,\n type IResolvedThinkingConfig\n} from './thinkingOptionsResolver';\n\nexport { callProxiedCompletionStream } from './streamingAdapters/proxy';\nexport type { IProviderCompletionStreamParams } from './streamingAdapters/common';\n\n/**\n * Calls the appropriate streaming chat completion API for a given provider.\n *\n * @remarks\n * Pre-flight rejection: when `descriptor.streamingCorsRestricted === true`\n * and the call isn't being routed through a proxy, this returns\n * `Result.fail` before fetch is invoked. Callers should route through\n * {@link AiAssist.callProxiedCompletionStream} or surface the failure to the user.\n *\n * Connection-time failures (auth, network, non-2xx) surface as the outer\n * `Result.fail`. Once iteration begins, errors mid-stream surface as a\n * terminal error event ({@link AiAssist.IAiStreamError}) followed by the iterable\n * ending. The final successful event is {@link AiAssist.IAiStreamDone}.\n *\n * @param params - Request parameters including descriptor, API key, prompt, and optional tools\n * @returns A streaming iterable of unified events, or a Result.fail\n * @public\n */\nexport async function callProviderCompletionStream(\n params: IProviderCompletionStreamParams\n): Promise<Result<AsyncIterable<IAiStreamEvent>>> {\n const {\n descriptor,\n apiKey,\n prompt,\n messagesBefore,\n temperature,\n modelOverride,\n logger,\n tools,\n signal,\n endpoint,\n thinking\n } = params;\n\n const baseUrlResult = resolveEffectiveBaseUrl(descriptor, endpoint);\n if (baseUrlResult.isFailure()) {\n return fail(baseUrlResult.message);\n }\n if (descriptor.streamingCorsRestricted) {\n return fail(`provider \"${descriptor.id}\" requires a proxy for streaming; none is configured`);\n }\n if (prompt.attachments.length > 0 && !descriptor.acceptsImageInput) {\n return fail(`provider \"${descriptor.id}\" does not accept image input`);\n }\n\n const hasTools = tools !== undefined && tools.length > 0;\n const discriminator = providerDiscriminatorForId(descriptor.id);\n const hasThinkingConfig =\n discriminator !== undefined &&\n (thinking?.effort !== undefined ||\n thinking?.providers?.some((b) => b.provider === 'other' || b.provider === discriminator) === true);\n const modelContext = hasThinkingConfig ? 'thinking' : hasTools ? 'tools' : undefined;\n\n const model = resolveModel(modelOverride ?? descriptor.defaultModel, modelContext);\n if (model.length === 0) {\n return fail(\n `provider \"${descriptor.id}\": no model resolved; pass modelOverride or set descriptor.defaultModel`\n );\n }\n\n let resolvedThinking: IResolvedThinkingConfig | undefined;\n if (thinking !== undefined) {\n if (discriminator !== undefined) {\n const mergeResult = mergeThinkingConfig(thinking, model, discriminator);\n /* c8 ignore next 3 - mergeThinkingConfig always succeeds; defensive guard */\n if (mergeResult.isFailure()) {\n return fail(mergeResult.message);\n }\n resolvedThinking = mergeResult.value;\n const conflictResult = checkTemperatureConflict(resolvedThinking, discriminator, temperature);\n if (conflictResult.isFailure()) {\n return fail(conflictResult.message);\n }\n }\n }\n\n const effectiveTemperature = temperature ?? 0.7;\n\n const config: IStreamApiConfig = {\n baseUrl: baseUrlResult.value,\n apiKey,\n model\n };\n\n switch (descriptor.apiFormat) {\n case 'openai':\n if (hasTools) {\n return callOpenAiResponsesStream(\n config,\n prompt,\n tools,\n messagesBefore,\n effectiveTemperature,\n logger,\n signal,\n resolvedThinking\n );\n }\n return callOpenAiChatStream(\n config,\n prompt,\n messagesBefore,\n effectiveTemperature,\n logger,\n signal,\n resolvedThinking\n );\n case 'anthropic':\n return callAnthropicStream(\n config,\n prompt,\n messagesBefore,\n effectiveTemperature,\n tools,\n logger,\n signal,\n resolvedThinking\n );\n case 'gemini':\n return callGeminiStream(\n config,\n prompt,\n messagesBefore,\n effectiveTemperature,\n tools,\n logger,\n signal,\n resolvedThinking\n );\n /* c8 ignore next 4 - defensive coding: exhaustive switch guaranteed by TypeScript */\n default: {\n const _exhaustive: never = descriptor.apiFormat;\n return fail(`unsupported API format: ${String(_exhaustive)}`);\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"streamingClient.js","sourceRoot":"","sources":["../../../src/packlets/ai-assist/streamingClient.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) 2026 Erik Fortune\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\n/**\n * Streaming chat completion client. Public façade over the per-format\n * adapters under `streamingAdapters/`: provider dispatch and pre-flight\n * validation live here; format-specific request/translation logic and the\n * typed event-payload validators live with each adapter.\n *\n * @packageDocumentation\n */\n\nimport { fail, Result } from '@fgv/ts-utils';\n\nimport { resolveEffectiveBaseUrl } from './endpoint';\nimport { type IAiStreamEvent, resolveModel } from './model';\nimport { callAnthropicStream } from './streamingAdapters/anthropic';\nimport { type IProviderCompletionStreamParams, type IStreamApiConfig } from './streamingAdapters/common';\nimport { callGeminiStream } from './streamingAdapters/gemini';\nimport { callOpenAiChatStream } from './streamingAdapters/openaiChat';\nimport { callOpenAiResponsesStream } from './streamingAdapters/openaiResponses';\nimport {\n checkTemperatureConflict,\n mergeThinkingConfig,\n providerDiscriminatorForId,\n type IResolvedThinkingConfig\n} from './thinkingOptionsResolver';\n\nexport { callProxiedCompletionStream } from './streamingAdapters/proxy';\nexport type { IProviderCompletionStreamParams } from './streamingAdapters/common';\n\n/**\n * Re-export `executeClientToolTurn` from the continuation builder so callers\n * can import it directly from the streaming-client surface.\n *\n * @remarks\n * **Why `clientTools` does not flow through `callProviderCompletionStream`:**\n * Client tools require a stateful per-stream accumulation buffer (for thinking\n * blocks, tool-use args, Gemini functionCall accumulation) that is owned by the\n * continuation builder's execution context. Passing `clientTools` through the\n * generic `callProviderCompletionStream` entry would require the caller to manage\n * the accumulation buffer externally. `executeClientToolTurn` is therefore the\n * canonical (and only) call path for client tools in Phase C. Callers that need\n * both server tools and client tools in the same request should use\n * `executeClientToolTurn` with both `tools` (server) and `clientTools` present;\n * `callProviderCompletionStream` is for server-tools-only or no-tools requests.\n */\n/* c8 ignore next 5 - barrel re-export; function is covered by clientToolContinuationBuilder tests */\nexport {\n executeClientToolTurn,\n type IExecuteClientToolTurnParams,\n type IExecuteClientToolTurnResult\n} from './streamingAdapters/clientToolContinuationBuilder';\n\n/**\n * Calls the appropriate streaming chat completion API for a given provider.\n *\n * @remarks\n * Pre-flight rejection: when `descriptor.streamingCorsRestricted === true`\n * and the call isn't being routed through a proxy, this returns\n * `Result.fail` before fetch is invoked. Callers should route through\n * {@link AiAssist.callProxiedCompletionStream} or surface the failure to the user.\n *\n * Connection-time failures (auth, network, non-2xx) surface as the outer\n * `Result.fail`. Once iteration begins, errors mid-stream surface as a\n * terminal error event ({@link AiAssist.IAiStreamError}) followed by the iterable\n * ending. The final successful event is {@link AiAssist.IAiStreamDone}.\n *\n * @param params - Request parameters including descriptor, API key, prompt, and optional tools\n * @returns A streaming iterable of unified events, or a Result.fail\n * @public\n */\nexport async function callProviderCompletionStream(\n params: IProviderCompletionStreamParams\n): Promise<Result<AsyncIterable<IAiStreamEvent>>> {\n const {\n descriptor,\n apiKey,\n prompt,\n messagesBefore,\n temperature,\n modelOverride,\n logger,\n tools,\n signal,\n endpoint,\n thinking\n } = params;\n\n const baseUrlResult = resolveEffectiveBaseUrl(descriptor, endpoint);\n if (baseUrlResult.isFailure()) {\n return fail(baseUrlResult.message);\n }\n if (descriptor.streamingCorsRestricted) {\n return fail(`provider \"${descriptor.id}\" requires a proxy for streaming; none is configured`);\n }\n if (prompt.attachments.length > 0 && !descriptor.acceptsImageInput) {\n return fail(`provider \"${descriptor.id}\" does not accept image input`);\n }\n\n const hasTools = tools !== undefined && tools.length > 0;\n const discriminator = providerDiscriminatorForId(descriptor.id);\n const hasThinkingConfig =\n discriminator !== undefined &&\n (thinking?.effort !== undefined ||\n thinking?.providers?.some((b) => b.provider === 'other' || b.provider === discriminator) === true);\n const modelContext = hasThinkingConfig ? 'thinking' : hasTools ? 'tools' : undefined;\n\n const model = resolveModel(modelOverride ?? descriptor.defaultModel, modelContext);\n if (model.length === 0) {\n return fail(\n `provider \"${descriptor.id}\": no model resolved; pass modelOverride or set descriptor.defaultModel`\n );\n }\n\n let resolvedThinking: IResolvedThinkingConfig | undefined;\n if (thinking !== undefined) {\n if (discriminator !== undefined) {\n const mergeResult = mergeThinkingConfig(thinking, model, discriminator);\n /* c8 ignore next 3 - mergeThinkingConfig always succeeds; defensive guard */\n if (mergeResult.isFailure()) {\n return fail(mergeResult.message);\n }\n resolvedThinking = mergeResult.value;\n const conflictResult = checkTemperatureConflict(resolvedThinking, discriminator, temperature);\n if (conflictResult.isFailure()) {\n return fail(conflictResult.message);\n }\n }\n }\n\n const effectiveTemperature = temperature ?? 0.7;\n\n const config: IStreamApiConfig = {\n baseUrl: baseUrlResult.value,\n apiKey,\n model\n };\n\n switch (descriptor.apiFormat) {\n case 'openai':\n if (hasTools) {\n return callOpenAiResponsesStream(\n config,\n prompt,\n tools,\n messagesBefore,\n effectiveTemperature,\n logger,\n signal,\n resolvedThinking\n );\n }\n return callOpenAiChatStream(\n config,\n prompt,\n messagesBefore,\n effectiveTemperature,\n logger,\n signal,\n resolvedThinking\n );\n case 'anthropic':\n return callAnthropicStream(\n config,\n prompt,\n messagesBefore,\n effectiveTemperature,\n tools,\n logger,\n signal,\n resolvedThinking\n );\n case 'gemini':\n return callGeminiStream(\n config,\n prompt,\n messagesBefore,\n effectiveTemperature,\n tools,\n logger,\n signal,\n resolvedThinking\n );\n /* c8 ignore next 4 - defensive coding: exhaustive switch guaranteed by TypeScript */\n default: {\n const _exhaustive: never = descriptor.apiFormat;\n return fail(`unsupported API format: ${String(_exhaustive)}`);\n }\n }\n}\n"]}
|
|
@@ -20,10 +20,14 @@ export declare function providerDiscriminatorForId(providerId: string): Thinking
|
|
|
20
20
|
/**
|
|
21
21
|
* Resolved thinking wire parameters for a specific provider, after merging
|
|
22
22
|
* all applicable config blocks. Ready for provider-specific wire encoding.
|
|
23
|
-
*
|
|
23
|
+
*
|
|
24
|
+
* Callers that pre-resolve thinking config outside of the standard streaming
|
|
25
|
+
* helpers (e.g. `executeClientToolTurn`) accept this type via the
|
|
26
|
+
* `resolvedThinking` parameter and pass it directly to the adapter layer.
|
|
27
|
+
* @public
|
|
24
28
|
*/
|
|
25
29
|
export interface IResolvedThinkingConfig {
|
|
26
|
-
/** Anthropic:
|
|
30
|
+
/** Anthropic: effort level; emit-site converts to `thinking.budget_tokens` via `anthropicEffortToBudgetTokens`. */
|
|
27
31
|
readonly anthropicEffort?: IAnthropicThinkingConfig['effort'];
|
|
28
32
|
/** OpenAI Chat: reasoning_effort value; OpenAI Responses: reasoning.effort */
|
|
29
33
|
readonly openAiEffort?: IOpenAiThinkingConfig['effort'];
|
|
@@ -34,6 +38,18 @@ export interface IResolvedThinkingConfig {
|
|
|
34
38
|
/** Other/passthrough: merged verbatim into wire request */
|
|
35
39
|
readonly otherParams?: JsonObject;
|
|
36
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* Maps Anthropic effort level to the `thinking.budget_tokens` integer that the
|
|
43
|
+
* Anthropic API requires when `thinking.type === 'enabled'`.
|
|
44
|
+
*
|
|
45
|
+
* Policy: low = 2048, medium = 8192, high = 24000, max = 32000. The lower three
|
|
46
|
+
* align with the Anthropic-published minimum-meaningful budget, a mid-range
|
|
47
|
+
* default, and a "deep thinking" allotment respectively. `max` targets Opus 4.6's
|
|
48
|
+
* deepest budget and stays within typical model limits.
|
|
49
|
+
*
|
|
50
|
+
* @public
|
|
51
|
+
*/
|
|
52
|
+
export declare function anthropicEffortToBudgetTokens(effort: NonNullable<IAnthropicThinkingConfig['effort']>): number;
|
|
37
53
|
/**
|
|
38
54
|
* Resolves the effective thinking wire parameters for a specific resolved model
|
|
39
55
|
* by merging all applicable config blocks in precedence order.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"thinkingOptionsResolver.d.ts","sourceRoot":"","sources":["../../../src/packlets/ai-assist/thinkingOptionsResolver.ts"],"names":[],"mappings":"AAoBA;;;GAGG;AAEH,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAQ,MAAM,EAAW,MAAM,eAAe,CAAC;AAEtD,OAAO,KAAK,EACV,eAAe,EAEf,wBAAwB,EACxB,qBAAqB,EACrB,kBAAkB,EACnB,MAAM,SAAS,CAAC;AAMjB;;;;GAIG;AACH,MAAM,MAAM,6BAA6B,GAAG,WAAW,GAAG,QAAQ,GAAG,QAAQ,GAAG,KAAK,CAAC;AAEtF;;;;GAIG;AACH,wBAAgB,0BAA0B,CAAC,UAAU,EAAE,MAAM,GAAG,6BAA6B,GAAG,SAAS,CAaxG;AAMD
|
|
1
|
+
{"version":3,"file":"thinkingOptionsResolver.d.ts","sourceRoot":"","sources":["../../../src/packlets/ai-assist/thinkingOptionsResolver.ts"],"names":[],"mappings":"AAoBA;;;GAGG;AAEH,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAQ,MAAM,EAAW,MAAM,eAAe,CAAC;AAEtD,OAAO,KAAK,EACV,eAAe,EAEf,wBAAwB,EACxB,qBAAqB,EACrB,kBAAkB,EACnB,MAAM,SAAS,CAAC;AAMjB;;;;GAIG;AACH,MAAM,MAAM,6BAA6B,GAAG,WAAW,GAAG,QAAQ,GAAG,QAAQ,GAAG,KAAK,CAAC;AAEtF;;;;GAIG;AACH,wBAAgB,0BAA0B,CAAC,UAAU,EAAE,MAAM,GAAG,6BAA6B,GAAG,SAAS,CAaxG;AAMD;;;;;;;;GAQG;AACH,MAAM,WAAW,uBAAuB;IACtC,mHAAmH;IACnH,QAAQ,CAAC,eAAe,CAAC,EAAE,wBAAwB,CAAC,QAAQ,CAAC,CAAC;IAC9D,8EAA8E;IAC9E,QAAQ,CAAC,YAAY,CAAC,EAAE,qBAAqB,CAAC,QAAQ,CAAC,CAAC;IACxD,6DAA6D;IAC7D,QAAQ,CAAC,oBAAoB,CAAC,EAAE,MAAM,CAAC;IACvC,oDAAoD;IACpD,QAAQ,CAAC,SAAS,CAAC,EAAE,kBAAkB,CAAC,QAAQ,CAAC,CAAC;IAClD,2DAA2D;IAC3D,QAAQ,CAAC,WAAW,CAAC,EAAE,UAAU,CAAC;CACnC;AAaD;;;;;;;;;;GAUG;AACH,wBAAgB,6BAA6B,CAC3C,MAAM,EAAE,WAAW,CAAC,wBAAwB,CAAC,QAAQ,CAAC,CAAC,GACtD,MAAM,CAWR;AAuFD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,eAAe,EACvB,aAAa,EAAE,MAAM,EACrB,aAAa,EAAE,6BAA6B,GAC3C,MAAM,CAAC,uBAAuB,CAAC,CAyCjC;AAyDD;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,uBAAuB,EACjC,aAAa,EAAE,6BAA6B,EAC5C,WAAW,EAAE,MAAM,GAAG,SAAS,GAC9B,MAAM,CAAC,SAAS,CAAC,CAkCnB"}
|