Quickstart¶
From pip install to a working result. Every example on this page is executed in CI
against the fake providers, so none of it can quietly rot.
Install¶
pip install anyinfer
The core depends on httpx2 and jsonschema and nothing else. Providers that need more
come as extras; see installation.
The fastest start is anyinfer init: it inspects the machine, reports which providers
are already usable (a running Ollama, a set credential variable), and writes a valid
anyinfer.json plus a runnable starter.py, without ever storing a secret or
installing anything. The
CLI guide covers what it detects and
its flags. The file it writes is the
shared configuration the SDK, CLI, and sidecar all
read.
Your First Call¶
import anyinfer as ai
text = "The 0.1 release adds embeddings, reranking, and an OpenAI-compatible sidecar."
client = ai.Client(
[
ai.ProviderSettings.of("anthropic", api_key="env://ANTHROPIC_API_KEY"),
]
)
result = client.generate(
"Summarize this in one sentence:\n" + text,
target="anthropic:claude-sonnet-4-5",
)
print(result.text)
client.close()
import anyinfer as ai
text = "The 0.1 release adds embeddings, reranking, and an OpenAI-compatible sidecar."
async with ai.AsyncClient(
[
ai.ProviderSettings.of("anthropic", api_key="env://ANTHROPIC_API_KEY"),
]
) as client:
result = await client.generate(
"Summarize this in one sentence:\n" + text,
target="anthropic:claude-sonnet-4-5",
)
print(result.text)
The highlighted line is the only one that changes when you point the same call at a different provider or a local model.
Note the credential: "env://ANTHROPIC_API_KEY" is a reference, safe to keep in a
config file. It is resolved once and registered for redaction, so the key can never
appear in a log line or an error message. See
credentials.
Client owns a background event loop, so use it as a context manager or call
close():
with ai.Client([ai.ProviderSettings.of("ollama")]) as client:
result = client.generate("Why is the sky blue?", target="ollama:qwen3:8b")
Streaming¶
with client.stream(messages, target="ollama:qwen3:8b") as stream:
for event in stream:
if isinstance(event, ai.TextDelta):
print(event.text, end="", flush=True)
final = stream.result
print(f"\n\n{final.usage.output_tokens} tokens in {final.timing.total_ms:.0f} ms")
Using the stream as a context manager matters: leaving the block early cancels the in-flight request instead of letting it run on. See stream typed events.
Aliases: Don't Hardcode Model Names¶
small, medium, and large resolve to a concrete model for whichever provider you
have configured:
client = ai.Client(
[
ai.ProviderSettings.of("ollama"),
ai.ProviderSettings.of("anthropic", api_key="env://ANTHROPIC_API_KEY"),
]
)
result = client.generate(prompt, target="medium") # -> ollama, since it is listed first
The order you configure providers is the preference order. See targets and aliases.
Structured Output¶
Pass a JSON schema and get back a validated Python value:
SUMMARY = {
"type": "object",
"properties": {
"headline": {"type": "string"},
"topics": {"type": "array", "items": {"type": "string"}},
},
"required": ["headline", "topics"],
}
result = client.generate(
article,
target="medium",
schema=SUMMARY,
repair=ai.Repair(max_attempts=1),
)
print(result.structured["headline"])
print(result.structured_mechanism) # "json_schema", "grammar", "json_mode", or "prompt"
AnyInfer uses the strongest mechanism the provider supports, then validates the result
against your schema regardless. repair allows the model to correct itself once if it
gets the shape wrong. See structured output.
Fallback Chains¶
route = ai.Route(
targets=("anthropic:claude-sonnet-4-5", "openai:gpt-5", "ollama:qwen3:8b"),
retry=ai.Retry(max_attempts=3),
)
result = client.generate(prompt, route=route)
print(f"served by {result.target}")
for attempt in result.attempts:
print(f" {attempt.target} -> {attempt.outcome}")
Every result carries its full routing trail, so "why was this slow?" is answerable after the fact. See routing and rate limits.
Beyond Generation¶
The same client embeds and reranks (client.embed(), client.rerank()), typed and
routed like generation, with a
safety rule that keeps
fallback from mixing incompatible vector spaces. And a local model is just another
target: with llama-cpp configured, one generate() call downloads a pinned,
hash-verified model, tunes a server for your hardware, and answers on loopback; see
run a model locally.
Key Takeaways
- One call shape covers every provider; only the
target=string changes. - Credentials are references (
env://…), resolved once and redacted everywhere. - A schema is validated client-side no matter which mechanism the provider offers.
- Every result carries its attempt trail, so routing decisions are inspectable after the fact.