memra 0.2.13__py3-none-any.whl → 0.2.15__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 (62) hide show
  1. memra/cli.py +322 -51
  2. {memra-0.2.13.dist-info → memra-0.2.15.dist-info}/METADATA +1 -1
  3. {memra-0.2.13.dist-info → memra-0.2.15.dist-info}/RECORD +7 -61
  4. memra-0.2.15.dist-info/top_level.txt +1 -0
  5. memra-0.2.13.dist-info/top_level.txt +0 -4
  6. memra-ops/app.py +0 -808
  7. memra-ops/config/config.py +0 -25
  8. memra-ops/config.py +0 -34
  9. memra-ops/logic/__init__.py +0 -1
  10. memra-ops/logic/file_tools.py +0 -43
  11. memra-ops/logic/invoice_tools.py +0 -668
  12. memra-ops/logic/invoice_tools_fix.py +0 -66
  13. memra-ops/mcp_bridge_server.py +0 -1178
  14. memra-ops/scripts/check_database.py +0 -37
  15. memra-ops/scripts/clear_database.py +0 -48
  16. memra-ops/scripts/monitor_database.py +0 -67
  17. memra-ops/scripts/release.py +0 -133
  18. memra-ops/scripts/reset_database.py +0 -65
  19. memra-ops/scripts/start_memra.py +0 -334
  20. memra-ops/scripts/stop_memra.py +0 -132
  21. memra-ops/server_tool_registry.py +0 -190
  22. memra-ops/tests/test_llm_text_to_sql.py +0 -115
  23. memra-ops/tests/test_llm_vs_pattern.py +0 -130
  24. memra-ops/tests/test_mcp_schema_aware.py +0 -124
  25. memra-ops/tests/test_schema_aware_sql.py +0 -139
  26. memra-ops/tests/test_schema_aware_sql_simple.py +0 -66
  27. memra-ops/tests/test_text_to_sql_demo.py +0 -140
  28. memra-ops/tools/mcp_bridge_server.py +0 -851
  29. memra-sdk/examples/accounts_payable.py +0 -215
  30. memra-sdk/examples/accounts_payable_client.py +0 -217
  31. memra-sdk/examples/accounts_payable_mcp.py +0 -200
  32. memra-sdk/examples/ask_questions.py +0 -123
  33. memra-sdk/examples/invoice_processing.py +0 -116
  34. memra-sdk/examples/propane_delivery.py +0 -87
  35. memra-sdk/examples/simple_text_to_sql.py +0 -158
  36. memra-sdk/memra/__init__.py +0 -31
  37. memra-sdk/memra/discovery.py +0 -15
  38. memra-sdk/memra/discovery_client.py +0 -49
  39. memra-sdk/memra/execution.py +0 -481
  40. memra-sdk/memra/models.py +0 -99
  41. memra-sdk/memra/tool_registry.py +0 -343
  42. memra-sdk/memra/tool_registry_client.py +0 -106
  43. memra-sdk/scripts/release.py +0 -133
  44. memra-sdk/setup.py +0 -52
  45. memra-workflows/accounts_payable/accounts_payable.py +0 -215
  46. memra-workflows/accounts_payable/accounts_payable_client.py +0 -216
  47. memra-workflows/accounts_payable/accounts_payable_mcp.py +0 -200
  48. memra-workflows/accounts_payable/accounts_payable_smart.py +0 -221
  49. memra-workflows/invoice_processing/invoice_processing.py +0 -116
  50. memra-workflows/invoice_processing/smart_invoice_processor.py +0 -220
  51. memra-workflows/logic/__init__.py +0 -1
  52. memra-workflows/logic/file_tools.py +0 -50
  53. memra-workflows/logic/invoice_tools.py +0 -501
  54. memra-workflows/logic/propane_agents.py +0 -52
  55. memra-workflows/mcp_bridge_server.py +0 -230
  56. memra-workflows/propane_delivery/propane_delivery.py +0 -87
  57. memra-workflows/text_to_sql/complete_invoice_workflow_with_queries.py +0 -208
  58. memra-workflows/text_to_sql/complete_text_to_sql_system.py +0 -266
  59. memra-workflows/text_to_sql/file_discovery_demo.py +0 -156
  60. {memra-0.2.13.dist-info → memra-0.2.15.dist-info}/LICENSE +0 -0
  61. {memra-0.2.13.dist-info → memra-0.2.15.dist-info}/WHEEL +0 -0
  62. {memra-0.2.13.dist-info → memra-0.2.15.dist-info}/entry_points.txt +0 -0
@@ -1,132 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- Memra System Stop Script
4
- Gracefully stops all Memra system dependencies
5
- """
6
-
7
- import os
8
- import sys
9
- import subprocess
10
- import requests
11
- import signal
12
- from pathlib import Path
13
-
14
- class MemraStop:
15
- def __init__(self):
16
- self.project_root = Path(__file__).parent.parent
17
-
18
- def print_banner(self):
19
- """Print stop banner"""
20
- print("=" * 60)
21
- print("🛑 MEMRA SYSTEM SHUTDOWN")
22
- print("=" * 60)
23
- print("Stopping all Memra system dependencies...")
24
- print()
25
-
26
- def stop_mcp_bridge(self):
27
- """Stop the MCP bridge server"""
28
- print("🌉 Stopping MCP Bridge Server...")
29
-
30
- try:
31
- # Try to send a graceful shutdown signal
32
- response = requests.get('http://localhost:8081/health', timeout=5)
33
- if response.status_code == 200:
34
- print(" MCP Bridge Server is running, stopping...")
35
-
36
- # Find and kill the MCP bridge process
37
- result = subprocess.run(['pkill', '-f', 'mcp_bridge_server.py'],
38
- capture_output=True, text=True)
39
-
40
- if result.returncode == 0:
41
- print("✅ MCP Bridge Server stopped")
42
- else:
43
- print("⚠️ MCP Bridge Server process not found (may already be stopped)")
44
- else:
45
- print("✅ MCP Bridge Server is not running")
46
-
47
- except requests.RequestException:
48
- print("✅ MCP Bridge Server is not running")
49
-
50
- def stop_postgresql(self):
51
- """Stop PostgreSQL using Docker Compose"""
52
- print("🐘 Stopping PostgreSQL...")
53
-
54
- try:
55
- # Check if PostgreSQL container is running
56
- result = subprocess.run(['docker', 'ps', '--filter', 'name=memra-postgres'],
57
- capture_output=True, text=True)
58
-
59
- if 'memra-postgres' in result.stdout:
60
- print(" PostgreSQL container is running, stopping...")
61
-
62
- # Stop PostgreSQL container
63
- result = subprocess.run(['docker-compose', 'stop', 'postgres'],
64
- cwd=self.project_root, capture_output=True, text=True)
65
-
66
- if result.returncode == 0:
67
- print("✅ PostgreSQL stopped")
68
- else:
69
- print(f"⚠️ Warning: Failed to stop PostgreSQL: {result.stderr}")
70
- else:
71
- print("✅ PostgreSQL is not running")
72
-
73
- except Exception as e:
74
- print(f"⚠️ Warning: Error stopping PostgreSQL: {e}")
75
-
76
- def cleanup_docker(self):
77
- """Clean up any orphaned Docker containers"""
78
- print("🧹 Cleaning up Docker containers...")
79
-
80
- try:
81
- # Remove any stopped containers
82
- result = subprocess.run(['docker', 'container', 'prune', '-f'],
83
- capture_output=True, text=True)
84
-
85
- if result.returncode == 0:
86
- print("✅ Docker containers cleaned up")
87
- else:
88
- print("⚠️ Warning: Failed to clean up Docker containers")
89
-
90
- except Exception as e:
91
- print(f"⚠️ Warning: Error cleaning up Docker: {e}")
92
-
93
- def stop(self):
94
- """Main stop sequence"""
95
- try:
96
- self.print_banner()
97
-
98
- # Stop MCP bridge server
99
- self.stop_mcp_bridge()
100
-
101
- # Stop PostgreSQL
102
- self.stop_postgresql()
103
-
104
- # Clean up Docker
105
- self.cleanup_docker()
106
-
107
- print("\n" + "=" * 60)
108
- print("✅ MEMRA SYSTEM STOPPED SUCCESSFULLY!")
109
- print("=" * 60)
110
- print("All services have been stopped:")
111
- print(" • MCP Bridge Server")
112
- print(" • PostgreSQL Database")
113
- print(" • Docker containers cleaned up")
114
- print()
115
- print("💡 To start the system again, run:")
116
- print(" ./scripts/start_memra.sh")
117
- print("=" * 60)
118
-
119
- except Exception as e:
120
- print(f"❌ Stop failed: {e}")
121
- return False
122
-
123
- return True
124
-
125
- def main():
126
- """Main entry point"""
127
- stop = MemraStop()
128
- success = stop.stop()
129
- sys.exit(0 if success else 1)
130
-
131
- if __name__ == "__main__":
132
- main()
@@ -1,190 +0,0 @@
1
- import importlib
2
- import logging
3
- import sys
4
- import os
5
- from typing import Dict, Any, List, Optional, Callable
6
- from pathlib import Path
7
-
8
- logger = logging.getLogger(__name__)
9
-
10
- class ServerToolRegistry:
11
- """Server-side registry for managing and executing tools from logic directory"""
12
-
13
- def __init__(self):
14
- self.tools: Dict[str, Dict[str, Any]] = {}
15
- self._add_project_to_path()
16
- self._load_builtin_tools()
17
-
18
- def _add_project_to_path(self):
19
- """Add the project root to Python path so we can import logic modules"""
20
- # Get the directory containing this file (project root)
21
- current_dir = Path(__file__).parent
22
-
23
- if str(current_dir) not in sys.path:
24
- sys.path.insert(0, str(current_dir))
25
-
26
- def _load_builtin_tools(self):
27
- """Load tools from the logic directory"""
28
- try:
29
- # Load invoice tools
30
- from logic.invoice_tools import (
31
- DatabaseQueryTool, PDFProcessor, OCRTool,
32
- InvoiceExtractionWorkflow, DataValidator, PostgresInsert
33
- )
34
-
35
- self.register_tool("DatabaseQueryTool", DatabaseQueryTool, "memra",
36
- "Query database schemas and data")
37
- self.register_tool("PDFProcessor", PDFProcessor, "memra",
38
- "Process PDF files and extract content")
39
- self.register_tool("OCRTool", OCRTool, "memra",
40
- "Perform OCR on images and documents")
41
- self.register_tool("InvoiceExtractionWorkflow", InvoiceExtractionWorkflow, "memra",
42
- "Extract structured data from invoices")
43
- self.register_tool("DataValidator", DataValidator, "memra",
44
- "Validate data against schemas")
45
- self.register_tool("PostgresInsert", PostgresInsert, "memra",
46
- "Insert data into PostgreSQL database")
47
-
48
- # Load file tools
49
- from logic.file_tools import FileReader
50
- self.register_tool("FileReader", FileReader, "memra",
51
- "Read files from the filesystem")
52
-
53
- logger.info(f"Loaded {len(self.tools)} builtin tools")
54
-
55
- except ImportError as e:
56
- logger.warning(f"Could not load some tools: {e}")
57
-
58
- def register_tool(self, name: str, tool_class: type, hosted_by: str, description: str):
59
- """Register a tool in the registry"""
60
- self.tools[name] = {
61
- "class": tool_class,
62
- "hosted_by": hosted_by,
63
- "description": description
64
- }
65
- logger.debug(f"Registered tool: {name} (hosted by {hosted_by})")
66
-
67
- def discover_tools(self, hosted_by: Optional[str] = None) -> List[Dict[str, Any]]:
68
- """Discover available tools, optionally filtered by host"""
69
- tools = []
70
- for name, info in self.tools.items():
71
- if hosted_by is None or info["hosted_by"] == hosted_by:
72
- tools.append({
73
- "name": name,
74
- "hosted_by": info["hosted_by"],
75
- "description": info["description"]
76
- })
77
- return tools
78
-
79
- def execute_tool(self, tool_name: str, hosted_by: str, input_data: Dict[str, Any],
80
- config: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
81
- """Execute a tool with the given input data"""
82
- if tool_name not in self.tools:
83
- return {
84
- "success": False,
85
- "error": f"Tool '{tool_name}' not found in registry"
86
- }
87
-
88
- tool_info = self.tools[tool_name]
89
- if tool_info["hosted_by"] != hosted_by:
90
- return {
91
- "success": False,
92
- "error": f"Tool '{tool_name}' is hosted by '{tool_info['hosted_by']}', not '{hosted_by}'"
93
- }
94
-
95
- try:
96
- # Instantiate tool
97
- tool_class = tool_info["class"]
98
-
99
- # Some tools need credentials/config for initialization
100
- if tool_name in ["DatabaseQueryTool", "PostgresInsert"]:
101
- if "connection" in input_data:
102
- # Parse connection string or use credentials
103
- credentials = self._parse_connection(input_data["connection"])
104
- tool_instance = tool_class(credentials)
105
- else:
106
- return {
107
- "success": False,
108
- "error": f"Tool '{tool_name}' requires database credentials"
109
- }
110
- elif tool_name == "InvoiceExtractionWorkflow":
111
- # This tool needs to be instantiated to initialize the LLM client
112
- tool_instance = tool_class()
113
- else:
114
- tool_instance = tool_class()
115
-
116
- # Execute tool based on its type
117
- result = self._execute_tool_method(tool_instance, tool_name, input_data, config)
118
-
119
- return {
120
- "success": True,
121
- "data": result
122
- }
123
-
124
- except Exception as e:
125
- logger.error(f"Tool execution failed for {tool_name}: {str(e)}")
126
- return {
127
- "success": False,
128
- "error": str(e)
129
- }
130
-
131
- def _execute_tool_method(self, tool_instance: Any, tool_name: str,
132
- input_data: Dict[str, Any], config: Optional[Dict[str, Any]]) -> Dict[str, Any]:
133
- """Execute the appropriate method on the tool instance"""
134
-
135
- if tool_name == "DatabaseQueryTool":
136
- return tool_instance.get_schema("invoices") # Default to invoices table
137
-
138
- elif tool_name == "PDFProcessor":
139
- file_path = input_data.get("file", "")
140
- # Handle both "schema" and "invoice_schema" keys
141
- schema = input_data.get("schema") or input_data.get("invoice_schema", {})
142
- return tool_instance.process_pdf(file_path, schema)
143
-
144
- elif tool_name == "OCRTool":
145
- # Assume PDF processor output is passed as input
146
- return {"extracted_text": tool_instance.extract_text(input_data)}
147
-
148
- elif tool_name == "InvoiceExtractionWorkflow":
149
- text = input_data.get("extracted_text", "")
150
- schema = input_data.get("invoice_schema", {})
151
- return tool_instance.extract_data(text, schema)
152
-
153
- elif tool_name == "DataValidator":
154
- data = input_data.get("invoice_data", {})
155
- schema = input_data.get("invoice_schema", {})
156
- return tool_instance.validate(data, schema)
157
-
158
- elif tool_name == "PostgresInsert":
159
- data = input_data.get("invoice_data", {})
160
- return tool_instance.insert_record("invoices", data)
161
-
162
- elif tool_name == "FileReader":
163
- file_path = config.get("path") if config else input_data.get("file_path")
164
- if not file_path:
165
- raise ValueError("FileReader requires a file path")
166
- return tool_instance.read_file(file_path)
167
-
168
- else:
169
- raise ValueError(f"Unknown tool execution method for {tool_name}")
170
-
171
- def _parse_connection(self, connection_string: str) -> Dict[str, Any]:
172
- """Parse a connection string into credentials"""
173
- # Simple parser for postgres://user:pass@host:port/database
174
- if connection_string.startswith("postgres://"):
175
- # This is a simplified parser - in production you'd use a proper URL parser
176
- parts = connection_string.replace("postgres://", "").split("/")
177
- db_part = parts[1] if len(parts) > 1 else "finance"
178
- auth_host = parts[0].split("@")
179
- host_port = auth_host[1].split(":") if len(auth_host) > 1 else ["localhost", "5432"]
180
- user_pass = auth_host[0].split(":") if len(auth_host) > 1 else ["user", "pass"]
181
-
182
- return {
183
- "host": host_port[0],
184
- "port": int(host_port[1]) if len(host_port) > 1 else 5432,
185
- "database": db_part,
186
- "user": user_pass[0],
187
- "password": user_pass[1] if len(user_pass) > 1 else ""
188
- }
189
-
190
- return {"connection_string": connection_string}
@@ -1,115 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- Test script to verify LLM-based text-to-SQL generation
4
- """
5
-
6
- import requests
7
- import json
8
- import time
9
-
10
- def test_llm_text_to_sql():
11
- """Test the new LLM-based text-to-SQL generation"""
12
- bridge_url = "http://localhost:8081"
13
- bridge_secret = "test-secret-for-development"
14
-
15
- # Test questions to verify LLM vs pattern matching
16
- test_questions = [
17
- "Show me all invoices from Air Liquide",
18
- "What is the total amount of all invoices?",
19
- "How many invoices do we have?",
20
- "Show me the 3 most recent invoices",
21
- "What is the average invoice amount?",
22
- "Find invoices with amounts greater than 1000", # This should test LLM capabilities
23
- "Show me invoices from last month", # This should test LLM capabilities
24
- "Which vendor has the highest total invoice amount?" # Complex query for LLM
25
- ]
26
-
27
- # Schema info for context
28
- schema_info = {
29
- "schema": {
30
- "invoices": {
31
- "columns": [
32
- {"name": "id", "type": "integer"},
33
- {"name": "vendor_name", "type": "text"},
34
- {"name": "invoice_number", "type": "text"},
35
- {"name": "invoice_date", "type": "date"},
36
- {"name": "total_amount", "type": "numeric"},
37
- {"name": "tax_amount", "type": "numeric"},
38
- {"name": "line_items", "type": "jsonb"},
39
- {"name": "status", "type": "text"}
40
- ]
41
- }
42
- }
43
- }
44
-
45
- headers = {
46
- "Content-Type": "application/json",
47
- "X-Bridge-Secret": bridge_secret
48
- }
49
-
50
- print("🧪 Testing LLM-based Text-to-SQL Generation")
51
- print("=" * 60)
52
-
53
- for i, question in enumerate(test_questions, 1):
54
- print(f"\n🎯 Test {i}: {question}")
55
- print("-" * 50)
56
-
57
- # Prepare request
58
- request_data = {
59
- "tool_name": "TextToSQLGenerator",
60
- "input_data": {
61
- "question": question,
62
- "schema_info": schema_info
63
- }
64
- }
65
-
66
- try:
67
- start_time = time.time()
68
-
69
- # Make request to MCP bridge
70
- response = requests.post(
71
- f"{bridge_url}/execute_tool",
72
- json=request_data,
73
- headers=headers,
74
- timeout=30 # Longer timeout for LLM calls
75
- )
76
-
77
- end_time = time.time()
78
- duration = end_time - start_time
79
-
80
- if response.status_code == 200:
81
- result = response.json()
82
-
83
- if result.get("success"):
84
- data = result.get("data", {})
85
- sql_query = data.get("generated_sql", "")
86
- explanation = data.get("explanation", "")
87
- confidence = data.get("confidence", "unknown")
88
- method = data.get("method", "unknown")
89
-
90
- print(f"✅ Success ({duration:.1f}s)")
91
- print(f"📝 SQL: {sql_query}")
92
- print(f"💡 Method: {method}")
93
- print(f"🎯 Confidence: {confidence}")
94
- print(f"📖 Explanation: {explanation}")
95
-
96
- # Highlight if this is using LLM
97
- if method == "llm":
98
- print("🚀 Using LLM generation!")
99
- elif method == "pattern_matching":
100
- print("⚠️ Fallback to pattern matching")
101
-
102
- else:
103
- print(f"❌ Failed: {result.get('error', 'Unknown error')}")
104
-
105
- else:
106
- print(f"❌ HTTP Error: {response.status_code}")
107
- print(f"Response: {response.text}")
108
-
109
- except Exception as e:
110
- print(f"❌ Exception: {str(e)}")
111
-
112
- print(f"\n✨ LLM Text-to-SQL testing completed!")
113
-
114
- if __name__ == "__main__":
115
- test_llm_text_to_sql()
@@ -1,130 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- Test script to demonstrate LLM vs Pattern Matching for text-to-SQL
4
- """
5
-
6
- import requests
7
- import json
8
- import time
9
-
10
- def test_query(question, description=""):
11
- """Test a single query and return results"""
12
- bridge_url = "http://localhost:8081"
13
- bridge_secret = "test-secret-for-development"
14
-
15
- schema_info = {
16
- "schema": {
17
- "invoices": {
18
- "columns": [
19
- {"name": "id", "type": "integer"},
20
- {"name": "vendor_name", "type": "text"},
21
- {"name": "invoice_number", "type": "text"},
22
- {"name": "invoice_date", "type": "date"},
23
- {"name": "total_amount", "type": "numeric"},
24
- {"name": "tax_amount", "type": "numeric"},
25
- {"name": "line_items", "type": "jsonb"},
26
- {"name": "status", "type": "text"}
27
- ]
28
- }
29
- }
30
- }
31
-
32
- headers = {
33
- "Content-Type": "application/json",
34
- "X-Bridge-Secret": bridge_secret
35
- }
36
-
37
- request_data = {
38
- "tool_name": "TextToSQLGenerator",
39
- "input_data": {
40
- "question": question,
41
- "schema_info": schema_info
42
- }
43
- }
44
-
45
- print(f"\n🎯 {description}")
46
- print(f"❓ Question: {question}")
47
- print("-" * 60)
48
-
49
- try:
50
- start_time = time.time()
51
-
52
- response = requests.post(
53
- f"{bridge_url}/execute_tool",
54
- json=request_data,
55
- headers=headers,
56
- timeout=30
57
- )
58
-
59
- end_time = time.time()
60
- duration = end_time - start_time
61
-
62
- if response.status_code == 200:
63
- result = response.json()
64
-
65
- if result.get("success"):
66
- data = result.get("data", {})
67
- sql_query = data.get("generated_sql", "")
68
- method = data.get("method", "unknown")
69
- confidence = data.get("confidence", "unknown")
70
-
71
- print(f"✅ Success ({duration:.1f}s)")
72
- print(f"📝 SQL: {sql_query}")
73
- print(f"💡 Method: {method}")
74
- print(f"🎯 Confidence: {confidence}")
75
-
76
- if method == "llm":
77
- print("🚀 LLM Generation - Advanced natural language understanding!")
78
- elif method == "pattern_matching":
79
- print("⚙️ Pattern Matching - Rule-based fallback")
80
- else:
81
- print("❓ Unknown method")
82
-
83
- return True
84
- else:
85
- print(f"❌ Failed: {result.get('error', 'Unknown error')}")
86
- return False
87
- else:
88
- print(f"❌ HTTP Error: {response.status_code}")
89
- return False
90
-
91
- except Exception as e:
92
- print(f"❌ Exception: {str(e)}")
93
- return False
94
-
95
- def main():
96
- """Test various queries to demonstrate LLM capabilities"""
97
- print("🧪 LLM vs Pattern Matching Text-to-SQL Comparison")
98
- print("=" * 70)
99
-
100
- # Test cases that should showcase LLM capabilities
101
- test_cases = [
102
- ("Show me all invoices from Air Liquide", "Simple vendor filtering (both methods can handle)"),
103
- ("Find invoices with amounts greater than 1000", "Numeric comparison (LLM advantage)"),
104
- ("Show me invoices from last month", "Date range query (LLM advantage)"),
105
- ("Which vendor has the highest total invoice amount?", "Complex aggregation with grouping (LLM advantage)"),
106
- ("Find invoices where the tax amount is more than 10% of total", "Complex calculation (LLM advantage)"),
107
- ("Show me all pending invoices sorted by amount", "Status filtering with sorting (LLM advantage)"),
108
- ("What is the average invoice amount?", "Simple aggregation (both methods can handle)"),
109
- ("List all unique vendors", "Distinct query (both methods can handle)"),
110
- ("Find invoices with line items containing 'software'", "JSON field querying (LLM advantage)"),
111
- ("Show me the top 5 vendors by total invoice value", "Complex grouping and ranking (LLM advantage)")
112
- ]
113
-
114
- success_count = 0
115
- llm_count = 0
116
- pattern_count = 0
117
-
118
- for question, description in test_cases:
119
- if test_query(question, description):
120
- success_count += 1
121
- # Note: We'd need to parse the response to count methods accurately
122
-
123
- print(f"\n✨ Testing completed!")
124
- print(f"📊 {success_count}/{len(test_cases)} queries successful")
125
- print(f"\n🎉 The LLM-based text-to-SQL system is now active!")
126
- print(f"🔄 It automatically falls back to pattern matching if LLM fails")
127
- print(f"🚀 Complex queries now benefit from advanced natural language understanding")
128
-
129
- if __name__ == "__main__":
130
- main()