- strawberry-graphql[asgi] dependency added - GeneratedReport and LessonPlanStatus @key types with resolve_reference - RouterAuthMiddleware validates Router-Authorization header on /graphql - GraphQL endpoint mounted at /graphql in FastAPI app - WorkflowStateStore injected for lesson plan status resolution
109 lines
3.3 KiB
Python
109 lines
3.3 KiB
Python
"""配置管理(pydantic-settings,12-factor 合规)."""
|
||
|
||
from pydantic_settings import BaseSettings
|
||
|
||
|
||
class Settings(BaseSettings):
|
||
"""应用配置.
|
||
|
||
配置优先级:环境变量 > .env 文件 > 默认值.
|
||
"""
|
||
|
||
# 服务
|
||
service_name: str = "ai"
|
||
http_port: int = 3008
|
||
grpc_port: int = 50058
|
||
dev_mode: bool = False
|
||
|
||
# 可观测性
|
||
otel_endpoint: str = "http://localhost:4318"
|
||
log_level: str = "info"
|
||
|
||
# LLM Provider 配置
|
||
openai_api_key: str = ""
|
||
openai_base_url: str = "https://api.openai.com/v1"
|
||
anthropic_api_key: str = ""
|
||
anthropic_base_url: str = "https://api.anthropic.com"
|
||
baichuan_api_key: str = ""
|
||
baichuan_base_url: str = "https://api.baichuan-ai.com/v1"
|
||
ollama_base_url: str = "" # 本地 Ollama,如 http://localhost:11434
|
||
|
||
# Provider 优先级(按顺序 failover)
|
||
llm_provider_priority: str = "openai,anthropic,baichuan,local_ollama"
|
||
|
||
# LLM 调用参数
|
||
llm_timeout_seconds: float = 30.0
|
||
llm_stream_connect_timeout: float = 30.0
|
||
llm_stream_read_timeout: float = 60.0
|
||
llm_max_retries: int = 3
|
||
|
||
# 默认模型
|
||
default_chat_model: str = "gpt-4o-mini"
|
||
default_question_model: str = "gpt-4o-mini"
|
||
|
||
# Redis(限流 + 缓存 + 工作流状态)
|
||
redis_url: str = "redis://localhost:6379/0"
|
||
redis_rate_limit_user_per_min: int = 10
|
||
redis_rate_limit_ip_per_min: int = 30
|
||
redis_rate_limit_school_per_min: int = 100
|
||
|
||
# Kafka(用量事件发布,派生数据豁免 Outbox)
|
||
kafka_bootstrap_servers: str = "localhost:9092"
|
||
kafka_ai_usage_topic: str = "edu.ai.usage"
|
||
kafka_producer_transactional_id: str = "ai-service-producer"
|
||
|
||
# 下游 gRPC
|
||
content_grpc_endpoint: str = "localhost:50054"
|
||
data_ana_grpc_endpoint: str = "localhost:50055"
|
||
iam_grpc_endpoint: str = "localhost:50052"
|
||
|
||
# GraphQL Federation 2 子图(v2.1 ADR-036 Router-Authorization 信任凭证)
|
||
router_auth_secret: str = ""
|
||
|
||
# 备课工作流
|
||
workflow_ttl_seconds: int = 86400 # 24h
|
||
workflow_max_retries: int = 3
|
||
|
||
# 评估
|
||
evaluation_pass_threshold: float = 0.7
|
||
evaluation_excellent_threshold: float = 0.85
|
||
|
||
# 配额(月度 token 预算)
|
||
default_school_monthly_budget: int = 1_000_000
|
||
default_teacher_monthly_budget: int = 100_000
|
||
|
||
model_config = {"env_file": ".env", "env_prefix": "", "extra": "ignore"}
|
||
|
||
@property
|
||
def is_dev(self) -> bool:
|
||
"""是否处于开发模式."""
|
||
return self.dev_mode
|
||
|
||
@property
|
||
def llm_available(self) -> bool:
|
||
"""LLM 是否可用(至少一个 provider 配置了 API key)."""
|
||
return bool(
|
||
self.openai_api_key
|
||
or self.anthropic_api_key
|
||
or self.baichuan_api_key
|
||
or self.ollama_base_url,
|
||
)
|
||
|
||
@property
|
||
def provider_priority_list(self) -> list[str]:
|
||
"""Provider 优先级列表."""
|
||
return [p.strip() for p in self.llm_provider_priority.split(",") if p.strip()]
|
||
|
||
@property
|
||
def providers_status(self) -> dict[str, bool]:
|
||
"""各 Provider 配置状态."""
|
||
return {
|
||
"openai": bool(self.openai_api_key),
|
||
"anthropic": bool(self.anthropic_api_key),
|
||
"baichuan": bool(self.baichuan_api_key),
|
||
"local_ollama": bool(self.ollama_base_url),
|
||
}
|
||
|
||
|
||
settings = Settings()
|