quantalogic 0.58.0__py3-none-any.whl → 0.59.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.
- quantalogic/agent.py +540 -123
- quantalogic/agent_factory.py +44 -40
- quantalogic/config.py +8 -7
- quantalogic/event_emitter.py +2 -2
- quantalogic/main.py +81 -5
- quantalogic/prompts/chat_system_prompt.j2 +54 -0
- quantalogic/task_runner.py +335 -178
- {quantalogic-0.58.0.dist-info → quantalogic-0.59.0.dist-info}/METADATA +124 -30
- {quantalogic-0.58.0.dist-info → quantalogic-0.59.0.dist-info}/RECORD +12 -11
- {quantalogic-0.58.0.dist-info → quantalogic-0.59.0.dist-info}/LICENSE +0 -0
- {quantalogic-0.58.0.dist-info → quantalogic-0.59.0.dist-info}/WHEEL +0 -0
- {quantalogic-0.58.0.dist-info → quantalogic-0.59.0.dist-info}/entry_points.txt +0 -0
quantalogic/agent_factory.py
CHANGED
@@ -4,6 +4,12 @@ from loguru import logger
|
|
4
4
|
|
5
5
|
from quantalogic.agent import Agent
|
6
6
|
from quantalogic.agent_config import (
|
7
|
+
DuckDuckGoSearchTool,
|
8
|
+
NodeJsTool,
|
9
|
+
PythonTool,
|
10
|
+
SearchDefinitionNames,
|
11
|
+
TaskCompleteTool,
|
12
|
+
WikipediaSearchTool,
|
7
13
|
create_basic_agent,
|
8
14
|
create_full_agent,
|
9
15
|
create_interpreter_agent,
|
@@ -26,38 +32,19 @@ class AgentRegistry:
|
|
26
32
|
|
27
33
|
@classmethod
|
28
34
|
def register_agent(cls, name: str, agent: Agent) -> None:
|
29
|
-
"""Register an agent instance with a name.
|
30
|
-
|
31
|
-
Args:
|
32
|
-
name: Unique name for the agent
|
33
|
-
agent: Agent instance to register
|
34
|
-
"""
|
35
|
+
"""Register an agent instance with a name."""
|
35
36
|
if name in cls._agents:
|
36
37
|
raise ValueError(f"Agent with name {name} already exists")
|
37
38
|
cls._agents[name] = agent
|
38
39
|
|
39
40
|
@classmethod
|
40
41
|
def get_agent(cls, name: str) -> Agent:
|
41
|
-
"""Retrieve a registered agent by name.
|
42
|
-
|
43
|
-
Args:
|
44
|
-
name: Name of the agent to retrieve
|
45
|
-
|
46
|
-
Returns:
|
47
|
-
Registered Agent instance
|
48
|
-
|
49
|
-
Raises:
|
50
|
-
KeyError: If no agent with that name exists
|
51
|
-
"""
|
42
|
+
"""Retrieve a registered agent by name."""
|
52
43
|
return cls._agents[name]
|
53
44
|
|
54
45
|
@classmethod
|
55
46
|
def list_agents(cls) -> Dict[str, str]:
|
56
|
-
"""List all registered agents.
|
57
|
-
|
58
|
-
Returns:
|
59
|
-
Dictionary mapping agent names to their types
|
60
|
-
"""
|
47
|
+
"""List all registered agents."""
|
61
48
|
return {name: type(agent).__name__ for name, agent in cls._agents.items()}
|
62
49
|
|
63
50
|
|
@@ -75,7 +62,9 @@ def create_agent_for_mode(
|
|
75
62
|
tools: Optional[List[Any]] = None,
|
76
63
|
event_emitter: Any = None,
|
77
64
|
specific_expertise: str = "",
|
78
|
-
memory: AgentMemory | None = None
|
65
|
+
memory: AgentMemory | None = None,
|
66
|
+
chat_system_prompt: Optional[str] = None,
|
67
|
+
tool_mode: Optional[str] = None,
|
79
68
|
) -> Agent:
|
80
69
|
"""Create an agent based on the specified mode.
|
81
70
|
|
@@ -91,6 +80,8 @@ def create_agent_for_mode(
|
|
91
80
|
event_emitter: Optional event emitter to use in the agent
|
92
81
|
specific_expertise: Optional specific expertise for the agent
|
93
82
|
memory: Optional AgentMemory instance to use in the agent
|
83
|
+
chat_system_prompt: Optional persona for chat mode
|
84
|
+
tool_mode: Optional tool or toolset to prioritize in chat mode
|
94
85
|
|
95
86
|
Returns:
|
96
87
|
Agent: The created agent instance
|
@@ -103,8 +94,33 @@ def create_agent_for_mode(
|
|
103
94
|
logger.debug(f"Using no_stream: {no_stream}")
|
104
95
|
logger.debug(f"Using compact_every_n_iteration: {compact_every_n_iteration}")
|
105
96
|
logger.debug(f"Using max_tokens_working_memory: {max_tokens_working_memory}")
|
106
|
-
|
107
|
-
|
97
|
+
logger.debug(f"Using tool_mode: {tool_mode}")
|
98
|
+
|
99
|
+
# Default tools if none provided
|
100
|
+
if tools is None:
|
101
|
+
tools = [TaskCompleteTool()]
|
102
|
+
|
103
|
+
if mode == "chat":
|
104
|
+
logger.debug(f"Creating chat agent with persona: {chat_system_prompt}")
|
105
|
+
# Customize tools based on tool_mode
|
106
|
+
if tool_mode:
|
107
|
+
if tool_mode == "search":
|
108
|
+
tools.extend([DuckDuckGoSearchTool(), WikipediaSearchTool()])
|
109
|
+
elif tool_mode == "code":
|
110
|
+
tools.extend([PythonTool(), NodeJsTool(), SearchDefinitionNames()])
|
111
|
+
elif tool_mode in [t.name for t in tools]: # Specific tool name
|
112
|
+
tools = [t for t in tools if t.name == tool_mode or isinstance(t, TaskCompleteTool)]
|
113
|
+
else:
|
114
|
+
logger.warning(f"Unknown tool mode '{tool_mode}', using default tools")
|
115
|
+
agent = Agent(
|
116
|
+
model_name=model_name,
|
117
|
+
memory=memory if memory else AgentMemory(),
|
118
|
+
tools=tools,
|
119
|
+
chat_system_prompt=chat_system_prompt,
|
120
|
+
tool_mode=tool_mode,
|
121
|
+
)
|
122
|
+
return agent
|
123
|
+
elif mode == "code":
|
108
124
|
logger.debug("Creating code agent without basic mode")
|
109
125
|
agent = create_coding_agent(
|
110
126
|
model_name,
|
@@ -116,7 +132,7 @@ def create_agent_for_mode(
|
|
116
132
|
max_tokens_working_memory=max_tokens_working_memory,
|
117
133
|
)
|
118
134
|
return agent
|
119
|
-
|
135
|
+
elif mode == "code-basic":
|
120
136
|
agent = create_coding_agent(
|
121
137
|
model_name,
|
122
138
|
vision_model_name,
|
@@ -161,7 +177,7 @@ def create_agent_for_mode(
|
|
161
177
|
max_tokens_working_memory=max_tokens_working_memory,
|
162
178
|
)
|
163
179
|
return agent
|
164
|
-
|
180
|
+
elif mode == "search-full":
|
165
181
|
agent = create_search_agent(
|
166
182
|
model_name,
|
167
183
|
mode_full=True,
|
@@ -170,17 +186,5 @@ def create_agent_for_mode(
|
|
170
186
|
max_tokens_working_memory=max_tokens_working_memory,
|
171
187
|
)
|
172
188
|
return agent
|
173
|
-
# if mode == "custom":
|
174
|
-
# agent = create_custom_agent(
|
175
|
-
# model_name,
|
176
|
-
# vision_model_name,
|
177
|
-
# no_stream=no_stream,
|
178
|
-
# compact_every_n_iteration=compact_every_n_iteration,
|
179
|
-
# max_tokens_working_memory=max_tokens_working_memory,
|
180
|
-
# specific_expertise=specific_expertise,
|
181
|
-
# tools=tools,
|
182
|
-
# memory=memory
|
183
|
-
# )
|
184
|
-
# return agent
|
185
189
|
else:
|
186
|
-
raise ValueError(f"Unknown agent mode: {mode}")
|
190
|
+
raise ValueError(f"Unknown agent mode: {mode}")
|
quantalogic/config.py
CHANGED
@@ -1,18 +1,19 @@
|
|
1
|
-
from dataclasses import dataclass
|
2
1
|
from typing import Optional
|
3
2
|
|
3
|
+
from pydantic import BaseModel
|
4
4
|
|
5
|
-
@dataclass
|
6
|
-
class QLConfig:
|
7
|
-
"""Central configuration for QuantaLogic agent parameters."""
|
8
5
|
|
6
|
+
class QLConfig(BaseModel):
|
9
7
|
model_name: str
|
10
8
|
verbose: bool
|
11
9
|
mode: str
|
12
10
|
log: str
|
13
|
-
vision_model_name: Optional[str]
|
11
|
+
vision_model_name: Optional[str] = None
|
14
12
|
max_iterations: int
|
15
|
-
compact_every_n_iteration: Optional[int]
|
16
|
-
max_tokens_working_memory: Optional[int]
|
13
|
+
compact_every_n_iteration: Optional[int] = None
|
14
|
+
max_tokens_working_memory: Optional[int] = None
|
17
15
|
no_stream: bool
|
18
16
|
thinking_model_name: str
|
17
|
+
chat_system_prompt: Optional[str] = None
|
18
|
+
tool_mode: Optional[str] = None # Added field for tool mode
|
19
|
+
auto_tool_call: bool = True # Default to True for automatic tool execution
|
quantalogic/event_emitter.py
CHANGED
@@ -329,9 +329,9 @@ class EventEmitter:
|
|
329
329
|
"""
|
330
330
|
with self._lock:
|
331
331
|
return {
|
332
|
-
"wildcard_listeners": [(
|
332
|
+
"wildcard_listeners": [(listener.__name__, p, m) for listener, p, m in self._wildcard_listeners],
|
333
333
|
"event_listeners": {
|
334
|
-
evt: [(
|
334
|
+
evt: [(listener.__name__, p, m) for listener, p, m in listeners] for evt, listeners in self._listeners.items()
|
335
335
|
},
|
336
336
|
}
|
337
337
|
|
quantalogic/main.py
CHANGED
@@ -16,7 +16,6 @@ from quantalogic.version import get_version
|
|
16
16
|
# Load environment variables from .env file
|
17
17
|
load_dotenv()
|
18
18
|
|
19
|
-
|
20
19
|
# Configure logger
|
21
20
|
logger.remove()
|
22
21
|
|
@@ -45,7 +44,7 @@ except ImportError as e:
|
|
45
44
|
termios = None
|
46
45
|
tty = None
|
47
46
|
|
48
|
-
AGENT_MODES = ["code", "basic", "interpreter", "full", "code-basic", "search", "search-full"]
|
47
|
+
AGENT_MODES = ["code", "basic", "interpreter", "full", "code-basic", "search", "search-full", "chat"] # Added "chat"
|
49
48
|
|
50
49
|
|
51
50
|
def setup_terminal():
|
@@ -104,7 +103,7 @@ def restore_terminal(old_settings):
|
|
104
103
|
help="Set logging level (info/debug/warning).",
|
105
104
|
)
|
106
105
|
@click.option("--verbose", is_flag=True, help="Enable verbose output.")
|
107
|
-
@click.option("--mode", type=click.Choice(AGENT_MODES), default="basic", help="Agent mode (code/search/full).")
|
106
|
+
@click.option("--mode", type=click.Choice(AGENT_MODES), default="basic", help="Agent mode (code/search/full/chat).")
|
108
107
|
@click.option(
|
109
108
|
"--vision-model-name",
|
110
109
|
default=None,
|
@@ -170,6 +169,7 @@ def cli(
|
|
170
169
|
max_tokens_working_memory=max_tokens_working_memory,
|
171
170
|
no_stream=False, # Default value for backward compatibility
|
172
171
|
thinking_model_name=thinking_model,
|
172
|
+
chat_system_prompt=None, # Default to None for non-chat modes
|
173
173
|
)
|
174
174
|
ctx.invoke(
|
175
175
|
task,
|
@@ -193,7 +193,7 @@ def cli(
|
|
193
193
|
help='Specify the model to use (litellm format, e.g. "openrouter/deepseek/deepseek-chat").',
|
194
194
|
)
|
195
195
|
@click.option("--verbose", is_flag=True, help="Enable verbose output.")
|
196
|
-
@click.option("--mode", type=click.Choice(AGENT_MODES), default="basic", help="Agent mode (code/search/full).")
|
196
|
+
@click.option("--mode", type=click.Choice(AGENT_MODES), default="basic", help="Agent mode (code/search/full/chat).")
|
197
197
|
@click.option(
|
198
198
|
"--log",
|
199
199
|
type=click.Choice(["info", "debug", "warning"]),
|
@@ -263,6 +263,7 @@ def task(
|
|
263
263
|
max_tokens_working_memory=max_tokens_working_memory,
|
264
264
|
no_stream=no_stream,
|
265
265
|
thinking_model_name=thinking_model,
|
266
|
+
chat_system_prompt=None # Default to None for task command
|
266
267
|
)
|
267
268
|
|
268
269
|
task_runner(
|
@@ -304,6 +305,81 @@ def list_models(search: Optional[str] = None):
|
|
304
305
|
console.print(f"- {model}")
|
305
306
|
|
306
307
|
|
308
|
+
@cli.command()
|
309
|
+
@click.option(
|
310
|
+
"--persona",
|
311
|
+
default="You are a friendly, helpful AI assistant.",
|
312
|
+
help="Specify the persona for chat mode (e.g., 'You are a witty pirate AI')."
|
313
|
+
)
|
314
|
+
@click.option(
|
315
|
+
"--model-name",
|
316
|
+
default=MODEL_NAME,
|
317
|
+
help='Specify the model to use (litellm format, e.g. "openai/gpt-4o-mini").',
|
318
|
+
)
|
319
|
+
@click.option(
|
320
|
+
"--log",
|
321
|
+
type=click.Choice(["info", "debug", "warning"]),
|
322
|
+
default="info",
|
323
|
+
help="Set logging level (info/debug/warning).",
|
324
|
+
)
|
325
|
+
@click.option("--verbose", is_flag=True, help="Enable verbose output.")
|
326
|
+
@click.option(
|
327
|
+
"--vision-model-name",
|
328
|
+
default=None,
|
329
|
+
help='Specify the vision model to use (litellm format, e.g. "openrouter/openai/gpt-4o-mini").',
|
330
|
+
)
|
331
|
+
@click.option(
|
332
|
+
"--no-stream",
|
333
|
+
is_flag=True,
|
334
|
+
help="Disable streaming output (default: streaming enabled).",
|
335
|
+
)
|
336
|
+
@click.option(
|
337
|
+
"--tool-mode",
|
338
|
+
type=str,
|
339
|
+
default=None,
|
340
|
+
help="Specify a tool or toolset to use in chat mode (e.g., 'search', 'code', or a specific tool name).",
|
341
|
+
)
|
342
|
+
@click.option(
|
343
|
+
"--auto-tool-call",
|
344
|
+
is_flag=True,
|
345
|
+
default=True,
|
346
|
+
help="Enable automatic tool execution and interpretation in chat mode (default: True).",
|
347
|
+
)
|
348
|
+
def chat(
|
349
|
+
persona: str,
|
350
|
+
model_name: str,
|
351
|
+
log: str,
|
352
|
+
verbose: bool,
|
353
|
+
vision_model_name: str | None,
|
354
|
+
no_stream: bool,
|
355
|
+
tool_mode: Optional[str],
|
356
|
+
auto_tool_call: bool,
|
357
|
+
) -> None:
|
358
|
+
"""Start a conversational chat with the QuantaLogic agent."""
|
359
|
+
console = Console()
|
360
|
+
config = QLConfig(
|
361
|
+
model_name=model_name,
|
362
|
+
verbose=verbose,
|
363
|
+
mode="chat",
|
364
|
+
log=log,
|
365
|
+
vision_model_name=vision_model_name,
|
366
|
+
max_iterations=1, # Chat mode doesn't need iterations
|
367
|
+
compact_every_n_iteration=None,
|
368
|
+
max_tokens_working_memory=None,
|
369
|
+
no_stream=no_stream,
|
370
|
+
thinking_model_name="default",
|
371
|
+
chat_system_prompt=persona,
|
372
|
+
tool_mode=tool_mode,
|
373
|
+
auto_tool_call=auto_tool_call,
|
374
|
+
)
|
375
|
+
try:
|
376
|
+
task_runner(console, None, config, None)
|
377
|
+
except Exception as e:
|
378
|
+
console.print(f"[red]Error in chat mode: {str(e)}[/red]")
|
379
|
+
logger.error(f"Error in chat execution: {e}", exc_info=True)
|
380
|
+
sys.exit(1)
|
381
|
+
|
382
|
+
|
307
383
|
def main():
|
308
384
|
"""Main entry point."""
|
309
385
|
old_settings = setup_terminal()
|
@@ -314,4 +390,4 @@ def main():
|
|
314
390
|
|
315
391
|
|
316
392
|
if __name__ == "__main__":
|
317
|
-
main()
|
393
|
+
main()
|
@@ -0,0 +1,54 @@
|
|
1
|
+
### Chat System Configuration
|
2
|
+
|
3
|
+
#### Agent Persona
|
4
|
+
{{ persona }}
|
5
|
+
|
6
|
+
You must reply to the user directly, without any additional information except if we must call a tool to execute a task before replying to the user.
|
7
|
+
|
8
|
+
#### Tool Usage Guidelines
|
9
|
+
|
10
|
+
When using tools, you MUST use the EXACT tool name as shown in the Tools list below. Each tool has specific required parameters.
|
11
|
+
|
12
|
+
Tools must be called using the following XML format, with NO additional text:
|
13
|
+
|
14
|
+
```xml
|
15
|
+
<action>
|
16
|
+
<tool_name> <!-- Use the EXACT tool name from the list below -->
|
17
|
+
<parameter_name>parameter_value</parameter_name> <!-- Include all required parameters -->
|
18
|
+
<another_parameter>value</another_parameter> <!-- Optional parameters if needed -->
|
19
|
+
</tool_name>
|
20
|
+
</action>
|
21
|
+
```
|
22
|
+
|
23
|
+
#### Examples of Correct Tool Usage
|
24
|
+
|
25
|
+
**Example 1 - Search Tool:**
|
26
|
+
```xml
|
27
|
+
<action>
|
28
|
+
<duckduckgo_tool>
|
29
|
+
<query>search query text</query>
|
30
|
+
<max_results>5</max_results> <!-- Always include max_results for search -->
|
31
|
+
</duckduckgo_tool>
|
32
|
+
</action>
|
33
|
+
```
|
34
|
+
|
35
|
+
**Example 2 - Task Completion Tool:**
|
36
|
+
```xml
|
37
|
+
<action>
|
38
|
+
<task_complete>
|
39
|
+
<answer>Your final answer to the task</answer>
|
40
|
+
</task_complete>
|
41
|
+
</action>
|
42
|
+
```
|
43
|
+
|
44
|
+
#### Operational Tools Available
|
45
|
+
|
46
|
+
{% if tools_prompt != "No tools available." %}
|
47
|
+
**Tools Available**:
|
48
|
+
{{ tools_prompt | indent(2) }}
|
49
|
+
{% else %}
|
50
|
+
**Tools Available**: None
|
51
|
+
{% endif %}
|
52
|
+
|
53
|
+
Don't invent tools that are not listed.
|
54
|
+
|