Replace the Google Gemini lock-in with a unified chat client usable with any
OpenAI-compatible /v1 endpoint (OpenAI, OpenRouter, DeepSeek, Moonshot, Qwen,
Ollama, Gemini-via-compat...). Switch provider purely via AI_BASE_URL/AI_MODEL.
- ai/llm_client.py (new): lazy openai SDK, per-candidate 429/503/timeout
failover across AI_MODEL_FAILOVER, robust JSON extraction, reasoning-budget
retry, image analysis via base64 data URI, singleton get_llm_client()
- settings.py: GeminiSettings -> LLMSettings (api_key/base_url/model/failover/
vision_model), keeps GEMINI_API_KEY fallback so existing .env keeps working
- news_analyzer.py: analyze_with_gemini -> analyze_with_llm (same output keys);
FinBERT now loads in a daemon warm-up thread with lexicon fallback - the
transformers first import was blocking the asyncio main loop 15-45s
(importlib.metadata full package scan); HF_ENDPOINT hf-mirror setdefault
- telegram_bot.py: news digest + AI-vision chart analysis via the LLM client
- requirements: google-genai -> openai>=1.0; docs/.env.example/README updated
for the new provider-agnostic setup; dashboard meta updated
Co-Authored-By: Claude Code <noreply@anthropic.com>