@robota-sdk/agent-provider-openai 3.0.0-beta.59 → 3.0.0-beta.61
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/CHANGELOG.md +24 -0
- package/README.md +66 -44
- package/dist/browser/index.d.ts +81 -13
- package/dist/browser/index.js +2 -2
- package/dist/node/index.cjs +2 -2
- package/dist/node/index.d.cts +81 -13
- package/dist/node/index.d.ts +81 -13
- package/dist/node/index.js +2 -2
- package/package.json +8 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
# @robota-sdk/agent-provider-openai
|
|
2
2
|
|
|
3
|
+
## 3.0.0-beta.61
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 36eb7a9: Add provider-owned native replay payload hooks, replay validation coverage, and a session log validation command.
|
|
8
|
+
- d97bdf2: Add provider-owned model catalog metadata, route `/model` suggestions through the active provider, and make `cli:dev` resolve the CLI workspace dependency closure through source export conditions.
|
|
9
|
+
- Updated dependencies [1c0d44c]
|
|
10
|
+
- Updated dependencies [36eb7a9]
|
|
11
|
+
- Updated dependencies [d97bdf2]
|
|
12
|
+
- @robota-sdk/agent-core@3.0.0-beta.61
|
|
13
|
+
- @robota-sdk/agent-provider-openai-compatible@3.0.0-beta.61
|
|
14
|
+
|
|
15
|
+
## 3.0.0-beta.60
|
|
16
|
+
|
|
17
|
+
### Minor Changes
|
|
18
|
+
|
|
19
|
+
- 7439391: Add provider-neutral native web search/fetch capability contracts, explicit unsupported handling for OpenAI-compatible/LM Studio profiles, and local WebFetch/WebSearch permission/documentation alignment.
|
|
20
|
+
|
|
21
|
+
### Patch Changes
|
|
22
|
+
|
|
23
|
+
- Updated dependencies [7439391]
|
|
24
|
+
- @robota-sdk/agent-core@3.0.0-beta.60
|
|
25
|
+
- @robota-sdk/agent-provider-openai-compatible@3.0.0-beta.60
|
|
26
|
+
|
|
3
27
|
## 3.0.0-beta.59
|
|
4
28
|
|
|
5
29
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -1,21 +1,24 @@
|
|
|
1
1
|
# @robota-sdk/agent-provider-openai
|
|
2
2
|
|
|
3
|
-
OpenAI Provider for Robota SDK -
|
|
3
|
+
OpenAI Provider for Robota SDK - type-safe integration with the official OpenAI Responses API, structured outputs, streaming, and function calling.
|
|
4
4
|
|
|
5
5
|
## 🚀 Features
|
|
6
6
|
|
|
7
7
|
### Core Capabilities
|
|
8
8
|
|
|
9
9
|
- **🎯 Type-Safe Integration**: Complete TypeScript support with zero `any` types
|
|
10
|
-
- **🤖
|
|
10
|
+
- **🤖 OpenAI Model Support**: Official OpenAI models through the Responses API by default
|
|
11
11
|
- **⚡ Real-Time Streaming**: Asynchronous streaming responses with proper error handling
|
|
12
12
|
- **🛠️ Function Calling**: Native OpenAI function calling with type validation
|
|
13
|
+
- **🧩 Structured Outputs**: JSON object and JSON Schema response formats
|
|
14
|
+
- **🌐 Capability Reporting**: Explicit native web search/fetch capability state without hidden local-tool fallback
|
|
15
|
+
- **Provider-Owned Model Catalog Refresh**: `/model` can discover OpenAI models through the provider definition without CLI/TUI-owned model lists
|
|
13
16
|
- **🔄 Provider-Agnostic Design**: Seamless integration with other Robota providers
|
|
14
17
|
- **📊 Payload Logging**: Optional API request/response logging for debugging
|
|
15
18
|
|
|
16
19
|
### Architecture Highlights
|
|
17
20
|
|
|
18
|
-
- **
|
|
21
|
+
- **Provider Base Contract**: `AbstractAIProvider` implementation for the Robota provider interface
|
|
19
22
|
- **Facade Pattern**: Modular design with separated concerns
|
|
20
23
|
- **Error Safety**: Comprehensive error handling without any-type compromises
|
|
21
24
|
- **OpenAI SDK Compatibility**: Direct integration with official OpenAI SDK types
|
|
@@ -37,6 +40,7 @@ import { OpenAIProvider } from '@robota-sdk/agent-provider-openai';
|
|
|
37
40
|
// Create type-safe OpenAI provider
|
|
38
41
|
const provider = new OpenAIProvider({
|
|
39
42
|
apiKey: process.env.OPENAI_API_KEY,
|
|
43
|
+
defaultModel: 'gpt-4o',
|
|
40
44
|
});
|
|
41
45
|
|
|
42
46
|
// Create Robota agent with OpenAI provider
|
|
@@ -45,7 +49,7 @@ const agent = new Robota({
|
|
|
45
49
|
aiProviders: [provider],
|
|
46
50
|
defaultModel: {
|
|
47
51
|
provider: 'openai',
|
|
48
|
-
model: 'gpt-
|
|
52
|
+
model: 'gpt-4o',
|
|
49
53
|
temperature: 0.7,
|
|
50
54
|
systemMessage: 'You are a helpful AI assistant specialized in technical topics.',
|
|
51
55
|
},
|
|
@@ -61,7 +65,7 @@ await agent.destroy();
|
|
|
61
65
|
|
|
62
66
|
### OpenAI-Compatible Endpoints
|
|
63
67
|
|
|
64
|
-
|
|
68
|
+
The official OpenAI profile uses the Responses API by default. Set `baseURL`, or set `apiSurface: 'chat-completions'`, only when you intentionally need 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
69
|
|
|
66
70
|
```typescript
|
|
67
71
|
import { OpenAIProvider } from '@robota-sdk/agent-provider-openai';
|
|
@@ -77,7 +81,17 @@ Gemma-family local models should use `@robota-sdk/agent-provider-gemma` instead
|
|
|
77
81
|
OpenAI provider so Gemma chat-template channel markers are projected out of user-facing
|
|
78
82
|
streamed text.
|
|
79
83
|
|
|
80
|
-
For provider packages that share OpenAI-compatible transport code, the reusable primitives live in `@robota-sdk/agent-provider-openai-compatible`. This package owns OpenAI product semantics and
|
|
84
|
+
For provider packages that share OpenAI-compatible transport code, the reusable primitives live in `@robota-sdk/agent-provider-openai-compatible`. This package owns OpenAI product semantics and an explicit compatibility mode; model-family behavior such as Gemma reasoning/tool-call projection belongs in that model-family provider.
|
|
85
|
+
|
|
86
|
+
OpenAI-compatible Chat Completions profiles, including LM Studio-style `baseURL` profiles, are custom function-tool capable but are not treated as provider-native hosted web search/fetch providers. Use Robota local `WebSearch`/`WebFetch` tools for explicit local web access unless a concrete provider package documents hosted web support.
|
|
87
|
+
|
|
88
|
+
### Model Catalog Refresh
|
|
89
|
+
|
|
90
|
+
`createOpenAIProviderDefinition()` exposes provider-owned setup help links and a
|
|
91
|
+
provider-owned `refreshModelCatalog` hook. SDK command
|
|
92
|
+
common APIs may call this hook to query the OpenAI Models API with the effective provider profile and
|
|
93
|
+
surface catalog freshness in `/model` output. CLI/TUI layers must render that result rather than
|
|
94
|
+
owning OpenAI model metadata.
|
|
81
95
|
|
|
82
96
|
### Streaming Responses
|
|
83
97
|
|
|
@@ -97,7 +111,11 @@ for await (const chunk of stream) {
|
|
|
97
111
|
}
|
|
98
112
|
```
|
|
99
113
|
|
|
100
|
-
When `chat()` receives an `onTextDelta` callback, the provider uses the
|
|
114
|
+
When `chat()` receives an `onTextDelta` callback, the provider uses the selected API surface's streaming path internally, forwards text deltas to the callback, assembles streamed tool-call chunks, and returns the final assistant message.
|
|
115
|
+
|
|
116
|
+
### Native Replay Payload Capture
|
|
117
|
+
|
|
118
|
+
When `IChatOptions.onProviderNativeRawPayload` is provided, the provider emits exact OpenAI SDK request, response, and stream event payloads with `apiSurface` set to either `responses` or `chat-completions`. `agent-core` routes these provider-owned callbacks into provider-neutral `provider_native_raw_payload` execution events for replay-grade session logs.
|
|
101
119
|
|
|
102
120
|
## 🛠️ Function Calling
|
|
103
121
|
|
|
@@ -179,12 +197,12 @@ const agent = new Robota({
|
|
|
179
197
|
aiProviders: [openaiProvider, anthropicProvider, googleProvider],
|
|
180
198
|
defaultModel: {
|
|
181
199
|
provider: 'openai',
|
|
182
|
-
model: 'gpt-
|
|
200
|
+
model: 'gpt-4o',
|
|
183
201
|
},
|
|
184
202
|
});
|
|
185
203
|
|
|
186
204
|
// Dynamic provider switching
|
|
187
|
-
const openaiResponse = await agent.run('Respond using GPT-
|
|
205
|
+
const openaiResponse = await agent.run('Respond using GPT-4o');
|
|
188
206
|
|
|
189
207
|
agent.setModel({ provider: 'anthropic', model: 'claude-3-sonnet-20240229' });
|
|
190
208
|
const claudeResponse = await agent.run('Respond using Claude');
|
|
@@ -194,19 +212,16 @@ const claudeResponse = await agent.run('Respond using Claude');
|
|
|
194
212
|
|
|
195
213
|
```typescript
|
|
196
214
|
interface IOpenAIProviderOptions {
|
|
197
|
-
// Required
|
|
198
|
-
client: OpenAI; // OpenAI SDK client instance
|
|
199
|
-
|
|
200
215
|
// Model Configuration
|
|
201
|
-
|
|
202
|
-
temperature?: number; // 0-1, default: 0.7
|
|
203
|
-
maxTokens?: number; // Maximum tokens to generate
|
|
216
|
+
defaultModel?: string; // Used when chat options do not provide a model
|
|
204
217
|
|
|
205
218
|
// API Configuration
|
|
206
219
|
apiKey?: string; // API key (if not set in client)
|
|
220
|
+
client?: OpenAI; // OpenAI SDK client instance
|
|
207
221
|
organization?: string; // OpenAI organization ID
|
|
208
222
|
timeout?: number; // Request timeout (ms)
|
|
209
|
-
baseURL?: string; // Custom API base URL
|
|
223
|
+
baseURL?: string; // Custom API base URL; defaults to Chat Completions compatibility
|
|
224
|
+
apiSurface?: 'responses' | 'chat-completions';
|
|
210
225
|
|
|
211
226
|
// Response Configuration
|
|
212
227
|
responseFormat?: 'text' | 'json_object' | 'json_schema';
|
|
@@ -217,6 +232,13 @@ interface IOpenAIProviderOptions {
|
|
|
217
232
|
schema?: Record<string, string | number | boolean | object>;
|
|
218
233
|
strict?: boolean;
|
|
219
234
|
};
|
|
235
|
+
reasoning?: {
|
|
236
|
+
effort?: 'low' | 'medium' | 'high';
|
|
237
|
+
summary?: 'auto' | 'concise' | 'detailed';
|
|
238
|
+
};
|
|
239
|
+
store?: boolean;
|
|
240
|
+
includeEncryptedReasoning?: boolean;
|
|
241
|
+
strictTools?: boolean;
|
|
220
242
|
|
|
221
243
|
// Debugging & Logging
|
|
222
244
|
payloadLogger?: IPayloadLogger; // Environment-specific payload logger
|
|
@@ -228,31 +250,26 @@ interface IOpenAIProviderOptions {
|
|
|
228
250
|
}
|
|
229
251
|
```
|
|
230
252
|
|
|
231
|
-
## 📋
|
|
253
|
+
## 📋 Model Guidance
|
|
232
254
|
|
|
233
|
-
| Model
|
|
234
|
-
|
|
|
235
|
-
| `gpt-
|
|
236
|
-
| `gpt-4
|
|
237
|
-
| `
|
|
238
|
-
| `gpt-4-vision-preview` | Vision capabilities | Image analysis and understanding |
|
|
255
|
+
| Model | Description | Use Cases |
|
|
256
|
+
| --------- | --------------------- | ------------------------------------------- |
|
|
257
|
+
| `gpt-4o` | General-purpose model | Multimodal chat, tool use, balanced latency |
|
|
258
|
+
| `gpt-4.1` | Strong coding model | Code generation, refactoring, analysis |
|
|
259
|
+
| `o3` | Reasoning model | Complex reasoning and planning |
|
|
239
260
|
|
|
240
261
|
## 🔍 API Reference
|
|
241
262
|
|
|
242
263
|
### OpenAIProvider Class
|
|
243
264
|
|
|
244
265
|
```typescript
|
|
245
|
-
class OpenAIProvider extends
|
|
246
|
-
IOpenAIProviderOptions,
|
|
247
|
-
UniversalMessage,
|
|
248
|
-
UniversalMessage
|
|
249
|
-
> {
|
|
266
|
+
class OpenAIProvider extends AbstractAIProvider {
|
|
250
267
|
// Core methods
|
|
251
|
-
async chat(messages:
|
|
268
|
+
async chat(messages: TUniversalMessage[], options?: IChatOptions): Promise<TUniversalMessage>;
|
|
252
269
|
async chatStream(
|
|
253
|
-
messages:
|
|
254
|
-
options?:
|
|
255
|
-
): AsyncIterable<
|
|
270
|
+
messages: TUniversalMessage[],
|
|
271
|
+
options?: IChatOptions,
|
|
272
|
+
): AsyncIterable<TUniversalMessage>;
|
|
256
273
|
|
|
257
274
|
// Provider information
|
|
258
275
|
readonly name: string = 'openai';
|
|
@@ -269,8 +286,8 @@ class OpenAIProvider extends BaseAIProvider<
|
|
|
269
286
|
|
|
270
287
|
```typescript
|
|
271
288
|
// Chat Options
|
|
272
|
-
interface
|
|
273
|
-
tools?:
|
|
289
|
+
interface IChatOptions {
|
|
290
|
+
tools?: IToolSchema[];
|
|
274
291
|
maxTokens?: number;
|
|
275
292
|
temperature?: number;
|
|
276
293
|
model?: string;
|
|
@@ -286,7 +303,7 @@ interface OpenAIToolCall {
|
|
|
286
303
|
};
|
|
287
304
|
}
|
|
288
305
|
|
|
289
|
-
interface
|
|
306
|
+
interface IOpenAILogData {
|
|
290
307
|
model: string;
|
|
291
308
|
messagesCount: number;
|
|
292
309
|
hasTools: boolean;
|
|
@@ -311,7 +328,7 @@ import { FilePayloadLogger } from '@robota-sdk/agent-provider-openai/loggers/fil
|
|
|
311
328
|
|
|
312
329
|
const provider = new OpenAIProvider({
|
|
313
330
|
client: openaiClient,
|
|
314
|
-
|
|
331
|
+
defaultModel: 'gpt-4o',
|
|
315
332
|
payloadLogger: new FilePayloadLogger({
|
|
316
333
|
logDir: './logs/openai-api',
|
|
317
334
|
enabled: true,
|
|
@@ -328,7 +345,7 @@ import { ConsolePayloadLogger } from '@robota-sdk/agent-provider-openai/loggers/
|
|
|
328
345
|
|
|
329
346
|
const provider = new OpenAIProvider({
|
|
330
347
|
client: openaiClient,
|
|
331
|
-
|
|
348
|
+
defaultModel: 'gpt-4o',
|
|
332
349
|
payloadLogger: new ConsolePayloadLogger({
|
|
333
350
|
enabled: true,
|
|
334
351
|
includeTimestamp: true,
|
|
@@ -341,7 +358,7 @@ const provider = new OpenAIProvider({
|
|
|
341
358
|
```typescript
|
|
342
359
|
const provider = new OpenAIProvider({
|
|
343
360
|
client: openaiClient,
|
|
344
|
-
|
|
361
|
+
defaultModel: 'gpt-4o',
|
|
345
362
|
// payloadLogger: undefined (default - no logging)
|
|
346
363
|
});
|
|
347
364
|
```
|
|
@@ -351,14 +368,16 @@ const provider = new OpenAIProvider({
|
|
|
351
368
|
You can create custom logger implementations by implementing the IPayloadLogger interface:
|
|
352
369
|
|
|
353
370
|
```typescript
|
|
354
|
-
import type { IPayloadLogger
|
|
371
|
+
import type { IPayloadLogger } from '@robota-sdk/agent-provider-openai';
|
|
372
|
+
|
|
373
|
+
type OpenAILogPayload = Parameters<IPayloadLogger['logPayload']>[0];
|
|
355
374
|
|
|
356
375
|
class CustomPayloadLogger implements IPayloadLogger {
|
|
357
376
|
isEnabled(): boolean {
|
|
358
377
|
return true;
|
|
359
378
|
}
|
|
360
379
|
|
|
361
|
-
async logPayload(payload:
|
|
380
|
+
async logPayload(payload: OpenAILogPayload, type: 'chat' | 'stream'): Promise<void> {
|
|
362
381
|
// Custom logging implementation
|
|
363
382
|
console.log(`[Custom Logger] ${type}:`, payload);
|
|
364
383
|
}
|
|
@@ -408,7 +427,10 @@ try {
|
|
|
408
427
|
```typescript
|
|
409
428
|
const provider = new OpenAIProvider({
|
|
410
429
|
client: openaiClient,
|
|
411
|
-
|
|
430
|
+
defaultModel: 'gpt-4o',
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
const response = await provider.chat(messages, {
|
|
412
434
|
maxTokens: 1000, // Limit response length
|
|
413
435
|
temperature: 0.3, // More deterministic responses
|
|
414
436
|
});
|
|
@@ -416,9 +438,9 @@ const provider = new OpenAIProvider({
|
|
|
416
438
|
|
|
417
439
|
### Model Selection Strategy
|
|
418
440
|
|
|
419
|
-
- Use `gpt-
|
|
420
|
-
- Use `gpt-4` for
|
|
421
|
-
- Use `
|
|
441
|
+
- Use `gpt-4o` for balanced multimodal chat and tool use
|
|
442
|
+
- Use `gpt-4.1` for coding-heavy workflows
|
|
443
|
+
- Use reasoning models such as `o3` when explicit reasoning controls are required
|
|
422
444
|
|
|
423
445
|
## 🤝 Contributing
|
|
424
446
|
|
package/dist/browser/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import OpenAI from 'openai';
|
|
2
|
-
import { ILogger, IExecutor, TProviderOptionValueBase, AbstractAIProvider, TTextDeltaCallback, TUniversalMessage, IChatOptions, IProviderDefinition } from '@robota-sdk/agent-core';
|
|
2
|
+
import { ILogger, IExecutor, TProviderOptionValueBase, AbstractAIProvider, TTextDeltaCallback, TUniversalMessage, IChatOptions, IProviderCapabilities, IProviderProfileConfig, IProviderModelCatalog, IProviderDefinition } from '@robota-sdk/agent-core';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Payload logging data structure
|
|
@@ -56,10 +56,25 @@ interface IPayloadLoggerOptions {
|
|
|
56
56
|
logger?: ILogger;
|
|
57
57
|
}
|
|
58
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
|
+
}
|
|
59
74
|
/**
|
|
60
75
|
* Valid provider option value types
|
|
61
76
|
*/
|
|
62
|
-
type TOpenAIProviderOptionValue = string | number | boolean | undefined | null | OpenAI | IPayloadLogger | ILogger | IExecutor | TProviderOptionValueBase | TOpenAIProviderOptionValue[] | {
|
|
77
|
+
type TOpenAIProviderOptionValue = string | number | boolean | undefined | null | IOpenAIJsonSchemaDefinition | IOpenAINativeWebToolsOptions | IOpenAIResponsesReasoningOptions | OpenAI | IPayloadLogger | ILogger | IExecutor | TProviderOptionValueBase | TOpenAIProviderOptionValue[] | {
|
|
63
78
|
[key: string]: TOpenAIProviderOptionValue;
|
|
64
79
|
};
|
|
65
80
|
/**
|
|
@@ -86,6 +101,17 @@ interface IOpenAIProviderOptions {
|
|
|
86
101
|
* API base URL (default: 'https://api.openai.com/v1')
|
|
87
102
|
*/
|
|
88
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;
|
|
89
115
|
/**
|
|
90
116
|
* Response format (default: 'text')
|
|
91
117
|
* - 'text': Plain text response
|
|
@@ -96,12 +122,32 @@ interface IOpenAIProviderOptions {
|
|
|
96
122
|
/**
|
|
97
123
|
* JSON schema for structured outputs (required when responseFormat is 'json_schema')
|
|
98
124
|
*/
|
|
99
|
-
jsonSchema?:
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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;
|
|
105
151
|
/**
|
|
106
152
|
* OpenAI client instance (optional: will be created from apiKey if not provided)
|
|
107
153
|
*/
|
|
@@ -168,7 +214,7 @@ interface IOpenAIProviderOptions {
|
|
|
168
214
|
/**
|
|
169
215
|
* OpenAI provider implementation for Robota
|
|
170
216
|
*
|
|
171
|
-
* Provides integration with OpenAI
|
|
217
|
+
* Provides integration with OpenAI models through the Robota provider contract.
|
|
172
218
|
* Uses OpenAI SDK native types internally for optimal performance and feature support.
|
|
173
219
|
*
|
|
174
220
|
* @public
|
|
@@ -178,6 +224,7 @@ declare class OpenAIProvider extends AbstractAIProvider {
|
|
|
178
224
|
readonly version = "1.0.0";
|
|
179
225
|
private readonly client?;
|
|
180
226
|
private readonly options;
|
|
227
|
+
private readonly apiSurface;
|
|
181
228
|
private readonly payloadLogger;
|
|
182
229
|
private readonly responseParser;
|
|
183
230
|
/**
|
|
@@ -189,9 +236,9 @@ declare class OpenAIProvider extends AbstractAIProvider {
|
|
|
189
236
|
onTextDelta?: TTextDeltaCallback;
|
|
190
237
|
constructor(options: IOpenAIProviderOptions);
|
|
191
238
|
chat(messages: TUniversalMessage[], options?: IChatOptions): Promise<TUniversalMessage>;
|
|
192
|
-
private chatWithStreamingAssembly;
|
|
193
239
|
chatStream(messages: TUniversalMessage[], options?: IChatOptions): AsyncIterable<TUniversalMessage>;
|
|
194
240
|
supportsTools(): boolean;
|
|
241
|
+
getCapabilities(): IProviderCapabilities;
|
|
195
242
|
validateConfig(): boolean;
|
|
196
243
|
dispose(): Promise<void>;
|
|
197
244
|
protected validateMessages(messages: TUniversalMessage[]): void;
|
|
@@ -231,9 +278,30 @@ declare class OpenAIConversationAdapter {
|
|
|
231
278
|
static addSystemPromptIfNeeded(messages: OpenAI.Chat.ChatCompletionMessageParam[], systemPrompt?: string): OpenAI.Chat.ChatCompletionMessageParam[];
|
|
232
279
|
}
|
|
233
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
|
+
|
|
234
303
|
declare const DEFAULT_OPENAI_PROVIDER_MODEL: string | undefined;
|
|
235
|
-
declare const
|
|
236
|
-
declare const DEFAULT_OPENAI_COMPATIBLE_PROVIDER_BASE_URL = "http://localhost:1234/v1";
|
|
304
|
+
declare const DEFAULT_OPENAI_PROVIDER_API_KEY_REFERENCE = "$ENV:OPENAI_API_KEY";
|
|
237
305
|
declare function createOpenAIProviderDefinition(): IProviderDefinition;
|
|
238
306
|
|
|
239
|
-
export {
|
|
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 };
|
package/dist/browser/index.js
CHANGED
|
@@ -1,2 +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 m(a){return convertToOpenAICompatibleMessages(a)}function c(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=m(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:c(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=m(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:c(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 F=void 0,h="lm-studio",f="http://localhost:1234/v1";function z(){return {type:"openai",displayName:"OpenAI Compatible",description:"OpenAI or OpenAI-compatible Chat Completions endpoint",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{
|
|
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};
|
package/dist/node/index.cjs
CHANGED
|
@@ -1,2 +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 m(a){return agentProviderOpenaiCompatible.convertToOpenAICompatibleMessages(a)}function c(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=m(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:c(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=m(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:c(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 F=void 0,h="lm-studio",f="http://localhost:1234/v1";function z(){return {type:"openai",displayName:"OpenAI Compatible",description:"OpenAI or OpenAI-compatible Chat Completions endpoint",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.
|
|
1
|
+
'use strict';var Se=require('openai'),agentCore=require('@robota-sdk/agent-core'),agentProviderOpenaiCompatible=require('@robota-sdk/agent-provider-openai-compatible'),crypto=require('crypto');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var Se__default=/*#__PURE__*/_interopDefault(Se);var p=class{parser;constructor(t){this.parser=new agentProviderOpenaiCompatible.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 agentProviderOpenaiCompatible.convertToOpenAICompatibleMessages(e)}function T(e){return agentProviderOpenaiCompatible.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 agentProviderOpenaiCompatible.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 agentProviderOpenaiCompatible.assembleOpenAICompatibleStream({stream:agentProviderOpenaiCompatible.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:crypto.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:agentProviderOpenaiCompatible.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:crypto.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 agentCore.AbstractAIProvider{name="openai";version="1.0.0";client;options;apiSurface;payloadLogger;responseParser;onTextDelta;constructor(t){if(super(t.logger||agentCore.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__default.default({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:agentProviderOpenaiCompatible.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
|
+
exports.DEFAULT_OPENAI_PROVIDER_API_KEY_REFERENCE=Y;exports.DEFAULT_OPENAI_PROVIDER_MODEL=St;exports.OpenAIConversationAdapter=J;exports.OpenAIProvider=i;exports.createOpenAIProviderDefinition=Mt;exports.refreshOpenAIModelCatalog=H;
|
package/dist/node/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import OpenAI from 'openai';
|
|
2
|
-
import { ILogger, IExecutor, TProviderOptionValueBase, AbstractAIProvider, TTextDeltaCallback, TUniversalMessage, IChatOptions, IProviderDefinition } from '@robota-sdk/agent-core';
|
|
2
|
+
import { ILogger, IExecutor, TProviderOptionValueBase, AbstractAIProvider, TTextDeltaCallback, TUniversalMessage, IChatOptions, IProviderCapabilities, IProviderProfileConfig, IProviderModelCatalog, IProviderDefinition } from '@robota-sdk/agent-core';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Payload logging data structure
|
|
@@ -56,10 +56,25 @@ interface IPayloadLoggerOptions {
|
|
|
56
56
|
logger?: ILogger;
|
|
57
57
|
}
|
|
58
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
|
+
}
|
|
59
74
|
/**
|
|
60
75
|
* Valid provider option value types
|
|
61
76
|
*/
|
|
62
|
-
type TOpenAIProviderOptionValue = string | number | boolean | undefined | null | OpenAI | IPayloadLogger | ILogger | IExecutor | TProviderOptionValueBase | TOpenAIProviderOptionValue[] | {
|
|
77
|
+
type TOpenAIProviderOptionValue = string | number | boolean | undefined | null | IOpenAIJsonSchemaDefinition | IOpenAINativeWebToolsOptions | IOpenAIResponsesReasoningOptions | OpenAI | IPayloadLogger | ILogger | IExecutor | TProviderOptionValueBase | TOpenAIProviderOptionValue[] | {
|
|
63
78
|
[key: string]: TOpenAIProviderOptionValue;
|
|
64
79
|
};
|
|
65
80
|
/**
|
|
@@ -86,6 +101,17 @@ interface IOpenAIProviderOptions {
|
|
|
86
101
|
* API base URL (default: 'https://api.openai.com/v1')
|
|
87
102
|
*/
|
|
88
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;
|
|
89
115
|
/**
|
|
90
116
|
* Response format (default: 'text')
|
|
91
117
|
* - 'text': Plain text response
|
|
@@ -96,12 +122,32 @@ interface IOpenAIProviderOptions {
|
|
|
96
122
|
/**
|
|
97
123
|
* JSON schema for structured outputs (required when responseFormat is 'json_schema')
|
|
98
124
|
*/
|
|
99
|
-
jsonSchema?:
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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;
|
|
105
151
|
/**
|
|
106
152
|
* OpenAI client instance (optional: will be created from apiKey if not provided)
|
|
107
153
|
*/
|
|
@@ -168,7 +214,7 @@ interface IOpenAIProviderOptions {
|
|
|
168
214
|
/**
|
|
169
215
|
* OpenAI provider implementation for Robota
|
|
170
216
|
*
|
|
171
|
-
* Provides integration with OpenAI
|
|
217
|
+
* Provides integration with OpenAI models through the Robota provider contract.
|
|
172
218
|
* Uses OpenAI SDK native types internally for optimal performance and feature support.
|
|
173
219
|
*
|
|
174
220
|
* @public
|
|
@@ -178,6 +224,7 @@ declare class OpenAIProvider extends AbstractAIProvider {
|
|
|
178
224
|
readonly version = "1.0.0";
|
|
179
225
|
private readonly client?;
|
|
180
226
|
private readonly options;
|
|
227
|
+
private readonly apiSurface;
|
|
181
228
|
private readonly payloadLogger;
|
|
182
229
|
private readonly responseParser;
|
|
183
230
|
/**
|
|
@@ -189,9 +236,9 @@ declare class OpenAIProvider extends AbstractAIProvider {
|
|
|
189
236
|
onTextDelta?: TTextDeltaCallback;
|
|
190
237
|
constructor(options: IOpenAIProviderOptions);
|
|
191
238
|
chat(messages: TUniversalMessage[], options?: IChatOptions): Promise<TUniversalMessage>;
|
|
192
|
-
private chatWithStreamingAssembly;
|
|
193
239
|
chatStream(messages: TUniversalMessage[], options?: IChatOptions): AsyncIterable<TUniversalMessage>;
|
|
194
240
|
supportsTools(): boolean;
|
|
241
|
+
getCapabilities(): IProviderCapabilities;
|
|
195
242
|
validateConfig(): boolean;
|
|
196
243
|
dispose(): Promise<void>;
|
|
197
244
|
protected validateMessages(messages: TUniversalMessage[]): void;
|
|
@@ -231,9 +278,30 @@ declare class OpenAIConversationAdapter {
|
|
|
231
278
|
static addSystemPromptIfNeeded(messages: OpenAI.Chat.ChatCompletionMessageParam[], systemPrompt?: string): OpenAI.Chat.ChatCompletionMessageParam[];
|
|
232
279
|
}
|
|
233
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
|
+
|
|
234
303
|
declare const DEFAULT_OPENAI_PROVIDER_MODEL: string | undefined;
|
|
235
|
-
declare const
|
|
236
|
-
declare const DEFAULT_OPENAI_COMPATIBLE_PROVIDER_BASE_URL = "http://localhost:1234/v1";
|
|
304
|
+
declare const DEFAULT_OPENAI_PROVIDER_API_KEY_REFERENCE = "$ENV:OPENAI_API_KEY";
|
|
237
305
|
declare function createOpenAIProviderDefinition(): IProviderDefinition;
|
|
238
306
|
|
|
239
|
-
export {
|
|
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 };
|
package/dist/node/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import OpenAI from 'openai';
|
|
2
|
-
import { ILogger, IExecutor, TProviderOptionValueBase, AbstractAIProvider, TTextDeltaCallback, TUniversalMessage, IChatOptions, IProviderDefinition } from '@robota-sdk/agent-core';
|
|
2
|
+
import { ILogger, IExecutor, TProviderOptionValueBase, AbstractAIProvider, TTextDeltaCallback, TUniversalMessage, IChatOptions, IProviderCapabilities, IProviderProfileConfig, IProviderModelCatalog, IProviderDefinition } from '@robota-sdk/agent-core';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Payload logging data structure
|
|
@@ -56,10 +56,25 @@ interface IPayloadLoggerOptions {
|
|
|
56
56
|
logger?: ILogger;
|
|
57
57
|
}
|
|
58
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
|
+
}
|
|
59
74
|
/**
|
|
60
75
|
* Valid provider option value types
|
|
61
76
|
*/
|
|
62
|
-
type TOpenAIProviderOptionValue = string | number | boolean | undefined | null | OpenAI | IPayloadLogger | ILogger | IExecutor | TProviderOptionValueBase | TOpenAIProviderOptionValue[] | {
|
|
77
|
+
type TOpenAIProviderOptionValue = string | number | boolean | undefined | null | IOpenAIJsonSchemaDefinition | IOpenAINativeWebToolsOptions | IOpenAIResponsesReasoningOptions | OpenAI | IPayloadLogger | ILogger | IExecutor | TProviderOptionValueBase | TOpenAIProviderOptionValue[] | {
|
|
63
78
|
[key: string]: TOpenAIProviderOptionValue;
|
|
64
79
|
};
|
|
65
80
|
/**
|
|
@@ -86,6 +101,17 @@ interface IOpenAIProviderOptions {
|
|
|
86
101
|
* API base URL (default: 'https://api.openai.com/v1')
|
|
87
102
|
*/
|
|
88
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;
|
|
89
115
|
/**
|
|
90
116
|
* Response format (default: 'text')
|
|
91
117
|
* - 'text': Plain text response
|
|
@@ -96,12 +122,32 @@ interface IOpenAIProviderOptions {
|
|
|
96
122
|
/**
|
|
97
123
|
* JSON schema for structured outputs (required when responseFormat is 'json_schema')
|
|
98
124
|
*/
|
|
99
|
-
jsonSchema?:
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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;
|
|
105
151
|
/**
|
|
106
152
|
* OpenAI client instance (optional: will be created from apiKey if not provided)
|
|
107
153
|
*/
|
|
@@ -168,7 +214,7 @@ interface IOpenAIProviderOptions {
|
|
|
168
214
|
/**
|
|
169
215
|
* OpenAI provider implementation for Robota
|
|
170
216
|
*
|
|
171
|
-
* Provides integration with OpenAI
|
|
217
|
+
* Provides integration with OpenAI models through the Robota provider contract.
|
|
172
218
|
* Uses OpenAI SDK native types internally for optimal performance and feature support.
|
|
173
219
|
*
|
|
174
220
|
* @public
|
|
@@ -178,6 +224,7 @@ declare class OpenAIProvider extends AbstractAIProvider {
|
|
|
178
224
|
readonly version = "1.0.0";
|
|
179
225
|
private readonly client?;
|
|
180
226
|
private readonly options;
|
|
227
|
+
private readonly apiSurface;
|
|
181
228
|
private readonly payloadLogger;
|
|
182
229
|
private readonly responseParser;
|
|
183
230
|
/**
|
|
@@ -189,9 +236,9 @@ declare class OpenAIProvider extends AbstractAIProvider {
|
|
|
189
236
|
onTextDelta?: TTextDeltaCallback;
|
|
190
237
|
constructor(options: IOpenAIProviderOptions);
|
|
191
238
|
chat(messages: TUniversalMessage[], options?: IChatOptions): Promise<TUniversalMessage>;
|
|
192
|
-
private chatWithStreamingAssembly;
|
|
193
239
|
chatStream(messages: TUniversalMessage[], options?: IChatOptions): AsyncIterable<TUniversalMessage>;
|
|
194
240
|
supportsTools(): boolean;
|
|
241
|
+
getCapabilities(): IProviderCapabilities;
|
|
195
242
|
validateConfig(): boolean;
|
|
196
243
|
dispose(): Promise<void>;
|
|
197
244
|
protected validateMessages(messages: TUniversalMessage[]): void;
|
|
@@ -231,9 +278,30 @@ declare class OpenAIConversationAdapter {
|
|
|
231
278
|
static addSystemPromptIfNeeded(messages: OpenAI.Chat.ChatCompletionMessageParam[], systemPrompt?: string): OpenAI.Chat.ChatCompletionMessageParam[];
|
|
232
279
|
}
|
|
233
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
|
+
|
|
234
303
|
declare const DEFAULT_OPENAI_PROVIDER_MODEL: string | undefined;
|
|
235
|
-
declare const
|
|
236
|
-
declare const DEFAULT_OPENAI_COMPATIBLE_PROVIDER_BASE_URL = "http://localhost:1234/v1";
|
|
304
|
+
declare const DEFAULT_OPENAI_PROVIDER_API_KEY_REFERENCE = "$ENV:OPENAI_API_KEY";
|
|
237
305
|
declare function createOpenAIProviderDefinition(): IProviderDefinition;
|
|
238
306
|
|
|
239
|
-
export {
|
|
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 };
|
package/dist/node/index.js
CHANGED
|
@@ -1,2 +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 m(a){return convertToOpenAICompatibleMessages(a)}function c(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=m(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:c(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=m(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:c(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 F=void 0,h="lm-studio",f="http://localhost:1234/v1";function z(){return {type:"openai",displayName:"OpenAI Compatible",description:"OpenAI or OpenAI-compatible Chat Completions endpoint",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{
|
|
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};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@robota-sdk/agent-provider-openai",
|
|
3
|
-
"version": "3.0.0-beta.
|
|
4
|
-
"description": "OpenAI integration for Robota SDK
|
|
3
|
+
"version": "3.0.0-beta.61",
|
|
4
|
+
"description": "OpenAI integration for Robota SDK using the Responses API, structured outputs, streaming, and tool calling",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/node/index.js",
|
|
7
7
|
"module": "dist/node/index.mjs",
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
"exports": {
|
|
10
10
|
".": {
|
|
11
11
|
"types": "./dist/node/index.d.ts",
|
|
12
|
+
"source": "./src/index.ts",
|
|
12
13
|
"node": {
|
|
13
14
|
"import": "./dist/node/index.js",
|
|
14
15
|
"require": "./dist/node/index.cjs"
|
|
@@ -53,7 +54,8 @@
|
|
|
53
54
|
"openai",
|
|
54
55
|
"gpt",
|
|
55
56
|
"gpt-4",
|
|
56
|
-
"
|
|
57
|
+
"responses-api",
|
|
58
|
+
"structured-outputs",
|
|
57
59
|
"chatgpt",
|
|
58
60
|
"function-calling",
|
|
59
61
|
"typescript",
|
|
@@ -75,10 +77,10 @@
|
|
|
75
77
|
"license": "MIT",
|
|
76
78
|
"dependencies": {
|
|
77
79
|
"openai": "^4.98.0",
|
|
78
|
-
"@robota-sdk/agent-provider-openai-compatible": "3.0.0-beta.
|
|
80
|
+
"@robota-sdk/agent-provider-openai-compatible": "3.0.0-beta.61"
|
|
79
81
|
},
|
|
80
82
|
"peerDependencies": {
|
|
81
|
-
"@robota-sdk/agent-core": "3.0.0-beta.
|
|
83
|
+
"@robota-sdk/agent-core": "3.0.0-beta.61"
|
|
82
84
|
},
|
|
83
85
|
"devDependencies": {
|
|
84
86
|
"@typescript-eslint/eslint-plugin": "^6.18.0",
|
|
@@ -88,7 +90,7 @@
|
|
|
88
90
|
"tsup": "^8.0.1",
|
|
89
91
|
"typescript": "^5.3.3",
|
|
90
92
|
"vitest": "^1.1.0",
|
|
91
|
-
"@robota-sdk/agent-core": "3.0.0-beta.
|
|
93
|
+
"@robota-sdk/agent-core": "3.0.0-beta.61"
|
|
92
94
|
},
|
|
93
95
|
"publishConfig": {
|
|
94
96
|
"access": "public"
|