pydftracer 2.0.5.post2.dev0__tar.gz → 2.0.6.post2.dev0__tar.gz

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.
Files changed (60) hide show
  1. {pydftracer-2.0.5.post2.dev0 → pydftracer-2.0.6.post2.dev0}/PKG-INFO +1 -1
  2. pydftracer-2.0.6.post2.dev0/docs/source/api/agent.rst +109 -0
  3. {pydftracer-2.0.5.post2.dev0 → pydftracer-2.0.6.post2.dev0}/docs/source/api/index.rst +3 -0
  4. {pydftracer-2.0.5.post2.dev0 → pydftracer-2.0.6.post2.dev0}/docs/source/index.rst +1 -0
  5. {pydftracer-2.0.5.post2.dev0 → pydftracer-2.0.6.post2.dev0}/pyproject.toml +3 -0
  6. {pydftracer-2.0.5.post2.dev0 → pydftracer-2.0.6.post2.dev0}/python/dftracer/python/__init__.py +2 -0
  7. {pydftracer-2.0.5.post2.dev0 → pydftracer-2.0.6.post2.dev0}/python/dftracer/python/_version.py +3 -3
  8. pydftracer-2.0.6.post2.dev0/python/dftracer/python/agent.py +9 -0
  9. pydftracer-2.0.6.post2.dev0/python/dftracer/python/agent_common.py +381 -0
  10. pydftracer-2.0.6.post2.dev0/python/dftracer/python/agent_init.py +56 -0
  11. pydftracer-2.0.6.post2.dev0/python/dftracer/python/agent_langgraph.py +211 -0
  12. {pydftracer-2.0.5.post2.dev0 → pydftracer-2.0.6.post2.dev0}/python/dftracer/python/ai_common.py +60 -67
  13. {pydftracer-2.0.5.post2.dev0 → pydftracer-2.0.6.post2.dev0}/python/dftracer/python/common.py +31 -1
  14. {pydftracer-2.0.5.post2.dev0 → pydftracer-2.0.6.post2.dev0}/python/dftracer/python/dbg/__init__.py +1 -0
  15. pydftracer-2.0.6.post2.dev0/python/dftracer/python/dbg/agent.py +9 -0
  16. {pydftracer-2.0.5.post2.dev0 → pydftracer-2.0.6.post2.dev0}/python/pydftracer.egg-info/PKG-INFO +1 -1
  17. {pydftracer-2.0.5.post2.dev0 → pydftracer-2.0.6.post2.dev0}/python/pydftracer.egg-info/SOURCES.txt +6 -0
  18. {pydftracer-2.0.5.post2.dev0 → pydftracer-2.0.6.post2.dev0}/python/pydftracer.egg-info/scm_file_list.json +8 -0
  19. pydftracer-2.0.6.post2.dev0/python/pydftracer.egg-info/scm_version.json +8 -0
  20. pydftracer-2.0.5.post2.dev0/python/pydftracer.egg-info/scm_version.json +0 -8
  21. {pydftracer-2.0.5.post2.dev0 → pydftracer-2.0.6.post2.dev0}/.readthedocs.yml +0 -0
  22. {pydftracer-2.0.5.post2.dev0 → pydftracer-2.0.6.post2.dev0}/LICENSE +0 -0
  23. {pydftracer-2.0.5.post2.dev0 → pydftracer-2.0.6.post2.dev0}/MANIFEST.in +0 -0
  24. {pydftracer-2.0.5.post2.dev0 → pydftracer-2.0.6.post2.dev0}/NOTICE +0 -0
  25. {pydftracer-2.0.5.post2.dev0 → pydftracer-2.0.6.post2.dev0}/README.md +0 -0
  26. {pydftracer-2.0.5.post2.dev0 → pydftracer-2.0.6.post2.dev0}/docs/Makefile +0 -0
  27. {pydftracer-2.0.5.post2.dev0 → pydftracer-2.0.6.post2.dev0}/docs/README.md +0 -0
  28. {pydftracer-2.0.5.post2.dev0 → pydftracer-2.0.6.post2.dev0}/docs/make.bat +0 -0
  29. {pydftracer-2.0.5.post2.dev0 → pydftracer-2.0.6.post2.dev0}/docs/requirements.txt +0 -0
  30. {pydftracer-2.0.5.post2.dev0 → pydftracer-2.0.6.post2.dev0}/docs/source/ai_ml_guide.rst +0 -0
  31. {pydftracer-2.0.5.post2.dev0 → pydftracer-2.0.6.post2.dev0}/docs/source/api/ai.rst +0 -0
  32. {pydftracer-2.0.5.post2.dev0 → pydftracer-2.0.6.post2.dev0}/docs/source/api/core.rst +0 -0
  33. {pydftracer-2.0.5.post2.dev0 → pydftracer-2.0.6.post2.dev0}/docs/source/api/dynamo.rst +0 -0
  34. {pydftracer-2.0.5.post2.dev0 → pydftracer-2.0.6.post2.dev0}/docs/source/api/env.rst +0 -0
  35. {pydftracer-2.0.5.post2.dev0 → pydftracer-2.0.6.post2.dev0}/docs/source/conf.py +0 -0
  36. {pydftracer-2.0.5.post2.dev0 → pydftracer-2.0.6.post2.dev0}/docs/source/developers.rst +0 -0
  37. {pydftracer-2.0.5.post2.dev0 → pydftracer-2.0.6.post2.dev0}/docs/source/dynamo_guide.rst +0 -0
  38. {pydftracer-2.0.5.post2.dev0 → pydftracer-2.0.6.post2.dev0}/docs/source/examples.rst +0 -0
  39. {pydftracer-2.0.5.post2.dev0 → pydftracer-2.0.6.post2.dev0}/docs/source/installation.rst +0 -0
  40. {pydftracer-2.0.5.post2.dev0 → pydftracer-2.0.6.post2.dev0}/docs/source/pytorch.rst +0 -0
  41. {pydftracer-2.0.5.post2.dev0 → pydftracer-2.0.6.post2.dev0}/docs/source/quickstart.rst +0 -0
  42. {pydftracer-2.0.5.post2.dev0 → pydftracer-2.0.6.post2.dev0}/docs/source/type_safety.rst +0 -0
  43. {pydftracer-2.0.5.post2.dev0 → pydftracer-2.0.6.post2.dev0}/python/dftracer/python/ai.py +0 -0
  44. {pydftracer-2.0.5.post2.dev0 → pydftracer-2.0.6.post2.dev0}/python/dftracer/python/ai_init.py +0 -0
  45. {pydftracer-2.0.5.post2.dev0 → pydftracer-2.0.6.post2.dev0}/python/dftracer/python/dbg/ai.py +0 -0
  46. {pydftracer-2.0.5.post2.dev0 → pydftracer-2.0.6.post2.dev0}/python/dftracer/python/dbg/logger.py +0 -0
  47. {pydftracer-2.0.5.post2.dev0 → pydftracer-2.0.6.post2.dev0}/python/dftracer/python/dbg/torch/__init__.py +0 -0
  48. {pydftracer-2.0.5.post2.dev0 → pydftracer-2.0.6.post2.dev0}/python/dftracer/python/dbg/torch/profiler.py +0 -0
  49. {pydftracer-2.0.5.post2.dev0 → pydftracer-2.0.6.post2.dev0}/python/dftracer/python/dynamo.py +0 -0
  50. {pydftracer-2.0.5.post2.dev0 → pydftracer-2.0.6.post2.dev0}/python/dftracer/python/env.py +0 -0
  51. {pydftracer-2.0.5.post2.dev0 → pydftracer-2.0.6.post2.dev0}/python/dftracer/python/logger.py +0 -0
  52. {pydftracer-2.0.5.post2.dev0 → pydftracer-2.0.6.post2.dev0}/python/dftracer/python/py.typed +0 -0
  53. {pydftracer-2.0.5.post2.dev0 → pydftracer-2.0.6.post2.dev0}/python/dftracer/python/torch/__init__.py +0 -0
  54. {pydftracer-2.0.5.post2.dev0 → pydftracer-2.0.6.post2.dev0}/python/dftracer/python/torch/common.py +0 -0
  55. {pydftracer-2.0.5.post2.dev0 → pydftracer-2.0.6.post2.dev0}/python/dftracer/python/torch/profiler.py +0 -0
  56. {pydftracer-2.0.5.post2.dev0 → pydftracer-2.0.6.post2.dev0}/python/pydftracer.egg-info/dependency_links.txt +0 -0
  57. {pydftracer-2.0.5.post2.dev0 → pydftracer-2.0.6.post2.dev0}/python/pydftracer.egg-info/requires.txt +0 -0
  58. {pydftracer-2.0.5.post2.dev0 → pydftracer-2.0.6.post2.dev0}/python/pydftracer.egg-info/top_level.txt +0 -0
  59. {pydftracer-2.0.5.post2.dev0 → pydftracer-2.0.6.post2.dev0}/setup.cfg +0 -0
  60. {pydftracer-2.0.5.post2.dev0 → pydftracer-2.0.6.post2.dev0}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pydftracer
3
- Version: 2.0.5.post2.dev0
3
+ Version: 2.0.6.post2.dev0
4
4
  Summary: Python libs for DFTracer
5
5
  Author-email: "Hariharan Devarajan (Hari)" <hariharandev1@llnl.gov>, Ray Andrew Sinurat <raydreww@gmail.com>
6
6
  Maintainer-email: "Hariharan Devarajan (Hari)" <hariharandev1@llnl.gov>, Ray Andrew Sinurat <raydreww@gmail.com>
@@ -0,0 +1,109 @@
1
+ Agent API
2
+ =========
3
+
4
+ This section documents the agent tracing namespace and its event model.
5
+
6
+ Agent Module
7
+ ------------
8
+
9
+ The ``agent`` module provides decorators and utilities for tracing agentic workflows.
10
+
11
+ .. automodule:: dftracer.python.agent
12
+ :members:
13
+ :undoc-members:
14
+
15
+ Agent Categories
16
+ ----------------
17
+
18
+ .. autoclass:: dftracer.python.AgentCategory
19
+ :members:
20
+ :undoc-members:
21
+
22
+ Agent Components
23
+ ----------------
24
+
25
+ .. autoclass:: dftracer.python.Workflow
26
+ :members:
27
+ :undoc-members:
28
+
29
+ .. autoclass:: dftracer.python.AgentStep
30
+ :members:
31
+ :undoc-members:
32
+
33
+ .. autoclass:: dftracer.python.LLM
34
+ :members:
35
+ :undoc-members:
36
+
37
+ .. autoclass:: dftracer.python.Tool
38
+ :members:
39
+ :undoc-members:
40
+
41
+ .. autoclass:: dftracer.python.AgentData
42
+ :members:
43
+ :undoc-members:
44
+
45
+ .. autoclass:: dftracer.python.Message
46
+ :members:
47
+ :undoc-members:
48
+
49
+ .. autoclass:: dftracer.python.Judge
50
+ :members:
51
+ :undoc-members:
52
+
53
+ Agent Instance
54
+ --------------
55
+
56
+ .. autoclass:: dftracer.python.Agent
57
+ :members:
58
+ :undoc-members:
59
+
60
+ Convenience aliases:
61
+
62
+ - ``agent`` - Main agent tracing instance
63
+ - ``workflow`` - Workflow tracing category
64
+ - ``agent_step`` - Agent step tracing category
65
+ - ``llm`` - LLM tracing category
66
+ - ``tool`` - Tool tracing category
67
+ - ``agent_data`` - Data operation tracing category
68
+ - ``message`` - Message/event tracing category
69
+ - ``judge`` - Evaluation/retry tracing category
70
+
71
+ Span Arguments and Aggregation
72
+ ------------------------------
73
+
74
+ Spans carry their ``args`` (``trace_id``, ``step``, token counts, and so on) in the
75
+ trace, and ``span.update_args({...})`` adds values after the work finishes, such as
76
+ token usage from an LLM response.
77
+
78
+ DFTracer can aggregate short events into counters to keep traces small. An
79
+ aggregated event no longer carries its ``args``, so with the default aggregation a
80
+ short ``llm.call`` or ``tool.call`` loses its arguments, token counts included. To
81
+ keep agent spans intact, either turn aggregation off:
82
+
83
+ .. code-block:: bash
84
+
85
+ DFTRACER_ENABLE_AGGREGATION=0
86
+
87
+ or aggregate only the I/O events, with the ``SELECTIVE`` type and a rules file that
88
+ includes only POSIX and STDIO:
89
+
90
+ .. code-block:: bash
91
+
92
+ DFTRACER_ENABLE_AGGREGATION=1
93
+ DFTRACER_AGGREGATION_TYPE=SELECTIVE
94
+ DFTRACER_AGGREGATION_FILE=aggregation_rules.yaml
95
+
96
+ .. code-block:: yaml
97
+
98
+ inclusion:
99
+ - cat IN {POSIX,STDIO}
100
+ exclusion:
101
+
102
+ LangGraph Adapter
103
+ -----------------
104
+
105
+ Utilities for tracing LangGraph-style node and workflow invocation boundaries:
106
+
107
+ .. autofunction:: dftracer.python.get_or_init_trace_context
108
+ .. autofunction:: dftracer.python.traced_node
109
+ .. autofunction:: dftracer.python.trace_invoke
@@ -8,6 +8,7 @@ This section contains the complete API reference for pydftracer.
8
8
 
9
9
  core
10
10
  ai
11
+ agent
11
12
  dynamo
12
13
  env
13
14
 
@@ -35,6 +36,7 @@ AI/ML Support
35
36
  ~~~~~~~~~~~~~
36
37
 
37
38
  - ``DFTracerAI`` - Base class for AI/ML tracing
39
+ - ``Agent`` - Base class for agent/workflow tracing
38
40
  - ``Dynamo`` - PyTorch Dynamo integration class
39
41
  - ``dynamo`` - Pre-configured Dynamo instance for tracing
40
42
  - ``create_backend`` - Create a custom torch.compile backend with DFTracer
@@ -58,5 +60,6 @@ Modules
58
60
 
59
61
  - :doc:`core` - Core tracing functionality (logger, common utilities)
60
62
  - :doc:`ai` - AI/ML specific tracing features
63
+ - :doc:`agent` - Agent/workflow tracing features
61
64
  - :doc:`dynamo` - PyTorch Dynamo integration
62
65
  - :doc:`env` - Environment configuration and setup
@@ -15,6 +15,7 @@ Features
15
15
  - **Type-Safe Decorators**: Fully type-checked with mypy, preserves function signatures
16
16
  - **Function Decorators**: Simple ``@dft_fn`` decorator for tracing Python functions
17
17
  - **AI/ML Support**: Specialized tracing for AI/ML workflows
18
+ - **Agent Workflow Support**: Structured tracing for agentic workflows
18
19
  - **PyTorch Integration**: Capture PyTorch profiler events to DFTracer traces
19
20
  - **PyTorch Dynamo Integration**: Wrapper of PyTorch's Dynamo
20
21
  - **Automatic I/O Tracing**: Transparent tracing of I/O operations when enabled
@@ -125,7 +125,10 @@ ignore = [
125
125
  "tests/*" = ["B011", "B023"]
126
126
  "python/dftracer/python/logger.py" = ["E402", "F405"]
127
127
  "python/dftracer/python/dbg/logger.py" = ["E402", "F405"]
128
+ "python/dftracer/python/dbg/agent.py" = ["E402"]
128
129
  "python/dftracer/python/dbg/ai.py" = ["E402"]
130
+ "python/dftracer/python/agent.py" = ["E402"]
131
+ "python/dftracer/python/agent_init.py" = ["F405"]
129
132
  "python/dftracer/python/ai.py" = ["E402"]
130
133
  "python/dftracer/python/ai_common.py" = ["F405"]
131
134
  "python/dftracer/python/ai_init.py" = ["F405"]
@@ -1,5 +1,7 @@
1
1
  from importlib.metadata import PackageNotFoundError, version
2
2
 
3
+ from dftracer.python.agent import *
4
+ from dftracer.python.agent_langgraph import *
3
5
  from dftracer.python.ai import *
4
6
  from dftracer.python.dynamo import *
5
7
  from dftracer.python.logger import *
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '2.0.5.post2.dev0'
22
- __version_tuple__ = version_tuple = (2, 0, 5, 'post2', 'dev0')
21
+ __version__ = version = '2.0.6.post2.dev0'
22
+ __version_tuple__ = version_tuple = (2, 0, 6, 'post2', 'dev0')
23
23
 
24
- __commit_id__ = commit_id = 'gb88b10fd4'
24
+ __commit_id__ = commit_id = 'g68307eab6'
@@ -0,0 +1,9 @@
1
+ import dftracer.python.ai_common as ai_common
2
+ from dftracer.python.logger import dft_fn as dft_fn_orig
3
+ from dftracer.python.logger import dftracer as dftracer_orig
4
+
5
+ ai_common.dft_fn = dft_fn_orig # type: ignore[misc]
6
+ ai_common.dftracer = dftracer_orig # type: ignore[misc]
7
+
8
+ from dftracer.python.agent_common import *
9
+ from dftracer.python.agent_init import *
@@ -0,0 +1,381 @@
1
+ from typing import Any, Optional
2
+
3
+ from dftracer.python.ai_common import DFTracerAI
4
+ from dftracer.python.common import StringEnum, auto
5
+
6
+ ROOT_NAME = "agent_root"
7
+ ROOT_CAT = "agent_root"
8
+
9
+ TRACE_ID_KEY = "trace_id"
10
+ WORKFLOW_ID_KEY = "workflow_id"
11
+ RUN_ID_KEY = "run_id"
12
+ STEP_ID_KEY = "step_id"
13
+ AGENT_ID_KEY = "agent_id"
14
+ AGENT_ROLE_KEY = "agent_role"
15
+ SPAN_ID_KEY = "span_id"
16
+ PARENT_SPAN_ID_KEY = "parent_span_id"
17
+ TOOL_CALL_ID_KEY = "tool_call_id"
18
+ LLM_CALL_ID_KEY = "llm_call_id"
19
+ FRAMEWORK_KEY = "framework"
20
+ MODEL_NAME_KEY = "model_name"
21
+ MESSAGE_TYPE_KEY = "message_type"
22
+ LOGICAL_PATH_KEY = "logical_path"
23
+ PHYSICAL_PATH_KEY = "physical_path"
24
+ FORMAT_KEY = "format"
25
+ OPERATION_KIND_KEY = "operation_kind"
26
+ SUCCESS_KEY = "success"
27
+ ERROR_TYPE_KEY = "error_type"
28
+
29
+ # Token usage tracking keys for LLM calls
30
+ PROMPT_TOKENS_KEY = "prompt_tokens"
31
+ COMPLETION_TOKENS_KEY = "completion_tokens"
32
+ TOTAL_TOKENS_KEY = "total_tokens"
33
+ CONTEXT_SIZE_KEY = "context_size"
34
+ CONTEXT_WINDOW_KEY = "context_window"
35
+ CONTEXT_UTILIZATION_KEY = "context_utilization"
36
+
37
+
38
+ class AgentCategory(StringEnum):
39
+ WORKFLOW = auto()
40
+ STEP = auto()
41
+ LLM = auto()
42
+ TOOL = auto()
43
+ DATA = auto()
44
+ MESSAGE = auto()
45
+ JUDGE = auto()
46
+
47
+
48
+ class WorkflowEvent(StringEnum):
49
+ RUN = auto()
50
+ RESUME = auto()
51
+
52
+
53
+ class AgentStepEvent(StringEnum):
54
+ PLAN = auto()
55
+ ACT = auto()
56
+ OBSERVE = auto()
57
+ REFLECT = auto()
58
+
59
+
60
+ class LLMEvent(StringEnum):
61
+ CALL = auto()
62
+ STREAM = auto()
63
+ PARSE = auto()
64
+
65
+
66
+ class ToolEvent(StringEnum):
67
+ CALL = auto()
68
+ RESULT = auto()
69
+
70
+
71
+ class AgentDataEvent(StringEnum):
72
+ LOAD = auto()
73
+ SAVE = auto()
74
+
75
+
76
+ class MessageEvent(StringEnum):
77
+ SEND = auto()
78
+ RECEIVE = auto()
79
+ STREAM = auto()
80
+
81
+
82
+ class JudgeEvent(StringEnum):
83
+ EVALUATE = auto()
84
+ RETRY = auto()
85
+
86
+
87
+ class Workflow(DFTracerAI):
88
+ run: DFTracerAI
89
+ resume: DFTracerAI
90
+
91
+ def __init__(
92
+ self,
93
+ epoch: Optional[int] = None,
94
+ step: Optional[int] = None,
95
+ image_idx: Optional[int] = None,
96
+ image_size: Optional[Any] = None,
97
+ enable: bool = True,
98
+ ):
99
+ super().__init__(
100
+ cat=AgentCategory.WORKFLOW,
101
+ name=AgentCategory.WORKFLOW,
102
+ epoch=epoch,
103
+ step=step,
104
+ image_idx=image_idx,
105
+ image_size=image_size,
106
+ enable=enable,
107
+ )
108
+ self.create_children(
109
+ {
110
+ "run": WorkflowEvent.RUN,
111
+ "resume": WorkflowEvent.RESUME,
112
+ }
113
+ )
114
+
115
+
116
+ class AgentStep(DFTracerAI):
117
+ plan: DFTracerAI
118
+ act: DFTracerAI
119
+ observe: DFTracerAI
120
+ reflect: DFTracerAI
121
+
122
+ def __init__(
123
+ self,
124
+ epoch: Optional[int] = None,
125
+ step: Optional[int] = None,
126
+ image_idx: Optional[int] = None,
127
+ image_size: Optional[Any] = None,
128
+ enable: bool = True,
129
+ ):
130
+ super().__init__(
131
+ cat=AgentCategory.STEP,
132
+ name=AgentCategory.STEP,
133
+ epoch=epoch,
134
+ step=step,
135
+ image_idx=image_idx,
136
+ image_size=image_size,
137
+ enable=enable,
138
+ )
139
+ self.create_children(
140
+ {
141
+ "plan": AgentStepEvent.PLAN,
142
+ "act": AgentStepEvent.ACT,
143
+ "observe": AgentStepEvent.OBSERVE,
144
+ "reflect": AgentStepEvent.REFLECT,
145
+ }
146
+ )
147
+
148
+
149
+ class LLM(DFTracerAI):
150
+ call: DFTracerAI
151
+ stream: DFTracerAI
152
+ parse: DFTracerAI
153
+
154
+ def __init__(
155
+ self,
156
+ epoch: Optional[int] = None,
157
+ step: Optional[int] = None,
158
+ image_idx: Optional[int] = None,
159
+ image_size: Optional[Any] = None,
160
+ enable: bool = True,
161
+ ):
162
+ super().__init__(
163
+ cat=AgentCategory.LLM,
164
+ name=AgentCategory.LLM,
165
+ epoch=epoch,
166
+ step=step,
167
+ image_idx=image_idx,
168
+ image_size=image_size,
169
+ enable=enable,
170
+ )
171
+ self.create_children(
172
+ {
173
+ "call": LLMEvent.CALL,
174
+ "stream": LLMEvent.STREAM,
175
+ "parse": LLMEvent.PARSE,
176
+ }
177
+ )
178
+
179
+
180
+ class Tool(DFTracerAI):
181
+ call: DFTracerAI
182
+ result: DFTracerAI
183
+
184
+ def __init__(
185
+ self,
186
+ epoch: Optional[int] = None,
187
+ step: Optional[int] = None,
188
+ image_idx: Optional[int] = None,
189
+ image_size: Optional[Any] = None,
190
+ enable: bool = True,
191
+ ):
192
+ super().__init__(
193
+ cat=AgentCategory.TOOL,
194
+ name=AgentCategory.TOOL,
195
+ epoch=epoch,
196
+ step=step,
197
+ image_idx=image_idx,
198
+ image_size=image_size,
199
+ enable=enable,
200
+ )
201
+ self.create_children(
202
+ {
203
+ "call": ToolEvent.CALL,
204
+ "result": ToolEvent.RESULT,
205
+ }
206
+ )
207
+
208
+
209
+ class AgentData(DFTracerAI):
210
+ load: DFTracerAI
211
+ save: DFTracerAI
212
+
213
+ def __init__(
214
+ self,
215
+ epoch: Optional[int] = None,
216
+ step: Optional[int] = None,
217
+ image_idx: Optional[int] = None,
218
+ image_size: Optional[Any] = None,
219
+ enable: bool = True,
220
+ ):
221
+ super().__init__(
222
+ cat=AgentCategory.DATA,
223
+ name=AgentCategory.DATA,
224
+ epoch=epoch,
225
+ step=step,
226
+ image_idx=image_idx,
227
+ image_size=image_size,
228
+ enable=enable,
229
+ )
230
+ self.create_children(
231
+ {
232
+ "load": AgentDataEvent.LOAD,
233
+ "save": AgentDataEvent.SAVE,
234
+ }
235
+ )
236
+
237
+
238
+ class Message(DFTracerAI):
239
+ send: DFTracerAI
240
+ receive: DFTracerAI
241
+ stream: DFTracerAI
242
+
243
+ def __init__(
244
+ self,
245
+ epoch: Optional[int] = None,
246
+ step: Optional[int] = None,
247
+ image_idx: Optional[int] = None,
248
+ image_size: Optional[Any] = None,
249
+ enable: bool = True,
250
+ ):
251
+ super().__init__(
252
+ cat=AgentCategory.MESSAGE,
253
+ name=AgentCategory.MESSAGE,
254
+ epoch=epoch,
255
+ step=step,
256
+ image_idx=image_idx,
257
+ image_size=image_size,
258
+ enable=enable,
259
+ )
260
+ self.create_children(
261
+ {
262
+ "send": MessageEvent.SEND,
263
+ "receive": MessageEvent.RECEIVE,
264
+ "stream": MessageEvent.STREAM,
265
+ }
266
+ )
267
+
268
+
269
+ class Judge(DFTracerAI):
270
+ evaluate: DFTracerAI
271
+ retry: DFTracerAI
272
+
273
+ def __init__(
274
+ self,
275
+ epoch: Optional[int] = None,
276
+ step: Optional[int] = None,
277
+ image_idx: Optional[int] = None,
278
+ image_size: Optional[Any] = None,
279
+ enable: bool = True,
280
+ ):
281
+ super().__init__(
282
+ cat=AgentCategory.JUDGE,
283
+ name=AgentCategory.JUDGE,
284
+ epoch=epoch,
285
+ step=step,
286
+ image_idx=image_idx,
287
+ image_size=image_size,
288
+ enable=enable,
289
+ )
290
+ self.create_children(
291
+ {
292
+ "evaluate": JudgeEvent.EVALUATE,
293
+ "retry": JudgeEvent.RETRY,
294
+ }
295
+ )
296
+
297
+
298
+ # fmt: off
299
+ class Agent(DFTracerAI):
300
+ workflow: Workflow
301
+ step: AgentStep
302
+ llm: LLM
303
+ tool: Tool
304
+ data: AgentData
305
+ message: Message
306
+ judge: Judge
307
+
308
+ def __init__(
309
+ self,
310
+ epoch: Optional[int] = None,
311
+ step: Optional[int] = None,
312
+ image_idx: Optional[int] = None,
313
+ image_size: Optional[Any] = None,
314
+ enable: bool = True,
315
+ ):
316
+ super().__init__(cat=ROOT_CAT, name=ROOT_NAME, epoch=epoch, step=step, image_idx=image_idx, image_size=image_size, enable=enable)
317
+ self.workflow = Workflow(epoch=epoch, step=step, image_idx=image_idx, image_size=image_size, enable=enable)
318
+ self.step = AgentStep(epoch=epoch, step=step, image_idx=image_idx, image_size=image_size, enable=enable)
319
+ self.llm = LLM(epoch=epoch, step=step, image_idx=image_idx, image_size=image_size, enable=enable)
320
+ self.tool = Tool(epoch=epoch, step=step, image_idx=image_idx, image_size=image_size, enable=enable)
321
+ self.data = AgentData(epoch=epoch, step=step, image_idx=image_idx, image_size=image_size, enable=enable)
322
+ self.message = Message(epoch=epoch, step=step, image_idx=image_idx, image_size=image_size, enable=enable)
323
+ self.judge = Judge(epoch=epoch, step=step, image_idx=image_idx, image_size=image_size, enable=enable)
324
+
325
+ self._children = {
326
+ "workflow": self.workflow,
327
+ "step": self.step,
328
+ "llm": self.llm,
329
+ "tool": self.tool,
330
+ "data": self.data,
331
+ "message": self.message,
332
+ "judge": self.judge,
333
+ }
334
+ # fmt: on
335
+
336
+
337
+ __all__ = [
338
+ "ROOT_NAME",
339
+ "ROOT_CAT",
340
+ "TRACE_ID_KEY",
341
+ "WORKFLOW_ID_KEY",
342
+ "RUN_ID_KEY",
343
+ "STEP_ID_KEY",
344
+ "AGENT_ID_KEY",
345
+ "AGENT_ROLE_KEY",
346
+ "SPAN_ID_KEY",
347
+ "PARENT_SPAN_ID_KEY",
348
+ "TOOL_CALL_ID_KEY",
349
+ "LLM_CALL_ID_KEY",
350
+ "FRAMEWORK_KEY",
351
+ "MODEL_NAME_KEY",
352
+ "MESSAGE_TYPE_KEY",
353
+ "LOGICAL_PATH_KEY",
354
+ "PHYSICAL_PATH_KEY",
355
+ "FORMAT_KEY",
356
+ "OPERATION_KIND_KEY",
357
+ "SUCCESS_KEY",
358
+ "ERROR_TYPE_KEY",
359
+ "PROMPT_TOKENS_KEY",
360
+ "COMPLETION_TOKENS_KEY",
361
+ "TOTAL_TOKENS_KEY",
362
+ "CONTEXT_SIZE_KEY",
363
+ "CONTEXT_WINDOW_KEY",
364
+ "CONTEXT_UTILIZATION_KEY",
365
+ "AgentCategory",
366
+ "WorkflowEvent",
367
+ "AgentStepEvent",
368
+ "LLMEvent",
369
+ "ToolEvent",
370
+ "AgentDataEvent",
371
+ "MessageEvent",
372
+ "JudgeEvent",
373
+ "Workflow",
374
+ "AgentStep",
375
+ "LLM",
376
+ "Tool",
377
+ "AgentData",
378
+ "Message",
379
+ "Judge",
380
+ "Agent",
381
+ ]
@@ -0,0 +1,56 @@
1
+ from dftracer.python.agent_common import *
2
+
3
+ agent = Agent()
4
+ workflow = agent.workflow
5
+ agent_step = agent.step
6
+ llm = agent.llm
7
+ tool = agent.tool
8
+ agent_data = agent.data
9
+ message = agent.message
10
+ judge = agent.judge
11
+
12
+ __all__ = [
13
+ "ROOT_NAME",
14
+ "ROOT_CAT",
15
+ "TRACE_ID_KEY",
16
+ "WORKFLOW_ID_KEY",
17
+ "RUN_ID_KEY",
18
+ "STEP_ID_KEY",
19
+ "AGENT_ID_KEY",
20
+ "AGENT_ROLE_KEY",
21
+ "SPAN_ID_KEY",
22
+ "PARENT_SPAN_ID_KEY",
23
+ "TOOL_CALL_ID_KEY",
24
+ "LLM_CALL_ID_KEY",
25
+ "FRAMEWORK_KEY",
26
+ "MODEL_NAME_KEY",
27
+ "MESSAGE_TYPE_KEY",
28
+ "LOGICAL_PATH_KEY",
29
+ "PHYSICAL_PATH_KEY",
30
+ "FORMAT_KEY",
31
+ "OPERATION_KIND_KEY",
32
+ "SUCCESS_KEY",
33
+ "ERROR_TYPE_KEY",
34
+ "PROMPT_TOKENS_KEY",
35
+ "COMPLETION_TOKENS_KEY",
36
+ "TOTAL_TOKENS_KEY",
37
+ "CONTEXT_SIZE_KEY",
38
+ "CONTEXT_WINDOW_KEY",
39
+ "CONTEXT_UTILIZATION_KEY",
40
+ "AgentCategory",
41
+ "WorkflowEvent",
42
+ "AgentStepEvent",
43
+ "LLMEvent",
44
+ "ToolEvent",
45
+ "AgentDataEvent",
46
+ "MessageEvent",
47
+ "JudgeEvent",
48
+ "agent",
49
+ "workflow",
50
+ "agent_step",
51
+ "llm",
52
+ "tool",
53
+ "agent_data",
54
+ "message",
55
+ "judge",
56
+ ]
@@ -0,0 +1,211 @@
1
+ import uuid
2
+ from typing import Any, Callable, Dict, Mapping, MutableMapping, Optional
3
+
4
+ from dftracer.python.agent_common import (
5
+ AGENT_ID_KEY,
6
+ AGENT_ROLE_KEY,
7
+ FRAMEWORK_KEY,
8
+ OPERATION_KIND_KEY,
9
+ PARENT_SPAN_ID_KEY,
10
+ RUN_ID_KEY,
11
+ SPAN_ID_KEY,
12
+ STEP_ID_KEY,
13
+ TRACE_ID_KEY,
14
+ WORKFLOW_ID_KEY,
15
+ )
16
+ from dftracer.python.agent_init import agent_step, workflow
17
+ from dftracer.python.ai_common import DFTracerAI
18
+ from dftracer.python.common import P, R
19
+
20
+ DEFAULT_FRAMEWORK_NAME = "langgraph"
21
+ DEFAULT_WORKFLOW_EVENT_NAME = "langgraph.invoke"
22
+ DEFAULT_NODE_OPERATION_KIND = "node"
23
+ DEFAULT_WORKFLOW_OPERATION_KIND = "workflow"
24
+
25
+
26
+ def _new_id() -> str:
27
+ return uuid.uuid4().hex
28
+
29
+
30
+ def _extract_thread_id(config: Optional[Mapping[str, Any]]) -> Optional[str]:
31
+ if not config:
32
+ return None
33
+ configurable = config.get("configurable")
34
+ if not isinstance(configurable, Mapping):
35
+ return None
36
+ thread_id = configurable.get("thread_id")
37
+ if thread_id is None:
38
+ return None
39
+ return str(thread_id)
40
+
41
+
42
+ def _prune_none_values(args: Dict[str, Any]) -> Dict[str, Any]:
43
+ return {k: v for k, v in args.items() if v is not None}
44
+
45
+
46
+ def _get_step_event_tracer(step_event: str) -> DFTracerAI:
47
+ tracer: Optional[DFTracerAI] = getattr(agent_step, step_event, None)
48
+ if tracer is None:
49
+ valid_events = ", ".join(sorted(agent_step._children.keys()))
50
+ raise ValueError(
51
+ f"Unknown step_event '{step_event}'. Valid values: {valid_events}"
52
+ )
53
+ return tracer
54
+
55
+
56
+ def get_or_init_trace_context(
57
+ state: Optional[Mapping[str, Any]] = None,
58
+ config: Optional[Mapping[str, Any]] = None,
59
+ ) -> Dict[str, Optional[str]]:
60
+ state = state or {}
61
+
62
+ trace_id = state.get(TRACE_ID_KEY)
63
+ if trace_id is not None:
64
+ trace_id = str(trace_id)
65
+ if not trace_id:
66
+ trace_id = _new_id()
67
+
68
+ workflow_id = state.get(WORKFLOW_ID_KEY)
69
+ if workflow_id is not None:
70
+ workflow_id = str(workflow_id)
71
+ if not workflow_id:
72
+ workflow_id = _extract_thread_id(config) or _new_id()
73
+
74
+ parent_span_id = state.get(PARENT_SPAN_ID_KEY)
75
+ if parent_span_id is not None:
76
+ parent_span_id = str(parent_span_id)
77
+
78
+ return {
79
+ TRACE_ID_KEY: trace_id,
80
+ WORKFLOW_ID_KEY: workflow_id,
81
+ PARENT_SPAN_ID_KEY: parent_span_id,
82
+ }
83
+
84
+
85
+ def _merge_trace_updates(
86
+ updates: MutableMapping[str, Any], trace_id: str, workflow_id: str, span_id: str
87
+ ) -> MutableMapping[str, Any]:
88
+ updates.setdefault(TRACE_ID_KEY, trace_id)
89
+ updates.setdefault(WORKFLOW_ID_KEY, workflow_id)
90
+ updates.setdefault(PARENT_SPAN_ID_KEY, span_id)
91
+ return updates
92
+
93
+
94
+ def traced_node(
95
+ node_name: str,
96
+ *,
97
+ agent_role: str = "",
98
+ step_event: str = "act",
99
+ framework: str = DEFAULT_FRAMEWORK_NAME,
100
+ ) -> Callable[[Callable[P, R]], Callable[P, R]]:
101
+ tracer = _get_step_event_tracer(step_event)
102
+
103
+ def _decorator(fn: Callable[P, R]) -> Callable[P, R]:
104
+ def _wrapped(*args: P.args, **kwargs: P.kwargs) -> R:
105
+ state: Optional[Mapping[str, Any]] = None
106
+ kw_state = kwargs.get("state")
107
+ if len(args) > 0 and isinstance(args[0], Mapping):
108
+ state = args[0]
109
+ elif isinstance(kw_state, Mapping):
110
+ state = kw_state
111
+
112
+ config: Optional[Mapping[str, Any]] = None
113
+ kw_config = kwargs.get("config")
114
+ if len(args) > 1 and isinstance(args[1], Mapping):
115
+ config = args[1]
116
+ elif isinstance(kw_config, Mapping):
117
+ config = kw_config
118
+
119
+ ctx = get_or_init_trace_context(state=state, config=config)
120
+ span_id = _new_id()
121
+ event_args = _prune_none_values(
122
+ {
123
+ TRACE_ID_KEY: ctx[TRACE_ID_KEY],
124
+ WORKFLOW_ID_KEY: ctx[WORKFLOW_ID_KEY],
125
+ SPAN_ID_KEY: span_id,
126
+ STEP_ID_KEY: span_id,
127
+ PARENT_SPAN_ID_KEY: ctx[PARENT_SPAN_ID_KEY],
128
+ AGENT_ID_KEY: node_name,
129
+ AGENT_ROLE_KEY: agent_role or None,
130
+ FRAMEWORK_KEY: framework,
131
+ OPERATION_KIND_KEY: DEFAULT_NODE_OPERATION_KIND,
132
+ }
133
+ )
134
+
135
+ with tracer(args=event_args):
136
+ result = fn(*args, **kwargs)
137
+
138
+ if result is None:
139
+ updates: MutableMapping[str, Any] = {}
140
+ merged = _merge_trace_updates(
141
+ updates=updates,
142
+ trace_id=str(ctx[TRACE_ID_KEY]),
143
+ workflow_id=str(ctx[WORKFLOW_ID_KEY]),
144
+ span_id=span_id,
145
+ )
146
+ return merged # type: ignore[return-value]
147
+
148
+ if isinstance(result, Mapping):
149
+ updates = dict(result)
150
+ merged = _merge_trace_updates(
151
+ updates=updates,
152
+ trace_id=str(ctx[TRACE_ID_KEY]),
153
+ workflow_id=str(ctx[WORKFLOW_ID_KEY]),
154
+ span_id=span_id,
155
+ )
156
+ return merged # type: ignore[return-value]
157
+
158
+ return result
159
+
160
+ return _wrapped
161
+
162
+ return _decorator
163
+
164
+
165
+ def trace_invoke(
166
+ app: Any,
167
+ state: Any,
168
+ config: Optional[Mapping[str, Any]] = None,
169
+ *,
170
+ workflow_name: str = DEFAULT_WORKFLOW_EVENT_NAME,
171
+ framework: str = DEFAULT_FRAMEWORK_NAME,
172
+ run_id: Optional[str] = None,
173
+ ) -> Any:
174
+ if not hasattr(app, "invoke"):
175
+ raise AttributeError("Expected app to have an 'invoke' method")
176
+
177
+ state_mapping = state if isinstance(state, Mapping) else None
178
+ ctx = get_or_init_trace_context(state=state_mapping, config=config)
179
+ run_id = run_id or _new_id()
180
+ span_id = _new_id()
181
+
182
+ event_args = _prune_none_values(
183
+ {
184
+ TRACE_ID_KEY: ctx[TRACE_ID_KEY],
185
+ WORKFLOW_ID_KEY: ctx[WORKFLOW_ID_KEY],
186
+ RUN_ID_KEY: run_id,
187
+ SPAN_ID_KEY: span_id,
188
+ PARENT_SPAN_ID_KEY: ctx[PARENT_SPAN_ID_KEY],
189
+ FRAMEWORK_KEY: framework,
190
+ OPERATION_KIND_KEY: DEFAULT_WORKFLOW_OPERATION_KIND,
191
+ }
192
+ )
193
+
194
+ workflow_tracer = (
195
+ workflow.run.derive(workflow_name) if workflow_name else workflow.run
196
+ )
197
+ with workflow_tracer(args=event_args):
198
+ if config is None:
199
+ return app.invoke(state)
200
+ return app.invoke(state, config=config)
201
+
202
+
203
+ __all__ = [
204
+ "DEFAULT_FRAMEWORK_NAME",
205
+ "DEFAULT_WORKFLOW_EVENT_NAME",
206
+ "DEFAULT_NODE_OPERATION_KIND",
207
+ "DEFAULT_WORKFLOW_OPERATION_KIND",
208
+ "get_or_init_trace_context",
209
+ "traced_node",
210
+ "trace_invoke",
211
+ ]
@@ -1,6 +1,5 @@
1
1
  import functools
2
- import sys
3
- from typing import Any, Callable, Dict, Iterator, List, Optional, overload
2
+ from typing import Any, Callable, Dict, Iterator, Optional, overload
4
3
 
5
4
  from dftracer.python.common import *
6
5
  from dftracer.python.env import *
@@ -8,34 +7,6 @@ from dftracer.python.env import *
8
7
  dft_fn = None # type: ignore
9
8
  dftracer = None # type: ignore
10
9
 
11
- if sys.version_info >= (3, 11):
12
- from enum import StrEnum as StringEnum
13
- from enum import auto
14
- # StrEnum in 3.11 already lower-case
15
- else:
16
- from enum import Enum, auto
17
-
18
- # MIT License: https://github.com/irgeek/StrEnum/blob/master/strenum/__init__.py
19
- class StringEnum(str, Enum):
20
- # this is a specific version that will lowercase the values
21
-
22
- def __new__(cls, value: Any, *args: Any, **kwargs: Any) -> "StringEnum":
23
- if not isinstance(value, (str, auto)):
24
- raise TypeError( # pragma: no cover
25
- f"Values of StrEnums must be strings: {value!r} is a {type(value)}"
26
- )
27
- return super().__new__(cls, value, *args, **kwargs)
28
-
29
- def __str__(self) -> str:
30
- return str(self.value)
31
-
32
- @staticmethod
33
- def _generate_next_value_(
34
- name: str, start: int, count: int, last_values: List[Any]
35
- ) -> str:
36
- return name.lower()
37
-
38
-
39
10
  ITER_COUNT_NAME = "count"
40
11
  INIT_NAME = "init"
41
12
  BLOCK_NAME = "block"
@@ -123,44 +94,16 @@ class _DFTracerAI:
123
94
  image_size: Optional[Any] = None,
124
95
  args: Optional[Dict[str, Any]] = None,
125
96
  ) -> Union[Callable[P, R], "DFTracerAI"]:
126
- if epoch is not None:
127
- self.profiler._arguments_int["epoch"] = TagValue(
128
- epoch, TagDType.INT, TagType.KEY
129
- ).value()
130
- if step is not None:
131
- self.profiler._arguments_int["step"] = TagValue(
132
- step, TagDType.INT, TagType.KEY
133
- ).value()
134
- if image_idx is not None:
135
- self.profiler._arguments_int["image_idx"] = TagValue(
136
- image_idx, TagDType.INT, TagType.KEY
137
- ).value()
138
- if image_size is not None:
139
- self.profiler._arguments_float["image_size"] = TagValue(
140
- image_size, TagDType.FLOAT, TagType.KEY
141
- ).value()
142
- args = args or {}
143
- for key, value in args.items():
144
- if isinstance(value, TagValue):
145
- new_value = value._value
146
- else:
147
- new_value = value
148
- if isinstance(new_value, int):
149
- self.profiler._arguments_int[key] = TagValue(
150
- new_value, TagDType.INT, TagType.KEY
151
- ).value()
152
- elif isinstance(new_value, float):
153
- self.profiler._arguments_float[key] = TagValue(
154
- new_value, TagDType.FLOAT, TagType.KEY
155
- ).value()
156
- else:
157
- self.profiler._arguments_string[key] = TagValue(
158
- str(new_value), TagDType.STRING, TagType.KEY
159
- ).value()
160
-
161
97
  is_enabled = self.profiler._enable if enable is None else enable
162
98
 
163
99
  if fn:
100
+ self.update(
101
+ epoch=epoch,
102
+ step=step,
103
+ image_idx=image_idx,
104
+ image_size=image_size,
105
+ args=args,
106
+ )
164
107
 
165
108
  def _decorator(f: Callable[P, R]) -> Callable[P, R]:
166
109
  @functools.wraps(f)
@@ -174,15 +117,25 @@ class _DFTracerAI:
174
117
 
175
118
  return _decorator(fn)
176
119
  else:
177
- return DFTracerAI(
120
+ tracer = DFTracerAI(
178
121
  cat=self.profiler._cat,
179
122
  name=self.profiler._name,
123
+ enable=is_enabled,
124
+ )
125
+ if DFTRACER_ENABLE and tracer.profiler._enable:
126
+ tracer.profiler._arguments_int = dict(self.profiler._arguments_int)
127
+ tracer.profiler._arguments_float = dict(self.profiler._arguments_float)
128
+ tracer.profiler._arguments_string = dict(
129
+ self.profiler._arguments_string
130
+ )
131
+ tracer.update(
180
132
  epoch=epoch,
181
133
  step=step,
182
134
  image_idx=image_idx,
183
135
  image_size=image_size,
184
- enable=is_enabled,
136
+ args=args,
185
137
  )
138
+ return tracer
186
139
 
187
140
  def __enter__(self) -> "_DFTracerAI":
188
141
  self.profiler.__enter__()
@@ -285,6 +238,46 @@ class _DFTracerAI:
285
238
  ).value()
286
239
  return self
287
240
 
241
+ def update_args(self, args: Dict[str, Any]) -> "_DFTracerAI":
242
+ """Update event arguments during span execution (e.g., token usage from LLM response).
243
+
244
+ This method allows adding or updating args after __enter__ but before __exit__,
245
+ which is useful for capturing metrics from API responses (like token counts).
246
+
247
+ Args:
248
+ args: Dictionary of argument names to values. Int, float, and str types are supported.
249
+
250
+ Returns:
251
+ self for chaining.
252
+
253
+ Example:
254
+ with llm.call(args={"model": "gpt-4"}) as span:
255
+ response = api.call()
256
+ span.update_args({
257
+ "prompt_tokens": response["usage"]["prompt_tokens"],
258
+ "completion_tokens": response["usage"]["completion_tokens"],
259
+ })
260
+ """
261
+ if DFTRACER_ENABLE and self.profiler._enable:
262
+ for key, value in args.items():
263
+ if isinstance(value, TagValue):
264
+ new_value = value._value
265
+ else:
266
+ new_value = value
267
+ if isinstance(new_value, int):
268
+ self.profiler._arguments_int[key] = TagValue(
269
+ new_value, TagDType.INT, TagType.KEY
270
+ ).value()
271
+ elif isinstance(new_value, float):
272
+ self.profiler._arguments_float[key] = TagValue(
273
+ new_value, TagDType.FLOAT, TagType.KEY
274
+ ).value()
275
+ else:
276
+ self.profiler._arguments_string[key] = TagValue(
277
+ str(new_value), TagDType.STRING, TagType.KEY
278
+ ).value()
279
+ return self
280
+
288
281
  def iter(
289
282
  self,
290
283
  iterator: Iterator,
@@ -4,7 +4,7 @@ import os
4
4
  import signal
5
5
  import sys
6
6
  from collections.abc import Iterable, Sequence
7
- from enum import Enum, IntEnum
7
+ from enum import Enum, IntEnum, auto
8
8
  from functools import wraps
9
9
  from pathlib import Path
10
10
  from typing import (
@@ -29,6 +29,36 @@ P = ParamSpec("P") # For capturing function parameters
29
29
  R = TypeVar("R") # For capturing function return types
30
30
  T = TypeVar("T") # For generic iterator types
31
31
 
32
+
33
+ # StrEnum for Python < 3.11, shared by the ai and agent namespaces.
34
+ if sys.version_info >= (3, 11):
35
+ from enum import StrEnum as StringEnum
36
+ from enum import auto
37
+ # StrEnum in 3.11 already lower-case
38
+ else:
39
+ from enum import Enum, auto
40
+
41
+ # MIT License: https://github.com/irgeek/StrEnum/blob/master/strenum/__init__.py
42
+ class StringEnum(str, Enum):
43
+ # this is a specific version that will lowercase the values
44
+
45
+ def __new__(cls, value: Any, *args: Any, **kwargs: Any) -> "StringEnum":
46
+ if not isinstance(value, (str, auto)):
47
+ raise TypeError( # pragma: no cover
48
+ f"Values of StrEnums must be strings: {value!r} is a {type(value)}"
49
+ )
50
+ return super().__new__(cls, value, *args, **kwargs)
51
+
52
+ def __str__(self) -> str:
53
+ return str(self.value)
54
+
55
+ @staticmethod
56
+ def _generate_next_value_(
57
+ name: str, start: int, count: int, last_values: List[Any]
58
+ ) -> str:
59
+ return name.lower()
60
+
61
+
32
62
  # Type alias for TagValue return type
33
63
  TagValueTuple = Tuple[int, Union[int, float, str]]
34
64
 
@@ -1,3 +1,4 @@
1
+ from dftracer.python.dbg.agent import *
1
2
  from dftracer.python.dbg.ai import *
2
3
  from dftracer.python.dbg.logger import *
3
4
  from dftracer.python.dynamo import *
@@ -0,0 +1,9 @@
1
+ import dftracer.python.ai_common as ai_common
2
+ from dftracer.python.dbg.logger import dft_fn as dft_fn_orig
3
+ from dftracer.python.dbg.logger import dftracer as dftracer_orig
4
+
5
+ ai_common.dft_fn = dft_fn_orig # type: ignore[misc]
6
+ ai_common.dftracer = dftracer_orig # type: ignore[misc]
7
+
8
+ from dftracer.python.agent_common import *
9
+ from dftracer.python.agent_init import *
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pydftracer
3
- Version: 2.0.5.post2.dev0
3
+ Version: 2.0.6.post2.dev0
4
4
  Summary: Python libs for DFTracer
5
5
  Author-email: "Hariharan Devarajan (Hari)" <hariharandev1@llnl.gov>, Ray Andrew Sinurat <raydreww@gmail.com>
6
6
  Maintainer-email: "Hariharan Devarajan (Hari)" <hariharandev1@llnl.gov>, Ray Andrew Sinurat <raydreww@gmail.com>
@@ -19,6 +19,7 @@ docs/source/installation.rst
19
19
  docs/source/pytorch.rst
20
20
  docs/source/quickstart.rst
21
21
  docs/source/type_safety.rst
22
+ docs/source/api/agent.rst
22
23
  docs/source/api/ai.rst
23
24
  docs/source/api/core.rst
24
25
  docs/source/api/dynamo.rst
@@ -26,6 +27,10 @@ docs/source/api/env.rst
26
27
  docs/source/api/index.rst
27
28
  python/dftracer/python/__init__.py
28
29
  python/dftracer/python/_version.py
30
+ python/dftracer/python/agent.py
31
+ python/dftracer/python/agent_common.py
32
+ python/dftracer/python/agent_init.py
33
+ python/dftracer/python/agent_langgraph.py
29
34
  python/dftracer/python/ai.py
30
35
  python/dftracer/python/ai_common.py
31
36
  python/dftracer/python/ai_init.py
@@ -35,6 +40,7 @@ python/dftracer/python/env.py
35
40
  python/dftracer/python/logger.py
36
41
  python/dftracer/python/py.typed
37
42
  python/dftracer/python/dbg/__init__.py
43
+ python/dftracer/python/dbg/agent.py
38
44
  python/dftracer/python/dbg/ai.py
39
45
  python/dftracer/python/dbg/logger.py
40
46
  python/dftracer/python/dbg/torch/__init__.py
@@ -14,6 +14,7 @@
14
14
  "docs/make.bat",
15
15
  "docs/requirements.txt",
16
16
  "docs/source/ai_ml_guide.rst",
17
+ "docs/source/api/agent.rst",
17
18
  "docs/source/api/ai.rst",
18
19
  "docs/source/api/core.rst",
19
20
  "docs/source/api/dynamo.rst",
@@ -30,11 +31,16 @@
30
31
  "docs/source/type_safety.rst",
31
32
  "pyproject.toml",
32
33
  "python/dftracer/python/__init__.py",
34
+ "python/dftracer/python/agent.py",
35
+ "python/dftracer/python/agent_common.py",
36
+ "python/dftracer/python/agent_init.py",
37
+ "python/dftracer/python/agent_langgraph.py",
33
38
  "python/dftracer/python/ai.py",
34
39
  "python/dftracer/python/ai_common.py",
35
40
  "python/dftracer/python/ai_init.py",
36
41
  "python/dftracer/python/common.py",
37
42
  "python/dftracer/python/dbg/__init__.py",
43
+ "python/dftracer/python/dbg/agent.py",
38
44
  "python/dftracer/python/dbg/ai.py",
39
45
  "python/dftracer/python/dbg/logger.py",
40
46
  "python/dftracer/python/dbg/torch/__init__.py",
@@ -51,6 +57,8 @@
51
57
  "tests/__init__.py",
52
58
  "tests/poc/pytorch_profiler/requirements.txt",
53
59
  "tests/poc/pytorch_profiler/test.py",
60
+ "tests/test_agent.py",
61
+ "tests/test_agent_langgraph.py",
54
62
  "tests/test_ai_logging.py",
55
63
  "tests/test_coverage_improvements.py",
56
64
  "tests/test_dftracer.py",
@@ -0,0 +1,8 @@
1
+ {
2
+ "tag": "2.0.6",
3
+ "distance": 2,
4
+ "node": "g68307eab60eb6cfecdcda3ad507585fb8241a2f6",
5
+ "dirty": false,
6
+ "branch": "develop",
7
+ "node_date": "2026-10-27"
8
+ }
@@ -1,8 +0,0 @@
1
- {
2
- "tag": "2.0.5",
3
- "distance": 2,
4
- "node": "gb88b10fd4221cd6ed3653a3c34c1eab883bce041",
5
- "dirty": false,
6
- "branch": "develop",
7
- "node_date": "2026-10-14"
8
- }