agentx-python 0.8.27__py3-none-any.whl → 0.8.28__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.
@@ -14,9 +14,16 @@ from __future__ import annotations
14
14
  import asyncio
15
15
  import inspect
16
16
  import json
17
+ import logging
18
+ import threading
17
19
  import time
18
20
  from typing import Any, Callable, Dict, Optional
19
21
 
22
+ logger = logging.getLogger(__name__)
23
+
24
+ # Sentinel for finish_llm_call's `active_span`: "not passed" is distinct from "passed None".
25
+ _UNSET: Any = object()
26
+
20
27
  from agentx.tracing.tracer import Tracer, _safe_serialize
21
28
 
22
29
 
@@ -109,6 +116,7 @@ def finish_llm_call(
109
116
  cache_write_tokens: Optional[int] = None,
110
117
  tool_definitions: Optional[list] = None,
111
118
  call_metadata: Optional[Dict[str, Any]] = None,
119
+ active_span: Any = _UNSET,
112
120
  ) -> None:
113
121
  """
114
122
  Close out one raw-client LLM call - shared by the ``on_finish``/exit
@@ -129,7 +137,11 @@ def finish_llm_call(
129
137
  if tool_definitions:
130
138
  metadata = {**(metadata or {}), "tools": tool_definitions}
131
139
 
132
- active_span = tracer.current_span
140
+ # The parent is the span that was active when the CALL was made. Streaming patches pass it
141
+ # explicitly: a stream finalizes later (exhaustion, close, or garbage collection), by which
142
+ # time a different span may be active, and the call must not be grafted onto it.
143
+ if active_span is _UNSET:
144
+ active_span = tracer.current_span
133
145
  if active_span is not None:
134
146
  # The definitions describe the whole call's toolbox - attach them to the enclosing
135
147
  # span's metadata (first capture wins) so the ROOT trace carries them for the
@@ -168,6 +180,9 @@ def finish_llm_call(
168
180
  )
169
181
  span.__enter__()
170
182
  span._start = start_t
183
+ # The call ended at end_t (a stream's last chunk), not at whatever later moment this
184
+ # runs - __exit__ honors the override instead of measuring to time.time().
185
+ span._end_override = end_t
171
186
  span.input = input_repr
172
187
  span.output = output
173
188
  if error:
@@ -217,7 +232,13 @@ class TracedStream:
217
232
 
218
233
  Attribute access falls through to the wrapped stream (``.response``,
219
234
  provider helpers), and ``__iter__``/``__aiter__`` return ``self`` so early
220
- ``break`` leaves no half-driven generator behind.
235
+ ``break`` leaves no half-driven generator behind. It is a proxy, not a
236
+ subclass: ``isinstance(stream, openai.Stream)`` is False and ``repr()``
237
+ shows the proxy - branch on ``stream=True`` in your own code, not on type.
238
+
239
+ Tracing never breaks the caller: a failure while building or sending the
240
+ trace is logged and swallowed, and the stream's own iteration/close
241
+ semantics are untouched.
221
242
  """
222
243
 
223
244
  def __init__(
@@ -230,6 +251,8 @@ class TracedStream:
230
251
  self._accumulator = accumulator
231
252
  self._on_finish = on_finish
232
253
  self._done = False
254
+ # A watchdog close() racing the reader's StopIteration must not finalize twice.
255
+ self._done_lock = threading.Lock()
233
256
  self._first_chunk_t: Optional[float] = None
234
257
  self._last_chunk_t: Optional[float] = None
235
258
  self._sync_iter: Any = None
@@ -250,9 +273,10 @@ class TracedStream:
250
273
  pass
251
274
 
252
275
  def _finish(self, error: Optional[str]) -> None:
253
- if self._done:
254
- return
255
- self._done = True
276
+ with self._done_lock:
277
+ if self._done:
278
+ return
279
+ self._done = True
256
280
  try:
257
281
  result = self._accumulator.result()
258
282
  except Exception:
@@ -264,7 +288,12 @@ class TracedStream:
264
288
  # The response "ended" at its last chunk, not at whatever later moment the caller closed
265
289
  # or dropped the stream - that is the latency the user experienced.
266
290
  result["end_t"] = self._last_chunk_t if self._last_chunk_t is not None else time.time()
267
- self._on_finish(result, error)
291
+ try:
292
+ self._on_finish(result, error)
293
+ except Exception:
294
+ # Building or sending the trace failed. The caller's stream ended normally and must
295
+ # see it end normally - tracing is never allowed to raise into inference code.
296
+ logger.debug("Streamed call could not be traced", exc_info=True)
268
297
 
269
298
  @property
270
299
  def first_chunk_at(self) -> Optional[float]:
@@ -283,9 +312,14 @@ class TracedStream:
283
312
  except StopIteration:
284
313
  self._finish(None)
285
314
  raise
286
- except BaseException as exc:
315
+ except Exception as exc:
287
316
  self._finish(str(exc))
288
317
  raise
318
+ except BaseException:
319
+ # KeyboardInterrupt / GeneratorExit: a cancellation, not the provider failing -
320
+ # record what streamed so far without inventing an error message.
321
+ self._finish(None)
322
+ raise
289
323
  self._observe(chunk)
290
324
  return chunk
291
325
 
@@ -302,9 +336,12 @@ class TracedStream:
302
336
  except StopAsyncIteration:
303
337
  self._finish(None)
304
338
  raise
305
- except BaseException as exc:
339
+ except Exception as exc:
306
340
  self._finish(str(exc))
307
341
  raise
342
+ except BaseException:
343
+ self._finish(None)
344
+ raise
308
345
  self._observe(chunk)
309
346
  return chunk
310
347
 
@@ -338,7 +375,17 @@ class TracedStream:
338
375
  close = getattr(self._stream, "close", None)
339
376
  try:
340
377
  if close is not None:
341
- close()
378
+ result = close()
379
+ if inspect.isawaitable(result):
380
+ # openai's AsyncStream spells its close `async def close()`. A sync close()
381
+ # on it (an easy slip inside async code) would drop the coroutine and leak
382
+ # the connection; run it on the loop when there is one, else at least don't
383
+ # leave an un-awaited coroutine behind.
384
+ try:
385
+ asyncio.get_running_loop().create_task(result)
386
+ except RuntimeError:
387
+ result.close() # type: ignore[union-attr]
388
+ logger.warning("close() called on an async stream outside an event loop - use aclose()")
342
389
  finally:
343
390
  self._finish(None)
344
391
 
@@ -355,6 +402,10 @@ class TracedStream:
355
402
  self._finish(None)
356
403
 
357
404
  def __getattr__(self, item: str) -> Any:
405
+ # Only public attributes delegate. Private names must resolve on the proxy itself, or a
406
+ # half-constructed instance (no _stream yet) would recurse forever looking for it.
407
+ if item.startswith("_"):
408
+ raise AttributeError(item)
358
409
  return getattr(self._stream, item)
359
410
 
360
411
  def __del__(self) -> None:
@@ -232,13 +232,21 @@ def _patch_create(
232
232
  input_repr = _safe_serialize(input_messages)
233
233
 
234
234
  if kwargs.get("stream"):
235
+ # Parent fixed at call time - see openai.py's patched_create for why.
236
+ parent = tracer.current_span
237
+
235
238
  def on_stream_finish(collected: Dict[str, Any], error: Optional[str]) -> None:
239
+ ttft = collected.get("time_to_first_token_ms")
240
+ call_metadata: Dict[str, Any] = {"streaming": True}
241
+ if ttft is not None:
242
+ call_metadata["timeToFirstTokenMs"] = ttft
236
243
  finish_llm_call(
237
244
  tracer,
238
245
  name=name,
239
246
  framework="anthropic",
240
247
  metadata=metadata,
241
- call_metadata={"streaming": True, "timeToFirstTokenMs": collected.get("time_to_first_token_ms")},
248
+ call_metadata=call_metadata,
249
+ active_span=parent,
242
250
  session_id=session_id,
243
251
  start_t=start_t,
244
252
  end_t=collected.get("end_t") or time.time(),
@@ -318,8 +326,9 @@ def _patch_stream(
318
326
  # only shows up in whether `with`/`async with` and
319
327
  # `get_final_message()` are used, handled inside `_TracedStream`.
320
328
  start_t = time.time()
329
+ parent = tracer.current_span
321
330
  ctx = original_stream(*args, **kwargs)
322
- input_repr = _safe_serialize(_prepend_system(kwargs.get("messages"), kwargs.get("system")))
331
+ input_repr = _safe_serialize(_prepend_system(kwargs.get("messages") or (args[0] if args else None), kwargs.get("system")))
323
332
  model = kwargs.get("model")
324
333
  tool_definitions = capture_tool_definitions(kwargs.get("tools"))
325
334
 
@@ -365,6 +374,29 @@ def _patch_stream(
365
374
  """
366
375
 
367
376
  _inner: Any = None
377
+ _sent: bool = False
378
+
379
+ # What streamed so far, WITHOUT draining the rest of the response: the SDK's
380
+ # get_final_message() calls until_done(), which would turn an early `break` into a
381
+ # blocking read of every remaining token. The snapshot is the final message once the
382
+ # stream was consumed, and honestly partial when the caller stopped early.
383
+ def _snapshot(self_inner):
384
+ inner = self_inner._inner
385
+ if inner is None:
386
+ return None
387
+ try:
388
+ return getattr(inner, "current_message_snapshot", None)
389
+ except Exception:
390
+ return None
391
+
392
+ def _send_once(self_inner, end_t: float, error: Optional[str], snapshot: Any) -> None:
393
+ if self_inner._sent:
394
+ return
395
+ self_inner._sent = True
396
+ try:
397
+ build_and_send(end_t, error, snapshot)
398
+ except Exception:
399
+ pass # tracing never raises into the caller
368
400
 
369
401
  def __enter__(self_inner):
370
402
  self_inner._inner = ctx.__enter__()
@@ -373,15 +405,12 @@ def _patch_stream(
373
405
  def __exit__(self_inner, exc_type, exc_val, tb):
374
406
  end_t = time.time()
375
407
  error = str(exc_val) if exc_val else None
376
- final_message = None
377
- if error is None and self_inner._inner is not None:
378
- try:
379
- final_message = self_inner._inner.get_final_message()
380
- except Exception:
381
- pass
382
- result = ctx.__exit__(exc_type, exc_val, tb)
383
- build_and_send(end_t, error, final_message)
384
- return result
408
+ # Snapshot BEFORE the manager closes the stream (the earlier bug read it after).
409
+ snapshot = self_inner._snapshot()
410
+ try:
411
+ return ctx.__exit__(exc_type, exc_val, tb)
412
+ finally:
413
+ self_inner._send_once(end_t, error, snapshot)
385
414
 
386
415
  async def __aenter__(self_inner):
387
416
  self_inner._inner = await ctx.__aenter__()
@@ -390,16 +419,19 @@ def _patch_stream(
390
419
  async def __aexit__(self_inner, exc_type, exc_val, tb):
391
420
  end_t = time.time()
392
421
  error = str(exc_val) if exc_val else None
393
- final_message = None
394
- if error is None and self_inner._inner is not None:
395
- try:
396
- raw = self_inner._inner.get_final_message()
397
- final_message = await raw if inspect.isawaitable(raw) else raw
398
- except Exception:
399
- pass
400
- result = await ctx.__aexit__(exc_type, exc_val, tb)
401
- build_and_send(end_t, error, final_message)
402
- return result
422
+ snapshot = self_inner._snapshot()
423
+ try:
424
+ return await ctx.__aexit__(exc_type, exc_val, tb)
425
+ finally:
426
+ self_inner._send_once(end_t, error, snapshot)
427
+
428
+ def __del__(self_inner):
429
+ # A helper stream that was entered but never exited still records what it saw.
430
+ try:
431
+ if self_inner._inner is not None:
432
+ self_inner._send_once(time.time(), None, self_inner._snapshot())
433
+ except Exception:
434
+ pass
403
435
 
404
436
  def __iter__(self_inner):
405
437
  return iter(ctx)
@@ -27,8 +27,11 @@ Works with both ``openai.OpenAI`` and ``openai.AsyncOpenAI`` clients. Token
27
27
  usage comes straight off the response's OpenAI-shaped ``usage`` block; NIM
28
28
  reports no prompt-cache fields, so cache token counts stay unset.
29
29
 
30
- Streaming calls (``stream=True``) are passed through untouched and are not
31
- currently traced - same posture as ``patch_openai_client``, see its docstring.
30
+ Streaming calls (``stream=True``) are traced too, exactly as
31
+ ``patch_openai_client`` traces them: the stream is wrapped in a transparent
32
+ proxy that assembles the reply from the consumed chunks (token usage when the
33
+ endpoint sends it on the final chunk, e.g. with
34
+ ``stream_options={"include_usage": True}``).
32
35
 
33
36
  Requires: ``pip install "agentx-python[nvidia-nim]"`` (installs the ``openai``
34
37
  client package; there is no separate NIM SDK dependency).
@@ -56,8 +59,9 @@ def patch_nim_client(
56
59
  call with ``framework="nvidia-nim"``.
57
60
 
58
61
  The original method is still called and its return value passed through
59
- unchanged. Sync and async clients both work; ``stream=True`` calls pass
60
- through untraced. Patching is idempotent - and because it shares the guard
62
+ unchanged. Sync and async clients both work; ``stream=True`` calls are
63
+ traced through the same stream proxy as ``patch_openai_client``. Patching
64
+ is idempotent - and because it shares the guard
61
65
  with ``patch_openai_client``, whichever of the two patched a given client
62
66
  first wins (patch each client with the integration that matches where its
63
67
  ``base_url`` actually points).
@@ -30,7 +30,14 @@ from __future__ import annotations
30
30
  import time
31
31
  from typing import Any, Dict, Optional, Tuple
32
32
 
33
+ import logging
34
+
33
35
  from agentx.tracing.tracer import Tracer, _safe_serialize
36
+
37
+ logger = logging.getLogger(__name__)
38
+ # Warn once per process, not per call: a streamed OpenAI call carries no usage unless the caller
39
+ # asked for it, and a silent zero would under-report every streaming app's spend.
40
+ _warned_stream_usage = False
34
41
  from agentx.integrations._traced_call import (
35
42
  StreamAccumulator,
36
43
  capture_tool_definitions,
@@ -203,14 +210,30 @@ def _patch_chat_completions_create(
203
210
  tool_definitions = capture_tool_definitions(kwargs.get("tools"))
204
211
 
205
212
  if kwargs.get("stream"):
213
+ # The parent is fixed at call time: the stream finalizes later, possibly inside an
214
+ # unrelated span (or none), and must not attach to whatever is active then.
215
+ parent = tracer.current_span
216
+
206
217
  def on_stream_finish(collected: Dict[str, Any], error: Optional[str]) -> None:
218
+ global _warned_stream_usage
207
219
  ttft = collected.get("time_to_first_token_ms")
220
+ call_metadata: Dict[str, Any] = {"streaming": True}
221
+ if ttft is not None:
222
+ call_metadata["timeToFirstTokenMs"] = ttft
223
+ if error is None and collected.get("input_tokens") is None and not _warned_stream_usage:
224
+ _warned_stream_usage = True
225
+ logger.warning(
226
+ "Streamed %s call carried no token usage - pass stream_options={\"include_usage\": True} "
227
+ "so traces (and cost) reflect streamed traffic.",
228
+ framework,
229
+ )
208
230
  finish_llm_call(
209
231
  tracer,
210
232
  name=name,
211
233
  framework=framework,
212
234
  metadata=metadata,
213
- call_metadata={"streaming": True, "timeToFirstTokenMs": ttft},
235
+ call_metadata=call_metadata,
236
+ active_span=parent,
214
237
  session_id=session_id,
215
238
  start_t=start_t,
216
239
  end_t=collected.get("end_t") or time.time(),
@@ -19,14 +19,14 @@ from agentx.monitor.sessions import MonitorSessionClient
19
19
  from agentx.monitor.signals import MonitorSignalClient
20
20
 
21
21
  __all__ = [
22
- "AlertEvent",
23
- "AlertRule",
24
- "AlertRulesClient",
25
22
  "AgentXImprovementGroupsError",
26
23
  "AgentXJudgeScorersError",
27
24
  "AgentXMonitorError",
28
25
  "AgentXScorerGroupsError",
29
26
  "AgentXScorersError",
27
+ "AlertEvent",
28
+ "AlertRule",
29
+ "AlertRulesClient",
30
30
  "ImprovementGroupsClient",
31
31
  "JudgeScorer",
32
32
  "JudgeScorerBuilder",
@@ -17,6 +17,22 @@ _ALIASES = {
17
17
  }
18
18
 
19
19
 
20
+ def _validate(
21
+ metric: Optional[str] = None, operator: Optional[str] = None, channels: Optional[List[Dict[str, str]]] = None
22
+ ) -> None:
23
+ """Local checks for the fields the engine would otherwise 400 on - ``None`` means "not given"
24
+ (an update that leaves the field alone)."""
25
+ if metric is not None and metric not in ALERT_METRICS:
26
+ raise ValueError(f"metric must be one of {ALERT_METRICS}, got {metric!r}")
27
+ if operator is not None and operator not in ("gt", "lt"):
28
+ raise ValueError(f"operator must be 'gt' or 'lt', got {operator!r}")
29
+ if channels is not None:
30
+ for channel in channels:
31
+ kind = channel.get("kind") if isinstance(channel, dict) else None
32
+ if kind not in ALERT_CHANNEL_KINDS:
33
+ raise ValueError(f"channel kind must be one of {ALERT_CHANNEL_KINDS}, got {kind!r}")
34
+
35
+
20
36
  class AlertRule(dict):
21
37
  """Wire object for one KPI alert rule (dict subclass so unknown fields round-trip)."""
22
38
 
@@ -48,12 +64,12 @@ class AlertEvent(dict):
48
64
 
49
65
  @property
50
66
  def kind(self) -> str:
51
- return str(self.get("kind"))
67
+ return str(self.get("kind") or "")
52
68
 
53
69
  @property
54
70
  def delivered(self) -> bool:
55
71
  deliveries = self.get("deliveries") or []
56
- return bool(deliveries) and all(bool(d.get("ok")) for d in deliveries)
72
+ return bool(deliveries) and all(isinstance(d, dict) and bool(d.get("ok")) for d in deliveries)
57
73
 
58
74
 
59
75
  def slack(url: str) -> Dict[str, str]:
@@ -138,13 +154,7 @@ class AlertRulesClient:
138
154
  """Create a rule. ``operator`` is ``"gt"`` (above) or ``"lt"`` (below); rates are
139
155
  fractions (``0.10`` = 10%), latency is milliseconds, cost is USD. ``channels`` takes the
140
156
  dicts the module-level helpers build (``slack(url)``, ``pagerduty(key)``, ...)."""
141
- if metric not in ALERT_METRICS:
142
- raise ValueError(f"metric must be one of {ALERT_METRICS}, got {metric!r}")
143
- if operator not in ("gt", "lt"):
144
- raise ValueError(f"operator must be 'gt' or 'lt', got {operator!r}")
145
- for channel in channels:
146
- if channel.get("kind") not in ALERT_CHANNEL_KINDS:
147
- raise ValueError(f"channel kind must be one of {ALERT_CHANNEL_KINDS}, got {channel.get('kind')!r}")
157
+ _validate(metric=metric, operator=operator, channels=channels)
148
158
  payload: Dict[str, Any] = {
149
159
  "name": name,
150
160
  "metric": metric,
@@ -165,7 +175,10 @@ class AlertRulesClient:
165
175
 
166
176
  def update(self, rule_id: str, **fields: Any) -> AlertRule:
167
177
  """Sparse update; snake_case kwargs are mapped to the wire. Changing the metric,
168
- operator, threshold, window, or agent resets the rule's firing state."""
178
+ operator, threshold, window, or agent resets the rule's firing state, and a rule that
179
+ was firing sends its channels a final ``resolved`` notification first. The same local
180
+ checks as ``create`` apply to whichever of ``metric``, ``operator``, ``channels`` are
181
+ given."""
169
182
  payload: Dict[str, Any] = {}
170
183
  for key, value in fields.items():
171
184
  wire_key = _ALIASES.get(key, key)
@@ -175,12 +188,13 @@ class AlertRulesClient:
175
188
  "silently ignore this (see AlertRulesClient.create for the field names)."
176
189
  )
177
190
  payload[wire_key] = value
191
+ _validate(metric=payload.get("metric"), operator=payload.get("operator"), channels=payload.get("channels"))
178
192
  data = self._request("PUT", f"/agent-monitoring/alert-rules/{rule_id}", json=payload)
179
193
  return AlertRule(data.get("rule", data))
180
194
 
181
195
  def delete(self, rule_id: str) -> None:
182
- """Deletes the rule and its history. A PagerDuty incident the rule opened is not
183
- resolved by this - close it in PagerDuty."""
196
+ """Deletes the rule and its history. A rule that is firing sends its channels a final
197
+ ``resolved`` notification (closing the PagerDuty incident it opened) before it goes."""
184
198
  self._request("DELETE", f"/agent-monitoring/alert-rules/{rule_id}", retry=False)
185
199
 
186
200
  def events(self, rule_id: str, limit: int = 50) -> List[AlertEvent]:
agentx/tracing/tracer.py CHANGED
@@ -165,6 +165,9 @@ class _TraceSpan:
165
165
  self.tool_calls: list = []
166
166
 
167
167
  self._start: Optional[float] = None
168
+ # Set by callers that know when the work actually ended (a streamed LLM call's last
169
+ # chunk) so __exit__ does not measure to "now" - see finish_llm_call's root path.
170
+ self._end_override: Optional[float] = None
168
171
  self._error: Optional[str] = None
169
172
 
170
173
  self._captured_model: Optional[str] = None
@@ -212,7 +215,8 @@ class _TraceSpan:
212
215
 
213
216
  def __exit__(self, exc_type, exc_val, tb):
214
217
  self._tracer._pop_active_span(self)
215
- latency_ms = int((time.time() - self._start) * 1000) if self._start else None
218
+ ended_at = self._end_override if self._end_override is not None else time.time()
219
+ latency_ms = int((ended_at - self._start) * 1000) if self._start else None
216
220
  if exc_val is not None and self._error is None:
217
221
  self._error = str(exc_val)
218
222
 
agentx/version.py CHANGED
@@ -1,7 +1,7 @@
1
- VERSION = "0.8.27"
1
+ VERSION = "0.8.28"
2
2
 
3
3
  # The AgentX-trace-eval release this SDK version is tested against - what `agentx-trace-eval`
4
4
  # installs and converges to (see agentx/cli.py). Bump together with VERSION when releasing, so
5
5
  # every published SDK names a known-good engine+dashboard pair. Users can override with
6
6
  # AGENTX_TRACE_EVAL_VERSION=<tag|latest>.
7
- ENGINE_VERSION = "v0.3.30"
7
+ ENGINE_VERSION = "v0.3.31"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentx-python
3
- Version: 0.8.27
3
+ Version: 0.8.28
4
4
  Summary: Official Python SDK for AgentX (https://www.agentx.so/)
5
5
  Home-page: https://github.com/AgentX-ai/AgentX-python
6
6
  Author: Robin Wang and AgentX Team
@@ -260,6 +260,12 @@ extra:
260
260
  | LlamaIndex | `pip install "agentx-python[llamaindex]"` | `AgentXLlamaIndexHandler` |
261
261
  | AutoGen | `pip install "agentx-python[autogen]"` | `AgentXAutoGenObserver` |
262
262
 
263
+ Raw-client patches (`patch_openai_client`, `patch_nim_client`, `patch_anthropic_client`) trace
264
+ streaming calls too: the returned stream is a transparent proxy that assembles the reply from the
265
+ chunks you consume, with latency measured to the last chunk and the time to first token in the
266
+ trace metadata. OpenAI-compatible endpoints only send token usage on streams when you pass
267
+ `stream_options={"include_usage": True}`.
268
+
263
269
  > **Warning: pick ONE instrumentation layer per LLM call.** Do not combine
264
270
  > `AgentXCallbackHandler` (or any framework integration) with a patched provider client
265
271
  > (`patch_openai_client`, `patch_anthropic_client`, `patch_genai_client`) on the same code
@@ -10,7 +10,7 @@ agentx/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
10
  agentx/testing.py,sha256=0shZEid_vhJgBegpO_loUq6APYDxQrUgD3jvcRdIDV8,7372
11
11
  agentx/traces.py,sha256=Jh07n3GLS1SGRsDoLDTUaAbuMEdvjqxCCMA9gUcSZ0M,2331
12
12
  agentx/util.py,sha256=ivCuFQ5AGUw9fR9fJy5HrSp3lLwwngsDwppnvyZpq9E,1471
13
- agentx/version.py,sha256=KdSKPJMRW7kj12djMaC4Y9MJOtmKcvAm9zC6IV3Qeig,366
13
+ agentx/version.py,sha256=GG8-5mWn22akjdeFMAvH6YuyN72ciRGt8EIqmtQjbdw,366
14
14
  agentx/evaluations/__init__.py,sha256=Erv7RGFlRxGTG4rVb2uHhCqLLEX_iAWKqkZKzK6CumE,262
15
15
  agentx/evaluations/_term.py,sha256=WFpiNzdgDBeJJ-Gg-6X7TwwxllobuE4OqFUTuDQvS3Y,2529
16
16
  agentx/evaluations/client.py,sha256=7c35tS8zM3fuvFfVDQk5etTTEFiIykmPjuJxkuFyFTY,38013
@@ -28,8 +28,8 @@ agentx/evaluations/adapters/http_endpoint.py,sha256=-Gika9lBcXjtbnyB-uNR6nYEVJhW
28
28
  agentx/evaluations/adapters/precomputed.py,sha256=vxnQELmpiU2NkQATfiVjG7v1ZmmkrEA70Y11ijfYtHw,1208
29
29
  agentx/evaluations/adapters/raw.py,sha256=FkDq_mdf21-xt5EHnGyIGcS6VZxkEkFcmM9VTd-T5sA,1130
30
30
  agentx/integrations/__init__.py,sha256=p-YHLIudYxQj1bBj77NiVPsP0VQHemX5E59phkI3Y-Q,711
31
- agentx/integrations/_traced_call.py,sha256=I8t37HUVp-VJYcHo26zkJs80A7S8ajh7F9JQ_IRfNCU,14740
32
- agentx/integrations/anthropic.py,sha256=y-6A32X8CLrwttjSiJuDWunQmi9QuzMYhWHMuutfRJY,16655
31
+ agentx/integrations/_traced_call.py,sha256=N-Yk1Bos2rEdlE6h9OvJYqK54FPLqUJD4dN7vOwhnK0,17602
32
+ agentx/integrations/anthropic.py,sha256=44KEiTv830xjMB2rNyXe92Y-Ha3laxAyATR0qubiQEo,18117
33
33
  agentx/integrations/autogen.py,sha256=V1kvTjxQqOG9KsYd4dztaRYH7zTknW0_fqzKjIQG5dk,9507
34
34
  agentx/integrations/crewai.py,sha256=SCObRsrS40P8jOGoI_T5oW4IYuoBW5WjBNuc7zUBU70,14346
35
35
  agentx/integrations/databricks.py,sha256=vKXnur-LWMTzctFuIKte2N826sydXVlpi1qFXo2dfio,18044
@@ -39,13 +39,13 @@ agentx/integrations/langchain.py,sha256=YDeEFT2irhoEvydwt3QjbmTpk_AZZbYPS9euOR18
39
39
  agentx/integrations/litellm.py,sha256=eW8iCaNq0feRbZma8iOvmJ28WiAO7ZaRmlpPjHAtcvo,7072
40
40
  agentx/integrations/llamaindex.py,sha256=ruzMiC2TC3Xhub4xoXKPHjsvzV1sQ3QqhjkAdMxUUi4,18001
41
41
  agentx/integrations/moveworks.py,sha256=IyBswLE5LwMSp1VbIvG5izYV5BvXmv4atZelnrDn8SE,25670
42
- agentx/integrations/nvidia_nim.py,sha256=bvS6ebEnQZT1DVlIjQQ2RKLI4uPPTdlMKINgDwj0Z-o,2961
43
- agentx/integrations/openai.py,sha256=0DMKmbQ1Rdd-gIaJuGJ-JEcvMnQ6620d0EQcZrNC46Q,11249
42
+ agentx/integrations/nvidia_nim.py,sha256=SVt_WSTsDvyxrCwbioeQ-NZWQL9PI8zXl9Y7ObXUVsU,3165
43
+ agentx/integrations/openai.py,sha256=WWD2SgqZa-zdz54rrOP739cj5vuImbkE6rg-tTC7Slw,12389
44
44
  agentx/integrations/openai_agents.py,sha256=KSOQ55eRPFxxkqm7p4Dsz6HcJ_9uUEwlyywPI8dwbvY,14852
45
- agentx/monitor/__init__.py,sha256=37zUS2j8FZO98TXdrzE42td9kRIWhypm3yqUR5sBVeg,1853
45
+ agentx/monitor/__init__.py,sha256=3KsLb0wedvh16wa8W2G3Z2bZ-LPxe5xmw2wAYmqtfZA,1853
46
46
  agentx/monitor/_transport.py,sha256=ge0pmdvFdUtCJRkTLvAgjuTQVA8YOCYGQNoyRuselRk,2077
47
47
  agentx/monitor/agents.py,sha256=KhsHVhkqa6V0e0lPcuo04fh44_qSaJOaGcD6U6qXSFo,1622
48
- agentx/monitor/alert_rules.py,sha256=UgK6Cnrcz9F9p1QU-eAg1NsjrtfMoA-dwX8AWKwEcVc,8990
48
+ agentx/monitor/alert_rules.py,sha256=KIZoxyjPG4snnwmO3h4GEbKtlWULmcwGqNMLsdweXew,9871
49
49
  agentx/monitor/client.py,sha256=oZctmh77Ki5WnZI6vQaj_tDlmdP6XtkDYt3MKhnvGsU,27563
50
50
  agentx/monitor/improvement_groups.py,sha256=MrXexaBLPNzZHrqZjiCPfmwa-DSr-RHuFdP73SKrMJM,6143
51
51
  agentx/monitor/judge_scorers.py,sha256=Xyoafo9ljeq5RpLe5HxvsL0Y0FebFQ-2CxYz8-E1Wio,21025
@@ -68,10 +68,10 @@ agentx/tracing/ci_types.py,sha256=b-W2LowRhNBVUdaoMPgd4bnKQy9AxhfiikftRwGPAqU,17
68
68
  agentx/tracing/eval_scope.py,sha256=ElMbPxpqpQBVuaUnHNlw9RIQu71oyOpd0yR55bDH8eI,2144
69
69
  agentx/tracing/framework_detect.py,sha256=uV4O7Th-4_2UkdooAyaWCOIA0jwLJbblFQ_ucFDjeJM,2638
70
70
  agentx/tracing/ingest_client.py,sha256=teukTPckFWPg5RhpbGbVDBejBzPIPXUO3_DJcuVh2uc,22660
71
- agentx/tracing/tracer.py,sha256=a9BALnb7UyyOUMAIZW6z-4QkevsO3TsBkY8GiJUEnS4,68848
72
- agentx_python-0.8.27.dist-info/licenses/LICENSE,sha256=gZVsM-nLsE8vlaY6NXXsVoo6IlCClxkToAWmhgT3y_s,10762
73
- agentx_python-0.8.27.dist-info/METADATA,sha256=j6fxEPZbrq1VwHmUYEUGAvRawZH52BlTrogVjrYCy-0,22986
74
- agentx_python-0.8.27.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
75
- agentx_python-0.8.27.dist-info/entry_points.txt,sha256=rQqF1JTY3T1yfviBU1r8PU-5mBdfOk6P4bZ1L4BskIM,172
76
- agentx_python-0.8.27.dist-info/top_level.txt,sha256=s-q-HB9Gb_QdrZNacSeQyF_c25gQooMy7DlxzgLOHPk,7
77
- agentx_python-0.8.27.dist-info/RECORD,,
71
+ agentx/tracing/tracer.py,sha256=N7rIobHM9KdPU9mqBLVqgk_1mqtCNjBD57uAtXn9DO0,69167
72
+ agentx_python-0.8.28.dist-info/licenses/LICENSE,sha256=gZVsM-nLsE8vlaY6NXXsVoo6IlCClxkToAWmhgT3y_s,10762
73
+ agentx_python-0.8.28.dist-info/METADATA,sha256=9sFd2gVCfCWgweOoBcTHqM4IUpfHipe0qxYBEiLU4E4,23408
74
+ agentx_python-0.8.28.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
75
+ agentx_python-0.8.28.dist-info/entry_points.txt,sha256=rQqF1JTY3T1yfviBU1r8PU-5mBdfOk6P4bZ1L4BskIM,172
76
+ agentx_python-0.8.28.dist-info/top_level.txt,sha256=s-q-HB9Gb_QdrZNacSeQyF_c25gQooMy7DlxzgLOHPk,7
77
+ agentx_python-0.8.28.dist-info/RECORD,,