browser-use 0.5.0 → 0.6.0
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/dist/agent/service.js +2 -0
- package/dist/agent/system_prompt.md +269 -0
- package/dist/agent/system_prompt_anthropic_flash.md +240 -0
- package/dist/agent/system_prompt_browser_use.md +18 -0
- package/dist/agent/system_prompt_browser_use_flash.md +15 -0
- package/dist/agent/system_prompt_browser_use_no_thinking.md +17 -0
- package/dist/agent/system_prompt_flash.md +16 -0
- package/dist/agent/system_prompt_flash_anthropic.md +30 -0
- package/dist/agent/system_prompt_no_thinking.md +245 -0
- package/dist/browser/cloud/index.d.ts +1 -0
- package/dist/browser/cloud/index.js +1 -0
- package/dist/browser/cloud/management.d.ts +130 -0
- package/dist/browser/cloud/management.js +140 -0
- package/dist/browser/events.d.ts +61 -3
- package/dist/browser/events.js +66 -0
- package/dist/browser/profile.d.ts +1 -0
- package/dist/browser/profile.js +1 -0
- package/dist/browser/session.d.ts +56 -2
- package/dist/browser/session.js +596 -24
- package/dist/browser/watchdogs/base.js +34 -1
- package/dist/browser/watchdogs/captcha-watchdog.d.ts +26 -0
- package/dist/browser/watchdogs/captcha-watchdog.js +151 -0
- package/dist/browser/watchdogs/index.d.ts +1 -0
- package/dist/browser/watchdogs/index.js +1 -0
- package/dist/browser/watchdogs/screenshot-watchdog.js +4 -3
- package/dist/cli.d.ts +120 -0
- package/dist/cli.js +1816 -4
- package/dist/controller/service.js +106 -362
- package/dist/controller/views.d.ts +9 -6
- package/dist/controller/views.js +8 -5
- package/dist/filesystem/file-system.js +1 -1
- package/dist/llm/litellm/chat.d.ts +11 -0
- package/dist/llm/litellm/chat.js +16 -0
- package/dist/llm/litellm/index.d.ts +1 -0
- package/dist/llm/litellm/index.js +1 -0
- package/dist/llm/models.js +29 -3
- package/dist/llm/oci-raw/chat.d.ts +64 -0
- package/dist/llm/oci-raw/chat.js +350 -0
- package/dist/llm/oci-raw/index.d.ts +2 -0
- package/dist/llm/oci-raw/index.js +2 -0
- package/dist/llm/oci-raw/serializer.d.ts +12 -0
- package/dist/llm/oci-raw/serializer.js +128 -0
- package/dist/mcp/server.d.ts +1 -0
- package/dist/mcp/server.js +62 -13
- package/dist/skill-cli/direct.d.ts +100 -0
- package/dist/skill-cli/direct.js +984 -0
- package/dist/skill-cli/index.d.ts +2 -0
- package/dist/skill-cli/index.js +2 -0
- package/dist/skill-cli/server.d.ts +2 -0
- package/dist/skill-cli/server.js +472 -11
- package/dist/skill-cli/tunnel.d.ts +61 -0
- package/dist/skill-cli/tunnel.js +257 -0
- package/dist/sync/auth.d.ts +8 -0
- package/dist/sync/auth.js +12 -0
- package/package.json +22 -4
package/dist/llm/models.js
CHANGED
|
@@ -6,7 +6,9 @@ import { ChatCerebras } from './cerebras/chat.js';
|
|
|
6
6
|
import { ChatDeepSeek } from './deepseek/chat.js';
|
|
7
7
|
import { ChatGoogle } from './google/chat.js';
|
|
8
8
|
import { ChatGroq } from './groq/chat.js';
|
|
9
|
+
import { ChatLiteLLM } from './litellm/chat.js';
|
|
9
10
|
import { ChatMistral } from './mistral/chat.js';
|
|
11
|
+
import { ChatOCIRaw } from './oci-raw/chat.js';
|
|
10
12
|
import { ChatOllama } from './ollama/chat.js';
|
|
11
13
|
import { ChatOpenAI } from './openai/chat.js';
|
|
12
14
|
import { ChatOpenRouter } from './openrouter/chat.js';
|
|
@@ -25,6 +27,8 @@ const AVAILABLE_PROVIDERS = [
|
|
|
25
27
|
'mistral',
|
|
26
28
|
'cerebras',
|
|
27
29
|
'vercel',
|
|
30
|
+
'litellm',
|
|
31
|
+
'oci',
|
|
28
32
|
];
|
|
29
33
|
const MISTRAL_ALIAS_MAP = {
|
|
30
34
|
large: 'mistral-large-latest',
|
|
@@ -132,6 +136,12 @@ const inferProviderFromModel = (model) => {
|
|
|
132
136
|
if (lower.startsWith('vercel:')) {
|
|
133
137
|
return 'vercel';
|
|
134
138
|
}
|
|
139
|
+
if (lower.startsWith('litellm:')) {
|
|
140
|
+
return 'litellm';
|
|
141
|
+
}
|
|
142
|
+
if (lower.startsWith('oci:')) {
|
|
143
|
+
return 'oci';
|
|
144
|
+
}
|
|
135
145
|
if (lower.startsWith('mistral-') ||
|
|
136
146
|
lower.startsWith('codestral') ||
|
|
137
147
|
lower.startsWith('pixtral')) {
|
|
@@ -182,6 +192,12 @@ const normalizeModelForProvider = (provider, model) => {
|
|
|
182
192
|
if (provider === 'vercel' && lower.startsWith('vercel:')) {
|
|
183
193
|
return model.slice('vercel:'.length);
|
|
184
194
|
}
|
|
195
|
+
if (provider === 'litellm' && lower.startsWith('litellm:')) {
|
|
196
|
+
return model.slice('litellm:'.length);
|
|
197
|
+
}
|
|
198
|
+
if (provider === 'oci' && lower.startsWith('oci:')) {
|
|
199
|
+
return model.slice('oci:'.length);
|
|
200
|
+
}
|
|
185
201
|
if (provider === 'aws' && lower.startsWith('bedrock:')) {
|
|
186
202
|
return model.slice('bedrock:'.length);
|
|
187
203
|
}
|
|
@@ -248,6 +264,16 @@ const buildProviderModel = (provider, model) => {
|
|
|
248
264
|
apiKey: process.env.VERCEL_API_KEY,
|
|
249
265
|
baseURL: process.env.VERCEL_BASE_URL,
|
|
250
266
|
});
|
|
267
|
+
case 'litellm':
|
|
268
|
+
return new ChatLiteLLM({
|
|
269
|
+
model,
|
|
270
|
+
apiKey: process.env.LITELLM_API_KEY,
|
|
271
|
+
baseURL: process.env.LITELLM_API_BASE ?? process.env.LITELLM_BASE_URL,
|
|
272
|
+
});
|
|
273
|
+
case 'oci':
|
|
274
|
+
return new ChatOCIRaw({
|
|
275
|
+
model,
|
|
276
|
+
});
|
|
251
277
|
case 'aws':
|
|
252
278
|
return new ChatBedrockConverse({
|
|
253
279
|
model,
|
|
@@ -294,13 +320,13 @@ export const getLlmByName = (modelName) => {
|
|
|
294
320
|
const separator = normalizedName.indexOf('_');
|
|
295
321
|
if (separator > 0) {
|
|
296
322
|
const provider = normalizedName.slice(0, separator);
|
|
297
|
-
if (provider === 'oci') {
|
|
298
|
-
throw new Error('OCI models require manual configuration. Use ChatOCIRaw directly with your OCI credentials.');
|
|
299
|
-
}
|
|
300
323
|
const modelPart = normalizedName.slice(separator + 1);
|
|
301
324
|
if (provider === 'bu') {
|
|
302
325
|
return buildProviderModel('browser-use', `bu-${modelPart.replace(/_/g, '-')}`);
|
|
303
326
|
}
|
|
327
|
+
if (provider === 'oci') {
|
|
328
|
+
return buildProviderModel('oci', modelPart);
|
|
329
|
+
}
|
|
304
330
|
if (provider === 'browser-use') {
|
|
305
331
|
return buildProviderModel('browser-use', modelPart.replace(/_/g, '/'));
|
|
306
332
|
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { BaseChatModel, ChatInvokeOptions } from '../base.js';
|
|
2
|
+
import type { Message } from '../messages.js';
|
|
3
|
+
import { ChatInvokeCompletion } from '../views.js';
|
|
4
|
+
export interface ChatOCIRawOptions {
|
|
5
|
+
model?: string;
|
|
6
|
+
modelId?: string;
|
|
7
|
+
serviceEndpoint?: string;
|
|
8
|
+
compartmentId?: string;
|
|
9
|
+
provider?: string;
|
|
10
|
+
temperature?: number | null;
|
|
11
|
+
maxTokens?: number | null;
|
|
12
|
+
frequencyPenalty?: number | null;
|
|
13
|
+
presencePenalty?: number | null;
|
|
14
|
+
topP?: number | null;
|
|
15
|
+
topK?: number | null;
|
|
16
|
+
authType?: 'API_KEY' | 'INSTANCE_PRINCIPAL' | 'RESOURCE_PRINCIPAL' | string;
|
|
17
|
+
authProfile?: string;
|
|
18
|
+
configFilePath?: string;
|
|
19
|
+
tenancyId?: string;
|
|
20
|
+
userId?: string;
|
|
21
|
+
fingerprint?: string;
|
|
22
|
+
privateKey?: string;
|
|
23
|
+
passphrase?: string | null;
|
|
24
|
+
responseSchemaName?: string;
|
|
25
|
+
}
|
|
26
|
+
export declare class ChatOCIRaw implements BaseChatModel {
|
|
27
|
+
model: string;
|
|
28
|
+
provider: string;
|
|
29
|
+
private readonly serviceEndpoint;
|
|
30
|
+
private readonly compartmentId;
|
|
31
|
+
private readonly ociProvider;
|
|
32
|
+
private readonly temperature;
|
|
33
|
+
private readonly maxTokens;
|
|
34
|
+
private readonly frequencyPenalty;
|
|
35
|
+
private readonly presencePenalty;
|
|
36
|
+
private readonly topP;
|
|
37
|
+
private readonly topK;
|
|
38
|
+
private readonly authType;
|
|
39
|
+
private readonly authProfile;
|
|
40
|
+
private readonly configFilePath?;
|
|
41
|
+
private readonly tenancyId?;
|
|
42
|
+
private readonly userId?;
|
|
43
|
+
private readonly fingerprint?;
|
|
44
|
+
private readonly privateKey?;
|
|
45
|
+
private readonly passphrase;
|
|
46
|
+
private readonly responseSchemaName;
|
|
47
|
+
private clientPromise;
|
|
48
|
+
constructor(options?: ChatOCIRawOptions);
|
|
49
|
+
get name(): string;
|
|
50
|
+
get model_name(): string;
|
|
51
|
+
private usesCohereFormat;
|
|
52
|
+
private createAuthProvider;
|
|
53
|
+
private getClient;
|
|
54
|
+
private getUsage;
|
|
55
|
+
private buildGenericRequest;
|
|
56
|
+
private buildCohereRequest;
|
|
57
|
+
private buildChatRequest;
|
|
58
|
+
private extractText;
|
|
59
|
+
private mapError;
|
|
60
|
+
ainvoke(messages: Message[], outputFormat?: undefined, options?: ChatInvokeOptions): Promise<ChatInvokeCompletion<string>>;
|
|
61
|
+
ainvoke<T>(messages: Message[], outputFormat: {
|
|
62
|
+
parse: (input: unknown) => T;
|
|
63
|
+
} | undefined, options?: ChatInvokeOptions): Promise<ChatInvokeCompletion<T>>;
|
|
64
|
+
}
|
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
import { ConfigFileAuthenticationDetailsProvider, InstancePrincipalsAuthenticationDetailsProviderBuilder, ResourcePrincipalAuthenticationDetailsProvider, SimpleAuthenticationDetailsProvider, } from 'oci-common';
|
|
2
|
+
import { GenerativeAiInferenceClient } from 'oci-generativeaiinference';
|
|
3
|
+
import { ModelProviderError, ModelRateLimitError } from '../exceptions.js';
|
|
4
|
+
import { SchemaOptimizer, zodSchemaToJsonSchema } from '../schema.js';
|
|
5
|
+
import { ChatInvokeCompletion } from '../views.js';
|
|
6
|
+
import { OCIRawMessageSerializer } from './serializer.js';
|
|
7
|
+
const parseStructuredJson = (text) => {
|
|
8
|
+
let jsonText = String(text ?? '').trim();
|
|
9
|
+
if (jsonText.startsWith('```json') && jsonText.endsWith('```')) {
|
|
10
|
+
jsonText = jsonText.slice(7, -3).trim();
|
|
11
|
+
}
|
|
12
|
+
else if (jsonText.startsWith('```') && jsonText.endsWith('```')) {
|
|
13
|
+
jsonText = jsonText.slice(3, -3).trim();
|
|
14
|
+
}
|
|
15
|
+
return JSON.parse(jsonText);
|
|
16
|
+
};
|
|
17
|
+
const extractZodSchema = (outputFormat) => {
|
|
18
|
+
const direct = outputFormat;
|
|
19
|
+
if (direct &&
|
|
20
|
+
typeof direct === 'object' &&
|
|
21
|
+
typeof direct.safeParse === 'function' &&
|
|
22
|
+
typeof direct.parse === 'function') {
|
|
23
|
+
return direct;
|
|
24
|
+
}
|
|
25
|
+
const nested = outputFormat?.schema;
|
|
26
|
+
if (nested &&
|
|
27
|
+
typeof nested === 'object' &&
|
|
28
|
+
typeof nested.safeParse === 'function' &&
|
|
29
|
+
typeof nested.parse === 'function') {
|
|
30
|
+
return nested;
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
};
|
|
34
|
+
const parseOutput = (outputFormat, payload) => {
|
|
35
|
+
const maybeWrapped = outputFormat;
|
|
36
|
+
if (maybeWrapped &&
|
|
37
|
+
typeof maybeWrapped === 'object' &&
|
|
38
|
+
maybeWrapped.schema &&
|
|
39
|
+
typeof maybeWrapped.schema.parse === 'function') {
|
|
40
|
+
return maybeWrapped.schema.parse(payload);
|
|
41
|
+
}
|
|
42
|
+
return outputFormat.parse(payload);
|
|
43
|
+
};
|
|
44
|
+
const appendJsonInstruction = (messages, schema) => {
|
|
45
|
+
const instruction = '\n\nIMPORTANT: You must respond with ONLY a valid JSON object ' +
|
|
46
|
+
'(no markdown, no code blocks, no explanations)' +
|
|
47
|
+
(schema
|
|
48
|
+
? ` that exactly matches this schema:\n${JSON.stringify(schema, null, 2)}`
|
|
49
|
+
: '.');
|
|
50
|
+
const target = [...messages]
|
|
51
|
+
.reverse()
|
|
52
|
+
.find((message) => message.role === 'USER' || message.role === 'SYSTEM') ?? null;
|
|
53
|
+
if (target) {
|
|
54
|
+
const content = Array.isArray(target.content)
|
|
55
|
+
? [...target.content]
|
|
56
|
+
: [];
|
|
57
|
+
content.push({
|
|
58
|
+
type: 'TEXT',
|
|
59
|
+
text: instruction,
|
|
60
|
+
});
|
|
61
|
+
target.content = content;
|
|
62
|
+
return messages;
|
|
63
|
+
}
|
|
64
|
+
return [
|
|
65
|
+
{
|
|
66
|
+
role: 'SYSTEM',
|
|
67
|
+
content: [
|
|
68
|
+
{
|
|
69
|
+
type: 'TEXT',
|
|
70
|
+
text: instruction,
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
},
|
|
74
|
+
...messages,
|
|
75
|
+
];
|
|
76
|
+
};
|
|
77
|
+
export class ChatOCIRaw {
|
|
78
|
+
model;
|
|
79
|
+
provider = 'oci-raw';
|
|
80
|
+
serviceEndpoint;
|
|
81
|
+
compartmentId;
|
|
82
|
+
ociProvider;
|
|
83
|
+
temperature;
|
|
84
|
+
maxTokens;
|
|
85
|
+
frequencyPenalty;
|
|
86
|
+
presencePenalty;
|
|
87
|
+
topP;
|
|
88
|
+
topK;
|
|
89
|
+
authType;
|
|
90
|
+
authProfile;
|
|
91
|
+
configFilePath;
|
|
92
|
+
tenancyId;
|
|
93
|
+
userId;
|
|
94
|
+
fingerprint;
|
|
95
|
+
privateKey;
|
|
96
|
+
passphrase;
|
|
97
|
+
responseSchemaName;
|
|
98
|
+
clientPromise = null;
|
|
99
|
+
constructor(options = {}) {
|
|
100
|
+
this.model =
|
|
101
|
+
options.model ??
|
|
102
|
+
options.modelId ??
|
|
103
|
+
process.env.OCI_MODEL_ID ??
|
|
104
|
+
(() => {
|
|
105
|
+
throw new Error('ChatOCIRaw requires model or OCI_MODEL_ID');
|
|
106
|
+
})();
|
|
107
|
+
this.serviceEndpoint =
|
|
108
|
+
options.serviceEndpoint ??
|
|
109
|
+
process.env.OCI_SERVICE_ENDPOINT ??
|
|
110
|
+
(() => {
|
|
111
|
+
throw new Error('ChatOCIRaw requires serviceEndpoint or OCI_SERVICE_ENDPOINT');
|
|
112
|
+
})();
|
|
113
|
+
this.compartmentId =
|
|
114
|
+
options.compartmentId ??
|
|
115
|
+
process.env.OCI_COMPARTMENT_ID ??
|
|
116
|
+
(() => {
|
|
117
|
+
throw new Error('ChatOCIRaw requires compartmentId or OCI_COMPARTMENT_ID');
|
|
118
|
+
})();
|
|
119
|
+
this.ociProvider = options.provider ?? process.env.OCI_PROVIDER ?? 'meta';
|
|
120
|
+
this.temperature = options.temperature ?? 1.0;
|
|
121
|
+
this.maxTokens = options.maxTokens ?? 600;
|
|
122
|
+
this.frequencyPenalty = options.frequencyPenalty ?? 0.0;
|
|
123
|
+
this.presencePenalty = options.presencePenalty ?? 0.0;
|
|
124
|
+
this.topP = options.topP ?? 0.75;
|
|
125
|
+
this.topK = options.topK ?? 0;
|
|
126
|
+
this.authType = options.authType ?? process.env.OCI_AUTH_TYPE ?? 'API_KEY';
|
|
127
|
+
this.authProfile =
|
|
128
|
+
options.authProfile ??
|
|
129
|
+
process.env.OCI_AUTH_PROFILE ??
|
|
130
|
+
process.env.OCI_CONFIG_PROFILE ??
|
|
131
|
+
'DEFAULT';
|
|
132
|
+
this.configFilePath = options.configFilePath ?? process.env.OCI_CONFIG_FILE;
|
|
133
|
+
this.tenancyId = options.tenancyId ?? process.env.OCI_TENANCY_ID;
|
|
134
|
+
this.userId = options.userId ?? process.env.OCI_USER_ID;
|
|
135
|
+
this.fingerprint = options.fingerprint ?? process.env.OCI_FINGERPRINT;
|
|
136
|
+
this.privateKey = options.privateKey ?? process.env.OCI_PRIVATE_KEY;
|
|
137
|
+
this.passphrase =
|
|
138
|
+
options.passphrase ?? process.env.OCI_PRIVATE_KEY_PASSPHRASE ?? null;
|
|
139
|
+
this.responseSchemaName =
|
|
140
|
+
options.responseSchemaName ?? 'browser_use_response';
|
|
141
|
+
}
|
|
142
|
+
get name() {
|
|
143
|
+
if (this.model.length <= 90) {
|
|
144
|
+
return this.model;
|
|
145
|
+
}
|
|
146
|
+
const parts = this.model.split('.');
|
|
147
|
+
if (parts.length >= 4) {
|
|
148
|
+
return `oci-${this.ociProvider}-${parts[3]}`;
|
|
149
|
+
}
|
|
150
|
+
return `oci-${this.ociProvider}-model`;
|
|
151
|
+
}
|
|
152
|
+
get model_name() {
|
|
153
|
+
return this.name;
|
|
154
|
+
}
|
|
155
|
+
usesCohereFormat() {
|
|
156
|
+
return this.ociProvider.toLowerCase() === 'cohere';
|
|
157
|
+
}
|
|
158
|
+
async createAuthProvider() {
|
|
159
|
+
const authType = this.authType.toUpperCase();
|
|
160
|
+
if (authType === 'INSTANCE_PRINCIPAL') {
|
|
161
|
+
return new InstancePrincipalsAuthenticationDetailsProviderBuilder().build();
|
|
162
|
+
}
|
|
163
|
+
if (authType === 'RESOURCE_PRINCIPAL') {
|
|
164
|
+
return ResourcePrincipalAuthenticationDetailsProvider.builder();
|
|
165
|
+
}
|
|
166
|
+
if (this.tenancyId && this.userId && this.fingerprint && this.privateKey) {
|
|
167
|
+
return new SimpleAuthenticationDetailsProvider(this.tenancyId, this.userId, this.fingerprint, this.privateKey, this.passphrase);
|
|
168
|
+
}
|
|
169
|
+
return new ConfigFileAuthenticationDetailsProvider(this.configFilePath, this.authProfile);
|
|
170
|
+
}
|
|
171
|
+
async getClient() {
|
|
172
|
+
if (!this.clientPromise) {
|
|
173
|
+
this.clientPromise = (async () => {
|
|
174
|
+
const authenticationDetailsProvider = await this.createAuthProvider();
|
|
175
|
+
const client = new GenerativeAiInferenceClient({
|
|
176
|
+
authenticationDetailsProvider,
|
|
177
|
+
});
|
|
178
|
+
client.endpoint = this.serviceEndpoint;
|
|
179
|
+
return client;
|
|
180
|
+
})();
|
|
181
|
+
}
|
|
182
|
+
return this.clientPromise;
|
|
183
|
+
}
|
|
184
|
+
getUsage(payload) {
|
|
185
|
+
const usage = payload?.usage;
|
|
186
|
+
if (!usage) {
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
const reasoningTokens = usage.completionTokensDetails?.reasoningTokens ?? 0;
|
|
190
|
+
const completionTokens = (usage.completionTokens ?? 0) + reasoningTokens;
|
|
191
|
+
return {
|
|
192
|
+
prompt_tokens: usage.promptTokens ?? 0,
|
|
193
|
+
prompt_cached_tokens: usage.promptTokensDetails?.cachedTokens ?? null,
|
|
194
|
+
prompt_cache_creation_tokens: null,
|
|
195
|
+
prompt_image_tokens: null,
|
|
196
|
+
completion_tokens: completionTokens,
|
|
197
|
+
total_tokens: usage.totalTokens ?? (usage.promptTokens ?? 0) + completionTokens,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
buildGenericRequest(messages, outputFormat) {
|
|
201
|
+
const zodSchema = extractZodSchema(outputFormat);
|
|
202
|
+
const serializedMessages = OCIRawMessageSerializer.serializeMessages(messages);
|
|
203
|
+
const optimizedSchema = zodSchema
|
|
204
|
+
? SchemaOptimizer.createOptimizedJsonSchema(zodSchemaToJsonSchema(zodSchema))
|
|
205
|
+
: null;
|
|
206
|
+
const requestMessages = outputFormat && !zodSchema
|
|
207
|
+
? appendJsonInstruction(serializedMessages, null)
|
|
208
|
+
: serializedMessages;
|
|
209
|
+
const request = {
|
|
210
|
+
apiFormat: 'GENERIC',
|
|
211
|
+
messages: requestMessages,
|
|
212
|
+
};
|
|
213
|
+
if (this.temperature !== null) {
|
|
214
|
+
request.temperature = this.temperature;
|
|
215
|
+
}
|
|
216
|
+
if (this.maxTokens !== null) {
|
|
217
|
+
request.maxTokens = this.maxTokens;
|
|
218
|
+
}
|
|
219
|
+
if (this.frequencyPenalty !== null) {
|
|
220
|
+
request.frequencyPenalty = this.frequencyPenalty;
|
|
221
|
+
}
|
|
222
|
+
if (this.presencePenalty !== null) {
|
|
223
|
+
request.presencePenalty = this.presencePenalty;
|
|
224
|
+
}
|
|
225
|
+
if (this.topP !== null) {
|
|
226
|
+
request.topP = this.topP;
|
|
227
|
+
}
|
|
228
|
+
if (this.topK !== null) {
|
|
229
|
+
request.topK = this.topK;
|
|
230
|
+
}
|
|
231
|
+
if (optimizedSchema) {
|
|
232
|
+
request.responseFormat = {
|
|
233
|
+
type: 'JSON_SCHEMA',
|
|
234
|
+
jsonSchema: {
|
|
235
|
+
name: this.responseSchemaName,
|
|
236
|
+
schema: optimizedSchema,
|
|
237
|
+
isStrict: true,
|
|
238
|
+
},
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
return request;
|
|
242
|
+
}
|
|
243
|
+
buildCohereRequest(messages, outputFormat) {
|
|
244
|
+
const zodSchema = extractZodSchema(outputFormat);
|
|
245
|
+
const optimizedSchema = zodSchema
|
|
246
|
+
? SchemaOptimizer.createOptimizedJsonSchema(zodSchemaToJsonSchema(zodSchema))
|
|
247
|
+
: null;
|
|
248
|
+
let conversation = OCIRawMessageSerializer.serializeMessagesForCohere(messages);
|
|
249
|
+
if (outputFormat) {
|
|
250
|
+
conversation +=
|
|
251
|
+
'\n\nIMPORTANT: You must respond with ONLY a valid JSON object (no markdown, no code blocks, no explanations)' +
|
|
252
|
+
(optimizedSchema
|
|
253
|
+
? ` that exactly matches this schema:\n${JSON.stringify(optimizedSchema, null, 2)}`
|
|
254
|
+
: '.');
|
|
255
|
+
}
|
|
256
|
+
const request = {
|
|
257
|
+
apiFormat: 'COHERE',
|
|
258
|
+
message: conversation,
|
|
259
|
+
};
|
|
260
|
+
if (this.temperature !== null) {
|
|
261
|
+
request.temperature = this.temperature;
|
|
262
|
+
}
|
|
263
|
+
if (this.maxTokens !== null) {
|
|
264
|
+
request.maxTokens = this.maxTokens;
|
|
265
|
+
}
|
|
266
|
+
if (this.frequencyPenalty !== null) {
|
|
267
|
+
request.frequencyPenalty = this.frequencyPenalty;
|
|
268
|
+
}
|
|
269
|
+
if (this.presencePenalty !== null) {
|
|
270
|
+
request.presencePenalty = this.presencePenalty;
|
|
271
|
+
}
|
|
272
|
+
if (this.topP !== null) {
|
|
273
|
+
request.topP = this.topP;
|
|
274
|
+
}
|
|
275
|
+
if (this.topK !== null) {
|
|
276
|
+
request.topK = this.topK;
|
|
277
|
+
}
|
|
278
|
+
return request;
|
|
279
|
+
}
|
|
280
|
+
buildChatRequest(messages, outputFormat) {
|
|
281
|
+
const chatRequest = this.usesCohereFormat()
|
|
282
|
+
? this.buildCohereRequest(messages, outputFormat)
|
|
283
|
+
: this.buildGenericRequest(messages, outputFormat);
|
|
284
|
+
return {
|
|
285
|
+
chatDetails: {
|
|
286
|
+
compartmentId: this.compartmentId,
|
|
287
|
+
servingMode: {
|
|
288
|
+
servingType: 'ON_DEMAND',
|
|
289
|
+
modelId: this.model,
|
|
290
|
+
},
|
|
291
|
+
chatRequest,
|
|
292
|
+
},
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
extractText(payload) {
|
|
296
|
+
if (!payload) {
|
|
297
|
+
throw new ModelProviderError('OCI response did not include chatResponse payload', 502, this.name);
|
|
298
|
+
}
|
|
299
|
+
if (payload.apiFormat === 'COHERE') {
|
|
300
|
+
const coherePayload = payload;
|
|
301
|
+
return {
|
|
302
|
+
text: coherePayload.text ?? '',
|
|
303
|
+
thinking: null,
|
|
304
|
+
stopReason: coherePayload.finishReason ?? null,
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
const genericPayload = payload;
|
|
308
|
+
const choice = genericPayload.choices?.[0];
|
|
309
|
+
const text = choice?.message?.content
|
|
310
|
+
?.filter((part) => part.type === 'TEXT')
|
|
311
|
+
.map((part) => part.text ?? '')
|
|
312
|
+
.join('\n')
|
|
313
|
+
.trim();
|
|
314
|
+
return {
|
|
315
|
+
text: text ?? '',
|
|
316
|
+
thinking: choice?.message?.reasoningContent ?? null,
|
|
317
|
+
stopReason: choice?.finishReason ?? null,
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
mapError(error) {
|
|
321
|
+
const statusCode = typeof error?.statusCode === 'number'
|
|
322
|
+
? error.statusCode
|
|
323
|
+
: 502;
|
|
324
|
+
const message = String(error?.message ?? error ?? 'OCI request failed');
|
|
325
|
+
if (statusCode === 429) {
|
|
326
|
+
throw new ModelRateLimitError(message, statusCode, this.name);
|
|
327
|
+
}
|
|
328
|
+
throw new ModelProviderError(message, statusCode, this.name);
|
|
329
|
+
}
|
|
330
|
+
async ainvoke(messages, outputFormat, _options = {}) {
|
|
331
|
+
try {
|
|
332
|
+
const client = await this.getClient();
|
|
333
|
+
const response = (await client.chat(this.buildChatRequest(messages, outputFormat)));
|
|
334
|
+
if (!response || typeof response !== 'object') {
|
|
335
|
+
throw new ModelProviderError('OCI chat request returned an empty response', 502, this.name);
|
|
336
|
+
}
|
|
337
|
+
const payload = response.chatResult?.chatResponse;
|
|
338
|
+
const usage = this.getUsage(payload);
|
|
339
|
+
const { text, thinking, stopReason } = this.extractText(payload);
|
|
340
|
+
if (!outputFormat) {
|
|
341
|
+
return new ChatInvokeCompletion(text, usage, thinking, null, stopReason);
|
|
342
|
+
}
|
|
343
|
+
const parsed = parseOutput(outputFormat, parseStructuredJson(text));
|
|
344
|
+
return new ChatInvokeCompletion(parsed, usage, thinking, null, stopReason);
|
|
345
|
+
}
|
|
346
|
+
catch (error) {
|
|
347
|
+
this.mapError(error);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type Message } from '../messages.js';
|
|
2
|
+
type OciChatContent = Record<string, unknown>;
|
|
3
|
+
type OciMessage = {
|
|
4
|
+
role: string;
|
|
5
|
+
name?: string;
|
|
6
|
+
content: OciChatContent[];
|
|
7
|
+
};
|
|
8
|
+
export declare class OCIRawMessageSerializer {
|
|
9
|
+
static serializeMessages(messages: Message[]): OciMessage[];
|
|
10
|
+
static serializeMessagesForCohere(messages: Message[]): string;
|
|
11
|
+
}
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { AssistantMessage, ContentPartImageParam, ContentPartRefusalParam, ContentPartTextParam, SystemMessage, UserMessage, } from '../messages.js';
|
|
2
|
+
const textContent = (text) => ({
|
|
3
|
+
type: 'TEXT',
|
|
4
|
+
text,
|
|
5
|
+
});
|
|
6
|
+
const imageContent = (url) => ({
|
|
7
|
+
type: 'IMAGE',
|
|
8
|
+
imageUrl: {
|
|
9
|
+
url,
|
|
10
|
+
},
|
|
11
|
+
});
|
|
12
|
+
const contentPartsToOci = (content) => {
|
|
13
|
+
if (typeof content === 'string') {
|
|
14
|
+
return [textContent(content)];
|
|
15
|
+
}
|
|
16
|
+
if (!Array.isArray(content)) {
|
|
17
|
+
return [];
|
|
18
|
+
}
|
|
19
|
+
const parts = [];
|
|
20
|
+
for (const part of content) {
|
|
21
|
+
if (part instanceof ContentPartTextParam) {
|
|
22
|
+
parts.push(textContent(part.text));
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
if (part instanceof ContentPartImageParam) {
|
|
26
|
+
parts.push(imageContent(part.image_url.url));
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
if (part instanceof ContentPartRefusalParam) {
|
|
30
|
+
parts.push(textContent(`[Refusal] ${part.refusal}`));
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return parts;
|
|
34
|
+
};
|
|
35
|
+
const serializeRole = (message) => {
|
|
36
|
+
if (message instanceof SystemMessage) {
|
|
37
|
+
return 'SYSTEM';
|
|
38
|
+
}
|
|
39
|
+
if (message instanceof AssistantMessage) {
|
|
40
|
+
return 'ASSISTANT';
|
|
41
|
+
}
|
|
42
|
+
return 'USER';
|
|
43
|
+
};
|
|
44
|
+
const serializeName = (message) => {
|
|
45
|
+
if (message instanceof UserMessage || message instanceof SystemMessage) {
|
|
46
|
+
return message.name ?? undefined;
|
|
47
|
+
}
|
|
48
|
+
return undefined;
|
|
49
|
+
};
|
|
50
|
+
export class OCIRawMessageSerializer {
|
|
51
|
+
static serializeMessages(messages) {
|
|
52
|
+
const serialized = [];
|
|
53
|
+
for (const message of messages) {
|
|
54
|
+
const content = message instanceof AssistantMessage
|
|
55
|
+
? contentPartsToOci(message.content)
|
|
56
|
+
: contentPartsToOci(message.content);
|
|
57
|
+
if (content.length === 0) {
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
serialized.push({
|
|
61
|
+
role: serializeRole(message),
|
|
62
|
+
name: serializeName(message),
|
|
63
|
+
content,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
return serialized;
|
|
67
|
+
}
|
|
68
|
+
static serializeMessagesForCohere(messages) {
|
|
69
|
+
const conversationParts = [];
|
|
70
|
+
for (const message of messages) {
|
|
71
|
+
let text = '';
|
|
72
|
+
if (message instanceof UserMessage || message instanceof SystemMessage) {
|
|
73
|
+
const content = message.content;
|
|
74
|
+
if (typeof content === 'string') {
|
|
75
|
+
text = content;
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
text = content
|
|
79
|
+
.map((part) => {
|
|
80
|
+
if (part instanceof ContentPartTextParam) {
|
|
81
|
+
return part.text;
|
|
82
|
+
}
|
|
83
|
+
if (part instanceof ContentPartImageParam) {
|
|
84
|
+
return part.image_url.url.startsWith('data:image/')
|
|
85
|
+
? '[Image: base64_data]'
|
|
86
|
+
: '[Image: external_url]';
|
|
87
|
+
}
|
|
88
|
+
return '';
|
|
89
|
+
})
|
|
90
|
+
.filter(Boolean)
|
|
91
|
+
.join(' ');
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
else if (message instanceof AssistantMessage) {
|
|
95
|
+
if (typeof message.content === 'string') {
|
|
96
|
+
text = message.content;
|
|
97
|
+
}
|
|
98
|
+
else if (Array.isArray(message.content)) {
|
|
99
|
+
text = message.content
|
|
100
|
+
.map((part) => {
|
|
101
|
+
if (part instanceof ContentPartTextParam) {
|
|
102
|
+
return part.text;
|
|
103
|
+
}
|
|
104
|
+
if (part instanceof ContentPartRefusalParam) {
|
|
105
|
+
return `[Refusal] ${part.refusal}`;
|
|
106
|
+
}
|
|
107
|
+
return '';
|
|
108
|
+
})
|
|
109
|
+
.filter(Boolean)
|
|
110
|
+
.join(' ');
|
|
111
|
+
}
|
|
112
|
+
else if (message.refusal) {
|
|
113
|
+
text = `[Refusal] ${message.refusal}`;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if (!text) {
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
const prefix = message instanceof SystemMessage
|
|
120
|
+
? 'System'
|
|
121
|
+
: message instanceof AssistantMessage
|
|
122
|
+
? 'Assistant'
|
|
123
|
+
: 'User';
|
|
124
|
+
conversationParts.push(`${prefix}: ${text}`);
|
|
125
|
+
}
|
|
126
|
+
return conversationParts.join('\n\n');
|
|
127
|
+
}
|
|
128
|
+
}
|
package/dist/mcp/server.d.ts
CHANGED