| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126 |
- from __future__ import annotations
- import asyncio
- import tempfile
- from dataclasses import asdict
- from pathlib import Path
- import unittest
- from model_selector.cache import SnapshotCache
- from model_selector.providers import ProviderModel, derive_features, provider_model_to_config_entry
- from model_selector.service import ModelDashboardService
- from model_selector.config import OpenClawConfigRepository
- class FakeAdapter:
- def __init__(self, provider_key: str, models: list[dict]) -> None:
- self.provider_key = provider_key
- self.models = models
- async def fetch(self):
- from model_selector.config import provider_label
- from model_selector.providers import ProviderModel
- return [
- ProviderModel(
- provider_key=self.provider_key,
- provider_label=provider_label(self.provider_key),
- model_id=item["id"],
- name=item.get("name", item["id"]),
- source="live",
- reasoning=item.get("reasoning"),
- context_window=item.get("context_window"),
- max_tokens=item.get("max_tokens"),
- pricing_prompt=item.get("pricing_prompt"),
- pricing_completion=item.get("pricing_completion"),
- pricing_request=item.get("pricing_request"),
- input_modalities=tuple(item.get("input_modalities", ("text",))),
- output_modalities=tuple(item.get("output_modalities", ("text",))),
- supported_parameters=tuple(item.get("supported_parameters", ())),
- active=item.get("active"),
- features=tuple(item.get("features", ())),
- raw=item,
- default_parameters=item.get("default_parameters"),
- )
- for item in self.models
- ]
- class ProviderTests(unittest.IsolatedAsyncioTestCase):
- def test_feature_derivation_understands_free_and_structured(self) -> None:
- raw = {
- "id": "openai/gpt-oss-20b",
- "pricing": {"prompt": "0", "completion": "0", "request": "0"},
- "supported_parameters": ["response_format", "tools"],
- "architecture": {"input_modalities": ["text"], "output_modalities": ["text"]},
- }
- features = derive_features("openrouter", raw)
- self.assertIn("free", features)
- self.assertIn("structured output", features)
- self.assertIn("tool use", features)
- def test_openrouter_reasoning_surfaces_in_config_entry(self) -> None:
- model = ProviderModel(
- provider_key="openrouter",
- provider_label="OpenRouter",
- model_id="~anthropic/claude-sonnet-latest",
- name="Claude Sonnet",
- source="live",
- reasoning=True,
- context_window=200000,
- max_tokens=64000,
- pricing_prompt=None,
- pricing_completion=None,
- pricing_request=None,
- input_modalities=("text", "image", "file"),
- output_modalities=("text",),
- supported_parameters=("reasoning", "response_format"),
- active=None,
- features=("reasoning",),
- raw={"id": "~anthropic/claude-sonnet-latest"},
- default_parameters={"reasoning": {"effort": "high"}},
- )
- entry = provider_model_to_config_entry(model)
- self.assertTrue(entry["reasoning"])
- self.assertEqual(entry["thinking"], {"effort": "high"})
- self.assertEqual(entry["input"], ["text", "image"])
- async def test_refresh_writes_snapshot_json(self) -> None:
- with tempfile.TemporaryDirectory() as tmp:
- config_path = Path(tmp) / "openclaw.json"
- cache_dir = Path(tmp) / "cache"
- config_path.write_text(
- """
- {
- "models": {
- "providers": {
- "openai": {"models": [{"id": "gpt-5.4", "name": "GPT-5.4", "reasoning": true, "input": ["text"], "contextWindow": 100, "maxTokens": 10}]},
- "groq-cloud": {"models": [{"id": "openai/gpt-oss-20b", "name": "GPT OSS 20B", "reasoning": true, "input": ["text"], "contextWindow": 100, "maxTokens": 10}]},
- "openrouter": {"models": [{"id": "openai/gpt-oss-120b", "name": "GPT OSS 120B", "reasoning": true, "input": ["text"], "contextWindow": 100, "maxTokens": 10}]}
- }
- },
- "agents": {"defaults": {"models": {}}}
- }
- """.strip(),
- encoding="utf-8",
- )
- repo = OpenClawConfigRepository(config_path)
- service = ModelDashboardService(
- repo,
- SnapshotCache(cache_dir),
- adapters={
- "openai": FakeAdapter("openai", [{"id": "gpt-5.4", "name": "GPT-5.4", "reasoning": True}]),
- "groq-cloud": FakeAdapter("groq-cloud", [{"id": "openai/gpt-oss-20b", "name": "GPT OSS 20B", "reasoning": True}]),
- "openrouter": FakeAdapter("openrouter", [{"id": "openai/gpt-oss-120b", "name": "GPT OSS 120B"}]),
- },
- )
- states = await service.refresh_all()
- self.assertIn("openai", states)
- self.assertTrue((cache_dir / "openai.json").exists())
- self.assertTrue((cache_dir / "groq-cloud.json").exists())
- self.assertTrue((cache_dir / "openrouter.json").exists())
|