"""MorphSQL — Hugging Face Space (conversion-first, DS-friendly)."""
from __future__ import annotations
import inspect
import gradio as gr
from morphsql import __product_name__, __version__
from morphsql.ai.risk_model import train_and_save
from morphsql.eval.pairs import ensure_pairs_file
from demo.handlers import (
HERO_EXAMPLE,
PLAYGROUND_EXAMPLE_LABELS,
SOURCE_DROPDOWN,
TARGET_DROPDOWN,
analyze_sql_object_ui,
convert_for_ui,
convert_upload_for_ui,
copilot_chat,
get_leaderboard_md,
load_sql_from_upload,
on_example_selected,
run_behavior_rag,
run_eval_suite,
run_feature_migration,
run_workbench_ui,
submit_eval_score,
)
from demo.theme import CUSTOM_CSS, build_theme
SPACE_URL = "https://huggingface.co/spaces/dgvj-work/morphsql"
GITHUB_URL = "https://github.com/dgvj-work/morphsql"
# Soft-fail boot so the Space still loads if artifacts are regenerating
try:
ensure_pairs_file()
except Exception as exc: # pragma: no cover
print(f"[MorphSQL] pairs bootstrap skipped: {exc}")
try:
train_and_save()
except Exception as exc: # pragma: no cover
print(f"[MorphSQL] risk model bootstrap skipped: {exc}")
try:
_BOOT = convert_for_ui(HERO_EXAMPLE, "snowflake", "pandas")
except Exception as exc: # pragma: no cover
print(f"[MorphSQL] boot convert failed: {exc}")
_BOOT = (
"Paste SQL and click **Convert**.",
"",
"Ready",
f"[Space]({SPACE_URL})",
None,
None,
"# Convert SQL to get a notebook cell",
'from morphsql.ai import pipeline\nprint(pipeline("sql-migration")("SELECT 1", source="snowflake", target="pandas"))\n',
)
def _blocks_style_kwargs() -> dict:
"""Gradio 5 (HF Spaces): theme/css on Blocks. Gradio 6+: those moved to launch()."""
params = inspect.signature(gr.Blocks.__init__).parameters
kwargs: dict = {}
if "theme" in params:
kwargs["theme"] = build_theme()
if "css" in params:
kwargs["css"] = CUSTOM_CSS
return kwargs
def _launch_style_kwargs() -> dict:
"""Pass theme/css to launch() when Blocks no longer accepts them (Gradio 6+)."""
if "theme" in inspect.signature(gr.Blocks.__init__).parameters:
return {}
return {"theme": build_theme(), "css": CUSTOM_CSS}
def _chatbot(**kwargs):
"""Chatbot with type='messages' when supported (Gradio 5.x)."""
params = inspect.signature(gr.Chatbot.__init__).parameters
if "type" in params and "type" not in kwargs:
kwargs["type"] = "messages"
return gr.Chatbot(**kwargs)
def _build_demo() -> gr.Blocks:
with gr.Blocks(
title=f"{__product_name__} — SQL → pandas / PySpark",
**_blocks_style_kwargs(),
) as demo:
eval_state = gr.State(value={})
eval_category = gr.State(value="all")
gr.HTML(
f"""
"""
)
with gr.Tabs():
with gr.Tab("Convert"):
gr.Markdown(
"1. Choose input dialect + output · "
"2. Paste SQL, load an example, or **upload** a `.sql` / `.zip` · "
"3. **Convert** → preview + **download** to your machine"
)
example = gr.Dropdown(
choices=PLAYGROUND_EXAMPLE_LABELS,
value=PLAYGROUND_EXAMPLE_LABELS[0],
label="Load a data-science / AI example",
info="Fills SQL, converts, runs sample preview.",
)
with gr.Row(equal_height=True):
source = gr.Dropdown(
choices=SOURCE_DROPDOWN,
value="snowflake",
label="SQL is written for",
scale=2,
min_width=160,
)
target = gr.Dropdown(
choices=TARGET_DROPDOWN,
value="pandas",
label="Convert to",
scale=3,
min_width=220,
)
convert_btn = gr.Button(
"Convert",
variant="primary",
scale=1,
min_width=120,
elem_classes=["action-btn"],
)
with gr.Row(equal_height=False):
sql_upload = gr.File(
label="Upload SQL (.sql / .txt) or a .zip of SQL files",
file_types=[".sql", ".txt", ".ddl", ".zip"],
type="filepath",
scale=4,
min_width=280,
elem_classes=["sql-upload-box"],
)
with gr.Column(scale=1, min_width=180, elem_classes=["upload-action-col"]):
upload_convert_btn = gr.Button(
"Upload & Convert → Download",
variant="primary",
elem_classes=["action-btn", "upload-action-btn"],
)
status = gr.Textbox(
label="Status",
value=_BOOT[2],
interactive=False,
max_lines=1,
)
with gr.Row():
sql_in = gr.Textbox(
label="Input SQL",
value=HERO_EXAMPLE,
lines=12,
max_lines=24,
placeholder="Paste warehouse SQL, or upload a file above…",
)
sql_out = gr.Textbox(
label="Output code",
value=_BOOT[1],
lines=12,
max_lines=24,
)
with gr.Row():
download = gr.File(
label="Download converted file (.py / .sql / .zip)",
value=_BOOT[5],
elem_classes=["download-box"],
)
preview = gr.Dataframe(
label="Sample preview",
value=_BOOT[4],
wrap=True,
elem_classes=["preview-table"],
)
notes = gr.Markdown(value=_BOOT[0])
with gr.Accordion("Notebook starter cell", open=False):
notebook = gr.Code(
language="python",
value=_BOOT[6],
lines=10,
)
with gr.Accordion("Hugging Face pipeline API (AI / ML)", open=True):
gr.Markdown(
"Same style as `transformers.pipeline` — use in Colab, HF Jobs, or training scripts."
)
api_code = gr.Code(
language="python",
value=_BOOT[7],
lines=12,
)
share = gr.Markdown(value=_BOOT[3])
outs = [
sql_in,
source,
target,
notes,
sql_out,
status,
share,
preview,
download,
notebook,
api_code,
]
example.change(on_example_selected, inputs=[example], outputs=outs)
convert_outs = [
notes,
sql_out,
status,
share,
preview,
download,
notebook,
api_code,
]
convert_btn.click(
convert_for_ui,
inputs=[sql_in, source, target],
outputs=convert_outs,
)
sql_in.submit(
convert_for_ui,
inputs=[sql_in, source, target],
outputs=convert_outs,
)
# Upload: fill the editor immediately; button converts + refreshes download
sql_upload.change(
load_sql_from_upload,
inputs=[sql_upload, sql_in],
outputs=[sql_in],
)
upload_convert_outs = [
sql_in,
notes,
sql_out,
status,
share,
preview,
download,
notebook,
api_code,
]
upload_convert_btn.click(
convert_upload_for_ui,
inputs=[sql_upload, sql_in, source, target],
outputs=upload_convert_outs,
)
with gr.Tab("Guide"):
gr.Markdown(
f"""
## For AI / ML practitioners
MorphSQL is a **deterministic** SQL→pandas / PySpark codegen tool (not a chat LLM). Use it when you have
warehouse SQL for labels/features and want a Python frame for training or Spark jobs.
**Typical path**
1. Convert feature SQL → pandas or PySpark
2. Point `tables[...]` at parquet / `datasets` / warehouse extracts / Spark tables
3. Feed `result` into sklearn, XGBoost, Transformers, or Spark ML
## Why data scientists use this
Warehouse SQL often lives in BI tools. MorphSQL rewrites dialect quirks (NVL, ZEROIFNULL, dates)
into pandas or PySpark you can run in Jupyter / Colab / Databricks.
## Recommended workflow
1. Paste SQL, load an example, or **upload** a `.sql` / `.zip`
2. Choose **Convert to** (pandas / PySpark / Snowflake / BigQuery / dbt)
3. Click **Convert** or **Upload & Convert → Download**
4. Download the `.py` / `.sql` / `.zip` to your machine and replace synthetic tables with real data
## Output choices
| Convert to | Best for |
|---|---|
| **Python (pandas)** | Feature engineering, EDA, model training prep |
| **Python (PySpark)** | Large-scale Spark DataFrame transforms |
| **Snowflake / BigQuery SQL** | Keeping transforms in the warehouse |
| **dbt project** | Productionizing SQL into models |
## More tab (Lab)
| Tool | Use when |
|---|---|
| **Object assess** | Need risk/complexity scoring for one SQL object |
| **Repository workbench** | Scanning a SQL repo (sample or zip) with lineage + validation |
| **ML feature SQL** | Migrating Vertica feature-engineering SQL → Snowflake/dbt |
| **Copilot** | Migration Q&A (keyword / optional HF LLM) |
| **Behavior notes / Eval** | Dialect quirks lookup and offline conversion scoring |
```python
from morphsql.ai import pipeline
out = pipeline("sql-migration")(sql, source="snowflake", target="pandas")
# or target="pyspark"
# out["converted_sql"] → exec / save as features.py
```
[Space]({SPACE_URL}) · [GitHub]({GITHUB_URL})
"""
)
with gr.Tab("More"):
gr.Markdown(
"Lab extras beyond day-to-day Convert: assess objects, scan a repo, "
"migrate feature SQL, ask the copilot, or run offline eval."
)
report_state = gr.State(value=None)
with gr.Accordion("Object assess & convert", open=False):
gr.Markdown(
"Score complexity/risk and convert a single SQL object "
"(pandas, PySpark, warehouse SQL, or dbt)."
)
with gr.Row():
assess_source = gr.Dropdown(
choices=SOURCE_DROPDOWN, value="vertica", label="Source"
)
assess_target = gr.Dropdown(
choices=TARGET_DROPDOWN, value="snowflake", label="Target"
)
assess_btn = gr.Button("Assess & Convert", variant="secondary")
assess_sql = gr.Textbox(
label="SQL object",
value="SELECT customer_id, ZEROIFNULL(order_amount) AS order_amount "
"FROM staging.orders WHERE order_date >= CURRENT_DATE - 30",
lines=8,
)
assess_badge = gr.Textbox(label="Score", interactive=False, max_lines=1)
with gr.Row():
assess_analysis = gr.Markdown()
assess_risk = gr.HTML()
assess_out = gr.Textbox(label="Converted output", lines=10)
assess_notes = gr.Markdown()
assess_btn.click(
analyze_sql_object_ui,
[assess_sql, assess_source, assess_target],
[assess_analysis, assess_risk, assess_badge, assess_out, assess_notes],
)
with gr.Accordion("Repository workbench", open=False):
gr.Markdown(
"Scan the sample Vertica repo (or upload a `.zip`), then convert / "
"validate / preview dbt + lineage."
)
with gr.Row():
wb_source = gr.Dropdown(
choices=SOURCE_DROPDOWN, value="vertica", label="Source"
)
wb_target = gr.Dropdown(
choices=TARGET_DROPDOWN, value="snowflake", label="Target"
)
wb_sample = gr.Checkbox(value=True, label="Use sample repository")
wb_upload = gr.File(label="Or upload SQL repo (.zip)", file_types=[".zip"])
wb_btn = gr.Button("Run migration intelligence", variant="secondary")
wb_summary = gr.Markdown()
wb_metrics = gr.Markdown()
with gr.Row():
wb_risk = gr.HTML()
wb_dist = gr.HTML()
wb_objects = gr.Markdown()
wb_lineage = gr.HTML()
with gr.Row():
wb_rationalization = gr.Markdown()
wb_runbook = gr.Markdown()
with gr.Row():
wb_dbt = gr.Markdown()
wb_validation = gr.Markdown()
wb_export = gr.Code(language="json", lines=8, label="Export JSON")
wb_btn.click(
run_workbench_ui,
[wb_upload, wb_sample, wb_source, wb_target],
[
wb_summary,
wb_objects,
wb_rationalization,
wb_runbook,
wb_dbt,
wb_validation,
wb_metrics,
wb_risk,
wb_dist,
wb_lineage,
wb_export,
report_state,
],
)
with gr.Accordion("ML feature SQL migration", open=False):
gr.Markdown(
"Convert `examples/ml_features/churn_feature_sql.sql` "
"(Vertica feature engineering) → Snowflake SQL or a dbt feature mart."
)
with gr.Row():
feat_target = gr.Dropdown(
choices=[
("Snowflake SQL", "snowflake"),
("dbt project (Snowflake)", "dbt-snowflake"),
],
value="snowflake",
label="Output",
)
feat_btn = gr.Button("Migrate feature SQL", variant="secondary")
feat_md = gr.Markdown()
feat_out = gr.Textbox(label="Output", lines=14)
feat_btn.click(run_feature_migration, [feat_target], [feat_md, feat_out])
with gr.Accordion("Migration copilot", open=False):
gr.Markdown(
"Ask migration questions. Uses keyword/HF fallback guidance "
"(set `HF_TOKEN` for LLM replies)."
)
copilot = _chatbot(label="Copilot", height=320)
copilot_msg = gr.Textbox(
label="Message",
placeholder="e.g. How should I handle ZEROIFNULL on Snowflake?",
lines=2,
)
with gr.Row():
copilot_source = gr.Dropdown(
choices=SOURCE_DROPDOWN, value="vertica", label="Source"
)
copilot_target = gr.Dropdown(
choices=TARGET_DROPDOWN, value="snowflake", label="Target"
)
copilot_btn = gr.Button("Ask", variant="secondary")
copilot_sql = gr.Textbox(
label="Optional SQL context",
value="SELECT ZEROIFNULL(amount) FROM staging.transactions",
lines=3,
)
copilot_btn.click(
copilot_chat,
[
copilot_msg,
copilot,
report_state,
copilot_sql,
copilot_source,
copilot_target,
],
[copilot, copilot_msg],
)
copilot_msg.submit(
copilot_chat,
[
copilot_msg,
copilot,
report_state,
copilot_sql,
copilot_source,
copilot_target,
],
[copilot, copilot_msg],
)
with gr.Accordion("Dialect behavior notes", open=False):
rag_q = gr.Textbox(
label="Question",
value="Oracle empty string vs Snowflake NULL",
lines=2,
)
with gr.Row():
rag_source = gr.Dropdown(
choices=SOURCE_DROPDOWN, value="oracle", label="From"
)
rag_target = gr.Dropdown(
choices=[
("Snowflake SQL", "snowflake"),
("BigQuery SQL", "bigquery"),
],
value="snowflake",
label="To",
)
rag_run = gr.Button("Search", variant="secondary")
rag_out = gr.Markdown()
rag_run.click(
run_behavior_rag,
[rag_q, rag_source, rag_target],
[rag_out],
)
with gr.Accordion("Offline conversion eval", open=False):
with gr.Row():
eval_limit = gr.Slider(10, 200, value=40, step=10, label="Pairs")
eval_run = gr.Button("Run eval", variant="secondary")
eval_summary = gr.Markdown()
eval_detail = gr.Markdown()
with gr.Row():
lb_name = gr.Textbox(label="Handle", value="hf-user")
lb_submit = gr.Button("Submit local score")
leaderboard_md = gr.Markdown(value=get_leaderboard_md())
eval_run.click(
run_eval_suite,
[eval_limit, eval_category],
[eval_summary, eval_detail, eval_state],
)
lb_submit.click(
submit_eval_score, [lb_name, eval_state], [leaderboard_md]
)
gr.Markdown(
f""
)
return demo
demo = _build_demo()
if __name__ == "__main__":
demo.launch(**_launch_style_kwargs())