neuralleap commited on
Commit
b47e099
·
verified ·
1 Parent(s): dcbde8d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +369 -107
app.py CHANGED
@@ -1,107 +1,369 @@
1
- ---
2
- title: GPT-Style Chat Assistant
3
- emoji: 🤖
4
- colorFrom: blue
5
- colorTo: green
6
- sdk: streamlit
7
- sdk_version: 1.24.0
8
- app_file: app.py
9
- pinned: false
10
- ---
11
-
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
13
-
14
- # GPT-Style Chat Assistant
15
-
16
- A sophisticated Streamlit app that mimics the ChatGPT interface, using OpenAI's GPT models for advanced reasoning capabilities.
17
-
18
- ![Screenshot](https://i.ibb.co/bJfyXXn/gpt-style-assistant.png)
19
-
20
- ## Features
21
-
22
- - **ChatGPT-like interface** with conversation sidebar
23
- - **Multiple conversation management**:
24
- - Create new chats
25
- - Switch between conversations
26
- - Delete old conversations
27
- - **Model selection** - choose between different OpenAI models:
28
- - GPT-3.5-Turbo (default)
29
- - GPT-4
30
- - GPT-3.5-Turbo-16k (for longer conversations)
31
- - GPT-4-Turbo
32
- - **Conversation history** preserved between sessions
33
- - **Demo mode** that works without an API key
34
- - **Responsive design** that adapts to different screen sizes
35
-
36
- ## Setup
37
-
38
- 1. Clone this repository
39
- 2. Install the required packages:
40
- ```
41
- pip install -r requirements.txt
42
- ```
43
- 3. (Required for full functionality) Get an OpenAI API key:
44
- - Create an account at [OpenAI](https://platform.openai.com/signup)
45
- - Go to [API Keys](https://platform.openai.com/account/api-keys)
46
- - Create a new API key
47
- - Add it to a `.env` file in the root directory:
48
- ```
49
- OPENAI_API_KEY=your_api_key_here
50
- ```
51
-
52
- ## Running Locally
53
-
54
- Run the app with the following command:
55
-
56
- ```
57
- streamlit run app.py
58
- ```
59
-
60
- If you're having trouble with the `streamlit` command not being found, you can use the full path to the executable:
61
- ```
62
- python -m streamlit run app.py
63
- ```
64
-
65
- Or use the provided runner script:
66
- ```
67
- python run_app.py
68
- ```
69
-
70
- This will start the app and open it in your default browser.
71
-
72
- ## Demo Mode
73
-
74
- The app includes a demo mode that works without an API key. In this mode:
75
- - The assistant will provide pre-defined responses to common questions
76
- - You'll see a text field in the sidebar where you can optionally enter an OpenAI API key
77
- - For a full chatbot experience with reasoning capabilities, it's recommended to add your OpenAI API key
78
-
79
- ## Deploying to Hugging Face Spaces
80
-
81
- 1. Create a new Space on Hugging Face Spaces (https://huggingface.co/spaces)
82
- 2. Choose Streamlit as the SDK
83
- 3. Link your GitHub repository or upload the files directly
84
- 4. Add your OpenAI API key as a secret:
85
- - Go to Settings > Repository Secrets
86
- - Add a secret named `OPENAI_API_KEY` with your key
87
-
88
- The app will automatically deploy and be available at your Hugging Face Spaces URL.
89
-
90
- ## About the Model
91
-
92
- This app lets you select from multiple OpenAI models:
93
-
94
- - **GPT-3.5-Turbo**: Fast and cost-effective, suitable for most tasks
95
- - **GPT-4**: More capable but slower and more expensive
96
- - **GPT-3.5-Turbo-16k**: Supports longer conversations with extended context window
97
- - **GPT-4-Turbo**: OpenAI's most advanced model with significantly larger context window
98
-
99
- You can select your preferred model in the sidebar. Note that model availability depends on your OpenAI API key permissions and subscription tier.
100
-
101
- ## Customization
102
-
103
- You can easily customize the app by:
104
- - Changing the model parameters in the `AVAILABLE_MODELS` dictionary
105
- - Adjusting the styling in the CSS section
106
- - Adding more features to the sidebar
107
- - Creating additional response templates for the demo mode
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import requests
3
+ from dotenv import load_dotenv
4
+ import os
5
+ import sys
6
+ import time
7
+ import random
8
+ import uuid
9
+ from datetime import datetime
10
+ import openai
11
+
12
+ # Load environment variables
13
+ load_dotenv()
14
+
15
+ # Set page config with a wider layout
16
+ st.set_page_config(
17
+ page_title="GPT-Style Chat Assistant",
18
+ page_icon="🤖",
19
+ layout="wide"
20
+ )
21
+
22
+ # Add custom CSS for better styling
23
+ st.markdown("""
24
+ <style>
25
+ .main-content {
26
+ max-width: 800px;
27
+ margin: 0 auto;
28
+ padding: 1rem;
29
+ }
30
+ .chat-message {
31
+ padding: 1.5rem;
32
+ border-radius: 0.5rem;
33
+ margin-bottom: 1rem;
34
+ display: flex;
35
+ flex-direction: column;
36
+ }
37
+ .user-message {
38
+ background-color: #f0f2f6;
39
+ }
40
+ .assistant-message {
41
+ background-color: #e6f3f7;
42
+ }
43
+ .chat-input {
44
+ position: fixed;
45
+ bottom: 0;
46
+ width: 100%;
47
+ padding: 1rem;
48
+ background-color: white;
49
+ }
50
+ .sidebar-conv {
51
+ padding: 0.5rem 1rem;
52
+ border-radius: 0.5rem;
53
+ margin-bottom: 0.5rem;
54
+ cursor: pointer;
55
+ transition: background-color 0.3s;
56
+ }
57
+ .sidebar-conv:hover {
58
+ background-color: #f0f2f6;
59
+ }
60
+ .active-conv {
61
+ background-color: #e6f3f7;
62
+ font-weight: bold;
63
+ }
64
+ </style>
65
+ """, unsafe_allow_html=True)
66
+
67
+ # Initialize session state
68
+ if "conversations" not in st.session_state:
69
+ st.session_state.conversations = {}
70
+
71
+ if "current_conversation_id" not in st.session_state:
72
+ new_id = str(uuid.uuid4())
73
+ st.session_state.current_conversation_id = new_id
74
+ st.session_state.conversations[new_id] = {
75
+ "title": f"New chat {datetime.now().strftime('%H:%M')}",
76
+ "messages": []
77
+ }
78
+
79
+ # Initialize selected model
80
+ if "selected_model" not in st.session_state:
81
+ st.session_state.selected_model = "gpt-3.5-turbo"
82
+
83
+ # Get OpenAI API key from environment or let user enter it
84
+ openai_api_key = os.getenv("OPENAI_API_KEY")
85
+
86
+ # Configure OpenAI client
87
+ if openai_api_key:
88
+ client = openai.OpenAI(api_key=openai_api_key)
89
+
90
+ # Available models with descriptions and token limits
91
+ AVAILABLE_MODELS = {
92
+ "gpt-3.5-turbo": {
93
+ "description": "Fast and cost-effective",
94
+ "max_tokens": 4096,
95
+ "output_tokens": 500,
96
+ "temperature": 0.7
97
+ },
98
+ "gpt-4": {
99
+ "description": "More capable but slower",
100
+ "max_tokens": 8192,
101
+ "output_tokens": 800,
102
+ "temperature": 0.7
103
+ },
104
+ "gpt-3.5-turbo-16k": {
105
+ "description": "Longer context window",
106
+ "max_tokens": 16384,
107
+ "output_tokens": 1000,
108
+ "temperature": 0.7
109
+ },
110
+ "gpt-4-turbo": {
111
+ "description": "Most powerful model (if available)",
112
+ "max_tokens": 128000,
113
+ "output_tokens": 1200,
114
+ "temperature": 0.7
115
+ }
116
+ }
117
+
118
+ # Function to call OpenAI API
119
+ def get_ai_response(prompt, history):
120
+ # Use demo mode if no API key is provided
121
+ if not openai_api_key:
122
+ return get_demo_response(prompt)
123
+
124
+ try:
125
+ # Format messages for OpenAI
126
+ messages = []
127
+
128
+ # Add system message
129
+ messages.append({
130
+ "role": "system",
131
+ "content": "You are a helpful assistant that provides clear, concise, and accurate information."
132
+ })
133
+
134
+ # Add conversation history
135
+ for msg in history:
136
+ messages.append({
137
+ "role": msg["role"],
138
+ "content": msg["content"]
139
+ })
140
+
141
+ # Add the current prompt
142
+ messages.append({
143
+ "role": "user",
144
+ "content": prompt
145
+ })
146
+
147
+ # Get model configuration
148
+ model = st.session_state.selected_model
149
+ model_config = AVAILABLE_MODELS.get(model, AVAILABLE_MODELS["gpt-3.5-turbo"])
150
+ output_tokens = model_config["output_tokens"]
151
+ temperature = model_config["temperature"]
152
+
153
+ # Call OpenAI API
154
+ response = client.chat.completions.create(
155
+ model=model,
156
+ messages=messages,
157
+ temperature=temperature,
158
+ max_tokens=output_tokens,
159
+ stream=False
160
+ )
161
+
162
+ # Extract the response
163
+ return response.choices[0].message.content
164
+
165
+ except Exception as e:
166
+ st.error(f"An error occurred: {str(e)}")
167
+ return "I'm sorry, I encountered an error while processing your request. Please check your OpenAI API key or try again later."
168
+
169
+ # Demo mode responses for when no API key is available
170
+ def get_demo_response(prompt):
171
+ prompt_lower = prompt.lower()
172
+
173
+ # Simple response templates
174
+ greetings = [
175
+ "Hello! How can I assist you today?",
176
+ "Hi there! I'm a demo AI assistant. What can I help you with?",
177
+ "Greetings! I'm running in demo mode. Feel free to ask simple questions."
178
+ ]
179
+
180
+ farewells = [
181
+ "Goodbye! Have a great day!",
182
+ "Farewell! Come back soon!",
183
+ "Take care! It was nice chatting with you."
184
+ ]
185
+
186
+ info_responses = [
187
+ "I'm a simple AI assistant running in demo mode. To use the full features, please provide an OpenAI API key.",
188
+ "This is a demo version with limited capabilities. For a better experience, add your OpenAI API key.",
189
+ "I'm just demonstrating basic functionality. Get a free API key from OpenAI to unlock my full potential!"
190
+ ]
191
+
192
+ reasoning_examples = [
193
+ "This is a demonstration of how I would process a reasoning task. In a real scenario with the full model, I would analyze the problem step by step, consider multiple angles, and provide a detailed explanation.",
194
+ "When solving problems, I would typically break them down into smaller parts, examine each component, and build towards a comprehensive solution. This demo just simulates that process.",
195
+ "Reasoning typically involves identifying key facts, applying logical rules, and drawing conclusions based on available information. With a proper API key, I could demonstrate this more effectively."
196
+ ]
197
+
198
+ # Simple pattern matching
199
+ if any(word in prompt_lower for word in ["hello", "hi", "hey", "greetings"]):
200
+ return random.choice(greetings)
201
+ elif any(word in prompt_lower for word in ["bye", "goodbye", "farewell", "see you"]):
202
+ return random.choice(farewells)
203
+ elif any(phrase in prompt_lower for phrase in ["who are you", "what are you", "tell me about yourself", "what can you do"]):
204
+ return random.choice(info_responses)
205
+ elif any(word in prompt_lower for word in ["think", "reason", "analyze", "solve", "explain", "why", "how"]):
206
+ return random.choice(reasoning_examples)
207
+ elif "weather" in prompt_lower:
208
+ return "I'm sorry, I don't have access to real-time weather data in demo mode."
209
+ elif any(word in prompt_lower for word in ["help", "assist", "support"]):
210
+ return "To get better assistance, please add your OpenAI API key. You can get one for free at https://platform.openai.com/account/api-keys."
211
+ else:
212
+ return "I'm running in demo mode with limited responses. For a full conversation experience, please add your OpenAI API key above."
213
+
214
+ # Function to create a new conversation
215
+ def create_new_chat():
216
+ new_id = str(uuid.uuid4())
217
+ st.session_state.current_conversation_id = new_id
218
+ st.session_state.conversations[new_id] = {
219
+ "title": f"New chat {datetime.now().strftime('%H:%M')}",
220
+ "messages": []
221
+ }
222
+
223
+ # Function to update conversation title based on first message
224
+ def update_conversation_title(conv_id, user_message):
225
+ current_title = st.session_state.conversations[conv_id]["title"]
226
+ if current_title.startswith("New chat"):
227
+ # Limit title length to prevent overflow
228
+ new_title = user_message[:30] + "..." if len(user_message) > 30 else user_message
229
+ st.session_state.conversations[conv_id]["title"] = new_title
230
+
231
+ # Function to delete a conversation
232
+ def delete_conversation(conv_id):
233
+ if conv_id in st.session_state.conversations:
234
+ del st.session_state.conversations[conv_id]
235
+ # If we deleted the current conversation, set a new one
236
+ if conv_id == st.session_state.current_conversation_id:
237
+ if st.session_state.conversations:
238
+ st.session_state.current_conversation_id = next(iter(st.session_state.conversations))
239
+ else:
240
+ create_new_chat()
241
+
242
+ # Create a two-column layout
243
+ sidebar, main_content = st.columns([1, 3])
244
+
245
+ # Sidebar (conversation history)
246
+ with sidebar:
247
+ st.sidebar.title("Conversations")
248
+
249
+ # Add a new chat button
250
+ if st.sidebar.button("+ New Chat", use_container_width=True):
251
+ create_new_chat()
252
+ st.rerun()
253
+
254
+ st.sidebar.markdown("---")
255
+
256
+ # API token input in sidebar if not available
257
+ if not openai_api_key:
258
+ st.sidebar.info("⚠️ No OpenAI API key found.", icon="ℹ️")
259
+ entered_token = st.sidebar.text_input("Enter OpenAI API Key", type="password")
260
+ if entered_token:
261
+ openai_api_key = entered_token
262
+ client = openai.OpenAI(api_key=openai_api_key)
263
+
264
+ # Model selection dropdown
265
+ st.sidebar.subheader("Model Selection")
266
+ model_options = list(AVAILABLE_MODELS.keys())
267
+ model_descriptions = [f"{model} - {AVAILABLE_MODELS[model]['description']}" for model in model_options]
268
+ selected_model_index = model_options.index(st.session_state.selected_model) if st.session_state.selected_model in model_options else 0
269
+
270
+ selected_description = st.sidebar.selectbox(
271
+ "Choose a model:",
272
+ model_descriptions,
273
+ index=selected_model_index
274
+ )
275
+
276
+ # Extract model name from description
277
+ selected_model = model_options[model_descriptions.index(selected_description)]
278
+ if selected_model != st.session_state.selected_model:
279
+ st.session_state.selected_model = selected_model
280
+ st.sidebar.info(f"Model set to {selected_model}")
281
+
282
+ # Show model details
283
+ with st.sidebar.expander("Model Details"):
284
+ model_info = AVAILABLE_MODELS[selected_model]
285
+ st.write(f"**Description:** {model_info['description']}")
286
+ st.write(f"**Max tokens:** {model_info['max_tokens']}")
287
+ st.write(f"**Default temperature:** {model_info['temperature']}")
288
+ st.write("""
289
+ **Note:** Some models may not be available with your current API key.
290
+ If you encounter an error, try selecting a different model.
291
+ """)
292
+
293
+ st.sidebar.markdown("---")
294
+
295
+ # Display conversation history
296
+ for conv_id, conv_data in st.session_state.conversations.items():
297
+ col1, col2 = st.sidebar.columns([4, 1])
298
+ is_active = conv_id == st.session_state.current_conversation_id
299
+
300
+ with col1:
301
+ if st.button(
302
+ conv_data["title"],
303
+ key=f"conv_{conv_id}",
304
+ use_container_width=True,
305
+ type="secondary" if is_active else "tertiary"
306
+ ):
307
+ st.session_state.current_conversation_id = conv_id
308
+ st.rerun()
309
+
310
+ with col2:
311
+ if st.button("🗑️", key=f"del_{conv_id}"):
312
+ delete_conversation(conv_id)
313
+ st.rerun()
314
+
315
+ # Main content area
316
+ with main_content:
317
+ st.write("") # Add some space at the top
318
+
319
+ # Get current conversation
320
+ current_id = st.session_state.current_conversation_id
321
+ current_conv = st.session_state.conversations.get(current_id, {"messages": []})
322
+ messages = current_conv["messages"]
323
+
324
+ # Create a container for the chat area (scrollable)
325
+ chat_container = st.container()
326
+
327
+ # Display chat messages
328
+ with chat_container:
329
+ for i, message in enumerate(messages):
330
+ with st.chat_message(message["role"]):
331
+ st.markdown(message["content"])
332
+
333
+ # Chat input at the bottom
334
+ prompt = st.chat_input("What's on your mind?")
335
+
336
+ if prompt:
337
+ # Add user message to the current conversation
338
+ messages.append({"role": "user", "content": prompt})
339
+
340
+ # Update conversation title if this is the first message
341
+ if len(messages) == 1:
342
+ update_conversation_title(current_id, prompt)
343
+
344
+ # Display user message
345
+ with st.chat_message("user"):
346
+ st.markdown(prompt)
347
+
348
+ # Display assistant response with typing animation
349
+ with st.chat_message("assistant"):
350
+ message_placeholder = st.empty()
351
+
352
+ # Get response from AI
353
+ full_response = get_ai_response(prompt, messages[:-1])
354
+
355
+ # Simulate typing
356
+ displayed_response = ""
357
+ for i in range(len(full_response)):
358
+ displayed_response += full_response[i]
359
+ message_placeholder.markdown(displayed_response + "▌")
360
+ time.sleep(0.005) # Slightly faster typing
361
+
362
+ # Display final response
363
+ message_placeholder.markdown(full_response)
364
+
365
+ # Add assistant response to the conversation
366
+ messages.append({"role": "assistant", "content": full_response})
367
+
368
+ # Force a rerun to update the sidebar with the new conversation title
369
+ st.rerun()