Metadata-Version: 2.4
Name: loomkit
Version: 0.0.2
Summary: A tiny, swappable LLM orchestration core.
Author: Devyanshu Jadon
License: MIT
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Provides-Extra: gemini
Requires-Dist: google-genai>=1.0.0; extra == "gemini"
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"

# Loomkit

A tiny, swappable LLM orchestration core. The seam under an agent harness — without LangChain-scale bloat.

v0 ships the smallest useful surface:

- A `Provider` Protocol — anything with the right `generate()` signature is a provider, no inheritance.
- `Message` / `Response` / `Usage` dataclasses.
- One concrete provider: `GeminiProvider` (official `google-genai` SDK).

That's it. Streaming, tool calling, agents, retries, and graphs are deliberately deferred — they layer on through the Provider seam without breaking call sites.

## Install

```bash
pip install loomkit                # core only, zero deps
pip install 'loomkit[gemini]'      # adds google-genai
pip install 'loomkit[gemini,dev]'  # plus pytest
```

Requires Python 3.10+.

## Quickstart

```python
import os
from loomkit import Message
from loomkit.providers.gemini import GeminiProvider

# Reads GEMINI_API_KEY (or GOOGLE_API_KEY) from env; or pass api_key=...
provider = 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)
```

## Why 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.

```python
class MyProvider:
    def generate(self, messages, *, model, temperature=None,
                 max_tokens=None, stop=None) -> Response: ...
```

## What's deferred (and how it lands)

| Feature | How it'll be added |
|---|---|
| Streaming | `generate_stream()` method 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 |

## Development

```bash
pip install -e '.[gemini,dev]'
pytest -q
```

The Gemini smoke test is skipped unless `GEMINI_API_KEY` is set.
