@fgv/ts-extras 5.1.0-55 → 5.1.0-57

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.
Files changed (184) hide show
  1. package/CAPABILITIES.md +96 -0
  2. package/dist/packlets/ai-assist/cacheRequest.js +118 -0
  3. package/dist/packlets/ai-assist/cacheRequest.js.map +1 -0
  4. package/dist/packlets/ai-assist/chatRequestBuilders.js +72 -7
  5. package/dist/packlets/ai-assist/chatRequestBuilders.js.map +1 -1
  6. package/dist/packlets/ai-assist/completionClient.js +195 -58
  7. package/dist/packlets/ai-assist/completionClient.js.map +1 -1
  8. package/dist/packlets/ai-assist/converters.js +54 -0
  9. package/dist/packlets/ai-assist/converters.js.map +1 -1
  10. package/dist/packlets/ai-assist/embeddingClient.js +16 -1
  11. package/dist/packlets/ai-assist/embeddingClient.js.map +1 -1
  12. package/dist/packlets/ai-assist/imageGenerationClient.js +17 -2
  13. package/dist/packlets/ai-assist/imageGenerationClient.js.map +1 -1
  14. package/dist/packlets/ai-assist/index.js +3 -1
  15. package/dist/packlets/ai-assist/index.js.map +1 -1
  16. package/dist/packlets/ai-assist/listModelsClient.js +19 -1
  17. package/dist/packlets/ai-assist/listModelsClient.js.map +1 -1
  18. package/dist/packlets/ai-assist/model.js.map +1 -1
  19. package/dist/packlets/ai-assist/promptCacheAnthropicBreakpointHarness.js +200 -0
  20. package/dist/packlets/ai-assist/promptCacheAnthropicBreakpointHarness.js.map +1 -0
  21. package/dist/packlets/ai-assist/promptCacheObservabilityHarness.js +137 -0
  22. package/dist/packlets/ai-assist/promptCacheObservabilityHarness.js.map +1 -0
  23. package/dist/packlets/ai-assist/promptCacheRoutingAbHarness.js +156 -0
  24. package/dist/packlets/ai-assist/promptCacheRoutingAbHarness.js.map +1 -0
  25. package/dist/packlets/ai-assist/promptCacheUsageMath.js +124 -0
  26. package/dist/packlets/ai-assist/promptCacheUsageMath.js.map +1 -0
  27. package/dist/packlets/ai-assist/registry.js +9 -11
  28. package/dist/packlets/ai-assist/registry.js.map +1 -1
  29. package/dist/packlets/ai-assist/streamUsageCapability.js +134 -0
  30. package/dist/packlets/ai-assist/streamUsageCapability.js.map +1 -0
  31. package/dist/packlets/ai-assist/streamingAdapters/anthropic.js +32 -9
  32. package/dist/packlets/ai-assist/streamingAdapters/anthropic.js.map +1 -1
  33. package/dist/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.js +81 -35
  34. package/dist/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.js.map +1 -1
  35. package/dist/packlets/ai-assist/streamingAdapters/common.js +25 -1
  36. package/dist/packlets/ai-assist/streamingAdapters/common.js.map +1 -1
  37. package/dist/packlets/ai-assist/streamingAdapters/gemini.js +12 -5
  38. package/dist/packlets/ai-assist/streamingAdapters/gemini.js.map +1 -1
  39. package/dist/packlets/ai-assist/streamingAdapters/openaiChat.js +45 -11
  40. package/dist/packlets/ai-assist/streamingAdapters/openaiChat.js.map +1 -1
  41. package/dist/packlets/ai-assist/streamingAdapters/openaiResponses.js +33 -22
  42. package/dist/packlets/ai-assist/streamingAdapters/openaiResponses.js.map +1 -1
  43. package/dist/packlets/ai-assist/streamingAdapters/proxy.js +62 -4
  44. package/dist/packlets/ai-assist/streamingAdapters/proxy.js.map +1 -1
  45. package/dist/packlets/ai-assist/streamingClient.js +20 -5
  46. package/dist/packlets/ai-assist/streamingClient.js.map +1 -1
  47. package/dist/packlets/ai-assist/thinkingOptionsResolver.js +23 -5
  48. package/dist/packlets/ai-assist/thinkingOptionsResolver.js.map +1 -1
  49. package/dist/packlets/ai-assist/toolFormats.js +80 -0
  50. package/dist/packlets/ai-assist/toolFormats.js.map +1 -1
  51. package/dist/packlets/ai-assist/toolTypes.js +21 -0
  52. package/dist/packlets/ai-assist/toolTypes.js.map +1 -0
  53. package/dist/packlets/ai-assist/usageNormalization.js +178 -0
  54. package/dist/packlets/ai-assist/usageNormalization.js.map +1 -0
  55. package/dist/packlets/ai-assist/usageTypes.js +21 -0
  56. package/dist/packlets/ai-assist/usageTypes.js.map +1 -0
  57. package/dist/packlets/mustache/index.js.map +1 -1
  58. package/dist/packlets/mustache/interfaces.js.map +1 -1
  59. package/dist/packlets/mustache/mustacheTemplate.js +91 -0
  60. package/dist/packlets/mustache/mustacheTemplate.js.map +1 -1
  61. package/dist/ts-extras.d.ts +764 -48
  62. package/lib/packlets/ai-assist/cacheRequest.d.ts +100 -0
  63. package/lib/packlets/ai-assist/cacheRequest.d.ts.map +1 -0
  64. package/lib/packlets/ai-assist/cacheRequest.js +123 -0
  65. package/lib/packlets/ai-assist/cacheRequest.js.map +1 -0
  66. package/lib/packlets/ai-assist/chatRequestBuilders.d.ts +36 -6
  67. package/lib/packlets/ai-assist/chatRequestBuilders.d.ts.map +1 -1
  68. package/lib/packlets/ai-assist/chatRequestBuilders.js +75 -7
  69. package/lib/packlets/ai-assist/chatRequestBuilders.js.map +1 -1
  70. package/lib/packlets/ai-assist/completionClient.d.ts +19 -0
  71. package/lib/packlets/ai-assist/completionClient.d.ts.map +1 -1
  72. package/lib/packlets/ai-assist/completionClient.js +194 -57
  73. package/lib/packlets/ai-assist/completionClient.js.map +1 -1
  74. package/lib/packlets/ai-assist/converters.d.ts +46 -0
  75. package/lib/packlets/ai-assist/converters.d.ts.map +1 -1
  76. package/lib/packlets/ai-assist/converters.js +55 -1
  77. package/lib/packlets/ai-assist/converters.js.map +1 -1
  78. package/lib/packlets/ai-assist/embeddingClient.d.ts +9 -0
  79. package/lib/packlets/ai-assist/embeddingClient.d.ts.map +1 -1
  80. package/lib/packlets/ai-assist/embeddingClient.js +16 -1
  81. package/lib/packlets/ai-assist/embeddingClient.js.map +1 -1
  82. package/lib/packlets/ai-assist/imageGenerationClient.d.ts +9 -0
  83. package/lib/packlets/ai-assist/imageGenerationClient.d.ts.map +1 -1
  84. package/lib/packlets/ai-assist/imageGenerationClient.js +17 -2
  85. package/lib/packlets/ai-assist/imageGenerationClient.js.map +1 -1
  86. package/lib/packlets/ai-assist/index.d.ts +7 -2
  87. package/lib/packlets/ai-assist/index.d.ts.map +1 -1
  88. package/lib/packlets/ai-assist/index.js +11 -1
  89. package/lib/packlets/ai-assist/index.js.map +1 -1
  90. package/lib/packlets/ai-assist/listModelsClient.d.ts +12 -0
  91. package/lib/packlets/ai-assist/listModelsClient.d.ts.map +1 -1
  92. package/lib/packlets/ai-assist/listModelsClient.js +19 -1
  93. package/lib/packlets/ai-assist/listModelsClient.js.map +1 -1
  94. package/lib/packlets/ai-assist/model.d.ts +81 -295
  95. package/lib/packlets/ai-assist/model.d.ts.map +1 -1
  96. package/lib/packlets/ai-assist/model.js.map +1 -1
  97. package/lib/packlets/ai-assist/promptCacheAnthropicBreakpointHarness.d.ts +124 -0
  98. package/lib/packlets/ai-assist/promptCacheAnthropicBreakpointHarness.d.ts.map +1 -0
  99. package/lib/packlets/ai-assist/promptCacheAnthropicBreakpointHarness.js +208 -0
  100. package/lib/packlets/ai-assist/promptCacheAnthropicBreakpointHarness.js.map +1 -0
  101. package/lib/packlets/ai-assist/promptCacheObservabilityHarness.d.ts +77 -0
  102. package/lib/packlets/ai-assist/promptCacheObservabilityHarness.d.ts.map +1 -0
  103. package/lib/packlets/ai-assist/promptCacheObservabilityHarness.js +143 -0
  104. package/lib/packlets/ai-assist/promptCacheObservabilityHarness.js.map +1 -0
  105. package/lib/packlets/ai-assist/promptCacheRoutingAbHarness.d.ts +84 -0
  106. package/lib/packlets/ai-assist/promptCacheRoutingAbHarness.d.ts.map +1 -0
  107. package/lib/packlets/ai-assist/promptCacheRoutingAbHarness.js +163 -0
  108. package/lib/packlets/ai-assist/promptCacheRoutingAbHarness.js.map +1 -0
  109. package/lib/packlets/ai-assist/promptCacheUsageMath.d.ts +105 -0
  110. package/lib/packlets/ai-assist/promptCacheUsageMath.d.ts.map +1 -0
  111. package/lib/packlets/ai-assist/promptCacheUsageMath.js +130 -0
  112. package/lib/packlets/ai-assist/promptCacheUsageMath.js.map +1 -0
  113. package/lib/packlets/ai-assist/registry.d.ts.map +1 -1
  114. package/lib/packlets/ai-assist/registry.js +9 -11
  115. package/lib/packlets/ai-assist/registry.js.map +1 -1
  116. package/lib/packlets/ai-assist/streamUsageCapability.d.ts +129 -0
  117. package/lib/packlets/ai-assist/streamUsageCapability.d.ts.map +1 -0
  118. package/lib/packlets/ai-assist/streamUsageCapability.js +140 -0
  119. package/lib/packlets/ai-assist/streamUsageCapability.js.map +1 -0
  120. package/lib/packlets/ai-assist/streamingAdapters/anthropic.d.ts +2 -1
  121. package/lib/packlets/ai-assist/streamingAdapters/anthropic.d.ts.map +1 -1
  122. package/lib/packlets/ai-assist/streamingAdapters/anthropic.js +29 -6
  123. package/lib/packlets/ai-assist/streamingAdapters/anthropic.js.map +1 -1
  124. package/lib/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.d.ts +58 -14
  125. package/lib/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.d.ts.map +1 -1
  126. package/lib/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.js +81 -36
  127. package/lib/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.js.map +1 -1
  128. package/lib/packlets/ai-assist/streamingAdapters/common.d.ts +38 -3
  129. package/lib/packlets/ai-assist/streamingAdapters/common.d.ts.map +1 -1
  130. package/lib/packlets/ai-assist/streamingAdapters/common.js +25 -1
  131. package/lib/packlets/ai-assist/streamingAdapters/common.js.map +1 -1
  132. package/lib/packlets/ai-assist/streamingAdapters/gemini.d.ts.map +1 -1
  133. package/lib/packlets/ai-assist/streamingAdapters/gemini.js +12 -5
  134. package/lib/packlets/ai-assist/streamingAdapters/gemini.js.map +1 -1
  135. package/lib/packlets/ai-assist/streamingAdapters/openaiChat.d.ts +2 -1
  136. package/lib/packlets/ai-assist/streamingAdapters/openaiChat.d.ts.map +1 -1
  137. package/lib/packlets/ai-assist/streamingAdapters/openaiChat.js +42 -8
  138. package/lib/packlets/ai-assist/streamingAdapters/openaiChat.js.map +1 -1
  139. package/lib/packlets/ai-assist/streamingAdapters/openaiResponses.d.ts +2 -1
  140. package/lib/packlets/ai-assist/streamingAdapters/openaiResponses.d.ts.map +1 -1
  141. package/lib/packlets/ai-assist/streamingAdapters/openaiResponses.js +30 -19
  142. package/lib/packlets/ai-assist/streamingAdapters/openaiResponses.js.map +1 -1
  143. package/lib/packlets/ai-assist/streamingAdapters/proxy.d.ts +22 -0
  144. package/lib/packlets/ai-assist/streamingAdapters/proxy.d.ts.map +1 -1
  145. package/lib/packlets/ai-assist/streamingAdapters/proxy.js +62 -4
  146. package/lib/packlets/ai-assist/streamingAdapters/proxy.js.map +1 -1
  147. package/lib/packlets/ai-assist/streamingClient.d.ts +5 -0
  148. package/lib/packlets/ai-assist/streamingClient.d.ts.map +1 -1
  149. package/lib/packlets/ai-assist/streamingClient.js +20 -5
  150. package/lib/packlets/ai-assist/streamingClient.js.map +1 -1
  151. package/lib/packlets/ai-assist/thinkingOptionsResolver.d.ts +11 -3
  152. package/lib/packlets/ai-assist/thinkingOptionsResolver.d.ts.map +1 -1
  153. package/lib/packlets/ai-assist/thinkingOptionsResolver.js +23 -5
  154. package/lib/packlets/ai-assist/thinkingOptionsResolver.js.map +1 -1
  155. package/lib/packlets/ai-assist/toolFormats.d.ts +55 -1
  156. package/lib/packlets/ai-assist/toolFormats.d.ts.map +1 -1
  157. package/lib/packlets/ai-assist/toolFormats.js +82 -0
  158. package/lib/packlets/ai-assist/toolFormats.js.map +1 -1
  159. package/lib/packlets/ai-assist/toolTypes.d.ts +386 -0
  160. package/lib/packlets/ai-assist/toolTypes.d.ts.map +1 -0
  161. package/lib/packlets/ai-assist/toolTypes.js +22 -0
  162. package/lib/packlets/ai-assist/toolTypes.js.map +1 -0
  163. package/lib/packlets/ai-assist/usageNormalization.d.ts +58 -0
  164. package/lib/packlets/ai-assist/usageNormalization.d.ts.map +1 -0
  165. package/lib/packlets/ai-assist/usageNormalization.js +184 -0
  166. package/lib/packlets/ai-assist/usageNormalization.js.map +1 -0
  167. package/lib/packlets/ai-assist/usageTypes.d.ts +84 -0
  168. package/lib/packlets/ai-assist/usageTypes.d.ts.map +1 -0
  169. package/lib/packlets/ai-assist/usageTypes.js +22 -0
  170. package/lib/packlets/ai-assist/usageTypes.js.map +1 -0
  171. package/lib/packlets/mustache/index.d.ts +1 -1
  172. package/lib/packlets/mustache/index.d.ts.map +1 -1
  173. package/lib/packlets/mustache/index.js.map +1 -1
  174. package/lib/packlets/mustache/interfaces.d.ts +30 -0
  175. package/lib/packlets/mustache/interfaces.d.ts.map +1 -1
  176. package/lib/packlets/mustache/interfaces.js.map +1 -1
  177. package/lib/packlets/mustache/mustacheTemplate.d.ts +56 -1
  178. package/lib/packlets/mustache/mustacheTemplate.d.ts.map +1 -1
  179. package/lib/packlets/mustache/mustacheTemplate.js +91 -0
  180. package/lib/packlets/mustache/mustacheTemplate.js.map +1 -1
  181. package/package.json +16 -16
  182. package/perf/promptCacheAnthropicBreakpoint.js +135 -0
  183. package/perf/promptCacheObservability.js +107 -0
  184. package/perf/promptCacheRoutingAb.js +105 -0
@@ -44,11 +44,12 @@ var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _ar
44
44
  *
45
45
  * @packageDocumentation
46
46
  */
47
- import { succeed, Validators } from '@fgv/ts-utils';
48
- import { buildMessages, buildOpenAiChatUserContent } from '../chatRequestBuilders';
47
+ import { fail, succeed, Validators } from '@fgv/ts-utils';
48
+ import { buildMessages, buildOpenAiChatSystemContent, buildOpenAiChatUserContent } from '../chatRequestBuilders';
49
49
  import { bearerAuthHeader } from '../endpoint';
50
50
  import { parseSseEventJson, readSseEvents } from '../sseParser';
51
- import { openSseConnection, validateEventPayload } from './common';
51
+ import { normalizeOpenAiChatUsage } from '../usageNormalization';
52
+ import { jsonObjectOrNullValidator, openSseConnection, validateEventPayload } from './common';
52
53
  // eslint-disable-next-line @rushstack/no-new-null
53
54
  const stringOrNull = Validators.isA('string-or-null',
54
55
  // eslint-disable-next-line @rushstack/no-new-null
@@ -58,7 +59,8 @@ const openAiChatStreamChoice = Validators.object({
58
59
  finish_reason: stringOrNull.optional()
59
60
  }, { options: { optionalFields: ['delta', 'finish_reason'] } });
60
61
  const openAiChatStreamChunk = Validators.object({
61
- choices: Validators.arrayOf(openAiChatStreamChoice)
62
+ choices: Validators.arrayOf(openAiChatStreamChoice),
63
+ usage: jsonObjectOrNullValidator.optional()
62
64
  });
63
65
  // ============================================================================
64
66
  // Stream translator
@@ -68,13 +70,14 @@ const openAiChatStreamChunk = Validators.object({
68
70
  *
69
71
  * @internal
70
72
  */
71
- function translateOpenAiChatStream(response) {
73
+ function translateOpenAiChatStream(response, reportsUsage) {
72
74
  return __asyncGenerator(this, arguments, function* translateOpenAiChatStream_1() {
73
75
  var _a, e_1, _b, _c;
74
76
  var _d;
75
77
  let fullText = '';
76
78
  let truncated = false;
77
79
  let receivedDone = false;
80
+ let usageRaw;
78
81
  try {
79
82
  /* c8 ignore next - body is non-null at this point per openSseConnection */
80
83
  if (!response.body)
@@ -90,9 +93,19 @@ function translateOpenAiChatStream(response) {
90
93
  continue;
91
94
  }
92
95
  const chunk = validateEventPayload(json, openAiChatStreamChunk);
96
+ // Every intermediate chunk carries literal `null` once `stream_options.include_usage`
97
+ // is set; only the terminal chunk's populated object should update the accumulator.
98
+ // Gated on reportsUsage (same descriptor check that decides whether the request even
99
+ // asked for it, see callOpenAiChatStream) — this adapter is shared by descriptors with
100
+ // no confirmed cache-usage reporting, and an unprompted usage block from one of those
101
+ // carries no cache information (see supportsCacheUsageReporting).
102
+ if (reportsUsage && (chunk === null || chunk === void 0 ? void 0 : chunk.usage) !== undefined && chunk.usage !== null) {
103
+ usageRaw = chunk.usage;
104
+ }
93
105
  /* c8 ignore next 1 - defensive: chunk?.choices optional chain unreachable after validation */
94
106
  const choice = chunk === null || chunk === void 0 ? void 0 : chunk.choices[0];
95
- /* c8 ignore next 3 - defensive: SSE events without choices are skipped */
107
+ // The `stream_options.include_usage` terminal chunk carries `choices: []` and only
108
+ // `usage` — already captured above, so it is expected (not defensive) to fall through here.
96
109
  if (!choice) {
97
110
  continue;
98
111
  }
@@ -122,7 +135,8 @@ function translateOpenAiChatStream(response) {
122
135
  return yield __await(void 0);
123
136
  } /* c8 ignore stop */
124
137
  if (receivedDone) {
125
- yield yield __await({ type: 'done', truncated, fullText });
138
+ const usage = normalizeOpenAiChatUsage(usageRaw);
139
+ yield yield __await(Object.assign({ type: 'done', truncated, fullText }, (usage !== undefined ? { usage } : {})));
126
140
  }
127
141
  else {
128
142
  yield yield __await({ type: 'error', message: 'OpenAI stream ended without a finish_reason' });
@@ -138,15 +152,30 @@ function translateOpenAiChatStream(response) {
138
152
  *
139
153
  * @internal
140
154
  */
141
- export async function callOpenAiChatStream(config, prompt, messagesBefore, temperature, logger, signal, resolvedThinking, maxTokens, useMaxCompletionTokensField = false) {
155
+ export async function callOpenAiChatStream(config, prompt, messagesBefore, temperature, logger, signal, resolvedThinking, maxTokens, useMaxCompletionTokensField = false, includeStreamUsage = false, cache, cacheKeyHeader) {
142
156
  var _a;
143
157
  const url = `${config.baseUrl}/chat/completions`;
144
- const messages = buildMessages(prompt.system, buildOpenAiChatUserContent(prompt), {
158
+ const systemContentResult = buildOpenAiChatSystemContent(prompt.system, cache);
159
+ if (systemContentResult.isFailure()) {
160
+ return fail(systemContentResult.message);
161
+ }
162
+ const messages = buildMessages(systemContentResult.value, buildOpenAiChatUserContent(prompt), {
145
163
  head: messagesBefore
146
164
  });
147
165
  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;
148
166
  const supportsReasoning = config.model !== 'grok-4';
149
- const body = { model: config.model, messages, stream: true };
167
+ const body = Object.assign({ model: config.model, messages, stream: true }, ((cache === null || cache === void 0 ? void 0 : cache.cacheKey) !== undefined && cacheKeyHeader === undefined
168
+ ? // eslint-disable-next-line @typescript-eslint/naming-convention -- wire field name
169
+ { prompt_cache_key: cache.cacheKey }
170
+ : {}));
171
+ // Chat Completions omits the usage block from every streaming response unless asked —
172
+ // unlike the Responses API and Anthropic, which report it unconditionally. Gated per
173
+ // AiAssist.supportsStreamUsageOption: this adapter also carries providers whose tolerance
174
+ // for an unrecognized request field is unverified (a strict self-hosted server can reject
175
+ // the whole request rather than ignore it), so the field is sent only where it's confirmed.
176
+ if (includeStreamUsage) {
177
+ body.stream_options = { include_usage: true };
178
+ }
150
179
  if (effort !== undefined && supportsReasoning) {
151
180
  body.reasoning_effort = effort;
152
181
  }
@@ -167,9 +196,14 @@ export async function callOpenAiChatStream(config, prompt, messagesBefore, tempe
167
196
  Object.assign(body, resolvedThinking.otherParams);
168
197
  }
169
198
  const headers = bearerAuthHeader(config.apiKey);
199
+ // Sticky-routing key, when the provider carries it as a header rather than a body field —
200
+ // see the identical header on callOpenAiCompletion's request in completionClient.ts.
201
+ if ((cache === null || cache === void 0 ? void 0 : cache.cacheKey) !== undefined && cacheKeyHeader !== undefined) {
202
+ headers[cacheKeyHeader] = cache.cacheKey;
203
+ }
170
204
  /* c8 ignore next 1 - optional logger */
171
205
  logger === null || logger === void 0 ? void 0 : logger.info(`OpenAI streaming completion: model=${config.model}`);
172
206
  const conn = await openSseConnection(url, headers, body, logger, signal);
173
- return conn.onSuccess((response) => succeed(translateOpenAiChatStream(response)));
207
+ return conn.onSuccess((response) => succeed(translateOpenAiChatStream(response, includeStreamUsage)));
174
208
  }
175
209
  //# sourceMappingURL=openaiChat.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"openaiChat.js","sourceRoot":"","sources":["../../../../src/packlets/ai-assist/streamingAdapters/openaiChat.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 OpenAI Chat Completions (also used for Groq, Mistral,\n * and other Chat-Completions-compatible providers when no tools are\n * requested).\n *\n * @packageDocumentation\n */\n\nimport { type Logging, Result, succeed, type Validator, Validators } from '@fgv/ts-utils';\n\nimport { buildMessages, buildOpenAiChatUserContent } from '../chatRequestBuilders';\nimport { bearerAuthHeader } from '../endpoint';\nimport { AiPrompt, type IAiStreamEvent, type IChatMessage } from '../model';\nimport { parseSseEventJson, readSseEvents } from '../sseParser';\nimport { type IResolvedThinkingConfig } from '../thinkingOptionsResolver';\nimport { IStreamApiConfig, openSseConnection, validateEventPayload } from './common';\n\n// ============================================================================\n// Event payload shapes\n// ============================================================================\n\n/**\n * The shape of `choices[0]` in an OpenAI Chat Completions streaming chunk.\n * Both `delta.content` and `finish_reason` are optional — content arrives in\n * intermediate chunks, finish_reason in the terminal chunk. The wire sends\n * `finish_reason: null` (literal null, not absent) on intermediate chunks,\n * so the validator must accept null alongside string.\n *\n * @internal\n */\ninterface IOpenAiChatStreamChoice {\n readonly delta?: { readonly content?: string };\n // eslint-disable-next-line @rushstack/no-new-null\n readonly finish_reason?: string | null;\n}\n\n/**\n * One streaming chunk from the OpenAI Chat Completions endpoint. Always has\n * a `choices` array with one element in the streaming mode.\n *\n * @internal\n */\ninterface IOpenAiChatStreamChunk {\n readonly choices: ReadonlyArray<IOpenAiChatStreamChoice>;\n}\n\n// eslint-disable-next-line @rushstack/no-new-null\nconst stringOrNull: Validator<string | null> = Validators.isA<string | null>(\n 'string-or-null',\n // eslint-disable-next-line @rushstack/no-new-null\n (v: unknown): v is string | null => typeof v === 'string' || v === null\n);\n\nconst openAiChatStreamChoice: Validator<IOpenAiChatStreamChoice> = Validators.object<IOpenAiChatStreamChoice>(\n {\n delta: Validators.object<{ content?: string }>(\n { content: Validators.string },\n { options: { optionalFields: ['content'] } }\n ).optional(),\n finish_reason: stringOrNull.optional()\n },\n { options: { optionalFields: ['delta', 'finish_reason'] } }\n);\n\nconst openAiChatStreamChunk: Validator<IOpenAiChatStreamChunk> = Validators.object<IOpenAiChatStreamChunk>({\n choices: Validators.arrayOf(openAiChatStreamChoice)\n});\n\n// ============================================================================\n// Stream translator\n// ============================================================================\n\n/**\n * Translates an OpenAI Chat Completions SSE stream into unified events.\n *\n * @internal\n */\nasync function* translateOpenAiChatStream(response: Response): AsyncGenerator<IAiStreamEvent> {\n let fullText = '';\n let truncated = false;\n let receivedDone = 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 json = parseSseEventJson(message.data);\n if (json === undefined) {\n // [DONE] sentinel or unparseable; skip\n continue;\n }\n const chunk = validateEventPayload(json, openAiChatStreamChunk);\n /* c8 ignore next 1 - defensive: chunk?.choices optional chain unreachable after validation */\n const choice = chunk?.choices[0];\n /* c8 ignore next 3 - defensive: SSE events without choices are skipped */\n if (!choice) {\n continue;\n }\n /* c8 ignore next 1 - defensive: choice.delta?.content optional chain unreachable after validation */\n const delta = choice.delta?.content;\n if (typeof delta === 'string' && delta.length > 0) {\n fullText += delta;\n yield { type: 'text-delta', delta };\n }\n const finish = choice.finish_reason;\n if (typeof finish === 'string' && finish.length > 0) {\n truncated = finish === 'length';\n receivedDone = true;\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 (receivedDone) {\n yield { type: 'done', truncated, fullText };\n } else {\n yield { type: 'error', message: 'OpenAI stream ended without a finish_reason' };\n }\n}\n\n// ============================================================================\n// Per-format request caller\n// ============================================================================\n\n/**\n * Issues a streaming Chat Completions request and returns the unified-event\n * iterable on success.\n *\n * @internal\n */\nexport async function callOpenAiChatStream(\n config: IStreamApiConfig,\n prompt: AiPrompt,\n messagesBefore: ReadonlyArray<IChatMessage> | undefined,\n temperature: number | undefined,\n logger?: Logging.ILogger,\n signal?: AbortSignal,\n resolvedThinking?: IResolvedThinkingConfig,\n maxTokens?: number,\n useMaxCompletionTokensField: boolean = false\n): Promise<Result<AsyncIterable<IAiStreamEvent>>> {\n const url = `${config.baseUrl}/chat/completions`;\n const messages = buildMessages(prompt.system, buildOpenAiChatUserContent(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> = { model: config.model, messages, stream: true };\n if (effort !== undefined && supportsReasoning) {\n body.reasoning_effort = effort;\n }\n // Temperature is sent only when the caller explicitly provided one; omitting it lets the\n // provider apply its own default (current-gen models reject a caller-supplied default). This\n // adapter is reached only via the conflict-guarded streaming entry point — the client-tool path\n // routes OpenAI through the Responses adapter, not chat — so, like callOpenAiCompletion, no\n // effort gate is needed: temperature is present here only when the effort is undefined/'none'.\n if (temperature !== undefined) {\n body.temperature = temperature;\n }\n // Omitted when the caller doesn't set maxTokens. See AiAssist.usesMaxCompletionTokensField for\n // the field-name split between genuine OpenAI and the other openai-format providers.\n if (maxTokens !== undefined) {\n body[useMaxCompletionTokensField ? 'max_completion_tokens' : 'max_tokens'] = maxTokens;\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 1 - optional logger */\n logger?.info(`OpenAI streaming completion: model=${config.model}`);\n const conn = await openSseConnection(url, headers, body, logger, signal);\n return conn.onSuccess((response) => succeed(translateOpenAiChatStream(response)));\n}\n"]}
1
+ {"version":3,"file":"openaiChat.js","sourceRoot":"","sources":["../../../../src/packlets/ai-assist/streamingAdapters/openaiChat.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 OpenAI Chat Completions (also used for Groq, Mistral,\n * and other Chat-Completions-compatible providers when no tools are\n * requested).\n *\n * @packageDocumentation\n */\n\nimport { fail, type Logging, Result, succeed, type Validator, Validators } from '@fgv/ts-utils';\nimport { type JsonObject } from '@fgv/ts-json-base';\n\nimport {\n buildMessages,\n buildOpenAiChatSystemContent,\n buildOpenAiChatUserContent\n} from '../chatRequestBuilders';\nimport { type IAiCacheRequest } from '../cacheRequest';\nimport { bearerAuthHeader } from '../endpoint';\nimport { AiPrompt, type IAiStreamEvent, type IChatMessage } from '../model';\nimport { parseSseEventJson, readSseEvents } from '../sseParser';\nimport { type IResolvedThinkingConfig } from '../thinkingOptionsResolver';\nimport { normalizeOpenAiChatUsage } from '../usageNormalization';\nimport {\n IStreamApiConfig,\n jsonObjectOrNullValidator,\n openSseConnection,\n validateEventPayload\n} from './common';\n\n// ============================================================================\n// Event payload shapes\n// ============================================================================\n\n/**\n * The shape of `choices[0]` in an OpenAI Chat Completions streaming chunk.\n * Both `delta.content` and `finish_reason` are optional — content arrives in\n * intermediate chunks, finish_reason in the terminal chunk. The wire sends\n * `finish_reason: null` (literal null, not absent) on intermediate chunks,\n * so the validator must accept null alongside string.\n *\n * @internal\n */\ninterface IOpenAiChatStreamChoice {\n readonly delta?: { readonly content?: string };\n // eslint-disable-next-line @rushstack/no-new-null\n readonly finish_reason?: string | null;\n}\n\n/**\n * One streaming chunk from the OpenAI Chat Completions endpoint. Always has\n * a `choices` array with one element in the streaming mode.\n *\n * @internal\n */\ninterface IOpenAiChatStreamChunk {\n readonly choices: ReadonlyArray<IOpenAiChatStreamChoice>;\n /**\n * Only present at all when the request carries\n * `stream_options.include_usage: true` (see `callOpenAiChatStream`) — without\n * it OpenAI Chat Completions never emits a usage block while streaming,\n * unlike every other adapter here. When\n * present, the wire sends literal `null` on every chunk except the terminal\n * one, which carries the populated object; `null` must validate (not fail the\n * whole chunk) or every intermediate chunk — including its `delta.content` and\n * `finish_reason` — is dropped along with it.\n */\n // eslint-disable-next-line @rushstack/no-new-null\n readonly usage?: JsonObject | null;\n}\n\n// eslint-disable-next-line @rushstack/no-new-null\nconst stringOrNull: Validator<string | null> = Validators.isA<string | null>(\n 'string-or-null',\n // eslint-disable-next-line @rushstack/no-new-null\n (v: unknown): v is string | null => typeof v === 'string' || v === null\n);\n\nconst openAiChatStreamChoice: Validator<IOpenAiChatStreamChoice> = Validators.object<IOpenAiChatStreamChoice>(\n {\n delta: Validators.object<{ content?: string }>(\n { content: Validators.string },\n { options: { optionalFields: ['content'] } }\n ).optional(),\n finish_reason: stringOrNull.optional()\n },\n { options: { optionalFields: ['delta', 'finish_reason'] } }\n);\n\nconst openAiChatStreamChunk: Validator<IOpenAiChatStreamChunk> = Validators.object<IOpenAiChatStreamChunk>({\n choices: Validators.arrayOf(openAiChatStreamChoice),\n usage: jsonObjectOrNullValidator.optional()\n});\n\n// ============================================================================\n// Stream translator\n// ============================================================================\n\n/**\n * Translates an OpenAI Chat Completions SSE stream into unified events.\n *\n * @internal\n */\nasync function* translateOpenAiChatStream(\n response: Response,\n reportsUsage: boolean\n): AsyncGenerator<IAiStreamEvent> {\n let fullText = '';\n let truncated = false;\n let receivedDone = false;\n let usageRaw: JsonObject | undefined;\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 json = parseSseEventJson(message.data);\n if (json === undefined) {\n // [DONE] sentinel or unparseable; skip\n continue;\n }\n const chunk = validateEventPayload(json, openAiChatStreamChunk);\n // Every intermediate chunk carries literal `null` once `stream_options.include_usage`\n // is set; only the terminal chunk's populated object should update the accumulator.\n // Gated on reportsUsage (same descriptor check that decides whether the request even\n // asked for it, see callOpenAiChatStream) — this adapter is shared by descriptors with\n // no confirmed cache-usage reporting, and an unprompted usage block from one of those\n // carries no cache information (see supportsCacheUsageReporting).\n if (reportsUsage && chunk?.usage !== undefined && chunk.usage !== null) {\n usageRaw = chunk.usage;\n }\n /* c8 ignore next 1 - defensive: chunk?.choices optional chain unreachable after validation */\n const choice = chunk?.choices[0];\n // The `stream_options.include_usage` terminal chunk carries `choices: []` and only\n // `usage` — already captured above, so it is expected (not defensive) to fall through here.\n if (!choice) {\n continue;\n }\n /* c8 ignore next 1 - defensive: choice.delta?.content optional chain unreachable after validation */\n const delta = choice.delta?.content;\n if (typeof delta === 'string' && delta.length > 0) {\n fullText += delta;\n yield { type: 'text-delta', delta };\n }\n const finish = choice.finish_reason;\n if (typeof finish === 'string' && finish.length > 0) {\n truncated = finish === 'length';\n receivedDone = true;\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 (receivedDone) {\n const usage = normalizeOpenAiChatUsage(usageRaw);\n yield { type: 'done', truncated, fullText, ...(usage !== undefined ? { usage } : {}) };\n } else {\n yield { type: 'error', message: 'OpenAI stream ended without a finish_reason' };\n }\n}\n\n// ============================================================================\n// Per-format request caller\n// ============================================================================\n\n/**\n * Issues a streaming Chat Completions request and returns the unified-event\n * iterable on success.\n *\n * @internal\n */\nexport async function callOpenAiChatStream(\n config: IStreamApiConfig,\n prompt: AiPrompt,\n messagesBefore: ReadonlyArray<IChatMessage> | undefined,\n temperature: number | undefined,\n logger?: Logging.ILogger,\n signal?: AbortSignal,\n resolvedThinking?: IResolvedThinkingConfig,\n maxTokens?: number,\n useMaxCompletionTokensField: boolean = false,\n includeStreamUsage: boolean = false,\n cache?: IAiCacheRequest,\n cacheKeyHeader?: string\n): Promise<Result<AsyncIterable<IAiStreamEvent>>> {\n const url = `${config.baseUrl}/chat/completions`;\n const systemContentResult = buildOpenAiChatSystemContent(prompt.system, cache);\n if (systemContentResult.isFailure()) {\n return fail(systemContentResult.message);\n }\n const messages = buildMessages(systemContentResult.value, buildOpenAiChatUserContent(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 messages,\n stream: true,\n // Pure additive routing plumbing — see the identical field and gating comment on\n // callOpenAiCompletion's body in completionClient.ts. `cacheKeyHeader` is set when the\n // provider carries the routing key as a header instead (xAI), so the body field is\n // omitted in that case to avoid sending an unrecognized field twice.\n ...(cache?.cacheKey !== undefined && cacheKeyHeader === undefined\n ? // eslint-disable-next-line @typescript-eslint/naming-convention -- wire field name\n { prompt_cache_key: cache.cacheKey }\n : {})\n };\n // Chat Completions omits the usage block from every streaming response unless asked —\n // unlike the Responses API and Anthropic, which report it unconditionally. Gated per\n // AiAssist.supportsStreamUsageOption: this adapter also carries providers whose tolerance\n // for an unrecognized request field is unverified (a strict self-hosted server can reject\n // the whole request rather than ignore it), so the field is sent only where it's confirmed.\n if (includeStreamUsage) {\n body.stream_options = { include_usage: true };\n }\n if (effort !== undefined && supportsReasoning) {\n body.reasoning_effort = effort;\n }\n // Temperature is sent only when the caller explicitly provided one; omitting it lets the\n // provider apply its own default (current-gen models reject a caller-supplied default). This\n // adapter is reached only via the conflict-guarded streaming entry point — the client-tool path\n // routes OpenAI through the Responses adapter, not chat — so, like callOpenAiCompletion, no\n // effort gate is needed: temperature is present here only when the effort is undefined/'none'.\n if (temperature !== undefined) {\n body.temperature = temperature;\n }\n // Omitted when the caller doesn't set maxTokens. See AiAssist.usesMaxCompletionTokensField for\n // the field-name split between genuine OpenAI and the other openai-format providers.\n if (maxTokens !== undefined) {\n body[useMaxCompletionTokensField ? 'max_completion_tokens' : 'max_tokens'] = maxTokens;\n }\n if (resolvedThinking?.otherParams !== undefined) {\n Object.assign(body, resolvedThinking.otherParams);\n }\n const headers: Record<string, string> = bearerAuthHeader(config.apiKey);\n // Sticky-routing key, when the provider carries it as a header rather than a body field —\n // see the identical header on callOpenAiCompletion's request in completionClient.ts.\n if (cache?.cacheKey !== undefined && cacheKeyHeader !== undefined) {\n headers[cacheKeyHeader] = cache.cacheKey;\n }\n /* c8 ignore next 1 - optional logger */\n logger?.info(`OpenAI streaming completion: model=${config.model}`);\n const conn = await openSseConnection(url, headers, body, logger, signal);\n return conn.onSuccess((response) => succeed(translateOpenAiChatStream(response, includeStreamUsage)));\n}\n"]}
@@ -48,12 +48,13 @@ var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _ar
48
48
  *
49
49
  * @packageDocumentation
50
50
  */
51
- import { succeed, Validators } from '@fgv/ts-utils';
52
- import { buildMessages, buildOpenAiResponsesUserContent } from '../chatRequestBuilders';
51
+ import { fail, succeed, Validators } from '@fgv/ts-utils';
52
+ import { buildMessages, buildOpenAiResponsesSystemContent, buildOpenAiResponsesUserContent } from '../chatRequestBuilders';
53
53
  import { bearerAuthHeader } from '../endpoint';
54
54
  import { parseSseEventJson, readSseEvents } from '../sseParser';
55
55
  import { toResponsesApiTools } from '../toolFormats';
56
- import { UNRECOGNIZED_EVENT_WARN_TAG, formatUnrecognizedEventPayloadPreview, openSseConnection, validateEventPayload } from './common';
56
+ import { normalizeOpenAiResponsesUsage } from '../usageNormalization';
57
+ import { UNRECOGNIZED_EVENT_WARN_TAG, formatUnrecognizedEventPayloadPreview, jsonObjectOrNullValidator, openSseConnection, validateEventPayload } from './common';
57
58
  const responsesDeltaPayload = Validators.object({
58
59
  delta: Validators.string
59
60
  });
@@ -76,8 +77,9 @@ const responsesIncompleteDetails = Validators.object({ reason: Validators.string
76
77
  const responsesCompletedPayload = Validators.object({
77
78
  response: Validators.object({
78
79
  status: Validators.string.optional(),
79
- incomplete_details: responsesIncompleteDetails.optional()
80
- }, { options: { optionalFields: ['status', 'incomplete_details'] } })
80
+ incomplete_details: responsesIncompleteDetails.optional(),
81
+ usage: jsonObjectOrNullValidator.optional()
82
+ })
81
83
  });
82
84
  const responsesErrorInner = Validators.object({ message: Validators.string.optional() }, { options: { optionalFields: ['message'] } });
83
85
  const responsesErrorPayload = Validators.object({
@@ -174,14 +176,15 @@ const RECOGNIZED_OPENAI_RESPONSES_EVENTS = new Set([
174
176
  *
175
177
  * @internal
176
178
  */
177
- function translateOpenAiResponsesStream(response, functionCallMap, logger) {
179
+ function translateOpenAiResponsesStream(response, functionCallMap, reportsUsage, logger) {
178
180
  return __asyncGenerator(this, arguments, function* translateOpenAiResponsesStream_1() {
179
181
  var _a, e_1, _b, _c;
180
- var _d, _e, _f, _g, _h;
182
+ var _d, _e, _f, _g, _h, _j;
181
183
  let fullText = '';
182
184
  let truncated = false;
183
185
  let completed = false;
184
186
  let incompleteReason;
187
+ let usageRaw;
185
188
  // OpenAI / xAI Responses API emits function_call_arguments.{delta,done} events keyed by
186
189
  // `item_id` (the fc_* output-item id). The harness and continuation builder key by
187
190
  // `call_id` (the call_* id). This map correlates the two — populated when the
@@ -195,9 +198,9 @@ function translateOpenAiResponsesStream(response, functionCallMap, logger) {
195
198
  if (!response.body)
196
199
  return yield __await(void 0);
197
200
  try {
198
- for (var _j = true, _k = __asyncValues(readSseEvents(response.body)), _l; _l = yield __await(_k.next()), _a = _l.done, !_a; _j = true) {
199
- _c = _l.value;
200
- _j = false;
201
+ for (var _k = true, _l = __asyncValues(readSseEvents(response.body)), _m; _m = yield __await(_l.next()), _a = _m.done, !_a; _k = true) {
202
+ _c = _m.value;
203
+ _k = false;
201
204
  const message = _c;
202
205
  const eventName = message.event;
203
206
  if (eventName === 'response.output_text.delta') {
@@ -269,7 +272,7 @@ function translateOpenAiResponsesStream(response, functionCallMap, logger) {
269
272
  ? parsed
270
273
  : {};
271
274
  }
272
- catch (_m) {
275
+ catch (_o) {
273
276
  args = {};
274
277
  }
275
278
  /* c8 ignore stop */
@@ -287,6 +290,10 @@ function translateOpenAiResponsesStream(response, functionCallMap, logger) {
287
290
  // event so a stray incomplete_details on a non-incomplete payload never leaks
288
291
  // through, and a later (defensive) completed event can't leave a stale reason.
289
292
  incompleteReason = truncated ? (_e = payload.response.incomplete_details) === null || _e === void 0 ? void 0 : _e.reason : undefined;
293
+ // Gated the same way as the non-streaming Responses path (see
294
+ // supportsCacheUsageReporting) — this adapter is shared by descriptors with no
295
+ // confirmed cache-usage reporting.
296
+ usageRaw = reportsUsage ? (_f = payload.response.usage) !== null && _f !== void 0 ? _f : undefined : undefined;
290
297
  }
291
298
  completed = true;
292
299
  /* c8 ignore next 1 - defensive: eventName === 'error' alternative not exercised in tests */
@@ -294,7 +301,7 @@ function translateOpenAiResponsesStream(response, functionCallMap, logger) {
294
301
  else if (eventName === 'response.failed' || eventName === 'error') {
295
302
  const payload = validateEventPayload(parseSseEventJson(message.data), responsesErrorPayload);
296
303
  /* c8 ignore next 1 - defensive: payload?.error and payload?.message null branches unreachable */
297
- 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';
304
+ const errMsg = (_j = (_h = (_g = payload === null || payload === void 0 ? void 0 : payload.error) === null || _g === void 0 ? void 0 : _g.message) !== null && _h !== void 0 ? _h : payload === null || payload === void 0 ? void 0 : payload.message) !== null && _j !== void 0 ? _j : 'Responses API stream failed';
298
305
  yield yield __await({ type: 'error', message: errMsg });
299
306
  return yield __await(void 0);
300
307
  }
@@ -319,7 +326,7 @@ function translateOpenAiResponsesStream(response, functionCallMap, logger) {
319
326
  catch (e_1_1) { e_1 = { error: e_1_1 }; }
320
327
  finally {
321
328
  try {
322
- if (!_j && !_a && (_b = _k.return)) yield __await(_b.call(_k));
329
+ if (!_k && !_a && (_b = _l.return)) yield __await(_b.call(_l));
323
330
  }
324
331
  finally { if (e_1) throw e_1.error; }
325
332
  }
@@ -329,7 +336,8 @@ function translateOpenAiResponsesStream(response, functionCallMap, logger) {
329
336
  return yield __await(void 0);
330
337
  } /* c8 ignore stop */
331
338
  if (completed) {
332
- yield yield __await({ type: 'done', truncated, fullText, incompleteReason });
339
+ const usage = normalizeOpenAiResponsesUsage(usageRaw);
340
+ yield yield __await(Object.assign({ type: 'done', truncated, fullText, incompleteReason }, (usage !== undefined ? { usage } : {})));
333
341
  }
334
342
  else {
335
343
  yield yield __await({ type: 'error', message: 'Responses API stream ended without a completed event' });
@@ -345,20 +353,23 @@ function translateOpenAiResponsesStream(response, functionCallMap, logger) {
345
353
  *
346
354
  * @internal
347
355
  */
348
- export async function callOpenAiResponsesStream(config, prompt, tools = [], messagesBefore, temperature, logger, signal, resolvedThinking, functionCallMap, continuationMessages, maxTokens) {
356
+ export async function callOpenAiResponsesStream(config, prompt, tools = [], messagesBefore, temperature, logger, signal, resolvedThinking, functionCallMap, continuationMessages, maxTokens, reportsUsage = false, cache) {
349
357
  var _a;
350
358
  const url = `${config.baseUrl}/responses`;
351
- const input = buildMessages(prompt.system, buildOpenAiResponsesUserContent(prompt), {
359
+ const systemContentResult = buildOpenAiResponsesSystemContent(prompt.system, cache);
360
+ if (systemContentResult.isFailure()) {
361
+ return fail(systemContentResult.message);
362
+ }
363
+ const input = buildMessages(systemContentResult.value, buildOpenAiResponsesUserContent(prompt), {
352
364
  head: messagesBefore,
353
365
  rawTail: continuationMessages
354
366
  });
355
367
  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;
356
368
  const supportsReasoning = config.model !== 'grok-4';
357
- const body = {
358
- model: config.model,
359
- input,
360
- stream: true
361
- };
369
+ const body = Object.assign({ model: config.model, input, stream: true }, ((cache === null || cache === void 0 ? void 0 : cache.cacheKey) !== undefined
370
+ ? // eslint-disable-next-line @typescript-eslint/naming-convention -- wire field name
371
+ { prompt_cache_key: cache.cacheKey }
372
+ : {}));
362
373
  // `tools` is omitted entirely when none are requested — a Responses-only model routed
363
374
  // here for tier/model reasons (not tools) must not send an empty tools array.
364
375
  if (tools.length > 0) {
@@ -385,6 +396,6 @@ export async function callOpenAiResponsesStream(config, prompt, tools = [], mess
385
396
  logger === null || logger === void 0 ? void 0 : logger.info(`OpenAI Responses streaming: model=${config.model}, tools=${tools.map((t) => t.type).join(',')}`);
386
397
  const callMap = functionCallMap !== null && functionCallMap !== void 0 ? functionCallMap : new Map();
387
398
  const conn = await openSseConnection(url, headers, body, logger, signal);
388
- return conn.onSuccess((response) => succeed(translateOpenAiResponsesStream(response, callMap, logger)));
399
+ return conn.onSuccess((response) => succeed(translateOpenAiResponsesStream(response, callMap, reportsUsage, logger)));
389
400
  }
390
401
  //# 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 * 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 | undefined,\n logger?: Logging.ILogger,\n signal?: AbortSignal,\n resolvedThinking?: IResolvedThinkingConfig,\n functionCallMap?: Map<string, IAccumulatedFunctionCall>,\n continuationMessages?: ReadonlyArray<JsonObject>,\n maxTokens?: number\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 stream: true\n };\n // `tools` is omitted entirely when none are requested — a Responses-only model routed\n // here for tier/model reasons (not tools) must not send an empty tools array.\n if (tools.length > 0) {\n body.tools = toResponsesApiTools(tools);\n }\n if (effort !== undefined && supportsReasoning) {\n body.reasoning = { effort };\n }\n // Temperature is sent only when the caller explicitly provided one (see callOpenAiChatStream).\n if (effort === undefined || effort === 'none') {\n if (temperature !== undefined) {\n body.temperature = temperature;\n }\n }\n // Shared by OpenAI and xAI — both route through the Responses API with the same field name.\n if (maxTokens !== undefined) {\n body.max_output_tokens = maxTokens;\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"]}
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 { fail, type Logging, Result, succeed, type Validator, Validators } from '@fgv/ts-utils';\nimport { type JsonObject } from '@fgv/ts-json-base';\n\nimport {\n buildMessages,\n buildOpenAiResponsesSystemContent,\n buildOpenAiResponsesUserContent\n} from '../chatRequestBuilders';\nimport { type IAiCacheRequest } from '../cacheRequest';\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 { normalizeOpenAiResponsesUsage } from '../usageNormalization';\nimport {\n IStreamApiConfig,\n UNRECOGNIZED_EVENT_WARN_TAG,\n formatUnrecognizedEventPayloadPreview,\n jsonObjectOrNullValidator,\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 * `null` when the provider has no usage block for this response (not just\n * absent) — must validate, or the whole `response.completed` payload is\n * rejected and `status`/`incomplete_details` are lost along with it.\n */\n // eslint-disable-next-line @rushstack/no-new-null\n readonly usage?: JsonObject | null;\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<{\n status?: string;\n incomplete_details?: { reason?: string };\n // eslint-disable-next-line @rushstack/no-new-null\n usage?: JsonObject | null;\n }>({\n status: Validators.string.optional(),\n incomplete_details: responsesIncompleteDetails.optional(),\n usage: jsonObjectOrNullValidator.optional()\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 reportsUsage: boolean,\n logger?: Logging.ILogger\n): AsyncGenerator<IAiStreamEvent> {\n let fullText = '';\n let truncated = false;\n let completed = false;\n let incompleteReason: string | undefined;\n let usageRaw: JsonObject | 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 // Gated the same way as the non-streaming Responses path (see\n // supportsCacheUsageReporting) — this adapter is shared by descriptors with no\n // confirmed cache-usage reporting.\n usageRaw = reportsUsage ? payload.response.usage ?? undefined : 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 const usage = normalizeOpenAiResponsesUsage(usageRaw);\n yield { type: 'done', truncated, fullText, incompleteReason, ...(usage !== undefined ? { usage } : {}) };\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 | undefined,\n logger?: Logging.ILogger,\n signal?: AbortSignal,\n resolvedThinking?: IResolvedThinkingConfig,\n functionCallMap?: Map<string, IAccumulatedFunctionCall>,\n continuationMessages?: ReadonlyArray<JsonObject>,\n maxTokens?: number,\n reportsUsage: boolean = false,\n cache?: IAiCacheRequest\n): Promise<Result<AsyncIterable<IAiStreamEvent>>> {\n const url = `${config.baseUrl}/responses`;\n const systemContentResult = buildOpenAiResponsesSystemContent(prompt.system, cache);\n if (systemContentResult.isFailure()) {\n return fail(systemContentResult.message);\n }\n const input = buildMessages(systemContentResult.value, 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 stream: true,\n // See the identical field and gating comment on callOpenAiResponsesCompletion's body in\n // completionClient.ts — same routing plumbing, confirmed on both Chat Completions and the\n // Responses API. The Responses API takes the routing key as a body field on every provider\n // that supports it (see supportsPromptCacheRouting), so there is no header variant here.\n ...(cache?.cacheKey !== undefined\n ? // eslint-disable-next-line @typescript-eslint/naming-convention -- wire field name\n { prompt_cache_key: cache.cacheKey }\n : {})\n };\n // `tools` is omitted entirely when none are requested — a Responses-only model routed\n // here for tier/model reasons (not tools) must not send an empty tools array.\n if (tools.length > 0) {\n body.tools = toResponsesApiTools(tools);\n }\n if (effort !== undefined && supportsReasoning) {\n body.reasoning = { effort };\n }\n // Temperature is sent only when the caller explicitly provided one (see callOpenAiChatStream).\n if (effort === undefined || effort === 'none') {\n if (temperature !== undefined) {\n body.temperature = temperature;\n }\n }\n // Shared by OpenAI and xAI — both route through the Responses API with the same field name.\n if (maxTokens !== undefined) {\n body.max_output_tokens = maxTokens;\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) =>\n succeed(translateOpenAiResponsesStream(response, callMap, reportsUsage, logger))\n );\n}\n"]}