@robota-sdk/agent-provider-openai 3.0.0-beta.63 → 3.0.0-beta.82

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/LICENSE +661 -21
  2. package/dist/node/index.cjs +1 -2
  3. package/dist/node/index.d.cts +271 -267
  4. package/dist/node/index.d.cts.map +1 -0
  5. package/dist/node/index.d.ts +271 -267
  6. package/dist/node/index.d.ts.map +1 -0
  7. package/dist/node/index.js +2 -2
  8. package/dist/node/index.js.map +1 -0
  9. package/dist/node/loggers/index.cjs +1 -0
  10. package/dist/node/loggers/index.d.cts +94 -0
  11. package/dist/node/loggers/index.d.cts.map +1 -0
  12. package/dist/node/loggers/index.d.ts +94 -0
  13. package/dist/node/loggers/index.d.ts.map +1 -0
  14. package/dist/node/loggers/index.js +2 -0
  15. package/dist/node/loggers/index.js.map +1 -0
  16. package/dist/node/payload-logger-BaW0K8yI.d.cts +61 -0
  17. package/dist/node/payload-logger-BaW0K8yI.d.cts.map +1 -0
  18. package/dist/node/payload-logger-BaW0K8yI.d.ts +61 -0
  19. package/dist/node/payload-logger-BaW0K8yI.d.ts.map +1 -0
  20. package/dist/node/rolldown-runtime-CMqjfN_6.cjs +1 -0
  21. package/package.json +69 -86
  22. package/src/index.ts +1 -0
  23. package/src/openai/__tests__/endpoint-provenance.test.ts +44 -0
  24. package/src/openai/__tests__/provider-errors.test.ts +114 -0
  25. package/src/openai/__tests__/request-format.test.ts +59 -0
  26. package/src/openai/__tests__/strict-tools-closure.test.ts +136 -0
  27. package/src/openai/__tests__/tool-schema-projection.test.ts +302 -0
  28. package/src/openai/__tests__/trace-context-wire.test.ts +114 -0
  29. package/src/openai/adapter.test.ts +494 -0
  30. package/src/openai/adapter.ts +145 -0
  31. package/src/openai/chat-completions-chat.ts +225 -0
  32. package/src/openai/executor-integration.test.ts +213 -0
  33. package/src/openai/index.ts +19 -0
  34. package/src/openai/interfaces/payload-logger.ts +48 -0
  35. package/src/openai/loggers/console-payload-logger.test.ts +173 -0
  36. package/src/openai/loggers/console-payload-logger.ts +96 -0
  37. package/src/openai/loggers/console.ts +9 -0
  38. package/src/openai/loggers/file-payload-logger.test.ts +243 -0
  39. package/src/openai/loggers/file-payload-logger.ts +124 -0
  40. package/src/openai/loggers/file.ts +9 -0
  41. package/src/openai/loggers/index.ts +12 -0
  42. package/src/openai/loggers/sanitize-openai-log-data.test.ts +89 -0
  43. package/src/openai/loggers/sanitize-openai-log-data.ts +14 -0
  44. package/src/openai/message-converter.ts +23 -0
  45. package/src/openai/model-effort-table.ts +19 -0
  46. package/src/openai/model-effort-verification-config.test.ts +45 -0
  47. package/src/openai/model-effort-verification-config.ts +35 -0
  48. package/src/openai/openai-request-format.ts +116 -0
  49. package/src/openai/parsers/response-parser.test.ts +407 -0
  50. package/src/openai/parsers/response-parser.ts +48 -0
  51. package/src/openai/provider-definition.test.ts +74 -0
  52. package/src/openai/provider-definition.ts +139 -0
  53. package/src/openai/provider.test.ts +1686 -0
  54. package/src/openai/provider.ts +392 -0
  55. package/src/openai/reasoning-effort.test.ts +272 -0
  56. package/src/openai/reasoning-effort.ts +49 -0
  57. package/src/openai/request-id.ts +39 -0
  58. package/src/openai/request-options.ts +14 -0
  59. package/src/openai/responses-chat.ts +285 -0
  60. package/src/openai/responses-converter.ts +122 -0
  61. package/src/openai/responses-parser.ts +297 -0
  62. package/src/openai/responses-stream-utils.ts +45 -0
  63. package/src/openai/responses-types.ts +193 -0
  64. package/src/openai/streaming/stream-assembler.ts +3 -0
  65. package/src/openai/types/api-types.ts +113 -0
  66. package/src/openai/types.ts +235 -0
  67. package/CHANGELOG.md +0 -614
  68. package/README.md +0 -467
  69. package/dist/browser/index.d.ts +0 -307
  70. package/dist/browser/index.js +0 -2
@@ -1,307 +0,0 @@
1
- import OpenAI from 'openai';
2
- import { ILogger, IExecutor, TProviderOptionValueBase, AbstractAIProvider, TTextDeltaCallback, TUniversalMessage, IChatOptions, IProviderCapabilities, IProviderProfileConfig, IProviderModelCatalog, IProviderDefinition } from '@robota-sdk/agent-core';
3
-
4
- /**
5
- * Payload logging data structure
6
- */
7
- interface IOpenAILogData {
8
- model: string;
9
- messagesCount: number;
10
- hasTools: boolean;
11
- temperature?: number | undefined;
12
- maxTokens?: number | undefined;
13
- timestamp: string;
14
- requestId?: string | undefined;
15
- }
16
-
17
- /**
18
- * IPayloadLogger interface for logging OpenAI API payloads
19
- *
20
- * This interface provides a contract for different logging implementations:
21
- * - FilePayloadLogger: Node.js file-based logging
22
- * - ConsolePayloadLogger: Browser console-based logging
23
- * - Custom implementations: User-defined loggers
24
- */
25
- interface IPayloadLogger {
26
- /**
27
- * Check if logging is enabled
28
- * @returns true if logging is active, false otherwise
29
- */
30
- isEnabled(): boolean;
31
- /**
32
- * Log API payload data
33
- * @param payload - The API request/response payload data
34
- * @param type - Type of operation ('chat' or 'stream')
35
- */
36
- logPayload(payload: IOpenAILogData, type: 'chat' | 'stream'): Promise<void>;
37
- }
38
- /**
39
- * Configuration options for payload loggers
40
- */
41
- interface IPayloadLoggerOptions {
42
- /**
43
- * Whether logging is enabled
44
- * @defaultValue true
45
- */
46
- enabled?: boolean;
47
- /**
48
- * Include timestamp in log entries
49
- * @defaultValue true
50
- */
51
- includeTimestamp?: boolean;
52
- /**
53
- * Logger instance for console output
54
- * @defaultValue SilentLogger
55
- */
56
- logger?: ILogger;
57
- }
58
-
59
- type TOpenAIApiSurface = 'responses' | 'chat-completions';
60
- interface IOpenAIJsonSchemaDefinition {
61
- name: string;
62
- description?: string;
63
- schema?: Record<string, TOpenAIProviderOptionValue>;
64
- strict?: boolean;
65
- }
66
- interface IOpenAIResponsesReasoningOptions {
67
- effort?: 'low' | 'medium' | 'high';
68
- summary?: 'auto' | 'concise' | 'detailed';
69
- }
70
- interface IOpenAINativeWebToolsOptions {
71
- webSearch?: boolean;
72
- webFetch?: boolean;
73
- }
74
- /**
75
- * Valid provider option value types
76
- */
77
- type TOpenAIProviderOptionValue = string | number | boolean | undefined | null | IOpenAIJsonSchemaDefinition | IOpenAINativeWebToolsOptions | IOpenAIResponsesReasoningOptions | OpenAI | IPayloadLogger | ILogger | IExecutor | TProviderOptionValueBase | TOpenAIProviderOptionValue[] | {
78
- [key: string]: TOpenAIProviderOptionValue;
79
- };
80
- /**
81
- * OpenAI provider options
82
- */
83
- interface IOpenAIProviderOptions {
84
- /**
85
- * Additional provider-specific options
86
- */
87
- [key: string]: TOpenAIProviderOptionValue;
88
- /**
89
- * OpenAI API key (required when client is not provided)
90
- */
91
- apiKey?: string;
92
- /**
93
- * OpenAI organization ID (optional)
94
- */
95
- organization?: string;
96
- /**
97
- * API request timeout (milliseconds)
98
- */
99
- timeout?: number;
100
- /**
101
- * API base URL (default: 'https://api.openai.com/v1')
102
- */
103
- baseURL?: string;
104
- /**
105
- * Default model used when chat options do not provide a model.
106
- */
107
- defaultModel?: string;
108
- /**
109
- * API surface to use for direct OpenAI calls.
110
- *
111
- * Defaults to Responses for official OpenAI calls. Profiles with baseURL use
112
- * Chat Completions by default for OpenAI-compatible endpoint compatibility.
113
- */
114
- apiSurface?: TOpenAIApiSurface;
115
- /**
116
- * Response format (default: 'text')
117
- * - 'text': Plain text response
118
- * - 'json_object': JSON object mode (requires system message)
119
- * - 'json_schema': Structured Outputs with schema validation
120
- */
121
- responseFormat?: 'text' | 'json_object' | 'json_schema';
122
- /**
123
- * JSON schema for structured outputs (required when responseFormat is 'json_schema')
124
- */
125
- jsonSchema?: IOpenAIJsonSchemaDefinition;
126
- /**
127
- * Responses API reasoning controls. Hidden reasoning is never exposed in message
128
- * content; only explicit summaries/encrypted items requested here are represented.
129
- */
130
- reasoning?: IOpenAIResponsesReasoningOptions;
131
- /**
132
- * Whether OpenAI should store Responses API results. Defaults to OpenAI API behavior.
133
- */
134
- store?: boolean;
135
- /**
136
- * Include encrypted reasoning items for stateless reasoning continuation.
137
- */
138
- includeEncryptedReasoning?: boolean;
139
- /**
140
- * Opt into strict custom function parameter validation where supported.
141
- */
142
- strictTools?: boolean;
143
- /**
144
- * Provider-native hosted web tool request from provider profile options.
145
- *
146
- * OpenAI-compatible Chat Completions endpoints do not support this Robota
147
- * native web contract. The provider rejects unsupported configurations before
148
- * any model request is sent.
149
- */
150
- nativeWebTools?: IOpenAINativeWebToolsOptions;
151
- /**
152
- * OpenAI client instance (optional: will be created from apiKey if not provided)
153
- */
154
- client?: OpenAI;
155
- /**
156
- * Payload logger instance for debugging API requests/responses
157
- *
158
- * Use different implementations based on your environment:
159
- * - FilePayloadLogger: Node.js file-based logging
160
- * - ConsolePayloadLogger: Browser console-based logging
161
- * - Custom: Implement IPayloadLogger interface
162
- *
163
- * @example
164
- * ```typescript
165
- * // Node.js
166
- * import { FilePayloadLogger } from '@robota-sdk/agent-provider-openai/loggers/file';
167
- * const provider = new OpenAIProvider({
168
- * client: openaiClient,
169
- * payloadLogger: new FilePayloadLogger({ logDir: './logs/openai' })
170
- * });
171
- *
172
- * // Browser
173
- * import { ConsolePayloadLogger } from '@robota-sdk/agent-provider-openai/loggers/console';
174
- * const provider = new OpenAIProvider({
175
- * client: openaiClient,
176
- * payloadLogger: new ConsolePayloadLogger()
177
- * });
178
- * ```
179
- */
180
- payloadLogger?: IPayloadLogger;
181
- /**
182
- * Optional executor for handling AI requests
183
- *
184
- * When provided, the provider will delegate all chat operations to this executor
185
- * instead of making direct API calls. This enables remote execution capabilities.
186
- *
187
- * @example
188
- * ```typescript
189
- * import { LocalExecutor, RemoteExecutor } from '@robota-sdk/agent-core';
190
- *
191
- * // Local execution (registers this provider)
192
- * const localExecutor = new LocalExecutor();
193
- * localExecutor.registerProvider('openai', new OpenAIProvider({ apiKey: 'sk-...' }));
194
- *
195
- * // Remote execution
196
- * const remoteExecutor = new RemoteExecutor({
197
- * serverUrl: 'https://api.robota.io',
198
- * userApiKey: 'user-token-123'
199
- * });
200
- *
201
- * const provider = new OpenAIProvider({
202
- * executor: remoteExecutor // No direct API key needed
203
- * });
204
- * ```
205
- */
206
- executor?: IExecutor;
207
- /**
208
- * Logger instance for internal OpenAI provider logging
209
- * @defaultValue SilentLogger
210
- */
211
- logger?: ILogger;
212
- }
213
-
214
- /**
215
- * OpenAI provider implementation for Robota
216
- *
217
- * Provides integration with OpenAI models through the Robota provider contract.
218
- * Uses OpenAI SDK native types internally for optimal performance and feature support.
219
- *
220
- * @public
221
- */
222
- declare class OpenAIProvider extends AbstractAIProvider {
223
- readonly name = "openai";
224
- readonly version = "1.0.0";
225
- private readonly client?;
226
- private readonly options;
227
- private readonly apiSurface;
228
- private readonly payloadLogger;
229
- private readonly responseParser;
230
- /**
231
- * Optional callback for text deltas during streaming.
232
- * Set by the consumer (e.g., Session) to receive real-time text chunks.
233
- * When set, chat() uses streaming internally while still returning
234
- * the complete assembled message.
235
- */
236
- onTextDelta?: TTextDeltaCallback;
237
- constructor(options: IOpenAIProviderOptions);
238
- chat(messages: TUniversalMessage[], options?: IChatOptions): Promise<TUniversalMessage>;
239
- chatStream(messages: TUniversalMessage[], options?: IChatOptions): AsyncIterable<TUniversalMessage>;
240
- supportsTools(): boolean;
241
- getCapabilities(): IProviderCapabilities;
242
- validateConfig(): boolean;
243
- dispose(): Promise<void>;
244
- protected validateMessages(messages: TUniversalMessage[]): void;
245
- }
246
-
247
- /**
248
- * OpenAI Conversation Adapter
249
- *
250
- * Converts between TUniversalMessage format and OpenAI native types.
251
- * Provides bidirectional conversion for seamless integration.
252
- *
253
- * @public
254
- */
255
- declare class OpenAIConversationAdapter {
256
- /**
257
- * Filter messages for OpenAI compatibility
258
- *
259
- * OpenAI has specific requirements:
260
- * - Tool messages must have valid toolCallId
261
- * - Messages must be in proper sequence
262
- * - Tool messages without toolCallId should be excluded
263
- */
264
- static filterMessagesForOpenAI(messages: TUniversalMessage[]): TUniversalMessage[];
265
- /**
266
- * Convert TUniversalMessage array to OpenAI message format
267
- * Now properly handles tool messages for OpenAI's tool calling feature
268
- */
269
- static toOpenAIFormat(messages: TUniversalMessage[]): OpenAI.Chat.ChatCompletionMessageParam[];
270
- /**
271
- * Convert a single TUniversalMessage to OpenAI format
272
- * Handles all message types including tool messages
273
- */
274
- static convertMessage(msg: TUniversalMessage): OpenAI.Chat.ChatCompletionMessageParam;
275
- /**
276
- * Add system prompt to message array if needed
277
- */
278
- static addSystemPromptIfNeeded(messages: OpenAI.Chat.ChatCompletionMessageParam[], systemPrompt?: string): OpenAI.Chat.ChatCompletionMessageParam[];
279
- }
280
-
281
- interface IOpenAIModelCatalogResponse {
282
- data?: readonly IOpenAIModelCatalogResource[];
283
- }
284
- interface IOpenAIModelCatalogResource {
285
- id?: string;
286
- }
287
- interface IOpenAIModelCatalogFetchInit {
288
- headers: Record<string, string>;
289
- }
290
- interface IOpenAIModelCatalogFetchResponse {
291
- ok: boolean;
292
- status: number;
293
- statusText?: string;
294
- json: () => Promise<IOpenAIModelCatalogResponse>;
295
- }
296
- type TOpenAIModelCatalogFetch = (url: string, init: IOpenAIModelCatalogFetchInit) => Promise<IOpenAIModelCatalogFetchResponse>;
297
- interface IRefreshOpenAIModelCatalogOptions {
298
- fetcher?: TOpenAIModelCatalogFetch;
299
- now?: () => Date;
300
- }
301
- declare function refreshOpenAIModelCatalog(profile: IProviderProfileConfig, options?: IRefreshOpenAIModelCatalogOptions): Promise<IProviderModelCatalog>;
302
-
303
- declare const DEFAULT_OPENAI_PROVIDER_MODEL: string | undefined;
304
- declare const DEFAULT_OPENAI_PROVIDER_API_KEY_REFERENCE = "$ENV:OPENAI_API_KEY";
305
- declare function createOpenAIProviderDefinition(): IProviderDefinition;
306
-
307
- export { DEFAULT_OPENAI_PROVIDER_API_KEY_REFERENCE, DEFAULT_OPENAI_PROVIDER_MODEL, type IOpenAIJsonSchemaDefinition, type IOpenAIModelCatalogFetchInit, type IOpenAIModelCatalogFetchResponse, type IOpenAIModelCatalogResource, type IOpenAIModelCatalogResponse, type IOpenAINativeWebToolsOptions, type IOpenAIProviderOptions, type IOpenAIResponsesReasoningOptions, type IPayloadLogger, type IPayloadLoggerOptions, type IRefreshOpenAIModelCatalogOptions, OpenAIConversationAdapter, OpenAIProvider, type TOpenAIApiSurface, type TOpenAIModelCatalogFetch, type TOpenAIProviderOptionValue, createOpenAIProviderDefinition, refreshOpenAIModelCatalog };
@@ -1,2 +0,0 @@
1
- import Se from'openai';import {AbstractAIProvider,SilentLogger}from'@robota-sdk/agent-core';import {probeOpenAICompatibleProfile,OpenAICompatibleResponseParser,observeProviderNativeRawPayloadStream,assembleOpenAICompatibleStream,convertToOpenAICompatibleMessages,convertToOpenAICompatibleTools}from'@robota-sdk/agent-provider-openai-compatible';import {randomUUID}from'crypto';var p=class{parser;constructor(t){this.parser=new OpenAICompatibleResponseParser({logger:t});}parseResponse(t){try{return this.parser.parseResponse(t)}catch(n){let o=h(n instanceof Error?n.message:"OpenAI response parsing failed");throw new Error(`OpenAI response parsing failed: ${o}`)}}parseStreamingChunk(t){try{return this.parser.parseStreamingChunk(t)}catch(n){let o=h(n instanceof Error?n.message:"OpenAI chunk parsing failed");throw new Error(`OpenAI chunk parsing failed: ${o}`)}}};function h(e){return e.replace(/^OpenAI-compatible response parsing failed: /,"").replace(/^OpenAI-compatible chunk parsing failed: /,"").replace("OpenAI-compatible response","OpenAI response")}function g(e){if(e.responseFormat===void 0||e.responseFormat==="text")return;if(e.responseFormat==="json_object")return {type:"json_object"};let t=y(e);return {type:"json_schema",json_schema:{name:t.name,schema:t.schema,...t.description!==void 0&&{description:t.description},...t.strict!==void 0&&{strict:t.strict}}}}function A(e){if(e.responseFormat===void 0||e.responseFormat==="text")return;if(e.responseFormat==="json_object")return {format:{type:"json_object"}};let t=y(e);return {format:{type:"json_schema",name:t.name,schema:t.schema,...t.description!==void 0&&{description:t.description},...t.strict!==void 0&&{strict:t.strict}}}}function y(e){let t=e.jsonSchema;if(e.responseFormat!=="json_schema")throw new Error(`Unsupported OpenAI response format: ${e.responseFormat}`);if(!t?.schema)throw new Error("OpenAI jsonSchema.schema is required when responseFormat is json_schema");return {name:t.name,schema:t.schema,...t.description!==void 0&&{description:t.description},...t.strict!==void 0&&{strict:t.strict}}}function v(e){return convertToOpenAICompatibleMessages(e)}function T(e){return convertToOpenAICompatibleTools(e)}async function b(e){let t=M(e.client);try{let n=S(e);if(e.chatOptions?.onTextDelta??e.onTextDelta)return await ee(t,e,{...n,stream:!0});await d(e,n,"chat"),e.chatOptions?.onProviderNativeRawPayload?.({provider:"openai",apiSurface:"chat-completions",payloadKind:"request",payload:n});let s=await t.chat.completions.create(n);return e.chatOptions?.onProviderNativeRawPayload?.({provider:"openai",apiSurface:"chat-completions",payloadKind:"response",payload:s}),e.responseParser.parseResponse(s)}catch(n){let s=n.message||"OpenAI API request failed";throw new Error(`OpenAI chat failed: ${s}`)}}async function*P(e){let t=M(e.client);try{let n={...S(e),stream:!0};await d(e,n,"stream"),e.chatOptions?.onProviderNativeRawPayload?.({provider:"openai",apiSurface:"chat-completions",payloadKind:"request",payload:n});let o=await t.chat.completions.create(n,e.chatOptions?.signal?{signal:e.chatOptions.signal}:void 0);for await(let s of observeProviderNativeRawPayloadStream(o,{provider:"openai",apiSurface:"chat-completions",onProviderNativeRawPayload:e.chatOptions?.onProviderNativeRawPayload})){let r=e.responseParser.parseStreamingChunk(s);r&&(yield r);}}catch(n){let s=n.message||"OpenAI API request failed";throw new Error(`OpenAI stream failed: ${s}`)}}function S(e){let t=v(e.messages),n=e.chatOptions?.model??e.providerOptions.defaultModel;if(!n)throw new Error("Model is required in chat options. Please specify a model in defaultModel configuration.");let o=g(e.providerOptions);return {model:n,messages:t,...e.chatOptions?.temperature!==void 0&&{temperature:e.chatOptions.temperature},...e.chatOptions?.maxTokens!==void 0&&{max_tokens:e.chatOptions.maxTokens},...e.chatOptions?.tools&&{tools:T(e.chatOptions.tools),tool_choice:"auto"},...o!==void 0&&{response_format:o}}}async function ee(e,t,n){try{await d(t,n,"stream"),t.chatOptions?.onProviderNativeRawPayload?.({provider:"openai",apiSurface:"chat-completions",payloadKind:"request",payload:n});let o=await e.chat.completions.create(n,t.chatOptions?.signal?{signal:t.chatOptions.signal}:void 0);return assembleOpenAICompatibleStream({stream:observeProviderNativeRawPayloadStream(o,{provider:"openai",apiSurface:"chat-completions",onProviderNativeRawPayload:t.chatOptions?.onProviderNativeRawPayload}),onTextDelta:t.chatOptions?.onTextDelta??t.onTextDelta,signal:t.chatOptions?.signal})}catch(o){let r=o.message||"OpenAI streaming request failed";throw new Error(`OpenAI stream failed: ${r}`)}}async function d(e,t,n){if(!e.payloadLogger?.isEnabled())return;let o={model:t.model,messagesCount:t.messages.length,hasTools:!!t.tools,temperature:t.temperature??void 0,maxTokens:t.max_tokens??void 0,timestamp:new Date().toISOString()};await e.payloadLogger.logPayload(o,n);}function M(e){if(!e)throw new Error("OpenAI client not available. Either provide a client/apiKey or use an executor.");return e}function x(e){return e.flatMap(t=>te(t))}function w(e,t){let n=e?.map(o=>({type:"function",name:o.name,description:o.description,parameters:o.parameters,strict:t??false}))??[];return n.length>0?n:void 0}function te(e){if(e.role==="user")return [l("user",oe(e.content,e.parts))];if(e.role==="system")return [l("system",e.content)];if(e.role==="tool"){if(!e.toolCallId||e.toolCallId.trim().length===0)throw new Error(`Tool message missing toolCallId: ${JSON.stringify(e)}`);return [{type:"function_call_output",call_id:e.toolCallId,output:e.content||""}]}return ne(e)}function ne(e){let t=[];e.content&&e.content.length>0&&t.push(l("assistant",e.content));for(let n of e.toolCalls??[])t.push({type:"function_call",call_id:n.id,name:n.function.name,arguments:n.function.arguments});return t.length===0&&t.push(l("assistant","")),t}function oe(e,t){if(!t||t.length===0)return e;let n=t.map(o=>se(o));return e.length>0&&!t.some(o=>o.type==="text")?[{type:"input_text",text:e},...n]:n}function se(e){return e.type==="text"?{type:"input_text",text:e.text}:e.type==="image_uri"?{type:"input_image",image_url:e.uri}:{type:"input_image",image_url:`data:${e.mimeType};base64,${e.data}`}}function l(e,t){return {role:e,content:t}}async function*E(e,t){let n=e[Symbol.asyncIterator]();try{for(;!t?.aborted;){let o=await re(n,t);if(o.done||t?.aborted)break;yield o.value;}}finally{t?.aborted&&await n.return?.();}}async function re(e,t){if(!t)return e.next();if(t.aborted)return {done:true,value:void 0};let n,o=new Promise(s=>{n=()=>s({done:true,value:void 0}),t.addEventListener("abort",n,{once:true});});try{return await Promise.race([e.next(),o])}finally{n&&t.removeEventListener("abort",n);}}function U(e){j(e);let t=e.output??[],n=e.output_text??N(t);return u({content:n,toolCalls:k(t),response:e,reasoning:c(t)})}async function _(e){let t={textParts:[],toolCalls:[],reasoning:W()};for await(let n of E(e.stream,e.signal))ie(t,n,e.onTextDelta);return t.completedResponse!==void 0?le(t):u({content:t.textParts.join(""),toolCalls:t.toolCalls,reasoning:t.reasoning})}function ie(e,t,n){if(t.type==="response.output_text.delta"){e.textParts.push(t.delta),n?.(t.delta);return}if(t.type==="response.completed"){e.completedResponse=t.response,m(e.reasoning,c(t.response.output??[]));return}if(t.type==="response.output_item.done"){pe(e,t.item);return}if(t.type==="response.error"||t.type==="response.failed"||t.type==="response.incomplete")throw new Error(`OpenAI Responses API failed: ${fe(t)}`)}function pe(e,t){if(D(t)){e.toolCalls.push(F(t));return}L(t)&&m(e.reasoning,c([t]));}function le(e){let t=e.completedResponse;if(t===void 0)throw new Error("OpenAI Responses stream completed without response metadata");j(t);let n=t.output??[],o=k(n),s=e.textParts.length>0?e.textParts.join(""):t.output_text??N(n),r=c(n);return m(r,e.reasoning),u({content:s,toolCalls:o.length>0?o:e.toolCalls,response:t,reasoning:r})}function u(e){return {id:randomUUID(),role:"assistant",content:e.content,state:"complete",timestamp:new Date,...e.toolCalls.length>0&&{toolCalls:e.toolCalls},...e.response?.usage!==void 0&&{usage:de(e.response.usage)},metadata:ce(e.response,e.reasoning)}}function ce(e,t){return {providerApiSurface:"responses",...e?.id!==void 0&&{responseId:e.id},...e?.model!==void 0&&{model:e.model},...e?.status!==void 0&&{finishReason:e.status},...t.reasoningSummaryCount>0&&{reasoningSummaryCount:t.reasoningSummaryCount,reasoningSummaries:t.reasoningSummaries},...t.hasEncryptedReasoning&&{hasEncryptedReasoning:true}}}function de(e){return {promptTokens:e.input_tokens??0,completionTokens:e.output_tokens??0,totalTokens:e.total_tokens??0}}function k(e){return e.filter(D).map(t=>F(t))}function D(e){return e.type==="function_call"&&"call_id"in e&&"name"in e&&"arguments"in e}function F(e){return {id:e.call_id,type:"function",function:{name:e.name,arguments:e.arguments}}}function N(e){return e.filter(me).flatMap(t=>t.content).map(ue).join("")}function ue(e){return e.type==="refusal"?e.refusal:e.text}function me(e){return e.type==="message"&&"content"in e&&Array.isArray(e.content)}function c(e){let t=W();for(let n of e){if(!L(n))continue;let o=n.summary?.map(s=>s.text).filter(Ie)??[];t.reasoningSummaryCount+=o.length,t.reasoningSummaries.push(...o),t.hasEncryptedReasoning=t.hasEncryptedReasoning||!!n.encrypted_content;}return t}function L(e){return e.type==="reasoning"}function W(){return {reasoningSummaryCount:0,reasoningSummaries:[],hasEncryptedReasoning:false}}function m(e,t){e.reasoningSummaryCount+=t.reasoningSummaryCount,e.reasoningSummaries.push(...t.reasoningSummaries),e.hasEncryptedReasoning=e.hasEncryptedReasoning||t.hasEncryptedReasoning;}function Ie(e){return e!==void 0}function j(e){if(e.status==="failed"||e.status==="incomplete")throw new Error(`OpenAI Responses API failed: ${e.error?.message??e.status}`)}function fe(e){return e.message??e.error?.message??e.response?.error?.message??"unknown error"}async function $(e){let t=e.chatOptions?.onTextDelta??e.onTextDelta;if(t)return q({...e,chatOptions:{...e.chatOptions,onTextDelta:t}});if(!e.client)throw new Error("OpenAI Responses client not available.");try{let n=V(e);e.chatOptions?.onProviderNativeRawPayload?.({provider:"openai",apiSurface:"responses",payloadKind:"request",payload:n});let o=await e.client.responses.create(n,e.chatOptions?.signal?{signal:e.chatOptions.signal}:void 0);return e.chatOptions?.onProviderNativeRawPayload?.({provider:"openai",apiSurface:"responses",payloadKind:"response",payload:o}),U(o)}catch(n){let s=n.message||"OpenAI Responses API request failed";throw new Error(`OpenAI responses failed: ${s}`)}}async function*K(e){let t={deltas:[]},n=e.chatOptions?.onTextDelta??e.onTextDelta,o=q({...e,chatOptions:{...e.chatOptions,onTextDelta:s=>{n?.(s),ve(t,Ae(s));}}}).then(s=>Te(t,s)).catch(s=>Re(t,Pe(s)));yield*ye(t),await o;}async function q(e){if(!e.client)throw new Error("OpenAI Responses client not available.");try{let t=ge(e);e.chatOptions?.onProviderNativeRawPayload?.({provider:"openai",apiSurface:"responses",payloadKind:"request",payload:t});let n=await e.client.responses.create(t,e.chatOptions?.signal?{signal:e.chatOptions.signal}:void 0);return _({stream:observeProviderNativeRawPayloadStream(n,{provider:"openai",apiSurface:"responses",onProviderNativeRawPayload:e.chatOptions?.onProviderNativeRawPayload}),onTextDelta:e.chatOptions?.onTextDelta,signal:e.chatOptions?.signal})}catch(t){let o=t.message||"OpenAI Responses streaming request failed";throw new Error(`OpenAI responses stream failed: ${o}`)}}function V(e){let t=e.chatOptions?.model??e.providerOptions.defaultModel;if(!t)throw new Error("Model is required in chat options. Please specify a model in defaultModel configuration.");let n=w(e.chatOptions?.tools,e.providerOptions.strictTools),o=A(e.providerOptions);return {model:t,input:x(e.messages),...n!==void 0&&{tools:n,tool_choice:"auto"},...e.chatOptions?.temperature!==void 0&&{temperature:e.chatOptions.temperature},...e.chatOptions?.maxTokens!==void 0&&{max_output_tokens:e.chatOptions.maxTokens},...o!==void 0&&{text:o},...e.providerOptions.reasoning!==void 0&&{reasoning:e.providerOptions.reasoning},...e.providerOptions.includeEncryptedReasoning===true&&{include:["reasoning.encrypted_content"]},...e.providerOptions.store!==void 0&&{store:e.providerOptions.store}}}function ge(e){return {...V(e),stream:true}}function Ae(e){return {id:randomUUID(),role:"assistant",content:e,state:"complete",timestamp:new Date,metadata:{providerApiSurface:"responses",isStreamChunk:true,isComplete:false}}}async function*ye(e){for(;;){let t=e.deltas.shift();if(t!==void 0){yield t;continue}if(e.error!==void 0)throw e.error;if(e.finalMessage!==void 0){yield be(e.finalMessage);return}await Ce(e);}}function ve(e,t){e.deltas.push(t),I(e);}function Te(e,t){e.finalMessage=t,I(e);}function Re(e,t){e.error=t,I(e);}function Ce(e){return new Promise(t=>{e.wake=t;})}function I(e){e.wake?.(),e.wake=void 0;}function be(e){return {...e,content:"",metadata:{...e.metadata,isStreamChunk:true,isComplete:true}}}function Pe(e){return e instanceof Error?e:new Error(String(e))}var i=class extends AbstractAIProvider{name="openai";version="1.0.0";client;options;apiSurface;payloadLogger;responseParser;onTextDelta;constructor(t){if(super(t.logger||SilentLogger),this.options=t,this.apiSurface=we(t),Ee(this.apiSurface,t.nativeWebTools),t.executor&&(this.executor=t.executor),!this.executor)if(t.client)this.client=t.client;else if(t.apiKey)this.client=new Se({apiKey:t.apiKey,...t.organization&&{organization:t.organization},...t.timeout&&{timeout:t.timeout},...t.baseURL&&{baseURL:t.baseURL}});else throw new Error("Either OpenAI client, apiKey, or executor is required");this.responseParser=new p(this.logger),this.payloadLogger=t.payloadLogger;}async chat(t,n){if(this.validateMessages(t),this.validateNativeWebTools(n?.nativeWebTools),this.executor)try{return await this.executeViaExecutorOrDirect(t,n)}catch(o){throw this.logger.error("OpenAI Provider executor chat error:",o instanceof Error?o.message:String(o)),o}return this.apiSurface==="responses"?$({client:this.client,messages:t,chatOptions:n,providerOptions:this.options,onTextDelta:this.onTextDelta}):b({client:this.client,messages:t,chatOptions:n,providerOptions:this.options,payloadLogger:this.payloadLogger,responseParser:this.responseParser,onTextDelta:this.onTextDelta})}async*chatStream(t,n){if(this.validateNativeWebTools(n?.nativeWebTools),this.executor)try{yield*this.executeStreamViaExecutorOrDirect(t,n);return}catch(o){throw this.logger.error("OpenAI Provider executor stream error:",o instanceof Error?o.message:String(o)),o}if(this.apiSurface==="responses"){yield*K({client:this.client,messages:t,chatOptions:n,providerOptions:this.options,onTextDelta:this.onTextDelta});return}yield*P({client:this.client,messages:t,chatOptions:n,providerOptions:this.options,payloadLogger:this.payloadLogger,responseParser:this.responseParser,onTextDelta:this.onTextDelta});}supportsTools(){return true}getCapabilities(){let t=this.apiSurface==="chat-completions"?"openai-compatible-chat-completions":"openai-responses";return {functionCalling:{supported:true},nativeWebTools:{webSearch:{supported:false,enabled:false,source:t,reason:Q(this.apiSurface,"search")},webFetch:{supported:false,enabled:false,source:t,reason:Q(this.apiSurface,"fetch")}}}}validateConfig(){return !!this.client&&!!this.options}async dispose(){}validateMessages(t){super.validateMessages(t);for(let n of t)if(n.role==="assistant"){let o=n;if(o.toolCalls&&o.toolCalls.length>0&&o.content==="")continue}}};function we(e){return e.apiSurface!==void 0?e.apiSurface:e.baseURL?"chat-completions":"responses"}function Q(e,t){return e==="chat-completions"?`OpenAI-compatible Chat Completions endpoints support declared function tools, not provider-native web ${t}.`:`OpenAI Responses native web ${t} is not wired in this Robota provider version.`}function Ee(e,t){if(!(t?.webSearch!==true&&t?.webFetch!==true))throw new Error(`Provider openai native web search/fetch is not supported for apiSurface ${e} in this Robota provider version.`)}var J=class{static filterMessagesForOpenAI(t){return t.filter(n=>n.role==="user"||n.role==="assistant"||n.role==="system"?true:n.role==="tool"?!!(n.toolCallId&&n.toolCallId.trim()!==""&&n.toolCallId!=="unknown"):false)}static toOpenAIFormat(t){return this.filterMessagesForOpenAI(t).map(o=>this.convertMessage(o))}static convertMessage(t){let n=t.role;if(n==="user")return {role:"user",content:t.content};if(n==="assistant"){let s=t;return s.toolCalls&&s.toolCalls.length>0?{role:"assistant",content:s.content===""?null:s.content||null,tool_calls:s.toolCalls.map(a=>({id:a.id,type:"function",function:{name:a.function.name,arguments:a.function.arguments}}))}:{role:"assistant",content:s.content===null||s.content===""?null:s.content||""}}if(n==="system")return {role:"system",content:t.content};if(n==="tool"){if(!t.toolCallId||t.toolCallId.trim()==="")throw new Error(`Tool message missing toolCallId: ${JSON.stringify(t)}`);return {role:"tool",content:t.content,tool_call_id:t.toolCallId}}let o=n;throw new Error(`Unsupported message role: ${o}`)}static addSystemPromptIfNeeded(t,n){return !n||t.some(s=>s.role==="system")?t:[{role:"system",content:n},...t]}};var Ue="https://api.openai.com/v1/models",z="https://platform.openai.com/docs/api-reference/models/list";async function H(e,t={}){if(!e.apiKey)return f("OpenAI model catalog refresh requires apiKey.");let n=t.fetcher??Fe,o=t.now??(()=>new Date),s=_e(e.baseURL);try{let r=await n(s,{headers:{Authorization:`Bearer ${e.apiKey}`}});if(!r.ok)return f(De(r));let a=await r.json(),O=ke(a);return {status:"live",entries:O,lastVerifiedAt:o().toISOString(),sourceUrl:z,message:`${O.length} OpenAI model(s) discovered.`}}catch(r){return f(r instanceof Error?r.message:String(r))}}function _e(e){return e?`${e.replace(/\/$/,"")}/models`:Ue}function ke(e){return (e.data??[]).map(t=>t.id).filter(t=>t!==void 0&&t.trim().length>0).map(t=>({id:t,displayName:t,lifecycle:"active"}))}function De(e){let t=e.statusText?` ${e.statusText}`:"";return `OpenAI model catalog refresh failed: HTTP ${e.status}${t}`}function f(e){return {status:"unavailable",sourceUrl:z,message:e}}async function Fe(e,t){let n=await fetch(e,t);return {ok:n.ok,status:n.status,statusText:n.statusText,json:()=>n.json()}}var St=void 0,Y="$ENV:OPENAI_API_KEY",Le="https://platform.openai.com/api-keys",We="https://developers.openai.com/api/reference/overview#authentication",je="2026-05-08",$e=[{kind:"api-key",label:"OpenAI API keys",url:Le,sourceUrl:We,lastVerifiedAt:je}];function Mt(){return {type:"openai",displayName:"OpenAI",description:"Official OpenAI Responses API provider",defaults:{apiKey:Y},modelCatalog:{status:"unavailable",sourceUrl:"https://platform.openai.com/docs/api-reference/models/list",message:"OpenAI model availability should be discovered live from GET /v1/models."},setupHelpLinks:$e,setupSteps:[{key:"model",title:"OpenAI model",required:true},{key:"apiKey",title:"OpenAI API key",defaultValue:Y,masked:true}],requiresApiKey:true,probeProfile:probeOpenAICompatibleProfile,refreshModelCatalog:({profile:e})=>H(e),createProvider:e=>{let t=qe(e.options),n=Ve(e.options);return Qe(e.baseURL,t,n),new i({apiKey:Ke(e.apiKey),...e.baseURL!==void 0&&{baseURL:e.baseURL},...e.timeout!==void 0&&{timeout:e.timeout},...t!==void 0&&{apiSurface:t},...n!==void 0&&{nativeWebTools:n},defaultModel:e.model})}}}function Ke(e){if(!e)throw new Error("Provider openai requires apiKey");return e}function qe(e){let t=e?.apiSurface;if(t==="responses"||t==="chat-completions")return t}function Ve(e){let t=G(e?.nativeWebTools)??G(e?.builtInWebTools);if(t!==void 0)return t}function G(e){if(e==null||e instanceof Date||Array.isArray(e)||typeof e!="object")return;let t=e.webSearch,n=e.webFetch;return {...typeof t=="boolean"&&{webSearch:t},...typeof n=="boolean"&&{webFetch:n}}}function Qe(e,t,n){if(!(n?.webSearch!==true&&n?.webFetch!==true))throw e!==void 0||t==="chat-completions"?new Error("Provider openai profile uses an OpenAI-compatible Chat Completions endpoint; native web search/fetch is not supported for this profile. Use Robota local WebSearch/WebFetch tools or a provider with documented hosted web support."):new Error("Provider openai native web search/fetch is not wired in this Robota provider version. Use Robota local WebSearch/WebFetch tools or a provider with documented hosted web support.")}
2
- export{Y as DEFAULT_OPENAI_PROVIDER_API_KEY_REFERENCE,St as DEFAULT_OPENAI_PROVIDER_MODEL,J as OpenAIConversationAdapter,i as OpenAIProvider,Mt as createOpenAIProviderDefinition,H as refreshOpenAIModelCatalog};