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.
- quantalogic/__init__.py +0 -4
- quantalogic/agent.py +603 -363
- quantalogic/agent_config.py +233 -46
- quantalogic/agent_factory.py +34 -22
- quantalogic/coding_agent.py +16 -14
- quantalogic/config.py +2 -1
- quantalogic/console_print_events.py +4 -8
- quantalogic/console_print_token.py +2 -2
- quantalogic/docs_cli.py +15 -10
- quantalogic/event_emitter.py +258 -83
- quantalogic/flow/__init__.py +23 -0
- quantalogic/flow/flow.py +595 -0
- quantalogic/flow/flow_extractor.py +672 -0
- quantalogic/flow/flow_generator.py +89 -0
- quantalogic/flow/flow_manager.py +407 -0
- quantalogic/flow/flow_manager_schema.py +169 -0
- quantalogic/flow/flow_yaml.md +419 -0
- quantalogic/generative_model.py +109 -77
- quantalogic/get_model_info.py +5 -5
- quantalogic/interactive_text_editor.py +100 -73
- quantalogic/main.py +17 -21
- quantalogic/model_info_list.py +3 -3
- quantalogic/model_info_litellm.py +14 -14
- quantalogic/prompts.py +2 -1
- quantalogic/{llm.py → quantlitellm.py} +29 -39
- quantalogic/search_agent.py +4 -4
- quantalogic/server/models.py +4 -1
- quantalogic/task_file_reader.py +5 -5
- quantalogic/task_runner.py +20 -20
- quantalogic/tool_manager.py +10 -21
- quantalogic/tools/__init__.py +98 -68
- quantalogic/tools/composio/composio.py +416 -0
- quantalogic/tools/{generate_database_report_tool.py → database/generate_database_report_tool.py} +4 -9
- quantalogic/tools/database/sql_query_tool_advanced.py +261 -0
- quantalogic/tools/document_tools/markdown_to_docx_tool.py +620 -0
- quantalogic/tools/document_tools/markdown_to_epub_tool.py +438 -0
- quantalogic/tools/document_tools/markdown_to_html_tool.py +362 -0
- quantalogic/tools/document_tools/markdown_to_ipynb_tool.py +319 -0
- quantalogic/tools/document_tools/markdown_to_latex_tool.py +420 -0
- quantalogic/tools/document_tools/markdown_to_pdf_tool.py +623 -0
- quantalogic/tools/document_tools/markdown_to_pptx_tool.py +319 -0
- quantalogic/tools/duckduckgo_search_tool.py +2 -4
- quantalogic/tools/finance/alpha_vantage_tool.py +440 -0
- quantalogic/tools/finance/ccxt_tool.py +373 -0
- quantalogic/tools/finance/finance_llm_tool.py +387 -0
- quantalogic/tools/finance/google_finance.py +192 -0
- quantalogic/tools/finance/market_intelligence_tool.py +520 -0
- quantalogic/tools/finance/technical_analysis_tool.py +491 -0
- quantalogic/tools/finance/tradingview_tool.py +336 -0
- quantalogic/tools/finance/yahoo_finance.py +236 -0
- quantalogic/tools/git/bitbucket_clone_repo_tool.py +181 -0
- quantalogic/tools/git/bitbucket_operations_tool.py +326 -0
- quantalogic/tools/git/clone_repo_tool.py +189 -0
- quantalogic/tools/git/git_operations_tool.py +532 -0
- quantalogic/tools/google_packages/google_news_tool.py +480 -0
- quantalogic/tools/grep_app_tool.py +123 -186
- quantalogic/tools/{dalle_e.py → image_generation/dalle_e.py} +37 -27
- quantalogic/tools/jinja_tool.py +6 -10
- quantalogic/tools/language_handlers/__init__.py +22 -9
- quantalogic/tools/list_directory_tool.py +131 -42
- quantalogic/tools/llm_tool.py +45 -15
- quantalogic/tools/llm_vision_tool.py +59 -7
- quantalogic/tools/markitdown_tool.py +17 -5
- quantalogic/tools/nasa_packages/models.py +47 -0
- quantalogic/tools/nasa_packages/nasa_apod_tool.py +232 -0
- quantalogic/tools/nasa_packages/nasa_neows_tool.py +147 -0
- quantalogic/tools/nasa_packages/services.py +82 -0
- quantalogic/tools/presentation_tools/presentation_llm_tool.py +396 -0
- quantalogic/tools/product_hunt/product_hunt_tool.py +258 -0
- quantalogic/tools/product_hunt/services.py +63 -0
- quantalogic/tools/rag_tool/__init__.py +48 -0
- quantalogic/tools/rag_tool/document_metadata.py +15 -0
- quantalogic/tools/rag_tool/query_response.py +20 -0
- quantalogic/tools/rag_tool/rag_tool.py +566 -0
- quantalogic/tools/rag_tool/rag_tool_beta.py +264 -0
- quantalogic/tools/read_html_tool.py +24 -38
- quantalogic/tools/replace_in_file_tool.py +10 -10
- quantalogic/tools/safe_python_interpreter_tool.py +10 -24
- quantalogic/tools/search_definition_names.py +2 -2
- quantalogic/tools/sequence_tool.py +14 -23
- quantalogic/tools/sql_query_tool.py +17 -19
- quantalogic/tools/tool.py +39 -15
- quantalogic/tools/unified_diff_tool.py +1 -1
- quantalogic/tools/utilities/csv_processor_tool.py +234 -0
- quantalogic/tools/utilities/download_file_tool.py +179 -0
- quantalogic/tools/utilities/mermaid_validator_tool.py +661 -0
- quantalogic/tools/utils/__init__.py +1 -4
- quantalogic/tools/utils/create_sample_database.py +24 -38
- quantalogic/tools/utils/generate_database_report.py +74 -82
- quantalogic/tools/wikipedia_search_tool.py +17 -21
- quantalogic/utils/ask_user_validation.py +1 -1
- quantalogic/utils/async_utils.py +35 -0
- quantalogic/utils/check_version.py +3 -5
- quantalogic/utils/get_all_models.py +2 -1
- quantalogic/utils/git_ls.py +21 -7
- quantalogic/utils/lm_studio_model_info.py +9 -7
- quantalogic/utils/python_interpreter.py +113 -43
- quantalogic/utils/xml_utility.py +178 -0
- quantalogic/version_check.py +1 -1
- quantalogic/welcome_message.py +7 -7
- quantalogic/xml_parser.py +0 -1
- {quantalogic-0.35.0.dist-info → quantalogic-0.50.0.dist-info}/METADATA +40 -1
- quantalogic-0.50.0.dist-info/RECORD +148 -0
- quantalogic-0.35.0.dist-info/RECORD +0 -102
- {quantalogic-0.35.0.dist-info → quantalogic-0.50.0.dist-info}/LICENSE +0 -0
- {quantalogic-0.35.0.dist-info → quantalogic-0.50.0.dist-info}/WHEEL +0 -0
- {quantalogic-0.35.0.dist-info → quantalogic-0.50.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__ =
|
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__ =
|
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(
|
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__ =
|
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__ =
|
44
|
+
__tablename__ = "orders"
|
43
45
|
id = Column(Integer, primary_key=True)
|
44
46
|
order_date = Column(Date)
|
45
|
-
customer_id = Column(Integer, ForeignKey(
|
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__ =
|
52
|
+
__tablename__ = "order_items"
|
51
53
|
id = Column(Integer, primary_key=True)
|
52
54
|
quantity = Column(Integer)
|
53
|
-
order_id = Column(Integer, ForeignKey(
|
54
|
-
product_id = Column(Integer, ForeignKey(
|
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
|
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
|
-
|
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[
|
34
|
+
for table in db_metadata["tables"]:
|
39
35
|
columns = inspector.get_columns(table)
|
40
|
-
pk = inspector.get_pk_constraint(table).get(
|
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[
|
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[
|
75
|
-
tgt_table = fk[
|
76
|
-
tgt_col = fk[
|
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[
|
81
|
-
is_unique = src_col_meta.get(
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
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][
|
121
|
-
pk_col = table_metadata[table][
|
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
|
158
|
+
|
159
|
+
return "\n".join(md)
|
170
160
|
|
171
161
|
|
172
|
-
def generate_erd_section(
|
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][
|
179
|
-
col_type = str(col[
|
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[
|
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[
|
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[
|
195
|
-
source_table = fk[
|
196
|
-
source_cardinality = get_source_cardinality(fk[
|
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[
|
215
|
+
key = (fk["target_table"], fk["source_table"])
|
224
216
|
if key in cardinalities:
|
225
217
|
continue
|
226
|
-
|
227
|
-
if fk[
|
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[
|
247
|
-
pk = "Yes" if col[
|
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[
|
246
|
+
if meta["indexes"]:
|
255
247
|
md.append("#### Indexes\n")
|
256
|
-
for idx in meta[
|
257
|
-
columns = ", ".join(idx[
|
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
|
|
@@ -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)
|