flowlines 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- flowlines-0.1.0/.claude/settings.local.json +31 -0
- flowlines-0.1.0/.github/workflows/ci.yml +69 -0
- flowlines-0.1.0/.gitignore +13 -0
- flowlines-0.1.0/.pre-commit-config.yaml +23 -0
- flowlines-0.1.0/.python-version +1 -0
- flowlines-0.1.0/CLAUDE.md +71 -0
- flowlines-0.1.0/LICENSE +21 -0
- flowlines-0.1.0/PKG-INFO +258 -0
- flowlines-0.1.0/PUBLISH.md +60 -0
- flowlines-0.1.0/README.md +194 -0
- flowlines-0.1.0/examples/openai/.env.example +2 -0
- flowlines-0.1.0/examples/openai/README.md +42 -0
- flowlines-0.1.0/examples/openai/agent.py +204 -0
- flowlines-0.1.0/flowlines/__init__.py +11 -0
- flowlines-0.1.0/flowlines/_context.py +76 -0
- flowlines-0.1.0/flowlines/_exporter.py +58 -0
- flowlines-0.1.0/flowlines/_filter.py +20 -0
- flowlines-0.1.0/flowlines/_init.py +366 -0
- flowlines-0.1.0/flowlines/_patches.py +54 -0
- flowlines-0.1.0/flowlines/py.typed +0 -0
- flowlines-0.1.0/integration_tests/__init__.py +0 -0
- flowlines-0.1.0/integration_tests/conftest.py +383 -0
- flowlines-0.1.0/integration_tests/test_basic.py +159 -0
- flowlines-0.1.0/integration_tests/test_basic_anthropic.py +114 -0
- flowlines-0.1.0/integration_tests/test_basic_gemini.py +114 -0
- flowlines-0.1.0/integration_tests/test_noconflict_with_otel.py +202 -0
- flowlines-0.1.0/integration_tests/test_noconflict_with_traceloop.py +84 -0
- flowlines-0.1.0/pyproject.toml +102 -0
- flowlines-0.1.0/pyrightconfig.json +4 -0
- flowlines-0.1.0/tests/__init__.py +0 -0
- flowlines-0.1.0/tests/test_context.py +418 -0
- flowlines-0.1.0/tests/test_exporter.py +145 -0
- flowlines-0.1.0/tests/test_filter.py +55 -0
- flowlines-0.1.0/tests/test_init.py +739 -0
- flowlines-0.1.0/uv.lock +3052 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"permissions": {
|
|
3
|
+
"allow": [
|
|
4
|
+
"Bash(uv sync:*)",
|
|
5
|
+
"Bash(uv run pytest:*)",
|
|
6
|
+
"Bash(uv run:*)",
|
|
7
|
+
"Bash(uv pip index versions:*)",
|
|
8
|
+
"WebSearch",
|
|
9
|
+
"WebFetch(domain:pypi.org)",
|
|
10
|
+
"Bash(git add:*)",
|
|
11
|
+
"Bash(git commit:*)",
|
|
12
|
+
"WebFetch(domain:github.com)",
|
|
13
|
+
"Bash(uv pip list:*)",
|
|
14
|
+
"Bash(pip index:*)",
|
|
15
|
+
"Bash(uv add:*)",
|
|
16
|
+
"Bash(ANTHROPIC_API_KEY=dummy GEMINI_API_KEY=dummy uv run pytest:*)",
|
|
17
|
+
"Bash(OPENAI_API_KEY=dummy ANTHROPIC_API_KEY=dummy GEMINI_API_KEY=dummy uv run pytest:*)",
|
|
18
|
+
"WebFetch(domain:platform.openai.com)",
|
|
19
|
+
"WebFetch(domain:cookbook.openai.com)",
|
|
20
|
+
"WebFetch(domain:community.openai.com)",
|
|
21
|
+
"WebFetch(domain:developers.openai.com)",
|
|
22
|
+
"WebFetch(domain:docs.windsurf.com)",
|
|
23
|
+
"WebFetch(domain:docs.cline.bot)",
|
|
24
|
+
"WebFetch(domain:aider.chat)",
|
|
25
|
+
"WebFetch(domain:docs.github.com)",
|
|
26
|
+
"WebFetch(domain:antigravity.google)",
|
|
27
|
+
"WebFetch(domain:codelabs.developers.google.com)",
|
|
28
|
+
"Bash(GEMINI_API_KEY=AIzaSyD-cuHx5wvLOEhOrJbLJLJx4xNLJbg1DyM uv run:*)"
|
|
29
|
+
]
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
lint:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v6
|
|
14
|
+
- uses: astral-sh/setup-uv@v7
|
|
15
|
+
- uses: actions/setup-python@v6
|
|
16
|
+
with:
|
|
17
|
+
python-version: "3.13"
|
|
18
|
+
|
|
19
|
+
- name: Install dependencies
|
|
20
|
+
run: uv sync
|
|
21
|
+
|
|
22
|
+
- name: Check formatting
|
|
23
|
+
run: uv run ruff format --check flowlines/ tests/ integration_tests/
|
|
24
|
+
|
|
25
|
+
- name: Lint
|
|
26
|
+
run: uv run ruff check flowlines/ tests/ integration_tests/
|
|
27
|
+
|
|
28
|
+
- name: Type check (mypy)
|
|
29
|
+
run: uv run mypy flowlines/
|
|
30
|
+
|
|
31
|
+
- name: Type check (pyright)
|
|
32
|
+
run: uv run pyright
|
|
33
|
+
|
|
34
|
+
test:
|
|
35
|
+
runs-on: ubuntu-latest
|
|
36
|
+
strategy:
|
|
37
|
+
matrix:
|
|
38
|
+
python-version: ["3.11", "3.12", "3.13"]
|
|
39
|
+
steps:
|
|
40
|
+
- uses: actions/checkout@v6
|
|
41
|
+
- uses: astral-sh/setup-uv@v7
|
|
42
|
+
- uses: actions/setup-python@v6
|
|
43
|
+
with:
|
|
44
|
+
python-version: ${{ matrix.python-version }}
|
|
45
|
+
|
|
46
|
+
- name: Install dependencies
|
|
47
|
+
run: uv sync
|
|
48
|
+
|
|
49
|
+
- name: Run tests
|
|
50
|
+
run: uv run pytest tests/
|
|
51
|
+
|
|
52
|
+
integration-test:
|
|
53
|
+
runs-on: ubuntu-latest
|
|
54
|
+
steps:
|
|
55
|
+
- uses: actions/checkout@v6
|
|
56
|
+
- uses: astral-sh/setup-uv@v7
|
|
57
|
+
- uses: actions/setup-python@v6
|
|
58
|
+
with:
|
|
59
|
+
python-version: "3.13"
|
|
60
|
+
|
|
61
|
+
- name: Install dependencies
|
|
62
|
+
run: uv sync --group integration
|
|
63
|
+
|
|
64
|
+
- name: Run integration tests
|
|
65
|
+
env:
|
|
66
|
+
OPENAI_API_KEY: ${{ secrets.IT_OPENAI_API_KEY }}
|
|
67
|
+
ANTHROPIC_API_KEY: ${{ secrets.IT_ANTHROPIC_API_KEY }}
|
|
68
|
+
GEMINI_API_KEY: ${{ secrets.IT_GEMINI_API_KEY }}
|
|
69
|
+
run: uv run pytest integration_tests/
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
3
|
+
rev: v0.15.1
|
|
4
|
+
hooks:
|
|
5
|
+
- id: ruff
|
|
6
|
+
args: [--fix]
|
|
7
|
+
- id: ruff-format
|
|
8
|
+
|
|
9
|
+
- repo: local
|
|
10
|
+
hooks:
|
|
11
|
+
- id: pyright
|
|
12
|
+
name: pyright
|
|
13
|
+
entry: uv run pyright
|
|
14
|
+
language: system
|
|
15
|
+
types: [python]
|
|
16
|
+
pass_filenames: false
|
|
17
|
+
|
|
18
|
+
- id: mypy
|
|
19
|
+
name: mypy
|
|
20
|
+
entry: uv run mypy
|
|
21
|
+
language: system
|
|
22
|
+
types: [python]
|
|
23
|
+
pass_filenames: false
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.14
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Project
|
|
6
|
+
|
|
7
|
+
Flowlines SDK for Python — an observability SDK that instruments LLM provider APIs using OpenTelemetry. It captures requests, responses, timing, and errors, filtering to only LLM-related spans and exporting them via OTLP/HTTP to the Flowlines backend.
|
|
8
|
+
|
|
9
|
+
## Commands
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
# Install dependencies
|
|
13
|
+
uv sync
|
|
14
|
+
|
|
15
|
+
# Run unit tests
|
|
16
|
+
uv run pytest tests/
|
|
17
|
+
|
|
18
|
+
# Run a single test
|
|
19
|
+
uv run pytest tests/test_init.py::TestClassName::test_method_name
|
|
20
|
+
|
|
21
|
+
# Run integration tests (requires OPENAI_API_KEY env var)
|
|
22
|
+
uv run pytest integration_tests/
|
|
23
|
+
|
|
24
|
+
# Lint
|
|
25
|
+
uv run ruff check flowlines/ tests/ integration_tests/
|
|
26
|
+
|
|
27
|
+
# Format
|
|
28
|
+
uv run ruff format flowlines/ tests/ integration_tests/
|
|
29
|
+
|
|
30
|
+
# Type checking
|
|
31
|
+
uv run mypy flowlines/
|
|
32
|
+
uv run pyright
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Architecture
|
|
36
|
+
|
|
37
|
+
All source is in `flowlines/`. The public API exports `Flowlines` and `FlowlinesExporter` from `__init__.py`.
|
|
38
|
+
|
|
39
|
+
### Core modules
|
|
40
|
+
|
|
41
|
+
- **`_init.py`** — `Flowlines` singleton with three initialization modes:
|
|
42
|
+
- **Mode A** (default): Creates its own `TracerProvider`, registers all available instrumentors automatically
|
|
43
|
+
- **Mode B1** (`has_external_otel=True`): User manages their own `TracerProvider`; Flowlines provides `create_span_processor()` and `get_instrumentors()` for manual integration
|
|
44
|
+
- **Mode B2** (`has_traceloop=True`): Traceloop already initialized; Flowlines adds its span processor to the existing provider
|
|
45
|
+
- **`_context.py`** — `FlowlinesSpanProcessor` (decorator over `BatchSpanProcessor`) that injects `user_id`/`session_id`/`agent_id` from `ContextVar` into spans at start time. Context is set via `flowlines.context()` context manager or `Flowlines.set_context()`/`clear_context()` imperative API.
|
|
46
|
+
- **`_exporter.py`** — `FlowlinesExporter` wraps `OTLPSpanExporter`, filtering exported spans to only those with `gen_ai.*` or `ai.*` attributes (via `_filter.py`)
|
|
47
|
+
- **`_filter.py`** — `is_llm_span()` function that identifies LLM-related spans by attribute prefixes
|
|
48
|
+
|
|
49
|
+
### Key patterns
|
|
50
|
+
|
|
51
|
+
- **Singleton**: `Flowlines` enforces single instance; `_reset()` classmethod exists for test teardown
|
|
52
|
+
- **Decorator pattern**: Both `FlowlinesSpanProcessor` and `FlowlinesExporter` wrap inner OpenTelemetry objects
|
|
53
|
+
- **Dynamic instrumentor discovery**: Registry of `(library_name, module_path, class_name)` tuples; each instrumentor is loaded only if its library is importable
|
|
54
|
+
|
|
55
|
+
## Code Conventions
|
|
56
|
+
|
|
57
|
+
- Private modules prefixed with underscore (`_init.py`, `_context.py`, etc.)
|
|
58
|
+
- `from __future__ import annotations` in all modules
|
|
59
|
+
- Strict type checking enabled (mypy strict + pyright strict)
|
|
60
|
+
- All function signatures have type annotations including return types
|
|
61
|
+
- Google-style docstrings with Args/Returns/Raises sections
|
|
62
|
+
- Ruff lint rules: `E`, `F`, `W`, `I`, `UP`, `B`, `SIM`, `TCH`
|
|
63
|
+
|
|
64
|
+
## Test Conventions
|
|
65
|
+
|
|
66
|
+
- Unit tests in `tests/` use `unittest.mock.patch` and `MagicMock` extensively
|
|
67
|
+
- `autouse` fixtures reset the `Flowlines` singleton before each test
|
|
68
|
+
- Tests organized in classes by feature/mode
|
|
69
|
+
- All test functions have type annotations (`-> None`) and docstrings
|
|
70
|
+
- Integration tests in `integration_tests/` use an in-process OTLP capture server
|
|
71
|
+
- `pytest-asyncio` with `asyncio_mode = "auto"`
|
flowlines-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Flowlines
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
flowlines-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: flowlines
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Automatic instrumentation for LLM provider APIs — capture requests, responses, timing, and errors with minimal code changes.
|
|
5
|
+
Project-URL: Homepage, https://github.com/flowlines/flowlines-sdk
|
|
6
|
+
Project-URL: Repository, https://github.com/flowlines/flowlines-sdk
|
|
7
|
+
Author: Flowlines
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
18
|
+
Classifier: Typing :: Typed
|
|
19
|
+
Requires-Python: >=3.11
|
|
20
|
+
Requires-Dist: opentelemetry-api>=1.20
|
|
21
|
+
Requires-Dist: opentelemetry-exporter-otlp-proto-http>=1.20
|
|
22
|
+
Requires-Dist: opentelemetry-sdk>=1.20
|
|
23
|
+
Provides-Extra: all
|
|
24
|
+
Requires-Dist: opentelemetry-instrumentation-anthropic>=0.40; extra == 'all'
|
|
25
|
+
Requires-Dist: opentelemetry-instrumentation-bedrock>=0.40; extra == 'all'
|
|
26
|
+
Requires-Dist: opentelemetry-instrumentation-chromadb>=0.40; extra == 'all'
|
|
27
|
+
Requires-Dist: opentelemetry-instrumentation-cohere>=0.40; extra == 'all'
|
|
28
|
+
Requires-Dist: opentelemetry-instrumentation-google-generativeai>=0.40; extra == 'all'
|
|
29
|
+
Requires-Dist: opentelemetry-instrumentation-langchain>=0.40; extra == 'all'
|
|
30
|
+
Requires-Dist: opentelemetry-instrumentation-llamaindex>=0.40; extra == 'all'
|
|
31
|
+
Requires-Dist: opentelemetry-instrumentation-mcp>=0.40; extra == 'all'
|
|
32
|
+
Requires-Dist: opentelemetry-instrumentation-openai>=0.40; extra == 'all'
|
|
33
|
+
Requires-Dist: opentelemetry-instrumentation-pinecone>=0.40; extra == 'all'
|
|
34
|
+
Requires-Dist: opentelemetry-instrumentation-qdrant>=0.40; extra == 'all'
|
|
35
|
+
Requires-Dist: opentelemetry-instrumentation-together>=0.40; extra == 'all'
|
|
36
|
+
Requires-Dist: opentelemetry-instrumentation-vertexai>=0.40; extra == 'all'
|
|
37
|
+
Provides-Extra: anthropic
|
|
38
|
+
Requires-Dist: opentelemetry-instrumentation-anthropic>=0.40; extra == 'anthropic'
|
|
39
|
+
Provides-Extra: bedrock
|
|
40
|
+
Requires-Dist: opentelemetry-instrumentation-bedrock>=0.40; extra == 'bedrock'
|
|
41
|
+
Provides-Extra: chromadb
|
|
42
|
+
Requires-Dist: opentelemetry-instrumentation-chromadb>=0.40; extra == 'chromadb'
|
|
43
|
+
Provides-Extra: cohere
|
|
44
|
+
Requires-Dist: opentelemetry-instrumentation-cohere>=0.40; extra == 'cohere'
|
|
45
|
+
Provides-Extra: google-generativeai
|
|
46
|
+
Requires-Dist: opentelemetry-instrumentation-google-generativeai>=0.40; extra == 'google-generativeai'
|
|
47
|
+
Provides-Extra: langchain
|
|
48
|
+
Requires-Dist: opentelemetry-instrumentation-langchain>=0.40; extra == 'langchain'
|
|
49
|
+
Provides-Extra: llamaindex
|
|
50
|
+
Requires-Dist: opentelemetry-instrumentation-llamaindex>=0.40; extra == 'llamaindex'
|
|
51
|
+
Provides-Extra: mcp
|
|
52
|
+
Requires-Dist: opentelemetry-instrumentation-mcp>=0.40; extra == 'mcp'
|
|
53
|
+
Provides-Extra: openai
|
|
54
|
+
Requires-Dist: opentelemetry-instrumentation-openai>=0.40; extra == 'openai'
|
|
55
|
+
Provides-Extra: pinecone
|
|
56
|
+
Requires-Dist: opentelemetry-instrumentation-pinecone>=0.40; extra == 'pinecone'
|
|
57
|
+
Provides-Extra: qdrant
|
|
58
|
+
Requires-Dist: opentelemetry-instrumentation-qdrant>=0.40; extra == 'qdrant'
|
|
59
|
+
Provides-Extra: together
|
|
60
|
+
Requires-Dist: opentelemetry-instrumentation-together>=0.40; extra == 'together'
|
|
61
|
+
Provides-Extra: vertexai
|
|
62
|
+
Requires-Dist: opentelemetry-instrumentation-vertexai>=0.40; extra == 'vertexai'
|
|
63
|
+
Description-Content-Type: text/markdown
|
|
64
|
+
|
|
65
|
+
# Flowlines SDK for Python
|
|
66
|
+
|
|
67
|
+
Observability for LLM-powered applications. The Flowlines SDK instruments LLM provider APIs using OpenTelemetry — it captures requests, responses, timing, and errors, and exports them to the Flowlines backend via OTLP/HTTP.
|
|
68
|
+
|
|
69
|
+
Supported providers: **OpenAI**, **Anthropic**, **AWS Bedrock**, **Cohere**, **Vertex AI**, **Together AI**. Also instruments **LangChain**, **LlamaIndex**, **MCP**, **Pinecone**, **ChromaDB**, and **Qdrant**.
|
|
70
|
+
|
|
71
|
+
## Requirements
|
|
72
|
+
|
|
73
|
+
- Python 3.11+
|
|
74
|
+
|
|
75
|
+
## Installation
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
pip install flowlines
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Then install instrumentation extras for the providers you use:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
# Single provider
|
|
85
|
+
pip install flowlines[openai]
|
|
86
|
+
|
|
87
|
+
# Multiple providers
|
|
88
|
+
pip install flowlines[openai,anthropic]
|
|
89
|
+
|
|
90
|
+
# All supported providers
|
|
91
|
+
pip install flowlines[all]
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Available extras: `openai`, `anthropic`, `bedrock`, `cohere`, `vertexai`, `together`, `pinecone`, `chromadb`, `qdrant`, `langchain`, `llamaindex`, `mcp`.
|
|
95
|
+
|
|
96
|
+
## AI coding agent integration
|
|
97
|
+
|
|
98
|
+
If you use an AI coding agent, you can install the Flowlines skill so your agent knows how to integrate the SDK into your project:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
npx skills add flowlines-ai/skills
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Then, just ask your agent to integrate Flowlines into your project.
|
|
105
|
+
|
|
106
|
+
## Quick start
|
|
107
|
+
|
|
108
|
+
If you don't have an existing OpenTelemetry setup, this is all you need:
|
|
109
|
+
|
|
110
|
+
```python
|
|
111
|
+
from flowlines import Flowlines
|
|
112
|
+
|
|
113
|
+
flowlines = Flowlines(api_key="your-flowlines-api-key")
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
That's it. Every LLM call made through an installed provider is now automatically captured and exported to Flowlines. The SDK:
|
|
117
|
+
|
|
118
|
+
1. Creates an OpenTelemetry `TracerProvider`
|
|
119
|
+
2. Detects which LLM libraries are installed and instruments them
|
|
120
|
+
3. Filters spans to only export LLM-related telemetry
|
|
121
|
+
4. Sends data to the Flowlines backend via OTLP/HTTP
|
|
122
|
+
|
|
123
|
+
### User, session, and agent tracking
|
|
124
|
+
|
|
125
|
+
Tag LLM calls with a user ID, session ID, and/or agent ID using the `context()` context manager:
|
|
126
|
+
|
|
127
|
+
```python
|
|
128
|
+
with flowlines.context(user_id="user-42", session_id="sess-abc"):
|
|
129
|
+
client.chat.completions.create(...) # this span gets user_id and session_id
|
|
130
|
+
client.chat.completions.create(...) # same
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
You can also attach an `agent_id` to identify which agent produced the spans:
|
|
134
|
+
|
|
135
|
+
```python
|
|
136
|
+
with flowlines.context(user_id="user-42", session_id="sess-abc", agent_id="agent-1"):
|
|
137
|
+
client.chat.completions.create(...)
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
The `session_id` and `agent_id` are optional — you can track just the user:
|
|
141
|
+
|
|
142
|
+
```python
|
|
143
|
+
with flowlines.context(user_id="user-42"):
|
|
144
|
+
client.chat.completions.create(...)
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
For cases where a context manager doesn't fit (e.g. across request boundaries), use the imperative API. `set_context()` returns a token — pass it to `clear_context()` to restore the previous state:
|
|
148
|
+
|
|
149
|
+
```python
|
|
150
|
+
token = Flowlines.set_context(user_id="user-42", session_id="sess-abc", agent_id="agent-1")
|
|
151
|
+
try:
|
|
152
|
+
client.chat.completions.create(...)
|
|
153
|
+
finally:
|
|
154
|
+
Flowlines.clear_context(token)
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Context tracking is thread-safe and async-safe.
|
|
158
|
+
|
|
159
|
+
### Custom endpoint
|
|
160
|
+
|
|
161
|
+
By default, data is sent to `https://ingest.flowlines.ai`. You can override this:
|
|
162
|
+
|
|
163
|
+
```python
|
|
164
|
+
flowlines = Flowlines(
|
|
165
|
+
api_key="your-flowlines-api-key",
|
|
166
|
+
endpoint="https://your-custom-endpoint.example.com",
|
|
167
|
+
)
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
The endpoint must use HTTPS, unless it targets `localhost` / `127.0.0.1` / `::1` (useful for local development).
|
|
171
|
+
|
|
172
|
+
## Usage with an existing OpenTelemetry setup
|
|
173
|
+
|
|
174
|
+
If your application already has its own `TracerProvider`, pass `has_external_otel=True` to prevent the SDK from creating a second one:
|
|
175
|
+
|
|
176
|
+
```python
|
|
177
|
+
from flowlines import Flowlines
|
|
178
|
+
|
|
179
|
+
flowlines = Flowlines(
|
|
180
|
+
api_key="your-flowlines-api-key",
|
|
181
|
+
has_external_otel=True,
|
|
182
|
+
)
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
In this mode, the SDK does **not** create a `TracerProvider` or register instrumentors. You are responsible for wiring things up yourself:
|
|
186
|
+
|
|
187
|
+
```python
|
|
188
|
+
from opentelemetry.sdk.trace import TracerProvider
|
|
189
|
+
|
|
190
|
+
provider = TracerProvider()
|
|
191
|
+
|
|
192
|
+
# 1. Add the Flowlines span processor to your provider
|
|
193
|
+
processor = flowlines.create_span_processor()
|
|
194
|
+
provider.add_span_processor(processor)
|
|
195
|
+
|
|
196
|
+
# 2. Instrument providers using the Flowlines instrumentor registry
|
|
197
|
+
for instrumentor in flowlines.get_instrumentors():
|
|
198
|
+
instrumentor.instrument(tracer_provider=provider)
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
- `create_span_processor()` returns a span processor that filters and exports LLM spans to Flowlines. Call it exactly once.
|
|
202
|
+
- `get_instrumentors()` returns instrumentor instances for every supported provider library that is currently installed. You can also skip this and register instrumentors yourself.
|
|
203
|
+
|
|
204
|
+
## Troubleshooting
|
|
205
|
+
|
|
206
|
+
### Verbose mode
|
|
207
|
+
|
|
208
|
+
Pass `verbose=True` to enable debug logging. This prints detailed information about initialization, instrumentor discovery, span filtering, and export results to stderr:
|
|
209
|
+
|
|
210
|
+
```python
|
|
211
|
+
flowlines = Flowlines(api_key="your-flowlines-api-key", verbose=True)
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Example output:
|
|
215
|
+
|
|
216
|
+
```
|
|
217
|
+
[flowlines] Initializing Flowlines SDK (endpoint=https://ingest.flowlines.ai)
|
|
218
|
+
[flowlines] Mode A: creating TracerProvider and registering instrumentors
|
|
219
|
+
[flowlines] Instrumentor loaded: OpenAIInstrumentor
|
|
220
|
+
[flowlines] Instrumentor skipped: anthropic (library not installed)
|
|
221
|
+
[flowlines] Total instrumentors loaded: 1
|
|
222
|
+
[flowlines] Flowlines SDK initialized successfully
|
|
223
|
+
[flowlines] Export: 2/5 span(s) are LLM-related — sending to backend
|
|
224
|
+
[flowlines] Export: succeeded
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### No spans appearing in Flowlines
|
|
228
|
+
|
|
229
|
+
- **Enable verbose mode.** Pass `verbose=True` to see exactly what the SDK is doing — which instrumentors are loaded, how many spans are captured, and whether exports succeed.
|
|
230
|
+
- **Missing instrumentation extras.** The SDK only instruments providers whose instrumentation package is installed. For example, if you use OpenAI, make sure you installed `flowlines[openai]`. Check your installed packages with `pip list | grep opentelemetry-instrumentation`.
|
|
231
|
+
- **Flowlines initialized too late.** The `Flowlines()` constructor must run **before** any LLM calls. If the provider client is created before instrumentation is set up, those calls won't be captured.
|
|
232
|
+
- **Wrong API key.** Verify that the `api_key` you pass is valid. The SDK will export spans, but the backend will reject them silently if the key is invalid.
|
|
233
|
+
|
|
234
|
+
### `RuntimeError: Flowlines is a singleton`
|
|
235
|
+
|
|
236
|
+
`Flowlines` enforces a single instance. You're calling `Flowlines(...)` more than once. Store the instance and reuse it, or initialize it once at application startup.
|
|
237
|
+
|
|
238
|
+
### `ValueError: Endpoint must use HTTPS`
|
|
239
|
+
|
|
240
|
+
The SDK requires HTTPS for all endpoints except loopback addresses (`localhost`, `127.0.0.1`, `::1`). If you're testing locally, use `http://localhost:<port>`.
|
|
241
|
+
|
|
242
|
+
### Spans are missing `user_id` / `session_id`
|
|
243
|
+
|
|
244
|
+
Make sure the LLM call happens **inside** the `flowlines.context()` block or between `set_context()` and `clear_context()`. If you're using threads or async tasks, note that context does not propagate automatically to child threads — set it in each task.
|
|
245
|
+
|
|
246
|
+
### Duplicate spans or conflicting `TracerProvider`
|
|
247
|
+
|
|
248
|
+
If you already have an OpenTelemetry setup, you **must** pass `has_external_otel=True`. Otherwise the SDK creates its own `TracerProvider`, which conflicts with yours. See [Usage with an existing OpenTelemetry setup](#usage-with-an-existing-opentelemetry-setup).
|
|
249
|
+
|
|
250
|
+
## Examples
|
|
251
|
+
|
|
252
|
+
See the [`examples/`](examples/) directory for working sample applications:
|
|
253
|
+
|
|
254
|
+
- **[OpenAI conversational agent](examples/openai/)** — Interactive agent with tool calling, demonstrating Mode A auto-instrumentation and context propagation.
|
|
255
|
+
|
|
256
|
+
## License
|
|
257
|
+
|
|
258
|
+
MIT
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Publishing to PyPI
|
|
2
|
+
|
|
3
|
+
## Prerequisites
|
|
4
|
+
|
|
5
|
+
1. Install the build tools:
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
uv tool install twine
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
2. Create accounts and API tokens:
|
|
12
|
+
- **TestPyPI**: https://test.pypi.org/account/register/ — create an API token at https://test.pypi.org/manage/account/
|
|
13
|
+
- **PyPI**: https://pypi.org/account/register/ — create an API token at https://pypi.org/manage/account/
|
|
14
|
+
|
|
15
|
+
## Bump the version
|
|
16
|
+
|
|
17
|
+
Edit `version` in `pyproject.toml`:
|
|
18
|
+
|
|
19
|
+
```toml
|
|
20
|
+
version = "0.2.0"
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Build
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
rm -rf dist/
|
|
27
|
+
uv build
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
This produces `dist/flowlines-<version>.tar.gz` and `dist/flowlines-<version>-py3-none-any.whl`.
|
|
31
|
+
|
|
32
|
+
## Publish to TestPyPI
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
uvx twine upload --repository testpypi dist/*
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
You will be prompted for credentials. Use `__token__` as the username and your TestPyPI API token as the password.
|
|
39
|
+
|
|
40
|
+
Verify the result at `https://test.pypi.org/project/flowlines/`.
|
|
41
|
+
|
|
42
|
+
To install from TestPyPI and check it works:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ flowlines
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
(`--extra-index-url` is needed so that dependencies are still fetched from the real PyPI.)
|
|
49
|
+
|
|
50
|
+
## Publish to PyPI
|
|
51
|
+
|
|
52
|
+
Once you're satisfied with the TestPyPI release:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
uvx twine upload dist/*
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Use `__token__` as the username and your PyPI API token as the password.
|
|
59
|
+
|
|
60
|
+
Verify the result at `https://pypi.org/project/flowlines/`.
|