sqlseed-ai 0.2.5__tar.gz → 0.2.6__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.
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/.gitignore +10 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/PKG-INFO +26 -8
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/README.md +25 -7
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/README.zh-CN.md +22 -8
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/src/sqlseed_ai/_hardware.py +65 -14
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/src/sqlseed_ai/analyzer/_caller.py +2 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/src/sqlseed_ai/analyzer/_streaming.py +8 -3
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/src/sqlseed_ai/config.py +6 -1
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/src/sqlseed_ai/mcp.py +25 -6
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/src/sqlseed_ai/refiner.py +4 -3
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/test_ai_hardware.py +154 -4
- sqlseed_ai-0.2.6/tests/test_analyzer_client_lifecycle.py +122 -0
- sqlseed_ai-0.2.6/tests/test_http_probe_lifecycle.py +66 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/test_mcp.py +40 -1
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/test_refiner_table_boundaries.py +88 -9
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/LICENSE +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/pyproject.toml +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/src/sqlseed_ai/__init__.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/src/sqlseed_ai/_client.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/src/sqlseed_ai/_generator_names.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/src/sqlseed_ai/_json_utils.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/src/sqlseed_ai/_model_selector.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/src/sqlseed_ai/_prompts.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/src/sqlseed_ai/_tools.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/src/sqlseed_ai/ai_mediator.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/src/sqlseed_ai/analyzer/__init__.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/src/sqlseed_ai/analyzer/_context.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/src/sqlseed_ai/analyzer/_json_parser.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/src/sqlseed_ai/analyzer/_tool_calling.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/src/sqlseed_ai/auto_heal/__init__.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/src/sqlseed_ai/auto_heal/_check_inference.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/src/sqlseed_ai/auto_heal/_cross_column_checks.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/src/sqlseed_ai/auto_heal/orchestrator.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/src/sqlseed_ai/auto_heal/time_budget.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/src/sqlseed_ai/cli/__init__.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/src/sqlseed_ai/cli/ai_commands.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/src/sqlseed_ai/contracts/__init__.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/src/sqlseed_ai/contracts/builtin_violations.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/src/sqlseed_ai/contracts/matrix.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/src/sqlseed_ai/contracts/registry.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/src/sqlseed_ai/errors.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/src/sqlseed_ai/examples.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/src/sqlseed_ai/exceptions.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/src/sqlseed_ai/healer/__init__.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/src/sqlseed_ai/healer/_client.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/src/sqlseed_ai/healer/_llm_call.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/src/sqlseed_ai/healer/candidate_validation.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/src/sqlseed_ai/healer/context_detector.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/src/sqlseed_ai/healer/degrader.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/src/sqlseed_ai/healer/diff_learner.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/src/sqlseed_ai/healer/failure_classifier.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/src/sqlseed_ai/healer/level1_subgraph_healer.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/src/sqlseed_ai/healer/level2_column_healer.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/src/sqlseed_ai/healer/level3_compact_healer.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/src/sqlseed_ai/healer/models.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/src/sqlseed_ai/healer/orchestrator.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/src/sqlseed_ai/healer/oscillation.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/src/sqlseed_ai/healer/post_repair.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/src/sqlseed_ai/healer/subgraph.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/src/sqlseed_ai/repair/__init__.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/src/sqlseed_ai/repair/executor.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/src/sqlseed_ai/repair/models.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/src/sqlseed_ai/repair/pipeline.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/src/sqlseed_ai/repair/strategies.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/src/sqlseed_ai/runtime.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/src/sqlseed_ai/validator/__init__.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/src/sqlseed_ai/validator/composite_fk.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/src/sqlseed_ai/validator/cross_column.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/src/sqlseed_ai/validator/dialect_parser.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/src/sqlseed_ai/validator/main.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/src/sqlseed_ai/validator/models.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/src/sqlseed_ai/validator/schema_snapshot.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/src/sqlseed_ai/validator/shadow_fk_scan.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/src/sqlseed_ai/validator/single_column.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/.pylintrc +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/__init__.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/conftest.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/healer/__init__.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/healer/conftest.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/healer/scenario_helpers.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/healer/test_context_detector.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/healer/test_failure_classifier.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/healer/test_heal_orchestrator_real.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/healer/test_level1_subgraph_healer_real.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/healer/test_level2_column_healer_real.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/healer/test_level2_context_builder.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/healer/test_level3_compact_healer_real.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/healer/test_llm_availability.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/healer/test_llm_call_boundary.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/http_helpers.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/property/__init__.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/property/test_matrix_completeness.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/schema_helpers.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/test_ai_analyzer_streaming.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/test_ai_caller.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/test_ai_client.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/test_ai_commands.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/test_ai_config.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/test_ai_errors.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/test_ai_json_utils.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/test_ai_mediator.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/test_ai_model_selector.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/test_ai_plugin.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/test_ai_plugin_init.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/test_ai_preserve_names.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/test_ai_prompts_tools.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/test_ai_tool_calling.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/test_auto_heal_orchestrator.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/test_auto_heal_sonar_boundaries.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/test_auto_heal_time_budget.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/test_cli_auto_heal.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/test_cli_input_contract.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/test_cli_suggestion_diagnostics.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/test_cli_suggestion_identity.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/test_client_loopback.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/test_contracts_builtin.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/test_contracts_matrix.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/test_contracts_registry.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/test_healer_candidate_contract.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/test_healer_degrader.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/test_healer_diff_learner.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/test_healer_models.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/test_healer_oscillation.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/test_healer_post_repair.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/test_healer_subgraph.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/test_mcp_error_redaction.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/test_mcp_stdio.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/test_nonweb_ai_boundaries.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/test_prompts_p0_p3.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/test_quality_error_boundaries.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/test_real_llm_environment.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/test_refiner.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/test_refiner_json_recovery.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/test_repair_executor.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/test_repair_models.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/test_repair_pipeline.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/test_repair_strategies.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/test_runtime.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/test_schema_snapshot.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/test_validator_composite_fk.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/test_validator_cross_column.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/test_validator_dialect_parser.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/test_validator_main.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/test_validator_models.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/test_validator_shadow_fk_scan.py +0 -0
- {sqlseed_ai-0.2.5 → sqlseed_ai-0.2.6}/tests/test_validator_single_column.py +0 -0
|
@@ -59,6 +59,16 @@ dmypy.json
|
|
|
59
59
|
*.db
|
|
60
60
|
*.sqlite
|
|
61
61
|
*.sqlite3
|
|
62
|
+
# SQLite runtime sidecars
|
|
63
|
+
*.db-wal
|
|
64
|
+
*.db-shm
|
|
65
|
+
*.db-journal
|
|
66
|
+
*.sqlite-wal
|
|
67
|
+
*.sqlite-shm
|
|
68
|
+
*.sqlite-journal
|
|
69
|
+
*.sqlite3-wal
|
|
70
|
+
*.sqlite3-shm
|
|
71
|
+
*.sqlite3-journal
|
|
62
72
|
.sqlseed_cache/
|
|
63
73
|
snapshots/
|
|
64
74
|
.env
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: sqlseed-ai
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.6
|
|
4
4
|
Summary: Optional LLM schema analysis and contract-driven configuration repair for sqlseed
|
|
5
5
|
Project-URL: Documentation, https://sunbos.github.io/sqlseed/gemma4-integration/
|
|
6
6
|
Project-URL: Homepage, https://github.com/sunbos/sqlseed
|
|
@@ -46,12 +46,12 @@ backend test; installing the plugin does not perform one.
|
|
|
46
46
|
|
|
47
47
|
## Installation
|
|
48
48
|
|
|
49
|
-
These instructions target version 0.2.
|
|
49
|
+
These instructions target version 0.2.6. Check [Releases](https://github.com/sunbos/sqlseed/releases)
|
|
50
50
|
for publication status; use the source installation below to test an unpublished candidate.
|
|
51
51
|
Use a Python 3.10+ virtual environment:
|
|
52
52
|
|
|
53
53
|
```bash
|
|
54
|
-
python -m pip install "sqlseed-ai==0.2.
|
|
54
|
+
python -m pip install "sqlseed-ai==0.2.6"
|
|
55
55
|
```
|
|
56
56
|
|
|
57
57
|
Core 0.2.4 and older lack the shared diagnostic interfaces required by this version.
|
|
@@ -136,7 +136,7 @@ actual generated values and database constraints.
|
|
|
136
136
|
The AI MCP entry point requires the `mcp` extra:
|
|
137
137
|
|
|
138
138
|
```bash
|
|
139
|
-
python -m pip install "sqlseed-ai[mcp]==0.2.
|
|
139
|
+
python -m pip install "sqlseed-ai[mcp]==0.2.6"
|
|
140
140
|
mcp-server-sqlseed-ai
|
|
141
141
|
```
|
|
142
142
|
|
|
@@ -189,13 +189,31 @@ Strict tool calling also rejects arrays, scalars and `null` arguments as `invali
|
|
|
189
189
|
compatibility mode may still fall back to response text. Parsed suggestions still
|
|
190
190
|
require scope and rule validation.
|
|
191
191
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
192
|
+
Single-table suggestion caches use a versioned hash of sorted column names, encoded
|
|
193
|
+
as a JSON array so names containing delimiters remain distinct. The check detects
|
|
194
|
+
added, removed, or renamed columns; column order, types, and constraints are not
|
|
195
|
+
included. Caches using the older delimiter encoding are ignored and regenerated.
|
|
196
|
+
Use `--no-cache` to analyze again without cached suggestions. This cache check is
|
|
197
|
+
separate from AutoHeal's full schema fingerprint. Malformed cache metadata
|
|
198
|
+
or configuration containers are treated as cache misses. Review model output before
|
|
199
|
+
writing data.
|
|
200
|
+
|
|
201
|
+
## Hardware estimates on macOS
|
|
202
|
+
|
|
203
|
+
The AI MCP model list distinguishes Apple unified memory, Intel shared graphics
|
|
204
|
+
memory, and dedicated GPU memory. Apple unified RAM is not reported as dedicated
|
|
205
|
+
VRAM or added to system RAM. `unified_memory_budget_gb` is a static heuristic:
|
|
206
|
+
`max(0, min(total_ram_gb * 0.75, total_ram_gb - 4))`, reserving at least 4 GiB or
|
|
207
|
+
25% for the system. It is not measured free memory or a Metal allocation limit.
|
|
208
|
+
|
|
209
|
+
For an identified Apple GPU on macOS, model screening compares that budget with
|
|
210
|
+
both existing minimum RAM and VRAM estimates and reports at most `capable`.
|
|
211
|
+
This does not verify Metal acceleration, backend/model support, or successful
|
|
212
|
+
inference; loaded applications and context size can require more memory.
|
|
195
213
|
|
|
196
214
|
## Requirements
|
|
197
215
|
|
|
198
|
-
These metadata requirements apply to version 0.2.
|
|
216
|
+
These metadata requirements apply to version 0.2.6 and its source candidates.
|
|
199
217
|
Use local Core and plugins together when developing from source.
|
|
200
218
|
|
|
201
219
|
- Python `>=3.10`
|
|
@@ -14,12 +14,12 @@ backend test; installing the plugin does not perform one.
|
|
|
14
14
|
|
|
15
15
|
## Installation
|
|
16
16
|
|
|
17
|
-
These instructions target version 0.2.
|
|
17
|
+
These instructions target version 0.2.6. Check [Releases](https://github.com/sunbos/sqlseed/releases)
|
|
18
18
|
for publication status; use the source installation below to test an unpublished candidate.
|
|
19
19
|
Use a Python 3.10+ virtual environment:
|
|
20
20
|
|
|
21
21
|
```bash
|
|
22
|
-
python -m pip install "sqlseed-ai==0.2.
|
|
22
|
+
python -m pip install "sqlseed-ai==0.2.6"
|
|
23
23
|
```
|
|
24
24
|
|
|
25
25
|
Core 0.2.4 and older lack the shared diagnostic interfaces required by this version.
|
|
@@ -104,7 +104,7 @@ actual generated values and database constraints.
|
|
|
104
104
|
The AI MCP entry point requires the `mcp` extra:
|
|
105
105
|
|
|
106
106
|
```bash
|
|
107
|
-
python -m pip install "sqlseed-ai[mcp]==0.2.
|
|
107
|
+
python -m pip install "sqlseed-ai[mcp]==0.2.6"
|
|
108
108
|
mcp-server-sqlseed-ai
|
|
109
109
|
```
|
|
110
110
|
|
|
@@ -157,13 +157,31 @@ Strict tool calling also rejects arrays, scalars and `null` arguments as `invali
|
|
|
157
157
|
compatibility mode may still fall back to response text. Parsed suggestions still
|
|
158
158
|
require scope and rule validation.
|
|
159
159
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
160
|
+
Single-table suggestion caches use a versioned hash of sorted column names, encoded
|
|
161
|
+
as a JSON array so names containing delimiters remain distinct. The check detects
|
|
162
|
+
added, removed, or renamed columns; column order, types, and constraints are not
|
|
163
|
+
included. Caches using the older delimiter encoding are ignored and regenerated.
|
|
164
|
+
Use `--no-cache` to analyze again without cached suggestions. This cache check is
|
|
165
|
+
separate from AutoHeal's full schema fingerprint. Malformed cache metadata
|
|
166
|
+
or configuration containers are treated as cache misses. Review model output before
|
|
167
|
+
writing data.
|
|
168
|
+
|
|
169
|
+
## Hardware estimates on macOS
|
|
170
|
+
|
|
171
|
+
The AI MCP model list distinguishes Apple unified memory, Intel shared graphics
|
|
172
|
+
memory, and dedicated GPU memory. Apple unified RAM is not reported as dedicated
|
|
173
|
+
VRAM or added to system RAM. `unified_memory_budget_gb` is a static heuristic:
|
|
174
|
+
`max(0, min(total_ram_gb * 0.75, total_ram_gb - 4))`, reserving at least 4 GiB or
|
|
175
|
+
25% for the system. It is not measured free memory or a Metal allocation limit.
|
|
176
|
+
|
|
177
|
+
For an identified Apple GPU on macOS, model screening compares that budget with
|
|
178
|
+
both existing minimum RAM and VRAM estimates and reports at most `capable`.
|
|
179
|
+
This does not verify Metal acceleration, backend/model support, or successful
|
|
180
|
+
inference; loaded applications and context size can require more memory.
|
|
163
181
|
|
|
164
182
|
## Requirements
|
|
165
183
|
|
|
166
|
-
These metadata requirements apply to version 0.2.
|
|
184
|
+
These metadata requirements apply to version 0.2.6 and its source candidates.
|
|
167
185
|
Use local Core and plugins together when developing from source.
|
|
168
186
|
|
|
169
187
|
- Python `>=3.10`
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
[English](https://github.com/sunbos/sqlseed/blob/main/plugins/sqlseed-ai/README.md) |
|
|
4
4
|
**[中文](https://github.com/sunbos/sqlseed/blob/main/plugins/sqlseed-ai/README.zh-CN.md)**
|
|
5
5
|
|
|
6
|
-
[sqlseed](https://sunbos.github.io/sqlseed/) 的可选 LLM Schema 分析与契约驱动配置修复插件。
|
|
6
|
+
[sqlseed](https://sunbos.github.io/sqlseed/zh-CN/) 的可选 LLM Schema 分析与契约驱动配置修复插件。
|
|
7
7
|
提供列规则建议、配置校验与修复,以及模板候选值生成。接受的配置可交由 Core 离线执行。
|
|
8
8
|
|
|
9
9
|
支持 Google AI Studio、LM Studio、Ollama 和 OpenAI-compatible API 后端。
|
|
@@ -11,10 +11,10 @@
|
|
|
11
11
|
|
|
12
12
|
## 安装
|
|
13
13
|
|
|
14
|
-
本文安装说明对应 0.2.
|
|
14
|
+
本文安装说明对应 0.2.6 版本;发布状态以 [Releases](https://github.com/sunbos/sqlseed/releases) 为准。测试尚未发布的候选版本时,使用下方的源码安装方式。请先创建并激活 Python 3.10+ 虚拟环境:
|
|
15
15
|
|
|
16
16
|
```bash
|
|
17
|
-
python -m pip install "sqlseed-ai==0.2.
|
|
17
|
+
python -m pip install "sqlseed-ai==0.2.6"
|
|
18
18
|
```
|
|
19
19
|
|
|
20
20
|
Core 0.2.4 及更早版本缺少本版本使用的共享诊断接口。
|
|
@@ -91,7 +91,7 @@ native/custom 方法、实际生成值及依赖数据库状态的约束需另行
|
|
|
91
91
|
AI MCP 入口要求本包的 `mcp` extra:
|
|
92
92
|
|
|
93
93
|
```bash
|
|
94
|
-
python -m pip install "sqlseed-ai[mcp]==0.2.
|
|
94
|
+
python -m pip install "sqlseed-ai[mcp]==0.2.6"
|
|
95
95
|
mcp-server-sqlseed-ai
|
|
96
96
|
```
|
|
97
97
|
|
|
@@ -194,7 +194,10 @@ Ollama 使用 JSON 对象模式。服务明确拒绝该格式时,只进行一
|
|
|
194
194
|
|
|
195
195
|
### 文件缓存
|
|
196
196
|
|
|
197
|
-
|
|
197
|
+
单表建议缓存使用带版本标识的 schema hash,将排序后的列名编码为 JSON 数组,
|
|
198
|
+
避免含分隔符的不同列名集合混淆。校验覆盖列的增删和重命名,不包含列顺序、类型或约束。
|
|
199
|
+
旧分隔符编码的缓存会被忽略并重新生成;可用 `--no-cache` 跳过缓存重新分析。
|
|
200
|
+
此处的缓存校验与 AutoHeal 使用的完整 schema 指纹不同。
|
|
198
201
|
缓存元数据或配置容器类型无效时,按缓存未命中重新分析。
|
|
199
202
|
默认路径为 macOS 的 `~/Library/Caches/sqlseed/ai_configs/`、Linux 的
|
|
200
203
|
`$XDG_CACHE_HOME/sqlseed/ai_configs/`(未设置时为 `~/.cache/sqlseed/ai_configs/`),
|
|
@@ -215,9 +218,20 @@ AI 配置缓存包含 schema hash,结构变化会使旧建议失效;`--no-ca
|
|
|
215
218
|
CLI 命令另由 `sqlseed.cli_commands` entry point 注册。本插件不实现 provider 或
|
|
216
219
|
column-mapper 注册 hooks,也不要求 Core 导入 AI 实现。
|
|
217
220
|
|
|
221
|
+
## macOS 硬件估算
|
|
222
|
+
|
|
223
|
+
AI MCP 模型列表区分 Apple 统一内存、Intel 共享显存与独立显存。Apple 统一内存
|
|
224
|
+
不会被报告为独立显存,也不会与系统 RAM 相加。`unified_memory_budget_gb` 是静态
|
|
225
|
+
启发式预算:`max(0, min(total_ram_gb * 0.75, total_ram_gb - 4))`,为系统预留
|
|
226
|
+
至少 4 GiB 或 25% 内存。它不是实测可用内存,也不是 Metal 分配上限。
|
|
227
|
+
|
|
228
|
+
仅在 macOS 上识别出 Apple GPU 后,模型筛选才使用该预算与既有最低 RAM、VRAM
|
|
229
|
+
估算同时比较,最高返回 `capable`。该结果不代表已验证 Metal 加速、后端或模型支持,
|
|
230
|
+
也不保证推理成功;其他应用占用和上下文大小可能增加实际内存需求。
|
|
231
|
+
|
|
218
232
|
## 依赖
|
|
219
233
|
|
|
220
|
-
以下为 0.2.
|
|
234
|
+
以下为 0.2.6 版本及其源码候选的依赖要求。源码开发时请在同一次解析中安装本地 Core 和插件。
|
|
221
235
|
|
|
222
236
|
- Python `>=3.10`
|
|
223
237
|
- `sqlseed>=0.2.5.dev0,<0.3`
|
|
@@ -228,8 +242,8 @@ column-mapper 注册 hooks,也不要求 Core 导入 AI 实现。
|
|
|
228
242
|
- 可选 `mcp` extra:`mcp>=1.0,<2`
|
|
229
243
|
- 实际模型请求需要已配置且可达的后端
|
|
230
244
|
|
|
231
|
-
更多信息见[AI 集成指南](https://sunbos.github.io/sqlseed/
|
|
232
|
-
[升级说明](https://sunbos.github.io/sqlseed/
|
|
245
|
+
更多信息见[AI 集成指南](https://sunbos.github.io/sqlseed/zh-CN/gemma4-integration/)、
|
|
246
|
+
[升级说明](https://sunbos.github.io/sqlseed/zh-CN/migration/)和
|
|
233
247
|
[配置源码](https://github.com/sunbos/sqlseed/blob/main/plugins/sqlseed-ai/src/sqlseed_ai/config.py)。
|
|
234
248
|
|
|
235
249
|
许可证:[AGPL-3.0-or-later](https://github.com/sunbos/sqlseed/blob/main/LICENSE)。
|
|
@@ -11,8 +11,10 @@ from __future__ import annotations
|
|
|
11
11
|
import ctypes
|
|
12
12
|
import json
|
|
13
13
|
import platform
|
|
14
|
+
import re
|
|
14
15
|
import subprocess
|
|
15
16
|
import time
|
|
17
|
+
from math import isfinite
|
|
16
18
|
from typing import Any, NamedTuple
|
|
17
19
|
|
|
18
20
|
from sqlseed._utils.logger import get_logger
|
|
@@ -200,8 +202,34 @@ def _detect_gpu_nvidia() -> list[dict[str, Any]]:
|
|
|
200
202
|
return []
|
|
201
203
|
|
|
202
204
|
|
|
205
|
+
def _video_memory_mb(value: object) -> int:
|
|
206
|
+
"""Parse a profiler memory quantity without trusting malformed card fields."""
|
|
207
|
+
if not isinstance(value, str):
|
|
208
|
+
return 0
|
|
209
|
+
if (match := re.fullmatch(r"(\d+(?:\.\d+)?)\s*(MB|GB)", value.strip(), re.IGNORECASE)) is None:
|
|
210
|
+
return 0
|
|
211
|
+
size_mb = float(match[1]) * (1024 if match[2].upper() == "GB" else 1)
|
|
212
|
+
return int(size_mb) if isfinite(size_mb) else 0
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def _macos_gpu_memory(gpu_info: dict[str, object], vendor: str) -> tuple[str, int]:
|
|
216
|
+
"""Separate shared and dedicated video memory from Apple unified RAM."""
|
|
217
|
+
if vendor == "apple":
|
|
218
|
+
return "unified", 0
|
|
219
|
+
memory_type = "shared" if "spdisplays_vram_shared" in gpu_info else "dedicated"
|
|
220
|
+
vram_mb = next(
|
|
221
|
+
(
|
|
222
|
+
size
|
|
223
|
+
for key in ("spdisplays_vram", "spdisplays_vram_shared", "_spdisplays_vram")
|
|
224
|
+
if (size := _video_memory_mb(gpu_info.get(key))) > 0
|
|
225
|
+
),
|
|
226
|
+
0,
|
|
227
|
+
)
|
|
228
|
+
return memory_type, vram_mb
|
|
229
|
+
|
|
230
|
+
|
|
203
231
|
def _detect_gpu_macos() -> list[dict[str, Any]]:
|
|
204
|
-
"""Detect Apple
|
|
232
|
+
"""Detect Apple, Intel and discrete GPUs via macOS system_profiler."""
|
|
205
233
|
try:
|
|
206
234
|
result = subprocess.run(
|
|
207
235
|
["system_profiler", "SPDisplaysDataType", "-json"],
|
|
@@ -214,26 +242,30 @@ def _detect_gpu_macos() -> list[dict[str, Any]]:
|
|
|
214
242
|
return []
|
|
215
243
|
|
|
216
244
|
data = json.loads(result.stdout)
|
|
245
|
+
if not isinstance(data, dict):
|
|
246
|
+
return []
|
|
217
247
|
displays = data.get("SPDisplaysDataType", [])
|
|
248
|
+
if not isinstance(displays, list):
|
|
249
|
+
return []
|
|
218
250
|
gpus: list[dict[str, Any]] = []
|
|
219
251
|
for gpu_info in displays:
|
|
252
|
+
if not isinstance(gpu_info, dict):
|
|
253
|
+
continue
|
|
220
254
|
name = gpu_info.get("sppci_model", "Unknown GPU")
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
val = int(parts[0])
|
|
227
|
-
unit = parts[1].upper()
|
|
228
|
-
vram_mb = val * 1024 if "GB" in unit else val
|
|
255
|
+
if not isinstance(name, str):
|
|
256
|
+
name = "Unknown GPU"
|
|
257
|
+
vendor_label = str(gpu_info.get("spdisplays_vendor") or name).lower()
|
|
258
|
+
vendor = next((v for v in ("apple", "intel", "amd", "nvidia") if v in vendor_label), "unknown")
|
|
259
|
+
memory_type, vram_mb = _macos_gpu_memory(gpu_info, vendor)
|
|
229
260
|
|
|
230
261
|
gpus.append(
|
|
231
262
|
{
|
|
232
263
|
"name": name,
|
|
233
264
|
"vram_total_mb": vram_mb,
|
|
234
|
-
"vram_free_mb": 0, #
|
|
265
|
+
"vram_free_mb": 0, # system_profiler does not report free VRAM
|
|
235
266
|
"vram_total_gb": round(vram_mb / 1024, 1),
|
|
236
|
-
"vendor":
|
|
267
|
+
"vendor": vendor,
|
|
268
|
+
"memory_type": memory_type,
|
|
237
269
|
}
|
|
238
270
|
)
|
|
239
271
|
return gpus
|
|
@@ -252,6 +284,13 @@ def _detect_gpus() -> list[dict[str, Any]]:
|
|
|
252
284
|
return []
|
|
253
285
|
|
|
254
286
|
|
|
287
|
+
def _has_apple_unified_memory(hw: dict[str, Any]) -> bool:
|
|
288
|
+
"""Require an identified Apple GPU on macOS, including under Rosetta."""
|
|
289
|
+
return hw.get("platform", {}).get("system") == "Darwin" and any(
|
|
290
|
+
gpu.get("vendor") == "apple" and gpu.get("memory_type") == "unified" for gpu in hw.get("gpus", [])
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
|
|
255
294
|
# ── Public API ───────────────────────────────────────────────────────
|
|
256
295
|
|
|
257
296
|
|
|
@@ -262,7 +301,8 @@ def detect_hardware() -> dict[str, Any]:
|
|
|
262
301
|
platform: {system, release, machine}
|
|
263
302
|
ram: {total_gb, available_gb}
|
|
264
303
|
gpus: [{name, vram_total_mb, vram_free_mb, vram_total_gb, vendor, ...}]
|
|
265
|
-
max_vram_gb: float
|
|
304
|
+
max_vram_gb: float (largest reported non-unified VRAM quantity)
|
|
305
|
+
unified_memory_budget_gb: float (heuristic budget, not measured free VRAM)
|
|
266
306
|
"""
|
|
267
307
|
if _HardwareCache.data is not None:
|
|
268
308
|
cached_time, cached_result = _HardwareCache.data
|
|
@@ -271,9 +311,9 @@ def detect_hardware() -> dict[str, Any]:
|
|
|
271
311
|
|
|
272
312
|
ram = _detect_system_ram()
|
|
273
313
|
gpus = _detect_gpus()
|
|
274
|
-
max_vram = max((g.get("vram_total_gb", 0) for g in gpus), default=0)
|
|
314
|
+
max_vram = max((g.get("vram_total_gb", 0) for g in gpus if g.get("memory_type") != "unified"), default=0)
|
|
275
315
|
|
|
276
|
-
result = {
|
|
316
|
+
result: dict[str, Any] = {
|
|
277
317
|
"platform": {
|
|
278
318
|
"system": platform.system(),
|
|
279
319
|
"release": platform.release(),
|
|
@@ -282,7 +322,13 @@ def detect_hardware() -> dict[str, Any]:
|
|
|
282
322
|
"ram": ram,
|
|
283
323
|
"gpus": gpus,
|
|
284
324
|
"max_vram_gb": max_vram,
|
|
325
|
+
"unified_memory_budget_gb": 0.0,
|
|
285
326
|
}
|
|
327
|
+
if _has_apple_unified_memory(result):
|
|
328
|
+
# A static screening heuristic, not Metal's runtime allocation limit.
|
|
329
|
+
# Reserve at least 4 GiB / 25% for the OS and other applications.
|
|
330
|
+
total_ram = ram["total_gb"]
|
|
331
|
+
result["unified_memory_budget_gb"] = max(0.0, min(total_ram * 0.75, total_ram - 4.0))
|
|
286
332
|
|
|
287
333
|
_HardwareCache.data = (time.monotonic(), result)
|
|
288
334
|
logger.info(
|
|
@@ -340,6 +386,11 @@ def evaluate_model_status(
|
|
|
340
386
|
return "recommended"
|
|
341
387
|
if max_vram >= req.min_vram_gb:
|
|
342
388
|
return "capable"
|
|
389
|
+
if _has_apple_unified_memory(hw):
|
|
390
|
+
budget = hw.get("unified_memory_budget_gb", 0)
|
|
391
|
+
# RAM and GPU allocations share this budget; never add them or grant
|
|
392
|
+
# a recommendation without verifying the backend and actual model.
|
|
393
|
+
return "capable" if budget >= max(req.min_ram_gb, req.min_vram_gb) else "insufficient"
|
|
343
394
|
if total_ram >= req.min_ram_gb and max_vram == 0:
|
|
344
395
|
return "cpu_only"
|
|
345
396
|
if total_ram >= req.min_ram_gb:
|
|
@@ -8,6 +8,7 @@ request strategy (tool calling vs JSON mode vs text mode).
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
10
|
import time
|
|
11
|
+
from contextlib import ExitStack
|
|
11
12
|
from typing import TYPE_CHECKING, Any, NoReturn
|
|
12
13
|
|
|
13
14
|
from sqlseed_ai._client import APIConnectionError, APIError, APITimeoutError, get_openai_client
|
|
@@ -217,15 +218,17 @@ class StreamingHandlerMixin(_InteractionLoggingMixin):
|
|
|
217
218
|
raise RuntimeError("AIConfig must be initialized before calling LLM")
|
|
218
219
|
client = get_openai_client(self._config)
|
|
219
220
|
start_time = time.time()
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
on_progress("connecting", {"model": model or self._config.model})
|
|
221
|
+
resources = ExitStack()
|
|
222
|
+
resources.callback(client.close)
|
|
223
223
|
|
|
224
224
|
try:
|
|
225
|
+
if on_progress:
|
|
226
|
+
on_progress("connecting", {"model": model or self._config.model})
|
|
225
227
|
kwargs = self._build_llm_kwargs(stream=True, model=model)
|
|
226
228
|
kwargs["messages"] = messages
|
|
227
229
|
|
|
228
230
|
stream = self._create_streaming_response(client, kwargs, strict_json=strict_json)
|
|
231
|
+
resources.callback(stream.close)
|
|
229
232
|
|
|
230
233
|
content, token_count = self._collect_stream_chunks(stream, on_progress, strict_json=strict_json)
|
|
231
234
|
|
|
@@ -285,6 +288,8 @@ class StreamingHandlerMixin(_InteractionLoggingMixin):
|
|
|
285
288
|
error=str(e),
|
|
286
289
|
)
|
|
287
290
|
self._handle_llm_api_exception(e, model, streaming=True)
|
|
291
|
+
finally:
|
|
292
|
+
resources.close()
|
|
288
293
|
|
|
289
294
|
def _create_streaming_response(self, client: Any, kwargs: dict[str, Any], *, strict_json: bool) -> Any:
|
|
290
295
|
"""Apply local JSON sampling without changing cloud streaming dispatch."""
|
|
@@ -12,6 +12,7 @@ import json
|
|
|
12
12
|
import os
|
|
13
13
|
import re
|
|
14
14
|
import time
|
|
15
|
+
import urllib.error
|
|
15
16
|
import urllib.request
|
|
16
17
|
from enum import Enum
|
|
17
18
|
from typing import Any, Literal, cast
|
|
@@ -317,7 +318,9 @@ class AIConfig(BaseModel):
|
|
|
317
318
|
models = [str(m.get("id")) for m in data.get("data", []) if m.get("id")]
|
|
318
319
|
self._all_models_cache = (time.monotonic(), models)
|
|
319
320
|
return models
|
|
320
|
-
except (OSError, ValueError, KeyError):
|
|
321
|
+
except (OSError, ValueError, KeyError) as error:
|
|
322
|
+
if isinstance(error, urllib.error.HTTPError):
|
|
323
|
+
error.close()
|
|
321
324
|
logger.debug("Could not auto-detect local models", backend=self.backend.value)
|
|
322
325
|
|
|
323
326
|
# Cache negative result too, to avoid hammering a dead endpoint
|
|
@@ -626,6 +629,8 @@ class AIConfig(BaseModel):
|
|
|
626
629
|
return info
|
|
627
630
|
|
|
628
631
|
except (OSError, ValueError, RuntimeError) as e:
|
|
632
|
+
if isinstance(e, urllib.error.HTTPError):
|
|
633
|
+
e.close()
|
|
629
634
|
logger.debug("Inference speed probe failed", error=str(e))
|
|
630
635
|
return None
|
|
631
636
|
|
|
@@ -19,6 +19,7 @@ required", not "online/offline". This package requires an LLM runtime.
|
|
|
19
19
|
from __future__ import annotations
|
|
20
20
|
|
|
21
21
|
import json
|
|
22
|
+
import urllib.error
|
|
22
23
|
import urllib.request
|
|
23
24
|
from typing import Any
|
|
24
25
|
|
|
@@ -30,7 +31,12 @@ except ImportError as _exc: # pragma: no cover - import error path
|
|
|
30
31
|
raise ImportError("mcp SDK not installed. Install with: pip install 'sqlseed-ai[mcp]'") from _exc
|
|
31
32
|
|
|
32
33
|
from sqlseed_ai import AIBackend, AIConfig, AiConfigRefiner, AISuggestionFailedError, GemmaModel, SchemaAnalyzer
|
|
33
|
-
from sqlseed_ai._hardware import
|
|
34
|
+
from sqlseed_ai._hardware import (
|
|
35
|
+
MODEL_REQUIREMENTS,
|
|
36
|
+
_has_apple_unified_memory,
|
|
37
|
+
detect_hardware,
|
|
38
|
+
evaluate_model_status,
|
|
39
|
+
)
|
|
34
40
|
|
|
35
41
|
from sqlseed import ColumnConfig, DataOrchestrator
|
|
36
42
|
from sqlseed._utils import paths
|
|
@@ -97,7 +103,12 @@ def sqlseed_ai_generate_yaml(
|
|
|
97
103
|
semantics. For rule-driven offline generation, use the
|
|
98
104
|
``sqlseed_generate_yaml`` tool from ``mcp-server-sqlseed`` instead.
|
|
99
105
|
|
|
100
|
-
Supported backends: google_ai_studio
|
|
106
|
+
Supported backends: google_ai_studio, lm_studio, ollama, openai_compat.
|
|
107
|
+
Backend selection uses the backend argument, then SQLSEED_AI_BACKEND,
|
|
108
|
+
then recognized URL patterns in SQLSEED_AI_BASE_URL or OPENAI_BASE_URL;
|
|
109
|
+
otherwise it defaults to openai_compat. The base_url argument only overrides
|
|
110
|
+
the endpoint and does not infer the backend. Pass backend explicitly when
|
|
111
|
+
using base_url to select a provider.
|
|
101
112
|
Returns a YAML string for human review.
|
|
102
113
|
"""
|
|
103
114
|
try:
|
|
@@ -148,7 +159,10 @@ def sqlseed_gemma4_analyze(
|
|
|
148
159
|
table structure and recommend data generation configurations. It demonstrates
|
|
149
160
|
Gemma 4's Native Function Calling feature for the AI Agent track.
|
|
150
161
|
|
|
151
|
-
Supported backends: google_ai_studio
|
|
162
|
+
Supported backends: google_ai_studio, lm_studio, ollama, openai_compat.
|
|
163
|
+
Backend selection uses the backend argument, then SQLSEED_AI_BACKEND,
|
|
164
|
+
then recognized URL patterns in SQLSEED_AI_BASE_URL or OPENAI_BASE_URL;
|
|
165
|
+
otherwise it defaults to openai_compat.
|
|
152
166
|
Supported models: gemma-4-26b-a4b-it (default), gemma-4-31b-it, gemma-4-12b-it,
|
|
153
167
|
gemma-4-e4b-it, gemma-4-e2b-it.
|
|
154
168
|
"""
|
|
@@ -293,8 +307,9 @@ def _check_local_backend(backend_id: str, url: str) -> dict[str, Any]:
|
|
|
293
307
|
data = json.loads(resp.read().decode())
|
|
294
308
|
loaded = [m.get("id", "unknown") for m in data.get("data", []) if m.get("id")]
|
|
295
309
|
reachable = True
|
|
296
|
-
except (OSError, ValueError):
|
|
297
|
-
|
|
310
|
+
except (OSError, ValueError) as error:
|
|
311
|
+
if isinstance(error, urllib.error.HTTPError):
|
|
312
|
+
error.close()
|
|
298
313
|
|
|
299
314
|
if reachable and loaded:
|
|
300
315
|
reason = f"{len(loaded)} model(s) loaded"
|
|
@@ -350,12 +365,15 @@ def _build_models(hw: dict[str, Any]) -> list[dict[str, Any]]:
|
|
|
350
365
|
for member in GemmaModel:
|
|
351
366
|
status = evaluate_model_status(member.value, hw)
|
|
352
367
|
req = MODEL_REQUIREMENTS.get(member.value)
|
|
368
|
+
status_description = _STATUS_ICONS.get(status, status)
|
|
369
|
+
if _has_apple_unified_memory(hw) and status != "cloud_only":
|
|
370
|
+
status_description += "; heuristic unified-memory budget, backend/model support not verified"
|
|
353
371
|
models.append(
|
|
354
372
|
{
|
|
355
373
|
"id": member.value,
|
|
356
374
|
"display_name": member.display_name,
|
|
357
375
|
"status": status,
|
|
358
|
-
"status_description":
|
|
376
|
+
"status_description": status_description,
|
|
359
377
|
"local_only": member.is_local_only,
|
|
360
378
|
"requirements": {
|
|
361
379
|
"min_ram_gb": req.min_ram_gb if req else 0,
|
|
@@ -417,6 +435,7 @@ def sqlseed_list_gemma_models() -> dict[str, Any]:
|
|
|
417
435
|
"ram": hw["ram"],
|
|
418
436
|
"gpus": hw["gpus"],
|
|
419
437
|
"max_vram_gb": hw["max_vram_gb"],
|
|
438
|
+
"unified_memory_budget_gb": hw.get("unified_memory_budget_gb", 0.0),
|
|
420
439
|
},
|
|
421
440
|
}
|
|
422
441
|
|
|
@@ -559,15 +559,16 @@ class AiConfigRefiner:
|
|
|
559
559
|
table_name: Name of the table to hash.
|
|
560
560
|
|
|
561
561
|
Returns:
|
|
562
|
-
|
|
562
|
+
Versioned, truncated SHA-256 digest of unambiguously encoded sorted column names.
|
|
563
563
|
|
|
564
564
|
Raises:
|
|
565
565
|
ValueError: If the target has no columns to generate, including a missing table.
|
|
566
566
|
"""
|
|
567
567
|
if not (column_names := orch.get_column_names(table_name)):
|
|
568
568
|
raise ValueError(f"Table '{table_name}' does not exist or has no columns")
|
|
569
|
-
raw =
|
|
570
|
-
|
|
569
|
+
raw = json.dumps(sorted(column_names), ensure_ascii=True, separators=(",", ":"))
|
|
570
|
+
# Keep legacy delimiter hashes separate even when a column name spells JSON.
|
|
571
|
+
return "columns-v2:" + hashlib.sha256(raw.encode("utf-8")).hexdigest()[:16]
|
|
571
572
|
|
|
572
573
|
def _validate_config(
|
|
573
574
|
self,
|