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