diff --git a/api/routers/conversation_contexts.py b/api/routers/conversation_contexts.py index c585189..0ed4f9a 100644 --- a/api/routers/conversation_contexts.py +++ b/api/routers/conversation_contexts.py @@ -76,8 +76,18 @@ def list_conversation_contexts( status_code=status.HTTP_200_OK, ) 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"), - 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( default=10, ge=1, @@ -96,20 +106,33 @@ def recall_context( """ Retrieve relevant contexts formatted for Claude prompt injection. - This endpoint returns a token-efficient markdown string ready for - injection into Claude's prompt. It's the main context recall API. + This endpoint returns contexts matching the search criteria with + properly formatted JSON response containing the contexts array. Query Parameters: + - search_term: Full-text search across title and summary (uses FULLTEXT index) - project_id: Filter contexts by project - tags: Filter contexts by tags (any match) - limit: Maximum number of contexts to retrieve - 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: - formatted_context = conversation_context_service.get_recall_context( + contexts, total = conversation_context_service.get_recall_context( db=db, + search_term=search_term, project_id=project_id, tags=tags, limit=limit, @@ -117,11 +140,13 @@ def recall_context( ) return { - "context": formatted_context, + "total": total, + "limit": limit, + "search_term": search_term, "project_id": str(project_id) if project_id else None, "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: