vibesurf 0.1.10__py3-none-any.whl → 0.1.12__py3-none-any.whl
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.
Potentially problematic release.
This version of vibesurf might be problematic. Click here for more details.
- vibe_surf/_version.py +2 -2
- vibe_surf/agents/browser_use_agent.py +68 -45
- vibe_surf/agents/prompts/report_writer_prompt.py +73 -0
- vibe_surf/agents/prompts/vibe_surf_prompt.py +85 -172
- vibe_surf/agents/report_writer_agent.py +380 -226
- vibe_surf/agents/vibe_surf_agent.py +880 -825
- vibe_surf/agents/views.py +130 -0
- vibe_surf/backend/api/activity.py +3 -1
- vibe_surf/backend/api/browser.py +9 -5
- vibe_surf/backend/api/config.py +8 -5
- vibe_surf/backend/api/files.py +59 -50
- vibe_surf/backend/api/models.py +2 -2
- vibe_surf/backend/api/task.py +46 -13
- vibe_surf/backend/database/manager.py +24 -18
- vibe_surf/backend/database/queries.py +199 -192
- vibe_surf/backend/database/schemas.py +1 -1
- vibe_surf/backend/main.py +4 -2
- vibe_surf/backend/shared_state.py +28 -35
- vibe_surf/backend/utils/encryption.py +3 -1
- vibe_surf/backend/utils/llm_factory.py +41 -36
- vibe_surf/browser/agent_browser_session.py +0 -4
- vibe_surf/browser/browser_manager.py +14 -8
- vibe_surf/browser/utils.py +5 -3
- vibe_surf/browser/watchdogs/dom_watchdog.py +0 -45
- vibe_surf/chrome_extension/background.js +4 -0
- vibe_surf/chrome_extension/scripts/api-client.js +13 -0
- vibe_surf/chrome_extension/scripts/file-manager.js +27 -71
- vibe_surf/chrome_extension/scripts/session-manager.js +21 -3
- vibe_surf/chrome_extension/scripts/ui-manager.js +831 -48
- vibe_surf/chrome_extension/sidepanel.html +21 -4
- vibe_surf/chrome_extension/styles/activity.css +365 -5
- vibe_surf/chrome_extension/styles/input.css +139 -0
- vibe_surf/cli.py +5 -22
- vibe_surf/common.py +35 -0
- vibe_surf/llm/openai_compatible.py +217 -99
- vibe_surf/logger.py +99 -0
- vibe_surf/{controller/vibesurf_tools.py → tools/browser_use_tools.py} +233 -219
- vibe_surf/tools/file_system.py +437 -0
- vibe_surf/{controller → tools}/mcp_client.py +4 -3
- vibe_surf/tools/report_writer_tools.py +21 -0
- vibe_surf/tools/vibesurf_tools.py +657 -0
- vibe_surf/tools/views.py +120 -0
- {vibesurf-0.1.10.dist-info → vibesurf-0.1.12.dist-info}/METADATA +6 -2
- {vibesurf-0.1.10.dist-info → vibesurf-0.1.12.dist-info}/RECORD +49 -43
- vibe_surf/controller/file_system.py +0 -53
- vibe_surf/controller/views.py +0 -37
- /vibe_surf/{controller → tools}/__init__.py +0 -0
- {vibesurf-0.1.10.dist-info → vibesurf-0.1.12.dist-info}/WHEEL +0 -0
- {vibesurf-0.1.10.dist-info → vibesurf-0.1.12.dist-info}/entry_points.txt +0 -0
- {vibesurf-0.1.10.dist-info → vibesurf-0.1.12.dist-info}/licenses/LICENSE +0 -0
- {vibesurf-0.1.10.dist-info → vibesurf-0.1.12.dist-info}/top_level.txt +0 -0
|
@@ -58,7 +58,7 @@ class McpServerConfig(BaseModel):
|
|
|
58
58
|
mcpServers: Dict[str, McpServerParams] = Field(default_factory=dict)
|
|
59
59
|
|
|
60
60
|
class ControllerConfiguration(BaseModel):
|
|
61
|
-
"""Schema for Task.mcp_server_config JSON field (legacy
|
|
61
|
+
"""Schema for Task.mcp_server_config JSON field (legacy tools config)"""
|
|
62
62
|
|
|
63
63
|
# Action control
|
|
64
64
|
exclude_actions: List[str] = Field(default_factory=list)
|
vibe_surf/backend/main.py
CHANGED
|
@@ -25,8 +25,10 @@ from .api.browser import router as browser_router
|
|
|
25
25
|
from . import shared_state
|
|
26
26
|
|
|
27
27
|
# Configure logging
|
|
28
|
-
|
|
29
|
-
logger
|
|
28
|
+
|
|
29
|
+
from vibe_surf.logger import get_logger
|
|
30
|
+
|
|
31
|
+
logger = get_logger(__name__)
|
|
30
32
|
|
|
31
33
|
app = FastAPI(
|
|
32
34
|
title="VibeSurf Backend API",
|
|
@@ -15,7 +15,8 @@ from pathlib import Path
|
|
|
15
15
|
|
|
16
16
|
# VibeSurf components
|
|
17
17
|
from vibe_surf.agents.vibe_surf_agent import VibeSurfAgent
|
|
18
|
-
from vibe_surf.
|
|
18
|
+
from vibe_surf.tools.browser_use_tools import BrowserUseTools
|
|
19
|
+
from vibe_surf.tools.vibesurf_tools import VibeSurfTools
|
|
19
20
|
from vibe_surf.browser.browser_manager import BrowserManager
|
|
20
21
|
from browser_use.llm.base import BaseChatModel
|
|
21
22
|
from browser_use.llm.openai.chat import ChatOpenAI
|
|
@@ -29,7 +30,7 @@ logger = logging.getLogger(__name__)
|
|
|
29
30
|
# Global VibeSurf components
|
|
30
31
|
vibesurf_agent: Optional[VibeSurfAgent] = None
|
|
31
32
|
browser_manager: Optional[BrowserManager] = None
|
|
32
|
-
|
|
33
|
+
vibesurf_tools: Optional[VibeSurfTools] = None
|
|
33
34
|
llm: Optional[BaseChatModel] = None
|
|
34
35
|
db_manager: Optional['DatabaseManager'] = None
|
|
35
36
|
|
|
@@ -50,10 +51,13 @@ active_task: Optional[Dict[str, Any]] = None
|
|
|
50
51
|
|
|
51
52
|
def get_all_components():
|
|
52
53
|
"""Get all components as a dictionary"""
|
|
54
|
+
global vibesurf_agent, browser_manager, vibesurf_tools, llm, db_manager
|
|
55
|
+
global workspace_dir, browser_execution_path, browser_user_data, active_mcp_server, envs
|
|
56
|
+
|
|
53
57
|
return {
|
|
54
58
|
"vibesurf_agent": vibesurf_agent,
|
|
55
59
|
"browser_manager": browser_manager,
|
|
56
|
-
"
|
|
60
|
+
"tools": vibesurf_tools,
|
|
57
61
|
"llm": llm,
|
|
58
62
|
"db_manager": db_manager,
|
|
59
63
|
"workspace_dir": workspace_dir,
|
|
@@ -67,15 +71,15 @@ def get_all_components():
|
|
|
67
71
|
|
|
68
72
|
def set_components(**kwargs):
|
|
69
73
|
"""Update global components"""
|
|
70
|
-
global vibesurf_agent, browser_manager,
|
|
74
|
+
global vibesurf_agent, browser_manager, vibesurf_tools, llm, db_manager
|
|
71
75
|
global workspace_dir, browser_execution_path, browser_user_data, active_mcp_server, envs
|
|
72
76
|
|
|
73
77
|
if "vibesurf_agent" in kwargs:
|
|
74
78
|
vibesurf_agent = kwargs["vibesurf_agent"]
|
|
75
79
|
if "browser_manager" in kwargs:
|
|
76
80
|
browser_manager = kwargs["browser_manager"]
|
|
77
|
-
if "
|
|
78
|
-
|
|
81
|
+
if "tools" in kwargs:
|
|
82
|
+
vibesurf_tools = kwargs["tools"]
|
|
79
83
|
if "llm" in kwargs:
|
|
80
84
|
llm = kwargs["llm"]
|
|
81
85
|
if "db_manager" in kwargs:
|
|
@@ -223,8 +227,8 @@ def clear_active_task():
|
|
|
223
227
|
|
|
224
228
|
|
|
225
229
|
async def _check_and_update_mcp_servers(db_session):
|
|
226
|
-
"""Check if MCP server configuration has changed and update
|
|
227
|
-
global
|
|
230
|
+
"""Check if MCP server configuration has changed and update tools if needed"""
|
|
231
|
+
global vibesurf_tools, active_mcp_server
|
|
228
232
|
|
|
229
233
|
try:
|
|
230
234
|
if not db_session:
|
|
@@ -238,21 +242,21 @@ async def _check_and_update_mcp_servers(db_session):
|
|
|
238
242
|
|
|
239
243
|
# Compare with shared state
|
|
240
244
|
if current_active_servers != active_mcp_server:
|
|
241
|
-
logger.info(f"MCP server configuration changed. Updating
|
|
245
|
+
logger.info(f"MCP server configuration changed. Updating tools...")
|
|
242
246
|
logger.info(f"Old config: {active_mcp_server}")
|
|
243
247
|
logger.info(f"New config: {current_active_servers}")
|
|
244
248
|
|
|
245
249
|
# Update shared state
|
|
246
250
|
active_mcp_server = current_active_servers.copy()
|
|
247
251
|
|
|
248
|
-
# Create new MCP server config for
|
|
252
|
+
# Create new MCP server config for tools
|
|
249
253
|
mcp_server_config = await _build_mcp_server_config(active_profiles)
|
|
250
254
|
|
|
251
255
|
# Unregister old MCP clients and register new ones
|
|
252
|
-
if
|
|
253
|
-
await
|
|
254
|
-
|
|
255
|
-
await
|
|
256
|
+
if vibesurf_tools:
|
|
257
|
+
await vibesurf_tools.unregister_mcp_clients()
|
|
258
|
+
vibesurf_tools.mcp_server_config = mcp_server_config
|
|
259
|
+
await vibesurf_tools.register_mcp_clients()
|
|
256
260
|
logger.info("✅ Controller MCP configuration updated successfully")
|
|
257
261
|
|
|
258
262
|
except Exception as e:
|
|
@@ -310,25 +314,13 @@ async def _load_active_mcp_servers():
|
|
|
310
314
|
|
|
311
315
|
async def initialize_vibesurf_components():
|
|
312
316
|
"""Initialize VibeSurf components from environment variables and default LLM profile"""
|
|
313
|
-
global vibesurf_agent, browser_manager,
|
|
317
|
+
global vibesurf_agent, browser_manager, vibesurf_tools, llm, db_manager
|
|
314
318
|
global workspace_dir, browser_execution_path, browser_user_data, envs
|
|
319
|
+
from vibe_surf import common
|
|
315
320
|
|
|
316
321
|
try:
|
|
317
322
|
# Load environment variables
|
|
318
|
-
|
|
319
|
-
if not env_workspace_dir or not env_workspace_dir.strip():
|
|
320
|
-
# Set default workspace directory based on OS
|
|
321
|
-
if platform.system() == "Windows":
|
|
322
|
-
default_workspace = os.path.join(os.environ.get("APPDATA", ""), "VibeSurf")
|
|
323
|
-
elif platform.system() == "Darwin": # macOS
|
|
324
|
-
default_workspace = os.path.join(os.path.expanduser("~"), "Library", "Application Support", "VibeSurf")
|
|
325
|
-
else: # Linux and others
|
|
326
|
-
default_workspace = os.path.join(os.path.expanduser("~"), ".vibesurf")
|
|
327
|
-
workspace_dir = default_workspace
|
|
328
|
-
else:
|
|
329
|
-
workspace_dir = env_workspace_dir
|
|
330
|
-
workspace_dir = os.path.abspath(workspace_dir)
|
|
331
|
-
os.makedirs(workspace_dir, exist_ok=True)
|
|
323
|
+
workspace_dir = common.get_workspace_dir()
|
|
332
324
|
logger.info("WorkSpace directory: {}".format(workspace_dir))
|
|
333
325
|
|
|
334
326
|
# Load environment configuration from envs.json
|
|
@@ -438,19 +430,19 @@ async def initialize_vibesurf_components():
|
|
|
438
430
|
# Load active MCP servers from database
|
|
439
431
|
mcp_server_config = await _load_active_mcp_servers()
|
|
440
432
|
|
|
441
|
-
# Initialize vibesurf
|
|
442
|
-
|
|
433
|
+
# Initialize vibesurf tools with MCP server config
|
|
434
|
+
vibesurf_tools = VibeSurfTools(mcp_server_config=mcp_server_config)
|
|
443
435
|
|
|
444
436
|
# Register MCP clients if there are any active MCP servers
|
|
445
437
|
if mcp_server_config and mcp_server_config.get("mcpServers"):
|
|
446
|
-
await
|
|
438
|
+
await vibesurf_tools.register_mcp_clients()
|
|
447
439
|
logger.info(f"✅ Registered {len(mcp_server_config['mcpServers'])} MCP servers")
|
|
448
440
|
|
|
449
441
|
# Initialize VibeSurfAgent
|
|
450
442
|
vibesurf_agent = VibeSurfAgent(
|
|
451
443
|
llm=llm,
|
|
452
444
|
browser_manager=browser_manager,
|
|
453
|
-
|
|
445
|
+
tools=vibesurf_tools,
|
|
454
446
|
workspace_dir=workspace_dir
|
|
455
447
|
)
|
|
456
448
|
|
|
@@ -532,8 +524,9 @@ async def update_llm_from_profile(profile_name: str):
|
|
|
532
524
|
|
|
533
525
|
# Update global state
|
|
534
526
|
llm = new_llm
|
|
535
|
-
if vibesurf_agent:
|
|
536
|
-
|
|
527
|
+
if vibesurf_agent and vibesurf_agent.token_cost_service:
|
|
528
|
+
# FIX: Register new LLM with token cost service to maintain tracking
|
|
529
|
+
vibesurf_agent.llm = vibesurf_agent.token_cost_service.register_llm(new_llm)
|
|
537
530
|
|
|
538
531
|
logger.info(f"✅ LLM updated to profile: {profile_name}")
|
|
539
532
|
return True
|
|
@@ -6,26 +6,29 @@ from typing import Optional
|
|
|
6
6
|
import logging
|
|
7
7
|
from ..llm_config import get_supported_providers, is_provider_supported
|
|
8
8
|
|
|
9
|
-
logger
|
|
9
|
+
from vibe_surf.logger import get_logger
|
|
10
|
+
|
|
11
|
+
logger = get_logger(__name__)
|
|
12
|
+
|
|
10
13
|
|
|
11
14
|
def create_llm_from_profile(llm_profile):
|
|
12
15
|
"""Create LLM instance from LLMProfile database record (dict or object)"""
|
|
13
16
|
try:
|
|
14
17
|
# Import LLM classes from browser_use and vibe_surf
|
|
15
18
|
from browser_use.llm import (
|
|
16
|
-
ChatOpenAI, ChatAnthropic, ChatGoogle, ChatAzureOpenAI,
|
|
17
|
-
ChatGroq, ChatOllama, ChatOpenRouter, ChatDeepSeek,
|
|
19
|
+
ChatOpenAI, ChatAnthropic, ChatGoogle, ChatAzureOpenAI,
|
|
20
|
+
ChatGroq, ChatOllama, ChatOpenRouter, ChatDeepSeek,
|
|
18
21
|
ChatAWSBedrock, ChatAnthropicBedrock
|
|
19
22
|
)
|
|
20
23
|
from vibe_surf.llm import ChatOpenAICompatible
|
|
21
|
-
|
|
24
|
+
|
|
22
25
|
# Handle both dict and object access patterns
|
|
23
26
|
def get_attr(obj, key, default=None):
|
|
24
27
|
if isinstance(obj, dict):
|
|
25
28
|
return obj.get(key, default)
|
|
26
29
|
else:
|
|
27
30
|
return getattr(obj, key, default)
|
|
28
|
-
|
|
31
|
+
|
|
29
32
|
provider = get_attr(llm_profile, 'provider')
|
|
30
33
|
model = get_attr(llm_profile, 'model')
|
|
31
34
|
api_key = get_attr(llm_profile, 'api_key') # Should already be decrypted by queries
|
|
@@ -36,11 +39,11 @@ def create_llm_from_profile(llm_profile):
|
|
|
36
39
|
frequency_penalty = get_attr(llm_profile, 'frequency_penalty')
|
|
37
40
|
seed = get_attr(llm_profile, 'seed')
|
|
38
41
|
provider_config = get_attr(llm_profile, 'provider_config', {})
|
|
39
|
-
|
|
42
|
+
|
|
40
43
|
# Validate provider
|
|
41
44
|
if not is_provider_supported(provider):
|
|
42
45
|
raise ValueError(f"Unsupported provider: {provider}. Supported: {get_supported_providers()}")
|
|
43
|
-
|
|
46
|
+
|
|
44
47
|
# Define provider-specific parameter support
|
|
45
48
|
provider_param_support = {
|
|
46
49
|
"openai": ["temperature"],
|
|
@@ -55,11 +58,11 @@ def create_llm_from_profile(llm_profile):
|
|
|
55
58
|
"anthropic_bedrock": ["temperature"],
|
|
56
59
|
"openai_compatible": ["temperature"]
|
|
57
60
|
}
|
|
58
|
-
|
|
61
|
+
|
|
59
62
|
# Build common parameters based on provider support
|
|
60
63
|
supported_params = provider_param_support.get(provider, [])
|
|
61
64
|
common_params = {}
|
|
62
|
-
|
|
65
|
+
|
|
63
66
|
if temperature is not None and "temperature" in supported_params:
|
|
64
67
|
common_params["temperature"] = temperature
|
|
65
68
|
if max_tokens is not None and "max_tokens" in supported_params:
|
|
@@ -70,11 +73,11 @@ def create_llm_from_profile(llm_profile):
|
|
|
70
73
|
common_params["frequency_penalty"] = frequency_penalty
|
|
71
74
|
if seed is not None and "seed" in supported_params:
|
|
72
75
|
common_params["seed"] = seed
|
|
73
|
-
|
|
76
|
+
|
|
74
77
|
# Add provider-specific config if available
|
|
75
78
|
if provider_config:
|
|
76
79
|
common_params.update(provider_config)
|
|
77
|
-
|
|
80
|
+
|
|
78
81
|
# Create LLM instance based on provider
|
|
79
82
|
if provider == "openai":
|
|
80
83
|
params = {
|
|
@@ -85,21 +88,21 @@ def create_llm_from_profile(llm_profile):
|
|
|
85
88
|
if base_url:
|
|
86
89
|
params["base_url"] = base_url
|
|
87
90
|
return ChatOpenAI(**params)
|
|
88
|
-
|
|
91
|
+
|
|
89
92
|
elif provider == "anthropic":
|
|
90
93
|
return ChatAnthropic(
|
|
91
94
|
model=model,
|
|
92
95
|
api_key=api_key,
|
|
93
96
|
**common_params
|
|
94
97
|
)
|
|
95
|
-
|
|
98
|
+
|
|
96
99
|
elif provider == "google":
|
|
97
100
|
return ChatGoogle(
|
|
98
101
|
model=model,
|
|
99
102
|
api_key=api_key,
|
|
100
103
|
**common_params
|
|
101
104
|
)
|
|
102
|
-
|
|
105
|
+
|
|
103
106
|
elif provider == "azure_openai":
|
|
104
107
|
if not base_url:
|
|
105
108
|
raise ValueError("Azure OpenAI requires base_url (azure_endpoint)")
|
|
@@ -110,14 +113,14 @@ def create_llm_from_profile(llm_profile):
|
|
|
110
113
|
azure_endpoint=base_url,
|
|
111
114
|
**common_params
|
|
112
115
|
)
|
|
113
|
-
|
|
116
|
+
|
|
114
117
|
elif provider == "groq":
|
|
115
118
|
return ChatGroq(
|
|
116
119
|
model=model,
|
|
117
120
|
api_key=api_key,
|
|
118
121
|
**common_params
|
|
119
122
|
)
|
|
120
|
-
|
|
123
|
+
|
|
121
124
|
elif provider == "ollama":
|
|
122
125
|
params = {
|
|
123
126
|
"model": model,
|
|
@@ -128,21 +131,21 @@ def create_llm_from_profile(llm_profile):
|
|
|
128
131
|
else:
|
|
129
132
|
params["host"] = "http://localhost:11434" # Default Ollama URL
|
|
130
133
|
return ChatOllama(**params)
|
|
131
|
-
|
|
134
|
+
|
|
132
135
|
elif provider == "openrouter":
|
|
133
136
|
return ChatOpenRouter(
|
|
134
137
|
model=model,
|
|
135
138
|
api_key=api_key,
|
|
136
139
|
**common_params
|
|
137
140
|
)
|
|
138
|
-
|
|
141
|
+
|
|
139
142
|
elif provider == "deepseek":
|
|
140
143
|
return ChatDeepSeek(
|
|
141
144
|
model=model,
|
|
142
145
|
api_key=api_key,
|
|
143
146
|
**common_params
|
|
144
147
|
)
|
|
145
|
-
|
|
148
|
+
|
|
146
149
|
elif provider == "aws_bedrock":
|
|
147
150
|
params = {
|
|
148
151
|
"model": model,
|
|
@@ -157,7 +160,7 @@ def create_llm_from_profile(llm_profile):
|
|
|
157
160
|
if 'aws_region' not in params:
|
|
158
161
|
params["aws_region"] = "us-east-1"
|
|
159
162
|
return ChatAWSBedrock(**params)
|
|
160
|
-
|
|
163
|
+
|
|
161
164
|
elif provider == "anthropic_bedrock":
|
|
162
165
|
params = {
|
|
163
166
|
"model": model,
|
|
@@ -170,7 +173,7 @@ def create_llm_from_profile(llm_profile):
|
|
|
170
173
|
if "region_name" in provider_config:
|
|
171
174
|
params["region_name"] = provider_config["region_name"]
|
|
172
175
|
return ChatAnthropicBedrock(**params)
|
|
173
|
-
|
|
176
|
+
|
|
174
177
|
elif provider == "openai_compatible":
|
|
175
178
|
if not base_url:
|
|
176
179
|
raise ValueError("OpenAI Compatible provider requires base_url")
|
|
@@ -180,61 +183,63 @@ def create_llm_from_profile(llm_profile):
|
|
|
180
183
|
base_url=base_url,
|
|
181
184
|
**common_params
|
|
182
185
|
)
|
|
183
|
-
|
|
186
|
+
|
|
184
187
|
else:
|
|
185
188
|
raise ValueError(f"Unsupported provider: {provider}")
|
|
186
|
-
|
|
189
|
+
|
|
187
190
|
except Exception as e:
|
|
188
191
|
logger.error(f"Failed to create LLM from profile: {e}")
|
|
189
192
|
raise RuntimeError(f"Failed to create LLM from profile: {str(e)}")
|
|
190
193
|
|
|
194
|
+
|
|
191
195
|
def validate_llm_configuration(provider: str, model: str, api_key: str, base_url: Optional[str] = None):
|
|
192
196
|
"""Validate LLM configuration parameters"""
|
|
193
197
|
if not provider:
|
|
194
198
|
raise ValueError("Provider is required")
|
|
195
|
-
|
|
199
|
+
|
|
196
200
|
if not model:
|
|
197
201
|
raise ValueError("Model is required")
|
|
198
|
-
|
|
202
|
+
|
|
199
203
|
if not is_provider_supported(provider):
|
|
200
204
|
raise ValueError(f"Unsupported provider: {provider}. Supported: {get_supported_providers()}")
|
|
201
|
-
|
|
205
|
+
|
|
202
206
|
# Provider-specific validation
|
|
203
207
|
from ..llm_config import get_provider_metadata
|
|
204
208
|
metadata = get_provider_metadata(provider)
|
|
205
|
-
|
|
209
|
+
|
|
206
210
|
if metadata.get("requires_api_key", True) and not api_key:
|
|
207
211
|
raise ValueError(f"API key is required for provider: {provider}")
|
|
208
|
-
|
|
212
|
+
|
|
209
213
|
if metadata.get("requires_base_url", False) and not base_url:
|
|
210
214
|
raise ValueError(f"Base URL is required for provider: {provider}")
|
|
211
|
-
|
|
215
|
+
|
|
212
216
|
return True
|
|
213
217
|
|
|
218
|
+
|
|
214
219
|
def get_llm_creation_parameters(provider: str):
|
|
215
220
|
"""Get the required and optional parameters for creating an LLM instance"""
|
|
216
221
|
from ..llm_config import get_provider_metadata
|
|
217
|
-
|
|
222
|
+
|
|
218
223
|
if not is_provider_supported(provider):
|
|
219
224
|
raise ValueError(f"Unsupported provider: {provider}")
|
|
220
|
-
|
|
225
|
+
|
|
221
226
|
metadata = get_provider_metadata(provider)
|
|
222
|
-
|
|
227
|
+
|
|
223
228
|
required_params = ["model"]
|
|
224
229
|
optional_params = ["temperature", "max_tokens", "top_p", "frequency_penalty", "seed"]
|
|
225
|
-
|
|
230
|
+
|
|
226
231
|
if metadata.get("requires_api_key", True):
|
|
227
232
|
required_params.append("api_key")
|
|
228
|
-
|
|
233
|
+
|
|
229
234
|
if metadata.get("requires_base_url", False):
|
|
230
235
|
required_params.append("base_url")
|
|
231
236
|
elif metadata.get("supports_base_url", False):
|
|
232
237
|
optional_params.append("base_url")
|
|
233
|
-
|
|
238
|
+
|
|
234
239
|
# Special cases for AWS Bedrock
|
|
235
240
|
if provider in ["aws_bedrock", "anthropic_bedrock"]:
|
|
236
241
|
required_params.extend(["aws_secret_access_key", "region_name"])
|
|
237
|
-
|
|
242
|
+
|
|
238
243
|
return {
|
|
239
244
|
"required": required_params,
|
|
240
245
|
"optional": optional_params,
|
|
@@ -720,10 +720,6 @@ class AgentBrowserSession(BrowserSession):
|
|
|
720
720
|
# Wait for reload
|
|
721
721
|
await asyncio.sleep(1.0)
|
|
722
722
|
|
|
723
|
-
# Note: We don't clear cached state here - let the next state fetch rebuild as needed
|
|
724
|
-
|
|
725
|
-
# Navigation is handled by BrowserSession via events
|
|
726
|
-
|
|
727
723
|
self.logger.info('🔄 Target refreshed')
|
|
728
724
|
except Exception as e:
|
|
729
725
|
raise
|
|
@@ -16,7 +16,9 @@ from vibe_surf.browser.agent_browser_session import AgentBrowserSession
|
|
|
16
16
|
if TYPE_CHECKING:
|
|
17
17
|
from browser_use.browser.session import BrowserSession
|
|
18
18
|
|
|
19
|
-
logger
|
|
19
|
+
from vibe_surf.logger import get_logger
|
|
20
|
+
|
|
21
|
+
logger = get_logger(__name__)
|
|
20
22
|
|
|
21
23
|
|
|
22
24
|
class BrowserManager:
|
|
@@ -80,11 +82,17 @@ class BrowserManager:
|
|
|
80
82
|
|
|
81
83
|
# Validate target assignment
|
|
82
84
|
if target_id:
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
85
|
+
try:
|
|
86
|
+
target_id = await self.main_browser_session.get_target_id_from_tab_id(target_id)
|
|
87
|
+
except Exception:
|
|
88
|
+
logger.warning(f"Target ID '{target_id}' not found.")
|
|
89
|
+
target_id = None
|
|
90
|
+
if target_id:
|
|
91
|
+
target_id_owner = self.get_target_owner(target_id)
|
|
92
|
+
if target_id_owner and target_id_owner != agent_id:
|
|
93
|
+
logger.warning(
|
|
94
|
+
f"Target id: {target_id} belongs to {target_id_owner}. You cannot assign it to {target_id_owner}.")
|
|
95
|
+
return False
|
|
88
96
|
|
|
89
97
|
# Get or create available target
|
|
90
98
|
if target_id is None:
|
|
@@ -166,8 +174,6 @@ class BrowserManager:
|
|
|
166
174
|
except Exception as e:
|
|
167
175
|
logger.warning(f"Error during agent {agent_id} cleanup: {e}")
|
|
168
176
|
|
|
169
|
-
# Note: We don't close the root browser session here as it's managed externally
|
|
170
|
-
|
|
171
177
|
async def __aenter__(self) -> "BrowserManager":
|
|
172
178
|
"""Async context manager entry."""
|
|
173
179
|
return self
|
vibe_surf/browser/utils.py
CHANGED
|
@@ -22,7 +22,9 @@ import numpy as np
|
|
|
22
22
|
from typing import Optional, Tuple, List, Any
|
|
23
23
|
import io
|
|
24
24
|
|
|
25
|
-
logger
|
|
25
|
+
from vibe_surf.logger import get_logger
|
|
26
|
+
|
|
27
|
+
logger = get_logger(__name__)
|
|
26
28
|
|
|
27
29
|
|
|
28
30
|
# List of common font file names (Prioritize preferred ones first)
|
|
@@ -601,7 +603,7 @@ def highlight_screenshot(screenshot_base64: str, elements: List[List[Any]]) -> s
|
|
|
601
603
|
|
|
602
604
|
# --- Default if all corners overlap (Logic unchanged) ---
|
|
603
605
|
if not found_non_overlapping_spot:
|
|
604
|
-
logger.debug(f"Could not avoid label overlap for index {highlight_index}. Defaulting to top-left.")
|
|
606
|
+
# logger.debug(f"Could not avoid label overlap for index {highlight_index}. Defaulting to top-left.")
|
|
605
607
|
chosen_label_bg_box, potential_text_ref_pos = calculate_label_placement(
|
|
606
608
|
corner='top_left', # Default corner from original code
|
|
607
609
|
outline_box=draw_box_outline,
|
|
@@ -618,7 +620,7 @@ def highlight_screenshot(screenshot_base64: str, elements: List[List[Any]]) -> s
|
|
|
618
620
|
potential_text_ref_pos[0] + w_padding // 2,
|
|
619
621
|
potential_text_ref_pos[1] + h_padding // 2) # ** OFFSET FROM ORIGINAL CODE **
|
|
620
622
|
else:
|
|
621
|
-
logger.debug(f"Default top-left placement failed for index {highlight_index}. Skipping label.")
|
|
623
|
+
# logger.debug(f"Default top-left placement failed for index {highlight_index}. Skipping label.")
|
|
622
624
|
chosen_label_bg_box = None
|
|
623
625
|
chosen_text_pos = None
|
|
624
626
|
|
|
@@ -72,51 +72,6 @@ class CustomDOMWatchdog(DOMWatchdog):
|
|
|
72
72
|
self.logger.debug(f'🔍 DOMWatchdog.on_BrowserStateRequestEvent: Tabs info: {tabs_info}')
|
|
73
73
|
|
|
74
74
|
try:
|
|
75
|
-
# Fast path for empty pages
|
|
76
|
-
# if not_a_meaningful_website:
|
|
77
|
-
# self.logger.debug(f'⚡ Skipping BuildDOMTree for empty target: {page_url}')
|
|
78
|
-
# self.logger.info(f'📸 Not taking screenshot for empty page: {page_url} (non-http/https URL)')
|
|
79
|
-
#
|
|
80
|
-
# # Create minimal DOM state
|
|
81
|
-
# content = SerializedDOMState(_root=None, selector_map={})
|
|
82
|
-
#
|
|
83
|
-
# # Skip screenshot for empty pages
|
|
84
|
-
# screenshot_b64 = None
|
|
85
|
-
#
|
|
86
|
-
# # Try to get page info from CDP, fall back to defaults if unavailable
|
|
87
|
-
# try:
|
|
88
|
-
# page_info = await self._get_page_info()
|
|
89
|
-
# except Exception as e:
|
|
90
|
-
# self.logger.debug(f'Failed to get page info from CDP for empty page: {e}, using fallback')
|
|
91
|
-
# # Use default viewport dimensions
|
|
92
|
-
# viewport = self.browser_session.browser_profile.viewport or {'width': 1280, 'height': 720}
|
|
93
|
-
# page_info = PageInfo(
|
|
94
|
-
# viewport_width=viewport['width'],
|
|
95
|
-
# viewport_height=viewport['height'],
|
|
96
|
-
# page_width=viewport['width'],
|
|
97
|
-
# page_height=viewport['height'],
|
|
98
|
-
# scroll_x=0,
|
|
99
|
-
# scroll_y=0,
|
|
100
|
-
# pixels_above=0,
|
|
101
|
-
# pixels_below=0,
|
|
102
|
-
# pixels_left=0,
|
|
103
|
-
# pixels_right=0,
|
|
104
|
-
# )
|
|
105
|
-
#
|
|
106
|
-
# return BrowserStateSummary(
|
|
107
|
-
# dom_state=content,
|
|
108
|
-
# url=page_url,
|
|
109
|
-
# title='Empty Tab',
|
|
110
|
-
# tabs=tabs_info,
|
|
111
|
-
# screenshot=screenshot_b64,
|
|
112
|
-
# page_info=page_info,
|
|
113
|
-
# pixels_above=0,
|
|
114
|
-
# pixels_below=0,
|
|
115
|
-
# browser_errors=[],
|
|
116
|
-
# is_pdf_viewer=False,
|
|
117
|
-
# recent_events=self._get_recent_events_str() if include_recent_events else None,
|
|
118
|
-
# )
|
|
119
|
-
|
|
120
75
|
# Execute DOM building and screenshot capture in parallel
|
|
121
76
|
dom_task = None
|
|
122
77
|
screenshot_task = None
|
|
@@ -202,6 +202,10 @@ class VibeSurfBackground {
|
|
|
202
202
|
result = await this.openFileSystem(message.data?.filePath);
|
|
203
203
|
break;
|
|
204
204
|
|
|
205
|
+
case 'GET_ALL_TABS':
|
|
206
|
+
result = await this.getAllTabs();
|
|
207
|
+
break;
|
|
208
|
+
|
|
205
209
|
default:
|
|
206
210
|
console.warn('[VibeSurf] Unknown message type:', message.type);
|
|
207
211
|
result = { error: 'Unknown message type' };
|
|
@@ -235,6 +235,10 @@ class VibeSurfAPIClient {
|
|
|
235
235
|
return this.post('/tasks/stop', { reason });
|
|
236
236
|
}
|
|
237
237
|
|
|
238
|
+
async addNewTask(newTask) {
|
|
239
|
+
return this.post('/tasks/add-new-task', { reason: newTask });
|
|
240
|
+
}
|
|
241
|
+
|
|
238
242
|
// Activity APIs
|
|
239
243
|
async getTaskInfo(taskId) {
|
|
240
244
|
return this.get(`/activity/${taskId}`);
|
|
@@ -416,6 +420,15 @@ class VibeSurfAPIClient {
|
|
|
416
420
|
return this.put('/config/controller', configData);
|
|
417
421
|
}
|
|
418
422
|
|
|
423
|
+
// Browser APIs
|
|
424
|
+
async getActiveBrowserTab() {
|
|
425
|
+
return this.get('/browser/active-tab');
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
async getAllBrowserTabs() {
|
|
429
|
+
return this.get('/browser/all-tabs');
|
|
430
|
+
}
|
|
431
|
+
|
|
419
432
|
// Utility methods
|
|
420
433
|
delay(ms) {
|
|
421
434
|
return new Promise(resolve => setTimeout(resolve, ms));
|