@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.
package/README.md ADDED
@@ -0,0 +1,443 @@
1
+ # @robota-sdk/agent-provider-openai
2
+
3
+ OpenAI Provider for Robota SDK - Complete type-safe integration with OpenAI's GPT models, featuring function calling, streaming, and advanced AI capabilities.
4
+
5
+ ## šŸš€ Features
6
+
7
+ ### Core Capabilities
8
+
9
+ - **šŸŽÆ Type-Safe Integration**: Complete TypeScript support with zero `any` types
10
+ - **šŸ¤– GPT Model Support**: GPT-4, GPT-3.5 Turbo, OpenAI models, and OpenAI-compatible chat completion models
11
+ - **⚔ Real-Time Streaming**: Asynchronous streaming responses with proper error handling
12
+ - **šŸ› ļø Function Calling**: Native OpenAI function calling with type validation
13
+ - **šŸ”„ Provider-Agnostic Design**: Seamless integration with other Robota providers
14
+ - **šŸ“Š Payload Logging**: Optional API request/response logging for debugging
15
+
16
+ ### Architecture Highlights
17
+
18
+ - **Generic Type Parameters**: Full `BaseAIProvider<TConfig, TMessage, TResponse>` implementation
19
+ - **Facade Pattern**: Modular design with separated concerns
20
+ - **Error Safety**: Comprehensive error handling without any-type compromises
21
+ - **OpenAI SDK Compatibility**: Direct integration with official OpenAI SDK types
22
+
23
+ ## šŸ“¦ Installation
24
+
25
+ ```bash
26
+ npm install @robota-sdk/agent-provider-openai @robota-sdk/agent-core openai
27
+ ```
28
+
29
+ ## šŸ”§ Basic Usage
30
+
31
+ ### Simple Chat Integration
32
+
33
+ ```typescript
34
+ import { Robota } from '@robota-sdk/agent-core';
35
+ import { OpenAIProvider } from '@robota-sdk/agent-provider-openai';
36
+
37
+ // Create type-safe OpenAI provider
38
+ const provider = new OpenAIProvider({
39
+ apiKey: process.env.OPENAI_API_KEY,
40
+ });
41
+
42
+ // Create Robota agent with OpenAI provider
43
+ const agent = new Robota({
44
+ name: 'MyAgent',
45
+ aiProviders: [provider],
46
+ defaultModel: {
47
+ provider: 'openai',
48
+ model: 'gpt-4',
49
+ temperature: 0.7,
50
+ systemMessage: 'You are a helpful AI assistant specialized in technical topics.',
51
+ },
52
+ });
53
+
54
+ // Execute conversation
55
+ const response = await agent.run('Explain the benefits of TypeScript over JavaScript');
56
+ console.log(response);
57
+
58
+ // Clean up
59
+ await agent.destroy();
60
+ ```
61
+
62
+ ### OpenAI-Compatible Endpoints
63
+
64
+ Use `baseURL` to point the provider at an OpenAI-compatible Chat Completions endpoint. For LM Studio, the local API typically listens on `http://localhost:1234/v1` and accepts a local placeholder API key:
65
+
66
+ ```typescript
67
+ import { OpenAIProvider } from '@robota-sdk/agent-provider-openai';
68
+
69
+ const provider = new OpenAIProvider({
70
+ apiKey: 'lm-studio',
71
+ baseURL: 'http://localhost:1234/v1',
72
+ defaultModel: '<local-openai-compatible-model>',
73
+ });
74
+ ```
75
+
76
+ Gemma-family local models should use `@robota-sdk/agent-provider-gemma` instead of this
77
+ OpenAI provider so Gemma chat-template channel markers are projected out of user-facing
78
+ streamed text.
79
+
80
+ ### Streaming Responses
81
+
82
+ ```typescript
83
+ // Real-time streaming for immediate feedback
84
+ const stream = await agent.runStream('Write a detailed explanation of machine learning');
85
+
86
+ for await (const chunk of stream) {
87
+ if (chunk.content) {
88
+ process.stdout.write(chunk.content);
89
+ }
90
+
91
+ // Handle streaming metadata
92
+ if (chunk.metadata?.isComplete) {
93
+ console.log('\nāœ“ Stream completed');
94
+ }
95
+ }
96
+ ```
97
+
98
+ When `chat()` receives an `onTextDelta` callback, the provider uses the streaming Chat Completions path internally, forwards text deltas to the callback, assembles streamed tool-call chunks, and returns the final assistant message.
99
+
100
+ ## šŸ› ļø Function Calling
101
+
102
+ OpenAI Provider supports type-safe function calling with automatic parameter validation:
103
+
104
+ ```typescript
105
+ import { FunctionTool } from '@robota-sdk/agent-core';
106
+ import { z } from 'zod';
107
+
108
+ // Define type-safe function tools
109
+ const weatherTool = new FunctionTool({
110
+ name: 'getWeather',
111
+ description: 'Get current weather information for a location',
112
+ parameters: z.object({
113
+ location: z.string().describe('City name'),
114
+ unit: z.enum(['celsius', 'fahrenheit']).default('celsius'),
115
+ }),
116
+ handler: async ({ location, unit }) => {
117
+ // Type-safe handler implementation
118
+ const weatherData = await fetchWeatherAPI(location, unit);
119
+ return {
120
+ temperature: weatherData.temp,
121
+ condition: weatherData.condition,
122
+ location,
123
+ unit,
124
+ };
125
+ },
126
+ });
127
+
128
+ const calculatorTool = new FunctionTool({
129
+ name: 'calculate',
130
+ description: 'Perform mathematical operations',
131
+ parameters: z.object({
132
+ operation: z.enum(['add', 'subtract', 'multiply', 'divide']),
133
+ a: z.number(),
134
+ b: z.number(),
135
+ }),
136
+ handler: async ({ operation, a, b }) => {
137
+ const operations = {
138
+ add: a + b,
139
+ subtract: a - b,
140
+ multiply: a * b,
141
+ divide: a / b,
142
+ };
143
+ return { result: operations[operation] };
144
+ },
145
+ });
146
+
147
+ // Register tools with the agent
148
+ agent.registerTool(weatherTool);
149
+ agent.registerTool(calculatorTool);
150
+
151
+ // Execute with function calling
152
+ const result = await agent.run("What's the weather in Tokyo and what's 25 * 4?");
153
+ ```
154
+
155
+ ## šŸ”„ Multi-Provider Architecture
156
+
157
+ Seamlessly integrate with other providers:
158
+
159
+ ```typescript
160
+ import { AnthropicProvider } from '@robota-sdk/agent-provider-anthropic';
161
+ import { GoogleProvider } from '@robota-sdk/agent-provider-google';
162
+
163
+ const openaiProvider = new OpenAIProvider({
164
+ apiKey: process.env.OPENAI_API_KEY,
165
+ });
166
+
167
+ const anthropicProvider = new AnthropicProvider({
168
+ apiKey: process.env.ANTHROPIC_API_KEY,
169
+ });
170
+
171
+ const googleProvider = new GoogleProvider({
172
+ apiKey: process.env.GOOGLE_AI_API_KEY,
173
+ });
174
+
175
+ const agent = new Robota({
176
+ name: 'MultiProviderAgent',
177
+ aiProviders: [openaiProvider, anthropicProvider, googleProvider],
178
+ defaultModel: {
179
+ provider: 'openai',
180
+ model: 'gpt-4',
181
+ },
182
+ });
183
+
184
+ // Dynamic provider switching
185
+ const openaiResponse = await agent.run('Respond using GPT-4');
186
+
187
+ agent.setModel({ provider: 'anthropic', model: 'claude-3-sonnet-20240229' });
188
+ const claudeResponse = await agent.run('Respond using Claude');
189
+ ```
190
+
191
+ ## āš™ļø Configuration Options
192
+
193
+ ```typescript
194
+ interface IOpenAIProviderOptions {
195
+ // Required
196
+ client: OpenAI; // OpenAI SDK client instance
197
+
198
+ // Model Configuration
199
+ model?: string; // Default: 'gpt-4'
200
+ temperature?: number; // 0-1, default: 0.7
201
+ maxTokens?: number; // Maximum tokens to generate
202
+
203
+ // API Configuration
204
+ apiKey?: string; // API key (if not set in client)
205
+ organization?: string; // OpenAI organization ID
206
+ timeout?: number; // Request timeout (ms)
207
+ baseURL?: string; // Custom API base URL
208
+
209
+ // Response Configuration
210
+ responseFormat?: 'text' | 'json_object' | 'json_schema';
211
+ jsonSchema?: {
212
+ // For structured outputs
213
+ name: string;
214
+ description?: string;
215
+ schema?: Record<string, string | number | boolean | object>;
216
+ strict?: boolean;
217
+ };
218
+
219
+ // Debugging & Logging
220
+ payloadLogger?: IPayloadLogger; // Environment-specific payload logger
221
+
222
+ // Interface-based logger implementations:
223
+ // - FilePayloadLogger: Node.js file-based logging
224
+ // - ConsolePayloadLogger: Browser console-based logging
225
+ // - Custom: Implement IPayloadLogger interface
226
+ }
227
+ ```
228
+
229
+ ## šŸ“‹ Supported Models
230
+
231
+ | Model | Description | Use Cases |
232
+ | ---------------------- | -------------------- | ------------------------------------------- |
233
+ | `gpt-4` | Most capable model | Complex reasoning, analysis, creative tasks |
234
+ | `gpt-4-turbo` | Faster GPT-4 variant | Balanced performance and cost |
235
+ | `gpt-3.5-turbo` | Fast and efficient | Simple conversations, basic tasks |
236
+ | `gpt-4-vision-preview` | Vision capabilities | Image analysis and understanding |
237
+
238
+ ## šŸ” API Reference
239
+
240
+ ### OpenAIProvider Class
241
+
242
+ ```typescript
243
+ class OpenAIProvider extends BaseAIProvider<
244
+ IOpenAIProviderOptions,
245
+ UniversalMessage,
246
+ UniversalMessage
247
+ > {
248
+ // Core methods
249
+ async chat(messages: UniversalMessage[], options?: ChatOptions): Promise<UniversalMessage>;
250
+ async chatStream(
251
+ messages: UniversalMessage[],
252
+ options?: ChatOptions,
253
+ ): AsyncIterable<UniversalMessage>;
254
+
255
+ // Provider information
256
+ readonly name: string = 'openai';
257
+ readonly version: string = '1.0.0';
258
+
259
+ // Utility methods
260
+ supportsTools(): boolean;
261
+ validateConfig(): boolean;
262
+ async dispose(): Promise<void>;
263
+ }
264
+ ```
265
+
266
+ ### Type Definitions
267
+
268
+ ```typescript
269
+ // Chat Options
270
+ interface ChatOptions {
271
+ tools?: ToolSchema[];
272
+ maxTokens?: number;
273
+ temperature?: number;
274
+ model?: string;
275
+ }
276
+
277
+ // OpenAI-specific types
278
+ interface OpenAIToolCall {
279
+ id: string;
280
+ type: 'function';
281
+ function: {
282
+ name: string;
283
+ arguments: string;
284
+ };
285
+ }
286
+
287
+ interface OpenAILogData {
288
+ model: string;
289
+ messagesCount: number;
290
+ hasTools: boolean;
291
+ temperature?: number;
292
+ maxTokens?: number;
293
+ timestamp: string;
294
+ requestId?: string;
295
+ }
296
+ ```
297
+
298
+ ## šŸ› Debugging & Logging
299
+
300
+ ### Environment-Specific Payload Logging
301
+
302
+ The OpenAI Provider supports environment-specific payload logging through interface-based dependency injection:
303
+
304
+ #### Node.js Environment (File-Based Logging)
305
+
306
+ ```typescript
307
+ import { OpenAIProvider } from '@robota-sdk/agent-provider-openai';
308
+ import { FilePayloadLogger } from '@robota-sdk/agent-provider-openai/loggers/file';
309
+
310
+ const provider = new OpenAIProvider({
311
+ client: openaiClient,
312
+ model: 'gpt-4',
313
+ payloadLogger: new FilePayloadLogger({
314
+ logDir: './logs/openai-api',
315
+ enabled: true,
316
+ includeTimestamp: true,
317
+ }),
318
+ });
319
+ ```
320
+
321
+ #### Browser Environment (Console-Based Logging)
322
+
323
+ ```typescript
324
+ import { OpenAIProvider } from '@robota-sdk/agent-provider-openai';
325
+ import { ConsolePayloadLogger } from '@robota-sdk/agent-provider-openai/loggers/console';
326
+
327
+ const provider = new OpenAIProvider({
328
+ client: openaiClient,
329
+ model: 'gpt-4',
330
+ payloadLogger: new ConsolePayloadLogger({
331
+ enabled: true,
332
+ includeTimestamp: true,
333
+ }),
334
+ });
335
+ ```
336
+
337
+ #### No Logging (Both Environments)
338
+
339
+ ```typescript
340
+ const provider = new OpenAIProvider({
341
+ client: openaiClient,
342
+ model: 'gpt-4',
343
+ // payloadLogger: undefined (default - no logging)
344
+ });
345
+ ```
346
+
347
+ ### Custom Logger Implementation
348
+
349
+ You can create custom logger implementations by implementing the IPayloadLogger interface:
350
+
351
+ ```typescript
352
+ import type { IPayloadLogger, OpenAILogData } from '@robota-sdk/agent-provider-openai';
353
+
354
+ class CustomPayloadLogger implements IPayloadLogger {
355
+ isEnabled(): boolean {
356
+ return true;
357
+ }
358
+
359
+ async logPayload(payload: OpenAILogData, type: 'chat' | 'stream'): Promise<void> {
360
+ // Custom logging implementation
361
+ console.log(`[Custom Logger] ${type}:`, payload);
362
+ }
363
+ }
364
+
365
+ const provider = new OpenAIProvider({
366
+ client: openaiClient,
367
+ payloadLogger: new CustomPayloadLogger(),
368
+ });
369
+ ```
370
+
371
+ This creates detailed logs of all API requests and responses for debugging purposes.
372
+
373
+ ## šŸ”’ Security Best Practices
374
+
375
+ ### API Key Management
376
+
377
+ ```typescript
378
+ // āœ… Good: Use environment variables
379
+ const client = new OpenAI({
380
+ apiKey: process.env.OPENAI_API_KEY,
381
+ });
382
+
383
+ // āŒ Bad: Hardcoded keys
384
+ const client = new OpenAI({
385
+ apiKey: 'sk-...', // Never do this!
386
+ });
387
+ ```
388
+
389
+ ### Error Handling
390
+
391
+ ```typescript
392
+ try {
393
+ const response = await agent.run('Your query');
394
+ } catch (error) {
395
+ if (error instanceof Error) {
396
+ console.error('AI Error:', error.message);
397
+ }
398
+ // Handle specific OpenAI errors
399
+ }
400
+ ```
401
+
402
+ ## šŸ“Š Performance Optimization
403
+
404
+ ### Token Management
405
+
406
+ ```typescript
407
+ const provider = new OpenAIProvider({
408
+ client: openaiClient,
409
+ model: 'gpt-4',
410
+ maxTokens: 1000, // Limit response length
411
+ temperature: 0.3, // More deterministic responses
412
+ });
413
+ ```
414
+
415
+ ### Model Selection Strategy
416
+
417
+ - Use `gpt-3.5-turbo` for simple tasks
418
+ - Use `gpt-4` for complex reasoning
419
+ - Use `gpt-4-turbo` for balanced performance
420
+
421
+ ## šŸ¤ Contributing
422
+
423
+ This package follows strict type safety guidelines:
424
+
425
+ - Zero `any` or `unknown` types allowed
426
+ - Complete TypeScript coverage
427
+ - Comprehensive error handling
428
+ - Provider-agnostic design principles
429
+
430
+ ## šŸ“„ License
431
+
432
+ MIT License - see LICENSE file for details.
433
+
434
+ ## šŸ”— Related Packages
435
+
436
+ - **[@robota-sdk/agent-core](../agents/)**: Core agent framework
437
+ - **[@robota-sdk/agent-provider-anthropic](../anthropic/)**: Anthropic Claude provider
438
+ - **[@robota-sdk/agent-provider-google](../google/)**: Google AI provider
439
+ - **[@robota-sdk/agent-team](../team/)**: assignTask MCP tool collection (team creation removed)
440
+
441
+ ---
442
+
443
+ For complete documentation and examples, visit the [Robota SDK Documentation](https://robota.io).
@@ -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};