Skip to content

Commit b476df9

Browse files
authored
Merge pull request #1652 from rocket-admin/backend_unsafe_query_logging
improved query extraction
2 parents 18d0a31 + c04ed10 commit b476df9

2 files changed

Lines changed: 59 additions & 6 deletions

File tree

backend/src/entities/visualizations/panel-position/use-cases/generate-panel-position-with-ai.use.case.ts

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -421,7 +421,7 @@ ${currentQuery}
421421
422422
${feedbackSection}
423423
424-
Respond with SQL only. Preserve column aliases. Valid ${connectionType} syntax.`;
424+
CRITICAL: Respond with the SQL query ONLY — no explanations, no comments, no markdown, no text before or after the query. Just the raw SQL statement. Preserve column aliases. Valid ${connectionType} syntax.`;
425425
}
426426

427427
private cleanQueryResponse(aiResponse: string): string {
@@ -437,8 +437,34 @@ Respond with SQL only. Preserve column aliases. Valid ${connectionType} syntax.`
437437
if (parsed.query_text) {
438438
return parsed.query_text;
439439
}
440-
} catch {
441-
// Not valid JSON, return as-is
440+
} catch {}
441+
}
442+
443+
const sqlPrefixes = ['SELECT', 'WITH', 'SHOW', 'DESCRIBE', 'DESC', 'EXPLAIN'];
444+
const upperCleaned = cleaned.toUpperCase();
445+
const startsWithSql = sqlPrefixes.some((prefix) => upperCleaned.startsWith(prefix));
446+
447+
if (!startsWithSql) {
448+
const sqlPattern = new RegExp(
449+
`^(${sqlPrefixes.join('|')})\\b`,
450+
'im',
451+
);
452+
const match = cleaned.match(sqlPattern);
453+
if (match) {
454+
const sqlStart = cleaned.indexOf(match[0]);
455+
let sqlBody = cleaned.slice(sqlStart);
456+
457+
sqlBody = sqlBody.replace(/```\s*$/, '').trim();
458+
459+
const lastSemicolon = sqlBody.lastIndexOf(';');
460+
if (lastSemicolon !== -1) {
461+
const afterSemicolon = sqlBody.slice(lastSemicolon + 1).trim();
462+
if (afterSemicolon.length > 0 && /[a-zA-Z]{3,}/.test(afterSemicolon)) {
463+
sqlBody = sqlBody.slice(0, lastSemicolon + 1);
464+
}
465+
}
466+
467+
return sqlBody.trim();
442468
}
443469
}
444470

backend/src/entities/visualizations/panel-position/use-cases/generate-table-dashboard-with-ai.use.case.ts

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -484,7 +484,7 @@ ${currentQuery}
484484
485485
${feedbackSection}
486486
487-
Respond with SQL only. Preserve column aliases. Valid ${connectionType} syntax.`;
487+
CRITICAL: Respond with the SQL query ONLY — no explanations, no comments, no markdown, no text before or after the query. Just the raw SQL statement. Preserve column aliases. Valid ${connectionType} syntax.`;
488488
}
489489

490490
private cleanQueryResponse(aiResponse: string): string {
@@ -500,8 +500,35 @@ Respond with SQL only. Preserve column aliases. Valid ${connectionType} syntax.`
500500
if (parsed.query_text) {
501501
return parsed.query_text;
502502
}
503-
} catch {
504-
// Not valid JSON, return as-is
503+
} catch {}
504+
}
505+
506+
// If AI included explanatory text around the SQL, extract the SQL statement.
507+
const sqlPrefixes = ['SELECT', 'WITH', 'SHOW', 'DESCRIBE', 'DESC', 'EXPLAIN'];
508+
const upperCleaned = cleaned.toUpperCase();
509+
const startsWithSql = sqlPrefixes.some((prefix) => upperCleaned.startsWith(prefix));
510+
511+
if (!startsWithSql) {
512+
const sqlPattern = new RegExp(
513+
`^(${sqlPrefixes.join('|')})\\b`,
514+
'im',
515+
);
516+
const match = cleaned.match(sqlPattern);
517+
if (match) {
518+
const sqlStart = cleaned.indexOf(match[0]);
519+
let sqlBody = cleaned.slice(sqlStart);
520+
521+
sqlBody = sqlBody.replace(/```\s*$/, '').trim();
522+
523+
const lastSemicolon = sqlBody.lastIndexOf(';');
524+
if (lastSemicolon !== -1) {
525+
const afterSemicolon = sqlBody.slice(lastSemicolon + 1).trim();
526+
if (afterSemicolon.length > 0 && /[a-zA-Z]{3,}/.test(afterSemicolon)) {
527+
sqlBody = sqlBody.slice(0, lastSemicolon + 1);
528+
}
529+
}
530+
531+
return sqlBody.trim();
505532
}
506533
}
507534

0 commit comments

Comments
 (0)