@proposit/proposit-core 1.10.0 → 2.0.0

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 (116) hide show
  1. package/README.md +1 -1
  2. package/dist/cli/commands/expressions.d.ts +29 -0
  3. package/dist/cli/commands/expressions.d.ts.map +1 -1
  4. package/dist/cli/commands/expressions.js +118 -92
  5. package/dist/cli/commands/expressions.js.map +1 -1
  6. package/dist/extensions/argument-ingestion/shared/finalize-response-v2.d.ts.map +1 -1
  7. package/dist/extensions/argument-ingestion/shared/finalize-response-v2.js +18 -2
  8. package/dist/extensions/argument-ingestion/shared/finalize-response-v2.js.map +1 -1
  9. package/dist/extensions/{ieee → citations/ieee}/citation-claim.d.ts +53 -59
  10. package/dist/extensions/{ieee → citations/ieee}/citation-claim.d.ts.map +1 -1
  11. package/dist/extensions/{ieee → citations/ieee}/citation-claim.js +2 -2
  12. package/dist/extensions/citations/ieee/citation-claim.js.map +1 -0
  13. package/dist/extensions/citations/ieee/formatting.d.ts.map +1 -0
  14. package/dist/extensions/{ieee → citations/ieee}/formatting.js +0 -1
  15. package/dist/extensions/citations/ieee/formatting.js.map +1 -0
  16. package/dist/extensions/citations/ieee/index.d.ts.map +1 -0
  17. package/dist/extensions/citations/ieee/index.js.map +1 -0
  18. package/dist/extensions/{ieee → citations/ieee}/references.d.ts +161 -181
  19. package/dist/extensions/citations/ieee/references.d.ts.map +1 -0
  20. package/dist/extensions/{ieee → citations/ieee}/references.js +48 -22
  21. package/dist/extensions/citations/ieee/references.js.map +1 -0
  22. package/dist/extensions/{ieee → citations/ieee}/relaxed.d.ts +159 -180
  23. package/dist/extensions/citations/ieee/relaxed.d.ts.map +1 -0
  24. package/dist/extensions/{ieee → citations/ieee}/relaxed.js +1 -4
  25. package/dist/extensions/{ieee → citations/ieee}/relaxed.js.map +1 -1
  26. package/dist/extensions/citations/ieee/segment-builder.d.ts.map +1 -0
  27. package/dist/extensions/citations/ieee/segment-builder.js.map +1 -0
  28. package/dist/extensions/{ieee → citations/ieee}/segment-templates.d.ts +0 -1
  29. package/dist/extensions/citations/ieee/segment-templates.d.ts.map +1 -0
  30. package/dist/extensions/{ieee → citations/ieee}/segment-templates.js +0 -27
  31. package/dist/extensions/citations/ieee/segment-templates.js.map +1 -0
  32. package/dist/extensions/citations/unparsed/index.d.ts +2 -0
  33. package/dist/extensions/citations/unparsed/index.d.ts.map +1 -0
  34. package/dist/extensions/citations/unparsed/index.js +2 -0
  35. package/dist/extensions/citations/unparsed/index.js.map +1 -0
  36. package/dist/extensions/citations/unparsed/unparsed-citation.d.ts +11 -0
  37. package/dist/extensions/citations/unparsed/unparsed-citation.d.ts.map +1 -0
  38. package/dist/extensions/citations/unparsed/unparsed-citation.js +22 -0
  39. package/dist/extensions/citations/unparsed/unparsed-citation.js.map +1 -0
  40. package/dist/extensions/openai/errors.d.ts +8 -0
  41. package/dist/extensions/openai/errors.d.ts.map +1 -1
  42. package/dist/extensions/openai/errors.js +58 -0
  43. package/dist/extensions/openai/errors.js.map +1 -1
  44. package/dist/extensions/openai/index.d.ts +4 -2
  45. package/dist/extensions/openai/index.d.ts.map +1 -1
  46. package/dist/extensions/openai/index.js +2 -1
  47. package/dist/extensions/openai/index.js.map +1 -1
  48. package/dist/extensions/openai/openai-http.d.ts +30 -0
  49. package/dist/extensions/openai/openai-http.d.ts.map +1 -0
  50. package/dist/extensions/openai/openai-http.js +310 -0
  51. package/dist/extensions/openai/openai-http.js.map +1 -0
  52. package/dist/extensions/openai/openai-parsing.d.ts +34 -0
  53. package/dist/extensions/openai/openai-parsing.d.ts.map +1 -0
  54. package/dist/extensions/openai/openai-parsing.js +226 -0
  55. package/dist/extensions/openai/openai-parsing.js.map +1 -0
  56. package/dist/extensions/openai/openai-retrieval.d.ts +150 -0
  57. package/dist/extensions/openai/openai-retrieval.d.ts.map +1 -0
  58. package/dist/extensions/openai/openai-retrieval.js +248 -0
  59. package/dist/extensions/openai/openai-retrieval.js.map +1 -0
  60. package/dist/extensions/openai/openai-tools.d.ts +9 -0
  61. package/dist/extensions/openai/openai-tools.d.ts.map +1 -0
  62. package/dist/extensions/openai/openai-tools.js +93 -0
  63. package/dist/extensions/openai/openai-tools.js.map +1 -0
  64. package/dist/extensions/openai/provider.d.ts +1 -100
  65. package/dist/extensions/openai/provider.d.ts.map +1 -1
  66. package/dist/extensions/openai/provider.js +5 -794
  67. package/dist/extensions/openai/provider.js.map +1 -1
  68. package/dist/extensions/openai/types.d.ts +1 -0
  69. package/dist/extensions/openai/types.d.ts.map +1 -1
  70. package/dist/extensions/openai/types.js +4 -1
  71. package/dist/extensions/openai/types.js.map +1 -1
  72. package/dist/lib/core/premise-engine.d.ts +15 -0
  73. package/dist/lib/core/premise-engine.d.ts.map +1 -1
  74. package/dist/lib/core/premise-engine.js +62 -127
  75. package/dist/lib/core/premise-engine.js.map +1 -1
  76. package/dist/lib/index.d.ts +3 -3
  77. package/dist/lib/index.d.ts.map +1 -1
  78. package/dist/lib/index.js +1 -1
  79. package/dist/lib/index.js.map +1 -1
  80. package/dist/lib/llm/index.d.ts +1 -1
  81. package/dist/lib/llm/index.d.ts.map +1 -1
  82. package/dist/lib/llm/types.d.ts +40 -0
  83. package/dist/lib/llm/types.d.ts.map +1 -1
  84. package/dist/lib/parsing/prompt-builder.d.ts.map +1 -1
  85. package/dist/lib/parsing/prompt-builder.js +14 -3
  86. package/dist/lib/parsing/prompt-builder.js.map +1 -1
  87. package/dist/lib/pipelines/execute.d.ts +175 -3
  88. package/dist/lib/pipelines/execute.d.ts.map +1 -1
  89. package/dist/lib/pipelines/execute.js +574 -219
  90. package/dist/lib/pipelines/execute.js.map +1 -1
  91. package/dist/lib/pipelines/index.d.ts +3 -3
  92. package/dist/lib/pipelines/index.d.ts.map +1 -1
  93. package/dist/lib/pipelines/index.js +2 -2
  94. package/dist/lib/pipelines/index.js.map +1 -1
  95. package/dist/lib/pipelines/stage-helpers.d.ts +89 -1
  96. package/dist/lib/pipelines/stage-helpers.d.ts.map +1 -1
  97. package/dist/lib/pipelines/stage-helpers.js +225 -31
  98. package/dist/lib/pipelines/stage-helpers.js.map +1 -1
  99. package/package.json +10 -5
  100. package/dist/extensions/ieee/citation-claim.js.map +0 -1
  101. package/dist/extensions/ieee/formatting.d.ts.map +0 -1
  102. package/dist/extensions/ieee/formatting.js.map +0 -1
  103. package/dist/extensions/ieee/index.d.ts.map +0 -1
  104. package/dist/extensions/ieee/index.js.map +0 -1
  105. package/dist/extensions/ieee/references.d.ts.map +0 -1
  106. package/dist/extensions/ieee/references.js.map +0 -1
  107. package/dist/extensions/ieee/relaxed.d.ts.map +0 -1
  108. package/dist/extensions/ieee/segment-builder.d.ts.map +0 -1
  109. package/dist/extensions/ieee/segment-builder.js.map +0 -1
  110. package/dist/extensions/ieee/segment-templates.d.ts.map +0 -1
  111. package/dist/extensions/ieee/segment-templates.js.map +0 -1
  112. /package/dist/extensions/{ieee → citations/ieee}/formatting.d.ts +0 -0
  113. /package/dist/extensions/{ieee → citations/ieee}/index.d.ts +0 -0
  114. /package/dist/extensions/{ieee → citations/ieee}/index.js +0 -0
  115. /package/dist/extensions/{ieee → citations/ieee}/segment-builder.d.ts +0 -0
  116. /package/dist/extensions/{ieee → citations/ieee}/segment-builder.js +0 -0
@@ -0,0 +1,310 @@
1
+ // HTTP transport for the OpenAI Responses API: the single-call POST,
2
+ // the foreground/background dispatch, the submit-then-poll background
3
+ // loop, and the background-stream create. These functions own the wire
4
+ // interaction (fetch, headers, abort handling, status classification);
5
+ // they return the parsed `TOpenAiResponsesEnvelope` and leave the
6
+ // retrieval-shaped projection to the layer above.
7
+ //
8
+ // Layering: this module sits above `openai-parsing.ts` (it consumes
9
+ // `readSseEnvelope` / `isAbortError`) and `errors.ts` (error classes +
10
+ // `classifyHttpError`); nothing here imports from `provider.ts` or
11
+ // `openai-retrieval.ts`, keeping the dependency graph acyclic.
12
+ import { isAbortError, readSseEnvelope } from "./openai-parsing.js";
13
+ import { classifyHttpError, ResponseNotFoundError, TransientLlmError, } from "./errors.js";
14
+ export function resolveFetch(injected, fnName) {
15
+ const fetchImpl = injected ?? globalThis.fetch;
16
+ if (!fetchImpl) {
17
+ throw new Error(`${fnName}: no fetch implementation available. Pass \`fetch\` explicitly or run in an environment that provides \`globalThis.fetch\` (Node ≥18, modern browsers, Expo).`);
18
+ }
19
+ return fetchImpl;
20
+ }
21
+ // -- HTTP --
22
+ export async function fetchResponseEnvelope(args) {
23
+ if (args.backgroundStream) {
24
+ return runBackgroundStream({
25
+ url: args.url,
26
+ apiKey: args.apiKey,
27
+ body: args.body,
28
+ fetchImpl: args.fetchImpl,
29
+ signal: args.signal,
30
+ onResponseId: args.onResponseId,
31
+ });
32
+ }
33
+ if (args.background) {
34
+ return runBackground({
35
+ url: args.url,
36
+ apiKey: args.apiKey,
37
+ body: args.body,
38
+ fetchImpl: args.fetchImpl,
39
+ signal: args.signal,
40
+ pollIntervalMs: args.pollIntervalMs,
41
+ });
42
+ }
43
+ if (args.stream) {
44
+ const response = await callOnce({
45
+ url: args.url,
46
+ apiKey: args.apiKey,
47
+ body: { ...args.body, stream: true },
48
+ fetchImpl: args.fetchImpl,
49
+ signal: args.signal,
50
+ });
51
+ return readSseEnvelope(response);
52
+ }
53
+ const response = await callOnce({
54
+ url: args.url,
55
+ apiKey: args.apiKey,
56
+ body: args.body,
57
+ fetchImpl: args.fetchImpl,
58
+ signal: args.signal,
59
+ });
60
+ return parseJsonOrThrowTransient(response, "OpenAI response body was not valid JSON");
61
+ }
62
+ export async function parseJsonOrThrowTransient(response, context) {
63
+ return response
64
+ .json()
65
+ .then((j) => j)
66
+ .catch((err) => {
67
+ throw new TransientLlmError({
68
+ message: `${context}: ${err instanceof Error ? err.message : String(err)}`,
69
+ });
70
+ });
71
+ }
72
+ function abortError() {
73
+ const e = new Error("The OpenAI background request was aborted.");
74
+ e.name = "AbortError";
75
+ return e;
76
+ }
77
+ // Resolves (never rejects) on abort by design: the poll loop owns the
78
+ // abort→cancel→throw decision at exactly two checkpoints (top-of-loop and
79
+ // the in-flight-GET catch), so this helper just needs to wake the loop
80
+ // promptly instead of waiting out the full interval. Keeping it
81
+ // non-throwing avoids a second, competing abort surface.
82
+ function abortableDelay(ms, signal) {
83
+ return new Promise((resolve) => {
84
+ if (signal?.aborted) {
85
+ resolve();
86
+ return;
87
+ }
88
+ const onAbort = () => {
89
+ cleanup();
90
+ resolve();
91
+ };
92
+ const cleanup = () => {
93
+ clearTimeout(timer);
94
+ signal?.removeEventListener("abort", onAbort);
95
+ };
96
+ const timer = setTimeout(() => {
97
+ cleanup();
98
+ resolve();
99
+ }, ms);
100
+ signal?.addEventListener("abort", onAbort, { once: true });
101
+ });
102
+ }
103
+ export async function getResponseById(args) {
104
+ let response;
105
+ try {
106
+ response = await args.fetchImpl(`${args.url}/${args.id}`, {
107
+ method: "GET",
108
+ headers: { Authorization: `Bearer ${args.apiKey}` },
109
+ signal: args.signal,
110
+ });
111
+ }
112
+ catch (err) {
113
+ if (isAbortError(err))
114
+ throw err;
115
+ throw new TransientLlmError({
116
+ message: `Network error polling OpenAI background response: ${err instanceof Error ? err.message : String(err)}`,
117
+ });
118
+ }
119
+ if (response.status === 404) {
120
+ // Response has aged out of the ~10-minute retention window or
121
+ // was never stored. Surface as a typed error so callers can
122
+ // clear the stored id and settle the stage as failed.
123
+ throw new ResponseNotFoundError({ responseId: args.id });
124
+ }
125
+ if (!response.ok) {
126
+ const errorBody = await response.text().catch(() => "");
127
+ throw classifyHttpError(response.status, `OpenAI poll ${response.status.toString()}: ${errorBody || response.statusText}`);
128
+ }
129
+ return parseJsonOrThrowTransient(response, "OpenAI poll body was not valid JSON");
130
+ }
131
+ // Intentionally takes no AbortSignal — cancel must fire even though the
132
+ // caller's signal has already aborted; passing the fired signal would
133
+ // abort the cancel itself.
134
+ async function cancelBackground(args) {
135
+ // Best-effort + idempotent — swallow errors; the abort is surfaced
136
+ // regardless of whether cancel succeeds.
137
+ try {
138
+ await args.fetchImpl(`${args.url}/${args.id}/cancel`, {
139
+ method: "POST",
140
+ headers: { Authorization: `Bearer ${args.apiKey}` },
141
+ });
142
+ }
143
+ catch {
144
+ // ignore
145
+ }
146
+ }
147
+ function isTerminalBackgroundStatus(status) {
148
+ return (status === "completed" ||
149
+ status === "failed" ||
150
+ status === "incomplete" ||
151
+ status === "cancelled");
152
+ }
153
+ /**
154
+ * Submit a single request with `{ background: true, stream: true,
155
+ * store: true }` and consume the resulting SSE stream live, returning
156
+ * the terminal envelope.
157
+ *
158
+ * A background response can only be streamed if it was *created* with
159
+ * `stream: true` (a background-without-stream response is poll-only and
160
+ * cannot later be streamed), so this mode uses one streaming create
161
+ * call rather than a separate non-streaming submit POST. The response
162
+ * id is therefore not in a JSON POST body — it arrives in the first
163
+ * `response.created` SSE event. `onResponseId` fires the moment that
164
+ * event is parsed (before the terminal event), so the caller can
165
+ * persist the id while the call is still in flight.
166
+ *
167
+ * The response keeps generating server-side even if the connection
168
+ * drops during stream consumption, and can be recovered via
169
+ * `retrieveResponse` within the ~10-minute retention window. A
170
+ * connection drop mid-stream (no terminal event before stream end) is
171
+ * classified as a `TransientLlmError` so the framework's retry policy
172
+ * applies — but because the id was already surfaced mid-flight, a
173
+ * crashed in-flight call can be recovered rather than blindly re-run.
174
+ */
175
+ async function runBackgroundStream(args) {
176
+ if (args.signal?.aborted)
177
+ throw abortError();
178
+ const httpResponse = await callOnce({
179
+ url: args.url,
180
+ apiKey: args.apiKey,
181
+ body: { ...args.body, background: true, stream: true, store: true },
182
+ fetchImpl: args.fetchImpl,
183
+ signal: args.signal,
184
+ });
185
+ return readSseEnvelope(httpResponse, args.onResponseId);
186
+ }
187
+ async function runBackground(args) {
188
+ if (args.signal?.aborted)
189
+ throw abortError();
190
+ const submit = await callOnce({
191
+ url: args.url,
192
+ apiKey: args.apiKey,
193
+ body: { ...args.body, background: true, store: true },
194
+ fetchImpl: args.fetchImpl,
195
+ signal: args.signal,
196
+ });
197
+ const submitEnvelope = await parseJsonOrThrowTransient(submit, "OpenAI background submit body was not valid JSON");
198
+ const id = submitEnvelope.id;
199
+ if (!id) {
200
+ throw new TransientLlmError({
201
+ message: "OpenAI background submit returned no response id.",
202
+ });
203
+ }
204
+ // Fast-path: a small/cached request can come back already terminal on
205
+ // submit — return it directly rather than issuing a redundant poll GET
206
+ // (and avoid a `store`-expiry window between submit and first poll).
207
+ if (isTerminalBackgroundStatus(submitEnvelope.status)) {
208
+ if (submitEnvelope.status === "cancelled")
209
+ throw abortError();
210
+ return submitEnvelope;
211
+ }
212
+ for (;;) {
213
+ if (args.signal?.aborted) {
214
+ await cancelBackground({
215
+ url: args.url,
216
+ id,
217
+ apiKey: args.apiKey,
218
+ fetchImpl: args.fetchImpl,
219
+ });
220
+ throw abortError();
221
+ }
222
+ let env;
223
+ try {
224
+ env = await getResponseById({
225
+ url: args.url,
226
+ id,
227
+ apiKey: args.apiKey,
228
+ fetchImpl: args.fetchImpl,
229
+ signal: args.signal,
230
+ });
231
+ }
232
+ catch (err) {
233
+ // Abort landing DURING an in-flight poll GET surfaces as an
234
+ // AbortError from getResponseById (real fetch rejects the
235
+ // in-flight request). The top-of-loop check alone would miss
236
+ // it, so cancel here before re-throwing — this is what makes
237
+ // the "AbortSignal → cancel POST" guarantee hold mid-poll.
238
+ if (isAbortError(err)) {
239
+ await cancelBackground({
240
+ url: args.url,
241
+ id,
242
+ apiKey: args.apiKey,
243
+ fetchImpl: args.fetchImpl,
244
+ });
245
+ throw abortError();
246
+ }
247
+ throw err;
248
+ }
249
+ const status = env.status;
250
+ // Deliberately inlined rather than calling
251
+ // `isTerminalBackgroundStatus`: `cancelled` is terminal too but
252
+ // needs the opposite disposition (throw `abortError()` vs. return
253
+ // the envelope), so the two terminal cases are split here.
254
+ if (status === "completed" ||
255
+ status === "failed" ||
256
+ status === "incomplete") {
257
+ return env;
258
+ }
259
+ if (status === "cancelled") {
260
+ throw abortError();
261
+ }
262
+ await abortableDelay(args.pollIntervalMs, args.signal);
263
+ }
264
+ }
265
+ export async function callOnce(args) {
266
+ let response;
267
+ try {
268
+ response = await args.fetchImpl(args.url, {
269
+ method: "POST",
270
+ headers: {
271
+ "Content-Type": "application/json",
272
+ Authorization: `Bearer ${args.apiKey}`,
273
+ },
274
+ body: JSON.stringify(args.body),
275
+ signal: args.signal,
276
+ });
277
+ }
278
+ catch (err) {
279
+ // Aborts come through as a thrown error with `name === "AbortError"`
280
+ // (DOM spec). Surface as-is so `llmStage`'s mid-flight-abort
281
+ // detector turns it into `StageAbortedError`. Other low-level
282
+ // fetch failures are transient.
283
+ if (isAbortError(err)) {
284
+ throw err;
285
+ }
286
+ throw new TransientLlmError({
287
+ message: `Network error calling OpenAI: ${err instanceof Error ? err.message : String(err)}`,
288
+ });
289
+ }
290
+ if (response.ok) {
291
+ return response;
292
+ }
293
+ const errorBody = await response.text().catch(() => "");
294
+ const message = `OpenAI Responses API ${response.status.toString()}: ${errorBody || response.statusText}`;
295
+ // Best-effort structured extraction of the provider error
296
+ // code/type so a 429 can be split into persistent quota exhaustion
297
+ // vs. transient throttling. Never throws on malformed bodies — an
298
+ // unparseable body leaves `providerErrorCode` undefined, which
299
+ // `classifyHttpError` treats as the safe transient default.
300
+ let providerErrorCode;
301
+ try {
302
+ const parsed = JSON.parse(errorBody);
303
+ providerErrorCode = parsed.error?.code ?? parsed.error?.type;
304
+ }
305
+ catch {
306
+ providerErrorCode = undefined;
307
+ }
308
+ throw classifyHttpError(response.status, message, providerErrorCode);
309
+ }
310
+ //# sourceMappingURL=openai-http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openai-http.js","sourceRoot":"","sources":["../../../src/extensions/openai/openai-http.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ import type { TLlmTokenUsage } from "../../lib/llm/types.js";
2
+ import type { TOpenAiOutputItem, TOpenAiResponsesEnvelope } from "./types.js";
3
+ /**
4
+ * Read an SSE `text/event-stream` body and return the envelope carried
5
+ * by the terminal event. A stream that ends with no terminal event
6
+ * (connection drop) throws `TransientLlmError` so the framework retries.
7
+ * `AbortError` from the underlying reader propagates verbatim so
8
+ * `llmStage` marks the stage `skipped`.
9
+ *
10
+ * `onResponseId`, when supplied, fires the moment the `response.created`
11
+ * lifecycle event is parsed — i.e. while the call is still streaming,
12
+ * before the terminal event arrives. This is the load-bearing seam for
13
+ * background-stream mode: it lets a caller persist the response id
14
+ * mid-flight so an in-flight call interrupted before completion can be
15
+ * recovered from the upstream's stored copy. Invoked at most once.
16
+ *
17
+ * Note: the event-separator scan assumes LF (`\n\n`) framing, which the
18
+ * OpenAI Responses API emits. Reuse against a strict CRLF-only SSE
19
+ * server would need the separator scan adjusted to `\r\n\r\n`.
20
+ */
21
+ export declare function readSseEnvelope(response: Response, onResponseId?: (responseId: string) => void): Promise<TOpenAiResponsesEnvelope>;
22
+ type TParsedFunctionCall = {
23
+ callId: string;
24
+ name: string;
25
+ arguments: string;
26
+ };
27
+ export declare function pickFunctionCalls(output: TOpenAiOutputItem[] | undefined): TParsedFunctionCall[];
28
+ export declare function extractAssistantText(output: TOpenAiOutputItem[] | undefined): string | undefined;
29
+ export declare function safeParseJson(raw: string): unknown;
30
+ export declare function extractUsage(envelope: TOpenAiResponsesEnvelope): TLlmTokenUsage;
31
+ export declare function mergeUsage(accumulated: TLlmTokenUsage, next: TLlmTokenUsage): TLlmTokenUsage;
32
+ export declare function isAbortError(err: unknown): boolean;
33
+ export {};
34
+ //# sourceMappingURL=openai-parsing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openai-parsing.d.ts","sourceRoot":"","sources":["../../../src/extensions/openai/openai-parsing.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AAE5D,OAAO,KAAK,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAA;AAoE7E;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,eAAe,CACjC,QAAQ,EAAE,QAAQ,EAClB,YAAY,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,GAC5C,OAAO,CAAC,wBAAwB,CAAC,CAkEnC;AAED,KAAK,mBAAmB,GAAG;IACvB,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;CACpB,CAAA;AAED,wBAAgB,iBAAiB,CAC7B,MAAM,EAAE,iBAAiB,EAAE,GAAG,SAAS,GACxC,mBAAmB,EAAE,CAoBvB;AAED,wBAAgB,oBAAoB,CAChC,MAAM,EAAE,iBAAiB,EAAE,GAAG,SAAS,GACxC,MAAM,GAAG,SAAS,CAiBpB;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAUlD;AAED,wBAAgB,YAAY,CACxB,QAAQ,EAAE,wBAAwB,GACnC,cAAc,CAYhB;AAED,wBAAgB,UAAU,CACtB,WAAW,EAAE,cAAc,EAC3B,IAAI,EAAE,cAAc,GACrB,cAAc,CAShB;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAMlD"}
@@ -0,0 +1,226 @@
1
+ // SSE stream reading and Responses-API envelope/output parsing for the
2
+ // OpenAI provider. These are pure, transport-agnostic helpers: given an
3
+ // HTTP `Response` (for streaming) or a parsed envelope, they extract the
4
+ // terminal envelope, assistant text, function calls, and token usage.
5
+ //
6
+ // `isAbortError` lives here as the lowest-level shared abort predicate —
7
+ // `readSseEnvelope` needs it, and the HTTP and retrieval layers (which
8
+ // sit above this module) import it down.
9
+ import { SchemaValidationLlmError, TransientLlmError } from "./errors.js";
10
+ const SSE_TERMINAL_EVENTS = new Set([
11
+ "response.completed",
12
+ "response.incomplete",
13
+ "response.failed",
14
+ ]);
15
+ // The lifecycle event the Responses API emits first, before any output
16
+ // chunks. It carries the response object — including `.id` — so a
17
+ // streaming consumer learns the id while the call is still in flight.
18
+ const SSE_CREATED_EVENT = "response.created";
19
+ /**
20
+ * Parse one SSE event block. Returns:
21
+ *
22
+ * * `{ kind: "terminal", envelope }` for a terminal Responses-API
23
+ * event (`response.completed` / `.incomplete` / `.failed`),
24
+ * carrying the embedded full `response` envelope;
25
+ * * `{ kind: "created", responseId }` for the lifecycle
26
+ * `response.created` event, surfacing the response id the moment
27
+ * it is known (before any output);
28
+ * * `undefined` for every intermediate / unrecognized event.
29
+ *
30
+ * The events carry a `type` field inside the data JSON, so we key off
31
+ * that and fall back to the SSE `event:` line.
32
+ */
33
+ function parseSseEvent(raw) {
34
+ let eventType;
35
+ const dataLines = [];
36
+ for (const line of raw.split("\n")) {
37
+ if (line.startsWith(":"))
38
+ continue; // SSE comment line — ignore.
39
+ if (line.startsWith("event:")) {
40
+ eventType = line.slice(6).trim();
41
+ }
42
+ else if (line.startsWith("data:")) {
43
+ dataLines.push(line.slice(5).replace(/^ /, ""));
44
+ }
45
+ }
46
+ if (dataLines.length === 0)
47
+ return undefined;
48
+ let parsed;
49
+ try {
50
+ parsed = JSON.parse(dataLines.join("\n"));
51
+ }
52
+ catch {
53
+ return undefined;
54
+ }
55
+ // Prefer the discriminator inside the data JSON (Responses-API
56
+ // events carry `type` there); fall back to the SSE `event:` line so
57
+ // the parser is robust if the API ever sends the type only there.
58
+ // (No `[DONE]` sentinel handling — that is a Chat-Completions frame
59
+ // the Responses API does not emit.)
60
+ const type = parsed.type ?? eventType;
61
+ if (type && SSE_TERMINAL_EVENTS.has(type) && parsed.response) {
62
+ return { kind: "terminal", envelope: parsed.response };
63
+ }
64
+ if (type === SSE_CREATED_EVENT && parsed.response?.id) {
65
+ return { kind: "created", responseId: parsed.response.id };
66
+ }
67
+ return undefined;
68
+ }
69
+ /**
70
+ * Read an SSE `text/event-stream` body and return the envelope carried
71
+ * by the terminal event. A stream that ends with no terminal event
72
+ * (connection drop) throws `TransientLlmError` so the framework retries.
73
+ * `AbortError` from the underlying reader propagates verbatim so
74
+ * `llmStage` marks the stage `skipped`.
75
+ *
76
+ * `onResponseId`, when supplied, fires the moment the `response.created`
77
+ * lifecycle event is parsed — i.e. while the call is still streaming,
78
+ * before the terminal event arrives. This is the load-bearing seam for
79
+ * background-stream mode: it lets a caller persist the response id
80
+ * mid-flight so an in-flight call interrupted before completion can be
81
+ * recovered from the upstream's stored copy. Invoked at most once.
82
+ *
83
+ * Note: the event-separator scan assumes LF (`\n\n`) framing, which the
84
+ * OpenAI Responses API emits. Reuse against a strict CRLF-only SSE
85
+ * server would need the separator scan adjusted to `\r\n\r\n`.
86
+ */
87
+ export async function readSseEnvelope(response, onResponseId) {
88
+ const body = response.body;
89
+ if (!body) {
90
+ throw new TransientLlmError({
91
+ message: "OpenAI streaming response carried no body.",
92
+ });
93
+ }
94
+ const reader = body.getReader();
95
+ const decoder = new TextDecoder();
96
+ let buffer = "";
97
+ let terminal;
98
+ let idSurfaced = false;
99
+ const handleEvent = (rawEvent) => {
100
+ const parsedEvent = parseSseEvent(rawEvent);
101
+ if (!parsedEvent)
102
+ return;
103
+ if (parsedEvent.kind === "terminal") {
104
+ terminal = parsedEvent.envelope;
105
+ return;
106
+ }
107
+ // kind === "created": surface the id once, the moment it's known.
108
+ if (!idSurfaced) {
109
+ idSurfaced = true;
110
+ onResponseId?.(parsedEvent.responseId);
111
+ }
112
+ };
113
+ try {
114
+ for (;;) {
115
+ const chunk = await reader.read();
116
+ if (chunk.done)
117
+ break;
118
+ buffer += decoder.decode(chunk.value, { stream: true });
119
+ let sep = buffer.indexOf("\n\n");
120
+ while (sep !== -1) {
121
+ const rawEvent = buffer.slice(0, sep);
122
+ buffer = buffer.slice(sep + 2);
123
+ handleEvent(rawEvent);
124
+ sep = buffer.indexOf("\n\n");
125
+ }
126
+ }
127
+ }
128
+ catch (err) {
129
+ if (isAbortError(err)) {
130
+ throw err;
131
+ }
132
+ throw new TransientLlmError({
133
+ message: `OpenAI streaming read failed: ${err instanceof Error ? err.message : String(err)}`,
134
+ });
135
+ }
136
+ // Flush any bytes the streaming decoder is still holding (an
137
+ // incomplete multi-byte UTF-8 sequence at the final chunk boundary),
138
+ // then scan for any remaining terminal frame.
139
+ buffer += decoder.decode();
140
+ let tailSep = buffer.indexOf("\n\n");
141
+ while (tailSep !== -1) {
142
+ const rawEvent = buffer.slice(0, tailSep);
143
+ buffer = buffer.slice(tailSep + 2);
144
+ handleEvent(rawEvent);
145
+ tailSep = buffer.indexOf("\n\n");
146
+ }
147
+ if (!terminal) {
148
+ throw new TransientLlmError({
149
+ message: "OpenAI streaming ended without a terminal response event (connection drop?).",
150
+ });
151
+ }
152
+ return terminal;
153
+ }
154
+ export function pickFunctionCalls(output) {
155
+ if (!output)
156
+ return [];
157
+ const calls = [];
158
+ for (const item of output) {
159
+ if (item.type === "function_call") {
160
+ /* eslint-disable @typescript-eslint/naming-convention */
161
+ const fc = item;
162
+ /* eslint-enable @typescript-eslint/naming-convention */
163
+ calls.push({
164
+ callId: fc.call_id,
165
+ name: fc.name,
166
+ arguments: fc.arguments,
167
+ });
168
+ }
169
+ }
170
+ return calls;
171
+ }
172
+ export function extractAssistantText(output) {
173
+ if (!output)
174
+ return undefined;
175
+ for (const item of output) {
176
+ if (item.type === "message") {
177
+ const msg = item;
178
+ const blocks = msg.content ?? [];
179
+ const textBlock = blocks.find((b) => b.type === "output_text" || b.type === "text");
180
+ if (textBlock?.text !== undefined) {
181
+ return textBlock.text;
182
+ }
183
+ }
184
+ }
185
+ return undefined;
186
+ }
187
+ export function safeParseJson(raw) {
188
+ try {
189
+ return JSON.parse(raw);
190
+ }
191
+ catch (err) {
192
+ throw new SchemaValidationLlmError({
193
+ message: `OpenAI returned malformed JSON in structured-output text: ${err instanceof Error ? err.message : String(err)}`,
194
+ });
195
+ }
196
+ }
197
+ export function extractUsage(envelope) {
198
+ const usage = envelope.usage;
199
+ if (!usage)
200
+ return { input: 0, output: 0 };
201
+ const result = {
202
+ input: usage.input_tokens ?? 0,
203
+ output: usage.output_tokens ?? 0,
204
+ };
205
+ const reasoning = usage.output_tokens_details?.reasoning_tokens;
206
+ if (reasoning !== undefined) {
207
+ result.reasoning = reasoning;
208
+ }
209
+ return result;
210
+ }
211
+ export function mergeUsage(accumulated, next) {
212
+ const merged = {
213
+ input: accumulated.input + next.input,
214
+ output: accumulated.output + next.output,
215
+ };
216
+ if (accumulated.reasoning !== undefined || next.reasoning !== undefined) {
217
+ merged.reasoning = (accumulated.reasoning ?? 0) + (next.reasoning ?? 0);
218
+ }
219
+ return merged;
220
+ }
221
+ export function isAbortError(err) {
222
+ return (typeof err === "object" &&
223
+ err !== null &&
224
+ err.name === "AbortError");
225
+ }
226
+ //# sourceMappingURL=openai-parsing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openai-parsing.js","sourceRoot":"","sources":["../../../src/extensions/openai/openai-parsing.ts"],"names":[],"mappings":"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"}