mojentic 0.6.0__py3-none-any.whl → 0.6.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -12,20 +12,20 @@ from mojentic.tracer.tracer_events import TracerEvent
12
12
  class NullTracer:
13
13
  """
14
14
  A no-op implementation of TracerSystem that silently discards all tracing operations.
15
-
15
+
16
16
  This class follows the Null Object Pattern to eliminate conditional checks in client code.
17
17
  All record methods are overridden to do nothing, and all query methods return empty results.
18
18
  """
19
-
19
+
20
20
  def __init__(self):
21
21
  """Initialize the NullTracer with disabled state."""
22
22
  self.enabled = False
23
23
  self.event_store = None
24
-
24
+
25
25
  def record_event(self, event: TracerEvent) -> None:
26
26
  """
27
27
  Do nothing implementation of record_event.
28
-
28
+
29
29
  Parameters
30
30
  ----------
31
31
  event : TracerEvent
@@ -33,16 +33,17 @@ class NullTracer:
33
33
  """
34
34
  # Do nothing
35
35
  pass
36
-
36
+
37
37
  def record_llm_call(self,
38
38
  model: str,
39
39
  messages: List[Dict],
40
40
  temperature: float = 1.0,
41
41
  tools: Optional[List[Dict]] = None,
42
- source: Any = None) -> None:
42
+ source: Any = None,
43
+ correlation_id: str = None) -> None:
43
44
  """
44
45
  Do nothing implementation of record_llm_call.
45
-
46
+
46
47
  Parameters
47
48
  ----------
48
49
  model : str
@@ -55,19 +56,22 @@ class NullTracer:
55
56
  The tools available to the LLM, if any.
56
57
  source : Any, optional
57
58
  The source of the event.
59
+ correlation_id : str, optional
60
+ UUID string that is copied from cause-to-affect for tracing events.
58
61
  """
59
62
  # Do nothing
60
63
  pass
61
-
64
+
62
65
  def record_llm_response(self,
63
66
  model: str,
64
67
  content: str,
65
68
  tool_calls: Optional[List[Dict]] = None,
66
69
  call_duration_ms: Optional[float] = None,
67
- source: Any = None) -> None:
70
+ source: Any = None,
71
+ correlation_id: str = None) -> None:
68
72
  """
69
73
  Do nothing implementation of record_llm_response.
70
-
74
+
71
75
  Parameters
72
76
  ----------
73
77
  model : str
@@ -80,19 +84,22 @@ class NullTracer:
80
84
  The duration of the LLM call in milliseconds.
81
85
  source : Any, optional
82
86
  The source of the event.
87
+ correlation_id : str, optional
88
+ UUID string that is copied from cause-to-affect for tracing events.
83
89
  """
84
90
  # Do nothing
85
91
  pass
86
-
92
+
87
93
  def record_tool_call(self,
88
94
  tool_name: str,
89
95
  arguments: Dict[str, Any],
90
96
  result: Any,
91
97
  caller: Optional[str] = None,
92
- source: Any = None) -> None:
98
+ source: Any = None,
99
+ correlation_id: str = None) -> None:
93
100
  """
94
101
  Do nothing implementation of record_tool_call.
95
-
102
+
96
103
  Parameters
97
104
  ----------
98
105
  tool_name : str
@@ -105,19 +112,22 @@ class NullTracer:
105
112
  The name of the agent or component calling the tool.
106
113
  source : Any, optional
107
114
  The source of the event.
115
+ correlation_id : str, optional
116
+ UUID string that is copied from cause-to-affect for tracing events.
108
117
  """
109
118
  # Do nothing
110
119
  pass
111
-
120
+
112
121
  def record_agent_interaction(self,
113
122
  from_agent: str,
114
123
  to_agent: str,
115
124
  event_type: str,
116
125
  event_id: Optional[str] = None,
117
- source: Any = None) -> None:
126
+ source: Any = None,
127
+ correlation_id: str = None) -> None:
118
128
  """
119
129
  Do nothing implementation of record_agent_interaction.
120
-
130
+
121
131
  Parameters
122
132
  ----------
123
133
  from_agent : str
@@ -130,10 +140,12 @@ class NullTracer:
130
140
  A unique identifier for the event.
131
141
  source : Any, optional
132
142
  The source of the event.
143
+ correlation_id : str, optional
144
+ UUID string that is copied from cause-to-affect for tracing events.
133
145
  """
134
146
  # Do nothing
135
147
  pass
136
-
148
+
137
149
  def get_events(self,
138
150
  event_type: Optional[Type[TracerEvent]] = None,
139
151
  start_time: Optional[float] = None,
@@ -141,7 +153,7 @@ class NullTracer:
141
153
  filter_func: Optional[Callable[[TracerEvent], bool]] = None) -> List[TracerEvent]:
142
154
  """
143
155
  Return an empty list for any get_events request.
144
-
156
+
145
157
  Parameters
146
158
  ----------
147
159
  event_type : Type[TracerEvent], optional
@@ -152,40 +164,40 @@ class NullTracer:
152
164
  Include events with timestamp <= end_time.
153
165
  filter_func : Callable[[TracerEvent], bool], optional
154
166
  Custom filter function to apply to events.
155
-
167
+
156
168
  Returns
157
169
  -------
158
170
  List[TracerEvent]
159
171
  An empty list.
160
172
  """
161
173
  return []
162
-
174
+
163
175
  def get_last_n_tracer_events(self, n: int, event_type: Optional[Type[TracerEvent]] = None) -> List[TracerEvent]:
164
176
  """
165
177
  Return an empty list for any get_last_n_tracer_events request.
166
-
178
+
167
179
  Parameters
168
180
  ----------
169
181
  n : int
170
182
  Number of events to return.
171
183
  event_type : Type[TracerEvent], optional
172
184
  Filter events by this specific tracer event type.
173
-
185
+
174
186
  Returns
175
187
  -------
176
188
  List[TracerEvent]
177
189
  An empty list.
178
190
  """
179
191
  return []
180
-
192
+
181
193
  def clear(self) -> None:
182
194
  """Do nothing implementation of clear method."""
183
195
  pass
184
-
196
+
185
197
  def enable(self) -> None:
186
198
  """No-op method for interface compatibility."""
187
199
  pass
188
-
200
+
189
201
  def disable(self) -> None:
190
202
  """No-op method for interface compatibility."""
191
- pass
203
+ pass
@@ -3,6 +3,7 @@ Defines tracer event types for tracking system interactions.
3
3
  """
4
4
  from typing import Any, Dict, List, Optional, Type
5
5
  from datetime import datetime
6
+ import uuid
6
7
 
7
8
  from pydantic import Field
8
9
 
@@ -12,24 +13,25 @@ from mojentic.event import Event
12
13
  class TracerEvent(Event):
13
14
  """
14
15
  Base class for all tracer-specific events.
15
-
16
+
16
17
  Tracer events are used to track system interactions for observability purposes.
17
18
  They are distinct from regular events which are used for agent communication.
18
19
  """
19
20
  timestamp: float = Field(..., description="Timestamp when the event occurred")
20
-
21
+ correlation_id: str = Field(default_factory=lambda: str(uuid.uuid4()), description="UUID string that is copied from cause-to-affect for tracing events")
22
+
21
23
  def printable_summary(self) -> str:
22
24
  """
23
25
  Return a formatted string summary of the event.
24
-
26
+
25
27
  Returns
26
28
  -------
27
29
  str
28
30
  A formatted string with the event information.
29
31
  """
30
32
  event_time = datetime.fromtimestamp(self.timestamp).strftime("%H:%M:%S.%f")[:-3]
31
- return f"[{event_time}] {type(self).__name__}"
32
-
33
+ return f"[{event_time}] {type(self).__name__} (correlation_id: {self.correlation_id})"
34
+
33
35
 
34
36
  class LLMCallTracerEvent(TracerEvent):
35
37
  """
@@ -39,25 +41,25 @@ class LLMCallTracerEvent(TracerEvent):
39
41
  messages: List[dict] = Field(..., description="The messages sent to the LLM")
40
42
  temperature: float = Field(1.0, description="The temperature setting used for the call")
41
43
  tools: Optional[List[Dict]] = Field(None, description="The tools available to the LLM, if any")
42
-
44
+
43
45
  def printable_summary(self) -> str:
44
46
  """Return a formatted summary of the LLM call event."""
45
47
  base_summary = super().printable_summary()
46
48
  summary = f"{base_summary}\n Model: {self.model}"
47
-
49
+
48
50
  if self.messages:
49
51
  msg_count = len(self.messages)
50
52
  summary += f"\n Messages: {msg_count} message{'s' if msg_count != 1 else ''}"
51
-
53
+
52
54
  if self.temperature != 1.0:
53
55
  summary += f"\n Temperature: {self.temperature}"
54
-
56
+
55
57
  if self.tools:
56
58
  tool_names = [tool.get('name', 'unknown') for tool in self.tools]
57
59
  summary += f"\n Available Tools: {', '.join(tool_names)}"
58
-
60
+
59
61
  return summary
60
-
62
+
61
63
 
62
64
  class LLMResponseTracerEvent(TracerEvent):
63
65
  """
@@ -67,23 +69,23 @@ class LLMResponseTracerEvent(TracerEvent):
67
69
  content: str = Field(..., description="The content of the LLM response")
68
70
  tool_calls: Optional[List[Dict]] = Field(None, description="Any tool calls made by the LLM")
69
71
  call_duration_ms: Optional[float] = Field(None, description="Duration of the LLM call in milliseconds")
70
-
72
+
71
73
  def printable_summary(self) -> str:
72
74
  """Return a formatted summary of the LLM response event."""
73
75
  base_summary = super().printable_summary()
74
76
  summary = f"{base_summary}\n Model: {self.model}"
75
-
77
+
76
78
  if self.content:
77
79
  content_preview = self.content[:100] + "..." if len(self.content) > 100 else self.content
78
80
  summary += f"\n Content: {content_preview}"
79
-
81
+
80
82
  if self.tool_calls:
81
83
  tool_count = len(self.tool_calls)
82
84
  summary += f"\n Tool Calls: {tool_count} call{'s' if tool_count != 1 else ''}"
83
-
85
+
84
86
  if self.call_duration_ms is not None:
85
87
  summary += f"\n Duration: {self.call_duration_ms:.2f}ms"
86
-
88
+
87
89
  return summary
88
90
 
89
91
 
@@ -95,23 +97,23 @@ class ToolCallTracerEvent(TracerEvent):
95
97
  arguments: Dict[str, Any] = Field(..., description="Arguments provided to the tool")
96
98
  result: Any = Field(..., description="Result returned by the tool")
97
99
  caller: Optional[str] = Field(None, description="Name of the agent or component that called the tool")
98
-
100
+
99
101
  def printable_summary(self) -> str:
100
102
  """Return a formatted summary of the tool call event."""
101
103
  base_summary = super().printable_summary()
102
104
  summary = f"{base_summary}\n Tool: {self.tool_name}"
103
-
105
+
104
106
  if self.arguments:
105
107
  summary += f"\n Arguments: {self.arguments}"
106
-
108
+
107
109
  if self.result is not None:
108
110
  result_str = str(self.result)
109
111
  result_preview = result_str[:100] + "..." if len(result_str) > 100 else result_str
110
112
  summary += f"\n Result: {result_preview}"
111
-
113
+
112
114
  if self.caller:
113
115
  summary += f"\n Caller: {self.caller}"
114
-
116
+
115
117
  return summary
116
118
 
117
119
 
@@ -123,14 +125,14 @@ class AgentInteractionTracerEvent(TracerEvent):
123
125
  to_agent: str = Field(..., description="Name of the agent receiving the event")
124
126
  event_type: str = Field(..., description="Type of event being processed")
125
127
  event_id: Optional[str] = Field(None, description="Unique identifier for the event")
126
-
128
+
127
129
  def printable_summary(self) -> str:
128
130
  """Return a formatted summary of the agent interaction event."""
129
131
  base_summary = super().printable_summary()
130
132
  summary = f"{base_summary}\n From: {self.from_agent} → To: {self.to_agent}"
131
133
  summary += f"\n Event Type: {self.event_type}"
132
-
134
+
133
135
  if self.event_id:
134
136
  summary += f"\n Event ID: {self.event_id}"
135
-
136
- return summary
137
+
138
+ return summary
@@ -24,16 +24,16 @@ logger = structlog.get_logger()
24
24
  class TracerSystem:
25
25
  """
26
26
  Central system for capturing and querying tracer events.
27
-
27
+
28
28
  The TracerSystem is responsible for recording events related to LLM calls,
29
29
  tool usage, and agent interactions, providing a way to trace through the
30
30
  major events of the system.
31
31
  """
32
-
32
+
33
33
  def __init__(self, event_store: Optional[EventStore] = None, enabled: bool = True):
34
34
  """
35
35
  Initialize the tracer system.
36
-
36
+
37
37
  Parameters
38
38
  ----------
39
39
  event_store : EventStore, optional
@@ -43,11 +43,11 @@ class TracerSystem:
43
43
  """
44
44
  self.event_store = event_store or EventStore()
45
45
  self.enabled = enabled
46
-
46
+
47
47
  def record_event(self, event: TracerEvent) -> None:
48
48
  """
49
49
  Record a tracer event in the event store.
50
-
50
+
51
51
  Parameters
52
52
  ----------
53
53
  event : TracerEvent
@@ -55,18 +55,19 @@ class TracerSystem:
55
55
  """
56
56
  if not self.enabled:
57
57
  return
58
-
58
+
59
59
  self.event_store.store(event)
60
-
60
+
61
61
  def record_llm_call(self,
62
62
  model: str,
63
63
  messages: List[Dict],
64
64
  temperature: float = 1.0,
65
65
  tools: Optional[List[Dict]] = None,
66
- source: Any = None) -> None:
66
+ source: Any = None,
67
+ correlation_id: str = None) -> None:
67
68
  """
68
69
  Record an LLM call event.
69
-
70
+
70
71
  Parameters
71
72
  ----------
72
73
  model : str
@@ -79,29 +80,33 @@ class TracerSystem:
79
80
  The tools available to the LLM, if any.
80
81
  source : Any, optional
81
82
  The source of the event. If None, the TracerSystem class will be used.
83
+ correlation_id : str, required
84
+ UUID string that is copied from cause-to-affect for tracing events.
82
85
  """
83
86
  if not self.enabled:
84
87
  return
85
-
88
+
86
89
  event = LLMCallTracerEvent(
87
90
  source=source or type(self),
88
91
  timestamp=time.time(),
89
92
  model=model,
90
93
  messages=messages,
91
94
  temperature=temperature,
92
- tools=tools
95
+ tools=tools,
96
+ correlation_id=correlation_id
93
97
  )
94
98
  self.event_store.store(event)
95
-
99
+
96
100
  def record_llm_response(self,
97
101
  model: str,
98
102
  content: str,
99
103
  tool_calls: Optional[List[Dict]] = None,
100
104
  call_duration_ms: Optional[float] = None,
101
- source: Any = None) -> None:
105
+ source: Any = None,
106
+ correlation_id: str = None) -> None:
102
107
  """
103
108
  Record an LLM response event.
104
-
109
+
105
110
  Parameters
106
111
  ----------
107
112
  model : str
@@ -114,29 +119,33 @@ class TracerSystem:
114
119
  The duration of the LLM call in milliseconds.
115
120
  source : Any, optional
116
121
  The source of the event. If None, the TracerSystem class will be used.
122
+ correlation_id : str, required
123
+ UUID string that is copied from cause-to-affect for tracing events.
117
124
  """
118
125
  if not self.enabled:
119
126
  return
120
-
127
+
121
128
  event = LLMResponseTracerEvent(
122
129
  source=source or type(self),
123
130
  timestamp=time.time(),
124
131
  model=model,
125
132
  content=content,
126
133
  tool_calls=tool_calls,
127
- call_duration_ms=call_duration_ms
134
+ call_duration_ms=call_duration_ms,
135
+ correlation_id=correlation_id
128
136
  )
129
137
  self.event_store.store(event)
130
-
138
+
131
139
  def record_tool_call(self,
132
140
  tool_name: str,
133
141
  arguments: Dict[str, Any],
134
142
  result: Any,
135
143
  caller: Optional[str] = None,
136
- source: Any = None) -> None:
144
+ source: Any = None,
145
+ correlation_id: str = None) -> None:
137
146
  """
138
147
  Record a tool call event.
139
-
148
+
140
149
  Parameters
141
150
  ----------
142
151
  tool_name : str
@@ -149,29 +158,33 @@ class TracerSystem:
149
158
  The name of the agent or component calling the tool.
150
159
  source : Any, optional
151
160
  The source of the event. If None, the TracerSystem class will be used.
161
+ correlation_id : str, required
162
+ UUID string that is copied from cause-to-affect for tracing events.
152
163
  """
153
164
  if not self.enabled:
154
165
  return
155
-
166
+
156
167
  event = ToolCallTracerEvent(
157
168
  source=source or type(self),
158
169
  timestamp=time.time(),
159
170
  tool_name=tool_name,
160
171
  arguments=arguments,
161
172
  result=result,
162
- caller=caller
173
+ caller=caller,
174
+ correlation_id=correlation_id
163
175
  )
164
176
  self.event_store.store(event)
165
-
177
+
166
178
  def record_agent_interaction(self,
167
179
  from_agent: str,
168
180
  to_agent: str,
169
181
  event_type: str,
170
182
  event_id: Optional[str] = None,
171
- source: Any = None) -> None:
183
+ source: Any = None,
184
+ correlation_id: str = None) -> None:
172
185
  """
173
186
  Record an agent interaction event.
174
-
187
+
175
188
  Parameters
176
189
  ----------
177
190
  from_agent : str
@@ -184,20 +197,23 @@ class TracerSystem:
184
197
  A unique identifier for the event.
185
198
  source : Any, optional
186
199
  The source of the event. If None, the TracerSystem class will be used.
200
+ correlation_id : str, required
201
+ UUID string that is copied from cause-to-affect for tracing events.
187
202
  """
188
203
  if not self.enabled:
189
204
  return
190
-
205
+
191
206
  event = AgentInteractionTracerEvent(
192
207
  source=source or type(self),
193
208
  timestamp=time.time(),
194
209
  from_agent=from_agent,
195
210
  to_agent=to_agent,
196
211
  event_type=event_type,
197
- event_id=event_id
212
+ event_id=event_id,
213
+ correlation_id=correlation_id
198
214
  )
199
215
  self.event_store.store(event)
200
-
216
+
201
217
  def get_events(self,
202
218
  event_type: Optional[Type[TracerEvent]] = None,
203
219
  start_time: Optional[float] = None,
@@ -205,10 +221,10 @@ class TracerSystem:
205
221
  filter_func: Optional[Callable[[TracerEvent], bool]] = None) -> List[TracerEvent]:
206
222
  """
207
223
  Get tracer events from the store, optionally filtered.
208
-
224
+
209
225
  This is a convenience wrapper around the EventStore's get_events method,
210
226
  specifically for tracer events.
211
-
227
+
212
228
  Parameters
213
229
  ----------
214
230
  event_type : Type[TracerEvent], optional
@@ -219,7 +235,7 @@ class TracerSystem:
219
235
  Include events with timestamp <= end_time.
220
236
  filter_func : Callable[[TracerEvent], bool], optional
221
237
  Custom filter function to apply to events.
222
-
238
+
223
239
  Returns
224
240
  -------
225
241
  List[TracerEvent]
@@ -227,33 +243,33 @@ class TracerSystem:
227
243
  """
228
244
  # First filter to only TracerEvents
229
245
  events = self.event_store.get_events(event_type=TracerEvent)
230
-
246
+
231
247
  # Then apply additional filters
232
248
  if event_type is not None:
233
249
  events = [e for e in events if isinstance(e, event_type)]
234
-
250
+
235
251
  if start_time is not None:
236
252
  events = [e for e in events if e.timestamp >= start_time]
237
-
253
+
238
254
  if end_time is not None:
239
255
  events = [e for e in events if e.timestamp <= end_time]
240
-
256
+
241
257
  if filter_func is not None:
242
258
  events = [e for e in events if filter_func(e)]
243
-
259
+
244
260
  return events
245
-
261
+
246
262
  def get_last_n_tracer_events(self, n: int, event_type: Optional[Type[TracerEvent]] = None) -> List[TracerEvent]:
247
263
  """
248
264
  Get the last N tracer events, optionally filtered by type.
249
-
265
+
250
266
  Parameters
251
267
  ----------
252
268
  n : int
253
269
  Number of events to return.
254
270
  event_type : Type[TracerEvent], optional
255
271
  Filter events by this specific tracer event type.
256
-
272
+
257
273
  Returns
258
274
  -------
259
275
  List[TracerEvent]
@@ -261,19 +277,19 @@ class TracerSystem:
261
277
  """
262
278
  base_type = event_type or TracerEvent
263
279
  return self.event_store.get_last_n_events(n, event_type=base_type)
264
-
280
+
265
281
  def clear(self) -> None:
266
282
  """
267
283
  Clear all events from the event store.
268
284
  """
269
285
  self.event_store.clear()
270
-
286
+
271
287
  def enable(self) -> None:
272
288
  """
273
289
  Enable the tracer system.
274
290
  """
275
291
  self.enabled = True
276
-
292
+
277
293
  def disable(self) -> None:
278
294
  """
279
295
  Disable the tracer system.
@@ -282,4 +298,4 @@ class TracerSystem:
282
298
 
283
299
 
284
300
  # Import and use the null tracer directly in client code via:
285
- # from mojentic.tracer import null_tracer
301
+ # from mojentic.tracer import null_tracer