Skip to content

Providers and plugins

APAB extends through Python entry points — install a package that registers the right group and APAB discovers it, no fork needed.

The LLMProvider protocol

A provider is any class with:

@property
def name(self) -> str: ...
def supports_tool_calling(self) -> bool: ...
def supports_streaming(self) -> bool: ...
def chat(self, messages, tools=None, **kwargs) -> dict: ...

chat returns a normalized response:

{"role": "assistant", "content": "...", "tool_calls": [{"name": ..., "arguments": {...}}]}

Five providers ship built-in: ollama (default, fully local), openai, anthropic, gemini, and openai_compatible (vLLM, LM Studio, Together, and any other OpenAI-style endpoint via base_url).

Per-call usage tracking

Each built-in provider exposes last_usage — a ProviderUsage with prompt/completion tokens, latency, and a cost estimate for the most recent call. It is deliberately not part of the protocol, so third-party providers without it keep working; consumers read it with getattr(provider, "last_usage", None). The orchestrator accumulates these into session totals that land in the manifest and on the session span.

Registering a provider

# your package's pyproject.toml
[project.entry-points."apab.llm_providers"]
my_provider = "my_pkg.provider:MyProvider"
# apab.yaml
llm:
  provider: my_provider
  model: whatever-your-provider-expects

The Ollama-specific fallback parser is worth knowing about: some local models emit tool calls as JSON in text instead of structured tool_calls; OllamaProvider detects and recovers those, which is part of why small local models work at all.

Other entry-point groups

Group Purpose Shipped implementations
apab.llm_providers LLM backends ollama, openai, anthropic, gemini, openai_compatible
apab.em_adapters Commercial EM tool import (HFSS/CST/FEKO) none yet — emtool_import_results defines the seam
apab.compute_backends Execution backends local

The ComputeBackend protocol anticipates cloud executors (AWS/GCP) without changing tool code: tools ask the backend to run, and the backend decides where.