File size: 7,138 Bytes
42da68f
 
 
 
 
999d8ef
7ac50fc
42da68f
 
 
5fac443
42da68f
5fac443
42da68f
 
5fac443
42da68f
 
 
 
 
 
 
 
 
 
 
 
5fac443
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42da68f
 
5fac443
42da68f
 
 
 
5fac443
 
 
42da68f
7ac50fc
5fac443
7ac50fc
42da68f
5fac443
 
 
 
42da68f
5fac443
7ac50fc
5fac443
 
7ac50fc
5fac443
 
 
42da68f
5fac443
 
 
 
 
 
42da68f
5fac443
42da68f
 
 
999d8ef
42da68f
 
999d8ef
 
42da68f
 
 
5fac443
42da68f
5fac443
999d8ef
 
 
 
 
5fac443
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
999d8ef
42da68f
5fac443
999d8ef
5fac443
 
999d8ef
 
5fac443
 
 
42da68f
 
 
 
 
5fac443
999d8ef
 
5fac443
42da68f
 
 
5fac443
 
 
 
999d8ef
 
5fac443
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
999d8ef
5fac443
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
import streamlit as st
import os
from groq import Groq
from dotenv import load_dotenv
import logging
import json
import re

# Configure logging
logging.basicConfig(
    filename="app.log",
    level=logging.DEBUG,
    format="%(asctime)s - %(levelname)s - %(message)s",
)

logging.debug("Logging is configured correctly.")

# Load environment variables
load_dotenv()
reflection_cycles = 2

# Define the Groq API key and initialize the client
GROQ_API_KEY = os.environ.get("GROQ_API_KEY")
if not GROQ_API_KEY:
    raise ValueError("API Key is not set. Please check your environment variables or .env file.")

client = Groq(api_key=GROQ_API_KEY)

# Define the ReAct system prompt template
SYSTEM_PROMPT_TEMPLATE = """
You are an advanced AI agent using the ReAct (Reasoning + Action) framework to solve complex tasks. Follow these steps iteratively:
1. Generate a "Thought" based on the current input or observations.
2. Decide on an "Action" (e.g., search, calculation, etc.) to take.
3. Return an "Observation" after the action to guide the next step.
Continue this loop until the task is solved or no further actions are needed. Return the result in this JSON format:

{{
  "thoughts": [
    {{
      "thought": "<Reasoning step>",
      "action": "<Action taken>",
      "observation": "<Result of the action>"
    }}
  ],
  "final_result": "<Final answer or solution>"
}}

Previous Context:
{history_context}

Input:
{user_input}
"""


# Initialize Streamlit app
st.title("ReAct AI Chatbot")

# Initialize session state
if "messages" not in st.session_state:
    st.session_state.messages = []
if "react_history" not in st.session_state:
    st.session_state.react_history = []


def sanitize_json(json_str):
    json_str = re.sub(r"[\x00-\x1F\x7F]", "", json_str)  # Remove control characters
    return json_str

def generate_react_response(user_input, react_history):
    """
    Generate a ReAct-based response for the given input.
    """
    try:
        # Combine history context
        MAX_HISTORY = 5
        history_context = "\n".join(react_history[-MAX_HISTORY:])
        logging.debug(f"History Context: {history_context}")

        # Ensure the user_input is sanitized
        user_input = sanitize_json(user_input)
        logging.debug(f"Sanitized User Input: {user_input}")

        # Format the system prompt
        formatted_prompt = SYSTEM_PROMPT_TEMPLATE.format_map({
            "user_input": user_input,
            "history_context": history_context or "No context available."
        })
        logging.debug(f"Formatted Prompt: {formatted_prompt}")

        # Send the request to the Groq API
        chat_completion = client.chat.completions.create(
            model="llama3-8b-8192",
            messages=[
                {"role": "system", "content": formatted_prompt},
                {"role": "user", "content": user_input},
            ],
            max_tokens=2048,
            temperature=0.7,
            top_p=0.9,
        )

        logging.debug(f"Raw API Response: {chat_completion}")

        # Extract content from the response
        content = chat_completion.choices[0].message.content
        if not content:
            logging.warning("Received empty content in API response.")
            return None

        # Updated regex patterns to capture full content including recipe details
        thought_match = re.search(r'\*\*Thought:?\*\*:?\s*"?(.*?)(?="?\s*\*\*Action|\n\n|$)', content, re.DOTALL | re.IGNORECASE)
        action_match = re.search(r'\*\*Action:?\*\*:?\s*"?(.*?)(?="?\s*\*\*Observation|\n\n|$)', content, re.DOTALL | re.IGNORECASE)
        observation_match = re.search(r'\*\*Observation:?\*\*:?\s*"?(.*?)(?=\n\n\*\*Thought|\Z)', content, re.DOTALL | re.IGNORECASE)

        # Extract and clean the matches
        thought = thought_match.group(1).strip(' "') if thought_match else "No thought provided"
        action = action_match.group(1).strip(' "') if action_match else "No action provided"
        observation = observation_match.group(1).strip(' "') if observation_match else "No observation provided"

        # Check if observation contains a recipe (indicated by "Ingredients:" or "Instructions:")
        if "Ingredients:" in observation or "Instructions:" in observation:
            final_result = observation  # Use the full recipe text as the final result
        else:
            final_result = observation if observation != "No observation provided" else "Ready to provide assistance once preferences are specified."

        parsed_response = {
            "thoughts": [{
                "thought": thought,
                "action": action,
                "observation": observation
            }],
            "final_result": final_result
        }

        logging.debug(f"Parsed Response: {parsed_response}")
        return parsed_response

    except Exception as e:
        logging.error(f"Error generating ReAct response: {e}", exc_info=True)
        return {
            "thoughts": [],
            "final_result": f"An error occurred: {str(e)}",
        }



# Display chat messages from history
for message in st.session_state.messages:
    with st.chat_message(message["role"]):
        st.markdown(message["content"])

# Accept user input
user_input = st.chat_input("Enter your query:")

if user_input:
    # Display user message
    st.chat_message("user").markdown(user_input)
    st.session_state.messages.append({"role": "user", "content": user_input})

    # Generate ReAct-based response
    with st.spinner("Thinking..."):
        response = generate_react_response(user_input, st.session_state.react_history)

        if response:
            try:
                # Process thoughts and actions
                thoughts = response.get("thoughts", [])
                for step in thoughts:
                    thought = step.get("thought", "No thought provided.")
                    action = step.get("action", "No action taken.")
                    observation = step.get("observation", "No observation.")

                    st.chat_message("assistant").markdown(
                        f"**Thought:** {thought}\n\n**Action:** {action}\n\n**Observation:** {observation}"
                    )
                    st.session_state.messages.append(
                        {
                            "role": "assistant",
                            "content": f"**Thought:** {thought}\n\n**Action:** {action}\n\n**Observation:** {observation}",
                        }
                    )

                # Final result
                final_result = response.get("final_result", "No final result.")
                st.chat_message("assistant").markdown(f"**Final Result:** {final_result}")
                st.session_state.messages.append({"role": "assistant", "content": f"**Final Result:** {final_result}"})

                # Update history
                st.session_state.react_history.append(f"User: {user_input}\nAI: {final_result}")

            except Exception as e:
                logging.error(f"Error processing ReAct response: {e}")
                st.error("Failed to process the ReAct response.")