Skip to main content

Unified LLM provider abstraction for Python — streaming, tool calling, extended thinking, cost tracking

Project description

pi-ai

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-streamingstream_simple() / complete_simple() with async iteration
  • Model catalogget_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-ai

Quick Start

import asyncio
from pi_ai import get_model, stream_simple, complete_simple, Context, UserMessage
from pi_ai 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_ai 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

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pi_llm-0.1.1.tar.gz (46.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pi_llm-0.1.1-py3-none-any.whl (36.6 kB view details)

Uploaded Python 3

File details

Details for the file pi_llm-0.1.1.tar.gz.

File metadata

  • Download URL: pi_llm-0.1.1.tar.gz
  • Upload date:
  • Size: 46.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.14

File hashes

Hashes for pi_llm-0.1.1.tar.gz
Algorithm Hash digest
SHA256 54120be7f95e0acb918dcafb3c502fd9be8dce390cb29f6193a213c2b0186fde
MD5 47cb70e63c36578dd33591e4df3422e2
BLAKE2b-256 e9a7829dd0ad4c0ee58a12a2b41dd1fe2809f1cd763054284fa8fcbc74371586

See more details on using hashes here.

File details

Details for the file pi_llm-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: pi_llm-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 36.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.14

File hashes

Hashes for pi_llm-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 0d63e8efb752c1f14b7557d13cbf2a7e32c06b3ff0bfd5d7eceeb5bbbc0c6e73
MD5 b8979ed768b5a960edb2a202cb79d61c
BLAKE2b-256 bdef467bf57273aa83ff51170e1917e59b373d4d14ffc596838e52a0814befcb

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page