22 Commits

Author SHA1 Message Date
mrlonely
5d3e4e374a fix: count DingTalk webhook as notification channel (#2230) 2026-08-19 22:52:10 +08:00
Alfred
628c5b6ef7 feat: add Codex App Server agent prototype (#2004) 2026-07-19 16:31:54 +08:00
Alfred
4c678850e2 feat: add Claude Code and OpenCode generation backends (#1850)
Co-authored-by: zhulinsen <42829555+ZhuLinsen@users.noreply.github.com>
2026-06-30 22:33:02 +08:00
Alfred
1f91024dec feat: 新增 codex_cli 本地生成后端 Phase2 (#1769)
* feat: add codex cli generation backend

* fix: harden local CLI backend phase 2

* fix: harden local cli output file handling

* fix: tighten codex cli backend contracts

* fix: support codex cli windows smoke path

* fix: make local cli tests portable on windows

* fix: avoid duplicate codex final output accounting
2026-06-24 20:19:19 +08:00
Alfred
2f75e832e3 feat: 添加 GenerationBackend Phase 1 抽象 (#1751)
* feat: add generation backend abstraction

* fix: tighten generation backend phase1 contract

* fix: preserve market review backend config errors
2026-06-22 22:42:53 +08:00
Delicious233
3471afbd98 feat: add Feishu App Bot notification sender with P2P and group support (#1553)
* feat: add Feishu App Bot notification sender with P2P and group support

The existing FeishuSender only supports custom robot Webhook mode.
This commit extends it to support App Bot (lark-oapi SDK) mode, auto-routing
between webhook (priority) and App Bot when FEISHU_APP_ID + FEISHU_APP_SECRET
+ FEISHU_CHAT_ID are configured.

Design:
- send_to_feishu() routes: webhook if URL set, else App Bot
- DCLP lazy client init with thread-safe sentinel guard
- Retry (3 attempts, exponential backoff) with fixed UUID for idempotency
- Card-first / text-fallback content strategy
- Chunking for long messages
- Runtime enum validation for FEISHU_RECEIVE_ID_TYPE and FEISHU_DOMAIN
- Safe SDK defaults (FEISHU_DOMAIN/LARK_DOMAIN) before import try-block
  so Webhook path never depends on lark-oapi SDK presence
- Config, diagnostics, setup check, notification test, and CI workflow
  all consistent with the new App Bot channel semantics
- lark-oapi>=1.0.0 already in requirements.txt (line 23)

Verification:
- 20/20 unit tests pass (help metadata + FeishuSender)
- E2E: real Feishu API — SDK import, token, client init, P2P text+card send all PASS
- Webhook regression: verified no SDK dependency for existing Webhook path

* fix: add missing Feishu App Bot locale entries and env table keys, harden sender error handling

CI fix 1 (test_registry_help_keys_exist_in_locales):
- Add zh-CN and en-US locale entries for FEISHU_CHAT_ID,
  FEISHU_RECEIVE_ID_TYPE, FEISHU_DOMAIN in settingsHelp.ts

CI fix 2 (test_notification_actions_env_table_matches_generated_output):
- Add FEISHU_RECEIVE_ID_TYPE, FEISHU_DOMAIN to feishu advanced_keys
  in CHANNEL_SPECS so they appear in KEY_SPECS
- Regenerate managed env table in docs/notifications.md

feishu_sender.py hardening:
- Catch network exceptions in webhook _post_payload so card-to-text
  fallback actually executes on transient failures
- Guard response.json() and isinstance(result, dict) against
  non-JSON / non-dict HTTP 200 responses
- Extract shared _build_card_body() to de-duplicate card payload
  construction between webhook and App Bot paths
- Rename module-level lark -> _lark to avoid shadowing
- Guard resp.get_log_id() with try/except
- Add None guard on send_to_feishu content parameter

e2e script improvements:
- Support FEISHU_OPEN_ID for P2P test, FEISHU_DOMAIN for Lark
- Add FEISHU_TEST_SEND_TEXT=1 for plain-text-only path testing
- Clarify docstring: setup validation + smoke test, not full e2e

* fix: consolidate Feishu App Bot notification contract

* fix: align Feishu domain help scope

---------

Co-authored-by: mumu <42829555+ZhuLinsen@users.noreply.github.com>
2026-06-05 08:56:42 +08:00
123kaze
d8a067ba47 feat: improve first-run configuration validation (#1558)
* feat: improve first-run configuration validation

* fix: clarify invalid LLM channel validation
2026-06-02 23:44:05 +08:00
Alfred
a75a0c502e feat: complete p6 notification channel cleanup (#1275) 2026-05-13 19:19:54 +08:00
Alfred
9f56238420 feat: 支持 ntfy 一等通知渠道(P6-A) (#1271)
* feat: add ntfy notification channel

* test: add ntfy smoke evidence images

* fix: validate ntfy endpoint in structured config

* fix: align ntfy endpoint validation
2026-05-13 12:47:46 +08:00
Alfred
9f70705234 feat: 添加 P4 通知降噪机制 (Refs #1200) (#1260)
* feat: add notification noise controls

* docs: clarify notification timezone fallback

* fix: address notification noise review feedback
2026-05-11 07:45:41 +08:00
Alfred
f9549c7f39 fix: add notification baseline diagnostics (#1200) (#1205)
Co-authored-by: mumu <42829555+ZhuLinsen@users.noreply.github.com>
2026-05-06 18:46:35 +08:00
mumu
672ed9b245 refactor: 优化飞书配置和文档 (#832) (#920) 2026-04-03 22:25:50 +08:00
mumu
65fe0446aa feat: 隐藏 LiteLLM 实现细节,简化用户侧模型接入体验 (#875) (#925)
* chore(issue-875): [feature]-隐藏-litellm-实现细节,简化用户侧模型接入体验
2026-04-01 16:02:07 +08:00
mumu
a3a820db83 fix: 为 Discord Webhook 请求补充签名验证 (#896) (#934)
* fix(issue-896): [bug]-为-discord-webhook-请求补充签名验证
2026-04-01 11:43:32 +08:00
mumu
ee64365e99 fix: 在不修改 GitHub Actions workflow 的前提下… (#860) (#931)
* fix(issue-860): [bug]-stock_group-/-email_group-的设置在gith

* fix(review-feedback-931): address latest review comments

* fix(review-feedback-931): address latest review comments

* fix(review-feedback-931): address latest review comments
2026-04-01 11:34:27 +08:00
Alfred
3eabeda06c feat: 支持 SearXNG 公共实例自动发现与受控轮询 (#752) (#769)
* feat: support SearXNG public instance discovery (#752)

* fix: back off cold-start failures for public searxng (#752)
2026-03-19 12:40:21 +08:00
Alfred
216a77f6a2 fix: decouple agent and analysis primary models (#692) (#741)
* fix #692: decouple agent and analysis primary model selection

* fix #692: preserve yaml alias for agent primary model

* docs #692: sync agent primary model in EN/CHT readmes
2026-03-18 19:08:36 +08:00
Xavier Zhou
7b7eff7187 fix: support legacy Discord channel env alias and align docs (#659) 2026-03-15 19:09:14 +08:00
mumu
a97f41637b feat(llm): multi-channel LLM config with WebUI channel editor (#641)
* feat(llm): multi-channel LLM config with WebUI editor, protocol/model normalization, and connectivity test

- Add env-based multi-channel LLM configuration (LLM_CHANNELS, per-channel
  PROTOCOL/BASE_URL/API_KEY/MODELS/ENABLED) with automatic protocol inference
  and model name normalization for LiteLLM Router
- Add full-featured LLM Channel Editor in WebUI with inline channel
  management, model selection, temperature control, and one-click
  connectivity testing per channel
- Add POST /api/v1/system-config/test-llm-channel endpoint for real-time
  LLM channel validation with latency reporting
- Add comprehensive validation for channel definitions, model references,
  and runtime selection in SystemConfigService
- Add unified LLM_TEMPERATURE with backward-compatible fallback to legacy
  provider-specific temperature vars (GEMINI/OPENAI/ANTHROPIC_TEMPERATURE)
- Default localhost channels to openai protocol (not ollama) for
  compatibility with vLLM/LM Studio/LocalAI; Ollama requires explicit
  PROTOCOL=ollama
- Allow direct-env LiteLLM providers (groq/*, bedrock/*, etc.) to bypass
  channel-only model validation on both backend and frontend
- Skip channel validation when LITELLM_CONFIG YAML is the active model source
- Normalize slash-prefixed model IDs (e.g. Qwen/Qwen3-8B on SiliconFlow)
  consistently between frontend and backend
- Prevent WebUI from resetting unsaved channel edits when unrelated AI
  settings in the same category are modified
- Protect against invalid temperature env values with try/except fallback
- Add regression tests for channel parsing, temperature fallback, protocol
  inference, YAML priority, and direct-env provider passthrough

Refs #544, Refs #507

* fix: resolve CI test failures and address PR review feedback

- Fix 3 failing validate_structured tests by aligning litellm_model with model lists
- Add direct-env provider bypass for primary and fallback model validation
- Add ValueError/TypeError -> 422 handling in test-llm-channel endpoint
- Add Space key preventDefault for accessible channel header toggle
- Canonicalize alias prefixes in normalizeModelForRuntime to prevent double-prefixing

* fix(ui): align temperature resolver and API_KEYS priority with backend

- Replace hard-coded temperature fallback chain with resolveTemperatureFromItems()
  that mirrors backend resolve_unified_llm_temperature: reads LLM_TEMPERATURE first,
  then the active model's provider-specific env, then any legacy *_TEMPERATURE
- Swap LLM_{NAME}_API_KEYS / LLM_{NAME}_API_KEY hydration order so multi-key
  configs (rotation/load-balancing) are not silently dropped on save

* fix(security): SSRF guard on base_url + validate parsed API key segments

- Add _is_safe_base_url() to block link-local/cloud metadata addresses
  (169.254.0.0/16, metadata.google.internal, 100.100.100.200) while
  preserving legitimate localhost/LAN endpoints like Ollama
- Apply ssrf_blocked validation error in _validate_llm_channel_definition
- Replace api_key_value truthy-string check with parsed-segment check so
  ',' / ' , ' inputs are caught as missing_api_key instead of silently
  producing zero-key channels at runtime
- Add regression tests: rejects_comma_only_api_key (3 subtests),
  rejects_ssrf_metadata_base_url (3 subtests), allows_localhost_base_url

* fix: direct-env models bypass Router when channel mode is active

In channel/YAML mode, Router.completion() is called for all models but
the Router model_list only contains channel/YAML-declared entries. Direct-
env providers (groq/, bedrock/, etc.) are never added to it, so they caused
'model not found' errors at inference time even though validate_structured()
correctly exempted them from membership checks.

Fix: before dispatching to the Router, check whether the model is actually
in the Router model_list (via get_configured_llm_models). Models absent from
the list fall through to litellm.completion() with env-based credentials,
matching the existing legacy-path behavior. Applied consistently in both
src/analyzer.py and src/agent/llm_adapter.py.

* fix: add missing get_configured_llm_models import in analyzer.py
2026-03-13 22:57:58 +08:00
LouisHong
ade41b613c #602 [PR 2] 配置引擎重构与系统 API (Config & API) (#611)
* feat: complete PR2 config and system API work

* fix(api): align agent model source detection

* fix(agent): collapse legacy fallback deployments

* fix: expose direct env providers in agent model discovery

---------

Co-authored-by: mumu <42829555+ZhuLinsen@users.noreply.github.com>
2026-03-12 20:47:30 +08:00
Alfred
4608f8f3a6 [feat] 增加 SearXNG支持 Issue #550 (#588)
* feat(search): add SearXNG support as quota-free fallback (Fixes #550)

Made-with: Cursor

* fix(pr-588): address Copilot review - test robustness, config urlparse, doc typo

- tests: fix flaky published_date assertion; add sleep patch to 3 retry tests; remove unused SearchResult import
- config: replace double urlparse with single-pass loop for SEARXNG_BASE_URLS
- docs: correct SERPAPI_KEYS to SERPAPI_API_KEYS in zeabur-deployment.md

Made-with: Cursor
2026-03-11 13:36:09 +08:00
Alfred
61ca49ddc5 Gap Remediation:配置校验与 Vision key 检查补全 (#525)
* feat: multi-channel LLM support with visual channel editor

- Add three-tier LLM config: LITELLM_CONFIG (YAML) > LLM_CHANNELS (env) > legacy keys
- Each channel gets independent base_url / api_key / models (no OPENAI_BASE_URL conflict)
- Support DEEPSEEK_API_KEY as standalone provider (auto-infers deepseek-chat model)
- Add LLMChannelEditor component with 9 presets (AIHubmix/DeepSeek/Dashscope/GLM/Moonshot/SiliconFlow/OpenRouter/Gemini/Custom)
- Expand config_registry with ~50 new fields for web settings coverage
- Rewrite .env.example AI section with clear quick-start guide (Scenario A vs B)
- Add litellm_config.example.yaml template
- Add PyYAML dependency for YAML config support
- Full backward compatibility: existing single-key configs work unchanged

* chore: replace placeholder key values with empty defaults in .env.example

* fix: resolve ESLint errors in LLMChannelEditor and HomePage

* refactor: use native litellm deepseek/ provider and extract shared LLM helpers

- Replace openai/deepseek-* + manual api_base with deepseek/ prefix (litellm
  natively resolves DEEPSEEK_API_KEY and base_url)
- Extract duplicated _get_api_keys_for_model and _extra_litellm_params from
  analyzer.py and llm_adapter.py into shared functions in config.py
- Update litellm_config.example.yaml to use deepseek/ prefix
- Thinking mode (deepseek-chat opt-in, deepseek-reasoner auto) unaffected:
  get_thinking_extra_body uses model short name stripped of provider prefix

* fix: gap remediation - config validation, Vision key check, market_analyzer test

- test_env: sync output format with validate_structured (severity-aware ✓/✗/⚠/·)
- config: Vision key warning when VISION_MODEL set but no valid provider API key
  - align key validity with get_api_keys_for_model (len >= 8)
  - check primary model provider + VISION_PROVIDER_PRIORITY
  - add vertex_ai → gemini_api_keys mapping
- tests: TestVisionKeyValidation (short key, primary provider edge cases)
- tests: market_analyzer generate_market_review fallback when generate_text returns None
- docs: CHANGELOG, full-guide Vision config validation note

* fix: address review - generate_text(), market_analyzer, forbidden set

- analyzer: add generate_text() as public API wrapping _call_litellm()
- market_analyzer: call generate_text() instead of _call_litellm directly
- tests: add _call_litellm and _litellm_available to forbidden set
- analyzer: update generate_text() docstring with forbidden attrs

---------

Co-authored-by: zhulinsen <zhuls97@163.com>
Co-authored-by: mumu <42829555+ZhuLinsen@users.noreply.github.com>
2026-03-06 21:51:54 +08:00