Skip to content

feat: preserve provider output languages - #72

Merged
IceCodeNew merged 1 commit into
masterfrom
codex/pr2-provider-languages
Jul 20, 2026
Merged

feat: preserve provider output languages#72
IceCodeNew merged 1 commit into
masterfrom
codex/pr2-provider-languages

Conversation

@IceCodeNew

@IceCodeNew IceCodeNew commented Jul 20, 2026

Copy link
Copy Markdown
Owner

What changed

Add output-language metadata to weather and capability providers. Providers with selectable API languages expose their supported choices, while fixed-language sources preserve their actual source language through snapshots and history.

The LLM payload now includes source-language metadata so matching output languages do not require unnecessary translation round trips.

Validation

  • Full test suite and coverage passed
  • Ruff, formatting, ty, secret, and workflow checks passed

Stacked on codex/pr1-capabilities.

Summary by CodeRabbit

  • 新功能
    • 支持按天气服务选择或匹配输出语言,并将语言信息贯穿天气、空气质量、过敏原及生活指数内容。
    • 支持保存来源语言,历史上下文和生成结果将保留准确的语言标记。
    • 增强多语言输出规则:仅在最终生成阶段进行一次必要翻译,避免中间语言转换。
  • 改进
    • 优化地区语言标签匹配,例如将 ja-JP 自动匹配至日语。
    • 保留旧数据兼容性,并为缺失语言信息的历史内容使用中文默认值。

@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@IceCodeNew, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 37 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2d695756-83c8-4cba-a953-59f4cc652919

📥 Commits

Reviewing files that changed from the base of the PR and between c94ba6b and 8dea20a.

📒 Files selected for processing (24)
  • README.md
  • docs/design.md
  • docs/notes.md
  • env.example
  • tests/test_air_quality.py
  • tests/test_allergen.py
  • tests/test_config.py
  • tests/test_languages.py
  • tests/test_service.py
  • tests/test_sources.py
  • tests/test_state.py
  • tests/test_weather_context.py
  • weather_briefing/air_quality.py
  • weather_briefing/allergen.py
  • weather_briefing/capabilities.py
  • weather_briefing/cli.py
  • weather_briefing/config.py
  • weather_briefing/languages.py
  • weather_briefing/models.py
  • weather_briefing/prompts.py
  • weather_briefing/service.py
  • weather_briefing/sources.py
  • weather_briefing/state.py
  • weather_briefing/weather_context.py
📝 Walkthrough

Walkthrough

This change adds normalized language metadata to providers, snapshots, source documents, SQLite context history, and LLM payloads. QWeather selects provider wire languages, Open-Meteo declares fixed language support, and prompts constrain final output translation behavior.

Changes

Language-aware weather context

Layer / File(s) Summary
Language contracts and model metadata
weather_briefing/languages.py, weather_briefing/models.py, weather_briefing/capabilities.py
Adds BCP 47-like normalization, LanguageSupport, provider capability metadata, and language fields on snapshots and source documents.
Provider selection and snapshot propagation
weather_briefing/weather_context.py, weather_briefing/air_quality.py, weather_briefing/allergen.py, weather_briefing/cli.py
Maps QWeather output languages to API codes, declares Open-Meteo support, propagates output languages through snapshots and source documents, and wires provider metadata.
Context persistence and briefing payloads
weather_briefing/state.py, weather_briefing/service.py, weather_briefing/prompts.py, docs/*.md
Persists source languages in SQLite, includes them in serialized history and fingerprints, sends language metadata to the LLM, and documents translation rules.
Language and integration validation
tests/test_languages.py, tests/test_weather_context.py, tests/test_state.py, tests/test_service.py
Tests normalization, language selection, provider request codes, snapshot propagation, SQLite persistence, serialization, and deterministic fingerprints.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant QWeatherProvider
  participant QWeatherAPI
  participant SQLiteStateStore
  participant BriefingService
  participant LLM
  Client->>QWeatherProvider: request output language
  QWeatherProvider->>QWeatherAPI: send mapped lang code
  QWeatherAPI-->>QWeatherProvider: return localized context
  QWeatherProvider->>SQLiteStateStore: persist source language
  BriefingService->>SQLiteStateStore: load recent context
  BriefingService->>LLM: send output and source languages
  LLM-->>BriefingService: return final-language briefing
Loading

Possibly related PRs

Suggested labels: 🕐 40+ Minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 56.41% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: preserving and propagating provider output-language metadata through the weather briefing flow.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/pr2-provider-languages

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Jul 20, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.76%. Comparing base (8a0d8d4) to head (8dea20a).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@           Coverage Diff            @@
##           master      #72    +/-   ##
========================================
  Coverage   99.76%   99.76%            
========================================
  Files          41       43     +2     
  Lines        7576     7800   +224     
  Branches      423      438    +15     
========================================
+ Hits         7558     7782   +224     
  Misses         13       13            
  Partials        5        5            

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

@IceCodeNew
IceCodeNew requested a review from Copilot July 20, 2026 09:38

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@IceCodeNew
IceCodeNew force-pushed the codex/pr1-capabilities branch from 1075795 to 9a3b918 Compare July 20, 2026 09:59
@IceCodeNew
IceCodeNew force-pushed the codex/pr2-provider-languages branch from e312334 to 0185f2f Compare July 20, 2026 09:59
@IceCodeNew

Copy link
Copy Markdown
Owner Author

/agentic_review

@qodo-code-review

qodo-code-review Bot commented Jul 20, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (1) 📜 Skill insights (0)

Context used
✅ Compliance rules (platform): 37 rules

Grey Divider


Remediation recommended

1. Hardcoded weather label dictionaries 📘 Rule violation ⚙ Maintainability ⭐ New
Description
Multiple modules in weather_briefing embed large, locale-dependent label/format reference tables
directly as Python literals (e.g., _WEATHER_DOCUMENT_LABELS, _QWEATHER_FORMATS,
_AIR_QUALITY_FORMATS, _ALLERGEN_FORMATS) instead of externalized, validated reference data
files. This increases maintenance overhead and risk of label/schema drift shipping without
centralized validation for these evolving tables.
Code

weather_briefing/weather_context.py[R45-125]

+_WEATHER_DOCUMENT_LABELS = {
+    "zh-CN": {
+        "separator": ":",
+        "section_separator": ":",
+        "unavailable": "不可用",
+        "updated_at": "更新时间",
+        "forecast": "天气预报",
+        "lifestyle": "生活与出行指数",
+        "summary": "天气概览",
+        "lifestyle_count": "生活与出行指数项数",
+    },
+    "zh-TW": {
+        "separator": ":",
+        "section_separator": ":",
+        "unavailable": "無法取得",
+        "updated_at": "更新時間",
+        "forecast": "天氣預報",
+        "lifestyle": "生活與出行指數",
+        "summary": "天氣概覽",
+        "lifestyle_count": "生活與出行指數項數",
+    },
+    "en": {
+        "separator": ": ",
+        "section_separator": ":",
+        "unavailable": "Unavailable",
+        "updated_at": "Updated at",
+        "forecast": "Weather forecast",
+        "lifestyle": "Lifestyle and travel indices",
+        "summary": "Weather summary",
+        "lifestyle_count": "Lifestyle and travel index count",
+    },
+    "ja": {
+        "separator": ":",
+        "section_separator": ":",
+        "unavailable": "利用不可",
+        "updated_at": "更新時刻",
+        "forecast": "天気予報",
+        "lifestyle": "生活・外出指数",
+        "summary": "天気概要",
+        "lifestyle_count": "生活・外出指数の件数",
+    },
+}
+
+_QWEATHER_FORMATS = {
+    "zh-CN": {
+        "day": (
+            "{date}:{day}转{night},{minimum}~{maximum}℃,{wind}{scale}级,"
+            "相对湿度{humidity}%,预计降水量{precipitation}毫米"
+        ),
+        "lifestyle": "{name}({category}):{text}",
+        "unknown": "未知",
+        "no_details": "无详细建议",
+    },
+    "zh-TW": {
+        "day": (
+            "{date}:{day}轉{night},{minimum}~{maximum}℃,{wind}{scale}級,"
+            "相對濕度{humidity}%,預計降水量{precipitation}毫米"
+        ),
+        "lifestyle": "{name}({category}):{text}",
+        "unknown": "未知",
+        "no_details": "無詳細建議",
+    },
+    "en": {
+        "day": (
+            "{date}: {day} to {night}; {minimum}-{maximum} °C; {wind}, force {scale}; "
+            "relative humidity {humidity}%; forecast precipitation {precipitation} mm"
+        ),
+        "lifestyle": "{name} ({category}): {text}",
+        "unknown": "Unknown",
+        "no_details": "No detailed advice",
+    },
+    "ja": {
+        "day": (
+            "{date}:{day}から{night}、{minimum}~{maximum}℃、{wind}{scale}級、"
+            "相対湿度{humidity}%、予想降水量{precipitation}mm"
+        ),
+        "lifestyle": "{name}({category}):{text}",
+        "unknown": "不明",
+        "no_details": "詳しいアドバイスはありません",
+    },
+}
Relevance

⭐⭐ Medium

No historical evidence found requiring i18n label tables be externalized; repo uses both JSON
reference_data and in-code labels.

PR-#23
PR-#72
PR-#59

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 2141692 requires large, changeable domain reference data to be externalized into
validated data files rather than hard-coded as Python constants. The cited additions show
multi-locale/multi-language reference tables—_WEATHER_DOCUMENT_LABELS and _QWEATHER_FORMATS in
weather_context.py, _AIR_QUALITY_FORMATS in air_quality.py, and _ALLERGEN_FORMATS in
allergen.py—all defined directly in application code, which demonstrates the noncompliant pattern
of embedding sizable reference data instead of loading it through a validated data-file mechanism.

Rule 2141692: Externalize domain reference data into validated data files
weather_briefing/weather_context.py[45-125]
weather_briefing/air_quality.py[17-78]
weather_briefing/allergen.py[12-53]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The PR introduces sizable localized label/format dictionaries as Python constants across multiple `weather_briefing` modules (weather context, air quality, allergen). Compliance (PR Compliance ID 2141692) expects this kind of large, mutable domain reference data to be externalized into dedicated validated data files and loaded via a validated loader to reduce maintenance risk and prevent label/schema drift.

## Issue Context
These dictionaries function as shared reference/i18n scaffolding that is likely to evolve independently of application logic; keeping them in code makes updates harder to validate and review consistently and encourages duplication across components.

## Fix Focus Areas
- weather_briefing/weather_context.py[45-125]
- weather_briefing/air_quality.py[17-78]
- weather_briefing/allergen.py[12-53]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. BCP47 parser limitation undocumented triggers ✓ Resolved 📘 Rule violation ⚙ Maintainability
Description
docs/notes.md documents an intentionally limited language-tag parser (not full BCP 47) but does
not provide explicit assumptions and concrete reevaluation triggers for revisiting this accepted
limitation. This can cause the limitation to persist without clear guardrails as requirements expand
(e.g., needing extensions/private-use tags).
Code

docs/notes.md[85]

+来源语言是输入边界的事实元数据,不是用户目标语种的别名。`SourceDocument` 构造时规范化基础 BCP 47-like 标签,再写入 SQLite 和 LLM payload,避免同一正文因大小写差异产生伪历史变化;旧数据库的语言列只保留迁移时已知的 `zh-CN` 默认值。`LanguageSupport.select()` 继续用于显式选择 provider 已声明的 wire 语言并严格拒绝未知值,而地点级用户目标语种通过 `match()` 按逐步去除区域/脚本后缀选择最接近的 provider 语言,匹配失败回到 provider 默认值。这样固定只输出 `ja` 的 API 可以服务 `ja-JP` 的用户而不在构造阶段失败;LLM 仍收到实际来源标签,并在同一主语言时避免无意义的往返翻译。当前标签解析刻意只覆盖应用所需的基础形式,不承诺完整 BCP 47 extension/private-use 语法;若需要这些语法,应引入专门的标准库或成熟解析器并重新审视 wire-code 映射。
Relevance

⭐⭐⭐ High

Team previously asked to add explicit assumptions/triggers for accepted limitations in docs/notes.md
(PR54).

PR-#54
PR-#58
PR-#68

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 2141676 requires that intentionally unresolved/accepted design concerns documented
in docs/notes.md include explicit assumptions and at least one concrete reevaluation trigger. The
added paragraph explicitly states the parser is intentionally limited and does not commit to full
BCP 47 syntax, but it does not enumerate assumptions nor concrete triggers for when this limitation
must be revisited.

Rule 2141676: Document assumptions and reevaluation triggers for intentionally unresolved design concerns
docs/notes.md[85-85]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`docs/notes.md` documents an accepted/intentional limitation (language-tag parsing only supports a basic subset) but does not include explicit assumptions and concrete triggers for reevaluation as required.

## Issue Context
The notes state that the current tag parsing intentionally does not cover full BCP 47 `extension/private-use` syntax and suggests introducing a standard library/parser “if needed”, but it does not spell out assumptions (what makes this acceptable today) or measurable triggers (when to revisit).

## Fix Focus Areas
- docs/notes.md[85-85]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Wrong source language tag ✓ Resolved 🐞 Bug ≡ Correctness
Description
snapshot_to_documents(), air_quality_to_document(), and allergen_to_document() tag
SourceDocument.language as snapshot.output_language, but the constructed content/history strings
contain hard-coded Chinese labels (e.g., “更新时间/健康提示/花粉过敏原”), making the persisted/LLM language
metadata incorrect for non-zh outputs. This can cause the LLM to skip translation (or apply it
incorrectly) based on a language tag that does not match the actual document text, violating the
documented “source language is factual metadata” contract.
Code

weather_briefing/weather_context.py[R791-795]

                f"今明天气预报:\n{weather}\n"
                f"生活与出行指数:\n{lifestyle}"
            ),
+            language=snapshot.output_language,
            history_summary=(
Relevance

⭐⭐⭐ High

Team enforces strict LLM/persistence contracts; similar “valid finding” fixes merged (air-quality
time + bounded history contracts).

PR-#59
PR-#68
PR-#58

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The code currently constructs SourceDocument strings with fixed Chinese labels while tagging the
document language as the provider-selected output_language, but the project notes explicitly define
SourceDocument.language as factual source-language metadata used in SQLite/LLM payloads and
translation decisions—so the tag must reflect the actual text language.

docs/notes.md[83-86]
weather_briefing/weather_context.py[777-807]
weather_briefing/weather_context.py[889-910]
weather_briefing/air_quality.py[95-129]
weather_briefing/allergen.py[22-48]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`SourceDocument.language` is treated as factual “source text language” and is persisted to SQLite and sent to the LLM. However, multiple adapters build SourceDocument content using fixed Chinese scaffolding strings while setting `language=` to the provider-selected output language (e.g. `ja`, `en`). This makes the language tag inaccurate (mixed-language content tagged as a single non-Chinese language) and can break the “translate once / don’t translate when languages match” rule.

## Issue Context
- `weather_context.snapshot_to_documents()` builds a document with Chinese headings (“更新时间/今明天气预报/生活与出行指数”) while tagging it as `snapshot.output_language`.
- `air_quality.air_quality_to_document()` and `allergen.allergen_to_document()` similarly embed Chinese labels (“健康提示/观测时间/花粉过敏原/总体等级”) while tagging `language=snapshot.output_language`.
- Design note: source language is *fact metadata* used for SQLite + LLM payload and translation decisions.

## Fix Focus Areas
Choose one:
1) **Localize all scaffolding text** (content + history_summary + history_value + any formatting helpers like `_format_qweather_day`) according to the selected language (`snapshot.output_language`), at least for the supported set (`zh-CN`, `zh-TW`, `en`, `ja`).
2) **Conservatively retag** documents as `zh-CN` (or a dedicated “mixed/unknown” tag if you introduce one) until scaffolding localization exists, so metadata matches reality.

Also add/extend tests to cover non-zh cases (e.g., QWeather `output_language='ja'` or `'en'`) and assert both:
- the document `language` tag
- the document text scaffolding language consistency

### Code locations
- weather_briefing/weather_context.py[777-807]
- weather_briefing/weather_context.py[889-910]
- weather_briefing/air_quality.py[95-129]
- weather_briefing/allergen.py[22-48]
- docs/notes.md[83-86]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (4)
4. Unnormalized language tags ✓ Resolved 🐞 Bug ≡ Correctness
Description
SourceDocument.language is stored in SQLite and used in context change detection/LLM payloads
without normalization or validation, so values like JA,  zh-hans , or other invalid strings can
be persisted and treated as content changes. This can create spurious history entries/fingerprint
changes and send non-BCP47 tags to the LLM despite the new contract expecting normalized tags.
Code

weather_briefing/models.py[R87-90]

    name: str
    url: str
    content: str
+    language: str = "zh-CN"
Relevance

⭐⭐⭐ High

Team repeatedly tightens/validates persisted & LLM-boundary data; e.g., strict input validation and
history semantics hardening in PRs 46/33/68.

PR-#46
PR-#33
PR-#68

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The repo introduces a normalization helper and tests for BCP-47-like tags, but the new
SourceDocument.language field is a free-form string that is written to/loaded from SQLite and
included in both history-change comparisons and the LLM payload without passing through the
normalizer.

weather_briefing/languages.py[11-17]
weather_briefing/models.py[82-94]
weather_briefing/state.py[423-471]
weather_briefing/service.py[675-835]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`SourceDocument.language` is introduced and then persisted/propagated, but it is never normalized/validated at ingestion time. This allows inconsistent casing/invalid tags to (a) trigger false “document changed” signals in context history selection (because language is part of `_context_document_value`) and (b) reach the LLM payload even though the prompt and tests establish an expectation of normalized BCP-47-like tags.

## Issue Context
- `normalize_language_tag()` exists and is tested, but it is not applied to `SourceDocument.language`.
- The state store writes/reads `language` as arbitrary TEXT.
- Service history selection compares documents including `language`.

## Fix Focus Areas
- weather_briefing/models.py[82-94]
- weather_briefing/state.py[423-471]
- weather_briefing/service.py[675-835]
- weather_briefing/languages.py[11-17]

## Suggested fix
1. Enforce the invariant at the data model boundary:
  - Add a `__post_init__` to `SourceDocument` (frozen dataclass) that normalizes `language` via `normalize_language_tag()` and raises on invalid values (or, if you prefer non-fatal behavior, normalizes and falls back to `zh-CN` with a log).
2. Defense-in-depth for persistence:
  - Normalize before writing to SQLite in `save_context_documents()` (or inside `_insert_context_documents`).
  - Normalize on read in `recent_context_documents()` to protect against pre-existing DB rows with unexpected values.
3. Add a small unit test that constructing/persisting a `SourceDocument(language="EN-us")` results in `en-US`, and that invalid tags are rejected (or coerced, depending on your chosen behavior).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. Missing docs/notes.md language decision ✓ Resolved 📘 Rule violation ⌂ Architecture
Description
This PR introduces provider output-language metadata and new translation/persistence semantics
(LanguageSupport, SourceDocument.language, and context_documents[*].language) but does not add
the required rationale/trade-offs/operating boundaries entry to docs/notes.md. Without this,
future changes can easily break the intended “translate once” and language-preservation behavior.
Code

weather_briefing/service.py[R604-609]

        return {
            "mode": kind,
+            "output_language": "zh-CN",
            "now": now.isoformat(),
            "forecast_date": str(forecast_date or now.in_timezone(self._settings.timezone).date()),
            "region": self._location.name,
Relevance

⭐⭐⭐ High

Repo regularly documents non-obvious architecture in docs/notes.md (tradeoffs/boundaries) in recent
PRs; missing entry likely flagged.

PR-#54
PR-#58
PR-#69

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 2141673 requires non-obvious architectural decisions introduced by the change to be
documented in docs/notes.md with rationale/trade-offs/boundaries. The PR adds language metadata
and new payload semantics in code, while docs/notes.md (which states it should capture such
rationale) contains no entry describing this decision.

Rule 2141673: Document non-obvious architectural decisions in docs/notes.md
weather_briefing/service.py[604-628]
weather_briefing/models.py[82-93]
weather_briefing/languages.py[1-59]
docs/notes.md[1-4]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The change introduces a non-obvious architectural decision (preserving provider/source language through snapshots/history and controlling translation behavior via `output_language` + per-document `language`) but `docs/notes.md` has no corresponding entry documenting decision, rationale, trade-offs, and operating boundaries.

## Issue Context
New/modified code adds a provider-neutral `LanguageSupport` abstraction, persists `SourceDocument.language`, and sends `language` metadata to the LLM payload. Compliance requires documenting non-obvious architectural decisions in `docs/notes.md`.

## Fix Focus Areas
- docs/notes.md[1-4]
- weather_briefing/service.py[604-626]
- weather_briefing/models.py[82-93]
- weather_briefing/languages.py[1-59]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


6. Context source language mislabeled ✓ Resolved 🐞 Bug ≡ Correctness
Description
SourceDocument now defaults language="zh-CN", but HTTPContextSource.fetch() constructs
SourceDocument without providing a language, so arbitrary context URLs are persisted/sent to the
LLM as Chinese regardless of the actual document text. This conflicts with the prompt contract that
context_documents[*].language is the factual source language and can cause the LLM to skip
translation when it should translate.
Code

weather_briefing/models.py[R92-100]

+    language: str = "zh-CN"
    has_allergen_information: bool = False
    history_summary: str | None = None
    history_value: str | None = None

+    def __post_init__(self) -> None:
+        """Normalize the language before persistence or LLM serialization."""
+        object.__setattr__(self, "language", normalize_language_tag(self.language))
+
Relevance

⭐⭐ Medium

No direct precedent; boundary/payload correctness is enforced generally, but not this exact
default-language omission.

PR-#33
PR-#46

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The PR makes language a first-class field on SourceDocument and the prompt depends on it being
accurate, but HTTPContextSource still omits the language argument, causing the default zh-CN to
be applied regardless of actual fetched content language.

weather_briefing/models.py[84-100]
weather_briefing/sources.py[168-185]
weather_briefing/prompts.py[15-18]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
With the new language-metadata contract, `SourceDocument.language` is treated as factual. However, HTTP-fetched context documents are created without specifying a language and therefore default to `zh-CN`, which can mislabel non-Chinese sources.

## Issue Context
- `SourceDocument.language` has a default of `"zh-CN"` and is normalized in `__post_init__`.
- `HTTPContextSource.fetch()` creates `SourceDocument(..., content=response.text)` without passing `language`.
- The system prompt explicitly tells the LLM that `context_documents[*].language` is the actual source-document language.

## Fix Focus Areas
- weather_briefing/models.py[84-100]
- weather_briefing/sources.py[168-185]
- weather_briefing/config.py[170-181]

## Suggested fix
- Extend `ContextSourceConfig` to include an optional `language` field (e.g., `language: str | None = None`).
- Update config parsing (`_context_source`) to accept `language` from `CONTEXT_SOURCES_JSON` and validate it with `normalize_language_tag`.
- Update `HTTPContextSource.fetch()` to pass `language=config.language` when provided.
- For missing language, prefer an explicit unknown tag like `"und"` (undefined) for HTTP context sources (instead of silently defaulting to `zh-CN`), so the metadata is not incorrect when the language is unknown.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


7. api_code may return None ✓ Resolved 🐞 Bug ☼ Reliability
Description
LanguageSupport.api_code() returns None when api_codes is missing a mapping, but provider code
(e.g., QWeatherProvider) uses the returned value directly for the lang query parameter. A
misconfigured LanguageSupport instance can therefore emit requests with an invalid/missing
language code or fail at the HTTP boundary.
Code

weather_briefing/languages.py[R64-67]

+    def api_code(self, selected: str | None = None) -> str | None:
+        """Return the provider wire-language code for a selected language."""
+        language = self.select(selected)
+        return dict(self.api_codes).get(language)
Relevance

⭐⭐ Medium

Team likes strict boundary validation, but no precedent on None-returning api_code mapping
completeness.

PR-#46
PR-#33

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
api_code() uses a dict lookup that returns None when a mapping is absent, and QWeatherProvider
uses that value as the lang query parameter without any guard, so a missing mapping can propagate
to the HTTP request boundary.

weather_briefing/languages.py[28-68]
weather_briefing/weather_context.py[193-234]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`LanguageSupport.api_code()` currently returns `Optional[str]` and can return `None` if `api_codes` does not contain an entry for a supported language. Some providers treat this value as required (e.g., pass it as the `lang` query parameter), so a partial/incorrect `api_codes` configuration becomes a latent runtime failure.

## Issue Context
- `LanguageSupport.__post_init__()` validates `default`/`supported` but does not validate `api_codes` keys/coverage.
- `QWeatherProvider` assigns `self._api_language = QWEATHER_LANGUAGE_SUPPORT.api_code(...)` and then uses it in `params={"lang": self._api_language}`.

## Fix Focus Areas
- weather_briefing/languages.py[28-68]
- weather_briefing/weather_context.py[193-234]

## Suggested fix
- In `LanguageSupport.__post_init__`, validate `api_codes`:
 - Normalize and validate each language key (must be in `supported`).
 - Ensure keys are unique after normalization.
 - If `api_codes` is provided for a provider that uses wire codes, require coverage for all `supported` languages (or at least for all selectable languages) and raise `ValueError` when missing.
- Additionally (defense in depth), in `QWeatherProvider.__init__` assert `self._api_language is not None` and raise a clear configuration error if it is (so failures are immediate and explicit).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Previous review results

Review updated until commit 8dea20a ⚖️ Balanced

Results up to commit 0185f2f ⚖️ Balanced


🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)


Remediation recommended
1. Missing docs/notes.md language decision ✓ Resolved 📘 Rule violation ⌂ Architecture
Description
This PR introduces provider output-language metadata and new translation/persistence semantics
(LanguageSupport, SourceDocument.language, and context_documents[*].language) but does not add
the required rationale/trade-offs/operating boundaries entry to docs/notes.md. Without this,
future changes can easily break the intended “translate once” and language-preservation behavior.
Code

weather_briefing/service.py[R604-609]

        return {
            "mode": kind,
+            "output_language": "zh-CN",
            "now": now.isoformat(),
            "forecast_date": str(forecast_date or now.in_timezone(self._settings.timezone).date()),
            "region": self._location.name,
Relevance

⭐⭐⭐ High

Repo regularly documents non-obvious architecture in docs/notes.md (tradeoffs/boundaries) in recent
PRs; missing entry likely flagged.

PR-#54
PR-#58
PR-#69

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 2141673 requires non-obvious architectural decisions introduced by the change to be
documented in docs/notes.md with rationale/trade-offs/boundaries. The PR adds language metadata
and new payload semantics in code, while docs/notes.md (which states it should capture such
rationale) contains no entry describing this decision.

Rule 2141673: Document non-obvious architectural decisions in docs/notes.md
weather_briefing/service.py[604-628]
weather_briefing/models.py[82-93]
weather_briefing/languages.py[1-59]
docs/notes.md[1-4]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The change introduces a non-obvious architectural decision (preserving provider/source language through snapshots/history and controlling translation behavior via `output_language` + per-document `language`) but `docs/notes.md` has no corresponding entry documenting decision, rationale, trade-offs, and operating boundaries.

## Issue Context
New/modified code adds a provider-neutral `LanguageSupport` abstraction, persists `SourceDocument.language`, and sends `language` metadata to the LLM payload. Compliance requires documenting non-obvious architectural decisions in `docs/notes.md`.

## Fix Focus Areas
- docs/notes.md[1-4]
- weather_briefing/service.py[604-626]
- weather_briefing/models.py[82-93]
- weather_briefing/languages.py[1-59]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Unnormalized language tags ✓ Resolved 🐞 Bug ≡ Correctness
Description
SourceDocument.language is stored in SQLite and used in context change detection/LLM payloads
without normalization or validation, so values like JA,  zh-hans , or other invalid strings can
be persisted and treated as content changes. This can create spurious history entries/fingerprint
changes and send non-BCP47 tags to the LLM despite the new contract expecting normalized tags.
Code

weather_briefing/models.py[R87-90]

    name: str
    url: str
    content: str
+    language: str = "zh-CN"
Relevance

⭐⭐⭐ High

Team repeatedly tightens/validates persisted & LLM-boundary data; e.g., strict input validation and
history semantics hardening in PRs 46/33/68.

PR-#46
PR-#33
PR-#68

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The repo introduces a normalization helper and tests for BCP-47-like tags, but the new
SourceDocument.language field is a free-form string that is written to/loaded from SQLite and
included in both history-change comparisons and the LLM payload without passing through the
normalizer.

weather_briefing/languages.py[11-17]
weather_briefing/models.py[82-94]
weather_briefing/state.py[423-471]
weather_briefing/service.py[675-835]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`SourceDocument.language` is introduced and then persisted/propagated, but it is never normalized/validated at ingestion time. This allows inconsistent casing/invalid tags to (a) trigger false “document changed” signals in context history selection (because language is part of `_context_document_value`) and (b) reach the LLM payload even though the prompt and tests establish an expectation of normalized BCP-47-like tags.

## Issue Context
- `normalize_language_tag()` exists and is tested, but it is not applied to `SourceDocument.language`.
- The state store writes/reads `language` as arbitrary TEXT.
- Service history selection compares documents including `language`.

## Fix Focus Areas
- weather_briefing/models.py[82-94]
- weather_briefing/state.py[423-471]
- weather_briefing/service.py[675-835]
- weather_briefing/languages.py[11-17]

## Suggested fix
1. Enforce the invariant at the data model boundary:
  - Add a `__post_init__` to `SourceDocument` (frozen dataclass) that normalizes `language` via `normalize_language_tag()` and raises on invalid values (or, if you prefer non-fatal behavior, normalizes and falls back to `zh-CN` with a log).
2. Defense-in-depth for persistence:
  - Normalize before writing to SQLite in `save_context_documents()` (or inside `_insert_context_documents`).
  - Normalize on read in `recent_context_documents()` to protect against pre-existing DB rows with unexpected values.
3. Add a small unit test that constructing/persisting a `SourceDocument(language="EN-us")` results in `en-US`, and that invalid tags are rejected (or coerced, depending on your chosen behavior).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Results up to commit c94ba6b ⚖️ Balanced


🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)


Remediation recommended
1. BCP47 parser limitation undocumented triggers ✓ Resolved 📘 Rule violation ⚙ Maintainability
Description
docs/notes.md documents an intentionally limited language-tag parser (not full BCP 47) but does
not provide explicit assumptions and concrete reevaluation triggers for revisiting this accepted
limitation. This can cause the limitation to persist without clear guardrails as requirements expand
(e.g., needing extensions/private-use tags).
Code

docs/notes.md[85]

+来源语言是输入边界的事实元数据,不是用户目标语种的别名。`SourceDocument` 构造时规范化基础 BCP 47-like 标签,再写入 SQLite 和 LLM payload,避免同一正文因大小写差异产生伪历史变化;旧数据库的语言列只保留迁移时已知的 `zh-CN` 默认值。`LanguageSupport.select()` 继续用于显式选择 provider 已声明的 wire 语言并严格拒绝未知值,而地点级用户目标语种通过 `match()` 按逐步去除区域/脚本后缀选择最接近的 provider 语言,匹配失败回到 provider 默认值。这样固定只输出 `ja` 的 API 可以服务 `ja-JP` 的用户而不在构造阶段失败;LLM 仍收到实际来源标签,并在同一主语言时避免无意义的往返翻译。当前标签解析刻意只覆盖应用所需的基础形式,不承诺完整 BCP 47 extension/private-use 语法;若需要这些语法,应引入专门的标准库或成熟解析器并重新审视 wire-code 映射。
Relevance

⭐⭐⭐ High

Team previously asked to add explicit assumptions/triggers for accepted limitations in docs/notes.md
(PR54).

PR-#54
PR-#58
PR-#68

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 2141676 requires that intentionally unresolved/accepted design concerns documented
in docs/notes.md include explicit assumptions and at least one concrete reevaluation trigger. The
added paragraph explicitly states the parser is intentionally limited and does not commit to full
BCP 47 syntax, but it does not enumerate assumptions nor concrete triggers for when this limitation
must be revisited.

Rule 2141676: Document assumptions and reevaluation triggers for intentionally unresolved design concerns
docs/notes.md[85-85]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`docs/notes.md` documents an accepted/intentional limitation (language-tag parsing only supports a basic subset) but does not include explicit assumptions and concrete triggers for reevaluation as required.

## Issue Context
The notes state that the current tag parsing intentionally does not cover full BCP 47 `extension/private-use` syntax and suggests introducing a standard library/parser “if needed”, but it does not spell out assumptions (what makes this acceptable today) or measurable triggers (when to revisit).

## Fix Focus Areas
- docs/notes.md[85-85]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Context source language mislabeled ✓ Resolved 🐞 Bug ≡ Correctness
Description
SourceDocument now defaults language="zh-CN", but HTTPContextSource.fetch() constructs
SourceDocument without providing a language, so arbitrary context URLs are persisted/sent to the
LLM as Chinese regardless of the actual document text. This conflicts with the prompt contract that
context_documents[*].language is the factual source language and can cause the LLM to skip
translation when it should translate.
Code

weather_briefing/models.py[R92-100]

+    language: str = "zh-CN"
    has_allergen_information: bool = False
    history_summary: str | None = None
    history_value: str | None = None

+    def __post_init__(self) -> None:
+        """Normalize the language before persistence or LLM serialization."""
+        object.__setattr__(self, "language", normalize_language_tag(self.language))
+
Relevance

⭐⭐ Medium

No direct precedent; boundary/payload correctness is enforced generally, but not this exact
default-language omission.

PR-#33
PR-#46

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The PR makes language a first-class field on SourceDocument and the prompt depends on it being
accurate, but HTTPContextSource still omits the language argument, causing the default zh-CN to
be applied regardless of actual fetched content language.

weather_briefing/models.py[84-100]
weather_briefing/sources.py[168-185]
weather_briefing/prompts.py[15-18]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
With the new language-metadata contract, `SourceDocument.language` is treated as factual. However, HTTP-fetched context documents are created without specifying a language and therefore default to `zh-CN`, which can mislabel non-Chinese sources.

## Issue Context
- `SourceDocument.language` has a default of `"zh-CN"` and is normalized in `__post_init__`.
- `HTTPContextSource.fetch()` creates `SourceDocument(..., content=response.text)` without passing `language`.
- The system prompt explicitly tells the LLM that `context_documents[*].language` is the actual source-document language.

## Fix Focus Areas
- weather_briefing/models.py[84-100]
- weather_briefing/sources.py[168-185]
- weather_briefing/config.py[170-181]

## Suggested fix
- Extend `ContextSourceConfig` to include an optional `language` field (e.g., `language: str | None = None`).
- Update config parsing (`_context_source`) to accept `language` from `CONTEXT_SOURCES_JSON` and validate it with `normalize_language_tag`.
- Update `HTTPContextSource.fetch()` to pass `language=config.language` when provided.
- For missing language, prefer an explicit unknown tag like `"und"` (undefined) for HTTP context sources (instead of silently defaulting to `zh-CN`), so the metadata is not incorrect when the language is unknown.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. api_code may return None ✓ Resolved 🐞 Bug ☼ Reliability
Description
LanguageSupport.api_code() returns None when api_codes is missing a mapping, but provider code
(e.g., QWeatherProvider) uses the returned value directly for the lang query parameter. A
misconfigured LanguageSupport instance can therefore emit requests with an invalid/missing
language code or fail at the HTTP boundary.
Code

weather_briefing/languages.py[R64-67]

+    def api_code(self, selected: str | None = None) -> str | None:
+        """Return the provider wire-language code for a selected language."""
+        language = self.select(selected)
+        return dict(self.api_codes).get(language)
Relevance

⭐⭐ Medium

Team likes strict boundary validation, but no precedent on None-returning api_code mapping
completeness.

PR-#46
PR-#33

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
api_code() uses a dict lookup that returns None when a mapping is absent, and QWeatherProvider
uses that value as the lang query parameter without any guard, so a missing mapping can propagate
to the HTTP request boundary.

weather_briefing/languages.py[28-68]
weather_briefing/weather_context.py[193-234]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`LanguageSupport.api_code()` currently returns `Optional[str]` and can return `None` if `api_codes` does not contain an entry for a supported language. Some providers treat this value as required (e.g., pass it as the `lang` query parameter), so a partial/incorrect `api_codes` configuration becomes a latent runtime failure.

## Issue Context
- `LanguageSupport.__post_init__()` validates `default`/`supported` but does not validate `api_codes` keys/coverage.
- `QWeatherProvider` assigns `self._api_language = QWEATHER_LANGUAGE_SUPPORT.api_code(...)` and then uses it in `params={"lang": self._api_language}`.

## Fix Focus Areas
- weather_briefing/languages.py[28-68]
- weather_briefing/weather_context.py[193-234]

## Suggested fix
- In `LanguageSupport.__post_init__`, validate `api_codes`:
 - Normalize and validate each language key (must be in `supported`).
 - Ensure keys are unique after normalization.
 - If `api_codes` is provided for a provider that uses wire codes, require coverage for all `supported` languages (or at least for all selectable languages) and raise `ValueError` when missing.
- Additionally (defense in depth), in `QWeatherProvider.__init__` assert `self._api_language is not None` and raise a clear configuration error if it is (so failures are immediate and explicit).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Qodo Logo

Comment thread weather_briefing/service.py
Comment thread weather_briefing/models.py
@IceCodeNew
IceCodeNew force-pushed the codex/pr1-capabilities branch 3 times, most recently from 706f574 to ecf4e29 Compare July 20, 2026 12:10
Base automatically changed from codex/pr1-capabilities to master July 20, 2026 12:44
@IceCodeNew
IceCodeNew force-pushed the codex/pr2-provider-languages branch from 0185f2f to c94ba6b Compare July 20, 2026 13:17
@IceCodeNew

Copy link
Copy Markdown
Owner Author

/agentic_review

@IceCodeNew
IceCodeNew marked this pull request as ready for review July 20, 2026 13:26
Comment thread weather_briefing/weather_context.py Outdated
@qodo-code-review

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit c94ba6b

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Preserve provider output languages across snapshots, history, and LLM payload

✨ Enhancement 🧪 Tests 📝 Documentation ⚙️ Configuration changes 🕐 40+ Minutes

Grey Divider

AI Description

• Add provider language metadata and normalize BCP-47-like tags at model boundaries.
• Persist source-language in context snapshots and include it in the LLM payload.
• Teach QWeather to select/match API languages while fixed-language providers retain true source
 language.
Diagram

graph TD
  P["Weather/capability providers"] --> L["LanguageSupport"] --> M["Snapshots & SourceDocument"] --> DB[("SQLite context_snapshots")] --> S(["Service payload builder"]) --> LLM["LLM prompt/input"]
  L -. "select/match + wire codes" .-> P
  subgraph Legend
    direction LR
    _svc(["Runtime service"]) ~~~ _mod["Module/model"] ~~~ _db[("Database")]
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Adopt a full BCP 47 language-tag parser library
  • ➕ Correct handling of edge cases (extensions, private-use, grandfathered tags)
  • ➕ Less bespoke validation logic to maintain long-term
  • ➖ Adds dependency and potential mapping complexity to provider wire codes
  • ➖ More surface area than currently needed (the app only needs a basic subset)
2. Keep language only at provider level (don’t persist per document)
  • ➕ Avoids schema migration and reduces stored data
  • ➕ Simplifies snapshot persistence
  • ➖ Loses fidelity for mixed-language inputs and historical replay
  • ➖ Makes LLM history correctness depend on runtime provider configuration
3. Store a separate translation-language field instead of source-language
  • ➕ Directly models the output requirement for generation
  • ➕ Could simplify prompt guidance if translation becomes a pipeline step
  • ➖ Conflates fact metadata (source language) with a user preference (target language)
  • ➖ Still needs source-language to avoid translation round trips and citation drift

Recommendation: The PR’s approach (persisting factual source-language per document, plus LanguageSupport for selectable vs fixed providers) is the most robust for history stability and LLM correctness. A full BCP 47 library is worth revisiting only if you need extensions/private-use tags or more complex normalization beyond the current constrained subset.

Files changed (16) +276 / -13

Enhancement (7) +137 / -9
air_quality.pyPropagate air-quality output_language into SourceDocument language +2/-0

Propagate air-quality output_language into SourceDocument language

• Sets AirQualitySnapshot.output_language and uses it when building SourceDocument so the document records true source language for LLM/history.

weather_briefing/air_quality.py

allergen.pyAttach allergen output_language to generated SourceDocument +1/-0

Attach allergen output_language to generated SourceDocument

• Uses AllergenSnapshot.output_language when generating SourceDocument so allergen context carries correct language metadata.

weather_briefing/allergen.py

capabilities.pyAdd language_support to ProviderCapabilities +3/-1

Add language_support to ProviderCapabilities

• Extends ProviderCapabilities to include LanguageSupport (default fixed zh-CN), enabling capability providers to declare selectable vs fixed output languages.

weather_briefing/capabilities.py

languages.pyIntroduce LanguageSupport and language-tag normalization utility +72/-0

Introduce LanguageSupport and language-tag normalization utility

• Adds normalize_language_tag() and a LanguageSupport dataclass supporting selectable/fixed languages, region-variant matching, and provider wire-code mapping.

weather_briefing/languages.py

models.pyAdd language metadata to SourceDocument and snapshots +10/-0

Add language metadata to SourceDocument and snapshots

• Adds SourceDocument.language with normalization at post-init, and adds output_language fields to AirQualitySnapshot, AllergenSnapshot, and WeatherContextSnapshot to preserve provider output language through the pipeline.

weather_briefing/models.py

service.pyInclude language fields in LLM payload and history value computation +13/-4

Include language fields in LLM payload and history value computation

• Adds output_language to the payload and includes per-document language in context_documents and history serialization; updates context document value hashing to incorporate language for stable change detection.

weather_briefing/service.py

weather_context.pyAdd provider language support constants and wire QWeather lang selection +36/-4

Add provider language support constants and wire QWeather lang selection

• Defines QWEATHER_LANGUAGE_SUPPORT and OPEN_METEO_LANGUAGE_SUPPORT, selects/matches requested output_language for QWeather and maps it to wire codes, and propagates output_language into snapshots and derived documents.

weather_briefing/weather_context.py

Tests (4) +119 / -0
test_languages.pyAdd unit tests for language normalization and LanguageSupport behavior +72/-0

Add unit tests for language normalization and LanguageSupport behavior

• Introduces tests for tag normalization/validation, selectable vs fixed language support, region-variant matching, and SourceDocument language normalization and rejection of invalid tags.

tests/test_languages.py

test_service.pyUpdate service payload/history tests for language fields +2/-0

Update service payload/history tests for language fields

• Extends context-history expectations to include language in stored/serialized document tuples and payload dictionaries.

tests/test_service.py

test_state.pyTest persistence of context snapshot language in SQLite store +16/-0

Test persistence of context snapshot language in SQLite store

• Adds a regression test ensuring a SourceDocument language survives save/load via SQLiteStateStore.

tests/test_state.py

test_weather_context.pyVerify provider language selection and snapshot output_language +29/-0

Verify provider language selection and snapshot output_language

• Asserts QWeather sends the correct lang wire parameter, propagates output_language into snapshots, and supports selecting/matching region-variant language tags.

tests/test_weather_context.py

Documentation (2) +4 / -0
design.mdDocument provider language support and source-language persistence +2/-0

Document provider language support and source-language persistence

• Adds a design note describing LanguageSupport, BCP 47 tagging for SourceDocument/snapshots, LLM handoff rules, and the SQLite migration defaulting legacy snapshots to zh-CN.

docs/design.md

notes.mdClarify semantics of source language vs user target language +2/-0

Clarify semantics of source language vs user target language

• Explains why source language is factual metadata, how normalization prevents false history churn, and how select() vs match() differ for provider wire languages and user requests.

docs/notes.md

Other (3) +16 / -4
cli.pyWire provider language_support into CLI provider capability configuration +4/-0

Wire provider language_support into CLI provider capability configuration

• Imports the provider LanguageSupport constants and attaches them to QWeather/Open-Meteo ProviderCapabilities instances used by the CLI publisher setup.

weather_briefing/cli.py

prompts.pyUpdate prompt rules to respect source language vs output_language +4/-0

Update prompt rules to respect source language vs output_language

• Adds explicit instructions that the model must output in output_language while treating context document language as the true source language, avoiding unnecessary translation round trips.

weather_briefing/prompts.py

state.pyPersist SourceDocument.language in SQLite context_snapshots +8/-4

Persist SourceDocument.language in SQLite context_snapshots

• Extends the context_snapshots schema with a non-null language column (default zh-CN), migrates existing DBs, and updates insert/select paths to round-trip language.

weather_briefing/state.py

Comment thread docs/notes.md Outdated
Comment thread weather_briefing/languages.py Outdated
Comment thread weather_briefing/models.py
@qodo-code-review

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit c94ba6b

@IceCodeNew
IceCodeNew force-pushed the codex/pr2-provider-languages branch from c94ba6b to 8dea20a Compare July 20, 2026 13:49
@IceCodeNew

Copy link
Copy Markdown
Owner Author

/agentic_review

@IceCodeNew
IceCodeNew merged commit 2533ec5 into master Jul 20, 2026
18 checks passed
@IceCodeNew
IceCodeNew deleted the codex/pr2-provider-languages branch July 20, 2026 13:52
)
OPEN_METEO_LANGUAGE_SUPPORT = LanguageSupport.fixed("zh-CN")

_WEATHER_DOCUMENT_LABELS = {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

1. Hardcoded weather label dictionaries 📘 Rule violation ⚙ Maintainability

Multiple modules in weather_briefing embed large, locale-dependent label/format reference tables
directly as Python literals (e.g., _WEATHER_DOCUMENT_LABELS, _QWEATHER_FORMATS,
_AIR_QUALITY_FORMATS, _ALLERGEN_FORMATS) instead of externalized, validated reference data
files. This increases maintenance overhead and risk of label/schema drift shipping without
centralized validation for these evolving tables.
Agent Prompt
## Issue description
The PR introduces sizable localized label/format dictionaries as Python constants across multiple `weather_briefing` modules (weather context, air quality, allergen). Compliance (PR Compliance ID 2141692) expects this kind of large, mutable domain reference data to be externalized into dedicated validated data files and loaded via a validated loader to reduce maintenance risk and prevent label/schema drift.

## Issue Context
These dictionaries function as shared reference/i18n scaffolding that is likely to evolve independently of application logic; keeping them in code makes updates harder to validate and review consistently and encourages duplication across components.

## Fix Focus Areas
- weather_briefing/weather_context.py[45-125]
- weather_briefing/air_quality.py[17-78]
- weather_briefing/allergen.py[12-53]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@qodo-code-review

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 8dea20a

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants