Metadata-Version: 2.4
Name: lanternfish
Version: 0.4.0
Summary: Lanternfish SDK: trace AI agent runs, decisions and releases with OpenTelemetry.
License-Expression: MIT
Project-URL: Documentation, https://github.com/TriTetra/agentlens-sdk/tree/master/lanternfish#readme
Project-URL: Source, https://github.com/TriTetra/agentlens-sdk
Keywords: ai-agents,observability,opentelemetry,llm,tracing
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Operating System :: OS Independent
Classifier: Typing :: Typed
Requires-Python: >=3.11
Description-Content-Type: text/markdown
Requires-Dist: opentelemetry-api<2,>=1.25.0
Requires-Dist: opentelemetry-sdk<2,>=1.25.0
Requires-Dist: requests<3,>=2.31.0
Provides-Extra: openai
Requires-Dist: opentelemetry-instrumentation-openai<1,>=0.40.0; extra == "openai"
Provides-Extra: anthropic
Requires-Dist: opentelemetry-instrumentation-anthropic<1,>=0.40.0; extra == "anthropic"
Provides-Extra: gemini
Requires-Dist: opentelemetry-instrumentation-google-generativeai<1,>=0.40.0; extra == "gemini"
Provides-Extra: groq
Requires-Dist: opentelemetry-instrumentation-groq<1,>=0.40.0; extra == "groq"
Provides-Extra: langchain
Requires-Dist: opentelemetry-instrumentation-langchain<1,>=0.40.0; extra == "langchain"
Provides-Extra: crewai
Requires-Dist: opentelemetry-instrumentation-crewai<1,>=0.40.0; extra == "crewai"
Provides-Extra: llamaindex
Requires-Dist: opentelemetry-instrumentation-llamaindex<1,>=0.40.0; extra == "llamaindex"
Provides-Extra: all
Requires-Dist: lanternfish[anthropic,crewai,gemini,groq,langchain,llamaindex,openai]; extra == "all"
Provides-Extra: dev
Requires-Dist: pytest>=7.0.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.23.0; extra == "dev"

# Lanternfish Python SDK

Record what your AI agent does: its LLM calls, its own steps, the decisions it makes and the release it runs on.
Lanternfish compares those runs over time and tells you what changed.

Built on OpenTelemetry. Works next to an existing OpenTelemetry or Logfire setup.

## Install

```bash
pip install "lanternfish[openai]"
```

Use the extra for each provider you call: `openai`, `anthropic`, `gemini`, `groq`, `langchain`, `crewai`,
`llamaindex`, or `all`. Requires Python 3.11+.

## Start

```bash
export LANTERNFISH_API_KEY="lfsk_..."            # Settings → API keys
export LANTERNFISH_ENDPOINT="https://your-lanternfish-endpoint"
```

```python
import lanternfish

lanternfish.instrument(agent="travel-agent")
```

From here on, calls to installed providers are recorded. The SDK never picks a destination by itself: without an
endpoint it stops with an error that says what to set.

## Record your own steps and decisions

```python
import lanternfish

@lanternfish.trace                                  # records arguments; capture_args=False for private data
def search_flights(destination: str) -> list[str]:
    ...

with lanternfish.set_user("user_42", session_id="sess_9"):   # removed again when the block ends
    with lanternfish.span("book-trip"):
        options = search_flights("Lisbon")
        with lanternfish.reasoning(name="pick_flight") as r:
            r.consider_option("KLM 1724", score=0.8, reason="Arrives before the meeting")
            r.decide("KLM 1724", confidence=0.8, reason="Arrives before the meeting")
```

A step that raises is marked failed, with the exception type and message.

## Releases

Every span carries the release it ran on: `release=` if you pass it, otherwise `LANTERNFISH_RELEASE`, otherwise the
commit your CI or host exposes (`GITHUB_SHA`, `VERCEL_GIT_COMMIT_SHA`, `RENDER_GIT_COMMIT`, …). To mark the moment a
release went live, call this from your deploy step:

```python
lanternfish.record_deployment("v1.8.2", metadata={"commit": "a1b2c3d", "environment": "production"})
```

## Release verdict: stop a deploy that regressed

Once a release and the one before it each have 30 runs, Lanternfish compares them: run duration, success rate and
cost per run, plus how the agent uses each tool (how many runs call it, how many times per run). The verdict is one of
`regressed`, `changed`, `improved`, `no_change` or `insufficient_data`, and every finding states both sides:

```text
v1.8.2 regressed against v1.8.1.
  - Success rate is down 18%: 80.0% vs 98.0% in v1.8.1.
  - search_flights is called 2.3 times per run, 1.1 in v1.8.1.
Based on 212 runs of v1.8.2 vs 1,840 runs of v1.8.1.
```

Gate a deploy on it from CI, after the canary has served traffic. The command exits 1 when the release regressed:

```bash
lanternfish verdict --release v1.8.2                       # gate on quality regressions
lanternfish verdict --release v1.8.2 --fail-on changed     # also on behavior changes
lanternfish verdict --release v1.8.2 --require-data        # also when there are not enough runs yet
```

Exit codes: 0 passed, 1 gate closed, 2 no verdict (unknown release, rejected key, API unreachable). From Python:
`lanternfish.get_verdict("v1.8.2").regressed`. The verdict endpoint accepts the project API key and returns only
aggregate numbers and tool names, never run content. The dashboard shows the same verdict under **Releases**.

## Options

| `instrument()` argument | If omitted | |
|---|---|---|
| `api_key` | `LANTERNFISH_API_KEY` | Project API key, sent only in the `x-api-key` header |
| `endpoint` | `LANTERNFISH_ENDPOINT` | Your Lanternfish API base URL |
| `agent` | — | Agent name on every run |
| `release` | `LANTERNFISH_RELEASE` or CI commit | Version of the running code |
| `environment` | `LANTERNFISH_ENVIRONMENT` | For example `production` |
| `include` | `[]` | Also record `"http"`, `"sql"`, `"redis"` calls |
| `capture_content` | `True` | Record prompt and completion text of LLM calls |

## What is recorded

- **LLM calls:** model, tokens, duration, errors and, unless `capture_content=False`, the prompt and completion text.
- **HTTP** (only with `include=["http"]`): method, host, path and status. Never bodies, headers or query strings.
- **SQL** (only with `include=["sql"]`): the statement with literals replaced by `?`. Never parameter values.

## Short scripts and serverless

Spans are sent in the background in batches. Call `lanternfish.flush()` before a short script or a serverless handler
returns; `lanternfish.shutdown()` runs on process exit by itself.

## Troubleshooting

```python
import logging
logging.basicConfig(level="INFO")
```

The `lanternfish` logger names the endpoint, lists the integrations it turned on, and logs every rejected batch with
the reason (invalid key, plan limit reached, endpoint unreachable). Sending never raises into your code; a temporary
error is retried once.

## License

MIT
