nl-sql / src /nl_sql /config /settings.py
liovina's picture
Deploy NL_SQL HEAD to HF Space (tracked files only)
4e1037f verified
Raw
History Blame Contribute Delete
8.6 kB
from functools import lru_cache
from typing import Literal
from pydantic import Field
from pydantic_settings import BaseSettings, SettingsConfigDict
ProviderName = Literal[
"mistral",
"github_models",
"groq",
"ollama",
"openrouter",
"perplexity",
"gracekelly",
"zen",
"grok_cli",
"claude_cli",
"local_vllm",
]
class Settings(BaseSettings):
model_config = SettingsConfigDict(
env_file=".env",
env_file_encoding="utf-8",
env_prefix="NL_SQL_",
extra="ignore",
case_sensitive=False,
populate_by_name=True,
)
log_level: str = "INFO"
default_provider: ProviderName = "mistral"
frontier_provider: ProviderName = "groq" # GitHub Models needs fine-grained PAT
local_provider: ProviderName = "ollama"
mistral_gen_model: str = "codestral-latest"
mistral_nl_model: str = "mistral-large-latest"
mistral_embed_model: str = "mistral-embed"
mistral_base_url: str = "https://api.mistral.ai/v1"
github_models_model: str = "openai/gpt-4o-mini"
github_models_base_url: str = "https://models.github.ai/inference"
groq_model: str = "llama-3.3-70b-versatile"
groq_base_url: str = "https://api.groq.com/openai/v1"
ollama_gen_model: str = "qwen2.5-coder:7b-instruct"
ollama_base_url: str = "http://localhost:11434/v1"
ollama_timeout_seconds: float = 180.0
# OpenRouter β€” heterogeneous-CSC slot. Default = deepseek-v4-flash:free
# (DeepSeek family, β‰  Mistral β€” needed so self-consistency votes don't
# collapse into one model's blind spots, as happened in config F + CSC
# merge-revision saturation on homogeneous codestral). Earlier picks
# rejected during 2026-05-20 probe:
# - z-ai/glm-4.5-air:free β†’ reasoning model, 2186 reasoning_tokens
# consumed the whole budget, content=empty (smoke5 β†’ 0% EA).
# - qwen/qwen3-coder:free β†’ Venice provider 429-loop (free quota).
# deepseek-v4-flash:free returned valid JSON+SQL on probe (LIMIT/OFFSET
# correct for 7th-row case). Other live free models cycle; check
# `D:\TXT\Free API Keys.txt` / smoke before switching.
# `deepseek/deepseek-v4-flash:free` used to live here and no longer exists β€”
# OpenRouter answers 404 and points at the paid slug, so this provider had
# been quietly dead. qwen3-coder is free, code-specialised and long-context;
# note the free tier is rate-limited upstream (429) and needs retry patience.
openrouter_model: str = "qwen/qwen3-coder:free"
openrouter_base_url: str = "https://openrouter.ai/api/v1"
# OpenCode Zen β€” free generator slot (see llm/providers/zen.py). Only the
# `*-free` catalogue is reachable: the workspace has no payment method, so
# paid ids answer CreditsError. Free ids as of 2026-07-14, all of which
# emitted valid SQL on probe: deepseek-v4-flash-free, north-mini-code-free,
# nemotron-3-ultra-free, mimo-v2.5-free, hy3-free, big-pickle.
zen_model: str = "deepseek-v4-flash-free"
zen_base_url: str = "https://opencode.ai/zen/v1"
zen_timeout_seconds: float = 180.0
# Grok Build CLI β€” subscription path (weekly quota, like GraceKelly), no key.
# `grok models` advertises grok-4.20-0309-reasoning but the backend rejects it;
# the tier serves grok-composer-2.5-fast. See llm/providers/grok_cli.py.
grok_cli_path: str = "grok"
grok_cli_model: str = "grok-composer-2.5-fast"
grok_cli_timeout_seconds: float = 600.0
grok_cli_max_turns: int = 4
"""Must be > 1. Grok spends its first turn announcing what it is about to do;
`--max-turns 1` returns that preamble instead of the SQL (47.0% / 31% invalid
on the first n=200 run). See llm/providers/grok_cli.py."""
grok_cli_effort: str | None = None
"""`--reasoning-effort`. None leaves the CLI on its own default."""
# Claude Code CLI β€” subscription path (plan limits, no key). `--system-prompt`
# replaces the harness prompt, so the model answers as a completion, not an
# agent. See llm/providers/claude_cli.py.
claude_cli_path: str = "claude"
claude_cli_model: str = "claude-sonnet-5"
claude_cli_timeout_seconds: float = 600.0
claude_cli_max_turns: int = 4
"""Must be > 1: a denied tool call eats a turn and the CLI then answers
`error_max_turns` instead of the SQL."""
claude_cli_effort: str | None = None
"""`--effort` (low|medium|high|xhigh|max). None leaves the CLI on its own
default. The spawned process does NOT inherit the effort of the session that
launched it, so this is the only way to run a max-effort ablation."""
# Local vLLM β€” rented-GPU student-model slot (S2, plan_autotune.md; see
# llm/providers/local_vllm.py). No fixed host like Ollama's :11434: the
# box only exists for the duration of a GPU rental, so `base_url` must be
# set in `.env` each session. `NL_SQL_LOCAL_LLM_API_KEY` defaults to the
# literal "dummy" β€” vLLM's OpenAI-compatible server does not check a key
# unless started with `--api-key`, but the openai SDK requires a
# non-empty string.
local_llm_base_url: str = "http://localhost:8000/v1"
local_llm_model: str = "Qwen/Qwen2.5-Coder-7B-Instruct"
local_llm_api_key: str = "dummy"
local_llm_timeout_seconds: float = 180.0
# Perplexity browser path via local GraceKelly (D:\GraceKelly). Free
# because it rides the user's Perplexity Pro subscription via Playwright.
# `claude-sonnet-4-6` here is the Perplexity menu label, not the
# Anthropic API model id β€” GraceKelly resolves it to the browser path.
perplexity_browser_model: str = "claude-sonnet-4-6"
perplexity_base_url: str = "http://127.0.0.1:8011"
# GraceKelly orchestrate path β€” the AgentFlow branch runs NL->SQL on
# Claude Sonnet 5 via GraceKelly (AgentFlow ADR 0008), not Mistral. The
# portfolio default_provider stays `mistral`; this slot is selected
# explicitly via build_provider("gracekelly"). `claude-sonnet-5` resolves
# through GraceKelly's live catalog to "Claude Sonnet 5.0".
gracekelly_model: str = "claude-sonnet-5"
gracekelly_base_url: str = "http://127.0.0.1:8011"
gracekelly_timeout_seconds: float = 900.0
"""Browser path, so this is a human-scale wait, not an API round-trip.
Measured on the NL_SQL generation prompt (schema + few-shot + rules, ~6k
tokens) against Perplexity: 150-436s per call. The former 180s default
timed out mid-benchmark and surfaced as `pipeline_exception`, which scores
as a miss and silently understates the model. 900s leaves headroom."""
mistral_api_key: str = Field(default="", validation_alias="MISTRAL_API_KEY")
github_token: str = Field(default="", validation_alias="GITHUB_TOKEN")
groq_api_key: str = Field(default="", validation_alias="GROQ_API_KEY")
openrouter_api_key: str = Field(default="", validation_alias="OPENROUTER_API_KEY")
zen_api_key: str = Field(default="", validation_alias="OPENCODE_API_KEY")
"""One or more OpenCode Zen keys, comma-separated. Free tiers meter per key,
so extra keys are extra headroom: the provider rotates through them on 429."""
# Optional API auth for the FastAPI surface. When empty, /ask and /databases
# are open β€” but the rate limiter still applies (keyed by client IP), so a
# keyless public deploy is not unthrottled. Read here (not os.environ) so a
# value in .env is honoured, per the api/main module docstring.
api_key: str = Field(default="", validation_alias="NL_SQL_API_KEY")
# Optional Postgres target. When `pg_dsn` is set, the registry registers a
# Postgres-backed database under `pg_db_id`. Point it at the read-only role
# (nl_sql_ro) for defence in depth β€” the engine also forces read-only
# transactions on top (see db/connection.py). Empty = SQLite-only (default).
pg_dsn: str = Field(default="", validation_alias="NL_SQL_PG_DSN")
pg_db_id: str = "pg_codebase_community"
pg_description: str = "StackExchange-derived BIRD codebase_community, loaded into Postgres 16."
# diskcache for LLM generate/embed responses (per docs/02_architecture_v2.md Β§6.5).
# Two subdirs ("gen", "embed") are created under this root by `nl_sql.llm.cache`.
llm_cache_dir: str = ".cache/llm"
llm_cache_size_limit_gb: int = 4
@lru_cache(maxsize=1)
def get_settings() -> Settings:
return Settings()