Skip to content

Commit 79f1a4e

Browse files
committed
Doc updates ,extending monocle for python example, scope guide WIP
Signed-off-by: Prasad Mujumdar <prasad@okahu.ai>
1 parent 7639961 commit 79f1a4e

14 files changed

Lines changed: 927 additions & 40 deletions

Monocle_scopes.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# This guide describes various options to set trace and scopes in your application
2+
3+
4+
### Set scope programatically in python
5+
```python
6+
from monocle_apptrace import monocle_trace_scope
7+
...
8+
with monocle_trace_scope("Conversation"):
9+
while True:
10+
message = input("How can I help you:")
11+
cleaned_message = gaurdrail_chai(message)
12+
result = rag_chat_chain.invoke(message)
13+
```
14+
The above code will generate two traces (one per chain invocation). All the spans in these traces will have an attribute called `Conversaion` with a unique value.
15+
```json
16+
"attributes": {
17+
"span.type": "inference",
18+
...
19+
"scope.conversation": "0xcb80e6f772968ed50ead80657b09cf52",
20+
```
21+
22+
### Set scope programatically in typescript
23+
TBD
24+
25+
### Set scope declaratively
26+
You can set scope via a configuration file at the method level or to track http header, without having to make changes

documentation/Extending_monocle.md

Lines changed: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
1+
# Custom Instrumentation with Monocle
2+
3+
This guide demonstrates how to use Monocle to instrument OpenAI and Vector DB interactions, collecting telemetry data to analyze and monitor their performance.
4+
5+
## Overview
6+
7+
The example includes the following components:
8+
9+
- **OpenAI Client (`openai_client.py`)**: A client for interacting with OpenAI's Chat API
10+
- **Vector Database (`vector_db.py`)**: An in-memory vector database with OpenAI embeddings
11+
- **Output Processors**: Configuration files that define how to extract and structure telemetry data
12+
- **Example script**: Shows how to instrument and run the application
13+
14+
## Component Details
15+
16+
### OpenAI Client
17+
18+
`OpenAIClient` is a wrapper around the OpenAI API that provides methods for:
19+
20+
- Making chat completion requests via the `chat()` method
21+
- Formatting messages for the API using `format_messages()`
22+
- Handling API responses and errors
23+
24+
```python
25+
# Initialize client
26+
client = OpenAIClient()
27+
28+
# Format messages and send to OpenAI
29+
messages = client.format_messages(
30+
system_prompts=["You are a helpful assistant."],
31+
user_prompts=["Tell me a joke about programming."]
32+
)
33+
response = client.chat(messages=messages, model="gpt-3.5-turbo")
34+
```
35+
36+
### Vector Database
37+
38+
`InMemoryVectorDB` is a simple vector database implementation that:
39+
40+
- Converts text to vector embeddings using OpenAI's embedding API
41+
- Stores vectors with associated metadata
42+
- Performs similarity searches using cosine similarity
43+
44+
```python
45+
# Initialize vector database
46+
vector_db = InMemoryVectorDB()
47+
48+
# Store documents
49+
vector_db.store_text("doc1", "Python is a programming language", {"source": "docs"})
50+
51+
# Search for similar documents
52+
results = vector_db.search_by_text("programming languages", top_k=2)
53+
```
54+
55+
## Instrumenting Your Code with Monocle
56+
57+
### 1. Define Output Processors
58+
59+
Output processors define what data to extract from your methods. Two examples are provided:
60+
61+
#### Inference Output Processor
62+
63+
`output_processor_inference.py` defines how to extract data from OpenAI chat completions:
64+
65+
```python
66+
INFERENCE_OUTPUT_PROCESSOR = {
67+
"type": "inference",
68+
"attributes": [
69+
[
70+
# Entity attributes for the provider
71+
{
72+
"attribute": "type",
73+
"accessor": lambda arguments: "openai"
74+
},
75+
{
76+
"attribute": "deployment",
77+
"accessor": lambda arguments: arguments['kwargs'].get('model', 'unknown')
78+
},
79+
# More attributes...
80+
]
81+
],
82+
"events": [
83+
{
84+
"name": "data.input",
85+
"attributes": [
86+
{
87+
"attribute": "input",
88+
"accessor": lambda arguments: [
89+
msg["content"]
90+
for msg in arguments['kwargs'].get('messages', [])
91+
] if isinstance(arguments['kwargs'].get('messages'), list) else []
92+
}
93+
]
94+
},
95+
# More events...
96+
]
97+
}
98+
```
99+
100+
#### Vector DB Output Processor
101+
102+
`output_processor_vector.py` defines how to extract data from vector database operations:
103+
104+
```python
105+
VECTOR_OUTPUT_PROCESSOR = {
106+
"type": "retrieval",
107+
"attributes": [
108+
[
109+
# Vector store attributes
110+
{
111+
"attribute": "name",
112+
"accessor": lambda arguments: type(arguments["instance"]).__name__,
113+
},
114+
# More attributes...
115+
]
116+
],
117+
"events": [
118+
{
119+
"name": "data.input",
120+
"attributes": [
121+
{
122+
"attribute": "input",
123+
"accessor": lambda arguments: arguments["args"][0] if arguments["args"] else None
124+
}
125+
]
126+
},
127+
# More events...
128+
]
129+
}
130+
```
131+
132+
### 2. Accessor Functions
133+
134+
The key to instrumentation is the `accessor` function, which extracts data from method calls:
135+
136+
- `arguments["instance"]`: The object instance (e.g., the OpenAIClient or InMemoryVectorDB)
137+
- `arguments["args"]`: Positional arguments passed to the method
138+
- `arguments["kwargs"]`: Keyword arguments passed to the method
139+
- `arguments["result"]`: The return value from the method call
140+
141+
These give you access to all inputs, outputs, and context of the instrumented methods.
142+
143+
### 3. Configure Instrumentation
144+
145+
Set up Monocle's telemetry system with your output processors:
146+
147+
```python
148+
from monocle_apptrace.instrumentation.common.wrapper_method import WrapperMethod
149+
from monocle_apptrace.instrumentation.common.instrumentor import setup_monocle_telemetry
150+
151+
setup_monocle_telemetry(
152+
workflow_name="openai.app",
153+
wrapper_methods=[
154+
WrapperMethod(
155+
package="openai_client", # Module name
156+
object_name="OpenAIClient", # Class name
157+
method="chat", # Method to instrument
158+
span_name="openai_client.chat", # Span name in telemetry
159+
output_processor=INFERENCE_OUTPUT_PROCESSOR
160+
),
161+
# More method wrappers...
162+
]
163+
)
164+
```
165+
166+
## Running the Example
167+
168+
1. Ensure you have your **OpenAI API key** available:
169+
170+
```bash
171+
export OPENAI_API_KEY=your_api_key_here
172+
```
173+
174+
2. Install the required packages:
175+
176+
```bash
177+
pip install -r requirements.txt
178+
```
179+
180+
3. Run the example script:
181+
182+
```bash
183+
python example.py
184+
# Or use the provided shell script
185+
./run_example.sh
186+
```
187+
188+
## Understanding the Telemetry Output
189+
190+
Monocle generates JSON trace files in your directory with names like:
191+
`monocle_trace_openai.app_<trace_id>_<timestamp>.json`
192+
193+
### Output Format
194+
195+
The trace files contain structured telemetry data:
196+
197+
```json
198+
{
199+
"name": "openai_client.chat",
200+
"context": { /* trace context */ },
201+
"attributes": {
202+
"entity.2.type": "openai",
203+
"entity.2.provider_name": "OpenAI",
204+
"entity.2.deployment": "gpt-3.5-turbo",
205+
"entity.2.inference_endpoint": "https://api.openai.com/v1",
206+
"entity.3.name": "gpt-3.5-turbo",
207+
"entity.3.type": "model.llm.gpt-3.5-turbo"
208+
},
209+
"events": [
210+
{
211+
"name": "data.input",
212+
"timestamp": "2025-02-27T10:36:49.985586Z",
213+
"attributes": {
214+
"input": [
215+
"You are a helpful AI assistant.",
216+
"Tell me a short joke about programming."
217+
]
218+
}
219+
},
220+
{
221+
"name": "data.output",
222+
"attributes": {
223+
"response": "Why do programmers prefer dark mode? Because the light attracts bugs!"
224+
}
225+
},
226+
{
227+
"name": "metadata",
228+
"attributes": {
229+
"prompt_tokens": 26,
230+
"completion_tokens": 14,
231+
"total_tokens": 40
232+
}
233+
}
234+
]
235+
}
236+
```
237+
238+
### Key Elements
239+
240+
1. **Attributes**: Contains information about the instrumented entity:
241+
- Model name and type
242+
- Deployment details
243+
- API endpoints
244+
- Provider information
245+
246+
2. **Events**: Contains captured events during the method execution:
247+
- `data.input`: The inputs provided to the method
248+
- `data.output`: The response or results from the method
249+
- `metadata`: Additional information like token usage
250+
251+
## Customizing for Your Application
252+
253+
To instrument your own code:
254+
255+
1. Create output processors tailored to your methods
256+
2. Use accessor functions to extract the data you need
257+
3. Set up telemetry with your method wrappers
258+
4. Run your application and analyze the generated traces
259+
260+
By customizing the output processors, you can collect exactly the telemetry data you need from any Python method.

documentation/Monocle_User_Guide.md

Lines changed: 2 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -210,49 +210,11 @@ Monocle exporters handle storing the trace for future analysis. By default each
210210

211211
## Using scopes
212212
Imagine you have a chatbot application that supports a long conversion ie multiple question/answer back and forth between end user and bot. It uses various genAI tech components like LLMs and vector stores. A simple instrumentation will generate a trace per genAI API call (eg invocation of a framework chat or direct OpenAI API). As the app developer or owner, you are more interested in tracking the conversions than just APIs. The scopes in Monocle enables that use case.
213-
You can set the scope in application either programatically or declaratively. You can specific a value for scope or Monocle will generate a unique value (GUID) which gives you options to choose what's best suited for your use case. Please see the different API references and configuration reference below for the all the available options.
214-
TBD!!
213+
You can set the scope in application either programatically or declaratively. You can specific a value for scope or Monocle will generate a unique value (GUID) which gives you options to choose what's best suited for your use case. Please see the [Monocle scopes guide](Monocle_scopes.md) for the details and examples.
215214

216-
### Set scope for a python
217-
```python
218-
from monocle_apptrace import monocle_trace_scope
219-
...
220-
with monocle_trace_scope("Conversation"):
221-
while True:
222-
message = input("How can I help you:")
223-
cleaned_message = gaurdrail_chai(message)
224-
result = rag_chat_chain.invoke(message)
225-
```
226-
The above code will generate two traces (one per chain invocation). All the spans in these traces will have an attribute called `Conversaion` with a unique value.
227-
```json
228-
"attributes": {
229-
"span.type": "inference",
230-
...
231-
"scope.conversation": "0xcb80e6f772968ed50ead80657b09cf52",
232-
```
233-
234-
### Set scope for a typescript method
235-
TBD
236215

237216
## Extending Monocle
238-
TBD
239-
240-
241-
##Monocle Reference
242-
### Python APIs
243-
#### Enable tracing
244-
#### Trace
245-
#### Scopes
246-
#### Customization
247-
248-
### Typescript APIs
249-
#### Enable tracing
250-
#### Trace
251-
#### Scopes
252-
#### Customization
217+
If you are using a genAI technology that's not yet supported by Monocle out of the box or have you own proparitory code, you can extend monocle to generate traces in the Monocle format. Please refer to [extending monocle guide](Extending_monocle.md)
253218

254-
### Configuration reference
255-
#### Scope
256-
#### Exporters
257219

258220

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from .openai_client import OpenAIClient
2+
3+
__all__ = ['OpenAIClient']

0 commit comments

Comments
 (0)