vibesurf 0.1.0__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/__init__.py +12 -0
- vibe_surf/_version.py +34 -0
- vibe_surf/agents/__init__.py +0 -0
- vibe_surf/agents/browser_use_agent.py +1106 -0
- vibe_surf/agents/prompts/__init__.py +1 -0
- vibe_surf/agents/prompts/vibe_surf_prompt.py +176 -0
- vibe_surf/agents/report_writer_agent.py +360 -0
- vibe_surf/agents/vibe_surf_agent.py +1632 -0
- vibe_surf/backend/__init__.py +0 -0
- vibe_surf/backend/api/__init__.py +3 -0
- vibe_surf/backend/api/activity.py +243 -0
- vibe_surf/backend/api/config.py +740 -0
- vibe_surf/backend/api/files.py +322 -0
- vibe_surf/backend/api/models.py +257 -0
- vibe_surf/backend/api/task.py +300 -0
- vibe_surf/backend/database/__init__.py +13 -0
- vibe_surf/backend/database/manager.py +129 -0
- vibe_surf/backend/database/models.py +164 -0
- vibe_surf/backend/database/queries.py +922 -0
- vibe_surf/backend/database/schemas.py +100 -0
- vibe_surf/backend/llm_config.py +182 -0
- vibe_surf/backend/main.py +137 -0
- vibe_surf/backend/migrations/__init__.py +16 -0
- vibe_surf/backend/migrations/init_db.py +303 -0
- vibe_surf/backend/migrations/seed_data.py +236 -0
- vibe_surf/backend/shared_state.py +601 -0
- vibe_surf/backend/utils/__init__.py +7 -0
- vibe_surf/backend/utils/encryption.py +164 -0
- vibe_surf/backend/utils/llm_factory.py +225 -0
- vibe_surf/browser/__init__.py +8 -0
- vibe_surf/browser/agen_browser_profile.py +130 -0
- vibe_surf/browser/agent_browser_session.py +416 -0
- vibe_surf/browser/browser_manager.py +296 -0
- vibe_surf/browser/utils.py +790 -0
- vibe_surf/browser/watchdogs/__init__.py +0 -0
- vibe_surf/browser/watchdogs/action_watchdog.py +291 -0
- vibe_surf/browser/watchdogs/dom_watchdog.py +954 -0
- vibe_surf/chrome_extension/background.js +558 -0
- vibe_surf/chrome_extension/config.js +48 -0
- vibe_surf/chrome_extension/content.js +284 -0
- vibe_surf/chrome_extension/dev-reload.js +47 -0
- vibe_surf/chrome_extension/icons/convert-svg.js +33 -0
- vibe_surf/chrome_extension/icons/logo-preview.html +187 -0
- vibe_surf/chrome_extension/icons/logo.png +0 -0
- vibe_surf/chrome_extension/manifest.json +53 -0
- vibe_surf/chrome_extension/popup.html +134 -0
- vibe_surf/chrome_extension/scripts/api-client.js +473 -0
- vibe_surf/chrome_extension/scripts/main.js +491 -0
- vibe_surf/chrome_extension/scripts/markdown-it.min.js +3 -0
- vibe_surf/chrome_extension/scripts/session-manager.js +599 -0
- vibe_surf/chrome_extension/scripts/ui-manager.js +3687 -0
- vibe_surf/chrome_extension/sidepanel.html +347 -0
- vibe_surf/chrome_extension/styles/animations.css +471 -0
- vibe_surf/chrome_extension/styles/components.css +670 -0
- vibe_surf/chrome_extension/styles/main.css +2307 -0
- vibe_surf/chrome_extension/styles/settings.css +1100 -0
- vibe_surf/cli.py +357 -0
- vibe_surf/controller/__init__.py +0 -0
- vibe_surf/controller/file_system.py +53 -0
- vibe_surf/controller/mcp_client.py +68 -0
- vibe_surf/controller/vibesurf_controller.py +616 -0
- vibe_surf/controller/views.py +37 -0
- vibe_surf/llm/__init__.py +21 -0
- vibe_surf/llm/openai_compatible.py +237 -0
- vibesurf-0.1.0.dist-info/METADATA +97 -0
- vibesurf-0.1.0.dist-info/RECORD +70 -0
- vibesurf-0.1.0.dist-info/WHEEL +5 -0
- vibesurf-0.1.0.dist-info/entry_points.txt +2 -0
- vibesurf-0.1.0.dist-info/licenses/LICENSE +201 -0
- vibesurf-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Prompts module for VibeSurf agents
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# Supervisor Agent System Prompt - Core controller role definition
|
|
2
|
+
SUPERVISOR_AGENT_SYSTEM_PROMPT = """
|
|
3
|
+
You are the VibeSurf Agent developed by [WarmShao](https://github.com/warmshao), you are a helpful browser assistant, the core controller of the browser automating. You manage todo lists, assign tasks, and coordinate the entire execution process.
|
|
4
|
+
Your mission is to do your best to help users vibe surfing the internet, the world and the future.
|
|
5
|
+
|
|
6
|
+
You may receive context in the user message with these keys(subset):
|
|
7
|
+
- User's New Request: The user's initial request with Upload Files(Optional, used for completed task or request). Always prioritize and execute the user's latest extracted request unless it's a supplement or continuation of previous tasks, in which case combine previous requests and results for informed decision-making.
|
|
8
|
+
- Current Todos: List of pending todo items that need completion in previous stage
|
|
9
|
+
- Completed Todos: List of already finished todo items with their results
|
|
10
|
+
- Previous Browser Execution Results: Results from previous executed browser automation tasks
|
|
11
|
+
- Generated Report Path: Generated report path from report writer agent
|
|
12
|
+
- Available Browser Tabs: Current available browser tabs(pages), format as [page_index] Page Title, Page Url and Page ID
|
|
13
|
+
|
|
14
|
+
Your responsibilities:
|
|
15
|
+
1. TODO Management: Generate initial todos if none exist, or update todos based on task results
|
|
16
|
+
2. Task Assignment: Decide whether to assign browser tasks (parallel/single) or report tasks(if user specified)
|
|
17
|
+
3. Progress Tracking: Determine if tasks are complete and ready for summary
|
|
18
|
+
|
|
19
|
+
Todo Item Creation and Update Guidelines:
|
|
20
|
+
- Keep todo items simple and goal-oriented (especially for browser agents)
|
|
21
|
+
- Focus on WHAT you want to achieve and WHAT results you expect
|
|
22
|
+
- DO NOT include detailed step-by-step instructions or implementation details
|
|
23
|
+
- DO NOT over-split tasks into too many granular items - keep logical groupings together
|
|
24
|
+
- Browser agents have strong planning and execution capabilities - they only need task descriptions and desired outcomes
|
|
25
|
+
- If this task requires some contextual information from previous result, please also describe it in the task, such as which file paths need to be read from and etc.
|
|
26
|
+
- Example: "Search for latest iPhone 15 prices and return comparison data" (NOT "Go to Apple website, navigate to iPhone section, find iPhone 15, check prices...")
|
|
27
|
+
|
|
28
|
+
Available Actions:
|
|
29
|
+
- "simple_response": Directly return response content or answer if you think this is a simple task, such as Basic calculations or conversions or General advice or recommendations based on common knowledge and etc.
|
|
30
|
+
- "generate_todos": Create initial todo list (only if no todos exist)
|
|
31
|
+
- "update_todos": Update or Replace all remaining todos based on results and progress
|
|
32
|
+
- "assign_browser_task": Assign browser automation tasks
|
|
33
|
+
- "assign_report_task": Assign HTML report generation task
|
|
34
|
+
- "summary_generation": Generate final markdown summary and complete the workflow when all requirements have been met
|
|
35
|
+
|
|
36
|
+
Task Assignment Guidelines:
|
|
37
|
+
- Browser tasks: Use when web research, data extraction, or automation is needed
|
|
38
|
+
- Report tasks: Use when user explicitly requests reports or when complex data needs structured presentation.
|
|
39
|
+
|
|
40
|
+
File Processing Capabilities:
|
|
41
|
+
- Browser agent can read and process various file types (text, documents, images, etc.)
|
|
42
|
+
- Browser agent can extract information from files and perform file analysis
|
|
43
|
+
- When users upload files for processing, summarization, or analysis tasks, these can be assigned to browser agent
|
|
44
|
+
- When creating file-related tasks, always include the specific absolute file paths in the task description. The path format provided by the user is generally like this: file:///{absolute file path}, Please only use the absolute file path.
|
|
45
|
+
- Examples: "Read and summarize the content from file path: path/to/document.pdf", "Analyze data from uploaded file: path/to/data.csv and generate insights"
|
|
46
|
+
|
|
47
|
+
Browser Task Execution Mode Rules:
|
|
48
|
+
- "single": Use for tasks on user-opened pages (form filling, automation on existing pages, dependent sequential tasks). ONLY supports 1 task.
|
|
49
|
+
- "parallel": Use for independent research tasks (web searching, deep research, data extraction from multiple sources) that can run concurrently for efficiency.
|
|
50
|
+
|
|
51
|
+
Browser Tab Management Guidelines:
|
|
52
|
+
|
|
53
|
+
**Single Mode:**
|
|
54
|
+
- No need to specify page_index - automatically uses the current active page
|
|
55
|
+
- Browser use agent can see all available tabs for context
|
|
56
|
+
- Default behavior works on the currently active tab
|
|
57
|
+
|
|
58
|
+
**Parallel Mode:**
|
|
59
|
+
- When generating tasks_to_execute, you can optionally specify page index using format: [[page_index, todo_index], [page_index, todo_index], todo_index]
|
|
60
|
+
- Examples: [[1, 0], [0, 1], 2] (execute todo_item[0] on page 1, todo_item[1] on page 0, todo_item[2] on new page)
|
|
61
|
+
- If page_index is specified: Agent will work on that specific page (may affect user's opened web pages)
|
|
62
|
+
- If page_index is NOT specified: Agent will open a new blank page to work on
|
|
63
|
+
- IMPORTANT: Only specify page_index when:
|
|
64
|
+
- User explicitly requests work on already opened pages, OR
|
|
65
|
+
- The target page is a blank page, OR
|
|
66
|
+
- Task only involves simple information gathering from open pages (no automation that changes page state)
|
|
67
|
+
- Parallel mode agents only see their specified page or newly opened page (isolation implemented)
|
|
68
|
+
- Avoid specifying page_index for tasks that involve automation unless explicitly requested by user
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
Browser Task Execution Requirements:
|
|
72
|
+
- For "assign_browser_task" action, use todo item indices in "tasks_to_execute" to reference items from "todo_items"
|
|
73
|
+
- Format: use integer indices (0-based) to reference todo_items, or [page_index, todo_index] for specific page assignment
|
|
74
|
+
- This ensures efficient referencing and proper tracking of todo items without duplication
|
|
75
|
+
|
|
76
|
+
Decision Rules:
|
|
77
|
+
- If no todos exist: generate_todos
|
|
78
|
+
- If browser tasks are pending: assign_browser_task
|
|
79
|
+
- If all browser tasks complete and user wants report: assign_report_task
|
|
80
|
+
- If all tasks complete: summary_generation
|
|
81
|
+
|
|
82
|
+
IMPORTANT: For "update_todos" action, always provide the complete list of remaining todo items to replace the current todo list. This ensures proper modification and cleanup of completed or unnecessary todos.
|
|
83
|
+
|
|
84
|
+
Respond with JSON in this exact format:
|
|
85
|
+
{{
|
|
86
|
+
"reasoning": "explanation of current situation and decision",
|
|
87
|
+
"action": "simple_response|generate_todos|update_todos|assign_browser_task|assign_report_task|summary_generation",
|
|
88
|
+
"simple_response_content": "the actual response content if this is a simple response task. Just directly write the answer, no more extra content.",
|
|
89
|
+
"summary_content": "the comprehensive markdown summary content when action is summary_generation. Include key findings, results, and links to generated files. For local file links, use the file:// protocol format: [Report Name](file:///absolute/path/to/file.html) to ensure proper file access in browser extensions.",
|
|
90
|
+
"todo_items": ["complete list of remaining todos - ALWAYS include for generate_todos and update_todos actions"],
|
|
91
|
+
"task_type": "parallel|single (for browser tasks)",
|
|
92
|
+
"tasks_to_execute": ["todo indices to execute now - use 0-based indices referencing todo_items, ONLY 1 index for single mode. For parallel mode, optionally use [[page_index, todo_index], todo_index] format to specify page"]
|
|
93
|
+
}}
|
|
94
|
+
|
|
95
|
+
The language of your output should remain the same as the user's request, including the content of the reasoning, response, todo list, browser task and etc. in values of JSON, but the names of the keys in the JSON should remain in English.
|
|
96
|
+
"""
|
|
97
|
+
|
|
98
|
+
REPORT_CONTENT_PROMPT = """
|
|
99
|
+
You are a professional report writer tasked with creating content that directly fulfills the user's request.
|
|
100
|
+
|
|
101
|
+
**User's Original Request:** {original_task}
|
|
102
|
+
**Report Type:** {report_type}
|
|
103
|
+
**Available Data:** {execution_results}
|
|
104
|
+
|
|
105
|
+
**Instructions:**
|
|
106
|
+
1. Focus ONLY on what the user specifically requested - ignore technical execution details
|
|
107
|
+
2. Create content that directly addresses the user's needs (comparison, analysis, research findings, etc.)
|
|
108
|
+
3. DO NOT include methodology, task overview, or technical process information
|
|
109
|
+
4. DO NOT mention agents, browser automation, or technical execution methods
|
|
110
|
+
5. Write as if you're delivering exactly what the user asked for
|
|
111
|
+
6. Use a professional, clear, and engaging style
|
|
112
|
+
7. Structure content with clear sections relevant to the user's request
|
|
113
|
+
8. If images or screenshots are available and would enhance the report presentation, include them in appropriate locations with proper context and descriptions
|
|
114
|
+
|
|
115
|
+
**Content Structure (adapt based on user's request):**
|
|
116
|
+
- Executive Summary (key findings relevant to user's request)
|
|
117
|
+
- Main Content (comparison, analysis, research findings - whatever user requested)
|
|
118
|
+
- Key Insights & Findings (specific to user's topic of interest)
|
|
119
|
+
- Conclusions & Recommendations (actionable insights for user's domain)
|
|
120
|
+
|
|
121
|
+
**Writing Style:**
|
|
122
|
+
- Professional and authoritative
|
|
123
|
+
- Data-driven with specific examples from the research
|
|
124
|
+
- Clear and concise
|
|
125
|
+
- Focus on subject matter insights, not process
|
|
126
|
+
- NO technical jargon about execution methods
|
|
127
|
+
|
|
128
|
+
Generate content that directly fulfills the user's request. Pretend you're a domain expert delivering exactly what they asked for.
|
|
129
|
+
"""
|
|
130
|
+
|
|
131
|
+
REPORT_FORMAT_PROMPT = """
|
|
132
|
+
Create a beautiful, professional HTML report. Output ONLY the HTML code with no explanation or additional text.
|
|
133
|
+
|
|
134
|
+
**Content to Format:**
|
|
135
|
+
{report_content}
|
|
136
|
+
|
|
137
|
+
**CRITICAL: Output Rules**
|
|
138
|
+
- Output ONLY HTML code starting with <!DOCTYPE html>
|
|
139
|
+
- NO introductory text, explanations, or comments before the HTML
|
|
140
|
+
- NO text after the HTML code
|
|
141
|
+
- NO markdown code blocks or formatting
|
|
142
|
+
- JUST the raw HTML document
|
|
143
|
+
|
|
144
|
+
**Design Requirements:**
|
|
145
|
+
1. Modern, professional HTML document with embedded CSS
|
|
146
|
+
2. Clean, readable design with proper typography
|
|
147
|
+
3. Responsive design principles
|
|
148
|
+
4. Professional color scheme (blues, grays, whites)
|
|
149
|
+
5. Proper spacing, margins, and visual hierarchy
|
|
150
|
+
6. Print-friendly design
|
|
151
|
+
7. Modern CSS features (flexbox, grid where appropriate)
|
|
152
|
+
|
|
153
|
+
**Structure Requirements:**
|
|
154
|
+
- Header with appropriate title (derived from content, NOT "Task Execution Report")
|
|
155
|
+
- Clearly defined sections with proper headings
|
|
156
|
+
- Data tables with professional styling
|
|
157
|
+
- Visual elements where appropriate
|
|
158
|
+
- Images and screenshots with proper styling, captions, and responsive design
|
|
159
|
+
- Clean footer
|
|
160
|
+
|
|
161
|
+
**Technical Requirements:**
|
|
162
|
+
- Complete HTML5 document with proper DOCTYPE
|
|
163
|
+
- Embedded CSS (no external dependencies)
|
|
164
|
+
- Responsive meta tags
|
|
165
|
+
- Semantic HTML elements
|
|
166
|
+
- Cross-browser compatibility
|
|
167
|
+
- Proper image handling with responsive design, appropriate sizing, and elegant layout
|
|
168
|
+
- Image captions and alt text for accessibility
|
|
169
|
+
|
|
170
|
+
**Title Guidelines:**
|
|
171
|
+
- Create title based on the actual content/comparison topic
|
|
172
|
+
- NOT "Task Execution Report" or similar generic titles
|
|
173
|
+
- Make it specific to what was researched/compared
|
|
174
|
+
|
|
175
|
+
IMPORTANT: Start your response immediately with <!DOCTYPE html> and output ONLY the HTML document.
|
|
176
|
+
"""
|
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import os
|
|
3
|
+
import time
|
|
4
|
+
from typing import Any, Dict, List
|
|
5
|
+
|
|
6
|
+
from browser_use.llm.base import BaseChatModel
|
|
7
|
+
from browser_use.llm.messages import UserMessage
|
|
8
|
+
|
|
9
|
+
from vibe_surf.agents.prompts.vibe_surf_prompt import (
|
|
10
|
+
REPORT_CONTENT_PROMPT,
|
|
11
|
+
REPORT_FORMAT_PROMPT
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ReportWriterAgent:
|
|
18
|
+
"""Agent responsible for generating HTML reports using two-phase LLM generation"""
|
|
19
|
+
|
|
20
|
+
def __init__(self, llm: BaseChatModel, workspace_dir: str):
|
|
21
|
+
"""
|
|
22
|
+
Initialize ReportWriterAgent
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
llm: Language model for generating report content
|
|
26
|
+
workspace_dir: Directory to save reports
|
|
27
|
+
"""
|
|
28
|
+
self.llm = llm
|
|
29
|
+
self.workspace_dir = workspace_dir
|
|
30
|
+
|
|
31
|
+
logger.info("📄 ReportWriterAgent initialized")
|
|
32
|
+
|
|
33
|
+
async def generate_report(self, report_data: Dict[str, Any]) -> str:
|
|
34
|
+
"""
|
|
35
|
+
Generate HTML report using two-phase approach: content generation then formatting
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
report_data: Dictionary containing:
|
|
39
|
+
- original_task: The original user task
|
|
40
|
+
- execution_results: List of BrowserTaskResult objects
|
|
41
|
+
- report_type: Type of report ("summary", "detailed", "none")
|
|
42
|
+
- upload_files: Optional list of uploaded files
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
str: Path to the generated report file
|
|
46
|
+
"""
|
|
47
|
+
logger.info(f"📝 Generating {report_data.get('report_type', 'summary')} report...")
|
|
48
|
+
|
|
49
|
+
try:
|
|
50
|
+
# Phase 1: Generate report content
|
|
51
|
+
logger.info("📖 Phase 1: Generating report content...")
|
|
52
|
+
report_content = await self._generate_content(report_data)
|
|
53
|
+
|
|
54
|
+
# Phase 2: Format content as HTML
|
|
55
|
+
logger.info("🎨 Phase 2: Formatting as HTML...")
|
|
56
|
+
html_content = await self._format_as_html(report_content)
|
|
57
|
+
|
|
58
|
+
# Save report to file
|
|
59
|
+
report_filename = f"report_{int(time.time())}.html"
|
|
60
|
+
reports_dir = os.path.join(self.workspace_dir, "reports")
|
|
61
|
+
os.makedirs(reports_dir, exist_ok=True)
|
|
62
|
+
report_path = os.path.join(reports_dir, report_filename)
|
|
63
|
+
|
|
64
|
+
with open(report_path, 'w', encoding='utf-8') as f:
|
|
65
|
+
f.write(html_content)
|
|
66
|
+
|
|
67
|
+
logger.info(f"✅ Report generated successfully: {report_path}")
|
|
68
|
+
return report_path
|
|
69
|
+
|
|
70
|
+
except Exception as e:
|
|
71
|
+
logger.error(f"❌ Failed to generate report: {e}")
|
|
72
|
+
# Generate a simple fallback report
|
|
73
|
+
fallback_path = await self._generate_fallback_report(report_data)
|
|
74
|
+
return fallback_path
|
|
75
|
+
|
|
76
|
+
async def _generate_content(self, report_data: Dict[str, Any]) -> str:
|
|
77
|
+
"""Generate the textual content for the report"""
|
|
78
|
+
# Format execution results for the prompt
|
|
79
|
+
results_text = self._format_execution_results(report_data.get('execution_results', []))
|
|
80
|
+
|
|
81
|
+
# Format upload files
|
|
82
|
+
upload_files = report_data.get('upload_files', [])
|
|
83
|
+
upload_files_text = "None" if not upload_files else ", ".join(upload_files)
|
|
84
|
+
|
|
85
|
+
# Generate content using the content prompt
|
|
86
|
+
content_prompt = REPORT_CONTENT_PROMPT.format(
|
|
87
|
+
original_task=report_data.get('original_task', 'No task specified'),
|
|
88
|
+
report_type=report_data.get('report_type', 'summary'),
|
|
89
|
+
upload_files=upload_files_text,
|
|
90
|
+
execution_results=results_text
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
response = await self.llm.ainvoke([UserMessage(content=content_prompt)])
|
|
94
|
+
return response.completion
|
|
95
|
+
|
|
96
|
+
async def _format_as_html(self, content: str) -> str:
|
|
97
|
+
"""Format the content as a professional HTML document"""
|
|
98
|
+
format_prompt = REPORT_FORMAT_PROMPT.format(report_content=content)
|
|
99
|
+
|
|
100
|
+
response = await self.llm.ainvoke([UserMessage(content=format_prompt)])
|
|
101
|
+
html_content = response.completion
|
|
102
|
+
|
|
103
|
+
# Clean up the HTML content if needed
|
|
104
|
+
html_content = self._clean_html_content(html_content)
|
|
105
|
+
|
|
106
|
+
return html_content
|
|
107
|
+
|
|
108
|
+
def _format_execution_results(self, execution_results) -> str:
|
|
109
|
+
"""Format execution results for the LLM prompt"""
|
|
110
|
+
if not execution_results:
|
|
111
|
+
return "No execution results available."
|
|
112
|
+
|
|
113
|
+
formatted_results = []
|
|
114
|
+
for i, result in enumerate(execution_results, 1):
|
|
115
|
+
status = "✅ Success" if result.success else "❌ Failed"
|
|
116
|
+
|
|
117
|
+
# Extract meaningful result content
|
|
118
|
+
result_content = "No result available"
|
|
119
|
+
if result.result:
|
|
120
|
+
# Truncate very long results but keep meaningful content
|
|
121
|
+
if len(result.result) > 500:
|
|
122
|
+
result_content = result.result[:497] + "..."
|
|
123
|
+
else:
|
|
124
|
+
result_content = result.result
|
|
125
|
+
elif result.error:
|
|
126
|
+
result_content = f"Error: {result.error}"
|
|
127
|
+
|
|
128
|
+
formatted_results.append(f"""
|
|
129
|
+
**Task {i}:** {result.task}
|
|
130
|
+
**Status:** {status}
|
|
131
|
+
**Agent:** {result.agent_id}
|
|
132
|
+
**Result:** {result_content}
|
|
133
|
+
**Success:** {'Yes' if result.success else 'No'}
|
|
134
|
+
""")
|
|
135
|
+
|
|
136
|
+
return "\n".join(formatted_results)
|
|
137
|
+
|
|
138
|
+
def _clean_html_content(self, html_content: str) -> str:
|
|
139
|
+
"""Clean and validate HTML content"""
|
|
140
|
+
# Remove markdown code block markers if present
|
|
141
|
+
html_content = html_content.strip()
|
|
142
|
+
if html_content.startswith("```html"):
|
|
143
|
+
html_content = html_content[7:].strip()
|
|
144
|
+
if html_content.startswith("```"):
|
|
145
|
+
html_content = html_content[3:].strip()
|
|
146
|
+
if html_content.endswith("```"):
|
|
147
|
+
html_content = html_content[:-3].strip()
|
|
148
|
+
|
|
149
|
+
# Ensure it starts with <!DOCTYPE html> or <html>
|
|
150
|
+
if not html_content.lower().startswith(('<!doctype', '<html')):
|
|
151
|
+
html_content = f"""<!DOCTYPE html>
|
|
152
|
+
<html lang="en">
|
|
153
|
+
<head>
|
|
154
|
+
<meta charset="UTF-8">
|
|
155
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
156
|
+
<title>VibeSurf Task Report</title>
|
|
157
|
+
<style>
|
|
158
|
+
body {{ font-family: Arial, sans-serif; margin: 20px; line-height: 1.6; }}
|
|
159
|
+
.container {{ max-width: 800px; margin: 0 auto; }}
|
|
160
|
+
h1 {{ color: #2c3e50; border-bottom: 2px solid #3498db; padding-bottom: 10px; }}
|
|
161
|
+
h2 {{ color: #34495e; margin-top: 30px; }}
|
|
162
|
+
.section {{ margin: 20px 0; padding: 15px; background: #f8f9fa; border-left: 4px solid #007bff; }}
|
|
163
|
+
</style>
|
|
164
|
+
</head>
|
|
165
|
+
<body>
|
|
166
|
+
<div class="container">
|
|
167
|
+
{html_content}
|
|
168
|
+
</div>
|
|
169
|
+
</body>
|
|
170
|
+
</html>"""
|
|
171
|
+
|
|
172
|
+
return html_content
|
|
173
|
+
|
|
174
|
+
async def _generate_fallback_report(self, report_data: Dict[str, Any]) -> str:
|
|
175
|
+
"""Generate a simple fallback report when LLM generation fails"""
|
|
176
|
+
logger.info("📝 Generating fallback report...")
|
|
177
|
+
|
|
178
|
+
upload_files = report_data.get('upload_files', [])
|
|
179
|
+
upload_files_section = ""
|
|
180
|
+
if upload_files:
|
|
181
|
+
upload_files_section = f"""
|
|
182
|
+
<div class="section">
|
|
183
|
+
<h2>Upload Files</h2>
|
|
184
|
+
<ul>
|
|
185
|
+
{"".join([f"<li>{file}</li>" for file in upload_files])}
|
|
186
|
+
</ul>
|
|
187
|
+
</div>"""
|
|
188
|
+
|
|
189
|
+
# Create a simple HTML report
|
|
190
|
+
html_content = f"""<!DOCTYPE html>
|
|
191
|
+
<html lang="en">
|
|
192
|
+
<head>
|
|
193
|
+
<meta charset="UTF-8">
|
|
194
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
195
|
+
<title>VibeSurf Task Report</title>
|
|
196
|
+
<style>
|
|
197
|
+
body {{
|
|
198
|
+
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
|
199
|
+
max-width: 1000px;
|
|
200
|
+
margin: 0 auto;
|
|
201
|
+
padding: 20px;
|
|
202
|
+
line-height: 1.6;
|
|
203
|
+
color: #333;
|
|
204
|
+
background-color: #f8f9fa;
|
|
205
|
+
}}
|
|
206
|
+
.container {{
|
|
207
|
+
background: white;
|
|
208
|
+
border-radius: 8px;
|
|
209
|
+
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
|
210
|
+
overflow: hidden;
|
|
211
|
+
}}
|
|
212
|
+
.header {{
|
|
213
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
214
|
+
color: white;
|
|
215
|
+
padding: 30px;
|
|
216
|
+
text-align: center;
|
|
217
|
+
}}
|
|
218
|
+
.header h1 {{
|
|
219
|
+
margin: 0;
|
|
220
|
+
font-size: 2.2em;
|
|
221
|
+
font-weight: 300;
|
|
222
|
+
}}
|
|
223
|
+
.section {{
|
|
224
|
+
margin: 0;
|
|
225
|
+
padding: 25px;
|
|
226
|
+
border-bottom: 1px solid #eee;
|
|
227
|
+
}}
|
|
228
|
+
.section:last-child {{
|
|
229
|
+
border-bottom: none;
|
|
230
|
+
}}
|
|
231
|
+
.section h2 {{
|
|
232
|
+
color: #2c3e50;
|
|
233
|
+
margin-top: 0;
|
|
234
|
+
margin-bottom: 15px;
|
|
235
|
+
font-size: 1.4em;
|
|
236
|
+
border-left: 4px solid #3498db;
|
|
237
|
+
padding-left: 15px;
|
|
238
|
+
}}
|
|
239
|
+
.success {{
|
|
240
|
+
color: #27ae60;
|
|
241
|
+
font-weight: 600;
|
|
242
|
+
}}
|
|
243
|
+
.error {{
|
|
244
|
+
color: #e74c3c;
|
|
245
|
+
font-weight: 600;
|
|
246
|
+
}}
|
|
247
|
+
table {{
|
|
248
|
+
width: 100%;
|
|
249
|
+
border-collapse: collapse;
|
|
250
|
+
margin-top: 15px;
|
|
251
|
+
background: white;
|
|
252
|
+
border-radius: 6px;
|
|
253
|
+
overflow: hidden;
|
|
254
|
+
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
|
|
255
|
+
}}
|
|
256
|
+
th, td {{
|
|
257
|
+
padding: 15px;
|
|
258
|
+
text-align: left;
|
|
259
|
+
border-bottom: 1px solid #eee;
|
|
260
|
+
}}
|
|
261
|
+
th {{
|
|
262
|
+
background: #34495e;
|
|
263
|
+
color: white;
|
|
264
|
+
font-weight: 600;
|
|
265
|
+
}}
|
|
266
|
+
tr:hover {{
|
|
267
|
+
background-color: #f8f9fa;
|
|
268
|
+
}}
|
|
269
|
+
.meta {{
|
|
270
|
+
background: #ecf0f1;
|
|
271
|
+
color: #7f8c8d;
|
|
272
|
+
text-align: center;
|
|
273
|
+
padding: 15px;
|
|
274
|
+
font-size: 0.9em;
|
|
275
|
+
}}
|
|
276
|
+
</style>
|
|
277
|
+
</head>
|
|
278
|
+
<body>
|
|
279
|
+
<div class="container">
|
|
280
|
+
<div class="header">
|
|
281
|
+
<h1>VibeSurf Task Report</h1>
|
|
282
|
+
<p>Generated on {time.strftime('%B %d, %Y at %H:%M:%S')}</p>
|
|
283
|
+
</div>
|
|
284
|
+
|
|
285
|
+
<div class="section">
|
|
286
|
+
<h2>Task Overview</h2>
|
|
287
|
+
<p><strong>Original Task:</strong> {report_data.get('original_task', 'No task specified')}</p>
|
|
288
|
+
<p><strong>Report Type:</strong> {report_data.get('report_type', 'summary').title()}</p>
|
|
289
|
+
</div>
|
|
290
|
+
{upload_files_section}
|
|
291
|
+
|
|
292
|
+
<div class="section">
|
|
293
|
+
<h2>Execution Results</h2>
|
|
294
|
+
<table>
|
|
295
|
+
<thead>
|
|
296
|
+
<tr>
|
|
297
|
+
<th>Task</th>
|
|
298
|
+
<th>Status</th>
|
|
299
|
+
<th>Agent</th>
|
|
300
|
+
<th>Result</th>
|
|
301
|
+
</tr>
|
|
302
|
+
</thead>
|
|
303
|
+
<tbody>
|
|
304
|
+
"""
|
|
305
|
+
|
|
306
|
+
# Add execution results to table
|
|
307
|
+
execution_results = report_data.get('execution_results', [])
|
|
308
|
+
if execution_results:
|
|
309
|
+
for result in execution_results:
|
|
310
|
+
status_class = "success" if result.success else "error"
|
|
311
|
+
status_text = "✅ Success" if result.success else "❌ Failed"
|
|
312
|
+
result_text = result.result or result.error or "No result"
|
|
313
|
+
# Truncate long results
|
|
314
|
+
if len(result_text) > 150:
|
|
315
|
+
result_text = result_text[:147] + "..."
|
|
316
|
+
|
|
317
|
+
html_content += f"""
|
|
318
|
+
<tr>
|
|
319
|
+
<td>{result.task}</td>
|
|
320
|
+
<td class="{status_class}">{status_text}</td>
|
|
321
|
+
<td>{result.agent_id}</td>
|
|
322
|
+
<td>{result_text}</td>
|
|
323
|
+
</tr>
|
|
324
|
+
"""
|
|
325
|
+
else:
|
|
326
|
+
html_content += """
|
|
327
|
+
<tr>
|
|
328
|
+
<td colspan="4" style="text-align: center; color: #7f8c8d; font-style: italic;">No execution results available</td>
|
|
329
|
+
</tr>
|
|
330
|
+
"""
|
|
331
|
+
|
|
332
|
+
html_content += """
|
|
333
|
+
</tbody>
|
|
334
|
+
</table>
|
|
335
|
+
</div>
|
|
336
|
+
|
|
337
|
+
<div class="section">
|
|
338
|
+
<h2>Summary</h2>
|
|
339
|
+
<p>This report was automatically generated by VibeSurf as a fallback when the advanced report generation encountered an issue. The report contains basic information about the task execution and results.</p>
|
|
340
|
+
<p>For future runs, ensure that the LLM service is properly configured and accessible for enhanced report generation capabilities.</p>
|
|
341
|
+
</div>
|
|
342
|
+
|
|
343
|
+
<div class="meta">
|
|
344
|
+
Generated by VibeSurf Agent Framework
|
|
345
|
+
</div>
|
|
346
|
+
</div>
|
|
347
|
+
</body>
|
|
348
|
+
</html>"""
|
|
349
|
+
|
|
350
|
+
# Save fallback report
|
|
351
|
+
report_filename = f"fallback_report_{int(time.time())}.html"
|
|
352
|
+
reports_dir = os.path.join(self.workspace_dir, "reports")
|
|
353
|
+
os.makedirs(reports_dir, exist_ok=True)
|
|
354
|
+
report_path = os.path.join(reports_dir, report_filename)
|
|
355
|
+
|
|
356
|
+
with open(report_path, 'w', encoding='utf-8') as f:
|
|
357
|
+
f.write(html_content)
|
|
358
|
+
|
|
359
|
+
logger.info(f"✅ Fallback report generated: {report_path}")
|
|
360
|
+
return report_path
|