nlp2sql 0.2.0rc5__tar.gz → 0.2.0rc7__tar.gz

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 (115) hide show
  1. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/.gitignore +1 -0
  2. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/CLAUDE.md +4 -1
  3. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/PKG-INFO +66 -13
  4. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/README.md +65 -12
  5. nlp2sql-0.2.0rc7/assets/nlp2sql-logo.png +0 -0
  6. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/docs/API.md +85 -1
  7. nlp2sql-0.2.0rc7/docs/ARCHITECTURE.md +267 -0
  8. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/docs/CONFIGURATION.md +9 -0
  9. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/examples/README.md +31 -0
  10. nlp2sql-0.2.0rc7/examples/strands-fastapi-agent/.dockerignore +41 -0
  11. nlp2sql-0.2.0rc7/examples/strands-fastapi-agent/.env.example +9 -0
  12. nlp2sql-0.2.0rc7/examples/strands-fastapi-agent/DOCKER_README.md +126 -0
  13. nlp2sql-0.2.0rc7/examples/strands-fastapi-agent/DOCKER_SETUP.md +1529 -0
  14. nlp2sql-0.2.0rc7/examples/strands-fastapi-agent/DOCKER_TESTING_SUMMARY.md +350 -0
  15. nlp2sql-0.2.0rc7/examples/strands-fastapi-agent/Dockerfile +60 -0
  16. nlp2sql-0.2.0rc7/examples/strands-fastapi-agent/INDEX.md +227 -0
  17. nlp2sql-0.2.0rc7/examples/strands-fastapi-agent/README.md +278 -0
  18. nlp2sql-0.2.0rc7/examples/strands-fastapi-agent/docker/init-ecommerce-schema.sql +362 -0
  19. nlp2sql-0.2.0rc7/examples/strands-fastapi-agent/docker-compose.yml +54 -0
  20. nlp2sql-0.2.0rc7/examples/strands-fastapi-agent/main.py +459 -0
  21. nlp2sql-0.2.0rc7/examples/strands-fastapi-agent/pyproject.toml +15 -0
  22. nlp2sql-0.2.0rc7/examples/strands-fastapi-agent/test-docker-endpoints.sh +101 -0
  23. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/mcp_server/__init__.py +1 -1
  24. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/pyproject.toml +1 -1
  25. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/src/nlp2sql/__init__.py +11 -4
  26. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/src/nlp2sql/adapters/anthropic_adapter.py +7 -9
  27. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/src/nlp2sql/adapters/gemini_adapter.py +7 -9
  28. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/src/nlp2sql/adapters/openai_adapter.py +7 -9
  29. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/src/nlp2sql/adapters/postgres_repository.py +34 -28
  30. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/src/nlp2sql/adapters/redshift_adapter.py +32 -26
  31. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/src/nlp2sql/cli.py +2 -0
  32. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/src/nlp2sql/config/settings.py +1 -1
  33. nlp2sql-0.2.0rc7/src/nlp2sql/core/database_prompts.py +48 -0
  34. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/src/nlp2sql/schema/analyzer.py +122 -74
  35. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/src/nlp2sql/schema/embedding_manager.py +32 -36
  36. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/src/nlp2sql/schema/manager.py +23 -30
  37. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/src/nlp2sql/services/query_service.py +112 -1
  38. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/tests/test_batch_scoring.py +258 -32
  39. nlp2sql-0.2.0rc7/tests/test_column_validation.py +180 -0
  40. nlp2sql-0.2.0rc7/tests/test_database_prompts.py +64 -0
  41. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/tests/test_embedding_manager.py +75 -0
  42. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/uv.lock +1 -1
  43. nlp2sql-0.2.0rc5/docs/ARCHITECTURE.md +0 -138
  44. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/.claude/commands/db.md +0 -0
  45. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/.claude/commands/quality.md +0 -0
  46. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/.claude/commands/test.md +0 -0
  47. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/.claude/rules/adapters.md +0 -0
  48. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/.claude/rules/architecture.md +0 -0
  49. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/.claude/rules/python-style.md +0 -0
  50. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/.claude/rules/testing.md +0 -0
  51. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/.claude/settings.json +0 -0
  52. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  53. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  54. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/.github/workflows/publish-pypi.yml +0 -0
  55. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/.github/workflows/publish-testpypi.yml +0 -0
  56. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/.mcp.json.example +0 -0
  57. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/.python-version +0 -0
  58. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/CONTRIBUTING.md +0 -0
  59. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/LICENSE +0 -0
  60. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/SECURITY.md +0 -0
  61. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/benchmarks/enterprise_benchmark_results.json +0 -0
  62. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/docker/.env.example +0 -0
  63. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/docker/README.md +0 -0
  64. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/docker/docker-compose.yml +0 -0
  65. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/docker/get-db-urls.sh +0 -0
  66. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/docker/init-schema.sql +0 -0
  67. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/docker/large-schema.sql +0 -0
  68. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/docker/localstack-init.sh +0 -0
  69. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/docker/test-redshift.sh +0 -0
  70. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/docs/ENTERPRISE.md +0 -0
  71. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/docs/Redshift.md +0 -0
  72. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/examples/advanced/test_multiple_providers.py +0 -0
  73. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/examples/advanced/test_simple_api.py +0 -0
  74. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/examples/database_specific/test_odoo_integration.py +0 -0
  75. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/examples/default_examples.json +0 -0
  76. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/examples/documentation/real_world_example.py +0 -0
  77. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/examples/getting_started/basic_usage.py +0 -0
  78. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/examples/getting_started/simple_demo.py +0 -0
  79. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/examples/getting_started/test_api_setup.py +0 -0
  80. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/examples/schema_management/test_auto_schema.py +0 -0
  81. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/examples/schema_management/test_schema_filters_comprehensive.py +0 -0
  82. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/install-dev.sh +0 -0
  83. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/mcp_server/README.md +0 -0
  84. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/mcp_server/example_config.json +0 -0
  85. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/mcp_server/pyproject.toml +0 -0
  86. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/mcp_server/server.py +0 -0
  87. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/src/nlp2sql/adapters/__init__.py +0 -0
  88. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/src/nlp2sql/adapters/local_embedding_adapter.py +0 -0
  89. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/src/nlp2sql/adapters/openai_embedding_adapter.py +0 -0
  90. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/src/nlp2sql/config/__init__.py +0 -0
  91. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/src/nlp2sql/core/__init__.py +0 -0
  92. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/src/nlp2sql/core/entities.py +0 -0
  93. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/src/nlp2sql/exceptions/__init__.py +0 -0
  94. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/src/nlp2sql/factories/__init__.py +0 -0
  95. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/src/nlp2sql/factories/repository_factory.py +0 -0
  96. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/src/nlp2sql/ports/__init__.py +0 -0
  97. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/src/nlp2sql/ports/ai_provider.py +0 -0
  98. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/src/nlp2sql/ports/cache.py +0 -0
  99. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/src/nlp2sql/ports/embedding_provider.py +0 -0
  100. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/src/nlp2sql/ports/query_optimizer.py +0 -0
  101. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/src/nlp2sql/ports/schema_repository.py +0 -0
  102. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/src/nlp2sql/ports/schema_strategy.py +0 -0
  103. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/src/nlp2sql/schema/__init__.py +0 -0
  104. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/src/nlp2sql/schema/example_loader.py +0 -0
  105. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/src/nlp2sql/schema/example_store.py +0 -0
  106. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/src/nlp2sql/services/__init__.py +0 -0
  107. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/src/nlp2sql/utils/__init__.py +0 -0
  108. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/test_tmp/.gitignore +0 -0
  109. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/tests/mcp/__init__.py +0 -0
  110. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/tests/mcp/test_sql_security.py +0 -0
  111. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/tests/test_basic.py +0 -0
  112. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/tests/test_integration.py +0 -0
  113. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/tests/test_redshift_integration.py +0 -0
  114. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/tests/test_repository_cache.py +0 -0
  115. {nlp2sql-0.2.0rc5 → nlp2sql-0.2.0rc7}/tests/test_repository_factory.py +0 -0
@@ -180,6 +180,7 @@ query_cache.db
180
180
  !.github/**/*.md
181
181
  !.claude/**/*.md
182
182
  !CLAUDE.md
183
+ !examples/**/*.md
183
184
 
184
185
  # Claude Code local settings
185
186
  .claude/settings.local.json
@@ -51,6 +51,8 @@ The codebase follows Clean Architecture (Hexagonal/Ports & Adapters):
51
51
  ```
52
52
  src/nlp2sql/
53
53
  ├── core/ # Business entities (pure Python, no external dependencies)
54
+ │ ├── entities.py # Query, SQLQuery, DatabaseType
55
+ │ └── database_prompts.py # SQL dialect hints for AI providers
54
56
  ├── ports/ # Interfaces/abstractions (contracts)
55
57
  │ ├── ai_provider.py # AIProviderPort - interface for AI providers
56
58
  │ ├── embedding_provider.py # EmbeddingProviderPort - interface for embeddings
@@ -69,7 +71,8 @@ src/nlp2sql/
69
71
  ├── schema/ # Schema management
70
72
  │ ├── schema_manager.py # Coordinates filtering and strategies
71
73
  │ ├── schema_analyzer.py # Scores schema relevance
72
- │ └── schema_embedding_manager.py # FAISS vector embeddings
74
+ │ ├── schema_embedding_manager.py # FAISS vector embeddings
75
+ │ └── example_store.py # ExampleStore - FAISS-indexed few-shot examples
73
76
  ├── config/ # Configuration (Pydantic Settings)
74
77
  ├── exceptions/ # Custom exceptions hierarchy
75
78
  └── cli.py # Click-based CLI
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nlp2sql
3
- Version: 0.2.0rc5
3
+ Version: 0.2.0rc7
4
4
  Summary: Enterprise-ready Natural Language to SQL converter with multi-provider support. Built for production scale (1000+ tables) with Clean Architecture.
5
5
  Project-URL: Homepage, https://github.com/luiscarbonel1991/nlp2sql
6
6
  Project-URL: Bug Tracker, https://github.com/luiscarbonel1991/nlp2sql/issues
@@ -77,11 +77,17 @@ Provides-Extra: oracle
77
77
  Requires-Dist: oracledb>=2.0.0; extra == 'oracle'
78
78
  Description-Content-Type: text/markdown
79
79
 
80
- # nlp2sql
80
+ <p align="center">
81
+ <img src="assets/nlp2sql-logo.png" alt="nlp2sql logo" width="400">
82
+ </p>
83
+
84
+ <p align="center">
85
+ <a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a>
86
+ <a href="https://www.python.org/downloads/"><img src="https://img.shields.io/badge/python-3.9+-blue.svg" alt="Python 3.9+"></a>
87
+ <a href="https://github.com/psf/black"><img src="https://img.shields.io/badge/code%20style-black-000000.svg" alt="Code style: black"></a>
88
+ </p>
81
89
 
82
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
83
- [![Python 3.9+](https://img.shields.io/badge/python-3.9+-blue.svg)](https://www.python.org/downloads/)
84
- [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
90
+ # nlp2sql
85
91
 
86
92
  **Enterprise-ready Natural Language to SQL converter with multi-provider support**
87
93
 
@@ -174,7 +180,33 @@ async def main():
174
180
  result2 = await service.generate_sql("Show recent orders")
175
181
  ```
176
182
 
177
- ### 4. Large Database with Schema Filtering
183
+ ### 4. Few-Shot Examples (Improved Accuracy)
184
+
185
+ ```python
186
+ from nlp2sql import ExampleStore, create_embedding_provider, create_and_initialize_service
187
+
188
+ # Create example store with domain-specific examples
189
+ embedding_provider = create_embedding_provider("openai", api_key=os.getenv("OPENAI_API_KEY"))
190
+ example_store = ExampleStore(embedding_provider=embedding_provider)
191
+ await example_store.add_examples([
192
+ {
193
+ "question": "What was the total revenue last month?",
194
+ "sql": "SELECT SUM(gross_revenue) FROM sales WHERE date >= DATE_TRUNC('month', DATEADD(month, -1, CURRENT_DATE))",
195
+ "database_type": "redshift",
196
+ "metadata": {"category": "aggregation"}
197
+ }
198
+ ])
199
+
200
+ service = await create_and_initialize_service(
201
+ database_url="redshift://user:pass@host:5439/db",
202
+ ai_provider="openai",
203
+ api_key=os.getenv("OPENAI_API_KEY"),
204
+ example_store=example_store
205
+ )
206
+ result = await service.generate_sql("Show me total sales this quarter")
207
+ ```
208
+
209
+ ### 5. Large Database with Schema Filtering
178
210
 
179
211
  ```python
180
212
  from nlp2sql import create_and_initialize_service
@@ -208,6 +240,23 @@ nlp2sql inspect --database-url postgresql://localhost/mydb
208
240
  nlp2sql benchmark --database-url postgresql://localhost/mydb
209
241
  ```
210
242
 
243
+ ## How It Works
244
+
245
+ ```
246
+ Question ──► Cache check ──► Schema retrieval ──► Relevance filtering ──► Context building ──► AI generation ──► Validation
247
+ │ │ │
248
+ SchemaRepository FAISS + TF-IDF hybrid Reuses precomputed
249
+ (+ disk cache) + batch scoring relevance scores
250
+ ```
251
+
252
+ 1. **Schema retrieval** -- Fetches tables from database via `SchemaRepository` (with disk cache for Redshift)
253
+ 2. **Relevance filtering** -- FAISS dense search + TF-IDF sparse search (50/50 hybrid) finds candidate tables; batch scoring refines with precomputed embeddings
254
+ 3. **Context building** -- Builds optimized schema context within token limits, reusing scores from step 2 (zero additional embedding calls)
255
+ 4. **SQL generation** -- AI provider (OpenAI, Anthropic, or Gemini) generates SQL from question + schema context
256
+ 5. **Validation** -- SQL syntax and safety checks before returning results
257
+
258
+ See [Architecture](docs/ARCHITECTURE.md) for the detailed flow with method references and design decisions.
259
+
211
260
  ## Provider Comparison
212
261
 
213
262
  | Provider | Context Size | Best For |
@@ -220,17 +269,21 @@ See [Configuration](docs/CONFIGURATION.md) for detailed provider setup.
220
269
 
221
270
  ## Architecture
222
271
 
272
+ Clean Architecture (Ports & Adapters) with three layers: core entities, port interfaces, and adapter implementations. The schema management layer uses FAISS + TF-IDF hybrid search for relevance filtering at scale.
273
+
223
274
  ```
224
275
  nlp2sql/
225
- ├── core/ # Business entities
226
- ├── ports/ # Interfaces/abstractions
227
- ├── adapters/ # External implementations (AI providers, databases)
228
- ├── services/ # Application services
229
- ├── schema/ # Schema management and embeddings
230
- ├── config/ # Configuration
231
- └── exceptions/ # Custom exceptions
276
+ ├── core/ # Business entities (pure Python, no dependencies)
277
+ ├── ports/ # Interfaces (AIProviderPort, SchemaRepositoryPort, EmbeddingProviderPort)
278
+ ├── adapters/ # Implementations (OpenAI, Anthropic, Gemini, PostgreSQL, Redshift)
279
+ ├── services/ # Orchestration (QueryGenerationService)
280
+ ├── schema/ # Schema management (SchemaManager, SchemaAnalyzer, SchemaEmbeddingManager)
281
+ ├── config/ # Pydantic Settings configuration
282
+ └── exceptions/ # Custom exception hierarchy
232
283
  ```
233
284
 
285
+ See [Architecture](docs/ARCHITECTURE.md) for the full component diagram, data flow, and design decisions.
286
+
234
287
  ## Development
235
288
 
236
289
  ```bash
@@ -1,8 +1,14 @@
1
- # nlp2sql
1
+ <p align="center">
2
+ <img src="assets/nlp2sql-logo.png" alt="nlp2sql logo" width="400">
3
+ </p>
4
+
5
+ <p align="center">
6
+ <a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a>
7
+ <a href="https://www.python.org/downloads/"><img src="https://img.shields.io/badge/python-3.9+-blue.svg" alt="Python 3.9+"></a>
8
+ <a href="https://github.com/psf/black"><img src="https://img.shields.io/badge/code%20style-black-000000.svg" alt="Code style: black"></a>
9
+ </p>
2
10
 
3
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
4
- [![Python 3.9+](https://img.shields.io/badge/python-3.9+-blue.svg)](https://www.python.org/downloads/)
5
- [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
11
+ # nlp2sql
6
12
 
7
13
  **Enterprise-ready Natural Language to SQL converter with multi-provider support**
8
14
 
@@ -95,7 +101,33 @@ async def main():
95
101
  result2 = await service.generate_sql("Show recent orders")
96
102
  ```
97
103
 
98
- ### 4. Large Database with Schema Filtering
104
+ ### 4. Few-Shot Examples (Improved Accuracy)
105
+
106
+ ```python
107
+ from nlp2sql import ExampleStore, create_embedding_provider, create_and_initialize_service
108
+
109
+ # Create example store with domain-specific examples
110
+ embedding_provider = create_embedding_provider("openai", api_key=os.getenv("OPENAI_API_KEY"))
111
+ example_store = ExampleStore(embedding_provider=embedding_provider)
112
+ await example_store.add_examples([
113
+ {
114
+ "question": "What was the total revenue last month?",
115
+ "sql": "SELECT SUM(gross_revenue) FROM sales WHERE date >= DATE_TRUNC('month', DATEADD(month, -1, CURRENT_DATE))",
116
+ "database_type": "redshift",
117
+ "metadata": {"category": "aggregation"}
118
+ }
119
+ ])
120
+
121
+ service = await create_and_initialize_service(
122
+ database_url="redshift://user:pass@host:5439/db",
123
+ ai_provider="openai",
124
+ api_key=os.getenv("OPENAI_API_KEY"),
125
+ example_store=example_store
126
+ )
127
+ result = await service.generate_sql("Show me total sales this quarter")
128
+ ```
129
+
130
+ ### 5. Large Database with Schema Filtering
99
131
 
100
132
  ```python
101
133
  from nlp2sql import create_and_initialize_service
@@ -129,6 +161,23 @@ nlp2sql inspect --database-url postgresql://localhost/mydb
129
161
  nlp2sql benchmark --database-url postgresql://localhost/mydb
130
162
  ```
131
163
 
164
+ ## How It Works
165
+
166
+ ```
167
+ Question ──► Cache check ──► Schema retrieval ──► Relevance filtering ──► Context building ──► AI generation ──► Validation
168
+ │ │ │
169
+ SchemaRepository FAISS + TF-IDF hybrid Reuses precomputed
170
+ (+ disk cache) + batch scoring relevance scores
171
+ ```
172
+
173
+ 1. **Schema retrieval** -- Fetches tables from database via `SchemaRepository` (with disk cache for Redshift)
174
+ 2. **Relevance filtering** -- FAISS dense search + TF-IDF sparse search (50/50 hybrid) finds candidate tables; batch scoring refines with precomputed embeddings
175
+ 3. **Context building** -- Builds optimized schema context within token limits, reusing scores from step 2 (zero additional embedding calls)
176
+ 4. **SQL generation** -- AI provider (OpenAI, Anthropic, or Gemini) generates SQL from question + schema context
177
+ 5. **Validation** -- SQL syntax and safety checks before returning results
178
+
179
+ See [Architecture](docs/ARCHITECTURE.md) for the detailed flow with method references and design decisions.
180
+
132
181
  ## Provider Comparison
133
182
 
134
183
  | Provider | Context Size | Best For |
@@ -141,17 +190,21 @@ See [Configuration](docs/CONFIGURATION.md) for detailed provider setup.
141
190
 
142
191
  ## Architecture
143
192
 
193
+ Clean Architecture (Ports & Adapters) with three layers: core entities, port interfaces, and adapter implementations. The schema management layer uses FAISS + TF-IDF hybrid search for relevance filtering at scale.
194
+
144
195
  ```
145
196
  nlp2sql/
146
- ├── core/ # Business entities
147
- ├── ports/ # Interfaces/abstractions
148
- ├── adapters/ # External implementations (AI providers, databases)
149
- ├── services/ # Application services
150
- ├── schema/ # Schema management and embeddings
151
- ├── config/ # Configuration
152
- └── exceptions/ # Custom exceptions
197
+ ├── core/ # Business entities (pure Python, no dependencies)
198
+ ├── ports/ # Interfaces (AIProviderPort, SchemaRepositoryPort, EmbeddingProviderPort)
199
+ ├── adapters/ # Implementations (OpenAI, Anthropic, Gemini, PostgreSQL, Redshift)
200
+ ├── services/ # Orchestration (QueryGenerationService)
201
+ ├── schema/ # Schema management (SchemaManager, SchemaAnalyzer, SchemaEmbeddingManager)
202
+ ├── config/ # Pydantic Settings configuration
203
+ └── exceptions/ # Custom exception hierarchy
153
204
  ```
154
205
 
206
+ See [Architecture](docs/ARCHITECTURE.md) for the full component diagram, data flow, and design decisions.
207
+
155
208
  ## Development
156
209
 
157
210
  ```bash
Binary file
@@ -4,6 +4,50 @@ Complete reference for the nlp2sql Python API and CLI.
4
4
 
5
5
  ## Python API
6
6
 
7
+ ### Request Flow
8
+
9
+ The three entry points differ in lifecycle and performance characteristics:
10
+
11
+ ```
12
+ generate_sql_from_db(question)
13
+ └─ create_and_initialize_service() # creates service + loads schema every call
14
+ └─ create_query_service() # wires components (AI adapter, repository, embeddings)
15
+ └─ service.initialize() # fetches schema, builds FAISS index (skips if cached on disk)
16
+ └─ service.generate_sql()
17
+
18
+ create_and_initialize_service() # init once, reuse for many queries (recommended)
19
+ └─ create_query_service()
20
+ └─ service.initialize()
21
+ └─ service.generate_sql() # schema + FAISS index already loaded
22
+ └─ service.generate_sql() # in-memory caches warm
23
+
24
+ create_query_service() # full manual control
25
+ └─ caller must call service.initialize() before first query
26
+ ```
27
+
28
+ **When to use which:**
29
+
30
+ | Function | Best for | Schema loading |
31
+ |----------|----------|----------------|
32
+ | `generate_sql_from_db` | One-off scripts, notebooks | Every call (init + query) |
33
+ | `create_and_initialize_service` | APIs, multi-query sessions | Once at startup |
34
+ | `create_query_service` | Custom initialization logic | Manual (`initialize()`) |
35
+
36
+ For multiple queries, always prefer `create_and_initialize_service` -- it loads the schema and FAISS index once, and subsequent `generate_sql()` calls benefit from in-memory caches:
37
+
38
+ ```python
39
+ # Init once, query many times
40
+ service = await create_and_initialize_service(
41
+ database_url="postgresql://user:pass@localhost:5432/db",
42
+ ai_provider="openai",
43
+ api_key=os.getenv("OPENAI_API_KEY"),
44
+ )
45
+
46
+ for question in ["Count users", "Show recent orders", "Revenue by month"]:
47
+ result = await service.generate_sql(question)
48
+ print(result["sql"])
49
+ ```
50
+
7
51
  ### Quick Start Functions
8
52
 
9
53
  #### `generate_sql_from_db`
@@ -36,6 +80,7 @@ print(result['validation']) # Validation results
36
80
  | `api_key` | `str` | Yes | Provider API key |
37
81
  | `embedding_provider_type` | `str` | No | Embedding provider: `local` (default), `openai` |
38
82
  | `schema_filters` | `dict` | No | Schema filtering options |
83
+ | `example_store` | `ExampleStore` | No | Pre-loaded few-shot examples for better SQL generation |
39
84
 
40
85
  **Returns:** `dict` with keys `sql`, `confidence`, `explanation`, `validation`
41
86
 
@@ -65,8 +110,11 @@ result2 = await service.generate_sql("Show recent orders")
65
110
  | `ai_provider` | `str` | Yes | AI provider name |
66
111
  | `api_key` | `str` | Yes | Provider API key |
67
112
  | `schema_filters` | `dict` | No | Schema filtering options |
68
- | `embedding_provider_type` | `str` | No | Embedding provider type |
113
+ | `embedding_provider_type` | `str` | No | Embedding provider type (see note below) |
69
114
  | `database_type` | `DatabaseType` | No | Database type (auto-detected) |
115
+ | `example_store` | `ExampleStore` | No | Pre-loaded few-shot examples for better SQL generation |
116
+
117
+ **Note on `embedding_provider_type`:** This controls how schema relevance filtering works. With `"local"` or `"openai"`, the system builds a FAISS vector index for semantic search (recommended for databases with 50+ tables). With `None` (default), the system attempts to load local embeddings silently and falls back to text-only matching if `sentence-transformers` is not installed. Text-only matching uses exact name, normalized singular/plural, and token overlap -- effective for small schemas but less accurate for large ones.
70
118
 
71
119
  #### `create_query_service`
72
120
 
@@ -95,6 +143,42 @@ result = await service.generate_sql(
95
143
  )
96
144
  ```
97
145
 
146
+ ### Few-Shot Examples
147
+
148
+ Improve SQL accuracy by providing domain-specific examples via `ExampleStore`. Examples are indexed with FAISS for semantic retrieval -- the most relevant examples are automatically included in the LLM prompt.
149
+
150
+ ```python
151
+ from nlp2sql import ExampleStore, create_embedding_provider, create_and_initialize_service
152
+
153
+ # Create example store
154
+ embedding_provider = create_embedding_provider("openai", api_key="your-key")
155
+ example_store = ExampleStore(embedding_provider=embedding_provider)
156
+
157
+ # Add domain-specific examples
158
+ await example_store.add_examples([
159
+ {
160
+ "question": "What was the total revenue last month?",
161
+ "sql": "SELECT SUM(gross_revenue) FROM sales WHERE date >= DATE_TRUNC('month', DATEADD(month, -1, CURRENT_DATE))",
162
+ "database_type": "redshift",
163
+ "metadata": {"category": "aggregation"}
164
+ },
165
+ {
166
+ "question": "Show orders by country",
167
+ "sql": "SELECT country, COUNT(*) AS total_orders FROM orders GROUP BY country ORDER BY total_orders DESC",
168
+ "database_type": "redshift",
169
+ "metadata": {"category": "aggregation"}
170
+ }
171
+ ])
172
+
173
+ # Pass to service
174
+ service = await create_and_initialize_service(
175
+ database_url="redshift://user:pass@host:5439/db",
176
+ ai_provider="openai",
177
+ api_key="your-key",
178
+ example_store=example_store
179
+ )
180
+ ```
181
+
98
182
  ### Embedding Providers
99
183
 
100
184
  ```python
@@ -0,0 +1,267 @@
1
+ # Architecture
2
+
3
+ nlp2sql follows Clean Architecture (Hexagonal/Ports & Adapters) principles, enabling multi-provider support and maintainability at enterprise scale.
4
+
5
+ ## Component Diagram
6
+
7
+ ```mermaid
8
+ graph TB
9
+ subgraph "Public API"
10
+ API[create_query_service<br>generate_sql_from_db]
11
+ end
12
+
13
+ subgraph "Service Layer"
14
+ QGS[QueryGenerationService]
15
+ end
16
+
17
+ subgraph "Schema Management"
18
+ SM[SchemaManager]
19
+ SA[SchemaAnalyzer<br>- relevance scoring<br>- compression]
20
+ SEM[SchemaEmbeddingManager<br>- FAISS index<br>- TF-IDF search]
21
+ end
22
+
23
+ subgraph "Ports (Interfaces)"
24
+ AIP[AIProviderPort]
25
+ SRP[SchemaRepositoryPort]
26
+ EP[EmbeddingProviderPort]
27
+ end
28
+
29
+ subgraph "AI Adapters"
30
+ OA[OpenAI Adapter]
31
+ AA[Anthropic Adapter]
32
+ GA[Gemini Adapter]
33
+ end
34
+
35
+ subgraph "Database Adapters"
36
+ PG[PostgreSQL Repository]
37
+ RS[Redshift Repository]
38
+ end
39
+
40
+ subgraph "Embedding Adapters"
41
+ LE[Local Embeddings<br>sentence-transformers]
42
+ OE[OpenAI Embeddings]
43
+ end
44
+
45
+ API --> QGS
46
+ QGS --> SM
47
+ QGS --> AIP
48
+ SM --> SA
49
+ SM --> SEM
50
+ SM --> SRP
51
+ SEM --> EP
52
+ AIP --> OA
53
+ AIP --> AA
54
+ AIP --> GA
55
+ SRP --> PG
56
+ SRP --> RS
57
+ EP --> LE
58
+ EP --> OE
59
+ ```
60
+
61
+ ## Component Descriptions
62
+
63
+ ### Public API
64
+
65
+ Entry points for the library (`__init__.py`):
66
+
67
+ | Function | Purpose |
68
+ |----------|---------|
69
+ | `generate_sql_from_db` | One-line convenience: creates service, loads schema, generates SQL, returns result |
70
+ | `create_and_initialize_service` | Creates and initializes service once; reuse for multiple queries (recommended) |
71
+ | `create_query_service` | Creates service with full control; caller must call `initialize()` manually |
72
+
73
+ ### Service Layer
74
+
75
+ **QueryGenerationService** (`services/query_service.py`) orchestrates the full pipeline:
76
+
77
+ 1. Check query cache
78
+ 2. Get optimal schema context via `SchemaManager`
79
+ 3. Find relevant examples (from `ExampleStore` or hardcoded fallback)
80
+ 4. Build `QueryContext` and call `AIProvider.generate_query()`
81
+ 5. Optionally optimize via `QueryOptimizerPort`
82
+ 6. Validate SQL via `AIProvider.validate_query()`
83
+ 7. Cache valid results
84
+
85
+ ### Schema Management
86
+
87
+ | Component | File | Responsibility |
88
+ |-----------|------|----------------|
89
+ | **SchemaManager** | `schema/manager.py` | Coordinates filtering, relevance search, and context building |
90
+ | **SchemaAnalyzer** | `schema/analyzer.py` | Scores table/column relevance (text + semantic), compresses schema within token limits |
91
+ | **SchemaEmbeddingManager** | `schema/embedding_manager.py` | Maintains FAISS vector index + TF-IDF sparse index for hybrid search |
92
+
93
+ ### Ports (Interfaces)
94
+
95
+ Abstract contracts in `ports/` that define layer boundaries:
96
+
97
+ - **AIProviderPort** (`ports/ai_provider.py`): `generate_query()`, `validate_query()`
98
+ - **SchemaRepositoryPort** (`ports/schema_repository.py`): `get_tables()`, `get_table_info()`, `get_related_tables()`
99
+ - **EmbeddingProviderPort** (`ports/embedding_provider.py`): `encode()`, `get_embedding_dimension()`
100
+ - **SchemaStrategyPort** (`ports/schema_strategy.py`): `build_context()`, `score_relevance()`, `chunk_schema()`
101
+ - **CachePort** (`ports/cache.py`): `get()`, `set()`, `clear()`
102
+
103
+ ### Adapters
104
+
105
+ Concrete implementations of ports:
106
+
107
+ | Layer | Adapters | Files |
108
+ |-------|----------|-------|
109
+ | AI Providers | OpenAI GPT, Anthropic Claude, Google Gemini | `adapters/openai_adapter.py`, `anthropic_adapter.py`, `gemini_adapter.py` |
110
+ | Databases | PostgreSQL, Amazon Redshift | `adapters/postgres_repository.py`, `redshift_adapter.py` |
111
+ | Embeddings | Local (sentence-transformers), OpenAI | `adapters/local_embedding_adapter.py`, `openai_embedding_adapter.py` |
112
+
113
+ ## Detailed Query Flow
114
+
115
+ ```mermaid
116
+ sequenceDiagram
117
+ participant User
118
+ participant QGS as QueryGenerationService
119
+ participant SM as SchemaManager
120
+ participant SEM as SchemaEmbeddingManager
121
+ participant SA as SchemaAnalyzer
122
+ participant Repo as SchemaRepository
123
+ participant AI as AIProvider
124
+
125
+ User->>QGS: generate_sql(question)
126
+ QGS->>QGS: Check query cache
127
+ QGS->>SM: get_optimal_schema_context(question)
128
+
129
+ SM->>SM: _find_relevant_tables(question)
130
+ SM->>SEM: search_similar_with_embedding(query)
131
+ SEM->>SEM: FAISS dense + TF-IDF sparse (50/50 hybrid)
132
+ SEM-->>SM: (results, query_embedding)
133
+
134
+ SM->>SEM: get_table_embeddings(table_names)
135
+ SEM->>SEM: Reconstruct from FAISS index (zero API calls)
136
+ SEM-->>SM: precomputed_element_embeddings
137
+
138
+ SM->>SA: score_relevance_batch(precomputed embeddings)
139
+ SA->>SA: Text scoring + semantic scoring (zero API calls)
140
+ SA-->>SM: scored tables
141
+
142
+ SM->>Repo: get_table_info(per table)
143
+ Repo-->>SM: TableInfo with columns, keys, indexes
144
+
145
+ Note over SM: Attaches relevance_score to each table dict
146
+
147
+ SM->>SA: build_context(tables with relevance_score)
148
+ SA->>SA: Reuses existing scores (zero API calls)
149
+ SA-->>SM: schema context string
150
+
151
+ SM-->>QGS: schema_context
152
+
153
+ QGS->>AI: generate_query(QueryContext)
154
+ AI-->>QGS: SQL + confidence + explanation
155
+ QGS->>AI: validate_query(sql, schema_context)
156
+ AI-->>QGS: validation result
157
+ QGS-->>User: {sql, confidence, explanation, validation}
158
+ ```
159
+
160
+ ### Step-by-step with file references
161
+
162
+ 1. **`generate_sql()`** (`services/query_service.py`) -- checks query cache, then calls `SchemaManager.get_optimal_schema_context()`
163
+ 2. **`get_optimal_schema_context()`** (`schema/manager.py`) -- checks schema cache, then calls `_find_relevant_tables()`
164
+ 3. **`_find_relevant_tables()`** (`schema/manager.py`) -- two strategies:
165
+ - **Strategy 1**: `SchemaEmbeddingManager.search_similar_with_embedding()` -- FAISS + TF-IDF hybrid search returns candidate tables + the query embedding
166
+ - **Strategy 2**: `SchemaAnalyzer.score_relevance_batch()` -- batch scores token-matched tables using precomputed query + element embeddings (reconstructed from FAISS via `get_table_embeddings()`)
167
+ 4. **`get_table_info()`** (`ports/schema_repository.py`) -- fetches full table metadata; manager attaches `relevance_score` to each table dict
168
+ 5. **`build_context()`** (`schema/analyzer.py`) -- builds schema context string within token limits; reuses pre-attached `relevance_score` (no re-scoring, zero additional embedding calls)
169
+ 6. **`generate_query()`** (`ports/ai_provider.py`) -- AI provider generates SQL from question + schema context
170
+ 7. **`validate_query()`** (`ports/ai_provider.py`) -- validates SQL syntax and safety
171
+
172
+ ## Schema Relevance Pipeline
173
+
174
+ The pipeline in `_find_relevant_tables()` (`schema/manager.py`) uses two complementary strategies to find the most relevant tables for a query:
175
+
176
+ ### Strategy 1: Hybrid FAISS + TF-IDF Search
177
+
178
+ `SchemaEmbeddingManager._search_similar_core()` (`schema/embedding_manager.py`) combines:
179
+
180
+ - **Dense search (FAISS)**: Semantic similarity via vector embeddings (`IndexFlatIP` inner product). Captures meaning (e.g., "revenue" matches "sales_amount").
181
+ - **Sparse search (TF-IDF)**: Exact keyword matching via `TfidfVectorizer` with unigram+bigram features. Captures exact names (e.g., "organizations" matches `organization` table).
182
+ - **Hybrid score**: `0.5 * dense_score + 0.5 * sparse_score`. Equal weighting ensures both semantic meaning and exact keyword matches contribute.
183
+
184
+ This returns candidate tables + the query embedding (for reuse downstream).
185
+
186
+ ### Strategy 2: Batch Scoring with Precomputed Embeddings
187
+
188
+ `SchemaAnalyzer.score_relevance_batch()` (`schema/analyzer.py`) refines candidates:
189
+
190
+ 1. **Text-based scoring** (fast, no API calls): exact name match, normalized singular/plural matching, token overlap, column-name matching
191
+ 2. **Semantic scoring** (only for elements with text score < 0.8): uses precomputed embeddings passed from the manager, avoiding new API calls
192
+ 3. The manager passes both the `query_embedding` from Strategy 1 and `element_embeddings` reconstructed from the FAISS index via `get_table_embeddings()`
193
+
194
+ ### Score Reuse in build_context
195
+
196
+ `SchemaManager.get_optimal_schema_context()` attaches the final `relevance_score` to each table dict before calling `SchemaAnalyzer.build_context()`. The analyzer checks for this key and skips re-scoring when present. This eliminates ~10-15 embedding API calls per query that would otherwise happen in the per-table `score_relevance()` path.
197
+
198
+ **Net result**: ~1 embedding API call per query (for the initial FAISS search), down from ~15-18 without these optimizations.
199
+
200
+ ## Caching Layers
201
+
202
+ The system has multiple caching layers:
203
+
204
+ | Layer | Scope | Storage | TTL |
205
+ |-------|-------|---------|-----|
206
+ | **Query result cache** | Full SQL response | `CachePort` (external) | Indefinite (valid results) |
207
+ | **Schema context cache** | Built schema string | `CachePort` (external) | Per query+db+tokens key |
208
+ | **Table relevance cache** | Relevant tables list | In-memory dict | Per `SchemaManager` instance |
209
+ | **All-tables cache** | Filtered table list | In-memory dict (`_schema_cache`) | Until `refresh_schema()` |
210
+ | **FAISS + TF-IDF index** | Schema embeddings | Disk (pickle + FAISS) | Until `NLP2SQL_SCHEMA_CACHE_TTL_HOURS` expires |
211
+ | **Query embedding cache** | Per-query embeddings | In-memory dict | Cleared per `_find_relevant_tables()` call |
212
+ | **Repository disk cache** | Schema metadata (Redshift) | Pickle file | `NLP2SQL_SCHEMA_CACHE_TTL_HOURS` |
213
+
214
+ ## Design Decisions
215
+
216
+ | Decision | Rationale |
217
+ |----------|-----------|
218
+ | **Ports & Adapters** | Swap AI/DB/embedding providers without touching core logic. Adding a new provider means implementing one port interface. |
219
+ | **FAISS + TF-IDF hybrid (50/50)** | Semantic embeddings alone miss exact keyword matches (e.g., "count organizations" should find `organization` table). TF-IDF catches these. Equal weighting balances both signals. |
220
+ | **Disk-cached FAISS index** | Avoids re-embedding 600+ schema elements on every run. Isolated per database+schema via MD5 hash of `{database_url}:{schema_name}`. |
221
+ | **Precomputed embeddings in batch scoring** | `_find_relevant_tables` reuses the query embedding from FAISS search and reconstructs table embeddings from the index. Result: ~1 embedding API call per query instead of ~15. |
222
+ | **relevance_score passthrough to build_context** | Manager already scored tables during `_find_relevant_tables`; analyzer reuses those scores in `build_context` instead of re-computing them (zero redundant API calls). |
223
+ | **Repository disk cache (Redshift)** | Redshift's `SVV_TABLES`/`SVV_COLUMNS` queries are expensive. Bulk query + pickle cache avoids repeated system catalog scans. |
224
+ | **Graceful embedding degradation** | System works without embeddings (text-based matching only). If `sentence-transformers` is not installed, logs a warning and continues. |
225
+ | **Schema filters applied before indexing** | Filters reduce the number of elements indexed in FAISS, making search faster and more focused. |
226
+
227
+ ## Directory Structure
228
+
229
+ ```
230
+ src/nlp2sql/
231
+ ├── core/ # Business entities (pure Python, no external dependencies)
232
+ │ ├── entities.py # Query, SQLQuery, DatabaseType
233
+ │ └── database_prompts.py # SQL dialect hints for AI providers (centralized)
234
+ ├── ports/ # Interfaces/abstractions
235
+ │ ├── ai_provider.py # AIProviderPort, QueryContext, QueryResponse
236
+ │ ├── schema_repository.py # SchemaRepositoryPort, TableInfo
237
+ │ ├── embedding_provider.py # EmbeddingProviderPort
238
+ │ ├── schema_strategy.py # SchemaStrategyPort, SchemaContext, SchemaChunk
239
+ │ ├── cache.py # CachePort
240
+ │ └── query_optimizer.py # QueryOptimizerPort
241
+ ├── adapters/ # External implementations
242
+ │ ├── openai_adapter.py # OpenAI GPT
243
+ │ ├── anthropic_adapter.py # Anthropic Claude
244
+ │ ├── gemini_adapter.py # Google Gemini
245
+ │ ├── postgres_repository.py # PostgreSQL
246
+ │ ├── redshift_adapter.py # Amazon Redshift (with disk cache)
247
+ │ ├── local_embedding_adapter.py # sentence-transformers
248
+ │ └── openai_embedding_adapter.py # OpenAI embeddings
249
+ ├── services/ # Application services
250
+ │ └── query_service.py # QueryGenerationService (orchestrator)
251
+ ├── schema/ # Schema management
252
+ │ ├── manager.py # SchemaManager (coordinates strategies)
253
+ │ ├── analyzer.py # SchemaAnalyzer (scoring, compression)
254
+ │ ├── embedding_manager.py # SchemaEmbeddingManager (FAISS + TF-IDF)
255
+ │ └── example_store.py # ExampleStore (FAISS-indexed few-shot examples)
256
+ ├── config/ # Configuration
257
+ │ └── settings.py # Pydantic Settings (env vars)
258
+ ├── exceptions/ # Custom exception hierarchy
259
+ └── factories.py # RepositoryFactory
260
+ ```
261
+
262
+ ## Related Documentation
263
+
264
+ - [API Reference](API.md) - Python API and CLI
265
+ - [Configuration](CONFIGURATION.md) - Environment variables and design rationale
266
+ - [Enterprise Guide](ENTERPRISE.md) - Large-scale deployment
267
+ - [Redshift Support](Redshift.md) - Amazon Redshift setup
@@ -55,6 +55,13 @@ export NLP2SQL_EMBEDDING_MODEL=all-MiniLM-L6-v2
55
55
  export NLP2SQL_EMBEDDING_PROVIDER=local
56
56
  ```
57
57
 
58
+ **Design rationale for key variables:**
59
+
60
+ - **`NLP2SQL_MAX_SCHEMA_TOKENS`** -- Limits the schema context sent to the AI provider. Higher values give the AI more schema information (better accuracy for complex joins) but cost more tokens. The default 8000 covers ~20-30 tables with full column definitions. For large schemas with many relevant tables, increase to 12000-16000.
61
+ - **`NLP2SQL_SCHEMA_CACHE_TTL_HOURS`** -- Controls when the FAISS index and schema metadata are refreshed from the database. The 24h default balances freshness against the cost of re-querying system catalogs (especially expensive for Redshift's `SVV_TABLES`/`SVV_COLUMNS`). Set lower in development or when schema changes frequently.
62
+ - **`NLP2SQL_EMBEDDINGS_DIR`** -- Where FAISS index files and TF-IDF metadata are stored on disk. Each database+schema combination gets its own subdirectory (MD5 hash of `{database_url}:{schema_name}`). Must be writable. In containerized environments (e.g., Claude Desktop MCP), the system falls back to `/tmp/nlp2sql_embeddings` if the default `./embeddings` is read-only.
63
+ - **`NLP2SQL_EMBEDDING_MODEL`** -- The `sentence-transformers` model for local embeddings. `all-MiniLM-L6-v2` (384 dimensions) is a good balance of speed and quality. Changing this after an index is built requires clearing the cache (`nlp2sql cache clear --embeddings`) since dimensions must match.
64
+
58
65
  ### General Settings
59
66
 
60
67
  | Variable | Required | Default | Description |
@@ -76,6 +83,8 @@ export TOKENIZERS_PARALLELISM=false
76
83
 
77
84
  Schema filters reduce the database schema to relevant tables for better performance and accuracy.
78
85
 
86
+ **Important:** Filters are applied *before* FAISS indexing (`SchemaManager._apply_schema_filters()` in `schema/manager.py`). Fewer elements indexed means faster semantic search and a more focused index. For enterprise databases (1000+ tables), well-chosen filters can reduce indexing time from minutes to seconds.
87
+
79
88
  ### Filter Options
80
89
 
81
90
  | Filter | Type | Description |