Skip to main content

pi-llm

Unified LLM provider abstraction for Python. Provides streaming and non-streaming interfaces to LLM APIs with automatic model discovery, token and cost tracking, and tool call support.

Features

  • Streaming & non-streaming — stream_simple() / complete_simple() with async iteration
  • Model catalog — get_model() with built-in pricing and metadata for OpenAI models
  • Tool calling — Define tools with JSON Schema, validate arguments automatically
  • Extended thinking — Unified reasoning level interface across providers
  • Cost tracking — Automatic token counting and dollar cost calculation
  • Event-driven — Fine-grained streaming events (text, thinking, tool calls)
  • Cross-provider types — Pydantic models with camelCase/snake_case interop

Supported Providers

  • OpenAI (Responses API)

More providers coming soon.

Installation

pip install pi-llm

Quick Start

import asyncio
from pi_llm import get_model, stream_simple, complete_simple, Context, UserMessage
from pi_llm import register_builtin_providers, SimpleStreamOptions

# Register providers (call once at startup)
register_builtin_providers()

model = get_model("openai", "gpt-4o")

context = Context(
    system_prompt="You are a helpful assistant.",
    messages=[UserMessage(content="What is Python?", timestamp=0)],
)

# Option 1: Non-streaming
async def main():
    message = await complete_simple(
        model, context,
        SimpleStreamOptions(api_key="sk-...")
    )
    for block in message.content:
        if hasattr(block, "text"):
            print(block.text)

asyncio.run(main())
# Option 2: Streaming
async def main_stream():
    event_stream = stream_simple(
        model, context,
        SimpleStreamOptions(api_key="sk-...")
    )
    async for event in event_stream:
        if event.type == "text_delta":
            print(event.delta, end="", flush=True)

asyncio.run(main_stream())

Tools

Define tools with JSON Schema and handle tool calls in a loop:

from pi_llm import Tool, ToolCall, ToolResultMessage, TextContent
import time

weather_tool = Tool(
    name="get_weather",
    description="Get current weather for a city",
    parameters={
        "type": "object",
        "properties": {"city": {"type": "string"}},
        "required": ["city"],
    },
)

context = Context(
    system_prompt="You can check the weather.",
    messages=[UserMessage(content="What's the weather in Tokyo?", timestamp=0)],
    tools=[weather_tool],
)

async def tool_loop():
    while True:
        message = await complete_simple(
            model, context,
            SimpleStreamOptions(api_key="sk-...")
        )
        context.messages.append(message)

        # Check for tool calls
        tool_calls = [c for c in message.content if isinstance(c, ToolCall)]
        if not tool_calls:
            break  # No more tool calls, done

        # Execute tool calls and add results
        for tc in tool_calls:
            result = f"25°C and sunny in {tc.arguments['city']}"
            context.messages.append(ToolResultMessage(
                tool_call_id=tc.id,
                tool_name=tc.name,
                content=[TextContent(text=result)],
                timestamp=int(time.time() * 1000),
            ))

    # Print final response
    for block in message.content:
        if hasattr(block, "text"):
            print(block.text)

Streaming Events

The event stream emits these events in order:

Event Description
StartEvent Streaming begins
TextStartEvent Text block begins
TextDeltaEvent Incremental text chunk
TextEndEvent Text block complete
ThinkingStartEvent Reasoning block begins
ThinkingDeltaEvent Reasoning chunk
ThinkingEndEvent Reasoning block complete
ToolCallStartEvent Tool call begins
ToolCallDeltaEvent Tool call argument chunk
ToolCallEndEvent Tool call complete
DoneEvent Streaming finished successfully
ErrorEvent Streaming ended with error

Extended Thinking

Enable reasoning for supported models:

message = await complete_simple(
    model, context,
    SimpleStreamOptions(
        api_key="sk-...",
        reasoning="medium",  # minimal, low, medium, high, xhigh
    )
)

License

MIT

Metadata

Release files for pi-llm 0.1.4

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pi-llm 0.1.4
File Size Uploaded Provenance Actions
pi_llm-0.1.4.tar.gz 46.4 kB Without provenance Details for pi_llm-0.1.4.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for pi-llm 0.1.4
File Interpreter ABI Platform Actions
pi_llm-0.1.4-py3-none-any.whl Python 3 none any Details for pi_llm-0.1.4-py3-none-any.whl

Total release size: 83.0 kB

Release files / pi_llm-0.1.4.tar.gz

Download URL pi_llm-0.1.4.tar.gz
Size 46.4 kB
Tags Source
SHA-256 checksum
How to use checksums
5bbef3018016196488e4f40cf74c57426c24e5600d23639dae780f49859dc0bd
BLAKE2b-256 checksum
How to use checksums
ce6e8e6f445ce0a96b38533c35f433e7ad0804eb559eafe59fea5e633f9294f7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.13

Release files / pi_llm-0.1.4-py3-none-any.whl

Download URL pi_llm-0.1.4-py3-none-any.whl
Size 36.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e1d7828dd2e446b6ae6f734f91fdfb4322cba9b943f48bc2c6bc2bc25a06904f
BLAKE2b-256 checksum
How to use checksums
475c10bf75aa07a088c9c86c488f3c245de9237fac5bbfb6c278e3edc865f95d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.13

Security for pi-llm 0.1.4

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page