browser-use 0.0.1 → 0.0.2

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.
Files changed (200) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +761 -0
  3. package/dist/agent/cloud-events.d.ts +264 -0
  4. package/dist/agent/cloud-events.js +318 -0
  5. package/dist/agent/gif.d.ts +15 -0
  6. package/dist/agent/gif.js +215 -0
  7. package/dist/agent/index.d.ts +8 -0
  8. package/dist/agent/index.js +8 -0
  9. package/dist/agent/message-manager/service.d.ts +30 -0
  10. package/dist/agent/message-manager/service.js +208 -0
  11. package/dist/agent/message-manager/utils.d.ts +2 -0
  12. package/dist/agent/message-manager/utils.js +41 -0
  13. package/dist/agent/message-manager/views.d.ts +26 -0
  14. package/dist/agent/message-manager/views.js +73 -0
  15. package/dist/agent/prompts.d.ts +52 -0
  16. package/dist/agent/prompts.js +259 -0
  17. package/dist/agent/service.d.ts +290 -0
  18. package/dist/agent/service.js +2200 -0
  19. package/dist/agent/views.d.ts +741 -0
  20. package/dist/agent/views.js +537 -0
  21. package/dist/browser/browser.d.ts +7 -0
  22. package/dist/browser/browser.js +5 -0
  23. package/dist/browser/context.d.ts +8 -0
  24. package/dist/browser/context.js +4 -0
  25. package/dist/browser/dvd-screensaver.d.ts +101 -0
  26. package/dist/browser/dvd-screensaver.js +270 -0
  27. package/dist/browser/extensions.d.ts +63 -0
  28. package/dist/browser/extensions.js +359 -0
  29. package/dist/browser/index.d.ts +10 -0
  30. package/dist/browser/index.js +9 -0
  31. package/dist/browser/playwright-manager.d.ts +47 -0
  32. package/dist/browser/playwright-manager.js +146 -0
  33. package/dist/browser/profile.d.ts +196 -0
  34. package/dist/browser/profile.js +815 -0
  35. package/dist/browser/session.d.ts +505 -0
  36. package/dist/browser/session.js +3409 -0
  37. package/dist/browser/types.d.ts +1184 -0
  38. package/dist/browser/types.js +1 -0
  39. package/dist/browser/utils.d.ts +1 -0
  40. package/dist/browser/utils.js +19 -0
  41. package/dist/browser/views.d.ts +78 -0
  42. package/dist/browser/views.js +72 -0
  43. package/dist/cli.d.ts +2 -0
  44. package/dist/cli.js +44 -0
  45. package/dist/config.d.ts +108 -0
  46. package/dist/config.js +430 -0
  47. package/dist/controller/index.d.ts +3 -0
  48. package/dist/controller/index.js +3 -0
  49. package/dist/controller/registry/index.d.ts +2 -0
  50. package/dist/controller/registry/index.js +2 -0
  51. package/dist/controller/registry/service.d.ts +45 -0
  52. package/dist/controller/registry/service.js +184 -0
  53. package/dist/controller/registry/views.d.ts +55 -0
  54. package/dist/controller/registry/views.js +174 -0
  55. package/dist/controller/service.d.ts +49 -0
  56. package/dist/controller/service.js +1176 -0
  57. package/dist/controller/views.d.ts +241 -0
  58. package/dist/controller/views.js +88 -0
  59. package/dist/dom/clickable-element-processor/service.d.ts +11 -0
  60. package/dist/dom/clickable-element-processor/service.js +60 -0
  61. package/dist/dom/dom_tree/index.js +1400 -0
  62. package/dist/dom/history-tree-processor/service.d.ts +14 -0
  63. package/dist/dom/history-tree-processor/service.js +75 -0
  64. package/dist/dom/history-tree-processor/view.d.ts +54 -0
  65. package/dist/dom/history-tree-processor/view.js +56 -0
  66. package/dist/dom/playground/extraction.d.ts +19 -0
  67. package/dist/dom/playground/extraction.js +187 -0
  68. package/dist/dom/playground/process-dom.d.ts +1 -0
  69. package/dist/dom/playground/process-dom.js +5 -0
  70. package/dist/dom/playground/test-accessibility.d.ts +44 -0
  71. package/dist/dom/playground/test-accessibility.js +111 -0
  72. package/dist/dom/service.d.ts +19 -0
  73. package/dist/dom/service.js +227 -0
  74. package/dist/dom/utils.d.ts +1 -0
  75. package/dist/dom/utils.js +6 -0
  76. package/dist/dom/views.d.ts +61 -0
  77. package/dist/dom/views.js +247 -0
  78. package/dist/event-bus.d.ts +11 -0
  79. package/dist/event-bus.js +19 -0
  80. package/dist/exceptions.d.ts +10 -0
  81. package/dist/exceptions.js +22 -0
  82. package/dist/filesystem/file-system.d.ts +68 -0
  83. package/dist/filesystem/file-system.js +412 -0
  84. package/dist/filesystem/index.d.ts +1 -0
  85. package/dist/filesystem/index.js +1 -0
  86. package/dist/index.d.ts +31 -0
  87. package/dist/index.js +33 -0
  88. package/dist/integrations/gmail/actions.d.ts +12 -0
  89. package/dist/integrations/gmail/actions.js +113 -0
  90. package/dist/integrations/gmail/index.d.ts +2 -0
  91. package/dist/integrations/gmail/index.js +2 -0
  92. package/dist/integrations/gmail/service.d.ts +61 -0
  93. package/dist/integrations/gmail/service.js +260 -0
  94. package/dist/llm/anthropic/chat.d.ts +28 -0
  95. package/dist/llm/anthropic/chat.js +126 -0
  96. package/dist/llm/anthropic/index.d.ts +2 -0
  97. package/dist/llm/anthropic/index.js +2 -0
  98. package/dist/llm/anthropic/serializer.d.ts +68 -0
  99. package/dist/llm/anthropic/serializer.js +285 -0
  100. package/dist/llm/aws/chat-anthropic.d.ts +61 -0
  101. package/dist/llm/aws/chat-anthropic.js +176 -0
  102. package/dist/llm/aws/chat-bedrock.d.ts +15 -0
  103. package/dist/llm/aws/chat-bedrock.js +80 -0
  104. package/dist/llm/aws/index.d.ts +3 -0
  105. package/dist/llm/aws/index.js +3 -0
  106. package/dist/llm/aws/serializer.d.ts +5 -0
  107. package/dist/llm/aws/serializer.js +68 -0
  108. package/dist/llm/azure/chat.d.ts +15 -0
  109. package/dist/llm/azure/chat.js +83 -0
  110. package/dist/llm/azure/index.d.ts +1 -0
  111. package/dist/llm/azure/index.js +1 -0
  112. package/dist/llm/base.d.ts +16 -0
  113. package/dist/llm/base.js +1 -0
  114. package/dist/llm/deepseek/chat.d.ts +15 -0
  115. package/dist/llm/deepseek/chat.js +51 -0
  116. package/dist/llm/deepseek/index.d.ts +2 -0
  117. package/dist/llm/deepseek/index.js +2 -0
  118. package/dist/llm/deepseek/serializer.d.ts +6 -0
  119. package/dist/llm/deepseek/serializer.js +57 -0
  120. package/dist/llm/exceptions.d.ts +10 -0
  121. package/dist/llm/exceptions.js +18 -0
  122. package/dist/llm/google/chat.d.ts +20 -0
  123. package/dist/llm/google/chat.js +144 -0
  124. package/dist/llm/google/index.d.ts +2 -0
  125. package/dist/llm/google/index.js +2 -0
  126. package/dist/llm/google/serializer.d.ts +6 -0
  127. package/dist/llm/google/serializer.js +64 -0
  128. package/dist/llm/groq/chat.d.ts +15 -0
  129. package/dist/llm/groq/chat.js +52 -0
  130. package/dist/llm/groq/index.d.ts +3 -0
  131. package/dist/llm/groq/index.js +3 -0
  132. package/dist/llm/groq/parser.d.ts +32 -0
  133. package/dist/llm/groq/parser.js +189 -0
  134. package/dist/llm/groq/serializer.d.ts +6 -0
  135. package/dist/llm/groq/serializer.js +56 -0
  136. package/dist/llm/messages.d.ts +77 -0
  137. package/dist/llm/messages.js +157 -0
  138. package/dist/llm/ollama/chat.d.ts +15 -0
  139. package/dist/llm/ollama/chat.js +77 -0
  140. package/dist/llm/ollama/index.d.ts +2 -0
  141. package/dist/llm/ollama/index.js +2 -0
  142. package/dist/llm/ollama/serializer.d.ts +6 -0
  143. package/dist/llm/ollama/serializer.js +53 -0
  144. package/dist/llm/openai/chat.d.ts +38 -0
  145. package/dist/llm/openai/chat.js +174 -0
  146. package/dist/llm/openai/index.d.ts +3 -0
  147. package/dist/llm/openai/index.js +3 -0
  148. package/dist/llm/openai/like.d.ts +17 -0
  149. package/dist/llm/openai/like.js +19 -0
  150. package/dist/llm/openai/serializer.d.ts +6 -0
  151. package/dist/llm/openai/serializer.js +57 -0
  152. package/dist/llm/openrouter/chat.d.ts +15 -0
  153. package/dist/llm/openrouter/chat.js +74 -0
  154. package/dist/llm/openrouter/index.d.ts +2 -0
  155. package/dist/llm/openrouter/index.js +2 -0
  156. package/dist/llm/openrouter/serializer.d.ts +3 -0
  157. package/dist/llm/openrouter/serializer.js +3 -0
  158. package/dist/llm/schema.d.ts +6 -0
  159. package/dist/llm/schema.js +77 -0
  160. package/dist/llm/views.d.ts +15 -0
  161. package/dist/llm/views.js +12 -0
  162. package/dist/logging-config.d.ts +25 -0
  163. package/dist/logging-config.js +89 -0
  164. package/dist/mcp/client.d.ts +142 -0
  165. package/dist/mcp/client.js +638 -0
  166. package/dist/mcp/controller.d.ts +6 -0
  167. package/dist/mcp/controller.js +38 -0
  168. package/dist/mcp/index.d.ts +3 -0
  169. package/dist/mcp/index.js +3 -0
  170. package/dist/mcp/server.d.ts +134 -0
  171. package/dist/mcp/server.js +759 -0
  172. package/dist/observability-decorators.d.ts +158 -0
  173. package/dist/observability-decorators.js +286 -0
  174. package/dist/observability.d.ts +23 -0
  175. package/dist/observability.js +58 -0
  176. package/dist/screenshots/index.d.ts +1 -0
  177. package/dist/screenshots/index.js +1 -0
  178. package/dist/screenshots/service.d.ts +6 -0
  179. package/dist/screenshots/service.js +28 -0
  180. package/dist/sync/auth.d.ts +27 -0
  181. package/dist/sync/auth.js +205 -0
  182. package/dist/sync/index.d.ts +2 -0
  183. package/dist/sync/index.js +2 -0
  184. package/dist/sync/service.d.ts +21 -0
  185. package/dist/sync/service.js +146 -0
  186. package/dist/telemetry/index.d.ts +2 -0
  187. package/dist/telemetry/index.js +2 -0
  188. package/dist/telemetry/service.d.ts +12 -0
  189. package/dist/telemetry/service.js +85 -0
  190. package/dist/telemetry/views.d.ts +112 -0
  191. package/dist/telemetry/views.js +112 -0
  192. package/dist/tokens/index.d.ts +2 -0
  193. package/dist/tokens/index.js +2 -0
  194. package/dist/tokens/service.d.ts +35 -0
  195. package/dist/tokens/service.js +423 -0
  196. package/dist/tokens/views.d.ts +58 -0
  197. package/dist/tokens/views.js +1 -0
  198. package/dist/utils.d.ts +128 -0
  199. package/dist/utils.js +529 -0
  200. package/package.json +94 -5
@@ -0,0 +1,260 @@
1
+ /**
2
+ * Gmail API Service for Browser Use
3
+ * Handles Gmail API authentication, email reading, and 2FA code extraction.
4
+ * This service provides a clean interface for agents to interact with Gmail.
5
+ */
6
+ import path from 'node:path';
7
+ import fs from 'node:fs';
8
+ import { google } from 'googleapis';
9
+ import { createLogger } from '../../logging-config.js';
10
+ import { CONFIG } from '../../config.js';
11
+ const logger = createLogger('browser_use.gmail');
12
+ export class GmailService {
13
+ static SCOPES = [
14
+ 'https://www.googleapis.com/auth/gmail.readonly',
15
+ ];
16
+ configDir;
17
+ credentialsFile;
18
+ tokenFile;
19
+ accessToken;
20
+ service = null;
21
+ creds = null;
22
+ _authenticated = false;
23
+ constructor(options = {}) {
24
+ // Set up configuration directory
25
+ this.configDir = options.config_dir || CONFIG.BROWSER_USE_CONFIG_DIR;
26
+ // Direct access token support
27
+ this.accessToken = options.access_token || null;
28
+ // Ensure config directory exists (only if not using direct token)
29
+ if (!this.accessToken) {
30
+ if (!fs.existsSync(this.configDir)) {
31
+ fs.mkdirSync(this.configDir, { recursive: true });
32
+ }
33
+ }
34
+ // Set up credential paths
35
+ this.credentialsFile =
36
+ options.credentials_file ||
37
+ path.join(this.configDir, 'gmail_credentials.json');
38
+ this.tokenFile =
39
+ options.token_file || path.join(this.configDir, 'gmail_token.json');
40
+ }
41
+ /**
42
+ * Check if Gmail service is authenticated
43
+ */
44
+ isAuthenticated() {
45
+ return this._authenticated && this.service !== null;
46
+ }
47
+ /**
48
+ * Handle OAuth authentication and token management
49
+ */
50
+ async authenticate() {
51
+ try {
52
+ logger.info('🔐 Authenticating with Gmail API...');
53
+ // Check if using direct access token
54
+ if (this.accessToken) {
55
+ logger.info('🔑 Using provided access token');
56
+ const auth = new google.auth.OAuth2();
57
+ auth.setCredentials({ access_token: this.accessToken });
58
+ this.service = google.gmail({ version: 'v1', auth });
59
+ this._authenticated = true;
60
+ logger.info('✅ Gmail API ready with access token!');
61
+ return true;
62
+ }
63
+ // Original file-based authentication flow
64
+ // Try to load existing tokens
65
+ if (fs.existsSync(this.tokenFile)) {
66
+ const tokenData = JSON.parse(fs.readFileSync(this.tokenFile, 'utf-8'));
67
+ const auth = new google.auth.OAuth2();
68
+ auth.setCredentials(tokenData);
69
+ this.creds = auth;
70
+ logger.debug('📁 Loaded existing tokens');
71
+ }
72
+ // If no valid credentials, run OAuth flow
73
+ if (!this.creds ||
74
+ !this.creds.credentials ||
75
+ !this.creds.credentials.access_token) {
76
+ if (this.creds &&
77
+ this.creds.credentials &&
78
+ this.creds.credentials.refresh_token) {
79
+ logger.info('🔄 Refreshing expired tokens...');
80
+ await this.creds.refreshAccessToken();
81
+ }
82
+ else {
83
+ logger.info('🌐 Starting OAuth flow...');
84
+ if (!fs.existsSync(this.credentialsFile)) {
85
+ logger.error(`❌ Gmail credentials file not found: ${this.credentialsFile}\n` +
86
+ 'Please download it from Google Cloud Console:\n' +
87
+ '1. Go to https://console.cloud.google.com/\n' +
88
+ '2. APIs & Services > Credentials\n' +
89
+ '3. Download OAuth 2.0 Client JSON\n' +
90
+ `4. Save as 'gmail_credentials.json' in ${this.configDir}/`);
91
+ return false;
92
+ }
93
+ const credentials = JSON.parse(fs.readFileSync(this.credentialsFile, 'utf-8'));
94
+ const { client_secret, client_id, redirect_uris } = credentials.installed || credentials.web;
95
+ const oAuth2Client = new google.auth.OAuth2(client_id, client_secret, redirect_uris[0]);
96
+ const authUrl = oAuth2Client.generateAuthUrl({
97
+ access_type: 'offline',
98
+ scope: GmailService.SCOPES,
99
+ });
100
+ logger.info(`🔗 Please visit this URL to authorize:\n${authUrl}`);
101
+ logger.info('⏳ Waiting for authorization code...');
102
+ // Note: In a real implementation, you would use a web server to handle the OAuth callback
103
+ // For now, we'll throw an error with instructions
104
+ throw new Error('OAuth flow requires manual intervention. Please:\n' +
105
+ `1. Visit: ${authUrl}\n` +
106
+ '2. Authorize the application\n' +
107
+ '3. Copy the authorization code\n' +
108
+ '4. Implement token exchange logic');
109
+ }
110
+ // Save tokens for next time
111
+ fs.writeFileSync(this.tokenFile, JSON.stringify(this.creds.credentials));
112
+ logger.info(`💾 Tokens saved to ${this.tokenFile}`);
113
+ }
114
+ // Build Gmail service
115
+ this.service = google.gmail({ version: 'v1', auth: this.creds });
116
+ this._authenticated = true;
117
+ logger.info('✅ Gmail API ready!');
118
+ return true;
119
+ }
120
+ catch (error) {
121
+ logger.error(`❌ Gmail authentication failed: ${error}`);
122
+ return false;
123
+ }
124
+ }
125
+ /**
126
+ * Get recent emails with optional query filter
127
+ */
128
+ async getRecentEmails(options = {}) {
129
+ const { max_results = 10, query = '', time_filter = '1h' } = options;
130
+ if (!this.isAuthenticated()) {
131
+ logger.error('❌ Gmail service not authenticated. Call authenticate() first.');
132
+ return [];
133
+ }
134
+ try {
135
+ // Add time filter to query if provided
136
+ let fullQuery = query;
137
+ if (time_filter && !query.includes('newer_than:')) {
138
+ fullQuery = `newer_than:${time_filter} ${query}`.trim();
139
+ }
140
+ logger.info(`📧 Fetching ${max_results} recent emails...`);
141
+ if (fullQuery) {
142
+ logger.debug(`🔍 Query: ${fullQuery}`);
143
+ }
144
+ // Get message list
145
+ const results = (await this.service.users.messages.list({
146
+ userId: 'me',
147
+ maxResults: max_results,
148
+ q: fullQuery,
149
+ }));
150
+ const messages = results.data.messages || [];
151
+ if (!messages.length) {
152
+ logger.info('📭 No messages found');
153
+ return [];
154
+ }
155
+ logger.info(`📨 Found ${messages.length} messages, fetching details...`);
156
+ // Get full message details
157
+ const emails = [];
158
+ for (let i = 0; i < messages.length; i++) {
159
+ const message = messages[i];
160
+ logger.debug(`📖 Reading email ${i + 1}/${messages.length}...`);
161
+ const fullMessage = (await this.service.users.messages.get({
162
+ userId: 'me',
163
+ id: message.id,
164
+ format: 'full',
165
+ }));
166
+ const emailData = this._parseEmail(fullMessage.data);
167
+ emails.push(emailData);
168
+ }
169
+ return emails;
170
+ }
171
+ catch (error) {
172
+ logger.error(`❌ Gmail API error: ${error.message || error}`);
173
+ return [];
174
+ }
175
+ }
176
+ /**
177
+ * Parse Gmail message into readable format
178
+ */
179
+ _parseEmail(message) {
180
+ const headers = message.payload?.headers || [];
181
+ const headerMap = {};
182
+ for (const header of headers) {
183
+ if (header.name && header.value) {
184
+ headerMap[header.name] = header.value;
185
+ }
186
+ }
187
+ return {
188
+ id: message.id || '',
189
+ thread_id: message.threadId || '',
190
+ subject: headerMap['Subject'] || '',
191
+ from: headerMap['From'] || '',
192
+ to: headerMap['To'] || '',
193
+ date: headerMap['Date'] || '',
194
+ timestamp: parseInt(message.internalDate || '0'),
195
+ body: this._extractBody(message.payload || {}),
196
+ raw_message: message,
197
+ };
198
+ }
199
+ /**
200
+ * Extract email body from payload
201
+ */
202
+ _extractBody(payload) {
203
+ let body = '';
204
+ if (payload.body?.data) {
205
+ // Simple email body
206
+ body = Buffer.from(payload.body.data, 'base64').toString('utf-8');
207
+ }
208
+ else if (payload.parts) {
209
+ // Multi-part email
210
+ for (const part of payload.parts) {
211
+ if (part.mimeType === 'text/plain' && part.body?.data) {
212
+ const partBody = Buffer.from(part.body.data, 'base64').toString('utf-8');
213
+ body += partBody;
214
+ }
215
+ else if (part.mimeType === 'text/html' && !body && part.body?.data) {
216
+ // Fallback to HTML if no plain text
217
+ body = Buffer.from(part.body.data, 'base64').toString('utf-8');
218
+ }
219
+ }
220
+ }
221
+ return body;
222
+ }
223
+ /**
224
+ * Send an email
225
+ */
226
+ async sendMessage(to, subject, body) {
227
+ if (!this.isAuthenticated()) {
228
+ logger.error('❌ Gmail service not authenticated.');
229
+ return null;
230
+ }
231
+ try {
232
+ const utf8Subject = `=?utf-8?B?${Buffer.from(subject).toString('base64')}?=`;
233
+ const messageParts = [
234
+ `To: ${to}`,
235
+ 'Content-Type: text/html; charset=utf-8',
236
+ 'MIME-Version: 1.0',
237
+ `Subject: ${utf8Subject}`,
238
+ '',
239
+ body,
240
+ ];
241
+ const message = messageParts.join('\n');
242
+ const encodedMessage = Buffer.from(message)
243
+ .toString('base64')
244
+ .replace(/\+/g, '-')
245
+ .replace(/\//g, '_')
246
+ .replace(/=+$/, '');
247
+ const res = await this.service.users.messages.send({
248
+ userId: 'me',
249
+ requestBody: {
250
+ raw: encodedMessage,
251
+ },
252
+ });
253
+ return res.data;
254
+ }
255
+ catch (error) {
256
+ logger.error(`❌ Failed to send email: ${error.message || error}`);
257
+ return null;
258
+ }
259
+ }
260
+ }
@@ -0,0 +1,28 @@
1
+ import type { BaseChatModel, ChatInvokeOptions } from '../base.js';
2
+ import { ChatInvokeCompletion } from '../views.js';
3
+ import { type Message } from '../messages.js';
4
+ export interface ChatAnthropicOptions {
5
+ model?: string;
6
+ apiKey?: string;
7
+ baseURL?: string;
8
+ maxTokens?: number;
9
+ temperature?: number | null;
10
+ topP?: number | null;
11
+ maxRetries?: number;
12
+ }
13
+ export declare class ChatAnthropic implements BaseChatModel {
14
+ model: string;
15
+ provider: string;
16
+ private client;
17
+ private maxTokens;
18
+ private temperature;
19
+ private topP;
20
+ constructor(options?: ChatAnthropicOptions);
21
+ get name(): string;
22
+ get model_name(): string;
23
+ private getUsage;
24
+ ainvoke(messages: Message[], output_format?: undefined, options?: ChatInvokeOptions): Promise<ChatInvokeCompletion<string>>;
25
+ ainvoke<T>(messages: Message[], output_format: {
26
+ parse: (input: string) => T;
27
+ } | undefined, options?: ChatInvokeOptions): Promise<ChatInvokeCompletion<T>>;
28
+ }
@@ -0,0 +1,126 @@
1
+ import Anthropic from '@anthropic-ai/sdk';
2
+ import { zodToJsonSchema } from 'zod-to-json-schema';
3
+ import { ChatInvokeCompletion } from '../views.js';
4
+ import { SystemMessage } from '../messages.js';
5
+ import { AnthropicMessageSerializer } from './serializer.js';
6
+ import { ModelProviderError, ModelRateLimitError } from '../exceptions.js';
7
+ export class ChatAnthropic {
8
+ model;
9
+ provider = 'anthropic';
10
+ client;
11
+ maxTokens;
12
+ temperature;
13
+ topP;
14
+ constructor(options = {}) {
15
+ const { model = 'claude-sonnet-4-20250514', apiKey, baseURL, maxTokens = 8192, temperature = null, topP = null, maxRetries = 10, } = options;
16
+ this.model = model;
17
+ this.maxTokens = maxTokens;
18
+ this.temperature = temperature;
19
+ this.topP = topP;
20
+ this.client = new Anthropic({
21
+ apiKey,
22
+ baseURL,
23
+ maxRetries,
24
+ });
25
+ }
26
+ get name() {
27
+ return this.model;
28
+ }
29
+ get model_name() {
30
+ return this.model;
31
+ }
32
+ getUsage(response) {
33
+ const cacheReadTokens = response.usage.cache_read_input_tokens ?? 0;
34
+ const cacheCreationTokens = response.usage.cache_creation_input_tokens ?? 0;
35
+ return {
36
+ prompt_tokens: response.usage.input_tokens + cacheReadTokens,
37
+ completion_tokens: response.usage.output_tokens,
38
+ total_tokens: response.usage.input_tokens + response.usage.output_tokens,
39
+ prompt_cached_tokens: cacheReadTokens || null,
40
+ prompt_cache_creation_tokens: cacheCreationTokens || null,
41
+ prompt_image_tokens: null,
42
+ };
43
+ }
44
+ async ainvoke(messages, output_format, options = {}) {
45
+ const serializer = new AnthropicMessageSerializer();
46
+ const [anthropicMessages] = serializer.serializeMessages(messages);
47
+ const systemMessage = messages.find((msg) => msg instanceof SystemMessage);
48
+ const system = systemMessage ? systemMessage.text : undefined;
49
+ let tools = undefined;
50
+ let toolChoice = undefined;
51
+ if (output_format && 'schema' in output_format && output_format.schema) {
52
+ // Assuming output_format is a Zod schema wrapper
53
+ try {
54
+ const jsonSchema = zodToJsonSchema(output_format, {
55
+ name: 'Response',
56
+ target: 'jsonSchema7',
57
+ });
58
+ tools = [
59
+ {
60
+ name: 'response',
61
+ description: 'The response to the user request',
62
+ input_schema: jsonSchema,
63
+ },
64
+ ];
65
+ toolChoice = { type: 'tool', name: 'response' };
66
+ }
67
+ catch (e) {
68
+ console.warn('Failed to convert output_format to JSON schema for Anthropic', e);
69
+ }
70
+ }
71
+ // Build model parameters
72
+ const modelParams = {};
73
+ if (this.temperature !== null) {
74
+ modelParams.temperature = this.temperature;
75
+ }
76
+ if (this.topP !== null) {
77
+ modelParams.top_p = this.topP;
78
+ }
79
+ // Add cache_control to tools if present
80
+ if (tools && tools.length > 0) {
81
+ tools = tools.map((tool, index) => {
82
+ if (index === tools.length - 1) {
83
+ return {
84
+ ...tool,
85
+ cache_control: { type: 'ephemeral' },
86
+ };
87
+ }
88
+ return tool;
89
+ });
90
+ }
91
+ try {
92
+ const response = await this.client.messages.create({
93
+ model: this.model,
94
+ max_tokens: this.maxTokens,
95
+ system: system,
96
+ messages: anthropicMessages,
97
+ tools: tools,
98
+ tool_choice: toolChoice,
99
+ ...modelParams,
100
+ }, options.signal ? { signal: options.signal } : undefined);
101
+ let completion = '';
102
+ // Handle tool use response
103
+ const toolUseBlock = response.content.find((block) => block.type === 'tool_use');
104
+ if (toolUseBlock && output_format) {
105
+ completion = output_format.parse(toolUseBlock.input);
106
+ }
107
+ else {
108
+ // Fallback to text content
109
+ const textBlock = response.content.find((block) => block.type === 'text');
110
+ completion = textBlock ? textBlock.text : '';
111
+ }
112
+ const usage = this.getUsage(response);
113
+ return new ChatInvokeCompletion(completion, usage);
114
+ }
115
+ catch (error) {
116
+ // Handle Anthropic-specific errors
117
+ if (error?.status === 429) {
118
+ throw new ModelRateLimitError(error?.message ?? 'Rate limit exceeded', 429, this.model);
119
+ }
120
+ if (error?.status >= 500) {
121
+ throw new ModelProviderError(error?.message ?? 'Server error', error.status, this.model);
122
+ }
123
+ throw new ModelProviderError(error?.message ?? String(error), error?.status ?? 500, this.model);
124
+ }
125
+ }
126
+ }
@@ -0,0 +1,2 @@
1
+ export * from './chat.js';
2
+ export * from './serializer.js';
@@ -0,0 +1,2 @@
1
+ export * from './chat.js';
2
+ export * from './serializer.js';
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Anthropic Message Serializer with Prompt Caching Support
3
+ *
4
+ * This serializer converts custom message types to Anthropic's MessageParam format
5
+ * and implements Anthropic's Prompt Caching feature to reduce costs by up to 90%.
6
+ *
7
+ * Caching Strategy:
8
+ * - Only the last message with cache=true will have cache_control enabled
9
+ * - Caching is most effective for system prompts and large conversation histories
10
+ * - Cache writes cost 25% more, but cache reads cost 90% less
11
+ *
12
+ * Example cost savings:
13
+ * - Without caching: 10,000 tokens @ $3/M = $0.030 per request
14
+ * - With caching (90% hit rate):
15
+ * - First request: 10,000 tokens @ $3.75/M (write) = $0.0375
16
+ * - Subsequent: 1,000 tokens @ $3/M + 9,000 tokens @ $0.30/M = $0.0057
17
+ * - Savings: 81% cost reduction
18
+ */
19
+ import type { MessageParam, TextBlockParam } from '@anthropic-ai/sdk/resources/messages.mjs';
20
+ import { type Message } from '../messages.js';
21
+ export declare class AnthropicMessageSerializer {
22
+ /**
23
+ * Serialize a list of messages, extracting any system message
24
+ *
25
+ * @param messages - List of messages to serialize
26
+ * @returns Tuple of [messages, system_message]
27
+ */
28
+ serializeMessages(messages: Message[]): [MessageParam[], (string | TextBlockParam[])?];
29
+ /**
30
+ * Serialize a single message
31
+ */
32
+ serializeMessage(message: Message): MessageParam;
33
+ /**
34
+ * Serialize cache control parameter
35
+ */
36
+ private _serializeCacheControl;
37
+ /**
38
+ * Serialize text content part with optional caching
39
+ */
40
+ private _serializeContentPartText;
41
+ /**
42
+ * Serialize image content part
43
+ */
44
+ private _serializeContentPartImage;
45
+ /**
46
+ * Serialize content (string or array) with optional caching
47
+ */
48
+ private _serializeContent;
49
+ /**
50
+ * Serialize content to string format (for system messages)
51
+ */
52
+ private _serializeContentToStr;
53
+ /**
54
+ * Check if URL is a base64 encoded image
55
+ */
56
+ private _isBase64Image;
57
+ /**
58
+ * Parse base64 data URL to extract media type and data
59
+ */
60
+ private _parseBase64Url;
61
+ /**
62
+ * Clean cache settings so only the last cache=true message remains cached
63
+ *
64
+ * Because of how Claude caching works, only the last cache message matters.
65
+ * This method automatically removes cache=True from all messages except the last one.
66
+ */
67
+ private _cleanCacheMessages;
68
+ }