Some checks failed
CD Pipeline / deploy (push) Failing after 2m47s
AgentContext/ActionPlan/ActionOutcome/AgentStrategyWeights were defined in both ai_models.py and autoheal_models.py, causing: "Table 'agent_context' is already defined for this MetaData instance" on every scheduler startup. ai_models.py is now a pure re-export shim from autoheal_models.py. autoheal_models.py remains the single source of truth (ADR-013). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
126 lines
4.8 KiB
Python
126 lines
4.8 KiB
Python
# database/ai_models.py
|
||
# ⚠️ 這四個 class 的原始定義已移至 autoheal_models.py(ADR-013 統一管理)。
|
||
# 此檔僅作向後相容 re-export shim,不再重複定義 SQLAlchemy Table,
|
||
# 以避免 "Table already defined for this MetaData instance" 衝突。
|
||
from .autoheal_models import ( # noqa: F401
|
||
AgentContext,
|
||
ActionPlan,
|
||
ActionOutcome,
|
||
AgentStrategyWeights,
|
||
)
|
||
|
||
# AI history and template models
|
||
from sqlalchemy import Column, Integer, String, DateTime, Text, Boolean, Float
|
||
from database.models import Base
|
||
from datetime import datetime
|
||
|
||
class AIGenerationHistory(Base):
|
||
"""
|
||
AI generation history tracking
|
||
"""
|
||
__tablename__ = 'ai_generation_history'
|
||
|
||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||
generation_type = Column(String(50), nullable=False) # product_desc, marketing_copy, etc.
|
||
product_name = Column(String(200))
|
||
input_keywords = Column(Text) # JSON string
|
||
input_trend_topic = Column(String(500))
|
||
output_content = Column(Text)
|
||
generation_duration = Column(Float) # seconds
|
||
is_favorite = Column(Boolean, default=False)
|
||
is_used = Column(Boolean, default=False)
|
||
created_by = Column(String(100))
|
||
created_at = Column(DateTime, default=datetime.now)
|
||
|
||
def to_dict(self):
|
||
return {
|
||
'id': self.id,
|
||
'generation_type': self.generation_type,
|
||
'product_name': self.product_name,
|
||
'input_keywords': self.input_keywords,
|
||
'input_trend_topic': self.input_trend_topic,
|
||
'output_content': self.output_content,
|
||
'generation_duration': self.generation_duration,
|
||
'is_favorite': self.is_favorite,
|
||
'is_used': self.is_used,
|
||
'created_by': self.created_by,
|
||
'created_at': self.created_at.isoformat() if self.created_at else None
|
||
}
|
||
|
||
class AIPromptTemplate(Base):
|
||
"""
|
||
AI prompt templates
|
||
"""
|
||
__tablename__ = 'ai_prompt_templates'
|
||
|
||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||
name = Column(String(200), nullable=False)
|
||
description = Column(Text)
|
||
template_type = Column(String(50), nullable=False) # product_desc, marketing_copy, etc.
|
||
prompt_content = Column(Text, nullable=False)
|
||
variables = Column(Text) # JSON string of variable definitions
|
||
is_active = Column(Boolean, default=True)
|
||
is_system = Column(Boolean, default=False)
|
||
created_by = Column(String(100))
|
||
created_at = Column(DateTime, default=datetime.now)
|
||
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)
|
||
|
||
def to_dict(self):
|
||
return {
|
||
'id': self.id,
|
||
'name': self.name,
|
||
'description': self.description,
|
||
'template_type': self.template_type,
|
||
'prompt_content': self.prompt_content,
|
||
'variables': self.variables,
|
||
'is_active': self.is_active,
|
||
'is_system': self.is_system,
|
||
'created_by': self.created_by,
|
||
'created_at': self.created_at.isoformat() if self.created_at else None,
|
||
'updated_at': self.updated_at.isoformat() if self.updated_at else None
|
||
}
|
||
|
||
class AIUsageTracking(Base):
|
||
"""
|
||
AI usage tracking for analytics and billing
|
||
"""
|
||
__tablename__ = 'ai_usage_tracking'
|
||
|
||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||
user_id = Column(String(100))
|
||
session_id = Column(String(100))
|
||
service_type = Column(String(50), nullable=False) # openai, ollama, etc.
|
||
model_name = Column(String(100))
|
||
prompt_tokens = Column(Integer, default=0)
|
||
completion_tokens = Column(Integer, default=0)
|
||
total_tokens = Column(Integer, default=0)
|
||
cost_usd = Column(Float, default=0.0)
|
||
request_type = Column(String(50)) # generation, analysis, etc.
|
||
status = Column(String(20), default='success') # success, failed
|
||
error_message = Column(Text)
|
||
duration_ms = Column(Float)
|
||
created_at = Column(DateTime, default=datetime.now)
|
||
|
||
def to_dict(self):
|
||
return {
|
||
'id': self.id,
|
||
'user_id': self.user_id,
|
||
'session_id': self.session_id,
|
||
'service_type': self.service_type,
|
||
'model_name': self.model_name,
|
||
'prompt_tokens': self.prompt_tokens,
|
||
'completion_tokens': self.completion_tokens,
|
||
'total_tokens': self.total_tokens,
|
||
'cost_usd': self.cost_usd,
|
||
'request_type': self.request_type,
|
||
'status': self.status,
|
||
'error_message': self.error_message,
|
||
'duration_ms': self.duration_ms,
|
||
'created_at': self.created_at.isoformat() if self.created_at else None
|
||
}
|
||
|
||
__all__ = [
|
||
"AgentContext", "ActionPlan", "ActionOutcome", "AgentStrategyWeights",
|
||
"AIGenerationHistory", "AIPromptTemplate", "AIUsageTracking"
|
||
]
|