Instructor (tracing)
Instructor is a Python LLM SDK for extracting structured data from model responses. Respan captures Instructor calls as chat spans and uses respan-tracing for workflow names, metadata propagation, and export.
Instructor does not create its own workflow trace object. Use @workflow, @task, and propagate_attributes from respan-tracing when you want recognizable trace names around Instructor calls.
Set up Respan
Create an account at platform.respan.ai and grab an API key .
Paste this into your coding agent:
Help me set up Respan tracing with the Instructor integration.
Follow https://www.respan.ai/docs/integrations/instructor.mdSetup
Install packages
pip install respan-tracing respan-instrumentation-instructor instructor openaiSet environment variables
export RESPAN_API_KEY="YOUR_RESPAN_API_KEY"
export OPENAI_API_KEY="YOUR_OPENAI_API_KEY"RESPAN_API_KEY exports traces to Respan. OPENAI_API_KEY is used by the OpenAI client in this tracing-only example.
Initialize and run
from typing import Literal, TypedDict
import instructor
from openai import OpenAI
from respan_tracing import RespanTelemetry, workflow
from respan_tracing.exporters import propagate_attributes
from respan_instrumentation_instructor import InstructorInstrumentor
telemetry = RespanTelemetry(
app_name="instructor-support-api",
is_auto_instrument=False,
)
InstructorInstrumentor().activate()
client = instructor.from_openai(
OpenAI(),
mode=instructor.Mode.TOOLS,
)
class SupportTicket(TypedDict):
customer: str
priority: Literal["low", "medium", "high"]
summary: str
@workflow(name="support_ticket_extraction")
def extract_ticket(message: str) -> SupportTicket:
return client.create(
response_model=SupportTicket,
max_retries=2,
messages=[
{
"role": "user",
"content": f"Extract the support ticket from this message: {message}",
}
],
model="gpt-4o-mini",
)
with propagate_attributes(
customer_identifier="customer_123",
thread_identifier="ticket_thread_abc",
metadata={"feature": "support-ticket-extraction"},
):
ticket = extract_ticket(
"ACME cannot log in before a security audit and needs help today."
)
print(dict(ticket))
View your trace
Open the Traces page to see the named workflow and the Instructor chat span. High-level Instructor calls are named by API when context is available, such as instructor.create, instructor.create_with_completion, instructor.create_iterable, and instructor.async_create.
Configuration
| Parameter | Type | Default | Description |
|---|---|---|---|
api_key | str | None | None | Falls back to RESPAN_API_KEY env var. |
base_url | str | None | None | Falls back to RESPAN_BASE_URL env var. |
app_name | str | None | None | Service name attached to exported spans. |
is_auto_instrument | bool | True | Set to False when activating InstructorInstrumentor manually to avoid duplicate generic OpenAI spans. |
customer_identifier | str | None | None | Default customer identifier for all spans. |
metadata | dict | None | None | Default metadata attached to all spans. |
environment | str | None | None | Environment tag, for example "production". |
InstructorInstrumentor() does not require constructor options. Activate it after RespanTelemetry is initialized.
What gets captured
| Instructor API | Captured by Respan |
|---|---|
client.create(...) | One chat span for the structured-output request. |
client.create_with_completion(...) | One chat span plus the parsed response returned to your code. |
client.create_iterable(...) | One chat span with the consumed iterable output after iteration completes. |
client.create_partial(...) | One chat span for the partial-object request. |
| Async clients | The same span behavior for AsyncInstructor. |
Captured attributes include prompt messages, serialized output, model, token usage, provider, response schema in llm.request.functions, customer_identifier, thread_identifier, and custom metadata.
Attributes
In RespanTelemetry
Set defaults at initialization. These apply to Instructor spans exported by this telemetry instance.
from respan_tracing import RespanTelemetry
from respan_instrumentation_instructor import InstructorInstrumentor
telemetry = RespanTelemetry(
app_name="structured-output-api",
is_auto_instrument=False,
customer_identifier="user_123",
metadata={"service": "extraction-api", "version": "1.0.0"},
)
InstructorInstrumentor().activate()With propagate_attributes
Override per request using a context scope.
from respan_tracing.exporters import propagate_attributes
with propagate_attributes(
customer_identifier="user_456",
thread_identifier="conversation_abc",
metadata={
"route": "invoice-extraction",
"example_script": "01_create.py",
},
):
invoice = client.create(
response_model=Invoice,
messages=[{"role": "user", "content": "Extract this invoice..."}],
model="gpt-4o-mini",
)| Attribute | Type | Description |
|---|---|---|
customer_identifier | str | Identifies the end user in Respan analytics. |
thread_identifier | str | Groups related structured-output calls into a conversation or job. |
metadata | dict | Custom key-value pairs. Merged with default metadata. |
Decorators
Decorators are optional for capture, but recommended for Instructor because Instructor itself does not create named workflow traces. Use @workflow to name an extraction pipeline and @task to split multi-step parsing.
from typing import TypedDict
from respan_tracing import task, workflow
class Invoice(TypedDict):
vendor: str
invoice_id: str
total_usd: float
@task(name="extract_invoice")
def extract_invoice(text: str) -> Invoice:
return client.create(
response_model=Invoice,
messages=[{"role": "user", "content": text}],
model="gpt-4o-mini",
)
@workflow(name="invoice_processing")
def invoice_processing(raw_invoice: str) -> None:
invoice = extract_invoice(raw_invoice)
print(dict(invoice))
invoice_processing("Northwind invoice NW-1042 totals 298.00 USD.")Examples
Structured extraction with create
from typing import TypedDict
class InvoiceLine(TypedDict):
sku: str
quantity: int
unit_price_usd: float
class Invoice(TypedDict):
vendor: str
invoice_id: str
line_items: list[InvoiceLine]
total_usd: float
invoice = client.create(
response_model=Invoice,
messages=[
{
"role": "user",
"content": "Northwind invoice NW-1042 has 2 LOG-001 at 49.50 and 1 SEC-010 at 199.00. Total is 298.00.",
}
],
model="gpt-4o-mini",
)
print(dict(invoice))Parsed output plus raw completion
from typing import Literal, TypedDict
class ReleaseNote(TypedDict):
title: str
category: Literal["feature", "fix", "docs"]
bullets: list[str]
release_note, completion = client.create_with_completion(
response_model=ReleaseNote,
messages=[
{
"role": "user",
"content": "Create a release note for preserving Instructor tool schemas in traces.",
}
],
model="gpt-4o-mini",
)
print(dict(release_note))
print(completion.id)Multiple objects with create_iterable
from typing import Literal, TypedDict
class ActionItem(TypedDict):
owner: str
task: str
status: Literal["new", "blocked", "done"]
items = list(
client.create_iterable(
response_model=ActionItem,
messages=[
{
"role": "user",
"content": "Maya will send the launch checklist. Noah is blocked on legal approval.",
}
],
model="gpt-4o-mini",
)
)
print([dict(item) for item in items])Async create
import asyncio
from typing import TypedDict
import instructor
from openai import AsyncOpenAI
async_client = instructor.from_openai(
AsyncOpenAI(),
mode=instructor.Mode.TOOLS,
)
class ProjectBrief(TypedDict):
title: str
owner: str
milestones: list[str]
@workflow(name="project_brief")
async def create_project_brief() -> ProjectBrief:
return await async_client.create(
response_model=ProjectBrief,
messages=[
{
"role": "user",
"content": "Build a project brief for the Respan Instructor tracing launch.",
}
],
model="gpt-4o-mini",
)
asyncio.run(create_project_brief())Hooks
Instructor hooks run inside the same traced request. Use them for local diagnostics; use Respan metadata for filtering and analytics.
from instructor.core.hooks import HookName
def on_completion_kwargs(**kwargs):
print({"model": kwargs.get("model"), "tool_count": len(kwargs.get("tools", []))})
client.on(HookName.COMPLETION_KWARGS, on_completion_kwargs)
try:
ticket = client.create(
response_model=SupportTicket,
max_retries=2,
messages=[{"role": "user", "content": "ACME needs urgent login help."}],
model="gpt-4o-mini",
)
finally:
client.off(HookName.COMPLETION_KWARGS, on_completion_kwargs)Gateway
See Instructor gateway setup to route this integration through the Respan gateway.