mohamedgamal04
QueryQuest — Hugging Face Spaces deploy (Docker)
0cac9cf
Raw
History Blame Contribute Delete
2.06 kB
"""Async LLM access for the core engine.
I keep the provider call and its error handling here so the engine stays a pure
orchestrator and the CLI never touches the OpenAI client directly.
Author: mohamedgamal04
"""
from __future__ import annotations
import json
from openai import (
APIConnectionError,
AsyncOpenAI,
AuthenticationError,
NotFoundError,
RateLimitError,
)
from ..sql.handoff import _candidate_json_strings, extract_sql_statements
from .models import EngineConfig
def _extract_explanation(output: str) -> str:
"""Pull the optional `explanation` field from the model's JSON payload."""
for candidate in _candidate_json_strings(output):
try:
parsed = json.loads(candidate)
except json.JSONDecodeError:
continue
if isinstance(parsed, dict) and isinstance(parsed.get("explanation"), str):
return parsed["explanation"].strip()
return ""
async def generate_sql(config: EngineConfig, user_prompt: str) -> tuple[str, list[str], str, str | None]:
"""Ask the provider for SQL. Returns (raw_output, statements, explanation, error)."""
client = AsyncOpenAI(base_url=config.base_url, api_key=config.api_key)
messages = [
{"role": "system", "content": config.system_prompt},
{"role": "user", "content": user_prompt},
]
try:
response = await client.chat.completions.create(model=config.model, messages=messages)
except AuthenticationError as error:
return "", [], "", f"Authentication failed: {error}"
except NotFoundError as error:
return "", [], "", f"Model or endpoint not found: {error}"
except RateLimitError as error:
return "", [], "", f"Rate limit reached: {error}"
except APIConnectionError as error:
return "", [], "", f"Could not reach the provider: {error}"
output = response.choices[0].message.content or ""
statements = extract_sql_statements(output)
explanation = _extract_explanation(output)
return output, statements, explanation, None