shrushhtijadhav/codingagent
1
1import gradio as gr2from sentence_transformers import SentenceTransformer3from sklearn.metrics.pairwise import cosine_similarity4import PyPDF25import requests6import os7from dotenv import load_dotenv8import uuid9 10# Load environment variables from .env file11load_dotenv()12 13# Load embedding model14embedder = SentenceTransformer("all-MiniLM-L6-v2")15 16# Get Gemini API key from environment variable17GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")18GEMINI_ENDPOINT = "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent"19 20# In-memory chat session storage: {chat_id: [(user, assistant), ...]}21chat_sessions = {}22 23def extract_pdf_text(pdf_file):24 reader = PyPDF2.PdfReader(pdf_file)25 text = ""26 for page in reader.pages:27 page_text = page.extract_text()28 if page_text:29 text += page_text + "\n"30 return text31 32def chunk_text(text, chunk_size=500):33 import re34 sentences = re.split(r'(?<=[.!?]) +', text)35 chunks = []36 current_chunk = ""37 for sentence in sentences:38 if len(current_chunk) + len(sentence) < chunk_size:39 current_chunk += " " + sentence40 else:41 chunks.append(current_chunk.strip())42 current_chunk = sentence43 if current_chunk:44 chunks.append(current_chunk.strip())45 return chunks46 47def retrieve_relevant_chunks(query, chunks, chunk_embeddings, top_k=2):48 query_embedding = embedder.encode([query])49 similarities = cosine_similarity(query_embedding, chunk_embeddings)[0]50 top_indices = similarities.argsort()[-top_k:][::-1]51 return [chunks[i] for i in top_indices]52 53def generate_gemini_response(prompt, history=None):54 # Use LLM-based summarization for history compression55 if history:56 history_text = llm_summarize_history(history, max_turns=5)57 prompt = f"{history_text}User: {prompt}\nAssistant:"58 headers = {59 "Content-Type": "application/json"60 }61 params = {62 "key": GEMINI_API_KEY63 }64 payload = {65 "contents": [66 {67 "parts": [68 {"text": prompt}69 ]70 }71 ]72 }73 response = requests.post(GEMINI_ENDPOINT, headers=headers, params=params, json=payload)74 if response.status_code == 200:75 result = response.json()76 try:77 return result["candidates"][0]["content"]["parts"][0]["text"].strip()78 except Exception:79 return str(result)80 else:81 return f"Error: {response.status_code} - {response.text}"82 83def improved_prompt(user_query, context=None):84 # Stronger prompt for code and explanations85 base = (86 "You are an expert coding assistant. "87 "Generate correct, complete, and well-explained code for the user's request. "88 "If the user asks for code, provide the code block with an detail explanation. "89 "If you use any context, cite it. If you don't know, say 'I don't know.'\n\n"90 )91 if context:92 base += f"Context from PDF or documents:\n{context}\n\n"93 base += f"User question:\n{user_query}\n\nAssistant:"94 return base95 96def chatbot_agent(user_query, pdf_file, chat_id):97 # Get chat history for this session98 history = chat_sessions.get(chat_id, [])99 context = None100 if pdf_file is not None:101 pdf_text = extract_pdf_text(pdf_file)102 chunks = chunk_text(pdf_text)103 chunk_embeddings = embedder.encode(chunks)104 relevant_chunks = retrieve_relevant_chunks(user_query, chunks, chunk_embeddings, top_k=3)105 context = "\n".join(relevant_chunks)106 prompt = improved_prompt(user_query, context)107 answer = generate_gemini_response(prompt, history)108 # Update history109 history.append((user_query, answer))110 chat_sessions[chat_id] = history111 return history112 113def new_chat():114 chat_id = str(uuid.uuid4())115 chat_sessions[chat_id] = []116 return chat_id, [], None117 118def delete_chat(chat_id):119 if chat_id in chat_sessions:120 del chat_sessions[chat_id]121 return gr.Dropdown.update(choices=list(chat_sessions.keys()), value=None), [], None122 123def load_chat(chat_id):124 history = chat_sessions.get(chat_id, [])125 # Convert to OpenAI-style messages for gr.Chatbot with type="messages"126 messages = []127 for user, assistant in history:128 messages.append({"role": "user", "content": user})129 messages.append({"role": "assistant", "content": assistant})130 return messages131 132def llm_summarize_history(history, max_turns=5):133 """134 Uses Gemini to summarize older chat history if it exceeds max_turns.135 Keeps the last max_turns exchanges in full, summarizes the rest.136 """137 if len(history) > max_turns:138 # Prepare summary prompt for older history139 summary_prompt = (140 "Summarize the following conversation in a concise way, preserving all important facts and context:\n\n"141 )142 for user, assistant in history[:-max_turns]:143 summary_prompt += f"User: {user}\nAssistant: {assistant}\n"144 summary = generate_gemini_response(summary_prompt)145 # Keep last max_turns exchanges in full146 recent_history = history[-max_turns:]147 history_text = summary + "\n"148 for user, assistant in recent_history:149 history_text += f"User: {user}\nAssistant: {assistant}\n"150 return history_text151 else:152 # If history is short, just concatenate153 history_text = ""154 for user, assistant in history:155 history_text += f"User: {user}\nAssistant: {assistant}\n"156 return history_text157 158with gr.Blocks(title="Coding Agent & PDF Chatbot") as iface:159 gr.Markdown(160 """161 # ๐ค Coding Agent & PDF Chatbot162 - Ask coding questions or request code generation.163 - Optionally upload a PDF to ask questions about its content.164 - Manage multiple chats: create, delete, and revisit previous conversations.165 """166 )167 with gr.Row():168 with gr.Column(scale=1):169 chat_selector = gr.Dropdown(label="Select Chat", choices=[], value=None)170 new_btn = gr.Button("โ New Chat")171 del_btn = gr.Button("๐๏ธ Delete Chat")172 with gr.Column(scale=3):173 chat = gr.Chatbot(label="Conversation", height=400, type="messages")174 pdf_input = gr.File(label="Upload a PDF (optional)", file_types=[".pdf"])175 user_input = gr.Textbox(label="Ask a coding question", lines=2)176 submit_btn = gr.Button("Send")177 state = gr.State(None) # Holds current chat_id178 179 # New chat180 def handle_new_chat(_):181 chat_id, history, _ = new_chat()182 return (183 gr.update(choices=list(chat_sessions.keys()), value=chat_id),184 chat_id,185 []186 )187 188 new_btn.click(189 handle_new_chat,190 inputs=[chat_selector],191 outputs=[chat_selector, state, chat]192 )193 194 # Delete chat195 def handle_delete_chat(_, chat_id):196 if chat_id in chat_sessions:197 del chat_sessions[chat_id]198 return (199 gr.update(choices=list(chat_sessions.keys()), value=None),200 [],201 None202 )203 204 del_btn.click(205 handle_delete_chat,206 inputs=[chat_selector, state],207 outputs=[chat_selector, chat, state]208 )209 210 # Load chat211 chat_selector.change(212 lambda chat_id: (chat_id, load_chat(chat_id)),213 inputs=[chat_selector],214 outputs=[state, chat]215 )216 217 # Send message218 def user_message(user_input, pdf_input, chat_id):219 if not chat_id:220 chat_id, _, _ = new_chat()221 history = chatbot_agent(user_input, pdf_input, chat_id)222 # Convert to OpenAI-style messages for gr.Chatbot with type="messages"223 messages = []224 for user, assistant in history:225 messages.append({"role": "user", "content": user})226 messages.append({"role": "assistant", "content": assistant})227 return chat_id, messages228 229 submit_btn.click(230 user_message,231 inputs=[user_input, pdf_input, state],232 outputs=[state, chat]233 )234 user_input.submit(235 user_message,236 inputs=[user_input, pdf_input, state],237 outputs=[state, chat]238 )239 240iface.launch()