Fix recall endpoint: Add search_term, input validation, and proper contexts array return

- Add search_term parameter with regex validation (alphanumeric + punctuation)
- Add tag validation to prevent SQL injection
- Change return format from {context: string} to {total, contexts: array}
- Use ConversationContextResponse schema for proper serialization
- Improves security and provides structured data for clients

Related: Context Recall System fixes (COMPLETE_SYSTEM_SUMMARY.md)
This commit is contained in:
2026-01-18 14:08:15 -07:00
parent 6c316aa701
commit a534a72a0f

View File

@@ -76,8 +76,18 @@ def list_conversation_contexts(
status_code=status.HTTP_200_OK, status_code=status.HTTP_200_OK,
) )
def recall_context( def recall_context(
search_term: Optional[str] = Query(
None,
max_length=200,
pattern=r'^[a-zA-Z0-9\s\-_.,!?()]+$',
description="Full-text search term (alphanumeric, spaces, and basic punctuation only)"
),
project_id: Optional[UUID] = Query(None, description="Filter by project ID"), project_id: Optional[UUID] = Query(None, description="Filter by project ID"),
tags: Optional[List[str]] = Query(None, description="Filter by tags (OR logic)"), tags: Optional[List[str]] = Query(
None,
description="Filter by tags (OR logic)",
max_items=20
),
limit: int = Query( limit: int = Query(
default=10, default=10,
ge=1, ge=1,
@@ -96,20 +106,33 @@ def recall_context(
""" """
Retrieve relevant contexts formatted for Claude prompt injection. Retrieve relevant contexts formatted for Claude prompt injection.
This endpoint returns a token-efficient markdown string ready for This endpoint returns contexts matching the search criteria with
injection into Claude's prompt. It's the main context recall API. properly formatted JSON response containing the contexts array.
Query Parameters: Query Parameters:
- search_term: Full-text search across title and summary (uses FULLTEXT index)
- project_id: Filter contexts by project - project_id: Filter contexts by project
- tags: Filter contexts by tags (any match) - tags: Filter contexts by tags (any match)
- limit: Maximum number of contexts to retrieve - limit: Maximum number of contexts to retrieve
- min_relevance_score: Minimum relevance score threshold - min_relevance_score: Minimum relevance score threshold
Returns a formatted string ready for prompt injection. Returns JSON with contexts array and metadata.
""" """
# Validate tags to prevent SQL injection
if tags:
import re
tag_pattern = re.compile(r'^[a-zA-Z0-9\-_]+$')
for tag in tags:
if not tag_pattern.match(tag):
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"Invalid tag format: '{tag}'. Tags must be alphanumeric with hyphens or underscores only."
)
try: try:
formatted_context = conversation_context_service.get_recall_context( contexts, total = conversation_context_service.get_recall_context(
db=db, db=db,
search_term=search_term,
project_id=project_id, project_id=project_id,
tags=tags, tags=tags,
limit=limit, limit=limit,
@@ -117,11 +140,13 @@ def recall_context(
) )
return { return {
"context": formatted_context, "total": total,
"limit": limit,
"search_term": search_term,
"project_id": str(project_id) if project_id else None, "project_id": str(project_id) if project_id else None,
"tags": tags, "tags": tags,
"limit": limit, "min_relevance_score": min_relevance_score,
"min_relevance_score": min_relevance_score "contexts": [ConversationContextResponse.model_validate(ctx) for ctx in contexts]
} }
except Exception as e: except Exception as e: