vibesurf 0.1.11__py3-none-any.whl → 0.1.13__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/vibe_surf_agent.py +1 -0
- vibe_surf/backend/api/task.py +47 -14
- vibe_surf/backend/shared_state.py +12 -5
- vibe_surf/backend/utils/llm_factory.py +10 -8
- vibe_surf/chrome_extension/scripts/api-client.js +6 -0
- vibe_surf/chrome_extension/scripts/modal-manager.js +41 -32
- vibe_surf/chrome_extension/scripts/session-manager.js +10 -0
- vibe_surf/chrome_extension/scripts/settings-manager.js +32 -0
- vibe_surf/chrome_extension/scripts/ui-manager.js +67 -1
- vibe_surf/chrome_extension/styles/components.css +173 -20
- vibe_surf/chrome_extension/styles/input.css +1 -95
- vibe_surf/cli.py +2 -1
- vibe_surf/llm/openai_compatible.py +72 -9
- vibe_surf/tools/file_system.py +25 -3
- {vibesurf-0.1.11.dist-info → vibesurf-0.1.13.dist-info}/METADATA +1 -1
- {vibesurf-0.1.11.dist-info → vibesurf-0.1.13.dist-info}/RECORD +21 -21
- {vibesurf-0.1.11.dist-info → vibesurf-0.1.13.dist-info}/WHEEL +0 -0
- {vibesurf-0.1.11.dist-info → vibesurf-0.1.13.dist-info}/entry_points.txt +0 -0
- {vibesurf-0.1.11.dist-info → vibesurf-0.1.13.dist-info}/licenses/LICENSE +0 -0
- {vibesurf-0.1.11.dist-info → vibesurf-0.1.13.dist-info}/top_level.txt +0 -0
vibe_surf/_version.py
CHANGED
|
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
|
|
|
28
28
|
commit_id: COMMIT_ID
|
|
29
29
|
__commit_id__: COMMIT_ID
|
|
30
30
|
|
|
31
|
-
__version__ = version = '0.1.
|
|
32
|
-
__version_tuple__ = version_tuple = (0, 1,
|
|
31
|
+
__version__ = version = '0.1.13'
|
|
32
|
+
__version_tuple__ = version_tuple = (0, 1, 13)
|
|
33
33
|
|
|
34
34
|
__commit_id__ = commit_id = None
|
|
@@ -363,6 +363,7 @@ async def _vibesurf_agent_node_impl(state: VibeSurfState) -> VibeSurfState:
|
|
|
363
363
|
|
|
364
364
|
# Format context information
|
|
365
365
|
context_info = []
|
|
366
|
+
context_info.append(f"Current Time: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
|
366
367
|
if browser_tabs:
|
|
367
368
|
browser_tabs_info = {}
|
|
368
369
|
for tab in browser_tabs:
|
vibe_surf/backend/api/task.py
CHANGED
|
@@ -22,7 +22,6 @@ from ..shared_state import (
|
|
|
22
22
|
is_task_running,
|
|
23
23
|
get_active_task_info,
|
|
24
24
|
clear_active_task,
|
|
25
|
-
browser_manager
|
|
26
25
|
)
|
|
27
26
|
|
|
28
27
|
from vibe_surf.logger import get_logger
|
|
@@ -49,7 +48,7 @@ async def submit_task(
|
|
|
49
48
|
):
|
|
50
49
|
"""Submit new task for execution (single task mode)"""
|
|
51
50
|
from ..database.queries import LLMProfileQueries
|
|
52
|
-
from ..shared_state import workspace_dir, active_task
|
|
51
|
+
from ..shared_state import workspace_dir, active_task, llm, current_llm_profile_name
|
|
53
52
|
|
|
54
53
|
# Check if task is already running
|
|
55
54
|
if is_task_running():
|
|
@@ -74,6 +73,18 @@ async def submit_task(
|
|
|
74
73
|
detail=f"LLM profile '{task_request.llm_profile_name}' not found"
|
|
75
74
|
)
|
|
76
75
|
|
|
76
|
+
# Initialize LLM for this task if needed
|
|
77
|
+
if not current_llm_profile_name or current_llm_profile_name != task_request.llm_profile_name:
|
|
78
|
+
current_llm_profile_name = task_request.llm_profile_name
|
|
79
|
+
success, message = await _ensure_llm_initialized(llm_profile)
|
|
80
|
+
if not success:
|
|
81
|
+
active_task = None
|
|
82
|
+
return {
|
|
83
|
+
"success": False,
|
|
84
|
+
"error": "llm_connection_failed",
|
|
85
|
+
"message": f"Cannot connect to LLM API: {message}",
|
|
86
|
+
"llm_profile": task_request.llm_profile_name
|
|
87
|
+
}
|
|
77
88
|
# Generate task ID
|
|
78
89
|
task_id = uuid7str()
|
|
79
90
|
|
|
@@ -106,10 +117,6 @@ async def submit_task(
|
|
|
106
117
|
)
|
|
107
118
|
await db.commit()
|
|
108
119
|
|
|
109
|
-
# Initialize LLM for this task if needed
|
|
110
|
-
if not active_task or active_task["llm_profile_name"] != task_request.llm_profile_name:
|
|
111
|
-
await _ensure_llm_initialized(llm_profile)
|
|
112
|
-
|
|
113
120
|
# Add background task
|
|
114
121
|
background_tasks.add_task(
|
|
115
122
|
execute_task_background,
|
|
@@ -139,20 +146,46 @@ async def submit_task(
|
|
|
139
146
|
|
|
140
147
|
|
|
141
148
|
async def _ensure_llm_initialized(llm_profile):
|
|
142
|
-
"""Ensure LLM is initialized with the specified profile"""
|
|
149
|
+
"""Ensure LLM is initialized with the specified profile and test connectivity"""
|
|
143
150
|
from ..utils.llm_factory import create_llm_from_profile
|
|
144
151
|
from ..shared_state import vibesurf_agent
|
|
152
|
+
from browser_use.llm import UserMessage
|
|
145
153
|
|
|
146
154
|
if not vibesurf_agent:
|
|
147
155
|
raise HTTPException(status_code=503, detail="VibeSurf agent not initialized")
|
|
148
156
|
|
|
149
|
-
|
|
150
|
-
|
|
157
|
+
try:
|
|
158
|
+
# Always create new LLM instance to ensure we're using the right profile
|
|
159
|
+
new_llm = create_llm_from_profile(llm_profile)
|
|
151
160
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
logger.info(f"LLM
|
|
161
|
+
# Test LLM connectivity with a simple question
|
|
162
|
+
test_message = UserMessage(content='What is the capital of France? Answer in one word.')
|
|
163
|
+
|
|
164
|
+
logger.info(f"Testing LLM connectivity for profile: {llm_profile['profile_name']}")
|
|
165
|
+
response = await new_llm.ainvoke([test_message])
|
|
166
|
+
|
|
167
|
+
# Check if response contains expected answer
|
|
168
|
+
if not response or not hasattr(response, 'completion'):
|
|
169
|
+
return False, f"LLM response validation failed: No completion content received"
|
|
170
|
+
|
|
171
|
+
completion = response.completion.lower() if response.completion else ""
|
|
172
|
+
if 'paris' not in completion:
|
|
173
|
+
logger.warning(f"LLM connectivity test returned unexpected answer: {response.completion}")
|
|
174
|
+
# Still continue if we got some response, just log the warning
|
|
175
|
+
|
|
176
|
+
logger.info(f"LLM connectivity test successful for profile: {llm_profile['profile_name']}")
|
|
177
|
+
|
|
178
|
+
# Update vibesurf agent's LLM and register with token cost service
|
|
179
|
+
if vibesurf_agent and vibesurf_agent.token_cost_service:
|
|
180
|
+
vibesurf_agent.llm = vibesurf_agent.token_cost_service.register_llm(new_llm)
|
|
181
|
+
logger.info(f"LLM updated and registered for token tracking for profile: {llm_profile['profile_name']}")
|
|
182
|
+
|
|
183
|
+
return True, "LLM initialized and tested successfully"
|
|
184
|
+
|
|
185
|
+
except Exception as e:
|
|
186
|
+
error_msg = f"LLM connectivity test failed: {str(e)}"
|
|
187
|
+
logger.error(error_msg)
|
|
188
|
+
return False, error_msg
|
|
156
189
|
|
|
157
190
|
|
|
158
191
|
@router.post("/pause")
|
|
@@ -248,7 +281,7 @@ async def stop_task(control_request: TaskControlRequest):
|
|
|
248
281
|
active_task["end_time"] = datetime.now()
|
|
249
282
|
|
|
250
283
|
# Clear active task
|
|
251
|
-
|
|
284
|
+
clear_active_task()
|
|
252
285
|
|
|
253
286
|
return {
|
|
254
287
|
"success": True,
|
|
@@ -33,6 +33,7 @@ browser_manager: Optional[BrowserManager] = None
|
|
|
33
33
|
vibesurf_tools: Optional[VibeSurfTools] = None
|
|
34
34
|
llm: Optional[BaseChatModel] = None
|
|
35
35
|
db_manager: Optional['DatabaseManager'] = None
|
|
36
|
+
current_llm_profile_name: Optional[str] = None
|
|
36
37
|
|
|
37
38
|
# Environment variables
|
|
38
39
|
workspace_dir: str = ""
|
|
@@ -51,7 +52,7 @@ active_task: Optional[Dict[str, Any]] = None
|
|
|
51
52
|
|
|
52
53
|
def get_all_components():
|
|
53
54
|
"""Get all components as a dictionary"""
|
|
54
|
-
global vibesurf_agent, browser_manager, vibesurf_tools, llm, db_manager
|
|
55
|
+
global vibesurf_agent, browser_manager, vibesurf_tools, llm, db_manager, current_llm_profile_name
|
|
55
56
|
global workspace_dir, browser_execution_path, browser_user_data, active_mcp_server, envs
|
|
56
57
|
|
|
57
58
|
return {
|
|
@@ -65,13 +66,14 @@ def get_all_components():
|
|
|
65
66
|
"browser_user_data": browser_user_data,
|
|
66
67
|
"active_mcp_server": active_mcp_server,
|
|
67
68
|
"active_task": active_task,
|
|
69
|
+
"current_llm_profile_name": current_llm_profile_name,
|
|
68
70
|
"envs": envs
|
|
69
71
|
}
|
|
70
72
|
|
|
71
73
|
|
|
72
74
|
def set_components(**kwargs):
|
|
73
75
|
"""Update global components"""
|
|
74
|
-
global vibesurf_agent, browser_manager, vibesurf_tools, llm, db_manager
|
|
76
|
+
global vibesurf_agent, browser_manager, vibesurf_tools, llm, db_manager, current_llm_profile_name
|
|
75
77
|
global workspace_dir, browser_execution_path, browser_user_data, active_mcp_server, envs
|
|
76
78
|
|
|
77
79
|
if "vibesurf_agent" in kwargs:
|
|
@@ -94,6 +96,8 @@ def set_components(**kwargs):
|
|
|
94
96
|
active_mcp_server = kwargs["active_mcp_server"]
|
|
95
97
|
if "envs" in kwargs:
|
|
96
98
|
envs = kwargs["envs"]
|
|
99
|
+
if "current_llm_profile_name" in kwargs:
|
|
100
|
+
envs = kwargs["current_llm_profile_name"]
|
|
97
101
|
|
|
98
102
|
|
|
99
103
|
async def execute_task_background(
|
|
@@ -105,9 +109,11 @@ async def execute_task_background(
|
|
|
105
109
|
db_session=None
|
|
106
110
|
):
|
|
107
111
|
"""Background task execution function for single task with LLM profile support"""
|
|
108
|
-
global vibesurf_agent, active_task
|
|
112
|
+
global vibesurf_agent, active_task, current_llm_profile_name
|
|
109
113
|
|
|
110
114
|
try:
|
|
115
|
+
current_llm_profile_name = llm_profile_name
|
|
116
|
+
|
|
111
117
|
# Check if MCP server configuration needs update
|
|
112
118
|
await _check_and_update_mcp_servers(db_session)
|
|
113
119
|
|
|
@@ -314,7 +320,7 @@ async def _load_active_mcp_servers():
|
|
|
314
320
|
|
|
315
321
|
async def initialize_vibesurf_components():
|
|
316
322
|
"""Initialize VibeSurf components from environment variables and default LLM profile"""
|
|
317
|
-
global vibesurf_agent, browser_manager, vibesurf_tools, llm, db_manager
|
|
323
|
+
global vibesurf_agent, browser_manager, vibesurf_tools, llm, db_manager, current_llm_profile_name
|
|
318
324
|
global workspace_dir, browser_execution_path, browser_user_data, envs
|
|
319
325
|
from vibe_surf import common
|
|
320
326
|
|
|
@@ -463,7 +469,7 @@ async def initialize_vibesurf_components():
|
|
|
463
469
|
|
|
464
470
|
async def _initialize_default_llm():
|
|
465
471
|
"""Initialize LLM from default profile or fallback to environment variables"""
|
|
466
|
-
global db_manager
|
|
472
|
+
global db_manager, current_llm_profile_name
|
|
467
473
|
|
|
468
474
|
try:
|
|
469
475
|
# Try to get default LLM profile from database
|
|
@@ -482,6 +488,7 @@ async def _initialize_default_llm():
|
|
|
482
488
|
)
|
|
483
489
|
if profile_with_key:
|
|
484
490
|
llm_instance = create_llm_from_profile(profile_with_key)
|
|
491
|
+
current_llm_profile_name = default_profile.profile_name
|
|
485
492
|
logger.info(f"✅ LLM initialized from default profile: {default_profile.profile_name}")
|
|
486
493
|
return llm_instance
|
|
487
494
|
break
|
|
@@ -4,6 +4,15 @@ LLM Factory utilities for creating LLM instances from profiles
|
|
|
4
4
|
|
|
5
5
|
from typing import Optional
|
|
6
6
|
import logging
|
|
7
|
+
# Import LLM classes from browser_use and vibe_surf
|
|
8
|
+
from browser_use.llm import (
|
|
9
|
+
BaseChatModel,
|
|
10
|
+
ChatOpenAI, ChatAnthropic, ChatGoogle, ChatAzureOpenAI,
|
|
11
|
+
ChatGroq, ChatOllama, ChatOpenRouter, ChatDeepSeek,
|
|
12
|
+
ChatAWSBedrock, ChatAnthropicBedrock
|
|
13
|
+
)
|
|
14
|
+
from vibe_surf.llm import ChatOpenAICompatible
|
|
15
|
+
|
|
7
16
|
from ..llm_config import get_supported_providers, is_provider_supported
|
|
8
17
|
|
|
9
18
|
from vibe_surf.logger import get_logger
|
|
@@ -11,16 +20,9 @@ from vibe_surf.logger import get_logger
|
|
|
11
20
|
logger = get_logger(__name__)
|
|
12
21
|
|
|
13
22
|
|
|
14
|
-
def create_llm_from_profile(llm_profile):
|
|
23
|
+
def create_llm_from_profile(llm_profile) -> BaseChatModel:
|
|
15
24
|
"""Create LLM instance from LLMProfile database record (dict or object)"""
|
|
16
25
|
try:
|
|
17
|
-
# Import LLM classes from browser_use and vibe_surf
|
|
18
|
-
from browser_use.llm import (
|
|
19
|
-
ChatOpenAI, ChatAnthropic, ChatGoogle, ChatAzureOpenAI,
|
|
20
|
-
ChatGroq, ChatOllama, ChatOpenRouter, ChatDeepSeek,
|
|
21
|
-
ChatAWSBedrock, ChatAnthropicBedrock
|
|
22
|
-
)
|
|
23
|
-
from vibe_surf.llm import ChatOpenAICompatible
|
|
24
26
|
|
|
25
27
|
# Handle both dict and object access patterns
|
|
26
28
|
def get_attr(obj, key, default=None):
|
|
@@ -99,6 +99,12 @@ class VibeSurfAPIClient {
|
|
|
99
99
|
if (error.status >= 400 && error.status < 500) {
|
|
100
100
|
throw error; // Client errors shouldn't be retried
|
|
101
101
|
}
|
|
102
|
+
|
|
103
|
+
// Don't retry on LLM connection failures
|
|
104
|
+
if (error.data && error.data.error === 'llm_connection_failed') {
|
|
105
|
+
console.log('[API] LLM connection failed - skipping retry');
|
|
106
|
+
throw error;
|
|
107
|
+
}
|
|
102
108
|
}
|
|
103
109
|
|
|
104
110
|
// Don't retry on timeout for the last attempt
|
|
@@ -61,20 +61,24 @@ class VibeSurfModalManager {
|
|
|
61
61
|
|
|
62
62
|
const modalId = this.generateModalId();
|
|
63
63
|
|
|
64
|
+
// Add warning icon to title and always show cancel for warning modals
|
|
65
|
+
const warningTitle = `⚠️ ${title}`;
|
|
66
|
+
const shouldShowCancel = showCancel || true; // Always show cancel for warning modals
|
|
67
|
+
|
|
64
68
|
const modalHTML = `
|
|
65
|
-
<div class="modal-overlay" id="${modalId}-overlay">
|
|
69
|
+
<div class="modal-overlay dynamic-modal warning-modal" id="${modalId}-overlay">
|
|
66
70
|
<div class="modal warning-modal ${className}" id="${modalId}">
|
|
67
|
-
<div class="modal-
|
|
68
|
-
<
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
71
|
+
<div class="modal-content">
|
|
72
|
+
<div class="modal-header">
|
|
73
|
+
<h3>${this.escapeHtml(warningTitle)}</h3>
|
|
74
|
+
</div>
|
|
75
|
+
<div class="modal-body">
|
|
76
|
+
<p>${this.escapeHtml(message)}</p>
|
|
77
|
+
</div>
|
|
78
|
+
<div class="modal-footer">
|
|
79
|
+
${shouldShowCancel ? `<button class="btn-secondary modal-cancel-btn" data-modal-id="${modalId}">${this.escapeHtml(cancelText)}</button>` : ''}
|
|
80
|
+
<button class="btn-primary modal-confirm-btn" data-modal-id="${modalId}">${this.escapeHtml(confirmText)}</button>
|
|
81
|
+
</div>
|
|
78
82
|
</div>
|
|
79
83
|
</div>
|
|
80
84
|
</div>
|
|
@@ -121,20 +125,23 @@ class VibeSurfModalManager {
|
|
|
121
125
|
|
|
122
126
|
const icon = iconMap[type] || iconMap.question;
|
|
123
127
|
|
|
128
|
+
// Add icon to title and remove close button
|
|
129
|
+
const iconTitle = `${icon} ${title}`;
|
|
130
|
+
|
|
124
131
|
const modalHTML = `
|
|
125
|
-
<div class="modal-overlay" id="${modalId}-overlay">
|
|
132
|
+
<div class="modal-overlay dynamic-modal" id="${modalId}-overlay">
|
|
126
133
|
<div class="modal confirm-modal ${className}" id="${modalId}">
|
|
127
|
-
<div class="modal-
|
|
128
|
-
<
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
134
|
+
<div class="modal-content">
|
|
135
|
+
<div class="modal-header">
|
|
136
|
+
<h3>${this.escapeHtml(iconTitle)}</h3>
|
|
137
|
+
</div>
|
|
138
|
+
<div class="modal-body">
|
|
139
|
+
<p>${this.escapeHtml(message)}</p>
|
|
140
|
+
</div>
|
|
141
|
+
<div class="modal-footer">
|
|
142
|
+
<button class="btn-secondary modal-cancel-btn" data-modal-id="${modalId}">${this.escapeHtml(cancelText)}</button>
|
|
143
|
+
<button class="btn-primary modal-confirm-btn" data-modal-id="${modalId}" ${type === 'danger' ? 'data-danger="true"' : ''}>${this.escapeHtml(confirmText)}</button>
|
|
144
|
+
</div>
|
|
138
145
|
</div>
|
|
139
146
|
</div>
|
|
140
147
|
</div>
|
|
@@ -174,16 +181,18 @@ class VibeSurfModalManager {
|
|
|
174
181
|
const modalId = this.generateModalId();
|
|
175
182
|
|
|
176
183
|
const modalHTML = `
|
|
177
|
-
<div class="modal-overlay ${backdrop ? 'backdrop' : ''}" id="${modalId}-overlay">
|
|
184
|
+
<div class="modal-overlay dynamic-modal ${backdrop ? 'backdrop' : ''}" id="${modalId}-overlay">
|
|
178
185
|
<div class="modal ${className}" id="${modalId}">
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
186
|
+
<div class="modal-content">
|
|
187
|
+
${title || showCloseButton ? `
|
|
188
|
+
<div class="modal-header">
|
|
189
|
+
${title ? `<h3>${this.escapeHtml(title)}</h3>` : ''}
|
|
190
|
+
${showCloseButton ? `<button class="modal-close-btn" data-modal-id="${modalId}">×</button>` : ''}
|
|
191
|
+
</div>
|
|
192
|
+
` : ''}
|
|
193
|
+
<div class="modal-body">
|
|
194
|
+
${content}
|
|
183
195
|
</div>
|
|
184
|
-
` : ''}
|
|
185
|
-
<div class="modal-body">
|
|
186
|
-
${content}
|
|
187
196
|
</div>
|
|
188
197
|
</div>
|
|
189
198
|
</div>
|
|
@@ -221,6 +221,16 @@ class VibeSurfSessionManager {
|
|
|
221
221
|
|
|
222
222
|
const response = await this.apiClient.submitTask(taskPayload);
|
|
223
223
|
|
|
224
|
+
// Check if the response indicates LLM connection failure
|
|
225
|
+
if (response && response.success === false && response.error === 'llm_connection_failed') {
|
|
226
|
+
console.log('[SessionManager] LLM connection failed, emitting taskError');
|
|
227
|
+
this.emit('taskError', {
|
|
228
|
+
error: response,
|
|
229
|
+
sessionId: this.currentSession.id
|
|
230
|
+
});
|
|
231
|
+
throw new Error(response.message || 'LLM connection failed');
|
|
232
|
+
}
|
|
233
|
+
|
|
224
234
|
// Update current session with task info
|
|
225
235
|
this.currentSession.currentTask = {
|
|
226
236
|
taskId: response.task_id,
|
|
@@ -1206,6 +1206,38 @@ class VibeSurfSettingsManager {
|
|
|
1206
1206
|
this.elements.backendUrl.value = url;
|
|
1207
1207
|
}
|
|
1208
1208
|
}
|
|
1209
|
+
|
|
1210
|
+
// Navigate to specific LLM profile for editing
|
|
1211
|
+
async navigateToLLMProfile(profileName) {
|
|
1212
|
+
console.log('[SettingsManager] Navigating to LLM profile:', profileName);
|
|
1213
|
+
|
|
1214
|
+
// First show the settings modal
|
|
1215
|
+
this.showSettings();
|
|
1216
|
+
|
|
1217
|
+
// Switch to LLM profiles tab
|
|
1218
|
+
const llmTab = document.querySelector('.settings-tab[data-tab="llm-profiles"]');
|
|
1219
|
+
if (llmTab) {
|
|
1220
|
+
llmTab.click(); // This will trigger handleTabSwitch
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
// Wait a moment for tab switching to complete
|
|
1224
|
+
setTimeout(async () => {
|
|
1225
|
+
// Find the profile in the current state
|
|
1226
|
+
const profile = this.state.llmProfiles.find(p => p.profile_name === profileName);
|
|
1227
|
+
|
|
1228
|
+
if (profile) {
|
|
1229
|
+
// Show the edit form for this profile
|
|
1230
|
+
await this.showProfileForm('llm', profile);
|
|
1231
|
+
console.log('[SettingsManager] LLM profile edit form shown for:', profileName);
|
|
1232
|
+
} else {
|
|
1233
|
+
console.warn('[SettingsManager] Profile not found:', profileName);
|
|
1234
|
+
this.emit('notification', {
|
|
1235
|
+
message: `LLM profile "${profileName}" not found. Please check if it still exists.`,
|
|
1236
|
+
type: 'warning'
|
|
1237
|
+
});
|
|
1238
|
+
}
|
|
1239
|
+
}, 100);
|
|
1240
|
+
}
|
|
1209
1241
|
}
|
|
1210
1242
|
|
|
1211
1243
|
// Export for use in other modules
|
|
@@ -359,6 +359,27 @@ class VibeSurfUIManager {
|
|
|
359
359
|
|
|
360
360
|
handleTaskError(data) {
|
|
361
361
|
console.error('[UIManager] Task error:', data.error);
|
|
362
|
+
console.log('[UIManager] Task error data structure:', JSON.stringify(data, null, 2));
|
|
363
|
+
|
|
364
|
+
// Check if this is an LLM connection failure
|
|
365
|
+
if (data.error && typeof data.error === 'object' && data.error.error === 'llm_connection_failed') {
|
|
366
|
+
console.log('[UIManager] Detected LLM connection failure from object error');
|
|
367
|
+
// Show LLM connection failed modal instead of generic notification
|
|
368
|
+
this.showLLMConnectionFailedModal(data.error);
|
|
369
|
+
this.updateControlPanel('ready'); // Reset UI since task failed to start
|
|
370
|
+
return;
|
|
371
|
+
} else if (data.error && typeof data.error === 'string' && data.error.includes('llm_connection_failed')) {
|
|
372
|
+
console.log('[UIManager] Detected LLM connection failure from string error');
|
|
373
|
+
// Handle case where error is a string containing the error type
|
|
374
|
+
this.showLLMConnectionFailedModal({
|
|
375
|
+
message: data.error,
|
|
376
|
+
llm_profile: 'unknown'
|
|
377
|
+
});
|
|
378
|
+
this.updateControlPanel('ready'); // Reset UI since task failed to start
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// Default error handling for other types of errors
|
|
362
383
|
this.showNotification(`Task error: ${data.error}`, 'error');
|
|
363
384
|
|
|
364
385
|
this.updateControlPanel('error');
|
|
@@ -718,7 +739,12 @@ class VibeSurfUIManager {
|
|
|
718
739
|
// Clear uploaded files after successful task submission
|
|
719
740
|
this.fileManager.clearUploadedFiles();
|
|
720
741
|
} catch (error) {
|
|
721
|
-
this
|
|
742
|
+
// Check if this is an LLM connection failure from API response
|
|
743
|
+
if (error.data && error.data.error === 'llm_connection_failed') {
|
|
744
|
+
this.showLLMConnectionFailedModal(error.data);
|
|
745
|
+
} else {
|
|
746
|
+
this.showNotification(`Failed to submit task: ${error.message}`, 'error');
|
|
747
|
+
}
|
|
722
748
|
}
|
|
723
749
|
}
|
|
724
750
|
|
|
@@ -1686,6 +1712,46 @@ class VibeSurfUIManager {
|
|
|
1686
1712
|
this.modalManager.showWarningModal(title, message, options);
|
|
1687
1713
|
}
|
|
1688
1714
|
|
|
1715
|
+
showLLMConnectionFailedModal(errorData) {
|
|
1716
|
+
console.log('[UIManager] showLLMConnectionFailedModal called with:', errorData);
|
|
1717
|
+
|
|
1718
|
+
const llmProfile = errorData.llm_profile || 'unknown';
|
|
1719
|
+
const errorMessage = errorData.message || 'Cannot connect to LLM API';
|
|
1720
|
+
|
|
1721
|
+
const title = 'LLM Connection Failed';
|
|
1722
|
+
const message = `${errorMessage}\n\nThe LLM profile "${llmProfile}" cannot be reached. Please check your LLM configuration and API credentials.`;
|
|
1723
|
+
|
|
1724
|
+
const options = {
|
|
1725
|
+
confirmText: 'Update LLM Profile',
|
|
1726
|
+
cancelText: 'Cancel',
|
|
1727
|
+
onConfirm: () => {
|
|
1728
|
+
// Navigate to the specific LLM profile edit page
|
|
1729
|
+
this.handleShowLLMProfileSettings(llmProfile);
|
|
1730
|
+
}
|
|
1731
|
+
};
|
|
1732
|
+
|
|
1733
|
+
console.log('[UIManager] Showing LLM connection failed modal');
|
|
1734
|
+
this.modalManager.showWarningModal(title, message, options);
|
|
1735
|
+
}
|
|
1736
|
+
|
|
1737
|
+
async handleShowLLMProfileSettings(profileName) {
|
|
1738
|
+
// Enhanced task running check
|
|
1739
|
+
const statusCheck = await this.checkTaskStatus();
|
|
1740
|
+
if (statusCheck.isRunning) {
|
|
1741
|
+
const canProceed = await this.showTaskRunningWarning('access settings');
|
|
1742
|
+
if (!canProceed) return;
|
|
1743
|
+
}
|
|
1744
|
+
|
|
1745
|
+
// Show settings and navigate to the specific LLM profile
|
|
1746
|
+
this.settingsManager.showSettings();
|
|
1747
|
+
|
|
1748
|
+
// Navigate to LLM profiles section and edit the specific profile
|
|
1749
|
+
// We'll add a method to settings manager to handle this
|
|
1750
|
+
if (this.settingsManager.navigateToLLMProfile) {
|
|
1751
|
+
this.settingsManager.navigateToLLMProfile(profileName);
|
|
1752
|
+
}
|
|
1753
|
+
}
|
|
1754
|
+
|
|
1689
1755
|
// Loading and notifications
|
|
1690
1756
|
showLoading(message = 'Loading...') {
|
|
1691
1757
|
this.state.isLoading = true;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* Components CSS - Modals, Forms, and Complex Components */
|
|
2
2
|
|
|
3
|
-
/* Modal Styles */
|
|
3
|
+
/* Modal Styles - Base modal container */
|
|
4
4
|
.modal {
|
|
5
5
|
position: fixed;
|
|
6
6
|
top: 0;
|
|
@@ -14,6 +14,12 @@
|
|
|
14
14
|
padding: var(--spacing-lg);
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
/* Hidden state for static modals */
|
|
18
|
+
.modal.hidden {
|
|
19
|
+
display: none;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/* Legacy modal overlay for existing modals */
|
|
17
23
|
.modal-overlay {
|
|
18
24
|
position: absolute;
|
|
19
25
|
top: 0;
|
|
@@ -24,6 +30,30 @@
|
|
|
24
30
|
backdrop-filter: blur(4px);
|
|
25
31
|
}
|
|
26
32
|
|
|
33
|
+
/* Dynamic modal overlay for modal-manager.js */
|
|
34
|
+
.modal-overlay.dynamic-modal {
|
|
35
|
+
position: fixed;
|
|
36
|
+
top: 0;
|
|
37
|
+
left: 0;
|
|
38
|
+
right: 0;
|
|
39
|
+
bottom: 0;
|
|
40
|
+
z-index: 10000;
|
|
41
|
+
display: flex;
|
|
42
|
+
align-items: center;
|
|
43
|
+
justify-content: center;
|
|
44
|
+
padding: var(--spacing-lg);
|
|
45
|
+
background-color: rgba(0, 0, 0, 0.5);
|
|
46
|
+
backdrop-filter: blur(4px);
|
|
47
|
+
opacity: 0;
|
|
48
|
+
visibility: hidden;
|
|
49
|
+
transition: all var(--transition-fast);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.modal-overlay.dynamic-modal.show {
|
|
53
|
+
opacity: 1;
|
|
54
|
+
visibility: visible;
|
|
55
|
+
}
|
|
56
|
+
|
|
27
57
|
.modal-content {
|
|
28
58
|
position: relative;
|
|
29
59
|
width: 100%;
|
|
@@ -37,6 +67,40 @@
|
|
|
37
67
|
overflow: hidden;
|
|
38
68
|
}
|
|
39
69
|
|
|
70
|
+
/* Modal Body - for dynamic modals from modal-manager.js */
|
|
71
|
+
.modal-body {
|
|
72
|
+
flex: 1;
|
|
73
|
+
padding: var(--spacing-lg) var(--spacing-xl);
|
|
74
|
+
overflow-y: auto;
|
|
75
|
+
display: flex;
|
|
76
|
+
flex-direction: column;
|
|
77
|
+
align-items: center;
|
|
78
|
+
text-align: center;
|
|
79
|
+
gap: var(--spacing-md);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.modal-body .warning-icon {
|
|
83
|
+
font-size: 2rem;
|
|
84
|
+
margin-bottom: var(--spacing-sm);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.modal-body p {
|
|
88
|
+
font-size: var(--font-size-sm);
|
|
89
|
+
color: var(--text-primary);
|
|
90
|
+
line-height: 1.6;
|
|
91
|
+
margin: 0;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/* Modal Footer - for dynamic modals from modal-manager.js */
|
|
95
|
+
.modal-footer {
|
|
96
|
+
display: flex;
|
|
97
|
+
gap: var(--spacing-sm);
|
|
98
|
+
justify-content: center;
|
|
99
|
+
padding: var(--spacing-lg) var(--spacing-xl) var(--spacing-xl);
|
|
100
|
+
border-top: 1px solid var(--border-color);
|
|
101
|
+
background-color: var(--bg-secondary);
|
|
102
|
+
}
|
|
103
|
+
|
|
40
104
|
.modal-header {
|
|
41
105
|
display: flex;
|
|
42
106
|
align-items: center;
|
|
@@ -70,6 +134,28 @@
|
|
|
70
134
|
color: var(--text-primary);
|
|
71
135
|
}
|
|
72
136
|
|
|
137
|
+
/* Modal Close Button - alternative class for dynamic modals */
|
|
138
|
+
.modal-close-btn {
|
|
139
|
+
display: flex;
|
|
140
|
+
align-items: center;
|
|
141
|
+
justify-content: center;
|
|
142
|
+
width: 32px;
|
|
143
|
+
height: 32px;
|
|
144
|
+
border: none;
|
|
145
|
+
border-radius: var(--radius-md);
|
|
146
|
+
background-color: transparent;
|
|
147
|
+
color: var(--text-secondary);
|
|
148
|
+
cursor: pointer;
|
|
149
|
+
transition: all var(--transition-fast);
|
|
150
|
+
font-size: 18px;
|
|
151
|
+
line-height: 1;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
.modal-close-btn:hover {
|
|
155
|
+
background-color: var(--bg-hover);
|
|
156
|
+
color: var(--text-primary);
|
|
157
|
+
}
|
|
158
|
+
|
|
73
159
|
/* History Modal */
|
|
74
160
|
.history-list {
|
|
75
161
|
flex: 1;
|
|
@@ -505,6 +591,47 @@
|
|
|
505
591
|
color: var(--text-secondary);
|
|
506
592
|
}
|
|
507
593
|
|
|
594
|
+
/* Button Styles for Dynamic Modals */
|
|
595
|
+
.btn-primary, .btn-secondary {
|
|
596
|
+
padding: 12px 24px;
|
|
597
|
+
border: none;
|
|
598
|
+
border-radius: var(--radius-md);
|
|
599
|
+
font-size: var(--font-size-sm);
|
|
600
|
+
font-weight: var(--font-weight-medium);
|
|
601
|
+
cursor: pointer;
|
|
602
|
+
transition: all var(--transition-fast);
|
|
603
|
+
min-width: 100px;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
.btn-primary {
|
|
607
|
+
background-color: var(--primary-color);
|
|
608
|
+
color: white;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
.btn-primary:hover {
|
|
612
|
+
background-color: var(--primary-hover);
|
|
613
|
+
transform: translateY(-1px);
|
|
614
|
+
box-shadow: 0 4px 16px rgba(0, 122, 204, 0.3);
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
.btn-secondary {
|
|
618
|
+
background-color: var(--bg-primary);
|
|
619
|
+
color: var(--text-primary);
|
|
620
|
+
border: 1px solid var(--border-color);
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
.btn-secondary:hover {
|
|
624
|
+
background-color: var(--bg-hover);
|
|
625
|
+
border-color: var(--border-hover);
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
.btn-primary:disabled, .btn-secondary:disabled {
|
|
629
|
+
opacity: 0.5;
|
|
630
|
+
cursor: not-allowed;
|
|
631
|
+
transform: none !important;
|
|
632
|
+
box-shadow: none !important;
|
|
633
|
+
}
|
|
634
|
+
|
|
508
635
|
/* Tooltip */
|
|
509
636
|
.tooltip {
|
|
510
637
|
position: relative;
|
|
@@ -578,22 +705,53 @@
|
|
|
578
705
|
max-width: 300px;
|
|
579
706
|
}
|
|
580
707
|
|
|
581
|
-
/*
|
|
582
|
-
.warning-modal {
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
background-color: rgba(
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
708
|
+
/* Dynamic Modal Styles for modal-manager.js */
|
|
709
|
+
.modal-overlay.dynamic-modal .modal.warning-modal .modal-content {
|
|
710
|
+
border: 2px solid var(--warning-color);
|
|
711
|
+
animation: modalSlideIn 0.3s ease-out;
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
.modal-overlay.dynamic-modal .modal.warning-modal .modal-header {
|
|
715
|
+
background-color: rgba(255, 193, 7, 0.1);
|
|
716
|
+
border-bottom: 1px solid rgba(255, 193, 7, 0.2);
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
.modal-overlay.dynamic-modal .modal.warning-modal .modal-header h3 {
|
|
720
|
+
color: var(--warning-color);
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
.modal-overlay.dynamic-modal .modal.warning-modal .warning-icon {
|
|
724
|
+
color: var(--warning-color);
|
|
725
|
+
filter: drop-shadow(0 2px 4px rgba(255, 193, 7, 0.3));
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
/* Confirm Modal enhancements */
|
|
729
|
+
.modal-overlay.dynamic-modal .modal.confirm-modal .modal-content {
|
|
730
|
+
animation: modalSlideIn 0.3s ease-out;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
/* Modal Animation */
|
|
734
|
+
@keyframes modalSlideIn {
|
|
735
|
+
from {
|
|
736
|
+
opacity: 0;
|
|
737
|
+
transform: translateY(-20px) scale(0.95);
|
|
738
|
+
}
|
|
739
|
+
to {
|
|
740
|
+
opacity: 1;
|
|
741
|
+
transform: translateY(0) scale(1);
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
/* Z-index management for different modal types */
|
|
746
|
+
.modal-overlay.dynamic-modal {
|
|
747
|
+
z-index: 10000; /* Higher than static modals */
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
.modal-overlay.dynamic-modal.warning-modal {
|
|
751
|
+
z-index: 15000; /* Highest priority for warning modals */
|
|
595
752
|
}
|
|
596
753
|
|
|
754
|
+
/* Legacy warning modal classes - keeping for backward compatibility but not used by modal-manager.js */
|
|
597
755
|
.warning-content {
|
|
598
756
|
background-color: var(--bg-primary);
|
|
599
757
|
border-radius: var(--radius-xl);
|
|
@@ -614,11 +772,6 @@
|
|
|
614
772
|
border-bottom: 1px solid rgba(255, 193, 7, 0.2);
|
|
615
773
|
}
|
|
616
774
|
|
|
617
|
-
.warning-icon {
|
|
618
|
-
font-size: 1.5rem;
|
|
619
|
-
color: var(--warning-color);
|
|
620
|
-
}
|
|
621
|
-
|
|
622
775
|
.warning-header h3 {
|
|
623
776
|
font-size: var(--font-size-lg);
|
|
624
777
|
font-weight: var(--font-weight-semibold);
|
|
@@ -256,101 +256,7 @@ select.task-running-disabled {
|
|
|
256
256
|
font-size: 14px;
|
|
257
257
|
}
|
|
258
258
|
|
|
259
|
-
/*
|
|
260
|
-
.modal-overlay.warning-modal {
|
|
261
|
-
z-index: 15000;
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
.warning-modal .modal-content {
|
|
265
|
-
max-width: 500px;
|
|
266
|
-
background: white;
|
|
267
|
-
border-radius: 12px;
|
|
268
|
-
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.2);
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
.warning-header {
|
|
272
|
-
display: flex;
|
|
273
|
-
align-items: center;
|
|
274
|
-
gap: 12px;
|
|
275
|
-
padding: 20px 20px 0;
|
|
276
|
-
border-bottom: 1px solid #eee;
|
|
277
|
-
margin-bottom: 16px;
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
.warning-icon {
|
|
281
|
-
font-size: 24px;
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
.warning-header h3 {
|
|
285
|
-
margin: 0;
|
|
286
|
-
color: #dc3545;
|
|
287
|
-
font-size: 18px;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
.warning-body {
|
|
291
|
-
padding: 0 20px 16px;
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
.warning-body p {
|
|
295
|
-
margin: 0 0 12px;
|
|
296
|
-
color: #666;
|
|
297
|
-
line-height: 1.5;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
.warning-details {
|
|
301
|
-
background: #f8f9fa;
|
|
302
|
-
border: 1px solid #e9ecef;
|
|
303
|
-
border-radius: 4px;
|
|
304
|
-
padding: 12px;
|
|
305
|
-
font-family: monospace;
|
|
306
|
-
font-size: 12px;
|
|
307
|
-
color: #495057;
|
|
308
|
-
white-space: pre-wrap;
|
|
309
|
-
margin: 0;
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
.warning-actions {
|
|
313
|
-
display: flex;
|
|
314
|
-
gap: 8px;
|
|
315
|
-
padding: 16px 20px 20px;
|
|
316
|
-
justify-content: flex-end;
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
.warning-actions .btn {
|
|
320
|
-
padding: 8px 16px;
|
|
321
|
-
border: none;
|
|
322
|
-
border-radius: 6px;
|
|
323
|
-
font-size: 14px;
|
|
324
|
-
cursor: pointer;
|
|
325
|
-
transition: all 0.2s ease;
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
.warning-actions .btn-danger {
|
|
329
|
-
background: #dc3545;
|
|
330
|
-
color: white;
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
.warning-actions .btn-danger:hover {
|
|
334
|
-
background: #c82333;
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
.warning-actions .btn-secondary {
|
|
338
|
-
background: #6c757d;
|
|
339
|
-
color: white;
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
.warning-actions .btn-secondary:hover {
|
|
343
|
-
background: #545b62;
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
.warning-actions .btn-primary {
|
|
347
|
-
background: #007bff;
|
|
348
|
-
color: white;
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
.warning-actions .btn-primary:hover {
|
|
352
|
-
background: #0056b3;
|
|
353
|
-
}
|
|
259
|
+
/* Warning modal styles removed - consolidated in components.css to avoid conflicts */
|
|
354
260
|
|
|
355
261
|
/* File Upload Styles */
|
|
356
262
|
.uploaded-files-container {
|
vibe_surf/cli.py
CHANGED
|
@@ -341,7 +341,8 @@ def start_backend(port: int) -> None:
|
|
|
341
341
|
def get_browser_execution_path() -> Optional[str]:
|
|
342
342
|
"""Get browser execution path from envs.json or environment variables."""
|
|
343
343
|
# 1. Load environment variables
|
|
344
|
-
from .common import get_workspace_dir
|
|
344
|
+
from vibe_surf.common import get_workspace_dir
|
|
345
|
+
|
|
345
346
|
workspace_dir = get_workspace_dir()
|
|
346
347
|
os.makedirs(workspace_dir, exist_ok=True)
|
|
347
348
|
logger.info("WorkSpace directory: {}".format(workspace_dir))
|
|
@@ -53,25 +53,41 @@ from browser_use.llm.views import ChatInvokeCompletion, ChatInvokeUsage
|
|
|
53
53
|
|
|
54
54
|
T = TypeVar('T', bound=BaseModel)
|
|
55
55
|
|
|
56
|
+
from vibe_surf.logger import get_logger
|
|
57
|
+
|
|
58
|
+
logger = get_logger(__name__)
|
|
59
|
+
|
|
56
60
|
|
|
57
61
|
@dataclass
|
|
58
62
|
class ChatOpenAICompatible(ChatOpenAI):
|
|
59
63
|
"""
|
|
60
|
-
OpenAI-compatible chat model with automatic
|
|
64
|
+
OpenAI-compatible chat model with automatic schema fix support for Gemini, Kimi, and Qwen models.
|
|
61
65
|
|
|
62
|
-
This class extends browser_use's ChatOpenAI to automatically detect
|
|
66
|
+
This class extends browser_use's ChatOpenAI to automatically detect special models
|
|
63
67
|
and apply the necessary schema fixes to work with OpenAI-compatible APIs.
|
|
64
68
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
+
Supported models:
|
|
70
|
+
- Gemini models: Removes 'additionalProperties', 'title', 'default' and resolves $ref
|
|
71
|
+
- Kimi/Moonshot models: Removes 'min_items', 'max_items', 'minItems', 'maxItems', 'default' with anyOf
|
|
72
|
+
- Qwen models: Ensures 'json' keyword is present in messages when using response_format
|
|
73
|
+
|
|
74
|
+
The class automatically detects the model type and applies appropriate fixes.
|
|
69
75
|
"""
|
|
70
76
|
|
|
71
77
|
def _is_gemini_model(self) -> bool:
|
|
72
78
|
"""Check if the current model is a Gemini model."""
|
|
73
79
|
return str(self.model).lower().startswith('gemini')
|
|
74
80
|
|
|
81
|
+
def _is_kimi_model(self) -> bool:
|
|
82
|
+
"""Check if the current model is a Kimi/Moonshot model."""
|
|
83
|
+
model_str = str(self.model).lower()
|
|
84
|
+
return 'kimi' in model_str or 'moonshot' in model_str
|
|
85
|
+
|
|
86
|
+
def _is_qwen_model(self) -> bool:
|
|
87
|
+
"""Check if the current model is a Qwen model."""
|
|
88
|
+
model_str = str(self.model).lower()
|
|
89
|
+
return 'qwen' in model_str
|
|
90
|
+
|
|
75
91
|
def _fix_gemini_schema(self, schema: dict[str, Any]) -> dict[str, Any]:
|
|
76
92
|
"""
|
|
77
93
|
Convert a Pydantic model to a Gemini-compatible schema.
|
|
@@ -147,6 +163,43 @@ class ChatOpenAICompatible(ChatOpenAI):
|
|
|
147
163
|
|
|
148
164
|
return clean_schema(schema)
|
|
149
165
|
|
|
166
|
+
def _fix_kimi_schema(self, schema: dict[str, Any]) -> dict[str, Any]:
|
|
167
|
+
"""
|
|
168
|
+
Convert a Pydantic model to a Kimi/Moonshot-compatible schema.
|
|
169
|
+
|
|
170
|
+
This function removes unsupported keywords like 'min_items' that Moonshot API doesn't support.
|
|
171
|
+
|
|
172
|
+
Args:
|
|
173
|
+
schema: The original JSON schema
|
|
174
|
+
|
|
175
|
+
Returns:
|
|
176
|
+
A cleaned schema compatible with Moonshot API
|
|
177
|
+
"""
|
|
178
|
+
|
|
179
|
+
def clean_schema(obj: Any) -> Any:
|
|
180
|
+
if isinstance(obj, dict):
|
|
181
|
+
cleaned = {}
|
|
182
|
+
has_any_of = 'anyOf' in obj
|
|
183
|
+
|
|
184
|
+
for key, value in obj.items():
|
|
185
|
+
# Remove unsupported keywords for Moonshot
|
|
186
|
+
if key in ['min_items', 'minItems']:
|
|
187
|
+
continue
|
|
188
|
+
# Remove 'default' when 'anyOf' is present (Moonshot restriction)
|
|
189
|
+
elif key == 'default' and has_any_of:
|
|
190
|
+
continue
|
|
191
|
+
# Remove other problematic keywords
|
|
192
|
+
elif key in ['title', 'additionalProperties']:
|
|
193
|
+
continue
|
|
194
|
+
else:
|
|
195
|
+
cleaned[key] = clean_schema(value)
|
|
196
|
+
return cleaned
|
|
197
|
+
elif isinstance(obj, list):
|
|
198
|
+
return [clean_schema(item) for item in obj]
|
|
199
|
+
return obj
|
|
200
|
+
|
|
201
|
+
return clean_schema(schema)
|
|
202
|
+
|
|
150
203
|
@overload
|
|
151
204
|
async def ainvoke(self, messages: list[BaseMessage], output_format: None = None) -> ChatInvokeCompletion[str]:
|
|
152
205
|
...
|
|
@@ -168,9 +221,9 @@ class ChatOpenAICompatible(ChatOpenAI):
|
|
|
168
221
|
Returns:
|
|
169
222
|
Either a string response or an instance of output_format
|
|
170
223
|
"""
|
|
171
|
-
# If this is not a
|
|
224
|
+
# If this is not a special model or no structured output is requested,
|
|
172
225
|
# use the parent implementation directly
|
|
173
|
-
if not self._is_gemini_model() or output_format is None:
|
|
226
|
+
if not (self._is_gemini_model() or self._is_kimi_model()) or output_format is None:
|
|
174
227
|
return await super().ainvoke(messages, output_format)
|
|
175
228
|
|
|
176
229
|
openai_messages = OpenAIMessageSerializer.serialize_messages(messages)
|
|
@@ -217,7 +270,17 @@ class ChatOpenAICompatible(ChatOpenAI):
|
|
|
217
270
|
|
|
218
271
|
else:
|
|
219
272
|
original_schema = SchemaOptimizer.create_optimized_json_schema(output_format)
|
|
220
|
-
|
|
273
|
+
|
|
274
|
+
# Apply appropriate schema fix based on model type
|
|
275
|
+
if self._is_gemini_model():
|
|
276
|
+
logger.debug(f"🔧 Applying Gemini schema fixes for model: {self.model}")
|
|
277
|
+
fixed_schema = self._fix_gemini_schema(original_schema)
|
|
278
|
+
elif self._is_kimi_model():
|
|
279
|
+
logger.debug(f"🔧 Applying Kimi/Moonshot schema fixes for model: {self.model}")
|
|
280
|
+
fixed_schema = self._fix_kimi_schema(original_schema)
|
|
281
|
+
else:
|
|
282
|
+
fixed_schema = original_schema
|
|
283
|
+
|
|
221
284
|
response_format: JSONSchema = {
|
|
222
285
|
'name': 'agent_output',
|
|
223
286
|
'strict': True,
|
vibe_surf/tools/file_system.py
CHANGED
|
@@ -229,6 +229,26 @@ class CustomFileSystem(FileSystem):
|
|
|
229
229
|
pattern = rf'^[a-zA-Z0-9_\-]+\.({extensions})$'
|
|
230
230
|
return bool(re.match(pattern, file_name))
|
|
231
231
|
|
|
232
|
+
async def append_file(self, full_filename: str, content: str) -> str:
|
|
233
|
+
"""Append content to file using file-specific append method"""
|
|
234
|
+
if not self._is_valid_filename(full_filename):
|
|
235
|
+
return INVALID_FILENAME_ERROR_MESSAGE
|
|
236
|
+
|
|
237
|
+
full_path = self.data_dir / full_filename
|
|
238
|
+
is_file_exist = await self.file_exist(full_filename)
|
|
239
|
+
if not is_file_exist:
|
|
240
|
+
return f"File '{full_filename}' not found."
|
|
241
|
+
|
|
242
|
+
try:
|
|
243
|
+
with open(str(full_path), encoding='utf-8', mode='a') as f:
|
|
244
|
+
f.write(content)
|
|
245
|
+
|
|
246
|
+
return f'Data appended to file {full_filename} successfully.'
|
|
247
|
+
except FileSystemError as e:
|
|
248
|
+
return str(e)
|
|
249
|
+
except Exception as e:
|
|
250
|
+
return f"Error: Could not append to file '{full_filename}'. {str(e)}"
|
|
251
|
+
|
|
232
252
|
async def write_file(self, full_filename: str, content: str) -> str:
|
|
233
253
|
"""Write content to file using file-specific write method"""
|
|
234
254
|
if not self._is_valid_filename(full_filename):
|
|
@@ -249,8 +269,9 @@ class CustomFileSystem(FileSystem):
|
|
|
249
269
|
file_obj = file_class(name=name_without_ext)
|
|
250
270
|
self.files[full_filename] = file_obj # Use full filename as key
|
|
251
271
|
|
|
252
|
-
|
|
253
|
-
|
|
272
|
+
with open(str(full_path), encoding='utf-8', mode='w') as f:
|
|
273
|
+
f.write(content)
|
|
274
|
+
|
|
254
275
|
return f'Data written to file {full_filename} successfully.'
|
|
255
276
|
except FileSystemError as e:
|
|
256
277
|
return str(e)
|
|
@@ -282,7 +303,8 @@ class CustomFileSystem(FileSystem):
|
|
|
282
303
|
self.files[full_filename] = file_obj # Use full filename as key
|
|
283
304
|
|
|
284
305
|
# Use file-specific write method
|
|
285
|
-
|
|
306
|
+
with open(str(full_path), encoding='utf-8', mode='w') as f:
|
|
307
|
+
f.write('')
|
|
286
308
|
return f'Create file {full_filename} successfully.'
|
|
287
309
|
except FileSystemError as e:
|
|
288
310
|
return str(e)
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
vibe_surf/__init__.py,sha256=WtduuMFGauMD_9dpk4fnRnLTAP6ka9Lfu0feAFNzLfo,339
|
|
2
|
-
vibe_surf/_version.py,sha256=
|
|
3
|
-
vibe_surf/cli.py,sha256=
|
|
2
|
+
vibe_surf/_version.py,sha256=Xz5RLbyPcCHHXte393JYfUy4Dt7uaeWyrGVw9SmJ0eg,706
|
|
3
|
+
vibe_surf/cli.py,sha256=pbep2dBeQqralZ8AggkH4h2nayBarbdN8lhZxo35gNU,16689
|
|
4
4
|
vibe_surf/common.py,sha256=_WWMxen5wFwzUjEShn3yDVC1OBFUiJ6Vccadi6tuG6w,1215
|
|
5
5
|
vibe_surf/logger.py,sha256=k53MFA96QX6t9OfcOf1Zws8PP0OOqjVJfhUD3Do9lKw,3043
|
|
6
6
|
vibe_surf/agents/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
7
|
vibe_surf/agents/browser_use_agent.py,sha256=1gbljYbB8drL_vZm2jFs5d3cZHWfB6CRIT6blwiPvhs,46024
|
|
8
8
|
vibe_surf/agents/report_writer_agent.py,sha256=tKELps-FjZq06OKXpoyS-zJzvfdYXM6UnXDrR2oZk6M,20970
|
|
9
|
-
vibe_surf/agents/vibe_surf_agent.py,sha256=
|
|
9
|
+
vibe_surf/agents/vibe_surf_agent.py,sha256=GRCqf198nqvS5HRu5Dv89qjiS-2Jn39KJR1r1Ih6vJQ,73115
|
|
10
10
|
vibe_surf/agents/views.py,sha256=bcVOxd9On2lJ4NSar5RqP1V7Nw_RBtkHJPE-f2kVdRA,4750
|
|
11
11
|
vibe_surf/agents/prompts/__init__.py,sha256=l4ieA0D8kLJthyNN85FKLNe4ExBa3stY3l-aImLDRD0,36
|
|
12
12
|
vibe_surf/agents/prompts/report_writer_prompt.py,sha256=sZE8MUT1CDLmRzbnbEQzAvTwJjpITgh2Q8g1_eXmkzE,4454
|
|
@@ -14,14 +14,14 @@ vibe_surf/agents/prompts/vibe_surf_prompt.py,sha256=8bC7IJNbK7_jLbxjVsta3urZIi-F
|
|
|
14
14
|
vibe_surf/backend/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
15
|
vibe_surf/backend/llm_config.py,sha256=9V8Gg065TQALbOKQnOqFWd8RzOJjegOD8w6YOf90Q7Y,5036
|
|
16
16
|
vibe_surf/backend/main.py,sha256=nZDs_FkYU7NdqSm1O4OwHJslYIJZL1Gr3CEKa8ByaEY,7178
|
|
17
|
-
vibe_surf/backend/shared_state.py,sha256=
|
|
17
|
+
vibe_surf/backend/shared_state.py,sha256=yEBWj6YI6WjVF2nuJ_nujo8yqHw2j8xikmxr0G8fnbs,23331
|
|
18
18
|
vibe_surf/backend/api/__init__.py,sha256=XxF1jUOORpLYCfFuPrrnUGRnOrr6ClH0_MNPU-4RnSs,68
|
|
19
19
|
vibe_surf/backend/api/activity.py,sha256=_cnHusqolt5Hf3KdAf6FK-3sBc-TSaadmb5dJxGI57A,9398
|
|
20
20
|
vibe_surf/backend/api/browser.py,sha256=NXedyZG3NIVRIx5O7d9mHwVWX-Q4_KsX5mSgfKt8UEA,2122
|
|
21
21
|
vibe_surf/backend/api/config.py,sha256=EwzxYvC6HlaVo2OFWjtBmBMjX4eW2q8hp7l2LO2GZV0,27124
|
|
22
22
|
vibe_surf/backend/api/files.py,sha256=kJMG9MWECKXwGh64Q6xvAzNjeZGcLhIEnn65HiMZHKE,11762
|
|
23
23
|
vibe_surf/backend/api/models.py,sha256=iyMBdpDkOfwx0h2yRkjmYjRdFyDErhO8XZcWTurdcmU,10347
|
|
24
|
-
vibe_surf/backend/api/task.py,sha256=
|
|
24
|
+
vibe_surf/backend/api/task.py,sha256=xxZC-SoRBwzoAMpCDZc7YAxBlehkGC7_s8bMsfA0cs0,14058
|
|
25
25
|
vibe_surf/backend/database/__init__.py,sha256=XhmcscnhgMhUyXML7m4SnuQIqkFpyY_zJ0D3yYa2RqQ,239
|
|
26
26
|
vibe_surf/backend/database/manager.py,sha256=aC8XQc68qDsaryrux7VfCEl2hnEZL5kfnEkXWXPg2ig,4427
|
|
27
27
|
vibe_surf/backend/database/models.py,sha256=mePuHsaSqJKA4TthvXbup_Ioann2_chxywiLKqAWyh4,7009
|
|
@@ -32,7 +32,7 @@ vibe_surf/backend/migrations/init_db.py,sha256=pY2Yq7K1vPxqT8r3jlAQcYEQWK-GGbb0F
|
|
|
32
32
|
vibe_surf/backend/migrations/seed_data.py,sha256=L6Ll-u8P4cICAUlD5y9urQPSUld6M67erSBCEIdw8Uc,8239
|
|
33
33
|
vibe_surf/backend/utils/__init__.py,sha256=V8leMFp7apAglUAoCHPZrNNcRHthSLYIudIJE5qwjb0,184
|
|
34
34
|
vibe_surf/backend/utils/encryption.py,sha256=CjLNh_n0Luhfa-6BB-icfzkiiDqj5b4Gu6MADU3p2eM,3754
|
|
35
|
-
vibe_surf/backend/utils/llm_factory.py,sha256=
|
|
35
|
+
vibe_surf/backend/utils/llm_factory.py,sha256=mNy8o3sw7vYJ8gwiTsrgXbG7Ri_B11ylE4KGcfHULp8,8972
|
|
36
36
|
vibe_surf/browser/__init__.py,sha256=_UToO2fZfSCrfjOcxhn4Qq7ZLbYeyPuUUEmqIva-Yv8,325
|
|
37
37
|
vibe_surf/browser/agen_browser_profile.py,sha256=4H4qDjqLqHAfMi3niBI9fd0S1GlVDWYDUUOrEQoNkl8,5677
|
|
38
38
|
vibe_surf/browser/agent_browser_session.py,sha256=rptz2MdXspS44zC21nJaLOmp5aEWsvBfvzpDk5Jr-mA,33884
|
|
@@ -52,21 +52,21 @@ vibe_surf/chrome_extension/icons/convert-svg.js,sha256=j137nZA7MJK35NtrwWff8yb3U
|
|
|
52
52
|
vibe_surf/chrome_extension/icons/logo-preview.html,sha256=hrgU45uziKHKIb8be9P4ZrZJyGggWnm2M5oEu89V5sM,6962
|
|
53
53
|
vibe_surf/chrome_extension/icons/logo.icns,sha256=ZzY1eIKF4dNhNW4CeE1UBQloxNVC7bQx3qcClo3CnMQ,1569615
|
|
54
54
|
vibe_surf/chrome_extension/icons/logo.png,sha256=PLmv1E6sCGXUE5ZDxr-pFPQd9Gvaw_f1TnYmF8VIssU,566385
|
|
55
|
-
vibe_surf/chrome_extension/scripts/api-client.js,sha256=
|
|
55
|
+
vibe_surf/chrome_extension/scripts/api-client.js,sha256=YUnMZOKUcJaGFl8wCpGxgCSvBKqbwCtYbQLbI32otrY,13844
|
|
56
56
|
vibe_surf/chrome_extension/scripts/file-manager.js,sha256=WcEaeoECq84jXqEo3Q2ywmqppwSUVm-0dtrbTYXL07g,15809
|
|
57
57
|
vibe_surf/chrome_extension/scripts/history-manager.js,sha256=J_EiBKrRp2cTxuy0Sq5oCju5YuX0_2pGG6NWrJSjC24,22218
|
|
58
58
|
vibe_surf/chrome_extension/scripts/main.js,sha256=WhmCGktQoSl7aaMl8a9ysJJiysAjf12bWGypMucCSVg,16913
|
|
59
59
|
vibe_surf/chrome_extension/scripts/markdown-it.min.js,sha256=gZ3xe0BdCJplNiHWTKrm6AGZydPy34jJKZqFIf-7hIw,102948
|
|
60
|
-
vibe_surf/chrome_extension/scripts/modal-manager.js,sha256=
|
|
61
|
-
vibe_surf/chrome_extension/scripts/session-manager.js,sha256=
|
|
62
|
-
vibe_surf/chrome_extension/scripts/settings-manager.js,sha256=
|
|
63
|
-
vibe_surf/chrome_extension/scripts/ui-manager.js,sha256=
|
|
60
|
+
vibe_surf/chrome_extension/scripts/modal-manager.js,sha256=9ekFaTmy8sk94KK9HZJ7739ObsZ6ssllmRLP9rQxUKU,14187
|
|
61
|
+
vibe_surf/chrome_extension/scripts/session-manager.js,sha256=sXh4j4dv3eKOGYeov8iUPt-DX5jKQLhcASDXRX9qW5Y,21126
|
|
62
|
+
vibe_surf/chrome_extension/scripts/settings-manager.js,sha256=rgstYooxZtV9QjqP8KRRQclHbeiZJozZZfv80A6M_P4,48601
|
|
63
|
+
vibe_surf/chrome_extension/scripts/ui-manager.js,sha256=_qMrS_3_jZn7lDlXkTlF4CwOZxdg3B3Enww95PaGsHA,81526
|
|
64
64
|
vibe_surf/chrome_extension/styles/activity.css,sha256=aEFa_abskrDQvwsesPVOyJW3rUQUEBQpMKPHhY94CoA,19601
|
|
65
65
|
vibe_surf/chrome_extension/styles/animations.css,sha256=TLAet_xXBxCA-H36BWP4xBGBIVjbDdAj7Q6OPxPsbE8,7891
|
|
66
66
|
vibe_surf/chrome_extension/styles/base.css,sha256=d8nt7Wej1r57G6pnBIGKEVkepFxlq8Ets0isngCf5w8,1296
|
|
67
|
-
vibe_surf/chrome_extension/styles/components.css,sha256=
|
|
67
|
+
vibe_surf/chrome_extension/styles/components.css,sha256=cbpYgT1ujG11axRa9xnHizgm4wfrpSzq_nkUOdfNZ0A,18495
|
|
68
68
|
vibe_surf/chrome_extension/styles/history-modal.css,sha256=xCg0EPz1N4kg47jnz9q9E1M4zGag8lJMt_msAOkZi3M,16583
|
|
69
|
-
vibe_surf/chrome_extension/styles/input.css,sha256=
|
|
69
|
+
vibe_surf/chrome_extension/styles/input.css,sha256=Sn2QulNV4jS2NJRByp7RVyZtCJbexw48AjLxgpB-XRw,10566
|
|
70
70
|
vibe_surf/chrome_extension/styles/layout.css,sha256=ggh-lWpP6sHubFkshf9dcmA52LiXw7VJmQXcM5_EGcY,3528
|
|
71
71
|
vibe_surf/chrome_extension/styles/responsive.css,sha256=a_SFX-PeOcenG4xMeoru5XFnnXdf0YhEyRzvTVVZHdI,8529
|
|
72
72
|
vibe_surf/chrome_extension/styles/settings-environment.css,sha256=OzgezmfZZ_yD60Jy9Uiq83mDmMCfk2dPPcZISb9Eqd4,3390
|
|
@@ -77,17 +77,17 @@ vibe_surf/chrome_extension/styles/settings-responsive.css,sha256=jLE0yG15n2aI6_6
|
|
|
77
77
|
vibe_surf/chrome_extension/styles/settings-utilities.css,sha256=3PuQS2857kg83d5erLbLdo_7J95-qV-qyNWS5M-w1oQ,505
|
|
78
78
|
vibe_surf/chrome_extension/styles/variables.css,sha256=VbFJ8L76AaJUqb6gCz9jEG_V6OX4YJa65I2iyeNJIw4,1411
|
|
79
79
|
vibe_surf/llm/__init__.py,sha256=_vDVPo6STf343p1SgMQrF5023hicAx0g83pK2Gbk4Ek,601
|
|
80
|
-
vibe_surf/llm/openai_compatible.py,sha256=
|
|
80
|
+
vibe_surf/llm/openai_compatible.py,sha256=uMnWcoZlBt_hMYgIkWFrwJmMH2wcswYkSLMPRcRd2dk,14854
|
|
81
81
|
vibe_surf/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
82
82
|
vibe_surf/tools/browser_use_tools.py,sha256=gWiBj-YPz7kQYi0yekuvpPQ2Z2-EBLuutF6rf71iOcM,28781
|
|
83
|
-
vibe_surf/tools/file_system.py,sha256=
|
|
83
|
+
vibe_surf/tools/file_system.py,sha256=gVdTd6qUfoyVfZMzmX9DXTyBbfXY56jVhUqtfm1qqps,18878
|
|
84
84
|
vibe_surf/tools/mcp_client.py,sha256=OeCoTgyx4MoY7JxXndK6pGHIoyFOhf5r7XCbx25y1Ec,2446
|
|
85
85
|
vibe_surf/tools/report_writer_tools.py,sha256=sUqUFr-_Rs8RJ0Bs77Hrp07kNwRIvHv7ErzSPYSlbTg,705
|
|
86
86
|
vibe_surf/tools/vibesurf_tools.py,sha256=uSsymefr-SrVAuDtvNGsB0Am4QeTuHuL-4KJABQW8dg,29880
|
|
87
87
|
vibe_surf/tools/views.py,sha256=KwzzXXZJqyW1oyszA0Z4buDDqAm-6PXsCAB0nB7z_sQ,4218
|
|
88
|
-
vibesurf-0.1.
|
|
89
|
-
vibesurf-0.1.
|
|
90
|
-
vibesurf-0.1.
|
|
91
|
-
vibesurf-0.1.
|
|
92
|
-
vibesurf-0.1.
|
|
93
|
-
vibesurf-0.1.
|
|
88
|
+
vibesurf-0.1.13.dist-info/licenses/LICENSE,sha256=czn6QYya0-jhLnStD9JqnMS-hwP5wRByipkrGTvoXLI,11355
|
|
89
|
+
vibesurf-0.1.13.dist-info/METADATA,sha256=PzpfRoPGUlDTpJI898y49j8lsx-ibdQNmD32H8W_dhU,5313
|
|
90
|
+
vibesurf-0.1.13.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
91
|
+
vibesurf-0.1.13.dist-info/entry_points.txt,sha256=UxqpvMocL-PR33S6vLF2OmXn-kVzM-DneMeZeHcPMM8,48
|
|
92
|
+
vibesurf-0.1.13.dist-info/top_level.txt,sha256=VPZGHqSb6EEqcJ4ZX6bHIuWfon5f6HXl3c7BYpbRqnY,10
|
|
93
|
+
vibesurf-0.1.13.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|