nlp2sql 0.2.0rc15__tar.gz → 0.2.0rc17__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 (180) hide show
  1. nlp2sql-0.2.0rc17/.github/scripts/verify_release_tag.py +48 -0
  2. nlp2sql-0.2.0rc17/.github/workflows/ci.yml +137 -0
  3. nlp2sql-0.2.0rc17/.github/workflows/publish-pypi.yml +115 -0
  4. nlp2sql-0.2.0rc17/.github/workflows/publish-testpypi.yml +90 -0
  5. nlp2sql-0.2.0rc17/CHANGELOG.md +185 -0
  6. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/CONTRIBUTING.md +16 -0
  7. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/PKG-INFO +2 -1
  8. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/docs/API.md +31 -11
  9. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/docs/CONFIGURATION.md +7 -0
  10. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/mcp_server/__init__.py +1 -1
  11. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/mcp_server/pyproject.toml +2 -2
  12. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/pyproject.toml +2 -1
  13. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/__init__.py +16 -2
  14. nlp2sql-0.2.0rc17/src/nlp2sql/adapters/_provider_errors.py +99 -0
  15. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/adapters/anthropic_adapter.py +58 -19
  16. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/adapters/gemini_adapter.py +66 -32
  17. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/adapters/openai_adapter.py +43 -23
  18. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/adapters/postgres_repository.py +11 -21
  19. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/adapters/redshift_adapter.py +11 -4
  20. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/cli.py +17 -21
  21. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/client.py +56 -20
  22. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/config/settings.py +3 -1
  23. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/core/sql_safety.py +29 -4
  24. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/exceptions/__init__.py +33 -0
  25. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/schema/manager.py +6 -1
  26. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/services/query_service.py +77 -5
  27. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/services/semantic_validation_service.py +24 -10
  28. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/tests/conftest.py +20 -0
  29. nlp2sql-0.2.0rc17/tests/data/gold_questions.json +58 -0
  30. nlp2sql-0.2.0rc17/tests/fakes/__init__.py +1 -0
  31. nlp2sql-0.2.0rc17/tests/fakes/fake_openai.py +152 -0
  32. nlp2sql-0.2.0rc17/tests/test_cli_setup.py +35 -0
  33. nlp2sql-0.2.0rc17/tests/test_connect_embeddings.py +120 -0
  34. nlp2sql-0.2.0rc17/tests/test_cost_per_question.py +117 -0
  35. nlp2sql-0.2.0rc17/tests/test_false_invalid_regression.py +36 -0
  36. nlp2sql-0.2.0rc17/tests/test_first_run.py +56 -0
  37. nlp2sql-0.2.0rc17/tests/test_provider_errors.py +267 -0
  38. nlp2sql-0.2.0rc17/tests/test_query_service_contract.py +150 -0
  39. nlp2sql-0.2.0rc17/tests/test_refresh_schema.py +46 -0
  40. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/tests/test_retrieval_pipeline.py +57 -0
  41. nlp2sql-0.2.0rc17/tests/test_schema_context_cache.py +58 -0
  42. nlp2sql-0.2.0rc17/tests/test_sql_safety.py +40 -0
  43. nlp2sql-0.2.0rc17/tests/test_version_consistency.py +60 -0
  44. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/uv.lock +3 -1
  45. nlp2sql-0.2.0rc15/.github/workflows/ci.yml +0 -84
  46. nlp2sql-0.2.0rc15/.github/workflows/publish-pypi.yml +0 -88
  47. nlp2sql-0.2.0rc15/.github/workflows/publish-testpypi.yml +0 -72
  48. nlp2sql-0.2.0rc15/CHANGELOG.md +0 -89
  49. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/.claude/commands/db.md +0 -0
  50. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/.claude/commands/quality.md +0 -0
  51. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/.claude/commands/test.md +0 -0
  52. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/.claude/rules/adapters.md +0 -0
  53. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/.claude/rules/architecture.md +0 -0
  54. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/.claude/rules/python-style.md +0 -0
  55. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/.claude/rules/testing.md +0 -0
  56. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/.claude/settings.json +0 -0
  57. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  58. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  59. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/.gitignore +0 -0
  60. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/.mcp.json.example +0 -0
  61. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/.python-version +0 -0
  62. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/CLAUDE.md +0 -0
  63. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/LICENSE +0 -0
  64. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/README.md +0 -0
  65. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/SECURITY.md +0 -0
  66. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/assets/nlp2sql-logo.png +0 -0
  67. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/benchmarks/enterprise_benchmark_results.json +0 -0
  68. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/docker/.env.example +0 -0
  69. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/docker/README.md +0 -0
  70. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/docker/docker-compose.yml +0 -0
  71. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/docker/get-db-urls.sh +0 -0
  72. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/docker/init-schema.sql +0 -0
  73. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/docker/large-schema.sql +0 -0
  74. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/docker/localstack-init.sh +0 -0
  75. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/docker/test-redshift.sh +0 -0
  76. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/docs/ARCHITECTURE.md +0 -0
  77. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/docs/ENTERPRISE.md +0 -0
  78. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/docs/Redshift.md +0 -0
  79. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/examples/README.md +0 -0
  80. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/examples/advanced/test_multiple_providers.py +0 -0
  81. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/examples/advanced/test_simple_api.py +0 -0
  82. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/examples/database_specific/test_odoo_integration.py +0 -0
  83. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/examples/default_examples.json +0 -0
  84. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/examples/documentation/real_world_example.py +0 -0
  85. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/examples/getting_started/basic_usage.py +0 -0
  86. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/examples/getting_started/simple_demo.py +0 -0
  87. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/examples/getting_started/test_api_setup.py +0 -0
  88. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/examples/schema_management/test_auto_schema.py +0 -0
  89. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/examples/schema_management/test_schema_filters_comprehensive.py +0 -0
  90. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/examples/strands-fastapi-agent/.dockerignore +0 -0
  91. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/examples/strands-fastapi-agent/.env.example +0 -0
  92. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/examples/strands-fastapi-agent/DOCKER_README.md +0 -0
  93. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/examples/strands-fastapi-agent/DOCKER_SETUP.md +0 -0
  94. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/examples/strands-fastapi-agent/DOCKER_TESTING_SUMMARY.md +0 -0
  95. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/examples/strands-fastapi-agent/Dockerfile +0 -0
  96. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/examples/strands-fastapi-agent/INDEX.md +0 -0
  97. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/examples/strands-fastapi-agent/README.md +0 -0
  98. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/examples/strands-fastapi-agent/docker/init-ecommerce-schema.sql +0 -0
  99. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/examples/strands-fastapi-agent/docker-compose.yml +0 -0
  100. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/examples/strands-fastapi-agent/main.py +0 -0
  101. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/examples/strands-fastapi-agent/pyproject.toml +0 -0
  102. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/examples/strands-fastapi-agent/test-docker-endpoints.sh +0 -0
  103. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/install-dev.sh +0 -0
  104. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/mcp_server/README.md +0 -0
  105. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/mcp_server/example_config.json +0 -0
  106. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/mcp_server/server.py +0 -0
  107. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/adapters/__init__.py +0 -0
  108. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/adapters/default_error_classifier.py +0 -0
  109. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/adapters/default_repair_policy.py +0 -0
  110. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/adapters/dict_semantic_resolver.py +0 -0
  111. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/adapters/file_semantic_resolver.py +0 -0
  112. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/adapters/local_embedding_adapter.py +0 -0
  113. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/adapters/noop_semantic_resolver.py +0 -0
  114. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/adapters/noop_semantic_validator.py +0 -0
  115. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/adapters/openai_embedding_adapter.py +0 -0
  116. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/adapters/pgvector_example_repository.py +0 -0
  117. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/adapters/regex_query_validator.py +0 -0
  118. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/adapters/schema_repository_execution_adapter.py +0 -0
  119. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/config/__init__.py +0 -0
  120. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/core/__init__.py +0 -0
  121. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/core/database_prompts.py +0 -0
  122. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/core/entities.py +0 -0
  123. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/core/provider_config.py +0 -0
  124. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/core/result.py +0 -0
  125. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/core/runtime.py +0 -0
  126. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/core/sql_keywords.py +0 -0
  127. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/factories/__init__.py +0 -0
  128. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/factories/repository_factory.py +0 -0
  129. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/ports/__init__.py +0 -0
  130. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/ports/ai_provider.py +0 -0
  131. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/ports/cache.py +0 -0
  132. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/ports/embedding_provider.py +0 -0
  133. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/ports/error_classifier.py +0 -0
  134. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/ports/example_repository.py +0 -0
  135. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/ports/query_execution.py +0 -0
  136. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/ports/query_optimizer.py +0 -0
  137. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/ports/query_safety.py +0 -0
  138. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/ports/query_validator.py +0 -0
  139. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/ports/repair_policy.py +0 -0
  140. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/ports/schema_repository.py +0 -0
  141. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/ports/schema_strategy.py +0 -0
  142. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/ports/semantic_resolver.py +0 -0
  143. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/ports/semantic_validator.py +0 -0
  144. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/schema/__init__.py +0 -0
  145. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/schema/analyzer.py +0 -0
  146. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/schema/embedding_manager.py +0 -0
  147. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/schema/example_loader.py +0 -0
  148. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/schema/example_store.py +0 -0
  149. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/services/__init__.py +0 -0
  150. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/services/example_selection_service.py +0 -0
  151. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/services/prompt_assembly_service.py +0 -0
  152. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/services/query_analysis_service.py +0 -0
  153. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/services/query_repair_service.py +0 -0
  154. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/services/semantic_resolution_service.py +0 -0
  155. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/services/sql_intent_planning_service.py +0 -0
  156. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/utils/__init__.py +0 -0
  157. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/utils/artifact_loader.py +0 -0
  158. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/utils/helpers.py +0 -0
  159. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/utils/semantic_prompt.py +0 -0
  160. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/src/nlp2sql/utils/storage.py +0 -0
  161. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/test_tmp/.gitignore +0 -0
  162. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/tests/mcp/__init__.py +0 -0
  163. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/tests/mcp/test_sql_security.py +0 -0
  164. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/tests/test_artifact_loader.py +0 -0
  165. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/tests/test_basic.py +0 -0
  166. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/tests/test_batch_scoring.py +0 -0
  167. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/tests/test_cli_semantic_support.py +0 -0
  168. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/tests/test_client_hooks.py +0 -0
  169. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/tests/test_column_validation.py +0 -0
  170. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/tests/test_database_prompts.py +0 -0
  171. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/tests/test_dsl_integration.py +0 -0
  172. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/tests/test_embedding_manager.py +0 -0
  173. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/tests/test_integration.py +0 -0
  174. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/tests/test_model_defaults.py +0 -0
  175. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/tests/test_pgvector_example_repository.py +0 -0
  176. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/tests/test_postgres_integration.py +0 -0
  177. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/tests/test_provider_compat_llm.py +0 -0
  178. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/tests/test_redshift_integration.py +0 -0
  179. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/tests/test_repository_cache.py +0 -0
  180. {nlp2sql-0.2.0rc15 → nlp2sql-0.2.0rc17}/tests/test_repository_factory.py +0 -0
@@ -0,0 +1,48 @@
1
+ """Check that a release tag matches the package version before publishing.
2
+
3
+ The publish workflows run this before building, so a package is only ever
4
+ published from a tag named ``v`` + the ``[project].version`` of pyproject.toml.
5
+
6
+ Usage: python .github/scripts/verify_release_tag.py v0.2.0rc16
7
+ """
8
+
9
+ import argparse
10
+ import re
11
+ import sys
12
+ from pathlib import Path
13
+ from typing import Optional
14
+
15
+ import tomllib
16
+
17
+ TAG_PATTERN = re.compile(r"v\d+\.\d+\.\d+(?:(?:a|b|rc)\d+)?")
18
+ PYPROJECT = Path(__file__).resolve().parents[2] / "pyproject.toml"
19
+ SEE_DOCS = "See 'Releasing (maintainers)' in CONTRIBUTING.md."
20
+
21
+
22
+ def fail(message: str) -> int:
23
+ print(f"::error title=Release tag check failed::{message} {SEE_DOCS}")
24
+ return 1
25
+
26
+
27
+ def main(argv: Optional[list[str]] = None) -> int:
28
+ parser = argparse.ArgumentParser(description="Check that a release tag matches the version in pyproject.toml.")
29
+ parser.add_argument("tag", help="release tag, for example v0.2.0rc16")
30
+ tag = parser.parse_args(argv).tag
31
+
32
+ if not TAG_PATTERN.fullmatch(tag):
33
+ return fail(f"'{tag}' is not a release tag: expected vX.Y.Z, optionally followed by aN, bN or rcN.")
34
+
35
+ with PYPROJECT.open("rb") as file:
36
+ version = tomllib.load(file)["project"]["version"]
37
+ if tag != f"v{version}":
38
+ return fail(
39
+ f"Tag {tag} does not match the pyproject.toml version ({version}) of the checked-out commit. "
40
+ "Was the release created before the version bump was pushed?"
41
+ )
42
+
43
+ print(f"Tag {tag} matches the pyproject.toml version {version}.")
44
+ return 0
45
+
46
+
47
+ if __name__ == "__main__":
48
+ sys.exit(main())
@@ -0,0 +1,137 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main, develop]
6
+ pull_request:
7
+ branches: [main, develop]
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ jobs:
13
+ test:
14
+ runs-on: ubuntu-24.04
15
+
16
+ steps:
17
+ - name: Checkout
18
+ uses: actions/checkout@v7
19
+
20
+ - name: Set up Python 3.12
21
+ uses: actions/setup-python@v7
22
+ with:
23
+ python-version: '3.12'
24
+
25
+ - name: Install uv
26
+ run: python -m pip install uv
27
+
28
+ - name: Install dependencies
29
+ run: |
30
+ uv pip install --system .
31
+ uv pip install --system ".[dev]"
32
+
33
+ - name: Unit tests
34
+ env:
35
+ PYTHONPATH: src
36
+ run: pytest -m "not integration and not llm" -q
37
+
38
+ pgvector-integration:
39
+ runs-on: ubuntu-24.04
40
+
41
+ services:
42
+ postgres:
43
+ image: pgvector/pgvector:pg16
44
+ env:
45
+ POSTGRES_USER: testuser
46
+ POSTGRES_PASSWORD: testpass
47
+ POSTGRES_DB: testdb
48
+ ports:
49
+ - 5432:5432
50
+ options: >-
51
+ --health-cmd "pg_isready -U testuser -d testdb"
52
+ --health-interval 10s
53
+ --health-timeout 5s
54
+ --health-retries 5
55
+
56
+ steps:
57
+ - name: Checkout
58
+ uses: actions/checkout@v7
59
+
60
+ - name: Set up Python 3.12
61
+ uses: actions/setup-python@v7
62
+ with:
63
+ python-version: '3.12'
64
+
65
+ - name: Install uv
66
+ run: python -m pip install uv
67
+
68
+ - name: Install dependencies (with pgvector extra)
69
+ run: |
70
+ uv pip install --system ".[dev]"
71
+ uv pip install --system ".[pgvector]"
72
+
73
+ - name: Load the e-commerce test schema
74
+ env:
75
+ PGPASSWORD: testpass
76
+ run: psql -h localhost -U testuser -d testdb -v ON_ERROR_STOP=1 -q -f docker/init-schema.sql
77
+
78
+ # Scoped suites so the job stays self-contained (the broader integration
79
+ # suite also needs LocalStack Redshift, out of scope here).
80
+ # NLP2SQL_REQUIRE_INTEGRATION makes the fixtures FAIL instead of skip, so a
81
+ # misconfigured service goes red rather than green-via-skip.
82
+ - name: pgvector integration tests
83
+ env:
84
+ PYTHONPATH: src
85
+ NLP2SQL_REQUIRE_INTEGRATION: "1"
86
+ NLP2SQL_TEST_POSTGRES_HOST: localhost
87
+ NLP2SQL_TEST_POSTGRES_PORT: "5432"
88
+ NLP2SQL_TEST_POSTGRES_USER: testuser
89
+ NLP2SQL_TEST_POSTGRES_PASS: testpass
90
+ NLP2SQL_TEST_POSTGRES_DB: testdb
91
+ run: pytest tests/test_pgvector_example_repository.py -m integration -q
92
+
93
+ # README Quick Start per provider, the false-invalid gold set and the cost of
94
+ # one question (LLM calls, tokens, database connections), with the OpenAI SDK
95
+ # pointed at tests/fakes/fake_openai.py (no keys, no cost).
96
+ - name: First-run regression tests
97
+ env:
98
+ PYTHONPATH: src
99
+ NLP2SQL_REQUIRE_INTEGRATION: "1"
100
+ NLP2SQL_TEST_POSTGRES_HOST: localhost
101
+ NLP2SQL_TEST_POSTGRES_PORT: "5432"
102
+ NLP2SQL_TEST_POSTGRES_USER: testuser
103
+ NLP2SQL_TEST_POSTGRES_PASS: testpass
104
+ NLP2SQL_TEST_POSTGRES_DB: testdb
105
+ run: pytest tests/test_first_run.py tests/test_false_invalid_regression.py tests/test_cost_per_question.py -m integration -q
106
+
107
+ # Installs the built wheel the way a user does: fresh environment, no lock
108
+ # file, latest dependency versions. Catches undeclared dependencies (rc13-rc15
109
+ # could not be imported without pyyaml) that the locked dev environment hides.
110
+ clean-install:
111
+ runs-on: ubuntu-24.04
112
+
113
+ steps:
114
+ - name: Checkout
115
+ uses: actions/checkout@v7
116
+
117
+ - name: Set up Python 3.12
118
+ uses: actions/setup-python@v7
119
+ with:
120
+ python-version: '3.12'
121
+
122
+ - name: Build the wheel
123
+ run: |
124
+ python -m pip install build
125
+ python -m build --wheel
126
+
127
+ - name: Install the wheel in a fresh environment
128
+ run: |
129
+ python -m venv "$RUNNER_TEMP/clean"
130
+ "$RUNNER_TEMP/clean/bin/pip" install dist/*.whl
131
+ "$RUNNER_TEMP/clean/bin/pip" list | grep -iE "^(openai|anthropic|google-generativeai|pyyaml) "
132
+
133
+ - name: Import the package and run the CLI
134
+ working-directory: ${{ runner.temp }}
135
+ run: |
136
+ "$RUNNER_TEMP/clean/bin/python" -c "import nlp2sql; print('nlp2sql', nlp2sql.__version__)"
137
+ "$RUNNER_TEMP/clean/bin/nlp2sql" --help
@@ -0,0 +1,115 @@
1
+ name: Publish Python Package to PyPI
2
+
3
+ # Production releases are decided by a tag: run this workflow from the release
4
+ # tag (Run workflow -> Use workflow from -> Tags -> vX.Y.Z). A run from a branch
5
+ # fails before anything is checked out. See "Releasing (maintainers)" in
6
+ # CONTRIBUTING.md.
7
+
8
+ on:
9
+ workflow_dispatch:
10
+ inputs:
11
+ confirm_production:
12
+ description: 'Type "PRODUCTION" to confirm publishing to live PyPI'
13
+ required: true
14
+ default: ''
15
+
16
+ permissions: {}
17
+
18
+ jobs:
19
+ build:
20
+ runs-on: ubuntu-24.04
21
+ permissions:
22
+ contents: read
23
+
24
+ steps:
25
+ - name: Require a tag ref
26
+ run: |
27
+ if [ "$GITHUB_REF_TYPE" != "tag" ]; then
28
+ echo "::error title=Not a tag::Run this workflow from a release tag (Use workflow from -> Tags), not from '$GITHUB_REF_NAME'."
29
+ exit 1
30
+ fi
31
+
32
+ - name: Validate production confirmation
33
+ env:
34
+ CONFIRM_PRODUCTION: ${{ inputs.confirm_production }}
35
+ run: |
36
+ if [ "$CONFIRM_PRODUCTION" != "PRODUCTION" ]; then
37
+ echo "::error::Production confirmation required. Type PRODUCTION to confirm."
38
+ exit 1
39
+ fi
40
+
41
+ - name: Require a published GitHub Release for the tag
42
+ env:
43
+ GH_TOKEN: ${{ github.token }}
44
+ run: |
45
+ is_draft=$(gh release view "$GITHUB_REF_NAME" -R "$GITHUB_REPOSITORY" --json isDraft -q .isDraft) || {
46
+ echo "::error::No GitHub Release found for $GITHUB_REF_NAME. Create and publish the release first."
47
+ exit 1
48
+ }
49
+ if [ "$is_draft" != "false" ]; then
50
+ echo "::error::The GitHub Release for $GITHUB_REF_NAME is still a draft."
51
+ exit 1
52
+ fi
53
+
54
+ - name: Checkout the tag
55
+ uses: actions/checkout@v7
56
+ with:
57
+ persist-credentials: false
58
+
59
+ - name: Set up Python 3.12
60
+ uses: actions/setup-python@v7
61
+ with:
62
+ python-version: '3.12'
63
+
64
+ - name: Verify the tag matches the package version
65
+ run: |
66
+ echo "Tag: $GITHUB_REF_NAME, commit: $GITHUB_SHA"
67
+ python .github/scripts/verify_release_tag.py "$GITHUB_REF_NAME"
68
+
69
+ - name: Install uv
70
+ run: python -m pip install uv
71
+
72
+ - name: Install project dependencies with uv
73
+ run: |
74
+ uv pip install --system .
75
+ uv pip install --system ".[dev]"
76
+
77
+ - name: Install build
78
+ run: python -m pip install build
79
+
80
+ - name: Run tests
81
+ env:
82
+ PYTHONPATH: src
83
+ run: pytest
84
+
85
+ - name: Build package
86
+ run: python -m build
87
+
88
+ - name: Upload distributions
89
+ uses: actions/upload-artifact@v7
90
+ with:
91
+ name: dist
92
+ path: dist/
93
+ if-no-files-found: error
94
+
95
+ publish:
96
+ needs: build
97
+ runs-on: ubuntu-24.04
98
+ environment: pypi
99
+
100
+ # Only this job can request a PyPI token (Trusted Publishing via OIDC), and
101
+ # it runs no project or third-party Python code.
102
+ permissions:
103
+ id-token: write
104
+
105
+ steps:
106
+ - name: Download distributions
107
+ uses: actions/download-artifact@v8
108
+ with:
109
+ name: dist
110
+ path: dist/
111
+
112
+ - name: Publish to PyPI
113
+ uses: pypa/gh-action-pypi-publish@release/v1
114
+ with:
115
+ repository-url: https://upload.pypi.org/legacy/
@@ -0,0 +1,90 @@
1
+ name: Publish Python Package to Test PyPI
2
+
3
+ # Runs automatically on the release tag when a GitHub Release is published, and
4
+ # can be re-run manually from a tag (Use workflow from -> Tags -> vX.Y.Z). A run
5
+ # from a branch fails before anything is checked out. See "Releasing
6
+ # (maintainers)" in CONTRIBUTING.md.
7
+
8
+ on:
9
+ release:
10
+ types: [published]
11
+ workflow_dispatch:
12
+
13
+ permissions: {}
14
+
15
+ jobs:
16
+ build:
17
+ runs-on: ubuntu-24.04
18
+ permissions:
19
+ contents: read
20
+
21
+ steps:
22
+ - name: Require a tag ref
23
+ run: |
24
+ if [ "$GITHUB_REF_TYPE" != "tag" ]; then
25
+ echo "::error title=Not a tag::Run this workflow from a release tag (Use workflow from -> Tags), not from '$GITHUB_REF_NAME'."
26
+ exit 1
27
+ fi
28
+
29
+ - name: Checkout the tag
30
+ uses: actions/checkout@v7
31
+ with:
32
+ persist-credentials: false
33
+
34
+ - name: Set up Python 3.12
35
+ uses: actions/setup-python@v7
36
+ with:
37
+ python-version: '3.12'
38
+
39
+ - name: Verify the tag matches the package version
40
+ run: |
41
+ echo "Tag: $GITHUB_REF_NAME, commit: $GITHUB_SHA"
42
+ python .github/scripts/verify_release_tag.py "$GITHUB_REF_NAME"
43
+
44
+ - name: Install uv
45
+ run: python -m pip install uv
46
+
47
+ - name: Install project dependencies with uv
48
+ run: |
49
+ uv pip install --system .
50
+ uv pip install --system ".[dev]"
51
+
52
+ - name: Install build
53
+ run: python -m pip install build
54
+
55
+ - name: Run tests
56
+ env:
57
+ PYTHONPATH: src
58
+ run: pytest
59
+
60
+ - name: Build package
61
+ run: python -m build
62
+
63
+ - name: Upload distributions
64
+ uses: actions/upload-artifact@v7
65
+ with:
66
+ name: dist
67
+ path: dist/
68
+ if-no-files-found: error
69
+
70
+ publish:
71
+ needs: build
72
+ runs-on: ubuntu-24.04
73
+ environment: testpypi
74
+
75
+ # Only this job can request a Test PyPI token (Trusted Publishing via OIDC),
76
+ # and it runs no project or third-party Python code.
77
+ permissions:
78
+ id-token: write
79
+
80
+ steps:
81
+ - name: Download distributions
82
+ uses: actions/download-artifact@v8
83
+ with:
84
+ name: dist
85
+ path: dist/
86
+
87
+ - name: Publish to Test PyPI
88
+ uses: pypa/gh-action-pypi-publish@release/v1
89
+ with:
90
+ repository-url: https://test.pypi.org/legacy/
@@ -0,0 +1,185 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## Versioning policy
9
+
10
+ - **MAJOR** — incompatible public API changes (removing/renaming a public symbol
11
+ or changing a port/ABC contract in a non-backward-compatible way).
12
+ - **MINOR** — backward-compatible functionality (new adapters, new optional
13
+ parameters, new optional extras).
14
+ - **PATCH** — backward-compatible bug fixes.
15
+
16
+ Pre-1.0 the project ships release candidates (e.g. `0.2.0rcN`). Public surface
17
+ changes — moving/renaming exported symbols, or adding methods to a published
18
+ port such as `ExampleRepositoryPort` — MUST be recorded under `Unreleased`
19
+ before release and called out as **Breaking** if they affect external
20
+ implementers. Backward-compatible shims are preferred over hard breaks while
21
+ pre-1.0.
22
+
23
+ ## [Unreleased]
24
+
25
+ ## [0.2.0rc17] - 2026-10-20
26
+
27
+ ### Changed
28
+ - **User-visible: the LLM no longer reviews its own SQL by default.** Every `ask()`
29
+ made a second LLM call that sent the whole schema context back to the model; it
30
+ doubled the latency of each question and its tokens were not reported. Column,
31
+ dialect, semantic and read-only checks still run, and execution modes still
32
+ validate against the database. Turn the review back on with
33
+ `connect(..., llm_validation=True)`, `NLP2SQL_LLM_VALIDATION=true` or
34
+ `nlp2sql query --llm-validation`. `NLP2SQL.validate(sql)` still asks the LLM.
35
+ - `QueryResult.tokens_used` now counts every LLM call made for the question
36
+ (retries, repairs and LLM validation); it used to report only the last
37
+ generation call. `metadata["token_usage"]` has the breakdown.
38
+ - `refresh_schema()` (PostgreSQL and Redshift) now drops the cached table list so
39
+ the next `get_tables()` reads the database again. PostgreSQL no longer runs
40
+ `ANALYZE` on the whole database on the first `connect()` and every 24 hours.
41
+ - Provider calls are retried only on rate limits, timeouts, connection errors and
42
+ provider-side errors. Authentication and request errors fail on the first
43
+ attempt, instead of after about 8 seconds and 3 requests.
44
+ - An answer from the model that is not the expected JSON (not JSON, not an object,
45
+ or without `sql`) is asked for again once, without waiting; it used to be retried
46
+ up to 3 times with 4-10 second waits. Both answers count in `tokens_used` and in
47
+ `metadata["token_usage"]["llm_calls"]`.
48
+ - An answer cut off at `max_tokens` (OpenAI `finish_reason="length"`, Anthropic
49
+ `stop_reason="max_tokens"`, Gemini `MAX_TOKENS`) fails on the first attempt with a
50
+ `ProviderException` that says to raise `max_tokens`, instead of "Invalid JSON
51
+ response" after 3 attempts. Anthropic refusals and Gemini safety blocks raise
52
+ `ProviderRequestError`.
53
+
54
+ ### Added
55
+ - `llm_validation` option on `connect()`, `create_query_service()` and
56
+ `create_and_initialize_service()`, `NLP2SQL_LLM_VALIDATION`, and
57
+ `nlp2sql query --llm-validation`.
58
+ - `ProviderAuthenticationError`, `ProviderRequestError`, `ProviderRateLimitError`
59
+ and `ProviderUnavailableError`, subclasses of `ProviderException`, raised by the
60
+ OpenAI, Anthropic and Gemini adapters and available as the `__cause__` of the
61
+ `QueryGenerationException` raised by `ask()`.
62
+ - `ProviderResponseFormatError` (subclass of `ProviderException`): the model answered
63
+ twice without the expected JSON. `details["tokens_used"]` has the tokens of both
64
+ answers.
65
+
66
+ ### Fixed
67
+ - Each `ask()` opened 5 new database connections per table in the schema context
68
+ (41 for a multi-table question). The context is now built from the table list
69
+ loaded at `connect()`, with no extra connections.
70
+ - `RedshiftRepository.refresh_schema()` was a no-op.
71
+ - The Anthropic and Gemini adapters raised `TypeError` instead of
72
+ `TokenLimitException` when the context was too large, and dropped the SDK error
73
+ as the cause of generation failures.
74
+ - The Anthropic adapter reported its own errors as "Failed to parse response: ...".
75
+ - `docs/API.md` showed catching `TokenLimitException` and `ProviderException`
76
+ directly from `ask()`; they arrive as the cause of `QueryGenerationException`.
77
+
78
+ ## [0.2.0rc16] - 2026-10-19
79
+
80
+ ### Changed
81
+ - Release: packages are published to PyPI only from a release tag whose name
82
+ matches the `pyproject.toml` version and that has a published GitHub Release.
83
+ The publish workflows build in one job and upload from another, so only the
84
+ upload job can request a PyPI token.
85
+ - CI: runs on `ubuntu-24.04` with the Node 24 versions of `actions/checkout` and
86
+ `actions/setup-python`, and fails when any copy of the package version is out
87
+ of sync with `pyproject.toml`.
88
+ - CI: a `clean-install` job installs the built wheel in a fresh environment
89
+ without the lock file, and the integration lane runs the README Quick Start
90
+ and a false-invalid regression set against a local fake of the OpenAI API.
91
+
92
+ ### Fixed
93
+ - `import nlp2sql` failed on a plain `pip install nlp2sql` since 0.2.0rc13
94
+ (`No module named 'yaml'`): `pyyaml` is now a declared dependency.
95
+ - `connect()` raised `Unknown embedding provider` with
96
+ `ProviderConfig(provider="anthropic")` or `"gemini"`, and with OpenAI failed
97
+ unless `OPENAI_API_KEY` was exported, because the `ProviderConfig` key never
98
+ reached the embedding adapter. Without an explicit embedding provider,
99
+ OpenAI chat now uses OpenAI embeddings with the same key; other providers use
100
+ local embeddings when `nlp2sql[embeddings-local]` is installed and no
101
+ embeddings otherwise. Few-shot examples share that provider, and are skipped
102
+ with a warning when there is none.
103
+ - Correct queries came back with `is_valid=False` when they did not use the
104
+ table the heuristic intent plan guessed from the question (4 of 14 in a
105
+ sample of realistic questions), even with no semantic layer. Plan-derived
106
+ expectations are now warnings; rules from a semantic layer are still errors.
107
+ - `is_valid` was `True` for queries that write (`DELETE ...` in the default
108
+ `generate_only` mode) and for queries whose execution failed. Both are now
109
+ invalid, with the reason in the validation errors.
110
+ - The read-only check rejected valid queries with keywords inside string
111
+ literals (`WHERE name = 'Gift Set'`), and allowed functions with side effects
112
+ such as `pg_terminate_backend`, `set_config`, `nextval` or `dblink`. It now
113
+ ignores literals and rejects those functions.
114
+ - `generate_sql(max_tokens=...)` and the CLI `--max-tokens` (default 1000) also
115
+ capped the schema context sent to the model; the schema budget is now always
116
+ `NLP2SQL_MAX_SCHEMA_TOKENS`.
117
+ - `nlp2sql setup` crashed when configuring a database (it called the
118
+ nonexistent `click.choice`), and offered MySQL and SQLite, which have no
119
+ adapter. It now offers PostgreSQL and Redshift.
120
+
121
+ ## [0.2.0rc15] - 2026-09-18
122
+
123
+ ### Changed
124
+ - Default chat models refreshed to current, non-retired IDs. **User-visible: cost,
125
+ latency and SQL style change for anyone who does not pin `model`.**
126
+ - OpenAI: `gpt-4o-mini` -> `gpt-5.6-luna` (1.05M context, 922K input, 128K output).
127
+ `gpt-4o-mini` remains callable; pin it with `ProviderConfig(model="gpt-4o-mini")`.
128
+ - Anthropic: `claude-sonnet-4-20250514` (retired by Anthropic on 2026-06-15) ->
129
+ `claude-sonnet-5` (1M context, 128K output).
130
+ - Gemini: `gemini-2.0-flash` (shut down by Google on 2026-06-01) ->
131
+ `gemini-3.6-flash` (1,048,576 context, 65,536 output).
132
+ - `ProviderConfig.DEFAULT_MODELS` is now the single source of truth for provider
133
+ defaults; the adapters, `Settings.get_provider_config()` and `nlp2sql providers list`
134
+ read from it.
135
+ - `AnthropicAdapter` no longer sends `temperature` to the Messages API: Claude 4.7+
136
+ models reject non-default sampling parameters (HTTP 400) and the `anthropic` SDK
137
+ removes the keyword in 1.0. `ProviderConfig(temperature=...)` is still accepted for
138
+ Anthropic but ignored (logged at INFO); explicitly pinned older Claude models now run
139
+ at the API default temperature.
140
+ - `OpenAIAdapter` sends `max_completion_tokens` instead of the deprecated `max_tokens`.
141
+ For reasoning models (every model outside the `gpt-3.5`, `gpt-4` and `chatgpt-4o`
142
+ families, including `gpt-5.6-*`) it sets `reasoning_effort` (default `low`, new
143
+ `reasoning_effort` constructor argument) and omits `temperature`; `gpt-4o`/`gpt-4.1`
144
+ keep receiving `temperature`. Response metadata gains `reasoning_effort` and
145
+ `reasoning_tokens`.
146
+ - Unknown model IDs no longer fall back to crippling context limits (OpenAI 8,192,
147
+ Gemini 30,720, Anthropic 100,000). They resolve by model family where possible and
148
+ otherwise fall back to 128,000 / 1,048,576 (Gemini family) / 200,000, logging a
149
+ warning when the adapter is constructed.
150
+ - `OpenAIAdapter` token counting maps the `gpt-5`, `gpt-4.1`, `gpt-4o` and `o1`-`o4`
151
+ families to `o200k_base` (tiktoken has no entry for `gpt-5.6-*`) and falls back to
152
+ `o200k_base`, never `cl100k_base`, for unknown IDs.
153
+ - `GeminiAdapter` reads token usage from `usage_metadata` instead of issuing extra
154
+ `count_tokens` calls after each response.
155
+ - Minimum dependency versions raised: `openai>=1.58.0` (`reasoning_effort`),
156
+ `tiktoken>=0.7.0` (`o200k_base`), `google-generativeai>=0.8.5` (`usage_metadata`).
157
+
158
+ ### Fixed
159
+ - `connect()` / `ask()` failed out of the box for Anthropic and Gemini because their
160
+ default model IDs had been retired by the providers.
161
+ - Reasoning/thinking models that exhaust `max_tokens` before emitting content now raise
162
+ an actionable `ProviderException` instead of "Invalid JSON response" or
163
+ "Failed to parse response".
164
+ - `AnthropicAdapter` and `GeminiAdapter` re-raise `ProviderException` after exhausting
165
+ retries instead of surfacing `tenacity.RetryError`.
166
+
167
+ ## [0.2.0rc14] - 2026-09-16
168
+
169
+ ### Added
170
+ - `PgvectorExampleRepository`: a Postgres + `pgvector` implementation of
171
+ `ExampleRepositoryPort`, an alternative to the on-disk FAISS `ExampleStore`
172
+ for storing and semantically searching few-shot examples. Stores examples in
173
+ a single table with a `namespace` column (per-database isolation via
174
+ `md5(database_url:schema_name)`, byte-identical to `ExampleStore`), cosine
175
+ distance search over an HNSW index, and idempotent `auto_migrate` DDL.
176
+ - Optional `pgvector` extra: `pip install 'nlp2sql[pgvector]'`.
177
+ - CI: a `pgvector-integration` job that runs the adapter's integration tests
178
+ against a `pgvector/pgvector:pg16` service. The job fails (rather than skips)
179
+ when the database or `vector` extension is unavailable
180
+ (`NLP2SQL_REQUIRE_INTEGRATION`).
181
+
182
+ ### Changed
183
+ - `docker/docker-compose.yml`: the `postgres` service image is now
184
+ `pgvector/pgvector:pg16` (drop-in replacement for `postgres:16`, adds the
185
+ `vector` extension) so integration tests run out of the box.
@@ -233,6 +233,22 @@ Brief description of changes and why they're needed.
233
233
  4. **Documentation review** for user-facing changes
234
234
  5. **Merge** after approval
235
235
 
236
+ ## Releasing (maintainers)
237
+
238
+ Packages are published only from a release tag. Both publish workflows refuse to run from a branch and check that the tag name is `v` followed by the `version` in `pyproject.toml`, so the latest commit on `main` never goes to PyPI by accident.
239
+
240
+ 1. **Bump the version** in every copy and move the `CHANGELOG.md` entries from `[Unreleased]` to a new `## [X.Y.Z] - YYYY-MM-DD` section. The copies are `pyproject.toml`, `src/nlp2sql/__init__.py`, `src/nlp2sql/config/settings.py`, `mcp_server/pyproject.toml` (`[project]` and `[tool.mcp]`), `mcp_server/__init__.py` and `uv.lock` (run `uv lock`). `tests/test_version_consistency.py` fails if any of them is out of sync.
241
+ 2. **Push the bump to `main` before creating the release.** The new tag points to whatever `main` is at that moment. To check a tag name against the current version, run `python .github/scripts/verify_release_tag.py vX.Y.Z`.
242
+ 3. **Create the GitHub Release** with a new tag `vX.Y.Z` targeting `main`. Publishing the release runs "Publish Python Package to Test PyPI" on that tag automatically.
243
+ 4. **Run the integration tests locally** on the tagged commit, with the test databases running (see [Development Setup](#development-setup)).
244
+ 5. **Publish to PyPI**: in **Actions**, open **Publish Python Package to PyPI**, click **Run workflow**, pick the tag under **Use workflow from** (Tags tab) and type `PRODUCTION`. From the command line:
245
+
246
+ ```bash
247
+ gh workflow run publish-pypi.yml --ref vX.Y.Z -f confirm_production=PRODUCTION
248
+ ```
249
+
250
+ The PyPI workflow stops before building if the run is not on a tag, if the tag has no published GitHub Release, or if the tag does not match the package version. Only its final job, which runs no project code, can request a PyPI token.
251
+
236
252
  ## Enterprise Contribution Guidelines
237
253
 
238
254
  ### Performance Considerations
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: nlp2sql
3
- Version: 0.2.0rc15
3
+ Version: 0.2.0rc17
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
@@ -36,6 +36,7 @@ Requires-Dist: psycopg2-binary>=2.9.0
36
36
  Requires-Dist: psycopg[binary]>=3.2.13
37
37
  Requires-Dist: pydantic-settings>=2.0.0
38
38
  Requires-Dist: pydantic>=2.5.0
39
+ Requires-Dist: pyyaml>=6.0
39
40
  Requires-Dist: redis>=5.0.0
40
41
  Requires-Dist: scikit-learn>=1.3.0
41
42
  Requires-Dist: sqlalchemy-redshift>=0.7.0