browser-use 0.0.1 → 0.1.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/LICENSE +21 -0
- package/README.md +761 -0
- package/dist/agent/cloud-events.d.ts +264 -0
- package/dist/agent/cloud-events.js +318 -0
- package/dist/agent/gif.d.ts +15 -0
- package/dist/agent/gif.js +215 -0
- package/dist/agent/index.d.ts +8 -0
- package/dist/agent/index.js +8 -0
- package/dist/agent/message-manager/service.d.ts +30 -0
- package/dist/agent/message-manager/service.js +208 -0
- package/dist/agent/message-manager/utils.d.ts +2 -0
- package/dist/agent/message-manager/utils.js +41 -0
- package/dist/agent/message-manager/views.d.ts +26 -0
- package/dist/agent/message-manager/views.js +73 -0
- package/dist/agent/prompts.d.ts +52 -0
- package/dist/agent/prompts.js +259 -0
- package/dist/agent/service.d.ts +290 -0
- package/dist/agent/service.js +2200 -0
- package/dist/agent/views.d.ts +741 -0
- package/dist/agent/views.js +537 -0
- package/dist/browser/browser.d.ts +7 -0
- package/dist/browser/browser.js +5 -0
- package/dist/browser/context.d.ts +8 -0
- package/dist/browser/context.js +4 -0
- package/dist/browser/dvd-screensaver.d.ts +101 -0
- package/dist/browser/dvd-screensaver.js +270 -0
- package/dist/browser/extensions.d.ts +63 -0
- package/dist/browser/extensions.js +359 -0
- package/dist/browser/index.d.ts +10 -0
- package/dist/browser/index.js +9 -0
- package/dist/browser/playwright-manager.d.ts +47 -0
- package/dist/browser/playwright-manager.js +146 -0
- package/dist/browser/profile.d.ts +196 -0
- package/dist/browser/profile.js +815 -0
- package/dist/browser/session.d.ts +505 -0
- package/dist/browser/session.js +3409 -0
- package/dist/browser/types.d.ts +1184 -0
- package/dist/browser/types.js +1 -0
- package/dist/browser/utils.d.ts +1 -0
- package/dist/browser/utils.js +19 -0
- package/dist/browser/views.d.ts +78 -0
- package/dist/browser/views.js +72 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +44 -0
- package/dist/config.d.ts +108 -0
- package/dist/config.js +430 -0
- package/dist/controller/index.d.ts +3 -0
- package/dist/controller/index.js +3 -0
- package/dist/controller/registry/index.d.ts +2 -0
- package/dist/controller/registry/index.js +2 -0
- package/dist/controller/registry/service.d.ts +45 -0
- package/dist/controller/registry/service.js +184 -0
- package/dist/controller/registry/views.d.ts +55 -0
- package/dist/controller/registry/views.js +174 -0
- package/dist/controller/service.d.ts +49 -0
- package/dist/controller/service.js +1176 -0
- package/dist/controller/views.d.ts +241 -0
- package/dist/controller/views.js +88 -0
- package/dist/dom/clickable-element-processor/service.d.ts +11 -0
- package/dist/dom/clickable-element-processor/service.js +60 -0
- package/dist/dom/dom_tree/index.js +1400 -0
- package/dist/dom/history-tree-processor/service.d.ts +14 -0
- package/dist/dom/history-tree-processor/service.js +75 -0
- package/dist/dom/history-tree-processor/view.d.ts +54 -0
- package/dist/dom/history-tree-processor/view.js +56 -0
- package/dist/dom/playground/extraction.d.ts +19 -0
- package/dist/dom/playground/extraction.js +187 -0
- package/dist/dom/playground/process-dom.d.ts +1 -0
- package/dist/dom/playground/process-dom.js +5 -0
- package/dist/dom/playground/test-accessibility.d.ts +44 -0
- package/dist/dom/playground/test-accessibility.js +111 -0
- package/dist/dom/service.d.ts +19 -0
- package/dist/dom/service.js +227 -0
- package/dist/dom/utils.d.ts +1 -0
- package/dist/dom/utils.js +6 -0
- package/dist/dom/views.d.ts +61 -0
- package/dist/dom/views.js +247 -0
- package/dist/event-bus.d.ts +11 -0
- package/dist/event-bus.js +19 -0
- package/dist/exceptions.d.ts +10 -0
- package/dist/exceptions.js +22 -0
- package/dist/filesystem/file-system.d.ts +68 -0
- package/dist/filesystem/file-system.js +412 -0
- package/dist/filesystem/index.d.ts +1 -0
- package/dist/filesystem/index.js +1 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.js +33 -0
- package/dist/integrations/gmail/actions.d.ts +12 -0
- package/dist/integrations/gmail/actions.js +113 -0
- package/dist/integrations/gmail/index.d.ts +2 -0
- package/dist/integrations/gmail/index.js +2 -0
- package/dist/integrations/gmail/service.d.ts +61 -0
- package/dist/integrations/gmail/service.js +260 -0
- package/dist/llm/anthropic/chat.d.ts +28 -0
- package/dist/llm/anthropic/chat.js +126 -0
- package/dist/llm/anthropic/index.d.ts +2 -0
- package/dist/llm/anthropic/index.js +2 -0
- package/dist/llm/anthropic/serializer.d.ts +68 -0
- package/dist/llm/anthropic/serializer.js +285 -0
- package/dist/llm/aws/chat-anthropic.d.ts +61 -0
- package/dist/llm/aws/chat-anthropic.js +176 -0
- package/dist/llm/aws/chat-bedrock.d.ts +15 -0
- package/dist/llm/aws/chat-bedrock.js +80 -0
- package/dist/llm/aws/index.d.ts +3 -0
- package/dist/llm/aws/index.js +3 -0
- package/dist/llm/aws/serializer.d.ts +5 -0
- package/dist/llm/aws/serializer.js +68 -0
- package/dist/llm/azure/chat.d.ts +15 -0
- package/dist/llm/azure/chat.js +83 -0
- package/dist/llm/azure/index.d.ts +1 -0
- package/dist/llm/azure/index.js +1 -0
- package/dist/llm/base.d.ts +16 -0
- package/dist/llm/base.js +1 -0
- package/dist/llm/deepseek/chat.d.ts +15 -0
- package/dist/llm/deepseek/chat.js +51 -0
- package/dist/llm/deepseek/index.d.ts +2 -0
- package/dist/llm/deepseek/index.js +2 -0
- package/dist/llm/deepseek/serializer.d.ts +6 -0
- package/dist/llm/deepseek/serializer.js +57 -0
- package/dist/llm/exceptions.d.ts +10 -0
- package/dist/llm/exceptions.js +18 -0
- package/dist/llm/google/chat.d.ts +20 -0
- package/dist/llm/google/chat.js +144 -0
- package/dist/llm/google/index.d.ts +2 -0
- package/dist/llm/google/index.js +2 -0
- package/dist/llm/google/serializer.d.ts +6 -0
- package/dist/llm/google/serializer.js +64 -0
- package/dist/llm/groq/chat.d.ts +15 -0
- package/dist/llm/groq/chat.js +52 -0
- package/dist/llm/groq/index.d.ts +3 -0
- package/dist/llm/groq/index.js +3 -0
- package/dist/llm/groq/parser.d.ts +32 -0
- package/dist/llm/groq/parser.js +189 -0
- package/dist/llm/groq/serializer.d.ts +6 -0
- package/dist/llm/groq/serializer.js +56 -0
- package/dist/llm/messages.d.ts +77 -0
- package/dist/llm/messages.js +157 -0
- package/dist/llm/ollama/chat.d.ts +15 -0
- package/dist/llm/ollama/chat.js +77 -0
- package/dist/llm/ollama/index.d.ts +2 -0
- package/dist/llm/ollama/index.js +2 -0
- package/dist/llm/ollama/serializer.d.ts +6 -0
- package/dist/llm/ollama/serializer.js +53 -0
- package/dist/llm/openai/chat.d.ts +38 -0
- package/dist/llm/openai/chat.js +174 -0
- package/dist/llm/openai/index.d.ts +3 -0
- package/dist/llm/openai/index.js +3 -0
- package/dist/llm/openai/like.d.ts +17 -0
- package/dist/llm/openai/like.js +19 -0
- package/dist/llm/openai/serializer.d.ts +6 -0
- package/dist/llm/openai/serializer.js +57 -0
- package/dist/llm/openrouter/chat.d.ts +15 -0
- package/dist/llm/openrouter/chat.js +74 -0
- package/dist/llm/openrouter/index.d.ts +2 -0
- package/dist/llm/openrouter/index.js +2 -0
- package/dist/llm/openrouter/serializer.d.ts +3 -0
- package/dist/llm/openrouter/serializer.js +3 -0
- package/dist/llm/schema.d.ts +6 -0
- package/dist/llm/schema.js +77 -0
- package/dist/llm/views.d.ts +15 -0
- package/dist/llm/views.js +12 -0
- package/dist/logging-config.d.ts +25 -0
- package/dist/logging-config.js +89 -0
- package/dist/mcp/client.d.ts +142 -0
- package/dist/mcp/client.js +638 -0
- package/dist/mcp/controller.d.ts +6 -0
- package/dist/mcp/controller.js +38 -0
- package/dist/mcp/index.d.ts +3 -0
- package/dist/mcp/index.js +3 -0
- package/dist/mcp/server.d.ts +134 -0
- package/dist/mcp/server.js +759 -0
- package/dist/observability-decorators.d.ts +158 -0
- package/dist/observability-decorators.js +286 -0
- package/dist/observability.d.ts +23 -0
- package/dist/observability.js +58 -0
- package/dist/screenshots/index.d.ts +1 -0
- package/dist/screenshots/index.js +1 -0
- package/dist/screenshots/service.d.ts +6 -0
- package/dist/screenshots/service.js +28 -0
- package/dist/sync/auth.d.ts +27 -0
- package/dist/sync/auth.js +205 -0
- package/dist/sync/index.d.ts +2 -0
- package/dist/sync/index.js +2 -0
- package/dist/sync/service.d.ts +21 -0
- package/dist/sync/service.js +146 -0
- package/dist/telemetry/index.d.ts +2 -0
- package/dist/telemetry/index.js +2 -0
- package/dist/telemetry/service.d.ts +12 -0
- package/dist/telemetry/service.js +85 -0
- package/dist/telemetry/views.d.ts +112 -0
- package/dist/telemetry/views.js +112 -0
- package/dist/tokens/index.d.ts +2 -0
- package/dist/tokens/index.js +2 -0
- package/dist/tokens/service.d.ts +35 -0
- package/dist/tokens/service.js +423 -0
- package/dist/tokens/views.d.ts +58 -0
- package/dist/tokens/views.js +1 -0
- package/dist/utils.d.ts +128 -0
- package/dist/utils.js +529 -0
- package/package.json +94 -5
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import Groq from 'groq-sdk';
|
|
2
|
+
import { ChatInvokeCompletion } from '../views.js';
|
|
3
|
+
import { GroqMessageSerializer } from './serializer.js';
|
|
4
|
+
export class ChatGroq {
|
|
5
|
+
model;
|
|
6
|
+
provider = 'groq';
|
|
7
|
+
client;
|
|
8
|
+
constructor(model = 'llama-3.1-70b-versatile') {
|
|
9
|
+
this.model = model;
|
|
10
|
+
this.client = new Groq({
|
|
11
|
+
apiKey: process.env.GROQ_API_KEY,
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
get name() {
|
|
15
|
+
return this.model;
|
|
16
|
+
}
|
|
17
|
+
get model_name() {
|
|
18
|
+
return this.model;
|
|
19
|
+
}
|
|
20
|
+
async ainvoke(messages, output_format, options = {}) {
|
|
21
|
+
const serializer = new GroqMessageSerializer();
|
|
22
|
+
const groqMessages = serializer.serialize(messages);
|
|
23
|
+
let responseFormat = undefined;
|
|
24
|
+
if (output_format && 'schema' in output_format && output_format.schema) {
|
|
25
|
+
// Groq supports JSON mode, but not full JSON schema validation in the same way as OpenAI yet (or maybe it does now).
|
|
26
|
+
// For now, we'll enforce JSON mode and prompt engineering or use tools if needed.
|
|
27
|
+
// But let's try to use json_object which is supported.
|
|
28
|
+
responseFormat = { type: 'json_object' };
|
|
29
|
+
}
|
|
30
|
+
const response = await this.client.chat.completions.create({
|
|
31
|
+
model: this.model,
|
|
32
|
+
messages: groqMessages,
|
|
33
|
+
response_format: responseFormat,
|
|
34
|
+
}, options.signal ? { signal: options.signal } : undefined);
|
|
35
|
+
const content = response.choices[0].message.content || '';
|
|
36
|
+
let completion = content;
|
|
37
|
+
if (output_format) {
|
|
38
|
+
try {
|
|
39
|
+
completion = output_format.parse(JSON.parse(content));
|
|
40
|
+
}
|
|
41
|
+
catch (e) {
|
|
42
|
+
console.error('Failed to parse completion', e);
|
|
43
|
+
throw e;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return new ChatInvokeCompletion(completion, {
|
|
47
|
+
prompt_tokens: response.usage?.prompt_tokens ?? 0,
|
|
48
|
+
completion_tokens: response.usage?.completion_tokens ?? 0,
|
|
49
|
+
total_tokens: response.usage?.total_tokens ?? 0,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { APIError } from 'groq-sdk';
|
|
2
|
+
export declare class ParseFailedGenerationError extends Error {
|
|
3
|
+
constructor(message: string);
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Extract JSON from model output, handling both plain JSON and code-block-wrapped JSON.
|
|
7
|
+
* This is used to parse Groq's failed_generation field when an API error occurs.
|
|
8
|
+
*
|
|
9
|
+
* @param error - The Groq API error containing failed_generation
|
|
10
|
+
* @param outputFormat - An object with a parse method (typically a Zod schema)
|
|
11
|
+
* @returns The parsed output in the expected format
|
|
12
|
+
* @throws ParseFailedGenerationError if the failed_generation field is missing
|
|
13
|
+
* @throws Error if JSON parsing fails
|
|
14
|
+
*/
|
|
15
|
+
export declare function tryParseGroqFailedGeneration<T>(error: APIError & {
|
|
16
|
+
body?: {
|
|
17
|
+
error?: {
|
|
18
|
+
failed_generation?: string;
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
}, outputFormat: {
|
|
22
|
+
parse: (input: string) => T;
|
|
23
|
+
}): T;
|
|
24
|
+
/**
|
|
25
|
+
* Fix control characters in JSON string values to make them valid JSON.
|
|
26
|
+
* This function escapes literal control characters (newlines, tabs, etc.) that
|
|
27
|
+
* appear inside JSON string values, while preserving the JSON structure.
|
|
28
|
+
*
|
|
29
|
+
* @param content - The JSON string to fix
|
|
30
|
+
* @returns The fixed JSON string
|
|
31
|
+
*/
|
|
32
|
+
export declare function fixControlCharactersInJson(content: string): string;
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { logger } from '../../logging-config.js';
|
|
2
|
+
export class ParseFailedGenerationError extends Error {
|
|
3
|
+
constructor(message) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.name = 'ParseFailedGenerationError';
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Extract JSON from model output, handling both plain JSON and code-block-wrapped JSON.
|
|
10
|
+
* This is used to parse Groq's failed_generation field when an API error occurs.
|
|
11
|
+
*
|
|
12
|
+
* @param error - The Groq API error containing failed_generation
|
|
13
|
+
* @param outputFormat - An object with a parse method (typically a Zod schema)
|
|
14
|
+
* @returns The parsed output in the expected format
|
|
15
|
+
* @throws ParseFailedGenerationError if the failed_generation field is missing
|
|
16
|
+
* @throws Error if JSON parsing fails
|
|
17
|
+
*/
|
|
18
|
+
export function tryParseGroqFailedGeneration(error, outputFormat) {
|
|
19
|
+
try {
|
|
20
|
+
const failedGeneration = error.body?.error?.failed_generation;
|
|
21
|
+
if (!failedGeneration) {
|
|
22
|
+
throw new Error('No failed_generation field in error body');
|
|
23
|
+
}
|
|
24
|
+
let content = failedGeneration;
|
|
25
|
+
// If content is wrapped in code blocks, extract just the JSON part
|
|
26
|
+
if (content.includes('```')) {
|
|
27
|
+
// Find the JSON content between code blocks
|
|
28
|
+
const parts = content.split('```');
|
|
29
|
+
content = parts[1] || content;
|
|
30
|
+
// Remove language identifier if present (e.g., 'json\n')
|
|
31
|
+
if (content.includes('\n')) {
|
|
32
|
+
const [first, ...rest] = content.split('\n');
|
|
33
|
+
// Check if first line is just a language identifier
|
|
34
|
+
if (first.trim().length < 20) {
|
|
35
|
+
content = rest.join('\n');
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
// Remove html-like tags before the first { and after the last }
|
|
40
|
+
// This handles cases like <|header_start|>assistant<|header_end|> and <function=AgentOutput>
|
|
41
|
+
// Only remove content before { if content doesn't already start with {
|
|
42
|
+
if (!content.trim().startsWith('{')) {
|
|
43
|
+
content = content.replace(/^.*?(?=\{)/s, '');
|
|
44
|
+
}
|
|
45
|
+
// Remove common HTML-like tags and patterns at the end, but be more conservative
|
|
46
|
+
// Look for patterns like </function>, <|header_start|>, etc. after the JSON
|
|
47
|
+
content = content.replace(/\}(\s*<[^>]*>.*?$)/s, '}');
|
|
48
|
+
content = content.replace(/\}(\s*<\|[^|]*\|>.*?$)/s, '}');
|
|
49
|
+
// Handle extra characters after the JSON, including stray braces
|
|
50
|
+
// Find the position of the last } that would close the main JSON object
|
|
51
|
+
content = content.trim();
|
|
52
|
+
if (content.endsWith('}')) {
|
|
53
|
+
// Try to parse and see if we get valid JSON
|
|
54
|
+
try {
|
|
55
|
+
JSON.parse(content);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
// If parsing fails, try to find the correct end of the JSON
|
|
59
|
+
// by counting braces and removing anything after the balanced JSON
|
|
60
|
+
let braceCount = 0;
|
|
61
|
+
let lastValidPos = -1;
|
|
62
|
+
for (let i = 0; i < content.length; i++) {
|
|
63
|
+
const char = content[i];
|
|
64
|
+
if (char === '{') {
|
|
65
|
+
braceCount++;
|
|
66
|
+
}
|
|
67
|
+
else if (char === '}') {
|
|
68
|
+
braceCount--;
|
|
69
|
+
if (braceCount === 0) {
|
|
70
|
+
lastValidPos = i + 1;
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (lastValidPos > 0) {
|
|
76
|
+
content = content.substring(0, lastValidPos);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// Fix control characters in JSON strings before parsing
|
|
81
|
+
// This handles cases where literal control characters appear in JSON values
|
|
82
|
+
content = fixControlCharactersInJson(content);
|
|
83
|
+
// Parse the cleaned content
|
|
84
|
+
let resultDict = JSON.parse(content);
|
|
85
|
+
// Some models occasionally respond with a list containing one dict
|
|
86
|
+
if (Array.isArray(resultDict) &&
|
|
87
|
+
resultDict.length === 1 &&
|
|
88
|
+
typeof resultDict[0] === 'object') {
|
|
89
|
+
resultDict = resultDict[0];
|
|
90
|
+
}
|
|
91
|
+
logger.debug(`Successfully parsed model output: ${JSON.stringify(resultDict)}`);
|
|
92
|
+
return outputFormat.parse(JSON.stringify(resultDict));
|
|
93
|
+
}
|
|
94
|
+
catch (err) {
|
|
95
|
+
if (err instanceof Error &&
|
|
96
|
+
err.message.includes('No failed_generation field')) {
|
|
97
|
+
throw new ParseFailedGenerationError(err.message);
|
|
98
|
+
}
|
|
99
|
+
if (err instanceof SyntaxError) {
|
|
100
|
+
logger.warning(`Failed to parse model output: ${err.message}`);
|
|
101
|
+
throw new Error(`Could not parse response. ${err.message}`);
|
|
102
|
+
}
|
|
103
|
+
const errorMessage = error.message || String(error);
|
|
104
|
+
throw new ParseFailedGenerationError(errorMessage);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Fix control characters in JSON string values to make them valid JSON.
|
|
109
|
+
* This function escapes literal control characters (newlines, tabs, etc.) that
|
|
110
|
+
* appear inside JSON string values, while preserving the JSON structure.
|
|
111
|
+
*
|
|
112
|
+
* @param content - The JSON string to fix
|
|
113
|
+
* @returns The fixed JSON string
|
|
114
|
+
*/
|
|
115
|
+
export function fixControlCharactersInJson(content) {
|
|
116
|
+
try {
|
|
117
|
+
// First try to parse as-is to see if it's already valid
|
|
118
|
+
JSON.parse(content);
|
|
119
|
+
return content;
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
// Continue to fix the content
|
|
123
|
+
}
|
|
124
|
+
// More sophisticated approach: only escape control characters inside string values
|
|
125
|
+
// while preserving JSON structure formatting
|
|
126
|
+
const result = [];
|
|
127
|
+
let i = 0;
|
|
128
|
+
let inString = false;
|
|
129
|
+
let escaped = false;
|
|
130
|
+
while (i < content.length) {
|
|
131
|
+
const char = content[i];
|
|
132
|
+
if (!inString) {
|
|
133
|
+
// Outside of string - check if we're entering a string
|
|
134
|
+
if (char === '"') {
|
|
135
|
+
inString = true;
|
|
136
|
+
}
|
|
137
|
+
result.push(char);
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
// Inside string - handle escaping and control characters
|
|
141
|
+
if (escaped) {
|
|
142
|
+
// Previous character was backslash, so this character is escaped
|
|
143
|
+
result.push(char);
|
|
144
|
+
escaped = false;
|
|
145
|
+
}
|
|
146
|
+
else if (char === '\\') {
|
|
147
|
+
// This is an escape character
|
|
148
|
+
result.push(char);
|
|
149
|
+
escaped = true;
|
|
150
|
+
}
|
|
151
|
+
else if (char === '"') {
|
|
152
|
+
// End of string
|
|
153
|
+
result.push(char);
|
|
154
|
+
inString = false;
|
|
155
|
+
}
|
|
156
|
+
else if (char === '\n') {
|
|
157
|
+
// Literal newline inside string - escape it
|
|
158
|
+
result.push('\\n');
|
|
159
|
+
}
|
|
160
|
+
else if (char === '\r') {
|
|
161
|
+
// Literal carriage return inside string - escape it
|
|
162
|
+
result.push('\\r');
|
|
163
|
+
}
|
|
164
|
+
else if (char === '\t') {
|
|
165
|
+
// Literal tab inside string - escape it
|
|
166
|
+
result.push('\\t');
|
|
167
|
+
}
|
|
168
|
+
else if (char === '\b') {
|
|
169
|
+
// Literal backspace inside string - escape it
|
|
170
|
+
result.push('\\b');
|
|
171
|
+
}
|
|
172
|
+
else if (char === '\f') {
|
|
173
|
+
// Literal form feed inside string - escape it
|
|
174
|
+
result.push('\\f');
|
|
175
|
+
}
|
|
176
|
+
else if (char.charCodeAt(0) < 32) {
|
|
177
|
+
// Other control characters inside string - convert to unicode escape
|
|
178
|
+
const code = char.charCodeAt(0);
|
|
179
|
+
result.push(`\\u${code.toString(16).padStart(4, '0')}`);
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
// Normal character inside string
|
|
183
|
+
result.push(char);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
i++;
|
|
187
|
+
}
|
|
188
|
+
return result.join('');
|
|
189
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ChatCompletionMessageParam } from 'groq-sdk/resources/chat/completions.mjs';
|
|
2
|
+
import { type Message } from '../messages.js';
|
|
3
|
+
export declare class GroqMessageSerializer {
|
|
4
|
+
serialize(messages: Message[]): ChatCompletionMessageParam[];
|
|
5
|
+
private serializeMessage;
|
|
6
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { AssistantMessage, ContentPartImageParam, ContentPartTextParam, SystemMessage, UserMessage, } from '../messages.js';
|
|
2
|
+
export class GroqMessageSerializer {
|
|
3
|
+
serialize(messages) {
|
|
4
|
+
return messages.map((message) => this.serializeMessage(message));
|
|
5
|
+
}
|
|
6
|
+
serializeMessage(message) {
|
|
7
|
+
if (message instanceof UserMessage) {
|
|
8
|
+
return {
|
|
9
|
+
role: 'user',
|
|
10
|
+
content: Array.isArray(message.content)
|
|
11
|
+
? message.content.map((part) => {
|
|
12
|
+
if (part instanceof ContentPartTextParam) {
|
|
13
|
+
return { type: 'text', text: part.text };
|
|
14
|
+
}
|
|
15
|
+
if (part instanceof ContentPartImageParam) {
|
|
16
|
+
return {
|
|
17
|
+
type: 'image_url',
|
|
18
|
+
image_url: {
|
|
19
|
+
url: part.image_url.url,
|
|
20
|
+
detail: part.image_url.detail,
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
return { type: 'text', text: '' };
|
|
25
|
+
})
|
|
26
|
+
: message.content,
|
|
27
|
+
name: message.name || undefined,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
if (message instanceof SystemMessage) {
|
|
31
|
+
return {
|
|
32
|
+
role: 'system',
|
|
33
|
+
content: Array.isArray(message.content)
|
|
34
|
+
? message.content.map((part) => part.text).join('\n')
|
|
35
|
+
: message.content,
|
|
36
|
+
name: message.name || undefined,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
if (message instanceof AssistantMessage) {
|
|
40
|
+
const toolCalls = message.tool_calls?.map((toolCall) => ({
|
|
41
|
+
id: toolCall.id,
|
|
42
|
+
type: 'function',
|
|
43
|
+
function: {
|
|
44
|
+
name: toolCall.functionCall.name,
|
|
45
|
+
arguments: toolCall.functionCall.arguments,
|
|
46
|
+
},
|
|
47
|
+
}));
|
|
48
|
+
return {
|
|
49
|
+
role: 'assistant',
|
|
50
|
+
content: typeof message.content === 'string' ? message.content : null,
|
|
51
|
+
tool_calls: toolCalls,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
throw new Error(`Unknown message type: ${message.constructor.name}`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
export type SupportedImageMediaType = 'image/jpeg' | 'image/png' | 'image/gif' | 'image/webp';
|
|
2
|
+
export declare class ContentPartTextParam {
|
|
3
|
+
text: string;
|
|
4
|
+
type: 'text';
|
|
5
|
+
constructor(text: string);
|
|
6
|
+
toString(): string;
|
|
7
|
+
}
|
|
8
|
+
export declare class ContentPartRefusalParam {
|
|
9
|
+
refusal: string;
|
|
10
|
+
type: 'refusal';
|
|
11
|
+
constructor(refusal: string);
|
|
12
|
+
toString(): string;
|
|
13
|
+
}
|
|
14
|
+
export declare class ImageURL {
|
|
15
|
+
url: string;
|
|
16
|
+
detail: 'auto' | 'low' | 'high';
|
|
17
|
+
media_type: SupportedImageMediaType;
|
|
18
|
+
constructor(url: string, detail?: 'auto' | 'low' | 'high', media?: SupportedImageMediaType);
|
|
19
|
+
toString(): string;
|
|
20
|
+
}
|
|
21
|
+
export declare class ContentPartImageParam {
|
|
22
|
+
image_url: ImageURL;
|
|
23
|
+
type: 'image_url';
|
|
24
|
+
constructor(image_url: ImageURL);
|
|
25
|
+
toString(): string;
|
|
26
|
+
}
|
|
27
|
+
export declare class FunctionCall {
|
|
28
|
+
name: string;
|
|
29
|
+
arguments: string;
|
|
30
|
+
constructor(name: string, args: string);
|
|
31
|
+
toString(): string;
|
|
32
|
+
}
|
|
33
|
+
export declare class ToolCall {
|
|
34
|
+
id: string;
|
|
35
|
+
functionCall: FunctionCall;
|
|
36
|
+
type: 'function';
|
|
37
|
+
constructor(id: string, functionCall: FunctionCall);
|
|
38
|
+
toString(): string;
|
|
39
|
+
}
|
|
40
|
+
type ContentPart = ContentPartTextParam | ContentPartImageParam | ContentPartRefusalParam;
|
|
41
|
+
export type MessageRole = 'user' | 'system' | 'assistant';
|
|
42
|
+
export declare abstract class MessageBase {
|
|
43
|
+
cache: boolean;
|
|
44
|
+
abstract role: MessageRole;
|
|
45
|
+
constructor(init?: Partial<MessageBase>);
|
|
46
|
+
}
|
|
47
|
+
export declare class UserMessage extends MessageBase {
|
|
48
|
+
role: MessageRole;
|
|
49
|
+
content: string | ContentPart[];
|
|
50
|
+
name: string | null;
|
|
51
|
+
constructor(content: string | ContentPart[], name?: string | null);
|
|
52
|
+
get text(): string;
|
|
53
|
+
toString(): string;
|
|
54
|
+
}
|
|
55
|
+
export declare class SystemMessage extends MessageBase {
|
|
56
|
+
role: MessageRole;
|
|
57
|
+
content: string | ContentPartTextParam[];
|
|
58
|
+
name: string | null;
|
|
59
|
+
constructor(content: string | ContentPartTextParam[], name?: string | null);
|
|
60
|
+
get text(): string;
|
|
61
|
+
toString(): string;
|
|
62
|
+
}
|
|
63
|
+
export declare class AssistantMessage extends MessageBase {
|
|
64
|
+
role: MessageRole;
|
|
65
|
+
content: string | ContentPart[] | null;
|
|
66
|
+
tool_calls: ToolCall[] | null;
|
|
67
|
+
refusal: string | null;
|
|
68
|
+
constructor(init: {
|
|
69
|
+
content?: string | ContentPart[] | null;
|
|
70
|
+
tool_calls?: ToolCall[] | null;
|
|
71
|
+
refusal?: string | null;
|
|
72
|
+
});
|
|
73
|
+
get text(): string;
|
|
74
|
+
toString(): string;
|
|
75
|
+
}
|
|
76
|
+
export type Message = UserMessage | SystemMessage | AssistantMessage;
|
|
77
|
+
export {};
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
const truncate = (text, maxLength = 50) => {
|
|
2
|
+
if (text.length <= maxLength) {
|
|
3
|
+
return text;
|
|
4
|
+
}
|
|
5
|
+
return `${text.slice(0, maxLength - 3)}...`;
|
|
6
|
+
};
|
|
7
|
+
const formatImageUrl = (url, maxLength = 50) => {
|
|
8
|
+
if (url.startsWith('data:')) {
|
|
9
|
+
const mediaType = url.split(';')[0]?.split(':')[1] ?? 'image';
|
|
10
|
+
return `<base64 ${mediaType}>`;
|
|
11
|
+
}
|
|
12
|
+
return truncate(url, maxLength);
|
|
13
|
+
};
|
|
14
|
+
export class ContentPartTextParam {
|
|
15
|
+
text;
|
|
16
|
+
type = 'text';
|
|
17
|
+
constructor(text) {
|
|
18
|
+
this.text = text;
|
|
19
|
+
}
|
|
20
|
+
toString() {
|
|
21
|
+
return `Text: ${truncate(this.text)}`;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export class ContentPartRefusalParam {
|
|
25
|
+
refusal;
|
|
26
|
+
type = 'refusal';
|
|
27
|
+
constructor(refusal) {
|
|
28
|
+
this.refusal = refusal;
|
|
29
|
+
}
|
|
30
|
+
toString() {
|
|
31
|
+
return `Refusal: ${truncate(this.refusal)}`;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
export class ImageURL {
|
|
35
|
+
url;
|
|
36
|
+
detail;
|
|
37
|
+
media_type;
|
|
38
|
+
constructor(url, detail = 'auto', media = 'image/png') {
|
|
39
|
+
this.url = url;
|
|
40
|
+
this.detail = detail;
|
|
41
|
+
this.media_type = media;
|
|
42
|
+
}
|
|
43
|
+
toString() {
|
|
44
|
+
return `🖼️ Image[${this.media_type}, detail=${this.detail}]: ${formatImageUrl(this.url)}`;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
export class ContentPartImageParam {
|
|
48
|
+
image_url;
|
|
49
|
+
type = 'image_url';
|
|
50
|
+
constructor(image_url) {
|
|
51
|
+
this.image_url = image_url;
|
|
52
|
+
}
|
|
53
|
+
toString() {
|
|
54
|
+
return this.image_url.toString();
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
export class FunctionCall {
|
|
58
|
+
name;
|
|
59
|
+
// @ts-ignore - 'arguments' is a reserved keyword but valid as property name
|
|
60
|
+
arguments;
|
|
61
|
+
constructor(name, args) {
|
|
62
|
+
this.name = name;
|
|
63
|
+
// @ts-ignore
|
|
64
|
+
this.arguments = args;
|
|
65
|
+
}
|
|
66
|
+
toString() {
|
|
67
|
+
return `${this.name}(${truncate(this.arguments, 80)})`;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
export class ToolCall {
|
|
71
|
+
id;
|
|
72
|
+
functionCall;
|
|
73
|
+
type = 'function';
|
|
74
|
+
constructor(id, functionCall) {
|
|
75
|
+
this.id = id;
|
|
76
|
+
this.functionCall = functionCall;
|
|
77
|
+
}
|
|
78
|
+
toString() {
|
|
79
|
+
return `ToolCall[${this.id}]: ${this.functionCall.toString()}`;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
export class MessageBase {
|
|
83
|
+
cache = false;
|
|
84
|
+
constructor(init) {
|
|
85
|
+
if (init?.cache !== undefined) {
|
|
86
|
+
this.cache = init.cache;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
export class UserMessage extends MessageBase {
|
|
91
|
+
role = 'user';
|
|
92
|
+
content;
|
|
93
|
+
name;
|
|
94
|
+
constructor(content, name = null) {
|
|
95
|
+
super();
|
|
96
|
+
this.content = content;
|
|
97
|
+
this.name = name;
|
|
98
|
+
}
|
|
99
|
+
get text() {
|
|
100
|
+
if (typeof this.content === 'string') {
|
|
101
|
+
return this.content;
|
|
102
|
+
}
|
|
103
|
+
return this.content
|
|
104
|
+
.filter((part) => part instanceof ContentPartTextParam)
|
|
105
|
+
.map((part) => part.text)
|
|
106
|
+
.join('\n');
|
|
107
|
+
}
|
|
108
|
+
toString() {
|
|
109
|
+
return `UserMessage(content=${this.text})`;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
export class SystemMessage extends MessageBase {
|
|
113
|
+
role = 'system';
|
|
114
|
+
content;
|
|
115
|
+
name;
|
|
116
|
+
constructor(content, name = null) {
|
|
117
|
+
super();
|
|
118
|
+
this.content = content;
|
|
119
|
+
this.name = name;
|
|
120
|
+
}
|
|
121
|
+
get text() {
|
|
122
|
+
if (typeof this.content === 'string') {
|
|
123
|
+
return this.content;
|
|
124
|
+
}
|
|
125
|
+
return this.content.map((part) => part.text).join('\n');
|
|
126
|
+
}
|
|
127
|
+
toString() {
|
|
128
|
+
return `SystemMessage(content=${this.text})`;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
export class AssistantMessage extends MessageBase {
|
|
132
|
+
role = 'assistant';
|
|
133
|
+
content;
|
|
134
|
+
tool_calls;
|
|
135
|
+
refusal;
|
|
136
|
+
constructor(init) {
|
|
137
|
+
super();
|
|
138
|
+
this.content = init.content ?? null;
|
|
139
|
+
this.tool_calls = init.tool_calls ?? null;
|
|
140
|
+
this.refusal = init.refusal ?? null;
|
|
141
|
+
}
|
|
142
|
+
get text() {
|
|
143
|
+
if (typeof this.content === 'string') {
|
|
144
|
+
return this.content;
|
|
145
|
+
}
|
|
146
|
+
if (Array.isArray(this.content)) {
|
|
147
|
+
return this.content
|
|
148
|
+
.filter((part) => part instanceof ContentPartTextParam)
|
|
149
|
+
.map((part) => part.text)
|
|
150
|
+
.join('\n');
|
|
151
|
+
}
|
|
152
|
+
return '';
|
|
153
|
+
}
|
|
154
|
+
toString() {
|
|
155
|
+
return `AssistantMessage(content=${this.text})`;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { BaseChatModel, ChatInvokeOptions } from '../base.js';
|
|
2
|
+
import { ChatInvokeCompletion } from '../views.js';
|
|
3
|
+
import type { Message } from '../messages.js';
|
|
4
|
+
export declare class ChatOllama implements BaseChatModel {
|
|
5
|
+
model: string;
|
|
6
|
+
provider: string;
|
|
7
|
+
private client;
|
|
8
|
+
constructor(model?: string, host?: string);
|
|
9
|
+
get name(): string;
|
|
10
|
+
get model_name(): string;
|
|
11
|
+
ainvoke(messages: Message[], output_format?: undefined, options?: ChatInvokeOptions): Promise<ChatInvokeCompletion<string>>;
|
|
12
|
+
ainvoke<T>(messages: Message[], output_format: {
|
|
13
|
+
parse: (input: string) => T;
|
|
14
|
+
} | undefined, options?: ChatInvokeOptions): Promise<ChatInvokeCompletion<T>>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { Ollama } from 'ollama';
|
|
2
|
+
import { ChatInvokeCompletion } from '../views.js';
|
|
3
|
+
import { OllamaMessageSerializer } from './serializer.js';
|
|
4
|
+
export class ChatOllama {
|
|
5
|
+
model;
|
|
6
|
+
provider = 'ollama';
|
|
7
|
+
client;
|
|
8
|
+
constructor(model = 'qwen2.5:latest', host = 'http://localhost:11434') {
|
|
9
|
+
this.model = model;
|
|
10
|
+
this.client = new Ollama({ host });
|
|
11
|
+
}
|
|
12
|
+
get name() {
|
|
13
|
+
return this.model;
|
|
14
|
+
}
|
|
15
|
+
get model_name() {
|
|
16
|
+
return this.model;
|
|
17
|
+
}
|
|
18
|
+
async ainvoke(messages, output_format, options = {}) {
|
|
19
|
+
const serializer = new OllamaMessageSerializer();
|
|
20
|
+
const ollamaMessages = serializer.serialize(messages);
|
|
21
|
+
let format = undefined;
|
|
22
|
+
if (output_format && 'schema' in output_format && output_format.schema) {
|
|
23
|
+
// Ollama supports 'json' format
|
|
24
|
+
format = 'json';
|
|
25
|
+
}
|
|
26
|
+
const requestPromise = this.client.chat({
|
|
27
|
+
model: this.model,
|
|
28
|
+
messages: ollamaMessages,
|
|
29
|
+
format: format,
|
|
30
|
+
stream: false,
|
|
31
|
+
});
|
|
32
|
+
const abortSignal = options.signal;
|
|
33
|
+
const response = abortSignal
|
|
34
|
+
? await new Promise((resolve, reject) => {
|
|
35
|
+
const onAbort = () => {
|
|
36
|
+
cleanup();
|
|
37
|
+
const error = new Error('Operation aborted');
|
|
38
|
+
error.name = 'AbortError';
|
|
39
|
+
reject(error);
|
|
40
|
+
};
|
|
41
|
+
const cleanup = () => {
|
|
42
|
+
abortSignal.removeEventListener('abort', onAbort);
|
|
43
|
+
};
|
|
44
|
+
if (abortSignal.aborted) {
|
|
45
|
+
onAbort();
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
abortSignal.addEventListener('abort', onAbort, { once: true });
|
|
49
|
+
requestPromise
|
|
50
|
+
.then((result) => {
|
|
51
|
+
cleanup();
|
|
52
|
+
resolve(result);
|
|
53
|
+
})
|
|
54
|
+
.catch((error) => {
|
|
55
|
+
cleanup();
|
|
56
|
+
reject(error);
|
|
57
|
+
});
|
|
58
|
+
})
|
|
59
|
+
: await requestPromise;
|
|
60
|
+
const content = response.message.content;
|
|
61
|
+
let completion = content;
|
|
62
|
+
if (output_format) {
|
|
63
|
+
try {
|
|
64
|
+
completion = output_format.parse(JSON.parse(content));
|
|
65
|
+
}
|
|
66
|
+
catch (e) {
|
|
67
|
+
console.error('Failed to parse completion', e);
|
|
68
|
+
throw e;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return new ChatInvokeCompletion(completion, {
|
|
72
|
+
prompt_tokens: response.prompt_eval_count ?? 0,
|
|
73
|
+
completion_tokens: response.eval_count ?? 0,
|
|
74
|
+
total_tokens: (response.prompt_eval_count ?? 0) + (response.eval_count ?? 0),
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|