@robota-sdk/agent-provider 3.0.0-beta.64
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/LICENSE +21 -0
- package/dist/browser/index.d.ts +1104 -0
- package/dist/browser/index.d.ts.map +1 -0
- package/dist/browser/index.js +7 -0
- package/dist/browser/index.js.map +1 -0
- package/dist/loggers/index.cjs +1 -0
- package/dist/loggers/index.d.ts +151 -0
- package/dist/loggers/index.d.ts.map +1 -0
- package/dist/loggers/index.js +2 -0
- package/dist/loggers/index.js.map +1 -0
- package/dist/node/anthropic/index.cjs +1 -0
- package/dist/node/anthropic/index.d.ts +158 -0
- package/dist/node/anthropic/index.d.ts.map +1 -0
- package/dist/node/anthropic/index.js +1 -0
- package/dist/node/anthropic--1vgLC-e.js +5 -0
- package/dist/node/anthropic--1vgLC-e.js.map +1 -0
- package/dist/node/anthropic-BFQ6DSCP.cjs +4 -0
- package/dist/node/bytedance/index.cjs +1 -0
- package/dist/node/bytedance/index.d.ts +74 -0
- package/dist/node/bytedance/index.d.ts.map +1 -0
- package/dist/node/bytedance/index.js +1 -0
- package/dist/node/bytedance-C_0sF_pJ.js +2 -0
- package/dist/node/bytedance-C_0sF_pJ.js.map +1 -0
- package/dist/node/bytedance-DVPxqEiC.cjs +1 -0
- package/dist/node/chunk-Bmb41Sf3.cjs +1 -0
- package/dist/node/deepseek/index.cjs +1 -0
- package/dist/node/deepseek/index.d.ts +2 -0
- package/dist/node/deepseek/index.js +1 -0
- package/dist/node/deepseek-_8Ixx7rA.js +2 -0
- package/dist/node/deepseek-_8Ixx7rA.js.map +1 -0
- package/dist/node/deepseek-oA2Y6bD0.cjs +1 -0
- package/dist/node/gemini/index.cjs +1 -0
- package/dist/node/gemini/index.d.ts +173 -0
- package/dist/node/gemini/index.d.ts.map +1 -0
- package/dist/node/gemini/index.js +1 -0
- package/dist/node/gemini-Bh2U87MY.js +4 -0
- package/dist/node/gemini-Bh2U87MY.js.map +1 -0
- package/dist/node/gemini-DSaNCxZj.cjs +3 -0
- package/dist/node/gemma/index.cjs +1 -0
- package/dist/node/gemma/index.d.ts +2 -0
- package/dist/node/gemma/index.js +1 -0
- package/dist/node/gemma-Dp_AfCUR.js +2 -0
- package/dist/node/gemma-Dp_AfCUR.js.map +1 -0
- package/dist/node/gemma-G-Pf_PnX.cjs +1 -0
- package/dist/node/google/index.cjs +1 -0
- package/dist/node/google/index.d.ts +14 -0
- package/dist/node/google/index.d.ts.map +1 -0
- package/dist/node/google/index.js +2 -0
- package/dist/node/google/index.js.map +1 -0
- package/dist/node/index-B6PnlDMd.d.ts +82 -0
- package/dist/node/index-B6PnlDMd.d.ts.map +1 -0
- package/dist/node/index-B7UvPJcI.d.ts +315 -0
- package/dist/node/index-B7UvPJcI.d.ts.map +1 -0
- package/dist/node/index-BLPOTNb5.d.ts +98 -0
- package/dist/node/index-BLPOTNb5.d.ts.map +1 -0
- package/dist/node/index-BqixM_XD.d.ts +231 -0
- package/dist/node/index-BqixM_XD.d.ts.map +1 -0
- package/dist/node/index-C3beaqKO.d.ts +231 -0
- package/dist/node/index-C3beaqKO.d.ts.map +1 -0
- package/dist/node/index-Cp2XRh9G.d.ts +82 -0
- package/dist/node/index-Cp2XRh9G.d.ts.map +1 -0
- package/dist/node/index-DSv5xruI.d.ts +98 -0
- package/dist/node/index-DSv5xruI.d.ts.map +1 -0
- package/dist/node/index-w0bV1uaP.d.ts +315 -0
- package/dist/node/index-w0bV1uaP.d.ts.map +1 -0
- package/dist/node/index.cjs +1 -0
- package/dist/node/index.d.ts +8 -0
- package/dist/node/index.js +1 -0
- package/dist/node/openai/index.cjs +1 -0
- package/dist/node/openai/index.d.ts +2 -0
- package/dist/node/openai/index.js +1 -0
- package/dist/node/openai-CRQjg4xF.js +2 -0
- package/dist/node/openai-CRQjg4xF.js.map +1 -0
- package/dist/node/openai-compatible-BYfyY5lb.cjs +1 -0
- package/dist/node/openai-compatible-Dm4Sof9e.js +2 -0
- package/dist/node/openai-compatible-Dm4Sof9e.js.map +1 -0
- package/dist/node/openai-xWC6pY7r.cjs +1 -0
- package/dist/node/qwen/index.cjs +1 -0
- package/dist/node/qwen/index.d.ts +2 -0
- package/dist/node/qwen/index.js +1 -0
- package/dist/node/qwen-ChUZobTL.js +2 -0
- package/dist/node/qwen-ChUZobTL.js.map +1 -0
- package/dist/node/qwen-CjT71vSM.cjs +1 -0
- package/package.json +157 -0
- package/src/anthropic/__tests__/abort-streaming.test.ts +199 -0
- package/src/anthropic/__tests__/model-catalog-refresh.test.ts +92 -0
- package/src/anthropic/__tests__/provider-definition.test.ts +55 -0
- package/src/anthropic/__tests__/provider.test.ts +1357 -0
- package/src/anthropic/__tests__/response-parser.test.ts +326 -0
- package/src/anthropic/index.ts +22 -0
- package/src/anthropic/message-converter.ts +181 -0
- package/src/anthropic/model-catalog-refresh.ts +128 -0
- package/src/anthropic/parsers/response-parser.ts +184 -0
- package/src/anthropic/provider-definition.ts +93 -0
- package/src/anthropic/provider.ts +290 -0
- package/src/anthropic/streaming-handler.ts +204 -0
- package/src/anthropic/types/api-types.ts +158 -0
- package/src/anthropic/types.ts +79 -0
- package/src/bytedance/http-client.test.ts +288 -0
- package/src/bytedance/http-client.ts +163 -0
- package/src/bytedance/index.ts +2 -0
- package/src/bytedance/provider.spec.ts +320 -0
- package/src/bytedance/provider.ts +171 -0
- package/src/bytedance/status-mapper.test.ts +299 -0
- package/src/bytedance/status-mapper.ts +141 -0
- package/src/bytedance/types.ts +68 -0
- package/src/deepseek/defaults.ts +4 -0
- package/src/deepseek/index.ts +22 -0
- package/src/deepseek/model-catalog-refresh.test.ts +57 -0
- package/src/deepseek/model-catalog-refresh.ts +105 -0
- package/src/deepseek/model-catalog.ts +55 -0
- package/src/deepseek/provider-definition.test.ts +109 -0
- package/src/deepseek/provider-definition.ts +132 -0
- package/src/deepseek/provider.test.ts +324 -0
- package/src/deepseek/provider.ts +298 -0
- package/src/deepseek/types.ts +37 -0
- package/src/gemini/execution-helpers.ts +233 -0
- package/src/gemini/genai-transport.test.ts +208 -0
- package/src/gemini/image-operations.test.ts +448 -0
- package/src/gemini/image-operations.ts +261 -0
- package/src/gemini/index.ts +11 -0
- package/src/gemini/message-converter.test.ts +616 -0
- package/src/gemini/message-converter.ts +140 -0
- package/src/gemini/model-catalog-refresh.test.ts +107 -0
- package/src/gemini/model-catalog-refresh.ts +92 -0
- package/src/gemini/provider-definition.test.ts +70 -0
- package/src/gemini/provider-definition.ts +78 -0
- package/src/gemini/provider-extended.test.ts +898 -0
- package/src/gemini/provider.spec.ts +216 -0
- package/src/gemini/provider.ts +279 -0
- package/src/gemini/request-converter.ts +226 -0
- package/src/gemini/tool-schema-converter.ts +78 -0
- package/src/gemini/types/api-types.ts +235 -0
- package/src/gemini/types.ts +121 -0
- package/src/gemma/index.ts +5 -0
- package/src/gemma/message-factory.ts +38 -0
- package/src/gemma/provider-definition.test.ts +43 -0
- package/src/gemma/provider-definition.ts +84 -0
- package/src/gemma/provider-projection.ts +49 -0
- package/src/gemma/provider.test.ts +628 -0
- package/src/gemma/provider.ts +308 -0
- package/src/gemma/pseudo-command-envelope.ts +58 -0
- package/src/gemma/pseudo-tool-call-projector.ts +243 -0
- package/src/gemma/pseudo-tool-call-tag-parser.ts +153 -0
- package/src/gemma/pseudo-tool-call-types.ts +31 -0
- package/src/gemma/reasoning-projector.test.ts +52 -0
- package/src/gemma/reasoning-projector.ts +144 -0
- package/src/gemma/streaming-projection.ts +79 -0
- package/src/gemma/tool-call-argument-parser.ts +126 -0
- package/src/gemma/tool-call-projector.test.ts +227 -0
- package/src/gemma/tool-call-projector.ts +264 -0
- package/src/gemma/types.ts +27 -0
- package/src/google/index.ts +11 -0
- package/src/google/provider-compat.test.ts +19 -0
- package/src/google/provider-definition.ts +6 -0
- package/src/google/provider.ts +10 -0
- package/src/google/types.ts +5 -0
- package/src/index.ts +9 -0
- package/src/openai/adapter.test.ts +494 -0
- package/src/openai/adapter.ts +145 -0
- package/src/openai/chat-completions-chat.ts +189 -0
- package/src/openai/executor-integration.test.ts +206 -0
- package/src/openai/index.ts +21 -0
- package/src/openai/interfaces/payload-logger.ts +48 -0
- package/src/openai/loggers/console-payload-logger.test.ts +173 -0
- package/src/openai/loggers/console-payload-logger.ts +94 -0
- package/src/openai/loggers/console.ts +9 -0
- package/src/openai/loggers/file-payload-logger.test.ts +238 -0
- package/src/openai/loggers/file-payload-logger.ts +112 -0
- package/src/openai/loggers/file.ts +9 -0
- package/src/openai/loggers/index.ts +12 -0
- package/src/openai/loggers/sanitize-openai-log-data.test.ts +89 -0
- package/src/openai/loggers/sanitize-openai-log-data.ts +14 -0
- package/src/openai/message-converter.ts +22 -0
- package/src/openai/model-catalog-refresh.test.ts +92 -0
- package/src/openai/model-catalog-refresh.ts +115 -0
- package/src/openai/openai-request-format.ts +92 -0
- package/src/openai/parsers/response-parser.test.ts +407 -0
- package/src/openai/parsers/response-parser.ts +47 -0
- package/src/openai/provider-definition.test.ts +75 -0
- package/src/openai/provider-definition.ts +132 -0
- package/src/openai/provider.test.ts +1402 -0
- package/src/openai/provider.ts +237 -0
- package/src/openai/responses-chat.ts +258 -0
- package/src/openai/responses-converter.ts +112 -0
- package/src/openai/responses-parser.ts +285 -0
- package/src/openai/responses-stream-utils.ts +45 -0
- package/src/openai/responses-types.ts +195 -0
- package/src/openai/streaming/stream-assembler.ts +3 -0
- package/src/openai/streaming/stream-handler.test.ts +367 -0
- package/src/openai/streaming/stream-handler.ts +119 -0
- package/src/openai/types/api-types.ts +112 -0
- package/src/openai/types.ts +194 -0
- package/src/qwen/defaults.ts +26 -0
- package/src/qwen/index.ts +5 -0
- package/src/qwen/model-catalog-refresh.test.ts +91 -0
- package/src/qwen/model-catalog-refresh.ts +97 -0
- package/src/qwen/provider-capabilities.ts +34 -0
- package/src/qwen/provider-definition.test.ts +139 -0
- package/src/qwen/provider-definition.ts +173 -0
- package/src/qwen/provider-streaming-assembly.ts +40 -0
- package/src/qwen/provider.test.ts +640 -0
- package/src/qwen/provider.ts +293 -0
- package/src/qwen/responses-chat.ts +194 -0
- package/src/qwen/responses-converter.ts +104 -0
- package/src/qwen/responses-parser.ts +299 -0
- package/src/qwen/responses-stream-utils.ts +38 -0
- package/src/qwen/types.ts +228 -0
- package/src/shared/openai-compatible/endpoint-probe.test.ts +52 -0
- package/src/shared/openai-compatible/endpoint-probe.ts +43 -0
- package/src/shared/openai-compatible/index.ts +6 -0
- package/src/shared/openai-compatible/message-converter.test.ts +111 -0
- package/src/shared/openai-compatible/message-converter.ts +84 -0
- package/src/shared/openai-compatible/native-payload-observer.test.ts +43 -0
- package/src/shared/openai-compatible/native-payload-observer.ts +26 -0
- package/src/shared/openai-compatible/response-parser.test.ts +172 -0
- package/src/shared/openai-compatible/response-parser.ts +180 -0
- package/src/shared/openai-compatible/stream-assembler.test.ts +266 -0
- package/src/shared/openai-compatible/stream-assembler.ts +248 -0
- package/src/shared/openai-compatible/types.ts +59 -0
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import type { IToolCall, TTextDeltaCallback, TUniversalMessage } from '@robota-sdk/agent-core';
|
|
3
|
+
import type {
|
|
4
|
+
IOpenAIResponsesErrorEvent,
|
|
5
|
+
IOpenAIResponsesFunctionCallOutputItem,
|
|
6
|
+
IOpenAIResponsesMessageOutputItem,
|
|
7
|
+
IOpenAIResponsesReasoningOutputItem,
|
|
8
|
+
IOpenAIResponsesResponse,
|
|
9
|
+
IOpenAIResponsesUsage,
|
|
10
|
+
TOpenAIResponsesOutputContent,
|
|
11
|
+
TOpenAIResponsesOutputItem,
|
|
12
|
+
TOpenAIResponsesStreamEvent,
|
|
13
|
+
} from './responses-types';
|
|
14
|
+
import { streamWithAbort } from './responses-stream-utils';
|
|
15
|
+
|
|
16
|
+
interface IOpenAIResponsesStreamAssemblyOptions {
|
|
17
|
+
stream: AsyncIterable<TOpenAIResponsesStreamEvent>;
|
|
18
|
+
onTextDelta?: TTextDeltaCallback;
|
|
19
|
+
signal?: AbortSignal;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface IOpenAIResponsesReasoningMetadata {
|
|
23
|
+
reasoningSummaryCount: number;
|
|
24
|
+
reasoningSummaries: string[];
|
|
25
|
+
hasEncryptedReasoning: boolean;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface IOpenAIResponseUsage {
|
|
29
|
+
promptTokens: number;
|
|
30
|
+
completionTokens: number;
|
|
31
|
+
totalTokens: number;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
interface IOpenAIResponsesStreamState {
|
|
35
|
+
textParts: string[];
|
|
36
|
+
toolCalls: IToolCall[];
|
|
37
|
+
completedResponse?: IOpenAIResponsesResponse;
|
|
38
|
+
reasoning: IOpenAIResponsesReasoningMetadata;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function parseOpenAIResponsesResponse(
|
|
42
|
+
response: IOpenAIResponsesResponse,
|
|
43
|
+
): TUniversalMessage {
|
|
44
|
+
assertUsableResponse(response);
|
|
45
|
+
const output = response.output ?? [];
|
|
46
|
+
const content = response.output_text ?? extractMessageText(output);
|
|
47
|
+
return buildAssistantMessage({
|
|
48
|
+
content,
|
|
49
|
+
toolCalls: extractFunctionToolCalls(output),
|
|
50
|
+
response,
|
|
51
|
+
reasoning: collectReasoningMetadata(output),
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export async function assembleOpenAIResponsesStream(
|
|
56
|
+
options: IOpenAIResponsesStreamAssemblyOptions,
|
|
57
|
+
): Promise<TUniversalMessage> {
|
|
58
|
+
const state: IOpenAIResponsesStreamState = {
|
|
59
|
+
textParts: [],
|
|
60
|
+
toolCalls: [],
|
|
61
|
+
reasoning: createEmptyReasoningMetadata(),
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
for await (const event of streamWithAbort(options.stream, options.signal)) {
|
|
65
|
+
applyStreamEvent(state, event, options.onTextDelta);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (state.completedResponse !== undefined) {
|
|
69
|
+
return buildMessageFromCompletedResponse(state);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return buildAssistantMessage({
|
|
73
|
+
content: state.textParts.join(''),
|
|
74
|
+
toolCalls: state.toolCalls,
|
|
75
|
+
reasoning: state.reasoning,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function applyStreamEvent(
|
|
80
|
+
state: IOpenAIResponsesStreamState,
|
|
81
|
+
event: TOpenAIResponsesStreamEvent,
|
|
82
|
+
onTextDelta: TTextDeltaCallback | undefined,
|
|
83
|
+
): void {
|
|
84
|
+
if (event.type === 'response.output_text.delta') {
|
|
85
|
+
state.textParts.push(event.delta);
|
|
86
|
+
onTextDelta?.(event.delta);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (event.type === 'response.completed') {
|
|
91
|
+
state.completedResponse = event.response;
|
|
92
|
+
mergeReasoningMetadata(state.reasoning, collectReasoningMetadata(event.response.output ?? []));
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (event.type === 'response.output_item.done') {
|
|
97
|
+
applyOutputItem(state, event.item);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (
|
|
102
|
+
event.type === 'response.error' ||
|
|
103
|
+
event.type === 'response.failed' ||
|
|
104
|
+
event.type === 'response.incomplete'
|
|
105
|
+
) {
|
|
106
|
+
throw new Error(`OpenAI Responses API failed: ${extractErrorMessage(event)}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function applyOutputItem(
|
|
111
|
+
state: IOpenAIResponsesStreamState,
|
|
112
|
+
item: TOpenAIResponsesOutputItem,
|
|
113
|
+
): void {
|
|
114
|
+
if (isFunctionCallOutputItem(item)) {
|
|
115
|
+
state.toolCalls.push(convertFunctionCall(item));
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
if (isReasoningOutputItem(item)) {
|
|
119
|
+
mergeReasoningMetadata(state.reasoning, collectReasoningMetadata([item]));
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function buildMessageFromCompletedResponse(state: IOpenAIResponsesStreamState): TUniversalMessage {
|
|
124
|
+
const response = state.completedResponse;
|
|
125
|
+
if (response === undefined) {
|
|
126
|
+
throw new Error('OpenAI Responses stream completed without response metadata');
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
assertUsableResponse(response);
|
|
130
|
+
const output = response.output ?? [];
|
|
131
|
+
const responseToolCalls = extractFunctionToolCalls(output);
|
|
132
|
+
const content =
|
|
133
|
+
state.textParts.length > 0
|
|
134
|
+
? state.textParts.join('')
|
|
135
|
+
: (response.output_text ?? extractMessageText(output));
|
|
136
|
+
const reasoning = collectReasoningMetadata(output);
|
|
137
|
+
mergeReasoningMetadata(reasoning, state.reasoning);
|
|
138
|
+
|
|
139
|
+
return buildAssistantMessage({
|
|
140
|
+
content,
|
|
141
|
+
toolCalls: responseToolCalls.length > 0 ? responseToolCalls : state.toolCalls,
|
|
142
|
+
response,
|
|
143
|
+
reasoning,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function buildAssistantMessage(input: {
|
|
148
|
+
content: string;
|
|
149
|
+
toolCalls: IToolCall[];
|
|
150
|
+
response?: IOpenAIResponsesResponse;
|
|
151
|
+
reasoning: IOpenAIResponsesReasoningMetadata;
|
|
152
|
+
}): TUniversalMessage {
|
|
153
|
+
return {
|
|
154
|
+
id: randomUUID(),
|
|
155
|
+
role: 'assistant',
|
|
156
|
+
content: input.content,
|
|
157
|
+
state: 'complete',
|
|
158
|
+
timestamp: new Date(),
|
|
159
|
+
...(input.toolCalls.length > 0 && { toolCalls: input.toolCalls }),
|
|
160
|
+
...(input.response?.usage !== undefined && { usage: mapUsage(input.response.usage) }),
|
|
161
|
+
metadata: buildMetadata(input.response, input.reasoning),
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function buildMetadata(
|
|
166
|
+
response: IOpenAIResponsesResponse | undefined,
|
|
167
|
+
reasoning: IOpenAIResponsesReasoningMetadata,
|
|
168
|
+
): NonNullable<TUniversalMessage['metadata']> {
|
|
169
|
+
return {
|
|
170
|
+
providerApiSurface: 'responses',
|
|
171
|
+
...(response?.id !== undefined && { responseId: response.id }),
|
|
172
|
+
...(response?.model !== undefined && { model: response.model }),
|
|
173
|
+
...(response?.status !== undefined && { finishReason: response.status }),
|
|
174
|
+
...(reasoning.reasoningSummaryCount > 0 && {
|
|
175
|
+
reasoningSummaryCount: reasoning.reasoningSummaryCount,
|
|
176
|
+
reasoningSummaries: reasoning.reasoningSummaries,
|
|
177
|
+
}),
|
|
178
|
+
...(reasoning.hasEncryptedReasoning && { hasEncryptedReasoning: true }),
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function mapUsage(usage: IOpenAIResponsesUsage): IOpenAIResponseUsage {
|
|
183
|
+
return {
|
|
184
|
+
promptTokens: usage.input_tokens ?? 0,
|
|
185
|
+
completionTokens: usage.output_tokens ?? 0,
|
|
186
|
+
totalTokens: usage.total_tokens ?? 0,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function extractFunctionToolCalls(output: readonly TOpenAIResponsesOutputItem[]): IToolCall[] {
|
|
191
|
+
return output.filter(isFunctionCallOutputItem).map((item) => convertFunctionCall(item));
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function isFunctionCallOutputItem(
|
|
195
|
+
item: TOpenAIResponsesOutputItem,
|
|
196
|
+
): item is IOpenAIResponsesFunctionCallOutputItem {
|
|
197
|
+
return (
|
|
198
|
+
item.type === 'function_call' && 'call_id' in item && 'name' in item && 'arguments' in item
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function convertFunctionCall(item: IOpenAIResponsesFunctionCallOutputItem): IToolCall {
|
|
203
|
+
return {
|
|
204
|
+
id: item.call_id,
|
|
205
|
+
type: 'function',
|
|
206
|
+
function: {
|
|
207
|
+
name: item.name,
|
|
208
|
+
arguments: item.arguments,
|
|
209
|
+
},
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function extractMessageText(output: readonly TOpenAIResponsesOutputItem[]): string {
|
|
214
|
+
return output
|
|
215
|
+
.filter(isMessageOutputItem)
|
|
216
|
+
.flatMap((item) => item.content)
|
|
217
|
+
.map(extractTextContent)
|
|
218
|
+
.join('');
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function extractTextContent(content: TOpenAIResponsesOutputContent): string {
|
|
222
|
+
if (content.type === 'refusal') {
|
|
223
|
+
return content.refusal;
|
|
224
|
+
}
|
|
225
|
+
return content.text;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function isMessageOutputItem(
|
|
229
|
+
item: TOpenAIResponsesOutputItem,
|
|
230
|
+
): item is IOpenAIResponsesMessageOutputItem {
|
|
231
|
+
return item.type === 'message' && 'content' in item && Array.isArray(item.content);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function collectReasoningMetadata(
|
|
235
|
+
output: readonly TOpenAIResponsesOutputItem[],
|
|
236
|
+
): IOpenAIResponsesReasoningMetadata {
|
|
237
|
+
const metadata = createEmptyReasoningMetadata();
|
|
238
|
+
for (const item of output) {
|
|
239
|
+
if (!isReasoningOutputItem(item)) {
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
const summaries = item.summary?.map((summary) => summary.text).filter(isString) ?? [];
|
|
243
|
+
metadata.reasoningSummaryCount += summaries.length;
|
|
244
|
+
metadata.reasoningSummaries.push(...summaries);
|
|
245
|
+
metadata.hasEncryptedReasoning = metadata.hasEncryptedReasoning || !!item.encrypted_content;
|
|
246
|
+
}
|
|
247
|
+
return metadata;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function isReasoningOutputItem(
|
|
251
|
+
item: TOpenAIResponsesOutputItem,
|
|
252
|
+
): item is IOpenAIResponsesReasoningOutputItem {
|
|
253
|
+
return item.type === 'reasoning';
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function createEmptyReasoningMetadata(): IOpenAIResponsesReasoningMetadata {
|
|
257
|
+
return {
|
|
258
|
+
reasoningSummaryCount: 0,
|
|
259
|
+
reasoningSummaries: [],
|
|
260
|
+
hasEncryptedReasoning: false,
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function mergeReasoningMetadata(
|
|
265
|
+
target: IOpenAIResponsesReasoningMetadata,
|
|
266
|
+
source: IOpenAIResponsesReasoningMetadata,
|
|
267
|
+
): void {
|
|
268
|
+
target.reasoningSummaryCount += source.reasoningSummaryCount;
|
|
269
|
+
target.reasoningSummaries.push(...source.reasoningSummaries);
|
|
270
|
+
target.hasEncryptedReasoning = target.hasEncryptedReasoning || source.hasEncryptedReasoning;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function isString(value: string | undefined): value is string {
|
|
274
|
+
return value !== undefined;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function assertUsableResponse(response: IOpenAIResponsesResponse): void {
|
|
278
|
+
if (response.status === 'failed' || response.status === 'incomplete') {
|
|
279
|
+
throw new Error(`OpenAI Responses API failed: ${response.error?.message ?? response.status}`);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function extractErrorMessage(event: IOpenAIResponsesErrorEvent): string {
|
|
284
|
+
return event.message ?? event.error?.message ?? event.response?.error?.message ?? 'unknown error';
|
|
285
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export async function* streamWithAbort<T>(
|
|
2
|
+
source: AsyncIterable<T>,
|
|
3
|
+
signal?: AbortSignal,
|
|
4
|
+
): AsyncGenerator<T> {
|
|
5
|
+
const iterator = source[Symbol.asyncIterator]();
|
|
6
|
+
try {
|
|
7
|
+
while (!signal?.aborted) {
|
|
8
|
+
const item = await nextStreamItem(iterator, signal);
|
|
9
|
+
if (item.done || signal?.aborted) {
|
|
10
|
+
break;
|
|
11
|
+
}
|
|
12
|
+
yield item.value;
|
|
13
|
+
}
|
|
14
|
+
} finally {
|
|
15
|
+
if (signal?.aborted) {
|
|
16
|
+
await iterator.return?.();
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async function nextStreamItem<T>(
|
|
22
|
+
iterator: AsyncIterator<T>,
|
|
23
|
+
signal?: AbortSignal,
|
|
24
|
+
): Promise<IteratorResult<T>> {
|
|
25
|
+
if (!signal) {
|
|
26
|
+
return iterator.next();
|
|
27
|
+
}
|
|
28
|
+
if (signal.aborted) {
|
|
29
|
+
return { done: true, value: undefined as T };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
let abortListener: (() => void) | undefined;
|
|
33
|
+
const aborted = new Promise<IteratorResult<T>>((resolve) => {
|
|
34
|
+
abortListener = (): void => resolve({ done: true, value: undefined as T });
|
|
35
|
+
signal.addEventListener('abort', abortListener, { once: true });
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
return await Promise.race([iterator.next(), aborted]);
|
|
40
|
+
} finally {
|
|
41
|
+
if (abortListener) {
|
|
42
|
+
signal.removeEventListener('abort', abortListener);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import type { IToolSchema } from '@robota-sdk/agent-core';
|
|
2
|
+
import type {
|
|
3
|
+
IOpenAIJsonSchemaDefinition,
|
|
4
|
+
IOpenAIResponsesReasoningOptions,
|
|
5
|
+
TOpenAIProviderOptionValue,
|
|
6
|
+
} from './types';
|
|
7
|
+
|
|
8
|
+
export type TOpenAIResponsesInputRole = 'user' | 'assistant' | 'system' | 'developer';
|
|
9
|
+
|
|
10
|
+
export interface IOpenAIResponsesInputTextContent {
|
|
11
|
+
type: 'input_text';
|
|
12
|
+
text: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface IOpenAIResponsesInputImageContent {
|
|
16
|
+
type: 'input_image';
|
|
17
|
+
image_url: string;
|
|
18
|
+
detail?: 'auto' | 'low' | 'high';
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export type TOpenAIResponsesInputContent =
|
|
22
|
+
| IOpenAIResponsesInputTextContent
|
|
23
|
+
| IOpenAIResponsesInputImageContent;
|
|
24
|
+
|
|
25
|
+
export interface IOpenAIResponsesMessageInput {
|
|
26
|
+
type?: 'message';
|
|
27
|
+
role: TOpenAIResponsesInputRole;
|
|
28
|
+
content: string | TOpenAIResponsesInputContent[];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface IOpenAIResponsesFunctionCallInput {
|
|
32
|
+
type: 'function_call';
|
|
33
|
+
call_id: string;
|
|
34
|
+
name: string;
|
|
35
|
+
arguments: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface IOpenAIResponsesFunctionCallOutputInput {
|
|
39
|
+
type: 'function_call_output';
|
|
40
|
+
call_id: string;
|
|
41
|
+
output: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export type TOpenAIResponsesInputItem =
|
|
45
|
+
| IOpenAIResponsesMessageInput
|
|
46
|
+
| IOpenAIResponsesFunctionCallInput
|
|
47
|
+
| IOpenAIResponsesFunctionCallOutputInput;
|
|
48
|
+
|
|
49
|
+
export interface IOpenAIResponsesFunctionTool {
|
|
50
|
+
type: 'function';
|
|
51
|
+
name: string;
|
|
52
|
+
parameters: IToolSchema['parameters'];
|
|
53
|
+
strict: boolean | null;
|
|
54
|
+
description?: string;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export type TOpenAIResponsesTool = IOpenAIResponsesFunctionTool;
|
|
58
|
+
|
|
59
|
+
export interface IOpenAIResponsesTextFormatText {
|
|
60
|
+
type: 'text';
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface IOpenAIResponsesTextFormatJsonObject {
|
|
64
|
+
type: 'json_object';
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface IOpenAIResponsesTextFormatJsonSchema extends IOpenAIJsonSchemaDefinition {
|
|
68
|
+
type: 'json_schema';
|
|
69
|
+
schema: Record<string, TOpenAIProviderOptionValue>;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export type TOpenAIResponsesTextFormat =
|
|
73
|
+
| IOpenAIResponsesTextFormatText
|
|
74
|
+
| IOpenAIResponsesTextFormatJsonObject
|
|
75
|
+
| IOpenAIResponsesTextFormatJsonSchema;
|
|
76
|
+
|
|
77
|
+
export interface IOpenAIResponsesTextConfig {
|
|
78
|
+
format: TOpenAIResponsesTextFormat;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface IOpenAIResponsesRequestBase {
|
|
82
|
+
model: string;
|
|
83
|
+
input: TOpenAIResponsesInputItem[];
|
|
84
|
+
tools?: TOpenAIResponsesTool[];
|
|
85
|
+
tool_choice?: 'auto' | 'none' | 'required';
|
|
86
|
+
text?: IOpenAIResponsesTextConfig;
|
|
87
|
+
temperature?: number;
|
|
88
|
+
max_output_tokens?: number;
|
|
89
|
+
reasoning?: IOpenAIResponsesReasoningOptions;
|
|
90
|
+
include?: 'reasoning.encrypted_content'[];
|
|
91
|
+
store?: boolean;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export interface IOpenAIResponsesRequestNonStreaming extends IOpenAIResponsesRequestBase {
|
|
95
|
+
stream?: false;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export interface IOpenAIResponsesRequestStreaming extends IOpenAIResponsesRequestBase {
|
|
99
|
+
stream: true;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export interface IOpenAIResponsesTextOutputContent {
|
|
103
|
+
type: 'output_text' | 'text';
|
|
104
|
+
text: string;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export interface IOpenAIResponsesRefusalOutputContent {
|
|
108
|
+
type: 'refusal';
|
|
109
|
+
refusal: string;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export type TOpenAIResponsesOutputContent =
|
|
113
|
+
| IOpenAIResponsesTextOutputContent
|
|
114
|
+
| IOpenAIResponsesRefusalOutputContent;
|
|
115
|
+
|
|
116
|
+
export interface IOpenAIResponsesMessageOutputItem {
|
|
117
|
+
type: 'message';
|
|
118
|
+
role?: 'assistant';
|
|
119
|
+
content: TOpenAIResponsesOutputContent[];
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export interface IOpenAIResponsesFunctionCallOutputItem {
|
|
123
|
+
type: 'function_call';
|
|
124
|
+
call_id: string;
|
|
125
|
+
name: string;
|
|
126
|
+
arguments: string;
|
|
127
|
+
id?: string;
|
|
128
|
+
status?: string;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export interface IOpenAIResponsesReasoningOutputItem {
|
|
132
|
+
type: 'reasoning';
|
|
133
|
+
summary?: Array<{ text?: string }>;
|
|
134
|
+
encrypted_content?: string;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export interface IOpenAIResponsesGenericOutputItem {
|
|
138
|
+
type: string;
|
|
139
|
+
id?: string;
|
|
140
|
+
status?: string;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export type TOpenAIResponsesOutputItem =
|
|
144
|
+
| IOpenAIResponsesMessageOutputItem
|
|
145
|
+
| IOpenAIResponsesFunctionCallOutputItem
|
|
146
|
+
| IOpenAIResponsesReasoningOutputItem
|
|
147
|
+
| IOpenAIResponsesGenericOutputItem;
|
|
148
|
+
|
|
149
|
+
export interface IOpenAIResponsesUsage {
|
|
150
|
+
input_tokens?: number;
|
|
151
|
+
output_tokens?: number;
|
|
152
|
+
total_tokens?: number;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export interface IOpenAIResponsesErrorBody {
|
|
156
|
+
message?: string;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export interface IOpenAIResponsesResponse {
|
|
160
|
+
id?: string;
|
|
161
|
+
model?: string;
|
|
162
|
+
output_text?: string;
|
|
163
|
+
output?: TOpenAIResponsesOutputItem[];
|
|
164
|
+
usage?: IOpenAIResponsesUsage;
|
|
165
|
+
status?: string;
|
|
166
|
+
error?: IOpenAIResponsesErrorBody | null;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export interface IOpenAIResponsesTextDeltaEvent {
|
|
170
|
+
type: 'response.output_text.delta';
|
|
171
|
+
delta: string;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export interface IOpenAIResponsesCompletedEvent {
|
|
175
|
+
type: 'response.completed';
|
|
176
|
+
response: IOpenAIResponsesResponse;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export interface IOpenAIResponsesOutputItemDoneEvent {
|
|
180
|
+
type: 'response.output_item.done';
|
|
181
|
+
item: TOpenAIResponsesOutputItem;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export interface IOpenAIResponsesErrorEvent {
|
|
185
|
+
type: 'response.error' | 'response.failed' | 'response.incomplete';
|
|
186
|
+
message?: string;
|
|
187
|
+
error?: IOpenAIResponsesErrorBody;
|
|
188
|
+
response?: IOpenAIResponsesResponse;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export type TOpenAIResponsesStreamEvent =
|
|
192
|
+
| IOpenAIResponsesTextDeltaEvent
|
|
193
|
+
| IOpenAIResponsesCompletedEvent
|
|
194
|
+
| IOpenAIResponsesOutputItemDoneEvent
|
|
195
|
+
| IOpenAIResponsesErrorEvent;
|