Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
91 commits
Select commit Hold shift + click to select a range
c07799d
sql-syntax-feedback
galshubeli Dec 16, 2025
54c1902
Merge branch 'staging' into add-database-feedback
galshubeli Dec 16, 2025
4100807
add-exception-feedback
galshubeli Dec 16, 2025
971f23a
update-prompt
galshubeli Dec 16, 2025
66914ff
Update api/agents/utils.py
galshubeli Dec 16, 2025
d606e6a
Update api/core/text2sql.py
galshubeli Dec 16, 2025
ef82f87
Update api/agents/utils.py
galshubeli Dec 16, 2025
f498148
pylint
galshubeli Dec 16, 2025
d5f4a90
Update api/agents/healer_agent.py
galshubeli Dec 18, 2025
4a39c11
Merge branch 'staging' into add-database-feedback
galshubeli Dec 18, 2025
9f48aec
Merge pull request #343 from FalkorDB/main
gkorland Dec 21, 2025
66672f7
Add concurrency settings to Playwright workflow
gkorland Dec 29, 2025
809915a
Implement concurrency for pull request workflows
gkorland Dec 29, 2025
fc6201b
Merge pull request #349 from FalkorDB/gkorland-patch-2
Naseem77 Dec 29, 2025
2e8c249
Update demo image in README
danshalev7 Dec 30, 2025
d020d55
Merge branch 'staging' into danshalev7-patch-1
galshubeli Dec 30, 2025
3e0f2f9
Merge pull request #352 from FalkorDB/danshalev7-patch-1
galshubeli Dec 30, 2025
3234031
fix(mobile): improve mobile UI spacing and header layout
Naseem77 Dec 31, 2025
b725c55
Merge pull request #353 from FalkorDB/fix-mobile-improve-mobile-UI-sp…
Naseem77 Jan 1, 2026
924b2fd
Fix E2E tests
Naseem77 Jan 1, 2026
4bc9b4d
fix tests
Naseem77 Jan 1, 2026
0da7498
Merge pull request #355 from FalkorDB/fix/e2e-test-failures
galshubeli Jan 4, 2026
32d49f5
Merge branch 'staging' into add-database-feedback
galshubeli Jan 4, 2026
f282ae8
pylint-fixes
galshubeli Jan 4, 2026
a8a3b45
pylint-fixes
galshubeli Jan 4, 2026
d2e6ed2
Fix E2E tests
Naseem77 Jan 4, 2026
b037584
Implement Clean Slate theme system
Naseem77 Jan 4, 2026
dea38a3
update-fl
galshubeli Jan 4, 2026
e99ded3
Update api/agents/healer_agent.py
galshubeli Jan 4, 2026
06d6c2a
Add privacy policy link and improve SQL display wrapping
Naseem77 Jan 4, 2026
536d506
pylint-fix
galshubeli Jan 5, 2026
3c9db6e
whiteline
galshubeli Jan 5, 2026
fab00db
Merge pull request #356 from FalkorDB/fix-e2e-tests
galshubeli Jan 5, 2026
cfa80c5
Merge branch 'staging' into add-database-feedback
galshubeli Jan 5, 2026
c226dba
rm-analysis-prompt
galshubeli Jan 5, 2026
cbc94c5
update background colors
Naseem77 Jan 5, 2026
0019c69
Merge branch 'staging' into implement-clean-slate-theme-system
Naseem77 Jan 5, 2026
36f71de
fix comments
Naseem77 Jan 5, 2026
02eee71
Merge branch 'staging' into fix/policy-link-and-improve-sql-display
Naseem77 Jan 5, 2026
22bc3e3
add copy sql query option
Naseem77 Jan 5, 2026
39c8f39
Merge branch 'fix/policy-link-and-improve-sql-display' of https://git…
Naseem77 Jan 5, 2026
29c8351
Merge pull request #337 from FalkorDB/add-database-feedback
galshubeli Jan 5, 2026
0242a6a
Bump aiohttp from 3.13.2 to 3.13.3 in the pip group across 1 directory
dependabot[bot] Jan 6, 2026
53495a9
Merge branch 'staging' into fix/policy-link-and-improve-sql-display
galshubeli Jan 6, 2026
132b093
Merge pull request #360 from FalkorDB/fix/policy-link-and-improve-sql…
galshubeli Jan 6, 2026
aeebaef
prompt-updates
galshubeli Jan 6, 2026
c36f747
Resolved conflicts in ChatMessage
Naseem77 Jan 6, 2026
a8c9f1e
production-ready-prompt
galshubeli Jan 6, 2026
eb4c553
rm-user-rule-conf
galshubeli Jan 6, 2026
c1c75ce
manage-graphs-rules
galshubeli Jan 6, 2026
09c3bc8
Update app/src/pages/Settings.tsx
galshubeli Jan 7, 2026
331ad50
Update api/app_factory.py
galshubeli Jan 7, 2026
67e0ef6
rm-logs
galshubeli Jan 7, 2026
79890a8
Merge branch 'staging' into rules-and-optimize-prompt
galshubeli Jan 7, 2026
1eb9caf
fix-lint
galshubeli Jan 7, 2026
be15e06
Merge pull request #357 from FalkorDB/implement-clean-slate-theme-system
danshalev7 Jan 7, 2026
19958a5
unified-descriptions
galshubeli Jan 7, 2026
bb121bb
Merge branch 'staging' into description-improvements
galshubeli Jan 7, 2026
1fe7fe4
Bump uvicorn from 0.38.0 to 0.40.0
dependabot[bot] Jan 7, 2026
db206cc
fix-lint
galshubeli Jan 7, 2026
6aeb699
Update api/utils.py
galshubeli Jan 7, 2026
578a6ab
Merge pull request #348 from FalkorDB/dependabot/pip/staging/uvicorn-…
gkorland Jan 7, 2026
803232f
Merge branch 'staging' into dependabot/pip/pip-ed395609ee
gkorland Jan 7, 2026
a3d8d95
fix-any-usage
galshubeli Jan 7, 2026
71a2976
Bump playwright from 1.56.0 to 1.57.0
dependabot[bot] Jan 7, 2026
552c24a
Merge branch 'staging' into description-improvements
galshubeli Jan 7, 2026
3f45eb1
fix-docs
galshubeli Jan 7, 2026
cdea661
fix-mu-error
galshubeli Jan 7, 2026
428734f
Merge branch 'staging' into rules-and-optimize-prompt
galshubeli Jan 7, 2026
c8f5a0a
reasonin-output
galshubeli Jan 7, 2026
9e9863c
lint-fix
galshubeli Jan 7, 2026
3a96f50
rm-instructions-comments
galshubeli Jan 7, 2026
f0d79f7
Merge pull request #366 from FalkorDB/description-improvements
galshubeli Jan 7, 2026
bccf653
Merge branch 'staging' into prompt-reasoning
galshubeli Jan 7, 2026
e37f339
Merge pull request #367 from FalkorDB/prompt-reasoning
galshubeli Jan 7, 2026
22202a5
Merge branch 'staging' into rules-and-optimize-prompt
galshubeli Jan 7, 2026
4542848
update-usage
galshubeli Jan 7, 2026
a354f30
Merge branch 'staging' into dependabot/pip/pip-ed395609ee
gkorland Jan 7, 2026
32624a8
Merge pull request #362 from FalkorDB/dependabot/pip/pip-ed395609ee
gkorland Jan 7, 2026
bafa923
Merge branch 'staging' into dependabot/pip/staging/playwright-1.57.0
gkorland Jan 7, 2026
3074b29
Merge pull request #335 from FalkorDB/dependabot/pip/staging/playwrig…
Naseem77 Jan 8, 2026
7ccb4b4
update-bg-colors
galshubeli Jan 8, 2026
7eb3b45
clear-save-b
galshubeli Jan 8, 2026
f22f0a8
state-and-stm
galshubeli Jan 8, 2026
bc10f35
update styling for moblie and settings tab
Naseem77 Jan 8, 2026
7395853
bump version for 0.0.14
Naseem77 Jan 8, 2026
588c56b
Merge branch 'staging' into rules-and-optimize-prompt
Naseem77 Jan 8, 2026
f3dd0a3
Merge pull request #363 from FalkorDB/rules-and-optimize-prompt
Naseem77 Jan 8, 2026
bab6bb0
fix-demo-usage
galshubeli Jan 8, 2026
847c8ea
lint
galshubeli Jan 8, 2026
8427655
Merge pull request #370 from FalkorDB/fix-demo-rules
galshubeli Jan 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/playwright.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ on:
pull_request:
branches: [ main, staging ]

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

permissions:
contents: read

Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ on:
pull_request:
branches: [ main, staging ]

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

permissions:
contents: read

Expand Down
4 changes: 2 additions & 2 deletions Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ name = "pypi"

[packages]
fastapi = "~=0.124.0"
uvicorn = "~=0.38.0"
uvicorn = "~=0.40.0"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

uvicorn 0.40.0 PyPI release security vulnerabilities breaking changes

💡 Result:

Short answer:

  • Uvicorn 0.40.0 (PyPI release) has no known direct security vulnerabilities. [1][2][3]
  • The main breaking change called out in the release notes is: drop of Python 3.9 support (upgrade requirement). [1]

Notes / context:

  • Snyk lists no known vulnerabilities for latest 0.40.0. [2]
  • PyPI shows the 0.40.0 package uploaded (Dec 21, 2025) and metadata. [3]
  • Be aware of related-project fixes: e.g., Starlette had a DoS multipart bug (CVE-2024-47874) fixed in Starlette 0.40.0 — if your app depends on Starlette/FastAPI, verify those dependency versions; that CVE is not a Uvicorn vulnerability but is relevant to ASGI apps. [4]

Sources:
[1] Uvicorn release notes (0.40.0).
[2] Snyk vulnerability page for uvicorn (latest 0.40.0).
[3] PyPI uvicorn project page (uvicorn-0.40.0).
[4] CVE-2024-47874 / Starlette advisory.


Verify Python version requirement compatibility with uvicorn 0.40.0.

Uvicorn 0.40.0 has no known security vulnerabilities, but it drops support for Python 3.9. Ensure the project's Python version requirement is set to 3.10 or higher. Also verify that FastAPI and related dependencies (particularly Starlette) are updated to compatible versions that address known security issues.

litellm = "~=1.80.9"
falkordb = "~=1.2.2"
psycopg2-binary = "~=2.9.11"
Expand All @@ -22,7 +22,7 @@ fastmcp = ">=2.13.1"
[dev-packages]
pytest = "~=8.4.2"
pylint = "~=4.0.3"
playwright = "~=1.56.0"
playwright = "~=1.57.0"
pytest-playwright = "~=0.7.1"
pytest-asyncio = "~=1.2.0"

Expand Down
271 changes: 136 additions & 135 deletions Pipfile.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Connect and ask questions: [![Discord](https://img.shields.io/badge/Discord-%235
[![Swagger UI](https://img.shields.io/badge/API-Swagger-11B48A?logo=swagger&logoColor=white)](https://app.queryweaver.ai/docs)
</div>

![queryweaver-demo-video-ui](https://github.com/user-attachments/assets/b66018cb-0e42-4907-8ac1-c169762ff22d)
![new-qw-ui-gif](https://github.com/user-attachments/assets/87bb6a50-5bf4-4217-ad05-f99e32ed2dd0)

## Get Started
### Docker
Expand Down
2 changes: 2 additions & 0 deletions api/agents/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@
from .relevancy_agent import RelevancyAgent
from .follow_up_agent import FollowUpAgent
from .response_formatter_agent import ResponseFormatterAgent
from .healer_agent import HealerAgent
from .utils import parse_response

__all__ = [
"AnalysisAgent",
"RelevancyAgent",
"FollowUpAgent",
"ResponseFormatterAgent",
"HealerAgent",
"parse_response"
]
239 changes: 161 additions & 78 deletions api/agents/analysis_agent.py

Large diffs are not rendered by default.

328 changes: 328 additions & 0 deletions api/agents/healer_agent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,328 @@
"""
HealerAgent - Specialized agent for fixing SQL syntax errors.

This agent focuses solely on correcting SQL queries that failed execution,
without requiring full graph context. It uses the error message and the
failed query to generate a corrected version.
"""
# pylint: disable=trailing-whitespace,line-too-long,too-many-arguments
# pylint: disable=too-many-positional-arguments,broad-exception-caught

import re
from typing import Dict, Callable, Any
from litellm import completion
from api.config import Config
from .utils import parse_response


class HealerAgent:
"""Agent specialized in fixing SQL syntax errors."""

def __init__(self, max_healing_attempts: int = 3):
"""Initialize the healer agent.

Args:
max_healing_attempts: Maximum number of healing attempts before giving up
"""
self.max_healing_attempts = max_healing_attempts
self.messages = []

@staticmethod
def validate_sql_syntax(sql_query: str) -> dict:
"""
Validate SQL query for basic syntax errors.
Similar to CypherValidator in the text-to-cypher PR.

Args:
sql_query: The SQL query to validate

Returns:
dict with 'is_valid', 'errors', and 'warnings' keys
"""
errors = []
warnings = []

query = sql_query.strip()

# Check if query is empty
if not query:
errors.append("Query is empty")
return {"is_valid": False, "errors": errors, "warnings": warnings}

# Check for basic SQL keywords
query_upper = query.upper()
has_sql_keywords = any(
kw in query_upper for kw in ["SELECT", "INSERT", "UPDATE", "DELETE", "WITH", "CREATE"]
)
if not has_sql_keywords:
errors.append("Query does not contain valid SQL keywords")

# Check for dangerous operations (for dev/test safety)
dangerous_patterns = [
r'\bDROP\s+TABLE\b', r'\bTRUNCATE\b', r'\bDELETE\s+FROM\s+\w+\s*;?\s*$'
]
for pattern in dangerous_patterns:
if re.search(pattern, query_upper):
warnings.append(f"Query contains potentially dangerous operation: {pattern}")

# Check for balanced parentheses
paren_count = 0
for char in query:
if char == '(':
paren_count += 1
elif char == ')':
paren_count -= 1
if paren_count < 0:
errors.append("Unbalanced parentheses in query")
break
if paren_count != 0:
errors.append("Unbalanced parentheses in query")
Comment on lines +68 to +79
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Duplicate "Unbalanced parentheses" error can be appended.

When a closing parenthesis causes paren_count to go negative (line 75-76), the error is appended and the loop breaks. However, after the loop, line 78-79 checks paren_count != 0 and appends the same error again.

🔎 Proposed fix
         # Check for balanced parentheses
         paren_count = 0
         for char in query:
             if char == '(':
                 paren_count += 1
             elif char == ')':
                 paren_count -= 1
                 if paren_count < 0:
                     errors.append("Unbalanced parentheses in query")
                     break
-        if paren_count != 0:
+        if paren_count > 0:  # Only check for unclosed opening parens
             errors.append("Unbalanced parentheses in query")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Check for balanced parentheses
paren_count = 0
for char in query:
if char == '(':
paren_count += 1
elif char == ')':
paren_count -= 1
if paren_count < 0:
errors.append("Unbalanced parentheses in query")
break
if paren_count != 0:
errors.append("Unbalanced parentheses in query")
# Check for balanced parentheses
paren_count = 0
for char in query:
if char == '(':
paren_count += 1
elif char == ')':
paren_count -= 1
if paren_count < 0:
errors.append("Unbalanced parentheses in query")
break
if paren_count > 0: # Only check for unclosed opening parens
errors.append("Unbalanced parentheses in query")
🤖 Prompt for AI Agents
In @api/agents/healer_agent.py around lines 68-79, The current parentheses check
can append "Unbalanced parentheses in query" twice: once when paren_count goes
negative inside the loop and again after the loop; update the post-loop
condition to only append when paren_count > 0 (so negative counts already
reported inside the loop aren’t duplicated), i.e., keep the existing in-loop
append-and-break on paren_count < 0 but change the final check from paren_count
!= 0 to paren_count > 0 (or alternatively introduce a boolean like
unbalanced_reported and skip the second append if it’s true) to ensure the error
is appended only once.


# Check for SELECT queries have proper structure
if query_upper.startswith("SELECT") or "SELECT" in query_upper:
if "FROM" not in query_upper and "DUAL" not in query_upper:
warnings.append("SELECT query missing FROM clause")

return {
"is_valid": len(errors) == 0,
"errors": errors,
"warnings": warnings
}

def _build_healing_prompt(
self,
failed_sql: str,
error_message: str,
db_description: str,
question: str,
database_type: str
) -> str:
"""Build a focused prompt for SQL query healing."""

# Analyze error to provide targeted hints
error_hints = self._analyze_error(error_message, database_type)

prompt = f"""You are a SQL query debugging expert. Your task is to fix a SQL query that failed execution.

DATABASE TYPE: {database_type.upper()}

FAILED SQL QUERY:
```sql
{failed_sql}
```

EXECUTION ERROR:
{error_message}

{f"ORIGINAL QUESTION: {question}" if question else ""}

{f"DATABASE INFO: {db_description}"}

COMMON ERROR PATTERNS:
{error_hints}

YOUR TASK:
1. Identify the exact cause of the error
2. Fix ONLY what's broken - don't rewrite the entire query
3. Ensure the fix is compatible with {database_type.upper()}
4. Maintain the original query logic and intent

CRITICAL RULES FOR {database_type.upper()}:
"""

if database_type == "sqlite":
prompt += """
- SQLite does NOT support EXTRACT() function - use strftime() instead
* EXTRACT(YEAR FROM date_col) → strftime('%Y', date_col)
* EXTRACT(MONTH FROM date_col) → strftime('%m', date_col)
* EXTRACT(DAY FROM date_col) → strftime('%d', date_col)
- SQLite column/table names are case-insensitive BUT must exist
- SQLite uses double quotes "column" for identifiers with special characters
- Use backticks `column` for compatibility
- No schema qualifiers (database.table.column)
"""
elif database_type == "postgresql":
prompt += """
- PostgreSQL is case-sensitive - use double quotes for mixed-case identifiers
- EXTRACT() is supported: EXTRACT(YEAR FROM date_col)
- Column references must match exact case when quoted
"""

prompt += """
RESPONSE FORMAT (valid JSON only):
{
"sql_query": "-- your fixed SQL query here",
"confidence": 85,
"explanation": "Brief explanation of what was fixed",
"changes_made": ["Changed EXTRACT to strftime", "Fixed column casing"]
}

IMPORTANT:
- Return ONLY the JSON object, no other text
- Fix ONLY the specific error, preserve the rest
- Test your fix mentally before responding
- If error is about a column/table name, check spelling carefully
"""

return prompt

def heal_and_execute( # pylint: disable=too-many-locals
self,
initial_sql: str,
initial_error: str,
execute_sql_func: Callable[[str], Any],
db_description: str = "",
question: str = "",
database_type: str = "sqlite"
) -> Dict[str, Any]:
"""Iteratively heal and execute SQL query until success or max attempts.

This method creates a conversation loop between the healer and the database:
1. Build initial prompt once with the failed SQL and error (including syntax validation)
2. Loop: Call LLM → Parse healed SQL → Execute → Check if successful
3. If successful, return results
4. If failed and not last attempt, add error feedback and repeat
5. If failed on last attempt, return failure

Args:
initial_sql: The initial SQL query that failed
initial_error: The error message from the initial execution failure
execute_sql_func: Function that executes SQL and returns results or raises exception
db_description: Optional database description
question: Optional original question
database_type: Type of database (sqlite, postgresql, mysql, etc.)

Returns:
Dict containing:
- success: Whether healing succeeded
- sql_query: Final SQL query (healed or original)
- query_results: Results from successful execution (if success=True)
- attempts: Number of healing attempts made
- final_error: Final error message (if success=False)
"""
self.messages = []

# Validate SQL syntax for additional error context
validation_result = self.validate_sql_syntax(initial_sql)
additional_context = ""
if validation_result["errors"]:
additional_context += f"\nSyntax errors: {', '.join(validation_result['errors'])}"
if validation_result["warnings"]:
additional_context += f"\nWarnings: {', '.join(validation_result['warnings'])}"
# Enhance error message with validation context
enhanced_error = initial_error + additional_context

# Build initial prompt once before the loop
prompt = self._build_healing_prompt(
failed_sql=initial_sql,
error_message=enhanced_error,
db_description=db_description,
question=question,
database_type=database_type
)
self.messages.append({"role": "user", "content": prompt})

for attempt in range(self.max_healing_attempts):
# Call LLM
response = completion(
model=Config.COMPLETION_MODEL,
messages=self.messages,
temperature=0.1,
max_tokens=2000
)

content = response.choices[0].message.content
self.messages.append({"role": "assistant", "content": content})

# Parse response
result = parse_response(content)
healed_sql = result.get("sql_query", "")

# Execute against database
error = None
try:
query_results = execute_sql_func(healed_sql)
except Exception as e:
error = str(e)

# Check if it worked
if error is None:
# Success!
return {
"success": True,
"sql_query": healed_sql,
"query_results": query_results,
"attempts": attempt + 1,
"final_error": None
}

# Failed - check if last attempt
if attempt >= self.max_healing_attempts - 1:
return {
"success": False,
"sql_query": healed_sql,
"query_results": None,
"attempts": attempt + 1,
"final_error": error
}

# Not last attempt - add feedback and continue
feedback = f"""The healed query failed with error:

```sql
{healed_sql}
```

ERROR:
{error}

Please fix this error."""
self.messages.append({"role": "user", "content": feedback})

# Fallback return
return {
"success": False,
"sql_query": initial_sql,
"query_results": None,
"attempts": self.max_healing_attempts,
"final_error": initial_error
}


def _analyze_error(self, error_message: str, database_type: str) -> str:
"""Analyze error message and provide targeted hints."""

error_lower = error_message.lower()
hints = []

# Common SQLite errors
if database_type == "sqlite":
if "near \"from\"" in error_lower or "syntax error" in error_lower:
hints.append("⚠️ EXTRACT() is NOT supported in SQLite - use strftime() instead!")
hints.append(" Example: strftime('%Y', date_column) for year")

if "no such column" in error_lower:
hints.append("⚠️ Column name doesn't exist - check spelling and case")
hints.append(" SQLite is case-insensitive but the column must exist")

if "no such table" in error_lower:
hints.append("⚠️ Table name doesn't exist - check spelling")

if "ambiguous column" in error_lower:
hints.append("⚠️ Ambiguous column - use table alias: table.column or alias.column")

# PostgreSQL errors
elif database_type == "postgresql":
if "column" in error_lower and "does not exist" in error_lower:
hints.append("⚠️ Column case mismatch - PostgreSQL is case-sensitive")
hints.append(' Use double quotes for mixed-case: "ColumnName"')

if "relation" in error_lower and "does not exist" in error_lower:
hints.append("⚠️ Table doesn't exist or case mismatch")

# Generic hints if no specific patterns matched
if not hints:
hints.append("⚠️ Check syntax compatibility with " + database_type.upper())
hints.append("⚠️ Verify column and table names exist")

return "\n".join(hints)
Loading