🔍 Code Extractor

class HybridResponseHandler

Maturity: 51

Orchestrates the complete workflow for generating hybrid PDF documents that combine LLM text responses with dynamically generated graphics (charts, diagrams, illustrations).

File:
/tf/active/vicechatdev/e-ink-llm/hybrid_response_handler.py
Lines:
39 - 202
Complexity:
complex

Purpose

HybridResponseHandler is responsible for processing LLM responses that contain special graphic placeholders, generating the requested graphics using an AI graphics generator, and assembling them into a final PDF document. It parses placeholder syntax, coordinates asynchronous graphic generation, handles fallback to standard text-only PDFs when no graphics are present, and manages the complete lifecycle from raw LLM response to final hybrid PDF output. This class is designed for applications that need to enhance text responses with visual elements optimized for e-ink displays.

Source Code

class HybridResponseHandler:
    """Handles the complete hybrid response generation workflow"""
    
    def __init__(self, api_key: str):
        self.graphics_generator = GraphicsGenerator(api_key)
        self.pdf_generator = HybridPDFGenerator()
        self.placeholder_pattern = re.compile(
            r'\[GRAPHIC:(\w+):([^:]+):([^\]]+)\]',
            re.IGNORECASE
        )
    
    async def process_hybrid_response(self, 
                                    llm_response: str, 
                                    metadata: Dict[str, Any],
                                    output_path: str,
                                    conversation_id: Optional[str] = None,
                                    exchange_number: Optional[int] = None) -> str:
        """
        Process a hybrid LLM response and generate final PDF
        
        Args:
            llm_response: LLM response containing text and graphic placeholders
            metadata: Processing metadata
            output_path: Path for final PDF
            conversation_id: Session conversation ID
            exchange_number: Exchange number in conversation
            
        Returns:
            Path to generated hybrid PDF
        """
        print(f"🎨 Processing hybrid response with graphics...")
        
        # Step 1: Parse response for graphics placeholders
        hybrid_response = self._parse_hybrid_response(llm_response, metadata)
        
        if not hybrid_response.placeholders:
            print(f"   • No graphics detected, using standard text generation")
            # Fall back to standard PDF generation
            from pdf_generator import PDFGenerator
            standard_generator = PDFGenerator()
            return standard_generator.create_response_pdf(
                llm_response, "", metadata, output_path, conversation_id, exchange_number
            )
        
        print(f"   • Found {len(hybrid_response.placeholders)} graphic placeholders")
        
        # Step 2: Generate graphics
        generated_graphics = await self._generate_graphics(hybrid_response.placeholders, metadata)
        
        # Step 3: Assemble final PDF
        final_pdf_path = await self._assemble_hybrid_pdf(
            hybrid_response, generated_graphics, output_path, 
            conversation_id, exchange_number
        )
        
        print(f"✅ Hybrid PDF generated: {Path(final_pdf_path).name}")
        return final_pdf_path
    
    def _parse_hybrid_response(self, llm_response: str, metadata: Dict[str, Any]) -> HybridResponse:
        """
        Parse LLM response to extract text content and graphic placeholders
        
        Expected placeholder format:
        [GRAPHIC:chart:Sales Data Comparison:{"type":"bar","data":[10,20,30],"labels":["A","B","C"]}]
        [GRAPHIC:diagram:Process Flow:{"steps":["Step 1","Step 2","Step 3"],"style":"flowchart"}]
        [GRAPHIC:illustration:Mathematical Concept:{"concept":"derivatives","style":"educational"}]
        """
        placeholders = []
        
        # Find all graphic placeholders
        matches = self.placeholder_pattern.findall(llm_response)
        
        for match in matches:
            graphic_type, description, params_json = match
            
            try:
                parameters = json.loads(params_json)
            except json.JSONDecodeError:
                print(f"⚠️ Warning: Invalid JSON in graphic placeholder: {params_json}")
                parameters = {"description": description}
            
            # Generate unique ID for this graphic
            placeholder_text = f"[GRAPHIC:{graphic_type}:{description}:{params_json}]"
            graphic_id = hashlib.md5(placeholder_text.encode()).hexdigest()[:8]
            
            placeholder = GraphicPlaceholder(
                id=graphic_id,
                graphic_type=graphic_type,
                description=description,
                parameters=parameters,
                position_marker=placeholder_text
            )
            
            placeholders.append(placeholder)
        
        return HybridResponse(
            text_content=llm_response,
            graphics=[],  # Will be populated after generation
            placeholders=placeholders,
            metadata=metadata
        )
    
    async def _generate_graphics(self, 
                               placeholders: List[GraphicPlaceholder], 
                               metadata: Dict[str, Any]) -> List[GraphicSpec]:
        """Generate all graphics from placeholders"""
        generated_graphics = []
        
        for placeholder in placeholders:
            print(f"   🎨 Generating {placeholder.graphic_type}: {placeholder.description}")
            
            try:
                # Convert placeholder to GraphicSpec
                graphic_spec = GraphicSpec(
                    id=placeholder.id,
                    type=GraphicType(placeholder.graphic_type),
                    description=placeholder.description,
                    parameters=placeholder.parameters,
                    style_preferences={
                        "eink_optimized": True,
                        "high_contrast": True,
                        "simple_style": True
                    }
                )
                
                # Generate the graphic
                generated_graphic = await self.graphics_generator.generate_graphic(graphic_spec)
                
                if generated_graphic:
                    generated_graphics.append(generated_graphic)
                    print(f"     ✅ Generated {placeholder.graphic_type}")
                else:
                    print(f"     ❌ Failed to generate {placeholder.graphic_type}")
                    
            except Exception as e:
                print(f"     ❌ Error generating {placeholder.graphic_type}: {e}")
                continue
        
        return generated_graphics
    
    async def _assemble_hybrid_pdf(self, 
                                 hybrid_response: HybridResponse,
                                 generated_graphics: List[GraphicSpec],
                                 output_path: str,
                                 conversation_id: Optional[str] = None,
                                 exchange_number: Optional[int] = None) -> str:
        """Assemble final PDF with text and graphics"""
        print(f"   📄 Assembling hybrid PDF...")
        
        # Create graphics lookup
        graphics_lookup = {graphic.id: graphic for graphic in generated_graphics}
        
        # Generate final PDF
        final_path = await self.pdf_generator.create_hybrid_pdf(
            text_content=hybrid_response.text_content,
            placeholders=hybrid_response.placeholders,
            graphics=graphics_lookup,
            metadata=hybrid_response.metadata,
            output_path=output_path,
            conversation_id=conversation_id,
            exchange_number=exchange_number
        )
        
        return final_path

Parameters

Name Type Default Kind
bases - -

Parameter Details

api_key: API key for the graphics generation service (passed to GraphicsGenerator). Required for authenticating requests to generate visual content like charts, diagrams, and illustrations.

Return Value

The constructor returns a HybridResponseHandler instance. The main method process_hybrid_response returns a string containing the file path to the generated hybrid PDF. If no graphics are detected, it falls back to standard PDF generation and returns that path instead.

Class Interface

Methods

__init__(self, api_key: str)

Purpose: Initialize the handler with required dependencies and compile the regex pattern for placeholder parsing

Parameters:

  • api_key: API key for the graphics generation service

Returns: None (constructor)

async process_hybrid_response(self, llm_response: str, metadata: Dict[str, Any], output_path: str, conversation_id: Optional[str] = None, exchange_number: Optional[int] = None) -> str

Purpose: Main entry point that orchestrates the complete workflow: parse placeholders, generate graphics, and assemble final PDF

Parameters:

  • llm_response: Raw LLM response text containing graphic placeholders in the format [GRAPHIC:type:description:json_params]
  • metadata: Dictionary containing processing metadata like query, timestamp, model used
  • output_path: File system path where the final PDF should be saved
  • conversation_id: Optional unique identifier for the conversation session
  • exchange_number: Optional sequential number of this exchange in the conversation

Returns: String containing the file path to the generated hybrid PDF document

_parse_hybrid_response(self, llm_response: str, metadata: Dict[str, Any]) -> HybridResponse

Purpose: Parse the LLM response to extract text content and identify all graphic placeholders using regex pattern matching

Parameters:

  • llm_response: Raw LLM response text to parse
  • metadata: Processing metadata to include in the HybridResponse object

Returns: HybridResponse object containing the original text, empty graphics list (populated later), list of parsed placeholders, and metadata

async _generate_graphics(self, placeholders: List[GraphicPlaceholder], metadata: Dict[str, Any]) -> List[GraphicSpec]

Purpose: Asynchronously generate all graphics from the parsed placeholders using the GraphicsGenerator

Parameters:

  • placeholders: List of GraphicPlaceholder objects extracted from the LLM response
  • metadata: Processing metadata (currently unused but available for future enhancements)

Returns: List of GraphicSpec objects representing successfully generated graphics (failed generations are skipped)

async _assemble_hybrid_pdf(self, hybrid_response: HybridResponse, generated_graphics: List[GraphicSpec], output_path: str, conversation_id: Optional[str] = None, exchange_number: Optional[int] = None) -> str

Purpose: Assemble the final PDF by combining text content with generated graphics at their placeholder positions

Parameters:

  • hybrid_response: HybridResponse object containing text content and placeholder information
  • generated_graphics: List of successfully generated GraphicSpec objects
  • output_path: File system path for the output PDF
  • conversation_id: Optional conversation identifier
  • exchange_number: Optional exchange number

Returns: String containing the file path to the assembled hybrid PDF

Attributes

Name Type Description Scope
graphics_generator GraphicsGenerator Instance of GraphicsGenerator used to create visual content from specifications instance
pdf_generator HybridPDFGenerator Instance of HybridPDFGenerator used to assemble text and graphics into final PDF instance
placeholder_pattern re.Pattern Compiled regex pattern for matching graphic placeholders in format [GRAPHIC:type:description:json_params], case-insensitive instance

Dependencies

  • json
  • re
  • asyncio
  • base64
  • typing
  • dataclasses
  • pathlib
  • hashlib
  • graphics_generator
  • hybrid_pdf_generator
  • pdf_generator

Required Imports

import json
import re
import asyncio
import base64
from typing import Dict, Any, List, Optional, Tuple, Union
from dataclasses import dataclass
from pathlib import Path
import hashlib
from graphics_generator import GraphicsGenerator, GraphicSpec, GraphicType
from hybrid_pdf_generator import HybridPDFGenerator

Conditional/Optional Imports

These imports are only needed under specific conditions:

from pdf_generator import PDFGenerator

Condition: only when no graphics placeholders are detected in the LLM response (fallback to standard PDF generation)

Required (conditional)

Usage Example

import asyncio
from hybrid_response_handler import HybridResponseHandler

# Initialize handler with API key
handler = HybridResponseHandler(api_key="your-api-key-here")

# LLM response with graphic placeholders
llm_response = '''
Here is the sales analysis:

[GRAPHIC:chart:Sales Data Comparison:{"type":"bar","data":[10,20,30],"labels":["Q1","Q2","Q3"]}]

The data shows steady growth across quarters.
'''

# Metadata for processing
metadata = {
    "query": "Show me sales data",
    "timestamp": "2024-01-15T10:30:00",
    "model": "gpt-4"
}

# Process and generate hybrid PDF
async def main():
    pdf_path = await handler.process_hybrid_response(
        llm_response=llm_response,
        metadata=metadata,
        output_path="./output/response.pdf",
        conversation_id="conv_123",
        exchange_number=1
    )
    print(f"Generated PDF: {pdf_path}")

asyncio.run(main())

Best Practices

  • Always use async/await when calling process_hybrid_response as it performs asynchronous graphics generation
  • Ensure the API key provided has sufficient permissions and quota for graphics generation
  • The placeholder format must be strictly followed: [GRAPHIC:type:description:json_params] where json_params is valid JSON
  • Handle the case where graphics generation may fail - the class will continue processing but skip failed graphics
  • The output_path directory must exist and be writable before calling process_hybrid_response
  • conversation_id and exchange_number are optional but recommended for tracking multi-turn conversations
  • The class automatically falls back to standard PDF generation if no graphics placeholders are found
  • Graphics are optimized for e-ink displays with high contrast and simple styles by default
  • Each graphic placeholder generates a unique ID based on MD5 hash to prevent duplicates
  • Error handling is built-in: failed graphics are logged but don't stop the entire process

Similar Components

AI-powered semantic similarity - components with related functionality:

  • function demo_hybrid_response 83.8% similar

    Demonstrates end-to-end hybrid response processing by converting an LLM response containing text and graphics placeholders into a formatted PDF document.

    From: /tf/active/vicechatdev/e-ink-llm/demo_hybrid_mode.py
  • class HybridResponse 77.1% similar

    A dataclass that encapsulates a complete hybrid response containing both text content and graphical elements with their placeholders and metadata.

    From: /tf/active/vicechatdev/e-ink-llm/hybrid_response_handler.py
  • class HybridPromptEnhancer 71.8% similar

    A utility class that enhances LLM prompts by adding instructions and formatting guidelines to encourage hybrid text+graphics responses with embedded graphic placeholders.

    From: /tf/active/vicechatdev/e-ink-llm/hybrid_response_handler.py
  • class HybridPDFGenerator 70.7% similar

    A class that generates hybrid PDF documents combining formatted text content with embedded graphics, optimized for e-ink displays.

    From: /tf/active/vicechatdev/e-ink-llm/hybrid_pdf_generator.py
  • class HybridSessionDocTemplate 66.8% similar

    A custom ReportLab document template class that extends BaseDocTemplate to create PDF documents with session information footers, specifically designed for hybrid text and graphics responses.

    From: /tf/active/vicechatdev/e-ink-llm/hybrid_pdf_generator.py
← Back to Browse