121 lines
4.7 KiB
Python
121 lines
4.7 KiB
Python
from __future__ import annotations
|
|
|
|
import re
|
|
|
|
from .models import FailureReport
|
|
|
|
_SECRET_PATTERNS = (
|
|
(
|
|
re.compile(r"(?i)\b(?:mysql|mariadb|postgres(?:ql)?|sqlserver)://[^\s]+"),
|
|
"[DATABASE_URL]",
|
|
),
|
|
(re.compile(r"(?i)(password|passwd|secret|token|api[_ -]?key)\s*[:=]\s*\S+"), r"\1=[REDACTED]"),
|
|
(re.compile(r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b"), "[EMAIL]"),
|
|
(re.compile(r"\b(?:\d{1,3}\.){3}\d{1,3}\b"), "[IP]"),
|
|
(re.compile(r"(?i)bearer\s+[A-Za-z0-9._~+/=-]+"), "Bearer [REDACTED]"),
|
|
(re.compile(r"\bAKIA[0-9A-Z]{16}\b"), "[AWS_KEY]"),
|
|
(re.compile(r"-----BEGIN [A-Z ]+KEY-----[\s\S]+?-----END [A-Z ]+KEY-----"), "[PRIVATE_KEY]"),
|
|
(re.compile(r"\b[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b"), "[JWT]"),
|
|
)
|
|
|
|
|
|
def redact(value: str | None) -> str:
|
|
result = value or ""
|
|
for pattern, replacement in _SECRET_PATTERNS:
|
|
result = pattern.sub(replacement, result)
|
|
return result[:12_000]
|
|
|
|
|
|
def build_task(report: FailureReport) -> str:
|
|
prompt = redact(report.prompt)
|
|
feedback = redact(report.user_feedback)
|
|
expected_behavior = redact(report.expected_behavior)
|
|
response = redact(report.response)
|
|
error = redact(report.error_message)
|
|
sql = redact(report.sql_query)
|
|
tenant_scope = ''
|
|
if report.context.get('database_type') == 'customer':
|
|
tenant_scope = """
|
|
Tenant routing fact:
|
|
The customer has already selected one tenant database. SQL inside that database must not
|
|
invent an additional customer discriminator column. Use only columns present in tracked
|
|
schema/semantics, and fix SQL-generation guidance rather than rewriting one unknown column
|
|
to another guessed column. TenantDatabaseService records the explicit source type in
|
|
database.connections.<connection>.tenant_type; use that metadata instead of database-name
|
|
patterns or heuristics.
|
|
"""
|
|
sql_semantics = ''
|
|
if report.category in {'incorrect_answer', 'sql_execution'}:
|
|
sql_semantics = """
|
|
SQL repair guidance:
|
|
When the SQL is syntactically valid but uses the wrong business calculation, prefer adding or
|
|
correcting tracked domain knowledge in config/*_semantics.php so the schema prompt teaches the
|
|
model the right joins, filters, and formula. Do not hardcode one user's question or article ID
|
|
in SQLGeneratorService, and do not add post-generation replacement of guessed SQL.
|
|
|
|
For regression coverage of prompt guidance, expose the relevant protected schema/business-
|
|
context builder through a test subclass and assert that its real output contains the required
|
|
formula and rules. Do not mock AIService to return the known-bad SQL and then expect prompt
|
|
guidance to rewrite that mocked response. Keep database fixture tests only when production code
|
|
itself deterministically builds and executes the SQL under test.
|
|
"""
|
|
|
|
return f"""You are fixing failure report #{report.id} in the Laravel AI Chat repository.
|
|
|
|
Rules:
|
|
- First add a regression test that reproduces the reported behavior.
|
|
- The regression test must execute the relevant production behavior; never mock or stub the behavior being tested.
|
|
- Make the smallest production change that fixes the root cause.
|
|
- Preserve read-only SQL validation and tenant isolation.
|
|
- Never read or modify .env files, credentials, deployment secrets, or production data.
|
|
- Do not weaken tests, authentication, authorization, SQL validation, or safety checks.
|
|
- Do not commit or push; the orchestrator owns Git publication.
|
|
|
|
Failure category: {report.category}
|
|
Feedback type: {report.feedback_type or 'not specified'}
|
|
Source: {report.source}
|
|
Model: {redact(str(report.context.get('model', 'unknown')))}
|
|
Database type: {redact(str(report.context.get('database_type', 'unknown')))}
|
|
|
|
User request:
|
|
{prompt or '[not available]'}
|
|
|
|
User feedback:
|
|
{feedback or '[not available]'}
|
|
|
|
Expected behavior:
|
|
{expected_behavior or '[not available]'}
|
|
|
|
Observed answer:
|
|
{response or '[not available]'}
|
|
|
|
Observed error:
|
|
{error or '[not available]'}
|
|
|
|
Generated SQL, if relevant:
|
|
{sql or '[not available]'}
|
|
{tenant_scope}
|
|
{sql_semantics}
|
|
|
|
Run focused tests while working. Leave the repository with the fix and regression test applied.
|
|
"""
|
|
|
|
|
|
def build_retry_task(report: FailureReport, exception: Exception) -> str:
|
|
failure = redact(str(exception))[-6_000:]
|
|
|
|
return build_task(report) + f"""
|
|
|
|
The previous implementation failed isolated validation.
|
|
|
|
Correct the existing changes using this test output:
|
|
|
|
{failure}
|
|
|
|
Keep the regression test meaningful. Do not remove or weaken tests, safety checks,
|
|
authentication, authorization, SQL validation, or tenant isolation. If a new test calls an
|
|
unrelated specialized code path that correctly returns no result, repair the test to exercise
|
|
the owning production behavior; never broaden unrelated production routing merely to make the
|
|
test pass. Do not commit or push.
|
|
"""
|