quantalogic 0.31.1__py3-none-any.whl → 0.33.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 CHANGED
@@ -52,11 +52,7 @@ class ObserveResponseResult(BaseModel):
52
52
  class Agent(BaseModel):
53
53
  """Enhanced QuantaLogic agent implementing ReAct framework."""
54
54
 
55
- model_config = ConfigDict(
56
- arbitrary_types_allowed=True,
57
- validate_assignment=True,
58
- extra="forbid"
59
- )
55
+ model_config = ConfigDict(arbitrary_types_allowed=True, validate_assignment=True, extra="forbid")
60
56
 
61
57
  specific_expertise: str
62
58
  model: GenerativeModel
@@ -95,7 +91,7 @@ class Agent(BaseModel):
95
91
  """Initialize the agent with model, memory, tools, and configurations."""
96
92
  try:
97
93
  logger.debug("Initializing agent...")
98
-
94
+
99
95
  # Create event emitter
100
96
  event_emitter = EventEmitter()
101
97
 
@@ -142,9 +138,9 @@ class Agent(BaseModel):
142
138
  compact_every_n_iterations=compact_every_n_iterations or 30,
143
139
  max_tokens_working_memory=max_tokens_working_memory,
144
140
  )
145
-
141
+
146
142
  self._model_name = model_name
147
-
143
+
148
144
  logger.debug(f"Memory will be compacted every {self.compact_every_n_iterations} iterations")
149
145
  logger.debug(f"Max tokens for working memory set to: {self.max_tokens_working_memory}")
150
146
  logger.debug("Agent initialized successfully.")
@@ -168,7 +164,9 @@ class Agent(BaseModel):
168
164
  """Clear the memory and reset the session."""
169
165
  self._reset_session(clear_memory=True)
170
166
 
171
- def solve_task(self, task: str, max_iterations: int = 30, streaming: bool = False, clear_memory: bool = True) -> str:
167
+ def solve_task(
168
+ self, task: str, max_iterations: int = 30, streaming: bool = False, clear_memory: bool = True
169
+ ) -> str:
172
170
  """Solve the given task using the ReAct framework.
173
171
 
174
172
  Args:
@@ -182,7 +180,7 @@ class Agent(BaseModel):
182
180
  str: The final response after task completion.
183
181
  """
184
182
  logger.debug(f"Solving task... {task}")
185
- self._reset_session(task_to_solve=task, max_iterations=max_iterations,clear_memory=clear_memory)
183
+ self._reset_session(task_to_solve=task, max_iterations=max_iterations, clear_memory=clear_memory)
186
184
 
187
185
  # Generate task summary
188
186
  self.task_to_solve_summary = self._generate_task_summary(task)
@@ -228,7 +226,9 @@ class Agent(BaseModel):
228
226
  # For streaming, collect the response chunks
229
227
  content = ""
230
228
  for chunk in self.model.generate_with_history(
231
- messages_history=self.memory.memory, prompt=current_prompt, streaming=True
229
+ messages_history=self.memory.memory,
230
+ prompt=current_prompt,
231
+ streaming=True,
232
232
  ):
233
233
  content += chunk
234
234
 
@@ -245,7 +245,8 @@ class Agent(BaseModel):
245
245
  )
246
246
  else:
247
247
  result = self.model.generate_with_history(
248
- messages_history=self.memory.memory, prompt=current_prompt, streaming=False
248
+ messages_history=self.memory.memory, prompt=current_prompt, streaming=False,
249
+ stop_words=["thinking"]
249
250
  )
250
251
 
251
252
  content = result.response
@@ -296,7 +297,7 @@ class Agent(BaseModel):
296
297
 
297
298
  return answer
298
299
 
299
- def _reset_session(self, task_to_solve: str = "", max_iterations: int = 30,clear_memory: bool = True):
300
+ def _reset_session(self, task_to_solve: str = "", max_iterations: int = 30, clear_memory: bool = True):
300
301
  """Reset the agent's session."""
301
302
  logger.debug("Resetting session...")
302
303
  self.task_to_solve = task_to_solve
@@ -316,29 +317,30 @@ class Agent(BaseModel):
316
317
  def _compact_memory_if_needed(self, current_prompt: str = ""):
317
318
  """Compacts the memory if it exceeds the maximum occupancy or token limit."""
318
319
  ratio_occupied = self._calculate_context_occupancy()
319
-
320
+
320
321
  # Compact memory if any of these conditions are met:
321
322
  # 1. Memory occupancy exceeds MAX_OCCUPANCY, or
322
323
  # 2. Current iteration is a multiple of compact_every_n_iterations, or
323
324
  # 3. Working memory exceeds max_tokens_working_memory (if set)
324
325
  should_compact_by_occupancy = ratio_occupied >= MAX_OCCUPANCY
325
326
  should_compact_by_iteration = (
326
- self.compact_every_n_iterations is not None and
327
- self.current_iteration > 0 and
328
- self.current_iteration % self.compact_every_n_iterations == 0
327
+ self.compact_every_n_iterations is not None
328
+ and self.current_iteration > 0
329
+ and self.current_iteration % self.compact_every_n_iterations == 0
329
330
  )
330
331
  should_compact_by_token_limit = (
331
- self.max_tokens_working_memory is not None and
332
- self.total_tokens > self.max_tokens_working_memory
332
+ self.max_tokens_working_memory is not None and self.total_tokens > self.max_tokens_working_memory
333
333
  )
334
-
334
+
335
335
  if should_compact_by_occupancy or should_compact_by_iteration or should_compact_by_token_limit:
336
336
  if should_compact_by_occupancy:
337
337
  logger.debug(f"Memory compaction triggered: Occupancy {ratio_occupied}% exceeds {MAX_OCCUPANCY}%")
338
-
338
+
339
339
  if should_compact_by_iteration:
340
- logger.debug(f"Memory compaction triggered: Iteration {self.current_iteration} is a multiple of {self.compact_every_n_iterations}")
341
-
340
+ logger.debug(
341
+ f"Memory compaction triggered: Iteration {self.current_iteration} is a multiple of {self.compact_every_n_iterations}"
342
+ )
343
+
342
344
  self._emit_event("memory_full")
343
345
  self.memory.compact()
344
346
  self.total_tokens = self.model.token_counter_with_history(self.memory.memory, current_prompt)
@@ -399,7 +401,7 @@ class Agent(BaseModel):
399
401
  return self._handle_tool_execution_failure(response)
400
402
 
401
403
  variable_name = self.variable_store.add(response)
402
- new_prompt = self._format_observation_response(response, variable_name, iteration)
404
+ new_prompt = self._format_observation_response(response, executed_tool, variable_name, iteration)
403
405
 
404
406
  return ObserveResponseResult(
405
407
  next_prompt=new_prompt,
@@ -414,7 +416,7 @@ class Agent(BaseModel):
414
416
  """Extract tool usage from the response content."""
415
417
  if not content or not isinstance(content, str):
416
418
  return {}
417
-
419
+
418
420
  xml_parser = ToleranceXMLParser()
419
421
  tool_names = self.tools.tool_names()
420
422
  return xml_parser.extract_elements(text=content, element_names=tool_names)
@@ -461,7 +463,7 @@ class Agent(BaseModel):
461
463
  answer=None,
462
464
  )
463
465
 
464
- def _handle_repeated_tool_call(self, tool_name: str, arguments_with_values: dict) -> (str,str):
466
+ def _handle_repeated_tool_call(self, tool_name: str, arguments_with_values: dict) -> (str, str):
465
467
  """Handle the case where a tool call is repeated."""
466
468
  repeat_count = self.last_tool_call.get("count", 0)
467
469
  error_message = (
@@ -494,7 +496,9 @@ class Agent(BaseModel):
494
496
  answer=None,
495
497
  )
496
498
 
497
- def _format_observation_response(self, response: str, variable_name: str, iteration: int) -> str:
499
+ def _format_observation_response(
500
+ self, response: str, last_exectured_tool: str, variable_name: str, iteration: int
501
+ ) -> str:
498
502
  """Format the observation response with the given response, variable name, and iteration."""
499
503
  response_display = response
500
504
  if len(response) > MAX_RESPONSE_LENGTH:
@@ -504,29 +508,45 @@ class Agent(BaseModel):
504
508
  )
505
509
 
506
510
  # Format the response message
507
- formatted_response = (
508
- f"Your next step: you Must now plan the next tool call to complete the based on this new observation\n"
509
- f"\n--- Observations for iteration {iteration} / max {self.max_iterations} ---\n"
510
- f"\n--- Tool execution result in ${variable_name}$ ---\n"
511
- f"<{variable_name}>\n{response_display}\n</{variable_name}>\n\n"
512
- f"--- Tools ---\n{self._get_tools_names_prompt()}\n"
513
- f"--- Variables ---\n{self._get_variable_prompt()}\n"
514
- "Analyze this response to determine the next steps. If the step failed, reconsider your approach.\n"
515
- f"--- Task to solve summary ---\n{self.task_to_solve_summary}\n"
516
- "--- Format ---\n"
517
- "Respond only with two XML blocks in markdown as specified in system prompt.\n"
518
- "No extra comments must be added.\n"
511
+ formatted_response = formatted_response = (
512
+ "# Analysis and Next Action Decision Point\n\n"
513
+ f"📊 Progress: Iteration {iteration}/{self.max_iterations}\n\n"
514
+ "## Current Context\n"
515
+ f"```\n{self.task_to_solve_summary}```\n\n"
516
+ f"## Latest Tool {last_exectured_tool} Execution Result:\n"
517
+ f"Variable: ${variable_name}$\n"
518
+ f"```\n{response_display}```\n\n"
519
+ "## Available Resources\n"
520
+ f"🛠️ Tools:\n{self._get_tools_names_prompt()}\n\n"
521
+ f"📦 Variables:\n{self._get_variable_prompt()}\n\n"
522
+ "## Your Task\n"
523
+ "1. Analyze the execution result and progress, formalize if the current step is solved according to the task.\n"
524
+ "2. Determine the most effective next step\n"
525
+ "3. Select exactly ONE tool from the available list\n"
526
+ "4. Utilize variable interpolation where needed\n"
527
+ "## Response Requirements\n"
528
+ "Provide TWO markdown-formatted XML blocks:\n"
529
+ "1. Your analysis of the progression resulting from the execution of the tool in <thinking> tags, don't include <context_analysis/>\n"
530
+ "2. Your tool execution plan in <tool_name> tags\n\n"
531
+ "## Response Format\n"
519
532
  "```xml\n"
520
533
  "<thinking>\n"
521
- "...\n"
534
+ "[Detailed analysis of progress, and reasoning for next step]\n"
522
535
  "</thinking>\n"
523
536
  "```\n"
524
537
  "```xml\n"
525
- "< ...tool_name... >\n"
526
- "...\n"
527
- "</ ...tool_name... >\n"
528
- "```"
529
- )
538
+ "<action>\n"
539
+ "<selected_tool_name>\n"
540
+ "[Precise instruction for tool execution]\n"
541
+ "</selected_tool_name>\n"
542
+ "</action>\n"
543
+ "```\n\n"
544
+ "⚠️ Important:\n"
545
+ "- Respond ONLY with the two XML blocks\n"
546
+ "- No additional commentary\n"
547
+ "- If previous step failed, revise approach\n"
548
+ "- Ensure variable interpolation syntax is correct\n"
549
+ "- Utilize the <task_complete> tool to indicate task completion, display the result or if the task is deemed unfeasible.")
530
550
 
531
551
  return formatted_response
532
552
 
@@ -589,10 +609,10 @@ class Agent(BaseModel):
589
609
  arguments_with_values_interpolated = {
590
610
  key: self._interpolate_variables(value) for key, value in arguments_with_values.items()
591
611
  }
592
-
612
+
593
613
  arguments_with_values_interpolated = arguments_with_values_interpolated
594
614
 
595
- # test if tool need variables in context
615
+ # test if tool need variables in context
596
616
  if tool.need_variables:
597
617
  # Inject variables into the tool if needed
598
618
  arguments_with_values_interpolated["variables"] = self.variable_store
@@ -603,8 +623,7 @@ class Agent(BaseModel):
603
623
  try:
604
624
  # Convert arguments to proper types
605
625
  converted_args = self.tools.validate_and_convert_arguments(
606
- tool_name,
607
- arguments_with_values_interpolated
626
+ tool_name, arguments_with_values_interpolated
608
627
  )
609
628
  except ValueError as e:
610
629
  return "", f"Argument Error: {str(e)}"
@@ -637,9 +656,10 @@ class Agent(BaseModel):
637
656
  """Interpolate variables using $var$ syntax in the given text."""
638
657
  try:
639
658
  import re
659
+
640
660
  for var in self.variable_store.keys():
641
661
  # Escape the variable name for regex, but use raw value for replacement
642
- pattern = rf'\${re.escape(var)}\$'
662
+ pattern = rf"\${re.escape(var)}\$"
643
663
  replacement = self.variable_store[var]
644
664
  text = re.sub(pattern, replacement, text)
645
665
  return text
@@ -729,9 +749,7 @@ class Agent(BaseModel):
729
749
  # Remove the last assistant / user message
730
750
  user_message = memory_copy.pop()
731
751
  assistant_message = memory_copy.pop()
732
- summary = self.model.generate_with_history(
733
- messages_history=memory_copy, prompt=prompt_summary
734
- )
752
+ summary = self.model.generate_with_history(messages_history=memory_copy, prompt=prompt_summary)
735
753
  # Remove user message
736
754
  memory_copy.pop()
737
755
  # Replace by summary
@@ -751,6 +769,8 @@ class Agent(BaseModel):
751
769
  str: Generated task summary
752
770
  """
753
771
  try:
772
+ if len(content) < 200:
773
+ return content
754
774
  prompt = (
755
775
  "Create an ultra-concise task summary that captures ONLY: \n"
756
776
  "1. Primary objective/purpose\n"
@@ -123,7 +123,8 @@ class GenerativeModel:
123
123
 
124
124
  # Generate a response with conversation history and optional streaming
125
125
  def generate_with_history(
126
- self, messages_history: list[Message], prompt: str, image_url: str | None = None, streaming: bool = False
126
+ self, messages_history: list[Message], prompt: str, image_url: str | None = None, streaming: bool = False,
127
+ stop_words: list[str] | None = None
127
128
  ) -> ResponseStats:
128
129
  """Generate a response with conversation history and optional image.
129
130
 
@@ -132,6 +133,7 @@ class GenerativeModel:
132
133
  prompt: Current user prompt.
133
134
  image_url: Optional image URL for visual queries.
134
135
  streaming: Whether to stream the response.
136
+ stop_words: Optional list of stop words for streaming
135
137
 
136
138
  Returns:
137
139
  Detailed response statistics or a generator in streaming mode.
@@ -163,6 +165,7 @@ class GenerativeModel:
163
165
  model=self.model,
164
166
  messages=messages,
165
167
  num_retries=MIN_RETRIES,
168
+ stop=stop_words,
166
169
  )
167
170
 
168
171
  token_usage = TokenUsage(
@@ -181,7 +184,7 @@ class GenerativeModel:
181
184
  except Exception as e:
182
185
  self._handle_generation_exception(e)
183
186
 
184
- def _stream_response(self, messages):
187
+ def _stream_response(self, messages, stop_words: list[str] | None = None):
185
188
  """Private method to handle streaming responses."""
186
189
  try:
187
190
  for chunk in generate_completion(
@@ -189,7 +192,8 @@ class GenerativeModel:
189
192
  model=self.model,
190
193
  messages=messages,
191
194
  num_retries=MIN_RETRIES,
192
- stream=True, # Enable streaming
195
+ stream=True, # Enable streaming,
196
+ stop=stop_words,
193
197
  ):
194
198
  if chunk.choices[0].delta.content is not None:
195
199
  self.event_emitter.emit("stream_chunk", chunk.choices[0].delta.content)
@@ -1,44 +1,83 @@
1
- model_info = {
2
- "dashscope/qwen-max": {"max_output_tokens": 8 * 1024, "max_input_tokens": 32 * 1024},
3
- "dashscope/qwen-plus": {"max_output_tokens": 8 * 1024, "max_input_tokens": 131072},
4
- "dashscope/qwen-turbo": {"max_output_tokens": 8 * 1024, "max_input_tokens": 1000000},
5
- "deepseek-reasoner": {"max_output_tokens": 8 * 1024, "max_input_tokens": 1024 * 128},
6
- "openrouter/deepseek/deepseek-r1": {"max_output_tokens": 8 * 1024, "max_input_tokens": 1024 * 128},
7
- "openrouter/mistralai/mistral-large-2411": {"max_output_tokens": 128 * 1024, "max_input_tokens": 1024 * 128},
8
- "mistralai/mistral-large-2411": {"max_output_tokens": 128 * 1024, "max_input_tokens": 1024 * 128},
9
- "deepseek/deepseek-chat": {"max_output_tokens": 8* 1024, "max_input_tokens": 1024*64},
10
- "deepseek/deepseek-reasoner": {"max_output_tokens": 8* 1024, "max_input_tokens": 1024*64, "max_cot_tokens": 1024*32 },
11
- }
1
+ import loguru
2
+
3
+ from quantalogic.model_info_list import model_info
4
+ from quantalogic.model_info_litellm import litellm_get_model_max_input_tokens, litellm_get_model_max_output_tokens
5
+ from quantalogic.utils.lm_studio_model_info import ModelInfo, get_model_list
6
+
7
+ DEFAULT_MAX_OUTPUT_TOKENS = 4 * 1024 # Reasonable default for most models
8
+ DEFAULT_MAX_INPUT_TOKENS = 32 * 1024 # Reasonable default for most models
9
+
10
+
11
+ def validate_model_name(model_name: str) -> None:
12
+ if not isinstance(model_name, str) or not model_name.strip():
13
+ raise ValueError(f"Invalid model name: {model_name}")
12
14
 
13
15
 
14
16
  def print_model_info():
15
- for model, info in model_info.items():
16
- print(f"\n{model}:")
17
- print(f" Max Input Tokens: {info['max_input_tokens']:,}")
18
- print(f" Max Output Tokens: {info['max_output_tokens']:,}")
17
+ for info in model_info.values():
18
+ print(f"\n{info.model_name}:")
19
+ print(f" Max Input Tokens: {info.max_input_tokens:,}")
20
+ print(f" Max Output Tokens: {info.max_output_tokens:,}")
19
21
 
20
22
 
21
- if __name__ == "__main__":
22
- print_model_info()
23
+ def get_max_output_tokens(model_name: str) -> int:
24
+ """Get max output tokens with safe fallback"""
25
+ validate_model_name(model_name)
26
+
27
+ if model_name.startswith('lm_studio/'):
28
+ try:
29
+ models = get_model_list()
30
+ for model in models.data:
31
+ if model.id == model_name[len('lm_studio/'):]:
32
+ return model.max_context_length
33
+ except Exception:
34
+ loguru.logger.warning(f"Could not fetch LM Studio model info for {model_name}, using default")
23
35
 
36
+ if model_name in model_info:
37
+ return model_info[model_name].max_output_tokens
24
38
 
25
- def get_max_output_tokens(model_name: str) -> int | None:
26
- """Get the maximum output tokens for a given model name."""
27
- return model_info.get(model_name, {}).get("max_output_tokens", None)
39
+ try:
40
+ return litellm_get_model_max_output_tokens(model_name)
41
+ except Exception as e:
42
+ loguru.logger.warning(f"Model {model_name} not found in LiteLLM registry, using default")
43
+ return DEFAULT_MAX_OUTPUT_TOKENS
28
44
 
29
45
 
30
- def get_max_input_tokens(model_name: str) -> int | None:
31
- """Get the maximum input tokens for a given model name."""
32
- return model_info.get(model_name, {}).get("max_input_tokens", None)
46
+ def get_max_input_tokens(model_name: str) -> int:
47
+ """Get max input tokens with safe fallback"""
48
+ validate_model_name(model_name)
33
49
 
50
+ if model_name.startswith('lm_studio/'):
51
+ try:
52
+ models = get_model_list()
53
+ for model in models.data:
54
+ if model.id == model_name[len('lm_studio/'):]:
55
+ return model.max_context_length
56
+ except Exception:
57
+ loguru.logger.warning(f"Could not fetch LM Studio model info for {model_name}, using default")
34
58
 
35
- def get_max_tokens(model_name: str) -> int | None:
36
- """Get the maximum total tokens (input + output) for a given model name."""
37
- model_data = model_info.get(model_name, {})
38
- max_input = model_data.get("max_input_tokens")
39
- max_output = model_data.get("max_output_tokens")
59
+ if model_name in model_info:
60
+ return model_info[model_name].max_input_tokens
40
61
 
41
- if max_input is None or max_output is None:
42
- return None
62
+ try:
63
+ return litellm_get_model_max_input_tokens(model_name)
64
+ except Exception:
65
+ loguru.logger.warning(f"Model {model_name} not found in LiteLLM registry, using default")
66
+ return DEFAULT_MAX_INPUT_TOKENS
43
67
 
44
- return max_input + max_output
68
+
69
+ def get_max_tokens(model_name: str) -> int:
70
+ """Get total maximum tokens (input + output)"""
71
+ validate_model_name(model_name)
72
+
73
+ # Get input and output tokens separately
74
+ input_tokens = get_max_input_tokens(model_name)
75
+ output_tokens = get_max_output_tokens(model_name)
76
+
77
+ return input_tokens + output_tokens
78
+
79
+
80
+ if __name__ == "__main__":
81
+ print_model_info()
82
+ print(get_max_input_tokens("gpt-4o-mini"))
83
+ print(get_max_output_tokens("openrouter/openai/gpt-4o-mini"))
quantalogic/llm.py CHANGED
@@ -30,18 +30,56 @@ def get_model_info(model_name: str) -> dict | None:
30
30
  return model_info.get(model_name, None)
31
31
 
32
32
 
33
+ class ModelProviderConfig:
34
+ def __init__(self, prefix: str, provider: str, base_url: str, env_var: str):
35
+ self.prefix = prefix
36
+ self.provider = provider
37
+ self.base_url = base_url
38
+ self.env_var = env_var
39
+
40
+ def configure(self, model: str, kwargs: Dict[str, Any]) -> None:
41
+ kwargs["model"] = model.replace(self.prefix, "")
42
+ kwargs["custom_llm_provider"] = self.provider
43
+ kwargs["base_url"] = self.base_url
44
+ api_key = os.getenv(self.env_var)
45
+ if not api_key:
46
+ raise ValueError(f"{self.env_var} is not set in the environment variables.")
47
+ kwargs["api_key"] = api_key
48
+
49
+
50
+ # Default provider configurations
51
+ PROVIDERS = {
52
+ "dashscope": ModelProviderConfig(
53
+ prefix="dashscope/",
54
+ provider="openai",
55
+ base_url="https://dashscope-intl.aliyuncs.com/compatible-mode/v1",
56
+ env_var="DASHSCOPE_API_KEY"
57
+ ),
58
+ "nvidia": ModelProviderConfig(
59
+ prefix="nvidia/",
60
+ provider="openai",
61
+ base_url="https://integrate.api.nvidia.com/v1",
62
+ env_var="NVIDIA_API_KEY"
63
+ ),
64
+ "ovh": ModelProviderConfig(
65
+ prefix="ovh/",
66
+ provider="openai",
67
+ base_url="https://deepseek-r1-distill-llama-70b.endpoints.kepler.ai.cloud.ovh.net/api/openai_compat/v1",
68
+ env_var="OVH_API_KEY"
69
+ )
70
+ }
71
+
72
+
33
73
  def generate_completion(**kwargs: Dict[str, Any]) -> Any:
34
74
  """Wraps litellm completion with proper type hints."""
35
75
  model = kwargs.get("model", "")
36
- if model.startswith("dashscope/"):
37
- # Remove prefix and configure for OpenAI-compatible endpoint
38
- kwargs["model"] = model.replace("dashscope/", "")
39
- kwargs["custom_llm_provider"] = "openai" # Explicitly specify OpenAI provider
40
- kwargs["base_url"] = "https://dashscope-intl.aliyuncs.com/compatible-mode/v1"
41
- api_key = os.getenv("DASHSCOPE_API_KEY")
42
- if not api_key:
43
- raise ValueError("DASHSCOPE_API_KEY is not set in the environment variables.")
44
- kwargs["api_key"] = api_key
76
+
77
+ # Find matching provider
78
+ for provider_name, provider_config in PROVIDERS.items():
79
+ if model.startswith(provider_config.prefix):
80
+ provider_config.configure(model, kwargs)
81
+ break
82
+
45
83
  return completion(**kwargs)
46
84
 
47
85
 
@@ -0,0 +1,12 @@
1
+ from pydantic import BaseModel
2
+
3
+
4
+ class ModelInfo(BaseModel):
5
+ model_name: str
6
+ max_input_tokens: int
7
+ max_output_tokens: int
8
+ max_cot_tokens: int | None = None
9
+
10
+
11
+ class ModelNotFoundError(Exception):
12
+ """Raised when a model is not found in local registry"""
@@ -0,0 +1,60 @@
1
+ from quantalogic.model_info import ModelInfo
2
+
3
+ model_info = {
4
+ "dashscope/qwen-max": ModelInfo(
5
+ model_name="dashscope/qwen-max",
6
+ max_output_tokens=8 * 1024,
7
+ max_input_tokens=32 * 1024,
8
+ ),
9
+ "dashscope/qwen-plus": ModelInfo(
10
+ model_name="dashscope/qwen-plus",
11
+ max_output_tokens=8 * 1024,
12
+ max_input_tokens=131072,
13
+ ),
14
+ "dashscope/qwen-turbo": ModelInfo(
15
+ model_name="dashscope/qwen-turbo",
16
+ max_output_tokens=8 * 1024,
17
+ max_input_tokens=1000000,
18
+ ),
19
+ "deepseek-reasoner": ModelInfo(
20
+ model_name="deepseek-reasoner",
21
+ max_output_tokens=8 * 1024,
22
+ max_input_tokens=1024 * 128,
23
+ ),
24
+ "openrouter/deepseek/deepseek-r1": ModelInfo(
25
+ model_name="openrouter/deepseek/deepseek-r1",
26
+ max_output_tokens=8 * 1024,
27
+ max_input_tokens=1024 * 128,
28
+ ),
29
+ "openrouter/mistralai/mistral-large-2411": ModelInfo(
30
+ model_name="openrouter/mistralai/mistral-large-2411",
31
+ max_output_tokens=128 * 1024,
32
+ max_input_tokens=1024 * 128,
33
+ ),
34
+ "mistralai/mistral-large-2411": ModelInfo(
35
+ model_name="mistralai/mistral-large-2411",
36
+ max_output_tokens=128 * 1024,
37
+ max_input_tokens=1024 * 128,
38
+ ),
39
+ "deepseek/deepseek-chat": ModelInfo(
40
+ model_name="deepseek/deepseek-chat",
41
+ max_output_tokens=8 * 1024,
42
+ max_input_tokens=1024 * 64,
43
+ ),
44
+ "deepseek/deepseek-reasoner": ModelInfo(
45
+ model_name="deepseek/deepseek-reasoner",
46
+ max_output_tokens=8 * 1024,
47
+ max_input_tokens=1024 * 64,
48
+ max_cot_tokens=1024 * 32,
49
+ ),
50
+ "nvidia/deepseek-ai/deepseek-r1": ModelInfo(
51
+ model_name="nvidia/deepseek-ai/deepseek-r1",
52
+ max_output_tokens=8 * 1024,
53
+ max_input_tokens=1024 * 64,
54
+ ),
55
+ "ovh/DeepSeek-R1-Distill-Llama-70B": ModelInfo(
56
+ model_name="ovh/DeepSeek-R1-Distill-Llama-70B",
57
+ max_output_tokens=8 * 1024,
58
+ max_input_tokens=1024 * 64,
59
+ ),
60
+ }
@@ -0,0 +1,70 @@
1
+ import functools
2
+
3
+ import litellm
4
+
5
+
6
+ @functools.lru_cache(maxsize=32)
7
+ def litellm_get_model_info(model_name: str) -> dict | None:
8
+ """Get model information with prefix fallback logic using only litellm.
9
+
10
+ Args:
11
+ model_name: The model identifier to get information for
12
+
13
+ Returns:
14
+ Dictionary containing model information
15
+
16
+ Raises:
17
+ ValueError: If model info cannot be found after prefix fallbacks
18
+ """
19
+ tried_models = [model_name]
20
+
21
+ while True:
22
+ try:
23
+ # Attempt to get model info through litellm
24
+ info = litellm.get_model_info(model_name)
25
+ if info:
26
+ return info
27
+ except Exception:
28
+ pass
29
+
30
+ # Try removing one prefix level
31
+ parts = model_name.split("/")
32
+ if len(parts) <= 1:
33
+ break
34
+
35
+ model_name = "/".join(parts[1:])
36
+ tried_models.append(model_name)
37
+
38
+ return None
39
+
40
+
41
+ def litellm_get_model_max_input_tokens(model_name: str) -> int | None:
42
+ """Get maximum input tokens for a model using litellm.
43
+
44
+ Args:
45
+ model_name: The model identifier
46
+
47
+ Returns:
48
+ Maximum input tokens or None if not found
49
+ """
50
+ try:
51
+ info = litellm_get_model_info(model_name)
52
+ return info.get("max_input_tokens", 8192)
53
+ except Exception as e:
54
+ return 8192 # Default for many modern models
55
+
56
+
57
+ def litellm_get_model_max_output_tokens(model_name: str) -> int | None:
58
+ """Get maximum output tokens for a model using litellm.
59
+
60
+ Args:
61
+ model_name: The model identifier
62
+
63
+ Returns:
64
+ Maximum output tokens or None if not found
65
+ """
66
+ try:
67
+ info = litellm_get_model_info(model_name)
68
+ return info.get("max_output_tokens", 4096)
69
+ except Exception as e:
70
+ return 4096 # Conservative default
quantalogic/prompts.py CHANGED
@@ -1,119 +1,116 @@
1
+ from quantalogic.version import get_version
2
+
1
3
 
2
4
  def system_prompt(tools: str, environment: str, expertise: str = ""):
3
- """System prompt for the ReAct chatbot."""
5
+ """System prompt for the ReAct chatbot with enhanced cognitive architecture."""
4
6
  return f"""
5
- ### Core Identity
6
- You are QuantaLogic, an advanced ReAct AI Agent specializing in systematic problem-solving.
7
+ ### Agent Identity: QuantaLogic {get_version()}
8
+ Expert ReAct AI Agent implementing OODA (Observe-Orient-Decide-Act) loop with advanced problem-solving capabilities.
7
9
 
8
- ### Specific Expertise
10
+ ### Domain Expertise
9
11
  {expertise}
10
12
 
11
- ### Task Format
12
- Tasks will be presented within XML tags:
13
- <task>task_description</task>
13
+ ### Input Protocol
14
+ Task Format: <task>task_description</task>
15
+
16
+ ### Cognitive Framework
17
+ 1. 🔍 OBSERVE: Gather and process information
18
+ 2. 🧭 ORIENT: Analyze context and evaluate options
19
+ 3. 🎯 DECIDE: Select optimal action path
20
+ 4. ⚡ ACT: Execute precise tool operations
14
21
 
15
- ### Response Protocol
16
- Every response must contain exactly two XML blocks:
22
+ ### Response Schema [MANDATORY TWO-BLOCK FORMAT]
17
23
 
18
- 1. **Analysis Block**:
24
+ 1. 🧠 Analysis Block:
19
25
  ```xml
20
26
  <thinking>
21
- <!-- Follow this precise format. Be concise, dense, and use abbreviations, emojis, and Unicode characters to maximize density. -->
22
- <task_analysis_if_no_history>
23
- <!-- Only include if no conversation history exists: -->
24
- * Rewrite the <task> and its context in your own words, ensuring clarity and specificity.
25
- * Define detailed criteria for task completion if not already provided.
26
- * Identify key components, constraints, and potential challenges.
27
- * Break the <task> into smaller, manageable sub-tasks if necessary.
28
- - Each sub-task should have a clear objective, specific deliverables, and a logical sequence for progress tracking.
29
- </task_analysis_if_no_history>
30
- <success_criteria_if_no_history>
31
- <!-- Only include if no conversation history exists: -->
32
- * Specify measurable outcomes for task completion.
33
- * Define explicit quality benchmarks and performance indicators.
34
- * Note any constraints or limitations affecting the task.
35
- </success_criteria_if_no_history>
36
- <strategic_approach_if_no_history>
37
- <!-- Only include if no conversation history exists: -->
38
- * Outline a high-level strategy for solving the task.
39
- * Identify required resources, tools, or information.
40
- * Anticipate potential roadblocks and propose contingency plans.
41
- </strategic_approach_if_no_history>
42
- <last_observation>
43
- <!-- Include if conversation history exists: -->
44
- <variable>
45
- <name>...variable name...</name>
46
- <description>...concise description...</description>
47
- </variable>
48
- <result>
49
- ...concise description of the result...
50
- How does this result contribute to task progress?
51
- </result>
52
- </last_observation>
53
- <progress_analysis>
54
- <!-- Include if conversation history exists: -->
55
- * Summarize completed and failed steps concisely.
56
- * Identify and evaluate blockers or challenges.
57
- * Highlight repetitions and suggest reevaluating the approach if necessary.
58
- * Propose potential solutions or alternative strategies.
59
- </progress_analysis>
60
- <variables>
61
- <!-- Include if conversation history exists: -->
62
- * List all variable names and their current values concisely.
63
- </variables>
64
- <next_steps>
65
- * Outline immediate actions required.
66
- * Justify tool selection and parameter choices.
67
- * Use variable interpolation (e.g., `$var1$`) to minimize token generation.
68
- * Consider alternatives or reevaluate the plan if previous attempts failed.
69
- * Use the `task_complete` tool to confirm task completion.
70
- </next_steps>
71
- <taskpad>
72
- <!-- Optional: Use for notes about intermediate steps. -->
73
- <note>...</note>
74
- </taskpad>
27
+ <!-- COGNITIVE PROCESSING MATRIX -->
28
+
29
+ <!-- INITIAL TASK ANALYSIS - INCLUDE ONLY IF NO MESSAGE HISTORY EXISTS -->
30
+ <context_analysis when="no_history">
31
+ 📋 Task Decomposition based on task and history: Steps, Dependencies, Constraints
32
+ 🎯 Success Metrics: Quantifiable Outcomes
33
+ 🛠️ Resource Requirements: Tools, Data, Variables
34
+ ⚠️ Risk Assessment: Potential Failures, Mitigations
35
+ </context_analysis>
36
+
37
+ <!-- ALWAYS INCLUDE FOR ONGOING OPERATIONS -->
38
+ <execution_analysis>
39
+ <!-- ONGOING OPERATIONS -->
40
+ 🔄 Analyze Last Operation Results: Result, Impact, Effectiveness
41
+ • 📊 Progress Map: Completed%, Remaining%, Blockers
42
+ • 💾 Variable State: $var: value pairs
43
+ 📈 Performance Metrics: Speed, Quality, Resource Usage
44
+ </execution_analysis>
45
+
46
+ <decision_matrix>
47
+ <!-- ACTION PLANNING -->
48
+ • 🎯 Next Action: Tool Selection + Rationale
49
+ 📥 Input Parameters: Values + Variable Interpolation
50
+ • 🔄 Fallback Strategy: Alternative Approaches
51
+ ✅ Exit Criteria: Completion Conditions
52
+ </decision_matrix>
53
+
54
+ <memory_pad>
55
+ <!-- OPERATIONAL NOTES -->
56
+ 📝 Key Observations
57
+ • ⚡ Quick Access Data
58
+ </memory_pad>
75
59
  </thinking>
76
60
  ```
77
61
 
78
- 2. **Action Block**:
62
+ 2. Action Block:
79
63
  ```xml
64
+ <action>
80
65
  <tool_name>
81
- <!-- Replace `tool_name` with the name of the tool from the available tools. -->
82
- <parameter1>
83
- <!-- Use variable interpolation (e.g., `$var1$`) to pass context and minimize token generation. -->
84
- value1
85
- </parameter1>
86
- <parameter2>value2</parameter2>
66
+ <!-- PRECISE TOOL EXECUTION -->
67
+ <param1>value1</param1> <!-- Use $var$ for variable interpolation -->
68
+ <param2>value2</param2> <!-- Keep parameters minimal but sufficient -->
87
69
  </tool_name>
70
+ </action>
88
71
  ```
89
72
 
90
- ### Examples of Action Blocks
91
- - **New Task Example**:
73
+ ### Action Patterns
74
+ 1. 🆕 New Task:
92
75
  ```xml
93
- <data_analyzer>
94
- <file_path>$input_file$</file_path>
95
- <operation>validate_structure</operation>
96
- </data_analyzer>
76
+ <action>
77
+ <analyzer>
78
+ <input>$data$</input>
79
+ <mode>initialize</mode>
80
+ </analyzer>
81
+ </action>
97
82
  ```
98
83
 
99
- - **Continuing Task Example**:
84
+ 2. 🔄 Continuation:
100
85
  ```xml
101
- <memory_optimizer>
102
- <process_id>$current_process$</process_id>
103
- <target_utilization>75%</target_utilization>
104
- </memory_optimizer>
86
+ <action>
87
+ <processor>
88
+ <state>$current$</state>
89
+ <action>optimize</action>
90
+ </processor>
91
+ </action>
105
92
  ```
106
93
 
107
- - **Task Completion Example / When a task is completed**:
94
+ 3. Completion:
108
95
  ```xml
96
+ <action>
109
97
  <task_complete>
110
- <answer>Task completed successfully</answer>
98
+ <result>$final_output$</result>
111
99
  </task_complete>
100
+ </action>
112
101
  ```
113
102
 
114
- ### Available Tools
115
- {tools}
103
+ ### Operational Parameters
104
+ 🛠️ Tools: {tools}
105
+ 🌐 Environment: {environment}
116
106
 
117
- ### Environment Details
118
- {environment}
107
+ ### Execution Guidelines
108
+ 1. 🎯 Maintain laser focus on task objectives
109
+ 2. 📊 Use data-driven decision making
110
+ 3. 🔄 Implement feedback loops for continuous optimization
111
+ 4. ⚡ Maximize efficiency through variable interpolation
112
+ 5. 🔍 Monitor and validate each action's impact
113
+ 6. 🛑 Fail fast and adapt when encountering blockers
114
+ 7. ✅ Verify completion criteria rigorously
119
115
  """
116
+
@@ -233,6 +233,12 @@ def task_runner(
233
233
  "memory_summary",
234
234
  ]
235
235
 
236
+ #def ask_continue(event: str, data: any) -> None:
237
+ # ## Ask for ctrl+return
238
+ # if event == "task_think_end":
239
+ # ## Wait return on the keyboard
240
+ # input("Press [Enter] to continue...")
241
+
236
242
  # Add spinner control to event handlers
237
243
  def handle_task_think_start(*args, **kwargs):
238
244
  start_spinner(console)
@@ -246,6 +252,11 @@ def task_runner(
246
252
  if data is not None:
247
253
  console.print(data, end="", markup=False)
248
254
 
255
+ #agent.event_emitter.on(
256
+ # event="task_think_end",
257
+ # listener=ask_continue,
258
+ #)
259
+
249
260
  agent.event_emitter.on(
250
261
  event=events,
251
262
  listener=console_print_events,
@@ -62,6 +62,7 @@ class ReplaceInFileTool(Tool):
62
62
  "Updates sections of content in an existing file using SEARCH/REPLACE blocks. "
63
63
  "If exact matches are not found, the tool attempts to find similar sections based on similarity. "
64
64
  "Returns the updated content or an error."
65
+ "⚠️ THIS TOOL MUST BE USED IN PRIORITY TO UPDATE AN EXISTING FILE."
65
66
  )
66
67
  need_validation: bool = True
67
68
 
@@ -5,6 +5,7 @@ from .git_ls import git_ls
5
5
  from .get_environment import get_environment
6
6
  from .get_coding_environment import get_coding_environment
7
7
  from .get_quantalogic_rules_content import get_quantalogic_rules_file_content
8
+ from .lm_studio_model_info import get_model_list
8
9
 
9
10
  __all__ = [
10
11
  "download_http_file",
@@ -14,4 +15,5 @@ __all__ = [
14
15
  "get_environment",
15
16
  "get_coding_environment",
16
17
  "get_quantalogic_rules_file_content",
18
+ "get_model_list",
17
19
  ]
@@ -1,8 +1,8 @@
1
1
  import litellm
2
2
 
3
-
4
3
  from quantalogic.get_model_info import model_info
5
4
 
5
+
6
6
  def get_all_models() -> list[str]:
7
7
  """
8
8
  Retrieves a unified list of all model names supported by LiteLLM and Quantalogic.
@@ -0,0 +1,48 @@
1
+ from enum import Enum
2
+ from typing import List, Literal, Optional
3
+
4
+ from pydantic import BaseModel, Field
5
+
6
+
7
+ class ModelType(str, Enum):
8
+ LLM = "llm"
9
+ EMBEDDINGS = "embeddings"
10
+ VLM = "vlm"
11
+
12
+ class CompatibilityType(str, Enum):
13
+ MLX = "mlx"
14
+ GGUF = "gguf"
15
+
16
+ class ModelState(str, Enum):
17
+ LOADED = "loaded"
18
+ NOT_LOADED = "not-loaded"
19
+
20
+ class ModelInfo(BaseModel):
21
+ id: str = Field(..., description="Unique model identifier in LM Studio's namespace")
22
+ object: Literal["model"] = Field("model", description="Always 'model' for model objects")
23
+ type: ModelType = Field(..., description="Type of AI model")
24
+ publisher: str = Field(..., description="Organization or user who published the model")
25
+ arch: str = Field(..., description="Base architecture family")
26
+ compatibility_type: CompatibilityType = Field(..., alias="compatibility_type")
27
+ quantization: Optional[str] = Field(None, description="Quantization method if applicable")
28
+ state: ModelState = Field(..., description="Current loading state in LM Studio")
29
+ max_context_length: int = Field(..., alias="max_context_length", ge=0)
30
+ loaded_context_length: Optional[int] = Field(
31
+ None,
32
+ alias="loaded_context_length",
33
+ description="Currently allocated context length (only when loaded)",
34
+ ge=0
35
+ )
36
+
37
+ class ModelListResponse(BaseModel):
38
+ data: List[ModelInfo] = Field(..., description="List of available models")
39
+ object: Literal["list"] = Field("list", description="Always 'list' for list responses")
40
+
41
+ def get_model_list() -> ModelListResponse:
42
+ """Fetch and validate model information from LM Studio's API"""
43
+ import requests
44
+
45
+ response = requests.get("http://localhost:1234/api/v0/models")
46
+ response.raise_for_status()
47
+
48
+ return ModelListResponse(**response.json())
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: quantalogic
3
- Version: 0.31.1
3
+ Version: 0.33.0
4
4
  Summary: QuantaLogic ReAct Agents
5
5
  Author: Raphaël MANSUY
6
6
  Author-email: raphael.mansuy@gmail.com
@@ -184,12 +184,25 @@ See our [Release Notes](RELEASE_NOTES.MD) for detailed version history and chang
184
184
  | openrouter/openai/gpt-4o | OPENROUTER_API_KEY | OpenAI's GPT-4o model accessible through OpenRouter platform. |
185
185
  | openrouter/mistralai/mistral-large-2411 | OPENROUTER_API_KEY | Mistral's large model optimized for complex reasoning tasks, available through OpenRouter with enhanced multilingual capabilities. |
186
186
  | mistral/mistral-large-2407 | MISTRAL_API_KEY | Mistral's high-performance model designed for enterprise-grade applications, offering advanced reasoning and multilingual support. |
187
+ | nvidia/deepseek-ai/deepseek-r1 | NVIDIA_API_KEY | NVIDIA's DeepSeek R1 model optimized for high-performance AI tasks and advanced reasoning capabilities. |
188
+ | lm_studio/mistral-small-24b-instruct-2501 | LM_STUDIO_API_KEY | LM Studio's Mistral Small model optimized for local inference with advanced reasoning capabilities. |
187
189
  | dashscope/qwen-max | DASHSCOPE_API_KEY | Alibaba's Qwen-Max model optimized for maximum performance and extensive reasoning capabilities. |
188
190
  | dashscope/qwen-plus | DASHSCOPE_API_KEY | Alibaba's Qwen-Plus model offering balanced performance and cost-efficiency for a variety of tasks. |
189
191
  | dashscope/qwen-turbo | DASHSCOPE_API_KEY | Alibaba's Qwen-Turbo model designed for fast and efficient responses, ideal for high-throughput scenarios. |
190
192
 
191
193
  To configure the environment API key for Quantalogic using LiteLLM, set the required environment variable for your chosen provider and any optional variables like `OPENAI_API_BASE` or `OPENROUTER_REFERRER`. Use a `.env` file or a secrets manager to securely store these keys, and load them in your code using `python-dotenv`. For advanced configurations, refer to the [LiteLLM documentation](https://docs.litellm.ai/docs/).
192
194
 
195
+ ### LM Studio Local Setup
196
+
197
+ To use LM Studio with the Mistral model locally, set the following environment variables:
198
+
199
+ ```bash
200
+ export LM_STUDIO_API_BASE="http://localhost:1234/v1"
201
+ export LM_STUDIO_API_KEY="your-api-key-here"
202
+ ```
203
+
204
+ Replace `http://localhost:1234/v1` with your LM Studio server URL and `your-api-key-here` with your actual API key.
205
+
193
206
 
194
207
  ## 📦 Installation
195
208
 
@@ -1,5 +1,5 @@
1
1
  quantalogic/__init__.py,sha256=Su8CnOEdqKu4zTytjiP9P5olg-oIDuUA3fMWM1WUdRY,925
2
- quantalogic/agent.py,sha256=5TscD78hIQBaF9pfxIRysC3HjnTJ6e0nyKDwrZMXHak,33223
2
+ quantalogic/agent.py,sha256=3LllyyFILkmm6wz1CmT6942jIgT1oymqiYNzRYO6fAg,34272
3
3
  quantalogic/agent_config.py,sha256=SIRVSF0kkrYfvtyHiMCJhnm_nYqJCD2p1pN-reMIy24,7868
4
4
  quantalogic/agent_factory.py,sha256=HWKwN_DN57EPmME-hoCD2uJE0DqsPCzGU_V7nq54XzI,5284
5
5
  quantalogic/coding_agent.py,sha256=Z7ik6LUvLKDnaW9Ax1iZGC7p1WMnlYEUIlE5lkBP414,4975
@@ -8,14 +8,17 @@ quantalogic/console_print_events.py,sha256=KB-DGi52As8M96eUs1N_vgNqKIFtqv_H8NTOd
8
8
  quantalogic/console_print_token.py,sha256=qSU-3kmoZk4T5-1ybrEBi8tIXDPcz7eyWKhGh3E8uIg,395
9
9
  quantalogic/docs_cli.py,sha256=3giVbUpespB9ZdTSJ955A3BhcOaBl5Lwsn1AVy9XAeY,1663
10
10
  quantalogic/event_emitter.py,sha256=jqot2g4JRXc88K6PW837Oqxbf7shZfO-xdPaUWmzupk,7901
11
- quantalogic/generative_model.py,sha256=ut_BFy4BqDxNqUXVbM8e_C_CzwNuJkvGWRsbpbKaees,13423
12
- quantalogic/get_model_info.py,sha256=f64GpkpzeWXKRMBGG9edfAVP1-S-TclmxSaQC91vglw,1976
11
+ quantalogic/generative_model.py,sha256=K0lbtZuuQrI5McPC4pUZ_FdjL32dAy1vM18egmPGaa8,13638
12
+ quantalogic/get_model_info.py,sha256=_9Nb9JQ09HZzT-_gZUSvl4Er7uCXs5ys36sIBa-8DXA,3005
13
13
  quantalogic/interactive_text_editor.py,sha256=1vW4poJl7SItRGEeGQgtCFcmRDXmfCM8PE-uBtDBJuE,16658
14
- quantalogic/llm.py,sha256=yhuC1b5TCXojDXZEJK_PMcWUUxzrpI-gwzlIszAdJMM,4677
14
+ quantalogic/llm.py,sha256=98osu6LmjuYw4g-CDIp1SJkYlfoHNwdvq6yJqI-K5NA,5692
15
15
  quantalogic/main.py,sha256=__-4pX2pgoSFvt-aLdp6Qlrq55_SrwP_l8u2uTaQbjg,9262
16
16
  quantalogic/memory.py,sha256=zbtRuM05jaS2lJll-92dt5JfYVLERnF_m_9xqp2x-k0,6304
17
+ quantalogic/model_info.py,sha256=j7QqvjEFQDGpDOgQs8uTkVyI3a50Oa_nrsQjyxizTLc,272
18
+ quantalogic/model_info_list.py,sha256=qGDKI5oBbu5oyJSH4o4jGYGqyqYEUMXFRi_qMDAQ2nU,1997
19
+ quantalogic/model_info_litellm.py,sha256=m1Yt4SIiOBRWLx7S8f8k4fcTiKJZKtOvcPN_QvQ_Oxk,1880
17
20
  quantalogic/model_names.py,sha256=UZlz25zG9B2dpfwdw_e1Gw5qFsKQ7iME9FJh9Ts4u6s,938
18
- quantalogic/prompts.py,sha256=M-7rCaQoylnwxedhvy7VmQdgBG6TT1vmcf8_UzPTyY0,4035
21
+ quantalogic/prompts.py,sha256=2DgL979NBXzSfHfBw3KBf7-55wSzIk5vK-uDZbzRQTY,3158
19
22
  quantalogic/search_agent.py,sha256=EA_FAPP0dVuUbJ_lAGKfYq1FIJ6oLYzGMgKLMvBL4ZQ,2472
20
23
  quantalogic/server/__init__.py,sha256=8sz_PYAUCrkM6JM5EAUeIzNM4NPW6j6UT72JVkc21WQ,91
21
24
  quantalogic/server/agent_server.py,sha256=VXaaWqReUSZOCX7CaKS14jria8yZn1kLEc52E2hV7ZA,22510
@@ -26,7 +29,7 @@ quantalogic/server/static/js/event_visualizer.js,sha256=eFkkWyNZw3zOZlF18kxbfsWq
26
29
  quantalogic/server/static/js/quantalogic.js,sha256=x7TrlZGR1Y0WLK2DWl1xY847BhEWMPnL0Ua7KtOldUc,22311
27
30
  quantalogic/server/templates/index.html,sha256=nDnXJoQEm1vXbhXtgaYk0G5VXj0wwzE6KrqEDhHFpj4,7773
28
31
  quantalogic/task_file_reader.py,sha256=AMIJoeVY9Hhu0dBJ-C5EyaOFsXLkhn2oBhVs-WTnnLk,1460
29
- quantalogic/task_runner.py,sha256=Loa0hr-Bge-kVnTpoGuV7RotFdiAKQL_zj4OB4S6gko,9643
32
+ quantalogic/task_runner.py,sha256=6kL7o0br2YU8FNKyq-rKJo1oGKZKl73vtATpHusPIWQ,10038
30
33
  quantalogic/tool_manager.py,sha256=Uh-ufrJPufHqDUrFwKlXw3MOsVGc_4lQxuc6cRvZ7wU,7186
31
34
  quantalogic/tools/__init__.py,sha256=pTirT5UBynuTkAzFYebu7ttGAMP3_A0idFvDp6lGZJQ,2146
32
35
  quantalogic/tools/agent_tool.py,sha256=MXCXxWHRch7VK4UWhtRP1jeI8Np9Ne2CUGo8vm1oZiM,3064
@@ -59,7 +62,7 @@ quantalogic/tools/python_tool.py,sha256=70HLbfU2clOBgj4axDOtIKzXwEBMNGEAX1nGSf-K
59
62
  quantalogic/tools/read_file_block_tool.py,sha256=FTcDAUOOPQOvWRjnRI6nMI1Upus90klR4PC0pbPP_S8,5266
60
63
  quantalogic/tools/read_file_tool.py,sha256=l6k-SOIV9krpXAmUTkxzua51S-KHgzGqkcDlD5AD8K0,2710
61
64
  quantalogic/tools/read_html_tool.py,sha256=Vq2rHY8a36z1-4rN6c_kYjPUTQ4I2UT154PMpaoWSkA,11139
62
- quantalogic/tools/replace_in_file_tool.py,sha256=wC7OlV8UpR5JGCD3VGUWlBEE6fMdGEDRcwE22cDSG2E,13764
65
+ quantalogic/tools/replace_in_file_tool.py,sha256=95GqcOC0sZzbWHWzYDVwq7rEwChlOgKxb5cxKk-Y7W8,13844
63
66
  quantalogic/tools/ripgrep_tool.py,sha256=sRzHaWac9fa0cCGhECJN04jw_Ko0O3u45KDWzMIYcvY,14291
64
67
  quantalogic/tools/search_definition_names.py,sha256=ui0304UgUke6Ca-H3-S4JP9TsdHhRCR5uk9kPuobIDA,18769
65
68
  quantalogic/tools/serpapi_search_tool.py,sha256=sX-Noch77kGP2XiwislPNFyy3_4TH6TwMK6C81L3q9Y,5316
@@ -72,15 +75,16 @@ quantalogic/tools/utils/create_sample_database.py,sha256=Aus9xRLGfQfsYnxsAkJ5CW-
72
75
  quantalogic/tools/utils/generate_database_report.py,sha256=0D-5fWOfpAh1jEcld5OTQP5x6XkJE5jpNY6FyHv1L2s,10345
73
76
  quantalogic/tools/wikipedia_search_tool.py,sha256=bdZ_0dYTxpEfU04tBFsatnLM5P9Z3kAZgKQEjsopJLA,5405
74
77
  quantalogic/tools/write_file_tool.py,sha256=_mx9_Zjg2oMAAVzlcHEKjZVZUxQVgbRfcoMKgWnoZcg,3764
75
- quantalogic/utils/__init__.py,sha256=Ltq7tzLuHCl9BpCvfRVA9Sjrtp1RJesrn7G980lbl_c,563
78
+ quantalogic/utils/__init__.py,sha256=E442CJQuTohKzgI0Wrd4NZEpKascFjz6F4Vy8Y1c_0Y,634
76
79
  quantalogic/utils/ask_user_validation.py,sha256=F0jkbFJVXAImcSSP7op6dov5i80hRvZGRvBHbfcZrxg,340
77
80
  quantalogic/utils/check_version.py,sha256=grxTfJE85GMue1OAk8z8_q8tjEJxQ8RO6fN3fJ_qedg,1136
78
81
  quantalogic/utils/download_http_file.py,sha256=FTN3brq9WvCFvuBX-lYAhjsdYTzQT4m9m2vqlcyjkNk,3472
79
- quantalogic/utils/get_all_models.py,sha256=GGhonVHUS7MeS6eogmkEwZETSVgeGU9pWCLTzR0XxAU,544
82
+ quantalogic/utils/get_all_models.py,sha256=Ol4e60MwZiJhu8HZ2i_RpIumLmFYYrncB1X9q1KEQh0,544
80
83
  quantalogic/utils/get_coding_environment.py,sha256=oMK5ZanOqX_SFaJxUZQGlsAAaiLUgJufCJYDrHnHPuQ,584
81
84
  quantalogic/utils/get_environment.py,sha256=7wWruSHYTUlnQWW27qU3WFYZnncqqqdofsxAsUU7lhw,875
82
85
  quantalogic/utils/get_quantalogic_rules_content.py,sha256=fnEFTyClXzpI0MLaM-gB9R6l4CJlu2NnaYiR09ciJC8,673
83
86
  quantalogic/utils/git_ls.py,sha256=_k6QIQtc0aM1bsG340jBp4VrdevbcH8Pg2CV4r9oHok,5264
87
+ quantalogic/utils/lm_studio_model_info.py,sha256=1eDvZ-I9W8AZbCch1l5rdiSpUxL7qMnfZItdFZkmAWs,1819
84
88
  quantalogic/utils/read_file.py,sha256=tSRVHk8dIP4nNLL89v5kRki4hOTjVyjbmuEb2zwvwCY,2077
85
89
  quantalogic/utils/read_http_text_content.py,sha256=n3IayT5KcqctIVVF2gOQQAMf3Ow6eenlVgfXTpLcQbw,4410
86
90
  quantalogic/version.py,sha256=ea_cRutaQk5_lwlLbUUvPFuOT7Of7-gAsDl7wdveS-g,107
@@ -88,8 +92,8 @@ quantalogic/version_check.py,sha256=cttR1lR3OienGLl7NrK1Te1fhDkqSjCci7HC1vFUTSY,
88
92
  quantalogic/welcome_message.py,sha256=IXMhem8h7srzNUwvw8G_lmEkHU8PFfote021E_BXmVk,3039
89
93
  quantalogic/xml_parser.py,sha256=8yDxvKzAEnefNwUAR-wjerMDOj5T5cxak4WPIA83SBw,11516
90
94
  quantalogic/xml_tool_parser.py,sha256=Vz4LEgDbelJynD1siLOVkJ3gLlfHsUk65_gCwbYJyGc,3784
91
- quantalogic-0.31.1.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
92
- quantalogic-0.31.1.dist-info/METADATA,sha256=pLu3KI4tGKodQWubeVQm1G2P7tC9yZp22ZpOl35N074,22789
93
- quantalogic-0.31.1.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
94
- quantalogic-0.31.1.dist-info/entry_points.txt,sha256=h74O_Q3qBRCrDR99qvwB4BpBGzASPUIjCfxHq6Qnups,183
95
- quantalogic-0.31.1.dist-info/RECORD,,
95
+ quantalogic-0.33.0.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
96
+ quantalogic-0.33.0.dist-info/METADATA,sha256=ZPa-Y4-EhqTp_3ixcWa0hLI_grOtN8v5SnoWoZP9to0,23461
97
+ quantalogic-0.33.0.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
98
+ quantalogic-0.33.0.dist-info/entry_points.txt,sha256=h74O_Q3qBRCrDR99qvwB4BpBGzASPUIjCfxHq6Qnups,183
99
+ quantalogic-0.33.0.dist-info/RECORD,,