Files
novelty-seeking/backend/app/models/schemas.py
gbanyan 1ed1dab78f feat: Migrate to React Flow and add Fixed + Dynamic category mode
Frontend:
- Migrate MindmapDAG from D3.js to React Flow (@xyflow/react)
- Add custom node components (QueryNode, CategoryHeaderNode, AttributeNode)
- Add useDAGLayout hook for column-based layout
- Add "AI" badge for LLM-suggested categories
- Update CategorySelector with Fixed + Dynamic mode option
- Improve dark/light theme support

Backend:
- Add FIXED_PLUS_DYNAMIC category mode
- Filter duplicate category names in LLM suggestions
- Update prompts to exclude fixed categories when suggesting new ones
- Improve LLM service with better error handling and logging
- Auto-remove /no_think prefix for non-Qwen models
- Add smart JSON format detection for model compatibility
- Improve JSON extraction with multiple parsing strategies

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-03 01:22:57 +08:00

134 lines
3.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from pydantic import BaseModel
from typing import Optional, List, Dict
from enum import Enum
class AttributeNode(BaseModel):
name: str
category: Optional[str] = None # 材料, 功能, 用途, 使用族群
children: Optional[List["AttributeNode"]] = None
AttributeNode.model_rebuild()
class AnalyzeRequest(BaseModel):
query: str
model: Optional[str] = None
temperature: Optional[float] = 0.7
categories: Optional[List[str]] = None # 如果為 None使用預設類別
class AnalyzeResponse(BaseModel):
query: str
attributes: AttributeNode
class ModelListResponse(BaseModel):
models: List[str]
# ===== Multi-step streaming schemas =====
class Step1Result(BaseModel):
"""Step 1 的結果:各類別屬性列表"""
materials: List[str]
functions: List[str]
usages: List[str]
users: List[str]
class CausalChain(BaseModel):
"""單條因果鏈"""
material: str
function: str
usage: str
user: str
class StreamAnalyzeRequest(BaseModel):
"""多步驟分析請求(更新為支持動態類別)"""
query: str
model: Optional[str] = None
temperature: Optional[float] = 0.7
chain_count: int = 5 # 用戶可設定要生成多少條因果鏈
# 新增:動態類別支持
category_mode: Optional[str] = "dynamic_auto" # CategoryMode enum 值
custom_categories: Optional[List[str]] = None
suggested_category_count: int = 3 # 建議 LLM 生成的類別數量
class StreamAnalyzeResponse(BaseModel):
"""最終完整結果"""
query: str
step1_result: Step1Result
causal_chains: List[CausalChain]
attributes: AttributeNode
# ===== Dynamic category system schemas =====
class CategoryMode(str, Enum):
"""類別模式"""
FIXED_ONLY = "fixed_only"
FIXED_PLUS_CUSTOM = "fixed_plus_custom"
FIXED_PLUS_DYNAMIC = "fixed_plus_dynamic" # Fixed + LLM suggested
CUSTOM_ONLY = "custom_only"
DYNAMIC_AUTO = "dynamic_auto"
class CategoryDefinition(BaseModel):
"""類別定義"""
name: str
description: Optional[str] = None
is_fixed: bool = True # LLM 生成的為 False
order: int = 0
class Step0Result(BaseModel):
"""Step 0: LLM 分析建議類別"""
categories: List[CategoryDefinition]
class DynamicStep1Result(BaseModel):
"""動態版本的 Step 1 結果"""
attributes: Dict[str, List[str]] # {類別名: [屬性列表]}
class DynamicCausalChain(BaseModel):
"""動態版本的因果鏈"""
chain: Dict[str, str] # {類別名: 選中屬性}
# ===== DAG (Directed Acyclic Graph) schemas =====
class DAGNode(BaseModel):
"""DAG 節點 - 每個屬性只出現一次"""
id: str # 唯一 ID: "{category}_{index}"
name: str # 顯示名稱
category: str # 所屬類別
order: int # 欄位內位置
class DAGEdge(BaseModel):
"""DAG 邊 - 節點之間的連接"""
source_id: str
target_id: str
class AttributeDAG(BaseModel):
"""完整 DAG 結構"""
query: str
categories: List[CategoryDefinition]
nodes: List[DAGNode]
edges: List[DAGEdge]
class DAGRelationship(BaseModel):
"""Step 2 輸出 - 單一關係"""
source_category: str
source: str # source attribute name
target_category: str
target: str # target attribute name