Skip to content

Commit 5b1bf56

Browse files
committed
Doc updates, Monocle extension for Typescript
Signed-off-by: Prasad Mujumdar <prasad@okahu.ai>
1 parent e62f153 commit 5b1bf56

30 files changed

Lines changed: 1251 additions & 3 deletions
Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
# Monocle Custom Instrumentation Guide
2+
3+
Monocle allows you to easily instrument your GenAI applications to capture telemetry for both custom code and third-party libraries. This guide explains how to instrument your code, create output processors, and analyze the resulting telemetry.
4+
5+
## Instrumenting Custom Code
6+
7+
Monocle allows you to instrument your own custom wrappers around GenAI services. The `setupMonocle` function is used to configure instrumentation for your application.
8+
9+
### Basic Setup
10+
11+
```javascript
12+
const { setupMonocle } = require('monocle2ai');
13+
14+
setupMonocle(
15+
"myapp.name", // Service name
16+
[], // Custom hooks array (empty here)
17+
[ // Instrumentation configurations array
18+
{
19+
"package": require.resolve('./path/to/your/module'),
20+
"object": "YourClass",
21+
"method": "yourMethod",
22+
"spanName": "customSpanName",
23+
"output_processor": [
24+
YOUR_OUTPUT_PROCESSOR
25+
]
26+
}
27+
]
28+
);
29+
```
30+
31+
### Configuration Parameters
32+
33+
- **package**: Path to the module containing the class to instrument
34+
- **object**: Name of the class or object to instrument
35+
- **method**: Method name to instrument
36+
- **spanName**: Name of the span created when this method is called
37+
- **output_processor**: Array of processors that extract and format telemetry data
38+
39+
## Output Processors
40+
41+
Output processors define how to extract and format telemetry data from method calls. They have access to:
42+
43+
- **arguments**: All arguments passed to the method
44+
- **instance**: The object instance (this)
45+
- **response**: The return value from the method
46+
47+
### Output Processor Structure
48+
49+
```javascript
50+
const EXAMPLE_OUTPUT_PROCESSOR = {
51+
type: "inference", // Type of span (inference, retrieval, etc.)
52+
attributes: [ // Arrays of attribute definitions
53+
[
54+
{
55+
attribute: "name",
56+
accessor: arguments => arguments.instance.someProperty
57+
},
58+
// More attributes...
59+
]
60+
],
61+
events: [ // Events to capture
62+
{
63+
name: "data.input",
64+
attributes: [
65+
{
66+
attribute: "input",
67+
accessor: arguments => arguments.args[0] || null
68+
}
69+
]
70+
},
71+
// More events...
72+
]
73+
};
74+
```
75+
76+
## Example: Instrumenting Custom OpenAI Client
77+
78+
Here's how we instrument a custom OpenAI client:
79+
80+
```javascript
81+
setupMonocle(
82+
"openai.app",
83+
[],
84+
[
85+
{
86+
"package": require.resolve('./custom_ai_code/openaiClient'),
87+
"object": "OpenAIClient",
88+
"method": "chat",
89+
"spanName": "openaiClient.chat",
90+
"output_processor": [
91+
INFERENCE_OUTPUT_PROCESSOR
92+
]
93+
}
94+
]
95+
);
96+
```
97+
98+
The `INFERENCE_OUTPUT_PROCESSOR` extracts information like:
99+
- Model name and type from function arguments
100+
- Input prompts from method arguments
101+
- Response text from the method's return value
102+
- Usage metadata from the response object
103+
104+
## Example: Instrumenting Vector Database
105+
106+
```javascript
107+
{
108+
"package": require.resolve('./custom_ai_code/vectorDb'),
109+
"object": "InMemoryVectorDB",
110+
"method": "searchByText",
111+
"spanName": "vectorDb.searchByText",
112+
"output_processor": [
113+
VECTOR_OUTPUT_PROCESSOR
114+
]
115+
}
116+
```
117+
118+
The `VECTOR_OUTPUT_PROCESSOR` captures:
119+
- Vector store name and type from the instance
120+
- Embedding model information
121+
- Query inputs and search results
122+
123+
## Instrumenting NPM Modules
124+
125+
You can also instrument third-party NPM modules like Google's Generative AI SDK:
126+
127+
```javascript
128+
{
129+
"package": "@google/generative-ai",
130+
"object": "GenerativeModel",
131+
"method": "generateContent",
132+
"spanName": "gemini.generateContent",
133+
"output_processor": [
134+
GEMINI_OUTPUT_PROCESSOR
135+
]
136+
}
137+
```
138+
139+
For NPM modules, specify the package name directly instead of using `require.resolve()`.
140+
141+
## Output Processor to Trace Correlation
142+
143+
Let's see how output processors translate to actual traces:
144+
145+
### Vector DB Processor & Trace
146+
147+
The Vector DB processor extracts:
148+
- Vector store name: `accessor: arguments => arguments.instance.constructor.name`
149+
- Query text: `accessor: arguments => arguments.args[0] || null`
150+
- Results: `accessor: arguments => arguments.response.map(...).join(", ")`
151+
152+
This produces the following trace data:
153+
```json
154+
{
155+
"name": "vectorDb.searchByText",
156+
"attributes": {
157+
"span.type": "retrieval",
158+
"entity.2.name": "InMemoryVectorDB",
159+
"entity.2.type": "vectorstore.InMemoryVectorDB",
160+
"entity.3.name": "text-embedding-ada-002",
161+
"entity.3.type": "model.embedding.text-embedding-ada-002"
162+
},
163+
"events": [
164+
{
165+
"name": "data.input",
166+
"attributes": { "input": "programming languages" }
167+
},
168+
{
169+
"name": "data.output",
170+
"attributes": {
171+
"response": "JavaScript is a high-level programming language, Machine learning is a subset of artificial intelligence"
172+
}
173+
}
174+
]
175+
}
176+
```
177+
178+
### Gemini Output Processor & Trace
179+
180+
The Gemini output processor extracts:
181+
- Model name: `accessor: arguments => arguments.instance.model`
182+
- Input: `accessor: arguments => ...input text extraction logic...`
183+
- Response: `accessor: arguments => arguments.response.response.text()`
184+
- Usage metrics: Extracting token counts from response metadata
185+
186+
This produces the following trace data:
187+
```json
188+
{
189+
"name": "gemini.generateContent",
190+
"attributes": {
191+
"span.type": "inference",
192+
"entity.2.type": "gemini",
193+
"entity.2.provider_name": "Google",
194+
"entity.2.deployment": "models/gemini-1.5-flash",
195+
"entity.3.name": "models/gemini-1.5-flash",
196+
"entity.3.type": "model.llm.models/gemini-1.5-flash"
197+
},
198+
"events": [
199+
{
200+
"name": "data.input",
201+
"attributes": { "input": ["Tell me a short joke about programming."] }
202+
},
203+
{
204+
"name": "data.output",
205+
"attributes": { "response": "Why do programmers prefer dark mode? Because light attracts bugs!\n" }
206+
},
207+
{
208+
"name": "metadata",
209+
"attributes": {
210+
"prompt_tokens": 8,
211+
"completion_tokens": 14,
212+
"total_tokens": 22
213+
}
214+
}
215+
]
216+
}
217+
```
218+
219+
## Best Practices
220+
221+
1. **Accessor Functions**: Write robust accessor functions that handle missing or malformed data
222+
2. **Attribute Organization**: Group related attributes within the same array in the `attributes` section
223+
3. **Events**: Use standard event names like `data.input`, `data.output`, and `metadata`
224+
4. **Error Handling**: Add proper error handling in accessors to avoid instrumentation failures
225+
226+
## Conclusion
227+
228+
Monocle's custom instrumentation provides a flexible way to track your GenAI application's behavior. By defining output processors, you can extract meaningful telemetry data from any GenAI component, whether it's your custom code or a third-party library.

documentation/Monocle_User_Guide.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -210,10 +210,12 @@ 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/services 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 [Monocle cookbook](Monocle_scopes.md) for the details and examples.
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 python cookbook](./Monocle_Cookbook_python.md) for the details and examples.
214214

215215
## Extending Monocle
216-
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)
216+
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.
217+
- [Extending monocle guide for python](Extending_monocle_python.md) and [example](./examples/custom/custom_instrumentation_python/)
218+
- [Extending monocle guide for typescript](Extending_monocle_ts.md) and [example](./examples/custom/custom_instrumentation_ts/)
217219

218220
## Monocle API Referece
219221
### Python APIs

documentation/examples/custom/custom_instrumentation/__init__.py renamed to documentation/examples/custom/custom_instrumentation_python/__init__.py

File renamed without changes.

documentation/examples/custom/custom_instrumentation/custom_ai_code/openai_client.py renamed to documentation/examples/custom/custom_instrumentation_python/custom_ai_code/openai_client.py

File renamed without changes.

documentation/examples/custom/custom_instrumentation/custom_ai_code/vector_db.py renamed to documentation/examples/custom/custom_instrumentation_python/custom_ai_code/vector_db.py

File renamed without changes.

documentation/examples/custom/custom_instrumentation/example_custom.py renamed to documentation/examples/custom/custom_instrumentation_python/example_custom.py

File renamed without changes.

documentation/examples/custom/custom_instrumentation/example_gemini.py renamed to documentation/examples/custom/custom_instrumentation_python/example_gemini.py

File renamed without changes.

documentation/examples/custom/custom_instrumentation/output_processor_gemini.py renamed to documentation/examples/custom/custom_instrumentation_python/output_processor_gemini.py

File renamed without changes.

0 commit comments

Comments
 (0)