quantalogic 0.35.0__py3-none-any.whl → 0.40.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.40.0.dist-info}/METADATA +41 -1
  103. quantalogic-0.40.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.40.0.dist-info}/LICENSE +0 -0
  106. {quantalogic-0.35.0.dist-info → quantalogic-0.40.0.dist-info}/WHEEL +0 -0
  107. {quantalogic-0.35.0.dist-info → quantalogic-0.40.0.dist-info}/entry_points.txt +0 -0
@@ -8,16 +8,18 @@ from sqlalchemy.orm import declarative_base, relationship, sessionmaker
8
8
  Base = declarative_base()
9
9
  fake = Faker()
10
10
 
11
+
11
12
  def create_sample_database(db_path: str) -> None:
12
13
  """
13
14
  Creates a sample SQLite database with 5 tables and 10 rows each.
14
-
15
+
15
16
  Args:
16
17
  db_path: Path to the SQLite database file (e.g., 'sample.db')
17
18
  """
19
+
18
20
  # Define database schema
19
21
  class Customer(Base):
20
- __tablename__ = 'customers'
22
+ __tablename__ = "customers"
21
23
  id = Column(Integer, primary_key=True)
22
24
  name = Column(String)
23
25
  email = Column(String)
@@ -25,38 +27,38 @@ def create_sample_database(db_path: str) -> None:
25
27
  orders = relationship("Order", back_populates="customer")
26
28
 
27
29
  class Address(Base):
28
- __tablename__ = 'addresses'
30
+ __tablename__ = "addresses"
29
31
  id = Column(Integer, primary_key=True)
30
32
  street = Column(String)
31
33
  city = Column(String)
32
- customer_id = Column(Integer, ForeignKey('customers.id'))
34
+ customer_id = Column(Integer, ForeignKey("customers.id"))
33
35
  customer = relationship("Customer", back_populates="addresses")
34
36
 
35
37
  class Product(Base):
36
- __tablename__ = 'products'
38
+ __tablename__ = "products"
37
39
  id = Column(Integer, primary_key=True)
38
40
  name = Column(String)
39
41
  price = Column(Float)
40
42
 
41
43
  class Order(Base):
42
- __tablename__ = 'orders'
44
+ __tablename__ = "orders"
43
45
  id = Column(Integer, primary_key=True)
44
46
  order_date = Column(Date)
45
- customer_id = Column(Integer, ForeignKey('customers.id'))
47
+ customer_id = Column(Integer, ForeignKey("customers.id"))
46
48
  customer = relationship("Customer", back_populates="orders")
47
49
  items = relationship("OrderItem", back_populates="order")
48
50
 
49
51
  class OrderItem(Base):
50
- __tablename__ = 'order_items'
52
+ __tablename__ = "order_items"
51
53
  id = Column(Integer, primary_key=True)
52
54
  quantity = Column(Integer)
53
- order_id = Column(Integer, ForeignKey('orders.id'))
54
- product_id = Column(Integer, ForeignKey('products.id'))
55
+ order_id = Column(Integer, ForeignKey("orders.id"))
56
+ product_id = Column(Integer, ForeignKey("products.id"))
55
57
  order = relationship("Order", back_populates="items")
56
58
  product = relationship("Product")
57
59
 
58
60
  # Create database and tables
59
- engine = create_engine(f'sqlite:///{db_path}')
61
+ engine = create_engine(f"sqlite:///{db_path}")
60
62
  Base.metadata.create_all(engine)
61
63
  Session = sessionmaker(bind=engine) # noqa: N806
62
64
  session = Session()
@@ -66,59 +68,43 @@ def create_sample_database(db_path: str) -> None:
66
68
  # Create 10 customers
67
69
  customers = []
68
70
  for _ in range(10):
69
- customer = Customer(
70
- name=fake.name(),
71
- email=fake.email()
72
- )
71
+ customer = Customer(name=fake.name(), email=fake.email())
73
72
  customers.append(customer)
74
73
  session.add(customer)
75
-
74
+
76
75
  session.commit()
77
76
 
78
77
  # Create 10 addresses (1 per customer)
79
78
  for customer in customers:
80
- address = Address(
81
- street=fake.street_address(),
82
- city=fake.city(),
83
- customer=customer
84
- )
79
+ address = Address(street=fake.street_address(), city=fake.city(), customer=customer)
85
80
  session.add(address)
86
-
81
+
87
82
  # Create 10 products
88
83
  products = []
89
84
  for _ in range(10):
90
- product = Product(
91
- name=fake.word().capitalize(),
92
- price=round(random.uniform(10, 1000), 2)
93
- )
85
+ product = Product(name=fake.word().capitalize(), price=round(random.uniform(10, 1000), 2))
94
86
  products.append(product)
95
87
  session.add(product)
96
-
88
+
97
89
  # Create 10 orders (1 per customer)
98
90
  orders = []
99
91
  start_date = datetime.now() - timedelta(days=365)
100
92
  for customer in customers:
101
- order = Order(
102
- order_date=fake.date_between(start_date=start_date),
103
- customer=customer
104
- )
93
+ order = Order(order_date=fake.date_between(start_date=start_date), customer=customer)
105
94
  orders.append(order)
106
95
  session.add(order)
107
-
96
+
108
97
  # Create 10 order items (1 per order)
109
98
  for order in orders:
110
- order_item = OrderItem(
111
- quantity=random.randint(1, 5),
112
- order=order,
113
- product=random.choice(products)
114
- )
99
+ order_item = OrderItem(quantity=random.randint(1, 5), order=order, product=random.choice(products))
115
100
  session.add(order_item)
116
101
 
117
102
  session.commit()
118
103
  finally:
119
104
  session.close()
120
105
 
106
+
121
107
  # Example usage
122
108
  if __name__ == "__main__":
123
109
  create_sample_database("sample.db")
124
- print("Sample database created successfully!")
110
+ print("Sample database created successfully!")
@@ -9,24 +9,20 @@ from sqlalchemy.engine import Inspector
9
9
  def generate_database_report(connection_string: str) -> str:
10
10
  """
11
11
  Generates a comprehensive Markdown database documentation report with ER diagram.
12
-
12
+
13
13
  Args:
14
14
  connection_string: SQLAlchemy-compatible database connection string
15
-
15
+
16
16
  Returns:
17
17
  Markdown-formatted report as a string
18
18
  """
19
19
  # Setup database connection and inspection
20
20
  engine = create_engine(connection_string)
21
21
  inspector = inspect(engine)
22
-
22
+
23
23
  # Collect database metadata
24
- db_metadata = {
25
- 'name': engine.url.database,
26
- 'dialect': engine.dialect.name,
27
- 'tables': inspector.get_table_names()
28
- }
29
-
24
+ db_metadata = {"name": engine.url.database, "dialect": engine.dialect.name, "tables": inspector.get_table_names()}
25
+
30
26
  # Initialize data structures
31
27
  graph = nx.DiGraph()
32
28
  table_metadata: Dict[str, dict] = {}
@@ -35,61 +31,55 @@ def generate_database_report(connection_string: str) -> str:
35
31
  sample_data: Dict[str, list] = {}
36
32
 
37
33
  # Collect schema metadata and relationships
38
- for table in db_metadata['tables']:
34
+ for table in db_metadata["tables"]:
39
35
  columns = inspector.get_columns(table)
40
- pk = inspector.get_pk_constraint(table).get('constrained_columns', [])
36
+ pk = inspector.get_pk_constraint(table).get("constrained_columns", [])
41
37
  indexes = inspector.get_indexes(table)
42
38
  fks = inspector.get_foreign_keys(table)
43
-
39
+
44
40
  # Process foreign keys
45
41
  for fk in fks:
46
42
  process_foreign_key(table, fk, inspector, graph, fk_relationships)
47
-
48
- table_metadata[table] = {
49
- 'columns': columns,
50
- 'primary_keys': pk,
51
- 'indexes': indexes,
52
- 'foreign_keys': fks
53
- }
43
+
44
+ table_metadata[table] = {"columns": columns, "primary_keys": pk, "indexes": indexes, "foreign_keys": fks}
54
45
 
55
46
  # Process tables in dependency order
56
- sorted_tables = get_sorted_tables(graph, db_metadata['tables'])
57
-
47
+ sorted_tables = get_sorted_tables(graph, db_metadata["tables"])
48
+
58
49
  # Collect sample data with parent-child relationships
59
50
  collect_sample_data(engine, sorted_tables, table_metadata, sample_data, sampled_ids)
60
-
51
+
61
52
  # Generate Markdown report
62
- return generate_markdown_report(db_metadata, sorted_tables, table_metadata,
63
- fk_relationships, sample_data)
53
+ return generate_markdown_report(db_metadata, sorted_tables, table_metadata, fk_relationships, sample_data)
64
54
 
65
55
 
66
56
  def process_foreign_key(
67
- table: str,
68
- fk: dict,
69
- inspector: Inspector,
70
- graph: nx.DiGraph,
71
- fk_relationships: List[dict]
57
+ table: str, fk: dict, inspector: Inspector, graph: nx.DiGraph, fk_relationships: List[dict]
72
58
  ) -> None:
73
59
  """Process and record foreign key relationships with cardinality information."""
74
- src_col = fk['constrained_columns'][0]
75
- tgt_table = fk['referred_table']
76
- tgt_col = fk['referred_columns'][0]
77
-
60
+ src_col = fk["constrained_columns"][0]
61
+ tgt_table = fk["referred_table"]
62
+ tgt_col = fk["referred_columns"][0]
63
+
78
64
  # Check uniqueness and nullability in source column
79
65
  src_columns = inspector.get_columns(table)
80
- src_col_meta = next(c for c in src_columns if c['name'] == src_col)
81
- is_unique = src_col_meta.get('unique', False) or src_col in inspector.get_pk_constraint(table).get('constrained_columns', [])
82
- is_nullable = src_col_meta['nullable']
83
-
84
- fk_relationships.append({
85
- 'source_table': table,
86
- 'source_column': src_col,
87
- 'target_table': tgt_table,
88
- 'target_column': tgt_col,
89
- 'constraint_name': fk['name'],
90
- 'is_unique': is_unique,
91
- 'is_nullable': is_nullable
92
- })
66
+ src_col_meta = next(c for c in src_columns if c["name"] == src_col)
67
+ is_unique = src_col_meta.get("unique", False) or src_col in inspector.get_pk_constraint(table).get(
68
+ "constrained_columns", []
69
+ )
70
+ is_nullable = src_col_meta["nullable"]
71
+
72
+ fk_relationships.append(
73
+ {
74
+ "source_table": table,
75
+ "source_column": src_col,
76
+ "target_table": tgt_table,
77
+ "target_column": tgt_col,
78
+ "constraint_name": fk["name"],
79
+ "is_unique": is_unique,
80
+ "is_nullable": is_nullable,
81
+ }
82
+ )
93
83
  graph.add_edge(table, tgt_table)
94
84
 
95
85
 
@@ -106,7 +96,7 @@ def collect_sample_data(
106
96
  tables: List[str],
107
97
  table_metadata: Dict[str, dict],
108
98
  sample_data: Dict[str, list],
109
- sampled_ids: Dict[str, list]
99
+ sampled_ids: Dict[str, list],
110
100
  ) -> None:
111
101
  """Collect sample data while maintaining referential integrity."""
112
102
  for table in tables:
@@ -115,10 +105,10 @@ def collect_sample_data(
115
105
  result = conn.execute(text(f"SELECT * FROM {table} LIMIT 5"))
116
106
  samples = [dict(row._mapping) for row in result]
117
107
  sample_data[table] = samples
118
-
108
+
119
109
  # Store IDs for child sampling
120
- if samples and table_metadata[table]['primary_keys']:
121
- pk_col = table_metadata[table]['primary_keys'][0]
110
+ if samples and table_metadata[table]["primary_keys"]:
111
+ pk_col = table_metadata[table]["primary_keys"][0]
122
112
  sampled_ids[table] = [row[pk_col] for row in samples]
123
113
 
124
114
 
@@ -127,24 +117,24 @@ def generate_markdown_report(
127
117
  tables: List[str],
128
118
  table_metadata: Dict[str, dict],
129
119
  fk_relationships: List[dict],
130
- sample_data: Dict[str, list]
120
+ sample_data: Dict[str, list],
131
121
  ) -> str:
132
122
  """Generate the complete Markdown report."""
133
123
  md = []
134
-
124
+
135
125
  # Database Summary
136
126
  md.append("# Database Documentation Report\n")
137
127
  md.append(f"**Database Type**: {db_metadata['dialect'].capitalize()}\n")
138
128
  md.append(f"**Database Name**: {db_metadata['name']}\n")
139
129
  md.append(f"**Total Tables**: {len(db_metadata['tables'])}\n")
140
130
  md.append(f"**Generated At**: {datetime.now(UTC).strftime('%Y-%m-%d %H:%M:%S UTC')}\n\n")
141
-
131
+
142
132
  # ERD Section
143
133
  md.append("## Entity Relationship Diagram\n")
144
134
  md.append("```mermaid\nerDiagram\n")
145
135
  generate_erd_section(md, tables, table_metadata, fk_relationships)
146
136
  md.append("```\n\n")
147
-
137
+
148
138
  # Schema Details
149
139
  md.append("## Schema Details\n")
150
140
  for table in tables:
@@ -152,48 +142,50 @@ def generate_markdown_report(
152
142
  md.append(f"### {table}\n")
153
143
  generate_columns_section(md, meta)
154
144
  generate_indexes_section(md, meta)
155
-
145
+
156
146
  # Relationships
157
147
  generate_relationships_section(md, fk_relationships)
158
-
148
+
159
149
  # Cardinality Report
160
150
  generate_cardinality_section(md, fk_relationships)
161
-
151
+
162
152
  # Data Samples
163
153
  md.append("## Data Samples\n")
164
154
  for table in tables:
165
155
  samples = sample_data[table]
166
156
  md.append(f"### {table}\n")
167
157
  generate_sample_table(md, samples)
168
-
169
- return '\n'.join(md)
158
+
159
+ return "\n".join(md)
170
160
 
171
161
 
172
- def generate_erd_section(md: List[str], tables: List[str], table_metadata: Dict[str, dict], fk_relationships: List[dict]) -> None:
162
+ def generate_erd_section(
163
+ md: List[str], tables: List[str], table_metadata: Dict[str, dict], fk_relationships: List[dict]
164
+ ) -> None:
173
165
  """Generate Mermaid ER diagram section."""
174
166
  # Define tables with their columns
175
167
  for table in tables:
176
168
  table_upper = table.upper()
177
169
  md.append(f" {table_upper} {{\n")
178
- for col in table_metadata[table]['columns']:
179
- col_type = str(col['type']).split('(')[0].upper() # Simplify type names
170
+ for col in table_metadata[table]["columns"]:
171
+ col_type = str(col["type"]).split("(")[0].upper() # Simplify type names
180
172
  annotations = []
181
- if col['name'] in table_metadata[table]['primary_keys']:
173
+ if col["name"] in table_metadata[table]["primary_keys"]:
182
174
  annotations.append("PK")
183
175
  # Check if column is a foreign key
184
176
  for fk in fk_relationships:
185
- if fk['source_table'] == table and fk['source_column'] == col['name']:
177
+ if fk["source_table"] == table and fk["source_column"] == col["name"]:
186
178
  annotations.append("FK")
187
179
  break
188
180
  annotation_str = " ".join(annotations)
189
181
  md.append(f" {col_type} {col['name']} {annotation_str}\n")
190
182
  md.append(" }\n")
191
-
183
+
192
184
  # Define relationships with cardinality
193
185
  for fk in fk_relationships:
194
- target_table = fk['target_table'].upper()
195
- source_table = fk['source_table'].upper()
196
- source_cardinality = get_source_cardinality(fk['is_unique'], fk['is_nullable'])
186
+ target_table = fk["target_table"].upper()
187
+ source_table = fk["source_table"].upper()
188
+ source_cardinality = get_source_cardinality(fk["is_unique"], fk["is_nullable"])
197
189
  md.append(f" {target_table} ||--{source_cardinality} {source_table} : \"{fk['constraint_name']}\"\n")
198
190
 
199
191
 
@@ -220,17 +212,17 @@ def generate_cardinality_section(md: List[str], fk_relationships: List[dict]) ->
220
212
  """Generate cardinality report section."""
221
213
  cardinalities = {}
222
214
  for fk in fk_relationships:
223
- key = (fk['target_table'], fk['source_table'])
215
+ key = (fk["target_table"], fk["source_table"])
224
216
  if key in cardinalities:
225
217
  continue
226
-
227
- if fk['is_unique']:
218
+
219
+ if fk["is_unique"]:
228
220
  cardinality = "(1) → (1)"
229
221
  else:
230
222
  cardinality = "(1) → (N)"
231
-
223
+
232
224
  cardinalities[key] = f"{fk['target_table']} {cardinality} {fk['source_table']}"
233
-
225
+
234
226
  if cardinalities:
235
227
  md.append("## Cardinality Report\n")
236
228
  for entry in cardinalities.values():
@@ -243,18 +235,18 @@ def generate_columns_section(md: List[str], meta: dict) -> None:
243
235
  md.append("#### Columns\n")
244
236
  md.append("| Column Name | Data Type | Nullable? | Primary Key? |\n")
245
237
  md.append("|-------------|-----------|-----------|--------------|\n")
246
- for col in meta['columns']:
247
- pk = "Yes" if col['name'] in meta['primary_keys'] else "No"
238
+ for col in meta["columns"]:
239
+ pk = "Yes" if col["name"] in meta["primary_keys"] else "No"
248
240
  md.append(f"| `{col['name']}` | {col['type']} | {'Yes' if col['nullable'] else 'No'} | {pk} |\n")
249
241
  md.append("\n")
250
242
 
251
243
 
252
244
  def generate_indexes_section(md: List[str], meta: dict) -> None:
253
245
  """Generate indexes section."""
254
- if meta['indexes']:
246
+ if meta["indexes"]:
255
247
  md.append("#### Indexes\n")
256
- for idx in meta['indexes']:
257
- columns = ", ".join(idx['column_names'])
248
+ for idx in meta["indexes"]:
249
+ columns = ", ".join(idx["column_names"])
258
250
  md.append(f"- `{idx['name']}` ({idx['type'] or 'INDEX'}) → {columns}\n")
259
251
  md.append("\n")
260
252
 
@@ -264,11 +256,11 @@ def generate_sample_table(md: List[str], samples: list) -> None:
264
256
  if not samples:
265
257
  md.append("No records found.\n\n")
266
258
  return
267
-
259
+
268
260
  headers = samples[0].keys()
269
261
  md.append("| " + " | ".join(headers) + " |\n")
270
262
  md.append("|" + "|".join(["---"] * len(headers)) + "|\n")
271
-
263
+
272
264
  for row in samples:
273
265
  values = []
274
266
  for val in row.values():
@@ -282,8 +274,8 @@ def generate_sample_table(md: List[str], samples: list) -> None:
282
274
 
283
275
  if __name__ == "__main__":
284
276
  from quantalogic.tools.utils.create_sample_database import create_sample_database
285
-
277
+
286
278
  # Create and document sample database
287
279
  create_sample_database("sample.db")
288
280
  report = generate_database_report("sqlite:///sample.db")
289
- print(report)
281
+ print(report)
@@ -64,12 +64,12 @@ class WikipediaSearchTool(Tool):
64
64
 
65
65
  def execute(self, query: str, page: Union[str, int] = 1, num_results: Union[str, int] = 10) -> str:
66
66
  """Execute a search query using Wikipedia API and return results.
67
-
67
+
68
68
  Args:
69
69
  query: The search query to execute
70
70
  page: The page number to retrieve (1-based)
71
71
  num_results: Number of results to retrieve per page (1-50)
72
-
72
+
73
73
  Returns:
74
74
  Formatted search results as a string with the following format:
75
75
  ==== Page X of results ====
@@ -80,7 +80,7 @@ class WikipediaSearchTool(Tool):
80
80
  URL
81
81
  Description
82
82
  ==== End of page X ====
83
-
83
+
84
84
  Raises:
85
85
  ValueError: If any parameter is invalid
86
86
  RuntimeError: If API request fails
@@ -96,10 +96,10 @@ class WikipediaSearchTool(Tool):
96
96
 
97
97
  if page < 1:
98
98
  raise ValueError("Page number must be positive")
99
-
99
+
100
100
  if num_results < 1 or num_results > 50:
101
101
  raise ValueError("Number of results must be between 1 and 50")
102
-
102
+
103
103
  try:
104
104
  # Wikipedia API endpoint
105
105
  url = "https://en.wikipedia.org/w/api.php"
@@ -109,33 +109,33 @@ class WikipediaSearchTool(Tool):
109
109
  "list": "search",
110
110
  "srsearch": query,
111
111
  "srlimit": num_results,
112
- "sroffset": (page - 1) * num_results
112
+ "sroffset": (page - 1) * num_results,
113
113
  }
114
-
114
+
115
115
  response = requests.get(url, params=params)
116
116
  response.raise_for_status()
117
117
  data = response.json()
118
-
118
+
119
119
  output = []
120
120
  if "query" in data and "search" in data["query"]:
121
121
  for idx, result in enumerate(data["query"]["search"], 1):
122
122
  title = result.get("title", "No title")
123
123
  snippet = result.get("snippet", "No description")
124
124
  url = f"https://en.wikipedia.org/wiki/{title.replace(' ', '_')}"
125
-
125
+
126
126
  output.append(f"{idx}. {title}")
127
127
  output.append(f" {url}")
128
128
  output.append(f" {snippet}")
129
129
  output.append("")
130
-
130
+
131
131
  if not output:
132
132
  return "No results found"
133
-
133
+
134
134
  output.insert(0, f"==== Page {page} of results ====")
135
135
  output.append(f"==== End of page {page} ====")
136
-
136
+
137
137
  return "\n".join(output)
138
-
138
+
139
139
  except Exception as e:
140
140
  raise RuntimeError(f"Search failed: {str(e)}")
141
141
 
@@ -144,23 +144,19 @@ def main():
144
144
  """Demonstrate WikipediaSearchTool functionality."""
145
145
  try:
146
146
  tool = WikipediaSearchTool()
147
-
147
+
148
148
  # Test basic search functionality
149
149
  print("Testing WikipediaSearchTool with sample query...")
150
- results = tool.execute(
151
- query="Python programming",
152
- page=1,
153
- num_results=3
154
- )
150
+ results = tool.execute(query="Python programming", page=1, num_results=3)
155
151
  print(results)
156
-
152
+
157
153
  # Test error handling
158
154
  print("\nTesting error handling with invalid query...")
159
155
  try:
160
156
  tool.execute(query="")
161
157
  except ValueError as e:
162
158
  print(f"Caught expected ValueError: {e}")
163
-
159
+
164
160
  except Exception as e:
165
161
  print(f"Error in main: {e}")
166
162
 
@@ -9,4 +9,4 @@ def console_ask_for_user_validation(question: str = "Do you want to continue?")
9
9
  """
10
10
  from rich.prompt import Confirm
11
11
 
12
- return Confirm.ask(question, default=True)
12
+ return Confirm.ask(question, default=True)
@@ -0,0 +1,35 @@
1
+ """Utility functions for working with asynchronous code."""
2
+
3
+ import asyncio
4
+ from functools import wraps
5
+ from typing import Any, Callable, Coroutine, TypeVar
6
+
7
+ T = TypeVar("T")
8
+
9
+
10
+ def run_async(func: Callable[..., Coroutine[Any, Any, T]]) -> Callable[..., T]:
11
+ """
12
+ Decorator that runs an async function in a synchronous context.
13
+
14
+ This allows using async functions in sync code by automatically handling
15
+ the event loop management.
16
+
17
+ Args:
18
+ func: The async function to be wrapped
19
+
20
+ Returns:
21
+ A synchronous function that calls the async function
22
+ """
23
+
24
+ @wraps(func)
25
+ def wrapper(*args: Any, **kwargs: Any) -> T:
26
+ try:
27
+ loop = asyncio.get_event_loop()
28
+ except RuntimeError:
29
+ # Create a new event loop if one doesn't exist
30
+ loop = asyncio.new_event_loop()
31
+ asyncio.set_event_loop(loop)
32
+
33
+ return loop.run_until_complete(func(*args, **kwargs))
34
+
35
+ return wrapper
@@ -1,13 +1,12 @@
1
-
2
1
  import requests
3
2
  from packaging import version
4
3
 
5
4
  from quantalogic.version import get_version
6
5
 
7
6
 
8
- def check_if_is_latest_version() -> (bool,str|None):
7
+ def check_if_is_latest_version() -> (bool, str | None):
9
8
  """Check if the current version is the latest version on PyPI.
10
-
9
+
11
10
  Returns:
12
11
  bool: True if the current version is the latest, False otherwise
13
12
  """
@@ -32,7 +31,6 @@ def main():
32
31
  else:
33
32
  print("❌ Could not check version")
34
33
 
34
+
35
35
  if __name__ == "__main__":
36
36
  main()
37
-
38
-
@@ -14,7 +14,8 @@ def get_all_models() -> list[str]:
14
14
  quantalogic_models = set(model_info.keys())
15
15
  return list(litellm_models.union(quantalogic_models))
16
16
 
17
+
17
18
  # Example usage
18
19
  if __name__ == "__main__":
19
20
  models = get_all_models()
20
- print("Supported models:", models)
21
+ print("Supported models:", models)