config.py 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. """配置加载:从 .env 文件或环境变量读取,风格对齐 ContentFindAgentNew。
  2. 只读环境变量,不硬编码密钥。PG 是 M1 唯一必填项;其余环节配置随里程碑推进逐步使用。
  3. """
  4. from __future__ import annotations
  5. import os
  6. from dataclasses import dataclass
  7. from pathlib import Path
  8. # 我们自己的过程库 schema(与 OPEN_AIGC_PG_SCHEMA=public 区分开)
  9. DEFAULT_PG_SCHEMA = "creation_knowledge"
  10. def load_env_file(env_path: str | Path = ".env") -> dict[str, str]:
  11. """解析 .env 为 dict;文件不存在则返回空。与 crawapi_http._load_env_file 同款。"""
  12. path = Path(env_path)
  13. if not path.exists():
  14. return {}
  15. env: dict[str, str] = {}
  16. for line in path.read_text(encoding="utf-8").splitlines():
  17. stripped = line.strip()
  18. if not stripped or stripped.startswith("#") or "=" not in stripped:
  19. continue
  20. key, value = stripped.split("=", 1)
  21. env[key.strip()] = value.strip().strip('"').strip("'")
  22. return env
  23. def env_value(
  24. key: str,
  25. file_env: dict[str, str],
  26. default: str | None = None,
  27. required: bool = False,
  28. ) -> str:
  29. """取值优先级:os.environ > .env 文件 > default。required 缺失时报错。"""
  30. value = os.getenv(key) or file_env.get(key) or default
  31. if required and not value:
  32. raise RuntimeError(f"missing required env: {key}")
  33. return value or ""
  34. @dataclass
  35. class PgConfig:
  36. """过程库(Greenplum open_aigc,schema creation_knowledge)连接配置。"""
  37. host: str
  38. port: int
  39. user: str
  40. password: str
  41. database: str
  42. schema: str = DEFAULT_PG_SCHEMA
  43. timeout: int = 10
  44. @classmethod
  45. def from_env(cls, env_file: str | Path = ".env") -> "PgConfig":
  46. file_env = load_env_file(env_file)
  47. return cls(
  48. host=env_value("OPEN_AIGC_PG_HOST", file_env, required=True),
  49. port=int(env_value("OPEN_AIGC_PG_PORT", file_env, "5432")),
  50. user=env_value("OPEN_AIGC_PG_USER", file_env, required=True),
  51. password=env_value("OPEN_AIGC_PG_PASSWORD", file_env, required=True),
  52. database=env_value(
  53. "OPEN_AIGC_PG_DB_NAME", file_env, "open_aigc"
  54. ),
  55. schema=env_value("CK_PG_SCHEMA", file_env, DEFAULT_PG_SCHEMA),
  56. )
  57. @dataclass
  58. class Settings:
  59. """全流程配置聚合。M1 只用到 pg;其余字段供 M2+ 使用。"""
  60. pg: PgConfig
  61. # 爬虫(M2)
  62. crawler_base_url: str
  63. crawler_key: str
  64. crawler_timeout: int
  65. # 多模态提取(M3)
  66. video_model: str
  67. gemini_api_key: str
  68. openrouter_base_url: str
  69. openrouter_api_key: str
  70. # 判断/拆分/解构 LLM(M4)
  71. llm_model: str
  72. # 入库(M5)
  73. knowhub_api: str
  74. ingest_enabled: bool
  75. # 卡片 / 抽帧
  76. max_cards: int
  77. frames_dir: str
  78. douyin_ratio: str # 视频下载偏好码率(控成本/体积),如 540p
  79. @classmethod
  80. def from_env(cls, env_file: str | Path = ".env") -> "Settings":
  81. file_env = load_env_file(env_file)
  82. return cls(
  83. pg=PgConfig.from_env(env_file),
  84. crawler_base_url=env_value(
  85. "CONTENTFIND_API_CRAWAPI_BASE_URL", file_env,
  86. "http://crawler.aiddit.com",
  87. ),
  88. crawler_key=env_value("CONTENTFIND_API_CRAWAPI_KEY", file_env),
  89. crawler_timeout=int(
  90. env_value("CONTENTFIND_API_CRAWAPI_TIMEOUT_SECONDS", file_env, "30")
  91. ),
  92. video_model=env_value(
  93. "CONTENT_AGENT_VIDEO_LLM_MODEL", file_env,
  94. "google/gemini-3-flash-preview",
  95. ),
  96. gemini_api_key=env_value("GEMINI_API_KEY", file_env),
  97. openrouter_base_url=env_value(
  98. "OPENROUTER_BASE_URL", file_env, "https://openrouter.ai/api/v1"
  99. ),
  100. openrouter_api_key=env_value("OPENROUTER_API_KEY", file_env)
  101. or env_value("OPEN_ROUTER_API_KEY", file_env),
  102. llm_model=env_value("MODEL", file_env, "anthropic/claude-sonnet-4.5"),
  103. knowhub_api=env_value("KNOWHUB_API", file_env, "http://localhost:8000"),
  104. # 开发期默认关闭真实入库;显式置 true 才发送
  105. ingest_enabled=env_value("INGEST_ENABLED", file_env, "false").lower()
  106. in ("1", "true", "yes"),
  107. max_cards=int(env_value("CK_MAX_CARDS", file_env, "12")),
  108. frames_dir=env_value("CK_FRAMES_DIR", file_env, "runtime/frames"),
  109. douyin_ratio=env_value("CK_DOUYIN_RATIO", file_env, "540p"),
  110. )