Skip to Content
Launching the open-source gateway

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.md

Setup

Install packages

Terminal
pip install respan-tracing respan-instrumentation-instructor instructor openai

Set environment variables

Terminal
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

Python
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

ParameterTypeDefaultDescription
api_keystr | NoneNoneFalls back to RESPAN_API_KEY env var.
base_urlstr | NoneNoneFalls back to RESPAN_BASE_URL env var.
app_namestr | NoneNoneService name attached to exported spans.
is_auto_instrumentboolTrueSet to False when activating InstructorInstrumentor manually to avoid duplicate generic OpenAI spans.
customer_identifierstr | NoneNoneDefault customer identifier for all spans.
metadatadict | NoneNoneDefault metadata attached to all spans.
environmentstr | NoneNoneEnvironment tag, for example "production".

InstructorInstrumentor() does not require constructor options. Activate it after RespanTelemetry is initialized.

What gets captured

Instructor APICaptured 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 clientsThe 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.

Python
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.

Python
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", )
AttributeTypeDescription
customer_identifierstrIdentifies the end user in Respan analytics.
thread_identifierstrGroups related structured-output calls into a conversation or job.
metadatadictCustom 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.

Python
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

Python
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

Python
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

Python
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

Python
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.

Python
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.