quantalogic 0.35.0__py3-none-any.whl → 0.50.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 (107) hide show
  1. quantalogic/__init__.py +0 -4
  2. quantalogic/agent.py +603 -363
  3. quantalogic/agent_config.py +233 -46
  4. quantalogic/agent_factory.py +34 -22
  5. quantalogic/coding_agent.py +16 -14
  6. quantalogic/config.py +2 -1
  7. quantalogic/console_print_events.py +4 -8
  8. quantalogic/console_print_token.py +2 -2
  9. quantalogic/docs_cli.py +15 -10
  10. quantalogic/event_emitter.py +258 -83
  11. quantalogic/flow/__init__.py +23 -0
  12. quantalogic/flow/flow.py +595 -0
  13. quantalogic/flow/flow_extractor.py +672 -0
  14. quantalogic/flow/flow_generator.py +89 -0
  15. quantalogic/flow/flow_manager.py +407 -0
  16. quantalogic/flow/flow_manager_schema.py +169 -0
  17. quantalogic/flow/flow_yaml.md +419 -0
  18. quantalogic/generative_model.py +109 -77
  19. quantalogic/get_model_info.py +5 -5
  20. quantalogic/interactive_text_editor.py +100 -73
  21. quantalogic/main.py +17 -21
  22. quantalogic/model_info_list.py +3 -3
  23. quantalogic/model_info_litellm.py +14 -14
  24. quantalogic/prompts.py +2 -1
  25. quantalogic/{llm.py → quantlitellm.py} +29 -39
  26. quantalogic/search_agent.py +4 -4
  27. quantalogic/server/models.py +4 -1
  28. quantalogic/task_file_reader.py +5 -5
  29. quantalogic/task_runner.py +20 -20
  30. quantalogic/tool_manager.py +10 -21
  31. quantalogic/tools/__init__.py +98 -68
  32. quantalogic/tools/composio/composio.py +416 -0
  33. quantalogic/tools/{generate_database_report_tool.py → database/generate_database_report_tool.py} +4 -9
  34. quantalogic/tools/database/sql_query_tool_advanced.py +261 -0
  35. quantalogic/tools/document_tools/markdown_to_docx_tool.py +620 -0
  36. quantalogic/tools/document_tools/markdown_to_epub_tool.py +438 -0
  37. quantalogic/tools/document_tools/markdown_to_html_tool.py +362 -0
  38. quantalogic/tools/document_tools/markdown_to_ipynb_tool.py +319 -0
  39. quantalogic/tools/document_tools/markdown_to_latex_tool.py +420 -0
  40. quantalogic/tools/document_tools/markdown_to_pdf_tool.py +623 -0
  41. quantalogic/tools/document_tools/markdown_to_pptx_tool.py +319 -0
  42. quantalogic/tools/duckduckgo_search_tool.py +2 -4
  43. quantalogic/tools/finance/alpha_vantage_tool.py +440 -0
  44. quantalogic/tools/finance/ccxt_tool.py +373 -0
  45. quantalogic/tools/finance/finance_llm_tool.py +387 -0
  46. quantalogic/tools/finance/google_finance.py +192 -0
  47. quantalogic/tools/finance/market_intelligence_tool.py +520 -0
  48. quantalogic/tools/finance/technical_analysis_tool.py +491 -0
  49. quantalogic/tools/finance/tradingview_tool.py +336 -0
  50. quantalogic/tools/finance/yahoo_finance.py +236 -0
  51. quantalogic/tools/git/bitbucket_clone_repo_tool.py +181 -0
  52. quantalogic/tools/git/bitbucket_operations_tool.py +326 -0
  53. quantalogic/tools/git/clone_repo_tool.py +189 -0
  54. quantalogic/tools/git/git_operations_tool.py +532 -0
  55. quantalogic/tools/google_packages/google_news_tool.py +480 -0
  56. quantalogic/tools/grep_app_tool.py +123 -186
  57. quantalogic/tools/{dalle_e.py → image_generation/dalle_e.py} +37 -27
  58. quantalogic/tools/jinja_tool.py +6 -10
  59. quantalogic/tools/language_handlers/__init__.py +22 -9
  60. quantalogic/tools/list_directory_tool.py +131 -42
  61. quantalogic/tools/llm_tool.py +45 -15
  62. quantalogic/tools/llm_vision_tool.py +59 -7
  63. quantalogic/tools/markitdown_tool.py +17 -5
  64. quantalogic/tools/nasa_packages/models.py +47 -0
  65. quantalogic/tools/nasa_packages/nasa_apod_tool.py +232 -0
  66. quantalogic/tools/nasa_packages/nasa_neows_tool.py +147 -0
  67. quantalogic/tools/nasa_packages/services.py +82 -0
  68. quantalogic/tools/presentation_tools/presentation_llm_tool.py +396 -0
  69. quantalogic/tools/product_hunt/product_hunt_tool.py +258 -0
  70. quantalogic/tools/product_hunt/services.py +63 -0
  71. quantalogic/tools/rag_tool/__init__.py +48 -0
  72. quantalogic/tools/rag_tool/document_metadata.py +15 -0
  73. quantalogic/tools/rag_tool/query_response.py +20 -0
  74. quantalogic/tools/rag_tool/rag_tool.py +566 -0
  75. quantalogic/tools/rag_tool/rag_tool_beta.py +264 -0
  76. quantalogic/tools/read_html_tool.py +24 -38
  77. quantalogic/tools/replace_in_file_tool.py +10 -10
  78. quantalogic/tools/safe_python_interpreter_tool.py +10 -24
  79. quantalogic/tools/search_definition_names.py +2 -2
  80. quantalogic/tools/sequence_tool.py +14 -23
  81. quantalogic/tools/sql_query_tool.py +17 -19
  82. quantalogic/tools/tool.py +39 -15
  83. quantalogic/tools/unified_diff_tool.py +1 -1
  84. quantalogic/tools/utilities/csv_processor_tool.py +234 -0
  85. quantalogic/tools/utilities/download_file_tool.py +179 -0
  86. quantalogic/tools/utilities/mermaid_validator_tool.py +661 -0
  87. quantalogic/tools/utils/__init__.py +1 -4
  88. quantalogic/tools/utils/create_sample_database.py +24 -38
  89. quantalogic/tools/utils/generate_database_report.py +74 -82
  90. quantalogic/tools/wikipedia_search_tool.py +17 -21
  91. quantalogic/utils/ask_user_validation.py +1 -1
  92. quantalogic/utils/async_utils.py +35 -0
  93. quantalogic/utils/check_version.py +3 -5
  94. quantalogic/utils/get_all_models.py +2 -1
  95. quantalogic/utils/git_ls.py +21 -7
  96. quantalogic/utils/lm_studio_model_info.py +9 -7
  97. quantalogic/utils/python_interpreter.py +113 -43
  98. quantalogic/utils/xml_utility.py +178 -0
  99. quantalogic/version_check.py +1 -1
  100. quantalogic/welcome_message.py +7 -7
  101. quantalogic/xml_parser.py +0 -1
  102. {quantalogic-0.35.0.dist-info → quantalogic-0.50.0.dist-info}/METADATA +40 -1
  103. quantalogic-0.50.0.dist-info/RECORD +148 -0
  104. quantalogic-0.35.0.dist-info/RECORD +0 -102
  105. {quantalogic-0.35.0.dist-info → quantalogic-0.50.0.dist-info}/LICENSE +0 -0
  106. {quantalogic-0.35.0.dist-info → quantalogic-0.50.0.dist-info}/WHEEL +0 -0
  107. {quantalogic-0.35.0.dist-info → quantalogic-0.50.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,420 @@
1
+ """Tool for converting markdown content to well-structured LaTeX documents.
2
+
3
+ Why this tool:
4
+ - Provides a standardized way to convert markdown to professional LaTeX documents
5
+ - Supports mathematical equations and scientific content
6
+ - Handles citations, references, and bibliographies
7
+ - Maintains consistent academic formatting
8
+ - Supports custom document classes and packages
9
+ """
10
+
11
+ import json
12
+ import os
13
+ import re
14
+ from pathlib import Path
15
+ from typing import Any, ClassVar, Dict, List, Optional, Union
16
+
17
+ import mermaid
18
+ from loguru import logger
19
+
20
+ from quantalogic.tools.tool import Tool, ToolArgument
21
+
22
+
23
+ class MarkdownToLatexTool(Tool):
24
+ """Converts markdown to professional LaTeX documents with advanced formatting."""
25
+
26
+ model_config = {
27
+ "arbitrary_types_allowed": True
28
+ }
29
+
30
+ name: str = "markdown_to_latex_tool"
31
+ description: str = (
32
+ "Converts markdown to LaTeX with support for images, Mermaid diagrams, "
33
+ "code blocks, tables, and advanced formatting."
34
+ )
35
+ need_validation: bool = False
36
+
37
+ arguments: List[ToolArgument] = [
38
+ ToolArgument(
39
+ name="markdown_content",
40
+ arg_type="string",
41
+ description="Markdown content with support for LaTeX equations, citations, and academic formatting",
42
+ required=True,
43
+ example="# Title\n\nEquation: $E=mc^2$\n\nCite: [@einstein1905]",
44
+ ),
45
+ ToolArgument(
46
+ name="output_path",
47
+ arg_type="string",
48
+ description="Path for saving the LaTeX file",
49
+ required=True,
50
+ example="/path/to/output.tex",
51
+ ),
52
+ ToolArgument(
53
+ name="document_class",
54
+ arg_type="string",
55
+ description="LaTeX document class (article, report, book)",
56
+ required=False,
57
+ default="article",
58
+ ),
59
+ ToolArgument(
60
+ name="bibliography_file",
61
+ arg_type="string",
62
+ description="Optional path to BibTeX file",
63
+ required=False,
64
+ example="path/to/references.bib",
65
+ ),
66
+ ToolArgument(
67
+ name="style_config",
68
+ arg_type="string",
69
+ description="JSON string with style settings and packages",
70
+ required=False,
71
+ example='{"font_size": "12pt", "packages": ["amsmath", "graphicx"]}',
72
+ ),
73
+ ]
74
+
75
+ DOCUMENT_TEMPLATE: ClassVar[str] = r"""\documentclass[{font_size},{paper_size}]{article}
76
+
77
+ % Packages
78
+ {packages}
79
+
80
+ % Document settings
81
+ \usepackage[{margin}]{geometry}
82
+ \usepackage{{font_package}}
83
+ \usepackage[utf8]{inputenc}
84
+ \usepackage[T1]{fontenc}
85
+
86
+ % Hyperref settings
87
+ \hypersetup{
88
+ colorlinks=true,
89
+ linkcolor=blue,
90
+ filecolor=magenta,
91
+ urlcolor=cyan,
92
+ pdftitle={{title}}
93
+ }
94
+
95
+ \setlength{\parskip}{1em}
96
+ \renewcommand{\baselinestretch}{line_spacing}
97
+
98
+ {extra_preamble}
99
+
100
+ \begin{document}
101
+
102
+ {content}
103
+
104
+ {bibliography}
105
+
106
+ \end{document}
107
+ """
108
+
109
+ DEFAULT_PACKAGES: ClassVar[List[str]] = [
110
+ "graphicx",
111
+ "hyperref",
112
+ "listings",
113
+ "xcolor",
114
+ "amsmath",
115
+ "amssymb",
116
+ "booktabs",
117
+ "float",
118
+ "caption",
119
+ "subcaption",
120
+ "fancyhdr",
121
+ "titlesec",
122
+ "enumitem",
123
+ "microtype",
124
+ ]
125
+
126
+ DEFAULT_STYLES: ClassVar[Dict[str, Union[str, float, List[str]]]] = {
127
+ "font_size": "12pt",
128
+ "paper_size": "a4paper",
129
+ "margin": "margin=1in",
130
+ "font_package": "lmodern",
131
+ "line_spacing": 1.15,
132
+ "code_style": {
133
+ "basicstyle": r"\ttfamily\small",
134
+ "breaklines": "true",
135
+ "commentstyle": r"\color{gray}",
136
+ "keywordstyle": r"\color{blue}",
137
+ "stringstyle": r"\color{green!50!black}",
138
+ "numberstyle": r"\tiny\color{gray}",
139
+ "frame": "single",
140
+ "rulecolor": r"\color{black!30}",
141
+ "backgroundcolor": r"\color{gray!5}",
142
+ }
143
+ }
144
+
145
+ def _normalize_path(self, path: str) -> Path:
146
+ """Convert path string to normalized Path object."""
147
+ if path.startswith("~"):
148
+ path = os.path.expanduser(path)
149
+ return Path(path).resolve()
150
+
151
+ def _parse_style_config(self, style_config: Optional[str]) -> Dict[str, Any]:
152
+ """Parse and validate style configuration."""
153
+ try:
154
+ if not style_config:
155
+ return self.DEFAULT_STYLES.copy()
156
+
157
+ custom_styles = json.loads(style_config)
158
+ styles = self.DEFAULT_STYLES.copy()
159
+ styles.update(custom_styles)
160
+ return styles
161
+ except json.JSONDecodeError as e:
162
+ logger.error(f"Invalid style configuration JSON: {e}")
163
+ return self.DEFAULT_STYLES.copy()
164
+
165
+ def _escape_latex(self, text: str) -> str:
166
+ """Escape special LaTeX characters."""
167
+ chars = {
168
+ '&': '\\&',
169
+ '%': '\\%',
170
+ '$': '\\$',
171
+ '#': '\\#',
172
+ '_': '\\_',
173
+ '{': '\\{',
174
+ '}': '\\}',
175
+ '~': '\\textasciitilde{}',
176
+ '^': '\\textasciicircum{}',
177
+ '\\': '\\textbackslash{}',
178
+ }
179
+ pattern = '|'.join(map(re.escape, chars.keys()))
180
+ return re.sub(pattern, lambda m: chars[m.group()], text)
181
+
182
+ def _convert_math(self, text: str) -> str:
183
+ """Convert markdown math to LaTeX math."""
184
+ # Inline math
185
+ text = re.sub(r'\$([^$]+)\$', r'$\1$', text)
186
+ # Display math
187
+ text = re.sub(r'\$\$([^$]+)\$\$', r'\\[\1\\]', text)
188
+ return text
189
+
190
+ def _convert_code_blocks(self, text: str) -> str:
191
+ """Convert markdown code blocks to LaTeX listings."""
192
+ def replace_code(match):
193
+ code = match.group(2)
194
+ lang = match.group(1) if match.group(1) else ''
195
+ return (
196
+ f"\\begin{{lstlisting}}[language={lang}, "
197
+ "basicstyle=\\ttfamily\\small, "
198
+ "breaklines=true, "
199
+ "commentstyle=\\color{gray}, "
200
+ "keywordstyle=\\color{blue}, "
201
+ "stringstyle=\\color{green}, "
202
+ "numbers=left, "
203
+ "frame=single]\n"
204
+ f"{code}\n"
205
+ "\\end{lstlisting}\n"
206
+ )
207
+
208
+ return re.sub(
209
+ r'```(\w+)?\n(.*?)\n```',
210
+ replace_code,
211
+ text,
212
+ flags=re.DOTALL
213
+ )
214
+
215
+ def _convert_tables(self, text: str) -> str:
216
+ """Convert markdown tables to LaTeX tables."""
217
+ def convert_table(match):
218
+ lines = match.group(0).split('\n')
219
+ header = lines[0].strip('|').split('|')
220
+ alignment = lines[1].strip('|').split('|')
221
+ data = [line.strip('|').split('|') for line in lines[2:] if line.strip()]
222
+
223
+ # Determine column alignment
224
+ col_align = []
225
+ for col in alignment:
226
+ col = col.strip()
227
+ if col.startswith(':') and col.endswith(':'):
228
+ col_align.append('c')
229
+ elif col.endswith(':'):
230
+ col_align.append('r')
231
+ else:
232
+ col_align.append('l')
233
+
234
+ # Build LaTeX table
235
+ latex = "\\begin{table}[H]\n\\centering\n\\begin{tabular}"
236
+ latex += "{" + "".join(col_align) + "}\n"
237
+ latex += "\\toprule\n"
238
+ latex += " & ".join(h.strip() for h in header) + " \\\\\n"
239
+ latex += "\\midrule\n"
240
+ for row in data:
241
+ latex += " & ".join(cell.strip() for cell in row) + " \\\\\n"
242
+ latex += "\\bottomrule\n"
243
+ latex += "\\end{tabular}\n\\end{table}\n"
244
+
245
+ return latex
246
+
247
+ pattern = r'\|.+\|\n\|[-:| ]+\|\n(?:\|.+\|\n?)+'
248
+ return re.sub(pattern, convert_table, text)
249
+
250
+ def _process_citations(self, text: str, bib_file: Optional[Path]) -> tuple[str, bool]:
251
+ """Process markdown citations and return updated text and citation flag."""
252
+ has_citations = False
253
+
254
+ if bib_file and bib_file.exists():
255
+ # Convert [@citation] to \cite{citation}
256
+ text = re.sub(r'\[@([^\]]+)\]', r'\\cite{\1}', text)
257
+ has_citations = bool(re.search(r'\\cite{', text))
258
+
259
+ return text, has_citations
260
+
261
+ def _process_mermaid_diagrams(self, text: str, output_dir: Path) -> str:
262
+ """Convert Mermaid diagrams to TikZ or image figures."""
263
+ def replace_mermaid(match):
264
+ try:
265
+ diagram = mermaid.generate_diagram(match.group(1))
266
+ img_path = output_dir / f"diagram_{hash(match.group(1))}.pdf"
267
+
268
+ # Save diagram as PDF
269
+ with open(img_path, 'wb') as f:
270
+ f.write(diagram)
271
+
272
+ return (
273
+ "\\begin{figure}[H]\n"
274
+ "\\centering\n"
275
+ f"\\includegraphics[width=0.8\\textwidth]{{{img_path}}}\n"
276
+ "\\caption{Generated diagram}\n"
277
+ "\\end{figure}\n"
278
+ )
279
+ except Exception as e:
280
+ logger.error(f"Error processing Mermaid diagram: {e}")
281
+ return "% Error processing diagram\n"
282
+
283
+ return re.sub(
284
+ r'```mermaid\n(.*?)\n```',
285
+ replace_mermaid,
286
+ text,
287
+ flags=re.DOTALL
288
+ )
289
+
290
+ def execute(self, **kwargs) -> str:
291
+ """Execute the markdown to LaTeX conversion.
292
+
293
+ Args:
294
+ **kwargs: Tool arguments including markdown_content, output_path,
295
+ document_class, bibliography_file, and style_config
296
+
297
+ Returns:
298
+ Success message with output path
299
+ """
300
+ try:
301
+ markdown_content = kwargs['markdown_content']
302
+ output_path = self._normalize_path(kwargs['output_path'])
303
+ document_class = kwargs.get('document_class', 'article')
304
+ bib_file = kwargs.get('bibliography_file')
305
+ style_config = kwargs.get('style_config')
306
+
307
+ # Create output directory
308
+ output_path.parent.mkdir(parents=True, exist_ok=True)
309
+
310
+ # Parse style configuration
311
+ styles = self._parse_style_config(style_config)
312
+
313
+ # Process content
314
+ content = markdown_content
315
+
316
+ # Convert math expressions
317
+ content = self._convert_math(content)
318
+
319
+ # Process citations
320
+ bib_path = self._normalize_path(bib_file) if bib_file else None
321
+ content, has_citations = self._process_citations(content, bib_path)
322
+
323
+ # Convert code blocks
324
+ content = self._convert_code_blocks(content)
325
+
326
+ # Convert tables
327
+ content = self._convert_tables(content)
328
+
329
+ # Process Mermaid diagrams
330
+ content = self._process_mermaid_diagrams(content, output_path.parent)
331
+
332
+ # Extract title
333
+ title_match = re.search(r'^#\s+(.+)$', content, re.MULTILINE)
334
+ title = title_match.group(1) if title_match else 'Document'
335
+ content = re.sub(r'^#\s+(.+)$', r'\\title{\1}\n\\maketitle', content, flags=re.MULTILINE)
336
+
337
+ # Convert headers
338
+ content = re.sub(r'^#{6}\s+(.+)$', r'\\paragraph{\1}', content, flags=re.MULTILINE)
339
+ content = re.sub(r'^#{5}\s+(.+)$', r'\\subsubsection{\1}', content, flags=re.MULTILINE)
340
+ content = re.sub(r'^#{4}\s+(.+)$', r'\\subsection{\1}', content, flags=re.MULTILINE)
341
+ content = re.sub(r'^#{3}\s+(.+)$', r'\\section{\1}', content, flags=re.MULTILINE)
342
+ content = re.sub(r'^#{2}\s+(.+)$', r'\\section{\1}', content, flags=re.MULTILINE)
343
+
344
+ # Convert emphasis
345
+ content = re.sub(r'\*\*(.+?)\*\*', r'\\textbf{\1}', content)
346
+ content = re.sub(r'\*(.+?)\*', r'\\textit{\1}', content)
347
+
348
+ # Convert links
349
+ content = re.sub(r'\[([^\]]+)\]\(([^\)]+)\)', r'\\href{\2}{\1}', content)
350
+
351
+ # Generate package includes
352
+ packages = '\n'.join(f"\\usepackage{{{pkg}}}" for pkg in self.DEFAULT_PACKAGES)
353
+
354
+ # Generate bibliography settings
355
+ bibliography = ""
356
+ if has_citations and bib_path and bib_path.exists():
357
+ bibliography = (
358
+ f"\\bibliographystyle{{{styles['bibliography_style']}}}\n"
359
+ f"\\bibliography{{{bib_path.stem}}}"
360
+ )
361
+
362
+ # Generate final LaTeX
363
+ latex_content = self.DOCUMENT_TEMPLATE.format(
364
+ font_size=styles['font_size'],
365
+ paper_size=styles['paper_size'],
366
+ margin=styles['margin'],
367
+ font_package=styles['font_package'],
368
+ packages=packages,
369
+ title=title,
370
+ line_spacing=styles['line_spacing'],
371
+ extra_preamble="",
372
+ content=content,
373
+ bibliography=bibliography
374
+ )
375
+
376
+ # Write output file
377
+ with open(output_path, 'w', encoding='utf-8') as f:
378
+ f.write(latex_content)
379
+
380
+ return f"Successfully created LaTeX document at: {output_path}"
381
+
382
+ except Exception as e:
383
+ error_msg = f"Error converting markdown to LaTeX: {str(e)}"
384
+ logger.error(error_msg)
385
+ raise RuntimeError(error_msg)
386
+
387
+
388
+ if __name__ == "__main__":
389
+ # Example usage with error handling
390
+ try:
391
+ tool = MarkdownToLatexTool()
392
+ result = tool.execute(
393
+ markdown_content="""
394
+ # Sample Academic Document
395
+
396
+ ## Introduction
397
+
398
+ This is a sample document with an equation:
399
+
400
+ $$E = mc^2$$
401
+
402
+ And a citation [@einstein1905].
403
+
404
+ ```python
405
+ def calculate_energy(mass):
406
+ c = 299792458 # speed of light
407
+ return mass * (c ** 2)
408
+ ```
409
+
410
+ | Column 1 | Column 2 |
411
+ |:---------|----------:|
412
+ | Data 1 | Value 1 |
413
+ | Data 2 | Value 2 |
414
+ """,
415
+ output_path="sample.tex",
416
+ bibliography_file="references.bib"
417
+ )
418
+ print(result)
419
+ except Exception as e:
420
+ print(f"Error: {e}")