quantalogic 0.2.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.
Files changed (68) hide show
  1. quantalogic/__init__.py +20 -0
  2. quantalogic/agent.py +638 -0
  3. quantalogic/agent_config.py +138 -0
  4. quantalogic/coding_agent.py +83 -0
  5. quantalogic/event_emitter.py +223 -0
  6. quantalogic/generative_model.py +226 -0
  7. quantalogic/interactive_text_editor.py +190 -0
  8. quantalogic/main.py +185 -0
  9. quantalogic/memory.py +217 -0
  10. quantalogic/model_names.py +19 -0
  11. quantalogic/print_event.py +66 -0
  12. quantalogic/prompts.py +99 -0
  13. quantalogic/server/__init__.py +3 -0
  14. quantalogic/server/agent_server.py +633 -0
  15. quantalogic/server/models.py +60 -0
  16. quantalogic/server/routes.py +117 -0
  17. quantalogic/server/state.py +199 -0
  18. quantalogic/server/static/js/event_visualizer.js +430 -0
  19. quantalogic/server/static/js/quantalogic.js +571 -0
  20. quantalogic/server/templates/index.html +134 -0
  21. quantalogic/tool_manager.py +68 -0
  22. quantalogic/tools/__init__.py +46 -0
  23. quantalogic/tools/agent_tool.py +88 -0
  24. quantalogic/tools/download_http_file_tool.py +64 -0
  25. quantalogic/tools/edit_whole_content_tool.py +70 -0
  26. quantalogic/tools/elixir_tool.py +240 -0
  27. quantalogic/tools/execute_bash_command_tool.py +116 -0
  28. quantalogic/tools/input_question_tool.py +57 -0
  29. quantalogic/tools/language_handlers/__init__.py +21 -0
  30. quantalogic/tools/language_handlers/c_handler.py +33 -0
  31. quantalogic/tools/language_handlers/cpp_handler.py +33 -0
  32. quantalogic/tools/language_handlers/go_handler.py +33 -0
  33. quantalogic/tools/language_handlers/java_handler.py +37 -0
  34. quantalogic/tools/language_handlers/javascript_handler.py +42 -0
  35. quantalogic/tools/language_handlers/python_handler.py +29 -0
  36. quantalogic/tools/language_handlers/rust_handler.py +33 -0
  37. quantalogic/tools/language_handlers/scala_handler.py +33 -0
  38. quantalogic/tools/language_handlers/typescript_handler.py +42 -0
  39. quantalogic/tools/list_directory_tool.py +123 -0
  40. quantalogic/tools/llm_tool.py +119 -0
  41. quantalogic/tools/markitdown_tool.py +105 -0
  42. quantalogic/tools/nodejs_tool.py +515 -0
  43. quantalogic/tools/python_tool.py +469 -0
  44. quantalogic/tools/read_file_block_tool.py +140 -0
  45. quantalogic/tools/read_file_tool.py +79 -0
  46. quantalogic/tools/replace_in_file_tool.py +300 -0
  47. quantalogic/tools/ripgrep_tool.py +353 -0
  48. quantalogic/tools/search_definition_names.py +419 -0
  49. quantalogic/tools/task_complete_tool.py +35 -0
  50. quantalogic/tools/tool.py +146 -0
  51. quantalogic/tools/unified_diff_tool.py +387 -0
  52. quantalogic/tools/write_file_tool.py +97 -0
  53. quantalogic/utils/__init__.py +17 -0
  54. quantalogic/utils/ask_user_validation.py +12 -0
  55. quantalogic/utils/download_http_file.py +77 -0
  56. quantalogic/utils/get_coding_environment.py +15 -0
  57. quantalogic/utils/get_environment.py +26 -0
  58. quantalogic/utils/get_quantalogic_rules_content.py +19 -0
  59. quantalogic/utils/git_ls.py +121 -0
  60. quantalogic/utils/read_file.py +54 -0
  61. quantalogic/utils/read_http_text_content.py +101 -0
  62. quantalogic/xml_parser.py +242 -0
  63. quantalogic/xml_tool_parser.py +99 -0
  64. quantalogic-0.2.0.dist-info/LICENSE +201 -0
  65. quantalogic-0.2.0.dist-info/METADATA +1034 -0
  66. quantalogic-0.2.0.dist-info/RECORD +68 -0
  67. quantalogic-0.2.0.dist-info/WHEEL +4 -0
  68. quantalogic-0.2.0.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,33 @@
1
+ import tree_sitter_scala as tsscala
2
+ from tree_sitter import Language
3
+
4
+
5
+ class ScalaLanguageHandler:
6
+ """Handler for Scala-specific language processing."""
7
+
8
+ def get_language(self):
9
+ """Returns the Tree-sitter Language object for Scala."""
10
+ return Language(tsscala.language())
11
+
12
+ def validate_root_node(self, root_node):
13
+ """Validates the root node for Scala syntax trees."""
14
+ return root_node.type == "compilation_unit"
15
+
16
+ def process_node(
17
+ self, node, current_class, definitions, process_method, process_function, process_class, process_class_variable
18
+ ):
19
+ """Processes a node in a Scala syntax tree."""
20
+ if node.type in ("function_definition", "method_definition"):
21
+ if current_class:
22
+ process_method(node, definitions["classes"][current_class]["methods"])
23
+ else:
24
+ process_function(node, definitions["functions"])
25
+ return "function"
26
+ elif node.type == "class_definition":
27
+ class_name = process_class(node)
28
+ definitions["classes"][class_name] = {
29
+ "line": (node.start_point[0] + 1, node.end_point[0] + 1),
30
+ "methods": [],
31
+ "variables": [],
32
+ }
33
+ return "class"
@@ -0,0 +1,42 @@
1
+ import tree_sitter_typescript as tstypescript
2
+ from tree_sitter import Language
3
+
4
+
5
+ class TypeScriptLanguageHandler:
6
+ """Handler for TypeScript-specific language processing."""
7
+
8
+ def get_language(self):
9
+ """Returns the Tree-sitter Language object for TypeScript."""
10
+ return Language(tstypescript.language())
11
+
12
+ def validate_root_node(self, root_node):
13
+ """Validates the root node for TypeScript syntax trees."""
14
+ return root_node.type == "program"
15
+
16
+ def process_node(
17
+ self, node, current_class, definitions, process_method, process_function, process_class, process_class_variable
18
+ ):
19
+ """Processes a node in a TypeScript syntax tree."""
20
+ if node.type in ("function_declaration", "method_definition"):
21
+ if current_class:
22
+ process_method(node, definitions["classes"][current_class]["methods"])
23
+ else:
24
+ process_function(node, definitions["functions"])
25
+ return "function"
26
+ elif node.type == "class_declaration":
27
+ class_name = process_class(node)
28
+ definitions["classes"][class_name] = {
29
+ "line": (node.start_point[0] + 1, node.end_point[0] + 1),
30
+ "methods": [],
31
+ "variables": [],
32
+ }
33
+ return "class"
34
+ elif node.type == "method_definition":
35
+ if current_class:
36
+ process_method(node, definitions["classes"][current_class]["methods"])
37
+ return "method"
38
+ elif node.type == "class_variable_definition":
39
+ if current_class:
40
+ process_class_variable(node, definitions["classes"][current_class]["variables"])
41
+ return "variable"
42
+ return None
@@ -0,0 +1,123 @@
1
+ """Tool for listing the contents of a directory."""
2
+
3
+ import os
4
+
5
+ from quantalogic.tools.tool import Tool, ToolArgument
6
+ from quantalogic.utils.git_ls import git_ls
7
+
8
+
9
+ class ListDirectoryTool(Tool):
10
+ """Lists directory contents with pagination and .gitignore support."""
11
+
12
+ name: str = "list_directory_tool"
13
+ description: str = "Lists directory contents with pagination and .gitignore filtering"
14
+ arguments: list[ToolArgument] = [
15
+ ToolArgument(
16
+ name="directory_path",
17
+ arg_type="string",
18
+ description="Absolute or relative path to target directory",
19
+ required=True,
20
+ example="~/documents/projects",
21
+ ),
22
+ ToolArgument(
23
+ name="recursive",
24
+ arg_type="string",
25
+ description="Enable recursive traversal (true/false)",
26
+ required=False,
27
+ default="false",
28
+ example="true",
29
+ ),
30
+ ToolArgument(
31
+ name="max_depth",
32
+ arg_type="int",
33
+ description="Maximum directory traversal depth",
34
+ required=False,
35
+ default="1",
36
+ example="1",
37
+ ),
38
+ ToolArgument(
39
+ name="start_line",
40
+ arg_type="int",
41
+ description="First line to return in paginated results",
42
+ required=False,
43
+ default="1",
44
+ example="1",
45
+ ),
46
+ ToolArgument(
47
+ name="end_line",
48
+ arg_type="int",
49
+ description="Last line to return in paginated results",
50
+ required=False,
51
+ default="200",
52
+ example="200",
53
+ ),
54
+ ]
55
+
56
+ def execute(
57
+ self,
58
+ directory_path: str,
59
+ recursive: str = "false",
60
+ max_depth: str = "1",
61
+ start_line: str = "1",
62
+ end_line: str = "200",
63
+ ) -> str:
64
+ """
65
+ List directory contents with pagination and .gitignore support.
66
+
67
+ Args:
68
+ directory_path: Absolute or relative path to target directory
69
+ recursive: Enable recursive traversal (true/false)
70
+ max_depth: Maximum directory traversal depth
71
+ start_line: First line to return in paginated results
72
+ end_line: Last line to return in paginated results
73
+
74
+ Returns:
75
+ str: Paginated directory listing with metadata
76
+
77
+ Raises:
78
+ ValueError: For invalid directory paths or pagination parameters
79
+ """
80
+ # Expand user home directory to full path
81
+ # This ensures compatibility with '~' shorthand for home directory
82
+ if directory_path.startswith("~"):
83
+ directory_path = os.path.expanduser(directory_path)
84
+
85
+ # Validate directory existence and type
86
+ # Fail early with clear error messages if path is invalid
87
+ if not os.path.exists(directory_path):
88
+ raise ValueError(f"The directory '{directory_path}' does not exist.")
89
+ if not os.path.isdir(directory_path):
90
+ raise ValueError(f"The path '{directory_path}' is not a directory.")
91
+
92
+ # Safely convert inputs with default values
93
+ start = int(start_line or "1")
94
+ end = int(end_line or "200")
95
+ max_depth_int = int(max_depth or "1")
96
+ is_recursive = (recursive or "false").lower() == "true"
97
+
98
+ # Validate pagination parameters
99
+ if start > end:
100
+ raise ValueError("start_line must be less than or equal to end_line.")
101
+
102
+ try:
103
+ # Use git_ls for directory listing with .gitignore support
104
+ all_lines = git_ls(
105
+ directory_path=directory_path,
106
+ recursive=is_recursive,
107
+ max_depth=max_depth_int,
108
+ start_line=start,
109
+ end_line=end,
110
+ )
111
+ return all_lines
112
+ except Exception as e:
113
+ import traceback
114
+
115
+ traceback.print_exc()
116
+ return f"Error: {str(e)} occurred during directory listing. See logs for details."
117
+
118
+
119
+ if __name__ == "__main__":
120
+ tool = ListDirectoryTool()
121
+ current_directory = os.getcwd()
122
+ tool.execute(directory_path=current_directory, recursive="true")
123
+ print(tool.to_markdown())
@@ -0,0 +1,119 @@
1
+ """LLM Tool for generating answers to questions using a language model."""
2
+
3
+ import logging
4
+
5
+ from pydantic import ConfigDict, Field
6
+
7
+ from quantalogic.generative_model import GenerativeModel, Message
8
+ from quantalogic.tools.tool import Tool, ToolArgument
9
+
10
+
11
+ class LLMTool(Tool):
12
+ """Tool to generate answers using a specified language model."""
13
+
14
+ model_config = ConfigDict(arbitrary_types_allowed=True, extra="allow")
15
+
16
+ name: str = Field(default="llm_tool")
17
+ description: str = Field(
18
+ default=(
19
+ "Generates answers to questions using a specified language model. "
20
+ "Note: This tool operates in isolation and does not have access to: "
21
+ " - Memory: All context must be explicitly provided in the prompt. "
22
+ " - File system."
23
+ " - Variables: Any required variables should be interpolated into the prompt (e.g., $var1$). "
24
+ " - Other tools: It cannot invoke or interact with other tools. "
25
+ "Ensure all necessary information is included directly in your prompt."
26
+ )
27
+ )
28
+ arguments: list = Field(
29
+ default=[
30
+ ToolArgument(
31
+ name="system_prompt",
32
+ arg_type="string",
33
+ description=("The persona or system prompt to guide the language model's behavior. "),
34
+ required=True,
35
+ example=("You are an expert in natural language processing and machine learning. "),
36
+ ),
37
+ ToolArgument(
38
+ name="prompt",
39
+ arg_type="string",
40
+ description=("The question to ask the language model. Use interpolation if possible example $var1$."),
41
+ required=True,
42
+ example="What is the meaning of $var1$ ?",
43
+ ),
44
+ ToolArgument(
45
+ name="temperature",
46
+ arg_type="string",
47
+ description='Sampling temperature between "0.0" and "1.0": "0.0" no creativity, "1.0" full creativity. (float)',
48
+ required=True,
49
+ default="0.5",
50
+ example="0.5",
51
+ ),
52
+ ]
53
+ )
54
+
55
+ model_name: str = Field(..., description="The name of the language model to use")
56
+ generative_model: GenerativeModel | None = Field(default=None)
57
+
58
+ def model_post_init(self, __context):
59
+ """Initialize the generative model after model initialization."""
60
+ if self.generative_model is None:
61
+ self.generative_model = GenerativeModel(model=self.model_name)
62
+ logging.debug(f"Initialized LLMTool with model: {self.model_name}")
63
+
64
+ def execute(self, system_prompt: str, prompt: str, temperature: str = "0.7") -> str:
65
+ """Execute the tool to generate an answer based on the provided question.
66
+
67
+ Args:
68
+ system_prompt (str): The system prompt to guide the model.
69
+ prompt (str): The question to be answered.
70
+ temperature (str, optional): Sampling temperature. Defaults to "0.7".
71
+
72
+ Returns:
73
+ str: The generated answer.
74
+
75
+ Raises:
76
+ ValueError: If temperature is not a valid float between 0 and 1.
77
+ Exception: If there's an error during response generation.
78
+ """
79
+ try:
80
+ temp = float(temperature)
81
+ if not (0.0 <= temp <= 1.0):
82
+ raise ValueError("Temperature must be between 0 and 1.")
83
+ except ValueError as ve:
84
+ logging.error(f"Invalid temperature value: {temperature}")
85
+ raise ValueError(f"Invalid temperature value: {temperature}") from ve
86
+
87
+ # Prepare the messages history
88
+ messages_history = [
89
+ Message(role="system", content=system_prompt),
90
+ Message(role="user", content=prompt),
91
+ ]
92
+
93
+ # Set the model's temperature
94
+ if self.generative_model:
95
+ self.generative_model.temperature = temp
96
+
97
+ # Generate the response using the generative model
98
+ try:
99
+ response_stats = self.generative_model.generate_with_history(
100
+ messages_history=messages_history, prompt=""
101
+ )
102
+ response = response_stats.response.strip()
103
+ logging.info(f"Generated response: {response}")
104
+ return response
105
+ except Exception as e:
106
+ logging.error(f"Error generating response: {e}")
107
+ raise Exception(f"Error generating response: {e}") from e
108
+ else:
109
+ raise ValueError("Generative model not initialized")
110
+
111
+
112
+ if __name__ == "__main__":
113
+ # Example usage of LLMTool
114
+ tool = LLMTool(model_name="gpt-4o-mini")
115
+ system_prompt = 'Answer the question as truthfully as possible using the provided context, and if the answer is not contained within the context, say "I don\'t know".'
116
+ question = "What is the meaning of life?"
117
+ temperature = "0.7"
118
+ answer = tool.execute(system_prompt=system_prompt, prompt=question, temperature=temperature)
119
+ print(answer)
@@ -0,0 +1,105 @@
1
+ """Tool for converting various file formats to Markdown using the MarkItDown library."""
2
+
3
+ import os
4
+ import tempfile
5
+ from typing import Optional
6
+
7
+ from quantalogic.tools.tool import Tool, ToolArgument
8
+ from quantalogic.utils.download_http_file import download_http_file
9
+
10
+ MAX_LINES = 2000 # Maximum number of lines to return when no output file is specified
11
+
12
+
13
+ class MarkitdownTool(Tool):
14
+ """Tool for converting various file formats to Markdown using the MarkItDown library."""
15
+
16
+ name: str = "markitdown_tool"
17
+ description: str = (
18
+ "Converts various file formats to Markdown using the MarkItDown library. "
19
+ "Supports both local file paths and URLs (http://, https://). "
20
+ "Supported formats include: PDF, PowerPoint, Word, Excel, HTML"
21
+ "Don't use the output_file_path argument if you want to return the result in markdown directly"
22
+ )
23
+ arguments: list = [
24
+ ToolArgument(
25
+ name="file_path",
26
+ arg_type="string",
27
+ description="The path to the file to convert. Can be a local path or URL (http://, https://).",
28
+ required=True,
29
+ example="/path/to/file.txt or https://example.com/file.pdf",
30
+ ),
31
+ ToolArgument(
32
+ name="output_file_path",
33
+ arg_type="string",
34
+ description="Path to write the Markdown output to. You can use a temp file.",
35
+ required=False,
36
+ example="/path/to/output.md",
37
+ ),
38
+ ]
39
+
40
+ def execute(self, file_path: str, output_file_path: Optional[str] = None) -> str:
41
+ """Converts a file to Markdown and returns or writes the content.
42
+
43
+ Args:
44
+ file_path (str): The path to the file to convert. Can be a local path or URL.
45
+ output_file_path (str, optional): Optional path to write the Markdown output to.
46
+
47
+ Returns:
48
+ str: The Markdown content or a success message.
49
+ """
50
+ # Handle tilde expansion for local paths
51
+ if file_path.startswith("~"):
52
+ file_path = os.path.expanduser(file_path)
53
+
54
+ # Handle URL paths
55
+ if file_path.startswith(("http://", "https://")):
56
+ try:
57
+ # Create a temporary file
58
+ with tempfile.NamedTemporaryFile(delete=False) as temp_file:
59
+ temp_path = temp_file.name
60
+ # Download the file from URL
61
+ download_http_file(file_path, temp_path)
62
+ # Use the temporary file path for conversion
63
+ file_path = temp_path
64
+ is_temp_file = True
65
+ except Exception as e:
66
+ return f"Error downloading file from URL: {str(e)}"
67
+ else:
68
+ is_temp_file = False
69
+
70
+ try:
71
+ from markitdown import MarkItDown
72
+
73
+ md = MarkItDown()
74
+ result = md.convert(file_path)
75
+
76
+ if output_file_path:
77
+ with open(output_file_path, "w", encoding="utf-8") as f:
78
+ f.write(result.text_content)
79
+ output_message = f"Markdown content successfully written to {output_file_path}"
80
+ else:
81
+ # Truncate content if it exceeds MAX_LINES
82
+ lines = result.text_content.splitlines()
83
+ if len(lines) > MAX_LINES:
84
+ truncated_content = "\n".join(lines[:MAX_LINES])
85
+ output_message = f"Markdown content truncated to {MAX_LINES} lines:\n{truncated_content}"
86
+ else:
87
+ output_message = result.text_content
88
+
89
+ return output_message
90
+ except Exception as e:
91
+ return f"Error converting file to Markdown: {str(e)}"
92
+ finally:
93
+ # Clean up temporary file if it was created
94
+ if is_temp_file and os.path.exists(file_path):
95
+ os.remove(file_path)
96
+
97
+
98
+ if __name__ == "__main__":
99
+ tool = MarkitdownTool()
100
+ print(tool.to_markdown())
101
+
102
+ # Example usage:
103
+ print(tool.execute(file_path="./examples/2412.18601v1.pdf"))
104
+
105
+ print(tool.execute(file_path="https://arxiv.org/pdf/2412.18601"))