add all files

This commit is contained in:
Rucus
2026-02-17 09:29:34 -06:00
parent b8c8d67c67
commit 782d203799
21925 changed files with 2433086 additions and 0 deletions

View File

@@ -0,0 +1,75 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>SugarScale SQL Agent</title>
<link rel="stylesheet" href="/static/styles.css" />
</head>
<body>
<header>
<h1>SugarScale SQL Agent</h1>
<p>Ask natural-language questions and optionally execute the generated SQL.</p>
</header>
<main>
<section class="panel">
<form id="query-form">
<label for="question">Question</label>
<textarea id="question" name="question" rows="3" placeholder="e.g., How many loads were completed yesterday?" required></textarea>
<label for="tables">Table hints (comma-separated)</label>
<input id="tables" name="tables" type="text" placeholder="dbo.SugarLoadData" />
<div class="options">
<label>
<input type="checkbox" id="execute" name="execute" /> Execute SQL
</label>
<label for="max-rows">Max rows</label>
<input id="max-rows" name="max_rows" type="number" min="1" value="500" />
</div>
<button type="submit">Run</button>
</form>
</section>
<section class="panel" id="results" hidden>
<h2>Results</h2>
<div class="warning" id="warning" hidden></div>
<div id="summary"></div>
<details>
<summary>Generated SQL</summary>
<pre><code id="generated-sql"></code></pre>
</details>
<details id="sanitized-block" hidden>
<summary>Sanitized SQL (executed)</summary>
<pre><code id="sanitized-sql"></code></pre>
</details>
<div id="row-count"></div>
<table id="result-table" hidden>
<thead id="result-thead"></thead>
<tbody id="result-tbody"></tbody>
</table>
<div id="feedback-panel" hidden>
<h3>Was this answer helpful?</h3>
<div class="feedback-actions">
<button type="button" id="feedback-correct">Mark Correct</button>
<button type="button" id="feedback-incorrect" class="secondary">Needs Work</button>
</div>
<p id="feedback-status" hidden></p>
</div>
</section>
<section class="panel error" id="error" hidden>
<h2>Error</h2>
<pre id="error-message"></pre>
</section>
</main>
<footer>
<small>Powered by the SugarScale SQL agent.</small>
</footer>
<script src="/static/app.js" type="module"></script>
</body>
</html>

View File

@@ -0,0 +1,182 @@
const form = document.getElementById("query-form");
const resultsPanel = document.getElementById("results");
const errorPanel = document.getElementById("error");
const warningEl = document.getElementById("warning");
const summaryEl = document.getElementById("summary");
const generatedSqlEl = document.getElementById("generated-sql");
const sanitizedBlock = document.getElementById("sanitized-block");
const sanitizedSqlEl = document.getElementById("sanitized-sql");
const rowCountEl = document.getElementById("row-count");
const tableEl = document.getElementById("result-table");
const tableHeadEl = document.getElementById("result-thead");
const tableBodyEl = document.getElementById("result-tbody");
const errorMessageEl = document.getElementById("error-message");
const feedbackPanel = document.getElementById("feedback-panel");
const feedbackCorrectBtn = document.getElementById("feedback-correct");
const feedbackIncorrectBtn = document.getElementById("feedback-incorrect");
const feedbackStatus = document.getElementById("feedback-status");
let latestResult = null;
function resetPanels() {
resultsPanel.hidden = true;
errorPanel.hidden = true;
warningEl.hidden = true;
warningEl.textContent = "";
summaryEl.textContent = "";
generatedSqlEl.textContent = "";
sanitizedBlock.hidden = true;
sanitizedSqlEl.textContent = "";
rowCountEl.textContent = "";
tableEl.hidden = true;
tableHeadEl.innerHTML = "";
tableBodyEl.innerHTML = "";
errorMessageEl.textContent = "";
feedbackPanel.hidden = true;
feedbackStatus.hidden = true;
feedbackStatus.textContent = "";
latestResult = null;
}
function renderTable(columns, rows) {
if (!columns || !rows || rows.length === 0) {
tableEl.hidden = true;
return;
}
const headerRow = document.createElement("tr");
columns.forEach((col) => {
const th = document.createElement("th");
th.textContent = col;
headerRow.appendChild(th);
});
tableHeadEl.appendChild(headerRow);
rows.forEach((row) => {
const tr = document.createElement("tr");
columns.forEach((col) => {
const td = document.createElement("td");
const value = row[col];
td.textContent = value === null || value === undefined ? "" : String(value);
tr.appendChild(td);
});
tableBodyEl.appendChild(tr);
});
tableEl.hidden = false;
}
form.addEventListener("submit", async (event) => {
event.preventDefault();
resetPanels();
const question = document.getElementById("question").value.trim();
const tablesValue = document.getElementById("tables").value.trim();
const execute = document.getElementById("execute").checked;
const maxRows = parseInt(document.getElementById("max-rows").value, 10) || 500;
if (!question) {
errorPanel.hidden = false;
errorMessageEl.textContent = "Please enter a question.";
return;
}
const payload = {
question,
tables: tablesValue ? tablesValue.split(",").map((s) => s.trim()).filter(Boolean) : undefined,
execute,
max_rows: maxRows,
feedback: null,
};
try {
const response = await fetch("/api/query", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload),
});
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(errorData.detail || `Request failed with status ${response.status}`);
}
const data = await response.json();
summaryEl.textContent = data.summary || "(No summary provided)";
generatedSqlEl.textContent = data.sql || "";
if (data.llm_warning) {
warningEl.textContent = data.llm_warning;
warningEl.hidden = false;
}
if (data.sanitized_sql) {
sanitizedSqlEl.textContent = data.sanitized_sql;
sanitizedBlock.hidden = false;
}
if (typeof data.row_count === "number") {
rowCountEl.textContent = `Rows returned: ${data.row_count}`;
}
renderTable(data.columns, data.rows);
latestResult = {
question,
sql: data.sql || "",
summary: data.summary || "",
sanitized_sql: data.sanitized_sql || null,
};
feedbackPanel.hidden = false;
feedbackStatus.hidden = true;
feedbackStatus.textContent = "";
resultsPanel.hidden = false;
} catch (error) {
errorPanel.hidden = false;
errorMessageEl.textContent = error.message || String(error);
}
});
async function sendFeedback(tag) {
if (!latestResult) {
return;
}
const payload = {
question: latestResult.question,
sql: latestResult.sql,
summary: latestResult.summary,
sanitized_sql: latestResult.sanitized_sql,
feedback: tag,
};
feedbackStatus.hidden = false;
feedbackStatus.textContent = "Sending feedback...";
feedbackCorrectBtn.disabled = true;
feedbackIncorrectBtn.disabled = true;
try {
const response = await fetch("/api/feedback", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload),
});
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(errorData.detail || `Feedback request failed with status ${response.status}`);
}
feedbackStatus.textContent = tag === "correct" ? "Marked as correct." : "Marked as needs work.";
} catch (error) {
feedbackStatus.textContent = error.message || String(error);
} finally {
feedbackCorrectBtn.disabled = false;
feedbackIncorrectBtn.disabled = false;
}
}
feedbackCorrectBtn.addEventListener("click", () => sendFeedback("correct"));
feedbackIncorrectBtn.addEventListener("click", () => sendFeedback("incorrect"));

View File

@@ -0,0 +1,144 @@
* {
box-sizing: border-box;
}
body {
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
margin: 0;
padding: 0;
background: #f4f6f8;
color: #0f172a;
}
header,
footer {
background: #0f172a;
color: #f8fafc;
padding: 1.5rem;
text-align: center;
}
main {
max-width: 960px;
margin: 2rem auto;
display: grid;
gap: 1.5rem;
}
.panel {
background: #ffffff;
padding: 1.5rem;
border-radius: 12px;
box-shadow: 0 12px 24px rgba(15, 23, 42, 0.08);
}
.panel.error {
border-left: 6px solid #ef4444;
}
.warning {
background: #fef3c7;
color: #92400e;
border-left: 4px solid #f59e0b;
padding: 0.75rem 1rem;
border-radius: 8px;
margin-bottom: 1rem;
white-space: pre-wrap;
}
label {
display: block;
font-weight: 600;
margin-bottom: 0.35rem;
}
textarea,
input,
button {
width: 100%;
padding: 0.75rem;
border-radius: 8px;
border: 1px solid #cbd5f5;
font-size: 1rem;
margin-bottom: 1rem;
}
textarea:focus,
input:focus {
outline: none;
border-color: #2563eb;
box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.2);
}
.options {
display: flex;
gap: 1rem;
align-items: center;
}
.options label {
margin-bottom: 0;
font-weight: 500;
}
button {
background: linear-gradient(135deg, #2563eb, #1d4ed8);
color: #fff;
border: none;
cursor: pointer;
font-weight: 600;
transition: transform 0.15s ease, box-shadow 0.15s ease;
}
button:hover {
transform: translateY(-1px);
box-shadow: 0 10px 20px rgba(37, 99, 235, 0.25);
}
button.secondary {
background: linear-gradient(135deg, #64748b, #475569);
}
pre {
background: #0f172a;
color: #e2e8f0;
padding: 1rem;
border-radius: 8px;
overflow-x: auto;
}
table {
width: 100%;
border-collapse: collapse;
margin-top: 1rem;
}
th,
td {
border: 1px solid #e2e8f0;
padding: 0.65rem;
text-align: left;
}
th {
background: #f1f5f9;
}
#error-message {
white-space: pre-wrap;
}
#feedback-panel {
margin-top: 1.5rem;
}
.feedback-actions {
display: flex;
gap: 1rem;
margin-bottom: 0.75rem;
}
#feedback-status {
color: #0369a1;
font-weight: 600;
}