KUNAL SHAW commited on
Commit
d7ccaae
·
1 Parent(s): bc1532c

feat: Add HuggingFace Spaces support and LLM response generation

Browse files

- Added YAML frontmatter for HuggingFace Spaces deployment
- Added LLM response generation using Groq (not just retrieval)
- Added LangGraph workflow with retrieve -> generate pipeline
- Added USER_AGENT configuration to suppress warnings
- Added Dockerfile and docker-compose.yml for containerized deployment
- Added .streamlit config for theming and settings
- Updated requirements.txt for HuggingFace Spaces compatibility
- Improved secret loading for both local and cloud deployments

Files changed (8) hide show
  1. .gitignore +46 -2
  2. .streamlit/config.toml +23 -0
  3. .streamlit/secrets.toml.example +21 -0
  4. Dockerfile +46 -0
  5. README.md +14 -0
  6. app.py +157 -35
  7. docker-compose.yml +36 -0
  8. requirements.txt +22 -22
.gitignore CHANGED
@@ -6,17 +6,61 @@ __pycache__/
6
  env/
7
  venv/
8
  .venv/
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
 
10
  # Streamlit / IDE
11
- .streamlit/
12
  .vscode/
13
 
14
  # Secrets and credentials
15
  .env
16
  *.json
 
17
  cs23b1039@iiitr.ac.in-token.json
18
  *.sqlite
19
  .DS_Store
20
 
21
  # Logs
22
- *.log
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
  env/
7
  venv/
8
  .venv/
9
+ .Python
10
+ build/
11
+ develop-eggs/
12
+ dist/
13
+ downloads/
14
+ eggs/
15
+ .eggs/
16
+ lib/
17
+ lib64/
18
+ parts/
19
+ sdist/
20
+ var/
21
+ wheels/
22
+ *.egg-info/
23
+ .installed.cfg
24
+ *.egg
25
 
26
  # Streamlit / IDE
27
+ .streamlit/secrets.toml
28
  .vscode/
29
 
30
  # Secrets and credentials
31
  .env
32
  *.json
33
+ !package.json
34
  cs23b1039@iiitr.ac.in-token.json
35
  *.sqlite
36
  .DS_Store
37
 
38
  # Logs
39
+ *.log
40
+
41
+ # Testing
42
+ .pytest_cache/
43
+ .coverage
44
+ htmlcov/
45
+
46
+ # Docker
47
+ .docker/
48
+
49
+ # Jupyter Notebooks
50
+ .ipynb_checkpoints/
51
+ *.ipynb
52
+
53
+ # Model files (large)
54
+ *.h5
55
+ *.pkl
56
+ *.pt
57
+ *.pth
58
+
59
+ # OS generated
60
+ .DS_Store
61
+ .DS_Store?
62
+ ._*
63
+ .Spotlight-V100
64
+ .Trashes
65
+ ehthumbs.db
66
+ Thumbs.db
.streamlit/config.toml ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [theme]
2
+ primaryColor = "#667eea"
3
+ backgroundColor = "#ffffff"
4
+ secondaryBackgroundColor = "#f0f2f6"
5
+ textColor = "#262730"
6
+ font = "sans serif"
7
+
8
+ [server]
9
+ headless = true
10
+ port = 8501
11
+ enableXsrfProtection = true
12
+ maxUploadSize = 50
13
+
14
+ [browser]
15
+ gatherUsageStats = false
16
+ serverAddress = "localhost"
17
+
18
+ [runner]
19
+ magicEnabled = true
20
+
21
+ [client]
22
+ showErrorDetails = true
23
+ toolbarMode = "auto"
.streamlit/secrets.toml.example ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ==================== Streamlit Secrets Template ====================
2
+ # IMSKOS - Intelligent Multi-Source Knowledge Orchestration System
3
+ #
4
+ # For Streamlit Cloud deployment:
5
+ # 1. Go to your app settings on Streamlit Cloud
6
+ # 2. Navigate to "Secrets" section
7
+ # 3. Copy the contents below and fill in your actual values
8
+ #
9
+ # For local development:
10
+ # 1. Create a file at .streamlit/secrets.toml
11
+ # 2. Copy the contents below and fill in your actual values
12
+ # ====================================================================
13
+
14
+ # DataStax Astra DB Configuration
15
+ # Get these from: https://astra.datastax.com
16
+ ASTRA_DB_APPLICATION_TOKEN = "AstraCS:your_token_here"
17
+ ASTRA_DB_ID = "your_database_id_here"
18
+
19
+ # Groq API Configuration
20
+ # Get your API key from: https://console.groq.com
21
+ GROQ_API_KEY = "your_groq_api_key_here"
Dockerfile ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ==================== IMSKOS Dockerfile ====================
2
+ # Intelligent Multi-Source Knowledge Orchestration System
3
+ # Production-ready container configuration
4
+
5
+ FROM python:3.10-slim
6
+
7
+ # Set working directory
8
+ WORKDIR /app
9
+
10
+ # Set environment variables
11
+ ENV PYTHONDONTWRITEBYTECODE=1 \
12
+ PYTHONUNBUFFERED=1 \
13
+ STREAMLIT_SERVER_PORT=8501 \
14
+ STREAMLIT_SERVER_ADDRESS=0.0.0.0
15
+
16
+ # Install system dependencies
17
+ RUN apt-get update && apt-get install -y --no-install-recommends \
18
+ build-essential \
19
+ curl \
20
+ git \
21
+ && rm -rf /var/lib/apt/lists/*
22
+
23
+ # Copy requirements first for better caching
24
+ COPY requirements.txt .
25
+
26
+ # Install Python dependencies
27
+ RUN pip install --no-cache-dir --upgrade pip && \
28
+ pip install --no-cache-dir -r requirements.txt
29
+
30
+ # Copy application code
31
+ COPY . .
32
+
33
+ # Create non-root user for security
34
+ RUN useradd -m -u 1000 appuser && \
35
+ chown -R appuser:appuser /app
36
+ USER appuser
37
+
38
+ # Expose Streamlit port
39
+ EXPOSE 8501
40
+
41
+ # Health check
42
+ HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
43
+ CMD curl --fail http://localhost:8501/_stcore/health || exit 1
44
+
45
+ # Run Streamlit
46
+ CMD ["streamlit", "run", "app.py", "--server.port=8501", "--server.address=0.0.0.0", "--server.headless=true"]
README.md CHANGED
@@ -1,9 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  # 🧠 IMSKOS - Intelligent Multi-Source Knowledge Orchestration System
2
 
3
  [![Python 3.9+](https://img.shields.io/badge/python-3.9+-blue.svg)](https://www.python.org/downloads/)
4
  [![LangChain](https://img.shields.io/badge/LangChain-🦜-green.svg)](https://langchain.com/)
5
  [![LangGraph](https://img.shields.io/badge/LangGraph-🔗-orange.svg)](https://github.com/langchain-ai/langgraph)
6
  [![Streamlit](https://img.shields.io/badge/Streamlit-🎈-red.svg)](https://streamlit.io/)
 
7
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
8
 
9
  > **Enterprise-Grade Agentic RAG Framework with Adaptive Query Routing**
 
1
+ ---
2
+ title: IMSKOS - Intelligent Knowledge Orchestration
3
+ emoji: 🧠
4
+ colorFrom: purple
5
+ colorTo: blue
6
+ sdk: streamlit
7
+ sdk_version: 1.31.0
8
+ app_file: app.py
9
+ pinned: false
10
+ license: mit
11
+ short_description: Advanced Agentic RAG with LangGraph & Adaptive Query Routing
12
+ ---
13
+
14
  # 🧠 IMSKOS - Intelligent Multi-Source Knowledge Orchestration System
15
 
16
  [![Python 3.9+](https://img.shields.io/badge/python-3.9+-blue.svg)](https://www.python.org/downloads/)
17
  [![LangChain](https://img.shields.io/badge/LangChain-🦜-green.svg)](https://langchain.com/)
18
  [![LangGraph](https://img.shields.io/badge/LangGraph-🔗-orange.svg)](https://github.com/langchain-ai/langgraph)
19
  [![Streamlit](https://img.shields.io/badge/Streamlit-🎈-red.svg)](https://streamlit.io/)
20
+ [![HuggingFace](https://img.shields.io/badge/🤗%20Hugging%20Face-Spaces-yellow.svg)](https://huggingface.co/spaces)
21
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
22
 
23
  > **Enterprise-Grade Agentic RAG Framework with Adaptive Query Routing**
app.py CHANGED
@@ -13,7 +13,15 @@ An enterprise-grade, production-ready intelligent query routing system that leve
13
 
14
  import streamlit as st
15
  import os
16
- from typing import List, Dict, Any
 
 
 
 
 
 
 
 
17
 
18
  # Compatibility shim for different typing.ForwardRef._evaluate signatures
19
  # ------------------------------------------------------------
@@ -64,6 +72,8 @@ from langchain_community.tools import WikipediaQueryRun
64
  from langchain_core.prompts import ChatPromptTemplate
65
  from langchain_groq import ChatGroq
66
  from langchain_core.documents import Document
 
 
67
  from langgraph.graph import END, StateGraph, START
68
  from typing_extensions import TypedDict
69
  from pydantic import BaseModel, Field
@@ -71,6 +81,7 @@ from typing import Literal
71
  import time
72
  import json
73
  from datetime import datetime
 
74
 
75
  # Page Configuration
76
  st.set_page_config(
@@ -153,18 +164,44 @@ class Config:
153
 
154
  @staticmethod
155
  def load_env_variables():
156
- """Load and validate environment variables"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
157
  required_vars = {
158
- "ASTRA_DB_APPLICATION_TOKEN": os.getenv("ASTRA_DB_APPLICATION_TOKEN"),
159
- "ASTRA_DB_ID": os.getenv("ASTRA_DB_ID"),
160
- "GROQ_API_KEY": os.getenv("GROQ_API_KEY")
161
  }
162
 
163
  missing_vars = [key for key, value in required_vars.items() if not value]
164
 
165
  if missing_vars:
166
  st.error(f"⚠️ Missing environment variables: {', '.join(missing_vars)}")
167
- st.info("Please set these in your .env file or Streamlit secrets")
 
 
 
 
 
 
 
 
 
168
  st.stop()
169
 
170
  return required_vars
@@ -192,6 +229,7 @@ class GraphState(TypedDict):
192
  question: str
193
  generation: str
194
  documents: List[str]
 
195
 
196
  # ==================== Core System Classes ====================
197
 
@@ -264,6 +302,7 @@ class IntelligentRouter:
264
  self.groq_api_key = groq_api_key
265
  self.llm = None
266
  self.question_router = None
 
267
 
268
  def initialize(self):
269
  """Set up LLM and routing chain"""
@@ -294,18 +333,50 @@ Be precise in your routing decisions."""
294
  ])
295
 
296
  self.question_router = route_prompt | structured_llm
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
297
 
298
  def route(self, question: str) -> str:
299
  """Route question to appropriate data source"""
300
  result = self.question_router.invoke({"question": question})
301
  return result.datasource
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
302
 
303
  class AdaptiveRAGWorkflow:
304
  """LangGraph-based adaptive retrieval workflow"""
305
 
306
- def __init__(self, vector_store, question_router):
307
  self.vector_store = vector_store
308
- self.question_router = question_router
309
  self.retriever = vector_store.as_retriever(search_kwargs={"k": 4})
310
  self.wiki = self._setup_wikipedia()
311
  self.workflow = None
@@ -314,8 +385,8 @@ class AdaptiveRAGWorkflow:
314
  def _setup_wikipedia(self):
315
  """Initialize Wikipedia search tool"""
316
  api_wrapper = WikipediaAPIWrapper(
317
- top_k_results=1,
318
- doc_content_chars_max=500
319
  )
320
  return WikipediaQueryRun(api_wrapper=api_wrapper)
321
 
@@ -323,19 +394,37 @@ class AdaptiveRAGWorkflow:
323
  """Retrieve from vector store"""
324
  question = state["question"]
325
  documents = self.retriever.invoke(question)
326
- return {"documents": documents, "question": question}
327
 
328
  def wiki_search(self, state: Dict) -> Dict:
329
  """Search Wikipedia"""
330
  question = state["question"]
331
- docs = self.wiki.invoke({"query": question})
332
- wiki_results = Document(page_content=docs)
333
- return {"documents": wiki_results, "question": question}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
334
 
335
  def route_question(self, state: Dict) -> str:
336
  """Route based on question type"""
337
  question = state["question"]
338
- source = self.question_router.route(question)
339
 
340
  if source == "wiki_search":
341
  return "wiki_search"
@@ -349,8 +438,9 @@ class AdaptiveRAGWorkflow:
349
  # Add nodes
350
  workflow.add_node("wiki_search", self.wiki_search)
351
  workflow.add_node("retrieve", self.retrieve)
 
352
 
353
- # Add conditional edges
354
  workflow.add_conditional_edges(
355
  START,
356
  self.route_question,
@@ -360,8 +450,12 @@ class AdaptiveRAGWorkflow:
360
  },
361
  )
362
 
363
- workflow.add_edge("retrieve", END)
364
- workflow.add_edge("wiki_search", END)
 
 
 
 
365
 
366
  self.app = workflow.compile()
367
 
@@ -372,15 +466,25 @@ class AdaptiveRAGWorkflow:
372
  result = {
373
  "route": None,
374
  "documents": [],
 
375
  "execution_time": 0
376
  }
377
 
378
  start_time = time.time()
379
 
380
- for output in self.app.stream(inputs):
381
- for key, value in output.items():
382
- result["route"] = key
383
- result["documents"] = value.get("documents", [])
 
 
 
 
 
 
 
 
 
384
 
385
  result["execution_time"] = time.time() - start_time
386
 
@@ -615,9 +719,9 @@ def render_query_tab():
615
 
616
  # Routing information
617
  route = result["route"]
618
- route_class = "route-vector" if route == "retrieve" else "route-wiki"
619
- route_emoji = "🗄️" if route == "retrieve" else "📖"
620
- route_name = "Vector Store" if route == "retrieve" else "Wikipedia"
621
 
622
  col1, col2, col3 = st.columns(3)
623
  with col1:
@@ -629,22 +733,37 @@ def render_query_tab():
629
  with col2:
630
  st.metric("⚡ Execution Time", f"{result['execution_time']:.2f}s")
631
  with col3:
632
- st.metric("📄 Documents", len(result['documents']) if isinstance(result['documents'], list) else 1)
 
 
 
 
 
 
 
 
 
 
 
633
 
634
- # Display documents
635
- st.markdown("### 📄 Retrieved Information")
636
 
637
  documents = result['documents']
638
- if isinstance(documents, list):
639
  for i, doc in enumerate(documents[:5], 1):
640
- with st.expander(f"📌 Document {i}", expanded=(i == 1)):
641
- st.markdown(doc.page_content)
 
 
 
642
 
643
- if advanced_mode and hasattr(doc, 'metadata'):
644
  st.markdown("**Metadata:**")
645
  st.json(doc.metadata)
646
- else:
647
- st.markdown(documents.page_content)
 
648
 
649
  # Store query history
650
  if 'query_history' not in st.session_state:
@@ -654,11 +773,14 @@ def render_query_tab():
654
  "query": query,
655
  "route": route_name,
656
  "timestamp": datetime.now().strftime("%H:%M:%S"),
657
- "execution_time": result['execution_time']
 
658
  })
659
 
660
  except Exception as e:
661
  st.error(f"❌ Query execution failed: {str(e)}")
 
 
662
 
663
  def render_analytics_tab():
664
  """Render system analytics and monitoring"""
 
13
 
14
  import streamlit as st
15
  import os
16
+ from typing import List, Dict, Any, Optional
17
+ from dotenv import load_dotenv
18
+
19
+ # Load environment variables from .env file
20
+ load_dotenv()
21
+
22
+ # Set USER_AGENT to suppress warnings from web loaders
23
+ if not os.getenv("USER_AGENT"):
24
+ os.environ["USER_AGENT"] = "IMSKOS/1.0 (Intelligent Multi-Source Knowledge Orchestration System)"
25
 
26
  # Compatibility shim for different typing.ForwardRef._evaluate signatures
27
  # ------------------------------------------------------------
 
72
  from langchain_core.prompts import ChatPromptTemplate
73
  from langchain_groq import ChatGroq
74
  from langchain_core.documents import Document
75
+ from langchain_core.output_parsers import StrOutputParser
76
+ from langchain_core.runnables import RunnablePassthrough
77
  from langgraph.graph import END, StateGraph, START
78
  from typing_extensions import TypedDict
79
  from pydantic import BaseModel, Field
 
81
  import time
82
  import json
83
  from datetime import datetime
84
+ import traceback
85
 
86
  # Page Configuration
87
  st.set_page_config(
 
164
 
165
  @staticmethod
166
  def load_env_variables():
167
+ """Load and validate environment variables from multiple sources
168
+
169
+ Priority order:
170
+ 1. Streamlit secrets (for Streamlit Cloud / HuggingFace Spaces)
171
+ 2. Environment variables (for local development / Docker)
172
+ """
173
+
174
+ def get_secret(key: str) -> Optional[str]:
175
+ """Get secret from Streamlit secrets or environment variables"""
176
+ # First check Streamlit secrets (works on HuggingFace Spaces)
177
+ try:
178
+ if hasattr(st, 'secrets') and key in st.secrets:
179
+ return st.secrets[key]
180
+ except Exception:
181
+ pass
182
+ # Fall back to environment variables
183
+ return os.getenv(key)
184
+
185
  required_vars = {
186
+ "ASTRA_DB_APPLICATION_TOKEN": get_secret("ASTRA_DB_APPLICATION_TOKEN"),
187
+ "ASTRA_DB_ID": get_secret("ASTRA_DB_ID"),
188
+ "GROQ_API_KEY": get_secret("GROQ_API_KEY")
189
  }
190
 
191
  missing_vars = [key for key, value in required_vars.items() if not value]
192
 
193
  if missing_vars:
194
  st.error(f"⚠️ Missing environment variables: {', '.join(missing_vars)}")
195
+ st.info("""
196
+ **Setup Instructions:**
197
+ 1. **Local Development:** Create a `.env` file with your credentials
198
+ 2. **Streamlit Cloud:** Add secrets in the app settings
199
+
200
+ Required variables:
201
+ - `ASTRA_DB_APPLICATION_TOKEN` - Get from [DataStax Astra](https://astra.datastax.com)
202
+ - `ASTRA_DB_ID` - Your Astra DB database ID
203
+ - `GROQ_API_KEY` - Get from [Groq Console](https://console.groq.com)
204
+ """)
205
  st.stop()
206
 
207
  return required_vars
 
229
  question: str
230
  generation: str
231
  documents: List[str]
232
+ route: str
233
 
234
  # ==================== Core System Classes ====================
235
 
 
302
  self.groq_api_key = groq_api_key
303
  self.llm = None
304
  self.question_router = None
305
+ self.generation_chain = None
306
 
307
  def initialize(self):
308
  """Set up LLM and routing chain"""
 
333
  ])
334
 
335
  self.question_router = route_prompt | structured_llm
336
+
337
+ # Set up generation chain for synthesizing answers
338
+ generation_prompt = ChatPromptTemplate.from_messages([
339
+ ("system", """You are a helpful AI assistant specialized in providing accurate, informative answers.
340
+
341
+ Use the following retrieved context to answer the user's question.
342
+ If the context doesn't contain relevant information, say so and provide general guidance.
343
+ Be concise but comprehensive. Use bullet points for clarity when appropriate.
344
+
345
+ Context:
346
+ {context}"""),
347
+ ("human", "{question}")
348
+ ])
349
+
350
+ self.generation_chain = generation_prompt | self.llm | StrOutputParser()
351
 
352
  def route(self, question: str) -> str:
353
  """Route question to appropriate data source"""
354
  result = self.question_router.invoke({"question": question})
355
  return result.datasource
356
+
357
+ def generate_response(self, question: str, documents: List[Document]) -> str:
358
+ """Generate a coherent response from retrieved documents"""
359
+ # Format documents into context string
360
+ if isinstance(documents, list):
361
+ context = "\n\n".join([
362
+ f"Document {i+1}:\n{doc.page_content}"
363
+ for i, doc in enumerate(documents[:5])
364
+ ])
365
+ else:
366
+ context = documents.page_content if hasattr(documents, 'page_content') else str(documents)
367
+
368
+ response = self.generation_chain.invoke({
369
+ "context": context,
370
+ "question": question
371
+ })
372
+ return response
373
 
374
  class AdaptiveRAGWorkflow:
375
  """LangGraph-based adaptive retrieval workflow"""
376
 
377
+ def __init__(self, vector_store, router: IntelligentRouter):
378
  self.vector_store = vector_store
379
+ self.router = router
380
  self.retriever = vector_store.as_retriever(search_kwargs={"k": 4})
381
  self.wiki = self._setup_wikipedia()
382
  self.workflow = None
 
385
  def _setup_wikipedia(self):
386
  """Initialize Wikipedia search tool"""
387
  api_wrapper = WikipediaAPIWrapper(
388
+ top_k_results=2,
389
+ doc_content_chars_max=1000
390
  )
391
  return WikipediaQueryRun(api_wrapper=api_wrapper)
392
 
 
394
  """Retrieve from vector store"""
395
  question = state["question"]
396
  documents = self.retriever.invoke(question)
397
+ return {"documents": documents, "question": question, "route": "vectorstore"}
398
 
399
  def wiki_search(self, state: Dict) -> Dict:
400
  """Search Wikipedia"""
401
  question = state["question"]
402
+ try:
403
+ docs = self.wiki.invoke({"query": question})
404
+ wiki_results = Document(page_content=docs)
405
+ except Exception as e:
406
+ wiki_results = Document(page_content=f"Wikipedia search returned no results for this query. Error: {str(e)}")
407
+ return {"documents": [wiki_results], "question": question, "route": "wikipedia"}
408
+
409
+ def generate(self, state: Dict) -> Dict:
410
+ """Generate response from retrieved documents"""
411
+ question = state["question"]
412
+ documents = state["documents"]
413
+
414
+ # Use the router's generation chain to create a response
415
+ generation = self.router.generate_response(question, documents)
416
+
417
+ return {
418
+ "question": question,
419
+ "documents": documents,
420
+ "generation": generation,
421
+ "route": state.get("route", "unknown")
422
+ }
423
 
424
  def route_question(self, state: Dict) -> str:
425
  """Route based on question type"""
426
  question = state["question"]
427
+ source = self.router.route(question)
428
 
429
  if source == "wiki_search":
430
  return "wiki_search"
 
438
  # Add nodes
439
  workflow.add_node("wiki_search", self.wiki_search)
440
  workflow.add_node("retrieve", self.retrieve)
441
+ workflow.add_node("generate", self.generate)
442
 
443
+ # Add conditional edges from START
444
  workflow.add_conditional_edges(
445
  START,
446
  self.route_question,
 
450
  },
451
  )
452
 
453
+ # Both retrieval paths lead to generation
454
+ workflow.add_edge("retrieve", "generate")
455
+ workflow.add_edge("wiki_search", "generate")
456
+
457
+ # Generation leads to END
458
+ workflow.add_edge("generate", END)
459
 
460
  self.app = workflow.compile()
461
 
 
466
  result = {
467
  "route": None,
468
  "documents": [],
469
+ "generation": "",
470
  "execution_time": 0
471
  }
472
 
473
  start_time = time.time()
474
 
475
+ try:
476
+ for output in self.app.stream(inputs):
477
+ for key, value in output.items():
478
+ if key == "generate":
479
+ result["generation"] = value.get("generation", "")
480
+ result["route"] = value.get("route", "unknown")
481
+ result["documents"] = value.get("documents", [])
482
+ elif key in ["retrieve", "wiki_search"]:
483
+ result["route"] = value.get("route", key)
484
+ result["documents"] = value.get("documents", [])
485
+ except Exception as e:
486
+ result["generation"] = f"Error executing query: {str(e)}"
487
+ result["route"] = "error"
488
 
489
  result["execution_time"] = time.time() - start_time
490
 
 
719
 
720
  # Routing information
721
  route = result["route"]
722
+ route_class = "route-vector" if route == "vectorstore" else "route-wiki"
723
+ route_emoji = "🗄️" if route == "vectorstore" else "📖"
724
+ route_name = "Vector Store" if route == "vectorstore" else "Wikipedia"
725
 
726
  col1, col2, col3 = st.columns(3)
727
  with col1:
 
733
  with col2:
734
  st.metric("⚡ Execution Time", f"{result['execution_time']:.2f}s")
735
  with col3:
736
+ num_docs = len(result['documents']) if isinstance(result['documents'], list) else 1
737
+ st.metric("📄 Documents", num_docs)
738
+
739
+ # Display AI-generated response
740
+ st.markdown("### 🤖 AI-Generated Answer")
741
+ st.markdown(f"""
742
+ <div style="background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
743
+ padding: 1.5rem; border-radius: 10px; margin: 1rem 0;
744
+ border-left: 4px solid #667eea;">
745
+ {result['generation']}
746
+ </div>
747
+ """, unsafe_allow_html=True)
748
 
749
+ # Display source documents in expandable section
750
+ st.markdown("### 📄 Source Documents")
751
 
752
  documents = result['documents']
753
+ if isinstance(documents, list) and documents:
754
  for i, doc in enumerate(documents[:5], 1):
755
+ with st.expander(f"📌 Source Document {i}", expanded=False):
756
+ if hasattr(doc, 'page_content'):
757
+ st.markdown(doc.page_content)
758
+ else:
759
+ st.markdown(str(doc))
760
 
761
+ if advanced_mode and hasattr(doc, 'metadata') and doc.metadata:
762
  st.markdown("**Metadata:**")
763
  st.json(doc.metadata)
764
+ elif hasattr(documents, 'page_content'):
765
+ with st.expander("📌 Source Document", expanded=False):
766
+ st.markdown(documents.page_content)
767
 
768
  # Store query history
769
  if 'query_history' not in st.session_state:
 
773
  "query": query,
774
  "route": route_name,
775
  "timestamp": datetime.now().strftime("%H:%M:%S"),
776
+ "execution_time": result['execution_time'],
777
+ "response_preview": result['generation'][:100] + "..." if len(result['generation']) > 100 else result['generation']
778
  })
779
 
780
  except Exception as e:
781
  st.error(f"❌ Query execution failed: {str(e)}")
782
+ if st.checkbox("Show error details"):
783
+ st.code(traceback.format_exc())
784
 
785
  def render_analytics_tab():
786
  """Render system analytics and monitoring"""
docker-compose.yml ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ==================== Docker Compose ====================
2
+ # IMSKOS - Intelligent Multi-Source Knowledge Orchestration System
3
+ # Docker Compose configuration for local development and deployment
4
+
5
+ version: '3.8'
6
+
7
+ services:
8
+ imskos:
9
+ build:
10
+ context: .
11
+ dockerfile: Dockerfile
12
+ container_name: imskos-app
13
+ ports:
14
+ - "8501:8501"
15
+ environment:
16
+ - ASTRA_DB_APPLICATION_TOKEN=${ASTRA_DB_APPLICATION_TOKEN}
17
+ - ASTRA_DB_ID=${ASTRA_DB_ID}
18
+ - GROQ_API_KEY=${GROQ_API_KEY}
19
+ env_file:
20
+ - .env
21
+ restart: unless-stopped
22
+ healthcheck:
23
+ test: ["CMD", "curl", "-f", "http://localhost:8501/_stcore/health"]
24
+ interval: 30s
25
+ timeout: 10s
26
+ retries: 3
27
+ start_period: 40s
28
+ volumes:
29
+ # Mount for development (optional, remove for production)
30
+ - ./app.py:/app/app.py:ro
31
+ networks:
32
+ - imskos-network
33
+
34
+ networks:
35
+ imskos-network:
36
+ driver: bridge
requirements.txt CHANGED
@@ -1,34 +1,34 @@
1
  # ==================== Core Framework ====================
2
- streamlit==1.31.0
3
- python-dotenv==1.0.0
4
 
5
  # ==================== LangChain Ecosystem ====================
6
- langchain==0.1.16
7
- langchain-community==0.0.38
8
- langchain-core==0.1.46
9
- langchain-groq==0.1.3
10
- langchain-huggingface==0.0.1
11
- langgraph==0.0.43
12
- langchainhub==0.1.15
 
13
 
14
  # ==================== Vector Database & Embeddings ====================
15
- cassio==0.1.4
16
- sentence-transformers==2.5.1
17
 
18
  # ==================== Document Processing ====================
19
- tiktoken==0.6.0
20
- beautifulsoup4==4.12.3
21
- lxml==5.1.0
22
 
23
  # ==================== External APIs & Tools ====================
24
- wikipedia==1.4.0
25
- arxiv==2.1.0
26
 
27
  # ==================== Data & Utilities ====================
28
- pandas==2.2.1
29
- pydantic==2.6.4
30
- typing-extensions==4.10.0
31
 
32
- # ==================== Optional: Performance & Monitoring ====================
33
- # psutil==5.9.8
34
- # prometheus-client==0.20.0
 
1
  # ==================== Core Framework ====================
2
+ streamlit>=1.31.0,<2.0.0
3
+ python-dotenv>=1.0.0
4
 
5
  # ==================== LangChain Ecosystem ====================
6
+ langchain>=0.1.16
7
+ langchain-community>=0.0.38
8
+ langchain-core>=0.1.46
9
+ langchain-groq>=0.1.3
10
+ langchain-huggingface>=0.0.1
11
+ langchain-text-splitters>=0.0.1
12
+ langgraph>=0.0.43
13
+ langchainhub>=0.1.15
14
 
15
  # ==================== Vector Database & Embeddings ====================
16
+ cassio>=0.1.4
17
+ sentence-transformers>=2.5.1
18
 
19
  # ==================== Document Processing ====================
20
+ tiktoken>=0.6.0
21
+ beautifulsoup4>=4.12.3
22
+ lxml>=5.1.0
23
 
24
  # ==================== External APIs & Tools ====================
25
+ wikipedia>=1.4.0
 
26
 
27
  # ==================== Data & Utilities ====================
28
+ pandas>=2.2.1
29
+ pydantic>=2.6.4
30
+ typing-extensions>=4.10.0
31
 
32
+ # ==================== HTTP & Networking ====================
33
+ requests>=2.31.0
34
+ aiohttp>=3.9.0