Python reference
Generated from the glassflow-ai 0.7.0 docstrings, covering the
Python SDK’s public API surface module by module. Wording fixes happen
upstream in the SDK repository, never here.
glassflow.client
SDK entrypoint: configure OpenTelemetry and export GenAI traces via OTLP.
build_span_exporter
def build_span_exporter(config: GlassflowConfig) -> SpanExporterBuild the default OTLP/HTTP span exporter for a resolved config.
Arguments:
config- A resolved configuration; the exporter posts toconfig.traces_endpointwithconfig.headers.
Returns:
A ready-to-use OTLP/HTTP SpanExporter.
GlassflowClient Objects
class GlassflowClient()Handle over a configured tracer provider.
Returned by init. Exposes the lifecycle operations (flush,
shutdown) and tracer access for the pipeline it owns; the resolved
configuration is available as client.config.
get_tracer
def get_tracer(name: str = TRACER_NAME) -> trace.TracerReturn a tracer bound to this client’s provider.
Arguments:
name- Instrumentation scope name; defaults to the SDK’s own.
flush
def flush(timeout_millis: int = 30_000) -> boolForce-flush pending spans. Returns False on timeout.
shutdown
def shutdown() -> NoneDrain pending spans and stop. Releases the global init() slot.
Also stops the heartbeat thread and sends its final stopped ping,
so the backend can tell a clean shutdown from a vanished agent.
init
def init(*,
endpoint: str | None = None,
api_key: str | None = None,
service_name: str | None = None,
headers: dict[str, str] | None = None,
disabled: bool | None = None,
sample_rate: float | None = None,
capture_content: bool | None = None,
mask: Callable[[Any], Any] | None = None,
instruments: Sequence[str] | None = None,
span_exporter: SpanExporter | None = None,
heartbeat: bool | None = None,
heartbeat_interval: float | None = None,
agent_name: str | None = None,
heartbeat_transport: Callable[[dict[str, Any]], None] | None = None,
partial_spans: bool | None = None,
partial_spans_delay: float | None = None,
set_global: bool = True) -> GlassflowClientInitialize the SDK: build a tracer provider that exports OTLP traces.
Calling init() again while a global client is active logs a warning and
returns the existing client unchanged (the OpenTelemetry global tracer
provider is write-once); call shutdown() on it first to reconfigure.
Arguments:
endpoint- Base OTLP endpoint. Traces are sent to<endpoint>/v1/traces.api_key- API key; injected as anAuthorization: Bearerheader.service_name- Value for theservice.nameresource attribute.headers- Extra headers for the OTLP exporter.disabled- If True, no exporter is attached (spans are dropped).sample_rate- Head sampling ratio 0.0-1.0 (whole-trace). Default 1.0.capture_content- If False, strip prompt/response content at export. Default True.mask- Redact content attribute values at export (applies to all spans).instruments- Auto-instrumentation selection.None(default) enables every bundled instrumentor whose package is installed; a list restricts to those names;[]disables auto-instrumentation. Instrumentors are process-global, so withset_global=Falsethey are only enabled wheninstrumentsis passed explicitly.span_exporter- Override the default OTLP exporter (useful for testing).heartbeat- Enable the agent-lifetime heartbeat thread (GLASSFLOW_HEARTBEAT; default off this release). Pings<endpoint>/v1/heartbeatfrom init until process exit so the platform can tell a live-but-idle agent from a vanished one.heartbeat_interval- Seconds between pings (default 15, clamped to[5, 300]; the backend derives staleness from this).agent_name- Identity heartbeats group under; defaults toservice_name.heartbeat_transport- Override the heartbeat HTTP transport (useful for testing, likespan_exporter).set_global- Register the provider as the global OpenTelemetry provider.
get_tracer
def get_tracer(name: str = TRACER_NAME) -> trace.TracerReturn a tracer from the globally configured provider.
glassflow.observe
The @observe decorator: trace the developer’s own functions.
Wraps sync, async, generator, and async-generator functions (and methods),
recording timing, inputs/outputs, and exceptions as an OpenTelemetry span.
Spans nest automatically via OTel context propagation and are exported through
whatever provider init() configured (a no-op if the SDK was never initialized).
observe
def observe(func: Callable[..., Any] | None = None,
*,
name: str | None = None,
capture_input: bool = True,
capture_output: bool = True,
kind: SpanKind = SpanKind.CHAIN) -> AnyDecorate a function so each call is traced as a span.
Usable bare (@observe) or parameterized (@observe(name=..., ...)).
Supports sync functions, async def functions, generators, and async
generators; for generators the span covers the whole iteration and the
tracing context is attached only around each step. Exceptions are
recorded with ERROR status and always re-raised.
Arguments:
func- The decorated function (filled in by bare@observeusage).name- Span name; defaults to the function’s__qualname__.capture_input- Record call arguments as JSON ininput.value.capture_output- Record the return value as JSON inoutput.value.kind- Span taxonomy (openinference.span.kind); defaultCHAIN.
Returns:
The wrapped function (or a decorator, when used parameterized).
glassflow.spans
Manual span API.
Two surfaces, following the OpenTelemetry / Langfuse / Laminar convention:
start_as_current_spanis the context manager: it activates the span in the OTel context (so children nest under it) and auto-ends it.start_spanis manual: it returns anObservationyou must.end(). The span is parented to the current span at creation but is NOT set as current and does NOT auto-record exceptions. For lifetimes awithblock can’t express (streaming, callbacks, passing a span across boundaries).
start_generation / start_as_current_generation are the LLM-specialized
equivalents.
Observation Objects
class Observation()Handle for annotating a span from start_span / start_as_current_span.
Wraps an OpenTelemetry span and exposes the annotation surface for generic
(non-LLM) spans: input, output, and arbitrary attributes. Inputs and
outputs are serialized to JSON (with a repr fallback) and truncated at
8192 characters.
set_input
def set_input(value: Any) -> NoneRecord the span input (the input.value attribute).
Arguments:
value- Any value; serialized to JSON with areprfallback.
set_output
def set_output(value: Any) -> NoneRecord the span output (the output.value attribute).
Arguments:
value- Any value; serialized to JSON with areprfallback.
set_attribute
def set_attribute(key: str, value: Any) -> NoneSet an arbitrary attribute on the underlying span.
Arguments:
key- Attribute name.value- An OpenTelemetry-compatible attribute value.
update
def update(*, input: Any = None, output: Any = None) -> NoneRecord input and/or output in one call.
Arguments:
input- When notNone, forwarded to :meth:set_input.output- When notNone, forwarded to :meth:set_output.
end
def end() -> NoneEnd the underlying span.
Required for spans created with start_span; spans from
start_as_current_span end automatically when the block exits.
start_span
def start_span(name: str,
*,
kind: SpanKind = SpanKind.CHAIN,
input: Any = None) -> ObservationCreate a span and return an Observation. You MUST call .end().
The span is parented to the current span at creation, but is not set as the
current span and does not auto-record exceptions. Use start_as_current_span
for block-scoped tracing.
start_as_current_span
@contextmanager
def start_as_current_span(name: str,
*,
kind: SpanKind = SpanKind.CHAIN,
input: Any = None) -> Iterator[Observation]Open a span as the current span and yield an Observation; auto-ends.
Exceptions raised in the block are recorded and set the span status to ERROR
(OpenTelemetry’s start_as_current_span default), then re-raised.
glassflow.generation
LLM generation capture helpers.
start_as_current_generation (context manager) and start_generation (manual,
requires .end()) open an LLM-kind span and return a Generation handle for
recording gen_ai-native attributes (messages, model, usage, finish reason). LLM
spans are therefore readable by any gen_ai-compatible consumer.
Generation Objects
class Generation()Handle for recording gen_ai attributes on an LLM span.
Returned by start_generation and start_as_current_generation.
Messages passed to :meth:set_input / :meth:set_output are normalized
to the GenAI message shape ({"role", "parts": [...]}): bare strings,
OpenAI-style dicts (including tool_calls and tool responses), and
multimodal content lists are all accepted.
set_input
def set_input(messages: Messages) -> NoneRecord the request messages (gen_ai.input.messages).
Arguments:
messages- A string or list of messages in any supported format; bare strings default to theuserrole.
set_output
def set_output(messages: Messages) -> NoneRecord the response messages (gen_ai.output.messages).
Arguments:
messages- A string or list of messages in any supported format; bare strings default to theassistantrole.
set_response_model
def set_response_model(response_model: str) -> NoneRecord the model that produced the response (gen_ai.response.model).
Arguments:
response_model- Model identifier as reported by the provider, which may differ from the requested model.
set_usage
def set_usage(*,
input_tokens: int | None = None,
output_tokens: int | None = None) -> NoneRecord token usage (gen_ai.usage.input_tokens / output_tokens).
Send token counts, never cost: cost is computed server-side from model pricing.
Arguments:
input_tokens- Prompt tokens consumed, when known.output_tokens- Completion tokens produced, when known.
record_first_token
def record_first_token() -> NoneMark the arrival of the first streamed token (gen_ai.first_token event).
Call from a streaming loop when the first content chunk arrives; the
backend derives time-to-first-token as the event time minus the span
start. Idempotent: only the first call records; safe to call
unconditionally per chunk. A no-op after end().
set_finish_reasons
def set_finish_reasons(reasons: str | list[str]) -> NoneRecord why generation stopped (gen_ai.response.finish_reasons).
Arguments:
reasons- A single reason (wrapped into a list) or a list of reasons, e.g."stop","length","tool_calls".
update
def update(*,
input: Messages | None = None,
output: Messages | None = None) -> NoneRecord input and/or output messages in one call.
Arguments:
input- When notNone, forwarded to :meth:set_input.output- When notNone, forwarded to :meth:set_output.
end
def end() -> NoneEnd the underlying span.
Required for generations created with start_generation; spans from
start_as_current_generation end automatically when the block exits.
start_generation
def start_generation(name: str,
*,
model: str | None = None,
provider: str | None = None,
input: Messages | None = None,
model_parameters: dict[str, Any] | None = None,
operation: str = "chat") -> GenerationCreate an LLM-kind span and return a Generation. You MUST call .end().
Manual counterpart to start_as_current_generation: not set as current, no
auto-recording of exceptions.
Arguments:
name- Span name.model- Requested model (gen_ai.request.model).provider- Provider name (gen_ai.provider.name), e.g."openai".input- Request messages, recorded immediately viaset_input.model_parameters- Request parameters, each recorded asgen_ai.request.<key>.operation- Operation name (gen_ai.operation.name); default"chat".
Returns:
A Generation handle; call .end() when the call completes.
start_as_current_generation
@contextmanager
def start_as_current_generation(
name: str,
*,
model: str | None = None,
provider: str | None = None,
input: Messages | None = None,
model_parameters: dict[str, Any] | None = None,
operation: str = "chat") -> Iterator[Generation]Open an LLM-kind span as the current span and yield a Generation; auto-ends.
Children created inside the block nest under this span, and exceptions
raised in the block are recorded with ERROR status, then re-raised.
Accepts the same arguments as start_generation.
Yields:
A Generation handle for recording messages, usage, and response
metadata; the span ends when the block exits.
glassflow.config
Configuration resolution for the GlassFlow SDK.
Values are resolved with the precedence: explicit arguments > environment variables > built-in defaults.
GlassflowConfig Objects
@dataclass(frozen=True)
class GlassflowConfig()Resolved, immutable SDK configuration.
Produced by resolve_config (arguments over environment over
defaults); consumed by init and build_span_exporter.
traces_endpoint
@property
def traces_endpoint() -> strFull OTLP/HTTP traces URL (<endpoint>/v1/traces).
heartbeat_endpoint
@property
def heartbeat_endpoint() -> strHeartbeat URL (<endpoint>/v1/heartbeat), same host as traces.
resolve_config
def resolve_config(
*,
endpoint: str | None = None,
api_key: str | None = None,
service_name: str | None = None,
headers: dict[str, str] | None = None,
disabled: bool | None = None,
sample_rate: float | None = None,
capture_content: bool | None = None,
heartbeat: bool | None = None,
heartbeat_interval: float | None = None,
agent_name: str | None = None,
partial_spans: bool | None = None,
partial_spans_delay: float | None = None) -> GlassflowConfigResolve SDK configuration from arguments, environment, then defaults.
Explicit arguments win over GLASSFLOW_* environment variables, which
win over built-in defaults. sample_rate is clamped to [0.0, 1.0]
with a warning; boolean environment variables accept 1/true/
yes/on (case-insensitive).
Arguments:
endpoint- Base OTLP endpoint (GLASSFLOW_ENDPOINT).api_key- Bearer token for the managed platform (GLASSFLOW_API_KEY);Nonesends no Authorization header.service_name-service.nameresource attribute (GLASSFLOW_SERVICE_NAME).headers- Extra exporter headers; an explicitAuthorizationentry wins overapi_key.disabled- Kill switch (GLASSFLOW_DISABLED); spans are dropped in-process.sample_rate- Head-sampling ratio for root traces (GLASSFLOW_SAMPLE_RATE).capture_content- WhenFalse, content attributes are stripped at export (GLASSFLOW_CAPTURE_CONTENT).heartbeat- Enable the agent-lifetime heartbeat thread (GLASSFLOW_HEARTBEAT). Off by default this release.heartbeat_interval- Seconds between pings (GLASSFLOW_HEARTBEAT_INTERVAL), clamped to[5, 300]; the backend derives staleness from this, so the bounds are part of the wire contract.agent_name- Identity heartbeats group under (GLASSFLOW_AGENT_NAME); defaults toservice_nameso the agents view and the traces view agree on what an “agent” is.partial_spans- Export a content-free pending snapshot of every sampled span at span START (GLASSFLOW_PARTIAL_SPANS), so in-flight work is visible and crashes leave a record. Off by default until the backend’s unfinished-spans storage ships.partial_spans_delay- Debounce for pending snapshots (GLASSFLOW_PARTIAL_SPANS_DELAY), clamped to[0, 60]seconds.0(default) emits at span start;Nemits only if the span is still open after N seconds — spans that finish sooner cost no network at all.
Returns:
The resolved, immutable GlassflowConfig.
glassflow.semconv
Semantic conventions for the GlassFlow SDK.
Centralizes the OpenTelemetry instrumentation-scope name, the span-kind taxonomy,
and span attribute keys. Span kinds use the OpenInference openinference.span.kind
values (understood across the ecosystem); LLM specifics use OTel GenAI gen_ai.*.
SpanKind Objects
class SpanKind(str, Enum)Observation kind. Values are OpenInference openinference.span.kind values.
AGENT: an agent invocation or runLLM: a model call (generations use this)TOOL: a tool executionRETRIEVER: a retrieval / search stepEMBEDDING: an embedding computationCHAIN: a generic processing step (the default)
kind_attributes
def kind_attributes(kind: SpanKind) -> dict[str, str]Identity attributes for a span of kind, for setting at CREATION.
Pending snapshots (pending.py) are built at on_start, so taxonomy set
via set_attribute afterwards is invisible to them — passing these at
span creation is what makes a pending span classifiable.
set_span_kind
def set_span_kind(span: Span, kind: SpanKind) -> NoneStamp a span with its OpenInference kind and (if applicable) gen_ai operation.