agent-dev-cli 0.0.1b260119__py3-none-any.whl → 0.0.1b260205__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agent-dev-cli
3
- Version: 0.0.1b260119
3
+ Version: 0.0.1b260205
4
4
  Summary: AI Toolkit - CLI and SDK for agent debugging and workflow visualization
5
5
  Keywords: ai,agent,toolkit,debugging,workflow,visualization
6
6
  Author-email: Microsoft <aitkfeedback@microsoft.com>
@@ -26,9 +26,10 @@ Requires-Dist: python-dotenv>=1.2.1
26
26
  Requires-Dist: azure-ai-agents>=1.2.0b5
27
27
  Requires-Dist: starlette>=0.49.3
28
28
  Requires-Dist: openai>=1.109.1
29
- Requires-Dist: agent-framework-azure-ai>=1.0.0b251211
30
- Requires-Dist: azure-ai-agentserver-agentframework>=1.0.0b7
31
- Requires-Dist: azure-ai-agentserver-core>=1.0.0b7
29
+ Requires-Dist: agent-framework-azure-ai>=1.0.0b251211,<=1.0.0b260107
30
+ Requires-Dist: agent-framework-core>=1.0.0b251211,<=1.0.0b260107
31
+ Requires-Dist: azure-ai-agentserver-agentframework==1.0.0b10
32
+ Requires-Dist: azure-ai-agentserver-core==1.0.0b10
32
33
  Requires-Dist: click>=8.1.0
33
34
  Requires-Dist: azure-ai-projects>=2.0.0b2
34
35
  Project-URL: Documentation, https://github.com/microsoft/vscode-ai-toolkit#readme
@@ -78,7 +79,7 @@ The CLI automatically:
78
79
  If you prefer explicit control, you can integrate agentdev directly:
79
80
 
80
81
  ```python
81
- from agentdev import setup_test_tool
82
+ from agentdev import configure_agent_server
82
83
  from azure.ai.agentserver.agentframework import from_agent_framework
83
84
 
84
85
  # Create your agent
@@ -88,7 +89,7 @@ agent = build_agent(chat_client)
88
89
  agent_server = from_agent_framework(agent)
89
90
 
90
91
  # Setup workflow visualization
91
- setup_test_tool(agent_server)
92
+ configure_agent_server(agent_server)
92
93
 
93
94
  # Run the server
94
95
  await agent_server.run_async()
@@ -1,22 +1,22 @@
1
- agentdev/__init__.py,sha256=Gf0UUj3_2MdCadGtKtnyJeYY02iUH5ZMevelN8Um09M,557
1
+ agentdev/__init__.py,sha256=bl3B1MKFUJIhoLxHnfEktKYNvWweqJ-ppd9Kf0YOLlw,585
2
2
  agentdev/__main__.py,sha256=lHwzD56r-Jd0A_VOGRmv2PcP2AZ56dWz61o3zCBGxzY,135
3
- agentdev/_hooks.py,sha256=0bdq-K170vTmhE9a5JHL-DCXJOCpuG2LIvannQqH3jU,9819
4
- agentdev/_version.py,sha256=i-XLHa-ZbhQ-sdJ0UHTD6zCr_0GBfhh2au_NzzDWGwU,79
3
+ agentdev/_hooks.py,sha256=-K2vFMhhZh1D46dEUA1uGgGdU92k6qbXnU98TpgmjTw,9847
4
+ agentdev/_version.py,sha256=QTmmRJaAMr6GP280-vG-90EtyBrbr7gex8cu8UwRCiA,79
5
5
  agentdev/bootstrap.py,sha256=sL9CobtPdK4n2piHhFr1-K12AlWgTfgfbdzyfTYWuxc,2192
6
6
  agentdev/cli.py,sha256=SWM_SHfZdmLr6ZH8yuGtiMLcd9lhta5YjQxW9xICXac,4948
7
- agentdev/localdebug.py,sha256=qn75D3hISn2k8yFLRs0qXFrr_asCNRqyOcNy-97AWaY,2028
7
+ agentdev/localdebug.py,sha256=KL3CHFaPskgYU7knTcvY74BthVT7aCOxUZy6ccm12ug,3401
8
8
  agentdev/backend/__init__.py,sha256=BFiYU9_0aAfygA0oTs0o70etmH65_gX9cN53ZcMwFIY,197
9
9
  agentdev/backend/_conversations.py,sha256=ui62ml5pise0fF9ri7HITM_l4irMdmm2jJ7d_U1FL74,18518
10
- agentdev/backend/_utils.py,sha256=WkJT-5o1QvLQHOaQqm_RR7ivLMS8DO5w9chHGA2WSus,3348
10
+ agentdev/backend/_utils.py,sha256=MmysFnqhNmfCyb1cTfLtpGR66yJ2nElpcDQq8j_4hT0,3412
11
11
  agentdev/backend/code_analyzer.py,sha256=-B_1Qrf3cbWhvcxjxgk7xbHyfjKuL11GvtzY9wA1hkM,1565
12
12
  agentdev/backend/errors.py,sha256=95geZNBeAfxAWyJDCaGFXSFsCifUZ2V0PHmsPOYsmt4,53
13
- agentdev/backend/event_mapper.py,sha256=1TLY9JIsFdmFWcjaPHvRq7v_l7wUG9MY1wproD4dlRo,12485
14
- agentdev/backend/server.py,sha256=BRtZqJI2GxWraRwYkrpJNcDnhxZJ22aL0ScIZNhfNSk,12388
13
+ agentdev/backend/event_mapper.py,sha256=SG-FwGwRJAdwXrBzFtkucqa_P8Wj4QjNcgzdXk7XgQs,17442
14
+ agentdev/backend/server.py,sha256=WEXu37PkXyTpBHlddh_LhAksTNMWS5LEwkT7nMLEGaA,12685
15
15
  agentdev/backend/structs/__init__.py,sha256=UKpDMo_sjEtvmHI8Rys6_s4dUYaE0lz3-jw0T_BWoQ8,212
16
16
  agentdev/backend/structs/entity_response.py,sha256=dGG-9J3gpGxqu4LV_6UqeoyzjdBqFn8tiesu2EPXzmc,1079
17
17
  agentdev/backend/structs/request.py,sha256=lG0Tcp3bvR505DobO9u8XPfeVg-0DGKvt7mhPbGFJRg,2044
18
- agent_dev_cli-0.0.1b260119.dist-info/entry_points.txt,sha256=Zeb1F0rPDO1dIQorHn7Q-hpOg09ZDE7elz4fowvO6BQ,46
19
- agent_dev_cli-0.0.1b260119.dist-info/licenses/LICENSE,sha256=spth2ab8cfDqNmKBLSbKHNqnlnDIj-bYaw8insufb2w,3000
20
- agent_dev_cli-0.0.1b260119.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
21
- agent_dev_cli-0.0.1b260119.dist-info/METADATA,sha256=N-KawRcwWez4u9_YeOTgJ_T5OQloenRaXqaIm9ldIrM,4408
22
- agent_dev_cli-0.0.1b260119.dist-info/RECORD,,
18
+ agent_dev_cli-0.0.1b260205.dist-info/entry_points.txt,sha256=Zeb1F0rPDO1dIQorHn7Q-hpOg09ZDE7elz4fowvO6BQ,46
19
+ agent_dev_cli-0.0.1b260205.dist-info/licenses/LICENSE,sha256=spth2ab8cfDqNmKBLSbKHNqnlnDIj-bYaw8insufb2w,3000
20
+ agent_dev_cli-0.0.1b260205.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
21
+ agent_dev_cli-0.0.1b260205.dist-info/METADATA,sha256=ykBKkXqRr5E1-vUGohdI2GHh15o0w4rEpDZfAXAEOnU,4504
22
+ agent_dev_cli-0.0.1b260205.dist-info/RECORD,,
agentdev/__init__.py CHANGED
@@ -12,11 +12,11 @@ This package provides two ways to use agentdev:
12
12
 
13
13
  2. Programmatic API (Requires code modification):
14
14
 
15
- from agentdev import setup_test_tool
16
- setup_test_tool(agent_server)
15
+ from agentdev import configure_agent_server
16
+ configure_agent_server(agent_server)
17
17
  """
18
18
 
19
- from .localdebug import setup_test_tool
19
+ from .localdebug import configure_agent_server
20
20
  from ._version import __version__
21
21
 
22
- __all__ = ["setup_test_tool", "__version__"]
22
+ __all__ = ["configure_agent_server", "__version__"]
agentdev/_hooks.py CHANGED
@@ -7,7 +7,7 @@ capabilities without requiring user code modifications.
7
7
 
8
8
  The hook intercepts:
9
9
  - `azure.ai.agentserver.agentframework.from_agent_framework()`
10
- to automatically call `setup_test_tool()` on the created server.
10
+ to automatically call `configure_agent_server()` on the created server.
11
11
  """
12
12
 
13
13
  import sys
@@ -31,7 +31,7 @@ def _create_patched_from_agent_framework(original_func: Callable) -> Callable:
31
31
  original_func: The original from_agent_framework function
32
32
 
33
33
  Returns:
34
- Patched function that calls setup_test_tool automatically
34
+ Patched function that calls configure_agent_server automatically
35
35
  """
36
36
  @functools.wraps(original_func)
37
37
  def patched_from_agent_framework(agent: Any, **kwargs) -> Any:
@@ -48,8 +48,8 @@ def _create_patched_from_agent_framework(original_func: Callable) -> Callable:
48
48
 
49
49
  # Auto-inject agentdev visualization
50
50
  try:
51
- from agentdev.localdebug import setup_test_tool
52
- setup_test_tool(server)
51
+ from agentdev.localdebug import configure_agent_server
52
+ configure_agent_server(server)
53
53
 
54
54
  if verbose:
55
55
  print(f"agentdev Hook: Successfully injected agentdev instrumentation", file=sys.stderr)
agentdev/_version.py CHANGED
@@ -1,2 +1,2 @@
1
1
  """Version information for agentdev package."""
2
- __version__ = "0.0.1b260119"
2
+ __version__ = "0.0.1b260205"
@@ -36,11 +36,6 @@ def serialize_data(data: Any, max_depth: int = 10) -> Any:
36
36
  if isinstance(data, dict):
37
37
  return {k: serialize_data(v, max_depth - 1) for k, v in data.items()}
38
38
 
39
- # Handle ChatMessage - show as {"role":"xxx", "text":"xxx"}
40
- if isinstance(data, ChatMessage):
41
- role_str = data.role.value if hasattr(data.role, 'value') else str(data.role)
42
- return {"role": role_str, "text": data.text}
43
-
44
39
  # Handle AgentExecutorResponse
45
40
  if isinstance(data, AgentExecutorResponse):
46
41
  result = {}
@@ -57,6 +52,11 @@ def serialize_data(data: Any, max_depth: int = 10) -> Any:
57
52
  return serialize_data(dict_data, max_depth - 1)
58
53
  except Exception:
59
54
  pass
55
+
56
+ # Handle ChatMessage - Fallback if to_dict failed or is not available
57
+ if isinstance(data, ChatMessage):
58
+ role_str = data.role.value if hasattr(data.role, 'value') else str(data.role)
59
+ return {"role": role_str, "text": data.text, "contents": serialize_data(data.contents, max_depth - 1)}
60
60
 
61
61
  # Handle Pydantic models with model_dump()
62
62
  if hasattr(data, "model_dump") and callable(data.model_dump):
@@ -1,3 +1,4 @@
1
+ import json
1
2
  import time
2
3
  from typing import Callable, Type, Any
3
4
  from functools import wraps
@@ -132,16 +133,82 @@ class EventMapper():
132
133
 
133
134
  @event_mapper(AgentRunUpdateEvent)
134
135
  def _map_agent_run_update_event(self, ctx: MapperContext, event: AgentRunUpdateEvent) -> list[dict]:
135
- return [
136
- {
137
- "type":"response.output_text.delta",
138
- "content_index":0,
139
- "delta": event.data.text,
140
- "item_id": ctx.item_id + "_message",
141
- "logprobs":[],
142
- "output_index":ctx.output_index,
143
- }
144
- ]
136
+ events = []
137
+ update = event.data
138
+ logging.debug(f"[EventMapper] AgentRunUpdateEvent received, executor_id={event.executor_id}, contents={len(update.contents) if update.contents else 0}")
139
+ if not ctx.item_id:
140
+ # Fallback if item_id not set
141
+ ctx.item_id = f"exec_{event.executor_id}_{str(uuid.uuid4())[:8]}"
142
+
143
+ message_item_id = ctx.item_id + "_message"
144
+
145
+ for content in update.contents:
146
+ logging.debug(f"[EventMapper] Processing content type: {type(content).__name__}")
147
+ if isinstance(content, TextContent):
148
+ events.append({
149
+ "type":"response.output_text.delta",
150
+ "content_index":0,
151
+ "delta": content.text,
152
+ "item_id": message_item_id,
153
+ "logprobs":[],
154
+ "output_index":ctx.output_index,
155
+ })
156
+ elif isinstance(content, FunctionCallContent):
157
+ if content.call_id != ctx.call_id:
158
+ ctx.call_id = content.call_id
159
+ ctx.output_index += 1
160
+ events.append({
161
+ "type":"response.output_item.added",
162
+ "output_index": ctx.output_index,
163
+ "item":{
164
+ "type":"function_call",
165
+ "arguments":"",
166
+ "call_id": ctx.call_id,
167
+ "name": content.name,
168
+ "id": content.call_id,
169
+ "status":"in_progress",
170
+ },
171
+ })
172
+
173
+ # Handle arguments delta
174
+ delta_args = ""
175
+ if isinstance(content.arguments, str):
176
+ delta_args = content.arguments
177
+ elif content.arguments:
178
+ # Serialize dictionary arguments
179
+ try:
180
+ delta_args = json.dumps(serialize_data(content.arguments))
181
+ except Exception as e:
182
+ logging.warning(f"Failed to serialize arguments: {e}")
183
+ delta_args = str(content.arguments)
184
+
185
+ if delta_args:
186
+ events.append({
187
+ "type": "response.function_call_arguments.delta",
188
+ "output_index": ctx.output_index,
189
+ "item_id": ctx.call_id,
190
+ "delta": delta_args,
191
+ })
192
+
193
+ elif isinstance(content, FunctionResultContent):
194
+ serialized_result = None
195
+ try:
196
+ serialized_result = serialize_data(content.result)
197
+ except Exception as e:
198
+ logging.warning(f"Failed to serialize function result: {e}")
199
+ serialized_result = str(content.result)
200
+
201
+ events.append({
202
+ "type":"response.function_result.complete",
203
+ "call_id": content.call_id,
204
+ "output_index": ctx.output_index,
205
+ "output": serialized_result,
206
+ "status":"completed",
207
+ "item_id": ctx.item_id,
208
+ "timestamp":datetime.now().isoformat(),
209
+ })
210
+
211
+ return events
145
212
 
146
213
  @event_mapper(ExecutorEvent)
147
214
  def _map_executor_event(self, ctx: MapperContext, event: ExecutorEvent) -> list[dict]:
@@ -225,6 +292,7 @@ class EventMapper():
225
292
  @event_mapper(AgentRunResponseUpdate)
226
293
  def _map_agent_run_response_update(self, ctx: MapperContext, event: AgentRunResponseUpdate) -> list[dict]:
227
294
  item_id = event.message_id
295
+ logging.debug(f"[EventMapper] AgentRunResponseUpdate received, message_id={item_id}, contents={len(event.contents) if event.contents else 0}")
228
296
  results = []
229
297
  if item_id and item_id != ctx.item_id:
230
298
  ctx.item_id = item_id
@@ -253,16 +321,26 @@ class EventMapper():
253
321
 
254
322
  for content in event.contents:
255
323
  if isinstance(content, FunctionCallContent):
256
- # Arguments are always streamed in OpenAI. But not always in Agent Framework.
257
- # Argument streaming use last call_id to track the function call instance.
258
- if ctx.call_id and content.arguments:
259
- results.append({
260
- "type": "response.function_call_arguments.delta",
261
- "output_index": ctx.output_index,
262
- # DevUI set OpenAI's item_id to the value of Agent Framework's call_id, which might be a bug
263
- "item_id": ctx.call_id,
264
- "delta": content.arguments,
265
- })
324
+ is_same_call = ctx.call_id and content.call_id == ctx.call_id
325
+
326
+ if is_same_call and content.arguments:
327
+ delta_args = ""
328
+ if isinstance(content.arguments, str):
329
+ delta_args = content.arguments
330
+ elif content.arguments:
331
+ try:
332
+ delta_args = json.dumps(serialize_data(content.arguments))
333
+ except Exception as e:
334
+ logging.warning(f"Failed to serialize arguments: {e}")
335
+ delta_args = str(content.arguments)
336
+
337
+ if delta_args:
338
+ results.append({
339
+ "type": "response.function_call_arguments.delta",
340
+ "output_index": ctx.output_index,
341
+ "item_id": ctx.call_id,
342
+ "delta": delta_args,
343
+ })
266
344
  else:
267
345
  if content.call_id != ctx.call_id:
268
346
  ctx.call_id = content.call_id
@@ -279,6 +357,24 @@ class EventMapper():
279
357
  "status":"in_progress",
280
358
  },
281
359
  })
360
+ if content.arguments:
361
+ initial_args = ""
362
+ if isinstance(content.arguments, str):
363
+ initial_args = content.arguments
364
+ elif content.arguments:
365
+ try:
366
+ initial_args = json.dumps(serialize_data(content.arguments))
367
+ except Exception as e:
368
+ logging.warning(f"Failed to serialize initial arguments: {e}")
369
+ initial_args = str(content.arguments)
370
+
371
+ if initial_args:
372
+ results.append({
373
+ "type": "response.function_call_arguments.delta",
374
+ "output_index": ctx.output_index,
375
+ "item_id": ctx.call_id,
376
+ "delta": initial_args,
377
+ })
282
378
  elif isinstance(content, FunctionResultContent):
283
379
  results.append({
284
380
  "type":"response.function_result.complete",
@@ -306,10 +402,13 @@ class EventMapper():
306
402
 
307
403
  def map_event(self, ctx: MapperContext, event: Any) -> list[dict]:
308
404
  """Map an Agent Framework event to OpenAI Responses API events"""
405
+ logging.debug(f"[EventMapper] map_event called with event type: {type(event).__name__}")
309
406
  mapper = self._get_event_mapper(type(event))
310
407
  if not mapper:
311
408
  logging.warning("Unknown event: %s", type(event))
312
409
  return []
313
410
 
314
- return mapper(ctx, event)
411
+ result = mapper(ctx, event)
412
+ logging.debug(f"[EventMapper] Mapped to {len(result)} output events: {[e.get('type') for e in result]}")
413
+ return result
315
414
 
@@ -18,6 +18,8 @@ from starlette.websockets import WebSocket, WebSocketDisconnect
18
18
 
19
19
  from agent_framework import Workflow, ChatMessage, AgentProtocol, Executor
20
20
 
21
+ from .._version import __version__
22
+
21
23
  from .event_mapper import EventMapper, MapperContext
22
24
  from .structs.request import AgentFrameworkRequest
23
25
  from .structs.entity_response import EntityResponse
@@ -81,6 +83,7 @@ class TestToolServer:
81
83
  app = Starlette(routes=[
82
84
  # Responses API for workflow and agent
83
85
  Route("/v1/responses", endpoint=self.responses, methods=["POST"]),
86
+ Route("/version", endpoint=self.get_version, methods=["GET"]),
84
87
  Route("/entities", endpoint=self.list_entities, methods=["GET"]),
85
88
  Route("/entities/{entity_id}/info", endpoint=self.get_entity_info, methods=["GET"]),
86
89
  Route("/entities/{entity_id}/executor/{executor_id}/location", endpoint=self.get_executor_location, methods=["GET"]),
@@ -90,6 +93,10 @@ class TestToolServer:
90
93
  ])
91
94
  root_app.mount("/agentdev/", app)
92
95
 
96
+ async def get_version(self, raw_request: Request):
97
+ """Return the CLI version for compatibility checking."""
98
+ return JSONResponse({"version": __version__})
99
+
93
100
  async def list_entities(self, raw_request: Request):
94
101
  entities_info = []
95
102
  for entity in self._entities:
agentdev/localdebug.py CHANGED
@@ -7,9 +7,41 @@ This module provides functionality to set up workflow or agent visualization
7
7
  from agent_framework import WorkflowAgent
8
8
  import logging
9
9
  import time
10
+ from typing import Any, Optional
10
11
 
11
12
 
12
- def setup_test_tool(agent_server):
13
+ def _get_agent_from_server(agent_server: Any) -> Optional[Any]:
14
+ """
15
+ Extract the agent from an agent server instance.
16
+
17
+ Args:
18
+ agent_server: The agent server instance
19
+
20
+ Returns:
21
+ The agent instance, or None if not found
22
+ """
23
+ if hasattr(agent_server, '_agent') and agent_server._agent is not None:
24
+ return agent_server._agent
25
+
26
+ if hasattr(agent_server, 'agent') and agent_server.agent is not None:
27
+ return agent_server.agent
28
+
29
+ if hasattr(agent_server, '_workflow_factory') and agent_server._workflow_factory is not None:
30
+ try:
31
+ return agent_server._workflow_factory().as_agent()
32
+ except Exception as e:
33
+ logging.debug("Failed to build workflow agent (%s): %s", type(e).__name__, e)
34
+
35
+ if hasattr(agent_server, '_build_agent'):
36
+ try:
37
+ return agent_server._build_agent()
38
+ except Exception as e:
39
+ logging.debug("Failed to call _build_agent (%s): %s", type(e).__name__, e)
40
+
41
+ return None
42
+
43
+
44
+ def configure_agent_server(agent_server):
13
45
  """
14
46
  Set up workflow or agent visualization for an agent server.
15
47
 
@@ -18,12 +50,14 @@ def setup_test_tool(agent_server):
18
50
 
19
51
  Args:
20
52
  agent_server: The agent server instance to set up visualization for.
21
- Should have 'app' (Starlette) and 'agent' attributes.
53
+ Should have 'app' (Starlette) attribute. The agent is
54
+ extracted using `_get_agent_from_server()` which supports
55
+ both old and new SDK versions.
22
56
 
23
57
  Example:
24
58
  >>> from azure.ai.agentserver.agentframework import from_agent_framework
25
59
  >>> agent_server = from_agent_framework(agent)
26
- >>> setup_test_tool(agent_server)
60
+ >>> configure_agent_server(agent_server)
27
61
  >>> await agent_server.run_async()
28
62
  """
29
63
  from starlette.applications import Starlette
@@ -37,20 +71,22 @@ def setup_test_tool(agent_server):
37
71
  return JSONResponse({"status": "ok"}, status_code=200)
38
72
 
39
73
  app = agent_server.app
40
- agent = agent_server.agent
74
+ agent = _get_agent_from_server(agent_server)
75
+
76
+ if agent is None:
77
+ logging.warning("agentdev: Could not extract agent from server, visualization may be limited")
41
78
 
42
- # Mount health check endpoint
43
79
  app.mount(
44
80
  "/agentdev/health",
45
81
  Starlette(routes=[Route("/", health_check)])
46
82
  )
47
83
 
48
- # Prepare entities for visualization
49
84
  entities = []
50
- if isinstance(agent, WorkflowAgent):
51
- entities.append(agent.workflow)
52
- else:
53
- entities.append(agent)
85
+ if agent is not None:
86
+ if isinstance(agent, WorkflowAgent):
87
+ entities.append(agent.workflow)
88
+ else:
89
+ entities.append(agent)
54
90
 
55
91
  test_tool_server = TestToolServer(entities)
56
92
  test_tool_server.mount_backend(app)