Metadata-Version: 2.4
Name: glassflow-ai
Version: 0.2.0
Summary: GlassFlow SDK — OpenTelemetry-native tracing for AI agents and LLM applications.
Project-URL: Homepage, https://github.com/glassflow/glassflow-python
Project-URL: Repository, https://github.com/glassflow/glassflow-python.git
Project-URL: Issues, https://github.com/glassflow/glassflow-python/issues
Author-email: GlassFlow <hello@glassflow.dev>
License-Expression: MIT
License-File: LICENSE
Keywords: agents,genai,glassflow,llm,observability,opentelemetry,otel,tracing
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: opentelemetry-api>=1.24.0
Requires-Dist: opentelemetry-exporter-otlp-proto-http>=1.24.0
Requires-Dist: opentelemetry-sdk>=1.24.0
Description-Content-Type: text/markdown

# GlassFlow Python SDK

OpenTelemetry-native tracing for AI agents and LLM applications. `glassflow-ai`
emits [OpenTelemetry GenAI](https://opentelemetry.io/docs/specs/semconv/gen-ai/)
traces over OTLP to the managed GlassFlow observability platform (or any
OTLP-compatible backend).

> Status: alpha. APIs may change.

## Install

```bash
pip install glassflow-ai
```

## Quickstart

```python
import glassflow
from glassflow import observe, start_as_current_generation, start_as_current_span
from glassflow.semconv import SpanKind

glassflow.init(
    api_key="glassflow_...",          # or set GLASSFLOW_API_KEY
    service_name="my-agent",          # or set GLASSFLOW_SERVICE_NAME
)

# 1. Decorator — trace a whole function
@observe
def handle(query: str) -> str: ...

# 2. Context manager — trace a block
with start_as_current_span("retrieve", kind=SpanKind.RETRIEVER) as obs:
    obs.set_output(docs)

# 3. LLM generations — gen_ai-native
with start_as_current_generation("chat", model="gpt-4o", input=messages) as gen:
    gen.set_output(reply)
    gen.set_usage(input_tokens=42, output_tokens=17)
```

Each surface has a **manual** variant for lifetimes a `with` block can't express
(streaming, callbacks): `start_span(...)` / `start_generation(...)` return a handle
you `.update()` and must `.end()` yourself.

Configuration is resolved from explicit arguments first, then environment
variables:

| Argument       | Environment variable     | Default                        | Description                                                          |
| -------------- | ------------------------ | ------------------------------ | -------------------------------------------------------------------- |
| `endpoint`     | `GLASSFLOW_ENDPOINT`     | `https://ingest.glassflow.dev` | Base OTLP endpoint. Traces are sent to `<endpoint>/v1/traces`.       |
| `api_key`      | `GLASSFLOW_API_KEY`      | —                              | Injected as an `Authorization: Bearer <key>` header on every export. |
| `service_name` | `GLASSFLOW_SERVICE_NAME` | `unknown_service`              | Sets the OpenTelemetry `service.name` resource attribute.            |
| `disabled`     | `GLASSFLOW_DISABLED`     | `false`                        | Kill switch. When true, spans are created but never exported.        |
| `sample_rate`  | `GLASSFLOW_SAMPLE_RATE`  | `1.0`                          | Head sampling ratio `0.0`–`1.0` (whole-trace; children follow root). |
| `capture_content` | `GLASSFLOW_CAPTURE_CONTENT` | `true`                    | When false, prompt/response content is stripped at export (metadata still sent). |

`mask` is a code-only option (no env var): pass a callable to `init(mask=...)` and
it is applied to every content attribute value at export, across our spans and any
bundled third-party instrumentation.

```python
glassflow.init(mask=lambda value: "[REDACTED]")   # redact all captured content
glassflow.init(capture_content=False)             # drop content entirely, keep metadata
```

## Reliability

Export is designed to never block or crash your application:

- **Async batched export.** Spans are queued in-process and exported in batches
  from a background thread (`BatchSpanProcessor`). Span creation stays fast even
  when the backend is slow or unreachable.
- **Retries.** Transient failures (connection errors, 429/5xx) are retried with
  exponential backoff and jitter, bounded by the export timeout.
- **Graceful degradation.** If the backend stays down, spans are dropped and an
  error is logged — exceptions never propagate into application code. A failing
  `mask` callable drops only the affected attribute value (fail closed), never
  the batch.
- **Flush on shutdown.** Pending spans are flushed automatically at interpreter
  exit. Call `client.flush()` to force an export, or `client.shutdown()` to
  drain and stop.

Batching and backpressure are tunable via the standard OpenTelemetry env vars:
`OTEL_BSP_MAX_QUEUE_SIZE` (default 2048; spans beyond this are dropped),
`OTEL_BSP_SCHEDULE_DELAY` (default 5000 ms), `OTEL_BSP_MAX_EXPORT_BATCH_SIZE`
(default 512), and `OTEL_BSP_EXPORT_TIMEOUT` (default 30000 ms).

## Development

```bash
uv sync --group dev
uv run pytest
uv run ruff check . && uv run ruff format --check .
uv run mypy
```

## Releasing

Releases are automated with [release-please](https://github.com/googleapis/release-please)
and published to PyPI via Trusted Publishing.

1. Merge changes to `main` using [Conventional Commits](https://www.conventionalcommits.org/)
   (`feat:` → minor, `fix:` → patch, `feat!:`/`BREAKING CHANGE` → major).
2. release-please keeps a **Release PR** open that bumps `__version__` and updates
   `CHANGELOG.md`. Merge it when you want to cut a release.
3. Merging tags `vX.Y.Z`, creates a GitHub Release, and publishes to PyPI automatically.

Non-conventional commits are ignored for versioning.

## License

MIT

