@adminforth/completion-adapter-openai-responses 2.0.26 → 2.0.27
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/.woodpecker/buildRelease.sh +9 -0
- package/.woodpecker/buildSlackNotify.sh +44 -0
- package/.woodpecker/release.yml +56 -0
- package/dist/index.d.ts +16 -5
- package/dist/index.js +262 -178
- package/index.ts +159 -56
- package/package.json +37 -3
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
|
|
3
|
+
set -x
|
|
4
|
+
|
|
5
|
+
COMMIT_SHORT_SHA=$(echo $CI_COMMIT_SHA | cut -c1-8)
|
|
6
|
+
|
|
7
|
+
STATUS=${1}
|
|
8
|
+
|
|
9
|
+
if [ "$STATUS" = "success" ]; then
|
|
10
|
+
MESSAGE="Did a build without issues on \`$CI_REPO_NAME/$CI_COMMIT_BRANCH\`. Commit: _${CI_COMMIT_MESSAGE}_ (<$CI_COMMIT_URL|$COMMIT_SHORT_SHA>)"
|
|
11
|
+
|
|
12
|
+
curl -s -X POST -H "Content-Type: application/json" -d '{
|
|
13
|
+
"username": "'"$CI_COMMIT_AUTHOR"'",
|
|
14
|
+
"icon_url": "'"$CI_COMMIT_AUTHOR_AVATAR"'",
|
|
15
|
+
"attachments": [
|
|
16
|
+
{
|
|
17
|
+
"mrkdwn_in": ["text", "pretext"],
|
|
18
|
+
"color": "#36a64f",
|
|
19
|
+
"text": "'"$MESSAGE"'"
|
|
20
|
+
}
|
|
21
|
+
]
|
|
22
|
+
}' "$DEVELOPERS_SLACK_WEBHOOK"
|
|
23
|
+
exit 0
|
|
24
|
+
fi
|
|
25
|
+
export BUILD_LOG=$(cat ./build.log)
|
|
26
|
+
|
|
27
|
+
BUILD_LOG=$(echo $BUILD_LOG | sed 's/"/\\"/g')
|
|
28
|
+
|
|
29
|
+
MESSAGE="Broke \`$CI_REPO_NAME/$CI_COMMIT_BRANCH\` with commit _${CI_COMMIT_MESSAGE}_ (<$CI_COMMIT_URL|$COMMIT_SHORT_SHA>)"
|
|
30
|
+
CODE_BLOCK="\`\`\`$BUILD_LOG\n\`\`\`"
|
|
31
|
+
|
|
32
|
+
echo "Sending slack message to developers $MESSAGE"
|
|
33
|
+
curl -sS -X POST -H "Content-Type: application/json" -d '{
|
|
34
|
+
"username": "'"$CI_COMMIT_AUTHOR"'",
|
|
35
|
+
"icon_url": "'"$CI_COMMIT_AUTHOR_AVATAR"'",
|
|
36
|
+
"attachments": [
|
|
37
|
+
{
|
|
38
|
+
"mrkdwn_in": ["text", "pretext"],
|
|
39
|
+
"color": "#8A1C12",
|
|
40
|
+
"text": "'"$CODE_BLOCK"'",
|
|
41
|
+
"pretext": "'"$MESSAGE"'"
|
|
42
|
+
}
|
|
43
|
+
]
|
|
44
|
+
}' "$DEVELOPERS_SLACK_WEBHOOK" 2>&1
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
clone:
|
|
2
|
+
git:
|
|
3
|
+
image: woodpeckerci/plugin-git
|
|
4
|
+
settings:
|
|
5
|
+
partial: false
|
|
6
|
+
depth: 5
|
|
7
|
+
|
|
8
|
+
steps:
|
|
9
|
+
init-secrets:
|
|
10
|
+
when:
|
|
11
|
+
- event: push
|
|
12
|
+
image: infisical/cli
|
|
13
|
+
environment:
|
|
14
|
+
INFISICAL_TOKEN:
|
|
15
|
+
from_secret: VAULT_TOKEN
|
|
16
|
+
commands:
|
|
17
|
+
- infisical export --domain https://vault.devforth.io/api --format=dotenv-export --env="prod" > /woodpecker/deploy.vault.env
|
|
18
|
+
|
|
19
|
+
build:
|
|
20
|
+
image: devforth/node20-pnpm:latest
|
|
21
|
+
when:
|
|
22
|
+
- event: push
|
|
23
|
+
commands:
|
|
24
|
+
- . /woodpecker/deploy.vault.env
|
|
25
|
+
- pnpm install
|
|
26
|
+
- /bin/bash ./.woodpecker/buildRelease.sh
|
|
27
|
+
- npm audit signatures
|
|
28
|
+
|
|
29
|
+
release:
|
|
30
|
+
image: devforth/node20-pnpm:latest
|
|
31
|
+
when:
|
|
32
|
+
- event:
|
|
33
|
+
- push
|
|
34
|
+
branch:
|
|
35
|
+
- main
|
|
36
|
+
commands:
|
|
37
|
+
- . /woodpecker/deploy.vault.env
|
|
38
|
+
- pnpm exec semantic-release
|
|
39
|
+
|
|
40
|
+
slack-on-failure:
|
|
41
|
+
image: curlimages/curl
|
|
42
|
+
when:
|
|
43
|
+
- event: push
|
|
44
|
+
status: [failure]
|
|
45
|
+
commands:
|
|
46
|
+
- . /woodpecker/deploy.vault.env
|
|
47
|
+
- /bin/sh ./.woodpecker/buildSlackNotify.sh failure
|
|
48
|
+
|
|
49
|
+
slack-on-success:
|
|
50
|
+
image: curlimages/curl
|
|
51
|
+
when:
|
|
52
|
+
- event: push
|
|
53
|
+
status: [success]
|
|
54
|
+
commands:
|
|
55
|
+
- . /woodpecker/deploy.vault.env
|
|
56
|
+
- /bin/sh ./.woodpecker/buildSlackNotify.sh success
|
package/dist/index.d.ts
CHANGED
|
@@ -12,13 +12,28 @@ type CompletionRequestInput = {
|
|
|
12
12
|
reasoningEffort?: ReasoningEffort;
|
|
13
13
|
tools?: CompletionTool[];
|
|
14
14
|
onChunk?: StreamChunkCallback;
|
|
15
|
+
signal?: AbortSignal;
|
|
16
|
+
};
|
|
17
|
+
type UsedTokens = {
|
|
18
|
+
input_uncached: number;
|
|
19
|
+
input_cached: number;
|
|
20
|
+
output: number;
|
|
21
|
+
};
|
|
22
|
+
type CompletionResult = {
|
|
23
|
+
content?: string;
|
|
24
|
+
finishReason?: string;
|
|
25
|
+
error?: string;
|
|
26
|
+
used_tokens?: UsedTokens;
|
|
15
27
|
};
|
|
16
28
|
export default class CompletionAdapterOpenAIResponses implements CompletionAdapter {
|
|
17
29
|
options: AdapterOptions;
|
|
18
30
|
private encoding;
|
|
31
|
+
private activeAbortController;
|
|
19
32
|
constructor(options: AdapterOptions);
|
|
20
33
|
validate(): void;
|
|
21
34
|
measureTokensCount(content: string): number;
|
|
35
|
+
abort(): void;
|
|
36
|
+
isGenerationInProgress(): boolean;
|
|
22
37
|
private getConfiguredBaseUrl;
|
|
23
38
|
private shouldUseComplitionApi;
|
|
24
39
|
private shouldDumpRawRequest;
|
|
@@ -35,9 +50,5 @@ export default class CompletionAdapterOpenAIResponses implements CompletionAdapt
|
|
|
35
50
|
model: ChatOpenAI<import("@langchain/openai").ChatOpenAICallOptions>;
|
|
36
51
|
middleware: import("langchain").AgentMiddleware<undefined, undefined, unknown, readonly (import("@langchain/core/tools").ClientTool | import("@langchain/core/tools").ServerTool)[]>[];
|
|
37
52
|
};
|
|
38
|
-
complete: (requestOrContent: CompletionRequestInput | string, maxTokens?: number, outputSchema?: any, reasoningEffort?: ReasoningEffort, toolsOrOnChunk?: CompletionTool[] | StreamChunkCallback, onChunk?: StreamChunkCallback) => Promise<
|
|
39
|
-
content?: string;
|
|
40
|
-
finishReason?: string;
|
|
41
|
-
error?: string;
|
|
42
|
-
}>;
|
|
53
|
+
complete: (requestOrContent: CompletionRequestInput | string, maxTokens?: number, outputSchema?: any, reasoningEffort?: ReasoningEffort, toolsOrOnChunk?: CompletionTool[] | StreamChunkCallback, onChunk?: StreamChunkCallback) => Promise<CompletionResult>;
|
|
43
54
|
}
|
package/dist/index.js
CHANGED
|
@@ -68,6 +68,19 @@ function extractFunctionCall(data) {
|
|
|
68
68
|
}
|
|
69
69
|
return undefined;
|
|
70
70
|
}
|
|
71
|
+
function extractUsedTokens(data) {
|
|
72
|
+
var _a, _b;
|
|
73
|
+
const usage = data.usage;
|
|
74
|
+
if (!usage) {
|
|
75
|
+
return undefined;
|
|
76
|
+
}
|
|
77
|
+
const inputCached = (_b = (_a = usage.input_tokens_details) === null || _a === void 0 ? void 0 : _a.cached_tokens) !== null && _b !== void 0 ? _b : 0;
|
|
78
|
+
return {
|
|
79
|
+
input_uncached: Math.max(usage.input_tokens - inputCached, 0),
|
|
80
|
+
input_cached: inputCached,
|
|
81
|
+
output: usage.output_tokens,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
71
84
|
function executeToolCall(toolCall, tools) {
|
|
72
85
|
return __awaiter(this, void 0, void 0, function* () {
|
|
73
86
|
const tool = tools === null || tools === void 0 ? void 0 : tools.find((candidate) => candidate.name === toolCall.name);
|
|
@@ -101,7 +114,7 @@ function getAgentReasoningEffort(purpose) {
|
|
|
101
114
|
}
|
|
102
115
|
function buildReasoningConfig(params) {
|
|
103
116
|
var _a;
|
|
104
|
-
return Object.assign({ summary: "
|
|
117
|
+
return Object.assign({ summary: "auto", effort: params.effort }, ((_a = params.reasoning) !== null && _a !== void 0 ? _a : {}));
|
|
105
118
|
}
|
|
106
119
|
function getTurnKey(context) {
|
|
107
120
|
return `${context.sessionId}:${context.turnId}`;
|
|
@@ -160,6 +173,7 @@ function createOpenAiResponsesContinuationMiddleware() {
|
|
|
160
173
|
}
|
|
161
174
|
export default class CompletionAdapterOpenAIResponses {
|
|
162
175
|
constructor(options) {
|
|
176
|
+
this.activeAbortController = null;
|
|
163
177
|
this.complete = (requestOrContent_1, ...args_1) => __awaiter(this, [requestOrContent_1, ...args_1], void 0, function* (requestOrContent, maxTokens = 50, outputSchema, reasoningEffort = "low", toolsOrOnChunk, onChunk) {
|
|
164
178
|
var _a, _b, _c;
|
|
165
179
|
const request = typeof requestOrContent === "string"
|
|
@@ -174,7 +188,7 @@ export default class CompletionAdapterOpenAIResponses {
|
|
|
174
188
|
: onChunk,
|
|
175
189
|
}
|
|
176
190
|
: requestOrContent;
|
|
177
|
-
const { content, maxTokens: requestMaxTokens = 50, outputSchema: requestOutputSchema, reasoningEffort: requestReasoningEffort = "low", tools, onChunk: streamChunkCallback, } = request;
|
|
191
|
+
const { content, maxTokens: requestMaxTokens = 50, outputSchema: requestOutputSchema, reasoningEffort: requestReasoningEffort = "low", tools, onChunk: streamChunkCallback, signal: requestSignal, } = request;
|
|
178
192
|
const model = this.options.model || "gpt-5-nano";
|
|
179
193
|
const isStreaming = typeof streamChunkCallback === "function";
|
|
180
194
|
const extra = this.options.extraRequestBodyParameters;
|
|
@@ -205,212 +219,275 @@ export default class CompletionAdapterOpenAIResponses {
|
|
|
205
219
|
if (this.shouldDumpRawRequest()) {
|
|
206
220
|
this.dumpRawRequest(this.getResponsesUrl(), serializedBody);
|
|
207
221
|
}
|
|
208
|
-
const
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
if (!resp.ok) {
|
|
217
|
-
let errorMessage = `OpenAI request failed with status ${resp.status}`;
|
|
218
|
-
try {
|
|
219
|
-
const errorData = (yield resp.json());
|
|
220
|
-
if ((_a = errorData.error) === null || _a === void 0 ? void 0 : _a.message)
|
|
221
|
-
errorMessage = errorData.error.message;
|
|
222
|
-
}
|
|
223
|
-
catch (_e) { }
|
|
224
|
-
return { error: errorMessage };
|
|
222
|
+
const abortController = new AbortController();
|
|
223
|
+
this.activeAbortController = abortController;
|
|
224
|
+
const abortFromRequestSignal = () => abortController.abort(requestSignal === null || requestSignal === void 0 ? void 0 : requestSignal.reason);
|
|
225
|
+
if (requestSignal === null || requestSignal === void 0 ? void 0 : requestSignal.aborted) {
|
|
226
|
+
abortController.abort(requestSignal.reason);
|
|
227
|
+
}
|
|
228
|
+
else {
|
|
229
|
+
requestSignal === null || requestSignal === void 0 ? void 0 : requestSignal.addEventListener("abort", abortFromRequestSignal, { once: true });
|
|
225
230
|
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
+
let resp = null;
|
|
232
|
+
try {
|
|
233
|
+
resp = yield fetch(this.getResponsesUrl(), {
|
|
234
|
+
method: "POST",
|
|
235
|
+
headers: {
|
|
236
|
+
"Content-Type": "application/json",
|
|
237
|
+
Authorization: `Bearer ${this.options.openAiApiKey}`,
|
|
238
|
+
},
|
|
239
|
+
body: serializedBody,
|
|
240
|
+
signal: abortController.signal,
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
catch (error) {
|
|
244
|
+
if (this.activeAbortController === abortController) {
|
|
245
|
+
this.activeAbortController = null;
|
|
231
246
|
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
content: toolResult,
|
|
238
|
-
finishReason: "tool_call",
|
|
239
|
-
};
|
|
240
|
-
}
|
|
241
|
-
catch (error) {
|
|
242
|
-
return {
|
|
243
|
-
error: (error === null || error === void 0 ? void 0 : error.message) || "Tool execution failed",
|
|
244
|
-
finishReason: "tool_call",
|
|
245
|
-
};
|
|
246
|
-
}
|
|
247
|
+
if (abortController.signal.aborted) {
|
|
248
|
+
return {
|
|
249
|
+
error: (error === null || error === void 0 ? void 0 : error.message) || "Generation aborted",
|
|
250
|
+
finishReason: "aborted",
|
|
251
|
+
};
|
|
247
252
|
}
|
|
248
|
-
const parsedContent = extractOutputText(data);
|
|
249
253
|
return {
|
|
250
|
-
|
|
251
|
-
finishReason: ((_b = data.incomplete_details) === null || _b === void 0 ? void 0 : _b.reason)
|
|
252
|
-
? data.incomplete_details.reason
|
|
253
|
-
: undefined,
|
|
254
|
+
error: (error === null || error === void 0 ? void 0 : error.message) || "OpenAI request failed",
|
|
254
255
|
};
|
|
255
256
|
}
|
|
256
|
-
|
|
257
|
-
|
|
257
|
+
finally {
|
|
258
|
+
requestSignal === null || requestSignal === void 0 ? void 0 : requestSignal.removeEventListener("abort", abortFromRequestSignal);
|
|
258
259
|
}
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
let fullContent = "";
|
|
263
|
-
let fullReasoning = "";
|
|
264
|
-
let finishReason;
|
|
265
|
-
let completedResponse;
|
|
266
|
-
const handleEvent = (event, eventType) => __awaiter(this, void 0, void 0, function* () {
|
|
267
|
-
var _a, _b, _c, _d;
|
|
268
|
-
const type = (event === null || event === void 0 ? void 0 : event.type) || eventType;
|
|
269
|
-
if (type === "response.output_text.delta") {
|
|
270
|
-
const delta = (event === null || event === void 0 ? void 0 : event.delta) || "";
|
|
271
|
-
if (!delta)
|
|
272
|
-
return;
|
|
273
|
-
fullContent += delta;
|
|
274
|
-
yield (streamChunkCallback === null || streamChunkCallback === void 0 ? void 0 : streamChunkCallback(delta, { type: "output", delta, text: fullContent }));
|
|
275
|
-
return;
|
|
276
|
-
}
|
|
277
|
-
if (type === "response.reasoning_summary_text.delta" ||
|
|
278
|
-
type === "response.reasoning_text.delta") {
|
|
279
|
-
const delta = (event === null || event === void 0 ? void 0 : event.delta) || "";
|
|
280
|
-
if (!delta)
|
|
281
|
-
return;
|
|
282
|
-
fullReasoning += delta;
|
|
283
|
-
yield (streamChunkCallback === null || streamChunkCallback === void 0 ? void 0 : streamChunkCallback(delta, {
|
|
284
|
-
type: "reasoning",
|
|
285
|
-
delta,
|
|
286
|
-
text: fullReasoning,
|
|
287
|
-
}));
|
|
288
|
-
return;
|
|
289
|
-
}
|
|
290
|
-
if (type === "response.completed" || type === "response.incomplete") {
|
|
291
|
-
const response = event === null || event === void 0 ? void 0 : event.response;
|
|
292
|
-
if (!response)
|
|
293
|
-
return;
|
|
294
|
-
const finalContent = extractOutputText(response);
|
|
295
|
-
if (finalContent.startsWith(fullContent)) {
|
|
296
|
-
const delta = finalContent.slice(fullContent.length);
|
|
297
|
-
if (delta) {
|
|
298
|
-
fullContent = finalContent;
|
|
299
|
-
yield (streamChunkCallback === null || streamChunkCallback === void 0 ? void 0 : streamChunkCallback(delta, {
|
|
300
|
-
type: "output",
|
|
301
|
-
delta,
|
|
302
|
-
text: fullContent,
|
|
303
|
-
}));
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
const finalReasoning = extractReasoning(response) || "";
|
|
307
|
-
if (finalReasoning.startsWith(fullReasoning)) {
|
|
308
|
-
const delta = finalReasoning.slice(fullReasoning.length);
|
|
309
|
-
if (delta) {
|
|
310
|
-
fullReasoning = finalReasoning;
|
|
311
|
-
yield (streamChunkCallback === null || streamChunkCallback === void 0 ? void 0 : streamChunkCallback(delta, {
|
|
312
|
-
type: "reasoning",
|
|
313
|
-
delta,
|
|
314
|
-
text: fullReasoning,
|
|
315
|
-
}));
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
finishReason =
|
|
319
|
-
((_a = response.incomplete_details) === null || _a === void 0 ? void 0 : _a.reason) || response.status || finishReason;
|
|
320
|
-
completedResponse = response;
|
|
321
|
-
return;
|
|
322
|
-
}
|
|
323
|
-
if (type === "response.failed") {
|
|
324
|
-
throw new Error(((_c = (_b = event === null || event === void 0 ? void 0 : event.response) === null || _b === void 0 ? void 0 : _b.error) === null || _c === void 0 ? void 0 : _c.message) ||
|
|
325
|
-
((_d = event === null || event === void 0 ? void 0 : event.error) === null || _d === void 0 ? void 0 : _d.message) ||
|
|
326
|
-
"Response failed");
|
|
260
|
+
if (!resp) {
|
|
261
|
+
if (this.activeAbortController === abortController) {
|
|
262
|
+
this.activeAbortController = null;
|
|
327
263
|
}
|
|
328
|
-
|
|
264
|
+
return {
|
|
265
|
+
error: "OpenAI request failed",
|
|
266
|
+
};
|
|
267
|
+
}
|
|
329
268
|
try {
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
buffer = blocks.pop() || "";
|
|
337
|
-
for (const block of blocks) {
|
|
338
|
-
const parsedBlock = parseSseBlock(block);
|
|
339
|
-
if (!(parsedBlock === null || parsedBlock === void 0 ? void 0 : parsedBlock.data) || parsedBlock.data === "[DONE]")
|
|
340
|
-
continue;
|
|
341
|
-
let event;
|
|
342
|
-
try {
|
|
343
|
-
event = JSON.parse(parsedBlock.data);
|
|
344
|
-
}
|
|
345
|
-
catch (_f) {
|
|
346
|
-
continue;
|
|
347
|
-
}
|
|
348
|
-
if ((_c = event === null || event === void 0 ? void 0 : event.error) === null || _c === void 0 ? void 0 : _c.message) {
|
|
349
|
-
return { error: event.error.message };
|
|
350
|
-
}
|
|
351
|
-
yield handleEvent(event, parsedBlock.event);
|
|
269
|
+
if (!resp.ok) {
|
|
270
|
+
let errorMessage = `OpenAI request failed with status ${resp.status}`;
|
|
271
|
+
try {
|
|
272
|
+
const errorData = (yield resp.json());
|
|
273
|
+
if ((_a = errorData.error) === null || _a === void 0 ? void 0 : _a.message)
|
|
274
|
+
errorMessage = errorData.error.message;
|
|
352
275
|
}
|
|
276
|
+
catch (_e) { }
|
|
277
|
+
return { error: errorMessage };
|
|
353
278
|
}
|
|
354
|
-
if (
|
|
355
|
-
const
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
}
|
|
360
|
-
catch (error) {
|
|
361
|
-
return {
|
|
362
|
-
error: (error === null || error === void 0 ? void 0 : error.message) || "Streaming failed",
|
|
363
|
-
content: fullContent || undefined,
|
|
364
|
-
finishReason,
|
|
365
|
-
};
|
|
366
|
-
}
|
|
279
|
+
if (!isStreaming) {
|
|
280
|
+
const json = yield resp.json();
|
|
281
|
+
const data = json;
|
|
282
|
+
if (data.error) {
|
|
283
|
+
return { error: data.error.message };
|
|
367
284
|
}
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
const toolCall = extractFunctionCall(completedResponse);
|
|
285
|
+
const usedTokens = extractUsedTokens(data);
|
|
286
|
+
const toolCall = extractFunctionCall(data);
|
|
371
287
|
if (toolCall) {
|
|
372
288
|
try {
|
|
373
289
|
const toolResult = yield executeToolCall(toolCall, tools);
|
|
374
|
-
if (toolResult) {
|
|
375
|
-
const delta = toolResult.startsWith(fullContent)
|
|
376
|
-
? toolResult.slice(fullContent.length)
|
|
377
|
-
: toolResult;
|
|
378
|
-
if (delta) {
|
|
379
|
-
yield (streamChunkCallback === null || streamChunkCallback === void 0 ? void 0 : streamChunkCallback(delta, {
|
|
380
|
-
type: "output",
|
|
381
|
-
delta,
|
|
382
|
-
text: toolResult,
|
|
383
|
-
}));
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
290
|
return {
|
|
387
291
|
content: toolResult,
|
|
388
292
|
finishReason: "tool_call",
|
|
293
|
+
used_tokens: usedTokens,
|
|
389
294
|
};
|
|
390
295
|
}
|
|
391
296
|
catch (error) {
|
|
392
297
|
return {
|
|
393
298
|
error: (error === null || error === void 0 ? void 0 : error.message) || "Tool execution failed",
|
|
394
|
-
content: fullContent || undefined,
|
|
395
299
|
finishReason: "tool_call",
|
|
300
|
+
used_tokens: usedTokens,
|
|
396
301
|
};
|
|
397
302
|
}
|
|
398
303
|
}
|
|
304
|
+
const parsedContent = extractOutputText(data);
|
|
305
|
+
return {
|
|
306
|
+
content: parsedContent,
|
|
307
|
+
finishReason: ((_b = data.incomplete_details) === null || _b === void 0 ? void 0 : _b.reason)
|
|
308
|
+
? data.incomplete_details.reason
|
|
309
|
+
: undefined,
|
|
310
|
+
used_tokens: usedTokens,
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
if (!resp.body) {
|
|
314
|
+
return { error: "Response body is empty" };
|
|
315
|
+
}
|
|
316
|
+
const reader = resp.body.getReader();
|
|
317
|
+
const decoder = new TextDecoder("utf-8");
|
|
318
|
+
let buffer = "";
|
|
319
|
+
let fullContent = "";
|
|
320
|
+
let fullReasoning = "";
|
|
321
|
+
let finishReason;
|
|
322
|
+
let completedResponse;
|
|
323
|
+
let usedTokens;
|
|
324
|
+
const handleEvent = (event, eventType) => __awaiter(this, void 0, void 0, function* () {
|
|
325
|
+
var _a, _b, _c, _d;
|
|
326
|
+
const type = (event === null || event === void 0 ? void 0 : event.type) || eventType;
|
|
327
|
+
if (type === "response.output_text.delta") {
|
|
328
|
+
const delta = (event === null || event === void 0 ? void 0 : event.delta) || "";
|
|
329
|
+
if (!delta)
|
|
330
|
+
return;
|
|
331
|
+
fullContent += delta;
|
|
332
|
+
yield (streamChunkCallback === null || streamChunkCallback === void 0 ? void 0 : streamChunkCallback(delta, { type: "output", delta, text: fullContent }));
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
if (type === "response.reasoning_summary_text.delta" ||
|
|
336
|
+
type === "response.reasoning_text.delta") {
|
|
337
|
+
const delta = (event === null || event === void 0 ? void 0 : event.delta) || "";
|
|
338
|
+
if (!delta)
|
|
339
|
+
return;
|
|
340
|
+
fullReasoning += delta;
|
|
341
|
+
yield (streamChunkCallback === null || streamChunkCallback === void 0 ? void 0 : streamChunkCallback(delta, {
|
|
342
|
+
type: "reasoning",
|
|
343
|
+
delta,
|
|
344
|
+
text: fullReasoning,
|
|
345
|
+
}));
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
if (type === "response.completed" || type === "response.incomplete") {
|
|
349
|
+
const response = event === null || event === void 0 ? void 0 : event.response;
|
|
350
|
+
if (!response)
|
|
351
|
+
return;
|
|
352
|
+
const finalContent = extractOutputText(response);
|
|
353
|
+
if (finalContent.startsWith(fullContent)) {
|
|
354
|
+
const delta = finalContent.slice(fullContent.length);
|
|
355
|
+
if (delta) {
|
|
356
|
+
fullContent = finalContent;
|
|
357
|
+
yield (streamChunkCallback === null || streamChunkCallback === void 0 ? void 0 : streamChunkCallback(delta, {
|
|
358
|
+
type: "output",
|
|
359
|
+
delta,
|
|
360
|
+
text: fullContent,
|
|
361
|
+
}));
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
const finalReasoning = extractReasoning(response) || "";
|
|
365
|
+
if (finalReasoning.startsWith(fullReasoning)) {
|
|
366
|
+
const delta = finalReasoning.slice(fullReasoning.length);
|
|
367
|
+
if (delta) {
|
|
368
|
+
fullReasoning = finalReasoning;
|
|
369
|
+
yield (streamChunkCallback === null || streamChunkCallback === void 0 ? void 0 : streamChunkCallback(delta, {
|
|
370
|
+
type: "reasoning",
|
|
371
|
+
delta,
|
|
372
|
+
text: fullReasoning,
|
|
373
|
+
}));
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
finishReason =
|
|
377
|
+
((_a = response.incomplete_details) === null || _a === void 0 ? void 0 : _a.reason) || response.status || finishReason;
|
|
378
|
+
completedResponse = response;
|
|
379
|
+
usedTokens = extractUsedTokens(response);
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
if (type === "response.failed") {
|
|
383
|
+
throw new Error(((_c = (_b = event === null || event === void 0 ? void 0 : event.response) === null || _b === void 0 ? void 0 : _b.error) === null || _c === void 0 ? void 0 : _c.message) ||
|
|
384
|
+
((_d = event === null || event === void 0 ? void 0 : event.error) === null || _d === void 0 ? void 0 : _d.message) ||
|
|
385
|
+
"Response failed");
|
|
386
|
+
}
|
|
387
|
+
});
|
|
388
|
+
try {
|
|
389
|
+
while (true) {
|
|
390
|
+
const { value, done } = yield reader.read();
|
|
391
|
+
if (done)
|
|
392
|
+
break;
|
|
393
|
+
buffer += decoder.decode(value, { stream: true });
|
|
394
|
+
const blocks = buffer.split("\n\n");
|
|
395
|
+
buffer = blocks.pop() || "";
|
|
396
|
+
for (const block of blocks) {
|
|
397
|
+
const parsedBlock = parseSseBlock(block);
|
|
398
|
+
if (!(parsedBlock === null || parsedBlock === void 0 ? void 0 : parsedBlock.data) || parsedBlock.data === "[DONE]")
|
|
399
|
+
continue;
|
|
400
|
+
let event;
|
|
401
|
+
try {
|
|
402
|
+
event = JSON.parse(parsedBlock.data);
|
|
403
|
+
}
|
|
404
|
+
catch (_f) {
|
|
405
|
+
continue;
|
|
406
|
+
}
|
|
407
|
+
if ((_c = event === null || event === void 0 ? void 0 : event.error) === null || _c === void 0 ? void 0 : _c.message) {
|
|
408
|
+
return { error: event.error.message };
|
|
409
|
+
}
|
|
410
|
+
yield handleEvent(event, parsedBlock.event);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
if (buffer.trim()) {
|
|
414
|
+
const parsedBlock = parseSseBlock(buffer.trim());
|
|
415
|
+
if ((parsedBlock === null || parsedBlock === void 0 ? void 0 : parsedBlock.data) && parsedBlock.data !== "[DONE]") {
|
|
416
|
+
try {
|
|
417
|
+
yield handleEvent(JSON.parse(parsedBlock.data), parsedBlock.event);
|
|
418
|
+
}
|
|
419
|
+
catch (error) {
|
|
420
|
+
return {
|
|
421
|
+
error: (error === null || error === void 0 ? void 0 : error.message) || "Streaming failed",
|
|
422
|
+
content: fullContent || undefined,
|
|
423
|
+
finishReason,
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
if (completedResponse) {
|
|
429
|
+
const toolCall = extractFunctionCall(completedResponse);
|
|
430
|
+
if (toolCall) {
|
|
431
|
+
try {
|
|
432
|
+
const toolResult = yield executeToolCall(toolCall, tools);
|
|
433
|
+
if (toolResult) {
|
|
434
|
+
const delta = toolResult.startsWith(fullContent)
|
|
435
|
+
? toolResult.slice(fullContent.length)
|
|
436
|
+
: toolResult;
|
|
437
|
+
if (delta) {
|
|
438
|
+
yield (streamChunkCallback === null || streamChunkCallback === void 0 ? void 0 : streamChunkCallback(delta, {
|
|
439
|
+
type: "output",
|
|
440
|
+
delta,
|
|
441
|
+
text: toolResult,
|
|
442
|
+
}));
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
return {
|
|
446
|
+
content: toolResult,
|
|
447
|
+
finishReason: "tool_call",
|
|
448
|
+
used_tokens: usedTokens,
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
catch (error) {
|
|
452
|
+
return {
|
|
453
|
+
error: (error === null || error === void 0 ? void 0 : error.message) || "Tool execution failed",
|
|
454
|
+
content: fullContent || undefined,
|
|
455
|
+
finishReason: "tool_call",
|
|
456
|
+
used_tokens: usedTokens,
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
return {
|
|
462
|
+
content: fullContent || undefined,
|
|
463
|
+
finishReason,
|
|
464
|
+
used_tokens: usedTokens,
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
catch (error) {
|
|
468
|
+
if (abortController.signal.aborted) {
|
|
469
|
+
return {
|
|
470
|
+
error: (error === null || error === void 0 ? void 0 : error.message) || "Generation aborted",
|
|
471
|
+
content: fullContent || undefined,
|
|
472
|
+
finishReason: "aborted",
|
|
473
|
+
used_tokens: usedTokens,
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
return {
|
|
477
|
+
error: (error === null || error === void 0 ? void 0 : error.message) || "Streaming failed",
|
|
478
|
+
content: fullContent || undefined,
|
|
479
|
+
finishReason,
|
|
480
|
+
used_tokens: usedTokens,
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
finally {
|
|
484
|
+
reader.releaseLock();
|
|
399
485
|
}
|
|
400
|
-
return {
|
|
401
|
-
content: fullContent || undefined,
|
|
402
|
-
finishReason,
|
|
403
|
-
};
|
|
404
|
-
}
|
|
405
|
-
catch (error) {
|
|
406
|
-
return {
|
|
407
|
-
error: (error === null || error === void 0 ? void 0 : error.message) || "Streaming failed",
|
|
408
|
-
content: fullContent || undefined,
|
|
409
|
-
finishReason,
|
|
410
|
-
};
|
|
411
486
|
}
|
|
412
487
|
finally {
|
|
413
|
-
|
|
488
|
+
if (this.activeAbortController === abortController) {
|
|
489
|
+
this.activeAbortController = null;
|
|
490
|
+
}
|
|
414
491
|
}
|
|
415
492
|
});
|
|
416
493
|
this.options = options;
|
|
@@ -432,6 +509,13 @@ export default class CompletionAdapterOpenAIResponses {
|
|
|
432
509
|
measureTokensCount(content) {
|
|
433
510
|
return this.encoding.encode(content).length;
|
|
434
511
|
}
|
|
512
|
+
abort() {
|
|
513
|
+
var _a;
|
|
514
|
+
(_a = this.activeAbortController) === null || _a === void 0 ? void 0 : _a.abort();
|
|
515
|
+
}
|
|
516
|
+
isGenerationInProgress() {
|
|
517
|
+
return Boolean(this.activeAbortController);
|
|
518
|
+
}
|
|
435
519
|
getConfiguredBaseUrl() {
|
|
436
520
|
return this.options.baseUrl;
|
|
437
521
|
}
|
package/index.ts
CHANGED
|
@@ -34,6 +34,7 @@ type CompletionRequestInput = {
|
|
|
34
34
|
reasoningEffort?: ReasoningEffort;
|
|
35
35
|
tools?: CompletionTool[];
|
|
36
36
|
onChunk?: StreamChunkCallback;
|
|
37
|
+
signal?: AbortSignal;
|
|
37
38
|
};
|
|
38
39
|
|
|
39
40
|
type ResponseCreateBody = OpenAI.Responses.ResponseCreateParams;
|
|
@@ -59,6 +60,20 @@ type OpenAiResponsesMetadata = {
|
|
|
59
60
|
type OpenAiResponsesContext = {
|
|
60
61
|
sessionId: string;
|
|
61
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;
|
|
62
77
|
};
|
|
63
78
|
|
|
64
79
|
const DEFAULT_OPENAI_BASE_URL = "https://api.openai.com/v1";
|
|
@@ -118,6 +133,21 @@ function extractFunctionCall(
|
|
|
118
133
|
return undefined;
|
|
119
134
|
}
|
|
120
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
|
+
|
|
121
151
|
async function executeToolCall(
|
|
122
152
|
toolCall: OpenAIFunctionCall,
|
|
123
153
|
tools?: CompletionTool[],
|
|
@@ -158,7 +188,7 @@ function buildReasoningConfig(params: {
|
|
|
158
188
|
effort: Exclude<ReasoningEffort, "none"> | ReasoningEffort;
|
|
159
189
|
}) {
|
|
160
190
|
return {
|
|
161
|
-
summary: "
|
|
191
|
+
summary: "auto",
|
|
162
192
|
effort: params.effort,
|
|
163
193
|
...(params.reasoning ?? {}),
|
|
164
194
|
};
|
|
@@ -250,6 +280,7 @@ export default class CompletionAdapterOpenAIResponses
|
|
|
250
280
|
{
|
|
251
281
|
options: AdapterOptions;
|
|
252
282
|
private encoding: ReturnType<typeof encoding_for_model>;
|
|
283
|
+
private activeAbortController: AbortController | null = null;
|
|
253
284
|
|
|
254
285
|
constructor(options: AdapterOptions) {
|
|
255
286
|
this.options = options;
|
|
@@ -275,6 +306,14 @@ export default class CompletionAdapterOpenAIResponses
|
|
|
275
306
|
return this.encoding.encode(content).length;
|
|
276
307
|
}
|
|
277
308
|
|
|
309
|
+
abort() {
|
|
310
|
+
this.activeAbortController?.abort();
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
isGenerationInProgress() {
|
|
314
|
+
return Boolean(this.activeAbortController);
|
|
315
|
+
}
|
|
316
|
+
|
|
278
317
|
private getConfiguredBaseUrl() {
|
|
279
318
|
return this.options.baseUrl;
|
|
280
319
|
}
|
|
@@ -409,11 +448,7 @@ export default class CompletionAdapterOpenAIResponses
|
|
|
409
448
|
reasoningEffort: ReasoningEffort = "low",
|
|
410
449
|
toolsOrOnChunk?: CompletionTool[] | StreamChunkCallback,
|
|
411
450
|
onChunk?: StreamChunkCallback,
|
|
412
|
-
): Promise<{
|
|
413
|
-
content?: string;
|
|
414
|
-
finishReason?: string;
|
|
415
|
-
error?: string;
|
|
416
|
-
}> => {
|
|
451
|
+
): Promise<CompletionResult> => {
|
|
417
452
|
const request =
|
|
418
453
|
typeof requestOrContent === "string"
|
|
419
454
|
? {
|
|
@@ -435,6 +470,7 @@ export default class CompletionAdapterOpenAIResponses
|
|
|
435
470
|
reasoningEffort: requestReasoningEffort = "low",
|
|
436
471
|
tools,
|
|
437
472
|
onChunk: streamChunkCallback,
|
|
473
|
+
signal: requestSignal,
|
|
438
474
|
} = request;
|
|
439
475
|
const model = this.options.model || "gpt-5-nano";
|
|
440
476
|
const isStreaming = typeof streamChunkCallback === "function";
|
|
@@ -487,69 +523,117 @@ export default class CompletionAdapterOpenAIResponses
|
|
|
487
523
|
this.dumpRawRequest(this.getResponsesUrl(), serializedBody);
|
|
488
524
|
}
|
|
489
525
|
|
|
490
|
-
const
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
if (!resp.ok) {
|
|
500
|
-
let errorMessage = `OpenAI request failed with status ${resp.status}`;
|
|
501
|
-
try {
|
|
502
|
-
const errorData = (await resp.json()) as OpenAIErrorResponse;
|
|
503
|
-
if (errorData.error?.message) errorMessage = errorData.error.message;
|
|
504
|
-
} catch {}
|
|
505
|
-
return { error: errorMessage };
|
|
526
|
+
const abortController = new AbortController();
|
|
527
|
+
this.activeAbortController = abortController;
|
|
528
|
+
const abortFromRequestSignal = () => abortController.abort(requestSignal?.reason);
|
|
529
|
+
|
|
530
|
+
if (requestSignal?.aborted) {
|
|
531
|
+
abortController.abort(requestSignal.reason);
|
|
532
|
+
} else {
|
|
533
|
+
requestSignal?.addEventListener("abort", abortFromRequestSignal, { once: true });
|
|
506
534
|
}
|
|
507
535
|
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
536
|
+
let resp: Response | null = null;
|
|
537
|
+
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;
|
|
513
550
|
}
|
|
514
551
|
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
content: toolResult,
|
|
521
|
-
finishReason: "tool_call",
|
|
522
|
-
};
|
|
523
|
-
} catch (error: any) {
|
|
524
|
-
return {
|
|
525
|
-
error: error?.message || "Tool execution failed",
|
|
526
|
-
finishReason: "tool_call",
|
|
527
|
-
};
|
|
528
|
-
}
|
|
552
|
+
if (abortController.signal.aborted) {
|
|
553
|
+
return {
|
|
554
|
+
error: error?.message || "Generation aborted",
|
|
555
|
+
finishReason: "aborted",
|
|
556
|
+
};
|
|
529
557
|
}
|
|
530
558
|
|
|
531
|
-
const parsedContent = extractOutputText(data);
|
|
532
|
-
|
|
533
559
|
return {
|
|
534
|
-
|
|
535
|
-
finishReason: data.incomplete_details?.reason
|
|
536
|
-
? data.incomplete_details.reason
|
|
537
|
-
: undefined,
|
|
560
|
+
error: error?.message || "OpenAI request failed",
|
|
538
561
|
};
|
|
562
|
+
} finally {
|
|
563
|
+
requestSignal?.removeEventListener("abort", abortFromRequestSignal);
|
|
539
564
|
}
|
|
540
565
|
|
|
541
|
-
if (!resp
|
|
542
|
-
|
|
566
|
+
if (!resp) {
|
|
567
|
+
if (this.activeAbortController === abortController) {
|
|
568
|
+
this.activeAbortController = null;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
return {
|
|
572
|
+
error: "OpenAI request failed",
|
|
573
|
+
};
|
|
543
574
|
}
|
|
544
575
|
|
|
545
|
-
|
|
546
|
-
|
|
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
|
+
}
|
|
547
585
|
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
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;
|
|
553
637
|
|
|
554
638
|
const handleEvent = async (event: any, eventType?: string) => {
|
|
555
639
|
const type = event?.type || eventType;
|
|
@@ -610,6 +694,7 @@ export default class CompletionAdapterOpenAIResponses
|
|
|
610
694
|
finishReason =
|
|
611
695
|
response.incomplete_details?.reason || response.status || finishReason;
|
|
612
696
|
completedResponse = response;
|
|
697
|
+
usedTokens = extractUsedTokens(response);
|
|
613
698
|
return;
|
|
614
699
|
}
|
|
615
700
|
|
|
@@ -687,12 +772,14 @@ export default class CompletionAdapterOpenAIResponses
|
|
|
687
772
|
return {
|
|
688
773
|
content: toolResult,
|
|
689
774
|
finishReason: "tool_call",
|
|
775
|
+
used_tokens: usedTokens,
|
|
690
776
|
};
|
|
691
777
|
} catch (error: any) {
|
|
692
778
|
return {
|
|
693
779
|
error: error?.message || "Tool execution failed",
|
|
694
780
|
content: fullContent || undefined,
|
|
695
781
|
finishReason: "tool_call",
|
|
782
|
+
used_tokens: usedTokens,
|
|
696
783
|
};
|
|
697
784
|
}
|
|
698
785
|
}
|
|
@@ -701,15 +788,31 @@ export default class CompletionAdapterOpenAIResponses
|
|
|
701
788
|
return {
|
|
702
789
|
content: fullContent || undefined,
|
|
703
790
|
finishReason,
|
|
791
|
+
used_tokens: usedTokens,
|
|
704
792
|
};
|
|
705
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
|
+
|
|
706
803
|
return {
|
|
707
804
|
error: error?.message || "Streaming failed",
|
|
708
805
|
content: fullContent || undefined,
|
|
709
806
|
finishReason,
|
|
807
|
+
used_tokens: usedTokens,
|
|
710
808
|
};
|
|
711
809
|
} finally {
|
|
712
810
|
reader.releaseLock();
|
|
713
811
|
}
|
|
812
|
+
} finally {
|
|
813
|
+
if (this.activeAbortController === abortController) {
|
|
814
|
+
this.activeAbortController = null;
|
|
815
|
+
}
|
|
816
|
+
}
|
|
714
817
|
};
|
|
715
818
|
}
|
package/package.json
CHANGED
|
@@ -1,18 +1,27 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adminforth/completion-adapter-openai-responses",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.27",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"type": "module",
|
|
7
|
+
"homepage": "https://adminforth.dev/docs/tutorial/Adapters/completion-adapter-openai-responses/",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/devforth/adminforth-completion-adapter-openai-responses.git"
|
|
11
|
+
},
|
|
12
|
+
"publishConfig": {
|
|
13
|
+
"access": "public"
|
|
14
|
+
},
|
|
7
15
|
"scripts": {
|
|
8
|
-
"build": "tsc
|
|
9
|
-
"rollout": "npm run build && npm publish --access public"
|
|
16
|
+
"build": "tsc"
|
|
10
17
|
},
|
|
11
18
|
"keywords": [],
|
|
12
19
|
"author": "DevForth (https://devforth.io)",
|
|
13
20
|
"license": "MIT",
|
|
14
21
|
"description": "AdminForth completion adapter for the OpenAI Responses API.",
|
|
15
22
|
"devDependencies": {
|
|
23
|
+
"semantic-release": "^24.2.1",
|
|
24
|
+
"semantic-release-slack-bot": "^4.0.2",
|
|
16
25
|
"typescript": "^5.9.3"
|
|
17
26
|
},
|
|
18
27
|
"dependencies": {
|
|
@@ -24,5 +33,30 @@
|
|
|
24
33
|
},
|
|
25
34
|
"peerDependencies": {
|
|
26
35
|
"adminforth": "^2.24.0"
|
|
36
|
+
},
|
|
37
|
+
"release": {
|
|
38
|
+
"plugins": [
|
|
39
|
+
"@semantic-release/commit-analyzer",
|
|
40
|
+
"@semantic-release/release-notes-generator",
|
|
41
|
+
"@semantic-release/npm",
|
|
42
|
+
"@semantic-release/github",
|
|
43
|
+
[
|
|
44
|
+
"semantic-release-slack-bot",
|
|
45
|
+
{
|
|
46
|
+
"packageName": "@adminforth/completion-adapter-openai-responses",
|
|
47
|
+
"notifyOnSuccess": true,
|
|
48
|
+
"notifyOnFail": true,
|
|
49
|
+
"slackIcon": ":package:",
|
|
50
|
+
"markdownReleaseNotes": true
|
|
51
|
+
}
|
|
52
|
+
]
|
|
53
|
+
],
|
|
54
|
+
"branches": [
|
|
55
|
+
"main",
|
|
56
|
+
{
|
|
57
|
+
"name": "next",
|
|
58
|
+
"prerelease": true
|
|
59
|
+
}
|
|
60
|
+
]
|
|
27
61
|
}
|
|
28
62
|
}
|