From a one-liner to a typed program
A raw client.chat(...) returns a string you have to parse and hope is shaped right.
kaos-llm-core lets you program the model with types instead. You declare a
Signature — typed inputs and outputs — and a Call turns it into a function that
returns a validated, typed object. Structured output, schema, and validation are
handled for you.
Runs offline with a FunctionClient that returns canned JSON, so there’s no key
or cost. Swap in a real client and the same Call works.
Run it
Section titled “Run it”uv run examples/typed-call-offline.pyparties: ['Acme Corp', 'Globex LLC']The code
Section titled “The code”#!/usr/bin/env -S uv run --script# /// script# requires-python = ">=3.13"# dependencies = ["kaos-llm-core>=0.1.12,<0.2", "kaos-llm-client>=0.1.9,<0.2"]# ///"""From a raw prompt to a *typed* LLM call.
`kaos-llm-core` lets you program LLMs with types instead of prompt strings.You declare a `Signature` — typed inputs and outputs — and a `Call` turnsit into a function that returns a validated, typed object. No parsing offree-form text; the framework handles structured output and validation.
Runs offline with a `FunctionClient` fake model (returns canned JSON), sothere's no key or cost. Swap in a real client and the same `Call` works.
Run it:
uv run examples/typed-call-offline.py"""
from __future__ import annotations
import asyncioimport json
from kaos_llm_client.providers.function import FunctionClientfrom kaos_llm_client.types import ContentPart, ProviderResponsefrom kaos_llm_core import Call, InputField, OutputField, Signature
class ExtractParties(Signature): """Extract the parties named in a contract sentence."""
text: str = InputField(description="contract text") parties: list[str] = OutputField(description="names of the parties")
def fake_model(messages: list[dict], profile) -> ProviderResponse: # A real model would read the prompt and generate this; we return the # exact structured JSON we want so the example is deterministic. payload = {"parties": ["Acme Corp", "Globex LLC"]} return ProviderResponse( provider="function", model="function-test", raw={}, parts=[ContentPart(type="text", text=json.dumps(payload))], )
async def main() -> list[str]: call = Call( ExtractParties, model="function-test", client=FunctionClient(function=fake_model), ) result = await call(text="This agreement is between Acme Corp and Globex LLC.") # `result` is a typed object, not a string — result.parties is a list[str]. print(f"parties: {result.parties}") return result.parties
if __name__ == "__main__": parties = asyncio.run(main()) assert parties == ["Acme Corp", "Globex LLC"], partiesWhat to notice
Section titled “What to notice”- A
Signatureis a typed contract.InputField/OutputFielddeclare what goes in and what must come out.parties: list[str]means the result is a real list, validated — not a blob of text. Call(Signature, client=...)compiles the signature into an async callable.result = await call(text=...)returns a typed object;result.partiesis alist[str].- The offline authoring rule: the high-level starter helpers don’t take a
client=, so to run offline you drop down toCall(..., client=FunctionClient(...)). That’s the one fact to remember when writing tested LLM docs. - Programs compose. A
Programchains severalCalls (extract → classify → summarize), each typed. This typed-call is the atom; programs are the molecule.
You can call models with types. Now give them memory and tools.
Your first agent → Wrap typed calls in a stateful agent with memory — the thing KAOS is for.