Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:
runs-on: ubuntu-latest
environment:
name: pypi
url: https://pypi.org/project/glassflow-ai/
url: https://pypi.org/project/glassflow-rius/
permissions:
id-token: write # OIDC for PyPI Trusted Publishing
contents: read
Expand Down Expand Up @@ -98,5 +98,5 @@ jobs:
-e 's/\[([^]]+)\]\(([^)]+)\)/<\2|\1>/g' \
| head -c 3500)
jq -n --arg tag "$TAG" --arg url "$url" --arg body "$body" \
'{text: (":package: *glassflow-ai \($tag)* is on PyPI (<\($url)|release notes>)\n\n\($body)")}' \
'{text: (":package: *glassflow-rius \($tag)* is on PyPI (<\($url)|release notes>)\n\n\($body)")}' \
| curl -sS --fail-with-body -X POST -H 'Content-Type: application/json' -d @- "$SLACK_WEBHOOK"
8 changes: 4 additions & 4 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# CLAUDE.md — glassflow-python
# CLAUDE.md — rius-sdk-python

Conventions for the GlassFlow instrumentation SDK (Python). Follow these; they
override generic defaults.
Expand All @@ -10,7 +10,7 @@ emits **OpenTelemetry GenAI (`gen_ai.*`) traces over OTLP/HTTP** to the managed
GlassFlow platform (or any OTLP-compatible backend). GlassFlow is **managed-only**
— there is no self-host, so config targets the managed endpoint.

- Distribution name: `glassflow-ai` (PyPI) · import package: `glassflow`
- Distribution name: `glassflow-rius` (PyPI) · import package: `rius`
- `src/` layout; tests in `tests/`
- Python-first; the TS/JS SDK lives in a separate repository

Expand All @@ -33,7 +33,7 @@ formatter/linter (no black/isort/flake8). Commit `uv.lock`.
- **Python 3.10+**. Use modern typing (`X | None`, built-in generics).
- **Fully typed**; ship `py.typed`. `mypy --strict` must pass (config in `pyproject.toml`).
- **Ruff** for both lint and format; line length 100.
- Public API is re-exported from `glassflow/__init__.py` and listed in `__all__`.
- Public API is re-exported from `rius/__init__.py` and listed in `__all__`.
- Prefer **dependency injection over mocking** for testability
(e.g. `init(span_exporter=...)` instead of patching the OTLP exporter).

Expand Down Expand Up @@ -67,7 +67,7 @@ formatter/linter (no black/isort/flake8). Commit `uv.lock`.
## Versioning & releases

- **Single source of truth** for the version: `__version__` in
`src/glassflow/__init__.py` (annotated `# x-release-please-version`);
`src/rius/__init__.py` (annotated `# x-release-please-version`);
hatchling reads it. **Do not** edit the version anywhere else or hand-write
`CHANGELOG.md`.
- Releases are automated by **release-please** + **PyPI Trusted Publishing**
Expand Down
28 changes: 14 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# GlassFlow Python SDK

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

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

## Quickstart

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

glassflow.init(
rius.init(
api_key="glassflow_...", # or set GLASSFLOW_API_KEY
service_name="my-agent", # or set GLASSFLOW_SERVICE_NAME
)
Expand Down Expand Up @@ -61,9 +61,9 @@ bundled third-party instrumentation. A mask that accepts a `key` keyword also
receives the attribute key, for per-attribute decisions.

```python
glassflow.init(mask=lambda value: "[REDACTED]") # redact all captured content
glassflow.init(mask=lambda value, *, key: hash_pii(value) if "input" in key else value)
glassflow.init(capture_content=False) # drop content entirely, keep metadata
rius.init(mask=lambda value: "[REDACTED]") # redact all captured content
rius.init(mask=lambda value, *, key: hash_pii(value) if "input" in key else value)
rius.init(capture_content=False) # drop content entirely, keep metadata
```

## Auto-instrumentation
Expand All @@ -74,14 +74,14 @@ extras you need and `init()` enables whatever it finds; the instrumentation
spans nest under your `@observe` / `start_as_current_span` traces automatically.

```bash
pip install "glassflow-ai[openai]" # one provider
pip install "glassflow-ai[instruments]" # everything supported
pip install "glassflow-rius[openai]" # one provider
pip install "glassflow-rius[instruments]" # everything supported
```

```python
glassflow.init() # auto-enables installed instrumentors
glassflow.init(instruments=["openai"]) # restrict to specific ones
glassflow.init(instruments=[]) # disable auto-instrumentation
rius.init() # auto-enables installed instrumentors
rius.init(instruments=["openai"]) # restrict to specific ones
rius.init(instruments=[]) # disable auto-instrumentation
```

Supported instruments: `openai`, `anthropic`, `langchain`, `llama-index`,
Expand Down
18 changes: 9 additions & 9 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[project]
name = "glassflow-ai"
name = "glassflow-rius"
dynamic = ["version"]
description = "GlassFlow SDK — OpenTelemetry-native tracing for AI agents and LLM applications."
description = "GlassFlow Rius SDK — OpenTelemetry-native tracing for AI agents and LLM applications."
readme = "README.md"
requires-python = ">=3.10"
license = "MIT"
Expand All @@ -15,7 +15,7 @@ keywords = [
"llm",
"agents",
"genai",
"glassflow",
"rius",
]
classifiers = [
"Development Status :: 3 - Alpha",
Expand All @@ -36,7 +36,7 @@ dependencies = [
]

[project.optional-dependencies]
# Auto-instrumentation extras — one per entry in glassflow.instrumentation.REGISTRY.
# Auto-instrumentation extras — one per entry in rius.instrumentation.REGISTRY.
# Floors are the versions the registry smoke test last validated against; no caps
# (these packages break within 0.x either way — CI catches forward breakage).
openai = ["openinference-instrumentation-openai>=0.1.52"]
Expand All @@ -55,19 +55,19 @@ instruments = [
]

[project.urls]
Homepage = "https://github.com/glassflow/glassflow-python"
Repository = "https://github.com/glassflow/glassflow-python.git"
Issues = "https://github.com/glassflow/glassflow-python/issues"
Homepage = "https://github.com/glassflow/rius-sdk-python"
Repository = "https://github.com/glassflow/rius-sdk-python.git"
Issues = "https://github.com/glassflow/rius-sdk-python/issues"

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[tool.hatch.version]
path = "src/glassflow/__init__.py"
path = "src/rius/__init__.py"

[tool.hatch.build.targets.wheel]
packages = ["src/glassflow"]
packages = ["src/rius"]

[dependency-groups]
dev = [
Expand Down
4 changes: 2 additions & 2 deletions release-please-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
"packages": {
".": {
"release-type": "python",
"package-name": "glassflow-ai",
"package-name": "glassflow-rius",
"bump-minor-pre-major": true,
"include-component-in-tag": false,
"extra-files": ["src/glassflow/__init__.py"]
"extra-files": ["src/rius/__init__.py"]
}
}
}
File renamed without changes.
File renamed without changes.
8 changes: 4 additions & 4 deletions src/glassflow/client.py → src/rius/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ def init(
with _lock:
if set_global and _current_client is not None and not _current_client._is_shutdown:
logger.warning(
"glassflow.init() was already called; keeping the existing configuration "
"rius.init() was already called; keeping the existing configuration "
"(the OpenTelemetry global tracer provider is write-once). Call .shutdown() "
"on the existing client first if you need to reconfigure."
)
Expand Down Expand Up @@ -214,7 +214,7 @@ def _do_init(
resource = Resource.create(
{
"service.name": config.service_name,
"telemetry.distro.name": "glassflow-ai",
"telemetry.distro.name": "glassflow-rius",
"telemetry.distro.version": __version__,
}
)
Expand All @@ -240,9 +240,9 @@ def _do_init(
trace.set_tracer_provider(provider)
if trace.get_tracer_provider() is not provider:
logger.warning(
"could not register the glassflow tracer provider as the OpenTelemetry "
"could not register the rius tracer provider as the OpenTelemetry "
"global (another provider is already set); spans from @observe and "
"glassflow.get_tracer() will keep using the pre-existing provider. Use "
"rius.get_tracer() will keep using the pre-existing provider. Use "
"the returned client's get_tracer() for scoped tracing."
)

Expand Down
2 changes: 1 addition & 1 deletion src/glassflow/config.py → src/rius/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

logger = logging.getLogger(__name__)

DEFAULT_ENDPOINT = "https://ingest.glassflow.dev"
DEFAULT_ENDPOINT = "https://ingest.eu.console.rius-glassflow.com"
DEFAULT_SERVICE_NAME = "unknown_service"

ENV_ENDPOINT = "GLASSFLOW_ENDPOINT"
Expand Down
File renamed without changes.
2 changes: 1 addition & 1 deletion src/glassflow/heartbeat.py → src/rius/heartbeat.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ def instance_id(self) -> str:

def start(self) -> None:
"""Start the daemon thread; first ping goes out immediately."""
self._thread = threading.Thread(target=self._run, name="glassflow-heartbeat", daemon=True)
self._thread = threading.Thread(target=self._run, name="rius-heartbeat", daemon=True)
self._thread.start()
atexit.register(self.stop)
# A forked child must never reuse the parent's identity; the shared
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
We reuse existing OTel instrumentors rather than rebuilding provider/framework
instrumentation. The registry below maps a short name to an instrumentor class;
packages are imported lazily, so nothing here adds a hard dependency. Install
via extras (``pip install glassflow-ai[openai]``) and ``init()`` enables what
via extras (``pip install glassflow-rius[openai]``) and ``init()`` enables what
it finds, passing our tracer provider so instrumentation spans nest under ours.
"""

Expand Down Expand Up @@ -47,7 +47,7 @@ class InstrumentorSpec:
),
InstrumentorSpec("litellm", "openinference.instrumentation.litellm", "LiteLLMInstrumentor"),
# ours — first-class MCP tool-call spans (see instrumentation_mcp.py)
InstrumentorSpec("mcp", "glassflow.instrumentation_mcp", "MCPInstrumentor"),
InstrumentorSpec("mcp", "rius.instrumentation_mcp", "MCPInstrumentor"),
)


Expand Down Expand Up @@ -82,7 +82,7 @@ def enable_instrumentations(
if instruments is not None:
logger.warning(
"instrument %r requested but %r is not installed; "
'install it via `pip install "glassflow-ai[%s]"`',
'install it via `pip install "glassflow-rius[%s]"`',
spec.name,
spec.module,
spec.name,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
over MCP becomes a TOOL-kind span: tool name, arguments, result, latency, and
error status. Generic instrumentation SDKs cover MCP unevenly (the OpenInference
MCP package only propagates context; it creates no spans), so we instrument it
ourselves. Registered in :mod:`glassflow.instrumentation` under ``"mcp"``; the
ourselves. Registered in :mod:`rius.instrumentation` under ``"mcp"``; the
top-level import of ``mcp`` below makes an environment without the package look
"not installed" to the registry, exactly like a missing third-party instrumentor.

Expand Down
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion src/glassflow/pending.py → src/rius/pending.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ def __init__(

def _start_thread(self) -> None:
self._thread = threading.Thread(
target=self._run, name="glassflow-pending-scheduler", daemon=True
target=self._run, name="rius-pending-scheduler", daemon=True
)
self._thread.start()

Expand Down
File renamed without changes.
3 changes: 3 additions & 0 deletions src/glassflow/semconv.py → src/rius/semconv.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
from opentelemetry.trace import Span

# Instrumentation scope name (stamped on every span as otel.scope.name).
# Deliberately still "glassflow" after the Rius rebrand (GLA2-322): the value
# is wire-visible and the backend keys on it; renaming needs backend
# coordination, tracked separately.
TRACER_NAME = "glassflow"

# --- Attribute keys ---
Expand Down
File renamed without changes.
6 changes: 3 additions & 3 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,11 @@ def exported_spans() -> InMemorySpanExporter:


@pytest.fixture(autouse=True)
def _reset_glassflow_lifecycle() -> "Iterator[None]":
def _reset_rius_lifecycle() -> "Iterator[None]":
"""Clear module-level init()/instrumentation state between tests."""
yield
from glassflow import client as client_module
from glassflow import instrumentation as instrumentation_module
from rius import client as client_module
from rius import instrumentation as instrumentation_module

client_module._current_client = None
instrumentation_module._ENABLED.clear()
10 changes: 5 additions & 5 deletions tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.trace.export.in_memory_span_exporter import InMemorySpanExporter

from glassflow import init
from glassflow.client import GlassflowClient, build_span_exporter
from glassflow.config import resolve_config
from rius import init
from rius.client import GlassflowClient, build_span_exporter
from rius.config import resolve_config


def _memory_client(**kwargs: object) -> tuple[GlassflowClient, InMemorySpanExporter]:
Expand Down Expand Up @@ -39,15 +39,15 @@ def test_resource_has_service_name() -> None:
def test_resource_uses_distro_not_sdk_identity() -> None:
# Spec: telemetry.sdk.name MUST be "opentelemetry"; distributions identify
# themselves via telemetry.distro.*.
from glassflow import __version__
from rius import __version__

client, exporter = _memory_client()
with client.get_tracer().start_as_current_span("op"):
pass
client.flush()
resource_attrs = exporter.get_finished_spans()[0].resource.attributes
assert resource_attrs["telemetry.sdk.name"] == "opentelemetry"
assert resource_attrs["telemetry.distro.name"] == "glassflow-ai"
assert resource_attrs["telemetry.distro.name"] == "glassflow-rius"
assert resource_attrs["telemetry.distro.version"] == __version__


Expand Down
2 changes: 1 addition & 1 deletion tests/test_config.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import pytest

from glassflow.config import DEFAULT_ENDPOINT, GlassflowConfig, resolve_config
from rius.config import DEFAULT_ENDPOINT, GlassflowConfig, resolve_config

ENV_VARS = [
"GLASSFLOW_ENDPOINT",
Expand Down
2 changes: 1 addition & 1 deletion tests/test_generation.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from opentelemetry.sdk.trace import Event, ReadableSpan
from opentelemetry.sdk.trace.export.in_memory_span_exporter import InMemorySpanExporter

from glassflow import start_as_current_generation, start_generation
from rius import start_as_current_generation, start_generation

# --- context manager: start_as_current_generation ---

Expand Down
12 changes: 6 additions & 6 deletions tests/test_heartbeat.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@
import pytest
from opentelemetry.sdk.trace.export.in_memory_span_exporter import InMemorySpanExporter

from glassflow import init
from glassflow.config import resolve_config
from glassflow.heartbeat import HeartbeatSender, OpenRootSpanTracker
from rius import init
from rius.config import resolve_config
from rius.heartbeat import HeartbeatSender, OpenRootSpanTracker

# ---------------------------------------------------------------------------
# Config surface
Expand Down Expand Up @@ -288,7 +288,7 @@ def broken(_: dict[str, Any]) -> None:
tracker=OpenRootSpanTracker(),
transport=broken,
)
with caplog.at_level(logging.DEBUG, logger="glassflow.heartbeat"):
with caplog.at_level(logging.DEBUG, logger="rius.heartbeat"):
sender._send_ping() # noqa: SLF001
sender._send_ping() # noqa: SLF001
warnings = [r for r in caplog.records if r.levelno == logging.WARNING]
Expand Down Expand Up @@ -372,7 +372,7 @@ def log_message(self, *args: Any) -> None:
# init() then exit normally WITHOUT calling shutdown(): the atexit
# hook alone must produce the stopped ping.
result = subprocess.run(
[sys.executable, "-c", "import glassflow; glassflow.init(instruments=[])"],
[sys.executable, "-c", "import rius; rius.init(instruments=[])"],
env=env,
timeout=30,
capture_output=True,
Expand Down Expand Up @@ -437,7 +437,7 @@ def test_final_ping_timeout_defaults_shorter_than_ping_timeout() -> None:

def test_stopped_senders_leave_the_fork_registry() -> None:
"""init/shutdown cycles must not accumulate fork-handler references."""
from glassflow.heartbeat import _active_senders
from rius.heartbeat import _active_senders

before = len(_active_senders)
sent: list[dict[str, Any]] = []
Expand Down
6 changes: 3 additions & 3 deletions tests/test_instrumentation.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
import pytest
from opentelemetry.sdk.trace.export.in_memory_span_exporter import InMemorySpanExporter

import glassflow.instrumentation as instrumentation
from glassflow import init
from glassflow.instrumentation import InstrumentorSpec
import rius.instrumentation as instrumentation
from rius import init
from rius.instrumentation import InstrumentorSpec


class FakeInstrumentor:
Expand Down
Loading
Loading