Skip to main content

Installation

Quick Start

What Gets Traced

The DSPy integration automatically captures:
  • Module calls — input kwargs, output text (from Prediction result), configured model name
  • Token usage — prompt and completion tokens from get_lm_usage() or the usage attribute
  • Finish reason — completion finish reason (e.g. stop, length) extracted from the LM history
  • Errors — exception type and message captured in event properties, then re-raised
Works with dspy.Predict, dspy.ChainOfThought, and custom dspy.Module subclasses.

Configuration

ParameterTypeDefaultDescription
api_keystr | NoneNoneRaindrop API key. If omitted, telemetry is disabled.
user_idstr | NoneNoneAssociate all events with a user.
convo_idstr | NoneNoneGroup events into a conversation.
project_idstr | NoneNoneRoute events to a specific project (slug); omit for the default Production project.
tracing_enabledboolTrueEnable OpenTelemetry tracing.
bypass_otel_for_toolsboolTrueBypass OTEL instrumentation for tool calls.
debugboolFalseEnable debug logging (sets logger to DEBUG level).

Projects

Route events to a specific project by passing its slug as project_id:
project_id sets the X-Raindrop-Project-Id header on every event. Omit it (or pass "default") to use your org’s default Production project, which is the existing behavior. The same option is accepted by the create_raindrop_dspy(...) factory. Invalid slugs are ignored with a warning and no header is sent.

Multiple projects in one process

Available in raindrop-ai>=0.0.56. When one service runs several DSPy modules that should report to different projects, create one RaindropDSPy wrapper per project. Each wrapper owns its own raindrop.Raindrop client, so the two route independently — there is no shared module-level state:
Each wrapper owns its configuration and delivery pipeline, so modules handled by different wrappers route independently. To share a single client across wrappers (or with the module-level API), construct a raindrop.Raindrop yourself and pass it via client=:

Identifying Users

Use identify() to associate a user with traits after initialization:
The traits parameter accepts a dictionary with string, int, bool, or float values.

Tracking Signals

Use track_signal() to attach user feedback or edits to an AI event:
ParameterTypeDefaultDescription
event_idstrrequiredThe event ID to associate the signal with.
namestrrequiredSignal name (e.g. "thumbs_up", "user_feedback").
signal_type"default" | "feedback" | "edit""default"Signal type.
timestampstr | NoneNoneOptional ISO-8601 timestamp.
propertiesdict | NoneNoneOptional extra properties.
attachment_idstr | NoneNoneOptional attachment identifier.
commentstr | NoneNoneOptional comment text.
afterstr | NoneNoneOptional “after” value for edit signals.
sentiment"POSITIVE" | "NEGATIVE" | NoneNoneOptional sentiment.

Flushing and Shutdown

Always call shutdown() before your process exits to ensure all telemetry is shipped:

Finish Reason Tracking

The integration extracts finish_reason from the DSPy LM history automatically. After each call, it inspects dspy.settings.lm.history for the last response and reads response.choices[0].finish_reason. The value (e.g. stop, length) is included in event properties as dspy.finish_reason.

Token Tracking

Token usage is extracted from the DSPy Prediction result:
  1. get_lm_usage() — returns {model: {prompt_tokens, completion_tokens}}. Tokens are summed across models if multiple LMs are used.
  2. usage attribute — fallback for older DSPy versions. Supports both dict and object formats.
Tokens appear in event properties as ai.usage.prompt_tokens and ai.usage.completion_tokens.

Factory Function

The create_raindrop_dspy factory function is available for backwards compatibility:

Async Support

DSPy modules with async forward methods are automatically detected and wrapped:

Captured Properties

Each event includes the following properties when available:
PropertyDescription
ai.usage.prompt_tokensInput token count
ai.usage.completion_tokensOutput token count
dspy.finish_reasonCompletion finish reason (e.g. stop, length)
error.typeException class name (on error)
error.messageException message (on error)