glassflow-rius 0.8.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.
- glassflow_rius-0.8.0/.github/dependabot.yml +6 -0
- glassflow_rius-0.8.0/.github/workflows/ci.yml +69 -0
- glassflow_rius-0.8.0/.github/workflows/release.yml +102 -0
- glassflow_rius-0.8.0/.gitignore +22 -0
- glassflow_rius-0.8.0/.pre-commit-config.yaml +7 -0
- glassflow_rius-0.8.0/.release-please-manifest.json +3 -0
- glassflow_rius-0.8.0/CHANGELOG.md +112 -0
- glassflow_rius-0.8.0/CLAUDE.md +89 -0
- glassflow_rius-0.8.0/LICENSE +21 -0
- glassflow_rius-0.8.0/PKG-INFO +196 -0
- glassflow_rius-0.8.0/README.md +150 -0
- glassflow_rius-0.8.0/pyproject.toml +104 -0
- glassflow_rius-0.8.0/release-please-config.json +12 -0
- glassflow_rius-0.8.0/src/rius/__init__.py +28 -0
- glassflow_rius-0.8.0/src/rius/_serde.py +24 -0
- glassflow_rius-0.8.0/src/rius/client.py +279 -0
- glassflow_rius-0.8.0/src/rius/config.py +236 -0
- glassflow_rius-0.8.0/src/rius/generation.py +324 -0
- glassflow_rius-0.8.0/src/rius/heartbeat.py +258 -0
- glassflow_rius-0.8.0/src/rius/instrumentation.py +108 -0
- glassflow_rius-0.8.0/src/rius/instrumentation_mcp.py +138 -0
- glassflow_rius-0.8.0/src/rius/masking.py +139 -0
- glassflow_rius-0.8.0/src/rius/observe.py +185 -0
- glassflow_rius-0.8.0/src/rius/pending.py +266 -0
- glassflow_rius-0.8.0/src/rius/py.typed +0 -0
- glassflow_rius-0.8.0/src/rius/semconv.py +160 -0
- glassflow_rius-0.8.0/src/rius/spans.py +133 -0
- glassflow_rius-0.8.0/tests/conftest.py +43 -0
- glassflow_rius-0.8.0/tests/test_client.py +93 -0
- glassflow_rius-0.8.0/tests/test_config.py +118 -0
- glassflow_rius-0.8.0/tests/test_generation.py +286 -0
- glassflow_rius-0.8.0/tests/test_heartbeat.py +448 -0
- glassflow_rius-0.8.0/tests/test_instrumentation.py +230 -0
- glassflow_rius-0.8.0/tests/test_instrumentation_registry.py +31 -0
- glassflow_rius-0.8.0/tests/test_lifecycle.py +63 -0
- glassflow_rius-0.8.0/tests/test_masking.py +146 -0
- glassflow_rius-0.8.0/tests/test_mcp_instrumentation.py +270 -0
- glassflow_rius-0.8.0/tests/test_observe.py +196 -0
- glassflow_rius-0.8.0/tests/test_pending_delay.py +141 -0
- glassflow_rius-0.8.0/tests/test_pending_spans.py +165 -0
- glassflow_rius-0.8.0/tests/test_reliability.py +152 -0
- glassflow_rius-0.8.0/tests/test_semconv.py +36 -0
- glassflow_rius-0.8.0/tests/test_serde.py +26 -0
- glassflow_rius-0.8.0/tests/test_spans.py +76 -0
- glassflow_rius-0.8.0/uv.lock +1884 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
concurrency:
|
|
9
|
+
group: ci-${{ github.workflow }}-${{ github.ref }}
|
|
10
|
+
cancel-in-progress: true
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
lint:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v7.0.1
|
|
17
|
+
- uses: astral-sh/setup-uv@v7
|
|
18
|
+
with:
|
|
19
|
+
enable-cache: true
|
|
20
|
+
- run: uv sync --locked --group dev
|
|
21
|
+
- run: uv run ruff check .
|
|
22
|
+
- run: uv run ruff format --check .
|
|
23
|
+
- run: uv run mypy
|
|
24
|
+
- run: uv build
|
|
25
|
+
|
|
26
|
+
test:
|
|
27
|
+
runs-on: ubuntu-latest
|
|
28
|
+
strategy:
|
|
29
|
+
fail-fast: false
|
|
30
|
+
matrix:
|
|
31
|
+
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
|
|
32
|
+
steps:
|
|
33
|
+
- uses: actions/checkout@v7.0.1
|
|
34
|
+
- uses: astral-sh/setup-uv@v7
|
|
35
|
+
with:
|
|
36
|
+
enable-cache: true
|
|
37
|
+
python-version: ${{ matrix.python-version }}
|
|
38
|
+
- run: uv sync --locked --group dev
|
|
39
|
+
- run: uv run pytest
|
|
40
|
+
|
|
41
|
+
# The mcp extra is unbounded (mcp>=1.0.0) and the two majors have different
|
|
42
|
+
# result shapes (snake_case + InputRequiredResult in 2.x, GLA2-300); the
|
|
43
|
+
# default suite exercises the locked 1.x, this job re-runs the MCP
|
|
44
|
+
# instrumentation tests with 2.x overlaid so a future rename fails a build
|
|
45
|
+
# instead of silently un-flagging error results.
|
|
46
|
+
mcp-v2:
|
|
47
|
+
runs-on: ubuntu-latest
|
|
48
|
+
steps:
|
|
49
|
+
- uses: actions/checkout@v7.0.1
|
|
50
|
+
- uses: astral-sh/setup-uv@v7
|
|
51
|
+
with:
|
|
52
|
+
enable-cache: true
|
|
53
|
+
- run: uv sync --locked --group dev
|
|
54
|
+
- run: uv run --with "mcp>=2,<3" pytest tests/test_mcp_instrumentation.py
|
|
55
|
+
|
|
56
|
+
# Every instrumentation.REGISTRY entry must resolve against its real package —
|
|
57
|
+
# enable_instrumentations() swallows ImportError, so a typo'd module/class name
|
|
58
|
+
# would otherwise ship silently.
|
|
59
|
+
extras-smoke:
|
|
60
|
+
runs-on: ubuntu-latest
|
|
61
|
+
steps:
|
|
62
|
+
- uses: actions/checkout@v7.0.1
|
|
63
|
+
- uses: astral-sh/setup-uv@v7
|
|
64
|
+
with:
|
|
65
|
+
enable-cache: true
|
|
66
|
+
- run: uv sync --locked --group dev --all-extras
|
|
67
|
+
- run: uv run pytest tests/test_instrumentation_registry.py
|
|
68
|
+
env:
|
|
69
|
+
GLASSFLOW_ALL_EXTRAS: "1"
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
release-please:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
permissions:
|
|
11
|
+
contents: write
|
|
12
|
+
pull-requests: write
|
|
13
|
+
outputs:
|
|
14
|
+
release_created: ${{ steps.release.outputs.release_created }}
|
|
15
|
+
tag_name: ${{ steps.release.outputs.tag_name }}
|
|
16
|
+
steps:
|
|
17
|
+
- uses: googleapis/release-please-action@45996ed1f6d02564a971a2fa1b5860e934307cf7 # v5.0.0
|
|
18
|
+
id: release
|
|
19
|
+
with:
|
|
20
|
+
token: ${{ secrets.GITHUB_TOKEN }}
|
|
21
|
+
|
|
22
|
+
publish:
|
|
23
|
+
needs: release-please
|
|
24
|
+
if: ${{ needs.release-please.outputs.release_created == 'true' }}
|
|
25
|
+
runs-on: ubuntu-latest
|
|
26
|
+
environment:
|
|
27
|
+
name: pypi
|
|
28
|
+
url: https://pypi.org/project/glassflow-rius/
|
|
29
|
+
permissions:
|
|
30
|
+
id-token: write # OIDC for PyPI Trusted Publishing
|
|
31
|
+
contents: read
|
|
32
|
+
steps:
|
|
33
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
34
|
+
- uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0
|
|
35
|
+
with:
|
|
36
|
+
enable-cache: true
|
|
37
|
+
- run: uv sync --locked --group dev
|
|
38
|
+
- run: uv run pytest
|
|
39
|
+
- run: uv build
|
|
40
|
+
- uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # release/v1
|
|
41
|
+
|
|
42
|
+
# Tell glassflow-docs a new version shipped: its bump-api-reference workflow
|
|
43
|
+
# opens the pin-bump + regenerated-reference PR. Fires only after the PyPI
|
|
44
|
+
# publish succeeded (the docs workflow pip-installs the version). Needs
|
|
45
|
+
# DOCS_DISPATCH_TOKEN — a fine-grained PAT (or GitHub App token) with
|
|
46
|
+
# contents:write on glassflow/glassflow-docs; GITHUB_TOKEN cannot dispatch
|
|
47
|
+
# cross-repo. Skips gracefully while the secret is unset (the docs
|
|
48
|
+
# workflow's weekly schedule is the fallback).
|
|
49
|
+
dispatch-docs:
|
|
50
|
+
needs: [release-please, publish]
|
|
51
|
+
if: ${{ needs.release-please.outputs.release_created == 'true' }}
|
|
52
|
+
runs-on: ubuntu-latest
|
|
53
|
+
steps:
|
|
54
|
+
- name: Dispatch the docs API-reference bump
|
|
55
|
+
env:
|
|
56
|
+
TOKEN: ${{ secrets.DOCS_DISPATCH_TOKEN }}
|
|
57
|
+
TAG: ${{ needs.release-please.outputs.tag_name }}
|
|
58
|
+
run: |
|
|
59
|
+
if [ -z "$TOKEN" ]; then
|
|
60
|
+
echo "DOCS_DISPATCH_TOKEN not configured; skipping (the docs weekly fallback will catch up)." >> "$GITHUB_STEP_SUMMARY"
|
|
61
|
+
exit 0
|
|
62
|
+
fi
|
|
63
|
+
version="${TAG#v}"
|
|
64
|
+
curl -sS --fail-with-body -X POST \
|
|
65
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
66
|
+
-H "Accept: application/vnd.github+json" \
|
|
67
|
+
https://api.github.com/repos/glassflow/glassflow-docs/dispatches \
|
|
68
|
+
-d "{\"event_type\":\"sdk-release\",\"client_payload\":{\"version\":\"$version\"}}"
|
|
69
|
+
|
|
70
|
+
# Announce on Slack AFTER the PyPI publish succeeds, so the channel only
|
|
71
|
+
# ever sees releases that actually shipped. A separate `on: release`
|
|
72
|
+
# workflow would never fire: release-please creates the GitHub Release with
|
|
73
|
+
# GITHUB_TOKEN, whose events don't trigger other workflows.
|
|
74
|
+
notify:
|
|
75
|
+
needs: [release-please, publish]
|
|
76
|
+
if: ${{ needs.release-please.outputs.release_created == 'true' }}
|
|
77
|
+
runs-on: ubuntu-latest
|
|
78
|
+
permissions:
|
|
79
|
+
contents: read
|
|
80
|
+
steps:
|
|
81
|
+
- name: Post release summary to Slack
|
|
82
|
+
env:
|
|
83
|
+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
84
|
+
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_RELEASES }}
|
|
85
|
+
TAG: ${{ needs.release-please.outputs.tag_name }}
|
|
86
|
+
run: |
|
|
87
|
+
if [ -z "$SLACK_WEBHOOK" ]; then
|
|
88
|
+
echo "SLACK_WEBHOOK_RELEASES not configured; skipping." >> "$GITHUB_STEP_SUMMARY"
|
|
89
|
+
exit 0
|
|
90
|
+
fi
|
|
91
|
+
release_json=$(gh release view "$TAG" --repo "$GITHUB_REPOSITORY" --json body,url)
|
|
92
|
+
url=$(jq -r .url <<<"$release_json")
|
|
93
|
+
# release-please changelog markdown -> Slack mrkdwn:
|
|
94
|
+
# headings to bold, **bold** to *bold*, [text](url) to <url|text>
|
|
95
|
+
body=$(jq -r .body <<<"$release_json" | sed -E \
|
|
96
|
+
-e 's/^#{1,6} +(.*)$/*\1*/' \
|
|
97
|
+
-e 's/\*\*([^*]+)\*\*/*\1*/g' \
|
|
98
|
+
-e 's/\[([^]]+)\]\(([^)]+)\)/<\2|\1>/g' \
|
|
99
|
+
| head -c 3500)
|
|
100
|
+
jq -n --arg tag "$TAG" --arg url "$url" --arg body "$body" \
|
|
101
|
+
'{text: (":package: *glassflow-rius \($tag)* is on PyPI (<\($url)|release notes>)\n\n\($body)")}' \
|
|
102
|
+
| curl -sS --fail-with-body -X POST -H 'Content-Type: application/json' -d @- "$SLACK_WEBHOOK"
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
build/
|
|
6
|
+
dist/
|
|
7
|
+
|
|
8
|
+
# Virtual envs / uv
|
|
9
|
+
.venv/
|
|
10
|
+
|
|
11
|
+
# Test / coverage / type caches
|
|
12
|
+
.pytest_cache/
|
|
13
|
+
.ruff_cache/
|
|
14
|
+
.mypy_cache/
|
|
15
|
+
.coverage
|
|
16
|
+
coverage.xml
|
|
17
|
+
htmlcov/
|
|
18
|
+
|
|
19
|
+
# OS / editor
|
|
20
|
+
.DS_Store
|
|
21
|
+
.idea/
|
|
22
|
+
.vscode/
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [0.8.0](https://github.com/glassflow/rius-sdk-python/compare/v0.7.0...v0.8.0) (2026-08-05)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### ⚠ BREAKING CHANGES
|
|
7
|
+
|
|
8
|
+
* rebrand SDK to glassflow-rius, import rius ([#44](https://github.com/glassflow/rius-sdk-python/issues/44))
|
|
9
|
+
|
|
10
|
+
### Features
|
|
11
|
+
|
|
12
|
+
* rebrand SDK to glassflow-rius, import rius ([#44](https://github.com/glassflow/rius-sdk-python/issues/44)) ([82c91c1](https://github.com/glassflow/rius-sdk-python/commit/82c91c1f644ca31e53bc2737e8cf2326dfa1aba6))
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Documentation
|
|
16
|
+
|
|
17
|
+
* update readme title ([#46](https://github.com/glassflow/rius-sdk-python/issues/46)) ([4ae480a](https://github.com/glassflow/rius-sdk-python/commit/4ae480a626b4175789bac652bf9767bcbe50a705))
|
|
18
|
+
|
|
19
|
+
## [0.7.0](https://github.com/glassflow/glassflow-python/compare/v0.6.0...v0.7.0) (2026-08-04)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
### Features
|
|
23
|
+
|
|
24
|
+
* emit partial (pending) spans at span start ([#37](https://github.com/glassflow/glassflow-python/issues/37)) ([e39e555](https://github.com/glassflow/glassflow-python/commit/e39e55545bb718ab83c196c5ec5b2ce631c00cb8))
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
### Bug Fixes
|
|
28
|
+
|
|
29
|
+
* support mcp 2.x result shapes in MCP instrumentation ([#42](https://github.com/glassflow/glassflow-python/issues/42)) ([65aff4b](https://github.com/glassflow/glassflow-python/commit/65aff4b51bcbd9688f72dd0f0733dbc34bfb642d))
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
### Documentation
|
|
33
|
+
|
|
34
|
+
* remove em dashes from docstrings ([#39](https://github.com/glassflow/glassflow-python/issues/39)) ([7ca0af5](https://github.com/glassflow/glassflow-python/commit/7ca0af5a6237fec0918ab8745067da2e1f798004))
|
|
35
|
+
|
|
36
|
+
## [0.6.0](https://github.com/glassflow/glassflow-python/compare/v0.5.0...v0.6.0) (2026-07-20)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
### Features
|
|
40
|
+
|
|
41
|
+
* agent-lifetime heartbeat sender ([#32](https://github.com/glassflow/glassflow-python/issues/32)) ([7c592a8](https://github.com/glassflow/glassflow-python/commit/7c592a8822341e06b9bcd444502530a84dc80679))
|
|
42
|
+
|
|
43
|
+
## [0.5.0](https://github.com/glassflow/glassflow-python/compare/v0.4.1...v0.5.0) (2026-07-15)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
### Features
|
|
47
|
+
|
|
48
|
+
* record time-to-first-token on streaming generations ([#29](https://github.com/glassflow/glassflow-python/issues/29)) ([6c1ecaa](https://github.com/glassflow/glassflow-python/commit/6c1ecaa46c7086b2bbdccc5a4d4dd7ade036c3c7))
|
|
49
|
+
|
|
50
|
+
## [0.4.1](https://github.com/glassflow/glassflow-python/compare/v0.4.0...v0.4.1) (2026-07-09)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
### Documentation
|
|
54
|
+
|
|
55
|
+
* complete Google-style docstrings for the public API ([#26](https://github.com/glassflow/glassflow-python/issues/26)) ([75d82a1](https://github.com/glassflow/glassflow-python/commit/75d82a19617f89cebdc813f61757fdc3a5119b46))
|
|
56
|
+
|
|
57
|
+
## [0.4.0](https://github.com/glassflow/glassflow-python/compare/v0.3.0...v0.4.0) (2026-07-06)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
### Features
|
|
61
|
+
|
|
62
|
+
* first-class MCP tool-call instrumentation ([#24](https://github.com/glassflow/glassflow-python/issues/24)) ([623004f](https://github.com/glassflow/glassflow-python/commit/623004fdd388b7244d7bbc4374b1023ea7680793))
|
|
63
|
+
|
|
64
|
+
## [0.3.0](https://github.com/glassflow/glassflow-python/compare/v0.2.0...v0.3.0) (2026-07-05)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
### ⚠ BREAKING CHANGES
|
|
68
|
+
|
|
69
|
+
* Generation.set_model() is now set_response_model(); Generation.set_finish_reason() is now set_finish_reasons().
|
|
70
|
+
|
|
71
|
+
### Features
|
|
72
|
+
|
|
73
|
+
* bundled auto-instrumentation via OpenInference ([6945ab1](https://github.com/glassflow/glassflow-python/commit/6945ab13bead44f61d765273e23d9ce26513d6e2))
|
|
74
|
+
* pre-1.0 API cleanups from the SDK review ([a294b94](https://github.com/glassflow/glassflow-python/commit/a294b945a01c68da3f918a5e9721b5d67a27433c))
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
### Bug Fixes
|
|
78
|
+
|
|
79
|
+
* crash-proofing and semconv corrections ([dd06b18](https://github.com/glassflow/glassflow-python/commit/dd06b187b2396e2648dc60b690f4c6c1367b72bb))
|
|
80
|
+
* define init() lifecycle semantics ([4374298](https://github.com/glassflow/glassflow-python/commit/4374298615b5b08920a2b3bda96f4bdd27d760c9))
|
|
81
|
+
* emit gen_ai.*.messages in the spec role/parts shape ([a7225d0](https://github.com/glassflow/glassflow-python/commit/a7225d0987c5e63e645b7f5d0f074bb1a42ffd46))
|
|
82
|
+
* harden export-stage masking ([9781380](https://github.com/glassflow/glassflow-python/commit/9781380d68936a43b91db45403511d5b2fceeeb7))
|
|
83
|
+
|
|
84
|
+
## [0.2.0](https://github.com/glassflow/glassflow-python/compare/v0.1.0...v0.2.0) (2026-07-03)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
### ⚠ BREAKING CHANGES
|
|
88
|
+
|
|
89
|
+
* start_generation/start_as_current_generation param 'system' is now 'provider', and the emitted attribute is gen_ai.provider.name (was gen_ai.system).
|
|
90
|
+
|
|
91
|
+
### Features
|
|
92
|
+
|
|
93
|
+
* emit gen_ai.provider.name; rename generation param system -> provider ([f30ea71](https://github.com/glassflow/glassflow-python/commit/f30ea71674fe4d2797e9ab5d5842e393b40054c0))
|
|
94
|
+
* harden export pipeline reliability ([0934973](https://github.com/glassflow/glassflow-python/commit/093497379eaed0ae0b0d56c9653efc1f37438ed1))
|
|
95
|
+
* head-based sampling via sample_rate ([67d4fd1](https://github.com/glassflow/glassflow-python/commit/67d4fd1ea00950645866e391f3c38c6dcbf9cb8b))
|
|
96
|
+
* PII masking and content opt-out at export ([2c3b4b0](https://github.com/glassflow/glassflow-python/commit/2c3b4b054bb78e2bc1757e55b944ee8e636aa418))
|
|
97
|
+
|
|
98
|
+
## [0.1.0](https://github.com/glassflow/glassflow-python/compare/v0.0.1...v0.1.0) (2026-07-02)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
### Features
|
|
102
|
+
|
|
103
|
+
* add `@observe` decorator for tracing user functions ([4e0ba4d](https://github.com/glassflow/glassflow-python/commit/4e0ba4d1013bb527df655012010fcd7accf65004))
|
|
104
|
+
* add span-kind model (semconv) and kind param to `@observe` ([e1305d8](https://github.com/glassflow/glassflow-python/commit/e1305d8c05ca03b56975a67645a9c6f004c5a339))
|
|
105
|
+
* add start_generation LLM capture helper (gen_ai-native) ([415b168](https://github.com/glassflow/glassflow-python/commit/415b1685403ff2fa6bc4450fe0242e6cd0f5c9a8))
|
|
106
|
+
* add start_span manual span API + Observation handle ([70835f8](https://github.com/glassflow/glassflow-python/commit/70835f890ba2e48de22af99b393a9269d27cec1b))
|
|
107
|
+
* align span API naming + add manual create/update/end lifecycle ([17e8f31](https://github.com/glassflow/glassflow-python/commit/17e8f31e634963f97c53e2be1fed84d434e85b15))
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
### Documentation
|
|
111
|
+
|
|
112
|
+
* update README title to GlassFlow Python SDK ([33cdacb](https://github.com/glassflow/glassflow-python/commit/33cdacb67b11454c16febfccbc89bdc0593bcd18))
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# CLAUDE.md — rius-sdk-python
|
|
2
|
+
|
|
3
|
+
Conventions for the GlassFlow instrumentation SDK (Python). Follow these; they
|
|
4
|
+
override generic defaults.
|
|
5
|
+
|
|
6
|
+
## What this is
|
|
7
|
+
|
|
8
|
+
A public, OpenTelemetry-native tracing SDK for AI agents / LLM applications. It
|
|
9
|
+
emits **OpenTelemetry GenAI (`gen_ai.*`) traces over OTLP/HTTP** to the managed
|
|
10
|
+
GlassFlow platform (or any OTLP-compatible backend). GlassFlow is **managed-only**
|
|
11
|
+
— there is no self-host, so config targets the managed endpoint.
|
|
12
|
+
|
|
13
|
+
- Distribution name: `glassflow-rius` (PyPI) · import package: `rius`
|
|
14
|
+
- `src/` layout; tests in `tests/`
|
|
15
|
+
- Python-first; the TS/JS SDK lives in a separate repository
|
|
16
|
+
|
|
17
|
+
## Tooling (uv-native)
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
uv sync --group dev # set up the environment
|
|
21
|
+
uv run pytest # tests
|
|
22
|
+
uv run ruff check . # lint
|
|
23
|
+
uv run ruff format . # format (use --check in CI)
|
|
24
|
+
uv run mypy # type-check (strict)
|
|
25
|
+
uv build # build sdist + wheel
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Use `uv` + `ruff` as the toolchain. Do not add pip/venv workflows or a separate
|
|
29
|
+
formatter/linter (no black/isort/flake8). Commit `uv.lock`.
|
|
30
|
+
|
|
31
|
+
## Code conventions
|
|
32
|
+
|
|
33
|
+
- **Python 3.10+**. Use modern typing (`X | None`, built-in generics).
|
|
34
|
+
- **Fully typed**; ship `py.typed`. `mypy --strict` must pass (config in `pyproject.toml`).
|
|
35
|
+
- **Ruff** for both lint and format; line length 100.
|
|
36
|
+
- Public API is re-exported from `rius/__init__.py` and listed in `__all__`.
|
|
37
|
+
- Prefer **dependency injection over mocking** for testability
|
|
38
|
+
(e.g. `init(span_exporter=...)` instead of patching the OTLP exporter).
|
|
39
|
+
|
|
40
|
+
## OpenTelemetry conventions
|
|
41
|
+
|
|
42
|
+
- **Convention-native** wire format — emit established conventions directly, never
|
|
43
|
+
a bespoke `glassflow.*` namespace (we own the backend, and this gives free
|
|
44
|
+
third-party ingestion via the normalizer). Pick the richest convention per concept:
|
|
45
|
+
- **Span kind:** `openinference.span.kind` (`SpanKind` enum) — gen_ai's
|
|
46
|
+
`operation.name` is not a sufficient kind taxonomy. Set `gen_ai.operation.name`
|
|
47
|
+
too where it maps.
|
|
48
|
+
- **LLM/generation spans:** fully **gen_ai-native** (`gen_ai.input.messages` /
|
|
49
|
+
`output.messages`, `gen_ai.request.model`, `gen_ai.usage.*`) — see `generation.py`.
|
|
50
|
+
- **Generic/tool/retriever spans:** `input.value` / `output.value` (OpenInference);
|
|
51
|
+
gen_ai has no generic non-LLM I/O.
|
|
52
|
+
- Build on the OTel SDK primitives (`TracerProvider`, `BatchSpanProcessor`,
|
|
53
|
+
OTLP/HTTP exporter). Don't hand-roll tracing internals.
|
|
54
|
+
- **Span API naming** (OTel/Langfuse/Laminar convention): `start_as_current_*` =
|
|
55
|
+
context manager (activates context, auto-ends); bare `start_*` = manual (returns
|
|
56
|
+
a handle you must `.end()`). Don't invert these.
|
|
57
|
+
- All attribute keys live in `semconv.py`. Respect OTel norms (e.g. `service.name`).
|
|
58
|
+
|
|
59
|
+
## Testing — TDD (required)
|
|
60
|
+
|
|
61
|
+
- **Test-first.** Write a failing test, watch it fail for the right reason, then
|
|
62
|
+
write minimal code to pass. No production code without a failing test first.
|
|
63
|
+
- pytest; use real code over mocks (inject dependencies).
|
|
64
|
+
- Before pushing, all gates must be green: `ruff check`, `ruff format --check`,
|
|
65
|
+
`mypy`, `pytest`.
|
|
66
|
+
|
|
67
|
+
## Versioning & releases
|
|
68
|
+
|
|
69
|
+
- **Single source of truth** for the version: `__version__` in
|
|
70
|
+
`src/rius/__init__.py` (annotated `# x-release-please-version`);
|
|
71
|
+
hatchling reads it. **Do not** edit the version anywhere else or hand-write
|
|
72
|
+
`CHANGELOG.md`.
|
|
73
|
+
- Releases are automated by **release-please** + **PyPI Trusted Publishing**
|
|
74
|
+
(`.github/workflows/release.yml`). Merging the auto-generated Release PR bumps
|
|
75
|
+
the version, tags `vX.Y.Z`, creates a GitHub Release, and publishes to PyPI.
|
|
76
|
+
|
|
77
|
+
## Git & PR conventions
|
|
78
|
+
|
|
79
|
+
- **Branch from `main`.** Branch names: `<user>/<TICKET>-short-desc`
|
|
80
|
+
(e.g. `pablo/gla2-19-observe-decorator`). Linear team key is `GLA2`.
|
|
81
|
+
- **PRs are squash-merged; the PR title becomes the commit on `main`** and is
|
|
82
|
+
what release-please reads (the squash body is blank). PR titles must therefore
|
|
83
|
+
be Conventional Commits: `feat:` (minor), `fix:` (patch), `feat!:` (breaking —
|
|
84
|
+
must be marked in the **title**, since `BREAKING CHANGE:` footers in branch
|
|
85
|
+
commits don't survive the squash), plus `chore:`/`docs:`/`test:`/`refactor:`/
|
|
86
|
+
`ci:`. Non-conforming titles are ignored for versioning.
|
|
87
|
+
- **No AI attribution** in commits or PRs (no `Co-Authored-By: Claude`, no
|
|
88
|
+
"Generated with…" trailers).
|
|
89
|
+
- Open a PR; CI (lint/format/mypy + tests on Python 3.10–3.14 + extras smoke) must pass to merge.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 GlassFlow
|
|
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.
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: glassflow-rius
|
|
3
|
+
Version: 0.8.0
|
|
4
|
+
Summary: GlassFlow Rius SDK — OpenTelemetry-native tracing for AI agents and LLM applications.
|
|
5
|
+
Project-URL: Homepage, https://github.com/glassflow/rius-sdk-python
|
|
6
|
+
Project-URL: Repository, https://github.com/glassflow/rius-sdk-python.git
|
|
7
|
+
Project-URL: Issues, https://github.com/glassflow/rius-sdk-python/issues
|
|
8
|
+
Author-email: GlassFlow <hello@glassflow.dev>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: agents,genai,llm,observability,opentelemetry,otel,rius,tracing
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
21
|
+
Classifier: Typing :: Typed
|
|
22
|
+
Requires-Python: >=3.10
|
|
23
|
+
Requires-Dist: opentelemetry-api>=1.24.0
|
|
24
|
+
Requires-Dist: opentelemetry-exporter-otlp-proto-http>=1.24.0
|
|
25
|
+
Requires-Dist: opentelemetry-sdk>=1.24.0
|
|
26
|
+
Provides-Extra: anthropic
|
|
27
|
+
Requires-Dist: openinference-instrumentation-anthropic>=1.0.6; extra == 'anthropic'
|
|
28
|
+
Provides-Extra: instruments
|
|
29
|
+
Requires-Dist: mcp>=1.0.0; extra == 'instruments'
|
|
30
|
+
Requires-Dist: openinference-instrumentation-anthropic>=1.0.6; extra == 'instruments'
|
|
31
|
+
Requires-Dist: openinference-instrumentation-langchain>=0.1.67; extra == 'instruments'
|
|
32
|
+
Requires-Dist: openinference-instrumentation-litellm>=0.1.34; extra == 'instruments'
|
|
33
|
+
Requires-Dist: openinference-instrumentation-llama-index>=4.4.3; extra == 'instruments'
|
|
34
|
+
Requires-Dist: openinference-instrumentation-openai>=0.1.52; extra == 'instruments'
|
|
35
|
+
Provides-Extra: langchain
|
|
36
|
+
Requires-Dist: openinference-instrumentation-langchain>=0.1.67; extra == 'langchain'
|
|
37
|
+
Provides-Extra: litellm
|
|
38
|
+
Requires-Dist: openinference-instrumentation-litellm>=0.1.34; extra == 'litellm'
|
|
39
|
+
Provides-Extra: llama-index
|
|
40
|
+
Requires-Dist: openinference-instrumentation-llama-index>=4.4.3; extra == 'llama-index'
|
|
41
|
+
Provides-Extra: mcp
|
|
42
|
+
Requires-Dist: mcp>=1.0.0; extra == 'mcp'
|
|
43
|
+
Provides-Extra: openai
|
|
44
|
+
Requires-Dist: openinference-instrumentation-openai>=0.1.52; extra == 'openai'
|
|
45
|
+
Description-Content-Type: text/markdown
|
|
46
|
+
|
|
47
|
+
# Rius Python SDK
|
|
48
|
+
|
|
49
|
+
OpenTelemetry-native tracing for AI agents and LLM applications. `glassflow-rius`
|
|
50
|
+
emits [OpenTelemetry GenAI](https://opentelemetry.io/docs/specs/semconv/gen-ai/)
|
|
51
|
+
traces over OTLP to the managed GlassFlow observability platform (or any
|
|
52
|
+
OTLP-compatible backend).
|
|
53
|
+
|
|
54
|
+
> Status: alpha. APIs may change.
|
|
55
|
+
|
|
56
|
+
## Install
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
pip install glassflow-rius
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Quickstart
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
import rius
|
|
66
|
+
from rius import observe, start_as_current_generation, start_as_current_span
|
|
67
|
+
from rius.semconv import SpanKind
|
|
68
|
+
|
|
69
|
+
rius.init(
|
|
70
|
+
api_key="glassflow_...", # or set GLASSFLOW_API_KEY
|
|
71
|
+
service_name="my-agent", # or set GLASSFLOW_SERVICE_NAME
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
# 1. Decorator — trace a whole function
|
|
75
|
+
@observe
|
|
76
|
+
def handle(query: str) -> str: ...
|
|
77
|
+
|
|
78
|
+
# 2. Context manager — trace a block
|
|
79
|
+
with start_as_current_span("retrieve", kind=SpanKind.RETRIEVER) as obs:
|
|
80
|
+
obs.set_output(docs)
|
|
81
|
+
|
|
82
|
+
# 3. LLM generations — gen_ai-native
|
|
83
|
+
with start_as_current_generation("chat", model="gpt-4o", input=messages) as gen:
|
|
84
|
+
gen.set_output(reply)
|
|
85
|
+
gen.set_usage(input_tokens=42, output_tokens=17)
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Each surface has a **manual** variant for lifetimes a `with` block can't express
|
|
89
|
+
(streaming, callbacks): `start_span(...)` / `start_generation(...)` return a handle
|
|
90
|
+
you `.update()` and must `.end()` yourself.
|
|
91
|
+
|
|
92
|
+
Configuration is resolved from explicit arguments first, then environment
|
|
93
|
+
variables:
|
|
94
|
+
|
|
95
|
+
| Argument | Environment variable | Default | Description |
|
|
96
|
+
| -------------- | ------------------------ | ------------------------------ | -------------------------------------------------------------------- |
|
|
97
|
+
| `endpoint` | `GLASSFLOW_ENDPOINT` | `https://ingest.glassflow.dev` | Base OTLP endpoint. Traces are sent to `<endpoint>/v1/traces`. |
|
|
98
|
+
| `api_key` | `GLASSFLOW_API_KEY` | — | Injected as an `Authorization: Bearer <key>` header on every export. |
|
|
99
|
+
| `service_name` | `GLASSFLOW_SERVICE_NAME` | `unknown_service` | Sets the OpenTelemetry `service.name` resource attribute. |
|
|
100
|
+
| `disabled` | `GLASSFLOW_DISABLED` | `false` | Kill switch. When true, spans are created but never exported. |
|
|
101
|
+
| `sample_rate` | `GLASSFLOW_SAMPLE_RATE` | `1.0` | Head sampling ratio `0.0`–`1.0` (whole-trace; children follow root). |
|
|
102
|
+
| `capture_content` | `GLASSFLOW_CAPTURE_CONTENT` | `true` | When false, prompt/response content is stripped at export (metadata still sent). |
|
|
103
|
+
|
|
104
|
+
`mask` is a code-only option (no env var): pass a callable to `init(mask=...)` and
|
|
105
|
+
it is applied to every content attribute value at export, across our spans and any
|
|
106
|
+
bundled third-party instrumentation. A mask that accepts a `key` keyword also
|
|
107
|
+
receives the attribute key, for per-attribute decisions.
|
|
108
|
+
|
|
109
|
+
```python
|
|
110
|
+
rius.init(mask=lambda value: "[REDACTED]") # redact all captured content
|
|
111
|
+
rius.init(mask=lambda value, *, key: hash_pii(value) if "input" in key else value)
|
|
112
|
+
rius.init(capture_content=False) # drop content entirely, keep metadata
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Auto-instrumentation
|
|
116
|
+
|
|
117
|
+
The SDK bundles existing OTel instrumentors (OpenInference) as optional extras,
|
|
118
|
+
so a single install captures your LLM provider and framework calls. Install the
|
|
119
|
+
extras you need and `init()` enables whatever it finds; the instrumentation
|
|
120
|
+
spans nest under your `@observe` / `start_as_current_span` traces automatically.
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
pip install "glassflow-rius[openai]" # one provider
|
|
124
|
+
pip install "glassflow-rius[instruments]" # everything supported
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
```python
|
|
128
|
+
rius.init() # auto-enables installed instrumentors
|
|
129
|
+
rius.init(instruments=["openai"]) # restrict to specific ones
|
|
130
|
+
rius.init(instruments=[]) # disable auto-instrumentation
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Supported instruments: `openai`, `anthropic`, `langchain`, `llama-index`,
|
|
134
|
+
`litellm`, and `mcp`. Content captured by instrumentors is covered by the same
|
|
135
|
+
`mask` / `capture_content` controls as our own spans.
|
|
136
|
+
|
|
137
|
+
The `mcp` instrument is built in: if the [`mcp`](https://pypi.org/project/mcp/)
|
|
138
|
+
package is installed, every `ClientSession.call_tool()` your agent makes becomes
|
|
139
|
+
a first-class TOOL span (`execute_tool <name>`) with `gen_ai.tool.name`, the
|
|
140
|
+
arguments and result as `input.value`/`output.value`, latency, and error status
|
|
141
|
+
(including tools that return `isError` results).
|
|
142
|
+
|
|
143
|
+
Instrumentors patch libraries process-wide, so a scoped client
|
|
144
|
+
(`init(set_global=False)`) only enables them when `instruments=[...]` is passed
|
|
145
|
+
explicitly. Calling `init()` again while a client is active logs a warning and
|
|
146
|
+
returns the existing client unchanged; call `client.shutdown()` first to
|
|
147
|
+
reconfigure.
|
|
148
|
+
|
|
149
|
+
## Reliability
|
|
150
|
+
|
|
151
|
+
Export is designed to never block or crash your application:
|
|
152
|
+
|
|
153
|
+
- **Async batched export.** Spans are queued in-process and exported in batches
|
|
154
|
+
from a background thread (`BatchSpanProcessor`). Span creation stays fast even
|
|
155
|
+
when the backend is slow or unreachable.
|
|
156
|
+
- **Retries.** Transient failures (connection errors, 429/5xx) are retried with
|
|
157
|
+
exponential backoff and jitter, bounded by the export timeout.
|
|
158
|
+
- **Graceful degradation.** If the backend stays down, spans are dropped and an
|
|
159
|
+
error is logged — exceptions never propagate into application code. A failing
|
|
160
|
+
`mask` callable drops only the affected attribute value (fail closed), never
|
|
161
|
+
the batch.
|
|
162
|
+
- **Flush on shutdown.** Pending spans are flushed automatically at interpreter
|
|
163
|
+
exit. Call `client.flush()` to force an export, or `client.shutdown()` to
|
|
164
|
+
drain and stop.
|
|
165
|
+
|
|
166
|
+
Batching and backpressure are tunable via the standard OpenTelemetry env vars:
|
|
167
|
+
`OTEL_BSP_MAX_QUEUE_SIZE` (default 2048; spans beyond this are dropped),
|
|
168
|
+
`OTEL_BSP_SCHEDULE_DELAY` (default 5000 ms), `OTEL_BSP_MAX_EXPORT_BATCH_SIZE`
|
|
169
|
+
(default 512), and `OTEL_BSP_EXPORT_TIMEOUT` (default 30000 ms).
|
|
170
|
+
|
|
171
|
+
## Development
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
uv sync --group dev
|
|
175
|
+
uv run pytest
|
|
176
|
+
uv run ruff check . && uv run ruff format --check .
|
|
177
|
+
uv run mypy
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Releasing
|
|
181
|
+
|
|
182
|
+
Releases are automated with [release-please](https://github.com/googleapis/release-please)
|
|
183
|
+
and published to PyPI via Trusted Publishing.
|
|
184
|
+
|
|
185
|
+
1. Merge changes to `main` using [Conventional Commits](https://www.conventionalcommits.org/)
|
|
186
|
+
(`feat:` → minor, `fix:` → patch, `feat!:`/`BREAKING CHANGE` → major).
|
|
187
|
+
2. release-please keeps a **Release PR** open that bumps `__version__` and updates
|
|
188
|
+
`CHANGELOG.md`. Merge it when you want to cut a release.
|
|
189
|
+
3. Merging tags `vX.Y.Z`, creates a GitHub Release, and publishes to PyPI automatically.
|
|
190
|
+
|
|
191
|
+
Non-conventional commits are ignored for versioning.
|
|
192
|
+
|
|
193
|
+
## License
|
|
194
|
+
|
|
195
|
+
MIT
|
|
196
|
+
|