396 lines
12 KiB
Python
396 lines
12 KiB
Python
import os
|
||
import sys
|
||
import json
|
||
import requests
|
||
from datetime import datetime
|
||
from dotenv import load_dotenv
|
||
import pathlib
|
||
|
||
# Load .env from the same directory as this script
|
||
env_path = pathlib.Path(__file__).parent / ".env"
|
||
load_dotenv(dotenv_path=env_path)
|
||
# =================================================================
|
||
# Configuration
|
||
# =================================================================
|
||
|
||
API_KEY = os.getenv("OPENAI_API_KEY")
|
||
BASE_URL = os.getenv("OPENAI_BASE_URL")
|
||
MODEL = os.getenv("OPENAI_MODEL", "gpt-4o") # Default to gpt-4o if not set
|
||
|
||
if not API_KEY or not BASE_URL:
|
||
print(
|
||
"Error: OPENAI_API_KEY and OPENAI_BASE_URL environment variables must be set."
|
||
)
|
||
sys.exit(1)
|
||
|
||
# =================================================================
|
||
# Prompts (Extracted from 信息图.py)
|
||
# =================================================================
|
||
|
||
SYSTEM_PROMPT_INFOGRAPHIC_ASSISTANT = """
|
||
You are a professional infographic design expert who can analyze user-provided text content and convert it into AntV Infographic syntax format.
|
||
|
||
## Infographic Syntax Specification
|
||
|
||
Infographic syntax is a Mermaid-like declarative syntax for describing infographic templates, data, and themes.
|
||
|
||
### Syntax Rules
|
||
- Entry uses `infographic <template-name>`
|
||
- Key-value pairs are separated by spaces, **absolutely NO colons allowed**
|
||
- Use two spaces for indentation
|
||
- Object arrays use `-` with line breaks
|
||
|
||
⚠️ **IMPORTANT WARNING: This is NOT YAML format!**
|
||
- ❌ Wrong: `children:` `items:` `data:` (with colons)
|
||
- ✅ Correct: `children` `items` `data` (without colons)
|
||
|
||
### Template Library & Selection Guide
|
||
|
||
#### 1. List & Hierarchy (Text-heavy)
|
||
- **Linear & Short (Steps/Phases)** -> `list-row-horizontal-icon-arrow`
|
||
- **Linear & Long (Rankings/Details)** -> `list-vertical`
|
||
- **Grouped / Parallel (Features/Catalog)** -> `list-grid`
|
||
- **Hierarchical (Org Chart/Taxonomy)** -> `tree-vertical` or `tree-horizontal`
|
||
- **Central Idea (Brainstorming)** -> `mindmap`
|
||
|
||
#### 2. Sequence & Relationship (Flow-based)
|
||
- **Time-based (History/Plan)** -> `sequence-roadmap-vertical-simple`
|
||
- **Process Flow (Complex)** -> `sequence-zigzag` or `sequence-horizontal`
|
||
- **Resource Flow / Distribution** -> `relation-sankey`
|
||
- **Circular Relationship** -> `relation-circle`
|
||
|
||
#### 3. Comparison & Analysis
|
||
- **Binary Comparison (A vs B)** -> `compare-binary`
|
||
- **SWOT Analysis** -> `compare-swot`
|
||
- **Multi-item Comparison Table** -> `compare-table`
|
||
- **Quadrant Analysis (Importance vs Urgency)** -> `quadrant-quarter`
|
||
|
||
#### 4. Charts & Data (Metric-heavy)
|
||
- **Key Metrics / Data Cards** -> `statistic-card`
|
||
- **Distribution / Comparison** -> `chart-bar` or `chart-column`
|
||
- **Trend over Time** -> `chart-line` or `chart-area`
|
||
- **Proportion / Part-to-Whole** -> `chart-pie` or `chart-doughnut`
|
||
|
||
### Infographic Syntax Guide
|
||
|
||
#### 1. Structure
|
||
- **Entry**: `infographic <template-name>`
|
||
- **Blocks**: `data`, `theme`, `design` (optional)
|
||
- **Format**: Key-value pairs separated by spaces, 2-space indentation.
|
||
- **Arrays**: Object arrays use `-` (newline), simple arrays use inline values.
|
||
|
||
#### 2. Data Block (`data`)
|
||
- `title`: Main title
|
||
- `desc`: Subtitle or description
|
||
- `items`: List of data items
|
||
- - `label`: Item title
|
||
- - `value`: Numerical value (required for Charts/Stats)
|
||
- - `desc`: Item description (optional)
|
||
- - `icon`: Icon name (e.g., `mdi/rocket-launch`)
|
||
- - `time`: Time label (Optional, for Roadmap/Sequence)
|
||
- - `children`: Nested items (ONLY for Tree/Mindmap/Sankey/SWOT)
|
||
- - `illus`: Illustration name (ONLY for Quadrant)
|
||
|
||
#### 3. Theme Block (`theme`)
|
||
- `colorPrimary`: Main color (Hex)
|
||
- `colorBg`: Background color (Hex)
|
||
- `palette`: Color list (Space separated)
|
||
- `textColor`: Text color (Hex)
|
||
- `stylize`: Style effect (e.g., `rough`, `flat`)
|
||
|
||
### Examples
|
||
|
||
#### Chart (Bar Chart)
|
||
infographic chart-bar
|
||
data
|
||
title Revenue Growth
|
||
desc Monthly revenue in 2024
|
||
items
|
||
- label Jan
|
||
value 1200
|
||
- label Feb
|
||
value 1500
|
||
- label Mar
|
||
value 1800
|
||
|
||
#### Comparison (SWOT)
|
||
infographic compare-swot
|
||
data
|
||
title Project SWOT
|
||
items
|
||
- label Strengths
|
||
children
|
||
- label Strong team
|
||
- label Innovative tech
|
||
- label Weaknesses
|
||
children
|
||
- label Limited budget
|
||
- label Opportunities
|
||
children
|
||
- label Emerging market
|
||
- label Threats
|
||
children
|
||
- label High competition
|
||
|
||
#### Relationship (Sankey)
|
||
infographic relation-sankey
|
||
data
|
||
title Energy Flow
|
||
items
|
||
- label Solar
|
||
value 100
|
||
children
|
||
- label Grid
|
||
value 60
|
||
- label Battery
|
||
value 40
|
||
- label Wind
|
||
value 80
|
||
children
|
||
- label Grid
|
||
value 80
|
||
|
||
#### Quadrant (Importance vs Urgency)
|
||
infographic quadrant-quarter
|
||
data
|
||
title Task Management
|
||
items
|
||
- label Critical Bug
|
||
desc Fix immediately
|
||
illus mdi/bug
|
||
- label Feature Request
|
||
desc Plan for next sprint
|
||
illus mdi/star
|
||
|
||
### Output Rules
|
||
1. **Strict Syntax**: Follow the indentation and formatting rules exactly.
|
||
2. **No Explanations**: Output ONLY the syntax code block.
|
||
3. **Language**: Use the user's requested language for content.
|
||
"""
|
||
|
||
USER_PROMPT_GENERATE_INFOGRAPHIC = """
|
||
请分析以下文本内容,将其核心信息转换为 AntV Infographic 语法格式。
|
||
|
||
---
|
||
**用户上下文信息:**
|
||
用户姓名: {user_name}
|
||
当前日期时间: {current_date_time_str}
|
||
用户语言: {user_language}
|
||
---
|
||
|
||
**文本内容:**
|
||
{long_text_content}
|
||
|
||
请根据文本特点选择最合适的信息图模板,并输出规范的 infographic 语法。注意保持正确的缩进格式(两个空格)。
|
||
"""
|
||
|
||
# =================================================================
|
||
# Test Cases
|
||
# =================================================================
|
||
|
||
TEST_CASES = [
|
||
{
|
||
"name": "List Grid (Features)",
|
||
"content": """
|
||
MiniMax 2025 模型矩阵全解析:
|
||
1. 极致 MoE 架构优化:国内首批 MoE 路线坚定者。
|
||
2. Video-01 视频生成:杀手锏级多模态能力。
|
||
3. 情感陪伴与角色扮演:源自星野基因。
|
||
4. 超长上下文与精准召回:支持 128k+ 窗口。
|
||
""",
|
||
},
|
||
{
|
||
"name": "Tree Vertical (Hierarchy)",
|
||
"content": """
|
||
公司组织架构:
|
||
- 研发部
|
||
- 后端组
|
||
- 前端组
|
||
- 市场部
|
||
- 销售组
|
||
- 推广组
|
||
""",
|
||
},
|
||
{
|
||
"name": "Statistic Card (Metrics)",
|
||
"content": """
|
||
2024年Q4 核心指标:
|
||
- 总用户数:1,234,567
|
||
- 日活跃用户:89%
|
||
- 营收增长:+45%
|
||
""",
|
||
},
|
||
{
|
||
"name": "Mindmap (Brainstorming)",
|
||
"content": """
|
||
人工智能的应用领域:
|
||
- 生成式 AI:文本生成、图像生成、视频生成
|
||
- 预测性 AI:股市预测、天气预报
|
||
- 决策 AI:自动驾驶、游戏博弈
|
||
""",
|
||
},
|
||
{
|
||
"name": "SWOT Analysis",
|
||
"content": """
|
||
某初创咖啡品牌的 SWOT 分析:
|
||
优势:产品口味独特,选址精准。
|
||
劣势:品牌知名度低,资金压力大。
|
||
机会:线上外卖市场增长,年轻人对精品咖啡需求增加。
|
||
威胁:行业巨头价格战,原材料成本上涨。
|
||
""",
|
||
},
|
||
{
|
||
"name": "Sankey (Relationship)",
|
||
"content": """
|
||
家庭月度开支流向:
|
||
总收入 10000 元。
|
||
其中 4000 元用于房贷。
|
||
3000 元用于日常消费(包括 2000 元餐饮,1000 元交通)。
|
||
2000 元用于教育培训。
|
||
1000 元存入银行。
|
||
""",
|
||
},
|
||
{
|
||
"name": "Quadrant (Analysis)",
|
||
"content": """
|
||
个人任务象限分析:
|
||
- 紧急且重要:修复线上紧急 Bug,准备下午的客户会议。
|
||
- 重要不紧急:制定年度学习计划,健身锻炼。
|
||
- 紧急不重要:回复琐碎的邮件,接听推销电话。
|
||
- 不紧急不重要:刷社交媒体,看无聊的综艺。
|
||
""",
|
||
},
|
||
{
|
||
"name": "Chart Bar (Data)",
|
||
"content": """
|
||
2023年各季度营收情况(亿元):
|
||
第一季度:12.5
|
||
第二季度:15.8
|
||
第三季度:14.2
|
||
第四季度:18.9
|
||
""",
|
||
},
|
||
]
|
||
|
||
# =================================================================
|
||
# Helper Functions
|
||
# =================================================================
|
||
|
||
|
||
def generate_infographic(content):
|
||
now = datetime.now()
|
||
current_date_time_str = now.strftime("%Y年%m月%d日 %H:%M:%S")
|
||
|
||
formatted_user_prompt = USER_PROMPT_GENERATE_INFOGRAPHIC.format(
|
||
user_name="TestUser",
|
||
current_date_time_str=current_date_time_str,
|
||
user_language="zh-CN",
|
||
long_text_content=content,
|
||
)
|
||
|
||
headers = {"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"}
|
||
|
||
payload = {
|
||
"model": MODEL,
|
||
"messages": [
|
||
{"role": "system", "content": SYSTEM_PROMPT_INFOGRAPHIC_ASSISTANT},
|
||
{"role": "user", "content": formatted_user_prompt},
|
||
],
|
||
"stream": False,
|
||
}
|
||
|
||
try:
|
||
response = requests.post(
|
||
f"{BASE_URL}/chat/completions", headers=headers, json=payload
|
||
)
|
||
response.raise_for_status()
|
||
return response.json()["choices"][0]["message"]["content"]
|
||
except Exception as e:
|
||
print(f"API Request Failed: {e}")
|
||
return None
|
||
|
||
|
||
def validate_syntax(syntax):
|
||
if not syntax:
|
||
return False, "Empty output"
|
||
|
||
# Basic checks
|
||
if "infographic" not in syntax.lower():
|
||
return False, "Missing 'infographic' keyword"
|
||
|
||
if "data" not in syntax.lower() and "items" not in syntax.lower():
|
||
return False, "Missing 'data' or 'items' block"
|
||
|
||
# Check for colons in keys (simple heuristic)
|
||
lines = syntax.split("\n")
|
||
for line in lines:
|
||
stripped = line.strip()
|
||
if not stripped:
|
||
continue
|
||
# Ignore lines that are likely values or descriptions containing colons
|
||
first_word = stripped.split()[0]
|
||
if first_word in ["title", "desc", "label", "value", "time", "icon"]:
|
||
continue # These can have colons in value
|
||
|
||
# Check for key: pattern at start of line
|
||
if re.match(r"^\w+:", stripped):
|
||
return False, f"Found colon in key: {stripped}"
|
||
|
||
return True, "Syntax looks valid"
|
||
|
||
|
||
# =================================================================
|
||
# Main Execution
|
||
# =================================================================
|
||
|
||
import re
|
||
|
||
|
||
def main():
|
||
print(f"Starting Infographic Generation Verification...")
|
||
print(f"API: {BASE_URL}")
|
||
print(f"Model: {MODEL}")
|
||
print("-" * 50)
|
||
|
||
results = []
|
||
|
||
for case in TEST_CASES:
|
||
print(f"\nTesting Case: {case['name']}")
|
||
print("Generating...")
|
||
|
||
output = generate_infographic(case["content"])
|
||
|
||
if output:
|
||
# Clean output (remove markdown code blocks if present)
|
||
clean_output = output
|
||
if "```" in output:
|
||
match = re.search(
|
||
r"```(?:infographic|mermaid)?\s*(.*?)\s*```", output, re.DOTALL
|
||
)
|
||
if match:
|
||
clean_output = match.group(1).strip()
|
||
|
||
print(f"Output Preview:\n{clean_output[:200]}...")
|
||
|
||
is_valid, message = validate_syntax(clean_output)
|
||
if is_valid:
|
||
print(f"✅ Validation Passed: {message}")
|
||
results.append({"name": case["name"], "status": "PASS"})
|
||
else:
|
||
print(f"❌ Validation Failed: {message}")
|
||
print(f"Full Output:\n{output}")
|
||
results.append({"name": case["name"], "status": "FAIL"})
|
||
else:
|
||
print("❌ Generation Failed")
|
||
results.append({"name": case["name"], "status": "ERROR"})
|
||
|
||
print("\n" + "=" * 50)
|
||
print("Summary")
|
||
print("=" * 50)
|
||
for res in results:
|
||
print(f"{res['name']}: {res['status']}")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|