Traceback (most recent call last):
  File "C:\Users\hp\AppData\Local\Programs\Python\Python312\Lib\site-packages\streamlit\runtime\scriptrunner\script_runner.py", line 584, in _run_script
    exec(code, module.__dict__)
  File "C:\Users\hp\Downloads\app_openai.py", line 246, in <module>
    main()
  File "C:\Users\hp\Downloads\app_openai.py", line 239, in main
    display_steps(steps_record)
  File "C:\Users\hp\Downloads\app_openai.py", line 209, in display_steps
    with st.expander(f"{title} {depth_indicator}", expanded=False):
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\hp\AppData\Local\Programs\Python\Python312\Lib\site-packages\streamlit\runtime\metrics_util.py", line 397, in wrapped_func
    result = non_optional_func(*args, **kwargs)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\hp\AppData\Local\Programs\Python\Python312\Lib\site-packages\streamlit\elements\layouts.py", line 476, in expander
    return self.dg._block(block_proto=block_proto)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\hp\AppData\Local\Programs\Python\Python312\Lib\site-packages\streamlit\delta_generator.py", line 630, in _block
    raise StreamlitAPIException(
streamlit.errors.StreamlitAPIException: Expanders may not be nested inside other expanders.

Fix some errors and debug some stuff with json errors and other bugs.

```
import streamlit as st
import g4f
from g4f.client import Client
import os
import json
import time
import logging
from typing import Dict, Any, List, Tuple, Optional

# Configure logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

client = Client()

# Configuration for Depth vs. Breadth Balancing
MAX_DEPTH = 5        # Maximum depth per reasoning path
MAX_BREADTH = 3      # Maximum number of concurrent branches

def make_api_call(messages: List[Dict[str, Any]], max_tokens: int, is_final_answer: bool = False) -> Dict[str, Any]:
    for attempt in range(3):
        try:
            response = client.chat.completions.create(
                model="gpt-4",  # Corrected model name
                messages=messages,
                max_tokens=max_tokens,
                temperature=0.2,
                response_format={"type": "json_object"}
            )
            content = response.choices[0].message.content
            logging.info(f"API Response Content: {content}")
            parsed_content = json.loads(content)
            logging.info(f"Parsed Content: {parsed_content}")
            return parsed_content
        except json.JSONDecodeError as json_err:
            logging.error(f"JSON Decode Error: {json_err}")
            if attempt == 2:
                return {
                    "title": "Error",
                    "content": f"Failed to parse JSON after 3 attempts. Error: {str(json_err)}"
                }
        except Exception as e:
            logging.error(f"API Call Error: {str(e)}")
            if attempt == 2:
                if is_final_answer:
                    return {
                        "title": "Error",
                        "content": f"Failed to generate final answer after 3 attempts. Error: {str(e)}"
                    }
                else:
                    return {
                        "title": "Error",
                        "content": f"Failed to generate step after 3 attempts. Error: {str(e)}",
                        "next_actions": ["final_answer"]
                    }
        time.sleep(1)  # Wait for 1 second before retrying
    # If all attempts fail and not handled above
    return {
        "title": "Unknown Error",
        "content": "An unknown error occurred during the API call.",
        "next_actions": ["final_answer"]
    }

def generate_response(prompt: str):
    messages = [
        {"role": "system", "content": """You are an expert AI assistant with advanced reasoning capabilities. Your task is to provide detailed, step-by-step explanations of your thought process. Implement a non-linear decision tree in your reasoning where appropriate. For each step:
    
    1. Provide a clear, concise title describing the current reasoning phase.
    2. Elaborate on your thought process in the content section.
    3. Decide whether to continue reasoning or provide a final answer.
    4. If branching is needed, provide multiple next_actions leading to different reasoning paths.
    
    Implement a Spiral Cognition System to balance depth and breadth in your reasoning:
    - **Depth Focus:** Continuously return to core principles and deepen understanding.
    - **Breadth Focus:** Explore new areas and domain-specific branches.
    
    Response Format:
    Use JSON with keys: 'title', 'content', 'next_actions' (values: list containing 'continue', 'final_answer', or custom branch identifiers)
    
    Key Instructions:
    - Employ at least 5 distinct reasoning steps.
    - Incorporate non-linear decision-making with branching paths where necessary.
    - Implement Depth vs. Breadth Balancing using the Spiral Cognition System:
        - **Depth:** Ensure revisiting and deepening of core principles.
        - **Breadth:** Explore new domains and perspectives.
    - Acknowledge your limitations as an AI and explicitly state what you can and cannot do.
    - Actively explore and evaluate alternative answers or approaches.
    - Critically assess your own reasoning; identify potential flaws or biases.
    - When re-examining, employ a fundamentally different approach or perspective.
    - Utilize at least 3 diverse methods to derive or verify your answer.
    - Incorporate relevant domain knowledge and best practices in your reasoning.
    - Quantify certainty levels for each step and the final conclusion when applicable.
    - Consider potential edge cases or exceptions to your reasoning.
    - Provide clear justifications for eliminating alternative hypotheses.
    
    Example of a valid JSON response:
    {
        "title": "Initial Problem Analysis",
        "content": "To approach this problem effectively, I'll first break down the given information into key components. This involves identifying...[detailed explanation]... By structuring the problem this way, we can systematically address each aspect.",
        "next_actions": ["continue"]
    }```
    
    Or with branching:
    {
        "title": "Exploring Possible Solutions",
        "content": "There are multiple ways to tackle this issue. One approach is to use method A, which involves...[details]. Another approach is method B, focusing on...[details].",
        "next_actions": ["method_A_path", "method_B_path"]
    }
    """},  # System prompt
        {"role": "user", "content": prompt},
        {"role": "assistant", "content": "Thank you! I will now think step by step following my instructions, initiating multiple reasoning paths where applicable."}
    ]

    reasoning_tree = {}
    total_thinking_time = 0
    step_queue = [("root", messages.copy(), 1)]  # path_id, messages, current_depth
    steps_record = {}  # To store steps per path

    while step_queue:
        path_id, current_messages, current_depth = step_queue.pop(0)
        if path_id not in steps_record:
            steps_record[path_id] = []

        start_time = time.time()
        step_data = make_api_call(current_messages, 300)
        end_time = time.time()
        thinking_time = end_time - start_time
        total_thinking_time += thinking_time

        if not isinstance(step_data, dict):
            logging.error(f"Unexpected step_data type for path {path_id}: {type(step_data)}")
            step_data = {
                "title": "Error",
                "content": "Received invalid data format.",
                "next_actions": ["final_answer"]
            }

        title = step_data.get('title', 'No Title')
        content = step_data.get('content', '')
        next_actions = step_data.get('next_actions', [])

        steps_record[path_id].append((title, content, thinking_time, current_depth))

        # Append the assistant's response to messages
        current_messages.append({"role": "assistant", "content": json.dumps(step_data)})

        # Determine next steps with Depth vs. Breadth balancing
        if next_actions:
            # Depth Control: If current depth is less than MAX_DEPTH, allow continuation
            if current_depth < MAX_DEPTH:
                for action in next_actions:
                    if action == "continue":
                        new_path_id = f"{path_id}_depth_{current_depth + 1}"
                        step_queue.append((new_path_id, current_messages.copy(), current_depth + 1))
                    elif action == "final_answer":
                        # Generate final answer for this path
                        final_messages = current_messages.copy()
                        final_messages.append({"role": "user", "content": "Please provide the final answer based on your reasoning above."})
                        final_data = make_api_call(final_messages, 200, is_final_answer=True)
                        
                        if isinstance(final_data, dict):
                            final_content = final_data.get('content', '')
                        else:
                            logging.error(f"Final answer step_data is not a dict: {type(final_data)}")
                            final_content = "An error occurred while generating the final answer."

                        final_title = "Final Answer" if 'final_answer' not in steps_record[path_id][-1][0].lower() else f"Final Answer - {path_id}"
                        steps_record[path_id].append((final_title, final_content, thinking_time, current_depth))
                    else:
                        # Custom branch identifiers for breadth
                        if len([key for key in steps_record.keys() if key.startswith(path_id)]) < MAX_BREADTH:
                            new_path_id = f"{path_id}_{action}"
                            step_queue.append((new_path_id, current_messages.copy(), current_depth))
            else:
                # Depth limit reached, prioritize returning to fundamentals or ending
                for action in next_actions:
                    if action == "continue":
                        # Return to depth 1 (fundamentals)
                        new_path_id = f"{path_id}_return_depth_1"
                        step_queue.append((new_path_id, current_messages.copy(), 1))
                    elif action == "final_answer":
                        # Generate final answer for this path
                        final_messages = current_messages.copy()
                        final_messages.append({"role": "user", "content": "Please provide the final answer based on your reasoning above."})
                        final_data = make_api_call(final_messages, 200, is_final_answer=True)
                        
                        if isinstance(final_data, dict):
                            final_content = final_data.get('content', '')
                        else:
                            logging.error(f"Final answer step_data is not a dict: {type(final_data)}")
                            final_content = "An error occurred while generating the final answer."

                        final_title = "Final Answer" if 'final_answer' not in steps_record[path_id][-1][0].lower() else f"Final Answer - {path_id}"
                        steps_record[path_id].append((final_title, final_content, thinking_time, current_depth))
                    else:
                        # Limit breadth by ignoring additional branches beyond MAX_BREADTH
                        pass

    yield steps_record, total_thinking_time

def display_steps(steps_record: Dict[str, List[Tuple[str, str, float, int]]]):
    for path_id, steps in steps_record.items():
        with st.expander(f"Path: {path_id}", expanded=False):
            for title, content, thinking_time, depth in steps:
                depth_indicator = f"— Depth Level: {depth}"
                if title.lower().startswith("final answer"):
                    st.markdown(f"### {title} {depth_indicator}")
                    st.markdown(content.replace('\n', '<br>'), unsafe_allow_html=True)
                else:
                    with st.expander(f"{title} {depth_indicator}", expanded=False):
                        st.markdown(content.replace('\n', '<br>'), unsafe_allow_html=True)
                st.markdown(f"*Thinking time for this step: {thinking_time:.2f} seconds*")

def main():
    st.set_page_config(page_title="OpenAI Reasoning Chains with Decision Trees and Spiral Cognition", page_icon="🧠", layout="wide")

    st.title("Enhanced OpenAI Reasoning Chains with Non-Linear Decision Trees and Spiral Cognition System")

    st.markdown("""
    This enhanced prototype utilizes OpenAI's model to create dynamic reasoning chains with non-linear decision trees and implements the Spiral Cognition System for balanced depth and breadth exploration. 
    The reasoning process continuously revisits core principles while exploring new domains, providing a comprehensive and balanced analysis.

    Forked from [bklieger-groq](https://github.com/bklieger-groq)  
    Open source [repository here](https://github.com/win4r/o1)
    """)

    # Text input for user query
    user_query = st.text_input("Enter your query:", placeholder="e.g., How many 'R's are in the word strawberry?")

    if user_query:
        st.write("Generating response...")

        # Create empty elements to hold the generated text and total time
        response_container = st.empty()
        time_container = st.empty()

        # Generate and display the response
        for steps_record, total_thinking_time in generate_response(user_query):
            with response_container.container():
                display_steps(steps_record)

            # Only show total time when it's available at the end
            if total_thinking_time is not None:
                time_container.markdown(f"**Total thinking time: {total_thinking_time:.2f} seconds**")

if __name__ == "__main__":
    main()