quantalogic 0.2.7__py3-none-any.whl → 0.2.10__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 +35 -2
- quantalogic/agent_config.py +101 -68
- quantalogic/coding_agent.py +6 -1
- quantalogic/generative_model.py +63 -12
- quantalogic/main.py +62 -18
- quantalogic/prompts.py +15 -21
- quantalogic/tools/__init__.py +2 -0
- quantalogic/tools/llm_tool.py +2 -2
- quantalogic/tools/llm_vision_tool.py +140 -0
- quantalogic/version.py +1 -1
- {quantalogic-0.2.7.dist-info → quantalogic-0.2.10.dist-info}/METADATA +58 -89
- {quantalogic-0.2.7.dist-info → quantalogic-0.2.10.dist-info}/RECORD +15 -14
- {quantalogic-0.2.7.dist-info → quantalogic-0.2.10.dist-info}/LICENSE +0 -0
- {quantalogic-0.2.7.dist-info → quantalogic-0.2.10.dist-info}/WHEEL +0 -0
- {quantalogic-0.2.7.dist-info → quantalogic-0.2.10.dist-info}/entry_points.txt +0 -0
quantalogic/agent.py
CHANGED
@@ -64,6 +64,7 @@ class Agent(BaseModel):
|
|
64
64
|
event_emitter: EventEmitter = EventEmitter()
|
65
65
|
config: AgentConfig
|
66
66
|
task_to_solve: str
|
67
|
+
task_to_solve_summary: str = ""
|
67
68
|
ask_for_user_validation: Callable[[str], bool] = console_ask_for_user_validation
|
68
69
|
last_tool_call: dict[str, Any] = {} # Stores the last tool call information
|
69
70
|
total_tokens: int = 0 # Total tokens in the conversation
|
@@ -137,6 +138,9 @@ class Agent(BaseModel):
|
|
137
138
|
logger.debug(f"Solving task... {task}")
|
138
139
|
self._reset_session(task_to_solve=task, max_iterations=max_iterations)
|
139
140
|
|
141
|
+
# Generate task summary
|
142
|
+
self.task_to_solve_summary = self._generate_task_summary(task)
|
143
|
+
|
140
144
|
# Add system prompt to memory
|
141
145
|
self.memory.add(Message(role="system", content=self.config.system_prompt))
|
142
146
|
|
@@ -166,7 +170,7 @@ class Agent(BaseModel):
|
|
166
170
|
self._update_total_tokens(message_history=self.memory.memory, prompt=current_prompt)
|
167
171
|
|
168
172
|
# Emit event: Task Think Start after updating total tokens
|
169
|
-
self._emit_event("task_think_start")
|
173
|
+
self._emit_event("task_think_start", {"prompt": current_prompt})
|
170
174
|
|
171
175
|
self._compact_memory_if_needed(current_prompt)
|
172
176
|
|
@@ -443,6 +447,10 @@ class Agent(BaseModel):
|
|
443
447
|
"You must analyze this answer and evaluate what to do next to solve the task.\n"
|
444
448
|
"If the step failed, take a step back and rethink your approach.\n"
|
445
449
|
"\n"
|
450
|
+
"--- Task to solve summary ---\n"
|
451
|
+
"\n"
|
452
|
+
f"{self.task_to_solve_summary}"
|
453
|
+
"\n"
|
446
454
|
"--- Format ---\n"
|
447
455
|
"\n"
|
448
456
|
"You MUST respond with exactly two XML blocks formatted in markdown:\n"
|
@@ -546,6 +554,7 @@ class Agent(BaseModel):
|
|
546
554
|
"\n### Tools:\n"
|
547
555
|
"-----------------------------\n"
|
548
556
|
f"{self._get_tools_names_prompt()}\n"
|
557
|
+
"\n"
|
549
558
|
"### Variables:\n"
|
550
559
|
"-----------------------------\n"
|
551
560
|
f"{self._get_variable_prompt()}\n"
|
@@ -574,7 +583,7 @@ class Agent(BaseModel):
|
|
574
583
|
"\n"
|
575
584
|
"Available variables:\n"
|
576
585
|
"\n"
|
577
|
-
f"{', '.join(self.variable_store.keys())}\n"
|
586
|
+
f"{', '.join(self.variable_store.keys())}\n" if len(self.variable_store.keys()) > 0 else "None\n"
|
578
587
|
)
|
579
588
|
return prompt_use_variables
|
580
589
|
|
@@ -619,6 +628,28 @@ class Agent(BaseModel):
|
|
619
628
|
self.memory.memory = memory_copy
|
620
629
|
return summary.response
|
621
630
|
|
631
|
+
def _generate_task_summary(self, content: str) -> str:
|
632
|
+
"""Generate a concise summary of the given content using the generative model.
|
633
|
+
|
634
|
+
Args:
|
635
|
+
content (str): The content to summarize
|
636
|
+
|
637
|
+
Returns:
|
638
|
+
str: Generated summary
|
639
|
+
"""
|
640
|
+
try:
|
641
|
+
prompt = (
|
642
|
+
"Rewrite this task in a precise, dense, and concise manner:\n"
|
643
|
+
f"{content}\n"
|
644
|
+
"Summary should be 2-3 sentences maximum. No extra comments should be added.\n"
|
645
|
+
)
|
646
|
+
result = self.model.generate(prompt=prompt)
|
647
|
+
logger.debug(f"Generated summary: {result.response}")
|
648
|
+
return result.response
|
649
|
+
except Exception as e:
|
650
|
+
logger.error(f"Error generating summary: {str(e)}")
|
651
|
+
return f"Summary generation failed: {str(e)}"
|
652
|
+
|
622
653
|
def _update_session_memory(self, user_content: str, assistant_content: str) -> None:
|
623
654
|
"""
|
624
655
|
Log session messages to memory and emit events.
|
@@ -639,3 +670,5 @@ class Agent(BaseModel):
|
|
639
670
|
"session_add_message",
|
640
671
|
{"role": "assistant", "content": assistant_content},
|
641
672
|
)
|
673
|
+
|
674
|
+
|
quantalogic/agent_config.py
CHANGED
@@ -13,6 +13,7 @@ from quantalogic.tools import (
|
|
13
13
|
InputQuestionTool,
|
14
14
|
ListDirectoryTool,
|
15
15
|
LLMTool,
|
16
|
+
LLMVisionTool,
|
16
17
|
MarkitdownTool,
|
17
18
|
NodeJsTool,
|
18
19
|
PythonTool,
|
@@ -28,109 +29,141 @@ from quantalogic.tools import (
|
|
28
29
|
MODEL_NAME = "deepseek/deepseek-chat"
|
29
30
|
|
30
31
|
|
31
|
-
def create_agent(model_name) -> Agent:
|
32
|
+
def create_agent(model_name: str, vision_model_name: str | None) -> Agent:
|
32
33
|
"""Create an agent with the specified model and tools.
|
33
34
|
|
34
35
|
Args:
|
35
36
|
model_name (str): Name of the model to use
|
37
|
+
vision_model_name (str | None): Name of the vision model to use
|
38
|
+
|
39
|
+
Returns:
|
40
|
+
Agent: An agent with the specified model and tools
|
36
41
|
"""
|
42
|
+
tools = [
|
43
|
+
TaskCompleteTool(),
|
44
|
+
ReadFileTool(),
|
45
|
+
ReadFileBlockTool(),
|
46
|
+
WriteFileTool(),
|
47
|
+
EditWholeContentTool(),
|
48
|
+
InputQuestionTool(),
|
49
|
+
ListDirectoryTool(),
|
50
|
+
ExecuteBashCommandTool(),
|
51
|
+
ReplaceInFileTool(),
|
52
|
+
RipgrepTool(),
|
53
|
+
SearchDefinitionNames(),
|
54
|
+
MarkitdownTool(),
|
55
|
+
LLMTool(model_name=model_name),
|
56
|
+
DownloadHttpFileTool(),
|
57
|
+
]
|
58
|
+
|
59
|
+
if vision_model_name:
|
60
|
+
tools.append(LLMVisionTool(model_name=vision_model_name))
|
61
|
+
|
37
62
|
return Agent(
|
38
63
|
model_name=model_name,
|
39
|
-
tools=
|
40
|
-
TaskCompleteTool(),
|
41
|
-
ReadFileTool(),
|
42
|
-
ReadFileBlockTool(),
|
43
|
-
WriteFileTool(),
|
44
|
-
EditWholeContentTool(),
|
45
|
-
InputQuestionTool(),
|
46
|
-
ListDirectoryTool(),
|
47
|
-
ExecuteBashCommandTool(),
|
48
|
-
ReplaceInFileTool(),
|
49
|
-
RipgrepTool(),
|
50
|
-
SearchDefinitionNames(),
|
51
|
-
MarkitdownTool(),
|
52
|
-
LLMTool(model_name=model_name),
|
53
|
-
DownloadHttpFileTool(),
|
54
|
-
],
|
64
|
+
tools=tools,
|
55
65
|
)
|
56
66
|
|
57
67
|
|
58
|
-
def create_interpreter_agent(model_name: str) -> Agent:
|
68
|
+
def create_interpreter_agent(model_name: str, vision_model_name: str | None) -> Agent:
|
59
69
|
"""Create an interpreter agent with the specified model and tools.
|
60
70
|
|
61
71
|
Args:
|
62
72
|
model_name (str): Name of the model to use
|
63
|
-
|
64
|
-
return Agent(
|
65
|
-
model_name=model_name,
|
66
|
-
tools=[
|
67
|
-
TaskCompleteTool(),
|
68
|
-
ReadFileTool(),
|
69
|
-
ReadFileBlockTool(),
|
70
|
-
WriteFileTool(),
|
71
|
-
EditWholeContentTool(),
|
72
|
-
InputQuestionTool(),
|
73
|
-
ListDirectoryTool(),
|
74
|
-
ExecuteBashCommandTool(),
|
75
|
-
ReplaceInFileTool(),
|
76
|
-
RipgrepTool(),
|
77
|
-
PythonTool(),
|
78
|
-
NodeJsTool(),
|
79
|
-
SearchDefinitionNames(),
|
80
|
-
DownloadHttpFileTool(),
|
81
|
-
],
|
82
|
-
)
|
83
|
-
|
73
|
+
vision_model_name (str | None): Name of the vision model to use
|
84
74
|
|
85
|
-
|
75
|
+
Returns:
|
76
|
+
Agent: An interpreter agent with the specified model and tools
|
77
|
+
"""
|
78
|
+
tools = [
|
79
|
+
TaskCompleteTool(),
|
80
|
+
ReadFileTool(),
|
81
|
+
ReadFileBlockTool(),
|
82
|
+
WriteFileTool(),
|
83
|
+
EditWholeContentTool(),
|
84
|
+
InputQuestionTool(),
|
85
|
+
ListDirectoryTool(),
|
86
|
+
ExecuteBashCommandTool(),
|
87
|
+
ReplaceInFileTool(),
|
88
|
+
RipgrepTool(),
|
89
|
+
PythonTool(),
|
90
|
+
NodeJsTool(),
|
91
|
+
SearchDefinitionNames(),
|
92
|
+
MarkitdownTool(),
|
93
|
+
LLMTool(model_name=model_name),
|
94
|
+
DownloadHttpFileTool(),
|
95
|
+
]
|
96
|
+
return Agent(model_name=model_name, tools=tools)
|
97
|
+
|
98
|
+
|
99
|
+
def create_full_agent(model_name: str, vision_model_name: str | None) -> Agent:
|
86
100
|
"""Create an agent with the specified model and many tools.
|
87
101
|
|
88
102
|
Args:
|
89
103
|
model_name (str): Name of the model to use
|
104
|
+
vision_model_name (str | None): Name of the vision model to use
|
105
|
+
|
106
|
+
Returns:
|
107
|
+
Agent: An agent with the specified model and tools
|
108
|
+
|
90
109
|
"""
|
110
|
+
tools=[
|
111
|
+
TaskCompleteTool(),
|
112
|
+
ReadFileTool(),
|
113
|
+
ReadFileBlockTool(),
|
114
|
+
WriteFileTool(),
|
115
|
+
EditWholeContentTool(),
|
116
|
+
InputQuestionTool(),
|
117
|
+
ListDirectoryTool(),
|
118
|
+
ExecuteBashCommandTool(),
|
119
|
+
ReplaceInFileTool(),
|
120
|
+
RipgrepTool(),
|
121
|
+
PythonTool(),
|
122
|
+
NodeJsTool(),
|
123
|
+
SearchDefinitionNames(),
|
124
|
+
MarkitdownTool(),
|
125
|
+
LLMTool(model_name=model_name),
|
126
|
+
DownloadHttpFileTool(),
|
127
|
+
]
|
128
|
+
|
129
|
+
if vision_model_name:
|
130
|
+
tools.append(LLMVisionTool(model_name=vision_model_name))
|
131
|
+
|
91
132
|
return Agent(
|
92
133
|
model_name=model_name,
|
93
|
-
tools=
|
94
|
-
TaskCompleteTool(),
|
95
|
-
ReadFileTool(),
|
96
|
-
ReadFileBlockTool(),
|
97
|
-
WriteFileTool(),
|
98
|
-
EditWholeContentTool(),
|
99
|
-
InputQuestionTool(),
|
100
|
-
ListDirectoryTool(),
|
101
|
-
ExecuteBashCommandTool(),
|
102
|
-
ReplaceInFileTool(),
|
103
|
-
RipgrepTool(),
|
104
|
-
PythonTool(),
|
105
|
-
NodeJsTool(),
|
106
|
-
SearchDefinitionNames(),
|
107
|
-
MarkitdownTool(),
|
108
|
-
LLMTool(model_name=model_name),
|
109
|
-
DownloadHttpFileTool(),
|
110
|
-
],
|
134
|
+
tools=tools,
|
111
135
|
)
|
112
136
|
|
113
137
|
|
114
|
-
def create_orchestrator_agent(model_name: str) -> Agent:
|
138
|
+
def create_orchestrator_agent(model_name: str, vision_model_name: str | None = None) -> Agent:
|
115
139
|
"""Create an agent with the specified model and tools.
|
116
140
|
|
117
141
|
Args:
|
118
142
|
model_name (str): Name of the model to use
|
143
|
+
vision_model_name (str | None): Name of the vision model to use
|
144
|
+
|
145
|
+
Returns:
|
146
|
+
Agent: An agent with the specified model and tools
|
119
147
|
"""
|
120
148
|
# Rebuild AgentTool to resolve forward references
|
121
149
|
AgentTool.model_rebuild()
|
122
150
|
|
123
151
|
coding_agent_instance = create_coding_agent(model_name)
|
124
152
|
|
153
|
+
tools = [
|
154
|
+
TaskCompleteTool(),
|
155
|
+
ListDirectoryTool(),
|
156
|
+
ReadFileBlockTool(),
|
157
|
+
RipgrepTool(),
|
158
|
+
SearchDefinitionNames(),
|
159
|
+
LLMTool(model_name=MODEL_NAME),
|
160
|
+
AgentTool(agent=coding_agent_instance, agent_role="software expert", name="coder_agent_tool"),
|
161
|
+
]
|
162
|
+
|
163
|
+
if vision_model_name:
|
164
|
+
tools.append(LLMVisionTool(model_name=vision_model_name))
|
165
|
+
|
125
166
|
return Agent(
|
126
167
|
model_name=model_name,
|
127
|
-
tools=
|
128
|
-
TaskCompleteTool(),
|
129
|
-
ListDirectoryTool(),
|
130
|
-
ReadFileBlockTool(),
|
131
|
-
RipgrepTool(),
|
132
|
-
SearchDefinitionNames(),
|
133
|
-
LLMTool(model_name=MODEL_NAME),
|
134
|
-
AgentTool(agent=coding_agent_instance, agent_role="software expert", name="coder_agent_tool"),
|
135
|
-
],
|
168
|
+
tools=tools,
|
136
169
|
)
|
quantalogic/coding_agent.py
CHANGED
@@ -5,6 +5,7 @@ from quantalogic.tools import (
|
|
5
5
|
InputQuestionTool,
|
6
6
|
ListDirectoryTool,
|
7
7
|
LLMTool,
|
8
|
+
LLMVisionTool,
|
8
9
|
ReadFileBlockTool,
|
9
10
|
ReadFileTool,
|
10
11
|
ReplaceInFileTool,
|
@@ -17,11 +18,12 @@ from quantalogic.utils import get_coding_environment
|
|
17
18
|
from quantalogic.utils.get_quantalogic_rules_content import get_quantalogic_rules_file_content
|
18
19
|
|
19
20
|
|
20
|
-
def create_coding_agent(model_name: str, basic: bool = False) -> Agent:
|
21
|
+
def create_coding_agent(model_name: str,vision_model_name: str | None = None, basic: bool = False) -> Agent:
|
21
22
|
"""Creates and configures a coding agent with a comprehensive set of tools.
|
22
23
|
|
23
24
|
Args:
|
24
25
|
model_name (str): Name of the language model to use for the agent's core capabilities
|
26
|
+
vision_model_name (str | None): Name of the vision model to use for the agent's core capabilities
|
25
27
|
basic (bool, optional): If True, the agent will be configured with a basic set of tools.
|
26
28
|
|
27
29
|
Returns:
|
@@ -59,6 +61,9 @@ def create_coding_agent(model_name: str, basic: bool = False) -> Agent:
|
|
59
61
|
InputQuestionTool(),
|
60
62
|
]
|
61
63
|
|
64
|
+
if vision_model_name:
|
65
|
+
tools.append(LLMVisionTool(model_name=vision_model_name))
|
66
|
+
|
62
67
|
if not basic:
|
63
68
|
tools.append(
|
64
69
|
LLMTool(
|
quantalogic/generative_model.py
CHANGED
@@ -5,16 +5,17 @@ from litellm import completion, exceptions, get_max_tokens, get_model_info, toke
|
|
5
5
|
from loguru import logger
|
6
6
|
from pydantic import BaseModel, Field, field_validator
|
7
7
|
|
8
|
-
MIN_RETRIES =
|
8
|
+
MIN_RETRIES = 1
|
9
9
|
|
10
10
|
|
11
11
|
class Message(BaseModel):
|
12
12
|
"""Represents a message in a conversation with a specific role and content."""
|
13
13
|
|
14
14
|
role: str = Field(..., min_length=1)
|
15
|
-
content: str = Field(..., min_length=1)
|
15
|
+
content: str | dict = Field(..., min_length=1)
|
16
|
+
image_url: str | None = Field(default=None, pattern=r"^https?://")
|
16
17
|
|
17
|
-
@field_validator("role"
|
18
|
+
@field_validator("role")
|
18
19
|
@classmethod
|
19
20
|
def validate_not_empty(cls, v: str) -> str:
|
20
21
|
"""Validate that the field is not empty or whitespace-only."""
|
@@ -22,6 +23,26 @@ class Message(BaseModel):
|
|
22
23
|
raise ValueError("Field cannot be empty or whitespace-only")
|
23
24
|
return v
|
24
25
|
|
26
|
+
@field_validator("content")
|
27
|
+
@classmethod
|
28
|
+
def validate_content(cls, v: str | dict) -> str | dict:
|
29
|
+
"""Validate content based on its type."""
|
30
|
+
if isinstance(v, str):
|
31
|
+
if not v or not v.strip():
|
32
|
+
raise ValueError("Text content cannot be empty or whitespace-only")
|
33
|
+
elif isinstance(v, dict):
|
34
|
+
if not v.get("text") or not v.get("image_url"):
|
35
|
+
raise ValueError("Multimodal content must have both text and image_url")
|
36
|
+
return v
|
37
|
+
|
38
|
+
@field_validator("image_url")
|
39
|
+
@classmethod
|
40
|
+
def validate_image_url(cls, v: str | None) -> str | None:
|
41
|
+
"""Validate image URL format if present."""
|
42
|
+
if v and not v.startswith(("http://", "https://")):
|
43
|
+
raise ValueError("Image URL must start with http:// or https://")
|
44
|
+
return v
|
45
|
+
|
25
46
|
|
26
47
|
class TokenUsage(BaseModel):
|
27
48
|
"""Represents token usage statistics for a language model."""
|
@@ -59,6 +80,7 @@ class GenerativeModel:
|
|
59
80
|
temperature: Sampling temperature between 0 and 1.
|
60
81
|
Defaults to 0.7.
|
61
82
|
"""
|
83
|
+
logger.debug(f"Initializing GenerativeModel with model={model}, temperature={temperature}")
|
62
84
|
self.model = model
|
63
85
|
self.temperature = temperature
|
64
86
|
|
@@ -85,15 +107,16 @@ class GenerativeModel:
|
|
85
107
|
)
|
86
108
|
|
87
109
|
# Retry on specific retriable exceptions
|
88
|
-
def generate_with_history(self, messages_history: list[Message], prompt: str) -> ResponseStats:
|
89
|
-
"""Generate a response with conversation history.
|
110
|
+
def generate_with_history(self, messages_history: list[Message], prompt: str, image_url: str | None = None) -> ResponseStats:
|
111
|
+
"""Generate a response with conversation history and optional image.
|
90
112
|
|
91
|
-
Generates a response based on previous conversation messages
|
92
|
-
|
113
|
+
Generates a response based on previous conversation messages,
|
114
|
+
a new user prompt, and an optional image URL.
|
93
115
|
|
94
116
|
Args:
|
95
117
|
messages_history: Previous conversation messages.
|
96
118
|
prompt: Current user prompt.
|
119
|
+
image_url: Optional image URL for visual queries.
|
97
120
|
|
98
121
|
Returns:
|
99
122
|
Detailed response statistics.
|
@@ -105,7 +128,22 @@ class GenerativeModel:
|
|
105
128
|
Exception: For other unexpected errors.
|
106
129
|
"""
|
107
130
|
messages = [{"role": msg.role, "content": str(msg.content)} for msg in messages_history]
|
108
|
-
|
131
|
+
|
132
|
+
if image_url:
|
133
|
+
messages.append({
|
134
|
+
"role": "user",
|
135
|
+
"content": [
|
136
|
+
{"type": "text", "text": str(prompt)},
|
137
|
+
{
|
138
|
+
"type": "image_url",
|
139
|
+
"image_url": {
|
140
|
+
"url": image_url
|
141
|
+
}
|
142
|
+
}
|
143
|
+
]
|
144
|
+
})
|
145
|
+
else:
|
146
|
+
messages.append({"role": "user", "content": str(prompt)})
|
109
147
|
|
110
148
|
try:
|
111
149
|
logger.debug(f"Generating response for prompt: {prompt}")
|
@@ -140,9 +178,12 @@ class GenerativeModel:
|
|
140
178
|
}
|
141
179
|
|
142
180
|
logger.error("LLM Generation Error: {}", error_details)
|
181
|
+
logger.debug(f"Error details: {error_details}")
|
182
|
+
logger.debug(f"Model: {self.model}, Temperature: {self.temperature}")
|
143
183
|
|
144
184
|
# Handle authentication and permission errors
|
145
185
|
if isinstance(e, self.AUTH_EXCEPTIONS):
|
186
|
+
logger.debug("Authentication error occurred")
|
146
187
|
raise openai.AuthenticationError(
|
147
188
|
f"Authentication failed with provider {error_details['provider']}"
|
148
189
|
) from e
|
@@ -162,7 +203,7 @@ class GenerativeModel:
|
|
162
203
|
# Wrap unknown errors in APIError
|
163
204
|
raise openai.APIError(f"Unexpected error during generation: {str(e)}") from e
|
164
205
|
|
165
|
-
def generate(self, prompt: str) -> ResponseStats:
|
206
|
+
def generate(self, prompt: str, image_url: str | None = None) -> ResponseStats:
|
166
207
|
"""Generate a response without conversation history.
|
167
208
|
|
168
209
|
Generates a response for a single user prompt without
|
@@ -170,11 +211,12 @@ class GenerativeModel:
|
|
170
211
|
|
171
212
|
Args:
|
172
213
|
prompt: User prompt.
|
214
|
+
image_url: Optional image URL for visual queries.
|
173
215
|
|
174
216
|
Returns:
|
175
217
|
Detailed response statistics.
|
176
218
|
"""
|
177
|
-
return self.generate_with_history([], prompt)
|
219
|
+
return self.generate_with_history([], prompt, image_url)
|
178
220
|
|
179
221
|
def get_max_tokens(self) -> int:
|
180
222
|
"""Get the maximum number of tokens that can be generated by the model."""
|
@@ -182,8 +224,11 @@ class GenerativeModel:
|
|
182
224
|
|
183
225
|
def token_counter(self, messages: list[Message]) -> int:
|
184
226
|
"""Count the number of tokens in a list of messages."""
|
227
|
+
logger.debug(f"Counting tokens for {len(messages)} messages using model {self.model}")
|
185
228
|
litellm_messages = [{"role": msg.role, "content": str(msg.content)} for msg in messages]
|
186
|
-
|
229
|
+
token_count = token_counter(model=self.model, messages=litellm_messages)
|
230
|
+
logger.debug(f"Token count: {token_count}")
|
231
|
+
return token_count
|
187
232
|
|
188
233
|
def token_counter_with_history(self, messages_history: list[Message], prompt: str) -> int:
|
189
234
|
"""Count the number of tokens in a list of messages and a prompt."""
|
@@ -193,12 +238,18 @@ class GenerativeModel:
|
|
193
238
|
|
194
239
|
def get_model_info(self) -> dict | None:
|
195
240
|
"""Get information about the model."""
|
241
|
+
logger.debug(f"Retrieving model info for {self.model}")
|
196
242
|
model_info = get_model_info(self.model)
|
197
243
|
|
198
244
|
if not model_info:
|
199
|
-
|
245
|
+
logger.debug("Model info not found, trying without openrouter/ prefix")
|
200
246
|
model_info = get_model_info(self.model.replace("openrouter/", ""))
|
201
247
|
|
248
|
+
if model_info:
|
249
|
+
logger.debug(f"Model info retrieved: {model_info.keys()}")
|
250
|
+
else:
|
251
|
+
logger.debug("No model info available")
|
252
|
+
|
202
253
|
return model_info
|
203
254
|
|
204
255
|
def get_model_max_input_tokens(self) -> int:
|
quantalogic/main.py
CHANGED
@@ -33,20 +33,20 @@ from quantalogic.version import get_version # noqa: E402
|
|
33
33
|
AGENT_MODES = ["code", "basic", "interpreter", "full", "code-basic"]
|
34
34
|
|
35
35
|
|
36
|
-
def create_agent_for_mode(mode: str, model_name: str) -> Agent:
|
36
|
+
def create_agent_for_mode(mode: str, model_name: str, vision_model_name: str | None) -> Agent:
|
37
37
|
"""Create an agent based on the specified mode."""
|
38
38
|
logger.debug(f"Creating agent for mode: {mode} with model: {model_name}")
|
39
39
|
if mode == "code":
|
40
40
|
logger.debug("Creating code agent without basic mode")
|
41
|
-
return create_coding_agent(model_name, basic=False)
|
41
|
+
return create_coding_agent(model_name, vision_model_name, basic=False)
|
42
42
|
if mode == "code-basic":
|
43
|
-
return create_coding_agent(model_name, basic=True)
|
43
|
+
return create_coding_agent(model_name, vision_model_name, basic=True)
|
44
44
|
elif mode == "basic":
|
45
|
-
return create_orchestrator_agent(model_name)
|
45
|
+
return create_orchestrator_agent(model_name, vision_model_name)
|
46
46
|
elif mode == "full":
|
47
|
-
return create_full_agent(model_name)
|
47
|
+
return create_full_agent(model_name, vision_model_name)
|
48
48
|
elif mode == "interpreter":
|
49
|
-
return create_interpreter_agent(model_name)
|
49
|
+
return create_interpreter_agent(model_name, vision_model_name)
|
50
50
|
else:
|
51
51
|
raise ValueError(f"Unknown agent mode: {mode}")
|
52
52
|
|
@@ -54,13 +54,18 @@ def create_agent_for_mode(mode: str, model_name: str) -> Agent:
|
|
54
54
|
def configure_logger(log_level: str) -> None:
|
55
55
|
"""Configure the logger with the specified log level and format."""
|
56
56
|
logger.remove()
|
57
|
-
logger.add(
|
57
|
+
logger.add(
|
58
|
+
sys.stderr,
|
59
|
+
level=log_level.upper(),
|
60
|
+
format="<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green> | <level>{level: <8}</level> | <cyan>{process}</cyan> | <magenta>{file}:{line}</magenta> | {message}",
|
61
|
+
)
|
58
62
|
logger.info(f"Log level set to: {log_level}")
|
59
63
|
|
60
64
|
|
61
65
|
def set_litellm_verbose(verbose_mode: bool) -> None:
|
62
66
|
"""Set the verbosity of the litellm library."""
|
63
67
|
import litellm
|
68
|
+
|
64
69
|
litellm.set_verbose = verbose_mode
|
65
70
|
|
66
71
|
|
@@ -87,7 +92,7 @@ def get_task_from_file(file_path: str) -> str:
|
|
87
92
|
raise Exception(f"Unexpected error reading file: {e}")
|
88
93
|
|
89
94
|
|
90
|
-
def display_welcome_message(console: Console, model_name: str) -> None:
|
95
|
+
def display_welcome_message(console: Console, model_name: str, vision_model_name: str | None) -> None:
|
91
96
|
"""Display the welcome message and instructions."""
|
92
97
|
version = get_version()
|
93
98
|
console.print(
|
@@ -97,9 +102,10 @@ def display_welcome_message(console: Console, model_name: str) -> None:
|
|
97
102
|
"1. [bold]Describe your task[/bold]: Tell the AI what you need help with.\n"
|
98
103
|
"2. [bold]Submit your task[/bold]: Press [bold]Enter[/bold] twice to send your request.\n\n"
|
99
104
|
"3. [bold]Exit the app[/bold]: Leave the input blank and press [bold]Enter[/bold] twice to close the assistant.\n\n"
|
100
|
-
f"[yellow]
|
101
|
-
|
102
|
-
f"- Model: {model_name}\n
|
105
|
+
f"[yellow] 🤖 System Info:[/yellow]\n\n"
|
106
|
+
"\n"
|
107
|
+
f"- Model: {model_name}\n"
|
108
|
+
f"- Vision Model: {vision_model_name}\n\n"
|
103
109
|
"[bold magenta]💡 Pro Tips:[/bold magenta]\n\n"
|
104
110
|
"- Be as specific as possible in your task description to get the best results!\n"
|
105
111
|
"- Use clear and concise language when describing your task\n"
|
@@ -118,18 +124,38 @@ def display_welcome_message(console: Console, model_name: str) -> None:
|
|
118
124
|
default=MODEL_NAME,
|
119
125
|
help='Specify the model to use (litellm format, e.g. "openrouter/deepseek-chat").',
|
120
126
|
)
|
121
|
-
@click.option(
|
127
|
+
@click.option(
|
128
|
+
"--log",
|
129
|
+
type=click.Choice(["info", "debug", "warning"]),
|
130
|
+
default="info",
|
131
|
+
help="Set logging level (info/debug/warning).",
|
132
|
+
)
|
122
133
|
@click.option("--verbose", is_flag=True, help="Enable verbose output.")
|
123
134
|
@click.option("--mode", type=click.Choice(AGENT_MODES), default="code", help="Agent mode (code/search/full).")
|
135
|
+
@click.option(
|
136
|
+
"--vision-model-name",
|
137
|
+
default=None,
|
138
|
+
help='Specify the vision model to use (litellm format, e.g. "openrouter/A/gpt-4o-mini").',
|
139
|
+
)
|
124
140
|
@click.pass_context
|
125
|
-
def cli(
|
141
|
+
def cli(
|
142
|
+
ctx: click.Context,
|
143
|
+
version: bool,
|
144
|
+
model_name: str,
|
145
|
+
verbose: bool,
|
146
|
+
mode: str,
|
147
|
+
log: str,
|
148
|
+
vision_model_name: str | None,
|
149
|
+
) -> None:
|
126
150
|
"""QuantaLogic AI Assistant - A powerful AI tool for various tasks."""
|
127
151
|
if version:
|
128
152
|
console = Console()
|
129
153
|
console.print(f"QuantaLogic version: {get_version()}")
|
130
154
|
sys.exit(0)
|
131
155
|
if ctx.invoked_subcommand is None:
|
132
|
-
ctx.invoke(
|
156
|
+
ctx.invoke(
|
157
|
+
task, model_name=model_name, verbose=verbose, mode=mode, log=log, vision_model_name=vision_model_name
|
158
|
+
)
|
133
159
|
|
134
160
|
|
135
161
|
@cli.command()
|
@@ -141,9 +167,27 @@ def cli(ctx: click.Context, version: bool, model_name: str, verbose: bool, mode:
|
|
141
167
|
)
|
142
168
|
@click.option("--verbose", is_flag=True, help="Enable verbose output.")
|
143
169
|
@click.option("--mode", type=click.Choice(AGENT_MODES), default="code", help="Agent mode (code/search/full).")
|
144
|
-
@click.option(
|
170
|
+
@click.option(
|
171
|
+
"--log",
|
172
|
+
type=click.Choice(["info", "debug", "warning"]),
|
173
|
+
default="info",
|
174
|
+
help="Set logging level (info/debug/warning).",
|
175
|
+
)
|
176
|
+
@click.option(
|
177
|
+
"--vision-model-name",
|
178
|
+
default=None,
|
179
|
+
help='Specify the vision model to use (litellm format, e.g. "openrouter/openai/gpt-4o-mini").',
|
180
|
+
)
|
145
181
|
@click.argument("task", required=False)
|
146
|
-
def task(
|
182
|
+
def task(
|
183
|
+
file: Optional[str],
|
184
|
+
model_name: str,
|
185
|
+
verbose: bool,
|
186
|
+
mode: str,
|
187
|
+
log: str,
|
188
|
+
vision_model_name: str | None,
|
189
|
+
task: Optional[str],
|
190
|
+
) -> None:
|
147
191
|
"""Execute a task with the QuantaLogic AI Assistant."""
|
148
192
|
console = Console()
|
149
193
|
switch_verbose(verbose, log)
|
@@ -155,7 +199,7 @@ def task(file: Optional[str], model_name: str, verbose: bool, mode: str, log: st
|
|
155
199
|
if task:
|
156
200
|
task_content = task
|
157
201
|
else:
|
158
|
-
display_welcome_message(console, model_name)
|
202
|
+
display_welcome_message(console, model_name, vision_model_name)
|
159
203
|
logger.info("Waiting for user input...")
|
160
204
|
task_content = get_multiline_input(console).strip()
|
161
205
|
logger.info(f"User input received. Task content: {task_content}")
|
@@ -177,7 +221,7 @@ def task(file: Optional[str], model_name: str, verbose: bool, mode: str, log: st
|
|
177
221
|
sys.exit(0)
|
178
222
|
|
179
223
|
logger.debug(f"Creating agent for mode: {mode} with model: {model_name}")
|
180
|
-
agent = create_agent_for_mode(mode, model_name)
|
224
|
+
agent = create_agent_for_mode(mode, model_name, vision_model_name=vision_model_name)
|
181
225
|
logger.debug(f"Created agent for mode: {mode} with model: {model_name}")
|
182
226
|
|
183
227
|
events = [
|
quantalogic/prompts.py
CHANGED
@@ -14,17 +14,17 @@ Tasks will be presented within XML tags:
|
|
14
14
|
### Response Protocol
|
15
15
|
Every response must contain exactly two XML blocks:
|
16
16
|
|
17
|
-
Be very concise and precise in the <thinking> block
|
18
|
-
|
19
17
|
1. Analysis Block:
|
20
18
|
```xml
|
21
19
|
<thinking>
|
20
|
+
<!-- You must follow this precise format, be very concise and very precise -->
|
22
21
|
<task_analysis_if_no_history>
|
23
22
|
Only if no conversation history:
|
24
|
-
*
|
23
|
+
* Rewrite the <task> and its context with your own words in detailed, clear, and specific manner.
|
25
24
|
* If not previously defined, clarify detailed criteria for task completion.
|
26
25
|
* Identify key components, constraints, and potential challenges.
|
27
|
-
* Break
|
26
|
+
* Decompose into Sub-Tasks: Break the <task> down into smaller, manageable sub-tasks if necessary.
|
27
|
+
* Each sub-task should have a clear objective, specific deliverables, and be sequenced logically to facilitate step-by-step progress tracking.
|
28
28
|
</task_analysis_if_no_history>
|
29
29
|
<success_criteria_if_no_history>
|
30
30
|
If no conversation history:
|
@@ -50,20 +50,24 @@ Be very concise and precise in the <thinking> block
|
|
50
50
|
</result>
|
51
51
|
</last_observation>
|
52
52
|
<progess_analysis>
|
53
|
+
<!-- if there is a conversation history -->
|
53
54
|
* Detail each step failed and completed so far.
|
54
55
|
* Identify and evaluate any blockers or challenges to the progress of global task.
|
56
|
+
* Identify repetitions: if repeated steps, take a step back and rethink your approach.
|
55
57
|
* Provide potential solutions, and if needed, suggest reevaluating the approach and the plan.
|
56
58
|
</progess_analysis>
|
57
59
|
<variables>
|
60
|
+
<!-- if there is a conversation history -->
|
58
61
|
* List all variable names and concisely describe their current values.
|
59
62
|
</variables>
|
60
63
|
<next_steps>
|
61
64
|
* Outline immediate actions required.
|
62
65
|
* Justify tool selection and parameter choices.
|
63
|
-
*
|
64
|
-
* Consider alternatives if previous attempts were unsuccessful.
|
66
|
+
* Prefer variable interpolation if possible, to minimize generation of tokens.
|
67
|
+
* Consider alternatives, take a step back if previous attempts were unsuccessful to review the plan.
|
65
68
|
</next_steps>
|
66
69
|
<taskpad>
|
70
|
+
<!-- optional -->
|
67
71
|
<note>Use this to record notes about intermediate steps.</note>
|
68
72
|
</taskpad>
|
69
73
|
</thinking>
|
@@ -72,25 +76,15 @@ Be very concise and precise in the <thinking> block
|
|
72
76
|
2. Action Block:
|
73
77
|
```xml
|
74
78
|
<tool_name>
|
75
|
-
|
79
|
+
<!-- tool_name is the name of the tool from available tools -->
|
80
|
+
<parameter1>
|
81
|
+
<!-- Use variable interpolation to pass context to minimize generation of tokens, example: <content>$var1$<</content> -->
|
82
|
+
value1
|
83
|
+
</parameter1>
|
76
84
|
<parameter2>value2</parameter2>
|
77
85
|
</tool_name>
|
78
86
|
```
|
79
87
|
|
80
|
-
### Tool Usage Guidelines
|
81
|
-
1. Before Repeating a Tool Call:
|
82
|
-
- Review previous results in detail.
|
83
|
-
- State why a repeat is needed.
|
84
|
-
- Adjust parameters if necessary.
|
85
|
-
- Consider whether other tools are more appropriate.
|
86
|
-
- Use variable interpolation to pass context to minimize generation of tokens, example: <toolname>$var1$<</toolname>
|
87
|
-
|
88
|
-
2. When Tool Calls Fail:
|
89
|
-
- Examine the error message carefully.
|
90
|
-
- Adjust parameters if needed.
|
91
|
-
- Consider alternative tools.
|
92
|
-
- Break down complex processes into smaller steps if necessary.
|
93
|
-
|
94
88
|
### Available Tools
|
95
89
|
{tools}
|
96
90
|
|
quantalogic/tools/__init__.py
CHANGED
@@ -8,6 +8,7 @@ from .execute_bash_command_tool import ExecuteBashCommandTool
|
|
8
8
|
from .input_question_tool import InputQuestionTool
|
9
9
|
from .list_directory_tool import ListDirectoryTool
|
10
10
|
from .llm_tool import LLMTool
|
11
|
+
from .llm_vision_tool import LLMVisionTool
|
11
12
|
from .markitdown_tool import MarkitdownTool
|
12
13
|
from .nodejs_tool import NodeJsTool
|
13
14
|
from .python_tool import PythonTool
|
@@ -30,6 +31,7 @@ __all__ = [
|
|
30
31
|
"InputQuestionTool",
|
31
32
|
"ListDirectoryTool",
|
32
33
|
"LLMTool",
|
34
|
+
"LLMVisionTool",
|
33
35
|
"ExecuteBashCommandTool",
|
34
36
|
"PythonTool",
|
35
37
|
"ElixirTool",
|
quantalogic/tools/llm_tool.py
CHANGED
@@ -17,11 +17,11 @@ class LLMTool(Tool):
|
|
17
17
|
description: str = Field(
|
18
18
|
default=(
|
19
19
|
"Generates answers to questions using a specified language model. "
|
20
|
-
"Note: This tool operates in isolation and does not have access to: "
|
20
|
+
"Note: This tool operates in total isolation and does not have access to: "
|
21
21
|
" - Memory: All context must be explicitly provided in the prompt. "
|
22
22
|
" - File system."
|
23
23
|
" - Variables: Any required variables should be interpolated into the prompt (e.g., $var1$). "
|
24
|
-
" -
|
24
|
+
" - No access to Tools, URL, file, or other external resources. "
|
25
25
|
"Ensure all necessary information is included directly in your prompt."
|
26
26
|
)
|
27
27
|
)
|
@@ -0,0 +1,140 @@
|
|
1
|
+
"""LLM Vision Tool for analyzing images using a language model."""
|
2
|
+
|
3
|
+
import logging
|
4
|
+
from typing import Optional
|
5
|
+
|
6
|
+
from pydantic import ConfigDict, Field
|
7
|
+
|
8
|
+
from quantalogic.generative_model import GenerativeModel, Message
|
9
|
+
from quantalogic.tools.tool import Tool, ToolArgument
|
10
|
+
|
11
|
+
#DEFAULT_MODEL_NAME = "ollama/llama3.2-vision"
|
12
|
+
DEFAULT_MODEL_NAME = "openrouter/openai/gpt-4o-mini"
|
13
|
+
|
14
|
+
|
15
|
+
class LLMVisionTool(Tool):
|
16
|
+
"""Tool to analyze images using a specified language model."""
|
17
|
+
|
18
|
+
model_config = ConfigDict(arbitrary_types_allowed=True, extra="allow")
|
19
|
+
|
20
|
+
name: str = Field(default="llm_vision_tool")
|
21
|
+
description: str = Field(
|
22
|
+
default=(
|
23
|
+
"Analyzes images and generates responses using a specified language model. "
|
24
|
+
"Supports multimodal input combining text and images."
|
25
|
+
)
|
26
|
+
)
|
27
|
+
arguments: list = Field(
|
28
|
+
default=[
|
29
|
+
ToolArgument(
|
30
|
+
name="system_prompt",
|
31
|
+
arg_type="string",
|
32
|
+
description="The system prompt to guide the model's behavior",
|
33
|
+
required=True,
|
34
|
+
example="You are an expert in image analysis and visual understanding.",
|
35
|
+
),
|
36
|
+
ToolArgument(
|
37
|
+
name="prompt",
|
38
|
+
arg_type="string",
|
39
|
+
description="The question or instruction about the image",
|
40
|
+
required=True,
|
41
|
+
example="What is shown in this image?",
|
42
|
+
),
|
43
|
+
ToolArgument(
|
44
|
+
name="image_url",
|
45
|
+
arg_type="string",
|
46
|
+
description="URL of the image to analyze",
|
47
|
+
required=True,
|
48
|
+
example="https://example.com/image.jpg",
|
49
|
+
),
|
50
|
+
ToolArgument(
|
51
|
+
name="temperature",
|
52
|
+
arg_type="string",
|
53
|
+
description='Sampling temperature between "0.0" and "1.0"',
|
54
|
+
required=True,
|
55
|
+
default="0.7",
|
56
|
+
example="0.7",
|
57
|
+
),
|
58
|
+
]
|
59
|
+
)
|
60
|
+
|
61
|
+
model_name: str = Field(..., description="The name of the language model to use")
|
62
|
+
generative_model: Optional[GenerativeModel] = Field(default=None)
|
63
|
+
|
64
|
+
def model_post_init(self, __context):
|
65
|
+
"""Initialize the generative model after model initialization."""
|
66
|
+
if self.generative_model is None:
|
67
|
+
self.generative_model = GenerativeModel(model=self.model_name)
|
68
|
+
logging.debug(f"Initialized LLMVisionTool with model: {self.model_name}")
|
69
|
+
|
70
|
+
def execute(
|
71
|
+
self,
|
72
|
+
system_prompt: str,
|
73
|
+
prompt: str,
|
74
|
+
image_url: str,
|
75
|
+
temperature: str = "0.7"
|
76
|
+
) -> str:
|
77
|
+
"""Execute the tool to analyze an image and generate a response.
|
78
|
+
|
79
|
+
Args:
|
80
|
+
system_prompt: The system prompt to guide the model
|
81
|
+
prompt: The question or instruction about the image
|
82
|
+
image_url: URL of the image to analyze
|
83
|
+
temperature: Sampling temperature
|
84
|
+
|
85
|
+
Returns:
|
86
|
+
The generated response
|
87
|
+
|
88
|
+
Raises:
|
89
|
+
ValueError: If temperature is invalid or image_url is malformed
|
90
|
+
Exception: If there's an error during response generation
|
91
|
+
"""
|
92
|
+
try:
|
93
|
+
temp = float(temperature)
|
94
|
+
if not (0.0 <= temp <= 1.0):
|
95
|
+
raise ValueError("Temperature must be between 0 and 1.")
|
96
|
+
except ValueError as ve:
|
97
|
+
logging.error(f"Invalid temperature value: {temperature}")
|
98
|
+
raise ValueError(f"Invalid temperature value: {temperature}") from ve
|
99
|
+
|
100
|
+
if not image_url.startswith(("http://", "https://")):
|
101
|
+
raise ValueError("Image URL must start with http:// or https://")
|
102
|
+
|
103
|
+
# Prepare the messages history
|
104
|
+
messages_history = [
|
105
|
+
Message(role="system", content=system_prompt),
|
106
|
+
]
|
107
|
+
|
108
|
+
if self.generative_model is None:
|
109
|
+
self.generative_model = GenerativeModel(model=self.model_name)
|
110
|
+
|
111
|
+
self.generative_model.temperature = temp
|
112
|
+
|
113
|
+
try:
|
114
|
+
response_stats = self.generative_model.generate_with_history(
|
115
|
+
messages_history=messages_history,
|
116
|
+
prompt=prompt,
|
117
|
+
image_url=image_url
|
118
|
+
)
|
119
|
+
response = response_stats.response.strip()
|
120
|
+
logging.info(f"Generated response: {response}")
|
121
|
+
return response
|
122
|
+
except Exception as e:
|
123
|
+
logging.error(f"Error generating response: {e}")
|
124
|
+
raise Exception(f"Error generating response: {e}") from e
|
125
|
+
|
126
|
+
|
127
|
+
if __name__ == "__main__":
|
128
|
+
# Example usage
|
129
|
+
tool = LLMVisionTool(model_name=DEFAULT_MODEL_NAME)
|
130
|
+
system_prompt = "You are a vision expert."
|
131
|
+
question = "What is shown in this image? Describe it with details."
|
132
|
+
image_url = "https://fastly.picsum.photos/id/767/200/300.jpg?hmac=j5YA1cRw-jS6fK3Mx2ooPwl2_TS3RSyLmFmiM9TqLC4"
|
133
|
+
temperature = "0.7"
|
134
|
+
answer = tool.execute(
|
135
|
+
system_prompt=system_prompt,
|
136
|
+
prompt=question,
|
137
|
+
image_url=image_url,
|
138
|
+
temperature=temperature
|
139
|
+
)
|
140
|
+
print(answer)
|
quantalogic/version.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: quantalogic
|
3
|
-
Version: 0.2.
|
3
|
+
Version: 0.2.10
|
4
4
|
Summary: QuantaLogic ReAct Agents
|
5
5
|
Author: Raphaël MANSUY
|
6
6
|
Author-email: raphael.mansuy@gmail.com
|
@@ -117,15 +117,25 @@ pipx install quantalogic
|
|
117
117
|
The QuantaLogic CLI provides powerful command-line capabilities:
|
118
118
|
|
119
119
|
```bash
|
120
|
-
quantalogic [OPTIONS] COMMAND [ARGS]...
|
121
|
-
|
120
|
+
Usage: quantalogic [OPTIONS] COMMAND [ARGS]...
|
121
|
+
|
122
|
+
QuantaLogic AI Assistant - A powerful AI tool for various tasks.
|
122
123
|
|
123
|
-
|
124
|
-
--
|
125
|
-
--
|
126
|
-
|
127
|
-
--
|
128
|
-
|
124
|
+
Options:
|
125
|
+
--version Show version information.
|
126
|
+
--model-name TEXT Specify the text model to use (litellm format,
|
127
|
+
e.g. "openrouter/deepseek-chat").
|
128
|
+
--vision-model-name TEXT Specify the vision model to use (litellm format,
|
129
|
+
e.g. "openrouter/A/gpt-4o-mini").
|
130
|
+
--log [info|debug|warning] Set logging level (info/debug/warning).
|
131
|
+
--verbose Enable verbose output.
|
132
|
+
--mode [code|basic|interpreter|full|code-basic]
|
133
|
+
Agent mode (code/search/full).
|
134
|
+
--help Show this message and exit.
|
135
|
+
|
136
|
+
Commands:
|
137
|
+
task Execute a task with the QuantaLogic AI Assistant.
|
138
|
+
```
|
129
139
|
|
130
140
|
### Commands
|
131
141
|
task Execute a task with the QuantaLogic AI Assistant
|
@@ -140,33 +150,25 @@ task Execute a task with the QuantaLogic AI Assistant
|
|
140
150
|
- code-basic: Coding agent with basic reasoning
|
141
151
|
|
142
152
|
#### Task Execution
|
153
|
+
|
143
154
|
Tasks can be provided:
|
144
|
-
|
155
|
+
|
156
|
+
1. Directly via `task` parameter
|
145
157
|
2. Through a file using --file parameter
|
146
158
|
3. Interactively via standard input
|
147
159
|
|
148
|
-
#### Error Handling
|
149
|
-
The CLI provides comprehensive error handling:
|
150
|
-
- File not found errors
|
151
|
-
- Permission errors
|
152
|
-
- Model configuration errors
|
153
|
-
- Task validation errors
|
154
160
|
|
155
161
|
#### Examples
|
156
162
|
|
157
|
-
Basic task execution:
|
158
|
-
```bash
|
159
|
-
quantalogic --task "Write a Python function to calculate Fibonacci numbers"
|
160
|
-
```
|
161
163
|
|
162
164
|
Using a task file:
|
163
165
|
```bash
|
164
|
-
quantalogic --file tasks/example.md --verbose
|
166
|
+
quantalogic task --file tasks/example.md --verbose
|
165
167
|
```
|
166
168
|
|
167
169
|
Selecting agent mode:
|
168
170
|
```bash
|
169
|
-
quantalogic --mode interpreter
|
171
|
+
quantalogic --mode interpreter task "Explain quantum computing"
|
170
172
|
```
|
171
173
|
|
172
174
|
Interactive mode:
|
@@ -174,22 +176,7 @@ Interactive mode:
|
|
174
176
|
quantalogic
|
175
177
|
```
|
176
178
|
|
177
|
-
###
|
178
|
-
|
179
|
-
Basic task execution:
|
180
|
-
```bash
|
181
|
-
quantalogic --task "Write a Python function to calculate Fibonacci numbers"
|
182
|
-
```
|
183
|
-
|
184
|
-
Using a task file:
|
185
|
-
```bash
|
186
|
-
quantalogic --file tasks/example.md --verbose
|
187
|
-
```
|
188
|
-
|
189
|
-
Selecting agent mode:
|
190
|
-
```bash
|
191
|
-
quantalogic --mode interpreter --task "Explain quantum computing"
|
192
|
-
```
|
179
|
+
### Using QuantaLogic With code
|
193
180
|
|
194
181
|
```python
|
195
182
|
from quantalogic import Agent
|
@@ -223,32 +210,6 @@ result = agent.solve_task("Create a Python function that calculates the Fibonacc
|
|
223
210
|
print(result)
|
224
211
|
```
|
225
212
|
|
226
|
-
### Command Line Interface
|
227
|
-
|
228
|
-
```bash
|
229
|
-
# Simple task execution
|
230
|
-
quantalogic --task "Write a FastAPI endpoint that handles file uploads"
|
231
|
-
```
|
232
|
-
|
233
|
-
|
234
|
-
`instruction.md`
|
235
|
-
|
236
|
-
```md
|
237
|
-
Write a poem
|
238
|
-
Translate the poem in English
|
239
|
-
Choose 2 French Authors
|
240
|
-
And then rewrite the translation with the style of each author
|
241
|
-
```
|
242
|
-
|
243
|
-
`
|
244
|
-
```bash
|
245
|
-
# Advanced usage with options
|
246
|
-
quantalogic \
|
247
|
-
--model openrouter/deepseek-chat \
|
248
|
-
--execute-file instruction.md \
|
249
|
-
--verbose
|
250
|
-
```
|
251
|
-
|
252
213
|
## 📖 Examples
|
253
214
|
|
254
215
|
Here are some practical examples to help you get started:
|
@@ -426,7 +387,7 @@ By integrating these tools into its architecture, QuantaLogic allows agents to p
|
|
426
387
|
| Script Execution | Python Tool, Node.js Tool, Elixir Tool |
|
427
388
|
| File Operations | Read File Tool, Write File Tool, Edit Whole Content Tool, Replace In File Tool |
|
428
389
|
| Code Analysis | Search Definition Names Tool, Ripgrep Tool |
|
429
|
-
| Content Generation | LLM Tool
|
390
|
+
| Content Generation | LLM Tool, LLMVisionTool |
|
430
391
|
| Utility and Management | Download HTTP File Tool, List Directory Tool, Markitdown Tool, Unified Diff Tool |
|
431
392
|
|
432
393
|
---
|
@@ -722,7 +683,30 @@ print("Ripgrep Results:", output)
|
|
722
683
|
|
723
684
|
---
|
724
685
|
|
725
|
-
|
686
|
+
#### 14. LLMVisionTool
|
687
|
+
|
688
|
+
The **LLMVisionTool** enables processing of visual inputs using vision-language models.
|
689
|
+
|
690
|
+
##### Parameters
|
691
|
+
|
692
|
+
| Parameter | Type | Description | Example |
|
693
|
+
|----------------|---------|------------------------------------------------------------------------|--------------------------------------------|
|
694
|
+
| `image_path` | string | Path to the image file to process | `./path/to/image.png` |
|
695
|
+
| `prompt` | string | The question or instruction for the vision model | `Describe the contents of this image` |
|
696
|
+
| `temperature` | float | Sampling temperature between 0.0 and 1.0 | `0.7` |
|
697
|
+
|
698
|
+
##### Example Usage
|
699
|
+
```python
|
700
|
+
vision_tool = LLMVisionTool()
|
701
|
+
response = vision_tool.execute(
|
702
|
+
image_path="./path/to/image.png",
|
703
|
+
prompt="Describe the contents of this image",
|
704
|
+
temperature=0.7
|
705
|
+
)
|
706
|
+
print("Vision Model Response:", response)
|
707
|
+
```
|
708
|
+
|
709
|
+
#### 15. LLM Tool
|
726
710
|
|
727
711
|
The **LLM Tool** generates answers using a specified language model.
|
728
712
|
|
@@ -747,7 +731,7 @@ print("LLM Response:", response)
|
|
747
731
|
|
748
732
|
---
|
749
733
|
|
750
|
-
###
|
734
|
+
### 16. Download HTTP File Tool
|
751
735
|
|
752
736
|
The **Download HTTP File Tool** downloads a file from a specified HTTP URL.
|
753
737
|
|
@@ -767,7 +751,7 @@ print(result)
|
|
767
751
|
|
768
752
|
---
|
769
753
|
|
770
|
-
###
|
754
|
+
### 17. List Directory Tool
|
771
755
|
|
772
756
|
The **List Directory Tool** lists files in a specified directory.
|
773
757
|
|
@@ -786,7 +770,7 @@ print("Directory Files:", result)
|
|
786
770
|
|
787
771
|
---
|
788
772
|
|
789
|
-
###
|
773
|
+
### 18. Markitdown Tool
|
790
774
|
|
791
775
|
The **Markitdown Tool** processes markdown files, possibly for conversion or rendering.
|
792
776
|
|
@@ -803,26 +787,6 @@ result = markitdown_tool.execute(markdown_path="./path/to/file.md")
|
|
803
787
|
print("Processed Markdown Output:", result)
|
804
788
|
```
|
805
789
|
|
806
|
-
---
|
807
|
-
|
808
|
-
### 18. Unified Diff Tool
|
809
|
-
|
810
|
-
The **Unified Diff Tool** generates a unified diff between two texts or files.
|
811
|
-
|
812
|
-
#### Parameters
|
813
|
-
|
814
|
-
| Parameter | Type | Description | Example |
|
815
|
-
|--------------|--------|------------------------------------------------|------------------------|
|
816
|
-
| `original` | string | The original content or file path. | `old_text.txt` |
|
817
|
-
| `updated` | string | The updated content or file path. | `new_text.txt` |
|
818
|
-
|
819
|
-
#### Example Usage
|
820
|
-
```python
|
821
|
-
diff_tool = UnifiedDiffTool()
|
822
|
-
result = diff_tool.execute(original="old_text.txt", updated="new_text.txt")
|
823
|
-
print("Unified Diff Output:", result)
|
824
|
-
```
|
825
|
-
|
826
790
|
|
827
791
|
#### Creating Custom Tools
|
828
792
|
|
@@ -1048,3 +1012,8 @@ Copyright 2024 QuantaLogic Contributors
|
|
1048
1012
|
|
1049
1013
|
Licensed under the Apache License, Version 2.0. See [LICENSE](LICENSE) for details.
|
1050
1014
|
|
1015
|
+
## Project Growth
|
1016
|
+
[](https://star-history.com/#quantalogic/quantalogic&Date)
|
1017
|
+
|
1018
|
+
Initiated with ❤️ by Raphaël MANSUY. Founder of [Quantalogic](https://www.quantalogic.app).
|
1019
|
+
|
@@ -1,15 +1,15 @@
|
|
1
1
|
quantalogic/__init__.py,sha256=HFk7_19UzHzYwvPzb9QTQ4w_lPwTTPda61AYb8qggZY,686
|
2
|
-
quantalogic/agent.py,sha256=
|
3
|
-
quantalogic/agent_config.py,sha256=
|
4
|
-
quantalogic/coding_agent.py,sha256=
|
2
|
+
quantalogic/agent.py,sha256=s6kmgH2XLhtg1ZBl1SckV7ayFXkR_acs305Z2l1O2Bg,26680
|
3
|
+
quantalogic/agent_config.py,sha256=Gm-i84dQF7CC1NUEtXpdw-qgZh5Jp1q4ni_SxMIxm-U,4518
|
4
|
+
quantalogic/coding_agent.py,sha256=qT24jneNLUH1zMQUWBBTYDIVjCLPNm1iCaXB0v3w9yc,3469
|
5
5
|
quantalogic/event_emitter.py,sha256=jqot2g4JRXc88K6PW837Oqxbf7shZfO-xdPaUWmzupk,7901
|
6
|
-
quantalogic/generative_model.py,sha256=
|
6
|
+
quantalogic/generative_model.py,sha256=N5FjE0kMpKKdpP9_QEOZJ0rkOpYPaJWG91jzEyyovXA,10725
|
7
7
|
quantalogic/interactive_text_editor.py,sha256=kYeTA2qej5kxtPvAUHy_Dr2MhrGQAyenLFpW9mU9Rmw,6855
|
8
|
-
quantalogic/main.py,sha256=
|
8
|
+
quantalogic/main.py,sha256=0zsr8-BnDPaR3roagL-3RX71hf4N0PqcgnCnJDO-gII,9626
|
9
9
|
quantalogic/memory.py,sha256=zbtRuM05jaS2lJll-92dt5JfYVLERnF_m_9xqp2x-k0,6304
|
10
10
|
quantalogic/model_names.py,sha256=UZlz25zG9B2dpfwdw_e1Gw5qFsKQ7iME9FJh9Ts4u6s,938
|
11
11
|
quantalogic/print_event.py,sha256=-4qZmFI2BTkXuGE9DoKm6Vs-GzK1F9WJGt9GqpRQlQQ,2175
|
12
|
-
quantalogic/prompts.py,sha256=
|
12
|
+
quantalogic/prompts.py,sha256=BHIST57DYcTeTb7rvV1QkGLt0_B8Wk8a_9tsnsN6suk,3547
|
13
13
|
quantalogic/server/__init__.py,sha256=8sz_PYAUCrkM6JM5EAUeIzNM4NPW6j6UT72JVkc21WQ,91
|
14
14
|
quantalogic/server/agent_server.py,sha256=GmglYf-LeVQQOdikMFDvPq1R0wKt6wIBpufW8XzP-iE,22489
|
15
15
|
quantalogic/server/models.py,sha256=nVUGWElOsUw8QnRCGJylk25wCew_5gohe6nldYighUA,1322
|
@@ -19,7 +19,7 @@ quantalogic/server/static/js/event_visualizer.js,sha256=eFkkWyNZw3zOZlF18kxbfsWq
|
|
19
19
|
quantalogic/server/static/js/quantalogic.js,sha256=x7TrlZGR1Y0WLK2DWl1xY847BhEWMPnL0Ua7KtOldUc,22311
|
20
20
|
quantalogic/server/templates/index.html,sha256=nDnXJoQEm1vXbhXtgaYk0G5VXj0wwzE6KrqEDhHFpj4,7773
|
21
21
|
quantalogic/tool_manager.py,sha256=FyghX2M_yGmdL7ovJR4ZGYIiBwkxA-bPjpI-y4IFx4Y,2421
|
22
|
-
quantalogic/tools/__init__.py,sha256=
|
22
|
+
quantalogic/tools/__init__.py,sha256=OxZp1nWZC5EewMJJVEvP6Fd2RMFlpaIYLHqChXPG_6s,1495
|
23
23
|
quantalogic/tools/agent_tool.py,sha256=qeRp74EBqPSGu6JNZMATGyDoSCzPo7EnB2rmCP5wsBE,3050
|
24
24
|
quantalogic/tools/download_http_file_tool.py,sha256=wTfanbXjIRi5-qrbluuLvNmDNhvmYAnlMVb3dO8C2ss,2210
|
25
25
|
quantalogic/tools/edit_whole_content_tool.py,sha256=nXmpAvojvqvAcqNMy1kUKZ1ocboky_ZcnCR4SNCSPgw,2360
|
@@ -37,7 +37,8 @@ quantalogic/tools/language_handlers/rust_handler.py,sha256=t_AqKVa3KVk6SVkq_UjUU
|
|
37
37
|
quantalogic/tools/language_handlers/scala_handler.py,sha256=wr-cWOIFOc0UYwODmEtT6rV63Qf1NyNB_BLo23GLrvk,1281
|
38
38
|
quantalogic/tools/language_handlers/typescript_handler.py,sha256=L4vuJMYxKO3_83dQhdwZ9fogauIV7rwoicRT0xLGfkQ,1738
|
39
39
|
quantalogic/tools/list_directory_tool.py,sha256=8Hy38DelSh-mRqS_uDLpeBYoHLtEy5ji77xI-TJu3Ms,4176
|
40
|
-
quantalogic/tools/llm_tool.py,sha256=
|
40
|
+
quantalogic/tools/llm_tool.py,sha256=ktwsg1LI3rSPS5XCwp-GAFiYCaXPSXRzUaqjOexPfEg,5011
|
41
|
+
quantalogic/tools/llm_vision_tool.py,sha256=S99pKpIi8WUPREXXPArsuA7iTdXzUtiXg32J9NKCvlc,5028
|
41
42
|
quantalogic/tools/markitdown_tool.py,sha256=GHJMPdWmwF-CBu3vHWhy-kXJYRDluFkh18KN06yNHOc,4101
|
42
43
|
quantalogic/tools/nodejs_tool.py,sha256=2VTkZgtyXmv2E18CVaml3CKZE28WL9Tbv2IVdziv8wA,19903
|
43
44
|
quantalogic/tools/python_tool.py,sha256=t66ge3xXS55-wJkddnVU9210TuDVoRI0Y-rsZwWxYIk,18154
|
@@ -59,11 +60,11 @@ quantalogic/utils/get_quantalogic_rules_content.py,sha256=fnEFTyClXzpI0MLaM-gB9R
|
|
59
60
|
quantalogic/utils/git_ls.py,sha256=_aXg2TwqYv9CoOrhQ1gqHCqu1j8wOVigQNWbGncSDlM,4361
|
60
61
|
quantalogic/utils/read_file.py,sha256=tSRVHk8dIP4nNLL89v5kRki4hOTjVyjbmuEb2zwvwCY,2077
|
61
62
|
quantalogic/utils/read_http_text_content.py,sha256=1nRLQ9DHP_fKrm0rIEJBF0ROmB78e4lct2hUzD2PAUk,4408
|
62
|
-
quantalogic/version.py,sha256=
|
63
|
+
quantalogic/version.py,sha256=rbj6GCYHf5rUvES_yzIMSiI_7I2h-zkrmZOjre8NI7A,65
|
63
64
|
quantalogic/xml_parser.py,sha256=cTRorr5sVfkIzH72M0C-GQ9ROGPiz2FTT66U9ndjzhE,9538
|
64
65
|
quantalogic/xml_tool_parser.py,sha256=lsVzClZBrZan7wjCuCKnGHWzksXI3VMy_vWthxu2_bo,3738
|
65
|
-
quantalogic-0.2.
|
66
|
-
quantalogic-0.2.
|
67
|
-
quantalogic-0.2.
|
68
|
-
quantalogic-0.2.
|
69
|
-
quantalogic-0.2.
|
66
|
+
quantalogic-0.2.10.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
67
|
+
quantalogic-0.2.10.dist-info/METADATA,sha256=Hu3vVRYMVGIPJpoCxP_6S4XKbw6R5rKPxcvS5y4Xfnw,39747
|
68
|
+
quantalogic-0.2.10.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
69
|
+
quantalogic-0.2.10.dist-info/entry_points.txt,sha256=wgSq5SRU98yvlRHGEZD1Xn7sS5CSjH2RfUtTa6Qy28Q,52
|
70
|
+
quantalogic-0.2.10.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|