derdide / telegram_multi_llm_bot

ChatBot to allow prompting of multiple LLMs in parallel, among other things
MIT License
0 stars 0 forks source link

JSON support #13

Open derdide opened 3 months ago

derdide commented 3 months ago

Following the Structured Outputs feature, the idea would be to use JSON as a way to retrieve the reply from the models. This is not necessary at this stage, but this would certainly be beneficial for #5 - but would likely require #8 to be done first

derdide commented 3 months ago

Some first elements: Using JSON for the model's reply could indeed be beneficial in several ways. Let's explore this idea and how it could potentially help in your bot's context.

Here's how we could modify the process to use JSON responses:

import json

async def gpt_request(prompt, image_content=None, mode=None):
    try:
        # Prepare messages for GPT API request
        messages = [{"role": "user", "content": prompt}]
        if mode and mode in CHAT_MODES:
            messages.insert(0, {"role": "system", "content": CHAT_MODES[mode]})
        if image_content:
            messages = [
                {"role": "user", "content": [
                    {"type": "text", "text": prompt},
                    {"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{image_content}"}}
                ]}
            ]

        # Request JSON response
        messages.append({"role": "system", "content": "Please provide your response in JSON format with keys: 'main_response', 'summary', 'keywords'."})

        response = openai.chat.completions.create(
            model=OPENAI_MODEL,
            messages=messages,
            max_tokens=int(OPENAI_TOKENS),
            response_format={"type": "json_object"}
        )

        # Parse the JSON response
        json_response = json.loads(response.choices[0].message.content)
        return json_response

    except Exception as e:
        logger.error(f"Error in GPT request: {str(e)}")
        return {"error": "Error occurred while processing GPT request."}

async def process_message(update: Update, context: ContextTypes.DEFAULT_TYPE, model_request, model_name, image_content = None):
    # ... (previous code remains the same)

    # Request response from the specified AI model
    logger.info(f"Requesting response from {model_name}")
    json_response = await model_request(user_message, image_content, mode)
    logger.info(f"Received response from {model_name}.")

    if "error" in json_response:
        await context.bot.send_message(chat_id=chat_id, text=f"Error: {json_response['error']}")
        return

    # Prepare the full response with the model name and structured content
    full_response = f"*{model_name} says:*\n\n"
    full_response += f"Main Response:\n{json_response['main_response']}\n\n"
    full_response += f"Summary:\n{json_response['summary']}\n\n"
    full_response += f"Keywords: {', '.join(json_response['keywords'])}"

    # ... (rest of the function remains the same)

Benefits of using JSON responses:

  1. Structured Data: JSON provides a clear structure for the AI's response, making it easier to parse and handle different parts of the response separately.

  2. Easier Splitting: With structured data, you can more intelligently split long messages. For example, you could send the main response, summary, and keywords as separate messages if needed.

  3. Enhanced Features: You can easily add new fields to the JSON response to include additional information, such as confidence scores, sources, or follow-up questions.

  4. Error Handling: JSON responses make it easier to include and detect error states, improving the robustness of your bot.

  5. Consistency: Enforcing a JSON structure ensures that responses from different models (GPT and Claude) have a consistent format, simplifying your processing logic.

  6. Metadata: You can include metadata about the response, such as token usage or processing time, without cluttering the main response text.

Implementation considerations:

  1. Model Support: Ensure that both GPT and Claude models support returning responses in JSON format. You may need to adjust the prompt or use model-specific parameters to achieve this.

  2. Error Handling: Implement robust JSON parsing with try/except blocks to handle cases where the model might fail to produce valid JSON.

  3. User Experience: Consider how to present the structured information to the user in a readable format, especially for users who expect a more conversational interface.

  4. Flexibility: Design your JSON structure to be flexible enough to accommodate different types of responses and potential future extensions.

Here's how you might update the compare_command to leverage this JSON structure:

async def compare_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
    # ... (initial setup code remains the same)

    # Process the message with Claude
    claude_response = await process_message(update, context, claude_request, "Claude", image_content)

    await asyncio.sleep(2)

    # Process the message with GPT
    gpt_response = await process_message(update, context, gpt_request, "GPT", image_content)

    # Compare keywords
    claude_keywords = set(claude_response.get('keywords', []))
    gpt_keywords = set(gpt_response.get('keywords', []))
    common_keywords = claude_keywords.intersection(gpt_keywords)

    # Send comparison summary
    summary = f"Comparison Summary:\n\n"
    summary += f"Common Keywords: {', '.join(common_keywords)}\n\n"
    summary += f"Claude Unique Keywords: {', '.join(claude_keywords - gpt_keywords)}\n\n"
    summary += f"GPT Unique Keywords: {', '.join(gpt_keywords - claude_keywords)}\n\n"
    summary += "Check the individual responses for detailed answers."

    await context.bot.send_message(chat_id=update.effective_chat.id, text=summary)
    logger.info("Comparison command completed")

This implementation allows for more sophisticated comparison and analysis of the responses from different models. You can easily extend this to compare other aspects of the responses as well.

While implementing JSON responses requires some changes to your existing code, it could provide significant benefits in terms of structure, flexibility, and potential for future enhancements.