waldiez 0.5.10__py3-none-any.whl → 0.6.0__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.
Potentially problematic release.
This version of waldiez might be problematic. Click here for more details.
- waldiez/_version.py +1 -1
- waldiez/cli.py +1 -0
- waldiez/exporting/agent/exporter.py +6 -6
- waldiez/exporting/agent/extras/group_manager_agent_extas.py +6 -1
- waldiez/exporting/agent/extras/handoffs/after_work.py +1 -0
- waldiez/exporting/agent/extras/handoffs/available.py +1 -0
- waldiez/exporting/agent/extras/handoffs/handoff.py +1 -0
- waldiez/exporting/agent/extras/handoffs/target.py +1 -0
- waldiez/exporting/agent/termination.py +1 -0
- waldiez/exporting/core/constants.py +3 -1
- waldiez/exporting/core/extras/serializer.py +12 -10
- waldiez/exporting/core/types.py +1 -0
- waldiez/exporting/core/utils/llm_config.py +2 -2
- waldiez/exporting/flow/execution_generator.py +1 -0
- waldiez/exporting/flow/utils/common.py +1 -1
- waldiez/exporting/flow/utils/importing.py +1 -1
- waldiez/exporting/flow/utils/logging.py +3 -75
- waldiez/io/__init__.py +3 -1
- waldiez/io/_ws.py +2 -0
- waldiez/io/structured.py +81 -28
- waldiez/io/utils.py +16 -10
- waldiez/io/ws.py +2 -2
- waldiez/models/agents/agent/agent.py +2 -1
- waldiez/models/chat/chat.py +1 -0
- waldiez/models/chat/chat_data.py +0 -2
- waldiez/models/common/base.py +2 -0
- waldiez/models/common/handoff.py +2 -0
- waldiez/models/common/method_utils.py +2 -0
- waldiez/models/model/_llm.py +3 -0
- waldiez/models/tool/predefined/_email.py +3 -0
- waldiez/models/tool/predefined/_perplexity.py +1 -1
- waldiez/models/tool/predefined/_searxng.py +1 -1
- waldiez/models/tool/predefined/_wikipedia.py +1 -1
- waldiez/running/base_runner.py +81 -20
- waldiez/running/post_run.py +6 -0
- waldiez/running/pre_run.py +167 -45
- waldiez/running/standard_runner.py +5 -5
- waldiez/running/step_by_step/breakpoints_mixin.py +368 -44
- waldiez/running/step_by_step/command_handler.py +151 -0
- waldiez/running/step_by_step/events_processor.py +199 -0
- waldiez/running/step_by_step/step_by_step_models.py +358 -41
- waldiez/running/step_by_step/step_by_step_runner.py +358 -353
- waldiez/running/subprocess_runner/__base__.py +4 -7
- waldiez/running/subprocess_runner/_async_runner.py +1 -1
- waldiez/running/subprocess_runner/_sync_runner.py +5 -4
- waldiez/running/subprocess_runner/runner.py +9 -0
- waldiez/running/utils.py +116 -2
- waldiez/ws/__init__.py +8 -7
- waldiez/ws/_file_handler.py +0 -2
- waldiez/ws/_mock.py +74 -0
- waldiez/ws/cli.py +27 -3
- waldiez/ws/client_manager.py +45 -29
- waldiez/ws/models.py +18 -1
- waldiez/ws/reloader.py +23 -2
- waldiez/ws/server.py +47 -8
- waldiez/ws/utils.py +29 -4
- {waldiez-0.5.10.dist-info → waldiez-0.6.0.dist-info}/METADATA +53 -44
- {waldiez-0.5.10.dist-info → waldiez-0.6.0.dist-info}/RECORD +62 -59
- {waldiez-0.5.10.dist-info → waldiez-0.6.0.dist-info}/WHEEL +0 -0
- {waldiez-0.5.10.dist-info → waldiez-0.6.0.dist-info}/entry_points.txt +0 -0
- {waldiez-0.5.10.dist-info → waldiez-0.6.0.dist-info}/licenses/LICENSE +0 -0
- {waldiez-0.5.10.dist-info → waldiez-0.6.0.dist-info}/licenses/NOTICE.md +0 -0
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0.
|
|
2
|
+
# Copyright (c) 2024 - 2025 Waldiez and contributors.
|
|
3
|
+
# pylint: disable=unused-argument,no-self-use
|
|
4
|
+
"""Command handler for step-by-step execution."""
|
|
5
|
+
|
|
6
|
+
from typing import TYPE_CHECKING, Any, Union
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from autogen.events.base_event import BaseEvent # type: ignore
|
|
10
|
+
from autogen.messages.base_message import BaseMessage # type: ignore
|
|
11
|
+
|
|
12
|
+
# noinspection PyUnusedImports
|
|
13
|
+
from .step_by_step_runner import WaldiezStepByStepRunner
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
# pylint: disable=too-few-public-methods
|
|
17
|
+
class EventProcessor:
|
|
18
|
+
"""Processes events for the step-by-step runner."""
|
|
19
|
+
|
|
20
|
+
def __init__(self, runner: "WaldiezStepByStepRunner"):
|
|
21
|
+
self.runner = runner
|
|
22
|
+
|
|
23
|
+
def process_event(
|
|
24
|
+
self, event: Union["BaseEvent", "BaseMessage"]
|
|
25
|
+
) -> dict[str, Any]:
|
|
26
|
+
"""Shared logic for both sync and async event processing.
|
|
27
|
+
|
|
28
|
+
Parameters
|
|
29
|
+
----------
|
|
30
|
+
event : BaseEvent | BaseMessage
|
|
31
|
+
The event to process.
|
|
32
|
+
|
|
33
|
+
Returns
|
|
34
|
+
-------
|
|
35
|
+
dict[str, Any]
|
|
36
|
+
The result of processing the event.
|
|
37
|
+
"""
|
|
38
|
+
self.runner.event_plus_one()
|
|
39
|
+
self.runner.current_event = event
|
|
40
|
+
|
|
41
|
+
if self.runner.is_stop_requested():
|
|
42
|
+
return {"action": "stop", "reason": "stop_requested"}
|
|
43
|
+
|
|
44
|
+
event_info = self._create_event_info(event)
|
|
45
|
+
self._update_participant_info(event_info)
|
|
46
|
+
self._manage_event_history(event_info)
|
|
47
|
+
self._check_for_input_request(event_info)
|
|
48
|
+
|
|
49
|
+
should_break = self.runner.should_break_on_event(
|
|
50
|
+
event, self.runner.step_mode
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
"action": "continue",
|
|
55
|
+
"should_break": should_break,
|
|
56
|
+
"event_info": event_info,
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
def _create_event_info(
|
|
60
|
+
self, event: Union["BaseEvent", "BaseMessage"]
|
|
61
|
+
) -> dict[str, Any]:
|
|
62
|
+
"""Create event info dictionary from event object.
|
|
63
|
+
|
|
64
|
+
Parameters
|
|
65
|
+
----------
|
|
66
|
+
event : BaseEvent | BaseMessage
|
|
67
|
+
The event to convert to info dict.
|
|
68
|
+
|
|
69
|
+
Returns
|
|
70
|
+
-------
|
|
71
|
+
dict[str, Any]
|
|
72
|
+
Event information dictionary.
|
|
73
|
+
"""
|
|
74
|
+
event_info = event.model_dump(
|
|
75
|
+
mode="json", exclude_none=True, fallback=str
|
|
76
|
+
)
|
|
77
|
+
event_info["count"] = self.runner.event_count
|
|
78
|
+
event_info["sender"] = getattr(event, "sender", self.runner.last_sender)
|
|
79
|
+
event_info["recipient"] = getattr(
|
|
80
|
+
event, "recipient", self.runner.last_recipient
|
|
81
|
+
)
|
|
82
|
+
return event_info
|
|
83
|
+
|
|
84
|
+
def _update_participant_info(self, event_info: dict[str, Any]) -> None:
|
|
85
|
+
"""Update sender and recipient information in event_info.
|
|
86
|
+
|
|
87
|
+
Parameters
|
|
88
|
+
----------
|
|
89
|
+
event_info : dict[str, Any]
|
|
90
|
+
Event information dictionary to update.
|
|
91
|
+
"""
|
|
92
|
+
if not event_info["sender"] or not event_info["recipient"]:
|
|
93
|
+
self._extract_participants_from_content(event_info)
|
|
94
|
+
|
|
95
|
+
self._handle_group_chat_speaker(event_info)
|
|
96
|
+
self._extract_participants_from_direct_content(event_info)
|
|
97
|
+
|
|
98
|
+
# Update last known participants
|
|
99
|
+
self.runner.last_sender = event_info["sender"]
|
|
100
|
+
self.runner.last_recipient = event_info["recipient"]
|
|
101
|
+
|
|
102
|
+
def _extract_participants_from_content(
|
|
103
|
+
self, event_info: dict[str, Any]
|
|
104
|
+
) -> None:
|
|
105
|
+
"""Extract sender/recipient from nested content structure.
|
|
106
|
+
|
|
107
|
+
Parameters
|
|
108
|
+
----------
|
|
109
|
+
event_info : dict[str, Any]
|
|
110
|
+
Event information dictionary to update.
|
|
111
|
+
"""
|
|
112
|
+
content = event_info.get("content", {})
|
|
113
|
+
if (
|
|
114
|
+
isinstance(content, dict)
|
|
115
|
+
and "chat_info" in content
|
|
116
|
+
and isinstance(content["chat_info"], dict)
|
|
117
|
+
):
|
|
118
|
+
content = content.get("chat_info", {}) # pyright: ignore
|
|
119
|
+
|
|
120
|
+
if not event_info["sender"] and "sender" in content:
|
|
121
|
+
event_info["sender"] = content["sender"]
|
|
122
|
+
if not event_info["recipient"] and "recipient" in content:
|
|
123
|
+
event_info["recipient"] = content["recipient"]
|
|
124
|
+
|
|
125
|
+
def _handle_group_chat_speaker(self, event_info: dict[str, Any]) -> None:
|
|
126
|
+
"""Handle speaker information for group chat events.
|
|
127
|
+
|
|
128
|
+
Parameters
|
|
129
|
+
----------
|
|
130
|
+
event_info : dict[str, Any]
|
|
131
|
+
Event information dictionary to update.
|
|
132
|
+
"""
|
|
133
|
+
if (
|
|
134
|
+
event_info.get("type") == "group_chat_run_chat"
|
|
135
|
+
and "content" in event_info
|
|
136
|
+
and isinstance(event_info["content"], dict)
|
|
137
|
+
):
|
|
138
|
+
content = event_info.get("content", {})
|
|
139
|
+
speaker = content.get("speaker")
|
|
140
|
+
if isinstance(speaker, str) and speaker:
|
|
141
|
+
event_info["sender"] = speaker
|
|
142
|
+
|
|
143
|
+
def _extract_participants_from_direct_content(
|
|
144
|
+
self, event_info: dict[str, Any]
|
|
145
|
+
) -> None:
|
|
146
|
+
"""Extract sender/recipient directly from content dictionary.
|
|
147
|
+
|
|
148
|
+
Parameters
|
|
149
|
+
----------
|
|
150
|
+
event_info : dict[str, Any]
|
|
151
|
+
Event information dictionary to update.
|
|
152
|
+
"""
|
|
153
|
+
if "content" in event_info and isinstance(event_info["content"], dict):
|
|
154
|
+
content = event_info.get("content", {})
|
|
155
|
+
|
|
156
|
+
sender = content.get("sender", "")
|
|
157
|
+
if isinstance(sender, str) and sender:
|
|
158
|
+
event_info["sender"] = sender
|
|
159
|
+
|
|
160
|
+
recipient = content.get("recipient", "")
|
|
161
|
+
if isinstance(recipient, str) and recipient:
|
|
162
|
+
event_info["recipient"] = recipient
|
|
163
|
+
|
|
164
|
+
def _manage_event_history(self, event_info: dict[str, Any]) -> None:
|
|
165
|
+
"""Add event to history and manage history size limits.
|
|
166
|
+
|
|
167
|
+
Parameters
|
|
168
|
+
----------
|
|
169
|
+
event_info : dict[str, Any]
|
|
170
|
+
Event information to add to history.
|
|
171
|
+
"""
|
|
172
|
+
self.runner.add_to_history(event_info)
|
|
173
|
+
self._trim_history_if_needed()
|
|
174
|
+
|
|
175
|
+
def _trim_history_if_needed(self) -> None:
|
|
176
|
+
"""Remove oldest events from history if over size limit."""
|
|
177
|
+
current_history = self.runner.event_history
|
|
178
|
+
if len(current_history) > self.runner.max_event_history:
|
|
179
|
+
excess = len(current_history) - self.runner.max_event_history
|
|
180
|
+
for _ in range(excess):
|
|
181
|
+
self.runner.pop_event()
|
|
182
|
+
|
|
183
|
+
def _check_for_input_request(self, event_info: dict[str, Any]) -> None:
|
|
184
|
+
"""Swap participant names if we have an input request."""
|
|
185
|
+
if (
|
|
186
|
+
event_info["type"] in ("input_request", "debug_input_request")
|
|
187
|
+
and "sender" in event_info
|
|
188
|
+
and "recipient" in event_info
|
|
189
|
+
):
|
|
190
|
+
# swap them,
|
|
191
|
+
# before:
|
|
192
|
+
# "recipient" is the user (the one received the input request),
|
|
193
|
+
# make her the "sender" (the one typing...)
|
|
194
|
+
sender = event_info["sender"]
|
|
195
|
+
recipient = event_info["recipient"]
|
|
196
|
+
event_info["sender"] = recipient
|
|
197
|
+
event_info["recipient"] = sender
|
|
198
|
+
self.runner.last_sender = recipient
|
|
199
|
+
self.runner.last_recipient = sender
|
|
@@ -1,11 +1,23 @@
|
|
|
1
1
|
# SPDX-License-Identifier: Apache-2.0.
|
|
2
2
|
# Copyright (c) 2024 - 2025 Waldiez and contributors.
|
|
3
|
+
# pylint: disable=unused-argument,disable=line-too-long
|
|
4
|
+
# flake8: noqa: E501
|
|
3
5
|
"""Step-by-step execution models for Waldiez."""
|
|
4
6
|
|
|
7
|
+
import re
|
|
5
8
|
from enum import Enum
|
|
6
|
-
from typing import Annotated, Any,
|
|
9
|
+
from typing import Annotated, Any, Literal, Union
|
|
7
10
|
|
|
8
|
-
from pydantic import BaseModel, Field
|
|
11
|
+
from pydantic import BaseModel, Field, ValidationInfo, field_validator
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class WaldiezBreakpointType(Enum):
|
|
15
|
+
"""Types of breakpoints available."""
|
|
16
|
+
|
|
17
|
+
EVENT = "event" # Break on specific event type
|
|
18
|
+
AGENT = "agent" # Break on any event from specific agent
|
|
19
|
+
AGENT_EVENT = "agent_event" # Break on specific event from specific agent
|
|
20
|
+
ALL = "all" # Break on all events (default step mode)
|
|
9
21
|
|
|
10
22
|
|
|
11
23
|
class WaldiezDebugStepAction(Enum):
|
|
@@ -41,6 +53,302 @@ VALID_CONTROL_COMMANDS = {
|
|
|
41
53
|
}
|
|
42
54
|
|
|
43
55
|
|
|
56
|
+
class WaldiezBreakpoint(BaseModel):
|
|
57
|
+
"""Breakpoint definition with enhanced validation."""
|
|
58
|
+
|
|
59
|
+
type: WaldiezBreakpointType
|
|
60
|
+
event_type: str | None = None # Required for EVENT and AGENT_EVENT
|
|
61
|
+
agent_name: str | None = None # Required for AGENT and AGENT_EVENT
|
|
62
|
+
description: str | None = None # Human-readable description
|
|
63
|
+
|
|
64
|
+
# noinspection PyNestedDecorators,PyUnusedLocal
|
|
65
|
+
@field_validator("event_type")
|
|
66
|
+
@classmethod
|
|
67
|
+
def validate_event_type(
|
|
68
|
+
cls,
|
|
69
|
+
v: str | None,
|
|
70
|
+
info: ValidationInfo,
|
|
71
|
+
) -> str | None:
|
|
72
|
+
"""Validate event type format.
|
|
73
|
+
|
|
74
|
+
Parameters
|
|
75
|
+
----------
|
|
76
|
+
v : str | None
|
|
77
|
+
The event type to validate.
|
|
78
|
+
info : ValidationInfo
|
|
79
|
+
Validation context information.
|
|
80
|
+
|
|
81
|
+
Returns
|
|
82
|
+
-------
|
|
83
|
+
str | None
|
|
84
|
+
The validated event type or None if not provided.
|
|
85
|
+
|
|
86
|
+
Raises
|
|
87
|
+
------
|
|
88
|
+
ValueError
|
|
89
|
+
If the event type format is invalid.
|
|
90
|
+
"""
|
|
91
|
+
if v is None:
|
|
92
|
+
return v
|
|
93
|
+
|
|
94
|
+
# Basic validation - event types should be alphanumeric with underscores
|
|
95
|
+
if not re.match(r"^[a-zA-Z][a-zA-Z0-9_]*$", v):
|
|
96
|
+
raise ValueError(
|
|
97
|
+
"Invalid breakpoint format. Event type must start with a letter and contain only "
|
|
98
|
+
"letters, numbers, and underscores"
|
|
99
|
+
)
|
|
100
|
+
return v
|
|
101
|
+
|
|
102
|
+
# noinspection PyNestedDecorators,PyUnusedLocal
|
|
103
|
+
@field_validator("agent_name")
|
|
104
|
+
@classmethod
|
|
105
|
+
def validate_agent_name(
|
|
106
|
+
cls,
|
|
107
|
+
v: str | None,
|
|
108
|
+
info: ValidationInfo,
|
|
109
|
+
) -> str | None:
|
|
110
|
+
"""Validate agent name format.
|
|
111
|
+
|
|
112
|
+
Parameters
|
|
113
|
+
----------
|
|
114
|
+
v : str | None
|
|
115
|
+
The agent name to validate.
|
|
116
|
+
info : ValidationInfo
|
|
117
|
+
Validation context information.
|
|
118
|
+
|
|
119
|
+
Returns
|
|
120
|
+
-------
|
|
121
|
+
str | None
|
|
122
|
+
The validated agent name or None if not provided.
|
|
123
|
+
|
|
124
|
+
Raises
|
|
125
|
+
------
|
|
126
|
+
ValueError
|
|
127
|
+
If the agent name format is invalid.
|
|
128
|
+
"""
|
|
129
|
+
if v is None:
|
|
130
|
+
return v
|
|
131
|
+
|
|
132
|
+
# Agent names should not be empty or just whitespace
|
|
133
|
+
if not v.strip():
|
|
134
|
+
raise ValueError("Agent name cannot be empty or just whitespace")
|
|
135
|
+
|
|
136
|
+
return v.strip()
|
|
137
|
+
|
|
138
|
+
def model_post_init(self, __context: Any, /) -> None:
|
|
139
|
+
"""Validate breakpoint consistency after initialization.
|
|
140
|
+
|
|
141
|
+
Raises
|
|
142
|
+
------
|
|
143
|
+
ValueError
|
|
144
|
+
If the breakpoint configuration is invalid.
|
|
145
|
+
"""
|
|
146
|
+
if self.type == WaldiezBreakpointType.EVENT and not self.event_type:
|
|
147
|
+
raise ValueError("EVENT breakpoints require an event_type")
|
|
148
|
+
|
|
149
|
+
if self.type == WaldiezBreakpointType.AGENT and not self.agent_name:
|
|
150
|
+
raise ValueError("AGENT breakpoints require an agent_name")
|
|
151
|
+
|
|
152
|
+
if self.type == WaldiezBreakpointType.AGENT_EVENT:
|
|
153
|
+
if not self.event_type or not self.agent_name:
|
|
154
|
+
raise ValueError(
|
|
155
|
+
"AGENT_EVENT breakpoints require both"
|
|
156
|
+
" event_type and agent_name"
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
def __hash__(self) -> int:
|
|
160
|
+
"""Get the hash value for the breakpoint."""
|
|
161
|
+
return hash((self.type, self.event_type, self.agent_name))
|
|
162
|
+
|
|
163
|
+
def __str__(self) -> str:
|
|
164
|
+
"""Get the string representation for display."""
|
|
165
|
+
if self.type == WaldiezBreakpointType.EVENT:
|
|
166
|
+
return f"event:{self.event_type}"
|
|
167
|
+
if self.type == WaldiezBreakpointType.AGENT:
|
|
168
|
+
return f"agent:{self.agent_name}"
|
|
169
|
+
if self.type == WaldiezBreakpointType.AGENT_EVENT:
|
|
170
|
+
return f"{self.agent_name}:{self.event_type}"
|
|
171
|
+
# else: # ALL
|
|
172
|
+
return "all"
|
|
173
|
+
|
|
174
|
+
# pylint: disable=too-complex
|
|
175
|
+
@classmethod
|
|
176
|
+
def from_string( # noqa: C901
|
|
177
|
+
cls,
|
|
178
|
+
breakpoint_str: str,
|
|
179
|
+
) -> "WaldiezBreakpoint":
|
|
180
|
+
"""Parse breakpoint from string format with enhanced validation.
|
|
181
|
+
|
|
182
|
+
Parameters
|
|
183
|
+
----------
|
|
184
|
+
breakpoint_str : str
|
|
185
|
+
The string representation of the breakpoint.
|
|
186
|
+
|
|
187
|
+
Returns
|
|
188
|
+
-------
|
|
189
|
+
WaldiezBreakpoint
|
|
190
|
+
The parsed breakpoint object.
|
|
191
|
+
|
|
192
|
+
Raises
|
|
193
|
+
------
|
|
194
|
+
ValueError
|
|
195
|
+
If the breakpoint string format is invalid.
|
|
196
|
+
"""
|
|
197
|
+
if not breakpoint_str or not isinstance( # pyright: ignore
|
|
198
|
+
breakpoint_str,
|
|
199
|
+
str,
|
|
200
|
+
):
|
|
201
|
+
raise ValueError("Breakpoint specification cannot be empty")
|
|
202
|
+
|
|
203
|
+
breakpoint_str = breakpoint_str.strip()
|
|
204
|
+
if not breakpoint_str:
|
|
205
|
+
raise ValueError(
|
|
206
|
+
"Breakpoint specification cannot be just whitespace"
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
if breakpoint_str == "all":
|
|
210
|
+
return cls(type=WaldiezBreakpointType.ALL)
|
|
211
|
+
|
|
212
|
+
if breakpoint_str.startswith("event:"):
|
|
213
|
+
event_type = breakpoint_str[6:] # Remove "event:" prefix
|
|
214
|
+
if not event_type:
|
|
215
|
+
raise ValueError("Event type cannot be empty after 'event:'")
|
|
216
|
+
return cls(type=WaldiezBreakpointType.EVENT, event_type=event_type)
|
|
217
|
+
|
|
218
|
+
if breakpoint_str.startswith("agent:"):
|
|
219
|
+
agent_name = breakpoint_str[6:] # Remove "agent:" prefix
|
|
220
|
+
if not agent_name:
|
|
221
|
+
raise ValueError("Agent name cannot be empty after 'agent:'")
|
|
222
|
+
return cls(type=WaldiezBreakpointType.AGENT, agent_name=agent_name)
|
|
223
|
+
|
|
224
|
+
if ":" in breakpoint_str and not breakpoint_str.startswith(
|
|
225
|
+
("event:", "agent:")
|
|
226
|
+
):
|
|
227
|
+
# Format: "agent_name:event_type"
|
|
228
|
+
parts = breakpoint_str.split(":", 1)
|
|
229
|
+
if len(parts) != 2:
|
|
230
|
+
raise ValueError("Invalid agent:event format")
|
|
231
|
+
|
|
232
|
+
agent_name, event_type = parts
|
|
233
|
+
if not agent_name or not event_type:
|
|
234
|
+
raise ValueError(
|
|
235
|
+
"Both agent name and event type must be specified"
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
return cls(
|
|
239
|
+
type=WaldiezBreakpointType.AGENT_EVENT,
|
|
240
|
+
agent_name=agent_name,
|
|
241
|
+
event_type=event_type,
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
# Default to event type - but validate it's reasonable
|
|
245
|
+
if ":" in breakpoint_str:
|
|
246
|
+
raise ValueError(
|
|
247
|
+
"Invalid breakpoint format. Use 'event:type', 'agent:name', "
|
|
248
|
+
"'agent:event', or 'all'"
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
return cls(type=WaldiezBreakpointType.EVENT, event_type=breakpoint_str)
|
|
252
|
+
|
|
253
|
+
def matches(self, event: dict[str, Any]) -> bool:
|
|
254
|
+
"""Check if this breakpoint matches the given event.
|
|
255
|
+
|
|
256
|
+
Parameters
|
|
257
|
+
----------
|
|
258
|
+
event : dict[str, Any]
|
|
259
|
+
The event to check against.
|
|
260
|
+
|
|
261
|
+
Returns
|
|
262
|
+
-------
|
|
263
|
+
bool
|
|
264
|
+
True if the event matches the breakpoint, False otherwise.
|
|
265
|
+
"""
|
|
266
|
+
if self.type == WaldiezBreakpointType.ALL:
|
|
267
|
+
return True
|
|
268
|
+
|
|
269
|
+
if self.type == WaldiezBreakpointType.EVENT:
|
|
270
|
+
return event.get("type") == self.event_type
|
|
271
|
+
|
|
272
|
+
if self.type == WaldiezBreakpointType.AGENT:
|
|
273
|
+
return (
|
|
274
|
+
event.get("sender") == self.agent_name
|
|
275
|
+
or event.get("recipient") == self.agent_name
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
if self.type == WaldiezBreakpointType.AGENT_EVENT:
|
|
279
|
+
return event.get("type") == self.event_type and (
|
|
280
|
+
event.get("sender") == self.agent_name
|
|
281
|
+
or event.get("recipient") == self.agent_name
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
# noinspection PyUnreachableCode
|
|
285
|
+
return False
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
# Enhanced configuration class for runtime settings
|
|
289
|
+
class WaldiezDebugConfig(BaseModel):
|
|
290
|
+
"""Configuration for debug session settings."""
|
|
291
|
+
|
|
292
|
+
max_event_history: int = Field(default=1000, ge=1, le=10000)
|
|
293
|
+
auto_continue: bool = Field(default=False)
|
|
294
|
+
step_mode: bool = Field(default=True)
|
|
295
|
+
enable_stats_collection: bool = Field(default=True)
|
|
296
|
+
command_timeout_seconds: float = Field(default=300.0, gt=0)
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
# Rest of the existing message classes remain the same...
|
|
300
|
+
class WaldiezDebugBreakpointsList(BaseModel):
|
|
301
|
+
"""Debug breakpoints message."""
|
|
302
|
+
|
|
303
|
+
type: Literal["debug_breakpoints_list"] = "debug_breakpoints_list"
|
|
304
|
+
breakpoints: (
|
|
305
|
+
list[str | WaldiezBreakpoint] | list[str] | list[WaldiezBreakpoint]
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
@property
|
|
309
|
+
def breakpoint_objects(self) -> list[WaldiezBreakpoint]:
|
|
310
|
+
"""Get all breakpoints as WaldiezBreakpoint objects."""
|
|
311
|
+
result: list[WaldiezBreakpoint] = []
|
|
312
|
+
for bp in self.breakpoints:
|
|
313
|
+
if isinstance(bp, str):
|
|
314
|
+
try:
|
|
315
|
+
result.append(WaldiezBreakpoint.from_string(bp))
|
|
316
|
+
except ValueError:
|
|
317
|
+
# Skip invalid breakpoints rather than failing
|
|
318
|
+
continue
|
|
319
|
+
else:
|
|
320
|
+
result.append(bp)
|
|
321
|
+
return result
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
class WaldiezDebugBreakpointAdded(BaseModel):
|
|
325
|
+
"""Debug breakpoint added message."""
|
|
326
|
+
|
|
327
|
+
type: Literal["debug_breakpoint_added"] = "debug_breakpoint_added"
|
|
328
|
+
breakpoint: Union[str, WaldiezBreakpoint]
|
|
329
|
+
|
|
330
|
+
@property
|
|
331
|
+
def breakpoint_object(self) -> WaldiezBreakpoint:
|
|
332
|
+
"""Get breakpoint as WaldiezBreakpoint object."""
|
|
333
|
+
if isinstance(self.breakpoint, str):
|
|
334
|
+
return WaldiezBreakpoint.from_string(self.breakpoint)
|
|
335
|
+
return self.breakpoint
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
class WaldiezDebugBreakpointRemoved(BaseModel):
|
|
339
|
+
"""Debug breakpoint removed message."""
|
|
340
|
+
|
|
341
|
+
type: Literal["debug_breakpoint_removed"] = "debug_breakpoint_removed"
|
|
342
|
+
breakpoint: Union[str, WaldiezBreakpoint]
|
|
343
|
+
|
|
344
|
+
@property
|
|
345
|
+
def breakpoint_object(self) -> WaldiezBreakpoint:
|
|
346
|
+
"""Get breakpoint as WaldiezBreakpoint object."""
|
|
347
|
+
if isinstance(self.breakpoint, str):
|
|
348
|
+
return WaldiezBreakpoint.from_string(self.breakpoint)
|
|
349
|
+
return self.breakpoint
|
|
350
|
+
|
|
351
|
+
|
|
44
352
|
class WaldiezDebugHelpCommand(BaseModel):
|
|
45
353
|
"""Help command information."""
|
|
46
354
|
|
|
@@ -84,14 +392,14 @@ class WaldiezDebugEventInfo(BaseModel):
|
|
|
84
392
|
"""Debug event info message."""
|
|
85
393
|
|
|
86
394
|
type: Literal["debug_event_info"] = "debug_event_info"
|
|
87
|
-
event:
|
|
395
|
+
event: dict[str, Any]
|
|
88
396
|
|
|
89
397
|
|
|
90
398
|
class WaldiezDebugStats(BaseModel):
|
|
91
399
|
"""Debug stats message."""
|
|
92
400
|
|
|
93
401
|
type: Literal["debug_stats"] = "debug_stats"
|
|
94
|
-
stats:
|
|
402
|
+
stats: dict[str, Any]
|
|
95
403
|
|
|
96
404
|
|
|
97
405
|
class WaldiezDebugHelp(BaseModel):
|
|
@@ -108,28 +416,6 @@ class WaldiezDebugError(BaseModel):
|
|
|
108
416
|
error: str
|
|
109
417
|
|
|
110
418
|
|
|
111
|
-
class WaldiezDebugBreakpointsList(BaseModel):
|
|
112
|
-
"""Debug breakpoints message."""
|
|
113
|
-
|
|
114
|
-
type: Literal["debug_breakpoints_list"] = "debug_breakpoints_list"
|
|
115
|
-
breakpoints: list[str] # Event types
|
|
116
|
-
# Optional: Could extend to include agent+event combinations
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
class WaldiezDebugBreakpointAdded(BaseModel):
|
|
120
|
-
"""Debug breakpoint added message."""
|
|
121
|
-
|
|
122
|
-
type: Literal["debug_breakpoint_added"] = "debug_breakpoint_added"
|
|
123
|
-
breakpoint: str
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
class WaldiezDebugBreakpointRemoved(BaseModel):
|
|
127
|
-
"""Debug breakpoint removed message."""
|
|
128
|
-
|
|
129
|
-
type: Literal["debug_breakpoint_removed"] = "debug_breakpoint_removed"
|
|
130
|
-
breakpoint: str
|
|
131
|
-
|
|
132
|
-
|
|
133
419
|
class WaldiezDebugBreakpointCleared(BaseModel):
|
|
134
420
|
"""Debug breakpoint cleared message."""
|
|
135
421
|
|
|
@@ -165,44 +451,69 @@ class WaldiezDebugMessageWrapper(BaseModel):
|
|
|
165
451
|
HELP_MESSAGE = WaldiezDebugHelp(
|
|
166
452
|
help=[
|
|
167
453
|
WaldiezDebugHelpCommandGroup(
|
|
168
|
-
title="Commands",
|
|
454
|
+
title="Basic Commands",
|
|
169
455
|
commands=[
|
|
170
456
|
WaldiezDebugHelpCommand(
|
|
171
|
-
cmds=["continue", "c"], desc="Continue to the next step
|
|
457
|
+
cmds=["continue", "c"], desc="Continue to the next step"
|
|
172
458
|
),
|
|
173
459
|
WaldiezDebugHelpCommand(
|
|
174
|
-
cmds=["step", "s"], desc="Step through the next event
|
|
460
|
+
cmds=["step", "s"], desc="Step through the next event"
|
|
175
461
|
),
|
|
176
462
|
WaldiezDebugHelpCommand(
|
|
177
|
-
cmds=["run", "r"], desc="Run without stopping
|
|
463
|
+
cmds=["run", "r"], desc="Run without stopping"
|
|
178
464
|
),
|
|
179
465
|
WaldiezDebugHelpCommand(
|
|
180
|
-
cmds=["quit", "q"], desc="Quit the debugger
|
|
466
|
+
cmds=["quit", "q"], desc="Quit the debugger"
|
|
181
467
|
),
|
|
182
468
|
WaldiezDebugHelpCommand(
|
|
183
|
-
cmds=["info", "i"], desc="Show detailed event information
|
|
469
|
+
cmds=["info", "i"], desc="Show detailed event information"
|
|
184
470
|
),
|
|
185
471
|
WaldiezDebugHelpCommand(
|
|
186
|
-
cmds=["help", "h"], desc="Show this help message
|
|
472
|
+
cmds=["help", "h"], desc="Show this help message"
|
|
187
473
|
),
|
|
188
474
|
WaldiezDebugHelpCommand(
|
|
189
|
-
cmds=["stats", "st"], desc="Show execution statistics
|
|
475
|
+
cmds=["stats", "st"], desc="Show execution statistics"
|
|
190
476
|
),
|
|
477
|
+
],
|
|
478
|
+
),
|
|
479
|
+
WaldiezDebugHelpCommandGroup(
|
|
480
|
+
title="Breakpoint Commands",
|
|
481
|
+
commands=[
|
|
191
482
|
WaldiezDebugHelpCommand(
|
|
192
483
|
cmds=["add_breakpoint", "ab"],
|
|
193
|
-
desc="Add breakpoint
|
|
484
|
+
desc="Add breakpoint. Usage: 'ab [spec]' where spec is 'event:<type>', 'agent:<name>', '<name>:<event>', or 'all'",
|
|
194
485
|
),
|
|
195
486
|
WaldiezDebugHelpCommand(
|
|
196
487
|
cmds=["remove_breakpoint", "rb"],
|
|
197
|
-
desc="Remove breakpoint
|
|
488
|
+
desc="Remove breakpoint. Usage: 'rb [spec]' with same format as add",
|
|
198
489
|
),
|
|
199
490
|
WaldiezDebugHelpCommand(
|
|
200
|
-
cmds=["list_breakpoints", "lb"],
|
|
201
|
-
desc="List all breakpoints.",
|
|
491
|
+
cmds=["list_breakpoints", "lb"], desc="List all breakpoints"
|
|
202
492
|
),
|
|
203
493
|
WaldiezDebugHelpCommand(
|
|
204
494
|
cmds=["clear_breakpoints", "cb"],
|
|
205
|
-
desc="Clear all breakpoints
|
|
495
|
+
desc="Clear all breakpoints",
|
|
496
|
+
),
|
|
497
|
+
],
|
|
498
|
+
),
|
|
499
|
+
WaldiezDebugHelpCommandGroup(
|
|
500
|
+
title="Breakpoint Examples",
|
|
501
|
+
commands=[
|
|
502
|
+
WaldiezDebugHelpCommand(
|
|
503
|
+
desc="'ab' - Add breakpoint for the current event type"
|
|
504
|
+
),
|
|
505
|
+
WaldiezDebugHelpCommand(
|
|
506
|
+
desc="'ab event:tool_call' - Break on all 'tool_call' events"
|
|
507
|
+
),
|
|
508
|
+
WaldiezDebugHelpCommand(
|
|
509
|
+
desc="'ab agent:user' - Break on any event from 'user' agent"
|
|
510
|
+
),
|
|
511
|
+
WaldiezDebugHelpCommand(
|
|
512
|
+
desc="'ab assistant:tool_call' - Break on 'tool_call' events from 'assistant'"
|
|
513
|
+
),
|
|
514
|
+
WaldiezDebugHelpCommand(desc="'ab all' - Break on all events"),
|
|
515
|
+
WaldiezDebugHelpCommand(
|
|
516
|
+
desc="'rb event:tool_call' - Remove 'tool_call' event breakpoint"
|
|
206
517
|
),
|
|
207
518
|
],
|
|
208
519
|
),
|
|
@@ -213,10 +524,16 @@ HELP_MESSAGE = WaldiezDebugHelp(
|
|
|
213
524
|
desc="Press Enter alone to continue (same as 'c')"
|
|
214
525
|
),
|
|
215
526
|
WaldiezDebugHelpCommand(
|
|
216
|
-
desc="Use (s)tep to go through events one by one
|
|
527
|
+
desc="Use (s)tep to go through events one by one"
|
|
528
|
+
),
|
|
529
|
+
WaldiezDebugHelpCommand(
|
|
530
|
+
desc="Use (r)un to continue without stopping"
|
|
531
|
+
),
|
|
532
|
+
WaldiezDebugHelpCommand(
|
|
533
|
+
desc="Set specific breakpoints to avoid noise: 'ab event:message'"
|
|
217
534
|
),
|
|
218
535
|
WaldiezDebugHelpCommand(
|
|
219
|
-
desc="
|
|
536
|
+
desc="Check (st)ats regularly to monitor progress"
|
|
220
537
|
),
|
|
221
538
|
],
|
|
222
539
|
),
|