Skip to content

Commit b623895

Browse files
feat: Add tracing for OpenAI chat completions parse method
Co-authored-by: vinicius <vinicius@openlayer.com>
1 parent 1ec6ca7 commit b623895

File tree

6 files changed

+1430
-0
lines changed

6 files changed

+1430
-0
lines changed

CURSOR_MEMORY.md

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
# Cursor Memory - OpenLayer Python SDK
2+
3+
## Project Context
4+
This is the openlayer-python repository, which provides tracing and monitoring capabilities for various LLM providers including OpenAI, Anthropic, Azure OpenAI, and others.
5+
6+
## Current Task: OpenAI Parse Method Tracing Support
7+
**Branch**: `cursor/add-tracing-for-openai-chat-completions-parse-93b9`
8+
**Goal**: Add tracing support for OpenAI's `chat.completions.parse()` method
9+
10+
## Key Findings
11+
12+
### OpenAI Parse vs Create Methods
13+
- `create()`: Returns unstructured text responses
14+
- `parse()`: Returns structured outputs that conform to a schema (Pydantic models or JSON Schema)
15+
- The parse method is newer and designed for structured data extraction
16+
- Both methods have similar input parameters but different output handling
17+
18+
### Current Tracing Architecture
19+
**File**: `src/openlayer/lib/integrations/openai_tracer.py`
20+
21+
Key components:
22+
1. **Main Function**: `trace_openai()` - Patches the client to add tracing
23+
2. **Handler Functions**:
24+
- `handle_streaming_create()` - For streaming responses
25+
- `handle_non_streaming_create()` - For regular responses
26+
3. **Tracing Pipeline**:
27+
- `create_trace_args()` - Prepares trace data
28+
- `add_to_trace()` - Sends data to Openlayer
29+
4. **Output Parsing**: `parse_non_streaming_output_data()` - Extracts meaningful data from responses
30+
31+
### Current Implementation Pattern
32+
```python
33+
# Current pattern in trace_openai():
34+
create_func = client.chat.completions.create
35+
client.chat.completions.create = traced_create_func
36+
```
37+
38+
### Architecture Analysis
39+
40+
#### File Structure
41+
- **Main Entry Point**: `src/openlayer/lib/__init__.py` - Exposes `trace_openai()` and `trace_async_openai()`
42+
- **Sync Tracer**: `src/openlayer/lib/integrations/openai_tracer.py` - Core implementation
43+
- **Async Tracer**: `src/openlayer/lib/integrations/async_openai_tracer.py` - Async implementation
44+
45+
#### Key Functions in Sync Tracer
46+
1. `trace_openai()` - Main entry point that patches the client
47+
2. `handle_streaming_create()` - Handles streaming responses
48+
3. `handle_non_streaming_create()` - Handles regular responses
49+
4. `parse_non_streaming_output_data()` - Extracts output from responses
50+
5. `create_trace_args()` - Creates standardized trace data structure
51+
6. `add_to_trace()` - Sends trace data to Openlayer
52+
53+
#### Async Tracer Dependencies
54+
The async tracer imports utilities from sync tracer:
55+
```python
56+
from .openai_tracer import (
57+
get_model_parameters,
58+
create_trace_args,
59+
add_to_trace,
60+
parse_non_streaming_output_data,
61+
)
62+
```
63+
64+
### Design Principles
65+
1. **Minimal Intrusion**: Patch methods without changing user code
66+
2. **Comprehensive Tracing**: Capture timing, tokens, model params, I/O
67+
3. **Error Resilience**: Don't break user code if tracing fails
68+
4. **Streaming Support**: Handle both streaming and non-streaming responses
69+
70+
## Implementation Strategy for Parse Method
71+
72+
### Design Decisions
73+
74+
#### 1. Method Patching Strategy
75+
- Patch both `create` AND `parse` methods in `trace_openai()`
76+
- Use similar wrapper pattern for consistency
77+
- Share utility functions where possible
78+
79+
#### 2. Handler Functions Structure
80+
```python
81+
# New functions to add:
82+
- handle_streaming_parse() # For streaming parse calls
83+
- handle_non_streaming_parse() # For regular parse calls
84+
- parse_structured_output_data() # Parse method specific output handling
85+
```
86+
87+
#### 3. Output Data Handling
88+
The parse method returns structured data (Pydantic models, JSON Schema), so:
89+
1. **Capture the parsed structured output**: Use `.model_dump()` or similar serialization
90+
2. **Raw output**: Store the original response for debugging
91+
3. **Trace format**: Maintain compatibility with existing trace structure
92+
4. **Metadata**: Add parse-specific metadata (schema info, validation results, etc.)
93+
94+
#### 4. Implementation Plan
95+
96+
##### Phase 1: Sync Tracer Extension (openai_tracer.py)
97+
1. Modify `trace_openai()` to patch both `create` and `parse`
98+
2. Add `handle_streaming_parse()` and `handle_non_streaming_parse()`
99+
3. Add `parse_structured_output_data()` for output processing
100+
4. Update error handling to include parse-specific errors
101+
102+
##### Phase 2: Async Tracer Extension (async_openai_tracer.py)
103+
1. Modify `trace_async_openai()` to patch both methods
104+
2. Add async versions of parse handlers
105+
3. Import new utilities from sync tracer
106+
107+
##### Phase 3: Testing & Examples
108+
1. Create comprehensive tests for both sync/async
109+
2. Add example notebook showing parse method tracing
110+
3. Update documentation
111+
112+
### Key Considerations
113+
114+
#### Structured Output Serialization
115+
```python
116+
# Example parse response handling:
117+
def parse_structured_output_data(response):
118+
"""Handle structured output from parse method."""
119+
if hasattr(response, 'parsed') and response.parsed:
120+
# Structured output from parse
121+
if hasattr(response.parsed, 'model_dump'):
122+
# Pydantic model
123+
output_data = response.parsed.model_dump()
124+
else:
125+
# Other structured formats
126+
output_data = response.parsed
127+
else:
128+
# Fallback to regular content handling
129+
output_data = parse_non_streaming_output_data(response)
130+
return output_data
131+
```
132+
133+
#### Error Handling
134+
- Parse method may have validation errors that should be captured
135+
- Graceful fallback to regular create handling if parse-specific logic fails
136+
- Log parse-specific errors separately
137+
138+
## Implementation Status: COMPLETED ✅
139+
140+
### Completed Tasks
141+
1.**Research OpenAI parse method** - Understood differences vs create method
142+
2.**Architecture Analysis** - Analyzed existing tracing patterns
143+
3.**Design Implementation** - Created comprehensive design for parse tracing
144+
4.**Sync Tracer Implementation** - Added parse support to `openai_tracer.py`:
145+
- Modified `trace_openai()` to patch both create and parse methods
146+
- Added `handle_streaming_parse()` and `handle_non_streaming_parse()`
147+
- Created `parse_structured_output_data()` for structured output handling
148+
5.**Async Tracer Implementation** - Added parse support to `async_openai_tracer.py`:
149+
- Modified `trace_async_openai()` to patch parse method
150+
- Added async handler functions
151+
- Imported structured output utilities from sync tracer
152+
6.**Example Notebook** - Created comprehensive demo at `examples/tracing/openai/openai_parse_tracing.ipynb`
153+
7.**Unit Tests** - Created thorough test suite at `tests/test_openai_parse_tracing.py`
154+
8.**Documentation** - Updated README.md with tracing section including parse method
155+
156+
### Key Features Implemented
157+
- **Automatic Parse Method Detection**: Gracefully handles OpenAI clients with/without parse method
158+
- **Structured Output Support**: Properly serializes Pydantic models and JSON Schema responses
159+
- **Metadata Enhancement**: Adds parse-specific metadata (method type, response format)
160+
- **Error Resilience**: Tracing failures don't break user code
161+
- **Backward Compatibility**: Existing create method tracing unchanged
162+
- **Async Support**: Full async/await compatibility

README.md

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,65 @@ asyncio.run(main())
101101

102102
Functionality between the synchronous and asynchronous clients is otherwise identical.
103103

104+
## LLM Tracing
105+
106+
Openlayer provides automatic tracing for popular LLM providers, enabling you to monitor model performance, token usage, and response quality.
107+
108+
### OpenAI Tracing
109+
110+
Trace OpenAI chat completions (including the new structured output `parse` method) with automatic monitoring:
111+
112+
```python
113+
import openai
114+
from openlayer.lib import trace_openai
115+
116+
# Trace your OpenAI client
117+
client = trace_openai(openai.OpenAI())
118+
119+
# Use normally - both create and parse methods are automatically traced
120+
response = client.chat.completions.create(
121+
model="gpt-4o-mini",
122+
messages=[{"role": "user", "content": "Hello!"}]
123+
)
124+
125+
# NEW: Parse method support for structured outputs
126+
from pydantic import BaseModel
127+
128+
class Person(BaseModel):
129+
name: str
130+
age: int
131+
132+
structured_response = client.chat.completions.parse(
133+
model="gpt-4o-mini",
134+
messages=[{"role": "user", "content": "Extract: John Doe, 30 years old"}],
135+
response_format=Person
136+
)
137+
```
138+
139+
**What gets traced:**
140+
- Input messages and model parameters
141+
- Response content (structured data for parse method)
142+
- Token usage and latency metrics
143+
- Raw API responses for debugging
144+
- Custom inference IDs for request tracking
145+
146+
### Other LLM Providers
147+
148+
```python
149+
from openlayer.lib import trace_anthropic, trace_mistral, trace_groq
150+
151+
# Anthropic
152+
anthropic_client = trace_anthropic(anthropic.Anthropic())
153+
154+
# Mistral
155+
mistral_client = trace_mistral(mistralai.Mistral())
156+
157+
# Groq
158+
groq_client = trace_groq(groq.Groq())
159+
```
160+
161+
See the [examples directory](examples/tracing/) for comprehensive tracing examples with all supported providers.
162+
104163
### With aiohttp
105164

106165
By default, the async client uses `httpx` for HTTP requests. However, for improved concurrency performance you may also use `aiohttp` as the HTTP backend.

0 commit comments

Comments
 (0)