connectonion 0.5.8__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.
- connectonion/__init__.py +78 -0
- connectonion/address.py +320 -0
- connectonion/agent.py +450 -0
- connectonion/announce.py +84 -0
- connectonion/asgi.py +287 -0
- connectonion/auto_debug_exception.py +181 -0
- connectonion/cli/__init__.py +3 -0
- connectonion/cli/browser_agent/__init__.py +5 -0
- connectonion/cli/browser_agent/browser.py +243 -0
- connectonion/cli/browser_agent/prompt.md +107 -0
- connectonion/cli/commands/__init__.py +1 -0
- connectonion/cli/commands/auth_commands.py +527 -0
- connectonion/cli/commands/browser_commands.py +27 -0
- connectonion/cli/commands/create.py +511 -0
- connectonion/cli/commands/deploy_commands.py +220 -0
- connectonion/cli/commands/doctor_commands.py +173 -0
- connectonion/cli/commands/init.py +469 -0
- connectonion/cli/commands/project_cmd_lib.py +828 -0
- connectonion/cli/commands/reset_commands.py +149 -0
- connectonion/cli/commands/status_commands.py +168 -0
- connectonion/cli/docs/co-vibecoding-principles-docs-contexts-all-in-one.md +2010 -0
- connectonion/cli/docs/connectonion.md +1256 -0
- connectonion/cli/docs.md +123 -0
- connectonion/cli/main.py +148 -0
- connectonion/cli/templates/meta-agent/README.md +287 -0
- connectonion/cli/templates/meta-agent/agent.py +196 -0
- connectonion/cli/templates/meta-agent/prompts/answer_prompt.md +9 -0
- connectonion/cli/templates/meta-agent/prompts/docs_retrieve_prompt.md +15 -0
- connectonion/cli/templates/meta-agent/prompts/metagent.md +71 -0
- connectonion/cli/templates/meta-agent/prompts/think_prompt.md +18 -0
- connectonion/cli/templates/minimal/README.md +56 -0
- connectonion/cli/templates/minimal/agent.py +40 -0
- connectonion/cli/templates/playwright/README.md +118 -0
- connectonion/cli/templates/playwright/agent.py +336 -0
- connectonion/cli/templates/playwright/prompt.md +102 -0
- connectonion/cli/templates/playwright/requirements.txt +3 -0
- connectonion/cli/templates/web-research/agent.py +122 -0
- connectonion/connect.py +128 -0
- connectonion/console.py +539 -0
- connectonion/debug_agent/__init__.py +13 -0
- connectonion/debug_agent/agent.py +45 -0
- connectonion/debug_agent/prompts/debug_assistant.md +72 -0
- connectonion/debug_agent/runtime_inspector.py +406 -0
- connectonion/debug_explainer/__init__.py +10 -0
- connectonion/debug_explainer/explain_agent.py +114 -0
- connectonion/debug_explainer/explain_context.py +263 -0
- connectonion/debug_explainer/explainer_prompt.md +29 -0
- connectonion/debug_explainer/root_cause_analysis_prompt.md +43 -0
- connectonion/debugger_ui.py +1039 -0
- connectonion/decorators.py +208 -0
- connectonion/events.py +248 -0
- connectonion/execution_analyzer/__init__.py +9 -0
- connectonion/execution_analyzer/execution_analysis.py +93 -0
- connectonion/execution_analyzer/execution_analysis_prompt.md +47 -0
- connectonion/host.py +579 -0
- connectonion/interactive_debugger.py +342 -0
- connectonion/llm.py +801 -0
- connectonion/llm_do.py +307 -0
- connectonion/logger.py +300 -0
- connectonion/prompt_files/__init__.py +1 -0
- connectonion/prompt_files/analyze_contact.md +62 -0
- connectonion/prompt_files/eval_expected.md +12 -0
- connectonion/prompt_files/react_evaluate.md +11 -0
- connectonion/prompt_files/react_plan.md +16 -0
- connectonion/prompt_files/reflect.md +22 -0
- connectonion/prompts.py +144 -0
- connectonion/relay.py +200 -0
- connectonion/static/docs.html +688 -0
- connectonion/tool_executor.py +279 -0
- connectonion/tool_factory.py +186 -0
- connectonion/tool_registry.py +105 -0
- connectonion/trust.py +166 -0
- connectonion/trust_agents.py +71 -0
- connectonion/trust_functions.py +88 -0
- connectonion/tui/__init__.py +57 -0
- connectonion/tui/divider.py +39 -0
- connectonion/tui/dropdown.py +251 -0
- connectonion/tui/footer.py +31 -0
- connectonion/tui/fuzzy.py +56 -0
- connectonion/tui/input.py +278 -0
- connectonion/tui/keys.py +35 -0
- connectonion/tui/pick.py +130 -0
- connectonion/tui/providers.py +155 -0
- connectonion/tui/status_bar.py +163 -0
- connectonion/usage.py +161 -0
- connectonion/useful_events_handlers/__init__.py +16 -0
- connectonion/useful_events_handlers/reflect.py +116 -0
- connectonion/useful_plugins/__init__.py +20 -0
- connectonion/useful_plugins/calendar_plugin.py +163 -0
- connectonion/useful_plugins/eval.py +139 -0
- connectonion/useful_plugins/gmail_plugin.py +162 -0
- connectonion/useful_plugins/image_result_formatter.py +127 -0
- connectonion/useful_plugins/re_act.py +78 -0
- connectonion/useful_plugins/shell_approval.py +159 -0
- connectonion/useful_tools/__init__.py +44 -0
- connectonion/useful_tools/diff_writer.py +192 -0
- connectonion/useful_tools/get_emails.py +183 -0
- connectonion/useful_tools/gmail.py +1596 -0
- connectonion/useful_tools/google_calendar.py +613 -0
- connectonion/useful_tools/memory.py +380 -0
- connectonion/useful_tools/microsoft_calendar.py +604 -0
- connectonion/useful_tools/outlook.py +488 -0
- connectonion/useful_tools/send_email.py +205 -0
- connectonion/useful_tools/shell.py +97 -0
- connectonion/useful_tools/slash_command.py +201 -0
- connectonion/useful_tools/terminal.py +285 -0
- connectonion/useful_tools/todo_list.py +241 -0
- connectonion/useful_tools/web_fetch.py +216 -0
- connectonion/xray.py +467 -0
- connectonion-0.5.8.dist-info/METADATA +741 -0
- connectonion-0.5.8.dist-info/RECORD +113 -0
- connectonion-0.5.8.dist-info/WHEEL +4 -0
- connectonion-0.5.8.dist-info/entry_points.txt +3 -0
connectonion/xray.py
ADDED
|
@@ -0,0 +1,467 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Purpose: Provide runtime debugging context and visual trace for AI agent tool execution
|
|
3
|
+
LLM-Note:
|
|
4
|
+
Dependencies: imports from [inspect, builtins, typing] | imported by [tool_executor.py, __init__.py] | tested by [tests/test_xray_class.py, tests/test_xray_without_decorator.py, tests/test_xray_auto_trace.py]
|
|
5
|
+
Data flow: receives from tool_executor → inject_xray_context(agent, user_prompt, messages, iteration, previous_tools) → stores in builtins.xray global → tool accesses xray.agent, xray.task, etc. → tool calls xray.trace() to display formatted execution history → clear_xray_context() after execution
|
|
6
|
+
State/Effects: modifies builtins namespace by injecting global 'xray' object | stores thread-local context in XrayDecorator instance (_agent, _user_prompt, _messages, _iteration, _previous_tools) | clears context after tool execution | no file I/O or persistence
|
|
7
|
+
Integration: exposes @xray decorator, xray global object with .agent, .task, .user_prompt, .messages, .iteration, .previous_tools properties, .trace() method | inject_xray_context(), clear_xray_context(), is_xray_enabled() helper functions | tool_executor checks __xray_enabled__ attribute to auto-print Rich tables
|
|
8
|
+
Performance: lightweight context storage | trace() uses stack inspection to find agent instance | smart value formatting with truncation for strings (400 chars), lists, dicts, DataFrames, Images
|
|
9
|
+
Errors: trace() handles missing agent gracefully with helpful messages | handles missing current_session | handles empty execution history
|
|
10
|
+
|
|
11
|
+
ConnectOnion XRay Debugging Tool
|
|
12
|
+
|
|
13
|
+
This module provides the @xray decorator and xray context for debugging AI agent tools.
|
|
14
|
+
See everything your agent is thinking during tool execution.
|
|
15
|
+
|
|
16
|
+
Usage:
|
|
17
|
+
from connectonion.xray import xray
|
|
18
|
+
|
|
19
|
+
@xray
|
|
20
|
+
def my_tool(query: str):
|
|
21
|
+
print(xray.agent.name) # Access agent context
|
|
22
|
+
print(xray.task) # Access user prompt
|
|
23
|
+
xray.trace() # Display execution trace
|
|
24
|
+
return result
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
import inspect
|
|
28
|
+
import builtins
|
|
29
|
+
from typing import Any, Callable, Optional
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class XrayDecorator:
|
|
33
|
+
"""
|
|
34
|
+
Simple xray decorator that provides context access and auto-tracing.
|
|
35
|
+
|
|
36
|
+
Usage:
|
|
37
|
+
@xray # Auto-print trace after execution
|
|
38
|
+
@xray(trace=False) # No auto-trace
|
|
39
|
+
@xray(rich=False) # Simple text output
|
|
40
|
+
|
|
41
|
+
def my_tool(query: str):
|
|
42
|
+
print(xray.agent.name) # Access agent context
|
|
43
|
+
print(xray.task) # Access user prompt
|
|
44
|
+
xray.trace() # Manual trace display
|
|
45
|
+
return result
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
def __init__(self):
|
|
49
|
+
"""Initialize with empty context."""
|
|
50
|
+
# Store context directly (no wrapper class needed)
|
|
51
|
+
self._agent = None
|
|
52
|
+
self._user_prompt = None
|
|
53
|
+
self._messages = []
|
|
54
|
+
self._iteration = None
|
|
55
|
+
self._previous_tools = []
|
|
56
|
+
|
|
57
|
+
# Make available globally as 'xray' for easy access
|
|
58
|
+
builtins.xray = self
|
|
59
|
+
|
|
60
|
+
def __call__(self, func: Optional[Callable] = None, *, trace: bool = True, rich: bool = True) -> Any:
|
|
61
|
+
"""
|
|
62
|
+
Decorator that marks functions for auto-tracing.
|
|
63
|
+
|
|
64
|
+
@xray # Auto-print trace after execution (Rich format)
|
|
65
|
+
@xray(trace=False) # No auto-trace
|
|
66
|
+
@xray(rich=False) # Simple text output
|
|
67
|
+
|
|
68
|
+
The actual tracing logic is handled by tool_executor.py which checks
|
|
69
|
+
the __xray_enabled__ and __xray_rich__ attributes.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
func: Function to decorate (optional)
|
|
73
|
+
trace: Enable automatic tracing (default: True)
|
|
74
|
+
rich: Use Rich formatting for trace output (default: True)
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
Decorated function (no wrapper) with __xray_enabled__ attribute
|
|
78
|
+
"""
|
|
79
|
+
def decorator(f):
|
|
80
|
+
# Mark the function with xray settings
|
|
81
|
+
f.__xray_enabled__ = trace
|
|
82
|
+
f.__xray_rich__ = rich
|
|
83
|
+
return f
|
|
84
|
+
|
|
85
|
+
# Handle different call patterns
|
|
86
|
+
if func is None:
|
|
87
|
+
# Called with parentheses: @xray() or @xray(trace=False)
|
|
88
|
+
return decorator
|
|
89
|
+
else:
|
|
90
|
+
# Called without parentheses: @xray
|
|
91
|
+
return decorator(func)
|
|
92
|
+
|
|
93
|
+
# -------------------------------------------------------------------------
|
|
94
|
+
# Properties for accessing context data
|
|
95
|
+
# -------------------------------------------------------------------------
|
|
96
|
+
|
|
97
|
+
@property
|
|
98
|
+
def agent(self):
|
|
99
|
+
"""The Agent instance that called this tool."""
|
|
100
|
+
return self._agent
|
|
101
|
+
|
|
102
|
+
@property
|
|
103
|
+
def task(self):
|
|
104
|
+
"""The original user prompt/task (alias for user_prompt)."""
|
|
105
|
+
return self._user_prompt
|
|
106
|
+
|
|
107
|
+
@property
|
|
108
|
+
def user_prompt(self):
|
|
109
|
+
"""The original user prompt string from agent.input()."""
|
|
110
|
+
return self._user_prompt
|
|
111
|
+
|
|
112
|
+
@property
|
|
113
|
+
def messages(self):
|
|
114
|
+
"""Complete conversation history (the prompt)."""
|
|
115
|
+
return self._messages
|
|
116
|
+
|
|
117
|
+
@property
|
|
118
|
+
def iteration(self):
|
|
119
|
+
"""Current iteration number in the agent loop."""
|
|
120
|
+
return self._iteration
|
|
121
|
+
|
|
122
|
+
@property
|
|
123
|
+
def previous_tools(self):
|
|
124
|
+
"""List of tools called in previous iterations."""
|
|
125
|
+
return self._previous_tools
|
|
126
|
+
|
|
127
|
+
def _update(self, agent, user_prompt, messages, iteration, previous_tools):
|
|
128
|
+
"""Internal: Update context (called by tool_executor before tool runs)."""
|
|
129
|
+
self._agent = agent
|
|
130
|
+
self._user_prompt = user_prompt
|
|
131
|
+
self._messages = messages
|
|
132
|
+
self._iteration = iteration
|
|
133
|
+
self._previous_tools = previous_tools
|
|
134
|
+
|
|
135
|
+
def _clear(self):
|
|
136
|
+
"""Internal: Clear context after tool execution."""
|
|
137
|
+
self._agent = None
|
|
138
|
+
self._user_prompt = None
|
|
139
|
+
self._messages = []
|
|
140
|
+
self._iteration = None
|
|
141
|
+
self._previous_tools = []
|
|
142
|
+
|
|
143
|
+
def __repr__(self):
|
|
144
|
+
"""Provide helpful representation for debugging."""
|
|
145
|
+
if not self._agent:
|
|
146
|
+
return "<xray (no active context)>"
|
|
147
|
+
|
|
148
|
+
agent_name = self._agent.name if self._agent else 'None'
|
|
149
|
+
prompt_preview = (self._user_prompt[:50] + '...') if self._user_prompt and len(self._user_prompt) > 50 else self._user_prompt
|
|
150
|
+
|
|
151
|
+
lines = [
|
|
152
|
+
f"<xray active>",
|
|
153
|
+
f" agent: '{agent_name}'",
|
|
154
|
+
f" task: '{prompt_preview}'",
|
|
155
|
+
f" iteration: {self._iteration}",
|
|
156
|
+
f" messages: {len(self._messages)} items",
|
|
157
|
+
]
|
|
158
|
+
|
|
159
|
+
if self._previous_tools:
|
|
160
|
+
lines.append(f" previous_tools: {self._previous_tools}")
|
|
161
|
+
|
|
162
|
+
return '\n'.join(lines)
|
|
163
|
+
|
|
164
|
+
def trace(self):
|
|
165
|
+
"""
|
|
166
|
+
Display a visual trace of tool execution flow.
|
|
167
|
+
|
|
168
|
+
Uses stack inspection to find the agent instance, so it works
|
|
169
|
+
from anywhere in the call stack (inside tools, breakpoints, etc.)
|
|
170
|
+
|
|
171
|
+
Usage:
|
|
172
|
+
# Within a tool or anywhere in the call stack:
|
|
173
|
+
xray.trace() # Shows current execution flow
|
|
174
|
+
|
|
175
|
+
# In debugging session with breakpoint:
|
|
176
|
+
>>> xray.trace()
|
|
177
|
+
Task: "Analyze customer feedback and generate report"
|
|
178
|
+
|
|
179
|
+
[1] • 89ms analyze_document(text="Dear customer, Thank you for...")
|
|
180
|
+
IN → text: <string: 15,234 chars> "Dear customer, Thank you for..."
|
|
181
|
+
OUT ← {sentiment: "positive", topics: ["refund", "satisfaction"]}
|
|
182
|
+
|
|
183
|
+
[2] • 340ms process_image(image=<...>, enhance=true)
|
|
184
|
+
IN → image: <Image: JPEG 1920x1080, 2.3MB>
|
|
185
|
+
IN → enhance: true
|
|
186
|
+
OUT ← <Image: JPEG 1920x1080, 1.8MB, enhanced>
|
|
187
|
+
|
|
188
|
+
Total: 429ms • 2 steps • 1 iterations
|
|
189
|
+
|
|
190
|
+
Visual Format:
|
|
191
|
+
- Step numbers in brackets: [1], [2], etc.
|
|
192
|
+
- Timing shown after bullet (•) or ERROR/pending indicator
|
|
193
|
+
- Function signature shows first 2 params inline, rest as "..."
|
|
194
|
+
- IN → shows input parameters (one per line)
|
|
195
|
+
- OUT ← shows return values
|
|
196
|
+
- ERR ✗ shows errors
|
|
197
|
+
- Smart truncation for long strings, images, DataFrames
|
|
198
|
+
"""
|
|
199
|
+
# Use stack inspection to find agent instance
|
|
200
|
+
target_agent = None
|
|
201
|
+
for frame_info in inspect.stack():
|
|
202
|
+
frame_locals = frame_info.frame.f_locals
|
|
203
|
+
|
|
204
|
+
# Look for 'agent' in local variables
|
|
205
|
+
if 'agent' in frame_locals:
|
|
206
|
+
potential_agent = frame_locals['agent']
|
|
207
|
+
# Check if it has current_session (duck typing for Agent)
|
|
208
|
+
if hasattr(potential_agent, 'current_session'):
|
|
209
|
+
target_agent = potential_agent
|
|
210
|
+
break
|
|
211
|
+
|
|
212
|
+
# Also check 'self' in case we're in an agent method
|
|
213
|
+
if 'self' in frame_locals:
|
|
214
|
+
potential_agent = frame_locals['self']
|
|
215
|
+
if hasattr(potential_agent, 'current_session'):
|
|
216
|
+
target_agent = potential_agent
|
|
217
|
+
break
|
|
218
|
+
|
|
219
|
+
if not target_agent:
|
|
220
|
+
print("xray.trace() could not find agent in call stack.")
|
|
221
|
+
print("Make sure you're calling this from within a tool or agent method.")
|
|
222
|
+
return
|
|
223
|
+
|
|
224
|
+
if not target_agent.current_session:
|
|
225
|
+
print("No active session found on agent.")
|
|
226
|
+
print("Make sure the agent has been run with .input() first.")
|
|
227
|
+
return
|
|
228
|
+
|
|
229
|
+
# Get trace data from agent session
|
|
230
|
+
execution_history = [
|
|
231
|
+
entry for entry in target_agent.current_session.get('trace', [])
|
|
232
|
+
if entry.get('type') == 'tool_execution'
|
|
233
|
+
]
|
|
234
|
+
user_prompt = target_agent.current_session.get('user_prompt', '')
|
|
235
|
+
|
|
236
|
+
if not execution_history:
|
|
237
|
+
print("No tool execution history available.")
|
|
238
|
+
print("Make sure the agent has executed some tools first.")
|
|
239
|
+
return
|
|
240
|
+
|
|
241
|
+
# Display the prompt that was executed
|
|
242
|
+
if user_prompt:
|
|
243
|
+
print(f'User Prompt: "{user_prompt}"')
|
|
244
|
+
print()
|
|
245
|
+
|
|
246
|
+
# Display each tool execution with visual formatting
|
|
247
|
+
for i, entry in enumerate(execution_history, 1):
|
|
248
|
+
# Format timing with appropriate precision (timing is in milliseconds)
|
|
249
|
+
timing = entry.get('timing', 0)
|
|
250
|
+
if timing >= 1000:
|
|
251
|
+
timing_str = f"{timing/1000:.1f}s" # Show seconds for long operations
|
|
252
|
+
elif timing >= 1:
|
|
253
|
+
timing_str = f"{timing:.0f}ms" # Whole milliseconds
|
|
254
|
+
else:
|
|
255
|
+
timing_str = f"{timing:.2f}ms" # Sub-millisecond precision
|
|
256
|
+
|
|
257
|
+
# Format function call
|
|
258
|
+
func_name = entry.get('tool_name', 'unknown')
|
|
259
|
+
# Check both 'arguments' (new format) and 'parameters' (old format)
|
|
260
|
+
params = entry.get('arguments', entry.get('parameters', {}))
|
|
261
|
+
|
|
262
|
+
# Build parameter preview for function signature
|
|
263
|
+
# Shows first 2 params inline to keep the main line readable
|
|
264
|
+
param_preview = []
|
|
265
|
+
for k, v in list(params.items())[:2]: # Show first 2 params in signature
|
|
266
|
+
param_preview.append(f"{k}={self._format_value_preview(v)}")
|
|
267
|
+
if len(params) > 2:
|
|
268
|
+
param_preview.append("...") # Indicate more params exist
|
|
269
|
+
|
|
270
|
+
func_call = f"{func_name}({', '.join(param_preview)})"
|
|
271
|
+
|
|
272
|
+
# Status indicators for visual clarity
|
|
273
|
+
status = entry.get('status', 'success')
|
|
274
|
+
if status == 'error':
|
|
275
|
+
prefix = "ERROR" # Clearly mark errors
|
|
276
|
+
elif status == 'pending':
|
|
277
|
+
timing_str = "..." # Show operation in progress
|
|
278
|
+
prefix = "..."
|
|
279
|
+
else:
|
|
280
|
+
prefix = "•" # Success indicator
|
|
281
|
+
|
|
282
|
+
# Print main execution line with aligned columns
|
|
283
|
+
print(f"[{i}] {prefix} {timing_str:<6} {func_call}")
|
|
284
|
+
|
|
285
|
+
# Print input parameters (one per line for readability)
|
|
286
|
+
for param_name, param_value in params.items():
|
|
287
|
+
formatted_value = self._format_value_full(param_value)
|
|
288
|
+
print(f" IN → {param_name}: {formatted_value}")
|
|
289
|
+
|
|
290
|
+
# Print result or error based on status
|
|
291
|
+
if status == 'error':
|
|
292
|
+
error = entry.get('error', 'Unknown error')
|
|
293
|
+
print(f" ERR ✗ {error}")
|
|
294
|
+
elif status == 'pending':
|
|
295
|
+
print(f" ⋯ pending")
|
|
296
|
+
else:
|
|
297
|
+
result = entry.get('result')
|
|
298
|
+
formatted_result = self._format_value_full(result)
|
|
299
|
+
print(f" OUT ← {formatted_result}")
|
|
300
|
+
|
|
301
|
+
# Add spacing between entries for readability
|
|
302
|
+
if i < len(execution_history):
|
|
303
|
+
print()
|
|
304
|
+
|
|
305
|
+
# Summary line with total execution statistics
|
|
306
|
+
total_time = sum(e.get('timing', 0) for e in execution_history if e.get('timing'))
|
|
307
|
+
iterations = target_agent.current_session.get('iteration', 1)
|
|
308
|
+
|
|
309
|
+
# Format total time with same rules as individual timings
|
|
310
|
+
if total_time >= 1000:
|
|
311
|
+
total_str = f"{total_time/1000:.1f}s"
|
|
312
|
+
elif total_time >= 1:
|
|
313
|
+
total_str = f"{total_time:.0f}ms"
|
|
314
|
+
else:
|
|
315
|
+
total_str = f"{total_time:.2f}ms"
|
|
316
|
+
|
|
317
|
+
print(f"\nTotal: {total_str} • {len(execution_history)} steps • {iterations} iterations")
|
|
318
|
+
|
|
319
|
+
def _format_value_preview(self, value):
|
|
320
|
+
"""
|
|
321
|
+
Format a value for compact display in function signature.
|
|
322
|
+
|
|
323
|
+
Used in the main execution line to show parameter values inline
|
|
324
|
+
without taking too much horizontal space.
|
|
325
|
+
|
|
326
|
+
Args:
|
|
327
|
+
value: Any parameter value to format
|
|
328
|
+
|
|
329
|
+
Returns:
|
|
330
|
+
Compact string representation (max ~50 chars)
|
|
331
|
+
"""
|
|
332
|
+
if value is None:
|
|
333
|
+
return "None"
|
|
334
|
+
elif isinstance(value, str):
|
|
335
|
+
if len(value) > 50:
|
|
336
|
+
return f'"{value[:50]}..."'
|
|
337
|
+
return repr(value)
|
|
338
|
+
elif isinstance(value, (int, float, bool)):
|
|
339
|
+
return str(value)
|
|
340
|
+
elif isinstance(value, dict):
|
|
341
|
+
return "{...}" # Just indicate it's a dict
|
|
342
|
+
elif isinstance(value, list):
|
|
343
|
+
return "[...]" # Just indicate it's a list
|
|
344
|
+
else:
|
|
345
|
+
return "..." # Unknown type
|
|
346
|
+
|
|
347
|
+
def _format_value_full(self, value):
|
|
348
|
+
"""
|
|
349
|
+
Format a value for full display with smart truncation.
|
|
350
|
+
|
|
351
|
+
Used in the detailed parameter/result lines. Provides more detail
|
|
352
|
+
than preview format while still keeping output manageable.
|
|
353
|
+
|
|
354
|
+
Truncation strategies:
|
|
355
|
+
- Strings: Show first 400 chars (~4 sentences) with total length
|
|
356
|
+
- Lists: Show item count if > 3 items
|
|
357
|
+
- Dicts: Show first 3 keys if large
|
|
358
|
+
- DataFrames: Show dimensions (rows × columns)
|
|
359
|
+
- Images: Show format, dimensions, and estimated size
|
|
360
|
+
|
|
361
|
+
Args:
|
|
362
|
+
value: Any value to format for display
|
|
363
|
+
|
|
364
|
+
Returns:
|
|
365
|
+
Formatted string with smart truncation applied
|
|
366
|
+
"""
|
|
367
|
+
if value is None:
|
|
368
|
+
return "None"
|
|
369
|
+
elif isinstance(value, str):
|
|
370
|
+
# Show up to ~4 sentences worth of text (roughly 400 chars)
|
|
371
|
+
if len(value) > 400:
|
|
372
|
+
preview = value[:400].replace('\n', ' ')
|
|
373
|
+
return f'<string: {len(value):,} chars> "{preview}..."'
|
|
374
|
+
return repr(value)
|
|
375
|
+
elif isinstance(value, (int, float)):
|
|
376
|
+
return str(value)
|
|
377
|
+
elif isinstance(value, bool):
|
|
378
|
+
return str(value)
|
|
379
|
+
elif isinstance(value, dict):
|
|
380
|
+
# Show compact dict representation
|
|
381
|
+
if len(str(value)) <= 80:
|
|
382
|
+
return str(value)
|
|
383
|
+
# Show keys for large dicts
|
|
384
|
+
keys = list(value.keys())[:3]
|
|
385
|
+
key_str = ", ".join(f"{k}: ..." for k in keys)
|
|
386
|
+
if len(value) > 3:
|
|
387
|
+
key_str += f", ... ({len(value)-3} more)"
|
|
388
|
+
return f"{{{key_str}}}"
|
|
389
|
+
elif isinstance(value, list):
|
|
390
|
+
if len(value) == 0:
|
|
391
|
+
return "[]"
|
|
392
|
+
elif len(value) <= 3 and len(str(value)) <= 80:
|
|
393
|
+
return str(value)
|
|
394
|
+
else:
|
|
395
|
+
return f"[{len(value)} items]"
|
|
396
|
+
elif hasattr(value, '__class__'):
|
|
397
|
+
# Handle custom objects
|
|
398
|
+
class_name = value.__class__.__name__
|
|
399
|
+
|
|
400
|
+
# Special handling for common ML/data objects
|
|
401
|
+
if 'DataFrame' in class_name:
|
|
402
|
+
# Try to get shape info
|
|
403
|
+
if hasattr(value, 'shape'):
|
|
404
|
+
rows, cols = value.shape
|
|
405
|
+
return f"<DataFrame: {rows:,} rows × {cols} columns>"
|
|
406
|
+
return f"<{class_name}>"
|
|
407
|
+
elif 'Image' in class_name or 'PIL' in str(type(value)):
|
|
408
|
+
# Handle image objects
|
|
409
|
+
if hasattr(value, 'size'):
|
|
410
|
+
w, h = value.size
|
|
411
|
+
format_str = getattr(value, 'format', 'Unknown')
|
|
412
|
+
# Estimate size (rough)
|
|
413
|
+
size_mb = (w * h * 3) / (1024 * 1024)
|
|
414
|
+
return f"<Image: {format_str} {w}x{h}, {size_mb:.1f}MB>"
|
|
415
|
+
return f"<{class_name}>"
|
|
416
|
+
else:
|
|
417
|
+
return f"<{class_name} object>"
|
|
418
|
+
else:
|
|
419
|
+
return str(type(value).__name__)
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
# Create the global xray instance
|
|
423
|
+
xray = XrayDecorator()
|
|
424
|
+
|
|
425
|
+
|
|
426
|
+
# =============================================================================
|
|
427
|
+
# Helper Functions for Tool Executor Integration
|
|
428
|
+
# =============================================================================
|
|
429
|
+
|
|
430
|
+
def inject_xray_context(agent, user_prompt: str, messages: list,
|
|
431
|
+
iteration: int, previous_tools: list) -> None:
|
|
432
|
+
"""
|
|
433
|
+
Inject debugging context before tool execution.
|
|
434
|
+
|
|
435
|
+
This is called internally by tool_executor before running a tool,
|
|
436
|
+
making xray.agent, xray.task, etc. available inside the tool.
|
|
437
|
+
|
|
438
|
+
Args:
|
|
439
|
+
agent: The Agent instance
|
|
440
|
+
user_prompt: Original user prompt string from agent.input()
|
|
441
|
+
messages: Conversation history
|
|
442
|
+
iteration: Current iteration number
|
|
443
|
+
previous_tools: List of previously called tool names
|
|
444
|
+
"""
|
|
445
|
+
xray._update(agent, user_prompt, messages, iteration, previous_tools)
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
def clear_xray_context() -> None:
|
|
449
|
+
"""
|
|
450
|
+
Clear debugging context after tool execution.
|
|
451
|
+
|
|
452
|
+
This is called internally by tool_executor to prevent context leakage.
|
|
453
|
+
"""
|
|
454
|
+
xray._clear()
|
|
455
|
+
|
|
456
|
+
|
|
457
|
+
def is_xray_enabled(func: Callable) -> bool:
|
|
458
|
+
"""
|
|
459
|
+
Check if a function has the @xray decorator.
|
|
460
|
+
|
|
461
|
+
Args:
|
|
462
|
+
func: Function to check
|
|
463
|
+
|
|
464
|
+
Returns:
|
|
465
|
+
True if function is decorated with @xray
|
|
466
|
+
"""
|
|
467
|
+
return getattr(func, '__xray_enabled__', False)
|