diff --git a/README.md b/README.md index 0a5902a..d912615 100644 --- a/README.md +++ b/README.md @@ -6,10 +6,10 @@ An AI-powered Telegram bot playing "el gordo" — a blunt, politically incorrect ## Features -- **AI chat**: configurable personality with web search, powered by Qwen via OpenRouter +- **AI chat**: configurable personality with web search, powered by DeepSeek via OpenRouter - **Streaming responses**: AI replies stream token-by-token to Telegram (when no tools are active) - **Chat memory with RediSearch**: persistent conversation history, full-text search, automatic compaction -- **Incremental summaries**: `/resumen` streams conversation summaries using Minimax, with automatic context compaction +- **Incremental summaries**: `/resumen` streams conversation summaries using DeepSeek, with automatic context compaction - **Agentic tools**: AI can call tools (price lookup, calculator, web fetch, task scheduling) via function calling - **Market data**: `/prices`, `/usd`, `/petroleo`, `/devo`, `/powerlaw`, `/rainbow`, `/rulo`, `/eleccion` - **BCRA economic data**: `/bcra`, `/variables` @@ -52,10 +52,10 @@ uv run --locked python run_polling.py | Use | Provider | Model | |---|---|---| -| **Chat** | OpenRouter | `qwen/qwen3.6-plus` | +| **Chat** | OpenRouter | `~deepseek/deepseek-v4-flash-latest` | | **Vision** | OpenRouter | `google/gemini-3.1-flash-lite-preview` | | **Transcription** | Groq → OpenRouter fallback | `whisper-large-v3` → `google/gemini-3.1-flash-lite-preview` | -| **Summary** | OpenRouter | `minimax/minimax-m2.7` | +| **Summary** | OpenRouter | `~deepseek/deepseek-v4-flash-latest` | **Streaming**: token streaming only when no tools/web-search are active. Tool-enabled requests return complete responses. diff --git a/api/ai/pricing.py b/api/ai/pricing.py index 41fd68f..736cca6 100644 --- a/api/ai/pricing.py +++ b/api/ai/pricing.py @@ -30,7 +30,7 @@ "input_per_million": 250_000, "output_per_million": 1_500_000, }, - "deepseek/deepseek-v4-flash": { + "~deepseek/deepseek-v4-flash-latest": { "input_per_million": 400_000, "output_per_million": 1_200_000, }, @@ -40,7 +40,7 @@ def chat_output_token_limit(model: str) -> int: """Return a larger budget only for chat models that use hidden reasoning.""" - if str(model or "").split(":", 1)[0] == "deepseek/deepseek-v4-flash": + if str(model or "").split(":", 1)[0] == "~deepseek/deepseek-v4-flash-latest": return REASONING_CHAT_OUTPUT_TOKEN_LIMIT return CHAT_OUTPUT_TOKEN_LIMIT @@ -137,9 +137,9 @@ def estimate_chat_reserve_credits( messages: Sequence[Mapping[str, Any]], max_output_tokens: Optional[int] = None, extra_input_tokens: int = 0, - model: str = "deepseek/deepseek-v4-flash", + model: str = "~deepseek/deepseek-v4-flash-latest", ) -> int: - pricing = MODEL_PRICING_USD_MICROS.get(model, MODEL_PRICING_USD_MICROS["deepseek/deepseek-v4-flash"]) + pricing = MODEL_PRICING_USD_MICROS.get(model, MODEL_PRICING_USD_MICROS["~deepseek/deepseek-v4-flash-latest"]) output_token_limit = ( chat_output_token_limit(model) if max_output_tokens is None diff --git a/api/index.py b/api/index.py index 9b8c0f0..d1fae30 100644 --- a/api/index.py +++ b/api/index.py @@ -325,8 +325,8 @@ def _parse_timeframe( def make_chat_tz(offset: int = -3) -> timezone: return timezone(timedelta(hours=offset)) -PRIMARY_CHAT_MODEL = "deepseek/deepseek-v4-flash" -SUMMARY_MODEL = "deepseek/deepseek-v4-flash" +PRIMARY_CHAT_MODEL = "~deepseek/deepseek-v4-flash-latest" +SUMMARY_MODEL = "~deepseek/deepseek-v4-flash-latest" SUMMARY_MAX_TOKENS = 2048 COMPACTION_THRESHOLD = 40 COMPACTION_KEEP = 25 diff --git a/benchmark_bot.py b/benchmark_bot.py index 6acb530..2b240fb 100644 --- a/benchmark_bot.py +++ b/benchmark_bot.py @@ -35,13 +35,13 @@ def __init__(self, api_key: str): self.models = [ "qwen/qwen3.6-plus", "minimax/minimax-m2.7", - "deepseek/deepseek-v4-flash", + "~deepseek/deepseek-v4-flash-latest", ] self.model_pricing = { "qwen/qwen3.6-plus": {"input": 0.325, "output": 1.95, "context": "1M"}, "minimax/minimax-m2.7": {"input": 0.30, "output": 1.20, "context": "196K"}, - "deepseek/deepseek-v4-flash": {"input": 0.40, "output": 1.20, "context": "2M"}, + "~deepseek/deepseek-v4-flash-latest": {"input": 0.40, "output": 1.20, "context": "1M"}, } def load_bot_config(self) -> Dict[str, Any]: diff --git a/tests/test_admin_reporting.py b/tests/test_admin_reporting.py index cc94533..6af881f 100644 --- a/tests/test_admin_reporting.py +++ b/tests/test_admin_reporting.py @@ -68,7 +68,7 @@ def test_admin_report_formats_additional_context_in_english(): index.app_runtime.admin.report( "OpenRouter unexpected finish_reason=None", extra_context={ - "model": "deepseek/deepseek-v4-flash", + "model": "~deepseek/deepseek-v4-flash-latest", "enable_web_search": True, }, ) @@ -76,7 +76,7 @@ def test_admin_report_formats_additional_context_in_english(): assert send_msg.call_args.args[1] == ( "admin report from VPS: OpenRouter unexpected finish_reason=None" "\n\nadditional context:" - "\nmodel: deepseek/deepseek-v4-flash" + "\nmodel: ~deepseek/deepseek-v4-flash-latest" "\nenable_web_search: True" ) diff --git a/tests/test_ai_billing_internal.py b/tests/test_ai_billing_internal.py index c8ecfc9..c3670b1 100644 --- a/tests/test_ai_billing_internal.py +++ b/tests/test_ai_billing_internal.py @@ -48,11 +48,11 @@ def test_build_insufficient_credits_message_mentions_group_balances(): def test_chat_output_token_limit_is_model_specific(): assert ( - chat_output_token_limit("deepseek/deepseek-v4-flash") + chat_output_token_limit("~deepseek/deepseek-v4-flash-latest") == REASONING_CHAT_OUTPUT_TOKEN_LIMIT == 8192 ) - assert chat_output_token_limit("deepseek/deepseek-v4-flash:exacto") == 8192 + assert chat_output_token_limit("~deepseek/deepseek-v4-flash-latest:exacto") == 8192 assert chat_output_token_limit("other/model") == CHAT_OUTPUT_TOKEN_LIMIT == 1024 @@ -76,7 +76,7 @@ def test_calculate_billing_for_segments_applies_cached_token_discount(): [ { "kind": "chat", - "model": "deepseek/deepseek-v4-flash", + "model": "~deepseek/deepseek-v4-flash-latest", "usage": { "input_tokens": 1_000, "input_cached_tokens": 900, @@ -92,7 +92,7 @@ def test_calculate_billing_for_segments_applies_cached_token_discount(): assert breakdown["charged_credits_display"] == "0.2" assert breakdown["model_breakdown"] == [ { - "model": "deepseek/deepseek-v4-flash", + "model": "~deepseek/deepseek-v4-flash-latest", "usd_micros": 1_000, "input_tokens": 1_000, "input_cached_tokens": 900, @@ -107,12 +107,12 @@ def test_calculate_billing_for_segments_normalizes_billing_model_ids(): [ { "kind": "chat", - "model": "deepseek/deepseek-v4-flash", + "model": "~deepseek/deepseek-v4-flash-latest", "usage": {"input_tokens": 100, "output_tokens": 50}, }, { "kind": "chat", - "model": "deepseek/deepseek-v4-flash", + "model": "~deepseek/deepseek-v4-flash-latest", "usage": {"input_tokens": 100, "output_tokens": 50}, }, { @@ -135,8 +135,8 @@ def test_calculate_billing_for_segments_normalizes_billing_model_ids(): assert breakdown["raw_usd_micros"] > 0 assert [item["model"] for item in breakdown["model_breakdown"]] == [ - "deepseek/deepseek-v4-flash", - "deepseek/deepseek-v4-flash", + "~deepseek/deepseek-v4-flash-latest", + "~deepseek/deepseek-v4-flash-latest", "google/gemini-3.1-flash-lite-preview", "google/gemini-3.1-flash-lite-preview", "groq/whisper-large-v3", @@ -148,7 +148,7 @@ def test_calculate_billing_for_segments_bumps_pricing_version_for_deepseek_searc [ { "kind": "chat", - "model": "deepseek/deepseek-v4-flash", + "model": "~deepseek/deepseek-v4-flash-latest", "usage": {"input_tokens": 100, "output_tokens": 50}, "metadata": {"web_search_requests": 1}, } @@ -163,7 +163,7 @@ def test_calculate_billing_for_segments_reads_cached_tokens_from_prompt_token_de [ { "kind": "chat", - "model": "deepseek/deepseek-v4-flash", + "model": "~deepseek/deepseek-v4-flash-latest", "usage": { "prompt_tokens": 2_000, "completion_tokens": 100, @@ -178,7 +178,7 @@ def test_calculate_billing_for_segments_reads_cached_tokens_from_prompt_token_de assert breakdown["charged_credits_display"] == "0.2" assert breakdown["model_breakdown"] == [ { - "model": "deepseek/deepseek-v4-flash", + "model": "~deepseek/deepseek-v4-flash-latest", "usd_micros": 920, "input_tokens": 2_000, "input_cached_tokens": 1_500, @@ -194,7 +194,7 @@ def test_calculate_billing_for_segments_skips_cached_source_segments(): { "kind": "chat", "source": "cache", - "model": "deepseek/deepseek-v4-flash", + "model": "~deepseek/deepseek-v4-flash-latest", "usage": { "input_tokens": 10_000, "output_tokens": 500, @@ -216,7 +216,7 @@ def test_calculate_billing_for_segments_bills_web_search_requests(): [ { "kind": "chat", - "model": "deepseek/deepseek-v4-flash", + "model": "~deepseek/deepseek-v4-flash-latest", "usage": {"input_tokens": 100, "output_tokens": 50}, "metadata": {"web_search_requests": 2}, } @@ -235,7 +235,7 @@ def test_calculate_billing_for_segments_refunds_cache_only_usage_to_zero(): [ { "kind": "chat", - "model": "deepseek/deepseek-v4-flash", + "model": "~deepseek/deepseek-v4-flash-latest", "source": "cache", "usage": { "input_tokens": 10_000, @@ -288,7 +288,7 @@ def test_settle_reserved_ai_credits_refunds_successful_unused_reserve(): [ { "kind": "chat", - "model": "deepseek/deepseek-v4-flash", + "model": "~deepseek/deepseek-v4-flash-latest", "usage": { "input_tokens": 100, "output_tokens": 50, @@ -318,7 +318,7 @@ def test_settle_reserved_ai_credits_charges_extra_when_actual_exceeds_reserve(): [ { "kind": "chat", - "model": "deepseek/deepseek-v4-flash", + "model": "~deepseek/deepseek-v4-flash-latest", "usage": { "input_tokens": 4000, "output_tokens": 3000, @@ -404,7 +404,7 @@ def test_settle_reserved_ai_credits_records_debt_when_extra_charge_fails(): [ { "kind": "chat", - "model": "deepseek/deepseek-v4-flash", + "model": "~deepseek/deepseek-v4-flash-latest", "usage": { "input_tokens": 4000, "output_tokens": 3000, @@ -447,7 +447,7 @@ def test_settle_reserved_ai_credits_batch_converts_to_credits_once_and_refunds_o [ { "kind": "chat", - "model": "deepseek/deepseek-v4-flash", + "model": "~deepseek/deepseek-v4-flash-latest", "usage": { "input_tokens": 1, "output_tokens": 1, @@ -492,7 +492,7 @@ def test_settle_reserved_ai_credits_batch_mixed_sources_refunds_later_reserves() [ { "kind": "chat", - "model": "deepseek/deepseek-v4-flash", + "model": "~deepseek/deepseek-v4-flash-latest", "usage": { "input_tokens": 100, "output_tokens": 50, @@ -606,7 +606,7 @@ def test_settle_reserved_ai_credits_batch_charges_extra_once_when_total_exceeds_ }, { "kind": "chat", - "model": "deepseek/deepseek-v4-flash", + "model": "~deepseek/deepseek-v4-flash-latest", "usage": { "input_tokens": 4000, "output_tokens": 2000, @@ -635,7 +635,7 @@ def test_settle_reserved_ai_credits_keeps_reserve_when_groq_reports_zero_usage() [ { "kind": "chat", - "model": "deepseek/deepseek-v4-flash", + "model": "~deepseek/deepseek-v4-flash-latest", "usage": { "input_tokens": 0, "output_tokens": 0, @@ -669,7 +669,7 @@ def test_settle_reserved_ai_credits_refunds_cache_only_usage(): [ { "kind": "chat", - "model": "deepseek/deepseek-v4-flash", + "model": "~deepseek/deepseek-v4-flash-latest", "usage": { "prompt_tokens": 1_000, "prompt_tokens_details": {"cached_tokens": 900}, @@ -712,7 +712,7 @@ def test_settle_reserved_ai_credits_batch_keeps_full_reserve_when_total_usage_is [ { "kind": "chat", - "model": "deepseek/deepseek-v4-flash", + "model": "~deepseek/deepseek-v4-flash-latest", "usage": { "input_tokens": 0, "output_tokens": 0, @@ -763,7 +763,7 @@ def test_settle_reserved_ai_credits_batch_ignores_none_segments(): None, { "kind": "chat", - "model": "deepseek/deepseek-v4-flash", + "model": "~deepseek/deepseek-v4-flash-latest", "usage": { "input_tokens": 100, "output_tokens": 50, @@ -839,7 +839,7 @@ def test_settle_reserved_ai_credits_refunds_partial_chat_usage(): segments = [ { "kind": "chat", - "model": "deepseek/deepseek-v4-flash", + "model": "~deepseek/deepseek-v4-flash-latest", "usage": { "input_tokens": 1_000, "output_tokens": 100, @@ -929,7 +929,7 @@ def test_calculate_billing_uses_gateway_cost_when_higher_than_local(): [ { "kind": "chat", - "model": "deepseek/deepseek-v4-flash", + "model": "~deepseek/deepseek-v4-flash-latest", "usage": { "prompt_tokens": 100, "completion_tokens": 50, @@ -951,7 +951,7 @@ def test_calculate_billing_keeps_local_cost_when_higher_than_gateway(): [ { "kind": "chat", - "model": "deepseek/deepseek-v4-flash", + "model": "~deepseek/deepseek-v4-flash-latest", "usage": { "prompt_tokens": 4_000, "completion_tokens": 2_000, @@ -970,7 +970,7 @@ def test_calculate_billing_without_gateway_cost_uses_local(): [ { "kind": "chat", - "model": "deepseek/deepseek-v4-flash", + "model": "~deepseek/deepseek-v4-flash-latest", "usage": { "prompt_tokens": 100, "completion_tokens": 50, diff --git a/tests/test_ai_requests.py b/tests/test_ai_requests.py index e941aa1..ee9eb0c 100644 --- a/tests/test_ai_requests.py +++ b/tests/test_ai_requests.py @@ -118,7 +118,7 @@ def test_log_groq_request_result_logs_local_billing_details(): result = AIUsageResult( kind="chat", text="respuesta", - model="deepseek/deepseek-v4-flash", + model="~deepseek/deepseek-v4-flash-latest", usage={"input_tokens": 100, "output_tokens": 50}, metadata={"groq_account": "primary"}, ) @@ -183,7 +183,7 @@ def fake_attempt(account): return AIUsageResult( kind="chat", text="respuesta chat", - model="deepseek/deepseek-v4-flash", + model="~deepseek/deepseek-v4-flash-latest", metadata={"groq_account": account}, ) @@ -548,7 +548,7 @@ def test_complete_with_providers_openrouter_success(): openrouter_result = AIUsageResult( kind="chat", text="OpenRouter response", - model="deepseek/deepseek-v4-flash", + model="~deepseek/deepseek-v4-flash-latest", usage={"input_tokens": 100, "output_tokens": 50}, metadata={"provider": "openrouter"}, ) @@ -597,7 +597,7 @@ def test_complete_with_providers_records_openrouter_billing_on_success(monkeypat openrouter_result = AIUsageResult( kind="chat", text="OpenRouter response", - model="deepseek/deepseek-v4-flash", + model="~deepseek/deepseek-v4-flash-latest", usage={"input_tokens": 100, "output_tokens": 50}, metadata={"provider": "openrouter"}, ) diff --git a/tests/test_message_ai_media.py b/tests/test_message_ai_media.py index ce54477..35a0e90 100644 --- a/tests/test_message_ai_media.py +++ b/tests/test_message_ai_media.py @@ -105,7 +105,7 @@ def test_handle_msg_image_conversation_with_two_provider_requests_reserves_base_ }, { "kind": "chat", - "model": "deepseek/deepseek-v4-flash", + "model": "~deepseek/deepseek-v4-flash-latest", "usage": { "input_tokens": 1, "input_non_cached_tokens": 1, @@ -114,7 +114,7 @@ def test_handle_msg_image_conversation_with_two_provider_requests_reserves_base_ }, { "kind": "chat", - "model": "deepseek/deepseek-v4-flash", + "model": "~deepseek/deepseek-v4-flash-latest", "usage": { "input_tokens": 1, "input_non_cached_tokens": 1, @@ -175,7 +175,7 @@ def test_handle_msg_image_conversation_settles_in_single_batch(monkeypatch): }, { "kind": "chat", - "model": "deepseek/deepseek-v4-flash", + "model": "~deepseek/deepseek-v4-flash-latest", "usage": {"input_tokens": 1, "output_tokens": 1}, }, ], @@ -416,7 +416,7 @@ def test_handle_msg_ai_flow_settles_with_single_base_reserve_when_usage_is_tiny( billing_segments=[ { "kind": "chat", - "model": "deepseek/deepseek-v4-flash", + "model": "~deepseek/deepseek-v4-flash-latest", "usage": { "input_tokens": 1, "input_non_cached_tokens": 1, @@ -425,7 +425,7 @@ def test_handle_msg_ai_flow_settles_with_single_base_reserve_when_usage_is_tiny( }, { "kind": "chat", - "model": "deepseek/deepseek-v4-flash", + "model": "~deepseek/deepseek-v4-flash-latest", "usage": { "input_tokens": 1, "input_non_cached_tokens": 1, @@ -524,7 +524,7 @@ def test_handle_msg_ai_flow_keeps_single_reserve_for_three_tiny_segments(monkeyp billing_segments=[ { "kind": "chat", - "model": "deepseek/deepseek-v4-flash", + "model": "~deepseek/deepseek-v4-flash-latest", "usage": { "input_tokens": 1, "input_non_cached_tokens": 1, @@ -533,7 +533,7 @@ def test_handle_msg_ai_flow_keeps_single_reserve_for_three_tiny_segments(monkeyp }, { "kind": "chat", - "model": "deepseek/deepseek-v4-flash", + "model": "~deepseek/deepseek-v4-flash-latest", "usage": { "input_tokens": 1, "input_non_cached_tokens": 1, @@ -542,7 +542,7 @@ def test_handle_msg_ai_flow_keeps_single_reserve_for_three_tiny_segments(monkeyp }, { "kind": "chat", - "model": "deepseek/deepseek-v4-flash", + "model": "~deepseek/deepseek-v4-flash-latest", "usage": { "input_tokens": 1, "input_non_cached_tokens": 1, diff --git a/tests/test_message_billing.py b/tests/test_message_billing.py index 6ff8cc6..1163abc 100644 --- a/tests/test_message_billing.py +++ b/tests/test_message_billing.py @@ -449,14 +449,14 @@ def test_handle_msg_creditlog_admin_shows_recent_settlements(monkeypatch): "raw_usd_micros": 390, "model_breakdown": [ { - "model": "deepseek/deepseek-v4-flash", + "model": "~deepseek/deepseek-v4-flash-latest", "usd_micros": 325, "input_tokens": 1000, "input_cached_tokens": 800, "input_non_cached_tokens": 200, }, { - "model": "deepseek/deepseek-v4-flash", + "model": "~deepseek/deepseek-v4-flash-latest", "usd_micros": 65, "input_tokens": 200, "input_cached_tokens": 100, @@ -499,7 +499,7 @@ def test_handle_msg_creditlog_admin_shows_recent_settlements(monkeypatch): assert "requests: chat=3" in sent_text assert "cache_hits: chat=1" in sent_text assert "cacheados=900 ahorro_cache=0" in sent_text - assert "deepseek/deepseek-v4-flash=390" in sent_text + assert "~deepseek/deepseek-v4-flash-latest=390" in sent_text assert "web_search=8000 (2x)" in sent_text assert "python=500 (1x)" in sent_text diff --git a/tests/test_message_transcription.py b/tests/test_message_transcription.py index ea44d73..1531875 100644 --- a/tests/test_message_transcription.py +++ b/tests/test_message_transcription.py @@ -626,7 +626,7 @@ def test_handle_msg_auto_audio_plus_ai_response_charges_three_requests(monkeypat billing_segments=[ { "kind": "chat", - "model": "deepseek/deepseek-v4-flash", + "model": "~deepseek/deepseek-v4-flash-latest", "usage": { "input_tokens": 1, "input_non_cached_tokens": 1, diff --git a/tests/test_provider_runtime.py b/tests/test_provider_runtime.py index 0ee0dfb..8098802 100644 --- a/tests/test_provider_runtime.py +++ b/tests/test_provider_runtime.py @@ -764,7 +764,7 @@ def _build_retry_runtime(responses, *, extract_usage=lambda _response: {}): metadata=kwargs.get("metadata") or {}, ), extract_usage_map=extract_usage, - primary_model="deepseek/deepseek-v4-flash", + primary_model="~deepseek/deepseek-v4-flash-latest", max_tool_rounds=5, ), ToolRuntime(), @@ -825,7 +825,7 @@ def test_provider_runtime_retries_invalid_finish_reason_then_returns_result( assert result.text == "done" assert len(client.calls) == 2 assert all( - call["max_tokens"] == chat_output_token_limit("deepseek/deepseek-v4-flash") + call["max_tokens"] == chat_output_token_limit("~deepseek/deepseek-v4-flash-latest") for call in client.calls ) assert request_count.call_count == 2 @@ -908,7 +908,7 @@ def test_provider_runtime_reports_null_finish_reason_after_retries_exhausted(): assert admin_report.call_args.args[0] == "OpenRouter unexpected finish_reason=None" report_context = admin_report.call_args.kwargs["extra_context"] assert report_context == { - "model": "deepseek/deepseek-v4-flash", + "model": "~deepseek/deepseek-v4-flash-latest", "enable_web_search": True, "tool_round": 1, "response_id": "gen-4", diff --git a/tests/test_world_cup_goals.py b/tests/test_world_cup_goals.py index 95b7da5..86d5f5c 100644 --- a/tests/test_world_cup_goals.py +++ b/tests/test_world_cup_goals.py @@ -367,7 +367,7 @@ def ask_ai(_messages, *, response_meta, **_kwargs): response_meta["billing_segments"] = [ { "kind": "chat", - "model": "deepseek/deepseek-v4-flash", + "model": "~deepseek/deepseek-v4-flash-latest", "usage": {"prompt_tokens": 100, "completion_tokens": 50}, } ]