Metadata-Version: 2.4
Name: powerailabs-core
Version: 0.1.7
Summary: Foundation for the PowerAI Labs stack: shared types, token counting, prices, instrument(), event bus, OTel.
Author: Raghav Mishra
License-Expression: Apache-2.0
Requires-Python: >=3.11
Provides-Extra: anthropic
Requires-Dist: anthropic>=0.30; extra == 'anthropic'
Provides-Extra: openai
Requires-Dist: openai>=1.0; extra == 'openai'
Provides-Extra: otel
Requires-Dist: opentelemetry-api>=1.25; extra == 'otel'
Requires-Dist: opentelemetry-sdk>=1.25; extra == 'otel'
Provides-Extra: tiktoken
Requires-Dist: tiktoken>=0.7; extra == 'tiktoken'
Description-Content-Type: text/markdown

# powerailabs-core

The shared foundation for the PowerAI Labs stack: canonical types, provider-aware token counting,
an offline price table, one `instrument()` interception point, an in-process event bus, and
OpenTelemetry GenAI emitters. Tiny on purpose — it's the blast radius for every other tool.

**One `instrument()` call, every sibling tool observes the stream — no per-call wiring, offline by default.**

![PyPI](https://img.shields.io/pypi/v/powerailabs-core) ![license](https://img.shields.io/badge/license-Apache_2.0-blue) · usually installed transitively · `import powerailabs.core`

```python
from powerailabs.core import tokens, prices, instrument, bus

# Count tokens and price a call — fully offline, no API key, no network:
n = tokens.count([{"role": "user", "content": "Summarize the attached report in 3 bullets."}],
                 model="claude-opus-4-8")
cost = prices.estimate("claude-opus-4-8", input_tokens=n, output_tokens=200)

# Instrument any client once; tools subscribe to the normalized event stream:
@bus.subscribe
def on_call(call):                   # normalized LLMCall with usage + cost
    print(call.provider, call.model, call.cost)

client = instrument(openai_or_anthropic_client)   # idempotent, additive, sync + async
```

**Providers:** OpenAI · Anthropic · AWS Bedrock (`converse`) · Google Gemini (`generate_content`)
· Ollama (`chat`). For managed runtimes that only emit telemetry, `core.otel.ingest(span_attrs)`
turns `gen_ai.*` OTel attributes into the same bus events. Install `powerailabs-core[tiktoken]`
for **exact** OpenAI counts and a close BPE-based estimate for Claude/Gemini (a character heuristic
is the offline fallback; `tokens.method(model)` reports which is active), or `[otel]` to emit
`gen_ai.*` spans. Provider SDKs are always optional extras.

See [`docs/core.md`](https://github.com/PowerAI-Labs/powerailabs/blob/main/docs/core.md) · [CHANGELOG](https://github.com/PowerAI-Labs/powerailabs/blob/main/packages/powerailabs-core/CHANGELOG.md). *Part of the PowerAI Labs stack — [github.com/PowerAI-Labs/powerailabs](https://github.com/PowerAI-Labs/powerailabs).*
