Compare commits

...

16 Commits

Author SHA1 Message Date
Jiayi Zhang ee3ac165ce Merge pull request #1262 from zjyhhhher/ospp/chart-visualization-joy
Pre-commit checks / pre-commit-check (push) Failing after 0s
OSPP/feat: Integrate VMind to enable automated insight annotation and enhance report generation pipeline
2025-10-29 11:40:08 +08:00
zjyhhhher 224a5748d7 Revert "Revert "ospp/openmanus + vmind, report generation optimization""
This reverts commit 359785b162.
2025-10-28 23:04:33 +08:00
zjyhhhher d5f4a5f06f Revert "Revert "ospp/openmanus + vmind, Report Generation Function Optimization""
This reverts commit 63fbd7ebbb.
2025-10-28 23:03:38 +08:00
Jiayi Zhang 1380c20a53 Merge pull request #1260 from zjyhhhher/ospp/chart-visualization
Ospp/chart visualization  revert
2025-10-28 21:53:03 +08:00
zjyhhhher 63fbd7ebbb Revert "ospp/openmanus + vmind, Report Generation Function Optimization"
This reverts commit 68e5e6d10e.
2025-10-28 20:21:14 +08:00
zjyhhhher 359785b162 Revert "ospp/openmanus + vmind, report generation optimization"
This reverts commit 284d654293.
2025-10-28 20:20:41 +08:00
zjyhhhher 60a58a3ac8 Merge branch 'ospp/chart-visualization' of github.com:zjyhhhher/OpenManus into ospp/chart-visualization 2025-10-28 19:59:09 +08:00
zjyhhhher 284d654293 ospp/openmanus + vmind, report generation optimization 2025-10-28 19:48:24 +08:00
zjyhhhher 68e5e6d10e ospp/openmanus + vmind, Report Generation Function Optimization 2025-10-28 19:48:11 +08:00
Jiayi Zhang a058b97400 Merge pull request #1258 from FoundationAgents/revert-1257-revert-1238-ospp/chart-visualization
Revert "Revert "OSPP/feat: Integrate VMind to enable automated insight annotation and enhance report generation pipeline""
2025-10-28 18:57:43 +08:00
Jiayi Zhang 9a8c9dedd7 Revert "Revert "OSPP/feat: Integrate VMind to enable automated insight annotation and enhance report generation pipeline""
Pre-commit checks / pre-commit-check (push) Failing after 0s
2025-10-28 18:52:08 +08:00
Jiayi Zhang c3f1af3ef8 Merge pull request #1257 from FoundationAgents/revert-1238-ospp/chart-visualization
Revert "OSPP/feat: Integrate VMind to enable automated insight annotation and enhance report generation pipeline"
2025-10-28 18:32:06 +08:00
Jiayi Zhang d06fbfb5b3 Revert "OSPP/feat: Integrate VMind to enable automated insight annotation and enhance report generation pipeline"
Pre-commit checks / pre-commit-check (push) Failing after 1s
2025-10-28 18:31:05 +08:00
Jiayi Zhang b2f5e2a29d Merge pull request #1238 from zjyhhhher/ospp/chart-visualization
OSPP/feat: Integrate VMind to enable automated insight annotation and enhance report generation pipeline
2025-10-28 18:26:30 +08:00
zjyhhhher b56a8cbac1 ospp/openmanus + vmind, report generation optimization 2025-09-30 11:41:52 +08:00
lhahah e2a41f4062 ospp/openmanus + vmind, Report Generation Function Optimization 2025-09-30 11:28:43 +08:00
24 changed files with 1491 additions and 233 deletions
+2
View File
@@ -8,6 +8,8 @@ data/
# Workspace
workspace/
config/
### Python ###
# Byte-compiled / optimized / DLL files
__pycache__/
+2 -1
View File
@@ -16,5 +16,6 @@
},
"files.insertFinalNewline": true,
"files.trimTrailingWhitespace": true,
"editor.formatOnSave": true
"editor.formatOnSave": true,
"liveServer.settings.port": 5501
}
+51 -3
View File
@@ -4,9 +4,23 @@ from app.agent.toolcall import ToolCallAgent
from app.config import config
from app.prompt.visualization import NEXT_STEP_PROMPT, SYSTEM_PROMPT
from app.tool import Terminate, ToolCollection
from app.tool.chart_visualization.chart_prepare import VisualizationPrepare
from app.tool.chart_visualization.data_visualization import DataVisualization
from app.tool.chart_visualization.python_execute import NormalPythonExecute
# from app.tool.chart_visualization.chart_prepare import VisualizationPrepare
# from app.tool.chart_visualization.data_visualization import DataVisualization
# from app.tool.chart_visualization.initial_report_generation import GenerateInitialReport
# from app.tool.chart_visualization.final_report_generation import GenerateFinalReport
# from app.tool.chart_visualization.search_report_template import SearchReportTemplate
# from app.tool.chart_visualization.report_template_generation import ReportTemplateGeneration
# from app.tool.chart_visualization.initial_information_collection import InitialInformationCollection
from app.tool.chart_visualization.chart_prepare import VisualizationPrepare
from app.tool.chart_visualization.select_insights import SelectInsights
from app.tool.chart_visualization.add_insights import AddInsights
from app.tool.chart_visualization.data_visualization import DataVisualization
from app.tool.chart_visualization.v2.search_html_library import SearchHtmlLibrary
from app.tool.chart_visualization.v2.initial_report_generation import GenerateInitialReport
from app.tool.chart_visualization.v2.report_template_generation import ReportTemplateGeneration
from app.tool.chart_visualization.v2.final_report_generation import GenerateFinalReport
from app.tool.chart_visualization.v2.report_beautify import ReportBeautify
class DataAnalysis(ToolCallAgent):
@@ -18,7 +32,34 @@ class DataAnalysis(ToolCallAgent):
"""
name: str = "Data_Analysis"
description: str = "An analytical agent that utilizes python and data visualization tools to solve diverse data analysis tasks"
description: str = """
A data science agent specializing in Python-based analytics and advanced visualization techniques
for solving complex data analysis challenges.
Standard Report Generation Workflow:
1. Template Preparation:
- SearchHtmlLibrary: Identify suitable visualization templates
- ReportTemplateGeneration & GenerateInitialReport: Create initial report structure
2. Visualization Pipeline:
- VisualizationPrepare: Configure data for visualization
- DataVisualization: Generate interactive charts and graphs
3. Insight Enhancement:
- SelectInsights: Extract key findings from visualizations
- AddInsights: Annotate charts with analytical insights
4. Report Finalization:
- GenerateFinalReport: Replace the placeholders with charts
- ReportBeautify: Apply professional styling and formatting
Operational Protocol:
- First determine optimal visualization types based on dataset characteristics
- Utilize HTML template library to establish report framework
- Execute visualization pipeline to create data representations
- Enhance each chart with key insights you selected
- Assemble final report by embedding enriched visualizations
"""
system_prompt: str = SYSTEM_PROMPT.format(directory=config.workspace_root)
next_step_prompt: str = NEXT_STEP_PROMPT
@@ -30,8 +71,15 @@ class DataAnalysis(ToolCallAgent):
available_tools: ToolCollection = Field(
default_factory=lambda: ToolCollection(
NormalPythonExecute(),
SearchHtmlLibrary(),
ReportTemplateGeneration(),
GenerateInitialReport(),
GenerateFinalReport(),
ReportBeautify(),
AddInsights(),
VisualizationPrepare(),
DataVisualization(),
SelectInsights(),
Terminate(),
)
)
+27 -1
View File
@@ -1,7 +1,33 @@
SYSTEM_PROMPT = """You are an AI agent designed to data analysis / visualization task. You have various tools at your disposal that you can call upon to efficiently complete complex requests.
# Note:
1. The workspace directory is: {directory}; Read / write file in workspace
2. Generate analysis conclusion report in the end"""
2. Generate analysis conclusion report in the end
Standard Report Generation Workflow:
1. Template Preparation:
- SearchHtmlLibrary: Identify suitable visualization templates
- ReportTemplateGeneration & GenerateInitialReport: Create initial report structure
2. Visualization Pipeline:
- VisualizationPrepare: Configure data for visualization
- DataVisualization: Generate interactive charts and graphs
3. Insight Enhancement:
- SelectInsights: Extract key findings from visualizations
- AddInsights: Annotate charts with analytical insights
4. Report Finalization:
- GenerateFinalReport: Replace the placeholders with charts
- ReportBeautify: Apply professional styling and formatting
Operational Protocol:
- First determine optimal visualization types based on dataset characteristics
- Utilize HTML template library to establish report framework
- Execute visualization pipeline to create data representations
- Enhance each chart with key insights you selected
- Assemble final report by embedding enriched visualizations
"""
NEXT_STEP_PROMPT = """Based on user needs, break down the problem and use different tools step by step to solve it.
# Note
-3
View File
@@ -1,6 +1,3 @@
from app.tool.chart_visualization.chart_prepare import VisualizationPrepare
from app.tool.chart_visualization.data_visualization import DataVisualization
from app.tool.chart_visualization.python_execute import NormalPythonExecute
__all__ = ["DataVisualization", "VisualizationPrepare", "NormalPythonExecute"]
@@ -0,0 +1,228 @@
import sys
import asyncio
import json
import os
print(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))
from typing import Any, Hashable
import pandas as pd
from pydantic import Field, model_validator
from app.config import config
from app.llm import LLM
from app.logger import logger
from app.tool.base import BaseTool
class AddInsights(BaseTool):
name: str = "add_insights"
description: str = (
"Enhances charts by adding insights markers and annotations "
"using JSON data generated by the insights_selection tool. "
"This creates the final annotated visualization output."
)
parameters: dict = {
"type": "object",
"properties": {
"json_path": {
"type": "string",
"description": """Path to the JSON file generated by insights_selection tool.
Contains chart insights data in format:
{
"chartPath": string,
"insights_id": number[]
}""",
},
"output_type": {
"type": "string",
"description": "Visualization output format selection",
"default": "html",
"enum": [
"png", # Static image format
"html" # Interactive web format (recommended)
],
},
},
"required": ["json_path"],
}
llm: LLM = Field(default_factory=LLM, description="Language model instance")
@model_validator(mode="after")
def initialize_llm(self):
"""Initialize llm with default settings if not provided."""
if self.llm is None or not isinstance(self.llm, LLM):
self.llm = LLM(config_name=self.name.lower())
return self
def load_chart_with_css(self, chart_path):
# 读取 HTML 文件
with open(chart_path, 'r', encoding='utf-8') as f:
html_content = f.read()
html_content = html_content.replace('`', "'")
# 在 <head> 里插入 CSS
css = """
<style>
body, html {
margin: 0;
padding: 0;
height: 100%;
overflow: hidden;
}
#chart-container {
width: 100%;
height: 100%;
}
</style>
"""
# 如果原文件没有 <head>,直接插入到最前面
if "<head>" in html_content:
html_content = html_content.replace("<head>", "<head>" + css)
else:
html_content = css + html_content
with open(chart_path, 'w', encoding='utf-8') as f:
f.write(html_content)
def get_file_path(
self,
json_info: list[dict[str, str]],
path_str: str,
directory: str = None,
) -> list[str]:
res = []
for item in json_info:
if os.path.exists(item[path_str]):
res.append(item[path_str])
elif os.path.exists(
os.path.join(f"{directory or config.workspace_root}", item[path_str])
):
res.append(
os.path.join(
f"{directory or config.workspace_root}", item[path_str]
)
)
else:
raise Exception(f"No such file or directory: {item[path_str]}")
return res
async def add_insights(
self, json_info: list[dict[str, str]], output_type: str
) -> str:
data_list = []
chart_file_path = self.get_file_path(
json_info, "chartPath", os.path.join(config.workspace_root, "visualization")
)
for index, item in enumerate(json_info):
if "insights_id" in item:
data_list.append(
{
"file_name": os.path.basename(chart_file_path[index]).replace(
f".{output_type}", ""
),
"insights_id": item["insights_id"],
}
)
tasks = [
self.invoke_vmind(
insights_id=item["insights_id"],
file_name=item["file_name"],
output_type=output_type,
task_type="insight",
)
for item in data_list
]
results = await asyncio.gather(*tasks)
error_list = []
success_list = []
for index, result in enumerate(results):
chart_path = chart_file_path[index]
if "error" in result and "chart_path" not in result:
error_list.append(f"Error in {chart_path}: {result['error']}")
else:
success_list.append(chart_path)
self.load_chart_with_css(chart_path)
success_template = (
f"# Charts Update with Insights\n{','.join(success_list)}"
if len(success_list) > 0
else ""
)
if len(error_list) > 0:
return {
"observation": f"# Error in chart insights:{'\n'.join(error_list)}\n{success_template}",
"success": False,
}
else:
return {"observation": f"{success_template}"}
async def execute(
self,
json_path: str,
output_type: str | None = "html",
tool_type: str | None = "visualization",
language: str | None = "en",
) -> str:
try:
logger.info(f"📈 data_visualization with {json_path} in: {tool_type} ")
with open(json_path, "r", encoding="utf-8") as file:
json_info = json.load(file)
return await self.add_insights(json_info, output_type)
except Exception as e:
return {
"observation": f"Error: {e}",
"success": False,
}
async def invoke_vmind(
self,
file_name: str,
output_type: str,
task_type: str,
insights_id: list[str] = None,
dict_data: list[dict[Hashable, Any]] = None,
chart_description: str = None,
language: str = "en",
):
llm_config = {
"base_url": self.llm.base_url,
"model": self.llm.model,
"api_key": self.llm.api_key,
}
vmind_params = {
"llm_config": llm_config,
"user_prompt": chart_description,
"dataset": dict_data,
"file_name": file_name,
"output_type": output_type,
"insights_id": insights_id,
"task_type": task_type,
"directory": str(config.workspace_root),
"language": language,
}
process = await asyncio.create_subprocess_exec(
"npx",
"ts-node",
"src/chartVisualize.ts",
stdin=asyncio.subprocess.PIPE,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE,
cwd=os.path.dirname(__file__),
)
input_json = json.dumps(vmind_params, ensure_ascii=False).encode("utf-8")
try:
stdout, stderr = await process.communicate(input_json)
stdout_str = stdout.decode("utf-8")
stderr_str = stderr.decode("utf-8")
if process.returncode == 0:
return json.loads(stdout_str)
else:
return {"error": f"Node.js Error: {stderr_str}"}
except Exception as e:
return {"error": f"Subprocess Error: {str(e)}"}
+16 -16
View File
@@ -1,36 +1,36 @@
from app.tool.chart_visualization.python_execute import NormalPythonExecute
class VisualizationPrepare(NormalPythonExecute):
"""A tool for Chart Generation Preparation"""
name: str = "visualization_preparation"
description: str = "Using Python code to generates metadata of data_visualization tool. Outputs: 1) JSON Information. 2) Cleaned CSV data files (Optional)."
description: str = """
You need some charts to replace initial report's placeholders. So you need to use this tool first to prepare metadata for data_visualization tool.
Using Python code to generates metadata of data_visualization tool. Outputs: 1) JSON Information. 2) Cleaned CSV data files (Optional).
"""
parameters: dict = {
"type": "object",
"properties": {
"code_type": {
"description": "code type, visualization: csv -> chart; insight: choose insight into chart",
"description": "code type, visualization: csv -> chart",
"type": "string",
"default": "visualization",
"enum": ["visualization", "insight"],
"default": "visualization"
},
"code": {
"type": "string",
"description": """Python code for data_visualization prepare.
## Visualization Type
## Visualization Type (Initial Step)
1. Data loading logic
2. Csv Data and chart description generate
2.1 Csv data (The data you want to visulazation, cleaning / transform from origin data, saved in .csv)
2.2 Chart description of csv data (The chart title or description should be concise and clear. Examples: 'Product sales distribution', 'Monthly revenue trend'.)
2.1 Csv data (The data you want to visulazation, cleaning / transform from origin data, saved in .csv)
2.2 Chart description of csv data (The chart title or description should be concise and clear. Examples: 'Product sales distribution', 'Monthly revenue trend'.)
3. Save information in json file.( format: {"csvFilePath": string, "chartTitle": string}[])
## Insight Type
1. Select the insights from the data_visualization results that you want to add to the chart.
2. Save information in json file.( format: {"chartPath": string, "insights_id": number[]}[])
# Note
1. You can generate one or multiple csv data with different visualization needs.
2. Make each chart data esay, clean and different.
3. Json file saving in utf-8 with path print: print(json_path)
# Best Practices
1. Generate one or multiple csv data with different visualization needs based on the initial report
2. Make each chart data simple, clean and distinct
4. Json file saving in utf-8 with path print: print(json_path)
""",
},
},
@@ -14,12 +14,8 @@ from app.tool.base import BaseTool
class DataVisualization(BaseTool):
name: str = "data_visualization"
description: str = """Visualize statistical chart or Add insights in chart with JSON info from visualization_preparation tool. You can do steps as follows:
1. Visualize statistical chart
2. Choose insights into chart based on step 1 (Optional)
Outputs:
1. Charts (png/html)
2. Charts Insights (.md)(Optional)"""
description: str = """Visualize statistical chart with JSON info from visualization_preparation tool.
Outputs: Charts (png/html)"""
parameters: dict = {
"type": "object",
"properties": {
@@ -34,10 +30,9 @@ Outputs:
"enum": ["png", "html"],
},
"tool_type": {
"description": "visualize chart or add insights",
"description": "visualize",
"type": "string",
"default": "visualization",
"enum": ["visualization", "insight"],
},
"language": {
"description": "english(en) / chinese(zh)",
@@ -79,11 +74,43 @@ Outputs:
raise Exception(f"No such file or directory: {item[path_str]}")
return res
def load_chart_with_css(self, chart_path):
# 读取 HTML 文件
with open(chart_path, 'r', encoding='utf-8') as f:
html_content = f.read()
# 在 <head> 里插入 CSS
css = """
<style>
body, html {
margin: 0;
padding: 0;
height: 100%;
overflow: hidden;
}
#chart-container {
width: 100%;
height: 100%;
}
</style>
"""
# 如果原文件没有 <head>,直接插入到最前面
if "<head>" in html_content:
html_content = html_content.replace("<head>", "<head>" + css)
else:
html_content = css + html_content
with open(chart_path, 'w', encoding='utf-8') as f:
f.write(html_content)
def success_output_template(self, result: list[dict[str, str]]) -> str:
content = ""
if len(result) == 0:
return "Is EMPTY!"
for item in result:
chart_path=item['chart_path']
self.load_chart_with_css(chart_path)
content += f"""## {item['title']}\nChart saved in: {item['chart_path']}"""
if "insight_path" in item and item["insight_path"] and "insight_md" in item:
content += "\n" + item["insight_md"]
@@ -145,7 +172,7 @@ Outputs:
else:
return {"observation": f"{self.success_output_template(success_list)}"}
async def add_insighs(
async def add_insights(
self, json_info: list[dict[str, str]], output_type: str
) -> str:
data_list = []
@@ -207,7 +234,7 @@ Outputs:
if tool_type == "visualization":
return await self.data_visualization(json_info, output_type, language)
else:
return await self.add_insighs(json_info, output_type)
return await self.add_insights(json_info, output_type)
except Exception as e:
return {
"observation": f"Error: {e}",
+28 -13
View File
@@ -10,7 +10,7 @@
"license": "ISC",
"dependencies": {
"@visactor/vchart": "^1.13.7",
"@visactor/vmind": "2.0.5",
"@visactor/vmind": "2.0.6-alpha.2",
"get-stdin": "^9.0.0",
"puppeteer": "^24.9.0"
},
@@ -6319,9 +6319,9 @@
}
},
"node_modules/@visactor/calculator": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/@visactor/calculator/-/calculator-2.0.5.tgz",
"integrity": "sha512-/NBDB/wBQLeQuSspDBuiEAbbyfJS/xPX6mubVsLGhfy65UwUBojAQgmX25FcRJnUsRXooK5heshni19DBBf8xA==",
"version": "2.0.6-alpha.2",
"resolved": "https://registry.npmjs.org/@visactor/calculator/-/calculator-2.0.6-alpha.2.tgz",
"integrity": "sha512-eSihYc5cTOeH3gFIW5lBBSWk1PDPDrO/dhaz3G6ZfRRx/wLNf5K1W1jCUaKeNcfcLyydB+6JH7u1k8vm2oIznw==",
"dependencies": {
"@visactor/vutils": "~0.19.3",
"node-sql-parser": "~4.17.0",
@@ -6329,13 +6329,26 @@
}
},
"node_modules/@visactor/chart-advisor": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/@visactor/chart-advisor/-/chart-advisor-2.0.5.tgz",
"integrity": "sha512-pvHceRlworB7kDSmbWXUtherLLXh5nMj0aEGuxtzKQyHmeO0sjuu9gGXBFIgscGliSZM4tmeNrFU9eBLGJ8dxw==",
"version": "2.0.6-alpha.2",
"resolved": "https://registry.npmjs.org/@visactor/chart-advisor/-/chart-advisor-2.0.6-alpha.2.tgz",
"integrity": "sha512-QlhM5s3o48QtUDn0VmJB6xwYwBPgUh2SfxYosGKrbmajRFUb8e6I6LDKTNOda2dQ3/e0a04+zZIiDTpViMxkCw==",
"license": "MIT",
"dependencies": {
"@visactor/vutils": "~0.19.3"
}
},
"node_modules/@visactor/generate-vchart": {
"version": "2.0.6-alpha.2",
"resolved": "https://registry.npmjs.org/@visactor/generate-vchart/-/generate-vchart-2.0.6-alpha.2.tgz",
"integrity": "sha512-Md62wBLtAwIZ/a04xRyCfgeFDy0sgwUiBei6vD6FoE+vgEkpp9+Vf0jpMXK1sTuxFa3nxi8GxcUsDmiNwEZJ9w==",
"dependencies": {
"@visactor/vchart-theme": "~1.12.2",
"@visactor/vutils": "~0.19.3",
"dayjs": "~1.11.10",
"node-sql-parser": "~4.17.0",
"ts-pattern": "~4.1.4"
}
},
"node_modules/@visactor/vchart": {
"version": "1.13.8",
"resolved": "https://registry.npmjs.org/@visactor/vchart/-/vchart-1.13.8.tgz",
@@ -6496,14 +6509,16 @@
}
},
"node_modules/@visactor/vmind": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/@visactor/vmind/-/vmind-2.0.5.tgz",
"integrity": "sha512-QztQaeSkdeRZYOUlB4qaBpx3/swyO3JzFH8eYvSgvptS/rf8aQDZiufAUasafDLkcME5N6RpBGkcGYIDkmt74Q==",
"version": "2.0.6-alpha.2",
"resolved": "https://registry.npmjs.org/@visactor/vmind/-/vmind-2.0.6-alpha.2.tgz",
"integrity": "sha512-bPuZ4U7dIxHbYnu1oSuddJc5HvRN194Yair9kYqlx4fBqNukHAqWAUEyCdir9YzyJVDvSdEJ7xqmvXecW5W3WQ==",
"license": "MIT",
"dependencies": {
"@stdlib/stats-base-dists-t-quantile": "0.2.1",
"@visactor/calculator": "2.0.5",
"@visactor/chart-advisor": "2.0.5",
"@visactor/vchart-theme": "^1.11.2",
"@visactor/calculator": "2.0.6-alpha.2",
"@visactor/chart-advisor": "2.0.6-alpha.2",
"@visactor/generate-vchart": "2.0.6-alpha.2",
"@visactor/vchart-theme": "~1.12.2",
"@visactor/vdataset": "~0.19.3",
"@visactor/vutils": "~0.19.3",
"alasql": "~4.3.2",
+1 -1
View File
@@ -9,7 +9,7 @@
},
"dependencies": {
"@visactor/vchart": "^1.13.7",
"@visactor/vmind": "2.0.5",
"@visactor/vmind": "2.0.6-alpha.2",
"get-stdin": "^9.0.0",
"puppeteer": "^24.9.0"
},
@@ -0,0 +1,54 @@
from app.tool.chart_visualization.python_execute import NormalPythonExecute
class SelectInsights(NormalPythonExecute):
name: str = "insights_selection"
description: str = (
"This tool analyzes data_visualization tool's outputs and identifies key data insights for each chart."
"based on their importance ranking. Insights are prioritized in three tiers:\n"
"1 **Critical Insights**: 'abnormal_trend', 'abnormal_band', 'turning_point', 'overall_trend'\n"
"2 **Important Insights**: 'outlier', 'extreme_value', 'majority_value', 'avg'\n"
"3 **Basic Insights**: 'min', 'max'\n\n"
"**!Must be called immediately after data_visualization completes!**"
"**!All insights_id must come from the data_visualization analysis results!**"
)
parameters: dict = {
"type": "object",
"properties": {
"code": {
"type": "string",
"description": """Python code to analyze visualized charts and extract insights.
# PRIORITY REQUIREMENTS
Insights must be selected and ranked according to these importance tiers:
1. **First Priority**: Always include 'abnormal_trend', 'abnormal_band', 'turning_point', 'overall_trend' when present
2. **Second Priority**: Include 'outlier', 'extreme_value', 'majority_value', 'avg' if no first-tier insights exist
3. **Third Priority**: Fall back to 'min', 'max' only when no higher-priority insights are available
# EXECUTION REQUIREMENTS
1. **Timing**: MUST be called immediately after data_visualization completes
2. **Dependency**: MUST use insights from data_visualization output as the only source for insights_id
# CODE REQUIREMENTS
Your Python code must:
1. Analyze the data_visualization results to identify significant insights for each chart.
2. Save the findings in JSON format:
```json
[
{
"chartPath": "string", // Path to the generated chart
"insights_id": number[] // Array of key insight IDs FROM DATA_VISUALIZATION RESULTS
},
{
"chartPath": "string", // Path to the generated chart
"insights_id": number[] // Array of key insight IDs FROM DATA_VISUALIZATION RESULTS
},
...
]
```
Json file saving in utf-8 with path print: print(json_path)
""",
},
},
"required": ["code"],
}
@@ -1,7 +1,8 @@
import path from "path";
import fs from "fs";
import puppeteer from "puppeteer";
import VMind, { ChartType, DataTable } from "@visactor/vmind";
import VMind, { ChartType } from "@visactor/vmind";
import type { DataTable } from '@visactor/generate-vchart';
import { isString } from "@visactor/vutils";
enum AlgorithmType {
@@ -63,8 +64,7 @@ function getHtmlVChart(spec: any, width?: number, height?: number) {
<script src="https://unpkg.com/@visactor/vchart/build/index.min.js"></script>
</head>
<body>
<div id="chart-container" style="width: ${
width ? `${width}px` : "100%"
<div id="chart-container" style="width: ${width ? `${width}px` : "100%"
}; height: ${height ? `${height}px` : "100%"};"></div>
<script>
// parse spec with function
@@ -82,7 +82,7 @@ function getHtmlVChart(spec: any, width?: number, height?: number) {
return v;
});
}
const spec = parseSpec(\`${serializeSpec(spec)}\`);
const spec = parseSpec(\'${serializeSpec(spec)}\');
const chart = new VChart.VChart(spec, {
dom: 'chart-container'
});
@@ -242,7 +242,7 @@ async function generateChart(
ChartType.AreaChart,
ChartType.ScatterPlot,
ChartType.DualAxisChart,
].includes(chartType)
].includes(chartType as ChartType)
) {
const { insights: vmindInsights } = await vmind.getInsights(spec, {
maxNum: 6,
@@ -295,11 +295,14 @@ async function updateChartWithInsight(
}
) {
const { directory, outputType, fileName, insightsId } = options;
//???????为什么打印不出来???????
//console.log(options)
let res: { error?: string; chart_path?: string } = {};
try {
const specPath = getSavedPathName(directory, fileName, "json", true);
const spec = JSON.parse(fs.readFileSync(specPath, "utf8"));
// llm select index from 1
//console.log(spec)
const insights = (spec.insights || []).filter(
(_insight: any, index: number) => insightsId.includes(index + 1)
);
+85 -168
View File
@@ -1,5 +1,8 @@
import asyncio
import os
import sys
print(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))))
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))))
from app.agent.data_analysis import DataAnalysis
from app.logger import logger
@@ -7,173 +10,87 @@ from app.logger import logger
prefix = "Help me generate charts and save them locally, specifically:"
tasks = [
{
"prompt": "Help me show the sales of different products in different regions",
"data": """Product Name,Region,Sales
Coke,South,2350
Coke,East,1027
Coke,West,1027
Coke,North,1027
Sprite,South,215
Sprite,East,654
Sprite,West,159
Sprite,North,28
Fanta,South,345
Fanta,East,654
Fanta,West,2100
Fanta,North,1679
Xingmu,South,1476
Xingmu,East,830
Xingmu,West,532
Xingmu,North,498
""",
},
{
"prompt": "Show market share of each brand",
"data": """Brand Name,Market Share,Average Price,Net Profit
Apple,0.5,7068,314531
Samsung,0.2,6059,362345
Vivo,0.05,3406,234512
Nokia,0.01,1064,-1345
Xiaomi,0.1,4087,131345""",
},
{
"prompt": "Please help me show the sales trend of each product",
"data": """Date,Type,Value
2023-01-01,Product A,52.9
2023-01-01,Product B,63.6
2023-01-01,Product C,11.2
2023-01-02,Product A,45.7
2023-01-02,Product B,89.1
2023-01-02,Product C,21.4
2023-01-03,Product A,67.2
2023-01-03,Product B,82.4
2023-01-03,Product C,31.7
2023-01-04,Product A,80.7
2023-01-04,Product B,55.1
2023-01-04,Product C,21.1
2023-01-05,Product A,65.6
2023-01-05,Product B,78
2023-01-05,Product C,31.3
2023-01-06,Product A,75.6
2023-01-06,Product B,89.1
2023-01-06,Product C,63.5
2023-01-07,Product A,67.3
2023-01-07,Product B,77.2
2023-01-07,Product C,43.7
2023-01-08,Product A,96.1
2023-01-08,Product B,97.6
2023-01-08,Product C,59.9
2023-01-09,Product A,96.1
2023-01-09,Product B,100.6
2023-01-09,Product C,66.8
2023-01-10,Product A,101.6
2023-01-10,Product B,108.3
2023-01-10,Product C,56.9""",
},
{
"prompt": "Show the popularity of search keywords",
"data": """Keyword,Popularity
Hot Word,1000
Zao Le Wo Men,800
Rao Jian Huo,400
My Wish is World Peace,400
Xiu Xiu Xiu,400
Shenzhou 11,400
Hundred Birds Facing the Wind,400
China Women's Volleyball Team,400
My Guan Na,400
Leg Dong,400
Hot Pot Hero,400
Baby's Heart is Bitter,400
Olympics,400
Awesome My Brother,400
Poetry and Distance,400
Song Joong-ki,400
PPAP,400
Blue Thin Mushroom,400
Rain Dew Evenly,400
Friendship's Little Boat Says It Flips,400
Beijing Slump,400
Dedication,200
Apple,200
Dog Belt,200
Old Driver,200
Melon-Eating Crowd,200
Zootopia,200
City Will Play,200
Routine,200
Water Reverse,200
Why Don't You Go to Heaven,200
Snake Spirit Man,200
Why Don't You Go to Heaven,200
Samsung Explosion Gate,200
Little Li Oscar,200
Ugly People Need to Read More,200
Boyfriend Power,200
A Face of Confusion,200
Descendants of the Sun,200""",
},
{
"prompt": "Help me compare the performance of different electric vehicle brands using a scatter plot",
"data": """Range,Charging Time,Brand Name,Average Price
2904,46,Brand1,2350
1231,146,Brand2,1027
5675,324,Brand3,1242
543,57,Brand4,6754
326,234,Brand5,215
1124,67,Brand6,654
3426,81,Brand7,159
2134,24,Brand8,28
1234,52,Brand9,345
2345,27,Brand10,654
526,145,Brand11,2100
234,93,Brand12,1679
567,94,Brand13,1476
789,45,Brand14,830
469,75,Brand15,532
5689,54,Brand16,498
""",
},
{
"prompt": "Show conversion rates for each process",
"data": """Process,Conversion Rate,Month
Step1,100,1
Step2,80,1
Step3,60,1
Step4,40,1""",
},
{
"prompt": "Show the difference in breakfast consumption between men and women",
"data": """Day,Men-Breakfast,Women-Breakfast
Monday,15,22
Tuesday,12,10
Wednesday,15,20
Thursday,10,12
Friday,13,15
Saturday,10,15
Sunday,12,14""",
},
{
"prompt": "Help me show this person's performance in different aspects, is he a hexagonal warrior",
"data": """dimension,performance
Strength,5
Speed,5
Shooting,3
Endurance,5
Precision,5
Growth,5""",
},
{
"prompt": "Show data flow",
"data": """Origin,Destination,value
Node A,Node 1,10
Node A,Node 2,5
Node B,Node 2,8
Node B,Node 3,2
Node C,Node 2,4
Node A,Node C,2
Node C,Node 1,2""",
},
"prompt": "Help me show the daily sales performance metrics over time. ",
"data":
"""Table: 每日销售数据明细​
OrderDate,RegionCode,SalesAmount,DataQuality
2023-04-01,SW,25860.24,正常
2023-04-01,NE,5877.65,正常
2023-04-01,C,34271.58,正常
2023-04-01,N,6251.42,正常
2023-04-01,E,3113.46,正常
2023-04-02,NW,87717.84,正常
2023-04-02,E,53058.96,正常
2023-04-02,N,14409.92,正常
2023-04-02,NE,5540.92,正常
2023-04-03,C,41802.49,正常
2023-04-03,NE,9202.20,正常
2023-04-03,SW,583.30,正常
2023-04-03,N,560.56,正常
2023-04-03,E,96269.32,正常
2023-04-04,NE,106208.48,正常
2023-04-04,C,6231.62,正常
2023-04-04,E,84454.83,正常
2023-04-05,NE,312.82,正常
2023-04-05,SW,5718.89,正常
2023-04-05,C,39811.91,正常
2023-04-05,E,244163.47,正常
2023-04-05,N,79487.41,正常
2023-04-06,C,28648.34,正常
2023-04-06,N,18288.76,正常
2023-04-08,SW,67434.58,正常
2023-04-08,C,1176.00,正常
2023-04-08,NW,81264.54,正常
2023-04-08,E,87750.96,正常
2023-04-09,SW,67434.58,正常
2023-04-09,C,5902.34,正常
2023-04-09,E,22252.27,正常
2023-04-09,NE,98844.76,正常
2023-04-10,E,2677.36,正常
2023-04-10,SW,1444.52,正常
2023-04-10,NE,62082.61,正常
2023-04-10,C,677.38,正常
2023-04-11,SW,776.16,正常
2023-04-11,C,7487.00,正常
2023-04-11,E,57016.40,正常
2023-04-12,SW,7131.85,正常
2023-04-12,E,11837.81,正常
2023-04-12,C,207763.14,正常
2023-04-13,C,24299.30,正常
2023-04-13,E,9847.04,正常
2023-04-13,NE,15919.12,正常
2023-04-15,SW,33544.42,正常
2023-04-15,C,39935.98,正常
2023-04-15,N,60416.80,正常
2023-04-15,NE,1234.80,正常
2023-04-15,E,36007.16,正常
2023-04-16,E,108484.04,正常
2023-04-16,SW,2450.00,正常
2023-04-17,E,78099.14,正常
2023-04-17,C,49350.84,正常
2023-04-17,N,18480.84,正常
2023-04-18,NE,48419.84,正常
2023-04-18,C,118951.67,正常
2023-04-18,N,10853.89,正常
2023-04-18,SW,1627.58,正常
2023-04-18,E,32777.28,正常
2023-04-19,N,41905.78,正常
2023-04-19,E,47942.58,正常
2023-04-19,NE,48259.51,正常
2023-04-19,C,18021.22,正常
2023-04-22,NE,58530.50,正常
2023-04-22,E,22004.72,正常
2023-04-22,C,7729.26,正常
2023-04-23,E,49218.54,正常
2023-04-23,NE,13944.42,正常
2023-04-23,SW,3843.56,正常
2023-04-24,SW,16754.08,正常
2023-04-24,NE,2343.18,正常
2023-04-24,E,41413.82,正常
2023-04-24,C,723.24,正常
2023-04-25,C,10678.08,正常
2023-04-25,E,44791.10,正常"""
}
]
@@ -0,0 +1,84 @@
import asyncio
import os
import sys
print(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))))
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))))
from app.agent.data_analysis import DataAnalysis
from app.agent.manus import Manus
# from app.agent.manus import Manus
async def main():
# agent = DataAnalysis()
agent = Manus()
await agent.run(
"""Requirement: Analyze the following data and generate a simple data report with some charts in HTML format.
Table1: 学生基本信息
Name,Age,Gender,Grade,Class,StudentID,EnrollmentDate,GuardianName,GuardianPhone,Address,PreviousSchool
王悦,16,女,高一,3班,S20230147,2023-09-01,王建国,138-1234-5678,北京市海淀区学院路101号,海淀实验中学
Table2: 学科成绩详细数据
Subject,Teacher,TestType,TestDate,Score,ClassRank,GradeRank,ScoreChange,DifficultyLevel
数学,张老师,单元测验,2023-09-05,82,12,45,-,中等
数学,张老师,周测,2023-09-12,85,10,38,+3,中等
数学,张老师,月考,2023-09-28,87,8,35,+2,中等偏难
数学,张老师,期中考试,2023-10-15,92,5,22,+5,难
语文,李老师,单元测验,2023-09-06,80,15,50,-,中等
语文,李老师,周测,2023-09-13,83,12,42,+3,中等
语文,李老师,月考,2023-09-29,85,10,40,+2,中等偏难
语文,李老师,期中考试,2023-10-16,88,8,35,+3,难
英语,王老师,单元测验,2023-09-07,87,8,30,-,中等
英语,王老师,周测,2023-09-14,89,6,25,+2,中等
英语,王老师,月考,2023-09-30,90,5,20,+1,中等偏难
英语,王老师,期中考试,2023-10-17,93,4,18,+3,难
物理,刘老师,单元测验,2023-09-08,75,18,65,-,难
物理,刘老师,周测,2023-09-15,77,16,60,+2,难
物理,刘老师,月考,2023-10-01,78,15,58,+1,难
物理,刘老师,期中考试,2023-10-18,85,12,40,+7,中等偏难
化学,陈老师,单元测验,2023-09-09,79,14,55,-,中等偏难
化学,陈老师,周测,2023-09-16,81,12,50,+2,中等偏难
化学,陈老师,月考,2023-10-02,82,11,48,+1,中等偏难
化学,陈老师,期中考试,2023-10-19,84,10,45,+2,中等
Table3: 学习行为日数据(2023年9月)
Date,Weekday,StudyHours,HomeworkHours,ReadingMinutes,ScreenTime,PhysicalActivity,ClassAttendance,ParticipationScore,SleepHours,MoodScore
2023-09-01,周五,3.5,2.0,45,1.5,1.0,出勤,85,8.2,4
2023-09-02,周六,4.0,1.5,30,2.0,0.8,出勤,90,7.8,5
2023-09-03,周日,3.0,2.5,60,1.0,1.2,出勤,88,8.5,4
2023-09-04,周一,5.0,2.0,50,1.2,0.7,出勤,92,7.9,5
2023-09-05,周二,4.5,1.8,40,1.8,1.0,出勤,87,8.0,4
2023-09-06,周三,3.8,2.2,55,1.3,0.9,出勤,89,8.3,5
2023-09-07,周四,4.2,1.7,35,1.6,1.1,出勤,91,7.7,4
2023-09-08,周五,3.5,2.1,48,1.4,0.8,出勤,86,8.1,3
2023-09-09,周六,4.8,1.9,42,1.7,1.3,出勤,93,7.6,5
2023-09-10,周日,3.2,2.3,52,1.1,0.7,出勤,88,8.4,4
2023-09-11,周一,4.5,1.6,38,1.9,1.0,出勤,90,7.9,5
2023-09-12,周二,3.9,2.4,57,1.2,0.9,出勤,87,8.2,4
2023-09-13,周三,4.1,1.8,44,1.5,1.2,出勤,91,7.8,5
2023-09-14,周四,3.7,2.6,49,1.4,0.8,出勤,89,8.3,4
2023-09-15,周五,4.3,1.9,36,1.8,1.1,出勤,92,7.7,5
2023-09-16,周六,3.4,2.2,53,1.3,0.9,出勤,86,8.1,4
2023-09-17,周日,4.6,1.7,41,1.6,1.3,出勤,90,7.6,5
2023-09-18,周一,3.8,2.5,47,1.2,0.7,出勤,88,8.4,4
2023-09-19,周二,4.2,1.8,39,1.7,1.0,出勤,91,7.9,5
2023-09-20,周三,3.9,2.3,51,1.4,0.8,出勤,87,8.2,4
2023-09-21,周四,4.4,1.6,43,1.5,1.2,出勤,93,7.8,5
2023-09-22,周五,3.6,2.4,46,1.3,0.9,出勤,89,8.3,4
2023-09-23,周六,4.7,1.9,37,1.8,1.1,出勤,86,7.7,5
2023-09-24,周日,3.5,2.1,50,1.4,0.8,出勤,90,8.1,4
2023-09-25,周一,4.3,1.7,42,1.6,1.3,出勤,88,7.6,5
2023-09-26,周二,3.8,2.6,48,1.2,0.7,出勤,91,8.4,4
2023-09-27,周三,4.1,1.8,40,1.7,1.0,出勤,87,7.9,5
2023-09-28,周四,3.9,2.3,52,1.5,0.9,出勤,92,8.2,4
2023-09-29,周五,4.5,1.6,45,1.4,1.2,出勤,89,7.8,5
2023-09-30,周六,3.7,2.4,44,1.3,0.8,出勤,86,8.3,4
"""
)
if __name__ == "__main__":
asyncio.run(main())
@@ -0,0 +1,75 @@
import asyncio
import os
import sys
print(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))))
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))))
from app.agent.data_analysis import DataAnalysis
from app.agent.manus import Manus
# from app.agent.manus import Manus
async def main():
# agent = DataAnalysis()
agent = Manus()
await agent.run(
"""Requirement: Analyze the following data and generate a graphical data report in HTML format. The final product should be a data report.
Table1: 用户基本信息
Name,Age,Gender,Height(cm),Weight(kg),MeasureDate
张三,45,男,170,98.6,2023-10-01
Table2: 身体成分数据
Date,BodyFat(%),MuscleMass(kg),WaterContent(%),BoneMass(kg),VisceralFat,Protein(%)
2023-10-01,35.2,42.3,43.1,2.8,18,14.2
Table3: 每日营养活动数据
Date,CaloriesIntake(kcal),CaloriesBurned(kcal),ProteinIntake(g),CarbIntake(g),FatIntake(g),Steps,ActiveMinutes,SleepHours,MorningWeight(kg)
2023-09-01,3850,1250,85,480,120,3200,12,5.2,99.2
2023-09-02,4200,1100,90,520,135,2800,8,4.8,99.5
2023-09-03,3600,950,78,450,110,2500,5,5.5,99.8
2023-09-04,3950,1050,82,490,125,2900,10,4.5,100.1
2023-09-05,4100,1150,88,510,130,3100,15,5.0,100.3
2023-09-06,3750,980,80,460,115,2600,6,4.7,100.6
2023-09-07,4300,1200,92,540,140,3300,18,4.3,100.9
2023-09-08,3450,900,75,430,105,2400,4,5.2,101.2
2023-09-09,4000,1000,84,500,122,2700,9,4.9,101.5
2023-09-10,3800,975,79,470,118,2550,7,5.1,101.8
2023-09-11,4150,1100,86,515,128,3000,14,4.6,102.0
2023-09-12,3550,925,77,440,108,2300,3,5.4,102.3
2023-09-13,4250,1180,94,530,138,3400,20,4.2,102.6
2023-09-14,3700,990,81,465,113,2650,8,5.3,102.9
2023-09-15,4050,1070,87,505,127,2950,13,4.8,103.1
2023-09-16,3500,910,76,435,104,2250,2,5.6,103.4
2023-09-17,3900,1020,83,485,120,2750,11,4.7,103.7
2023-09-18,4350,1250,96,550,145,3500,22,4.0,104.0
2023-09-19,3650,960,79,455,112,2450,5,5.2,104.3
2023-09-20,4000,1030,85,495,124,2850,12,4.9,104.5
2023-09-21,3450,890,74,425,103,2200,1,5.5,104.8
2023-09-22,3850,995,82,475,119,2700,10,4.6,105.1
2023-09-23,4100,1120,89,515,131,3050,16,4.3,105.4
2023-09-24,3550,935,77,440,107,2350,4,5.4,105.7
2023-09-25,3950,1010,84,490,123,2800,13,4.8,106.0
2023-09-26,4200,1150,91,525,136,3200,19,4.1,106.3
2023-09-27,3600,945,78,445,109,2400,6,5.3,106.6
2023-09-28,4050,1080,87,505,128,2900,15,4.7,106.9
2023-09-29,3750,970,80,460,116,2600,9,5.0,107.2
2023-09-30,4300,1220,95,540,142,3350,23,3.9,107.5
Table4: 健康指标参考范围
Category,SubCategory,Range,Unit
BMI,Underweight,<18.5,kg/m²
BMI,Normal,18.5-24.9,kg/m²
BMI,Overweight,25-29.9,kg/m²
BMI,Obese,≥30,kg/m²
BodyFat,Male(40-59),11-22,%
BodyFat,Female(40-59),23-34,%
VisceralFat,Normal,1-9,Level
VisceralFat,High,10-14,Level
VisceralFat,Very High,≥15,Level
Sleep,Recommended,7-9,hours
Steps,Active,≥10000,steps/day
"""
)
if __name__ == "__main__":
asyncio.run(main())
@@ -0,0 +1,123 @@
import asyncio
import os
import sys
print(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))))
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))))
from app.agent.data_analysis import DataAnalysis
from app.agent.manus import Manus
# from app.agent.manus import Manus
async def main():
# agent = DataAnalysis()
agent = Manus()
await agent.run(
"""Requirement: Analyze the following data and generate a graphical data report in HTML format. The final product should be a data report.
Table1: 销售区域基本信息​
RegionCode,RegionName,Description
SW,西南,西南地区销售数据
NE,东北,东北地区销售数据
C,中南,中南地区销售数据
N,华北,华北地区销售数据
E,华东,华东地区销售数据
NW,西北,西北地区销售数据
Table2: 每日销售数据明细​
OrderDate,RegionCode,SalesAmount,DataQuality
2023-04-01,SW,25860.24,正常
2023-04-01,NE,5877.65,正常
2023-04-01,C,34271.58,正常
2023-04-01,N,6251.42,正常
2023-04-01,E,3113.46,正常
2023-04-02,NW,87717.84,正常
2023-04-02,E,53058.96,正常
2023-04-02,N,14409.92,正常
2023-04-02,NE,5540.92,正常
2023-04-03,C,41802.49,正常
2023-04-03,NE,9202.20,正常
2023-04-03,SW,583.30,正常
2023-04-03,N,560.56,正常
2023-04-03,E,96269.32,正常
2023-04-04,NE,106208.48,正常
2023-04-04,C,6231.62,正常
2023-04-04,E,84454.83,正常
2023-04-05,NE,312.82,正常
2023-04-05,SW,5718.89,正常
2023-04-05,C,39811.91,正常
2023-04-05,E,244163.47,正常
2023-04-05,N,79487.41,正常
2023-04-06,C,28648.34,正常
2023-04-06,N,18288.76,正常
2023-04-08,SW,67434.58,正常
2023-04-08,C,1176.00,正常
2023-04-08,NW,81264.54,正常
2023-04-08,E,87750.96,正常
2023-04-09,SW,67434.58,正常
2023-04-09,C,5902.34,正常
2023-04-09,E,22252.27,正常
2023-04-09,NE,98844.76,正常
2023-04-10,E,2677.36,正常
2023-04-10,SW,1444.52,正常
2023-04-10,NE,62082.61,正常
2023-04-10,C,677.38,正常
2023-04-11,SW,776.16,正常
2023-04-11,C,7487.00,正常
2023-04-11,E,57016.40,正常
2023-04-12,SW,7131.85,正常
2023-04-12,E,11837.81,正常
2023-04-12,C,207763.14,正常
2023-04-13,C,24299.30,正常
2023-04-13,E,9847.04,正常
2023-04-13,NE,15919.12,正常
2023-04-15,SW,33544.42,正常
2023-04-15,C,39935.98,正常
2023-04-15,N,60416.80,正常
2023-04-15,NE,1234.80,正常
2023-04-15,E,36007.16,正常
2023-04-16,E,108484.04,正常
2023-04-16,SW,2450.00,正常
2023-04-17,E,78099.14,正常
2023-04-17,C,49350.84,正常
2023-04-17,N,18480.84,正常
2023-04-18,NE,48419.84,正常
2023-04-18,C,118951.67,正常
2023-04-18,N,10853.89,正常
2023-04-18,SW,1627.58,正常
2023-04-18,E,32777.28,正常
2023-04-19,N,41905.78,正常
2023-04-19,E,47942.58,正常
2023-04-19,NE,48259.51,正常
2023-04-19,C,18021.22,正常
2023-04-22,NE,58530.50,正常
2023-04-22,E,22004.72,正常
2023-04-22,C,7729.26,正常
2023-04-23,E,49218.54,正常
2023-04-23,NE,13944.42,正常
2023-04-23,SW,3843.56,正常
2023-04-24,SW,16754.08,正常
2023-04-24,NE,2343.18,正常
2023-04-24,E,41413.82,正常
2023-04-24,C,723.24,正常
2023-04-25,C,10678.08,正常
2023-04-25,E,44791.10,正常
Table3: 销售指标分析参考​
Category,SubCategory,Range,Level,Description
销售金额,日销售额,>50000,优秀,单日销售额超过5万
销售金额,日销售额,20000-50000,良好,单日销售额2-5万
销售金额,日销售额,5000-20000,一般,单日销售额0.5-2万
销售金额,日销售额,<5000,待提升,单日销售额低于5千
区域表现,华东地区,持续领先,优秀,销售额稳定高位
区域表现,中南地区,波动较大,关注,销售额波动明显
区域表现,西南地区,整体偏低,待提升,需要业务拓展
数据完整性,日期覆盖,4月1-25日,完整,覆盖主要业务日期
数据质量,金额精度,两位小数,标准,财务数据标准格式
业务连续性,工作日,正常营业,良好,除周末外正常营业
"""
)
if __name__ == "__main__":
asyncio.run(main())
File diff suppressed because one or more lines are too long
@@ -0,0 +1,47 @@
from app.tool.chart_visualization.python_execute import NormalPythonExecute
class GenerateFinalReport(NormalPythonExecute):
"""A tool for generating final data analysis report"""
name: str = "generate_final_report"
description: str = """Replace the all placeholders in initial report and refine to generate final report.
Outputs: 1) HTML report file path"""
parameters: dict = {
"type": "object",
"properties": {
"code_type": {
"description": "code type, replacing: html with placeholders -> html with specific chart; check_refine: Make final adjustments and checks on the report",
"type": "string",
"default": "replacing",
"enum": ["replacing", "check_refine"],
},
"code": {
"type": "string",
"description": """Python code for replacing chart placeholders with specific chart file path, or for report checking and refining.
# Replacing Type
1. Find all placeholders
2. When replacing the chart path placeholder, use a relative path. ** src="/workspace/visualization/chart_name.html" **
3. When replacing the text placeholders, generate a detailed text description.
**When filling in the key insights below the charts, the insights must correspond with those previously added by the add insight tool.**
## Notice:
Use the absolute path starting with /workspace, for example, **/workspace/visualization/chart_name.html**
# Check_refine Type:
1. Check the entire html file
- Have all placeholders been filled?
- Whether the path of the chart is: /workspace/visualization/***.html
- If text-related placeholders still exist, please fill them in as much as possible. If chart path placeholders still exist, please delete that chart part of the report.
**When filling in the key insights below the charts, the insights must correspond with those previously added by the add insight tool.**
## Output Requirements
1. Generate **report.html** file
2. Print the file path: print(report_path)
3. Make sure the HTML includes Bootstrap for responsive design
""",
},
},
"required": ["code", "code_type"],
}
@@ -0,0 +1,35 @@
from app.tool.chart_visualization.python_execute import NormalPythonExecute
from typing import ClassVar
from pathlib import Path
class GenerateInitialReport(NormalPythonExecute):
"""A tool for generating initial data analysis reports based on the report template and user's input data"""
name: str = "generate_initial_report"
description: str = """Generates an initial HTML data analysis report based on the report template and user's input data.
After searhing and reading the report template, you should dynamically adapt the template content according to user input data,
intelligently determine which charts should be included in the report,
and automatically populate the fillable placeholders with corresponding data.
Outputs: 1) HTML report file path"""
parameters: dict = {
"type": "object",
"properties": {
"code": {
"type": "string",
"description": f"""Python code for generating initial HTML data report based on the report template and user's input data.
## Output Requirements
1. Generate initial_report.html file
2. Print the file path: print(report_path)
# Notes:
1. Refer to the searched report templates
2. Complete the applicable placeholders, and leave any unfilled ones as '[placeholder: ...]'.
""",
},
},
"required": ["code", "code_type"],
}
@@ -0,0 +1,48 @@
from app.tool.chart_visualization.python_execute import NormalPythonExecute
class ReportBeautify(NormalPythonExecute):
"""A tool for transforming a basic health report into a professional, visually appealing final version"""
name: str = "report_beautify"
description: str = """
This tool should be called **LAST** in the workflow to perform final beautification of the data report.
It will:
1. Apply advanced styling and layout enhancements
2. Add interactive elements and visual polish
3. Ensure mobile responsiveness
Key beautification features to implement:
- colorful and fancy background and color scheme
- Modern CSS styling with gradients and shadows
- Font Awesome icons for visual cues
- Animated progress bars for metrics
- Card-based layout with hover effects
- Responsive design for all devices
- Scroll-triggered animations
- Professional typography hierarchy
## Output Requirements
1. Generate **beautify_report.html** file
2. Print the file path: print(report_path)
"""
parameters: dict = {
"type": "object",
"properties": {
"code": {
"type": "string",
"description": """
Python code that beautify the report:
1. CSS/JS enhancements for modern styling
2. Structure optimization for better readability
3. Mobile responsiveness adjustments
Example tasks:
- Add Bootstrap 5 + Font Awesome
- Add metric cards with progress bars
- Create responsive tables
""",
},
},
"required": ["code"],
}
@@ -0,0 +1,82 @@
from app.tool.base import BaseTool, ToolResult
from typing import ClassVar
from pydantic import BaseModel, ConfigDict, Field, model_validator
from pathlib import Path
from app.config import config
from app.tool.chart_visualization.python_execute import NormalPythonExecute
class ReportTemplateGeneration(NormalPythonExecute):
"""A tool for generating the report template in users' local file system"""
name: str = "report_template_generation"
description: str = """Generate a customized HTML report template based on user input data and the natural language description of the report from the previous step.
When user requires a report, you need to use the search_html_library tool first, and then use this tool based on your search result.
In this tool, you will need to process two input parameters to generate a customized HTML report.
(1) First Input Parameter: Analyze the user-provided dataset for the report and generate: A natural language suggestion for the HTML report customization (e.g., layout, structure, recommended charts, and visualizations).
(2) Second Input Parameter: Based on the natural language suggestion, produce: Python code that dynamically generates the corresponding HTML report. The code should also save the HTML file to the local filesystem.
Outputs: HTML report template file path"""
parameters: dict = {
"type": "object",
"properties": {
"report_template_description": {
"description": "Natural language suggestion for HTML report structure (layout, sections, chart recommendations). Focus on framework only - no actual content needed.",
"type": "string",
},
"code": {
"type": "string",
"description": """Python code to generate an HTML template with standardized placeholders. Requirements:
1. **Use placeholder format: [placeholder: description]**
2. Generate template structure only - no real content or data
3. Use the components and theme you have searched before.
4. For text sections: Use placeholders for paragraphs/lists
5. For charts: Use iframe elements with placeholder paths (/workspace/visualization/[filename].html)
6. Maintain semantic HTML structure with appropriate classes
## Output Requirements
1. Generate **report_template.html** file
2. Print the file path: print(report_path)
Examples:
<div class="section">
<h2>Executive Summary</h2>
<div class="card">
<p>[placeholder: 1-3 paragraph summary]</p>
<div class="highlight">
<strong>Key Findings:</strong>
<ul>
<li>[placeholder: Key insight 1]</li>
<li>[placeholder: Key insight 2]</li>
</ul>
</div>
</div>
</div>
<div class="section">
<h2>[placeholder: Section name]</h2>
<div class="card">
<div class="card-header">[placeholder: Chart title]</div>
<div class="card-body">
<div class="chart-container">
<iframe src="[placeholder: /workspace/visualization/chart_name.html]"
width="100%" height="100%" frameborder="0"></iframe>
</div>
<div class="mt-3">
<h4>Key Insights:</h4>
<ul>
<li>[placeholder: Chart insight 1]</li>
<li>[placeholder: Chart insight 2]</li>
</ul>
</div>
</div>
</div>
</div>"""
},
},
"required": ["report_template_description", "code"],
}
async def execute(self, code: str, report_template_description: str | None = None, timeout=5):
return await super().execute(code, timeout)
@@ -0,0 +1,142 @@
from app.tool.base import BaseTool, ToolResult
from typing import ClassVar, Dict
from pydantic import BaseModel, ConfigDict, Field, model_validator
from pathlib import Path
from app.config import config
from app.tool.file_operators import (
FileOperator,
LocalFileOperator,
PathLike,
SandboxFileOperator,
)
from typing import List
class SearchHtmlLibraryResponse(ToolResult):
"""Structured response from the SearchHtmlLibrary tool, inheriting ToolResult."""
report_bootstrap_theme: str = Field(description="The theme of the bootstrap report")
components_content: Dict[str, str] = Field(description="The UI components content as a dictionary (component_name: html_content)")
def __str__(self) -> str:
"""Formatted string with indented HTML content"""
components_info = []
for name, content in self.components_content.items():
components_info.append(
f"{name}\n"
f"{content.strip()}\n"
f"{'-'*40}"
)
return (
f"📊 Report Theme: {self.report_bootstrap_theme}\n\n"
f"🛠️ Components Content:\n\n"
f"{'\n'.join(components_info)}"
)
class SearchHtmlLibrary(BaseTool):
"""A tool for searching the html library in users' local file system"""
name: str = "search_html_library"
description: str = """Check the type of user's input data, and select proper bootstrap theme and component from user's local file system.
When user requires a report, you need to use this tool first, to search the corresponding ui components and serve as a reference for subsequent report generation.
Then, use other tools to generate fancy report template, specific chart...
Outputs: 1) HTML components, 2) Bootstrap theme """
parameters: dict = {
"type": "object",
"properties": {
"report_bootstrap_theme":{
"description": "The theme of bootstrap template to use.",
"enum": [
# Light themes
"Brite"
"Cerulean",
"Materia",
"Cosmo",
"Flatly",
"Journal",
"Litera",
"Lumen",
"Minty",
"Pulse",
"Sandstone",
"Simplex",
"Sketchy",
"Spacelab",
"United",
"Zephyr",
# Dark themes
"Cyborg",
"Darkly",
"Slate",
"Solar",
"Superhero",
"Vapor",
"Lux",
# Special styles
"Quartz",
"Morph",
"Yeti"
],
"default": "Materia",
"type": "string",
},
"components": {
"description": "List of components to you will use in the report, you need to decide based on user's input data.",
"type": "array",
"items": {
"type": "string",
"enum": ["blockquote", "card", "chart", "indicator", "list", "nav", "nvabar", "progress", "table", "typography"]
},
"default": ["card", "chart", "table"],
"minItems": 2,
"uniqueItems": True
}
},
"required": ["report_bootstrap_theme", "components"],
}
_local_operator: LocalFileOperator = LocalFileOperator()
_sandbox_operator: SandboxFileOperator = SandboxFileOperator()
# def _get_operator(self, use_sandbox: bool) -> FileOperator:
def _get_operator(self) -> FileOperator:
"""Get the appropriate file operator based on execution mode."""
return (
self._sandbox_operator
if config.sandbox.use_sandbox
else self._local_operator
)
async def execute(
self,
report_bootstrap_theme: str,
components: List[str]
) -> SearchHtmlLibraryResponse:
"""
Execute the tool with the given parameters.
Reads HTML component files and returns their content in a dictionary.
"""
operator = self._get_operator()
components_content = {} # Initialize an empty dictionary to store component contents
for component in components:
path = f"/home/vm3/JoyZhao/OSPP/OpenManus/workspace/html_library/{component}.html"
print(f"Reading component: {path}")
try:
# Read the HTML file content
component_content = await operator.read_file(path)
# Store in dictionary with component name as key
components_content[component] = component_content
except Exception as e:
print(f"Failed to read component {component}: {str(e)}")
components_content[component] = f"Error loading {component} component"
theme=f"https://cdn.jsdelivr.net/npm/bootswatch@5/dist/{report_bootstrap_theme.lower()}/bootstrap.min.css"
return SearchHtmlLibraryResponse(
report_bootstrap_theme=theme,
components_content=components_content
)
+6
View File
@@ -0,0 +1,6 @@
{
"name": "OpenManus",
"lockfileVersion": 3,
"requires": true,
"packages": {}
}
-1
View File
@@ -1 +0,0 @@
This is a sample file. Files generated by OpenManus are stored in the current folder by default.