quantalogic 0.80__py3-none-any.whl → 0.93__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.
Files changed (55) hide show
  1. quantalogic/flow/__init__.py +16 -34
  2. quantalogic/main.py +11 -6
  3. quantalogic/tools/tool.py +8 -922
  4. quantalogic-0.93.dist-info/METADATA +475 -0
  5. {quantalogic-0.80.dist-info → quantalogic-0.93.dist-info}/RECORD +8 -54
  6. quantalogic/codeact/TODO.md +0 -14
  7. quantalogic/codeact/__init__.py +0 -0
  8. quantalogic/codeact/agent.py +0 -478
  9. quantalogic/codeact/cli.py +0 -50
  10. quantalogic/codeact/cli_commands/__init__.py +0 -0
  11. quantalogic/codeact/cli_commands/create_toolbox.py +0 -45
  12. quantalogic/codeact/cli_commands/install_toolbox.py +0 -20
  13. quantalogic/codeact/cli_commands/list_executor.py +0 -15
  14. quantalogic/codeact/cli_commands/list_reasoners.py +0 -15
  15. quantalogic/codeact/cli_commands/list_toolboxes.py +0 -47
  16. quantalogic/codeact/cli_commands/task.py +0 -215
  17. quantalogic/codeact/cli_commands/tool_info.py +0 -24
  18. quantalogic/codeact/cli_commands/uninstall_toolbox.py +0 -43
  19. quantalogic/codeact/config.yaml +0 -21
  20. quantalogic/codeact/constants.py +0 -9
  21. quantalogic/codeact/events.py +0 -85
  22. quantalogic/codeact/examples/README.md +0 -342
  23. quantalogic/codeact/examples/agent_sample.yaml +0 -29
  24. quantalogic/codeact/executor.py +0 -186
  25. quantalogic/codeact/history_manager.py +0 -94
  26. quantalogic/codeact/llm_util.py +0 -57
  27. quantalogic/codeact/plugin_manager.py +0 -92
  28. quantalogic/codeact/prompts/error_format.j2 +0 -11
  29. quantalogic/codeact/prompts/generate_action.j2 +0 -77
  30. quantalogic/codeact/prompts/generate_program.j2 +0 -52
  31. quantalogic/codeact/prompts/response_format.j2 +0 -11
  32. quantalogic/codeact/react_agent.py +0 -318
  33. quantalogic/codeact/reasoner.py +0 -185
  34. quantalogic/codeact/templates/toolbox/README.md.j2 +0 -10
  35. quantalogic/codeact/templates/toolbox/pyproject.toml.j2 +0 -16
  36. quantalogic/codeact/templates/toolbox/tools.py.j2 +0 -6
  37. quantalogic/codeact/templates.py +0 -7
  38. quantalogic/codeact/tools_manager.py +0 -258
  39. quantalogic/codeact/utils.py +0 -62
  40. quantalogic/codeact/xml_utils.py +0 -126
  41. quantalogic/flow/flow.py +0 -1070
  42. quantalogic/flow/flow_extractor.py +0 -783
  43. quantalogic/flow/flow_generator.py +0 -322
  44. quantalogic/flow/flow_manager.py +0 -676
  45. quantalogic/flow/flow_manager_schema.py +0 -287
  46. quantalogic/flow/flow_mermaid.py +0 -365
  47. quantalogic/flow/flow_validator.py +0 -479
  48. quantalogic/flow/flow_yaml.linkedin.md +0 -31
  49. quantalogic/flow/flow_yaml.md +0 -767
  50. quantalogic/flow/templates/prompt_check_inventory.j2 +0 -1
  51. quantalogic/flow/templates/system_check_inventory.j2 +0 -1
  52. quantalogic-0.80.dist-info/METADATA +0 -900
  53. {quantalogic-0.80.dist-info → quantalogic-0.93.dist-info}/LICENSE +0 -0
  54. {quantalogic-0.80.dist-info → quantalogic-0.93.dist-info}/WHEEL +0 -0
  55. {quantalogic-0.80.dist-info → quantalogic-0.93.dist-info}/entry_points.txt +0 -0
@@ -1,342 +0,0 @@
1
- # Quantalogic Agent Configuration YAML Format
2
-
3
- This document describes the YAML configuration format for the `AgentConfig` class in the Quantalogic framework, used to configure an AI agent’s behavior, tools, and components. The configuration is flexible, supporting both simple setups and advanced customizations, and is fully backward-compatible with earlier versions.
4
-
5
- ## Overview
6
-
7
- The YAML configuration file defines the settings for an `Agent` instance, controlling aspects such as the language model, task-solving parameters, tool usage, and agent personality. You can provide this configuration via a file (passed as a string to `Agent`) or directly as arguments to `AgentConfig`. All fields are optional unless specified, with sensible defaults ensuring compatibility with minimal setups.
8
-
9
- ### Example Minimal Configuration
10
- ```yaml
11
- model: "gemini/gemini-2.0-flash"
12
- max_iterations: 5
13
- name: "MathBot"
14
- personality: "witty"
15
- ```
16
-
17
- ### Example Advanced Configuration
18
- ```yaml
19
- name: "EquationSolver"
20
- model: "deepseek/deepseek-chat"
21
- max_iterations: 5
22
- max_history_tokens: 2000
23
- profile: "math_expert"
24
- customizations:
25
- personality:
26
- traits:
27
- - "witty"
28
- tools_config:
29
- - name: "math_tools"
30
- enabled: true
31
- config:
32
- precision: "high"
33
- api_key: "{{ env.MATH_API_KEY }}"
34
- reasoner:
35
- name: "default"
36
- config:
37
- temperature: 0.7
38
- executor:
39
- name: "default"
40
- config:
41
- timeout: 300
42
- sop: |
43
- Always provide clear, concise answers.
44
- Prioritize mathematical accuracy.
45
- ```
46
-
47
- ## Configuration Fields
48
-
49
- Below is a detailed breakdown of each field in the YAML configuration, including the new `name` field.
50
-
51
- ---
52
-
53
- ### `name`
54
- - **Description**: A unique identifier or nickname for the agent, included in the system prompt to personalize its identity.
55
- - **Type**: String (optional)
56
- - **Default**: `null`
57
- - **Example**:
58
- ```yaml
59
- name: "MathBot"
60
- ```
61
- - **Notes**: If provided, the agent introduces itself with this name in the system prompt (e.g., "I am MathBot, an AI assistant.").
62
-
63
- ---
64
-
65
- ### `model`
66
- - **Description**: Specifies the language model used by the agent for reasoning and text generation. Must be compatible with the `litellm` library.
67
- - **Type**: String
68
- - **Default**: `"gemini/gemini-2.0-flash"`
69
- - **Example**:
70
- ```yaml
71
- model: "deepseek/deepseek-chat"
72
- ```
73
-
74
- ---
75
-
76
- ### `max_iterations`
77
- - **Description**: Sets the maximum number of reasoning steps the agent will take to solve a task using the ReAct framework.
78
- - **Type**: Integer
79
- - **Default**: `5`
80
- - **Example**:
81
- ```yaml
82
- max_iterations: 10
83
- ```
84
-
85
- ---
86
-
87
- ### `tools`
88
- - **Description**: A list of pre-instantiated tools (as Python objects) to include in the agent. Typically used programmatically rather than in YAML.
89
- - **Type**: List of `Tool` or callable objects (optional)
90
- - **Default**: `null`
91
- - **Example** (Programmatic):
92
- ```python
93
- config = AgentConfig(tools=[my_custom_tool])
94
- ```
95
-
96
- ---
97
-
98
- ### `max_history_tokens`
99
- - **Description**: Limits the number of tokens stored in the agent’s history, affecting memory usage and context retention.
100
- - **Type**: Integer
101
- - **Default**: `8000` (from `MAX_HISTORY_TOKENS`)
102
- - **Example**:
103
- ```yaml
104
- max_history_tokens: 4000
105
- ```
106
-
107
- ---
108
-
109
- ### `toolbox_directory`
110
- - **Description**: Directory where custom toolbox modules are stored (used for local tool development).
111
- - **Type**: String
112
- - **Default**: `"toolboxes"`
113
- - **Example**:
114
- ```yaml
115
- toolbox_directory: "custom_tools"
116
- ```
117
-
118
- ---
119
-
120
- ### `enabled_toolboxes`
121
- - **Description**: A list of toolbox names to load from registered entry points (e.g., installed Python packages).
122
- - **Type**: List of strings (optional)
123
- - **Default**: `null`
124
- - **Example**:
125
- ```yaml
126
- enabled_toolboxes:
127
- - "math_tools"
128
- - "text_tools"
129
- ```
130
-
131
- ---
132
-
133
- ### `reasoner_name` (Legacy)
134
- - **Description**: Specifies the name of the reasoner plugin to use (older format, kept for compatibility).
135
- - **Type**: String
136
- - **Default**: `"default"`
137
- - **Example**:
138
- ```yaml
139
- reasoner_name: "advanced_reasoner"
140
- ```
141
-
142
- ---
143
-
144
- ### `executor_name` (Legacy)
145
- - **Description**: Specifies the name of the executor plugin to use (older format, kept for compatibility).
146
- - **Type**: String
147
- - **Default**: `"default"`
148
- - **Example**:
149
- ```yaml
150
- executor_name: "secure_executor"
151
- ```
152
-
153
- ---
154
-
155
- ### `personality`
156
- - **Description**: Defines the agent’s personality, influencing its system prompt. Can be a simple string (legacy) or a structured dictionary.
157
- - **Type**: String or Dictionary (optional)
158
- - **Default**: `null`
159
- - **Subfields (when dictionary)**:
160
- - `traits`: List of personality traits (e.g., "witty", "helpful").
161
- - `tone`: Tone of responses (e.g., "formal", "casual").
162
- - `humor_level`: Level of humor (e.g., "low", "medium", "high").
163
- - **Examples**:
164
- - Simple:
165
- ```yaml
166
- personality: "witty"
167
- ```
168
- - Structured:
169
- ```yaml
170
- personality:
171
- traits:
172
- - "witty"
173
- - "helpful"
174
- tone: "informal"
175
- humor_level: "medium"
176
- ```
177
-
178
- ---
179
-
180
- ### `backstory`
181
- - **Description**: Provides a backstory for the agent, included in the system prompt. Can be a string (legacy) or a dictionary.
182
- - **Type**: String or Dictionary (optional)
183
- - **Default**: `null`
184
- - **Subfields (when dictionary)**:
185
- - `origin`: Where the agent was created.
186
- - `purpose`: The agent’s intended purpose.
187
- - `experience`: Relevant past experience.
188
- - **Examples**:
189
- - Simple:
190
- ```yaml
191
- backstory: "A seasoned AI assistant."
192
- ```
193
- - Structured:
194
- ```yaml
195
- backstory:
196
- origin: "Created by Quantalogic."
197
- purpose: "Solve complex problems."
198
- experience: "Trained on 10,000+ math tasks."
199
- ```
200
-
201
- ---
202
-
203
- ### `sop`
204
- - **Description**: Standard Operating Procedure (SOP) as a multi-line string, guiding the agent’s behavior.
205
- - **Type**: String (optional)
206
- - **Default**: `null`
207
- - **Example**:
208
- ```yaml
209
- sop: |
210
- Always provide clear, concise answers.
211
- Prioritize accuracy over speed.
212
- ```
213
-
214
- ---
215
-
216
- ### `tools_config`
217
- - **Description**: Configures specific tools or toolboxes, allowing enabling/disabling and setting properties.
218
- - **Type**: List of dictionaries (optional)
219
- - **Default**: `null`
220
- - **Subfields**:
221
- - `name`: Name of the tool or toolbox (matches `tool.name` or `tool.toolbox_name`).
222
- - `enabled`: Boolean to include/exclude the tool/toolbox (default: `true`).
223
- - Additional key-value pairs: Properties to set on the tool (e.g., `config`, `precision`).
224
- - **Example**:
225
- ```yaml
226
- tools_config:
227
- - name: "math_tools"
228
- enabled: true
229
- config:
230
- precision: "high"
231
- - name: "agent_tool"
232
- enabled: false
233
- model: "custom_model"
234
- ```
235
-
236
- ---
237
-
238
- ### `reasoner`
239
- - **Description**: Configures the reasoning component, replacing `reasoner_name` with added flexibility.
240
- - **Type**: Dictionary (optional)
241
- - **Default**: `{"name": "default"}`
242
- - **Subfields**:
243
- - `name`: Name of the reasoner plugin (e.g., `"default"`).
244
- - `config`: Dictionary of reasoner-specific settings (e.g., `temperature`, `max_tokens`).
245
- - **Example**:
246
- ```yaml
247
- reasoner:
248
- name: "default"
249
- config:
250
- temperature: 0.7
251
- max_tokens: 1500
252
- ```
253
-
254
- ---
255
-
256
- ### `executor`
257
- - **Description**: Configures the execution component, replacing `executor_name`.
258
- - **Type**: Dictionary (optional)
259
- - **Default**: `{"name": "default"}`
260
- - **Subfields**:
261
- - `name`: Name of the executor plugin (e.g., `"default"`).
262
- - `config`: Dictionary of executor-specific settings (e.g., `timeout`).
263
- - **Example**:
264
- ```yaml
265
- executor:
266
- name: "default"
267
- config:
268
- timeout: 600
269
- ```
270
-
271
- ---
272
-
273
- ### `profile`
274
- - **Description**: Selects a predefined agent profile, setting defaults for other fields.
275
- - **Type**: String (optional)
276
- - **Default**: `null`
277
- - **Supported Profiles**:
278
- - `"math_expert"`: Configures for mathematical tasks.
279
- - `"creative_writer"`: Configures for creative tasks.
280
- - **Example**:
281
- ```yaml
282
- profile: "math_expert"
283
- ```
284
-
285
- ---
286
-
287
- ### `customizations`
288
- - **Description**: Overrides or extends profile defaults with custom settings.
289
- - **Type**: Dictionary (optional)
290
- - **Default**: `null`
291
- - **Example**:
292
- ```yaml
293
- profile: "math_expert"
294
- customizations:
295
- personality:
296
- traits:
297
- - "witty"
298
- tools_config:
299
- - name: "advanced_calculus"
300
- enabled: true
301
- ```
302
-
303
- ---
304
-
305
- ## Tool Configuration Details
306
- (unchanged from previous documentation, included for completeness)
307
-
308
- ---
309
-
310
- ## Backward Compatibility
311
-
312
- The updated configuration format remains fully compatible with older versions:
313
- - **New Field (`name`)**: Optional, defaults to `null`, so existing configs without `name` work unchanged.
314
- - **Legacy Fields**: `reasoner_name`, `executor_name`, `personality` (string), and `backstory` (string) are still supported.
315
- - **Minimal Configs**: A simple config like:
316
- ```yaml
317
- model: "gemini/gemini-2.0-flash"
318
- ```
319
- functions as before.
320
-
321
- ---
322
-
323
- ## Usage
324
-
325
- ### Example with Name
326
- ```yaml
327
- name: "MathWizard"
328
- model: "gemini/gemini-2.0-flash"
329
- max_iterations: 5
330
- profile: "math_expert"
331
- ```
332
-
333
- The agent will introduce itself as: "I am MathWizard, an AI assistant with the following personality traits: precise, logical."
334
-
335
- ---
336
-
337
- ## Best Practices
338
-
339
- - **Use `name`**: Assign a unique name to distinguish agents in multi-agent setups or logs.
340
- - **Profiles**: Leverage `profile` for quick setup, refining with `customizations`.
341
- - **Secrets**: Use `{{ env.VAR_NAME }}` for sensitive data in `tools_config`.
342
-
@@ -1,29 +0,0 @@
1
- # Model configuration
2
- model: "deepseek/deepseek-chat"
3
- # Task-solving parameters
4
- max_iterations: 5
5
- max_history_tokens: 2000
6
- # Toolbox and tool configuration
7
- toolbox_directory: "toolboxes"
8
- enabled_toolboxes:
9
- - math_tools
10
- # Component selection
11
- reasoner:
12
- name: "default"
13
- executor:
14
- name: "default"
15
- # Agent personality and behavior
16
- profile: "math_expert"
17
- customizations:
18
- personality:
19
- traits:
20
- - witty
21
- tools_config:
22
- - name: "math_tools"
23
- enabled: true
24
- config:
25
- precision: "high"
26
- sop: |
27
- Always provide clear, concise answers.
28
- Prioritize mathematical accuracy and user satisfaction.
29
- Inject humor where appropriate to keep interactions engaging.
@@ -1,186 +0,0 @@
1
- import asyncio
2
- import types
3
- from abc import ABC, abstractmethod
4
- from typing import Any, Callable, Dict, List, Optional # Added Optional, Any
5
-
6
- from lxml import etree
7
- from quantalogic_pythonbox import AsyncExecutionResult, execute_async
8
-
9
- from quantalogic.tools import Tool
10
-
11
- from .events import ToolExecutionCompletedEvent, ToolExecutionErrorEvent, ToolExecutionStartedEvent
12
- from .tools_manager import ToolRegistry
13
- from .utils import validate_code
14
- from .xml_utils import XMLResultHandler
15
-
16
-
17
- class BaseExecutor(ABC):
18
- """Abstract base class for execution components."""
19
-
20
- @abstractmethod
21
- async def execute_action(self, code: str, context_vars: Dict, step: int, timeout: int) -> str:
22
- pass
23
-
24
- @abstractmethod
25
- def register_tool(self, tool: Tool) -> None:
26
- pass
27
-
28
-
29
- class Executor(BaseExecutor):
30
- """Manages action execution and context updates with dynamic tool registration."""
31
-
32
- def __init__(self, tools: List[Tool], notify_event: Callable, config: Optional[Dict[str, Any]] = None, verbose: bool = True):
33
- self.registry = ToolRegistry()
34
- for tool in tools:
35
- self.registry.register(tool)
36
- self.tools: Dict[tuple[str, str], Tool] = self.registry.tools
37
- self.notify_event = notify_event
38
- self.config = config or {}
39
- self.verbose = verbose
40
- self.tool_namespace = self._build_tool_namespace()
41
-
42
- def _build_tool_namespace(self) -> Dict:
43
- """Build the namespace with tools grouped by toolbox using SimpleNamespace."""
44
- if not self.verbose:
45
- toolboxes = {}
46
- for (toolbox_name, tool_name), tool in self.tools.items():
47
- if toolbox_name not in toolboxes:
48
- toolboxes[toolbox_name] = types.SimpleNamespace()
49
- setattr(toolboxes[toolbox_name], tool_name, tool.async_execute)
50
- return {
51
- "asyncio": asyncio,
52
- "context_vars": {},
53
- **toolboxes,
54
- }
55
-
56
- def wrap_tool(tool):
57
- async def wrapped_tool(**kwargs):
58
- current_step = self.tool_namespace.get("current_step", None)
59
- parameters_summary = {
60
- k: str(v)[:100] + "..." if len(str(v)) > 100 else str(v) for k, v in kwargs.items()
61
- }
62
- await self.notify_event(
63
- ToolExecutionStartedEvent(
64
- event_type="ToolExecutionStarted",
65
- step_number=current_step,
66
- tool_name=f"{tool.toolbox_name or 'default'}.{tool.name}",
67
- parameters_summary=parameters_summary,
68
- )
69
- )
70
- try:
71
- result = await tool.async_execute(**kwargs)
72
- result_summary = str(result)[:100] + "..." if len(str(result)) > 100 else str(result)
73
- await self.notify_event(
74
- ToolExecutionCompletedEvent(
75
- event_type="ToolExecutionCompleted",
76
- step_number=current_step,
77
- tool_name=f"{tool.toolbox_name or 'default'}.{tool.name}",
78
- result_summary=result_summary,
79
- )
80
- )
81
- return result
82
- except Exception as e:
83
- await self.notify_event(
84
- ToolExecutionErrorEvent(
85
- event_type="ToolExecutionError",
86
- step_number=current_step,
87
- tool_name=f"{tool.toolbox_name or 'default'}.{tool.name}",
88
- error=str(e)
89
- )
90
- )
91
- raise
92
-
93
- return wrapped_tool
94
-
95
- toolboxes = {}
96
- for (toolbox_name, tool_name), tool in self.tools.items():
97
- if toolbox_name not in toolboxes:
98
- toolboxes[toolbox_name] = types.SimpleNamespace()
99
- setattr(toolboxes[toolbox_name], tool_name, wrap_tool(tool))
100
-
101
- return {
102
- "asyncio": asyncio,
103
- "context_vars": {},
104
- **toolboxes,
105
- }
106
-
107
- def register_tool(self, tool: Tool) -> None:
108
- """Register a new tool dynamically at runtime."""
109
- self.registry.register(tool)
110
- key = (tool.toolbox_name or "default", tool.name)
111
- self.tools[key] = tool
112
- toolbox_name = tool.toolbox_name or "default"
113
- if toolbox_name not in self.tool_namespace:
114
- self.tool_namespace[toolbox_name] = types.SimpleNamespace()
115
- setattr(self.tool_namespace[toolbox_name], tool.name,
116
- self._wrap_tool(tool) if self.verbose else tool.async_execute)
117
-
118
- def _wrap_tool(self, tool: Tool) -> Callable:
119
- """Wrap a tool function to handle execution events (internal use)."""
120
- async def wrapped_tool(**kwargs):
121
- current_step = self.tool_namespace.get("current_step", None)
122
- parameters_summary = {k: str(v)[:100] + "..." if len(str(v)) > 100 else str(v) for k, v in kwargs.items()}
123
- await self.notify_event(
124
- ToolExecutionStartedEvent(
125
- event_type="ToolExecutionStarted",
126
- step_number=current_step,
127
- tool_name=f"{tool.toolbox_name or 'default'}.{tool.name}",
128
- parameters_summary=parameters_summary,
129
- )
130
- )
131
- try:
132
- result = await tool.async_execute(**kwargs)
133
- result_summary = str(result)[:100] + "..." if len(str(result)) > 100 else str(result)
134
- await self.notify_event(
135
- ToolExecutionCompletedEvent(
136
- event_type="ToolExecutionCompleted",
137
- step_number=current_step,
138
- tool_name=f"{tool.toolbox_name or 'default'}.{tool.name}",
139
- result_summary=result_summary,
140
- )
141
- )
142
- return result
143
- except Exception as e:
144
- await self.notify_event(
145
- ToolExecutionErrorEvent(
146
- event_type="ToolExecutionError",
147
- step_number=current_step,
148
- tool_name=f"{tool.toolbox_name or 'default'}.{tool.name}",
149
- error=str(e)
150
- )
151
- )
152
- raise
153
-
154
- return wrapped_tool
155
-
156
- async def execute_action(self, code: str, context_vars: Dict, step: int, timeout: int = 300) -> str:
157
- """Execute the generated code and return the result with local variables, setting the step number."""
158
- self.tool_namespace["context_vars"] = context_vars
159
- self.tool_namespace["current_step"] = step
160
- timeout = self.config.get("timeout", timeout) # Use config timeout if provided
161
- if not validate_code(code):
162
- return etree.tostring(
163
- etree.Element("ExecutionResult", status="Error", message="Code lacks async main()"), encoding="unicode"
164
- )
165
-
166
- try:
167
- result: AsyncExecutionResult = await execute_async(
168
- code=code,
169
- timeout=timeout,
170
- entry_point="main",
171
- allowed_modules=["asyncio", "math", "random", "time"],
172
- namespace=self.tool_namespace,
173
- ignore_typing=True
174
- )
175
- if result.local_variables:
176
- context_vars.update(
177
- {k: v for k, v in result.local_variables.items() if not k.startswith("__") and not callable(v)}
178
- )
179
- return XMLResultHandler.format_execution_result(result)
180
- except Exception as e:
181
- root = etree.Element("ExecutionResult")
182
- root.append(etree.Element("Status", text="Error"))
183
- root.append(etree.Element("Value", text=f"Execution error: {str(e)}"))
184
- root.append(etree.Element("ExecutionTime", text="0.00 seconds"))
185
- root.append(etree.Element("Completed", text="false"))
186
- return etree.tostring(root, pretty_print=True, encoding="unicode")
@@ -1,94 +0,0 @@
1
- """History management module for storing and formatting agent steps."""
2
-
3
- from typing import Dict, List
4
-
5
- from loguru import logger
6
- from lxml import etree
7
-
8
- from .xml_utils import XMLResultHandler
9
-
10
-
11
- class HistoryManager:
12
- """Manages the storage and formatting of agent step history with persistent context."""
13
-
14
- def __init__(self, max_tokens: int = 64*1024, system_prompt: str = "", task_description: str = ""):
15
- """
16
- Initialize the HistoryManager with a token limit and persistent context.
17
-
18
- Args:
19
- max_tokens (int): Maximum number of tokens for history formatting (default: 16000).
20
- system_prompt (str): Persistent system-level instructions for the agent (default: "").
21
- task_description (str): Persistent description of the current task (default: "").
22
- """
23
- self.max_tokens: int = max_tokens
24
- self.system_prompt: str = system_prompt
25
- self.task_description: str = task_description
26
- self.store: List[Dict] = []
27
- logger.debug(f"Initialized HistoryManager with system_prompt: '{system_prompt}', task_description: '{task_description}'")
28
-
29
- def add_step(self, step_data: Dict) -> None:
30
- """
31
- Add a step to the history store.
32
-
33
- Args:
34
- step_data (Dict): Dictionary containing step details (step_number, thought, action, result).
35
- """
36
- self.store.append(step_data)
37
- logger.debug(f"Added step {step_data['step_number']} to history")
38
-
39
- def format_history(self, max_iterations: int) -> str:
40
- """
41
- Format the history with available variables, truncating to fit within max_tokens.
42
-
43
- Args:
44
- max_iterations (int): Maximum allowed iterations for context.
45
-
46
- Returns:
47
- str: Formatted string of previous steps, or "No previous steps" if empty.
48
- """
49
- included_steps: List[str] = []
50
- total_tokens: int = 0
51
- for step in reversed(self.store):
52
- try:
53
- root = etree.fromstring(step['result'])
54
- vars_elem = root.find("Variables")
55
- available_vars = (
56
- [var.get('name') for var in vars_elem.findall("Variable")]
57
- if vars_elem is not None else []
58
- )
59
- except etree.XMLSyntaxError:
60
- available_vars = []
61
-
62
- step_str: str = (
63
- f"===== Step {step['step_number']} of {max_iterations} max =====\n"
64
- f"Thought:\n{step['thought']}\n\n"
65
- f"Action:\n{step['action']}\n\n"
66
- f"Result:\n{XMLResultHandler.format_result_summary(step['result']) if step.get('result') else 'No result available'}\n"
67
- f"Available variables: {', '.join(available_vars) or 'None'}"
68
- )
69
- step_tokens: int = len(step_str.split())
70
- if total_tokens + step_tokens > self.max_tokens:
71
- break
72
- included_steps.append(step_str)
73
- total_tokens += step_tokens
74
- return "\n".join(reversed(included_steps)) or "No previous steps"
75
-
76
- def get_full_context(self, max_iterations: int) -> str:
77
- """
78
- Return the full context including system prompt, task description, and formatted history.
79
-
80
- Args:
81
- max_iterations (int): Maximum allowed iterations for history formatting.
82
-
83
- Returns:
84
- str: Combined string of system prompt, task description, and history.
85
- """
86
- context_parts = []
87
- if self.system_prompt:
88
- context_parts.append(f"System Prompt:\n{self.system_prompt}")
89
- if self.task_description:
90
- context_parts.append(f"Task Description:\n{self.task_description}")
91
- history_str = self.format_history(max_iterations)
92
- if history_str != "No previous steps":
93
- context_parts.append(f"History:\n{history_str}")
94
- return "\n\n".join(context_parts)