The seam under your agent harness

A tiny, swappable LLM core. A Protocol, a few dataclasses, one optional provider.

pip install loomkit
Quickstart
generate.py
from loomkit import Messagefrom loomkit.providers.gemini import GeminiProvider provider = GeminiProvider()resp = provider.generate(    [Message(role="user", content="Name three primary colors.")],    model="gemini-2.5-flash",)print(resp.text)

Smallest useful surface

v0 ships only what you need to call a model and swap backends later. Everything else is deliberately deferred.

Provider Protocol

Anything with the right generate() signature is a provider. No inheritance, no registration, no base class.

Message, Response, Usage

A handful of dataclasses. The smallest useful surface for chat-style generation.

Gemini out of the box

One concrete provider via the official google-genai SDK. Optional extra. Core stays zero-deps.

Built to layer

Streaming, tools, retries, and agents land through the Provider seam without breaking call sites.

Generate in a few lines

Install the Gemini extra, set GEMINI_API_KEY or GOOGLE_API_KEY, and call generate().

  • Typed against the Provider Protocol, so backends swap freely.
  • Response includes text, finish reason, and usage.
  • Core package stays dependency-free.
quickstart.py
import osfrom loomkit import Messagefrom loomkit.providers.gemini import GeminiProvider  # Reads GEMINI_API_KEY (or GOOGLE_API_KEY) from envprovider = GeminiProvider() resp = provider.generate(    [        Message(role="system", content="You are concise."),        Message(role="user", content="Name three primary colors."),    ],    model="gemini-2.5-flash",    temperature=0.2,) print(resp.text)print(resp.finish_reason, resp.usage)

A Protocol, not a base class

Provider is a typing.Protocol. Adding a new backend means writing one class with a generate() method matching the signature. No imports from loomkit, no inheritance, no registration.

Call sites typed against Provider accept any conforming class.

my_provider.py
class MyProvider:    def generate(self, messages, *, model, temperature=None,                 max_tokens=None, stop=None) -> Response: ...  # No loomkit imports. No inheritance. No registration. # Call sites typed against Provider accept any conforming class.

What lands next

Features layer through the Provider seam without breaking existing call sites. No kitchen-sink framework, just composable surfaces.

  • Streaming

    generate_stream() returning chunk iterables

  • Tool / function calling

    tools= kwarg; Message.content grows to a parts list

  • Async

    Sibling AsyncProvider Protocol with async def generate(...)

  • Retries / rate limiting

    Decorator providers: RetryingProvider(inner, ...)

  • Tracing / observability

    Same shape: TracingProvider(inner, sink)

  • Agent loops

    One layer up: loomkit.agents consumes a Provider

  • Graphs

    Top of the stack; consumes agents

Pick your extras

Core is zero-deps. Gemini and dev tooling are optional extras. Requires Python 3.10+.

  • Core only

    Zero dependencies

    pip install loomkit
  • With Gemini

    Adds google-genai

    pip install 'loomkit[gemini]'
  • Dev setup

    Plus pytest

    pip install 'loomkit[gemini,dev]'