Commit Graph

259 Commits

Author SHA1 Message Date
2569718930@qq.com 6c08a68413 @
性能与用户体验全面优化

    前端性能:
    - 移除 Three.js 依赖(~600KB),天气粒子改为纯 CSS 动画 + Canvas 2D
    - Google Fonts 切换为 next/font 自托管,消除跨域字体请求
    - 合并 ScanTerminalLightTheme.module.css (37KB) 到主 CSS,亮/暗主题统一用 CSS 变量
    - 新增 /api/dashboard/init 聚合端点,首次加载 4 次往返 → 1 次
    - 添加 Service Worker 静态资源缓存,修复 PWA manifest 配置

    用户体验:
    - 新增全局错误边界 error.tsx / global-error.tsx,崩溃不再白屏
    - 决策卡和城市详情的更新时间改为相对时间("15秒前"),每秒自动刷新
    - 数据陈旧时状态标签从青色切换为琥珀色提示

    DEB 算法增强:
    - 市场扫描路径接入 Open-Meteo 多模型数据(ECMWF/GFS/ICON/JMA/HRDPS 等)
    - MAE 计算加入时间衰减(decay_factor=0.85),近期模型误差权重更高

    Scope-risk: MEDIUM — 全量 170 测试通过,前端 TypeScript/build 通过,ruff 零告警
    Tested: python -m pytest -q (170 passed), npx tsc --noEmit (0 errors), npm run build (success), ruff check .
@
2026-05-14 21:31:05 +08:00
2569718930@qq.com 37494a7192 @
将 web/routes.py 拆分为模块化 router + service 架构

    - 新增 web/app_factory.py 集中注册 7 个域名 router
    - 新增 web/routers/ 薄壳路由层(auth/city/system/scan/ops/payments/analytics)
    - 新增 web/services/ 业务函数下沉(每域独立 service 文件)
    - web/routes.py 缩减为 city_runtime 的兼容重导出 facade
    - analysis_service.py/app.py 适配新入口并清理冗余导入

    Scope-risk: LOW — 全量 170 测试通过,router 注册顺序与原路由一致
    Tested: python -m pytest -q (170 passed), ruff check . (All checks passed)
@
2026-05-14 20:01:26 +08:00
2569718930@qq.com 02add6d994 feat: implement city weather detail API routing, dashboard data models, and monitoring infrastructure 2026-05-14 14:21:28 +08:00
2569718930@qq.com c006d13fea MonitorPanel 从独立 API 改为复用 DashboardStore 数据,砍掉 /api/m 和 /m/json 2026-05-14 00:43:19 +08:00
2569718930@qq.com c5fdb93ae5 监控页:后端 30s 缓存 + 前端 loading 态 2026-05-14 00:29:51 +08:00
2569718930@qq.com 2202f8e211 砍掉 /m HTML 页面,只保留 /m/json 给 React 组件用 2026-05-14 00:23:03 +08:00
2569718930@qq.com fc48795299 监控页从 iframe 改为原生 React 组件:无加载延迟,30s JSON 轮询 2026-05-14 00:18:26 +08:00
2569718930@qq.com 22b414f69e 监控页时间改为用户本地时间(JS toLocaleTimeString) 2026-05-14 00:10:43 +08:00
2569718930@qq.com 9dd59ceb4e 修复 ruff 风格:多行语句、bare except → except Exception 2026-05-13 23:55:54 +08:00
2569718930@qq.com a9b1449680 放弃 Jinja2,改用 f-string 直接拼 HTML,零模板引擎依赖 2026-05-13 23:54:53 +08:00
2569718930@qq.com a7ba7aed3a Debug:简化 context 排查 Jinja2 500 2026-05-13 23:46:18 +08:00
2569718930@qq.com 1240fbb673 跑道数据改用 tuple 避免 Jinja2 dict unhashable 错误 2026-05-13 23:41:31 +08:00
2569718930@qq.com d56889c2e5 修复 Jinja2 模板 dict 访问方式和条件表达式 2026-05-13 23:38:06 +08:00
2569718930@qq.com b94037f328 路由 /monitor → /m,URL 更短 2026-05-13 23:24:26 +08:00
2569718930@qq.com a221b3e969 用 Python 重写市场监控网页版:FastAPI+Jinja2+HTMX,复用 _analyze() 2026-05-13 23:19:49 +08:00
AmandaloveYang e344fae75f 首尔/釜山 AMOS 数据缓存 TTL 从 300s 降至 60s,对齐官网 1 分钟刷新频率
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 17:28:53 +08:00
2569718930@qq.com 8075fb66b7 AMOS 增加 info 级别日志追踪数据流
- weather_sources.py: _attach_korean_amos_data 记录 fetch 开始、成功/失败、温度/跑道数据
- amos_station_sources.py: _amos_get_page 记录页面匹配/不匹配
- amos_station_sources.py: fetch_amos_official_current 记录 HTML 获取和解析
- analysis_service.py: _analyze 记录 AMOS 数据是否到达分析层

重启后端后在日志中搜索 "AMOS" 可追踪完整数据流:
  AMOS: fetching for city=seoul
  AMOS page matched icao=RKSI length=...
  AMOS fetch_amos_official_current: got HTML for RKSI...
  AMOS: got data for city=seoul temp_c=... source=...
  AMOS _analyze: found amos data for city=seoul temp_c=...
2026-05-10 19:46:26 +08:00
2569718930@qq.com 40cd8fc2a6 修复机场观测面板不显示 + 放宽 AMOS 展示条件 2026-05-10 19:06:22 +08:00
2569718930@qq.com e23a90a961 修复釜山 AMOS 数据获取 + 跑道面板容错
AMOS 页面用 JS 切换机场,GET 参数无效。新增策略:
- 先 GET 建立 session,再 POST form data 切换机场
- 尝试多种 form data 组合(icao/stn/airport/code)
- 回退到 GET 参数尝试

跑道面板容错:
- 无温度数据时仍展示跑道风/能见度/RVR
- 温度缺失显示 "--" 而非隐藏整行
- analysis_service 输出条件放宽:有 temp_c 或 runway_obs 即可

Tested: python -m ruff check ., npx tsc --noEmit
2026-05-10 18:39:37 +08:00
2569718930@qq.com ba352feb34 首尔/釜山机场报文优先使用 AMOS(更新更快)
- analysis_service.py:AMOS 数据覆盖 airport_current 的 raw_metar、wind、pressure
- source_label 设为 "AMOS"(区别于 aviationweather.gov METAR)
- stale_for_today 强制 false(AMOS 数据本身证明了时效性)
- observation_time 优先使用 AMOS 时间戳
- AI 读取的 city_snapshot.current.raw_metar 来自 AMOS 跑道传感器

优先级:AMOS raw_metar > aviationweather.gov METAR(首尔/釜山)

Reason: AMOS 直连韩国机场系统,延迟更低,更新更快
2026-05-10 18:18:40 +08:00
2569718930@qq.com 04b2f0e4a3 AMOS 数据接入分析层和 AI 预测判定
问题:AMOS 数据在采集层取出后从未被 _analyze() 读取,AI 无法看到跑道级数据

修复:
- analysis_service.py:_analyze() 新增 AMOS 温度读取优先级
  观测来源顺序:结算源 > AMOS跑道传感器 > METAR > MGM > NMC
- AMOS 数据自动覆盖 current.wind_speed_kt、current.pressure_hpa、current.raw_metar
- 输出新增 "amos" 字段携带完整跑道数据(temp_source、runway_temps)
- scan_terminal_service.py:AI prompt 的 current 区块新增 pressure_hpa 和 observation_source
- AI 现在知晓数据来自 AMOS 跑道传感器(observation_source="amos"),可据此判断数据权威性

数据流:
  AMOS fetch -> raw["amos"] -> _analyze() -> current.observation_source="amos"
  -> result["amos"] -> _build_city_ai_prompt -> city_snapshot.current
  -> AI reads runway sensor temp/wind/pressure as authoritative observation

Tested: python -m ruff check ., npx tsc --noEmit
2026-05-10 18:16:10 +08:00
2569718930@qq.com e2bea367a1 校准漂移检测增加日志告警 + 更新架构文档
- core.py:drift.drifted=true 时输出 loguru warning,包含 delta% 和 sample 数量
- data-architecture-review.md:标记 2 项误诊(METAR TTL 实际 600s、轮询已有 AbortController 保护)
- 剩余真正待办:校准自动重训练(需算力)、缓存键细化(低优先级)
2026-05-10 17:06:43 +08:00
2569718930@qq.com b3ea8dcfa7 数据链路 P1 修复:ETag 缓存 + 校准漂移检测
P1-5 ETag 支持:
- 后端新增 _etag_middleware:GET /api/* 自动返回 ETag (MD5)
- 支持 If-None-Match 请求头,匹配时返回 304 + 30s Cache-Control
- 前端 cache: no-store → default,浏览器自动处理 ETag/304 节省带宽

P1-6 校准漂移检测:
- probability_calibration.py 新增 check_calibration_drift()
- 对比最近 200 条 daily_records 的 CRPS 与校准基线
- 漂移 >15% 时返回 warning 提示重新训练
- 集成到 /api/system/status 的 probability.drift 字段

Tested: python -m ruff check ., npx tsc --noEmit
2026-05-10 16:54:48 +08:00
2569718930@qq.com c0bb2acf78 修复数据链路 P0 瓶颈:缓存炸弹、Context 重渲染、LGBM 循环、TTL 不匹配
P0-1 sessionStorage 限制:
- writeCityDetailCacheBundle 只保留最近 3 个城市的详情
- 避免 3-10MB JSON 序列化阻塞主线程

P0-2 Context 拆分:
- 新增 CityDetailsContext 独立管理 cityDetailsByName 变更
- 新增 useCityDetails hook,只订阅详情的组件不再因 cities/proAccess 变化重渲染
- DashboardStoreContext 保持不变,向后兼容

P0-3 扫描终端 TTL:
- SCAN_TERMINAL_PAYLOAD_TTL_SEC 30s → 120s
- 匹配 ThreadPoolExecutor(4) x 60 城的实际重算耗时

P0-4 LGBM 循环依赖:
- LGBM 预测值不再作为 DEB 输入参与权重计算
- 保留为独立参考字段 lgbm.prediction 输出给前端展示
- 消除 DEB → LGBM 训练 → DEB 的循环

新增 docs/data-architecture-review.md 完整数据链路审查报告

Tested: npx tsc --noEmit, python -m ruff check .
2026-05-10 16:48:28 +08:00
2569718930@qq.com 9635387beb 移除日历视图:功能与决策卡重叠,维护成本高于价值
- 删除 CalendarView.tsx、calendar-action-utils.ts 及其测试文件
- 删除 ScanTerminalCalendar.module.css
- ScanTerminalDashboard:移除日历 tab 按钮、日历渲染分支、ContentView 类型中的 calendar
- ScanTerminalLightTheme:清理所有 .scan-calendar-* 规则(~40 行)
- scan-root-styles.ts:移除 ScanTerminalCalendar 导入

同时完善地理排序:
- scan_terminal_filters.py:market_region_from_tz_offset 细化为 7 个区域(东亚/东南亚/中亚/西亚/欧洲非洲/南美/北美)并增加 sort_order
- scan_terminal_city_row.py:传递 trading_region_sort
- dashboard-types.ts:增加 trading_region_sort 字段
- decision-utils.ts:sortRowsByUserTime 按 trading_region_sort 优先排序

Rejected: keep-calendar-view, calendar-actions-overlap-with-decision-cards
Tested: npx tsc --noEmit, python -m ruff check .
2026-05-10 15:48:18 +08:00
2569718930@qq.com 30faac989c 秒开 AI 预测最高温:preview 事件提前下发确定性预测值
之前 predicted_max 只在 AI 流式 final 事件返回后才显示(10-25 秒),
但后端 fallback 早在 1ms 内就算好了 cluster median / DEB 中枢。
现在 preview 事件同步下发确定性 predicted_max + range_low/high,
前端也在 fallback payload 中自动从 multi_model + DEB 计算预测值,
用户点击城市后 ~100ms 即可看到 AI 预测最高温。
2026-05-07 21:04:26 +08:00
2569718930@qq.com 7b82ba68df 回退 _analyze_summary 中未启用的 LGBM 增强步骤
LGBM 当前默认禁用 (POLYWEATHER_LGBM_ENABLED=false),predict_lgbm_daily_high
直接返回 None,在 _analyze 中也未实际参与 DEB 计算,_analyze_summary 无需重复。

Rejected: 在 LGBM 正式启用前,_analyze_summary 不应包含会被跳过的 LGBM 调用
2026-05-06 18:30:47 +08:00
2569718930@qq.com a684586cb4 统一 DEB 数据源为单一计算路径
getModelView 优先使用根级 detail.deb.prediction 保证与 AI 证据面板一致,
_build_city_detail_payload 补上缺失的 deb 和 multi_model_daily 字段,
_analyze_summary 补上 LGBM 增强步骤使其与 _analyze 产出相同的 DEB 值。

Constraint: 三个入口 (panel/full/summary) 的 DEB 必须经过同一套 calculate_dynamic_weights + LGBM 重算流程
Tested: ruff + tsc 全过
2026-05-06 18:24:15 +08:00
2569718930@qq.com 2f73828d4d 重构城市决策卡 hero 布局:去除 sticky 固定效果,右侧改为三指标并列对比
将 scan-ai-city-hero 从"左重右轻"改为左右均衡布局,右侧从单一预计高温数字
替换为当前温度/预计最高温/峰值时间三列并排指标卡,中间列高亮为主指标。
移除冗余的 pills 行和隐藏的 mobile-priority div,新鲜度条改为水平单行。
同步更新亮色主题 CSS 新增 metrics 样式。修复 fallback 模块未使用变量。

Constraint: dark/light 双主题完整覆盖
Scope-risk: hero 高度缩减约 30%,需确认移动端 MobileDecisionCard 不受影响
2026-05-06 17:57:40 +08:00
2569718930@qq.com 4ed9321756 修复流式 AI 请求 max_tokens 不足导致 JSON 截断
此前流式 650 tokens,只够 4 个文字字段。新增 predicted_max
等 6 个字段后,中文解读经常超出上限致 JSON 不完整,触发重试。
现提升至 900 tokens(主请求 1200),并更新测试断言。
2026-05-06 16:51:31 +08:00
2569718930@qq.com a1d5435dd7 强化 HKO 天文台观测的 AI 解读能力
- HKO instruction 从纯否定句改为正向指导,明确告知 AI 可用数据维度
  (温度、最高/最低温、湿度、10分钟风速风向)
- AI 输入 current 对象新增 wind_speed_kt/wind_dir/humidity 字段,
  HKO 和 METAR 城市均受益
- 提示词统一补充湿度分析要求
2026-05-06 16:48:05 +08:00
2569718930@qq.com a4ce450dbc 修复 system_prompt 源代码可读性,\uXXXX 转回正常中文
此前为避免 curly quote 问题用 \uXXXX 转义序列写入中文,运行正确但
源码不可读。现在改为正常 UTF-8 中文,引号改用「」避免与 Python
字符串定界符冲突。
2026-05-06 16:15:28 +08:00
2569718930@qq.com eb42d644a3 DEB 降级为模型集群中的普通一员,AI 不再照搬 DEB 做预测
此前 DEB 以独立字段 deb.prediction 传给 AI,提示词又要求"必须综合 DEB",
导致 AI 直接输出 DEB 值而非独立判断。改动:
- 移除 AI 输入中的独立 deb 字段,DEB 只作为 model_cluster.sources 中的
  一条记录 (model: "DEB (fusion)"),与其他模型平等
- 提示词改为:以模型集群集中区间为基线,用报文观测信号独立判断上修/下修/维持
- 流式/非流式提示词均强调"不要直接照搬 DEB 的值,差异是正常的"
- 回退路径改用模型集群中位数作为默认预测,DEB 仅作为备选参考
2026-05-06 16:03:22 +08:00
2569718930@qq.com c296bbb789 AI 机场报文解读流式请求增加最高温预测输出
此前 build_city_ai_stream_request 只要求 AI 输出 metar_read + reasoning,
明确禁止生成 predicted_max/range/confidence 等数值字段,流式解读只有
文字没有温度预测。现在让流式请求同步输出 predicted_max、range_low、
range_high、unit、confidence、final_judgment,使前端报文解读面板能
直接展示 AI 预测的今日最高温及置信区间。
2026-05-06 15:38:58 +08:00
2569718930@qq.com 6c203bee60 This version of Antigravity is no longer supported. Please upgrade to receive the latest features. 2026-05-01 11:27:16 +08:00
2569718930@qq.com 6da72b5488 This version of Antigravity is no longer supported. Please upgrade to receive the latest features. 2026-04-29 19:10:25 +08:00
2569718930@qq.com 5b32e71f5d This version of Antigravity is no longer supported. Please upgrade to receive the latest features. 2026-04-29 18:52:55 +08:00
2569718930@qq.com 20516000a2 Clarify scan terminal service boundaries
The scan terminal service had accumulated cache, payload, filtering, AI prompt, AI merge, METAR gate, ranking, and city-row construction details in one file. This splits those stable responsibilities into focused modules while preserving the endpoint payload shape and existing behavior.

Constraint: User-visible behavior and release version must remain unchanged for this internal refactor

Rejected: Rewrite the terminal scan flow around a new abstraction | too risky while production behavior is being stabilized

Confidence: high

Scope-risk: moderate

Directive: Keep scan_terminal_service.py as orchestration; add detailed rule changes to the focused modules instead of re-growing the service file

Tested: py_compile for extracted modules; ruff check .; pytest tests/test_scan_terminal_modules.py tests/test_web_observability.py; full pytest; npm run test:business; npm run build; git diff --cached --check
2026-04-28 17:13:45 +08:00
2569718930@qq.com b122e7cbae Stabilize the decision workspace data boundaries
The scan terminal had grown into overlapping CSS, request-state, AI-provider, and city-card data responsibilities. This refactor separates those boundaries without changing product behavior: CSS modules are split by surface, city AI prompt/provider/fallback logic is isolated, and scan terminal request state now has reusable RemoteData adapters plus business-state tests.

Constraint: Preserve existing global scan-terminal class names and API responses during the refactor

Constraint: No new dependencies; keep this as a file-boundary cleanup

Rejected: Introduce React Query now | higher migration risk than the requested lightweight query-client path

Rejected: Rewrite AI stream behavior | progressive/fallback states are product-sensitive and were only adapter-split

Confidence: high

Scope-risk: moderate

Reversibility: clean

Directive: Keep AI stream state changes covered by business snapshots before changing fallback/cache wording

Tested: npm run test:business; npx tsc --noEmit; npm run build; python pytest -q; ruff check; py_compile targeted city AI modules

Not-tested: Live DeepSeek provider network replay and browser visual QA
2026-04-28 14:45:34 +08:00
2569718930@qq.com 51969d0b4a Keep scan terminal lint clean after helper split
The AI helper extraction left a stale private helper import in the scan terminal service. Removing it keeps CI aligned with the current call graph without changing runtime behavior.

Constraint: CI runs ruff F401 as a blocking check.
Confidence: high
Scope-risk: narrow
Reversibility: clean
Tested: .\.codex-tmp\pydeps\bin\ruff.exe check .
Not-tested: Full backend test suite; change is import-only.
2026-04-28 08:38:00 +08:00
2569718930@qq.com 89a71d1bc0 Add Qingdao to the tradable city network
Qingdao needs the same airport-settlement path as the other Wunderground-backed APAC cities, so the registry, aliases, timezone, prewarm, official links, market focus, and tests now point to ZSQD / Qingdao Jiaodong International Airport.

Constraint: User supplied Wunderground Qingdao/ZSQD settlement URL.
Rejected: Add a partial registry-only entry | it would show in APIs without frontend links, prewarm coverage, or alias support.
Confidence: high
Scope-risk: narrow
Reversibility: clean
Tested: pytest tests/test_country_networks.py tests/test_web_observability.py::test_cities_endpoint_includes_new_wunderground_cities -q
Tested: npm run build
Not-tested: Live Wunderground fetch for ZSQD in production.
2026-04-28 07:08:33 +08:00
2569718930@qq.com de0effc40b Make city AI helpers maintainable outside scan service
The scan terminal service had grown into a 3.5k-line file that mixed endpoint orchestration, AI provider calls, fallback copy, JSON repair, and deterministic evidence guards. This extracts the city-AI helper layer into a focused module while preserving the old private names through imports for existing tests and callers.

Constraint: Keep behavior unchanged after the previous evidence-guard fixes and avoid a broad service rewrite.

Rejected: Split every scan-terminal concern at once | too much regression risk for this maintenance pass.

Confidence: high

Scope-risk: narrow

Tested: pytest tests/test_web_observability.py -q

Tested: npm run build
2026-04-28 06:40:53 +08:00
2569718930@qq.com 0c9b07faaf Keep provider reads behind deterministic evidence guards
DeepSeek can return a polished city forecast that conflicts with already-computed stale-observation, observed-break, or peak-window evidence. The completion path now carries the deterministic fallback guard state and overwrites only the critical fields when provider text or numbers contradict those local facts.

Constraint: Airport-read latency optimization keeps provider output narrow, so backend completion remains the authority for final highs and evidence conflicts.

Rejected: Trust provider final wording when present | it can reintroduce stale METAR anchors or miss observed high breaks.

Confidence: high

Scope-risk: narrow

Tested: pytest tests/test_web_observability.py -q

Tested: npm run build
2026-04-28 06:36:33 +08:00
2569718930@qq.com 0eab2fe628 Treat stale observations as weak evidence
City-card fallback reads now stop using stale METAR or official observations as strong live anchors. A stale observation no longer forces high/low revisions, and both backend and browser AI cache keys include the observation fingerprint so updated report times, receipt times, temperatures, or stale status invalidate old AI text.

Constraint: Cached city AI reads must not survive a material observation update

Rejected: Let stale METAR trigger observed-break revisions | stale reports can be older than the active temperature path

Confidence: high

Scope-risk: moderate

Tested: pytest tests/test_web_observability.py -q

Tested: npm run build
2026-04-28 06:28:29 +08:00
2569718930@qq.com 0a3242c6ec Tighten fallback reads around peak-window evidence
Fallback city-card reads now distinguish three cases that previously collapsed into the generic fast-evidence copy: observed highs above the model path, observed highs still lagging after the peak window, and low observations before the peak window that should wait for confirmation rather than down-revise immediately. The same pass removes three unused private helpers from the scan terminal service.

Constraint: Fallback output must be useful before the full AI airport-bulletin read returns

Rejected: Treat any low latest METAR as a down-revision | early-day observations can be below the forecast before the peak window

Rejected: Keep unused helper wrappers | they were unreferenced and added noise to an already large module

Confidence: high

Scope-risk: moderate

Tested: pytest tests/test_web_observability.py -q

Tested: npm run build
2026-04-28 06:22:07 +08:00
2569718930@qq.com d1d9f80f0f Revise fallback highs after observed breaks
Fast evidence mode should not say DEB and models support the center when the latest METAR has already exceeded that center or the model upper edge. The fallback now treats the live observation as a lower bound for the daily high and explains the upward revision pressure.

Constraint: Fallback output must remain useful before the full AI bulletin read returns

Rejected: Keep the original DEB center until AI completes | it can be lower than an already-observed temperature

Confidence: high

Scope-risk: narrow

Tested: pytest tests/test_web_observability.py::test_city_ai_fallback_revises_up_when_latest_metar_breaks_above_models tests/test_web_observability.py::test_city_ai_fallback_reasoning_identifies_fast_evidence_mode tests/test_web_observability.py::test_city_ai_stream_request_only_asks_provider_for_observation_read -q

Tested: npm run build
2026-04-27 12:33:56 +08:00
2569718930@qq.com c3f092fc28 Speed up streamed airport bulletin reads
The city card only needs the provider to interpret the latest METAR or official observation. Deterministic fields such as the high-temperature center, model cluster note and fallback risks are already available server-side, so the stream request now asks DeepSeek for only the observation read and concise reasoning.

Constraint: City cards still need a complete payload for both Chinese and English UI modes

Rejected: Keep generating the full decision schema in the stream | too much model output for every card

Rejected: Retry failed streams by default | it can double latency and the fallback can use partial streamed text

Confidence: high

Scope-risk: moderate

Tested: pytest tests/test_web_observability.py::test_city_ai_stream_request_only_asks_provider_for_observation_read tests/test_web_observability.py::test_city_ai_fallback_reasoning_identifies_fast_evidence_mode tests/test_web_observability.py::test_city_ai_partial_json_trims_dangling_taf_clause tests/test_web_observability.py::test_city_ai_schema_completion_trims_dangling_taf_clause -q

Tested: npm run build
2026-04-27 09:51:09 +08:00
2569718930@qq.com ee2a5338bb Avoid overstating fallback AI bulletin reads
The city decision fallback path is generated when the full DeepSeek city-airport read has not completed, so the reasoning copy now labels the state as fast evidence mode instead of saying the AI read is normal.

Constraint: Fallback output may use only DEB, model cluster, and latest observation evidence

Rejected: Keep 'AI read normal' wording | it implies a completed AI interpretation when the fallback path is active

Confidence: high

Scope-risk: narrow

Tested: pytest tests/test_web_observability.py::test_city_ai_fallback_reasoning_identifies_fast_evidence_mode tests/test_web_observability.py::test_city_ai_partial_json_trims_dangling_taf_clause tests/test_web_observability.py::test_city_ai_schema_completion_trims_dangling_taf_clause -q
2026-04-27 09:45:11 +08:00
2569718930@qq.com 4eb50ce880 Prevent dangling TAF fragments in city AI reads
City AI can return a partially streamed JSON string when the provider truncates output. The fallback previously kept an unfinished clause such as '但TAF显示', which made the forecast explanation look broken even though earlier evidence was usable.

Constraint: Provider JSON can be truncated after useful fields have already streamed

Rejected: Drop all partial AI text | would lose valid METAR interpretation already returned before truncation

Confidence: high

Scope-risk: narrow

Tested: pytest city AI truncation regression tests

Tested: npm run build

Not-tested: Live DeepSeek provider response
2026-04-27 08:28:00 +08:00
2569718930@qq.com d609d3803e feat: implement scan terminal service with caching and background refresh logic 2026-04-27 06:23:09 +08:00