Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions docs/design.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,20 @@
- `DeliveryProvider`:渲染并发送结果;
- `SQLiteStateStore`:保存文章、简报、预警和运行状态。

包职责如下:

- `config` 在环境变量和私密文件边界完成解析与校验;
- `geocoding` 包含定位协议、候选匹配、外部服务适配器和缓存解析器;
- `weather` 包含平台无关协议、各天气服务适配器、能力组合和来源文档转换;
- `llm` 只包含模型协议、结构化 schema、any-llm 兼容适配器和结果解析,不依赖天气领域;
- `delivery` 分离平台无关投递协议、渲染器和具体平台适配器;
- `application` 保存历史上下文预算、模型输入构造和输出契约修复等应用策略;
- `composition` 负责根据配置组装外部服务,`cli` 只负责命令分派、运行生命周期和调度;
- `persistence` 把 schema、固定格式序列化和运行时诊断与事务存储分开,业务结果仍由单个 `SQLiteStateStore` 原子提交;
- `data` 保存随程序发布的提示词、端点、分类和本地化资源,读取与领域校验由使用这些资源的功能模块负责。

包的 `__init__` 只导出有意支持的功能接口,测试直接引用行为的所有者模块。

## 配置入口

运行配置来自环境变量和两个私密 JSON 文件:
Expand Down
32 changes: 17 additions & 15 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,8 @@
from weather_briefing.cli import (
_LOGGER,
_SENSITIVE_SDK_LOGGERS,
PUBLISHER_BUILDERS,
WEATHER_PROVIDER_BUILDERS,
_aqicn_provider,
_briefing_delivery_policy,
_briefing_sent_today,
_build_jma,
_build_nea,
_build_open_meteo,
_build_qweather,
_build_weather_provider,
_configure_logging,
_delivery_provider,
_hour_in_cron,
Expand All @@ -38,20 +30,30 @@
_parse_forecast_date,
_parse_run_time,
_precision_reduction_notice,
_qweather_is_configured,
_save_resolved_location_fields,
_weather_context_provider,
_weather_provider_metadata,
build_parser,
daemon,
main,
run,
)
from weather_briefing.composition.providers import (
PUBLISHER_BUILDERS,
WEATHER_PROVIDER_BUILDERS,
_build_jma,
_build_nea,
_build_open_meteo,
_build_qweather,
)
from weather_briefing.composition.providers import aqicn_provider as _aqicn_provider
from weather_briefing.composition.providers import build_weather_provider as _build_weather_provider
from weather_briefing.composition.providers import qweather_is_configured as _qweather_is_configured
from weather_briefing.composition.providers import weather_provider_metadata as _weather_provider_metadata
from weather_briefing.config import ConfigurationError, Settings
from weather_briefing.models import LocationSpec, ResolvedLocation
from weather_briefing.registries import PublisherName, WeatherProviderName
from weather_briefing.state import SQLiteRuntimeDiagnostics, SQLiteStateStore
from weather_briefing.weather_context import QWeatherProvider
from weather_briefing.weather import QWeatherProvider

_REQUIRED_SENSITIVE_SDK_LOGGERS = frozenset({"any_llm", "openai", "httpx", "httpcore"})

Expand Down Expand Up @@ -1101,7 +1103,7 @@ async def test_deepseek_with_custom_base_url(
calls: list[tuple[tuple[object, ...], dict[str, object]]] = []
sdk_client = SimpleNamespace()
monkeypatch.setattr(
"weather_briefing.cli.create_any_llm_provider",
"weather_briefing.llm.any_llm.create_any_llm_provider",
lambda *args, **kwargs: calls.append((args, kwargs)) or sdk_client,
)
Comment thread
qodo-code-review[bot] marked this conversation as resolved.
settings = _make_fake_settings(
Expand All @@ -1125,7 +1127,7 @@ async def test_deepseek_with_custom_base_url(
async def test_deepseek_without_base_url(self, monkeypatch) -> None:
calls: list[tuple[tuple[object, ...], dict[str, object]]] = []
monkeypatch.setattr(
"weather_briefing.cli.create_any_llm_provider",
"weather_briefing.llm.any_llm.create_any_llm_provider",
lambda *args, **kwargs: calls.append((args, kwargs)) or SimpleNamespace(),
)
settings = _make_fake_settings(llm_provider="deepseek", llm_base_url=None)
Expand All @@ -1137,7 +1139,7 @@ async def test_deepseek_without_base_url(self, monkeypatch) -> None:
async def test_arbitrary_any_llm_provider_is_forwarded(self, monkeypatch) -> None:
calls: list[tuple[tuple[object, ...], dict[str, object]]] = []
monkeypatch.setattr(
"weather_briefing.cli.create_any_llm_provider",
"weather_briefing.llm.any_llm.create_any_llm_provider",
lambda *args, **kwargs: calls.append((args, kwargs)) or SimpleNamespace(),
)
settings = replace(_make_fake_settings(llm_provider="mistral"), api_key=None, llm_base_url=None)
Expand Down Expand Up @@ -1325,7 +1327,7 @@ def test_weather_provider_metadata_rejects_unregistered_provider() -> None:

async def test_no_weather_provider_available(monkeypatch, async_client: httpx.AsyncClient) -> None:
monkeypatch.setattr(
"weather_briefing.cli.weather_providers_for",
"weather_briefing.config.environment.weather_providers_for",
lambda *_: ("qweather",),
)
settings = _make_fake_settings(
Expand Down
2 changes: 1 addition & 1 deletion tests/test_geocoding.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import httpx
import pytest

from weather_briefing.data.resources import ReferenceDataError, reference_value
from weather_briefing.geocoding import (
CachedLocationResolver,
FallbackGeocodingProvider,
Expand All @@ -26,7 +27,6 @@
specific_location_name as _specific_location_name,
)
from weather_briefing.models import LocationSpec, ResolvedLocation
from weather_briefing.reference_data import ReferenceDataError, reference_value


class _NeverCalledGeocoder:
Expand Down
Loading