irregular6612's picture
feat(llm): local Ollama provider (ollama_local, no auth) + scenario-neutral agent directive + gpt-oss model toggle
78a6863
Raw
History Blame Contribute Delete
2.73 kB
"""Build an :class:`LLMProvider` from a ``"name:model"`` spec string.
Spec format: ``"<provider>:<model>"`` — e.g. ``"anthropic:claude-haiku-4-5"``,
``"openai:gpt-4o-mini"``, ``"gemini:gemini-2.0-flash"``,
``"ollama:qwen3.5:cloud"``, ``"fake:smoke"``. Only the FIRST ``:`` separates the
provider name from the model id (model ids may themselves contain ``:``).
Real providers are imported LAZILY (only when their spec is requested), so
importing this module — or building a ``FakeProvider`` — never requires the
heavy provider SDKs (openai / anthropic / google-genai / httpx) to be
installed. This keeps the offline test suite dependency-free.
"""
from __future__ import annotations
import importlib
from proteus.providers.base import LLMProvider
from proteus.providers.fake import FakeProvider
# Provider name -> (module path, class name). Imported lazily in make_provider.
_REGISTRY: dict[str, tuple[str, str]] = {
"openai": ("proteus.providers.openai", "OpenAIProvider"),
"anthropic": ("proteus.providers.anthropic_provider", "AnthropicProvider"),
"gemini": ("proteus.providers.gemini", "GeminiProvider"),
"ollama": ("proteus.providers.ollama_cloud", "OllamaCloudProvider"),
"ollama_local": ("proteus.providers.ollama_local", "OllamaLocalProvider"),
}
# The canned action a CLI ``fake:`` provider replays each turn (offline smoke).
_FAKE_DEFAULT_RESPONSE = "ACTION: stay"
def available_providers() -> list[str]:
"""Return the sorted list of provider names accepted by :func:`make_provider`."""
return sorted([*_REGISTRY, "fake"])
def make_provider(spec: str) -> LLMProvider:
"""Build a provider from a ``"<name>:<model>"`` spec.
Args:
spec: ``"<provider>:<model>"``. ``fake:<anything>`` yields an offline
:class:`FakeProvider`. Real providers are imported lazily.
Returns:
A concrete :class:`LLMProvider`.
Raises:
ValueError: If the spec has no ``:`` separator, or names an unknown
provider.
"""
name, sep, model = spec.partition(":")
if not sep:
raise ValueError(
f"Provider spec must be '<name>:<model>', got {spec!r}. "
f"Available providers: {', '.join(available_providers())}."
)
if name == "fake":
return FakeProvider(
responses=[_FAKE_DEFAULT_RESPONSE], model_name=model or "fake",
)
if name not in _REGISTRY:
raise ValueError(
f"Unknown provider {name!r}. "
f"Available providers: {', '.join(available_providers())}."
)
module_path, cls_name = _REGISTRY[name]
provider_cls = getattr(importlib.import_module(module_path), cls_name)
return provider_cls(model=model)