@robota-sdk/agent-provider-openai 3.0.0-beta.55

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.
@@ -0,0 +1,2 @@
1
+ 'use strict';var C=require('openai'),agentCore=require('@robota-sdk/agent-core'),agentProviderOpenaiCompatible=require('@robota-sdk/agent-provider-openai-compatible');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var C__default=/*#__PURE__*/_interopDefault(C);var p=class{parser;constructor(e){this.parser=new agentProviderOpenaiCompatible.OpenAICompatibleResponseParser({logger:e});}parseResponse(e){try{return this.parser.parseResponse(e)}catch(t){let r=d(t instanceof Error?t.message:"OpenAI response parsing failed");throw new Error(`OpenAI response parsing failed: ${r}`)}}parseStreamingChunk(e){try{return this.parser.parseStreamingChunk(e)}catch(t){let r=d(t instanceof Error?t.message:"OpenAI chunk parsing failed");throw new Error(`OpenAI chunk parsing failed: ${r}`)}}};function d(a){return a.replace(/^OpenAI-compatible response parsing failed: /,"").replace(/^OpenAI-compatible chunk parsing failed: /,"").replace("OpenAI-compatible response","OpenAI response")}function c(a){return agentProviderOpenaiCompatible.convertToOpenAICompatibleMessages(a)}function m(a){return agentProviderOpenaiCompatible.convertToOpenAICompatibleTools(a)}var i=class extends agentCore.AbstractAIProvider{name="openai";version="1.0.0";client;options;payloadLogger;responseParser;onTextDelta;constructor(e){if(super(e.logger||agentCore.SilentLogger),this.options=e,e.executor&&(this.executor=e.executor),!this.executor)if(e.client)this.client=e.client;else if(e.apiKey)this.client=new C__default.default({apiKey:e.apiKey,...e.organization&&{organization:e.organization},...e.timeout&&{timeout:e.timeout},...e.baseURL&&{baseURL:e.baseURL}});else throw new Error("Either OpenAI client, apiKey, or executor is required");this.responseParser=new p(this.logger),this.payloadLogger=e.payloadLogger;}async chat(e,t){if(this.validateMessages(e),this.executor)try{return await this.executeViaExecutorOrDirect(e,t)}catch(r){throw this.logger.error("OpenAI Provider executor chat error:",r instanceof Error?r.message:String(r)),r}if(!this.client)throw new Error("OpenAI client not available. Either provide a client/apiKey or use an executor.");try{let r=c(e),o=t;if(!o?.model)throw new Error("Model is required in chat options. Please specify a model in defaultModel configuration.");let s={model:o.model,messages:r,...o.temperature!==void 0&&{temperature:o.temperature},...o.maxTokens&&{max_tokens:o.maxTokens},...o.tools&&{tools:m(o.tools),tool_choice:"auto"}},n=o.onTextDelta??this.onTextDelta;if(n)return await this.chatWithStreamingAssembly({...s,stream:!0},{...o,onTextDelta:n});if(this.payloadLogger?.isEnabled()){let I={model:s.model,messagesCount:r.length,hasTools:!!s.tools,temperature:s.temperature??void 0,maxTokens:s.max_tokens??void 0,timestamp:new Date().toISOString()};await this.payloadLogger.logPayload(I,"chat");}let l=await this.client.chat.completions.create(s);return this.responseParser.parseResponse(l)}catch(r){let s=r.message||"OpenAI API request failed";throw new Error(`OpenAI chat failed: ${s}`)}}async chatWithStreamingAssembly(e,t){if(!this.client)throw new Error("OpenAI client not available. Either provide a client/apiKey or use an executor.");try{if(this.payloadLogger?.isEnabled()){let o={model:e.model,messagesCount:e.messages.length,hasTools:!!e.tools,temperature:e.temperature??void 0,maxTokens:e.max_tokens??void 0,timestamp:new Date().toISOString()};await this.payloadLogger.logPayload(o,"stream");}let r=await this.client.chat.completions.create(e,t.signal?{signal:t.signal}:void 0);return agentProviderOpenaiCompatible.assembleOpenAICompatibleStream({stream:r,onTextDelta:t.onTextDelta,signal:t.signal})}catch(r){let s=r.message||"OpenAI streaming request failed";throw new Error(`OpenAI stream failed: ${s}`)}}async*chatStream(e,t){if(this.executor)try{yield*this.executeStreamViaExecutorOrDirect(e,t);return}catch(r){throw this.logger.error("OpenAI Provider executor stream error:",r instanceof Error?r.message:String(r)),r}if(!this.client)throw new Error("OpenAI client not available. Either provide a client/apiKey or use an executor.");try{let r=c(e);if(!t?.model)throw new Error("Model is required in chat options. Please specify a model in defaultModel configuration.");let o={model:t.model,messages:r,stream:!0,...t?.temperature!==void 0&&{temperature:t.temperature},...t?.maxTokens&&{max_tokens:t.maxTokens},...t?.tools&&{tools:m(t.tools),tool_choice:"auto"}};if(this.payloadLogger?.isEnabled()){let n={model:o.model,messagesCount:r.length,hasTools:!!o.tools,temperature:o.temperature??void 0,maxTokens:o.max_tokens??void 0,timestamp:new Date().toISOString()};await this.payloadLogger.logPayload(n,"stream");}let s=await this.client.chat.completions.create(o);for await(let n of s){let l=this.responseParser.parseStreamingChunk(n);l&&(yield l);}}catch(r){let s=r.message||"OpenAI API request failed";throw new Error(`OpenAI stream failed: ${s}`)}}supportsTools(){return true}validateConfig(){return !!this.client&&!!this.options}async dispose(){}validateMessages(e){super.validateMessages(e);for(let t of e)if(t.role==="assistant"){let r=t;if(r.toolCalls&&r.toolCalls.length>0&&r.content==="")continue}}};var g=class{static filterMessagesForOpenAI(e){return e.filter(t=>t.role==="user"||t.role==="assistant"||t.role==="system"?true:t.role==="tool"?!!(t.toolCallId&&t.toolCallId.trim()!==""&&t.toolCallId!=="unknown"):false)}static toOpenAIFormat(e){return this.filterMessagesForOpenAI(e).map(r=>this.convertMessage(r))}static convertMessage(e){let t=e.role;if(t==="user")return {role:"user",content:e.content};if(t==="assistant"){let o=e;return o.toolCalls&&o.toolCalls.length>0?{role:"assistant",content:o.content===""?null:o.content||null,tool_calls:o.toolCalls.map(n=>({id:n.id,type:"function",function:{name:n.function.name,arguments:n.function.arguments}}))}:{role:"assistant",content:o.content===null||o.content===""?null:o.content||""}}if(t==="system")return {role:"system",content:e.content};if(t==="tool"){if(!e.toolCallId||e.toolCallId.trim()==="")throw new Error(`Tool message missing toolCallId: ${JSON.stringify(e)}`);return {role:"tool",content:e.content,tool_call_id:e.toolCallId}}let r=t;throw new Error(`Unsupported message role: ${r}`)}static addSystemPromptIfNeeded(e,t){return !t||e.some(o=>o.role==="system")?e:[{role:"system",content:t},...e]}};var N=void 0,h="lm-studio",f="http://localhost:1234/v1";function z(){return {type:"openai",defaults:{apiKey:h,baseURL:f},setupSteps:[{key:"baseURL",title:"OpenAI-compatible base URL",defaultValue:f},{key:"model",title:"OpenAI-compatible model",required:true},{key:"apiKey",title:"OpenAI-compatible API key",defaultValue:h,masked:true}],requiresApiKey:true,probeProfile:agentProviderOpenaiCompatible.probeOpenAICompatibleProfile,createProvider:a=>new i({apiKey:x(a.apiKey),...a.baseURL!==void 0&&{baseURL:a.baseURL},...a.timeout!==void 0&&{timeout:a.timeout},defaultModel:a.model})}}function x(a){if(!a)throw new Error("Provider openai requires apiKey");return a}
2
+ exports.DEFAULT_OPENAI_COMPATIBLE_PROVIDER_API_KEY=h;exports.DEFAULT_OPENAI_COMPATIBLE_PROVIDER_BASE_URL=f;exports.DEFAULT_OPENAI_PROVIDER_MODEL=N;exports.OpenAIConversationAdapter=g;exports.OpenAIProvider=i;exports.createOpenAIProviderDefinition=z;
@@ -0,0 +1,239 @@
1
+ import OpenAI from 'openai';
2
+ import { ILogger, IExecutor, TProviderOptionValueBase, AbstractAIProvider, TTextDeltaCallback, TUniversalMessage, IChatOptions, 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
+ /**
60
+ * Valid provider option value types
61
+ */
62
+ type TOpenAIProviderOptionValue = string | number | boolean | undefined | null | OpenAI | IPayloadLogger | ILogger | IExecutor | TProviderOptionValueBase | TOpenAIProviderOptionValue[] | {
63
+ [key: string]: TOpenAIProviderOptionValue;
64
+ };
65
+ /**
66
+ * OpenAI provider options
67
+ */
68
+ interface IOpenAIProviderOptions {
69
+ /**
70
+ * Additional provider-specific options
71
+ */
72
+ [key: string]: TOpenAIProviderOptionValue;
73
+ /**
74
+ * OpenAI API key (required when client is not provided)
75
+ */
76
+ apiKey?: string;
77
+ /**
78
+ * OpenAI organization ID (optional)
79
+ */
80
+ organization?: string;
81
+ /**
82
+ * API request timeout (milliseconds)
83
+ */
84
+ timeout?: number;
85
+ /**
86
+ * API base URL (default: 'https://api.openai.com/v1')
87
+ */
88
+ baseURL?: string;
89
+ /**
90
+ * Response format (default: 'text')
91
+ * - 'text': Plain text response
92
+ * - 'json_object': JSON object mode (requires system message)
93
+ * - 'json_schema': Structured Outputs with schema validation
94
+ */
95
+ responseFormat?: 'text' | 'json_object' | 'json_schema';
96
+ /**
97
+ * JSON schema for structured outputs (required when responseFormat is 'json_schema')
98
+ */
99
+ jsonSchema?: {
100
+ name: string;
101
+ description?: string;
102
+ schema?: Record<string, TOpenAIProviderOptionValue>;
103
+ strict?: boolean;
104
+ };
105
+ /**
106
+ * OpenAI client instance (optional: will be created from apiKey if not provided)
107
+ */
108
+ client?: OpenAI;
109
+ /**
110
+ * Payload logger instance for debugging API requests/responses
111
+ *
112
+ * Use different implementations based on your environment:
113
+ * - FilePayloadLogger: Node.js file-based logging
114
+ * - ConsolePayloadLogger: Browser console-based logging
115
+ * - Custom: Implement IPayloadLogger interface
116
+ *
117
+ * @example
118
+ * ```typescript
119
+ * // Node.js
120
+ * import { FilePayloadLogger } from '@robota-sdk/agent-provider-openai/loggers/file';
121
+ * const provider = new OpenAIProvider({
122
+ * client: openaiClient,
123
+ * payloadLogger: new FilePayloadLogger({ logDir: './logs/openai' })
124
+ * });
125
+ *
126
+ * // Browser
127
+ * import { ConsolePayloadLogger } from '@robota-sdk/agent-provider-openai/loggers/console';
128
+ * const provider = new OpenAIProvider({
129
+ * client: openaiClient,
130
+ * payloadLogger: new ConsolePayloadLogger()
131
+ * });
132
+ * ```
133
+ */
134
+ payloadLogger?: IPayloadLogger;
135
+ /**
136
+ * Optional executor for handling AI requests
137
+ *
138
+ * When provided, the provider will delegate all chat operations to this executor
139
+ * instead of making direct API calls. This enables remote execution capabilities.
140
+ *
141
+ * @example
142
+ * ```typescript
143
+ * import { LocalExecutor, RemoteExecutor } from '@robota-sdk/agent-core';
144
+ *
145
+ * // Local execution (registers this provider)
146
+ * const localExecutor = new LocalExecutor();
147
+ * localExecutor.registerProvider('openai', new OpenAIProvider({ apiKey: 'sk-...' }));
148
+ *
149
+ * // Remote execution
150
+ * const remoteExecutor = new RemoteExecutor({
151
+ * serverUrl: 'https://api.robota.io',
152
+ * userApiKey: 'user-token-123'
153
+ * });
154
+ *
155
+ * const provider = new OpenAIProvider({
156
+ * executor: remoteExecutor // No direct API key needed
157
+ * });
158
+ * ```
159
+ */
160
+ executor?: IExecutor;
161
+ /**
162
+ * Logger instance for internal OpenAI provider logging
163
+ * @defaultValue SilentLogger
164
+ */
165
+ logger?: ILogger;
166
+ }
167
+
168
+ /**
169
+ * OpenAI provider implementation for Robota
170
+ *
171
+ * Provides integration with OpenAI's GPT models following BaseAIProvider guidelines.
172
+ * Uses OpenAI SDK native types internally for optimal performance and feature support.
173
+ *
174
+ * @public
175
+ */
176
+ declare class OpenAIProvider extends AbstractAIProvider {
177
+ readonly name = "openai";
178
+ readonly version = "1.0.0";
179
+ private readonly client?;
180
+ private readonly options;
181
+ private readonly payloadLogger;
182
+ private readonly responseParser;
183
+ /**
184
+ * Optional callback for text deltas during streaming.
185
+ * Set by the consumer (e.g., Session) to receive real-time text chunks.
186
+ * When set, chat() uses streaming internally while still returning
187
+ * the complete assembled message.
188
+ */
189
+ onTextDelta?: TTextDeltaCallback;
190
+ constructor(options: IOpenAIProviderOptions);
191
+ chat(messages: TUniversalMessage[], options?: IChatOptions): Promise<TUniversalMessage>;
192
+ private chatWithStreamingAssembly;
193
+ chatStream(messages: TUniversalMessage[], options?: IChatOptions): AsyncIterable<TUniversalMessage>;
194
+ supportsTools(): boolean;
195
+ validateConfig(): boolean;
196
+ dispose(): Promise<void>;
197
+ protected validateMessages(messages: TUniversalMessage[]): void;
198
+ }
199
+
200
+ /**
201
+ * OpenAI Conversation Adapter
202
+ *
203
+ * Converts between TUniversalMessage format and OpenAI native types.
204
+ * Provides bidirectional conversion for seamless integration.
205
+ *
206
+ * @public
207
+ */
208
+ declare class OpenAIConversationAdapter {
209
+ /**
210
+ * Filter messages for OpenAI compatibility
211
+ *
212
+ * OpenAI has specific requirements:
213
+ * - Tool messages must have valid toolCallId
214
+ * - Messages must be in proper sequence
215
+ * - Tool messages without toolCallId should be excluded
216
+ */
217
+ static filterMessagesForOpenAI(messages: TUniversalMessage[]): TUniversalMessage[];
218
+ /**
219
+ * Convert TUniversalMessage array to OpenAI message format
220
+ * Now properly handles tool messages for OpenAI's tool calling feature
221
+ */
222
+ static toOpenAIFormat(messages: TUniversalMessage[]): OpenAI.Chat.ChatCompletionMessageParam[];
223
+ /**
224
+ * Convert a single TUniversalMessage to OpenAI format
225
+ * Handles all message types including tool messages
226
+ */
227
+ static convertMessage(msg: TUniversalMessage): OpenAI.Chat.ChatCompletionMessageParam;
228
+ /**
229
+ * Add system prompt to message array if needed
230
+ */
231
+ static addSystemPromptIfNeeded(messages: OpenAI.Chat.ChatCompletionMessageParam[], systemPrompt?: string): OpenAI.Chat.ChatCompletionMessageParam[];
232
+ }
233
+
234
+ declare const DEFAULT_OPENAI_PROVIDER_MODEL: string | undefined;
235
+ declare const DEFAULT_OPENAI_COMPATIBLE_PROVIDER_API_KEY = "lm-studio";
236
+ declare const DEFAULT_OPENAI_COMPATIBLE_PROVIDER_BASE_URL = "http://localhost:1234/v1";
237
+ declare function createOpenAIProviderDefinition(): IProviderDefinition;
238
+
239
+ export { DEFAULT_OPENAI_COMPATIBLE_PROVIDER_API_KEY, DEFAULT_OPENAI_COMPATIBLE_PROVIDER_BASE_URL, DEFAULT_OPENAI_PROVIDER_MODEL, type IOpenAIProviderOptions, type IPayloadLogger, type IPayloadLoggerOptions, OpenAIConversationAdapter, OpenAIProvider, type TOpenAIProviderOptionValue, createOpenAIProviderDefinition };
@@ -0,0 +1,239 @@
1
+ import OpenAI from 'openai';
2
+ import { ILogger, IExecutor, TProviderOptionValueBase, AbstractAIProvider, TTextDeltaCallback, TUniversalMessage, IChatOptions, 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
+ /**
60
+ * Valid provider option value types
61
+ */
62
+ type TOpenAIProviderOptionValue = string | number | boolean | undefined | null | OpenAI | IPayloadLogger | ILogger | IExecutor | TProviderOptionValueBase | TOpenAIProviderOptionValue[] | {
63
+ [key: string]: TOpenAIProviderOptionValue;
64
+ };
65
+ /**
66
+ * OpenAI provider options
67
+ */
68
+ interface IOpenAIProviderOptions {
69
+ /**
70
+ * Additional provider-specific options
71
+ */
72
+ [key: string]: TOpenAIProviderOptionValue;
73
+ /**
74
+ * OpenAI API key (required when client is not provided)
75
+ */
76
+ apiKey?: string;
77
+ /**
78
+ * OpenAI organization ID (optional)
79
+ */
80
+ organization?: string;
81
+ /**
82
+ * API request timeout (milliseconds)
83
+ */
84
+ timeout?: number;
85
+ /**
86
+ * API base URL (default: 'https://api.openai.com/v1')
87
+ */
88
+ baseURL?: string;
89
+ /**
90
+ * Response format (default: 'text')
91
+ * - 'text': Plain text response
92
+ * - 'json_object': JSON object mode (requires system message)
93
+ * - 'json_schema': Structured Outputs with schema validation
94
+ */
95
+ responseFormat?: 'text' | 'json_object' | 'json_schema';
96
+ /**
97
+ * JSON schema for structured outputs (required when responseFormat is 'json_schema')
98
+ */
99
+ jsonSchema?: {
100
+ name: string;
101
+ description?: string;
102
+ schema?: Record<string, TOpenAIProviderOptionValue>;
103
+ strict?: boolean;
104
+ };
105
+ /**
106
+ * OpenAI client instance (optional: will be created from apiKey if not provided)
107
+ */
108
+ client?: OpenAI;
109
+ /**
110
+ * Payload logger instance for debugging API requests/responses
111
+ *
112
+ * Use different implementations based on your environment:
113
+ * - FilePayloadLogger: Node.js file-based logging
114
+ * - ConsolePayloadLogger: Browser console-based logging
115
+ * - Custom: Implement IPayloadLogger interface
116
+ *
117
+ * @example
118
+ * ```typescript
119
+ * // Node.js
120
+ * import { FilePayloadLogger } from '@robota-sdk/agent-provider-openai/loggers/file';
121
+ * const provider = new OpenAIProvider({
122
+ * client: openaiClient,
123
+ * payloadLogger: new FilePayloadLogger({ logDir: './logs/openai' })
124
+ * });
125
+ *
126
+ * // Browser
127
+ * import { ConsolePayloadLogger } from '@robota-sdk/agent-provider-openai/loggers/console';
128
+ * const provider = new OpenAIProvider({
129
+ * client: openaiClient,
130
+ * payloadLogger: new ConsolePayloadLogger()
131
+ * });
132
+ * ```
133
+ */
134
+ payloadLogger?: IPayloadLogger;
135
+ /**
136
+ * Optional executor for handling AI requests
137
+ *
138
+ * When provided, the provider will delegate all chat operations to this executor
139
+ * instead of making direct API calls. This enables remote execution capabilities.
140
+ *
141
+ * @example
142
+ * ```typescript
143
+ * import { LocalExecutor, RemoteExecutor } from '@robota-sdk/agent-core';
144
+ *
145
+ * // Local execution (registers this provider)
146
+ * const localExecutor = new LocalExecutor();
147
+ * localExecutor.registerProvider('openai', new OpenAIProvider({ apiKey: 'sk-...' }));
148
+ *
149
+ * // Remote execution
150
+ * const remoteExecutor = new RemoteExecutor({
151
+ * serverUrl: 'https://api.robota.io',
152
+ * userApiKey: 'user-token-123'
153
+ * });
154
+ *
155
+ * const provider = new OpenAIProvider({
156
+ * executor: remoteExecutor // No direct API key needed
157
+ * });
158
+ * ```
159
+ */
160
+ executor?: IExecutor;
161
+ /**
162
+ * Logger instance for internal OpenAI provider logging
163
+ * @defaultValue SilentLogger
164
+ */
165
+ logger?: ILogger;
166
+ }
167
+
168
+ /**
169
+ * OpenAI provider implementation for Robota
170
+ *
171
+ * Provides integration with OpenAI's GPT models following BaseAIProvider guidelines.
172
+ * Uses OpenAI SDK native types internally for optimal performance and feature support.
173
+ *
174
+ * @public
175
+ */
176
+ declare class OpenAIProvider extends AbstractAIProvider {
177
+ readonly name = "openai";
178
+ readonly version = "1.0.0";
179
+ private readonly client?;
180
+ private readonly options;
181
+ private readonly payloadLogger;
182
+ private readonly responseParser;
183
+ /**
184
+ * Optional callback for text deltas during streaming.
185
+ * Set by the consumer (e.g., Session) to receive real-time text chunks.
186
+ * When set, chat() uses streaming internally while still returning
187
+ * the complete assembled message.
188
+ */
189
+ onTextDelta?: TTextDeltaCallback;
190
+ constructor(options: IOpenAIProviderOptions);
191
+ chat(messages: TUniversalMessage[], options?: IChatOptions): Promise<TUniversalMessage>;
192
+ private chatWithStreamingAssembly;
193
+ chatStream(messages: TUniversalMessage[], options?: IChatOptions): AsyncIterable<TUniversalMessage>;
194
+ supportsTools(): boolean;
195
+ validateConfig(): boolean;
196
+ dispose(): Promise<void>;
197
+ protected validateMessages(messages: TUniversalMessage[]): void;
198
+ }
199
+
200
+ /**
201
+ * OpenAI Conversation Adapter
202
+ *
203
+ * Converts between TUniversalMessage format and OpenAI native types.
204
+ * Provides bidirectional conversion for seamless integration.
205
+ *
206
+ * @public
207
+ */
208
+ declare class OpenAIConversationAdapter {
209
+ /**
210
+ * Filter messages for OpenAI compatibility
211
+ *
212
+ * OpenAI has specific requirements:
213
+ * - Tool messages must have valid toolCallId
214
+ * - Messages must be in proper sequence
215
+ * - Tool messages without toolCallId should be excluded
216
+ */
217
+ static filterMessagesForOpenAI(messages: TUniversalMessage[]): TUniversalMessage[];
218
+ /**
219
+ * Convert TUniversalMessage array to OpenAI message format
220
+ * Now properly handles tool messages for OpenAI's tool calling feature
221
+ */
222
+ static toOpenAIFormat(messages: TUniversalMessage[]): OpenAI.Chat.ChatCompletionMessageParam[];
223
+ /**
224
+ * Convert a single TUniversalMessage to OpenAI format
225
+ * Handles all message types including tool messages
226
+ */
227
+ static convertMessage(msg: TUniversalMessage): OpenAI.Chat.ChatCompletionMessageParam;
228
+ /**
229
+ * Add system prompt to message array if needed
230
+ */
231
+ static addSystemPromptIfNeeded(messages: OpenAI.Chat.ChatCompletionMessageParam[], systemPrompt?: string): OpenAI.Chat.ChatCompletionMessageParam[];
232
+ }
233
+
234
+ declare const DEFAULT_OPENAI_PROVIDER_MODEL: string | undefined;
235
+ declare const DEFAULT_OPENAI_COMPATIBLE_PROVIDER_API_KEY = "lm-studio";
236
+ declare const DEFAULT_OPENAI_COMPATIBLE_PROVIDER_BASE_URL = "http://localhost:1234/v1";
237
+ declare function createOpenAIProviderDefinition(): IProviderDefinition;
238
+
239
+ export { DEFAULT_OPENAI_COMPATIBLE_PROVIDER_API_KEY, DEFAULT_OPENAI_COMPATIBLE_PROVIDER_BASE_URL, DEFAULT_OPENAI_PROVIDER_MODEL, type IOpenAIProviderOptions, type IPayloadLogger, type IPayloadLoggerOptions, OpenAIConversationAdapter, OpenAIProvider, type TOpenAIProviderOptionValue, createOpenAIProviderDefinition };
@@ -0,0 +1,2 @@
1
+ import C from'openai';import {AbstractAIProvider,SilentLogger}from'@robota-sdk/agent-core';import {assembleOpenAICompatibleStream,probeOpenAICompatibleProfile,OpenAICompatibleResponseParser,convertToOpenAICompatibleMessages,convertToOpenAICompatibleTools}from'@robota-sdk/agent-provider-openai-compatible';var p=class{parser;constructor(e){this.parser=new OpenAICompatibleResponseParser({logger:e});}parseResponse(e){try{return this.parser.parseResponse(e)}catch(t){let r=d(t instanceof Error?t.message:"OpenAI response parsing failed");throw new Error(`OpenAI response parsing failed: ${r}`)}}parseStreamingChunk(e){try{return this.parser.parseStreamingChunk(e)}catch(t){let r=d(t instanceof Error?t.message:"OpenAI chunk parsing failed");throw new Error(`OpenAI chunk parsing failed: ${r}`)}}};function d(a){return a.replace(/^OpenAI-compatible response parsing failed: /,"").replace(/^OpenAI-compatible chunk parsing failed: /,"").replace("OpenAI-compatible response","OpenAI response")}function c(a){return convertToOpenAICompatibleMessages(a)}function m(a){return convertToOpenAICompatibleTools(a)}var i=class extends AbstractAIProvider{name="openai";version="1.0.0";client;options;payloadLogger;responseParser;onTextDelta;constructor(e){if(super(e.logger||SilentLogger),this.options=e,e.executor&&(this.executor=e.executor),!this.executor)if(e.client)this.client=e.client;else if(e.apiKey)this.client=new C({apiKey:e.apiKey,...e.organization&&{organization:e.organization},...e.timeout&&{timeout:e.timeout},...e.baseURL&&{baseURL:e.baseURL}});else throw new Error("Either OpenAI client, apiKey, or executor is required");this.responseParser=new p(this.logger),this.payloadLogger=e.payloadLogger;}async chat(e,t){if(this.validateMessages(e),this.executor)try{return await this.executeViaExecutorOrDirect(e,t)}catch(r){throw this.logger.error("OpenAI Provider executor chat error:",r instanceof Error?r.message:String(r)),r}if(!this.client)throw new Error("OpenAI client not available. Either provide a client/apiKey or use an executor.");try{let r=c(e),o=t;if(!o?.model)throw new Error("Model is required in chat options. Please specify a model in defaultModel configuration.");let s={model:o.model,messages:r,...o.temperature!==void 0&&{temperature:o.temperature},...o.maxTokens&&{max_tokens:o.maxTokens},...o.tools&&{tools:m(o.tools),tool_choice:"auto"}},n=o.onTextDelta??this.onTextDelta;if(n)return await this.chatWithStreamingAssembly({...s,stream:!0},{...o,onTextDelta:n});if(this.payloadLogger?.isEnabled()){let I={model:s.model,messagesCount:r.length,hasTools:!!s.tools,temperature:s.temperature??void 0,maxTokens:s.max_tokens??void 0,timestamp:new Date().toISOString()};await this.payloadLogger.logPayload(I,"chat");}let l=await this.client.chat.completions.create(s);return this.responseParser.parseResponse(l)}catch(r){let s=r.message||"OpenAI API request failed";throw new Error(`OpenAI chat failed: ${s}`)}}async chatWithStreamingAssembly(e,t){if(!this.client)throw new Error("OpenAI client not available. Either provide a client/apiKey or use an executor.");try{if(this.payloadLogger?.isEnabled()){let o={model:e.model,messagesCount:e.messages.length,hasTools:!!e.tools,temperature:e.temperature??void 0,maxTokens:e.max_tokens??void 0,timestamp:new Date().toISOString()};await this.payloadLogger.logPayload(o,"stream");}let r=await this.client.chat.completions.create(e,t.signal?{signal:t.signal}:void 0);return assembleOpenAICompatibleStream({stream:r,onTextDelta:t.onTextDelta,signal:t.signal})}catch(r){let s=r.message||"OpenAI streaming request failed";throw new Error(`OpenAI stream failed: ${s}`)}}async*chatStream(e,t){if(this.executor)try{yield*this.executeStreamViaExecutorOrDirect(e,t);return}catch(r){throw this.logger.error("OpenAI Provider executor stream error:",r instanceof Error?r.message:String(r)),r}if(!this.client)throw new Error("OpenAI client not available. Either provide a client/apiKey or use an executor.");try{let r=c(e);if(!t?.model)throw new Error("Model is required in chat options. Please specify a model in defaultModel configuration.");let o={model:t.model,messages:r,stream:!0,...t?.temperature!==void 0&&{temperature:t.temperature},...t?.maxTokens&&{max_tokens:t.maxTokens},...t?.tools&&{tools:m(t.tools),tool_choice:"auto"}};if(this.payloadLogger?.isEnabled()){let n={model:o.model,messagesCount:r.length,hasTools:!!o.tools,temperature:o.temperature??void 0,maxTokens:o.max_tokens??void 0,timestamp:new Date().toISOString()};await this.payloadLogger.logPayload(n,"stream");}let s=await this.client.chat.completions.create(o);for await(let n of s){let l=this.responseParser.parseStreamingChunk(n);l&&(yield l);}}catch(r){let s=r.message||"OpenAI API request failed";throw new Error(`OpenAI stream failed: ${s}`)}}supportsTools(){return true}validateConfig(){return !!this.client&&!!this.options}async dispose(){}validateMessages(e){super.validateMessages(e);for(let t of e)if(t.role==="assistant"){let r=t;if(r.toolCalls&&r.toolCalls.length>0&&r.content==="")continue}}};var g=class{static filterMessagesForOpenAI(e){return e.filter(t=>t.role==="user"||t.role==="assistant"||t.role==="system"?true:t.role==="tool"?!!(t.toolCallId&&t.toolCallId.trim()!==""&&t.toolCallId!=="unknown"):false)}static toOpenAIFormat(e){return this.filterMessagesForOpenAI(e).map(r=>this.convertMessage(r))}static convertMessage(e){let t=e.role;if(t==="user")return {role:"user",content:e.content};if(t==="assistant"){let o=e;return o.toolCalls&&o.toolCalls.length>0?{role:"assistant",content:o.content===""?null:o.content||null,tool_calls:o.toolCalls.map(n=>({id:n.id,type:"function",function:{name:n.function.name,arguments:n.function.arguments}}))}:{role:"assistant",content:o.content===null||o.content===""?null:o.content||""}}if(t==="system")return {role:"system",content:e.content};if(t==="tool"){if(!e.toolCallId||e.toolCallId.trim()==="")throw new Error(`Tool message missing toolCallId: ${JSON.stringify(e)}`);return {role:"tool",content:e.content,tool_call_id:e.toolCallId}}let r=t;throw new Error(`Unsupported message role: ${r}`)}static addSystemPromptIfNeeded(e,t){return !t||e.some(o=>o.role==="system")?e:[{role:"system",content:t},...e]}};var N=void 0,h="lm-studio",f="http://localhost:1234/v1";function z(){return {type:"openai",defaults:{apiKey:h,baseURL:f},setupSteps:[{key:"baseURL",title:"OpenAI-compatible base URL",defaultValue:f},{key:"model",title:"OpenAI-compatible model",required:true},{key:"apiKey",title:"OpenAI-compatible API key",defaultValue:h,masked:true}],requiresApiKey:true,probeProfile:probeOpenAICompatibleProfile,createProvider:a=>new i({apiKey:x(a.apiKey),...a.baseURL!==void 0&&{baseURL:a.baseURL},...a.timeout!==void 0&&{timeout:a.timeout},defaultModel:a.model})}}function x(a){if(!a)throw new Error("Provider openai requires apiKey");return a}
2
+ export{h as DEFAULT_OPENAI_COMPATIBLE_PROVIDER_API_KEY,f as DEFAULT_OPENAI_COMPATIBLE_PROVIDER_BASE_URL,N as DEFAULT_OPENAI_PROVIDER_MODEL,g as OpenAIConversationAdapter,i as OpenAIProvider,z as createOpenAIProviderDefinition};
package/package.json ADDED
@@ -0,0 +1,111 @@
1
+ {
2
+ "name": "@robota-sdk/agent-provider-openai",
3
+ "version": "3.0.0-beta.55",
4
+ "description": "OpenAI integration for Robota SDK - GPT-4, GPT-3.5, function calling, and tool integration with OpenAI's API",
5
+ "type": "module",
6
+ "main": "dist/node/index.js",
7
+ "module": "dist/node/index.mjs",
8
+ "types": "dist/node/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/node/index.d.ts",
12
+ "node": {
13
+ "import": "./dist/node/index.js",
14
+ "require": "./dist/node/index.cjs"
15
+ },
16
+ "browser": {
17
+ "import": "./dist/browser/index.js"
18
+ },
19
+ "default": {
20
+ "import": "./dist/node/index.js",
21
+ "require": "./dist/node/index.cjs"
22
+ }
23
+ },
24
+ "./loggers/file": {
25
+ "types": "./dist/loggers/file.d.ts",
26
+ "import": "./dist/loggers/file.js",
27
+ "require": "./dist/loggers/file.cjs"
28
+ },
29
+ "./loggers/console": {
30
+ "types": "./dist/loggers/console.d.ts",
31
+ "import": "./dist/loggers/console.js",
32
+ "require": "./dist/loggers/console.cjs"
33
+ }
34
+ },
35
+ "repository": {
36
+ "type": "git",
37
+ "url": "https://github.com/woojubb/robota.git",
38
+ "directory": "packages/agent-provider-openai"
39
+ },
40
+ "homepage": "https://robota.io/",
41
+ "bugs": {
42
+ "url": "https://github.com/woojubb/robota/issues"
43
+ },
44
+ "files": [
45
+ "dist",
46
+ "README.md",
47
+ "CHANGELOG.md"
48
+ ],
49
+ "keywords": [
50
+ "ai",
51
+ "agent",
52
+ "llm",
53
+ "openai",
54
+ "gpt",
55
+ "gpt-4",
56
+ "gpt-3.5",
57
+ "chatgpt",
58
+ "function-calling",
59
+ "typescript",
60
+ "javascript",
61
+ "chatbot",
62
+ "conversational-ai",
63
+ "artificial-intelligence",
64
+ "machine-learning",
65
+ "nlp",
66
+ "natural-language-processing",
67
+ "sdk",
68
+ "library",
69
+ "tool-integration",
70
+ "robota",
71
+ "openai-api",
72
+ "ai-integration"
73
+ ],
74
+ "author": "Robota SDK Team",
75
+ "license": "MIT",
76
+ "dependencies": {
77
+ "openai": "^4.98.0",
78
+ "@robota-sdk/agent-provider-openai-compatible": "3.0.0-beta.55"
79
+ },
80
+ "peerDependencies": {
81
+ "@robota-sdk/agent-core": "3.0.0-beta.55"
82
+ },
83
+ "devDependencies": {
84
+ "@typescript-eslint/eslint-plugin": "^6.18.0",
85
+ "@typescript-eslint/parser": "^6.18.0",
86
+ "eslint": "^8.56.0",
87
+ "rimraf": "^5.0.5",
88
+ "tsup": "^8.0.1",
89
+ "typescript": "^5.3.3",
90
+ "vitest": "^1.1.0",
91
+ "@robota-sdk/agent-core": "3.0.0-beta.55"
92
+ },
93
+ "publishConfig": {
94
+ "access": "public"
95
+ },
96
+ "scripts": {
97
+ "build": "tsup",
98
+ "build:js": "tsup --no-dts",
99
+ "build:types": "tsup --dts-only",
100
+ "dev": "tsup --watch",
101
+ "clean": "rimraf dist",
102
+ "test": "vitest run --passWithNoTests",
103
+ "test:watch": "vitest",
104
+ "test:coverage": "vitest run --coverage",
105
+ "lint": "eslint src --ext .ts,.tsx",
106
+ "typecheck": "tsc -p tsconfig.json --noEmit",
107
+ "lint:fix": "eslint src --ext .ts,.tsx --fix",
108
+ "scenario:verify": "pnpm --filter @robota-sdk/agent-core build && pnpm exec tsx examples/verify-executor.ts",
109
+ "scenario:record": "node ../../scripts/harness/record-owner-scenario.mjs --scope packages/agent-provider-openai --output examples/scenarios/executor-verify.record.json -- pnpm scenario:verify"
110
+ }
111
+ }