Skip to content

The model_embassy tour

example/model_embassy/ teaches Plugin Kit through a model-routing metaphor. An agent presents a ModelPassport (model family, requirements). The embassy registers one VisaOffice per provider. The first office that can honour the passport issues a ModelVisa carrying a ready-to-use client. Offices that cannot serve the request return null so the next candidate gets a turn.

Terminal window
cd example/model_embassy
dart pub get
dart run bin/01_passports_and_visas.dart

Replace the filename to run any other scenario. Each file is self-contained: every type the scenario uses is either defined inline or imported from the shared lib/.

The smallest version of the metaphor. One agent presents an Anthropic passport, one visa office is registered, the office issues a visa, and the agent uses the returned client.

final passport = ModelPassport(
modelFamily: 'anthropic',
modelId: 'claude-sonnet-4-5-20250929',
);
// `maybeRequest` returns null when every visa office concedes
// (`onRequest` handlers all returned null). The handler registration is
// `<AgentBoardingCall, ModelVisa>`, so on success we get a non-nullable
// `ModelVisa` back without an extra cast.
final visa = await session.maybeRequest<AgentBoardingCall, ModelVisa>(
AgentBoardingCall(passport),
);
if (visa == null) {
print('No visa issued for the passport.');
await runtime.dispose();
return;
}
final response = await visa.client.chat('Hello from the embassy!');

Concepts: Service Registry, Event Bus.

A VisaOffice is a StatefulPluginService. It attaches on session start and listens for AgentBoardingCall requests. Each request: check the passport’s model family against the office’s supported families. Match claims by building a client; no match returns null so the next office can try.

The scenario uses VerboseVisaOffice, which narrates each decision so the claim/concede flow is visible. Main presents one recognised passport and one unknown passport to show both branches.

Concepts: Plugin Services, Event Patterns.

Multiple visa offices, one per model family, each at a different priority. An AgentBoardingCall propagates through the offices in priority order until one claims it. If all concede, the request goes unanswered.

The scenario registers three providers:

  • Anthropic (priority 100): serves the anthropic family
  • OpenAI (priority 80): serves the openai family
  • Ollama (priority 50): serves meta, mistral, and ollama

Concepts: Service Registry, Event Patterns.

Event mutation in action. A router plugin subscribes to PrepareResponseEvent at priority 0 and rewrites event.passport based on prompt length. Later handlers (and the caller that emitted the event) see the mutated passport. The visa office processes the boarding call without knowing routing happened.

Short prompts (under 100 chars) get rewritten to a lightweight Haiku model. Long prompts keep their originally requested Sonnet. This is the pre-commit interception pattern applied to a routing decision.

Concepts: Event Patterns.

ServiceSettings plus ConfigNode inject per-session configuration into a StatefulPluginService. Three sessions share one runtime, and each session gets its own registered visa office instance that reads different api_key, base_url, and default_model values from the settings it was created with.

final settingsWithPriority = RuntimeSettings(
plugins: {const PluginId('formal'): const PluginConfig(enabled: false)},
services: {
Pin('chat', ['agent', 'model']): const ServiceSettings(
config: {'temperature': 0.7},
),
Pin.wildcard(['agent', 'tools']): const ServiceSettings(priority: 200),
},
);

Concepts: Configuration, Settings & Overrides.

The capstone. All three provider plugins register in one runtime. Three agents with distinct capability requirements each present a passport; the priority cascade (Anthropic 100, OpenAI 80, Ollama 50) routes each to the first office that can serve it. The returned visa carries a ready-to-use client.

If you are wiring multi-provider LLM routing in your own app, this scenario is the closest thing the docs have to a working template.

Concepts: every concept page that touches priority, request/response, or settings injection.