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.
Files changed (45) hide show
  1. glassflow_rius-0.8.0/.github/dependabot.yml +6 -0
  2. glassflow_rius-0.8.0/.github/workflows/ci.yml +69 -0
  3. glassflow_rius-0.8.0/.github/workflows/release.yml +102 -0
  4. glassflow_rius-0.8.0/.gitignore +22 -0
  5. glassflow_rius-0.8.0/.pre-commit-config.yaml +7 -0
  6. glassflow_rius-0.8.0/.release-please-manifest.json +3 -0
  7. glassflow_rius-0.8.0/CHANGELOG.md +112 -0
  8. glassflow_rius-0.8.0/CLAUDE.md +89 -0
  9. glassflow_rius-0.8.0/LICENSE +21 -0
  10. glassflow_rius-0.8.0/PKG-INFO +196 -0
  11. glassflow_rius-0.8.0/README.md +150 -0
  12. glassflow_rius-0.8.0/pyproject.toml +104 -0
  13. glassflow_rius-0.8.0/release-please-config.json +12 -0
  14. glassflow_rius-0.8.0/src/rius/__init__.py +28 -0
  15. glassflow_rius-0.8.0/src/rius/_serde.py +24 -0
  16. glassflow_rius-0.8.0/src/rius/client.py +279 -0
  17. glassflow_rius-0.8.0/src/rius/config.py +236 -0
  18. glassflow_rius-0.8.0/src/rius/generation.py +324 -0
  19. glassflow_rius-0.8.0/src/rius/heartbeat.py +258 -0
  20. glassflow_rius-0.8.0/src/rius/instrumentation.py +108 -0
  21. glassflow_rius-0.8.0/src/rius/instrumentation_mcp.py +138 -0
  22. glassflow_rius-0.8.0/src/rius/masking.py +139 -0
  23. glassflow_rius-0.8.0/src/rius/observe.py +185 -0
  24. glassflow_rius-0.8.0/src/rius/pending.py +266 -0
  25. glassflow_rius-0.8.0/src/rius/py.typed +0 -0
  26. glassflow_rius-0.8.0/src/rius/semconv.py +160 -0
  27. glassflow_rius-0.8.0/src/rius/spans.py +133 -0
  28. glassflow_rius-0.8.0/tests/conftest.py +43 -0
  29. glassflow_rius-0.8.0/tests/test_client.py +93 -0
  30. glassflow_rius-0.8.0/tests/test_config.py +118 -0
  31. glassflow_rius-0.8.0/tests/test_generation.py +286 -0
  32. glassflow_rius-0.8.0/tests/test_heartbeat.py +448 -0
  33. glassflow_rius-0.8.0/tests/test_instrumentation.py +230 -0
  34. glassflow_rius-0.8.0/tests/test_instrumentation_registry.py +31 -0
  35. glassflow_rius-0.8.0/tests/test_lifecycle.py +63 -0
  36. glassflow_rius-0.8.0/tests/test_masking.py +146 -0
  37. glassflow_rius-0.8.0/tests/test_mcp_instrumentation.py +270 -0
  38. glassflow_rius-0.8.0/tests/test_observe.py +196 -0
  39. glassflow_rius-0.8.0/tests/test_pending_delay.py +141 -0
  40. glassflow_rius-0.8.0/tests/test_pending_spans.py +165 -0
  41. glassflow_rius-0.8.0/tests/test_reliability.py +152 -0
  42. glassflow_rius-0.8.0/tests/test_semconv.py +36 -0
  43. glassflow_rius-0.8.0/tests/test_serde.py +26 -0
  44. glassflow_rius-0.8.0/tests/test_spans.py +76 -0
  45. glassflow_rius-0.8.0/uv.lock +1884 -0
@@ -0,0 +1,6 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: github-actions
4
+ directory: /
5
+ schedule:
6
+ interval: weekly
@@ -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,7 @@
1
+ repos:
2
+ - repo: https://github.com/astral-sh/ruff-pre-commit
3
+ rev: v0.15.20
4
+ hooks:
5
+ - id: ruff
6
+ args: [--fix]
7
+ - id: ruff-format
@@ -0,0 +1,3 @@
1
+ {
2
+ ".": "0.8.0"
3
+ }
@@ -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 -&gt; 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
+