v0.15.9: Auto-DESCRIBE retry, unmapped table recovery, animation queue
Expert retry loop enhanced: - On "Unknown column" error, auto-DESCRIBEs the failing table - DESCRIBE result injected into re-plan context - Unmapped tables handled via SELECT * LIMIT fallback - Recovery test step 4: abrechnungsinformationen (unmapped) → success Graph animation queue: - Events queued and played sequentially with 200ms interval - Prevents bulk HUD events from canceling each other's animations - Node pulses and edge flashes play one by one Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
067cbccea6
commit
d8ab778257
@ -128,8 +128,8 @@ NE → Geraete: JOIN geraete g ON g.NutzeinheitID = ne.ID
|
|||||||
Geraet → Verbrauch: JOIN geraeteverbraeuche gv ON gv.GeraetID = g.ID
|
Geraet → Verbrauch: JOIN geraeteverbraeuche gv ON gv.GeraetID = g.ID
|
||||||
|
|
||||||
RULES:
|
RULES:
|
||||||
- NEVER use DESCRIBE at runtime. You know the schema.
|
- For tables listed above: use ONLY the listed column names. Never guess.
|
||||||
- NEVER guess column names. Use ONLY columns listed above.
|
- For tables NOT listed above: use SELECT * with LIMIT to discover columns.
|
||||||
- For unknown tables: return an error, do not explore.
|
- If a query fails, the retry system will show you the error. Fix the column name and try again.
|
||||||
- Always LIMIT large queries (max 50 rows).
|
- Always LIMIT large queries (max 50 rows).
|
||||||
- Use LEFT JOIN when results might be empty."""
|
- Use LEFT JOIN when results might be empty."""
|
||||||
|
|||||||
@ -94,7 +94,9 @@ Write a concise, natural response. 1-3 sentences.
|
|||||||
plan_prompt += "\n\nPREVIOUS ATTEMPTS FAILED:\n"
|
plan_prompt += "\n\nPREVIOUS ATTEMPTS FAILED:\n"
|
||||||
for err in errors_so_far:
|
for err in errors_so_far:
|
||||||
plan_prompt += f"- Query: {err['query']}\n Error: {err['error']}\n"
|
plan_prompt += f"- Query: {err['query']}\n Error: {err['error']}\n"
|
||||||
plan_prompt += "\nFix the query using ONLY columns from the schema. Do NOT repeat the same mistake."
|
if 'describe' in err:
|
||||||
|
plan_prompt += f" DESCRIBE result: {err['describe'][:300]}\n"
|
||||||
|
plan_prompt += "\nFix the query. If a column was unknown, use the DESCRIBE result above or try SELECT * LIMIT 3 to see actual columns."
|
||||||
|
|
||||||
plan_messages = [
|
plan_messages = [
|
||||||
{"role": "system", "content": self.PLAN_SYSTEM.format(
|
{"role": "system", "content": self.PLAN_SYSTEM.format(
|
||||||
@ -143,11 +145,28 @@ Write a concise, natural response. 1-3 sentences.
|
|||||||
try:
|
try:
|
||||||
result = await asyncio.to_thread(run_db_query, query, database)
|
result = await asyncio.to_thread(run_db_query, query, database)
|
||||||
if result.startswith("Error:"):
|
if result.startswith("Error:"):
|
||||||
errors_so_far.append({"query": query, "error": result})
|
err_entry = {"query": query, "error": result}
|
||||||
|
# Auto-DESCRIBE on column errors to help retry
|
||||||
|
if "Unknown column" in result or "1054" in result:
|
||||||
|
import re
|
||||||
|
# Extract table name from query
|
||||||
|
tables_in_query = re.findall(r'FROM\s+(\w+)|JOIN\s+(\w+)', query, re.IGNORECASE)
|
||||||
|
for match in tables_in_query:
|
||||||
|
tname = match[0] or match[1]
|
||||||
|
if tname:
|
||||||
|
try:
|
||||||
|
desc = await asyncio.to_thread(run_db_query, f"DESCRIBE {tname}", database)
|
||||||
|
err_entry["describe"] = f"{tname}: {desc[:300]}"
|
||||||
|
await self.hud("tool_result", tool="describe",
|
||||||
|
output=f"Auto-DESCRIBE {tname}")
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
break
|
||||||
|
errors_so_far.append(err_entry)
|
||||||
had_error = True
|
had_error = True
|
||||||
await self.hud("tool_result", tool="query_db",
|
await self.hud("tool_result", tool="query_db",
|
||||||
output=f"ERROR (attempt {attempt}): {result[:150]}")
|
output=f"ERROR (attempt {attempt}): {result[:150]}")
|
||||||
break # stop executing, retry with new plan
|
break
|
||||||
tool_used = "query_db"
|
tool_used = "query_db"
|
||||||
tool_output = result
|
tool_output = result
|
||||||
await self.hud("tool_result", tool="query_db", output=result[:200])
|
await self.hud("tool_result", tool="query_db", output=result[:200])
|
||||||
|
|||||||
@ -152,6 +152,24 @@ export async function initGraph() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Animation queue: batch rapid events, play sequentially ---
|
||||||
|
const _animQueue = [];
|
||||||
|
let _animRunning = false;
|
||||||
|
const ANIM_INTERVAL = 200; // ms between queued animations
|
||||||
|
|
||||||
|
function _enqueue(fn) {
|
||||||
|
_animQueue.push(fn);
|
||||||
|
if (!_animRunning) _flushQueue();
|
||||||
|
}
|
||||||
|
|
||||||
|
function _flushQueue() {
|
||||||
|
if (!_animQueue.length) { _animRunning = false; return; }
|
||||||
|
_animRunning = true;
|
||||||
|
const fn = _animQueue.shift();
|
||||||
|
fn();
|
||||||
|
setTimeout(_flushQueue, ANIM_INTERVAL);
|
||||||
|
}
|
||||||
|
|
||||||
function pulseNode(id) {
|
function pulseNode(id) {
|
||||||
if (!cy) return;
|
if (!cy) return;
|
||||||
const node = cy.getElementById(id);
|
const node = cy.getElementById(id);
|
||||||
@ -170,6 +188,8 @@ function flashEdge(sourceId, targetId) {
|
|||||||
|
|
||||||
export function graphAnimate(event, node) {
|
export function graphAnimate(event, node) {
|
||||||
if (!cy) return;
|
if (!cy) return;
|
||||||
|
// Queue the animation instead of executing immediately
|
||||||
|
_enqueue(() => {
|
||||||
if (node && cy.getElementById(node).length) pulseNode(node);
|
if (node && cy.getElementById(node).length) pulseNode(node);
|
||||||
|
|
||||||
switch (event) {
|
switch (event) {
|
||||||
@ -193,6 +213,7 @@ export function graphAnimate(event, node) {
|
|||||||
case 'thinking': if (node) pulseNode(node); break;
|
case 'thinking': if (node) pulseNode(node); break;
|
||||||
case 'tick': pulseNode('sensor'); break;
|
case 'tick': pulseNode('sensor'); break;
|
||||||
}
|
}
|
||||||
|
}); // end _enqueue
|
||||||
}
|
}
|
||||||
|
|
||||||
export function startPhysics() {
|
export function startPhysics() {
|
||||||
|
|||||||
@ -25,3 +25,9 @@ not by reporting the error and stopping.
|
|||||||
- expect_trace: has tool_call
|
- expect_trace: has tool_call
|
||||||
- expect_response: not contains "I need assistance" or "developer" or "schema issue"
|
- expect_response: not contains "I need assistance" or "developer" or "schema issue"
|
||||||
- expect_response: length > 10
|
- expect_response: length > 10
|
||||||
|
|
||||||
|
### 4. Expert retries on unmapped table (abrechnungsinformationen)
|
||||||
|
- send: zeig mir die letzten 3 Abrechnungsinformationen
|
||||||
|
- expect_trace: has tool_call
|
||||||
|
- expect_response: not contains "Unknown column" or "1054"
|
||||||
|
- expect_response: length > 10
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user