Skip to main content

NaggyAI Python SDK — instrument LLM calls and query the NaggyAI platform

Project description

naggy

Python SDK for the NaggyAI platform.
Track LLM calls, query costs, get nags, and surface insights — from any Python app.


Install

pip install naggy                        # PyPI (coming soon)
pip install -e ~/Development/naggy-sdk  # local

Requires Python ≥ 3.9. Dependencies: httpx>=0.27, pydantic>=2.


Quick start

import naggy

naggy.init("nag_sk_...")   # once at startup

Auto-instrument Anthropic

import anthropic, naggy

naggy.init("nag_sk_...")
client = naggy.instrument_anthropic(anthropic.Anthropic())

# Every client.messages.create() now sends a telemetry event automatically
response = client.messages.create(
    model="claude-opus-4-7",
    max_tokens=1024,
    messages=[{"role": "user", "content": "Hello"}],
)

Auto-instrument OpenAI

import openai, naggy

naggy.init("nag_sk_...")
client = naggy.instrument_openai(openai.OpenAI())

response = client.chat.completions.create(
    model="gpt-4o",
    messages=[{"role": "user", "content": "Hello"}],
)

Pass environment="staging" to either instrument call to tag events accordingly.

@trace decorator

Wraps a function and sends one event per call. Tokens are auto-extracted from Anthropic/OpenAI response usage objects.

@naggy.trace("anthropic", "claude-opus-4-7")
def summarize(text: str) -> str:
    return client.messages.create(...).content[0].text

Optional kwargs: client= (override default client), environment="production".

trace_context — fine-grained control

with naggy.trace_context("anthropic", "claude-opus-4-7") as ctx:
    response = client.messages.create(...)
    ctx.input_tokens = response.usage.input_tokens
    ctx.output_tokens = response.usage.output_tokens
    ctx.cost_usd = 0.0042

TraceContext attributes you can set inside the block:

Attribute Type Default
input_tokens int 0
output_tokens int 0
cost_usd float 0.0
status str "success"

ctx.trace_id (str) is auto-generated — read it to link a feedback event to this trace.

Full signature:

trace_context(
    provider: str,
    model: str,
    *,
    client=None,             # override default NaggyClient
    environment="production",
    trace_id=None,           # supply your own UUID or let the SDK generate one
)

Auth

Every API call requires a nag_sk_... bearer key.
Create one in the Parallaxed dashboard or via the API:

client = naggy.get_client()
key = client.create_key(project_id="proj_...")
print(key["api_key"])   # nag_sk_...

API reference

Module-level helpers

naggy.init(api_key, *, base_url="https://parallaxed.app", timeout=15.0) -> NaggyClient
naggy.get_client() -> NaggyClient   # raises RuntimeError if init() not yet called

You can also instantiate directly:

from naggy import NaggyClient
client = NaggyClient("nag_sk_...", base_url="https://parallaxed.app", timeout=15.0)

Events

client.ingest(events: list[dict]) -> dict
# returns {"accepted": N}

client.list_events(
    *,
    provider: str | None = None,
    model: str | None = None,
    status: str | None = None,   # "success" | "error"
    limit: int = 50,
    offset: int = 0,
) -> list[dict]

Overview & config

client.overview() -> dict
client.config()   -> dict

Stats

client.cost_by_provider() -> list[dict]   # [{provider, calls, total_cost_usd, ...}]
client.cost_by_model()    -> list[dict]
client.error_stats()      -> dict         # {total_calls, total_errors, error_rate}

Naggy Score

client.score()                     -> dict         # {score, grade, ...}
client.score_history(*, limit=30)  -> list[dict]

Insights

client.list_insights(*, status="active", limit=50, offset=0)  -> list[dict]
client.generate_insights()                                     -> list[dict]
client.dismiss_insight(insight_id: str)                        -> dict
client.apply_insight(insight_id: str)                          -> dict

Alerts

client.list_alert_rules(*, limit=50, offset=0)  -> list[dict]
client.evaluate_alerts()                         -> list[dict]   # triggered rules
client.alert_history(*, limit=50)                -> list[dict]
client.intelligence()                            -> dict

Nags

client.nag_history(*, status="", limit=50, offset=0)  -> list[dict]
client.acknowledge_nag(nag_item_id: str)               -> dict

Traces

client.list_traces(*, limit=50, offset=0)  -> list[dict]
client.get_trace(trace_id: str)            -> dict

Feedback

client.submit_feedback(
    trace_id: str,
    *,
    score: float,              # 0.0–1.0
    label: str = "",           # e.g. "thumbs_up" | "thumbs_down"
    comment: str = "",
    environment: str = "production",
) -> dict

client.list_feedback(
    *,
    trace_id: str | None = None,
    limit: int = 50,
    offset: int = 0,
) -> list[dict]

Daily standup

client.standup()                     -> dict
client.standup_history(*, limit=30)  -> list[dict]

API key management

client.create_key(project_id: str, *, name: str = "default") -> dict   # {api_key, key_id}
client.list_keys(project_id: str)                             -> list[dict]
client.revoke_key(key_id: str)                                -> dict

Health

client.health() -> dict   # {status, active_projects, last_event_at} — no auth required

Event builders

Use these to construct event payloads for client.ingest().

from naggy import llm_event, feedback_event

event = llm_event(
    provider="anthropic",
    model="claude-opus-4-7",
    input_tokens=512,
    output_tokens=128,
    cost_usd=0.0063,
    latency_ms=820,
    status="success",                    # or "error"
    trace_id="...",                      # auto-generated UUID if omitted
    environment="production",
    metadata={"user_id": "u_123"},       # merged into the event dict
)

fb = feedback_event(
    trace_id="...",
    score=1.0,
    label="thumbs_up",
    comment="Great answer",
    environment="production",
)

client.ingest([event, fb])

Error handling

from naggy import NaggyHTTPError

try:
    client.score()
except NaggyHTTPError as e:
    print(e.status_code, e.detail)

Telemetry failures inside @trace, trace_context, and auto-instrumentation are silently swallowed — the SDK never raises inside your application code.

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

naggy-0.3.0.tar.gz (10.1 kB view details)

Uploaded Source

Built Distribution

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

naggy-0.3.0-py3-none-any.whl (12.2 kB view details)

Uploaded Python 3

File details

Details for the file naggy-0.3.0.tar.gz.

File metadata

  • Download URL: naggy-0.3.0.tar.gz
  • Upload date:
  • Size: 10.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.6

File hashes

Hashes for naggy-0.3.0.tar.gz
Algorithm Hash digest
SHA256 2d4d6125e41eff272edf9f04401cb322cce01df343003f8ef9884029a7474d84
MD5 6a83562bcdc9ba5ebc40ac8d0ff4bf15
BLAKE2b-256 0255c85d4050c8c6d40aadbe78e685a20a45be77f1f54bb43668e88cb8825a7d

See more details on using hashes here.

File details

Details for the file naggy-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: naggy-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 12.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.6

File hashes

Hashes for naggy-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 7f472481bad07bbabe855a7841a9c6ef420427080a29fb1cd1234ac196823a3b
MD5 994d9452dd49b2586abfbf92284f8197
BLAKE2b-256 8ffe638e48671b32a6321b9cf665945c1903aa9b6dc2c93a1be34e3de5e830a5

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