@minded-ai/mindedjs 2.0.25-beta.6 → 2.0.25
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/browserTask/executeBrowserTask.d.ts.map +1 -1
- package/dist/browserTask/executeBrowserTask.js +128 -11
- package/dist/browserTask/executeBrowserTask.js.map +1 -1
- package/dist/browserTask/executeBrowserTask.py +293 -3
- package/dist/browserTask/localBrowserTask.d.ts +30 -3
- package/dist/browserTask/localBrowserTask.d.ts.map +1 -1
- package/dist/browserTask/localBrowserTask.js +190 -128
- package/dist/browserTask/localBrowserTask.js.map +1 -1
- package/dist/browserTask/types.d.ts +9 -0
- package/dist/browserTask/types.d.ts.map +1 -1
- package/dist/cli/index.js +0 -0
- package/dist/cli/localOperatorSetup.d.ts.map +1 -1
- package/dist/cli/localOperatorSetup.js +0 -5
- package/dist/cli/localOperatorSetup.js.map +1 -1
- package/dist/nodes/addAppToolNode.d.ts.map +1 -1
- package/dist/nodes/addAppToolNode.js +21 -1
- package/dist/nodes/addAppToolNode.js.map +1 -1
- package/dist/nodes/addBrowserTaskRunNode.d.ts.map +1 -1
- package/dist/nodes/addBrowserTaskRunNode.js +36 -0
- package/dist/nodes/addBrowserTaskRunNode.js.map +1 -1
- package/package.json +4 -2
- package/src/browserTask/CookieStore.ts +49 -0
- package/src/browserTask/executeBrowserTask.py +293 -3
- package/src/browserTask/executeBrowserTask.ts +138 -17
- package/src/browserTask/localBrowserTask.ts +219 -146
- package/src/browserTask/types.ts +11 -1
- package/src/cli/localOperatorSetup.ts +0 -5
- package/src/nodes/addAppToolNode.ts +22 -1
- package/src/nodes/addBrowserTaskRunNode.ts +44 -0
- package/dist/browserTask/cdp.d.ts +0 -23
- package/dist/browserTask/cdp.d.ts.map +0 -1
- package/dist/browserTask/cdp.js +0 -162
- package/dist/browserTask/cdp.js.map +0 -1
- package/dist/browserTask/cookieStore.py +0 -108
- package/dist/guidelines/guidelinesManager.d.ts +0 -37
- package/dist/guidelines/guidelinesManager.d.ts.map +0 -1
- package/dist/guidelines/guidelinesManager.js +0 -172
- package/dist/guidelines/guidelinesManager.js.map +0 -1
- package/dist/internalTools/retell.d.ts +0 -12
- package/dist/internalTools/retell.d.ts.map +0 -1
- package/dist/internalTools/retell.js +0 -54
- package/dist/internalTools/retell.js.map +0 -1
- package/dist/internalTools/sendPlaceholderMessage.d.ts +0 -14
- package/dist/internalTools/sendPlaceholderMessage.d.ts.map +0 -1
- package/dist/internalTools/sendPlaceholderMessage.js +0 -61
- package/dist/internalTools/sendPlaceholderMessage.js.map +0 -1
- package/dist/platform/mindedChatOpenAI.d.ts +0 -5
- package/dist/platform/mindedChatOpenAI.d.ts.map +0 -1
- package/dist/platform/mindedChatOpenAI.js +0 -23
- package/dist/platform/mindedChatOpenAI.js.map +0 -1
- package/dist/utils/extractStateMemoryResponse.d.ts +0 -5
- package/dist/utils/extractStateMemoryResponse.d.ts.map +0 -1
- package/dist/utils/extractStateMemoryResponse.js +0 -91
- package/dist/utils/extractStateMemoryResponse.js.map +0 -1
- package/dist/utils/extractToolMemoryResponse.d.ts +0 -4
- package/dist/utils/extractToolMemoryResponse.d.ts.map +0 -1
- package/dist/utils/extractToolMemoryResponse.js +0 -16
- package/dist/utils/extractToolMemoryResponse.js.map +0 -1
|
@@ -41,6 +41,40 @@ const addBrowserTaskRunNode = async ({ graph, browserTaskNode, attachedToNodeNam
|
|
|
41
41
|
// We compile the env variables to avoid having to pass them to the platform in the tool input
|
|
42
42
|
const promptCompiledWithEnv = (0, compilePrompt_1.compilePrompt)(prompt, { env: process.env });
|
|
43
43
|
const { browserTaskMode } = (0, config_1.getConfig)();
|
|
44
|
+
// Build screenshot config conditionally based on environment variables
|
|
45
|
+
const screenshotConfig = (() => {
|
|
46
|
+
// Check for screenshots and logs independently
|
|
47
|
+
const enableScreenshotCaptureToS3 = process.env.ENABLE_BROWSER_SCREENSHOTS === 'true' || !!process.env.SCREENSHOT_S3_BUCKET;
|
|
48
|
+
const enableLogsCaptureToS3 = process.env.ENABLE_BROWSER_LOGS === 'true';
|
|
49
|
+
// If neither is enabled, return undefined
|
|
50
|
+
if (!enableScreenshotCaptureToS3 && !enableLogsCaptureToS3) {
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
// Build config with separate flags
|
|
54
|
+
const config = {
|
|
55
|
+
enableScreenshotCaptureToS3,
|
|
56
|
+
enableLogsCaptureToS3,
|
|
57
|
+
};
|
|
58
|
+
// Add optional S3 configuration if provided
|
|
59
|
+
if (process.env.SCREENSHOT_S3_BUCKET) {
|
|
60
|
+
config.s3_bucket = process.env.SCREENSHOT_S3_BUCKET;
|
|
61
|
+
}
|
|
62
|
+
if (process.env.SCREENSHOT_S3_PREFIX) {
|
|
63
|
+
config.s3_prefix = process.env.SCREENSHOT_S3_PREFIX;
|
|
64
|
+
}
|
|
65
|
+
if (process.env.AWS_REGION) {
|
|
66
|
+
config.aws_region = process.env.AWS_REGION;
|
|
67
|
+
}
|
|
68
|
+
logger_1.logger.debug({
|
|
69
|
+
msg: 'Browser task capture configuration',
|
|
70
|
+
screenshots: enableScreenshotCaptureToS3,
|
|
71
|
+
logs: enableLogsCaptureToS3,
|
|
72
|
+
bucket: config.s3_bucket || 'default',
|
|
73
|
+
prefix: config.s3_prefix || 'default',
|
|
74
|
+
region: config.aws_region || 'default',
|
|
75
|
+
});
|
|
76
|
+
return config;
|
|
77
|
+
})();
|
|
44
78
|
// Invoke browser task via socket
|
|
45
79
|
const result = await (0, executeBrowserTask_1.invokeBrowserTask)({
|
|
46
80
|
sessionId,
|
|
@@ -51,6 +85,8 @@ const addBrowserTaskRunNode = async ({ graph, browserTaskNode, attachedToNodeNam
|
|
|
51
85
|
browserTaskMode,
|
|
52
86
|
toolSchemas,
|
|
53
87
|
outputSchema,
|
|
88
|
+
screenshotConfig,
|
|
89
|
+
toolCallId: toolCall.id,
|
|
54
90
|
});
|
|
55
91
|
logger_1.logger.debug({
|
|
56
92
|
msg: 'Browser task completed',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"addBrowserTaskRunNode.js","sourceRoot":"","sources":["../../src/nodes/addBrowserTaskRunNode.ts"],"names":[],"mappings":";;;AAEA,sDAAsF;AACtF,uDAAkE;AAElE,4CAAyC;AACzC,8CAAqD;AAErD,+BAAoC;AACpC,0EAAsE;
|
|
1
|
+
{"version":3,"file":"addBrowserTaskRunNode.js","sourceRoot":"","sources":["../../src/nodes/addBrowserTaskRunNode.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@minded-ai/mindedjs",
|
|
3
|
-
"version": "2.0.25
|
|
3
|
+
"version": "2.0.25",
|
|
4
4
|
"description": "MindedJS is a TypeScript library for building agents.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -53,8 +53,10 @@
|
|
|
53
53
|
"dependencies": {
|
|
54
54
|
"@langchain/core": "^0.3.55",
|
|
55
55
|
"@langchain/langgraph": "^0.2.72",
|
|
56
|
+
"@types/chrome-remote-interface": "^0.31.14",
|
|
56
57
|
"@types/js-yaml": "^4.0.9",
|
|
57
58
|
"axios": "^1.9.0",
|
|
59
|
+
"chrome-remote-interface": "^0.33.3",
|
|
58
60
|
"ejs": "^3.1.10",
|
|
59
61
|
"flatted": "^3.3.3",
|
|
60
62
|
"js-yaml": "^4.1.0",
|
|
@@ -70,4 +72,4 @@
|
|
|
70
72
|
"peerDependencies": {
|
|
71
73
|
"playwright": "^1.55.0"
|
|
72
74
|
}
|
|
73
|
-
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import { logger } from '../utils/logger';
|
|
4
|
+
|
|
5
|
+
export interface StoredCookie {
|
|
6
|
+
name: string;
|
|
7
|
+
value: string;
|
|
8
|
+
domain?: string;
|
|
9
|
+
path?: string;
|
|
10
|
+
expires?: number; // seconds since epoch
|
|
11
|
+
secure?: boolean;
|
|
12
|
+
httpOnly?: boolean;
|
|
13
|
+
sameSite?: 'Strict' | 'Lax' | 'None';
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export class CookieStore {
|
|
17
|
+
constructor(private baseDir: string) { }
|
|
18
|
+
|
|
19
|
+
private fileFor() {
|
|
20
|
+
return path.join(this.baseDir, `last-session`);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async save(cookies: StoredCookie[]) {
|
|
24
|
+
fs.mkdirSync(this.baseDir, { recursive: true });
|
|
25
|
+
const file = this.fileFor();
|
|
26
|
+
|
|
27
|
+
// filter out expired cookies (avoid restoring junk)
|
|
28
|
+
const now = Math.floor(Date.now() / 1000);
|
|
29
|
+
const toPersist = cookies.filter((c) => !c.expires || c.expires > now);
|
|
30
|
+
|
|
31
|
+
// atomic write
|
|
32
|
+
try {
|
|
33
|
+
fs.writeFileSync(file, JSON.stringify({ version: 1, cookies: toPersist }, null, 2));
|
|
34
|
+
} catch (error) {
|
|
35
|
+
logger.error({ message: 'Failed to save cookies', error });
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async load(): Promise<StoredCookie[] | null> {
|
|
40
|
+
const file = this.fileFor();
|
|
41
|
+
if (!(fs.existsSync(file))) return null;
|
|
42
|
+
|
|
43
|
+
const raw = fs.readFileSync(file, 'utf-8');
|
|
44
|
+
const parsed = JSON.parse(raw);
|
|
45
|
+
if (!parsed || !Array.isArray(parsed.cookies)) return null;
|
|
46
|
+
return parsed.cookies as StoredCookie[];
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Browser Task Executor with Screenshot Capture Support
|
|
3
|
+
|
|
4
|
+
This script runs browser automation tasks using browser-use and can capture
|
|
5
|
+
screenshots at the end of each step, uploading them to S3 when configured.
|
|
6
|
+
"""
|
|
7
|
+
|
|
1
8
|
import asyncio
|
|
2
9
|
import json
|
|
3
10
|
from typing import List, Optional, Dict, Any, TypedDict
|
|
@@ -7,10 +14,18 @@ from browser_use.browser import BrowserProfile, BrowserSession
|
|
|
7
14
|
from browser_use.llm import ChatOpenAI
|
|
8
15
|
import os
|
|
9
16
|
import sys
|
|
17
|
+
import logging
|
|
18
|
+
import base64
|
|
19
|
+
from datetime import datetime
|
|
20
|
+
from pathlib import Path
|
|
10
21
|
from dotenv import load_dotenv
|
|
11
22
|
|
|
12
23
|
load_dotenv()
|
|
13
24
|
|
|
25
|
+
# Configure logging
|
|
26
|
+
logging.basicConfig(level=logging.INFO)
|
|
27
|
+
logger = logging.getLogger(__name__)
|
|
28
|
+
|
|
14
29
|
class OutputSchemaItemRequired(TypedDict):
|
|
15
30
|
name: str
|
|
16
31
|
type: str # 'string' | 'number'
|
|
@@ -20,6 +35,227 @@ class OutputSchemaItem(OutputSchemaItemRequired, total=False):
|
|
|
20
35
|
required: bool
|
|
21
36
|
|
|
22
37
|
|
|
38
|
+
class ScreenshotCapture:
|
|
39
|
+
"""Simple screenshot capture class that takes screenshots at step end and uploads to S3"""
|
|
40
|
+
|
|
41
|
+
def __init__(self, config: Optional[Dict[str, Any]] = None):
|
|
42
|
+
self.config = config or {}
|
|
43
|
+
|
|
44
|
+
# Import boto3 only when screenshot capture is needed
|
|
45
|
+
try:
|
|
46
|
+
import boto3
|
|
47
|
+
from botocore.exceptions import ClientError
|
|
48
|
+
self.boto3 = boto3
|
|
49
|
+
self.ClientError = ClientError
|
|
50
|
+
except ImportError:
|
|
51
|
+
raise ImportError(
|
|
52
|
+
"boto3 is required for screenshot capture. "
|
|
53
|
+
"Please install it with: uv pip install boto3 "
|
|
54
|
+
"or run: npx minded setup-local-operator"
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
# S3 configuration with defaults
|
|
58
|
+
self.s3_bucket = self.config.get('s3_bucket', os.getenv('SCREENSHOT_S3_BUCKET', 'bucket-not-set'))
|
|
59
|
+
self.s3_prefix = self.config.get('s3_prefix', os.getenv('SCREENSHOT_S3_PREFIX', 'browser-use-runs-screenshots/'))
|
|
60
|
+
|
|
61
|
+
# AWS region configuration
|
|
62
|
+
self.aws_region = self.config.get('aws_region', os.getenv('AWS_REGION', 'us-east-1'))
|
|
63
|
+
|
|
64
|
+
# Ensure prefix ends with slash
|
|
65
|
+
if not self.s3_prefix.endswith('/'):
|
|
66
|
+
self.s3_prefix += '/'
|
|
67
|
+
|
|
68
|
+
# Get session ID from config or generate one
|
|
69
|
+
self.session_id = self.config.get('session_id', datetime.now().strftime("%Y%m%d_%H%M%S_%f")[:-3])
|
|
70
|
+
|
|
71
|
+
# Initialize S3 client with region
|
|
72
|
+
self.s3_client = self.boto3.client('s3', region_name=self.aws_region)
|
|
73
|
+
|
|
74
|
+
# Track step counter
|
|
75
|
+
self.step_counter = 0
|
|
76
|
+
|
|
77
|
+
logger.info(f"📸 Screenshot capture ENABLED - will capture at end of each step")
|
|
78
|
+
logger.info(f" S3 destination: s3://{self.s3_bucket}/{self.s3_prefix}")
|
|
79
|
+
logger.info(f" Session ID: {self.session_id}")
|
|
80
|
+
logger.info(f" AWS Region: {self.aws_region}")
|
|
81
|
+
|
|
82
|
+
async def capture_screenshot(self, agent: Any) -> None:
|
|
83
|
+
"""Capture screenshot at step end and upload to S3"""
|
|
84
|
+
try:
|
|
85
|
+
# Get current page
|
|
86
|
+
page = await agent.browser_session.get_current_page()
|
|
87
|
+
|
|
88
|
+
# Get current URL for logging (browser-use might use method instead of property)
|
|
89
|
+
try:
|
|
90
|
+
current_url = page.url if hasattr(page, 'url') else page.url()
|
|
91
|
+
except:
|
|
92
|
+
current_url = "unknown"
|
|
93
|
+
|
|
94
|
+
# Update step counter
|
|
95
|
+
step_number = self.step_counter
|
|
96
|
+
self.step_counter += 1
|
|
97
|
+
|
|
98
|
+
logger.info(f"📸 Capturing screenshot #{step_number} at: {current_url}")
|
|
99
|
+
|
|
100
|
+
# Take screenshot - browser-use takes viewport by default
|
|
101
|
+
screenshot_data = await page.screenshot()
|
|
102
|
+
|
|
103
|
+
# Ensure we have bytes - browser-use might return base64 string or bytes
|
|
104
|
+
if isinstance(screenshot_data, bytes):
|
|
105
|
+
screenshot_bytes = screenshot_data
|
|
106
|
+
elif isinstance(screenshot_data, str):
|
|
107
|
+
# If it's base64 encoded string, decode it
|
|
108
|
+
screenshot_bytes = base64.b64decode(screenshot_data)
|
|
109
|
+
else:
|
|
110
|
+
# Try to get bytes from whatever format it is
|
|
111
|
+
screenshot_bytes = bytes(screenshot_data)
|
|
112
|
+
|
|
113
|
+
# Generate filename with metadata
|
|
114
|
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S_%f")[:-3]
|
|
115
|
+
filename = f"{self.session_id}/step_{step_number:04d}_{timestamp}.png"
|
|
116
|
+
|
|
117
|
+
# Upload to S3
|
|
118
|
+
s3_key = f"{self.s3_prefix}{filename}"
|
|
119
|
+
|
|
120
|
+
try:
|
|
121
|
+
# Verify we have valid data before uploading
|
|
122
|
+
if not screenshot_bytes or len(screenshot_bytes) == 0:
|
|
123
|
+
logger.error(f"❌ Screenshot bytes are empty!")
|
|
124
|
+
return
|
|
125
|
+
|
|
126
|
+
# Upload to S3 with KMS encryption
|
|
127
|
+
self.s3_client.put_object(
|
|
128
|
+
Bucket=self.s3_bucket,
|
|
129
|
+
Key=s3_key,
|
|
130
|
+
Body=screenshot_bytes,
|
|
131
|
+
ContentType='image/png',
|
|
132
|
+
ServerSideEncryption='aws:kms',
|
|
133
|
+
Tagging='retention=30d'
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
s3_url = f"s3://{self.s3_bucket}/{s3_key}"
|
|
137
|
+
logger.info(f"✅ Screenshot uploaded: {s3_url} (step {step_number}, size: {len(screenshot_bytes)} bytes)")
|
|
138
|
+
|
|
139
|
+
except self.ClientError as e:
|
|
140
|
+
logger.error(f"❌ Failed to upload screenshot #{step_number} to S3")
|
|
141
|
+
logger.error(f" Error: {str(e)}")
|
|
142
|
+
logger.error(f" Bucket: {self.s3_bucket}, Key: {s3_key}")
|
|
143
|
+
# Continue execution even if screenshot upload fails
|
|
144
|
+
|
|
145
|
+
except Exception as e:
|
|
146
|
+
step_num = getattr(self, 'step_counter', 'unknown')
|
|
147
|
+
logger.error(f"❌ Error capturing screenshot #{step_num}: {str(e)}")
|
|
148
|
+
# Don't raise - continue execution
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
class LogsCapture:
|
|
152
|
+
"""Logs capture class that saves browser-use history logs to S3"""
|
|
153
|
+
|
|
154
|
+
def __init__(self, config: Optional[Dict[str, Any]] = None):
|
|
155
|
+
self.config = config or {}
|
|
156
|
+
|
|
157
|
+
# Import boto3 only when logs capture is needed
|
|
158
|
+
try:
|
|
159
|
+
import boto3
|
|
160
|
+
from botocore.exceptions import ClientError
|
|
161
|
+
self.boto3 = boto3
|
|
162
|
+
self.ClientError = ClientError
|
|
163
|
+
except ImportError:
|
|
164
|
+
raise ImportError(
|
|
165
|
+
"boto3 is required for logs capture. "
|
|
166
|
+
"Please install it with: uv pip install boto3 "
|
|
167
|
+
"or run: npx minded setup-local-operator"
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
# S3 configuration with defaults
|
|
171
|
+
self.s3_bucket = self.config.get('s3_bucket', os.getenv('SCREENSHOT_S3_BUCKET', 'bucket-not-set'))
|
|
172
|
+
self.s3_prefix = self.config.get('s3_prefix', os.getenv('SCREENSHOT_S3_PREFIX', 'browser-use-runs-logs/'))
|
|
173
|
+
|
|
174
|
+
# AWS region configuration
|
|
175
|
+
self.aws_region = self.config.get('aws_region', os.getenv('AWS_REGION', 'us-east-1'))
|
|
176
|
+
|
|
177
|
+
# Ensure prefix ends with slash
|
|
178
|
+
if not self.s3_prefix.endswith('/'):
|
|
179
|
+
self.s3_prefix += '/'
|
|
180
|
+
|
|
181
|
+
# Get session ID and tool call ID from config
|
|
182
|
+
self.session_id = self.config.get('session_id', datetime.now().strftime("%Y%m%d_%H%M%S_%f")[:-3])
|
|
183
|
+
self.tool_call_id = self.config.get('tool_call_id', 'unknown')
|
|
184
|
+
|
|
185
|
+
# Initialize S3 client with region
|
|
186
|
+
self.s3_client = self.boto3.client('s3', region_name=self.aws_region)
|
|
187
|
+
|
|
188
|
+
# Track accumulated logs
|
|
189
|
+
self.log_entries: List[str] = []
|
|
190
|
+
self.step_counter = 0
|
|
191
|
+
|
|
192
|
+
logger.info(f"📝 Logs capture ENABLED - will save history logs periodically")
|
|
193
|
+
logger.info(f" S3 destination: s3://{self.s3_bucket}/{self.s3_prefix}")
|
|
194
|
+
logger.info(f" Session ID: {self.session_id}")
|
|
195
|
+
logger.info(f" Tool Call ID: {self.tool_call_id}")
|
|
196
|
+
logger.info(f" AWS Region: {self.aws_region}")
|
|
197
|
+
|
|
198
|
+
async def capture_logs(self, agent: Any) -> None:
|
|
199
|
+
"""Capture logs at step end and upload to S3"""
|
|
200
|
+
try:
|
|
201
|
+
step_number = self.step_counter
|
|
202
|
+
self.step_counter += 1
|
|
203
|
+
|
|
204
|
+
# Get history object from agent
|
|
205
|
+
history = agent.history
|
|
206
|
+
|
|
207
|
+
# Extract model outputs if available
|
|
208
|
+
try:
|
|
209
|
+
model_outputs = history.model_outputs() if hasattr(history, 'model_outputs') else []
|
|
210
|
+
for model_output in model_outputs:
|
|
211
|
+
print(f"model_output")
|
|
212
|
+
if hasattr(model_output, 'current_state'):
|
|
213
|
+
print(f"model_output.current_state")
|
|
214
|
+
if model_output.current_state.thinking:
|
|
215
|
+
self.log_entries.append('💡 Thinking: ' + model_output.current_state.thinking)
|
|
216
|
+
if model_output.current_state.memory:
|
|
217
|
+
self.log_entries.append('🧠 Memory: ' + model_output.current_state.memory)
|
|
218
|
+
if model_output.current_state.next_goal:
|
|
219
|
+
self.log_entries.append('🎯 Next goal: ' + model_output.current_state.next_goal)
|
|
220
|
+
print("Finished adding log entries")
|
|
221
|
+
except Exception as e:
|
|
222
|
+
logger.warning(f"Could not extract model outputs: {e}")
|
|
223
|
+
|
|
224
|
+
print(f"self.log_entries count: {len(self.log_entries)}")
|
|
225
|
+
|
|
226
|
+
# Upload to S3
|
|
227
|
+
s3_key = f"{self.s3_prefix}{self.session_id}/{self.tool_call_id}/operator.log"
|
|
228
|
+
|
|
229
|
+
try:
|
|
230
|
+
# Convert logs to JSON string
|
|
231
|
+
logs_json = json.dumps(self.log_entries, indent=2, default=str)
|
|
232
|
+
logs_bytes = logs_json.encode('utf-8')
|
|
233
|
+
|
|
234
|
+
# Upload to S3 with KMS encryption
|
|
235
|
+
self.s3_client.put_object(
|
|
236
|
+
Bucket=self.s3_bucket,
|
|
237
|
+
Key=s3_key,
|
|
238
|
+
Body=logs_bytes,
|
|
239
|
+
ContentType='application/json',
|
|
240
|
+
ServerSideEncryption='aws:kms',
|
|
241
|
+
Tagging='retention=30d'
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
s3_url = f"s3://{self.s3_bucket}/{s3_key}"
|
|
245
|
+
logger.info(f"✅ Logs uploaded: {s3_url} (step {step_number}, size: {len(logs_bytes)} bytes, total entries: {len(self.log_entries)})")
|
|
246
|
+
|
|
247
|
+
except self.ClientError as e:
|
|
248
|
+
logger.error(f"❌ Failed to upload logs for step #{step_number} to S3")
|
|
249
|
+
logger.error(f" Error: {str(e)}")
|
|
250
|
+
logger.error(f" Bucket: {self.s3_bucket}, Key: {s3_key}")
|
|
251
|
+
# Continue execution even if logs upload fails
|
|
252
|
+
|
|
253
|
+
except Exception as e:
|
|
254
|
+
step_num = getattr(self, 'step_counter', 'unknown')
|
|
255
|
+
logger.error(f"❌ Error capturing logs #{step_num}: {str(e)}")
|
|
256
|
+
# Don't raise - continue execution
|
|
257
|
+
|
|
258
|
+
|
|
23
259
|
def create_pydantic_model_from_schema(output_schema: Optional[List[Dict[str, Any]]]) -> Optional[type[BaseModel]]:
|
|
24
260
|
if not output_schema:
|
|
25
261
|
return None
|
|
@@ -43,7 +279,8 @@ def create_pydantic_model_from_schema(output_schema: Optional[List[Dict[str, Any
|
|
|
43
279
|
return None
|
|
44
280
|
|
|
45
281
|
|
|
46
|
-
async def main(session_id: str, cdp_url: str, task: str, output_schema_json: Optional[str] = None,
|
|
282
|
+
async def main(session_id: str, cdp_url: str, task: str, output_schema_json: Optional[str] = None,
|
|
283
|
+
otp_secret: Optional[str] = None, screenshot_config: Optional[Dict[str, Any]] = None, folder_path: str = None, tool_call_id: Optional[str] = None):
|
|
47
284
|
llm = ChatOpenAI(
|
|
48
285
|
model="gpt-4.1",
|
|
49
286
|
api_key=os.getenv("OPENAI_API_KEY"),
|
|
@@ -59,6 +296,53 @@ async def main(session_id: str, cdp_url: str, task: str, output_schema_json: Opt
|
|
|
59
296
|
browser_profile=BrowserProfile(downloads_path=folder_path),
|
|
60
297
|
cdp_url=cdp_url
|
|
61
298
|
)
|
|
299
|
+
|
|
300
|
+
# Initialize screenshot capture if enabled
|
|
301
|
+
screenshot_capture = None
|
|
302
|
+
logs_capture = None
|
|
303
|
+
on_step_end_hook = None
|
|
304
|
+
|
|
305
|
+
# Check if screenshots are enabled
|
|
306
|
+
enable_screenshot_capture_to_s3 = screenshot_config and screenshot_config.get('enableScreenshotCaptureToS3', False)
|
|
307
|
+
|
|
308
|
+
if enable_screenshot_capture_to_s3:
|
|
309
|
+
logger.info("-" * 50)
|
|
310
|
+
logger.info("🎯 Initializing screenshot capture for browser task")
|
|
311
|
+
logger.info(f" Session: {session_id}")
|
|
312
|
+
logger.info("-" * 50)
|
|
313
|
+
|
|
314
|
+
# Add session_id to config
|
|
315
|
+
screenshot_config['session_id'] = session_id
|
|
316
|
+
screenshot_capture = ScreenshotCapture(screenshot_config)
|
|
317
|
+
else:
|
|
318
|
+
logger.info("📷 Screenshot capture is DISABLED for this browser task")
|
|
319
|
+
|
|
320
|
+
# Initialize logs capture if enabled (independent from screenshots)
|
|
321
|
+
enable_logs_capture_to_s3 = screenshot_config and screenshot_config.get('enableLogsCaptureToS3', False)
|
|
322
|
+
|
|
323
|
+
if enable_logs_capture_to_s3:
|
|
324
|
+
logger.info("-" * 50)
|
|
325
|
+
logger.info("🎯 Initializing logs capture for browser task")
|
|
326
|
+
logger.info(f" Session: {session_id}")
|
|
327
|
+
logger.info(f" Tool Call ID: {tool_call_id}")
|
|
328
|
+
logger.info("-" * 50)
|
|
329
|
+
|
|
330
|
+
# Add session_id and tool_call_id to config
|
|
331
|
+
screenshot_config['session_id'] = session_id
|
|
332
|
+
screenshot_config['tool_call_id'] = tool_call_id
|
|
333
|
+
logs_capture = LogsCapture(screenshot_config)
|
|
334
|
+
else:
|
|
335
|
+
logger.info("📝 Logs capture is DISABLED for this browser task")
|
|
336
|
+
|
|
337
|
+
# Create composite hook function for step end
|
|
338
|
+
if screenshot_capture or logs_capture:
|
|
339
|
+
async def on_step_end(agent: Any) -> None:
|
|
340
|
+
if screenshot_capture:
|
|
341
|
+
await screenshot_capture.capture_screenshot(agent)
|
|
342
|
+
if logs_capture:
|
|
343
|
+
await logs_capture.capture_logs(agent)
|
|
344
|
+
|
|
345
|
+
on_step_end_hook = on_step_end
|
|
62
346
|
|
|
63
347
|
output_schema = None
|
|
64
348
|
if output_schema_json:
|
|
@@ -98,7 +382,11 @@ async def main(session_id: str, cdp_url: str, task: str, output_schema_json: Opt
|
|
|
98
382
|
available_file_paths=available_files,
|
|
99
383
|
browser_session=browser_session,
|
|
100
384
|
)
|
|
101
|
-
|
|
385
|
+
|
|
386
|
+
# Run agent with screenshot hook if configured
|
|
387
|
+
history = await agent.run(
|
|
388
|
+
on_step_end=on_step_end_hook
|
|
389
|
+
)
|
|
102
390
|
final_result = history.final_result()
|
|
103
391
|
|
|
104
392
|
# Parse result with output model if provided
|
|
@@ -142,12 +430,14 @@ if __name__ == '__main__':
|
|
|
142
430
|
task = payload.get('task')
|
|
143
431
|
output_schema = payload.get('outputSchema')
|
|
144
432
|
otp_secret = payload.get('otpSecret')
|
|
433
|
+
screenshot_config = payload.get('screenshotConfig')
|
|
145
434
|
folder_path = payload.get('folderPath')
|
|
435
|
+
tool_call_id = payload.get('toolCallId')
|
|
146
436
|
|
|
147
437
|
if not session_id or not cdp_url or not task or not folder_path:
|
|
148
438
|
raise SystemExit("Missing required fields in JSON payload: sessionId, cdpUrl, task, folderPath")
|
|
149
439
|
|
|
150
440
|
output_schema_json = json.dumps(output_schema) if output_schema is not None else None
|
|
151
|
-
asyncio.run(main(session_id, cdp_url, task, output_schema_json, otp_secret, folder_path))
|
|
441
|
+
asyncio.run(main(session_id, cdp_url, task, output_schema_json, otp_secret, screenshot_config, folder_path, tool_call_id))
|
|
152
442
|
else:
|
|
153
443
|
raise SystemExit("Usage: uv run executeBrowserTask.py; send a JSON payload via stdin")
|