@adminforth/completion-adapter-openai-responses 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.ts CHANGED
@@ -1,289 +1,35 @@
1
1
  import type { AdapterOptions } from "./types.js";
2
2
  import type {
3
3
  CompletionAdapter,
4
- CompletionStreamEvent,
5
4
  CompletionTool,
6
5
  } from "adminforth";
7
- import { AIMessage } from "@langchain/core/messages";
8
- import { ChatOpenAI } from "@langchain/openai";
9
- import { createMiddleware } from "langchain";
10
6
  import { encoding_for_model, type TiktokenModel } from "tiktoken";
11
- import type OpenAI from "openai";
7
+ import {
8
+ OpenAIResponsesService,
9
+ type CompletionRequestInput,
10
+ type CompletionResult,
11
+ type ReasoningEffort,
12
+ type StreamChunkCallback,
13
+ } from "./openai.js";
14
+ import {
15
+ createLangChainAgentSpec,
16
+ type AgentModelPurpose,
17
+ } from "./langchain.js";
12
18
 
13
19
  export type { AdapterOptions } from "./types.js";
14
20
 
15
- type StreamChunkCallback = (
16
- chunk: string,
17
- event?: CompletionStreamEvent,
18
- ) => void | Promise<void>;
19
-
20
- type ReasoningEffort =
21
- | "none"
22
- | "minimal"
23
- | "low"
24
- | "medium"
25
- | "high"
26
- | "xhigh";
27
-
28
- type AgentModelPurpose = "primary" | "summary";
29
-
30
- type CompletionRequestInput = {
31
- content: string;
32
- maxTokens?: number;
33
- outputSchema?: any;
34
- reasoningEffort?: ReasoningEffort;
35
- tools?: CompletionTool[];
36
- onChunk?: StreamChunkCallback;
37
- signal?: AbortSignal;
38
- };
39
-
40
- type ResponseCreateBody = OpenAI.Responses.ResponseCreateParams;
41
- type OpenAIResponsesSuccess = OpenAI.Responses.Response;
42
- type OpenAIErrorResponse = {
43
- error?: {
44
- message?: string;
45
- type?: string;
46
- param?: string | null;
47
- code?: string | null;
48
- };
49
- };
50
- type OpenAITool = OpenAI.Responses.Tool;
51
- type OpenAIFunctionCall = Extract<
52
- OpenAI.Responses.ResponseOutputItem,
53
- { type: "function_call" }
54
- >;
55
-
56
- type OpenAiResponsesMetadata = {
57
- id?: string;
58
- };
59
-
60
- type OpenAiResponsesContext = {
61
- sessionId: string;
62
- turnId: string;
63
- abortSignal?: AbortSignal;
64
- };
65
-
66
- type UsedTokens = {
67
- input_uncached: number;
68
- input_cached: number;
69
- output: number;
70
- };
71
-
72
- type CompletionResult = {
73
- content?: string;
74
- finishReason?: string;
75
- error?: string;
76
- used_tokens?: UsedTokens;
77
- };
78
-
79
- const DEFAULT_OPENAI_BASE_URL = "https://api.openai.com/v1";
80
- const RAW_REQUEST_LOG_PREFIX = "[CompletionAdapterOpenAIResponses] Raw /responses request";
81
-
82
- type FetchInput = Parameters<typeof fetch>[0];
83
- type FetchInit = Parameters<typeof fetch>[1];
84
-
85
- function extractOutputText(data: OpenAIResponsesSuccess): string {
86
- let text = "";
87
-
88
- for (const item of data.output ?? []) {
89
- if (item.type !== "message" || !Array.isArray(item.content)) continue;
90
- for (const part of item.content) {
91
- if (part.type === "output_text" && typeof part.text === "string") {
92
- text += part.text;
93
- }
94
- }
95
- }
96
-
97
- return text;
98
- }
99
-
100
- function extractReasoning(data: OpenAIResponsesSuccess): string | undefined {
101
- let reasoning = "";
102
-
103
- for (const item of data.output ?? []) {
104
- if (item.type !== "reasoning") continue;
105
-
106
- for (const part of item.summary ?? []) {
107
- if (part?.type === "summary_text" && typeof part.text === "string") {
108
- reasoning += part.text;
109
- }
110
- }
111
-
112
- if (!reasoning) {
113
- for (const part of item.content ?? []) {
114
- if (part?.type === "reasoning_text" && typeof part.text === "string") {
115
- reasoning += part.text;
116
- }
117
- }
118
- }
119
- }
120
-
121
- return reasoning || undefined;
122
- }
123
-
124
- function extractFunctionCall(
125
- data: OpenAIResponsesSuccess,
126
- ): OpenAIFunctionCall | undefined {
127
- for (const item of data.output ?? []) {
128
- if (item.type === "function_call") {
129
- return item;
130
- }
131
- }
132
-
133
- return undefined;
134
- }
135
-
136
- function extractUsedTokens(data: OpenAIResponsesSuccess): UsedTokens | undefined {
137
- const usage = data.usage;
138
- if (!usage) {
139
- return undefined;
140
- }
141
-
142
- const inputCached = usage.input_tokens_details?.cached_tokens ?? 0;
143
-
144
- return {
145
- input_uncached: Math.max(usage.input_tokens - inputCached, 0),
146
- input_cached: inputCached,
147
- output: usage.output_tokens,
148
- };
149
- }
150
-
151
- async function executeToolCall(
152
- toolCall: OpenAIFunctionCall,
153
- tools?: CompletionTool[],
154
- ): Promise<string> {
155
- const tool = tools?.find((candidate) => candidate.name === toolCall.name);
156
- if (!tool) {
157
- throw new Error(`Tool "${toolCall.name}" not found`);
158
- }
159
-
160
- const toolResult = await tool.handler(JSON.parse(toolCall.arguments));
161
- if (typeof toolResult === "string") return toolResult;
162
- if (typeof toolResult === "undefined") return "";
163
- return JSON.stringify(toolResult);
164
- }
165
-
166
- function parseSseBlock(block: string) {
167
- let event: string | undefined;
168
- let data = "";
169
-
170
- for (const rawLine of block.split("\n")) {
171
- const line = rawLine.trimEnd();
172
- if (!line) continue;
173
- if (line.startsWith("event:")) event = line.slice(6).trim();
174
- if (line.startsWith("data:")) data += line.slice(5).trim();
175
- }
176
-
177
- return data ? { event, data } : null;
178
- }
179
-
180
- function getAgentReasoningEffort(
181
- purpose: AgentModelPurpose,
182
- ): Exclude<ReasoningEffort, "none"> {
183
- return purpose === "summary" ? "minimal" : "low";
184
- }
185
-
186
- function buildReasoningConfig(params: {
187
- reasoning?: Record<string, unknown>;
188
- effort: Exclude<ReasoningEffort, "none"> | ReasoningEffort;
189
- }) {
190
- return {
191
- summary: "auto",
192
- effort: params.effort,
193
- ...(params.reasoning ?? {}),
194
- };
195
- }
196
-
197
- function getTurnKey(context: OpenAiResponsesContext) {
198
- return `${context.sessionId}:${context.turnId}`;
199
- }
200
-
201
- function getResponseId(message: AIMessage) {
202
- const metadata = message.response_metadata as OpenAiResponsesMetadata | undefined;
203
- return metadata?.id ?? null;
204
- }
205
-
206
- function getPreviousResponseId(modelSettings?: Record<string, unknown>) {
207
- return (modelSettings as { previous_response_id?: string } | undefined)
208
- ?.previous_response_id;
209
- }
210
-
211
- function getContinuationMessages<T extends { response_metadata?: unknown }>(
212
- messages: T[],
213
- previousResponseId: string,
214
- ) {
215
- let continuationStartIndex: number | null = null;
216
-
217
- for (let index = messages.length - 1; index >= 0; index -= 1) {
218
- const message = messages[index];
219
-
220
- if (
221
- AIMessage.isInstance(message) &&
222
- (message.response_metadata as OpenAiResponsesMetadata | undefined)?.id ===
223
- previousResponseId
224
- ) {
225
- continuationStartIndex = index + 1;
226
- break;
227
- }
228
- }
229
-
230
- if (continuationStartIndex === null) {
231
- return null;
232
- }
233
-
234
- return messages.slice(continuationStartIndex);
235
- }
236
-
237
- function createOpenAiResponsesContinuationMiddleware() {
238
- const responseIdsByTurn = new Map<string, string>();
239
-
240
- return createMiddleware({
241
- name: "OpenAiResponsesContinuationMiddleware",
242
- async wrapModelCall(request, handler) {
243
- const context = request.runtime.context as OpenAiResponsesContext;
244
- const turnKey = getTurnKey(context);
245
- const previousResponseId =
246
- getPreviousResponseId(request.modelSettings) ??
247
- responseIdsByTurn.get(turnKey);
248
- const continuationMessages = previousResponseId
249
- ? getContinuationMessages(request.messages, previousResponseId)
250
- : null;
251
-
252
- const response = (await handler(
253
- previousResponseId && continuationMessages
254
- ? {
255
- ...request,
256
- messages: continuationMessages,
257
- modelSettings: {
258
- ...request.modelSettings,
259
- previous_response_id: previousResponseId,
260
- },
261
- }
262
- : request,
263
- )) as AIMessage;
264
-
265
- const responseId = getResponseId(response);
266
-
267
- if (responseId) {
268
- responseIdsByTurn.set(turnKey, responseId);
269
- } else {
270
- responseIdsByTurn.delete(turnKey);
271
- }
272
-
273
- return response;
274
- },
275
- });
276
- }
277
-
278
- export default class CompletionAdapterOpenAIResponses
21
+ class CompletionAdapterOpenAIResponses
279
22
  implements CompletionAdapter
280
23
  {
281
24
  options: AdapterOptions;
282
25
  private encoding: ReturnType<typeof encoding_for_model>;
283
26
  private activeAbortController: AbortController | null = null;
27
+ private openAi: OpenAIResponsesService;
284
28
 
285
29
  constructor(options: AdapterOptions) {
286
30
  this.options = options;
31
+ this.openAi = new OpenAIResponsesService(options);
32
+
287
33
  try {
288
34
  this.encoding = encoding_for_model(
289
35
  (this.options.model || "gpt-5-nano") as TiktokenModel,
@@ -326,119 +72,18 @@ export default class CompletionAdapterOpenAIResponses
326
72
  return Boolean(this.getConfiguredBaseUrl());
327
73
  }
328
74
 
329
- private shouldDumpRawRequest() {
330
- return this.options.dumpRawRequest === true;
331
- }
332
-
333
- private getClientConfiguration() {
334
- const configuredBaseUrl = this.getConfiguredBaseUrl();
335
- const debugFetch = this.shouldDumpRawRequest()
336
- ? this.createResponsesDebugFetch()
337
- : undefined;
338
-
339
- if (!configuredBaseUrl && !debugFetch) {
340
- return undefined;
341
- }
342
-
343
- return {
344
- ...(configuredBaseUrl ? { baseURL: configuredBaseUrl } : {}),
345
- ...(debugFetch ? { fetch: debugFetch } : {}),
346
- };
347
- }
348
-
349
- private createResponsesDebugFetch() {
350
- return async (input: FetchInput, init?: FetchInit) => {
351
- const url = this.getFetchUrl(input);
352
-
353
- if (this.isResponsesUrl(url) && typeof init?.body === "string") {
354
- this.dumpRawRequest(url, init.body);
355
- }
356
-
357
- return fetch(input, init);
358
- };
359
- }
360
-
361
- private getFetchUrl(input: FetchInput) {
362
- if (typeof input === "string") {
363
- return input;
364
- }
365
-
366
- if (input instanceof URL) {
367
- return input.toString();
368
- }
369
-
370
- return input.url;
371
- }
372
-
373
- private isResponsesUrl(url: string) {
374
- try {
375
- return new URL(url).pathname.endsWith("/responses");
376
- } catch {
377
- return url.endsWith("/responses") || url.includes("/responses?");
378
- }
379
- }
380
-
381
- private dumpRawRequest(url: string, body: string) {
382
- console.info(`${RAW_REQUEST_LOG_PREFIX} ${url}`);
383
- try {
384
- console.info(JSON.stringify(JSON.parse(body), null, 2));
385
- } catch {
386
- console.info(body);
387
- }
388
- }
389
-
390
- private getResponsesUrl() {
391
- const baseUrl = this.getConfiguredBaseUrl() || DEFAULT_OPENAI_BASE_URL;
392
- const normalizedBaseUrl = baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
393
-
394
- return new URL("responses", normalizedBaseUrl).toString();
395
- }
396
-
397
75
  getLangChainAgentSpec(params: {
398
76
  maxTokens: number;
399
77
  purpose: AgentModelPurpose;
400
78
  }) {
401
- const extraRequestBodyParameters =
402
- (this.options.extraRequestBodyParameters || {}) as Record<string, unknown> & {
403
- reasoning?: Record<string, unknown>;
404
- text?: Record<string, unknown>;
405
- };
406
- const { reasoning, ...modelKwargs } = extraRequestBodyParameters;
407
- const configuredBaseUrl = this.getConfiguredBaseUrl();
408
- const normalizedModelKwargs = { ...modelKwargs };
409
-
410
-
411
- const clientConfiguration = this.getClientConfiguration();
412
- const useComplitionApi = this.shouldUseComplitionApi();
413
- const chatOpenAiOptions: Record<string, unknown> = {
414
- model: this.options.model || "gpt-5-nano",
415
- apiKey: this.options.openAiApiKey,
79
+ return createLangChainAgentSpec({
80
+ options: this.options,
416
81
  maxTokens: params.maxTokens,
417
- reasoning: buildReasoningConfig({
418
- reasoning,
419
- effort: getAgentReasoningEffort(params.purpose),
420
- }),
421
- modelKwargs: normalizedModelKwargs,
422
- };
423
-
424
- chatOpenAiOptions.useResponsesApi = !useComplitionApi;
425
-
426
- let supportsResponseContinuation = true;
427
- if (configuredBaseUrl || useComplitionApi) {
428
- supportsResponseContinuation = false;
429
- }
430
-
431
- if (clientConfiguration) {
432
- chatOpenAiOptions.configuration = clientConfiguration;
433
- }
434
-
435
- return {
436
- model: new ChatOpenAI(chatOpenAiOptions as any),
437
- middleware:
438
- params.purpose === "primary" && supportsResponseContinuation
439
- ? [createOpenAiResponsesContinuationMiddleware()]
440
- : [],
441
- };
82
+ purpose: params.purpose,
83
+ configuredBaseUrl: this.getConfiguredBaseUrl(),
84
+ clientConfiguration: this.openAi.getClientConfiguration(),
85
+ useComplitionApi: this.shouldUseComplitionApi(),
86
+ });
442
87
  }
443
88
 
444
89
  complete = async (
@@ -463,66 +108,7 @@ export default class CompletionAdapterOpenAIResponses
463
108
  : onChunk,
464
109
  }
465
110
  : requestOrContent;
466
- const {
467
- content,
468
- maxTokens: requestMaxTokens = 50,
469
- outputSchema: requestOutputSchema,
470
- reasoningEffort: requestReasoningEffort = "low",
471
- tools,
472
- onChunk: streamChunkCallback,
473
- signal: requestSignal,
474
- } = request;
475
- const model = this.options.model || "gpt-5-nano";
476
- const isStreaming = typeof streamChunkCallback === "function";
477
- const extra =
478
- this.options.extraRequestBodyParameters as
479
- | (Record<string, unknown> & { reasoning?: Record<string, unknown> })
480
- | undefined;
481
- const { reasoning: extraReasoning, ...extraWithoutReasoning } = extra ?? {};
482
- let openAiTools: OpenAITool[] | undefined = undefined;
483
- if (tools && tools.length > 0) {
484
- openAiTools = tools.map((tool) => ({
485
- type: "function",
486
- name: tool.name,
487
- description: tool.description,
488
- parameters: tool.input_schema,
489
- strict: false,
490
- }));
491
- }
492
-
493
- const body = {
494
- model,
495
- input: content,
496
- max_output_tokens: requestMaxTokens,
497
- stream: isStreaming,
498
- text: requestOutputSchema
499
- ? {
500
- format: {
501
- type: "json_schema",
502
- ...requestOutputSchema,
503
- },
504
- }
505
- : {
506
- format: {
507
- type: "text",
508
- },
509
- },
510
- reasoning: {
511
- ...buildReasoningConfig({
512
- reasoning: extraReasoning,
513
- effort: requestReasoningEffort,
514
- }),
515
- },
516
- tools: openAiTools,
517
- ...extraWithoutReasoning,
518
- } as ResponseCreateBody;
519
-
520
- const serializedBody = JSON.stringify(body);
521
-
522
- if (this.shouldDumpRawRequest()) {
523
- this.dumpRawRequest(this.getResponsesUrl(), serializedBody);
524
- }
525
-
111
+ const { signal: requestSignal } = request;
526
112
  const abortController = new AbortController();
527
113
  this.activeAbortController = abortController;
528
114
  const abortFromRequestSignal = () => abortController.abort(requestSignal?.reason);
@@ -533,286 +119,16 @@ export default class CompletionAdapterOpenAIResponses
533
119
  requestSignal?.addEventListener("abort", abortFromRequestSignal, { once: true });
534
120
  }
535
121
 
536
- let resp: Response | null = null;
537
122
  try {
538
- resp = await fetch(this.getResponsesUrl(), {
539
- method: "POST",
540
- headers: {
541
- "Content-Type": "application/json",
542
- Authorization: `Bearer ${this.options.openAiApiKey}`,
543
- },
544
- body: serializedBody,
545
- signal: abortController.signal,
546
- });
547
- } catch (error: any) {
548
- if (this.activeAbortController === abortController) {
549
- this.activeAbortController = null;
550
- }
551
-
552
- if (abortController.signal.aborted) {
553
- return {
554
- error: error?.message || "Generation aborted",
555
- finishReason: "aborted",
556
- };
557
- }
558
-
559
- return {
560
- error: error?.message || "OpenAI request failed",
561
- };
123
+ return await this.openAi.complete(request, abortController.signal);
562
124
  } finally {
563
125
  requestSignal?.removeEventListener("abort", abortFromRequestSignal);
564
- }
565
-
566
- if (!resp) {
567
- if (this.activeAbortController === abortController) {
568
- this.activeAbortController = null;
569
- }
570
-
571
- return {
572
- error: "OpenAI request failed",
573
- };
574
- }
575
-
576
- try {
577
- if (!resp.ok) {
578
- let errorMessage = `OpenAI request failed with status ${resp.status}`;
579
- try {
580
- const errorData = (await resp.json()) as OpenAIErrorResponse;
581
- if (errorData.error?.message) errorMessage = errorData.error.message;
582
- } catch {}
583
- return { error: errorMessage };
584
- }
585
-
586
- if (!isStreaming) {
587
- const json = await resp.json();
588
- const data = json as OpenAIResponsesSuccess & OpenAIErrorResponse;
589
- if (data.error) {
590
- return { error: data.error.message };
591
- }
592
-
593
- const usedTokens = extractUsedTokens(data);
594
-
595
- const toolCall = extractFunctionCall(data);
596
- if (toolCall) {
597
- try {
598
- const toolResult = await executeToolCall(toolCall, tools);
599
- return {
600
- content: toolResult,
601
- finishReason: "tool_call",
602
- used_tokens: usedTokens,
603
- };
604
- } catch (error: any) {
605
- return {
606
- error: error?.message || "Tool execution failed",
607
- finishReason: "tool_call",
608
- used_tokens: usedTokens,
609
- };
610
- }
611
- }
612
-
613
- const parsedContent = extractOutputText(data);
614
-
615
- return {
616
- content: parsedContent,
617
- finishReason: data.incomplete_details?.reason
618
- ? data.incomplete_details.reason
619
- : undefined,
620
- used_tokens: usedTokens,
621
- };
622
- }
623
-
624
- if (!resp.body) {
625
- return { error: "Response body is empty" };
626
- }
627
-
628
- const reader = resp.body.getReader();
629
- const decoder = new TextDecoder("utf-8");
630
-
631
- let buffer = "";
632
- let fullContent = "";
633
- let fullReasoning = "";
634
- let finishReason: string | undefined;
635
- let completedResponse: OpenAIResponsesSuccess | undefined;
636
- let usedTokens: UsedTokens | undefined;
637
-
638
- const handleEvent = async (event: any, eventType?: string) => {
639
- const type = event?.type || eventType;
640
-
641
- if (type === "response.output_text.delta") {
642
- const delta = event?.delta || "";
643
- if (!delta) return;
644
- fullContent += delta;
645
- await streamChunkCallback?.(delta, { type: "output", delta, text: fullContent });
646
- return;
647
- }
648
-
649
- if (
650
- type === "response.reasoning_summary_text.delta" ||
651
- type === "response.reasoning_text.delta"
652
- ) {
653
- const delta = event?.delta || "";
654
- if (!delta) return;
655
- fullReasoning += delta;
656
- await streamChunkCallback?.(delta, {
657
- type: "reasoning",
658
- delta,
659
- text: fullReasoning,
660
- });
661
- return;
662
- }
663
-
664
- if (type === "response.completed" || type === "response.incomplete") {
665
- const response = event?.response as OpenAIResponsesSuccess | undefined;
666
- if (!response) return;
667
-
668
- const finalContent = extractOutputText(response);
669
- if (finalContent.startsWith(fullContent)) {
670
- const delta = finalContent.slice(fullContent.length);
671
- if (delta) {
672
- fullContent = finalContent;
673
- await streamChunkCallback?.(delta, {
674
- type: "output",
675
- delta,
676
- text: fullContent,
677
- });
678
- }
679
- }
680
-
681
- const finalReasoning = extractReasoning(response) || "";
682
- if (finalReasoning.startsWith(fullReasoning)) {
683
- const delta = finalReasoning.slice(fullReasoning.length);
684
- if (delta) {
685
- fullReasoning = finalReasoning;
686
- await streamChunkCallback?.(delta, {
687
- type: "reasoning",
688
- delta,
689
- text: fullReasoning,
690
- });
691
- }
692
- }
693
-
694
- finishReason =
695
- response.incomplete_details?.reason || response.status || finishReason;
696
- completedResponse = response;
697
- usedTokens = extractUsedTokens(response);
698
- return;
699
- }
700
-
701
- if (type === "response.failed") {
702
- throw new Error(
703
- event?.response?.error?.message ||
704
- event?.error?.message ||
705
- "Response failed",
706
- );
707
- }
708
- };
709
-
710
- try {
711
- while (true) {
712
- const { value, done } = await reader.read();
713
- if (done) break;
714
-
715
- buffer += decoder.decode(value, { stream: true });
716
-
717
- const blocks = buffer.split("\n\n");
718
- buffer = blocks.pop() || "";
719
-
720
- for (const block of blocks) {
721
- const parsedBlock = parseSseBlock(block);
722
- if (!parsedBlock?.data || parsedBlock.data === "[DONE]") continue;
723
-
724
- let event: any;
725
- try {
726
- event = JSON.parse(parsedBlock.data);
727
- } catch {
728
- continue;
729
- }
730
-
731
- if (event?.error?.message) {
732
- return { error: event.error.message };
733
- }
734
-
735
- await handleEvent(event, parsedBlock.event);
736
- }
737
- }
738
-
739
- if (buffer.trim()) {
740
- const parsedBlock = parseSseBlock(buffer.trim());
741
- if (parsedBlock?.data && parsedBlock.data !== "[DONE]") {
742
- try {
743
- await handleEvent(JSON.parse(parsedBlock.data), parsedBlock.event);
744
- } catch (error: any) {
745
- return {
746
- error: error?.message || "Streaming failed",
747
- content: fullContent || undefined,
748
- finishReason,
749
- };
750
- }
751
- }
752
- }
753
-
754
- if (completedResponse) {
755
- const toolCall = extractFunctionCall(completedResponse);
756
- if (toolCall) {
757
- try {
758
- const toolResult = await executeToolCall(toolCall, tools);
759
- if (toolResult) {
760
- const delta = toolResult.startsWith(fullContent)
761
- ? toolResult.slice(fullContent.length)
762
- : toolResult;
763
- if (delta) {
764
- await streamChunkCallback?.(delta, {
765
- type: "output",
766
- delta,
767
- text: toolResult,
768
- });
769
- }
770
- }
771
-
772
- return {
773
- content: toolResult,
774
- finishReason: "tool_call",
775
- used_tokens: usedTokens,
776
- };
777
- } catch (error: any) {
778
- return {
779
- error: error?.message || "Tool execution failed",
780
- content: fullContent || undefined,
781
- finishReason: "tool_call",
782
- used_tokens: usedTokens,
783
- };
784
- }
785
- }
786
- }
787
-
788
- return {
789
- content: fullContent || undefined,
790
- finishReason,
791
- used_tokens: usedTokens,
792
- };
793
- } catch (error: any) {
794
- if (abortController.signal.aborted) {
795
- return {
796
- error: error?.message || "Generation aborted",
797
- content: fullContent || undefined,
798
- finishReason: "aborted",
799
- used_tokens: usedTokens,
800
- };
801
- }
802
-
803
- return {
804
- error: error?.message || "Streaming failed",
805
- content: fullContent || undefined,
806
- finishReason,
807
- used_tokens: usedTokens,
808
- };
809
- } finally {
810
- reader.releaseLock();
811
- }
812
- } finally {
813
126
  if (this.activeAbortController === abortController) {
814
127
  this.activeAbortController = null;
815
128
  }
816
129
  }
817
130
  };
818
- }
131
+ }
132
+
133
+ export { CompletionAdapterOpenAIResponses };
134
+ export default CompletionAdapterOpenAIResponses;