Files
OpenCode 68a9071cf1 feat: reconcile local master with origin (exa/hot-platform hunters, leaks ledger, latest results)
Local branch had diverged from origin/master (sibling commits on the same base). Rewrote local history linearly on top of origin/master, folding in all local content: exa/hot-platform discovery hunters, leaks ledger, nightly sweep orchestrator, updated .gitignore and skill docs, plus the latest hunt output and vault state. Remote-only files (vault keys, channel scripts) were restored rather than dropped, so the resulting tree is a full union of both sides.
2026-08-07 12:53:47 +08:00

1 line
19 KiB
JSON

{"repo": "chayapats/jarn", "files": {"tests/test_providers_extra.py": {"1ad4cbfb15914a5505cc5e0ebc8d7d27d983f377": {"t": 1786068440.871415, "n": 18709, "h": "68dc9cc0a48c1755", "c": "\"\"\"Extended provider coverage (groq/deepseek/xai openai-compatible; google/mistral).\"\"\"\n\nfrom __future__ import annotations\n\nfrom unittest.mock import patch\n\nimport pytest\n\nfrom jarn.config.schema import Config, ProviderConfig, ProviderType, RoutingConfig\nfrom jarn.providers import ModelFactory\n\n\ndef _factory(ptype, **prov):\n cfg = Config(\n default_profile=\"p\",\n providers={\"p\": ProviderConfig(type=ptype, **prov)},\n routing=RoutingConfig(main=\"p/model\"),\n )\n return ModelFactory(cfg)\n\n\n@pytest.mark.parametrize(\"ptype,base\", [\n (ProviderType.GROQ, \"https://api.groq.com/openai/v1\"),\n (ProviderType.DEEPSEEK, \"https://api.deepseek.com\"),\n (ProviderType.TOGETHER, \"https://api.together.xyz/v1\"),\n (ProviderType.FIREWORKS, \"https://api.fireworks.ai/inference/v1\"),\n (ProviderType.XAI, \"https://api.x.ai/v1\"),\n (ProviderType.OPENAI_COMPATIBLE, \"http://localhost:8000/v1\"),\n])\ndef test_openai_compatible_providers(ptype, base):\n factory = _factory(ptype, api_key=\"k\", base_url=base)\n with patch(\"langchain.chat_models.init_chat_model\") as m:\n m.return_value = object()\n factory.build(\"p/model\")\n assert m.call_args.kwargs[\"model_provider\"] == \"openai\"\n assert m.call_args.kwargs[\"base_url\"] == base\n assert m.call_args.kwargs[\"api_key\"] == \"k\"\n # Ask for streamed usage so cost/token tracking isn't blind on servers (LM\n # Studio, vLLM, …) that omit usage unless stream_options.include_usage is set.\n assert m.call_args.kwargs[\"stream_usage\"] is True\n\n\n@pytest.mark.parametrize(\"ptype,provider_str\", [\n (ProviderType.GOOGLE, \"google_genai\"),\n (ProviderType.MISTRAL, \"mistralai\"),\n])\ndef test_dedicated_providers(ptype, provider_str):\n factory = _factory(ptype, api_key=\"k\")\n with patch(\"langchain.chat_models.init_chat_model\") as m:\n m.return_value = object()\n factory.build(\"p/model\")\n assert m.call_args.kwargs[\"model_provider\"] == provider_str\n # stream_usage is an OpenAI-only kwarg — don't pass it to other model classes.\n assert \"stream_usage\" not in m.call_args.kwargs\n\n\ndef test_all_provider_types_have_defaults():\n from jarn.config.defaults import ALL_PROVIDERS, DEFAULT_MODELS, PROVIDER_ENV_VARS\n\n for p in ALL_PROVIDERS:\n assert p in DEFAULT_MODELS, f\"{p} missing default models\"\n # Every cloud provider should suggest an env var.\n from jarn.config.defaults import CLOUD_PROVIDERS\n for p in CLOUD_PROVIDERS:\n assert p in PROVIDER_ENV_VARS\n\n\n# -- P2.B: suggest_slug did-you-mean ----------------------------------------\n\ndef test_suggest_slug_dot_to_dash_anthropic():\n \"\"\"Dot-form slug on Anthropic provider yields a dash-form suggestion.\"\"\"\n from jarn.config.schema import ProviderType\n from jarn.providers.models import suggest_slug\n\n result = suggest_slug(ProviderType.ANTHROPIC, \"claude-opus-4.8\")\n assert result is not None\n assert \"claude-opus-4-8\" in result\n assert \"dashes\" in result or \"Anthropic\" in result\n\n\ndef test_suggest_slug_dash_to_dot_openrouter():\n \"\"\"Dash-form slug on OpenRouter provider yields a dot-form suggestion.\"\"\"\n from jarn.config.schema import ProviderType\n from jarn.providers.models import suggest_slug\n\n result = suggest_slug(ProviderType.OPENROUTER, \"anthropic/claude-opus-4-8\")\n # The candidate is anthropic/claude-opus-4.8 which matches OR default slugs\n assert result is not None\n assert \"4.8\" in result or \"claude-opus-4.8\" in result\n\n\ndef test_suggest_slug_no_match_returns_none():\n \"\"\"Completely unknown slug returns None (no false positive).\"\"\"\n from jarn.config.schema import ProviderType\n from jarn.providers.models import suggest_slug\n\n result = suggest_slug(ProviderType.ANTHROPIC, \"gpt-5-ultra-xyz-fake\")\n assert result is None\n\n\ndef test_suggest_slug_already_correct_returns_none():\n \"\"\"Correct dash-form slug on Anthropic returns None (no spurious suggestion).\"\"\"\n from jarn.config.schema import ProviderType\n from jarn.providers.models import suggest_slug\n\n # \"claude-opus-4-8\" is correct for Anthropic; swapping to \"claude-opus-4.8\"\n # would be the OR form — but the function should still return something or None\n # without crashing. We verify it does not return the same slug as suggestion.\n result = suggest_slug(ProviderType.ANTHROPIC, \"claude-opus-4-8\")\n # It may or may not suggest the OR form; it must not crash and must not\n # suggest the same slug unchanged.\n if result is not None:\n assert \"claude-opus-4-8\" not in result or \"claude-opus-4.8\" in result\n\n\ndef test_model_resolution_error_includes_suggestion():\n \"\"\"ModelResolutionError message includes did-you-mean when slug is swappable.\"\"\"\n from unittest.mock import patch\n\n from jarn.config.schema import Config, ProviderConfig, ProviderType, RoutingConfig\n from jarn.providers.models import ModelFactory, ModelResolutionError\n\n # Set up an Anthropic provider with a dot-form slug (wrong for Anthropic).\n cfg = Config(\n default_profile=\"anthropic\",\n providers={\"anthropic\": ProviderConfig(type=ProviderType.ANTHROPIC, api_key=\"fake-key\")},\n routing=RoutingConfig(main=\"anthropic/claude-opus-4.8\"),\n )\n factory = ModelFactory(cfg)\n\n def _raise(*a, **kw):\n raise ValueError(\"model not found\")\n\n with patch(\"langchain.chat_models.init_chat_model\", side_effect=_raise), pytest.raises(ModelResolutionError) as exc_info:\n factory.build(\"anthropic/claude-opus-4.8\")\n\n msg = str(exc_info.value)\n assert \"did you mean\" in msg\n assert \"claude-opus-4-8\" in msg\n\n\n# -- list_remote_models (local endpoint discovery; HTTP fully mocked) --------\n\n\nclass _FakeResp:\n def __init__(self, payload, *, status_ok=True):\n self._payload = payload\n self._ok = status_ok\n\n def raise_for_status(self):\n if not self._ok:\n import httpx\n\n raise httpx.HTTPStatusError(\"boom\", request=None, response=None)\n\n def json(self):\n return self._payload\n\n\ndef _capture_get(payload, *, status_ok=True):\n \"\"\"Return (patched_get, urls) — records every URL the discovery probe hits.\"\"\"\n urls: list[str] = []\n\n def _get(url, *args, **kwargs):\n urls.append(url)\n return _FakeResp(payload, status_ok=status_ok)\n\n return _get, urls\n\n\ndef test_list_remote_models_ollama_parses_tags():\n from jarn.providers import list_remote_models\n\n payload = {\"models\": [{\"name\": \"qwen3-coder:30b\"}, {\"name\": \"llama3:8b\"}]}\n get, urls = _capture_get(payload)\n prov = ProviderConfig(type=ProviderType.OLLAMA, base_url=\"http://localhost:11434\")\n with patch(\"httpx.get\", get):\n out = list_remote_models(prov)\n assert out == [\"qwen3-coder:30b\", \"llama3:8b\"]\n assert urls == [\"http://localhost:11434/api/tags\"]\n\n\ndef test_list_remote_models_lmstudio_parses_v1_models():\n from jarn.providers import list_remote_models\n\n payload = {\"data\": [{\"id\": \"qwen2.5-coder\"}, {\"id\": \"phi-4\"}]}\n get, urls = _capture_get(payload)\n # base_url already carries /v1 (normalize_base_url convention).\n prov = ProviderConfig(type=ProviderType.LMSTUDIO, base_url=\"http://localhost:1234/v1\")\n with patch(\"httpx.get\", get):\n out = list_remote_models(prov)\n assert out == [\"qwen2.5-coder\", \"phi-4\"]\n assert urls == [\"http://localhost:1234/v1/models\"]\n\n\ndef test_list_remote_models_openai_compatible_appends_v1_when_bare():\n from jarn.providers import list_remote_models\n\n payload = {\"data\": [{\"id\": \"local-model\"}]}\n get, urls = _capture_get(payload)\n prov = ProviderConfig(type=ProviderType.OPENAI_COMPATIBLE, base_url=\"http://localhost:8000\")\n with patch(\"httpx.get\", get):\n out = list_remote_models(prov)\n assert out == [\"local-model\"]\n assert urls == [\"http://localhost:8000/v1/models\"]\n\n\ndef test_list_remote_models_unreachable_returns_empty():\n \"\"\"A connection error must degrade to [] (manual entry), never raise.\"\"\"\n import httpx\n\n from jarn.providers import list_remote_models\n\n def _boom(*a, **k):\n raise httpx.ConnectError(\"no endpoint\")\n\n prov = ProviderConfig(type=ProviderType.OLLAMA, base_url=\"http://localhost:11434\")\n with patch(\"httpx.get\", _boom):\n assert list_remote_models(prov) == []\n\n\ndef test_slug_hint_is_provider_aware():\n \"\"\"The did-you-mean hint names the right convention per provider and is not\n misleadingly anthropic/openrouter-specific for other providers.\"\"\"\n from jarn.providers.models import _slug_hint\n\n assert \"dashes\" in _slug_hint(ProviderType.ANTHROPIC)\n assert \"dots\" in _slug_hint(ProviderType.OPENROUTER)\n generic = _slug_hint(ProviderType.GOOGLE)\n assert \"OpenRouter uses dots\" not in generic\n assert \"dot-vs-dash\" in generic\n\n\ndef test_list_remote_models_http_error_returns_empty():\n from jarn.providers import list_remote_models\n\n get, _ = _capture_get({\"models\": []}, status_ok=False)\n prov = ProviderConfig(type=ProviderType.OLLAMA, base_url=\"http://localhost:11434\")\n with patch(\"httpx.get\", get):\n assert list_remote_models(prov) == []\n\n\ndef test_list_remote_models_no_base_url_returns_empty():\n from jarn.providers import list_remote_models\n\n # No network call should be made when there's no endpoint to hit.\n def _fail(*a, **k):\n raise AssertionError(\"must not hit the network without a base_url\")\n\n prov = ProviderConfig(type=ProviderType.OLLAMA, base_url=None)\n with patch(\"httpx.get\", _fail):\n assert list_remote_models(prov) == []\n\n\ndef test_list_remote_models_non_local_provider_returns_empty():\n from jarn.providers import list_remote_models\n\n def _fail(*a, **k):\n raise AssertionError(\"must not probe cloud providers\")\n\n prov = ProviderConfig(type=ProviderType.ANTHROPIC, base_url=\"https://api.anthropic.com\")\n with patch(\"httpx.get\", _fail):\n assert list_remote_models(prov) == []\n\n\ndef test_disallowed_provider_extra_rejected_at_load(tmp_path):\n import yaml\n\n from jarn.config import ConfigError\n from jarn.config.loader import load_config\n\n gp = tmp_path / \"g.yaml\"\n gp.write_text(\n yaml.safe_dump(\n {\n \"providers\": {\n \"openai\": {\n \"type\": \"openai\",\n \"api_key\": \"sk-test\",\n \"default_headers\": {\"X-Evil\": \"yes\"},\n }\n }\n }\n ),\n encoding=\"utf-8\",\n )\n with pytest.raises(ConfigError, match=\"unknown extra\"):\n load_config(global_path=gp, project_path=None)\n\n\ndef test_allowed_provider_extra_reaches_constructor():\n factory = _factory(ProviderType.OPENAI, api_key=\"k\", extra={\"timeout\": 42})\n with patch(\"langchain.chat_models.init_chat_model\") as m:\n m.return_value = object()\n factory.build(\"p/model\")\n assert m.call_args.kwargs[\"timeout\"] == 42\n\n\ndef test_provider_headers_reach_constructor():\n factory = _factory(\n ProviderType.OPENAI,\n api_key=\"k\",\n headers={\"Authorization\": \"Bearer secret\"},\n )\n with patch(\"langchain.chat_models.init_chat_model\") as m:\n m.return_value = object()\n factory.build(\"p/model\")\n assert m.call_args.kwargs[\"default_headers\"] == {\"Authorization\": \"Bearer secret\"}\n\n\ndef test_list_remote_models_malformed_payload_returns_empty():\n from jarn.providers import list_remote_models\n\n # Missing keys / wrong shapes must not raise.\n get, _ = _capture_get({\"unexpected\": \"shape\"})\n prov = ProviderConfig(type=ProviderType.LMSTUDIO, base_url=\"http://localhost:1234/v1\")\n with patch(\"httpx.get\", get):\n assert list_remote_models(prov) == []\n\n\ndef test_list_remote_models_passes_auth_headers():\n from jarn.providers import list_remote_models\n\n payload = {\"data\": [{\"id\": \"local-model\"}]}\n captured: list[dict] = []\n\n def _get(url, *args, **kwargs):\n captured.append(kwargs.get(\"headers\") or {})\n return _FakeResp(payload)\n\n prov = ProviderConfig(\n type=ProviderType.OPENAI_COMPATIBLE,\n base_url=\"http://localhost:8000/v1\",\n api_key=\"secret-key\",\n headers={\"X-Custom\": \"yes\"},\n )\n with patch(\"httpx.get\", _get):\n assert list_remote_models(prov) == [\"local-model\"]\n assert captured[0][\"Authorization\"] == \"Bearer secret-key\"\n assert captured[0][\"X-Custom\"] == \"yes\"\n\n\ndef test_cache_invalidation():\n \"\"\"After invalidate_cache, build() re-resolves the provider with fresh config.\"\"\"\n from unittest.mock import MagicMock\n\n from jarn.config.schema import RoutingConfig\n from jarn.providers.models import ModelFactory\n\n cfg = Config(\n default_profile=\"p\",\n providers={\"p\": ProviderConfig(type=ProviderType.OPENAI, api_key=\"old-key\")},\n routing=RoutingConfig(main=\"p/model\"),\n )\n factory = ModelFactory(cfg)\n first = MagicMock(name=\"first\")\n second = MagicMock(name=\"second\")\n\n with patch(\"langchain.chat_models.init_chat_model\", side_effect=[first, second]) as m:\n assert factory.build(\"p/model\") is first\n cfg.providers[\"p\"].api_key = \"new-key\"\n factory.invalidate_cache()\n assert factory.build(\"p/model\") is second\n\n assert m.call_count == 2\n assert m.call_args_list[0].kwargs[\"api_key\"] == \"old-key\"\n assert m.call_args_list[1].kwargs[\"api_key\"] == \"new-key\"\n\n\n# -- remote_context_window (local-model context size for the gauge) ---------\n\n\ndef test_remote_context_window_lmstudio_prefers_loaded():\n \"\"\"LM Studio: query the native /api/v0 API and prefer loaded over max.\"\"\"\n from jarn.providers import remote_context_window\n\n prov = ProviderConfig(type=ProviderType.LMSTUDIO, base_url=\"http://localhost:1234/v1\")\n payload = {\"data\": [\n {\"id\": \"other\", \"loaded_context_length\": 999},\n {\"id\": \"qwen\", \"loaded_context_length\": 8192, \"max_context_length\": 32768},\n ]}\n get, urls = _capture_get(payload)\n with patch(\"httpx.get\", get):\n assert remote_context_window(prov, \"qwen\") == 8192\n assert urls == [\"http://localhost:1234/api/v0/models\"] # native API, not /v1\n\n\ndef test_remote_context_window_lmstudio_falls_back_to_max():\n from jarn.providers import remote_context_window\n\n prov = ProviderConfig(type=ProviderType.LMSTUDIO, base_url=\"http://localhost:1234/v1\")\n get, _ = _capture_get({\"data\": [{\"id\": \"qwen\", \"max_context_length\": 16384}]})\n with patch(\"httpx.get\", get):\n assert remote_context_window(prov, \"qwen\") == 16384\n\n\ndef test_remote_context_window_ollama_reads_model_info():\n from jarn.providers import remote_context_window\n\n prov = ProviderConfig(type=ProviderType.OLLAMA, base_url=\"http://localhost:11434\")\n payload = {\"model_info\": {\"general.architecture\": \"qwen2\", \"qwen2.context_length\": 4096}}\n posts: list[str] = []\n\n def _post(url, *a, **k):\n posts.append(url)\n return _FakeResp(payload)\n\n with patch(\"httpx.post\", _post):\n assert remote_context_window(prov, \"qwen2.5\") == 4096\n assert posts == [\"http://localhost:11434/api/show\"]\n\n\ndef test_remote_context_window_model_not_found_returns_none():\n from jarn.providers import remote_context_window\n\n prov = ProviderConfig(type=ProviderType.LMSTUDIO, base_url=\"http://localhost:1234/v1\")\n get, _ = _capture_get({\"data\": [{\"id\": \"other\", \"loaded_context_length\": 8192}]})\n with patch(\"httpx.get\", get):\n assert remote_context_window(prov, \"qwen\") is None\n\n\ndef test_remote_context_window_unreachable_returns_none():\n \"\"\"A connection error must degrade to None (hide the gauge), never raise.\"\"\"\n import httpx\n\n from jarn.providers import remote_context_window\n\n prov = ProviderConfig(type=ProviderType.LMSTUDIO, base_url=\"http://localhost:1234/v1\")\n\n def _boom(*a, **k):\n raise httpx.ConnectError(\"no endpoint\")\n\n with patch(\"httpx.get\", _boom):\n assert remote_context_window(prov, \"qwen\") is None\n\n\n# ---------------------------------------------------------------------------\n# T-4-8 (fix round 1) — the JARN_DEMO canned model is WIRED into the build path\n# ---------------------------------------------------------------------------\n\n\ndef test_demo_mode_wires_canned_model_into_build(monkeypatch):\n \"\"\"JARN_DEMO=1 makes ``build_main()`` return the canned demo model with NO\n real provider config and NO API key, and it yields a canned response with no\n network. Env unset: ``build_main()`` does NOT return the fake — it raises\n (no model configured), proving the fake is never silently substituted.\n\n This is the wiring proof: the reviewer found the demo functions had zero\n callers in the build path (dead code). This test consumes the wiring.\n \"\"\"\n from langchain_core.messages import HumanMessage\n\n from jarn.providers.models import ModelFactory, ModelResolutionError\n\n # A config with NO providers and NO configured model: a real build must fail.\n cfg = Config(providers={}, routing=RoutingConfig())\n\n # --- env unset: no fake substitution; build_main raises (nothing configured) ---\n monkeypatch.delenv(\"JARN_DEMO\", raising=False)\n with pytest.raises(ModelResolutionError):\n ModelFactory(cfg).build_main()\n\n # --- JARN_DEMO=1: canned model returned despite empty config / no API key ---\n monkeypatch.setenv(\"JARN_DEMO\", \"1\")\n model = ModelFactory(cfg).build_main()\n assert model._llm_type == \"jarn-demo-canned\", (\n \"build_main() must return the canned demo model when JARN_DEMO=1\"\n )\n # It yields a canned response with NO network and NO API key.\n reply = model.invoke([HumanMessage(content=\"add input validation to server.py\")])\n assert reply.content, \"demo model must return non-empty canned content\"\n\n\ndef test_demo_model_bind_tools_is_noop_and_survives(monkeypatch):\n \"\"\"The demo model must tolerate ``bind_tools`` (deepagents/langgraph calls it)\n — a raw GenericFakeChatModel raises NotImplementedError there.\"\"\"\n monkeypatch.setenv(\"JARN_DEMO\", \"1\")\n cfg = Config(providers={}, routing=RoutingConfig())\n model = ModelFactory(cfg).build_main()\n bound = model.bind_tools([]) # must not raise\n assert bound is not None\n\n\ndef test_demo_money_shot_has_edit_tool_call():\n \"\"\"The scripted demo must drive the money-shot DIFF via a write_file/edit_file\n tool call (not just prose), so the recorded GIF shows a real diff.\"\"\"\n from jarn.providers.models import _demo_messages\n\n msgs = _demo_messages()\n tool_calls = [\n tc for m in msgs for tc in (getattr(m, \"tool_calls\", None) or [])\n ]\n assert any(\n tc[\"name\"] in (\"write_file\", \"edit_file\") for tc in tool_calls\n ), \"the demo script must include a write_file/edit_file tool call for the diff step\"\n"}}}}