Santiago Casas commited on
Commit
8343c13
·
1 Parent(s): 52c5971

add project files and clapp.png

Browse files
Files changed (5) hide show
  1. .gitattributes +1 -0
  2. CLAPP.py +1033 -0
  3. README.md +1 -1
  4. images/CLAPP.png +3 -0
  5. requirements.txt +175 -0
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ *.png filter=lfs diff=lfs merge=lfs -text
CLAPP.py ADDED
@@ -0,0 +1,1033 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # This script requires Streamlit and LangChain
2
+ # Install it with: pip install streamlit openai langchain langchain-openai langchain-community
3
+
4
+ import streamlit as st
5
+ import time
6
+ import json
7
+ import os
8
+ import base64
9
+ import getpass
10
+ from cryptography.fernet import Fernet
11
+ from langchain_openai import ChatOpenAI
12
+ from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler
13
+ from langchain_core.messages import HumanMessage, SystemMessage
14
+ from langchain_openai import OpenAIEmbeddings
15
+ from langchain_community.vectorstores import FAISS
16
+ from langchain_community.document_loaders import PyPDFLoader
17
+ from langchain.text_splitter import RecursiveCharacterTextSplitter
18
+ from langchain_community.chat_message_histories import ChatMessageHistory
19
+ from langchain_core.documents import Document
20
+
21
+ from langchain.callbacks.base import BaseCallbackHandler
22
+
23
+ from pydantic import BaseModel, Field
24
+ from typing import Annotated
25
+
26
+
27
+ from autogen import ConversableAgent, LLMConfig, UpdateSystemMessage
28
+ import tempfile
29
+ from autogen.coding import LocalCommandLineCodeExecutor, CodeBlock
30
+ import matplotlib
31
+ matplotlib.use('Agg') # Set the backend to Agg before importing pyplot
32
+ import matplotlib.pyplot as plt
33
+ import io
34
+ from PIL import Image
35
+ import re
36
+ import subprocess
37
+ import sys
38
+ from typing import Tuple
39
+ import contextlib # for contextlib.contextmanager
40
+
41
+ # --- Helper Functions ---
42
+ def save_encrypted_key(encrypted_key, username):
43
+ """Save encrypted key to file with username prefix"""
44
+ try:
45
+ filename = f"{username}_encrypted_api_key" if username else ".encrypted_api_key"
46
+ with open(filename, "w") as f:
47
+ f.write(encrypted_key)
48
+ return True
49
+ except Exception as e:
50
+ return False
51
+
52
+ def load_encrypted_key(username):
53
+ """Load encrypted key from file with username prefix"""
54
+ try:
55
+ filename = f"{username}_encrypted_api_key" if username else ".encrypted_api_key"
56
+ with open(filename, "r") as f:
57
+ return f.read()
58
+ except FileNotFoundError:
59
+ return None
60
+
61
+ def read_keys_from_file(file_path):
62
+ with open(file_path, 'r') as file:
63
+ return json.load(file)
64
+
65
+ def read_prompt_from_file(path):
66
+ with open(path, 'r') as f:
67
+ return f.read()
68
+
69
+ class Response:
70
+ def __init__(self, content):
71
+ self.content = content
72
+
73
+
74
+ class Feedback(BaseModel):
75
+ grade: Annotated[int, Field(description="Score from 1 to 10")]
76
+ improvement_instructions: Annotated[str, Field(description="Advice on how to improve the reply")]
77
+
78
+ class StreamHandler(BaseCallbackHandler):
79
+ def __init__(self, container):
80
+ self.container = container
81
+ self.text = ""
82
+
83
+ def on_llm_new_token(self, token: str, **kwargs):
84
+ self.text += token
85
+ self.container.markdown(self.text + "▌")
86
+
87
+ # --- Streamlit Page Config ---
88
+ st.set_page_config(
89
+ page_title="CLAPP Agent",
90
+ page_icon="🤖",
91
+ layout="wide",
92
+ initial_sidebar_state="auto"
93
+ )
94
+
95
+ st.markdown("# CLAPP: CLASS LLM Agent for Pair Programming")
96
+ col1, col2, col3 = st.columns([1, 2, 1])
97
+ with col2:
98
+ st.image("images/CLAPP.png", width=400)
99
+
100
+
101
+ # New prompts for the swarm
102
+ Initial_Agent_Instructions = read_prompt_from_file("prompts/class_instructions.txt") # Reuse or adapt class_instructions
103
+ Review_Agent_Instructions = read_prompt_from_file("prompts/review_instructions.txt") # Adapt rating_instructions
104
+ #Typo_Agent_Instructions = read_prompt_from_file("prompts/typo_instructions.txt") # New prompt file
105
+ Formatting_Agent_Instructions = read_prompt_from_file("prompts/formatting_instructions.txt") # New prompt file
106
+ Code_Execution_Agent_Instructions = read_prompt_from_file("prompts/codeexecutor_instructions.txt") # New prompt file
107
+
108
+ # --- Initialize Session State ---
109
+ def init_session():
110
+ if "messages" not in st.session_state:
111
+ st.session_state.messages = []
112
+ if "debug" not in st.session_state:
113
+ st.session_state.debug = False
114
+ if "llm" not in st.session_state:
115
+ st.session_state.llm = None
116
+ if "llmBG" not in st.session_state:
117
+ st.session_state.llmBG = None
118
+ if "memory" not in st.session_state:
119
+ st.session_state.memory = ChatMessageHistory()
120
+ if "vector_store" not in st.session_state:
121
+ st.session_state.vector_store = None
122
+ if "last_token_count" not in st.session_state:
123
+ st.session_state.last_token_count = 0
124
+ if "selected_model" not in st.session_state:
125
+ st.session_state.selected_model = "gpt-4o-mini"
126
+ if "greeted" not in st.session_state:
127
+ st.session_state.greeted = False
128
+ if "debug_messages" not in st.session_state:
129
+ st.session_state.debug_messages = []
130
+
131
+
132
+ init_session()
133
+
134
+
135
+
136
+ # --- Sidebar Configuration ---
137
+ with st.sidebar:
138
+ st.header("🔐 API & Assistants")
139
+ api_key = st.text_input("1. OpenAI API Key", type="password")
140
+ username = st.text_input("2. Username (for saving your API key)", placeholder="Enter your username")
141
+ user_password = st.text_input("3. Password to encrypt/decrypt API key", type="password")
142
+
143
+ # When both API key and password are provided
144
+ if api_key and user_password:
145
+ # Create encryption key from password
146
+ key = base64.urlsafe_b64encode(user_password.ljust(32)[:32].encode())
147
+ fernet = Fernet(key)
148
+
149
+ # If this is a new API key, encrypt and save it
150
+ if "saved_api_key" not in st.session_state or api_key != st.session_state.saved_api_key:
151
+ try:
152
+ # Encrypt the API key
153
+ encrypted_key = fernet.encrypt(api_key.encode())
154
+
155
+ # Save to session state and file
156
+ st.session_state.saved_api_key = api_key
157
+ st.session_state.encrypted_key = encrypted_key.decode()
158
+
159
+ # Save to file
160
+ if save_encrypted_key(encrypted_key.decode(), username):
161
+ st.success("API key encrypted and saved! ✅")
162
+ else:
163
+ st.warning("API key encrypted but couldn't save to file! ⚠️")
164
+ except Exception as e:
165
+ st.error(f"Error saving API key: {str(e)}")
166
+
167
+ # Try to load saved API key if password is provided
168
+ elif user_password and not api_key:
169
+ # Try to load from file first
170
+ encrypted_key = load_encrypted_key(username)
171
+ if encrypted_key:
172
+ try:
173
+ # Recreate encryption key
174
+ key = base64.urlsafe_b64encode(user_password.ljust(32)[:32].encode())
175
+ fernet = Fernet(key)
176
+
177
+ # Decrypt the saved key
178
+ decrypted_key = fernet.decrypt(encrypted_key.encode()).decode()
179
+
180
+ # Set the API key
181
+ api_key = decrypted_key
182
+ st.session_state.saved_api_key = api_key
183
+ st.success("API key loaded successfully! 🔑")
184
+ except Exception as e:
185
+ st.error("Failed to decrypt API key. Wrong password? 🔒")
186
+ else:
187
+ st.warning("No saved API key found. Please enter your API key first. 🔑")
188
+
189
+ # Add clear saved key button
190
+ if st.button("🗑️ Clear Saved API Key"):
191
+ deleted_files = False
192
+ error_message = ""
193
+
194
+ # Try to delete username-specific file if it exists
195
+ if username:
196
+ filename = f"{username}_encrypted_api_key"
197
+ if os.path.exists(filename):
198
+ try:
199
+ os.remove(filename)
200
+ deleted_files = True
201
+ st.success(f"Deleted key file for user: {username}")
202
+ except Exception as e:
203
+ error_message += f"Error clearing {filename}: {str(e)}\n"
204
+
205
+ # Also try to delete the default file if it exists
206
+ if os.path.exists(".encrypted_api_key"):
207
+ try:
208
+ os.remove(".encrypted_api_key")
209
+ deleted_files = True
210
+ st.success("Deleted default key file")
211
+ except Exception as e:
212
+ error_message += f"Error clearing default key file: {str(e)}\n"
213
+
214
+ # Clean up session state
215
+ if "saved_api_key" in st.session_state:
216
+ del st.session_state.saved_api_key
217
+ if "encrypted_key" in st.session_state:
218
+ del st.session_state.encrypted_key
219
+
220
+ # Show appropriate message
221
+ if deleted_files:
222
+ st.info("Session cleared. Reloading page...")
223
+ time.sleep(1) # Brief pause so user can see the message
224
+ st.rerun()
225
+ elif error_message:
226
+ st.error(error_message)
227
+ else:
228
+ st.warning("No saved API keys found to delete.")
229
+
230
+ st.session_state.selected_model = st.selectbox(
231
+ "4. Choose LLM model 🧠",
232
+ options=["gpt-4o-mini", "gpt-4o"],
233
+ index=["gpt-4o-mini", "gpt-4o"].index(st.session_state.selected_model)
234
+ )
235
+
236
+
237
+ # Check if model has changed
238
+ if "previous_model" not in st.session_state:
239
+ st.session_state.previous_model = st.session_state.selected_model
240
+ elif st.session_state.previous_model != st.session_state.selected_model:
241
+ # Reset relevant state variables when model changes
242
+ st.session_state.vector_store = None
243
+ st.session_state.greeted = False
244
+ st.session_state.messages = []
245
+ st.session_state.memory = ChatMessageHistory()
246
+ st.session_state.previous_model = st.session_state.selected_model
247
+ st.info("Model changed! Please initialize again with the new model.")
248
+
249
+ st.write("### Response Mode")
250
+ col1, col2 = st.columns([1, 2])
251
+ with col1:
252
+ mode_is_fast = st.toggle("Fast Mode", value=True)
253
+ with col2:
254
+ if mode_is_fast:
255
+ st.caption("✨ Quick responses with good quality (recommended for most uses)")
256
+ else:
257
+ st.caption("🎯 Swarm mode, more refined responses (may take longer)")
258
+
259
+
260
+ if api_key:
261
+ os.environ["OPENAI_API_KEY"] = api_key
262
+
263
+ # Initialize only after model is selected
264
+ if st.button("🚀 Initialize with Selected Model"):
265
+ # First initialization without streaming
266
+ st.session_state.llm = ChatOpenAI(
267
+ model_name=st.session_state.selected_model,
268
+ openai_api_key=api_key,
269
+ temperature=1.0
270
+ )
271
+
272
+ if st.session_state.vector_store is None:
273
+ embedding_status = st.empty()
274
+ embedding_status.info("🔄 Processing and embedding your RAG data... This might take a moment! ⏳")
275
+ embeddings = OpenAIEmbeddings(model="text-embedding-3-large")
276
+
277
+ # Get all files from class-data directory
278
+ all_docs = []
279
+ for filename in os.listdir("./class-data"):
280
+ file_path = os.path.join("./class-data", filename)
281
+
282
+ if filename.endswith('.pdf'):
283
+ # Handle PDF files
284
+ loader = PyPDFLoader(file_path)
285
+ docs = loader.load()
286
+ all_docs.extend(docs)
287
+ elif filename.endswith(('.txt', '.py', '.ini')): # Added .py extension
288
+ # Handle text and Python files
289
+ with open(file_path, 'r', encoding='utf-8') as f:
290
+ text = f.read()
291
+ # Create a document with metadata
292
+ all_docs.append(Document(
293
+ page_content=text,
294
+ metadata={"source": filename, "type": "code" if filename.endswith('.py') else "text"}
295
+ ))
296
+
297
+ # Split and process all documents
298
+ text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
299
+ def sanitize(documents):
300
+ for doc in documents:
301
+ doc.page_content = doc.page_content.encode("utf-8", "ignore").decode("utf-8")
302
+ return documents
303
+
304
+ splits = text_splitter.split_documents(all_docs)
305
+ splits = sanitize(splits)
306
+
307
+ # Create vector store from all documents
308
+ st.session_state.vector_store = FAISS.from_documents(splits, embedding=embeddings)
309
+ embedding_status.empty() # Clear the loading message
310
+
311
+ # Initialize but don't generate welcome message yet
312
+ if not st.session_state.greeted:
313
+ # Just set the initialized flag, we'll generate the welcome message later
314
+ st.session_state.llm_initialized = True
315
+ st.rerun() # Refresh the page to show the initialized state
316
+
317
+ st.markdown("---") # Add a separator for better visual organization
318
+
319
+ # Check if CLASS is already installed
320
+ st.markdown("### 🔧 CLASS Setup")
321
+ if st.checkbox("Check CLASS installation status"):
322
+ try:
323
+ # Use sys.executable to run a simple test to see if classy can be imported
324
+ result = subprocess.run(
325
+ [sys.executable, "-c", "from classy import Class; print('CLASS successfully imported!')"],
326
+ capture_output=True,
327
+ text=True
328
+ )
329
+
330
+ if result.returncode == 0:
331
+ st.success("✅ CLASS is already installed and ready to use!")
332
+ else:
333
+ st.error("❌ The 'classy' module is not installed. Please install CLASS using the button below.")
334
+ if result.stderr:
335
+ st.code(result.stderr, language="bash")
336
+ except Exception as e:
337
+ st.error(f"❌ Error checking CLASS installation: {str(e)}")
338
+
339
+ # Add CLASS installation and testing buttons
340
+ st.text("If not installed, install CLASS to enable code execution and plotting")
341
+ if st.button("🔄 Install CLASS"):
342
+ # Show simple initial message
343
+ status_placeholder = st.empty()
344
+ status_placeholder.info("Installing CLASS... This could take a few minutes.")
345
+
346
+ try:
347
+ # Get the path to install_classy.sh
348
+ install_script_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'install_classy.sh')
349
+
350
+ # Make the script executable
351
+ os.chmod(install_script_path, 0o755)
352
+
353
+ # Run the installation script with shell=True to ensure proper execution
354
+ process = subprocess.Popen(
355
+ [install_script_path],
356
+ stdout=subprocess.PIPE,
357
+ stderr=subprocess.STDOUT,
358
+ text=True,
359
+ bufsize=1,
360
+ shell=True,
361
+ cwd=os.path.dirname(os.path.abspath(__file__))
362
+ )
363
+
364
+ # Create a placeholder for the current line
365
+ current_line_placeholder = st.empty()
366
+
367
+ # Collect output in the background while showing just the last line
368
+ output_text = ""
369
+ for line in iter(process.stdout.readline, ''):
370
+ output_text += line
371
+ # Update the placeholder with just the current line (real-time feedback)
372
+ if line.strip(): # Only update for non-empty lines
373
+ current_line_placeholder.info(f"Current: {line.strip()}")
374
+
375
+ # Get the final return code
376
+ return_code = process.wait()
377
+
378
+ # Clear the current line placeholder when done
379
+ current_line_placeholder.empty()
380
+
381
+ # Update status based on result
382
+ if return_code == 0:
383
+ status_placeholder.success("✅ CLASS installed successfully!")
384
+ else:
385
+ status_placeholder.error(f"❌ CLASS installation failed with return code: {return_code}")
386
+
387
+ # Display the full output in an expander (not expanded by default)
388
+ with st.expander("View Full Installation Log", expanded=False):
389
+ st.code(output_text)
390
+
391
+ except Exception as e:
392
+ status_placeholder.error(f"Installation failed with exception: {str(e)}")
393
+ st.exception(e) # Show the full exception for debugging
394
+
395
+ # Add test environment button
396
+ st.text("If CLASS is installed, test the environment")
397
+ if st.button("🧪 Test CLASS"):
398
+ # Show simple initial message
399
+ status_placeholder = st.empty()
400
+ status_placeholder.info("Testing CLASS environment... This could take a moment.")
401
+
402
+ try:
403
+ # Get the path to test_classy.py
404
+ test_script_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'test_classy.py')
405
+
406
+ # Create a temporary directory for the test
407
+ with tempfile.TemporaryDirectory() as temp_dir:
408
+ # Run the test script with streaming output
409
+ process = subprocess.Popen(
410
+ [sys.executable, test_script_path],
411
+ stdout=subprocess.PIPE,
412
+ stderr=subprocess.STDOUT,
413
+ text=True,
414
+ bufsize=1,
415
+ cwd=temp_dir
416
+ )
417
+
418
+ # Create a placeholder for the current line
419
+ current_line_placeholder = st.empty()
420
+
421
+ # Collect output in the background while showing just the last line
422
+ output_text = ""
423
+ for line in iter(process.stdout.readline, ''):
424
+ output_text += line
425
+ # Update the placeholder with just the current line (real-time feedback)
426
+ if line.strip(): # Only update for non-empty lines
427
+ current_line_placeholder.info(f"Current: {line.strip()}")
428
+
429
+ # Get the final return code
430
+ return_code = process.wait()
431
+
432
+ # Clear the current line placeholder when done
433
+ current_line_placeholder.empty()
434
+
435
+ # Update status based on result
436
+ if return_code == 0:
437
+ status_placeholder.success("✅ CLASS test completed successfully!")
438
+ else:
439
+ status_placeholder.error(f"❌ CLASS test failed with return code: {return_code}")
440
+
441
+
442
+ # Check for common errors
443
+ if "ModuleNotFoundError" in output_text or "ImportError" in output_text:
444
+ st.error("❌ Python module import error detected. Make sure CLASS is properly installed.")
445
+
446
+ if "CosmoSevereError" in output_text or "CosmoComputationError" in output_text:
447
+ st.error("❌ CLASS computation error detected.")
448
+
449
+ # Display the full output in an expander (not expanded by default)
450
+ with st.expander("View Full Test Log", expanded=False):
451
+ st.code(output_text)
452
+ # Check if the plot was generated
453
+ plot_path = os.path.join(temp_dir, 'cmb_temperature_spectrum.png')
454
+ if os.path.exists(plot_path):
455
+ # Show the plot if it was generated
456
+ st.subheader("Generated CMB Power Spectrum")
457
+ st.image(plot_path, use_container_width=True)
458
+ else:
459
+ st.warning("⚠️ No plot was generated")
460
+
461
+ except Exception as e:
462
+ status_placeholder.error(f"Test failed with exception: {str(e)}")
463
+ st.exception(e) # Show the full exception for debugging
464
+
465
+ st.markdown("---") # Add a separator for better visual organization
466
+ st.session_state.debug = st.checkbox("🔍 Show Debug Info")
467
+ if st.button("🗑️ Reset Chat"):
468
+ st.session_state.clear()
469
+ st.rerun()
470
+
471
+ if st.session_state.last_token_count > 0:
472
+ st.markdown(f"🧮 **Last response token usage:** `{st.session_state.last_token_count}` tokens")
473
+
474
+ # --- Display all saved plots in sidebar ---
475
+ if "generated_plots" in st.session_state and st.session_state.generated_plots:
476
+ with st.expander("📊 Plot Gallery", expanded=False):
477
+ st.write("All plots generated during this session:")
478
+ # Use a single column layout for the sidebar
479
+ for i, plot_path in enumerate(st.session_state.generated_plots):
480
+ if os.path.exists(plot_path):
481
+ st.image(plot_path, width=250, caption=os.path.basename(plot_path))
482
+ st.markdown("---") # Add separator between plots
483
+
484
+ # --- Retrieval + Prompt Construction ---
485
+ def build_messages(context, question, system):
486
+ system_msg = SystemMessage(content=system)
487
+ human_msg = HumanMessage(content=f"Context:\n{context}\n\nQuestion:\n{question}")
488
+ return [system_msg] + st.session_state.memory.messages + [human_msg]
489
+
490
+ def build_messages_rating(context, question, answer, system):
491
+ system_msg = SystemMessage(content=system)
492
+ human_msg = HumanMessage(content=f"Context:\n{context}\n\nQuestion:\n{question}\n\nAI Answer:\n{answer}")
493
+ return [system_msg] + st.session_state.memory.messages + [human_msg]
494
+
495
+ def build_messages_refinement(context, question, answer, feedback, system):
496
+ system_msg = SystemMessage(content=system)
497
+ human_msg = HumanMessage(content=f"Context:\n{context}\n\nQuestion:\n{question}\n\nAI Answer:\n{answer}\n\nReviewer Feedback:\n{feedback}")
498
+ return [system_msg] + st.session_state.memory.messages + [human_msg]
499
+
500
+ def format_memory_messages(memory_messages):
501
+ formatted = ""
502
+ for msg in memory_messages:
503
+ role = msg.type.capitalize() # 'human' -> 'Human'
504
+ content = msg.content
505
+ formatted += f"{role}: {content}\n\n"
506
+ return formatted.strip()
507
+
508
+
509
+ def retrieve_context(question):
510
+ docs = st.session_state.vector_store.similarity_search(question, k=4)
511
+ return "\n\n".join([doc.page_content for doc in docs])
512
+
513
+
514
+ # Set up code execution environment
515
+ #temp_dir = tempfile.TemporaryDirectory()
516
+
517
+ class PlotAwareExecutor(LocalCommandLineCodeExecutor):
518
+ def __init__(self, **kwargs):
519
+ import tempfile
520
+ # Create a persistent plots directory if it doesn't exist
521
+ plots_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'plots')
522
+ os.makedirs(plots_dir, exist_ok=True)
523
+
524
+ # Still use a temp dir for code execution
525
+ temp_dir = tempfile.TemporaryDirectory()
526
+ kwargs['work_dir'] = temp_dir.name
527
+ super().__init__(**kwargs)
528
+ self._temp_dir = temp_dir
529
+ self._plots_dir = plots_dir
530
+
531
+ @contextlib.contextmanager
532
+ def _capture_output(self):
533
+ old_out, old_err = sys.stdout, sys.stderr
534
+ buf_out, buf_err = io.StringIO(), io.StringIO()
535
+ sys.stdout, sys.stderr = buf_out, buf_err
536
+ try:
537
+ yield buf_out, buf_err
538
+ finally:
539
+ sys.stdout, sys.stderr = old_out, old_err
540
+
541
+ def execute_code(self, code: str):
542
+ # 1) Extract code from markdown
543
+ match = re.search(r"```(?:python)?\n(.*?)```", code, re.DOTALL)
544
+ cleaned = match.group(1) if match else code
545
+ cleaned = cleaned.replace("plt.show()", "")
546
+
547
+ # Add timestamp for saving figures only if there's plt usage in the code
548
+ timestamp = time.strftime("%Y-%m-%d-%H-%M-%S")
549
+ plot_filename = f'plot_{timestamp}.png'
550
+ plot_path = os.path.join(self._plots_dir, plot_filename)
551
+ temp_plot_path = None
552
+
553
+ for line in cleaned.split("\n"):
554
+ if "plt.savefig" in line:
555
+ temp_plot_path = os.path.join(self._temp_dir.name, f'temporary_{timestamp}.png')
556
+ cleaned = cleaned.replace(line, f"plt.savefig('{temp_plot_path}', dpi=300)")
557
+ break
558
+ else:
559
+ # If there's a plot but no save, auto-insert save
560
+ if "plt." in cleaned:
561
+ temp_plot_path = os.path.join(self._temp_dir.name, f'temporary_{timestamp}.png')
562
+ cleaned += f"\nplt.savefig('{temp_plot_path}')"
563
+
564
+ # Create a temporary Python file to execute
565
+ temp_script_path = os.path.join(self._temp_dir.name, f'temp_script_{timestamp}.py')
566
+ with open(temp_script_path, 'w') as f:
567
+ f.write(cleaned)
568
+
569
+ full_output = ""
570
+ try:
571
+ # 2) Capture stdout using subprocess
572
+ process = subprocess.Popen(
573
+ [sys.executable, temp_script_path],
574
+ stdout=subprocess.PIPE,
575
+ stderr=subprocess.STDOUT,
576
+ text=True,
577
+ bufsize=1,
578
+ cwd=self._temp_dir.name
579
+ )
580
+ stdout, _ = process.communicate()
581
+
582
+ # 3) Format the output
583
+ with self._capture_output() as (out_buf, err_buf):
584
+ if stdout:
585
+ out_buf.write(stdout)
586
+ stdout_text = out_buf.getvalue()
587
+ stderr_text = err_buf.getvalue()
588
+
589
+ if stdout_text:
590
+ full_output += f"STDOUT:\n{stdout_text}\n"
591
+ if stderr_text:
592
+ full_output += f"STDERR:\n{stderr_text}\n"
593
+
594
+ # Copy plot from temp to persistent location if it exists
595
+ if temp_plot_path and os.path.exists(temp_plot_path):
596
+ import shutil
597
+ shutil.copy2(temp_plot_path, plot_path)
598
+ # Initialize the plots list if it doesn't exist
599
+ if "generated_plots" not in st.session_state:
600
+ st.session_state.generated_plots = []
601
+ # Add the persistent plot path to session state
602
+ st.session_state.generated_plots.append(plot_path)
603
+
604
+ except Exception:
605
+ with self._capture_output() as (out_buf, err_buf):
606
+ import traceback
607
+ traceback.print_exc(file=sys.stderr)
608
+ full_output += f"STDERR:\n{err_buf.getvalue()}\n"
609
+
610
+ return full_output, plot_path
611
+
612
+ # Example instantiation:
613
+ executor = PlotAwareExecutor(timeout=10)
614
+
615
+ # Global agent configurations
616
+ initial_config = LLMConfig(
617
+ api_type="openai",
618
+ model=st.session_state.selected_model,
619
+ temperature=0.2, # Low temperature for consistent initial responses
620
+ api_key=api_key,
621
+ )
622
+
623
+ review_config = LLMConfig(
624
+ api_type="openai",
625
+ model=st.session_state.selected_model,
626
+ temperature=0.7, # Higher temperature for creative reviews
627
+ api_key=api_key,
628
+ response_format=Feedback
629
+ )
630
+
631
+ # typo_config = LLMConfig(
632
+ # api_type="openai",
633
+ # model=st.session_state.selected_model,
634
+ # temperature=0.1, # Very low temperature for precise code corrections
635
+ # api_key=api_key,
636
+ # )
637
+
638
+ formatting_config = LLMConfig(
639
+ api_type="openai",
640
+ model=st.session_state.selected_model,
641
+ temperature=0.3, # Moderate temperature for formatting
642
+ api_key=api_key,
643
+ )
644
+
645
+ code_execution_config = LLMConfig(
646
+ api_type="openai",
647
+ model=st.session_state.selected_model,
648
+ temperature=0.1, # Very low temperature for code execution
649
+ api_key=api_key,
650
+ )
651
+
652
+ # Global agent instances with updated system messages
653
+ initial_agent = ConversableAgent(
654
+ name="initial_agent",
655
+ system_message=f"""
656
+ {Initial_Agent_Instructions}""",
657
+ human_input_mode="NEVER",
658
+ llm_config=initial_config
659
+ )
660
+
661
+ review_agent = ConversableAgent(
662
+ name="review_agent",
663
+ system_message=f"""{Review_Agent_Instructions}""",
664
+ human_input_mode="NEVER",
665
+ llm_config=review_config
666
+ )
667
+
668
+ # typo_agent = ConversableAgent(
669
+ # name="typo_agent",
670
+ # system_message=f"""You are the typo and code correction agent. Your task is to:
671
+ # 1. Fix any typos or grammatical errors
672
+ # 2. Correct any code issues
673
+ # 3. Ensure proper formatting
674
+ # 4. Maintain the original meaning while improving clarity
675
+ # 5. Verify plots are saved to disk (not using show())
676
+ # 6. PRESERVE all code blocks exactly as they are unless there are actual errors
677
+ # 7. If no changes are needed, keep the original code blocks unchanged
678
+
679
+ # # {Typo_Agent_Instructions}""",
680
+ # # human_input_mode="NEVER",
681
+ # # llm_config=typo_config
682
+ # # )
683
+
684
+ formatting_agent = ConversableAgent(
685
+ name="formatting_agent",
686
+ system_message="""{Formatting_Agent_Instructions}""",
687
+ human_input_mode="NEVER",
688
+ llm_config=formatting_config
689
+ )
690
+
691
+ code_executor = ConversableAgent(
692
+ name="code_executor",
693
+ system_message="""{Code_Execution_Agent_Instructions}""",
694
+ human_input_mode="NEVER",
695
+ llm_config=code_execution_config,
696
+ code_execution_config={"executor": executor},
697
+ max_consecutive_auto_reply=50
698
+ )
699
+
700
+ def call_ai(context, user_input):
701
+ if mode_is_fast:
702
+ messages = build_messages(context, user_input, Initial_Agent_Instructions)
703
+ response = st.session_state.llm.invoke(messages)
704
+ return Response(content=response.content)
705
+ else:
706
+ # New Swarm Workflow for detailed mode
707
+ st.markdown("Thinking (Swarm Mode)... ")
708
+
709
+ # Format the conversation history for context
710
+ conversation_history = format_memory_messages(st.session_state.memory.messages)
711
+
712
+ # 1. Initial Agent generates the draft
713
+ st.markdown("Generating initial draft...")
714
+ chat_result_1 = initial_agent.initiate_chat(
715
+ recipient=initial_agent,
716
+ message=f"Conversation history:\n{conversation_history}\n\nContext from documents: {context}\n\nUser question: {user_input}",
717
+ max_turns=1,
718
+ summary_method="last_msg"
719
+ )
720
+ draft_answer = chat_result_1.summary
721
+ if st.session_state.debug:
722
+ st.session_state.debug_messages.append(("Initial Draft", draft_answer))
723
+
724
+ # 2. Review Agent critiques the draft
725
+ st.markdown("Reviewing draft...")
726
+ chat_result_2 = review_agent.initiate_chat(
727
+ recipient=review_agent,
728
+ message=f"Conversation history:\n{conversation_history}\n\nPlease review this draft answer:\n{draft_answer}",
729
+ max_turns=1,
730
+ summary_method="last_msg"
731
+ )
732
+ review_feedback = chat_result_2.summary
733
+ if st.session_state.debug:
734
+ st.session_state.debug_messages.append(("Review Feedback", review_feedback))
735
+
736
+ # # 3. Typo Agent corrects the draft
737
+ # st.markdown("Checking for typos...")
738
+ # chat_result_3 = typo_agent.initiate_chat(
739
+ # recipient=typo_agent,
740
+ # message=f"Original draft: {draft_answer}\n\nReview feedback: {review_feedback}",
741
+ # max_turns=1,
742
+ # summary_method="last_msg"
743
+ # )
744
+ # typo_corrected_answer = chat_result_3.summary
745
+ # if st.session_state.debug: st.text(f"Typo-Corrected Answer:\n{typo_corrected_answer}")
746
+
747
+ # 4. Formatting Agent formats the final answer
748
+ st.markdown("Formatting final answer...")
749
+ chat_result_4 = formatting_agent.initiate_chat(
750
+ recipient=formatting_agent,
751
+ message=f"""Please format this answer while preserving any code blocks:
752
+ {draft_answer}""",
753
+ max_turns=1,
754
+ summary_method="last_msg"
755
+ )
756
+ formatted_answer = chat_result_4.summary
757
+ if st.session_state.debug:
758
+ st.session_state.debug_messages.append(("Formatted Answer", formatted_answer))
759
+
760
+ # Check if the answer contains code
761
+ if "```python" in formatted_answer:
762
+ # Add a note about code execution
763
+ formatted_answer += "\n\n> 💡 **Note**: This answer contains code. If you want to execute it, type 'execute!' in the chat."
764
+ return Response(content=formatted_answer)
765
+ else:
766
+ return Response(content=formatted_answer)
767
+
768
+
769
+ # --- Chat Input ---
770
+ user_input = st.chat_input("Type your prompt here...")
771
+
772
+ # --- Display Full Chat History ---
773
+ for message in st.session_state.messages:
774
+ with st.chat_message(message["role"]):
775
+ # Check if this message contains a plot path marker
776
+ if "PLOT_PATH:" in message["content"]:
777
+ # Split content into text and plot path
778
+ parts = message["content"].split("PLOT_PATH:")
779
+ # Display the text part
780
+ st.markdown(parts[0])
781
+ # Display each plot path
782
+ for plot_info in parts[1:]:
783
+ plot_path = plot_info.split('\n')[0].strip()
784
+ if os.path.exists(plot_path):
785
+ st.image(plot_path, width=700)
786
+ else:
787
+ st.markdown(message["content"])
788
+
789
+ # --- Process New Prompt ---
790
+ if user_input:
791
+ # Show user input immediately
792
+ st.session_state.messages.append({"role": "user", "content": user_input})
793
+ with st.chat_message("user"):
794
+ st.markdown(user_input)
795
+
796
+ st.session_state.memory.add_user_message(user_input)
797
+ context = retrieve_context(user_input)
798
+
799
+ # Count prompt tokens using tiktoken if needed
800
+ try:
801
+ import tiktoken
802
+ enc = tiktoken.encoding_for_model("gpt-4")
803
+ st.session_state.last_token_count = len(enc.encode(user_input))
804
+ except:
805
+ st.session_state.last_token_count = 0
806
+
807
+ # Stream assistant response
808
+ with st.chat_message("assistant"):
809
+ stream_box = st.empty()
810
+ stream_handler = StreamHandler(stream_box)
811
+
812
+ # Second initialization with streaming
813
+ st.session_state.llm = ChatOpenAI(
814
+ model_name=st.session_state.selected_model,
815
+ streaming=True,
816
+ callbacks=[stream_handler],
817
+ openai_api_key=api_key,
818
+ temperature=0.2
819
+ )
820
+
821
+ # Check if this is an execution request
822
+ if user_input.strip().lower() == "execute!":
823
+ # Find the last assistant message containing code
824
+ last_assistant_message = None
825
+ for message in reversed(st.session_state.messages):
826
+ if message["role"] == "assistant" and "```" in message["content"]:
827
+ last_assistant_message = message["content"]
828
+ break
829
+
830
+ if last_assistant_message:
831
+ st.markdown("Executing code...")
832
+ st.info("🚀 Executing cleaned code...")
833
+ #chat_result = code_executor.initiate_chat(
834
+ # recipient=code_executor,
835
+ # message=f"Please execute this code:\n{last_assistant_message}",
836
+ # max_turns=1,
837
+ # summary_method="last_msg"
838
+ #)
839
+ #execution_output = chat_result.summary
840
+ execution_output, plot_path = executor.execute_code(last_assistant_message)
841
+ st.subheader("Execution Output")
842
+ st.text(execution_output) # now contains both STDOUT and STDERR
843
+
844
+ if os.path.exists(plot_path):
845
+ st.success("✅ Plot generated successfully!")
846
+ # Display the plot
847
+ #st.image(plot_path, use_container_width=True)
848
+ st.image(plot_path, width=700)
849
+ else:
850
+ st.warning("⚠️ No plot was generated")
851
+
852
+ # Check for errors and iterate if needed
853
+ max_iterations = 3 # Maximum number of iterations to prevent infinite loops
854
+ current_iteration = 0
855
+ has_errors = any(error_indicator in execution_output for error_indicator in ["Traceback", "Error:", "Exception:", "TypeError:", "ValueError:", "NameError:", "SyntaxError:", "Error in Class"])
856
+
857
+ while has_errors and current_iteration < max_iterations:
858
+ current_iteration += 1
859
+ st.error(f"Previous error: {execution_output}") # Show the actual error message
860
+ st.info(f"🔧 Fixing errors (attempt {current_iteration}/{max_iterations})...")
861
+
862
+ # Get new review with error information
863
+ review_message = f"""
864
+ Previous answer had errors during execution:
865
+ {execution_output}
866
+
867
+ Please review and suggest fixes for this answer. IMPORTANT: Preserve all code blocks exactly as they are, only fix actual errors:
868
+ {last_assistant_message}
869
+ """
870
+ chat_result_2 = review_agent.initiate_chat(
871
+ recipient=review_agent,
872
+ message=review_message,
873
+ max_turns=1,
874
+ summary_method="last_msg"
875
+ )
876
+ review_feedback = chat_result_2.summary
877
+ if st.session_state.debug:
878
+ st.session_state.debug_messages.append(("Error Review Feedback", review_feedback))
879
+
880
+ # Get corrected version
881
+ chat_result_3 = initial_agent.initiate_chat(
882
+ recipient=initial_agent,
883
+ message=f"""Original answer: {last_assistant_message}
884
+ Review feedback with error fixes: {review_feedback}
885
+ IMPORTANT: Only fix actual errors in the code blocks. Preserve all working code exactly as it is.""",
886
+ max_turns=1,
887
+ summary_method="last_msg"
888
+ )
889
+ corrected_answer = chat_result_3.summary
890
+ if st.session_state.debug:
891
+ st.session_state.debug_messages.append(("Corrected Answer", corrected_answer))
892
+
893
+ # Format the corrected answer
894
+ chat_result_4 = formatting_agent.initiate_chat(
895
+ recipient=formatting_agent,
896
+ message=f"""Please format this corrected answer while preserving all code blocks:
897
+ {corrected_answer}
898
+ """,
899
+ max_turns=1,
900
+ summary_method="last_msg"
901
+ )
902
+ formatted_answer = chat_result_4.summary
903
+ if st.session_state.debug:
904
+ st.session_state.debug_messages.append(("Formatted Corrected Answer", formatted_answer))
905
+
906
+ # Execute the corrected code
907
+ st.info("🚀 Executing corrected code...")
908
+ #chat_result = code_executor.initiate_chat(
909
+ # recipient=code_executor,
910
+ # message=f"Please execute this corrected code:\n{formatted_answer}",
911
+ # max_turns=1,
912
+ # summary_method="last_msg"
913
+ #)
914
+ #execution_output = chat_result.summary
915
+ execution_output, plot_path = executor.execute_code(formatted_answer)
916
+ st.subheader("Execution Output")
917
+ st.text(execution_output) # now contains both STDOUT and STDERR
918
+
919
+ if os.path.exists(plot_path):
920
+ st.success("✅ Plot generated successfully!")
921
+ # Display the plot
922
+ st.image(plot_path, width=700)
923
+ else:
924
+ st.warning("⚠️ No plot was generated")
925
+
926
+ if st.session_state.debug:
927
+ st.session_state.debug_messages.append(("Execution Output", execution_output))
928
+
929
+ # If we've reached the end of iterations and we're successful
930
+ if not has_errors or current_iteration == max_iterations:
931
+ # Add successful execution to the conversation with plot
932
+ final_answer = formatted_answer if formatted_answer else last_assistant_message
933
+ response_text = f"Execution completed successfully:\n{execution_output}\n\nThe following code was executed:\n```python\n{final_answer}\n```"
934
+
935
+ # Add plot path marker for rendering in the conversation
936
+ if os.path.exists(plot_path):
937
+ response_text += f"\n\nPLOT_PATH:{plot_path}\n"
938
+
939
+ if current_iteration > 0:
940
+ response_text = f"After {current_iteration} correction attempts: " + response_text
941
+
942
+ # Set the response variable with our constructed text that includes plot
943
+ response = Response(content=response_text)
944
+
945
+ # Update last_assistant_message with the formatted answer for next iteration
946
+ last_assistant_message = formatted_answer
947
+ has_errors = any(error_indicator in execution_output for error_indicator in ["Traceback", "Error:", "Exception:", "TypeError:", "ValueError:", "NameError:", "SyntaxError:", "Error in Class"])
948
+
949
+ if has_errors:
950
+ st.markdown("> ⚠️ **Note**: Some errors could not be fixed after multiple attempts. You can request changes by describing them in the chat.")
951
+ st.markdown(f"> ❌ Last execution message:\n{execution_output}")
952
+ response = Response(content=f"Execution completed with errors:\n{execution_output}")
953
+ else:
954
+ # Check for common error indicators in the output
955
+ if any(error_indicator in execution_output for error_indicator in ["Traceback", "Error:", "Exception:", "TypeError:", "ValueError:", "NameError:", "SyntaxError:"]):
956
+ st.markdown("> ⚠️ **Note**: Code execution completed but with errors. You can request changes by describing them in the chat.")
957
+ st.markdown(f"> ❌ Execution message:\n{execution_output}")
958
+ response = Response(content=f"Execution completed with errors:\n{execution_output}")
959
+ else:
960
+ st.markdown(f"> ✅ Code executed successfully. Last execution message:\n{execution_output}")
961
+
962
+ # Display the final code that was successfully executed
963
+ with st.expander("View Successfully Executed Code", expanded=False):
964
+ st.markdown(last_assistant_message)
965
+
966
+ # Create a response message that includes the plot path
967
+ response_text = f"Execution completed successfully:\n{execution_output}\n\nThe following code was executed:\n```python\n{last_assistant_message}\n```"
968
+
969
+ # Add plot path marker for rendering in the conversation
970
+ if os.path.exists(plot_path):
971
+ response_text += f"\n\nPLOT_PATH:{plot_path}\n"
972
+
973
+ response = Response(content=response_text)
974
+ else:
975
+ response = Response(content="No code found to execute in the previous messages.")
976
+ else:
977
+ response = call_ai(context, user_input)
978
+ if not mode_is_fast:
979
+ st.markdown(response.content)
980
+
981
+ st.session_state.memory.add_ai_message(response.content)
982
+ st.session_state.messages.append({"role": "assistant", "content": response.content})
983
+
984
+ # --- Display Welcome Message (outside of sidebar) ---
985
+ # This ensures the welcome message appears in the main content area
986
+ if "llm_initialized" in st.session_state and st.session_state.llm_initialized and not st.session_state.greeted:
987
+ # Create a chat message container for the welcome message
988
+ with st.chat_message("assistant"):
989
+ # Create empty container for streaming
990
+ welcome_container = st.empty()
991
+
992
+ # Set up the streaming handler
993
+ welcome_stream_handler = StreamHandler(welcome_container)
994
+
995
+ # Initialize streaming LLM
996
+ streaming_llm = ChatOpenAI(
997
+ model_name=st.session_state.selected_model,
998
+ streaming=True,
999
+ callbacks=[welcome_stream_handler],
1000
+ openai_api_key=api_key,
1001
+ temperature=0.2
1002
+ )
1003
+
1004
+ # Generate the streaming welcome message
1005
+ greeting = streaming_llm.invoke([
1006
+ SystemMessage(content=Initial_Agent_Instructions),
1007
+ HumanMessage(content="Please greet the user and briefly explain what you can do as the CLASS code assistant.")
1008
+ ])
1009
+
1010
+ # Save the completed message to history
1011
+ st.session_state.messages.append({"role": "assistant", "content": greeting.content})
1012
+ st.session_state.memory.add_ai_message(greeting.content)
1013
+ st.session_state.greeted = True
1014
+
1015
+ # --- Debug Info ---
1016
+ if st.session_state.debug:
1017
+ with st.sidebar.expander("🛠️ Debug Information", expanded=True):
1018
+ # Create a container for debug messages
1019
+ debug_container = st.container()
1020
+ with debug_container:
1021
+ st.markdown("### Debug Messages")
1022
+
1023
+ # Display all debug messages in a scrollable container
1024
+ for title, message in st.session_state.debug_messages:
1025
+ st.markdown(f"### {title}")
1026
+ st.markdown(message)
1027
+ st.markdown("---")
1028
+
1029
+ with st.sidebar.expander("🛠️ Context Used"):
1030
+ if "context" in locals():
1031
+ st.markdown(context)
1032
+ else:
1033
+ st.markdown("No context retrieved yet.")
README.md CHANGED
@@ -5,7 +5,7 @@ colorFrom: blue
5
  colorTo: gray
6
  sdk: streamlit
7
  sdk_version: 1.44.1
8
- app_file: app.py
9
  pinned: false
10
  license: mit
11
  short_description: 'CLAPP: Classy Llm Agent for Pair Programming'
 
5
  colorTo: gray
6
  sdk: streamlit
7
  sdk_version: 1.44.1
8
+ app_file: CLAPP.py
9
  pinned: false
10
  license: mit
11
  short_description: 'CLAPP: Classy Llm Agent for Pair Programming'
images/CLAPP.png ADDED

Git LFS Details

  • SHA256: f7d76381ed2ada5efa820be9ec7be16f2f6dcaa1bca0cac7b93860e19a1640bc
  • Pointer size: 132 Bytes
  • Size of remote file: 1.66 MB
requirements.txt ADDED
@@ -0,0 +1,175 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ag2==0.8.5
2
+ aiohappyeyeballs==2.6.1
3
+ aiohttp==3.11.14
4
+ aiosignal==1.3.2
5
+ altair==4.2.2
6
+ annotated-types==0.7.0
7
+ anyio==4.9.0
8
+ argon2-cffi==23.1.0
9
+ argon2-cffi-bindings==21.2.0
10
+ arrow==1.3.0
11
+ asttokens==3.0.0
12
+ async-lru==2.0.5
13
+ async-timeout==4.0.3
14
+ asyncer==0.0.8
15
+ attrs==25.3.0
16
+ babel==2.17.0
17
+ beautifulsoup4==4.13.3
18
+ bleach==6.2.0
19
+ blinker==1.9.0
20
+ cachetools==5.5.2
21
+ certifi==2025.1.31
22
+ cffi==1.17.1
23
+ charset-normalizer==3.4.1
24
+ click==8.1.8
25
+ comm==0.2.2
26
+ contourpy==1.3.1
27
+ cryptography==44.0.2
28
+ cycler==0.12.1
29
+ cython==3.0.12
30
+ dataclasses-json==0.6.7
31
+ debugpy==1.8.13
32
+ decorator==5.2.1
33
+ defusedxml==0.7.1
34
+ diskcache==5.6.3
35
+ distro==1.9.0
36
+ docker==7.1.0
37
+ entrypoints==0.4
38
+ exceptiongroup==1.2.2
39
+ executing==2.2.0
40
+ faiss-cpu==1.10.0
41
+ fastjsonschema==2.21.1
42
+ feedparser==6.0.11
43
+ fonttools==4.56.0
44
+ fqdn==1.5.1
45
+ frozenlist==1.5.0
46
+ gitdb==4.0.12
47
+ gitpython==3.1.44
48
+ greenlet==3.1.1
49
+ h11==0.14.0
50
+ httpcore==1.0.7
51
+ httpx==0.28.1
52
+ httpx-sse==0.4.0
53
+ idna==3.10
54
+ ipykernel==6.29.5
55
+ ipython==8.35.0
56
+ ipywidgets==8.1.5
57
+ isoduration==20.11.0
58
+ jedi==0.19.2
59
+ jinja2==3.1.6
60
+ jiter==0.9.0
61
+ json5==0.10.0
62
+ jsonpatch==1.33
63
+ jsonpointer==3.0.0
64
+ jsonschema==4.23.0
65
+ jsonschema-specifications==2024.10.1
66
+ jupyter==1.1.1
67
+ jupyter-client==8.6.3
68
+ jupyter-console==6.6.3
69
+ jupyter-core==5.7.2
70
+ jupyter-events==0.12.0
71
+ jupyter-lsp==2.2.5
72
+ jupyter-server==2.15.0
73
+ jupyter-server-terminals==0.5.3
74
+ jupyterlab==4.3.6
75
+ jupyterlab-pygments==0.3.0
76
+ jupyterlab-server==2.27.3
77
+ jupyterlab-widgets==3.0.13
78
+ kiwisolver==1.4.8
79
+ langchain==0.3.23
80
+ langchain-community==0.3.21
81
+ langchain-core==0.3.52
82
+ langchain-openai==0.3.13
83
+ langchain-text-splitters==0.3.8
84
+ langsmith==0.3.19
85
+ markupsafe==3.0.2
86
+ marshmallow==3.26.1
87
+ matplotlib==3.10.1
88
+ matplotlib-inline==0.1.7
89
+ mistune==3.1.3
90
+ multidict==6.2.0
91
+ mypy-extensions==1.0.0
92
+ nbclient==0.10.2
93
+ nbconvert==7.16.6
94
+ nbformat==5.10.4
95
+ nest-asyncio==1.6.0
96
+ notebook==7.3.3
97
+ notebook-shim==0.2.4
98
+ numpy==1.26.4
99
+ openai==1.68.2
100
+ orjson==3.10.16
101
+ overrides==7.7.0
102
+ packaging==24.2
103
+ pandas==2.2.3
104
+ pandocfilters==1.5.1
105
+ parso==0.8.4
106
+ pexpect==4.9.0
107
+ pillow==11.1.0
108
+ platformdirs==4.3.7
109
+ prometheus-client==0.21.1
110
+ prompt-toolkit==3.0.50
111
+ propcache==0.3.1
112
+ protobuf==5.29.4
113
+ psutil==7.0.0
114
+ ptyprocess==0.7.0
115
+ pure-eval==0.2.3
116
+ pyarrow==19.0.1
117
+ pyautogen==0.8.5
118
+ pycparser==2.22
119
+ pydantic==2.10.6
120
+ pydantic-core==2.27.2
121
+ pydantic-settings==2.8.1
122
+ pydeck==0.9.1
123
+ pygments==2.19.1
124
+ pyparsing==3.2.3
125
+ pypdf==5.4.0
126
+ python-dateutil==2.9.0.post0
127
+ python-dotenv==1.1.0
128
+ python-json-logger==3.3.0
129
+ pytz==2025.2
130
+ pyyaml==6.0.2
131
+ pyzmq==26.4.0
132
+ referencing==0.36.2
133
+ regex==2024.11.6
134
+ requests==2.32.3
135
+ requests-toolbelt==1.0.0
136
+ rfc3339-validator==0.1.4
137
+ rfc3986-validator==0.1.1
138
+ rpds-py==0.23.1
139
+ scipy==1.15.2
140
+ send2trash==1.8.3
141
+ setuptools==78.1.0
142
+ sgmllib3k==1.0.0
143
+ six==1.17.0
144
+ smmap==5.0.2
145
+ sniffio==1.3.1
146
+ soupsieve==2.6
147
+ sqlalchemy==2.0.39
148
+ stack-data==0.6.3
149
+ streamlit==1.43.2
150
+ tenacity==9.0.0
151
+ termcolor==3.0.1
152
+ terminado==0.18.1
153
+ tiktoken==0.9.0
154
+ tinycss2==1.4.0
155
+ toml==0.10.2
156
+ tomli==2.2.1
157
+ toolz==1.0.0
158
+ tornado==6.4.2
159
+ tqdm==4.67.1
160
+ traitlets==5.14.3
161
+ types-python-dateutil==2.9.0.20241206
162
+ typing-extensions==4.13.1
163
+ typing-inspect==0.9.0
164
+ tzdata==2025.2
165
+ uri-template==1.3.0
166
+ urllib3==2.3.0
167
+ uv==0.6.9
168
+ watchdog==6.0.0
169
+ wcwidth==0.2.13
170
+ webcolors==24.11.1
171
+ webencodings==0.5.1
172
+ websocket-client==1.8.0
173
+ widgetsnbextension==4.0.13
174
+ yarl==1.18.3
175
+ zstandard==0.23.0