DevForML commited on
Commit
8886323
·
verified ·
1 Parent(s): 7282067

Upload 12 files

Browse files
.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
+ uploads/documents/LLM_based_QA_chatbot_builder.pdf filter=lfs diff=lfs merge=lfs -text
Dockerfile ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Use an official Python runtime as a parent image
2
+ FROM python:3.11-slim
3
+
4
+ # Install system dependencies
5
+ RUN apt-get update && apt-get install -y gcc
6
+
7
+ # Set the working directory to /app
8
+ WORKDIR /app
9
+
10
+ # Copy the requirements file and install dependencies
11
+ COPY requirements.txt .
12
+ RUN pip install --upgrade pip && pip install -r requirements.txt
13
+
14
+ # Create necessary directories with appropriate permissions
15
+ RUN mkdir -p /app/cache /app/uploads /app/data /app/chats && chmod -R 777 /app/cache /app/uploads /app/data /app/chats
16
+ RUN chmod -R 777 /app
17
+
18
+ # Copy the application code
19
+ COPY . .
20
+
21
+ # Set environment variables
22
+ ENV FLASK_APP=app.py \
23
+ FLASK_ENV=production
24
+
25
+ EXPOSE 7860
26
+
27
+ # Run the application using Gunicorn
28
+ CMD ["gunicorn", "-k", "eventlet", "-w", "1", "-b", "0.0.0.0:7860", "--timeout", "600", "app:app"]
agent.py ADDED
@@ -0,0 +1,711 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #─── Basic imports ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
2
+ import os
3
+ import math
4
+ import sqlite3
5
+ import fitz # PyMuPDF for PDF parsing
6
+ from flask_socketio import SocketIO
7
+
8
+ # ─── Langchain Frameworks ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
9
+ from langchain.tools import Tool
10
+ from langchain.chat_models import ChatOpenAI
11
+ from langchain_groq import ChatGroq
12
+ from langchain_mistralai import ChatMistralAI
13
+ from langchain.agents import initialize_agent, AgentType
14
+ from langchain.schema import Document
15
+ from langchain.chains import RetrievalQA
16
+ from langchain.embeddings import OpenAIEmbeddings
17
+ from langchain_community.embeddings import HuggingFaceEmbeddings
18
+ from langchain.vectorstores import FAISS
19
+ from langchain.text_splitter import RecursiveCharacterTextSplitter
20
+ from langchain.prompts import PromptTemplate
21
+ from langchain_community.document_loaders import TextLoader, PyMuPDFLoader
22
+ # taking global variables from the app.py file
23
+ #from app import DB_PATH, DOC_PATH, IMG_PATH, OTH_PATH
24
+
25
+ # ─── File paths ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
26
+ import config
27
+ # Ensure this is at the very top
28
+
29
+ # ─── SQL Agent ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
30
+ from langchain_community.utilities import SQLDatabase
31
+ from langchain_community.agent_toolkits import SQLDatabaseToolkit
32
+ from langchain.chat_models import ChatOpenAI
33
+ from langgraph.prebuilt import create_react_agent
34
+ from langchain.agents import create_sql_agent
35
+
36
+ # ─── Memory ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
37
+ from langchain.memory import ConversationBufferMemory
38
+ from langchain.agents import initialize_agent, AgentType
39
+ from langchain.tools import Tool
40
+ from typing import List, Callable
41
+ from langchain.memory import ConversationBufferMemory
42
+ from langchain.schema import BaseMemory, AIMessage, HumanMessage, SystemMessage
43
+ from langchain.llms.base import LLM
44
+ from langchain.memory.chat_memory import BaseChatMemory
45
+ from pydantic import PrivateAttr
46
+ from langchain_core.messages import get_buffer_string
47
+
48
+ # 1) Create your memory object
49
+ from typing import List
50
+ from langchain.memory import ConversationBufferMemory
51
+ from langchain.schema import AIMessage, HumanMessage, SystemMessage
52
+ from langchain.llms.base import LLM
53
+ from langchain.memory.chat_memory import BaseChatMemory
54
+ from pydantic import PrivateAttr
55
+
56
+ class AutoSummaryMemory(ConversationBufferMemory):
57
+ _llm: LLM = PrivateAttr()
58
+ _max_entries: int = PrivateAttr()
59
+ _reduce_to: int = PrivateAttr()
60
+ _summary_system_prompt: str = PrivateAttr()
61
+
62
+ def __init__(
63
+ self,
64
+ llm: LLM,
65
+ memory_key: str = "chat_history",
66
+ return_messages: bool = True,
67
+ max_entries: int = 20,
68
+ reduce_to: int = 5,
69
+ summary_system_prompt: str = (
70
+ "Summarize the following conversation so far in a concise paragraph. "
71
+ "Keep important facts and questions."
72
+ )
73
+ ):
74
+ super().__init__(memory_key=memory_key, return_messages=return_messages)
75
+ self._llm = llm # PrivateAttr
76
+ self._max_entries = max_entries # PrivateAttr
77
+ self._reduce_to = reduce_to # PrivateAttr
78
+ self._summary_system_prompt = summary_system_prompt # PrivateAttr
79
+
80
+ def add_memory(self, inputs: dict, outputs: dict) -> None:
81
+ # Add the new turn as normal
82
+ super().add_memory(inputs=inputs, outputs=outputs)
83
+
84
+ # Check if memory length exceeded
85
+ msgs = self.chat_memory.messages
86
+ if len(msgs) >= self._max_entries:
87
+ full_text = "\n".join([f"{m.type}: {m.content}" for m in msgs])
88
+ summary = self._llm.predict(f"{self._summary_system_prompt}\n\n{full_text}")
89
+
90
+ recent = msgs[-self._reduce_to:]
91
+ self.chat_memory.messages = [
92
+ SystemMessage(content="Conversation summary: " + summary),
93
+ *recent
94
+ ]
95
+
96
+
97
+ # ─── Image Processing ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
98
+
99
+ from PIL import Image
100
+ import pytesseract
101
+ from transformers import pipeline
102
+ from groq import Groq
103
+ import config
104
+ import requests
105
+ from io import BytesIO
106
+ from PIL import Image
107
+ from transformers import pipeline, TrOCRProcessor, VisionEncoderDecoderModel
108
+ from PIL import Image
109
+ import requests
110
+ from io import BytesIO
111
+ import base64
112
+ from PIL import UnidentifiedImageError
113
+
114
+ # ─── Browser var ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
115
+ from typing import List, Dict
116
+ import json
117
+ from io import BytesIO
118
+ from langchain.tools import tool # or langchain_core.tools
119
+ from playwright.sync_api import sync_playwright
120
+ from duckduckgo_search import DDGS
121
+ from bs4 import BeautifulSoup
122
+ import requests
123
+
124
+
125
+
126
+ from playwright.sync_api import sync_playwright
127
+ # Attempt to import Playwright for dynamic page rendering
128
+ try:
129
+ from playwright.sync_api import sync_playwright
130
+ _playwright_available = True
131
+ except ImportError:
132
+ _playwright_available = False
133
+
134
+ # Define forbidden keywords for basic NSFW filtering
135
+ _forbidden = ["porn", "sex", "xxx", "nude", "erotic"]
136
+
137
+
138
+ # ─── LLM Setup ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
139
+
140
+
141
+ # Load OpenAI API key from environment (required for LLM and embeddings)
142
+ import os
143
+
144
+ # API Keys from .env file
145
+ os.environ.setdefault("OPENAI_API_KEY", "<YOUR_OPENAI_KEY>") # Set your own key or env var
146
+ os.environ["GROQ_API_KEY"] = os.getenv("GROQ_API_KEY", "default_key_or_placeholder")
147
+ os.environ["MISTRAL_API_KEY"] = os.getenv("MISTRAL_API_KEY", "default_key_or_placeholder")
148
+
149
+ # Tavily API Key
150
+ TAVILY_API_KEY = os.getenv("TAVILY_API_KEY", "default_key_or_placeholder")
151
+ _forbidden = ["nsfw", "porn", "sex", "explicit"]
152
+ _playwright_available = True # set False to disable Playwright
153
+
154
+ # Globals for RAG system
155
+ vector_store = None
156
+ rag_chain = None
157
+ DB_PATH = None # will be set when a .db is uploaded
158
+ DOC_PATH = None # will be set when a document is uploaded
159
+ IMG_PATH = None # will be set when an image is uploaded
160
+ OTH_PATH = None # will be set when an other file is uploaded
161
+
162
+
163
+ # ─── LLMS ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
164
+ #llm = ChatOpenAI(model_name="gpt-3.5-turbo", streaming=True, temperature=0)
165
+ llm = ChatGroq(model="meta-llama/llama-4-maverick-17b-128e-instruct", streaming=True, temperature=0)
166
+ #llm = ChatMistralAI(model="mistral-large-latest", streaming=True, temperature=0)
167
+
168
+
169
+ # ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
170
+ # ─────────────────────────────────────────────── Tool for browsing ────────────────────────────────────────────────���───────────────────────
171
+ # ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
172
+
173
+ def tavily_search(query: str, top_k: int = 3) -> List[Dict]:
174
+ """Call Tavily API and return a list of result dicts."""
175
+ if not TAVILY_API_KEY:
176
+ print("[Tavily] No API key set. Skipping Tavily search.")
177
+ return []
178
+ url = "https://api.tavily.com/search"
179
+ headers = {
180
+ "Authorization": f"Bearer {TAVILY_API_KEY}",
181
+ "Content-Type": "application/json",
182
+ }
183
+ payload = {"query": query, "num_results": top_k}
184
+ try:
185
+ resp = requests.post(url, headers=headers, json=payload, timeout=10)
186
+ resp.raise_for_status()
187
+ data = resp.json()
188
+ results = []
189
+ for item in data.get("results", []):
190
+ results.append({
191
+ "title": item.get("title", ""),
192
+ "url": item.get("url", ""),
193
+ "snippet": item.get("content", "")[:200],
194
+ "source": "Tavily"
195
+ })
196
+ return results
197
+ except (requests.exceptions.RequestException, ValueError) as e:
198
+ print(f"[Tavily] search failed: {e}")
199
+ return []
200
+
201
+ def duckduckgo_search(query: str, top_k: int = 3) -> List[Dict]:
202
+ """Query DuckDuckGo and return up to top_k raw SERP hits."""
203
+ try:
204
+ results = []
205
+ with DDGS() as ddgs:
206
+ for hit in ddgs.text(query, safesearch="On", max_results=top_k):
207
+ results.append({
208
+ "title": hit.get("title", ""),
209
+ "url": hit.get("href") or hit.get("url", ""),
210
+ "snippet": hit.get("body", ""),
211
+ "source": "DuckDuckGo"
212
+ })
213
+ if len(results) >= top_k:
214
+ break
215
+ return results
216
+ except Exception as e:
217
+ print(f"[DuckDuckGo] search failed: {e}")
218
+ return []
219
+
220
+ def hybrid_web_search(query: str, top_k: int = 3) -> str:
221
+ """
222
+ Returns a JSON string with combined Tavily + DuckDuckGo results.
223
+ Always returns non-empty JSON with at least a placeholder result.
224
+ """
225
+ tavily = tavily_search(query, top_k)
226
+ ddg = duckduckgo_search(query, top_k)
227
+ combined = tavily + ddg
228
+
229
+ # Always return at least a message to avoid agent crashes
230
+ if not combined:
231
+ combined = [{
232
+ "title": "No results found",
233
+ "url": "",
234
+ "snippet": f"Could not find suitable web results for '{query}'.",
235
+ "source": "None"
236
+ }]
237
+ output = {"query": query, "results": combined}
238
+ return json.dumps(output, ensure_ascii=False, indent=2)
239
+
240
+ def web_search(query: str, top_k: int = 3) -> str:
241
+ """
242
+ Full hybrid search with Playwright/BeautifulSoup scraping + Tavily/DuckDuckGo.
243
+ Always returns valid JSON output.
244
+ """
245
+ results: List[Dict] = []
246
+
247
+ # Step 1: DuckDuckGo + scraping
248
+ try:
249
+ with DDGS() as ddgs:
250
+ hits = ddgs.text(query, safesearch="On", max_results=top_k)
251
+ except Exception as e:
252
+ print(f"[web_search] DuckDuckGo lookup failed: {e}")
253
+ hits = []
254
+
255
+ for hit in hits:
256
+ url = hit.get("href") or hit.get("url")
257
+ if not url:
258
+ continue
259
+
260
+ try:
261
+ with sync_playwright() as pw:
262
+ browser = pw.chromium.launch(headless=True)
263
+ page = browser.new_page()
264
+ page.goto(url, wait_until="domcontentloaded", timeout=15000)
265
+ html = page.content()
266
+ browser.close()
267
+ soup = BeautifulSoup(html, "html.parser")
268
+ text = soup.get_text(separator=" ", strip=True)
269
+ except Exception as e:
270
+ print(f"[web_search] scraping failed for {url}: {e}")
271
+ continue
272
+
273
+ if any(f in text.lower() for f in _forbidden):
274
+ continue
275
+
276
+ excerpt = " ".join(text.split()[:200])
277
+ results.append({
278
+ "title": hit.get("title", ""),
279
+ "url": url,
280
+ "snippet": hit.get("body", ""),
281
+ "content": excerpt
282
+ })
283
+
284
+ # Step 2: Parse hybrid Tavily + DDG JSON into list
285
+ try:
286
+ raw = hybrid_web_search(query, top_k)
287
+ parsed = json.loads(raw)
288
+ other = parsed.get("results", [])
289
+ except Exception as e:
290
+ print(f"[web_search] parsing hybrid results failed: {e}")
291
+ other = []
292
+
293
+ # Step 3: Combine and return
294
+ combined = results + other
295
+ if not combined:
296
+ combined = [{
297
+ "title": "No results found",
298
+ "url": "",
299
+ "snippet": f"Could not find suitable content for '{query}'.",
300
+ "source": "None"
301
+ }]
302
+
303
+ output = {
304
+ "query": query,
305
+ "sources_count": len(combined),
306
+ "results": combined,
307
+ "sources": list({item.get("url", "") for item in combined if item.get("url")})
308
+ }
309
+ return json.dumps(output, ensure_ascii=False, indent=2)
310
+
311
+ # ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
312
+ # ─────────────────────────────────────────────── Tool for calculation ─────────────────────────────────────────────────────────────────────
313
+ # ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
314
+
315
+ def calculate(expr: str) -> str:
316
+ """
317
+ Evaluates a mathematical expression safely.
318
+ Uses Python's numexpr for security and speed:contentReference[oaicite:21]{index=21}.
319
+ """
320
+ try:
321
+ # Allow math constants
322
+ local_dict = {"pi": math.pi, "e": math.e}
323
+ # Evaluate expression using numexpr for safety/performance
324
+ import numexpr
325
+ result = numexpr.evaluate(expr, local_dict=local_dict)
326
+ return str(result.item())
327
+ except Exception as e:
328
+ return f"Error calculating expression: {e}"
329
+
330
+ # ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
331
+ # ─────────────────────────────────────────────── Tool for Date and time ───────────────────────────────────────────────────────────────────
332
+ # ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
333
+
334
+ def get_current_date(_: str = "") -> str:
335
+ """
336
+ Returns the current date and time. Ignoring input.
337
+ """
338
+ from datetime import datetime
339
+ return datetime.now().strftime("%Y-%m-%d %H:%M:%S")
340
+
341
+ # ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
342
+ # ─────────────────────────────────────────────── Tool for SQL Database ────────────────────────────────────────────────────────────────────
343
+ # ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
344
+
345
+
346
+
347
+ def create_sql_agent_function(db_uri: str, top_k: int = 5):
348
+ """
349
+ Creates a full-fledged SQL agent function that can answer natural language questions over a SQL database.
350
+
351
+ Args:
352
+ db_uri (str): The SQLAlchemy database URI, e.g. "sqlite:///Chinook.db"
353
+ top_k (int): Number of rows to limit in results (default 5)
354
+
355
+ Returns:
356
+ agent_executor: LangChain agent that can .run() or .stream()
357
+ """
358
+
359
+ # 1) Initialize the database + LLM + toolkit
360
+ db = SQLDatabase.from_uri(db_uri)
361
+ llm = ChatGroq(model="meta-llama/llama-4-maverick-17b-128e-instruct", streaming=False, temperature=0)
362
+ toolkit = SQLDatabaseToolkit(db=db, llm=llm)
363
+
364
+ # 2) Prompt with all required variables declared AND used
365
+ prompt = PromptTemplate(
366
+ template="""
367
+ You are an agent designed to interact with a SQL database.
368
+ Given the user question below, first generate a syntactically correct {dialect} query.
369
+ Then look at the results of that query, and return the answer.
370
+ Always limit to at most {top_k} rows unless the user specifies otherwise.
371
+ If you encounter an error, rewrite your SQL and retry.
372
+ DO NOT issue any INSERT/UPDATE/DELETE/DROP/ statements.
373
+ DO NOT try to create new database tables or columns when user has not asked for.
374
+ Always inspect the schema before querying.
375
+
376
+ Available tools: {tools}
377
+ Tool names: {tool_names}
378
+
379
+ User question: {input}
380
+
381
+ {agent_scratchpad}
382
+ """.strip(),
383
+ input_variables=["input", "dialect", "top_k", "agent_scratchpad", "tools", "tool_names"],
384
+ )
385
+
386
+ # 3) Create the agent with prompt + toolkit tools
387
+ agent_executor = create_sql_agent(
388
+ llm=llm,
389
+ toolkit=toolkit,
390
+ prompt=prompt,
391
+ verbose=False,
392
+ # pass top_k dynamically
393
+ extra_prompt_kwargs={"top_k": str(top_k), "dialect": db.dialect},
394
+ )
395
+
396
+ return agent_executor
397
+
398
+ def execute_sql(query: str) -> str:
399
+ """
400
+ Executes a SQL query against the uploaded SQLite DB (GLOBAL_DB_PATH).
401
+ Returns a string of results or error.
402
+ """
403
+ if DB_PATH is None:
404
+ return "No database uploaded. Please upload a SQLite file first."
405
+
406
+ print("DB_PATH--------->:", DB_PATH)
407
+
408
+ db_uri = f"sqlite:///{DB_PATH}"
409
+ agent_executor2 = create_sql_agent_function(db_uri, top_k=5)
410
+
411
+ try:
412
+ result = agent_executor2.run(query)
413
+ except Exception as e:
414
+ result = f"Agent / SQL error: {e}"
415
+ return result
416
+
417
+ # ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
418
+ # ─────────────────────────────────────────────── Tool for RAG (Document Intelligence) ─────────────────────────────────────────────────────
419
+ # ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
420
+
421
+ def rag_index_document(DOC_PATH: str) -> str:
422
+ """
423
+ Indexes the given document into the RAG vector store.
424
+ Supports text files or PDFs. Uses recursive text splitting for better chunking.
425
+ """
426
+ global vector_store, rag_chain
427
+ text = ""
428
+
429
+ # Read text from file
430
+ if DOC_PATH and DOC_PATH.lower().endswith(".pdf"):
431
+ doc = fitz.open(DOC_PATH)
432
+ for page in doc:
433
+ text += page.get_text()
434
+ else:
435
+ with open(DOC_PATH, 'r', encoding='utf-8') as f:
436
+ text = f.read()
437
+
438
+ # Split text using recursive text splitter
439
+ text_splitter = RecursiveCharacterTextSplitter(
440
+ chunk_size=500, # You can adjust this (e.g., 500-1000)
441
+ chunk_overlap=100 # Overlap for better context between chunks
442
+ )
443
+
444
+ # Split into chunks
445
+ texts = text_splitter.split_text(text)
446
+
447
+ # Create Document objects with metadata
448
+ docs = [Document(page_content=t, metadata={"source": DOC_PATH}) for t in texts]
449
+
450
+ # Initialize or append to FAISS vector store
451
+ embeddings = HuggingFaceEmbeddings(model_name='sentence-transformers/all-MiniLM-L6-v2')
452
+
453
+ if vector_store is None:
454
+ vector_store = FAISS.from_documents(docs, embeddings)
455
+ else:
456
+ vector_store.add_documents(docs)
457
+
458
+ retriever = vector_store.as_retriever(
459
+ search_type="mmr",
460
+ search_kwargs={
461
+ "k": 10,
462
+ "fetch_k": 10,
463
+ "lambda_mult": 0.25
464
+ }
465
+ )
466
+
467
+ # Build or update the RetrievalQA chain
468
+ rag_chain = RetrievalQA.from_chain_type(
469
+ llm=llm,
470
+ chain_type="stuff",
471
+ retriever=retriever,
472
+ return_source_documents=False
473
+ )
474
+
475
+
476
+ def rag_answer(query: str) -> str:
477
+ """
478
+ Answers a question using the RAG chain (on indexed documents).
479
+ """
480
+ global rag_chain
481
+ if rag_chain is None:
482
+ return "No documents indexed. Please upload documents via /upload_doc."
483
+ try:
484
+ answer = rag_chain.run(query)
485
+ return answer
486
+ except Exception as e:
487
+ return f"RAG error: {e}"
488
+
489
+ # ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
490
+ # ───────────────────────────────────── Tool for Image (understading, captioning & classification) ─────────────────────────────────────────
491
+ # ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
492
+
493
+ # Vision tools and functions
494
+ # Load image function
495
+ # def _load_image():
496
+ # try:
497
+ # if IMG_PATH.startswith("http"):
498
+ # res = requests.get(IMG_PATH)
499
+ # res.raise_for_status()
500
+ # img = Image.open(BytesIO(res.content))
501
+ # else:
502
+ # img = Image.open(IMG_PATH)
503
+ # return img.convert("RGB")
504
+ # except Exception as e:
505
+ # raise RuntimeError(f"Failed to load image: {e}")
506
+
507
+
508
+ def _load_image(resize_to=(512, 512)):
509
+ """
510
+ Load and resize the image from IMG_PATH.
511
+ If the image is not valid, raise an error.
512
+ """
513
+ try:
514
+ if IMG_PATH is None:
515
+ raise ValueError("No image uploaded. Please upload an image first.")
516
+ #return "No image uploaded. Please upload an image first."
517
+ with open(IMG_PATH, "rb") as f:
518
+ img = Image.open(f)
519
+ img.verify() # Verify it's an image
520
+ img = Image.open(IMG_PATH).convert("RGB") # Reopen after verify and convert
521
+ img = img.resize(resize_to) # resize image to reduce token size
522
+ return img
523
+ except UnidentifiedImageError:
524
+ raise ValueError(f"File at {IMG_PATH} is not a valid image.")
525
+ except Exception as e:
526
+ raise ValueError(f"Failed to load image at {IMG_PATH}: {str(e)}")
527
+
528
+ def _encode_image_to_base64():
529
+ img = _load_image()
530
+ buffer = BytesIO()
531
+ img.save(buffer, format="PNG", optimize=True) # save optimized PNG
532
+ return base64.b64encode(buffer.getvalue()).decode("utf-8")
533
+
534
+ def _call_llama_llm(prompt_text: str) -> str:
535
+ b64 = _encode_image_to_base64()
536
+ message = HumanMessage(
537
+ content=[
538
+ {"type": "text", "text": prompt_text},
539
+ {
540
+ "type": "image_url",
541
+ "image_url": {
542
+ "url": f"data:image/png;base64,{b64}"
543
+ }
544
+ }
545
+ ]
546
+ )
547
+ response = llm.invoke([message])
548
+ return response.content.strip()
549
+
550
+ def vision_query(task_prompt: str) -> str:
551
+ try:
552
+ return _call_llama_llm(task_prompt)
553
+ except Exception as llama_error:
554
+ print(f"[LLaMA-4V failed] {llama_error}")
555
+ try:
556
+ img = _load_image()
557
+ return pytesseract.image_to_string(img).strip()
558
+ except Exception as ocr_error:
559
+ print(f"[OCR fallback failed] {ocr_error}")
560
+ return "Unable to process the image or image is not uploaded. Please try again with a different input."
561
+
562
+ #### Create LangChain Tools ####
563
+ # ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
564
+ # ─────────────────────────────────────────────── Assigning tools as list ───────────────────────────────────────���──────────────────────────
565
+ # ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
566
+
567
+ tool_list = [
568
+ Tool(name="browse", func=web_search, description="Search the web and scrape top results. Uses DuckDuckGo (safe mode) for query. Prefers Playwright for loading pages, with requests/BeautifulSoup as fallback. Filters out any explicit content. Returns JSON with titles, URLs, and page text."),
569
+ Tool(name="calculate", func=calculate, description="Perform math calculations safely."),
570
+ Tool(name="date", func=get_current_date, description="Fetch the current date and time."),
571
+ Tool(name="sql", func=execute_sql, description="Execute SQL query on the uploaded database."),
572
+ Tool(name="rag", func=rag_answer, description="Answer questions using the uploaded documents with retrieval-augmented generation (RAG)."),
573
+ Tool(
574
+ name="vision",
575
+ func=vision_query,
576
+ description=(
577
+ "Perform any image-understanding task—e.g. read text, classify objects, "
578
+ "generate captions, count or locate items, answer questions about the scene, "
579
+ "detect NSFW content, etc.—powered by LLaMA 4-Vision. "
580
+ "If the request is OCR-style and LLaMA fails, it falls back to Tesseract OCR."
581
+ ),
582
+ ),
583
+ ]
584
+
585
+ # ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
586
+ # ─────────────────────────────────────────────── Added Memory to Agent ────────────────────────────────────────────────────────────────────
587
+ # ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
588
+
589
+ # 1) instantiate with your LLM
590
+ memory = AutoSummaryMemory(
591
+ llm=llm,
592
+ max_entries=20, # when chat ≥20 messages, trigger summary
593
+ reduce_to=5 # keep only last 5 after summarizing
594
+ )
595
+
596
+ # ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
597
+ # ─────────────────────────────────────────────── Initialize Agent ─────────────────────────────────────────────────────────────────────────
598
+ # ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
599
+
600
+ # Initialize the agent with OpenAI and our tools. We use a zero-shot-react-description agent.
601
+ agent_executor = initialize_agent(
602
+ tools=tool_list,
603
+ llm=llm,
604
+ agent=AgentType.CHAT_CONVERSATIONAL_REACT_DESCRIPTION,
605
+ memory=memory,
606
+ verbose=True,
607
+ handle_parsing_errors=True,
608
+ #max_iterations=10,
609
+ )
610
+
611
+ # ─── Streaming & Fallback ─────────────────────────────────────────────────────
612
+ # ─── Streaming helper ────────────────────────────────────────────────────────────
613
+ def run_stream(query: str, data_paths: List[str] = None):
614
+ """
615
+ Progressive token‐by‐token streaming from the agent.
616
+
617
+ Args:
618
+ query: The user’s natural-language question.
619
+ data_paths: List of file paths (DB_PATH, DOC_PATH, IMG_PATH, OTH_PATH).
620
+ """
621
+ # If no explicit list passed, rebuild from module globals
622
+ # if not data_paths:
623
+ # data_paths = [DB_PATH, DOC_PATH, IMG_PATH, OTH_PATH]
624
+ data_paths = [p for p in data_paths if p]
625
+ print(f"Data paths----------------->: {data_paths}")
626
+ # Re-inject each into the appropriate global (optional—keeps them current)
627
+ for path in data_paths:
628
+ ext = os.path.splitext(path)[1].lower()
629
+ if ext in {".png", ".jpg", ".jpeg", ".gif"}:
630
+ globals()['IMG_PATH'] = path
631
+ elif ext in {".pdf", ".txt", ".doc", ".docx"}:
632
+ globals()['DOC_PATH'] = path
633
+ elif ext in {".db", ".sqlite"}:
634
+ globals()['DB_PATH'] = path
635
+ else:
636
+ globals()['OTH_PATH'] = path
637
+
638
+ # Stream the agent response
639
+ hist = get_buffer_string(memory.chat_memory.messages)
640
+ print("Memory now contains:", memory.chat_memory.messages)
641
+ for chunk in agent_executor.stream({"input": query}):
642
+ text = chunk.get("text")
643
+ if text:
644
+ yield text
645
+
646
+ # # ─── Streaming & Fallback ─────────────────────────────────────────────────────
647
+ # def run_stream(query: str, data: str = None):
648
+ # """
649
+ # Progressive token‐by‐token streaming from the agent.
650
+
651
+ # Args:
652
+ # query: The user’s natural-language question.
653
+ # data: Path to a single uploaded file (image, document, or database).
654
+ # We will inspect its extension and set the appropriate config variable:
655
+ # .png/.jpg/.jpeg/.gif → IMG_PATH
656
+ # .pdf/.txt/.doc/.docx → DOC_PATH
657
+ # .db/.sqlite → DB_PATH
658
+ # others → OTH_PATH
659
+ # """
660
+ # global DB_PATH, DOC_PATH, IMG_PATH, OTH_PATH
661
+ # # 1) If data provided, dispatch into the right config variable
662
+ # if data:
663
+ # ext = os.path.splitext(data)[1].lower()
664
+ # if ext in {".png", ".jpg", ".jpeg", ".gif"}:
665
+ # IMG_PATH = data
666
+ # print(f"Image path set to: {IMG_PATH}")
667
+ # elif ext in {".pdf", ".txt", ".doc", ".docx"}:
668
+ # DOC_PATH = data
669
+ # print(f"Document path set to: {DOC_PATH}")
670
+ # elif ext in {".db", ".sqlite"}:
671
+ # DB_PATH = data
672
+ # print(f"Database path set to: {DB_PATH}")
673
+ # else:
674
+ # OTH_PATH = data
675
+ # print(f"Other file path set to: {OTH_PATH}")
676
+
677
+ # # 2) Stream the agent’s response
678
+ # for chunk in agent_executor.stream({"input": query}):
679
+ # text = chunk.get("text")
680
+ # if text:
681
+ # yield text
682
+
683
+ def run_full(query: str) -> str:
684
+ """
685
+ Fallback single‐shot answer (for pure-tool or final completeness).
686
+ """
687
+ return agent_executor.run(query)
688
+
689
+ # Expose for Flask
690
+ class AgentInterface:
691
+ def __init__(self, executor):
692
+ self.executor = executor
693
+ def run_stream(self, q):
694
+ return run_stream(q)
695
+ def run_full(self, q):
696
+ return run_full(q)
697
+
698
+ agent = AgentInterface(agent_executor)
699
+
700
+ __all__ = [
701
+ 'agent_executor', 'run_stream', 'run_full',
702
+ 'AgentInterface', 'GLOBAL_DB_PATH', 'rag_index_document'
703
+ ]
704
+
705
+ # ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
706
+ # ─────────────────────────────────────────────── Refresh Memory Session ───────────────────────────────────────────────────────────────────
707
+ # ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
708
+ # Refresh Memory
709
+ def refresh_memory():
710
+ memory.clear() # clear memory at start of each new session
711
+ memory.chat_memory.clear() # clear chat history
app.py ADDED
@@ -0,0 +1,337 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # app.py
2
+
3
+ import eventlet
4
+ eventlet.monkey_patch()
5
+
6
+ from flask import Flask, render_template, request, redirect, url_for, flash, session, send_from_directory
7
+ from flask_socketio import SocketIO
8
+ import traceback
9
+ import os
10
+ from werkzeug.utils import secure_filename
11
+ import json
12
+ import logging
13
+ import agent # your agent.py module
14
+ from agent import refresh_memory
15
+ from agent import run_stream
16
+ from typing import List, Dict
17
+ import markdown2
18
+ import re
19
+ import time
20
+
21
+
22
+ # ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
23
+ # ─────────────────────────────────────────────── Inialized VAR & FS ───────────────────────────────────────────────────────────────────
24
+ # ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
25
+ BASE_DIR = os.path.abspath(os.path.dirname(__file__))
26
+
27
+ UPLOAD_FOLDER = os.path.join(BASE_DIR, "uploads")
28
+ os.makedirs(UPLOAD_FOLDER, exist_ok=True)
29
+
30
+ CHAT_FOLDER = os.path.join(BASE_DIR, "chats")
31
+ os.makedirs(CHAT_FOLDER, exist_ok=True)
32
+
33
+ app = Flask(__name__, template_folder="templates")
34
+
35
+ # For storing the processed files
36
+ app.config["UPLOAD_FOLDER"] = UPLOAD_FOLDER
37
+ # For storing the Chat history and other files
38
+ app.config["CHAT_FOLDER"] = CHAT_FOLDER
39
+
40
+ app.secret_key = os.getenv("FLASK_SECRET", "supersecret")
41
+
42
+ # Use eventlet for async SocketIO
43
+ socketio = SocketIO(app, cors_allowed_origins="*", async_mode="eventlet")
44
+
45
+ # Allowed file extensions
46
+ ALLOWED_EXTENSIONS = {
47
+ ".db", ".sqlite", # SQLite databases
48
+ ".pdf", ".txt", ".doc", ".docx", # Documents
49
+ ".png", ".jpg", ".jpeg", ".gif" # Images
50
+ }
51
+
52
+ import config
53
+ DB_PATH = None # will be set when a .db is uploaded
54
+ DOC_PATH = None # will be set when a document is uploaded
55
+ IMG_PATH = None # will be set when an image is uploaded
56
+ OTH_PATH = None # will be set when an other file is uploaded
57
+
58
+ # import config
59
+ # IMG_PATH = "path/to/user_uploaded_file.jpg"
60
+ # DOC_PATH = "path/to/user_uploaded_file.pdf"
61
+ # DB_PATH = "path/to/user_uploaded_file.db"
62
+ # OTH_PATH = "path/to/user_uploaded_file.txt"
63
+
64
+ def allowed_file(filename: str) -> bool:
65
+ ext = os.path.splitext(filename.lower())[1]
66
+ return ext in ALLOWED_EXTENSIONS
67
+
68
+ #text cleaning function
69
+ def clean_html_chunk(text):
70
+ """
71
+ Removes outer <p>...</p> tags and trims extra backticks or 'json' words.
72
+ Similar to your 'format:' cleaning logic.
73
+ """
74
+ text = text.strip()
75
+
76
+ # Pattern to match single <p>...</p> wrapping
77
+ pattern = r'^<p>(.*?)</p>$'
78
+ match = re.match(pattern, text, re.DOTALL)
79
+ if match:
80
+ text = match.group(1).strip()
81
+
82
+ # Extra clean-up (optional, like your example)
83
+ text = text.strip('`').strip('json').strip()
84
+
85
+ return text
86
+
87
+
88
+ # ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
89
+ # ─────────────────────────────────────────────── AGENT Defination ────────────────────────────────────────────────────────────────────────────
90
+ # ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
91
+
92
+ def run_agent_thread(
93
+ prompt: str,
94
+ user_id: str,
95
+ chat_history: List[Dict], # ← new parameter
96
+ ):
97
+ """
98
+ Launches the agent in a background thread, streaming results back over SocketIO.
99
+ `data` is the uploaded file path (image, document, or DB) that will be injected
100
+ into config before the agent runs.
101
+ """
102
+ global DB_PATH, DOC_PATH, IMG_PATH, OTH_PATH # force re-initializing agent
103
+
104
+ # Build the list of all paths, skipping None or empty
105
+ data_paths = [DB_PATH, DOC_PATH, IMG_PATH, OTH_PATH]
106
+ data_paths = [p for p in data_paths if p]
107
+ print(f"Data paths----------------->: {data_paths}")
108
+ text_accum = ""
109
+ try:
110
+ # Stream using run_stream (which will also pick up the same globals)
111
+ for piece in run_stream(prompt, data_paths):
112
+ html_chunk = markdown2.markdown(piece, extras=["fenced-code-blocks", "tables", "strike", "task_list", "break-on-newline"])
113
+ print(f"HTML chunk: {html_chunk}") # Debugging output
114
+ html_chunk = clean_html_chunk(html_chunk)
115
+ text_accum += html_chunk # accumulate HTML
116
+ socketio.emit("final_stream", {"message": html_chunk})
117
+ print(f"Streaming chunk: {html_chunk}") # Debugging output
118
+ except Exception as e:
119
+ socketio.emit("error", {"message": f"Streaming error: {e}"})
120
+ traceback.print_exc()
121
+ return
122
+
123
+ # Fallback / finalize
124
+ try:
125
+ if not text_accum:
126
+ text_accum = markdown2.markdown(agent.agent.executor.run(prompt), extras=["fenced-code-blocks", "tables", "strike", "task_list", "break-on-newline"])
127
+ print(f"Final HTML chunk: {text_accum}") # Debugging output
128
+ text_accum = clean_html_chunk(text_accum)
129
+ print(f"Final text: {text_accum}") # Debugging output
130
+
131
+ socketio.emit("stream_complete", {"message": text_accum})
132
+ socketio.emit("final", {"message": text_accum})
133
+ chat_history.append({"user": prompt, "assistant": text_accum})
134
+
135
+ output_path = os.path.join(
136
+ app.config["CHAT_FOLDER"],
137
+ f"user_chat_no_{user_id}.json"
138
+ )
139
+ with open(output_path, "w", encoding="utf-8") as f:
140
+ json.dump(chat_history, f, ensure_ascii=False, indent=4)
141
+
142
+ except Exception as e:
143
+ socketio.emit("error", {"message": f"Final generation error: {e}"})
144
+ traceback.print_exc()
145
+
146
+ # ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
147
+ # ─────────────────────────────────────────────── Main Page ────────────────────────────────────────────────────────────────────────────────
148
+ # ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
149
+
150
+ @app.route("/")
151
+ def index():
152
+ if "user_id" not in session:
153
+ session["user_id"] = os.urandom(16).hex()
154
+ session["uploads"] = []
155
+ session["chat_history"] = []
156
+ refresh_memory()
157
+ else:
158
+ # Load previous chat history from JSON file if exists
159
+ user_id = session["user_id"]
160
+ chat_file = os.path.join(app.config.get("CHAT_FOLDER", "chat_history"), f"user_chat_no_{user_id}.json")
161
+ if os.path.exists(chat_file):
162
+ with open(chat_file, "r", encoding="utf-8") as f:
163
+ session["chat_history"] = json.load(f)
164
+
165
+ return render_template("index.html", chat_history=session.get("chat_history", []))
166
+
167
+ # ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
168
+ # ─────────────────────────────────────────────── Upload section ──────────────────────────────────────────────────────────────────────────
169
+ # ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
170
+
171
+ @app.route("/upload", methods=["GET", "POST"])
172
+ def upload():
173
+ global DB_PATH, DOC_PATH, IMG_PATH, OTH_PATH # force re-initializing agent
174
+
175
+ if request.method == "POST":
176
+ f = request.files.get("file")
177
+ if not f or f.filename == "":
178
+ flash("No file selected", "error")
179
+ return render_template("upload.html")
180
+
181
+ filename = secure_filename(f.filename)
182
+ if not allowed_file(filename):
183
+ flash("File type not supported", "error")
184
+ return render_template("upload.html")
185
+
186
+ ext = os.path.splitext(filename.lower())[1]
187
+
188
+ if ext in (".db", ".sqlite"):
189
+ save_path = os.path.join(app.config["UPLOAD_FOLDER"], "databases", filename)
190
+ os.makedirs(os.path.dirname(save_path), exist_ok=True)
191
+ global DB_PATH
192
+ DB_PATH = save_path
193
+ # DB_PATH = f"http://127.0.0.1:5000/uploads/databases/{filename}"
194
+ print(f"Database path: {save_path}")
195
+ f.save(save_path)
196
+ elif ext in (".pdf", ".txt", ".doc", ".docx"):
197
+ save_path = os.path.join(app.config["UPLOAD_FOLDER"], "documents", filename)
198
+ os.makedirs(os.path.dirname(save_path), exist_ok=True)
199
+ global DOC_PATH
200
+ DOC_PATH = save_path
201
+ # DOC_PATH = f"http://127.0.0.1:5000/uploads/documents/{filename}"
202
+ print(f"Document path: {save_path}")
203
+ f.save(save_path)
204
+ elif ext in (".png", ".jpg", ".jpeg", ".gif"):
205
+ save_path = os.path.join(app.config["UPLOAD_FOLDER"], "images", filename)
206
+ os.makedirs(os.path.dirname(save_path), exist_ok=True)
207
+ global IMG_PATH
208
+ IMG_PATH = save_path
209
+ # IMG_PATH = f"http://127.0.0.1:5000/uploads/images/{filename}"
210
+ print(f"Image path: {save_path}")
211
+ f.save(save_path)
212
+ else:
213
+ save_path = os.path.join(app.config["UPLOAD_FOLDER"], "others", filename)
214
+ os.makedirs(os.path.dirname(save_path), exist_ok=True)
215
+ global OTH_PATH
216
+ OTH_PATH = save_path
217
+ # OTH_PATH = f"http://127.0.0.1:5000/uploads/others/{filename}"
218
+ print(f"Other file path: {save_path}")
219
+ f.save(save_path)
220
+
221
+ #f.save(save_path)
222
+
223
+ # Add the uploaded file to the session
224
+ session["uploads"].append(filename)
225
+
226
+ # — Database files —
227
+ if ext in (".db", ".sqlite"):
228
+ DB_PATH = save_path
229
+ agent.GLOBAL_DB_PATH = DB_PATH
230
+ flash(f"Database uploaded and set: {filename}", "success")
231
+
232
+ # — Documents for RAG indexing —
233
+ elif ext in (".pdf", ".txt", ".doc", ".docx"):
234
+ agent.rag_index_document(save_path)
235
+ flash(f"Document indexed for RAG: {filename}", "success")
236
+
237
+ # — Images or other files —
238
+ else:
239
+ flash(f"File uploaded: {filename}", "success")
240
+
241
+ return redirect(url_for("index"))
242
+
243
+ # GET
244
+ return render_template("upload.html")
245
+
246
+ # ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
247
+ # ─────────────────────────────────────────────── Static Upload ───────────────────────────────────────────────────────────────────────────
248
+ # ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
249
+
250
+ @app.route('/uploads/databases/<filename>')
251
+ def serve_database(filename):
252
+ """
253
+ Serve an database file from the uploads/databases folder.
254
+ """
255
+ return send_from_directory(os.path.join(app.root_path, 'uploads', 'databases'), filename)
256
+
257
+ @app.route('/uploads/images/<filename>')
258
+ def serve_image(filename):
259
+ """
260
+ Serve an document file from the uploads/images folder.
261
+ """
262
+ return send_from_directory(os.path.join(app.root_path, 'uploads', 'images'), filename)
263
+
264
+ @app.route('/uploads/documents/<filename>')
265
+ def serve_document(filename):
266
+ """
267
+ Serve an image file from the uploads/documents folder.
268
+ """
269
+ return send_from_directory(os.path.join(app.root_path, 'uploads', 'documents'), filename)
270
+
271
+ @app.route('/uploads/others/<filename>')
272
+ def serve_other(filename):
273
+ """
274
+ Serve an other file from the uploads/others folder.
275
+ """
276
+ return send_from_directory(os.path.join(app.root_path, 'uploads', 'others'), filename)
277
+
278
+ # ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
279
+ # ─────────────────────────────────────────────── AGENT calling ───────────────────────────────────────────────────────────────────────────
280
+ # ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
281
+
282
+ @app.route("/generate", methods=["POST"])
283
+ def generate():
284
+ prompt = request.json.get("prompt", "").strip()
285
+ if not prompt:
286
+ return "No prompt provided", 400
287
+
288
+ socketio.start_background_task(
289
+ run_agent_thread,
290
+ prompt,
291
+ session["user_id"],
292
+ session["chat_history"]
293
+ # ← pass it here
294
+ )
295
+ return "OK", 200
296
+
297
+ # ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
298
+ # ─────────────────────────────────────────────── SESSION handling ───────────────────────────────────────────────────────────────────────
299
+ # ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
300
+
301
+ @app.route("/session-info")
302
+ def session_info():
303
+ # Endpoint to view session details (for debugging purposes)
304
+ return {
305
+ "user_id": session.get("user_id"),
306
+ "uploads": session.get("uploads", [])
307
+ }
308
+
309
+ # ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
310
+ # ─────────────────────────────────────────────── SESSION clearing ───────────────────────────────────────────────────────────────────────
311
+ # ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
312
+
313
+ @app.route("/clear_chat", methods=["POST"])
314
+ def clear_chat():
315
+ user_id = session.get("user_id")
316
+
317
+ # Remove saved JSON chat file
318
+ if user_id:
319
+ chat_file = os.path.join(app.config["CHAT_FOLDER"], f"user_chat_no_{user_id}.json")
320
+ if os.path.exists(chat_file):
321
+ os.remove(chat_file)
322
+
323
+ # Reset session
324
+ session.clear()
325
+
326
+ # Generate new session id and chat history
327
+ session["user_id"] = os.urandom(16).hex()
328
+ session["uploads"] = []
329
+ session["chat_history"] = []
330
+
331
+ # Refresh agent memory
332
+ refresh_memory()
333
+
334
+ return {"message": "Chat history and session cleared!"}, 200
335
+
336
+ if __name__ == "__main__":
337
+ socketio.run(app, debug=True, port=5000)
chats/user_chat_no_113a02f93587a6622aa86c130e31f025.json ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "assistant": "Mistral AI is a privately held company founded in April 2023 by Arthur Mensch, Guillaume Lample, and Timothée Lacroix. The ownership is distributed among its founders, investors, and possibly key employees.",
4
+ "user": "who is the owner of mistral ?"
5
+ },
6
+ {
7
+ "assistant": "The database is empty or does not contain any tables.",
8
+ "user": "can you get me details about the database ?"
9
+ },
10
+ {
11
+ "assistant": "The database contains the following tables with their respective CREATE statements: </p>\n\n<p>CREATE TABLE employees (id INTEGER PRIMARY KEY, name TEXT, department TEXT);<br />\nCREATE TABLE departments (id INTEGER PRIMARY KEY, name TEXT);<br />\nCREATE TABLE projects (id INTEGER PRIMARY KEY, name TEXT, employee<em>id INTEGER);<br />\nCREATE TABLE sqlite</em>sequence(name,seq);",
12
+ "user": "can you get me details about the database ?"
13
+ },
14
+ {
15
+ "user": "ok you get me the top can you format this into table ?",
16
+ "assistant": "<table>\n<thead>\n<tr>\n <th>id</th>\n <th>name</th>\n <th>salary</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n <td>1</td>\n <td>John Doe</td>\n <td>100000</td>\n</tr>\n<tr>\n <td>2</td>\n <td>Jane Smith</td>\n <td>90000</td>\n</tr>\n<tr>\n <td>3</td>\n <td>Bob Brown</td>\n <td>85000</td>\n</tr>\n<tr>\n <td>4</td>\n <td>Alice White</td>\n <td>80000</td>\n</tr>\n<tr>\n <td>5</td>\n <td>Mike Davis</td>\n <td>75000</td>\n</tr>\n</tbody>\n</table>"
17
+ }
18
+ ]
config.py ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import os
3
+
4
+ # config.py
5
+ DB_PATH = "your_db_path_here"
6
+ DOC_PATH = "your_doc_path_here"
7
+ IMG_PATH = "your_img_path_here"
8
+ OTH_PATH = "your_other_path_here"
requirements.txt ADDED
@@ -0,0 +1,134 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ accelerate
2
+ agent
3
+ aiohappyeyeballs
4
+ aiohttp
5
+ aiosignal
6
+ annotated-types
7
+ anyio
8
+ asttokens
9
+ attrs
10
+ beautifulsoup4
11
+ bidict
12
+ blinker
13
+ bs4
14
+ certifi
15
+ charset-normalizer
16
+ click
17
+ colorama
18
+ comm
19
+ dataclasses-json
20
+ debugpy
21
+ decorator
22
+ distro
23
+ dnspython
24
+ duckduckgo_search
25
+ eventlet
26
+ executing
27
+ faiss-cpu
28
+ filelock
29
+ Flask
30
+ Flask-SocketIO
31
+ frozenlist
32
+ fsspec
33
+ greenlet
34
+ groq
35
+ gunicorn
36
+ h11
37
+ httpcore
38
+ httpx
39
+ httpx-sse
40
+ huggingface-hub
41
+ idna
42
+ ipykernel
43
+ ipython
44
+ ipython_pygments_lexers
45
+ itsdangerous
46
+ jedi
47
+ Jinja2
48
+ joblib
49
+ jsonpatch
50
+ jsonpointer
51
+ jupyter_client
52
+ jupyter_core
53
+ langchain
54
+ langchain-community
55
+ langchain-core
56
+ langchain-groq
57
+ langchain-mistralai
58
+ langchain-text-splitters
59
+ langgraph
60
+ langgraph-checkpoint
61
+ langgraph-prebuilt
62
+ langgraph-sdk
63
+ langsmith
64
+ lxml
65
+ markdown2
66
+ MarkupSafe
67
+ marshmallow
68
+ matplotlib-inline
69
+ mpmath
70
+ multidict
71
+ mypy_extensions
72
+ nest-asyncio
73
+ networkx
74
+ numexpr
75
+ numpy
76
+ orjson
77
+ ormsgpack
78
+ packaging
79
+ parso
80
+ pillow
81
+ platformdirs
82
+ playwright
83
+ primp
84
+ prompt_toolkit
85
+ propcache
86
+ psutil
87
+ pure_eval
88
+ pydantic
89
+ pydantic-settings
90
+ pydantic_core
91
+ pyee
92
+ Pygments
93
+ PyMuPDF
94
+ pytesseract
95
+ python-dateutil
96
+ python-dotenv
97
+ python-engineio
98
+ python-socketio
99
+ pywin32
100
+ PyYAML
101
+ pyzmq
102
+ regex
103
+ requests
104
+ requests-toolbelt
105
+ safetensors
106
+ scikit-learn
107
+ scipy
108
+ sentence-transformers
109
+ setuptools
110
+ simple-websocket
111
+ six
112
+ sniffio
113
+ soupsieve
114
+ SQLAlchemy
115
+ stack-data
116
+ sympy
117
+ tenacity
118
+ threadpoolctl
119
+ tokenizers
120
+ torch
121
+ tornado
122
+ tqdm
123
+ traitlets
124
+ transformers
125
+ typing-inspect
126
+ typing-inspection
127
+ typing_extensions
128
+ urllib3
129
+ wcwidth
130
+ Werkzeug
131
+ wsproto
132
+ xxhash
133
+ yarl
134
+ zstandard
templates/index.html ADDED
@@ -0,0 +1,775 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <meta charset="UTF-8" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1">
7
+ <title>Agent Chat</title>
8
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.5.4/socket.io.js"></script>
9
+ <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
10
+ <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
11
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
12
+ <style>
13
+ :root {
14
+ --primary: #10a37f;
15
+ --primary-dark: #0d8a6a;
16
+ --bg-color: #343541;
17
+ --chat-bg: #444654;
18
+ --user-bg: #343541;
19
+ --text-color: #ececf1;
20
+ --text-secondary: #acacbe;
21
+ --border-color: #565869;
22
+ --log-bg: #2a2b32;
23
+ --error-color: #ef4146;
24
+ --warning-color: #f0b72f;
25
+ --panel-width: 300px;
26
+ /* Define panel width */
27
+ }
28
+
29
+ * {
30
+ box-sizing: border-box;
31
+ margin: 0;
32
+ padding: 0;
33
+ }
34
+
35
+ body {
36
+ background-color: var(--bg-color);
37
+ color: var(--text-color);
38
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
39
+ display: flex;
40
+ flex-direction: column;
41
+ height: 100vh;
42
+ overflow: hidden;
43
+ }
44
+
45
+ nav {
46
+ background-color: var(--user-bg);
47
+ padding: 12px 20px;
48
+ border-bottom: 1px solid var(--border-color);
49
+ display: flex;
50
+ justify-content: space-between;
51
+ align-items: center;
52
+ flex-shrink: 0;
53
+ /* Prevent nav from shrinking */
54
+ }
55
+
56
+ .nav-title {
57
+ font-size: 1.2rem;
58
+ font-weight: 600;
59
+ }
60
+
61
+ .nav-actions {
62
+ display: flex;
63
+ gap: 10px;
64
+ }
65
+
66
+ .btn {
67
+ background-color: var(--primary);
68
+ color: white;
69
+ border: none;
70
+ border-radius: 4px;
71
+ padding: 8px 12px;
72
+ font-size: 0.9rem;
73
+ cursor: pointer;
74
+ display: flex;
75
+ align-items: center;
76
+ gap: 6px;
77
+ transition: background-color 0.2s;
78
+ }
79
+
80
+ .btn:hover {
81
+ background-color: var(--primary-dark);
82
+ }
83
+
84
+ .btn-sm {
85
+ padding: 6px 10px;
86
+ font-size: 0.8rem;
87
+ }
88
+
89
+ .btn-icon {
90
+ padding: 8px;
91
+ border-radius: 4px;
92
+ background: transparent;
93
+ color: var(--text-color);
94
+ border: none;
95
+ /* Ensure no default button border */
96
+ cursor: pointer;
97
+ }
98
+
99
+ .btn-icon:hover {
100
+ background-color: rgba(255, 255, 255, 0.1);
101
+ }
102
+
103
+ main {
104
+ flex: 1;
105
+ display: flex;
106
+ flex-direction: column;
107
+ overflow: hidden;
108
+ }
109
+
110
+ .chat-container {
111
+ flex: 1;
112
+ overflow-y: auto;
113
+ padding: 20px 0;
114
+ }
115
+
116
+ .message {
117
+ padding: 20px;
118
+ display: flex;
119
+ max-width: 900px;
120
+ margin: 0 auto;
121
+ gap: 20px;
122
+ }
123
+
124
+ .message-user {
125
+ background-color: var(--user-bg);
126
+ }
127
+
128
+ .message-agent {
129
+ background-color: var(--chat-bg);
130
+ }
131
+
132
+ .avatar {
133
+ width: 36px;
134
+ height: 36px;
135
+ border-radius: 4px;
136
+ display: flex;
137
+ align-items: center;
138
+ justify-content: center;
139
+ flex-shrink: 0;
140
+ }
141
+
142
+ .avatar-user {
143
+ background-color: var(--primary);
144
+ }
145
+
146
+ .avatar-agent {
147
+ background-color: #5436da;
148
+ }
149
+
150
+ .message-content {
151
+ flex: 1;
152
+ line-height: 1.5;
153
+ padding-top: 4px;
154
+ }
155
+
156
+ .input-container {
157
+ padding: 20px;
158
+ border-top: 1px solid var(--border-color);
159
+ background-color: var(--user-bg);
160
+ position: relative;
161
+ flex-shrink: 0;
162
+ /* Prevent input area from shrinking */
163
+ }
164
+
165
+ .input-wrapper {
166
+ max-width: 900px;
167
+ margin: 0 auto;
168
+ position: relative;
169
+ }
170
+
171
+ .input-actions {
172
+ position: absolute;
173
+ right: 12px;
174
+ bottom: 12px;
175
+ display: flex;
176
+ gap: 8px;
177
+ z-index: 2;
178
+ }
179
+
180
+ textarea {
181
+ width: 100%;
182
+ min-height: 60px;
183
+ max-height: 200px;
184
+ padding: 12px 50px 12px 16px;
185
+ border-radius: 8px;
186
+ border: 1px solid var(--border-color);
187
+ background-color: var(--chat-bg);
188
+ color: var(--text-color);
189
+ resize: none;
190
+ font-size: 1rem;
191
+ line-height: 1.5;
192
+ }
193
+
194
+ textarea:focus {
195
+ outline: none;
196
+ border-color: var(--primary);
197
+ box-shadow: 0 0 0 1px var(--primary);
198
+ }
199
+
200
+ .typing-indicator {
201
+ display: flex;
202
+ gap: 4px;
203
+ padding: 0 20px 10px;
204
+ color: var(--text-secondary);
205
+ font-size: 0.9rem;
206
+ flex-shrink: 0;
207
+ /* Prevent indicator from shrinking */
208
+ }
209
+
210
+ .typing-dots {
211
+ display: flex;
212
+ gap: 2px;
213
+ align-items: flex-end;
214
+ }
215
+
216
+ .typing-dot {
217
+ width: 6px;
218
+ height: 6px;
219
+ background-color: var(--text-secondary);
220
+ border-radius: 50%;
221
+ animation: typingAnimation 1.4s infinite ease-in-out;
222
+ }
223
+
224
+ .typing-dot:nth-child(1) {
225
+ animation-delay: 0s;
226
+ }
227
+
228
+ .typing-dot:nth-child(2) {
229
+ animation-delay: 0.2s;
230
+ }
231
+
232
+ .typing-dot:nth-child(3) {
233
+ animation-delay: 0.4s;
234
+ }
235
+
236
+ @keyframes typingAnimation {
237
+
238
+ 0%,
239
+ 60%,
240
+ 100% {
241
+ transform: translateY(0);
242
+ }
243
+
244
+ 30% {
245
+ transform: translateY(-4px);
246
+ }
247
+ }
248
+
249
+ footer {
250
+ background-color: var(--user-bg);
251
+ padding: 12px 20px;
252
+ border-top: 1px solid var(--border-color);
253
+ font-size: 0.8rem;
254
+ color: var(--text-secondary);
255
+ text-align: center;
256
+ flex-shrink: 0;
257
+ /* Prevent footer from shrinking */
258
+ }
259
+
260
+ /* Side Panel Styles */
261
+ .side-panel {
262
+ position: fixed;
263
+ top: 0;
264
+ right: calc(-1 * var(--panel-width));
265
+ /* Initially hidden */
266
+ width: var(--panel-width);
267
+ height: 100vh;
268
+ background-color: var(--log-bg);
269
+ border-left: 1px solid var(--border-color);
270
+ box-shadow: -4px 0 12px rgba(0, 0, 0, 0.15);
271
+ transition: right 0.3s ease-in-out;
272
+ display: flex;
273
+ flex-direction: column;
274
+ z-index: 999;
275
+ /* Ensure it's above other content */
276
+ }
277
+
278
+ .side-panel.open {
279
+ right: 0;
280
+ /* Slide in */
281
+ }
282
+
283
+ .side-panel-header {
284
+ padding: 12px 20px;
285
+ background-color: var(--user-bg);
286
+ border-bottom: 1px solid var(--border-color);
287
+ display: flex;
288
+ justify-content: space-between;
289
+ align-items: center;
290
+ flex-shrink: 0;
291
+ }
292
+
293
+ .side-panel-header h3 {
294
+ font-size: 1rem;
295
+ margin: 0;
296
+ }
297
+
298
+ .side-panel-content {
299
+ flex: 1;
300
+ overflow-y: auto;
301
+ padding: 12px 20px;
302
+ font-size: 0.85rem;
303
+ color: var(--text-secondary);
304
+ }
305
+
306
+ .log-entry {
307
+ margin-bottom: 6px;
308
+ display: flex;
309
+ gap: 10px;
310
+ }
311
+
312
+ .log-timestamp {
313
+ color: var(--text-secondary);
314
+ flex-shrink: 0;
315
+ }
316
+
317
+ .log-message {
318
+ flex: 1;
319
+ word-break: break-word;
320
+ /* Prevent long words from overflowing */
321
+ }
322
+
323
+ .log-error {
324
+ color: var(--error-color);
325
+ }
326
+
327
+ .log-warning {
328
+ color: var(--warning-color);
329
+ }
330
+
331
+
332
+ .alert {
333
+ position: fixed;
334
+ top: 20px;
335
+ left: 50%;
336
+ transform: translateX(-50%);
337
+ padding: 12px 20px;
338
+ border-radius: 8px;
339
+ background-color: var(--error-color);
340
+ color: white;
341
+ z-index: 1000;
342
+ display: flex;
343
+ align-items: center;
344
+ gap: 8px;
345
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
346
+ animation: slideIn 0.3s ease-out;
347
+ }
348
+
349
+ .alert-success {
350
+ background-color: var(--primary);
351
+ }
352
+
353
+ .alert-warning {
354
+ background-color: var(--warning-color);
355
+ }
356
+
357
+ @keyframes slideIn {
358
+ from {
359
+ top: -50px;
360
+ opacity: 0;
361
+ }
362
+
363
+ to {
364
+ top: 20px;
365
+ opacity: 1;
366
+ }
367
+ }
368
+
369
+ /* Enhanced Markdown Styling */
370
+ .markdown-content {
371
+ line-height: 1.6;
372
+ }
373
+
374
+ .markdown-content h1,
375
+ .markdown-content h2,
376
+ .markdown-content h3,
377
+ .markdown-content h4,
378
+ .markdown-content h5,
379
+ .markdown-content h6 {
380
+ margin: 1.2em 0 0.6em;
381
+ font-weight: 600;
382
+ line-height: 1.3;
383
+ }
384
+
385
+ .markdown-content h1 {
386
+ font-size: 1.5em;
387
+ border-bottom: 1px solid var(--border-color);
388
+ padding-bottom: 0.3em;
389
+ }
390
+
391
+ .markdown-content h2 {
392
+ font-size: 1.3em;
393
+ border-bottom: 1px solid var(--border-color);
394
+ padding-bottom: 0.3em;
395
+ }
396
+
397
+ .markdown-content h3 {
398
+ font-size: 1.1em;
399
+ }
400
+
401
+ .markdown-content h4 {
402
+ font-size: 1em;
403
+ }
404
+
405
+ .markdown-content h5 {
406
+ font-size: 0.9em;
407
+ }
408
+
409
+ .markdown-content h6 {
410
+ font-size: 0.8em;
411
+ color: var(--text-secondary);
412
+ }
413
+
414
+ .markdown-content p {
415
+ margin: 0.8em 0;
416
+ }
417
+
418
+ .markdown-content pre {
419
+ background-color: rgba(0, 0, 0, 0.2);
420
+ padding: 1em;
421
+ border-radius: 6px;
422
+ overflow-x: auto;
423
+ margin: 1em 0;
424
+ border-left: 3px solid var(--primary);
425
+ }
426
+
427
+ .markdown-content code {
428
+ font-family: 'Courier New', Courier, monospace;
429
+ font-size: 0.9em;
430
+ background-color: rgba(27, 31, 35, 0.15);
431
+ padding: 0.2em 0.4em;
432
+ border-radius: 3px;
433
+ }
434
+
435
+ .markdown-content pre code {
436
+ background: transparent;
437
+ padding: 0;
438
+ border-radius: 0;
439
+ }
440
+
441
+ .markdown-content blockquote {
442
+ border-left: 4px solid var(--primary);
443
+ padding: 0 1em;
444
+ color: var(--text-secondary);
445
+ margin: 1em 0;
446
+ font-style: italic;
447
+ }
448
+
449
+ .markdown-content ul,
450
+ .markdown-content ol {
451
+ margin: 0.8em 0;
452
+ padding-left: 2em;
453
+ }
454
+
455
+ .markdown-content li {
456
+ margin: 0.4em 0;
457
+ }
458
+
459
+ .markdown-content table {
460
+ border-collapse: collapse;
461
+ width: 100%;
462
+ margin: 1em 0;
463
+ border: 1px solid var(--border-color);
464
+ }
465
+
466
+ .markdown-content th,
467
+ .markdown-content td {
468
+ padding: 0.5em 1em;
469
+ border: 1px solid var(--border-color);
470
+ }
471
+
472
+ .markdown-content th {
473
+ background-color: rgba(0, 0, 0, 0.1);
474
+ font-weight: 600;
475
+ }
476
+
477
+ .markdown-content tr:nth-child(even) {
478
+ background-color: rgba(0, 0, 0, 0.05);
479
+ }
480
+
481
+ .markdown-content a {
482
+ color: var(--primary);
483
+ text-decoration: none;
484
+ }
485
+
486
+ .markdown-content a:hover {
487
+ text-decoration: underline;
488
+ }
489
+
490
+ .markdown-content img {
491
+ max-width: 100%;
492
+ border-radius: 4px;
493
+ margin: 1em 0;
494
+ }
495
+
496
+ .markdown-content hr {
497
+ border: none;
498
+ border-top: 1px solid var(--border-color);
499
+ margin: 1.5em 0;
500
+ }
501
+ </style>
502
+ </head>
503
+
504
+ <body>
505
+ <nav>
506
+ <div class="nav-title">Agent Chat</div>
507
+ <div class="nav-actions">
508
+ <button class="btn" id="new-chat-btn">
509
+ <i class="fas fa-plus"></i> New Chat
510
+ </button>
511
+ <!-- Button to open logs panel -->
512
+ <button class="btn-icon" id="open-logs-btn" title="Show Logs">
513
+ <i class="fas fa-list-alt"></i>
514
+ </button>
515
+ </div>
516
+ </nav>
517
+
518
+ <main>
519
+ <div class="chat-container" id="chat">
520
+ <!-- Messages will be inserted here -->
521
+ {% for chat in chat_history %}
522
+ <div class="message message-user">
523
+ <div class="avatar avatar-user"><i class="fas fa-user"></i></div>
524
+ <div class="message-content"><div class="markdown-content">{{ chat.user }}</div></div>
525
+ </div>
526
+ <div class="message message-agent">
527
+ <div class="avatar avatar-agent"><i class="fas fa-robot"></i></div>
528
+ <div class="message-content"><div class="markdown-content">{{ chat.assistant | safe }}</div></div>
529
+ </div>
530
+ {% endfor %}
531
+ </div>
532
+
533
+ <div class="typing-indicator" id="typing-indicator" style="display: none;">
534
+ <div class="typing-dots">
535
+ <div class="typing-dot"></div>
536
+ <div class="typing-dot"></div>
537
+ <div class="typing-dot"></div>
538
+ </div>
539
+ <span>Agent is typing...</span>
540
+ </div>
541
+
542
+ <div class="input-container">
543
+ <div class="input-wrapper">
544
+ <textarea id="prompt" rows="1" placeholder="Message Agent..." autofocus></textarea>
545
+ <div class="input-actions">
546
+ <button class="btn-icon" id="upload-btn" title="Upload Database">
547
+ <i class="fas fa-upload"></i>
548
+ </button>
549
+ <button class="btn-icon" id="send-btn" title="Send message">
550
+ <i class="fas fa-paper-plane"></i>
551
+ </button>
552
+ </div>
553
+ </div>
554
+ </div>
555
+
556
+ </main>
557
+
558
+ <footer>
559
+ <div>Agent Chat v1.0 · © 2023</div>
560
+ </footer>
561
+
562
+ <!-- Side Panel for Logs -->
563
+ <div id="side-panel" class="side-panel">
564
+ <div class="side-panel-header">
565
+ <h3>Logs</h3>
566
+ <button class="btn-icon" id="close-panel-btn" title="Hide Logs">
567
+ <i class="fas fa-times"></i>
568
+ </button>
569
+ </div>
570
+ <div class="side-panel-content" id="logs">
571
+ <!-- Logs will be inserted here -->
572
+ </div>
573
+ </div>
574
+
575
+ <div id="flash-message" class="alert" style="display: none;"></div>
576
+
577
+ <script>
578
+ const socket = io();
579
+ const chatContainer = document.getElementById("chat");
580
+ const logsContainer = document.getElementById("logs"); // This is now the content div inside the panel
581
+ const sendButton = document.getElementById("send-btn");
582
+ const uploadButton = document.getElementById("upload-btn");
583
+ const promptTextarea = document.getElementById("prompt");
584
+ const flashMessage = document.getElementById('flash-message');
585
+ const typingIndicator = document.getElementById('typing-indicator');
586
+
587
+ // New elements for side panel
588
+ const sidePanel = document.getElementById('side-panel');
589
+ const openLogsBtn = document.getElementById('open-logs-btn');
590
+ const closePanelBtn = document.getElementById('close-panel-btn');
591
+
592
+
593
+ // Auto-resize textarea
594
+ promptTextarea.addEventListener('input', function () {
595
+ this.style.height = 'auto';
596
+ this.style.height = (this.scrollHeight) + 'px';
597
+ });
598
+
599
+ // Handle Enter key (Shift+Enter for new line)
600
+ promptTextarea.addEventListener('keydown', function (e) {
601
+ if (e.key === 'Enter' && !e.shiftKey) {
602
+ e.preventDefault();
603
+ sendMessage();
604
+ }
605
+ });
606
+
607
+ // Send button click handler
608
+ sendButton.addEventListener('click', sendMessage);
609
+
610
+ // Upload button click handler
611
+ uploadButton.addEventListener('click', () => {
612
+ window.location.href = '/upload';
613
+ });
614
+
615
+ // Side panel toggle handlers
616
+ openLogsBtn.addEventListener('click', () => {
617
+ sidePanel.classList.add('open');
618
+ });
619
+
620
+ closePanelBtn.addEventListener('click', () => {
621
+ sidePanel.classList.remove('open');
622
+ });
623
+
624
+
625
+ function sendMessage() {
626
+ const prompt = promptTextarea.value.trim();
627
+ if (!prompt) return;
628
+
629
+ addMessage("user", prompt);
630
+ promptTextarea.value = '';
631
+ promptTextarea.style.height = 'auto';
632
+ typingIndicator.style.display = 'flex';
633
+
634
+ fetch("/generate", {
635
+ method: "POST",
636
+ headers: { "Content-Type": "application/json" },
637
+ body: JSON.stringify({ prompt: prompt })
638
+ });
639
+ }
640
+
641
+ function addMessage(sender, text) {
642
+ const messageDiv = document.createElement("div");
643
+ messageDiv.classList.add("message", `message-${sender}`);
644
+
645
+ const avatarDiv = document.createElement("div");
646
+ avatarDiv.classList.add("avatar", `avatar-${sender}`);
647
+ avatarDiv.innerHTML = sender === 'user' ?
648
+ '<i class="fas fa-user"></i>' :
649
+ '<i class="fas fa-robot"></i>';
650
+
651
+ const contentDiv = document.createElement("div");
652
+ contentDiv.classList.add("message-content");
653
+ contentDiv.innerHTML = `<div class="markdown-content">${text}</div>`;
654
+
655
+ messageDiv.appendChild(avatarDiv);
656
+ messageDiv.appendChild(contentDiv);
657
+ chatContainer.appendChild(messageDiv);
658
+ chatContainer.scrollTop = chatContainer.scrollHeight;
659
+ }
660
+
661
+ function addLogMessage(text, type = 'info') {
662
+ const now = new Date();
663
+ const timestamp = now.toLocaleTimeString();
664
+
665
+ const logDiv = document.createElement("div");
666
+ logDiv.classList.add("log-entry");
667
+
668
+ const timeDiv = document.createElement("div");
669
+ timeDiv.classList.add("log-timestamp");
670
+ timeDiv.textContent = timestamp;
671
+
672
+ const messageDiv = document.createElement("div");
673
+ messageDiv.classList.add("log-message");
674
+ if (type !== 'info') messageDiv.classList.add(`log-${type}`);
675
+ messageDiv.textContent = text;
676
+
677
+ logDiv.appendChild(timeDiv);
678
+ logDiv.appendChild(messageDiv);
679
+ logsContainer.appendChild(logDiv); // Append to the logs content div
680
+ logsContainer.scrollTop = logsContainer.scrollHeight; // Scroll logs panel
681
+ }
682
+
683
+ function showFlashMessage(message, type = 'error') {
684
+ flashMessage.textContent = message;
685
+ flashMessage.className = `alert alert-${type}`;
686
+ flashMessage.style.display = 'flex';
687
+
688
+ setTimeout(() => {
689
+ flashMessage.style.display = 'none';
690
+ }, 3000);
691
+ }
692
+
693
+ // Socket.io handlers
694
+ let agentMessageDiv = null;
695
+
696
+ socket.on("final_stream", (data) => {
697
+ if (!agentMessageDiv) {
698
+ agentMessageDiv = document.createElement("div");
699
+ agentMessageDiv.classList.add("message", "message-agent");
700
+
701
+ const avatarDiv = document.createElement("div");
702
+ avatarDiv.classList.add("avatar", "avatar-agent");
703
+ avatarDiv.innerHTML = '<i class="fas fa-robot"></i>';
704
+
705
+ const contentDiv = document.createElement("div");
706
+ contentDiv.classList.add("message-content");
707
+ contentDiv.id = "agent-message-content";
708
+
709
+ agentMessageDiv.appendChild(avatarDiv);
710
+ agentMessageDiv.appendChild(contentDiv);
711
+ chatContainer.appendChild(agentMessageDiv);
712
+ }
713
+
714
+ const contentDiv = document.getElementById("agent-message-content");
715
+ // const markdownHTML = marked.parse(data.message);
716
+ // contentDiv.innerHTML = `<div class="markdown-content">${markdownHTML}</div>`;
717
+ const html = marked.parse(data.message);
718
+ // contentDiv.innerHTML = `<div class="markdown-content">${html}</div>`;
719
+ contentDiv.innerHTML = `<div class="markdown-content">${html}</div>`;
720
+ chatContainer.scrollTop = chatContainer.scrollHeight;
721
+ });
722
+
723
+ socket.on("final", (data) => {
724
+ typingIndicator.style.display = 'none';
725
+
726
+ if (agentMessageDiv) {
727
+ const contentDiv = document.getElementById("agent-message-content");
728
+ // const markdownHTML = marked.parse(data.message);
729
+ // contentDiv.innerHTML = `<div class="markdown-content">${markdownHTML}</div>`;
730
+ const html = marked.parse(data.message);
731
+ // contentDiv.innerHTML = `<div class="markdown-content">${html}</div>`;
732
+ contentDiv.innerHTML = `<div class="markdown-content">${html}</div>`;
733
+ agentMessageDiv = null;
734
+ } else {
735
+ addMessage("agent", data.message);
736
+ }
737
+ });
738
+
739
+ socket.on("log", (data) => {
740
+ addLogMessage(data.message, data.type || 'info');
741
+ });
742
+
743
+ socket.on("error", (data) => {
744
+ showFlashMessage(data.message, 'error');
745
+ typingIndicator.style.display = 'none';
746
+ });
747
+
748
+ // Handle flash messages from server
749
+ if (flashMessage) {
750
+ setTimeout(() => {
751
+ flashMessage.style.display = 'none';
752
+ }, 3000);
753
+ }
754
+
755
+ // Handle new chat button click
756
+ // This button will clear the chat and reload the page
757
+ document.getElementById('new-chat-btn').addEventListener('click', () => {
758
+ fetch('/clear_chat', { method: 'POST' })
759
+ .then(response => {
760
+ if (response.ok) {
761
+ location.reload(); // Reload the page to clear the chat
762
+ } else {
763
+ alert('Failed to clear chat.');
764
+ }
765
+ })
766
+ .catch(error => {
767
+ console.error('Error:', error);
768
+ alert('An error occurred while clearing the chat.');
769
+ });
770
+ });
771
+
772
+ </script>
773
+ </body>
774
+
775
+ </html>
templates/index2.html ADDED
@@ -0,0 +1,289 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>Agent Chat - Idea Generator</title>
7
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.5.4/socket.io.js"></script>
8
+ <style>
9
+ /* Overall dark theme */
10
+ body {
11
+ background-color: #1a1a2e;
12
+ color: #eaeaea;
13
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
14
+ margin: 0;
15
+ padding: 0;
16
+ }
17
+ .container {
18
+ max-width: 800px;
19
+ margin: 0 auto;
20
+ padding: 20px;
21
+ display: flex;
22
+ flex-direction: column;
23
+ gap: 20px;
24
+ }
25
+ h1 {
26
+ text-align: center;
27
+ margin-bottom: 10px;
28
+ color: #eaeaea;
29
+ }
30
+ /* Wrapper for chat area, logs, and input */
31
+ .chat-section {
32
+ display: flex;
33
+ flex-direction: column;
34
+ border: 1px solid #0f3460;
35
+ border-radius: 8px;
36
+ overflow: hidden;
37
+ }
38
+ /* Chat container */
39
+ .chat-container {
40
+ background-color: #16213e;
41
+ padding: 20px;
42
+ height: 400px;
43
+ overflow-y: auto;
44
+ }
45
+ /* Logs container with a flash (fade-in) effect */
46
+ .logs-container {
47
+ background-color: #0f3460;
48
+ padding: 10px 20px;
49
+ height: 80px;
50
+ overflow-y: auto;
51
+ }
52
+ .bubble.log {
53
+ background-color: #444;
54
+ color: #ccc;
55
+ font-size: 0.85rem;
56
+ border-radius: 5px;
57
+ padding: 5px 10px;
58
+ margin: 2px 0;
59
+ animation: flash 0.5s ease-in-out;
60
+ }
61
+ @keyframes flash {
62
+ 0% { opacity: 0; }
63
+ 100% { opacity: 1; }
64
+ }
65
+ /* Chat message styles */
66
+ .message {
67
+ margin-bottom: 10px;
68
+ display: flex;
69
+ align-items: flex-start;
70
+ }
71
+ .message.user {
72
+ justify-content: flex-end;
73
+ }
74
+ .message.agent {
75
+ justify-content: flex-start;
76
+ }
77
+ .bubble {
78
+ max-width: 70%;
79
+ padding: 10px 15px;
80
+ border-radius: 15px;
81
+ line-height: 1.4;
82
+ word-wrap: break-word;
83
+ }
84
+ .bubble.user {
85
+ background-color: #007bff;
86
+ color: #fff;
87
+ border-bottom-right-radius: 0;
88
+ }
89
+ .bubble.agent {
90
+ background-color: #2d3a55;
91
+ color: #fff;
92
+ border-bottom-left-radius: 0;
93
+ position: relative;
94
+ }
95
+ /* Input area styles */
96
+ .input-area {
97
+ display: flex;
98
+ flex-direction: column;
99
+ gap: 10px;
100
+ padding: 10px; /* Reduced padding */
101
+ background-color: #16213e;
102
+ box-sizing: border-box; /* Ensure padding counts toward total width/height */
103
+ }
104
+
105
+ .input-area input,
106
+ .input-area textarea {
107
+ width: 100%;
108
+ padding: 10px;
109
+ border-radius: 5px;
110
+ border: none;
111
+ resize: none;
112
+ font-size: 1rem;
113
+ box-sizing: border-box; /* Prevents overflow when combined with padding */
114
+ }
115
+
116
+ .input-area button {
117
+ padding: 10px 20px;
118
+ border-radius: 5px;
119
+ border: none;
120
+ background-color: #007bff;
121
+ color: #fff;
122
+ cursor: pointer;
123
+ align-self: flex-end;
124
+ }
125
+
126
+ .input-area button:hover {
127
+ background-color: #0056b3;
128
+ }
129
+ /* Loader spinner styling */
130
+ .loader {
131
+ border: 3px solid #f3f3f3;
132
+ border-top: 3px solid #007bff;
133
+ border-radius: 50%;
134
+ width: 16px;
135
+ height: 16px;
136
+ animation: spin 1s linear infinite;
137
+ margin-right: 10px;
138
+ display: inline-block;
139
+ vertical-align: middle;
140
+ }
141
+ @keyframes spin {
142
+ 0% { transform: rotate(0deg); }
143
+ 100% { transform: rotate(360deg); }
144
+ }
145
+ </style>
146
+ </head>
147
+ <body>
148
+ <div class="container">
149
+ <h1>Agent Chat - Idea Generator</h1>
150
+ <div class="chat-section">
151
+ <!-- Chat conversation container -->
152
+ <div class="chat-container" id="chat">
153
+ <!-- Conversation messages will appear here -->
154
+ </div>
155
+ <!-- Logs container -->
156
+ <div class="logs-container" id="logs">
157
+ <!-- Log messages will appear here -->
158
+ </div>
159
+ <!-- Input area -->
160
+ <div class="input-area">
161
+ <input type="text" id="title" placeholder="Title of Idea (optional)" />
162
+ <textarea id="roughIdea" rows="2" placeholder="Rough Idea (optional)"></textarea>
163
+ <input type="text" id="category" placeholder="Category (optional)" />
164
+ <textarea id="query" rows="3" placeholder="User Query (mandatory)"></textarea>
165
+ <button id="send">Send</button>
166
+ </div>
167
+ </div>
168
+ </div>
169
+
170
+ <script>
171
+ const socket = io();
172
+ const chatContainer = document.getElementById("chat");
173
+ const logsContainer = document.getElementById("logs");
174
+ const sendButton = document.getElementById("send");
175
+
176
+ // Function to add a chat message bubble
177
+ function addMessage(sender, text) {
178
+ const messageDiv = document.createElement("div");
179
+ messageDiv.classList.add("message", sender);
180
+ const bubbleDiv = document.createElement("div");
181
+ bubbleDiv.classList.add("bubble", sender);
182
+ bubbleDiv.textContent = text;
183
+ messageDiv.appendChild(bubbleDiv);
184
+ chatContainer.appendChild(messageDiv);
185
+ chatContainer.scrollTop = chatContainer.scrollHeight;
186
+ }
187
+
188
+ // Function to add a log message bubble with flash effect
189
+ function addLogMessage(text) {
190
+ const logDiv = document.createElement("div");
191
+ logDiv.classList.add("bubble", "log");
192
+ logDiv.textContent = text;
193
+ logsContainer.appendChild(logDiv);
194
+ logsContainer.scrollTop = logsContainer.scrollHeight;
195
+ }
196
+
197
+ // Variable to hold the agent's message bubble (with loader)
198
+ let agentMessageBubble = null;
199
+
200
+ // When the user clicks Send
201
+ sendButton.addEventListener("click", () => {
202
+ const title = document.getElementById("title").value.trim();
203
+ const roughIdea = document.getElementById("roughIdea").value.trim();
204
+ const category = document.getElementById("category").value.trim();
205
+ const query = document.getElementById("query").value.trim();
206
+
207
+ if (!query) {
208
+ alert("User Query is required.");
209
+ return;
210
+ }
211
+
212
+ // Display the user's message in chat.
213
+ addMessage("user", query);
214
+
215
+ // Clear input fields.
216
+ document.getElementById("title").value = "";
217
+ document.getElementById("roughIdea").value = "";
218
+ document.getElementById("category").value = "";
219
+ document.getElementById("query").value = "";
220
+
221
+ // Create an agent bubble with a loader to indicate AI is processing.
222
+ if (!agentMessageBubble) {
223
+ const messageDiv = document.createElement("div");
224
+ messageDiv.classList.add("message", "agent");
225
+ agentMessageBubble = document.createElement("div");
226
+ agentMessageBubble.classList.add("bubble", "agent");
227
+
228
+ // Create and add loader element.
229
+ const loader = document.createElement("div");
230
+ loader.classList.add("loader");
231
+ agentMessageBubble.appendChild(loader);
232
+
233
+ messageDiv.appendChild(agentMessageBubble);
234
+ chatContainer.appendChild(messageDiv);
235
+ chatContainer.scrollTop = chatContainer.scrollHeight;
236
+ }
237
+
238
+ // Send the collected fields to the server.
239
+ fetch("/generate", {
240
+ method: "POST",
241
+ headers: { "Content-Type": "application/json" },
242
+ body: JSON.stringify({
243
+ title: title,
244
+ rough_idea: roughIdea,
245
+ category: category,
246
+ query: query
247
+ })
248
+ });
249
+ });
250
+
251
+ // Handle streaming tokens from the agent.
252
+ socket.on("final_stream", (data) => {
253
+ if (agentMessageBubble) {
254
+ // Remove loader if still present.
255
+ const loader = agentMessageBubble.querySelector(".loader");
256
+ if (loader) {
257
+ loader.remove();
258
+ // Clear any leftover content.
259
+ agentMessageBubble.textContent = "";
260
+ }
261
+ // Append the streaming token.
262
+ agentMessageBubble.textContent += data.message;
263
+ chatContainer.scrollTop = chatContainer.scrollHeight;
264
+ } else {
265
+ // Fallback: create a new agent message if needed.
266
+ addMessage("agent", data.message);
267
+ }
268
+ });
269
+
270
+ // When the final answer is complete.
271
+ // When the final answer is complete.
272
+ socket.on("final", (data) => {
273
+ // If there's no existing agent bubble, create one.
274
+ if (!agentMessageBubble) {
275
+ addMessage("agent", "");
276
+ }
277
+ // Use innerHTML to preserve any HTML tags or Markdown-converted HTML
278
+ agentMessageBubble.innerHTML = data.message;
279
+ // Reset agentMessageBubble for next time
280
+ agentMessageBubble = null;
281
+ });
282
+
283
+ // Append log messages as they arrive.
284
+ socket.on("log", (data) => {
285
+ addLogMessage(data.message);
286
+ });
287
+ </script>
288
+ </body>
289
+ </html>
templates/upload.html ADDED
@@ -0,0 +1,332 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>Upload Database</title>
7
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
8
+ <style>
9
+ :root {
10
+ --primary: #10a37f;
11
+ --primary-dark: #0d8a6a;
12
+ --bg-color: #343541;
13
+ --chat-bg: #444654;
14
+ --user-bg: #343541;
15
+ --text-color: #ececf1;
16
+ --text-secondary: #acacbe;
17
+ --border-color: #565869;
18
+ --error-color: #ef4146;
19
+ --warning-color: #f0b72f;
20
+ }
21
+
22
+ * {
23
+ box-sizing: border-box;
24
+ margin: 0;
25
+ padding: 0;
26
+ }
27
+
28
+ body {
29
+ background-color: var(--bg-color);
30
+ color: var(--text-color);
31
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
32
+ display: flex;
33
+ flex-direction: column;
34
+ min-height: 100vh;
35
+ }
36
+
37
+ nav {
38
+ background-color: var(--user-bg);
39
+ padding: 12px 20px;
40
+ border-bottom: 1px solid var(--border-color);
41
+ display: flex;
42
+ justify-content: space-between;
43
+ align-items: center;
44
+ }
45
+
46
+ .nav-title {
47
+ font-size: 1.2rem;
48
+ font-weight: 600;
49
+ }
50
+
51
+ .btn {
52
+ background-color: var(--primary);
53
+ color: white;
54
+ border: none;
55
+ border-radius: 4px;
56
+ padding: 8px 12px;
57
+ font-size: 0.9rem;
58
+ cursor: pointer;
59
+ display: flex;
60
+ align-items: center;
61
+ gap: 6px;
62
+ transition: background-color 0.2s;
63
+ text-decoration: none;
64
+ }
65
+
66
+ .btn:hover {
67
+ background-color: var(--primary-dark);
68
+ }
69
+
70
+ main {
71
+ flex: 1;
72
+ display: flex;
73
+ flex-direction: column;
74
+ align-items: center;
75
+ padding: 40px 20px;
76
+ }
77
+
78
+ .upload-container {
79
+ max-width: 600px;
80
+ width: 100%;
81
+ background-color: var(--chat-bg);
82
+ border-radius: 8px;
83
+ padding: 30px;
84
+ box-shadow: 0 4px 12px rgba(0,0,0,0.1);
85
+ }
86
+
87
+ .upload-header {
88
+ margin-bottom: 24px;
89
+ text-align: center;
90
+ }
91
+
92
+ .upload-header h1 {
93
+ font-size: 1.5rem;
94
+ margin-bottom: 8px;
95
+ }
96
+
97
+ .upload-header p {
98
+ color: var(--text-secondary);
99
+ font-size: 0.9rem;
100
+ }
101
+
102
+ .upload-icon {
103
+ font-size: 2.5rem;
104
+ color: var(--primary);
105
+ margin-bottom: 16px;
106
+ }
107
+
108
+ .form-group {
109
+ margin-bottom: 20px;
110
+ }
111
+
112
+ .form-group label {
113
+ display: block;
114
+ margin-bottom: 8px;
115
+ font-size: 0.9rem;
116
+ color: var(--text-secondary);
117
+ }
118
+
119
+ .file-input-wrapper {
120
+ position: relative;
121
+ margin-bottom: 20px;
122
+ }
123
+
124
+ .file-input-label {
125
+ display: flex;
126
+ flex-direction: column;
127
+ align-items: center;
128
+ justify-content: center;
129
+ padding: 40px 20px;
130
+ border: 2px dashed var(--border-color);
131
+ border-radius: 8px;
132
+ cursor: pointer;
133
+ transition: all 0.2s;
134
+ text-align: center;
135
+ }
136
+
137
+ .file-input-label:hover {
138
+ border-color: var(--primary);
139
+ background-color: rgba(16, 163, 127, 0.05);
140
+ }
141
+
142
+ .file-input-label i {
143
+ font-size: 2rem;
144
+ color: var(--primary);
145
+ margin-bottom: 12px;
146
+ }
147
+
148
+ .file-input-label span {
149
+ font-size: 0.9rem;
150
+ color: var(--text-secondary);
151
+ }
152
+
153
+ .file-input-label strong {
154
+ color: var(--primary);
155
+ font-weight: 600;
156
+ }
157
+
158
+ .file-input {
159
+ position: absolute;
160
+ width: 0.1px;
161
+ height: 0.1px;
162
+ opacity: 0;
163
+ overflow: hidden;
164
+ z-index: -1;
165
+ }
166
+
167
+ .file-name {
168
+ margin-top: 8px;
169
+ font-size: 0.9rem;
170
+ color: var(--text-color);
171
+ display: none;
172
+ }
173
+
174
+ .submit-btn {
175
+ width: 100%;
176
+ padding: 12px;
177
+ font-size: 1rem;
178
+ font-weight: 500;
179
+ }
180
+
181
+ .submit-btn:disabled {
182
+ background-color: var(--border-color);
183
+ cursor: not-allowed;
184
+ }
185
+
186
+ .alert {
187
+ position: fixed;
188
+ top: 20px;
189
+ left: 50%;
190
+ transform: translateX(-50%);
191
+ padding: 12px 20px;
192
+ border-radius: 8px;
193
+ background-color: var(--error-color);
194
+ color: white;
195
+ z-index: 1000;
196
+ display: flex;
197
+ align-items: center;
198
+ gap: 8px;
199
+ box-shadow: 0 4px 12px rgba(0,0,0,0.15);
200
+ animation: slideIn 0.3s ease-out;
201
+ }
202
+
203
+ .alert-success {
204
+ background-color: var(--primary);
205
+ }
206
+
207
+ .alert-warning {
208
+ background-color: var(--warning-color);
209
+ }
210
+
211
+ @keyframes slideIn {
212
+ from { top: -50px; opacity: 0; }
213
+ to { top: 20px; opacity: 1; }
214
+ }
215
+
216
+ footer {
217
+ background-color: var(--user-bg);
218
+ padding: 12px 20px;
219
+ border-top: 1px solid var(--border-color);
220
+ font-size: 0.8rem;
221
+ color: var(--text-secondary);
222
+ text-align: center;
223
+ }
224
+ </style>
225
+ </head>
226
+ <body>
227
+ <nav>
228
+ <div class="nav-title">Agent Chat</div>
229
+ <a href="/" class="btn">
230
+ <i class="fas fa-arrow-left"></i> Back to Chat
231
+ </a>
232
+ </nav>
233
+
234
+ <main>
235
+ <div class="upload-container">
236
+ <div class="upload-header">
237
+ <div class="upload-icon">
238
+ <i class="fas fa-upload"></i>
239
+ </div>
240
+ <h1>Upload File</h1>
241
+ <p>Select file to upload </p>
242
+ </div>
243
+
244
+ <form method="POST" action="/upload" enctype="multipart/form-data" id="uploadForm">
245
+ <div class="form-group">
246
+ <div class="file-input-wrapper">
247
+ <input type="file" id="dbFile" name="file" accept=".db" class="file-input" required>
248
+ <label for="dbFile" class="file-input-label" id="fileInputLabel">
249
+ <i class="fas fa-file-arrow-up"></i>
250
+ <span>Drag & drop your file here or <strong>browse</strong></span>
251
+ <span class="file-name" id="fileName"></span>
252
+ </label>
253
+ </div>
254
+ </div>
255
+ <div class="form-group">
256
+ <button type="submit" class="btn submit-btn" id="submitBtn" disabled>
257
+ <i class="fas fa-upload"></i> Upload files
258
+ </button>
259
+ </div>
260
+ </form>
261
+ </div>
262
+ </main>
263
+
264
+ <footer>
265
+ <div>Agent Chat v1.0 · © 2023</div>
266
+ </footer>
267
+
268
+ <div id="flash-message" class="alert" style="display: none;"></div>
269
+
270
+ <script>
271
+ const fileInput = document.getElementById('dbFile');
272
+ const fileInputLabel = document.getElementById('fileInputLabel');
273
+ const fileName = document.getElementById('fileName');
274
+ const submitBtn = document.getElementById('submitBtn');
275
+ const uploadForm = document.getElementById('uploadForm');
276
+ const flashMessage = document.getElementById('flash-message');
277
+
278
+ // Handle file selection
279
+ fileInput.addEventListener('change', function() {
280
+ if (this.files.length > 0) {
281
+ const file = this.files[0];
282
+ fileName.textContent = file.name;
283
+ fileName.style.display = 'block';
284
+ submitBtn.disabled = false;
285
+
286
+ // Update label text
287
+ fileInputLabel.querySelector('span').textContent = 'Selected file:';
288
+ fileInputLabel.querySelector('i').className = 'fas fa-check-circle';
289
+ fileInputLabel.querySelector('i').style.color = 'var(--primary)';
290
+ } else {
291
+ fileName.style.display = 'none';
292
+ submitBtn.disabled = true;
293
+
294
+ // Reset label text
295
+ fileInputLabel.querySelector('span').textContent = 'Drag & drop your file here or <strong>browse</strong>';
296
+ fileInputLabel.querySelector('i').className = 'fas fa-cloud-upload-alt';
297
+ }
298
+ });
299
+
300
+ // Handle drag and drop
301
+ fileInputLabel.addEventListener('dragover', (e) => {
302
+ e.preventDefault();
303
+ fileInputLabel.style.borderColor = 'var(--primary)';
304
+ fileInputLabel.style.backgroundColor = 'rgba(16, 163, 127, 0.1)';
305
+ });
306
+
307
+ fileInputLabel.addEventListener('dragleave', () => {
308
+ fileInputLabel.style.borderColor = 'var(--border-color)';
309
+ fileInputLabel.style.backgroundColor = 'transparent';
310
+ });
311
+
312
+ fileInputLabel.addEventListener('drop', (e) => {
313
+ e.preventDefault();
314
+ fileInputLabel.style.borderColor = 'var(--border-color)';
315
+ fileInputLabel.style.backgroundColor = 'transparent';
316
+
317
+ if (e.dataTransfer.files.length) {
318
+ fileInput.files = e.dataTransfer.files;
319
+ const event = new Event('change');
320
+ fileInput.dispatchEvent(event);
321
+ }
322
+ });
323
+
324
+ // Handle flash messages
325
+ if (flashMessage) {
326
+ setTimeout(() => {
327
+ flashMessage.style.display = 'none';
328
+ }, 3000);
329
+ }
330
+ </script>
331
+ </body>
332
+ </html>
uploads/databases/employee.db ADDED
File without changes
uploads/documents/LLM_based_QA_chatbot_builder.pdf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:633e305a9a7fcdc69769e77d96db1fa715ab387c9f8745ebe77fa75590772848
3
+ size 1887485
uploads/images/how-ML-pipeline-Work.png ADDED