---
title: "LiteAgents SDK: Per-Turn Routing, Setup and Migration"
canonical: https://wavect.io/blog/liteagents-sdk-per-turn-model-routing/
language: en
description: "Set up LiteLLM’s LiteAgents SDK with per-turn model routing. Check package identity, Claude Agent SDK migration, Jev fallback behavior and cost per accepted task."
image: "https://wavect.io/img/blog/headers/header_liteagents-sdk-per-turn-model-routing.png"
---

[**Back**](/blog/overview/)

[![Kevin Riedl](/img/team/kevin.webp)](/team/kevin-riedl/)

[Kevin Riedl](/team/kevin-riedl/) https://linkedin.com/in/wsdt

16 min read · 25 Sep 2026 Last reviewed September 25, 2026

[**Next**](/blog/jev-ai-decision-model-review/)

# LiteAgents SDK: Per-Turn Routing, Setup and Migration

TL;DR

LiteAgents is BerriAI’s provider-independent Python agent SDK with a Claude Agent SDK-style query() interface. A stateful client can keep history while changing models. In the reviewed source, Jev routing is cached per user turn, not reclassified after every tool result. Verify the package identity, classifier endpoint, tool permissions and fallback behavior before using it with a repository. Measure cost per accepted task, not just a cheaper model call.

A login repair is not one uniform task. Diagnosing the cause, drafting a defensive change, checking tests and explaining a pull request require different kinds of work. Paying for the same heavyweight model throughout can be wasteful. Sending everything to the cheapest model can be worse if the repair then needs more retries or human correction.

**LiteAgents SDK makes model selection part of the agent runtime.** This guide focuses on the implementation questions: what “per turn” means, which package to install, how to write a custom router, what Jev fallback actually does, and what changes when migrating from the Claude Agent SDK.

**Source review:** 25 September 2026. Code-specific observations refer to BerriAI/liteagents commit `539a6e2`. The examples are source-checked, not a live-provider benchmark or a claim that Wavect has deployed this SDK for a client.

## What is LiteAgents SDK, and where does LiteLLM fit?

LiteAgents is BerriAI’s Python SDK for running an agent across model providers through a familiar `query()` interface. [LiteLLM’s product page](https://www.litellm.ai/liteagents) presents the central idea: select a model for the work at hand instead of permanently tying the agent to one model.

The [launch announcement](https://www.linkedin.com/posts/reffajnaahsi_your-agent-shouldnt-use-the-same-model-for-activity-7508328914746806272-UeCq) illustrates a login fix with Claude Opus 4.8 for planning, GPT-5.4 mini for implementation and tests, and Claude Sonnet 4.6 for the PR description. Treat those as example roles, not a benchmark-proven ranking or a promise that a single prompt produces that exact sequence.

The [reviewed SDK README](https://github.com/BerriAI/liteagents/blob/539a6e2a9669433ffa9034ae262718e30c4f80b6/README.md) documents fixed models, custom routers, Jev-based tier selection, a stateful client, MCP tool adapters and a separate fusion mode. LiteLLM provides the underlying model-access layer. LiteAgents adds the conversation and tool loop around it.

This is a specific SDK integration guide. For broader infrastructure choices, use our [LLM gateway and router comparison](/blog/llm-gateway-router-comparison-2026/). For the decision model itself, use the [Jev technical review](/blog/jev-ai-decision-model-review/). These are different architectural decisions.

## Does LiteAgents change models after every tool call?

**Not automatically with the reviewed Jev router.** A user turn and a model-call round are different units. The [client implementation](https://github.com/BerriAI/liteagents/blob/539a6e2a9669433ffa9034ae262718e30c4f80b6/src/liteagents/agent.py) increments its turn counter for each `agent.query(prompt)`. That one query can require several model calls and tool results.

The [tool-loop implementation](https://github.com/BerriAI/liteagents/blob/539a6e2a9669433ffa9034ae262718e30c4f80b6/src/liteagents/loop.py) asks the router before each model-call round. However, the [Jev router implementation](https://github.com/BerriAI/liteagents/blob/539a6e2a9669433ffa9034ae262718e30c4f80b6/src/liteagents/routers/jev.py) caches the selected model by `context.turn`. Subsequent rounds in that user turn reuse the choice. A custom router can inspect the current history and behave differently; the built-in Jev path does not reclassify every tool result.

| Boundary | Reviewed behavior | Implementation consequence |
| --- | --- | --- |
| User turn | One call to a stateful client’s query method | Use separate turns for explicit planning, drafting and summarizing stages. |
| Model-call round | A model response, possibly followed by tools and another response | The routing hook runs again, but Jev returns the cached model for that turn. |
| Fusion delegation | A main agent delegates work to a sidekick with separate history | Evaluate this separately from sequential model switching. |

Also distinguish retained context from routing context. The Jev adapter sends the current prompt and tier descriptions to its classifier, not the complete conversation history. A vague follow-up such as “do the next part” therefore gives that classifier less information than a self-contained task description. Create the router per client: its turn-index cache should not be shared blindly across independent sessions.

## How do you install the correct LiteAgents package?

**Verify the repository identity before copying an installation command.** The reviewed [BerriAI package metadata](https://github.com/BerriAI/liteagents/blob/539a6e2a9669433ffa9034ae262718e30c4f80b6/pyproject.toml) declares Python 3.10 or later and version `0.1.0`. On the review date, the [public PyPI entry named liteagents](https://pypi.org/project/liteagents/) instead showed a different project, version `0.0.2`, from January 2025. Matching names do not establish matching software.

For this source review, the unambiguous route is a clean environment and an explicit repository commit. The following shell commands require Git and a POSIX-style shell. Pinning identifies the code being installed; it does not prove that code or its dependencies are secure.

```
python3 -m venv .venv
source .venv/bin/activate
python -m pip install \
  "liteagents @ git+https://github.com/BerriAI/liteagents.git@539a6e2a9669433ffa9034ae262718e30c4f80b6"
python -c "from liteagents import LiteAgentOptions, LiteAgentClient; print('SDK imports OK')"
```

The [pip VCS installation reference](https://pip.pypa.io/en/stable/topics/vcs-support/) documents full-commit direct references. Recheck the vendor’s release channel when adopting the SDK, then lock and review transitive dependencies too. Do not reuse a production environment containing an unrelated package with the same name.

For `ImportError: cannot import name 'LiteAgentOptions'`, check `python -m pip show liteagents`, the interpreter being used, and whether a local `liteagents.py` is shadowing the package. An import error is not evidence that a provider API key is wrong.

## How do you route models without Jev?

**Implement an asynchronous `route(context)` method that returns an approved model identifier.** No classifier service is necessary when the application already knows the workflow stage. This creates a useful baseline before introducing probabilistic routing.

Set `LITEAGENTS_REASONING_MODEL`, `LITEAGENTS_FAST_MODEL` and `LITEAGENTS_BALANCED_MODEL` to provider-prefixed model IDs available to your account. Configure each selected provider’s credentials separately. The labels describe application roles, not guaranteed model capabilities.

This complete Python example keeps one conversation across three explicit turns. It drafts text only: no repository, shell, test runner or GitHub publishing tool is attached. Its timeout and output limits bound individual requests; a production job still needs an overall deadline and spend ceiling.

```
import asyncio
import os
from dataclasses import dataclass

from liteagents import (
    AssistantMessage, LiteAgentClient, LiteAgentOptions,
    TextBlock, TurnContext,
)

def required(name: str) -> str:
    value = os.environ.get(name, "").strip()
    if not value:
        raise RuntimeError(f"Set {name} to an approved provider/model ID")
    return value

@dataclass(frozen=True)
class StageRouter:
    models: tuple[str, ...]

async def route(self, context: TurnContext) -> str:
        index = context.turn - 1
        if not 0 <= index < len(self.models):
            raise RuntimeError("No approved model for this workflow stage")
        return self.models[index]

async def main() -> None:
    router = StageRouter(models=(
        required("LITEAGENTS_REASONING_MODEL"),
        required("LITEAGENTS_FAST_MODEL"),
        required("LITEAGENTS_BALANCED_MODEL"),
    ))
    options = LiteAgentOptions(
        model_router=router,
        system="Draft suggestions only. Never claim a tool or test was run.",
        max_tokens=1200,
        max_turns=4,
        model_kwargs={"timeout": 45, "num_retries": 0},
    )
    prompts = (
        "A login handler calls password.strip() before checking for None. "
        "Explain the failure and propose a defensive fix.",
        "Draft unit-test cases for that fix. Do not execute anything.",
        "Draft a PR description. Separate proposed changes from verified results.",
    )
    async with LiteAgentClient(options=options) as agent:
        for prompt in prompts:
            last = None
            async for event in agent.query(prompt):
                if isinstance(event, AssistantMessage):
                    last = event
                    print(f"model={event.model} stop={event.stop_reason}")
                    for block in event.content:
                        if isinstance(block, TextBlock):
                            print(block.text)
            if last is None or last.stop_reason not in {
                "end_turn", "stop", "stop_sequence",
            }:
                raise RuntimeError("Incomplete stage; do not continue automatically")

if __name__ == "__main__":
    asyncio.run(main())
```

The routing and client APIs follow the [SDK’s custom-router interface](https://github.com/BerriAI/liteagents/blob/539a6e2a9669433ffa9034ae262718e30c4f80b6/README.md). This deterministic example deliberately fails on an unexpected fourth stage instead of silently selecting a model. It also refuses to continue after an incomplete response. Do not parallelize calls on the same stateful client; use separate clients for independent jobs.

## How does Jev auto-routing choose a tier?

Use `JevAgent` for the convenience wrapper, or `JevModelRouter` inside `LiteAgentOptions` when combining routing with other options. Define candidate tiers and a fallback model. In the reviewed adapter, classification uses tier names and descriptions; the request contains no live model prices or measured quality scores. “Best fit” is the aim, not a proof of globally optimal cost.

The following is a configuration reference, not a verified live Jev integration. It explicitly reads `TYPESAFE_API_KEY` so missing configuration fails during setup rather than silently selecting the fallback.

```
import os
from liteagents import JevModelRouter, JevTier, LiteAgentOptions

router = JevModelRouter(
    tiers=(
        JevTier(name="FAST", model=os.environ["LITEAGENTS_FAST_MODEL"],
                description="Bounded edits and test-case drafting"),
        JevTier(name="BALANCED", model=os.environ["LITEAGENTS_BALANCED_MODEL"],
                description="Routine implementation and explanations"),
        JevTier(name="REASONING", model=os.environ["LITEAGENTS_REASONING_MODEL"],
                description="Ambiguous diagnosis and architecture"),
    ),
    fallback_model=os.environ["LITEAGENTS_REASONING_MODEL"],
    api_key=os.environ["TYPESAFE_API_KEY"],
    timeout=5.0,
)
options = LiteAgentOptions(model_router=router, max_tokens=1200, max_turns=4)
```

**Check the endpoint contract before relying on this path.** The [reviewed adapter](https://github.com/BerriAI/liteagents/blob/539a6e2a9669433ffa9034ae262718e30c4f80b6/src/liteagents/routers/jev.py) posts to `/v1/classify` and labels its HTTP contract illustrative/best-effort. [TypeSafe’s public model reference](https://docs.typesafe.ai/models) documents `POST /v1/systemone`. The mismatch needs a live compatibility test or a corrected adapter; it is not proof that an undocumented endpoint cannot exist.

Until that check passes, use the deterministic router above or an application-owned adapter tested against the documented service. Do not invent confidence thresholds for the built-in adapter: it consumes a tier string and does not expose the classifier’s probability distribution.

## Why does LiteAgents always use the fallback model?

**A completed response does not prove that auto-routing worked.** In the reviewed Jev implementation, a missing key, a handled HTTP error, invalid JSON or an unknown tier can select `fallback_model`. That may preserve an answer while eliminating the expected routing savings.

Check credentials, endpoint compatibility, response shape and exact tier names. Use an instrumented adapter to record whether a route was classified, forced by policy or selected after an error. Record the requested route alongside `AssistantMessage.model` and usage. The same model name can result from a legitimate choice or a fallback, so model names alone cannot distinguish the two.

Do not interpret fallback as universal exception handling. For example, the reviewed code expects JSON with a `tier` field; an unexpected top-level shape needs testing too. Nor is classifier fallback the same as provider failover: once a model has been selected, credentials, rate limits and generation can still fail. [LiteLLM documents provider failover separately](https://docs.litellm.ai/docs/proxy/reliability).

A fallback model must stay inside the same approved provider and data boundaries. “Use the safe model” is not an authorization decision and must not bypass a region, tenant or confidentiality restriction.

## Is LiteAgents a drop-in Claude Agent SDK replacement?

**Familiar interface, not automatic runtime parity.** Imports may be easy to change. Tools, permissions, persistence and execution assumptions need their own migration checks. [Anthropic’s Agent SDK overview](https://code.claude.com/docs/en/agent-sdk/overview) describes a Claude Code-based runtime with built-in tools, permissions, sessions and hooks.

| Area | LiteAgents entry point | What to verify |
| --- | --- | --- |
| Query and client | query(), LiteAgentOptions, LiteAgentClient | Prompt handling, events, stop reasons and error propagation in the application. |
| Tools | Explicit Tool instances or MCP adapters | Repository access, command execution and PR creation must actually be connected. |
| Permissions | Application-owned enforcement around tools | Rebuild required approvals and sandbox controls before permitting writes. |
| Conversation state | Stateful in-memory client; initial history can be supplied | Durable storage, tenant isolation, resumption and context limits. |
| Operational routing | Provider models or gateway aliases | Compatibility, credentials, budgets, failover and trace correlation. |

The [reviewed LiteAgents options and client](https://github.com/BerriAI/liteagents/blob/539a6e2a9669433ffa9034ae262718e30c4f80b6/src/liteagents/agent.py) define the local API boundary, while the [MCP documentation](https://github.com/BerriAI/liteagents/blob/539a6e2a9669433ffa9034ae262718e30c4f80b6/README.md) makes the application responsible for transport, authentication and session lifetime. Its adapter is not a permission system. Keep initialized MCP sessions open while the agent uses their tools, and expose only an explicit allowlist.

A LiteLLM gateway can handle deployment selection and reliability below the agent. The [LiteLLM Router documentation](https://docs.litellm.ai/docs/routing) describes that infrastructure layer. Do not treat installing the agent SDK as automatically deploying a gateway or enabling organization-wide spend controls.

## How should “fix the login bug, add tests and open a PR” work?

Make the boundaries explicit. First, diagnose the failing behavior using a reproducible example and read-only repository access. Next, let an implementation stage create a bounded patch on an isolated branch. A separate test tool must run the actual checks and return their exit status and output. A review stage evaluates the diff and the evidence before a publishing tool creates a draft PR.

The routing policy may assign a reasoning model to diagnosis, a smaller model to a narrow edit and a balanced model to the explanation. That assignment is a hypothesis to evaluate, not a universal prescription. An authentication bug can become security-sensitive even when the code change looks small.

**Keep execution evidence outside the model’s prose.** Store the commit identifier, changed files, test command, exit code and resulting PR identifier as tool results. Do not accept “tests passed” from a model that had no test tool. Keep merge and deployment approval separate from permission to draft a PR.

For a real implementation, use fresh workspaces, narrow repository tokens, write-path restrictions and an idempotency key for PR creation. These are recommended application controls, not features demonstrated by the text-only sample above.

## How is fusion different from model routing?

Routing chooses which model handles a call. Fusion lets a main agent delegate a subtask to a sidekick with its own conversation history. The [SDK’s fusion documentation](https://github.com/BerriAI/liteagents/blob/539a6e2a9669433ffa9034ae262718e30c4f80b6/README.md) describes configuring that sidekick through `FusionOptions`. Separate histories can support different work streams; they do not prove a cost reduction for your workload.

Test fusion separately from sequential switching. Count both agents’ input and output, waiting time, duplicated investigation and verification work. Persisted client history is also not a portable provider cache: a new provider can receive the conversation text without inheriting another provider’s cached tokens. Measure actual cache usage instead of assuming a free handoff.

History growth can erase savings from a smaller model. Use task-relevant evidence and explicit summaries, but preserve the information needed to reproduce the failure. Cross-provider routing also expands the list of systems that may receive prompts or code; approve that data flow before enabling a tier.

## Does per-turn routing actually reduce agent cost?

**Only if accepted work becomes cheaper at the required quality and latency.** A lower bill for an individual response is not enough. Include classification, generation, retries, tools, infrastructure and attributable human correction in the evaluation window.

`Cost per accepted task = total attributable cost of all attempts / number of accepted tasks`

| Policy | Tasks attempted | Total cost | Accepted tasks | Cost per accepted task |
| --- | --- | --- | --- | --- |
| Fixed-model baseline | 100 | USD 120 | 80 | USD 1.50 |
| Routed policy | 100 | USD 90 | 50 | USD 1.80 |

In this invented example, spending falls 25%, but cost per accepted task rises 20%. The routed policy also leaves more work unfinished. The right denominator changes the decision.

Compare four candidates on the same held-out tasks: the current fixed model, a cheaper fixed model, a deterministic stage router and the automatic router. Keep tools, acceptance criteria and maximum budgets consistent. Report completion quality, correction time, fallback frequency and p50/p95 end-to-end latency, not only average token cost. Broader accounting belongs in our [agent cost-per-action guide](/blog/ai-agent-cost-per-action-2026/).

## What should a LiteAgents pilot prove before deployment?

Start with reversible work and a small model allowlist. Keep routing in observation mode first: record its proposed choice while the known baseline completes the task. Then test actual routed execution on isolated copies. This recommendation tests routing without turning the initial experiment into permission to modify production.

| Test | Required evidence |
| --- | --- |
| Package and API identity | The expected imports resolve; the approved source and dependency versions are recorded. |
| Routing and fallback | Expected stage choices, classifier failures, malformed responses and unknown tiers are observable. |
| Incomplete execution | Timeouts, truncated responses and max_turns exhaustion cannot be reported as completed work. |
| Permissions and data | Unapproved providers, files, commands and cross-tenant history are blocked independently of the model. |
| Acceptance and rollback | A held-out evaluation meets agreed quality, cost and latency limits; the fixed-model policy remains available. |

Pay particular attention to `max_turns`. In the [reviewed tool loop](https://github.com/BerriAI/liteagents/blob/539a6e2a9669433ffa9034ae262718e30c4f80b6/src/liteagents/loop.py), exhausting the limit during tool use does not create a final answer. Track the last assistant message and treat `stop_reason="tool_use"` as unfinished. A returned stream is not automatically a successful business outcome.

## When is LiteAgents worth evaluating?

LiteAgents is interesting when your application needs provider flexibility and has tasks with genuinely different model requirements. It is less compelling when a single inexpensive model already satisfies the workload, or when migration would remove runtime controls you depend on without a replacement.

The useful progression is modest: verify the package, reproduce fixed-model behavior, introduce explicit stage routing, and add automatic classification only after its contract and economics pass. Do not confuse a compelling routing animation with a production acceptance test.

For implementation support, [Wavect’s AI engineering services](/services/artificial-intelligence/) cover application integration and evaluation. The [Twinsoft AI case study](/case-studies/twinsoft-ai/) provides related delivery context, not a LiteAgents deployment claim. Use the [pre-launch QA checklist](/software-development-guide/software-qa-checklist-before-launch/) to define acceptance gates, or [discuss a routed-agent pilot](/contact/) around one real workflow and its current baseline.

## LiteAgents setup and routing questions

### Can LiteAgents use different models in one conversation?

Yes. LiteAgentClient retains conversation history across query calls, and a router selects the model. In the reviewed Jev adapter, model choice is cached for each user turn. Separate query calls or a deliberately designed custom router are needed for different routing boundaries.

### Does LiteAgents automatically switch models after every tool call?

The routing hook is called for each model-call round, but the reviewed Jev router reuses its cached choice within one user turn. Do not assume a single request will automatically switch between planning, implementation and PR-writing models.

### Why can I not import LiteAgentOptions after installing liteagents?

Check package identity, the active Python environment and local files that may shadow the package. On the review date, the public PyPI name pointed to a different project from BerriAI’s SDK. The guide uses an explicit BerriAI repository commit to remove that ambiguity.

### Can I use LiteAgents without a TypeSafe API key?

Yes. A fixed model or a custom router does not require the Jev classification service. Credentials for the actual model providers are still necessary. The reviewed Jev path uses TYPESAFE_API_KEY; its missing-key fallback can hide that classification was never performed.

### Why is my LiteAgents run always using fallback_model?

Possible causes include a missing key, classifier request failure, invalid response or unknown tier. Check the classifier endpoint and add route-reason telemetry. Seeing a generated answer or a particular model name alone does not prove classification succeeded.

### Is LiteAgents a drop-in replacement for the Claude Agent SDK?

The query interface and message types are familiar, but tools, permission enforcement, state persistence and runtime behavior need separate migration checks. Do not assume that changing imports also reproduces Claude Code’s execution environment.

### Does the Python example actually fix a repository and open a PR?

No. It demonstrates three model-routed drafting turns with shared history. A real coding workflow additionally needs repository, edit, test and publishing tools with independent permissions and verified execution results.

### How should we measure LiteAgents routing savings?

Compare complete accepted tasks under the same acceptance criteria. Include all attempts, classifier requests, generation, tools, retries and correction work. Report cost per accepted task alongside quality, fallback rate and end-to-end latency.

## Final thoughts

Your agent should not use one model out of habit. But replacing that habit with an unverified router is not progress. Keep workflow boundaries explicit, make fallback visible, preserve tool permissions and let accepted-task measurements decide whether routing earns its place.

Models and infrastructure

## Continue through this cluster

Model selection, inference economics, local deployment, compression and serving architecture.

[Start with the cornerstone**Self-Hosting LLMs in the EU: When Open Weights Actually Pay Off**](/blog/self-hosting-llms-eu-cost/)

- [mcp-memory-service: Shared Memory for Claude Code and Cursor](/blog/mcp-memory-service-claude-code-cursor/)
- [Claude Opus 5.5: Best Uses, Prompts and Effort Settings](/blog/claude-opus-5-5-best-use-cases-workflows/)
- [Laya and Jev for Business: 6 Practical Workflows](/blog/laya-jev-business-workflows-roi/)
- [Laya vs Jev: What the Benchmarks Mean for AI Startups](/blog/laya-vs-jev-benchmark-ai-startup-moat/)
- [Jev AI Review: Decision Models for Agent Workflows](/blog/jev-ai-decision-model-review/)

[**Back**](/blog/overview/)

[![Kevin Riedl](/img/team/kevin.webp)](/team/kevin-riedl/)

[Kevin Riedl](/team/kevin-riedl/) https://linkedin.com/in/wsdt

16 min read · 25 Sep 2026 Last reviewed September 25, 2026

[**Next**](/blog/jev-ai-decision-model-review/)

## Structured Data

```json
{
  "@context": "https://schema.org",
  "@graph": [
    {
      "@id": "https://wavect.io/#organization",
      "@type": [
        "Organization",
        "ProfessionalService",
        "LocalBusiness"
      ],
      "employee": [
        {
          "@id": "https://wavect.io/team/kevin-riedl/#person",
          "@type": "Person",
          "jobTitle": "Managing Director",
          "name": "Kevin Riedl",
          "url": "https://wavect.io/team/kevin-riedl/",
          "worksFor": {
            "@id": "https://wavect.io/#organization",
            "@type": [
              "Organization",
              "ProfessionalService",
              "LocalBusiness"
            ]
          }
        },
        {
          "@id": "https://wavect.io/team/christof-jori/#person",
          "@type": "Person",
          "jobTitle": "Managing Director",
          "name": "Christof Jori",
          "url": "https://wavect.io/team/christof-jori/",
          "worksFor": {
            "@id": "https://wavect.io/#organization",
            "@type": [
              "Organization",
              "ProfessionalService",
              "LocalBusiness"
            ]
          }
        }
      ],
      "founder": [
        {
          "@id": "https://wavect.io/team/kevin-riedl/#person",
          "@type": "Person",
          "jobTitle": "Managing Director",
          "name": "Kevin Riedl",
          "url": "https://wavect.io/team/kevin-riedl/",
          "worksFor": {
            "@id": "https://wavect.io/#organization",
            "@type": [
              "Organization",
              "ProfessionalService",
              "LocalBusiness"
            ]
          }
        },
        {
          "@id": "https://wavect.io/team/christof-jori/#person",
          "@type": "Person",
          "jobTitle": "Managing Director",
          "name": "Christof Jori",
          "url": "https://wavect.io/team/christof-jori/",
          "worksFor": {
            "@id": "https://wavect.io/#organization",
            "@type": [
              "Organization",
              "ProfessionalService",
              "LocalBusiness"
            ]
          }
        }
      ],
      "legalRepresentative": [
        {
          "@id": "https://wavect.io/team/kevin-riedl/#person",
          "@type": "Person",
          "jobTitle": "Managing Director",
          "name": "Kevin Riedl",
          "url": "https://wavect.io/team/kevin-riedl/",
          "worksFor": {
            "@id": "https://wavect.io/#organization",
            "@type": [
              "Organization",
              "ProfessionalService",
              "LocalBusiness"
            ]
          }
        },
        {
          "@id": "https://wavect.io/team/christof-jori/#person",
          "@type": "Person",
          "jobTitle": "Managing Director",
          "name": "Christof Jori",
          "url": "https://wavect.io/team/christof-jori/",
          "worksFor": {
            "@id": "https://wavect.io/#organization",
            "@type": [
              "Organization",
              "ProfessionalService",
              "LocalBusiness"
            ]
          }
        }
      ],
      "name": "Wavect GmbH",
      "subjectOf": {
        "@id": "https://wavect.io/verified-claims.json#dataset",
        "@type": "Dataset",
        "creator": {
          "@id": "https://wavect.io/#organization",
          "@type": [
            "Organization",
            "ProfessionalService",
            "LocalBusiness"
          ]
        },
        "description": "A machine-readable registry of quantitative and qualitative claims published by Wavect, with review dates, localized page appearances and public third-party citations where available.",
        "inLanguage": "en",
        "isAccessibleForFree": true,
        "license": "https://creativecommons.org/licenses/by/4.0/",
        "name": "Wavect verified publication claims",
        "url": "https://wavect.io/verified-claims.json"
      },
      "url": "https://wavect.io/"
    },
    {
      "@id": "https://wavect.io/team/kevin-riedl/#person",
      "@type": "Person",
      "jobTitle": "Managing Director",
      "name": "Kevin Riedl",
      "sameAs": [
        "https://www.wikidata.org/wiki/Q139796365",
        "https://www.linkedin.com/in/wsdt",
        "https://github.com/wsdt"
      ],
      "url": "https://wavect.io/team/kevin-riedl/",
      "worksFor": {
        "@id": "https://wavect.io/#organization",
        "@type": [
          "Organization",
          "ProfessionalService",
          "LocalBusiness"
        ]
      }
    },
    {
      "@id": "https://wavect.io/team/christof-jori/#person",
      "@type": "Person",
      "jobTitle": "Managing Director",
      "name": "Christof Jori",
      "sameAs": [
        "https://www.wikidata.org/wiki/Q139796367",
        "https://www.linkedin.com/in/jocr77/",
        "https://github.com/jo-chris"
      ],
      "url": "https://wavect.io/team/christof-jori/",
      "worksFor": {
        "@id": "https://wavect.io/#organization",
        "@type": [
          "Organization",
          "ProfessionalService",
          "LocalBusiness"
        ]
      }
    },
    {
      "@id": "https://wavect.io/#website",
      "@type": "WebSite",
      "inLanguage": [
        "en",
        "de",
        "es",
        "zh"
      ],
      "name": "Wavect",
      "potentialAction": {
        "@type": "SearchAction",
        "query-input": "required name=search_term_string",
        "target": {
          "@type": "EntryPoint",
          "urlTemplate": "https://wavect.io/search/?q={search_term_string}"
        }
      },
      "publisher": {
        "@id": "https://wavect.io/#organization",
        "@type": [
          "Organization",
          "ProfessionalService",
          "LocalBusiness"
        ]
      },
      "url": "https://wavect.io/"
    },
    {
      "@id": "https://wavect.io/blog/liteagents-sdk-per-turn-model-routing/#webpage",
      "@type": "WebPage",
      "dateModified": "2026-09-25",
      "inLanguage": "en",
      "isPartOf": {
        "@id": "https://wavect.io/#website",
        "@type": "WebSite"
      },
      "lastReviewed": "2026-09-25",
      "url": "https://wavect.io/blog/liteagents-sdk-per-turn-model-routing/"
    }
  ]
}
```

```json
{
  "@context": "https://schema.org",
  "@type": "BlogPosting",
  "abstract": "LiteAgents is BerriAI’s provider-independent Python agent SDK with a Claude Agent SDK-style query() interface. A stateful client can keep history while changing models. In the reviewed source, Jev routing is cached per user turn, not reclassified after every tool result. Verify the package identity, classifier endpoint, tool permissions and fallback behavior before using it with a repository. Measure cost per accepted task, not just a cheaper model call.",
  "articleBody": " Blog overview/AI and agents/Models and infrastructure LiteAgents SDK: Per-Turn Routing, Setup and Migration TL;DR LiteAgents is BerriAI’s provider-independent Python agent SDK with a Claude Agent SDK-style query() interface. A stateful client can keep history while changing models. In the reviewed source, Jev routing is cached per user turn, not reclassified after every tool result. Verify the package identity, classifier endpoint, tool permissions and fallback behavior before using it with a repository. Measure cost per accepted task, not just a cheaper model call. A login repair is not one uniform task. Diagnosing the cause, drafting a defensive change, checking tests and explaining a pull request require different kinds of work. Paying for the same heavyweight model throughout can be wasteful. Sending everything to the cheapest model can be worse if the repair then needs more retries or human correction. LiteAgents SDK makes model selection part of the agent runtime. This guide focuses on the implementation questions: what “per turn” means, which package to install, how to write a custom router, what Jev fallback actually does, and what changes when migrating from the Claude Agent SDK. Source review: 25 September 2026. Code-specific observations refer to BerriAI/liteagents commit 539a6e2. The examples are source-checked, not a live-provider benchmark or a claim that Wavect has deployed this SDK for a client. What is LiteAgents SDK, and where does LiteLLM fit? LiteAgents is BerriAI’s Python SDK for running an agent across model providers through a familiar query() interface. LiteLLM’s product page presents the central idea: select a model for the work at hand instead of permanently tying the agent to one model. The launch announcement illustrates a login fix with Claude Opus 4.8 for planning, GPT-5.4 mini for implementation and tests, and Claude Sonnet 4.6 for the PR description. Treat those as example roles, not a benchmark-proven ranking or a promise that a single prompt produces that exact sequence. The reviewed SDK README documents fixed models, custom routers, Jev-based tier selection, a stateful client, MCP tool adapters and a separate fusion mode. LiteLLM provides the underlying model-access layer. LiteAgents adds the conversation and tool loop around it. This is a specific SDK integration guide. For broader infrastructure choices, use our LLM gateway and router comparison. For the decision model itself, use the Jev technical review. These are different architectural decisions. Does LiteAgents change models after every tool call? Not automatically with the reviewed Jev router. A user turn and a model-call round are different units. The client implementation increments its turn counter for each agent.query(prompt). That one query can require several model calls and tool results. The tool-loop implementation asks the router before each model-call round. However, the Jev router implementation caches the selected model by context.turn. Subsequent rounds in that user turn reuse the choice. A custom router can inspect the current history and behave differently; the built-in Jev path does not reclassify every tool result. Three boundaries that should not be confused BoundaryReviewed behaviorImplementation consequence User turnOne call to a stateful client’s query methodUse separate turns for explicit planning, drafting and summarizing stages. Model-call roundA model response, possibly followed by tools and another responseThe routing hook runs again, but Jev returns the cached model for that turn. Fusion delegationA main agent delegates work to a sidekick with separate historyEvaluate this separately from sequential model switching. Also distinguish retained context from routing context. The Jev adapter sends the current prompt and tier descriptions to its classifier, not the complete conversation history. A vague follow-up such as “do the next part” therefore gives that classifier less information than a self-contained task description. Create the router per client: its turn-index cache should not be shared blindly across independent sessions. How do you install the correct LiteAgents package? Verify the repository identity before copying an installation command. The reviewed BerriAI package metadata declares Python 3.10 or later and version 0.1.0. On the review date, the public PyPI entry named liteagents instead showed a different project, version 0.0.2, from January 2025. Matching names do not establish matching software. For this source review, the unambiguous route is a clean environment and an explicit repository commit. The following shell commands require Git and a POSIX-style shell. Pinning identifies the code being installed; it does not prove that code or its dependencies are secure. python3 -m venv .venv source .venv/bin/activate python -m pip install \\ \"liteagents @ git+https://github.com/BerriAI/liteagents.git@539a6e2a9669433ffa9034ae262718e30c4f80b6\" python -c \"from liteagents import",
  "articleSection": "AI Engineering",
  "author": {
    "@id": "https://wavect.io/team/kevin-riedl/#person",
    "@type": "Person",
    "name": "Kevin Riedl",
    "sameAs": [
      "https://www.wikidata.org/wiki/Q139796365",
      "https://www.linkedin.com/in/wsdt",
      "https://github.com/wsdt"
    ],
    "url": "https://wavect.io/team/kevin-riedl/"
  },
  "citation": [
    {
      "@type": "WebPage",
      "name": "LiteLLM’s product page",
      "url": "https://www.litellm.ai/liteagents"
    },
    {
      "@type": "WebPage",
      "name": "launch announcement",
      "url": "https://www.linkedin.com/posts/reffajnaahsi_your-agent-shouldnt-use-the-same-model-for-activity-7508328914746806272-UeCq"
    },
    {
      "@type": "WebPage",
      "name": "reviewed SDK README",
      "url": "https://github.com/BerriAI/liteagents/blob/539a6e2a9669433ffa9034ae262718e30c4f80b6/README.md"
    },
    {
      "@type": "WebPage",
      "name": "client implementation",
      "url": "https://github.com/BerriAI/liteagents/blob/539a6e2a9669433ffa9034ae262718e30c4f80b6/src/liteagents/agent.py"
    },
    {
      "@type": "WebPage",
      "name": "tool-loop implementation",
      "url": "https://github.com/BerriAI/liteagents/blob/539a6e2a9669433ffa9034ae262718e30c4f80b6/src/liteagents/loop.py"
    },
    {
      "@type": "WebPage",
      "name": "Jev router implementation",
      "url": "https://github.com/BerriAI/liteagents/blob/539a6e2a9669433ffa9034ae262718e30c4f80b6/src/liteagents/routers/jev.py"
    },
    {
      "@type": "WebPage",
      "name": "BerriAI package metadata",
      "url": "https://github.com/BerriAI/liteagents/blob/539a6e2a9669433ffa9034ae262718e30c4f80b6/pyproject.toml"
    },
    {
      "@type": "WebPage",
      "name": "public PyPI entry named liteagents",
      "url": "https://pypi.org/project/liteagents/"
    },
    {
      "@type": "WebPage",
      "name": "pip VCS installation reference",
      "url": "https://pip.pypa.io/en/stable/topics/vcs-support/"
    },
    {
      "@type": "WebPage",
      "name": "TypeSafe’s public model reference",
      "url": "https://docs.typesafe.ai/models"
    },
    {
      "@type": "WebPage",
      "name": "LiteLLM documents provider failover separately",
      "url": "https://docs.litellm.ai/docs/proxy/reliability"
    },
    {
      "@type": "WebPage",
      "name": "Anthropic’s Agent SDK overview",
      "url": "https://code.claude.com/docs/en/agent-sdk/overview"
    },
    {
      "@type": "WebPage",
      "name": "LiteLLM Router documentation",
      "url": "https://docs.litellm.ai/docs/routing"
    }
  ],
  "dateModified": "2026-09-25",
  "datePublished": "2026-09-25",
  "description": "LiteAgents is BerriAI’s provider-independent Python agent SDK with a Claude Agent SDK-style query() interface. A stateful client can keep history while changing models. In the reviewed source, Jev routing is cached per user turn, not reclassified after every tool result. Verify the package identity, classifier endpoint, tool permissions and fallback behavior before using it with a repository. Measure cost per accepted task, not just a cheaper model call.",
  "headline": "LiteAgents SDK: Per-Turn Routing, Setup and Migration",
  "image": "https://wavect.io/img/blog/headers/header_liteagents-sdk-per-turn-model-routing.svg",
  "inLanguage": "en",
  "keywords": "LiteAgents SDK, Model routing, AI engineering",
  "mainEntityOfPage": {
    "@id": "https://wavect.io/blog/liteagents-sdk-per-turn-model-routing/",
    "@type": "WebPage"
  },
  "publisher": {
    "@id": "https://wavect.io/#organization",
    "@type": [
      "Organization",
      "ProfessionalService",
      "LocalBusiness"
    ]
  },
  "url": "https://wavect.io/blog/liteagents-sdk-per-turn-model-routing/",
  "wordCount": 3291
}
```

```json
{
  "@context": "https://schema.org",
  "@type": "BreadcrumbList",
  "itemListElement": [
    {
      "@type": "ListItem",
      "item": "https://wavect.io/",
      "name": "Home",
      "position": 1
    },
    {
      "@type": "ListItem",
      "item": "https://wavect.io/blog/overview/",
      "name": "Blog overview",
      "position": 2
    },
    {
      "@type": "ListItem",
      "item": "https://wavect.io/blog/topics/ai-agents/",
      "name": "AI and agents",
      "position": 3
    },
    {
      "@type": "ListItem",
      "item": "https://wavect.io/blog/clusters/models-infrastructure/",
      "name": "Models and infrastructure",
      "position": 4
    },
    {
      "@type": "ListItem",
      "item": "https://wavect.io/blog/liteagents-sdk-per-turn-model-routing/",
      "name": "LiteAgents SDK: Per-Turn Routing, Setup and Migration",
      "position": 5
    }
  ]
}
```

```json
{
  "@context": "https://schema.org",
  "@type": "FAQPage",
  "mainEntity": [
    {
      "@type": "Question",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Yes. LiteAgentClient retains conversation history across query calls, and a router selects the model. In the reviewed Jev adapter, model choice is cached for each user turn. Separate query calls or a deliberately designed custom router are needed for different routing boundaries."
      },
      "name": "Can LiteAgents use different models in one conversation?"
    },
    {
      "@type": "Question",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "The routing hook is called for each model-call round, but the reviewed Jev router reuses its cached choice within one user turn. Do not assume a single request will automatically switch between planning, implementation and PR-writing models."
      },
      "name": "Does LiteAgents automatically switch models after every tool call?"
    },
    {
      "@type": "Question",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Check package identity, the active Python environment and local files that may shadow the package. On the review date, the public PyPI name pointed to a different project from BerriAI’s SDK. The guide uses an explicit BerriAI repository commit to remove that ambiguity."
      },
      "name": "Why can I not import LiteAgentOptions after installing liteagents?"
    },
    {
      "@type": "Question",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Yes. A fixed model or a custom router does not require the Jev classification service. Credentials for the actual model providers are still necessary. The reviewed Jev path uses TYPESAFE_API_KEY; its missing-key fallback can hide that classification was never performed."
      },
      "name": "Can I use LiteAgents without a TypeSafe API key?"
    },
    {
      "@type": "Question",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Possible causes include a missing key, classifier request failure, invalid response or unknown tier. Check the classifier endpoint and add route-reason telemetry. Seeing a generated answer or a particular model name alone does not prove classification succeeded."
      },
      "name": "Why is my LiteAgents run always using fallback_model?"
    },
    {
      "@type": "Question",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "The query interface and message types are familiar, but tools, permission enforcement, state persistence and runtime behavior need separate migration checks. Do not assume that changing imports also reproduces Claude Code’s execution environment."
      },
      "name": "Is LiteAgents a drop-in replacement for the Claude Agent SDK?"
    },
    {
      "@type": "Question",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "No. It demonstrates three model-routed drafting turns with shared history. A real coding workflow additionally needs repository, edit, test and publishing tools with independent permissions and verified execution results."
      },
      "name": "Does the Python example actually fix a repository and open a PR?"
    },
    {
      "@type": "Question",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Compare complete accepted tasks under the same acceptance criteria. Include all attempts, classifier requests, generation, tools, retries and correction work. Report cost per accepted task alongside quality, fallback rate and end-to-end latency."
      },
      "name": "How should we measure LiteAgents routing savings?"
    }
  ]
}
```
