vibesurf 0.1.16__tar.gz → 0.1.18__tar.gz
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.
- {vibesurf-0.1.16 → vibesurf-0.1.18}/PKG-INFO +1 -1
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/_version.py +3 -3
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/agents/browser_use_agent.py +6 -12
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/agents/report_writer_agent.py +3 -3
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/agents/vibe_surf_agent.py +17 -14
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/agents/views.py +4 -2
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/backend/api/models.py +3 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/backend/api/task.py +3 -1
- vibesurf-0.1.18/vibe_surf/backend/database/manager.py +318 -0
- vibesurf-0.1.18/vibe_surf/backend/database/migrations/v001_initial_schema.sql +118 -0
- vibesurf-0.1.18/vibe_surf/backend/database/migrations/v002_add_agent_mode.sql +6 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/backend/database/models.py +4 -1
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/backend/database/queries.py +4 -2
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/backend/shared_state.py +5 -3
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/scripts/api-client.js +4 -2
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/scripts/ui-manager.js +27 -1
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/sidepanel.html +5 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/styles/input.css +11 -3
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/llm/openai_compatible.py +1 -2
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibesurf.egg-info/PKG-INFO +1 -1
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibesurf.egg-info/SOURCES.txt +4 -6
- vibesurf-0.1.16/vibe_surf/backend/database/manager.py +0 -135
- vibesurf-0.1.16/vibe_surf/backend/migrations/__init__.py +0 -16
- vibesurf-0.1.16/vibe_surf/backend/migrations/init_db.py +0 -303
- vibesurf-0.1.16/vibe_surf/backend/migrations/seed_data.py +0 -236
- {vibesurf-0.1.16 → vibesurf-0.1.18}/.env.example +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/.github/workflows/publish.yml +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/.gitignore +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/.python-version +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/LICENSE +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/MANIFEST.in +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/README.md +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/docs/EXECUTABLE_BUILD.md +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/docs/PYPI_SETUP.md +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/pyproject.toml +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/scripts/build-local.bat +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/scripts/build-local.sh +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/setup.cfg +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/tests/test_agents.py +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/tests/test_backend_api.py +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/tests/test_browser.py +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/tests/test_tools.py +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/__init__.py +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/agents/__init__.py +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/agents/prompts/__init__.py +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/agents/prompts/report_writer_prompt.py +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/agents/prompts/vibe_surf_prompt.py +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/backend/__init__.py +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/backend/api/__init__.py +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/backend/api/activity.py +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/backend/api/browser.py +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/backend/api/config.py +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/backend/api/files.py +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/backend/database/__init__.py +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/backend/database/schemas.py +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/backend/llm_config.py +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/backend/main.py +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/backend/utils/__init__.py +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/backend/utils/encryption.py +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/backend/utils/llm_factory.py +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/browser/__init__.py +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/browser/agen_browser_profile.py +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/browser/agent_browser_session.py +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/browser/browser_manager.py +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/browser/utils.py +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/browser/watchdogs/__init__.py +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/browser/watchdogs/action_watchdog.py +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/browser/watchdogs/dom_watchdog.py +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/background.js +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/config.js +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/content.js +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/dev-reload.js +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/icons/convert-svg.js +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/icons/logo-preview.html +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/icons/logo.icns +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/icons/logo.png +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/manifest.json +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/popup.html +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/scripts/file-manager.js +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/scripts/history-manager.js +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/scripts/main.js +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/scripts/markdown-it.min.js +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/scripts/modal-manager.js +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/scripts/session-manager.js +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/scripts/settings-manager.js +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/styles/activity.css +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/styles/animations.css +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/styles/base.css +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/styles/components.css +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/styles/history-modal.css +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/styles/layout.css +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/styles/responsive.css +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/styles/settings-environment.css +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/styles/settings-forms.css +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/styles/settings-modal.css +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/styles/settings-profiles.css +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/styles/settings-responsive.css +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/styles/settings-utilities.css +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/chrome_extension/styles/variables.css +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/cli.py +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/common.py +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/llm/__init__.py +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/logger.py +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/tools/__init__.py +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/tools/browser_use_tools.py +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/tools/file_system.py +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/tools/mcp_client.py +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/tools/report_writer_tools.py +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/tools/vibesurf_tools.py +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibe_surf/tools/views.py +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibesurf.egg-info/dependency_links.txt +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibesurf.egg-info/entry_points.txt +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibesurf.egg-info/requires.txt +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibesurf.egg-info/top_level.txt +0 -0
- {vibesurf-0.1.16 → vibesurf-0.1.18}/vibesurf.spec +0 -0
|
@@ -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.18'
|
|
32
|
+
__version_tuple__ = version_tuple = (0, 1, 18)
|
|
33
33
|
|
|
34
|
-
__commit_id__ = commit_id = '
|
|
34
|
+
__commit_id__ = commit_id = 'g51b12c120'
|
|
@@ -566,12 +566,6 @@ class BrowserUseAgent(Agent):
|
|
|
566
566
|
agent_run_error = 'Agent stopped programmatically'
|
|
567
567
|
break
|
|
568
568
|
|
|
569
|
-
while self.state.paused:
|
|
570
|
-
await asyncio.sleep(0.2) # Small delay to prevent CPU spinning
|
|
571
|
-
if self.state.stopped: # Allow stopping while paused
|
|
572
|
-
agent_run_error = 'Agent stopped programmatically while paused'
|
|
573
|
-
break
|
|
574
|
-
|
|
575
569
|
if on_step_start is not None:
|
|
576
570
|
await on_step_start(self)
|
|
577
571
|
|
|
@@ -820,12 +814,12 @@ class BrowserUseAgent(Agent):
|
|
|
820
814
|
i = global_action_index + local_index
|
|
821
815
|
|
|
822
816
|
# Original sequential execution logic continues here...
|
|
823
|
-
if i > 0:
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
817
|
+
# if i > 0:
|
|
818
|
+
# # ONLY ALLOW TO CALL `done` IF IT IS A SINGLE ACTION
|
|
819
|
+
# if action.model_dump(exclude_unset=True).get('done') is not None:
|
|
820
|
+
# msg = f'Done action is allowed only as a single action - stopped after action {i} / {total_actions}.'
|
|
821
|
+
# self.logger.debug(msg)
|
|
822
|
+
# break
|
|
829
823
|
|
|
830
824
|
# DOM synchronization check - verify element indexes are still valid AFTER first action
|
|
831
825
|
if action.get_index() is not None and i != 0:
|
|
@@ -32,7 +32,7 @@ class ReportTaskResult(BaseModel):
|
|
|
32
32
|
class ReportWriterAgent:
|
|
33
33
|
"""Agent responsible for generating HTML reports using LLM-controlled flow"""
|
|
34
34
|
|
|
35
|
-
def __init__(self, llm: BaseChatModel, workspace_dir: str, step_callback=None,
|
|
35
|
+
def __init__(self, llm: BaseChatModel, workspace_dir: str, step_callback=None, use_thinking: bool = True):
|
|
36
36
|
"""
|
|
37
37
|
Initialize ReportWriterAgent
|
|
38
38
|
|
|
@@ -44,7 +44,7 @@ class ReportWriterAgent:
|
|
|
44
44
|
self.llm = llm
|
|
45
45
|
self.workspace_dir = os.path.abspath(workspace_dir)
|
|
46
46
|
self.step_callback = step_callback
|
|
47
|
-
self.
|
|
47
|
+
self.use_thinking = use_thinking
|
|
48
48
|
|
|
49
49
|
# Initialize file system and tools
|
|
50
50
|
self.file_system = CustomFileSystem(self.workspace_dir)
|
|
@@ -52,7 +52,7 @@ class ReportWriterAgent:
|
|
|
52
52
|
|
|
53
53
|
# Setup action model and agent output
|
|
54
54
|
self.ActionModel = self.tools.registry.create_action_model()
|
|
55
|
-
if self.
|
|
55
|
+
if self.use_thinking:
|
|
56
56
|
self.AgentOutput = CustomAgentOutput.type_with_custom_actions(self.ActionModel)
|
|
57
57
|
else:
|
|
58
58
|
self.AgentOutput = CustomAgentOutput.type_with_custom_actions_no_thinking(self.ActionModel)
|
|
@@ -38,6 +38,7 @@ from vibe_surf.browser.browser_manager import BrowserManager
|
|
|
38
38
|
from vibe_surf.tools.browser_use_tools import BrowserUseTools
|
|
39
39
|
from vibe_surf.tools.vibesurf_tools import VibeSurfTools
|
|
40
40
|
from vibe_surf.tools.file_system import CustomFileSystem
|
|
41
|
+
from vibe_surf.agents.views import VibeSurfAgentSettings
|
|
41
42
|
|
|
42
43
|
from vibe_surf.logger import get_logger
|
|
43
44
|
|
|
@@ -213,11 +214,11 @@ def create_browser_agent_step_callback(state: VibeSurfState, agent_name: str):
|
|
|
213
214
|
step_msg = f"## Step {step_num}\n\n"
|
|
214
215
|
|
|
215
216
|
# Add thinking if present
|
|
216
|
-
if agent_output.thinking:
|
|
217
|
+
if hasattr(agent_output, 'thinking') and agent_output.thinking:
|
|
217
218
|
step_msg += f"**💡 Thinking:**\n{agent_output.thinking}\n\n"
|
|
218
219
|
|
|
219
220
|
# Add evaluation if present
|
|
220
|
-
if agent_output.evaluation_previous_goal:
|
|
221
|
+
if hasattr(agent_output, 'evaluation_previous_goal') and agent_output.evaluation_previous_goal:
|
|
221
222
|
step_msg += f"**👍 Evaluation:**\n{agent_output.evaluation_previous_goal}\n\n"
|
|
222
223
|
|
|
223
224
|
# Add memory if present
|
|
@@ -225,7 +226,7 @@ def create_browser_agent_step_callback(state: VibeSurfState, agent_name: str):
|
|
|
225
226
|
# step_msg += f"**🧠 Memory:** {agent_output.memory}\n\n"
|
|
226
227
|
|
|
227
228
|
# Add next goal if present
|
|
228
|
-
if agent_output.next_goal:
|
|
229
|
+
if hasattr(agent_output, 'next_goal') and agent_output.next_goal:
|
|
229
230
|
step_msg += f"**🎯 Next Goal:**\n{agent_output.next_goal}\n\n"
|
|
230
231
|
|
|
231
232
|
# Add action summary
|
|
@@ -352,7 +353,7 @@ async def _vibesurf_agent_node_impl(state: VibeSurfState) -> VibeSurfState:
|
|
|
352
353
|
# Create action model and agent output using VibeSurfTools
|
|
353
354
|
vibesurf_agent = state.vibesurf_agent
|
|
354
355
|
ActionModel = vibesurf_agent.tools.registry.create_action_model()
|
|
355
|
-
if vibesurf_agent.
|
|
356
|
+
if vibesurf_agent.settings.agent_mode == "thinking":
|
|
356
357
|
AgentOutput = CustomAgentOutput.type_with_custom_actions(ActionModel)
|
|
357
358
|
else:
|
|
358
359
|
AgentOutput = CustomAgentOutput.type_with_custom_actions_no_thinking(ActionModel)
|
|
@@ -632,7 +633,9 @@ async def execute_parallel_browser_tasks(state: VibeSurfState) -> List[BrowserTa
|
|
|
632
633
|
file_system_path=str(bu_agent_workdir),
|
|
633
634
|
register_new_step_callback=step_callback,
|
|
634
635
|
extend_system_message=EXTEND_BU_SYSTEM_PROMPT,
|
|
635
|
-
token_cost_service=state.vibesurf_agent.token_cost_service
|
|
636
|
+
token_cost_service=state.vibesurf_agent.token_cost_service,
|
|
637
|
+
flash_mode=state.vibesurf_agent.settings.agent_mode == "flash",
|
|
638
|
+
use_thinking=state.vibesurf_agent.settings.agent_mode == "thinking"
|
|
636
639
|
)
|
|
637
640
|
agents.append(agent)
|
|
638
641
|
|
|
@@ -783,7 +786,9 @@ async def execute_single_browser_tasks(state: VibeSurfState) -> BrowserTaskResul
|
|
|
783
786
|
file_system_path=str(bu_agent_workdir),
|
|
784
787
|
register_new_step_callback=step_callback,
|
|
785
788
|
extend_system_message=EXTEND_BU_SYSTEM_PROMPT,
|
|
786
|
-
token_cost_service=state.vibesurf_agent.token_cost_service
|
|
789
|
+
token_cost_service=state.vibesurf_agent.token_cost_service,
|
|
790
|
+
flash_mode=state.vibesurf_agent.settings.agent_mode == "flash",
|
|
791
|
+
use_thinking=state.vibesurf_agent.settings.agent_mode == "thinking"
|
|
787
792
|
)
|
|
788
793
|
if state.vibesurf_agent and hasattr(state.vibesurf_agent, '_running_agents'):
|
|
789
794
|
state.vibesurf_agent._running_agents[agent_id] = agent
|
|
@@ -861,7 +866,7 @@ async def _report_task_execution_node_impl(state: VibeSurfState) -> VibeSurfStat
|
|
|
861
866
|
llm=state.vibesurf_agent.llm,
|
|
862
867
|
workspace_dir=str(state.vibesurf_agent.file_system.get_dir()),
|
|
863
868
|
step_callback=step_callback,
|
|
864
|
-
|
|
869
|
+
use_thinking=state.vibesurf_agent.settings.agent_mode == "thinking",
|
|
865
870
|
)
|
|
866
871
|
|
|
867
872
|
# Register report writer agent for control coordination
|
|
@@ -997,19 +1002,17 @@ class VibeSurfAgent:
|
|
|
997
1002
|
browser_manager: BrowserManager,
|
|
998
1003
|
tools: VibeSurfTools,
|
|
999
1004
|
workspace_dir: str = "./workspace",
|
|
1000
|
-
|
|
1001
|
-
calculate_token_cost: bool = True,
|
|
1005
|
+
settings: Optional[VibeSurfAgentSettings] = None
|
|
1002
1006
|
):
|
|
1003
1007
|
"""Initialize VibeSurfAgent with required components"""
|
|
1004
1008
|
self.llm: BaseChatModel = llm
|
|
1005
|
-
self.
|
|
1006
|
-
self.token_cost_service = TokenCost(include_cost=
|
|
1009
|
+
self.settings = settings or VibeSurfAgentSettings()
|
|
1010
|
+
self.token_cost_service = TokenCost(include_cost=self.settings.calculate_cost)
|
|
1007
1011
|
self.token_cost_service.register_llm(llm)
|
|
1008
1012
|
self.browser_manager: BrowserManager = browser_manager
|
|
1009
1013
|
self.tools: VibeSurfTools = tools
|
|
1010
1014
|
self.workspace_dir = workspace_dir
|
|
1011
1015
|
os.makedirs(self.workspace_dir, exist_ok=True)
|
|
1012
|
-
self.thinking_mode = thinking_mode
|
|
1013
1016
|
|
|
1014
1017
|
self.cur_session_id = None
|
|
1015
1018
|
self.file_system: Optional[CustomFileSystem] = None
|
|
@@ -1540,7 +1543,7 @@ Please continue with your assigned work, incorporating this guidance only if it'
|
|
|
1540
1543
|
task: str,
|
|
1541
1544
|
upload_files: Optional[List[str]] = None,
|
|
1542
1545
|
session_id: Optional[str] = None,
|
|
1543
|
-
|
|
1546
|
+
agent_mode: str = "thinking"
|
|
1544
1547
|
) -> str | None:
|
|
1545
1548
|
"""
|
|
1546
1549
|
Main execution method that returns markdown summary with control capabilities
|
|
@@ -1554,7 +1557,7 @@ Please continue with your assigned work, incorporating this guidance only if it'
|
|
|
1554
1557
|
"""
|
|
1555
1558
|
logger.info(f"🚀 Starting VibeSurfAgent execution for task: {task}. Powered by LLM model: {self.llm.model_name}")
|
|
1556
1559
|
try:
|
|
1557
|
-
self.
|
|
1560
|
+
self.settings.agent_mode = agent_mode
|
|
1558
1561
|
session_id = session_id or self.cur_session_id or uuid7str()
|
|
1559
1562
|
if session_id != self.cur_session_id:
|
|
1560
1563
|
# Load session-specific data when switching sessions
|
|
@@ -66,11 +66,13 @@ class VibeSurfAgentSettings(BaseModel):
|
|
|
66
66
|
max_history_items: int | None = None
|
|
67
67
|
include_token_cost: bool = False
|
|
68
68
|
|
|
69
|
-
calculate_cost: bool =
|
|
69
|
+
calculate_cost: bool = True
|
|
70
70
|
include_tool_call_examples: bool = False
|
|
71
71
|
llm_timeout: int = 60 # Timeout in seconds for LLM calls
|
|
72
72
|
step_timeout: int = 180 # Timeout in seconds for each step
|
|
73
73
|
|
|
74
|
+
agent_mode: str = "thinking" # thinking, no-thinking, flash
|
|
75
|
+
|
|
74
76
|
|
|
75
77
|
class CustomAgentOutput(BaseModel):
|
|
76
78
|
model_config = ConfigDict(arbitrary_types_allowed=True, extra='forbid')
|
|
@@ -117,7 +119,7 @@ class CustomAgentOutput(BaseModel):
|
|
|
117
119
|
return schema
|
|
118
120
|
|
|
119
121
|
model = create_model(
|
|
120
|
-
'
|
|
122
|
+
'AgentOutputNoThinking',
|
|
121
123
|
__base__=AgentOutputNoThinking,
|
|
122
124
|
action=(
|
|
123
125
|
list[custom_actions], # type: ignore
|
|
@@ -106,6 +106,7 @@ class TaskCreateRequest(BaseModel):
|
|
|
106
106
|
llm_profile_name: str = Field(description="LLM profile name to use")
|
|
107
107
|
upload_files_path: Optional[str] = Field(default=None, description="Path to uploaded files")
|
|
108
108
|
mcp_server_config: Optional[Dict[str, Any]] = Field(default=None, description="MCP server configuration")
|
|
109
|
+
agent_mode: str = Field(default="thinking", description="Agent mode: 'thinking', 'no-thinking', or 'flash'")
|
|
109
110
|
|
|
110
111
|
class TaskControlRequest(BaseModel):
|
|
111
112
|
"""Request model for task control operations (pause/resume/stop)"""
|
|
@@ -121,6 +122,7 @@ class TaskResponse(BaseModel):
|
|
|
121
122
|
upload_files_path: Optional[str] = None
|
|
122
123
|
workspace_dir: Optional[str] = None
|
|
123
124
|
mcp_server_config: Optional[Dict[str, Any]] = None
|
|
125
|
+
agent_mode: str = "thinking"
|
|
124
126
|
task_result: Optional[str] = None
|
|
125
127
|
error_message: Optional[str] = None
|
|
126
128
|
report_path: Optional[str] = None
|
|
@@ -145,6 +147,7 @@ class TaskResponse(BaseModel):
|
|
|
145
147
|
upload_files_path=task.upload_files_path,
|
|
146
148
|
workspace_dir=task.workspace_dir,
|
|
147
149
|
mcp_server_config=task.mcp_server_config,
|
|
150
|
+
agent_mode=task.agent_mode,
|
|
148
151
|
task_result=task.task_result,
|
|
149
152
|
error_message=task.error_message,
|
|
150
153
|
report_path=task.report_path,
|
|
@@ -117,7 +117,8 @@ async def submit_task(
|
|
|
117
117
|
mcp_server_config=mcp_server_config,
|
|
118
118
|
llm_profile_name=task_request.llm_profile_name,
|
|
119
119
|
workspace_dir=workspace_dir,
|
|
120
|
-
task_status="pending"
|
|
120
|
+
task_status="pending",
|
|
121
|
+
agent_mode=task_request.agent_mode
|
|
121
122
|
)
|
|
122
123
|
await db.commit()
|
|
123
124
|
|
|
@@ -129,6 +130,7 @@ async def submit_task(
|
|
|
129
130
|
task=task_request.task_description,
|
|
130
131
|
llm_profile_name=task_request.llm_profile_name,
|
|
131
132
|
upload_files=task_request.upload_files_path,
|
|
133
|
+
agent_mode=task_request.agent_mode,
|
|
132
134
|
db_session=db
|
|
133
135
|
)
|
|
134
136
|
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Database Manager for VibeSurf Session Management
|
|
3
|
+
|
|
4
|
+
Handles database connections, session management, and initialization
|
|
5
|
+
with optimized configuration for real-time operations.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import asyncio
|
|
9
|
+
import os
|
|
10
|
+
import glob
|
|
11
|
+
import pdb
|
|
12
|
+
import re
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
from typing import AsyncGenerator, List, Tuple, Optional
|
|
15
|
+
import logging
|
|
16
|
+
import aiosqlite
|
|
17
|
+
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
|
|
18
|
+
from sqlalchemy.orm import sessionmaker
|
|
19
|
+
from sqlalchemy.pool import StaticPool
|
|
20
|
+
from .models import Base
|
|
21
|
+
|
|
22
|
+
from vibe_surf.logger import get_logger
|
|
23
|
+
|
|
24
|
+
logger = get_logger(__name__)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class DBMigrationManager:
|
|
28
|
+
"""Simplified database migration manager."""
|
|
29
|
+
|
|
30
|
+
def __init__(self, db_path: str = None):
|
|
31
|
+
"""Initialize migration manager
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
db_path: Database file path. Will be extracted from database_url if not provided.
|
|
35
|
+
"""
|
|
36
|
+
if db_path is None:
|
|
37
|
+
# Extract database path from shared_state
|
|
38
|
+
from .. import shared_state
|
|
39
|
+
database_url = os.getenv(
|
|
40
|
+
'VIBESURF_DATABASE_URL',
|
|
41
|
+
f'sqlite+aiosqlite:///{os.path.join(shared_state.workspace_dir, "vibe_surf.db")}'
|
|
42
|
+
)
|
|
43
|
+
self.db_path = database_url
|
|
44
|
+
else:
|
|
45
|
+
self.db_path = db_path
|
|
46
|
+
|
|
47
|
+
# Extract path from sqlite URL
|
|
48
|
+
if self.db_path.startswith('sqlite+aiosqlite:///'):
|
|
49
|
+
self.db_path = self.db_path[20:] # Remove 'sqlite+aiosqlite:///' prefix
|
|
50
|
+
else:
|
|
51
|
+
raise ValueError(f"Migration manager only supports SQLite databases, got: {self.db_path}")
|
|
52
|
+
|
|
53
|
+
self.migrations_dir = Path(__file__).parent / "migrations"
|
|
54
|
+
|
|
55
|
+
async def get_db_version(self) -> int:
|
|
56
|
+
"""Get current database version."""
|
|
57
|
+
try:
|
|
58
|
+
async with aiosqlite.connect(self.db_path) as db:
|
|
59
|
+
cursor = await db.execute("PRAGMA user_version;")
|
|
60
|
+
result = await cursor.fetchone()
|
|
61
|
+
return result[0] if result else 0
|
|
62
|
+
except Exception:
|
|
63
|
+
return 0
|
|
64
|
+
|
|
65
|
+
async def set_db_version(self, version: int) -> None:
|
|
66
|
+
"""Set database version."""
|
|
67
|
+
async with aiosqlite.connect(self.db_path) as db:
|
|
68
|
+
await db.execute(f"PRAGMA user_version = {version};")
|
|
69
|
+
await db.commit()
|
|
70
|
+
|
|
71
|
+
def get_migration_files(self) -> List[Tuple[int, str]]:
|
|
72
|
+
"""Get migration files sorted by version. Returns (version, filepath) tuples."""
|
|
73
|
+
migrations = []
|
|
74
|
+
pattern = str(self.migrations_dir / "v*.sql")
|
|
75
|
+
|
|
76
|
+
for filepath in glob.glob(pattern):
|
|
77
|
+
filename = os.path.basename(filepath)
|
|
78
|
+
match = re.match(r'v(\d+)_.*\.sql$', filename)
|
|
79
|
+
if match:
|
|
80
|
+
version = int(match.group(1))
|
|
81
|
+
migrations.append((version, filepath))
|
|
82
|
+
else:
|
|
83
|
+
logger.warning(f"Migration file {filename} doesn't match pattern v000_description.sql")
|
|
84
|
+
|
|
85
|
+
return sorted(migrations, key=lambda x: x[0])
|
|
86
|
+
|
|
87
|
+
async def init_database(self) -> None:
|
|
88
|
+
"""Initialize database directory if needed."""
|
|
89
|
+
db_dir = os.path.dirname(self.db_path)
|
|
90
|
+
os.makedirs(db_dir, exist_ok=True)
|
|
91
|
+
|
|
92
|
+
current_version = await self.get_db_version()
|
|
93
|
+
|
|
94
|
+
if current_version == 0:
|
|
95
|
+
logger.info("Database version is 0, will be initialized via migrations...")
|
|
96
|
+
else:
|
|
97
|
+
logger.info(f"Database already exists (version {current_version})")
|
|
98
|
+
|
|
99
|
+
async def apply_migrations(self, target_version: Optional[int] = None) -> int:
|
|
100
|
+
"""Apply pending migrations. Returns final version."""
|
|
101
|
+
await self.init_database() # Ensure database exists
|
|
102
|
+
|
|
103
|
+
current_version = await self.get_db_version()
|
|
104
|
+
migrations = self.get_migration_files()
|
|
105
|
+
|
|
106
|
+
if not migrations:
|
|
107
|
+
logger.info("No migration files found")
|
|
108
|
+
return current_version
|
|
109
|
+
|
|
110
|
+
if target_version is None:
|
|
111
|
+
target_version = max(m[0] for m in migrations)
|
|
112
|
+
|
|
113
|
+
logger.info(f"Current version: {current_version}, Target: {target_version}")
|
|
114
|
+
|
|
115
|
+
if current_version >= target_version:
|
|
116
|
+
logger.info("Database is up to date")
|
|
117
|
+
return current_version
|
|
118
|
+
|
|
119
|
+
applied = 0
|
|
120
|
+
async with aiosqlite.connect(self.db_path) as db:
|
|
121
|
+
await db.execute("PRAGMA foreign_keys = ON;")
|
|
122
|
+
|
|
123
|
+
for version, filepath in migrations:
|
|
124
|
+
if version <= current_version or version > target_version:
|
|
125
|
+
continue
|
|
126
|
+
|
|
127
|
+
try:
|
|
128
|
+
logger.info(f"Applying migration v{version:03d}: {os.path.basename(filepath)}")
|
|
129
|
+
|
|
130
|
+
with open(filepath, 'r', encoding='utf-8') as f:
|
|
131
|
+
sql_content = f.read()
|
|
132
|
+
|
|
133
|
+
await db.executescript(sql_content)
|
|
134
|
+
await self.set_db_version(version)
|
|
135
|
+
applied += 1
|
|
136
|
+
|
|
137
|
+
logger.info(f"Successfully applied migration v{version:03d}")
|
|
138
|
+
|
|
139
|
+
except Exception as e:
|
|
140
|
+
logger.error(f"Migration v{version:03d} failed: {e}")
|
|
141
|
+
raise RuntimeError(f"Migration failed at version {version}: {e}")
|
|
142
|
+
|
|
143
|
+
final_version = await self.get_db_version()
|
|
144
|
+
logger.info(f"Applied {applied} migrations. Final version: {final_version}")
|
|
145
|
+
return final_version
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
class DatabaseManager:
|
|
149
|
+
"""Database connection and session management"""
|
|
150
|
+
|
|
151
|
+
def __init__(self, database_url: str = None):
|
|
152
|
+
"""Initialize database manager
|
|
153
|
+
|
|
154
|
+
Args:
|
|
155
|
+
database_url: Database connection URL. Defaults to SQLite if not provided.
|
|
156
|
+
"""
|
|
157
|
+
from .. import shared_state
|
|
158
|
+
self.database_url = database_url or os.getenv(
|
|
159
|
+
'VIBESURF_DATABASE_URL',
|
|
160
|
+
f'sqlite+aiosqlite:///{os.path.join(shared_state.workspace_dir, "vibe_surf.db")}'
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
# Configure engine based on database type
|
|
164
|
+
if self.database_url.startswith('sqlite'):
|
|
165
|
+
# SQLite configuration for development
|
|
166
|
+
self.engine = create_async_engine(
|
|
167
|
+
self.database_url,
|
|
168
|
+
poolclass=StaticPool,
|
|
169
|
+
connect_args={
|
|
170
|
+
"check_same_thread": False,
|
|
171
|
+
"timeout": 30
|
|
172
|
+
},
|
|
173
|
+
echo=False # Set to True for SQL debugging
|
|
174
|
+
)
|
|
175
|
+
else:
|
|
176
|
+
# PostgreSQL/MySQL configuration for production
|
|
177
|
+
self.engine = create_async_engine(
|
|
178
|
+
self.database_url,
|
|
179
|
+
pool_size=20,
|
|
180
|
+
max_overflow=30,
|
|
181
|
+
pool_pre_ping=True,
|
|
182
|
+
pool_recycle=3600,
|
|
183
|
+
echo=False
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
self.async_session_factory = sessionmaker(
|
|
187
|
+
self.engine,
|
|
188
|
+
class_=AsyncSession,
|
|
189
|
+
expire_on_commit=False
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
# Initialize migration manager for SQLite databases
|
|
193
|
+
if self.database_url.startswith('sqlite'):
|
|
194
|
+
try:
|
|
195
|
+
self.migration_manager = DBMigrationManager(db_path=self.database_url)
|
|
196
|
+
except Exception as e:
|
|
197
|
+
logger.warning(f"Failed to initialize migration manager: {e}")
|
|
198
|
+
self.migration_manager = None
|
|
199
|
+
else:
|
|
200
|
+
self.migration_manager = None
|
|
201
|
+
logger.info("Migration manager is only supported for SQLite databases")
|
|
202
|
+
|
|
203
|
+
async def create_tables(self, use_migrations: bool = True):
|
|
204
|
+
"""Create all database tables
|
|
205
|
+
|
|
206
|
+
Args:
|
|
207
|
+
use_migrations: If True, use migration system. If False, use direct table creation.
|
|
208
|
+
"""
|
|
209
|
+
if use_migrations and self.migration_manager:
|
|
210
|
+
logger.info("🔄 Using migration system to initialize database...")
|
|
211
|
+
try:
|
|
212
|
+
await self.migration_manager.apply_migrations()
|
|
213
|
+
logger.info("✅ Database initialized via migrations")
|
|
214
|
+
return
|
|
215
|
+
except Exception as e:
|
|
216
|
+
logger.warning(f"Migration failed, falling back to direct table creation: {e}")
|
|
217
|
+
|
|
218
|
+
# Fallback to direct table creation
|
|
219
|
+
logger.info("🔄 Using direct table creation...")
|
|
220
|
+
async with self.engine.begin() as conn:
|
|
221
|
+
await conn.run_sync(Base.metadata.create_all)
|
|
222
|
+
logger.info("✅ Database initialized via direct table creation")
|
|
223
|
+
|
|
224
|
+
async def drop_tables(self):
|
|
225
|
+
"""Drop all database tables"""
|
|
226
|
+
async with self.engine.begin() as conn:
|
|
227
|
+
await conn.run_sync(Base.metadata.drop_all)
|
|
228
|
+
|
|
229
|
+
async def get_session(self) -> AsyncGenerator[AsyncSession, None]:
|
|
230
|
+
"""Get async database session"""
|
|
231
|
+
async with self.async_session_factory() as session:
|
|
232
|
+
try:
|
|
233
|
+
yield session
|
|
234
|
+
await session.commit()
|
|
235
|
+
except Exception:
|
|
236
|
+
await session.rollback()
|
|
237
|
+
raise
|
|
238
|
+
finally:
|
|
239
|
+
await session.close()
|
|
240
|
+
|
|
241
|
+
async def apply_migrations(self, target_version: Optional[int] = None) -> int:
|
|
242
|
+
"""Apply database migrations
|
|
243
|
+
|
|
244
|
+
Args:
|
|
245
|
+
target_version: Target migration version. If None, applies all available migrations.
|
|
246
|
+
|
|
247
|
+
Returns:
|
|
248
|
+
Final database version after applying migrations.
|
|
249
|
+
|
|
250
|
+
Raises:
|
|
251
|
+
RuntimeError: If migration manager is not available or migration fails.
|
|
252
|
+
"""
|
|
253
|
+
if not self.migration_manager:
|
|
254
|
+
raise RuntimeError("Migration manager is not available. Only SQLite databases support migrations.")
|
|
255
|
+
|
|
256
|
+
return await self.migration_manager.apply_migrations(target_version)
|
|
257
|
+
|
|
258
|
+
async def get_db_version(self) -> int:
|
|
259
|
+
"""Get current database version
|
|
260
|
+
|
|
261
|
+
Returns:
|
|
262
|
+
Current database version, or 0 if not available.
|
|
263
|
+
"""
|
|
264
|
+
if not self.migration_manager:
|
|
265
|
+
logger.warning("Migration manager not available, cannot get database version")
|
|
266
|
+
return 0
|
|
267
|
+
|
|
268
|
+
return await self.migration_manager.get_db_version()
|
|
269
|
+
|
|
270
|
+
async def close(self):
|
|
271
|
+
"""Close database connections"""
|
|
272
|
+
await self.engine.dispose()
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
# Dependency for FastAPI
|
|
276
|
+
async def get_db_session() -> AsyncGenerator[AsyncSession, None]:
|
|
277
|
+
"""FastAPI dependency for database sessions"""
|
|
278
|
+
from .. import shared_state
|
|
279
|
+
|
|
280
|
+
if not shared_state.db_manager:
|
|
281
|
+
raise RuntimeError("Database manager not initialized. Call initialize_vibesurf_components() first.")
|
|
282
|
+
|
|
283
|
+
async for session in shared_state.db_manager.get_session():
|
|
284
|
+
yield session
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
# Database initialization script
|
|
288
|
+
async def init_database():
|
|
289
|
+
"""Initialize database with tables"""
|
|
290
|
+
from .. import shared_state
|
|
291
|
+
|
|
292
|
+
logger.info("🗄️ Initializing VibeSurf database...")
|
|
293
|
+
|
|
294
|
+
try:
|
|
295
|
+
if not shared_state.db_manager:
|
|
296
|
+
raise RuntimeError("Database manager not initialized. Call initialize_vibesurf_components() first.")
|
|
297
|
+
|
|
298
|
+
await shared_state.db_manager.create_tables()
|
|
299
|
+
logger.info("✅ Database tables created successfully")
|
|
300
|
+
logger.info("✅ VibeSurf database ready for single-task execution")
|
|
301
|
+
|
|
302
|
+
except Exception as e:
|
|
303
|
+
logger.error(f"❌ Database initialization failed: {e}")
|
|
304
|
+
raise
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
if __name__ == "__main__":
|
|
308
|
+
# For standalone execution, initialize a temporary db_manager
|
|
309
|
+
import os
|
|
310
|
+
from .. import shared_state
|
|
311
|
+
|
|
312
|
+
workspace_dir = os.getenv("VIBESURF_WORKSPACE", os.path.join(os.path.dirname(__file__), "../vibesurf_workspace"))
|
|
313
|
+
database_url = os.getenv(
|
|
314
|
+
'VIBESURF_DATABASE_URL',
|
|
315
|
+
f'sqlite+aiosqlite:///{os.path.join(workspace_dir, "vibe_surf.db")}'
|
|
316
|
+
)
|
|
317
|
+
shared_state.db_manager = DatabaseManager(database_url)
|
|
318
|
+
asyncio.run(init_database())
|