🔍 Code Extractor

function api_send_chat_message

Maturity: 56

Flask API endpoint that handles sending a message in a chat session, processes it through a hybrid RAG engine with configurable search and memory settings, and returns an AI-generated response with references.

File:
/tf/active/vicechatdev/vice_ai/complex_app.py
Lines:
1014 - 1203
Complexity:
complex

Purpose

This endpoint serves as the main chat interface for document-based conversations. It authenticates users, validates access to documents, processes user messages with optional context documents and collections, configures the RAG engine with custom parameters (search, memory, extensive search), generates AI responses in HTML format, and stores the conversation history with references. It supports advanced features like ChromaDB collection selection, uploaded document context, instruction templates, and reference filtering.

Source Code

def api_send_chat_message(session_id):
    """Send a message in a chat session"""
    try:
        chat_session = get_chat_session(session_id)
        if not chat_session:
            return jsonify({'error': 'Chat session not found'}), 404
        
        # Verify access to the document
        document = get_document(chat_session.document_id)
        if not document or document.author != get_user_id():
            return jsonify({'error': 'Access denied'}), 403
        
        data = request.get_json()
        message = data.get('message', '').strip()
        context_documents = data.get('context_documents', [])
        config = data.get('config', {})
        
        if not message:
            return jsonify({'error': 'Message cannot be empty'}), 400
        
        # Add user message to session
        chat_session.add_message('user', message)
        chat_session.context_documents = context_documents
        
        # Get section content for context
        section = document.get_section(chat_session.section_id)
        if section and section.content:
            # Convert HTML content back to Markdown for AI context (AI works better with Markdown)
            markdown_content = html_to_markdown(section.content)
            section_context = f"Current section content (in Markdown format):\n\n{markdown_content}\n\n"
        else:
            section_context = ""
        
        # Prepare chat engine
        if not chat_engine:
            return jsonify({'error': 'Chat engine not available'}), 500
        
        # Configure chat engine with enhanced parameters
        if hasattr(chat_engine, 'flow_control'):
            # Log current configuration
            logger.info(f"Chat: Received configuration: {config}")
            
            chat_engine.flow_control.update({
                'enable_search': config.get('enable_search', True),
                'enable_memory': config.get('enable_memory', True),
                'enable_extensive_search': config.get('enable_extensive_search', False),
                'extensive_search_chunks': config.get('extensive_chunks', 200),
                'target_summary_tokens': config.get('target_summary_tokens', config.get('summary_tokens', 5000)),
                'detail_level': config.get('detail_level', 'detailed'),
                'enable_keyword_filtering': config.get('enable_keyword_filtering', False),
                'enable_reference_filtering': config.get('enable_reference_filtering', True),
                'reference_threshold': config.get('reference_threshold', 0.3),
                'manual_keywords': ','.join(config.get('manual_keywords', [])) if isinstance(config.get('manual_keywords', []), list) else config.get('manual_keywords', ''),
                'detailed_instructions': config.get('detailed_instructions', '')
            })
            
            # Log applied configuration
            logger.info(f"Chat: Applied flow_control settings: enable_search={chat_engine.flow_control.get('enable_search')}, enable_memory={chat_engine.flow_control.get('enable_memory')}, enable_extensive_search={chat_engine.flow_control.get('enable_extensive_search')}")
        else:
            logger.warning("Chat: flow_control not available on chat engine")
        
        # Set instruction template if provided
        template = config.get('instruction_template')
        if template and hasattr(chat_engine, 'set_instruction_template'):
            chat_engine.set_instruction_template(template)
        elif template and hasattr(chat_engine, 'current_instruction_template'):
            chat_engine.current_instruction_template = template
        
        # Handle collections - Clear and add selected collections like in standalone app
        collections = config.get('collections', [])
        if hasattr(chat_engine, 'data_handles'):
            try:
                # Clear existing data sources first
                chat_engine.data_handles.clear_data()
                logger.info("Chat: Cleared existing data sources")
                
                # Add selected collections (only if collections are selected)
                if collections:
                    for collection in collections:
                        available_collections = getattr(chat_engine, 'available_collections', [])
                        
                        if collection in available_collections:
                            try:
                                # Configure processing steps based on user settings
                                processing_steps = ["similarity", "extend_query"]
                                
                                # Add collection as chromaDB type which is the correct type for ChromaDB collections
                                chat_engine.data_handles.add_data(
                                    name=f"Internal data store: {collection}",
                                    type="chromaDB",
                                    data=collection,
                                    filters="",
                                    processing_steps=processing_steps,
                                    inclusions=10,
                                    instructions=""
                                )
                                logger.info(f"Chat: ✅ Added ChromaDB collection: {collection} with processing steps: {processing_steps}")
                            except Exception as e:
                                logger.error(f"Chat: ❌ Failed to add collection {collection}: {e}")
                        else:
                            logger.warning(f"Chat: Collection '{collection}' not available")
                    logger.info(f"Chat: Added {len(collections)} collections to data sources")
                else:
                    logger.info("Chat: No collections selected - using all available data")
            except Exception as e:
                logger.error(f"Chat: Error handling collections: {e}")
        
        # Handle context documents if provided
        if context_documents:
            # Process uploaded context documents
            try:
                user_email = get_user_email()
                for doc_id in context_documents:
                    doc_info = get_uploaded_document(user_email, doc_id)
                    if doc_info and hasattr(chat_engine, 'data_handles'):
                        try:
                            # Add as text data source
                            processing_steps = ["similarity"]
                            
                            chat_engine.data_handles.add_data(
                                name=f"Uploaded document: {doc_info['name']}",
                                type="text",
                                data=doc_info['content'],
                                filters="",
                                processing_steps=processing_steps,
                                inclusions=5,  # Fewer inclusions for uploaded docs
                                instructions=f"This content is from the uploaded document '{doc_info['name']}'"
                            )
                            logger.info(f"Chat: ✅ Added uploaded document: {doc_info['name']}")
                        except Exception as e:
                            logger.error(f"Chat: ❌ Failed to add uploaded document {doc_info['name']}: {e}")
                    else:
                        logger.warning(f"Chat: Uploaded document {doc_id} not found or chat engine unavailable")
                logger.info(f"Chat: Processed {len(context_documents)} context documents")
            except Exception as e:
                logger.warning(f"Error processing context documents: {e}")
        
        # Set up context
        full_message = section_context + message
        
        # Generate response
        try:
            raw_response = chat_engine.response_callback(full_message)
            
            # Process response - AI generates Markdown
            if hasattr(raw_response, '_repr_markdown_'):
                markdown_response = raw_response._repr_markdown_()
            elif hasattr(raw_response, 'object'):
                markdown_response = str(raw_response.object)
            else:
                markdown_response = str(raw_response)
            
            # Convert Markdown to HTML for storage and display
            html_response = markdown_to_html(markdown_response)
            
            # Store both formats (HTML for display, Markdown for exports/context)
            response_data = {
                'html': html_response,
                'markdown': markdown_response,
                'format': 'html'  # Default format for display
            }
            
            # For backward compatibility, use HTML as the main response
            response = html_response
            
            # Get references
            available_references = []
            if hasattr(chat_engine, 'get_available_references'):
                try:
                    available_references = chat_engine.get_available_references(response_text=response)
                except Exception as e:
                    logger.warning(f"Could not retrieve references: {e}")
            
            # Add assistant message to session
            chat_session.add_message('assistant', response, available_references)
            
            return jsonify({
                'response': response,
                'references': available_references,
                'chat_session': chat_session.to_dict(),
                'message': 'Response generated successfully'
            })
            
        except Exception as e:
            logger.error(f"Chat response error: {e}")
            return jsonify({'error': f'Failed to generate response: {str(e)}'}), 500
        
    except Exception as e:
        logger.error(f"Send chat message error: {e}")
        return jsonify({'error': 'Failed to send message'}), 500

Parameters

Name Type Default Kind
session_id - - positional_or_keyword

Parameter Details

session_id: String identifier for the chat session. Used to retrieve the existing chat session from storage and verify user access to the associated document. Must correspond to a valid, existing chat session.

Return Value

Returns a Flask JSON response. On success (200): {'response': str (HTML formatted AI response), 'references': list (available references from RAG engine), 'chat_session': dict (updated session data), 'message': str}. On error: {'error': str} with status codes 400 (empty message), 403 (access denied), 404 (session not found), or 500 (server/engine errors).

Dependencies

  • flask
  • hybrid_rag_engine
  • auth.azure_auth
  • document_processor
  • markdown
  • python-docx
  • reportlab
  • werkzeug

Required Imports

from flask import Flask, request, jsonify, session
from hybrid_rag_engine import OneCo_hybrid_RAG
import logging
import json

Conditional/Optional Imports

These imports are only needed under specific conditions:

from auth.azure_auth import AzureSSO

Condition: Required for the @require_auth decorator to validate user authentication

Required (conditional)
from document_processor import DocumentProcessor

Condition: Required for markdown_to_html and html_to_markdown conversion functions

Required (conditional)

Usage Example

# Client-side usage example (JavaScript fetch)
const sessionId = 'abc123-session-id';
const response = await fetch(`/api/chat-sessions/${sessionId}/messages`, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer <token>'
  },
  body: JSON.stringify({
    message: 'Explain the methodology section',
    context_documents: ['doc-id-1', 'doc-id-2'],
    config: {
      enable_search: true,
      enable_memory: true,
      enable_extensive_search: false,
      extensive_chunks: 200,
      target_summary_tokens: 5000,
      detail_level: 'detailed',
      enable_keyword_filtering: false,
      enable_reference_filtering: true,
      reference_threshold: 0.3,
      manual_keywords: ['methodology', 'research'],
      detailed_instructions: 'Focus on quantitative methods',
      instruction_template: 'custom_template_name',
      collections: ['research_papers', 'technical_docs']
    }
  })
});
const data = await response.json();
console.log(data.response); // HTML formatted response
console.log(data.references); // Array of reference objects

Best Practices

  • Always validate that the message is not empty before processing to avoid unnecessary API calls
  • Ensure proper error handling for chat_engine availability before attempting to generate responses
  • Clear existing data sources before adding new collections to prevent context pollution
  • Use the config parameter to fine-tune RAG behavior for different use cases (e.g., disable search for simple queries)
  • Store both HTML and Markdown formats of responses for flexibility in display and export
  • Implement proper logging at key points (configuration, collection addition, errors) for debugging
  • Verify user access to both the chat session and associated document before processing
  • Handle exceptions gracefully at multiple levels (collection addition, document processing, response generation)
  • Use appropriate processing_steps for different data source types (similarity for uploaded docs, similarity+extend_query for collections)
  • Consider the extensive_search option for complex queries requiring deeper context retrieval
  • Set reasonable defaults for config parameters to ensure the endpoint works without explicit configuration
  • Validate that context_documents exist and are accessible before adding them to the RAG engine
  • Use reference_threshold to control the quality of references returned with responses

Similar Components

AI-powered semantic similarity - components with related functionality:

  • function api_send_chat_message_v1 91.6% similar

    Flask API endpoint that handles sending messages in a chat session, processes them through a RAG (Retrieval-Augmented Generation) engine with configurable LLM models, and returns AI-generated responses with references.

    From: /tf/active/vicechatdev/vice_ai/new_app.py
  • function api_chat 86.7% similar

    Flask API endpoint that handles chat requests asynchronously, processing user queries through a RAG (Retrieval-Augmented Generation) engine with support for multiple modes, memory, web search, and custom configurations.

    From: /tf/active/vicechatdev/docchat/app.py
  • function chat 84.9% similar

    Flask route handler that processes chat requests with RAG (Retrieval-Augmented Generation) capabilities, managing conversation sessions, chat history, and document-based question answering.

    From: /tf/active/vicechatdev/docchat/blueprint.py
  • function chat_v1 68.6% similar

    Flask route handler that renders the main chat interface with available collections and instruction templates, requiring authentication.

    From: /tf/active/vicechatdev/vice_ai/app.py
  • function analysis_chat 68.4% similar

    Flask route handler that processes chat messages for data analysis sessions, verifying user authentication and session ownership before delegating to the data analysis service.

    From: /tf/active/vicechatdev/vice_ai/new_app.py
← Back to Browse