fix
Browse files- .env.example +3 -0
- .gitattributes +0 -35
- README.md +55 -13
- agents +1 -0
- app.py +28 -0
- app/__init__.py +1 -0
- app/__pycache__/__init__.cpython-311.pyc +0 -0
- app/__pycache__/agent.cpython-311.pyc +0 -0
- app/__pycache__/retriever.cpython-311.pyc +0 -0
- app/agent.py +89 -0
- app/main.py +19 -0
- app/retriever.py +64 -0
- data/knowledge/invoice.txt +10 -0
- data/knowledge/resume.txt +11 -0
- data/knowledge/support_ticket.txt +10 -0
- requirements.txt +10 -0
- space.yaml +3 -0
.env.example
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
HF_EMBEDDING_MODEL=sentence-transformers/all-MiniLM-L6-v2
|
| 2 |
+
HF_GENERATION_MODEL=google/flan-t5-small
|
| 3 |
+
CHROMA_DB_DIR=.chroma_db
|
.gitattributes
DELETED
|
@@ -1,35 +0,0 @@
|
|
| 1 |
-
*.7z filter=lfs diff=lfs merge=lfs -text
|
| 2 |
-
*.arrow filter=lfs diff=lfs merge=lfs -text
|
| 3 |
-
*.bin filter=lfs diff=lfs merge=lfs -text
|
| 4 |
-
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
| 5 |
-
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
| 6 |
-
*.ftz filter=lfs diff=lfs merge=lfs -text
|
| 7 |
-
*.gz filter=lfs diff=lfs merge=lfs -text
|
| 8 |
-
*.h5 filter=lfs diff=lfs merge=lfs -text
|
| 9 |
-
*.joblib filter=lfs diff=lfs merge=lfs -text
|
| 10 |
-
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
| 11 |
-
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
| 12 |
-
*.model filter=lfs diff=lfs merge=lfs -text
|
| 13 |
-
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
| 14 |
-
*.npy filter=lfs diff=lfs merge=lfs -text
|
| 15 |
-
*.npz filter=lfs diff=lfs merge=lfs -text
|
| 16 |
-
*.onnx filter=lfs diff=lfs merge=lfs -text
|
| 17 |
-
*.ot filter=lfs diff=lfs merge=lfs -text
|
| 18 |
-
*.parquet filter=lfs diff=lfs merge=lfs -text
|
| 19 |
-
*.pb filter=lfs diff=lfs merge=lfs -text
|
| 20 |
-
*.pickle filter=lfs diff=lfs merge=lfs -text
|
| 21 |
-
*.pkl filter=lfs diff=lfs merge=lfs -text
|
| 22 |
-
*.pt filter=lfs diff=lfs merge=lfs -text
|
| 23 |
-
*.pth filter=lfs diff=lfs merge=lfs -text
|
| 24 |
-
*.rar filter=lfs diff=lfs merge=lfs -text
|
| 25 |
-
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 26 |
-
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
| 27 |
-
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
| 28 |
-
*.tar filter=lfs diff=lfs merge=lfs -text
|
| 29 |
-
*.tflite filter=lfs diff=lfs merge=lfs -text
|
| 30 |
-
*.tgz filter=lfs diff=lfs merge=lfs -text
|
| 31 |
-
*.wasm filter=lfs diff=lfs merge=lfs -text
|
| 32 |
-
*.xz 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
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
README.md
CHANGED
|
@@ -1,13 +1,55 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Agentic RAG Demo
|
| 2 |
+
|
| 3 |
+
This project is a compact, interview-friendly agentic retrieval-augmented generation (RAG) assistant. It answers questions over a small document set using a multi-step workflow:
|
| 4 |
+
|
| 5 |
+
1. The agent retrieves candidate passages from a local vector store.
|
| 6 |
+
2. A lightweight reranking step narrows the results.
|
| 7 |
+
3. A free open-source language model answers using the reranked evidence.
|
| 8 |
+
|
| 9 |
+
## Why this architecture
|
| 10 |
+
|
| 11 |
+
### Chunking strategy
|
| 12 |
+
|
| 13 |
+
The knowledge base is split with `RecursiveCharacterTextSplitter` at roughly 600 characters with 120 characters of overlap. This balances two goals:
|
| 14 |
+
|
| 15 |
+
- keep local context coherent for one business record or ticket
|
| 16 |
+
- avoid chunking too aggressively, which would lose important references and reduce answer quality
|
| 17 |
+
|
| 18 |
+
### Retrieval method
|
| 19 |
+
|
| 20 |
+
The app uses Chroma as the vector database and Hugging Face embeddings to create dense vector representations of each chunk. This is a good fit for a small demo because it is fast, local, and easy to inspect in the browser or terminal.
|
| 21 |
+
|
| 22 |
+
### Why rerank
|
| 23 |
+
|
| 24 |
+
Dense retrieval alone is often noisy. The rerank step gives a second signal by rewarding passages that are not only semantically close but also relevant to the literal question. That makes the final answer more grounded and less likely to hallucinate.
|
| 25 |
+
|
| 26 |
+
## Files
|
| 27 |
+
|
| 28 |
+
- `app/main.py` — runs the full demo
|
| 29 |
+
- `app/agent.py` — the agent graph and prompt orchestration
|
| 30 |
+
- `app/retriever.py` — vector store creation + retrieval + reranking
|
| 31 |
+
- `data/knowledge/` — sample documents such as an invoice, resume, and support ticket
|
| 32 |
+
|
| 33 |
+
## Run locally
|
| 34 |
+
|
| 35 |
+
```bash
|
| 36 |
+
pip install -r requirements.txt
|
| 37 |
+
copy .env.example .env
|
| 38 |
+
python app/main.py
|
| 39 |
+
```
|
| 40 |
+
|
| 41 |
+
No API key is required for the default free-model path.
|
| 42 |
+
|
| 43 |
+
## Deploy to Hugging Face Space
|
| 44 |
+
|
| 45 |
+
1. Push this repository to a GitHub repo.
|
| 46 |
+
2. Create a new Hugging Face Space.
|
| 47 |
+
3. Choose `Gradio` as the SDK.
|
| 48 |
+
4. Point the Space at the repo.
|
| 49 |
+
5. Keep the Space free-model path only; no API key is needed for the default deployment.
|
| 50 |
+
|
| 51 |
+
The default Space behavior uses Hugging Face-hosted free models for both embeddings and generation, which keeps the app portable and cheap to run.
|
| 52 |
+
|
| 53 |
+
The included `space.yaml` file tells Hugging Face to launch the Gradio app from `app.py`. The app is intentionally designed to run without any special server-only configuration.
|
| 54 |
+
|
| 55 |
+
If the local free-model generation backend is unavailable at runtime, the app gracefully falls back to a grounded evidence summary rather than hard-failing.
|
agents
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
Subproject commit 250a96691bb4265cc0d0709cba2a88a701945c8e
|
app.py
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import gradio as gr
|
| 2 |
+
|
| 3 |
+
from app.agent import run_agent
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
with gr.Blocks(title="Agentic RAG Demo") as demo:
|
| 7 |
+
gr.Markdown(
|
| 8 |
+
"""
|
| 9 |
+
# Agentic RAG Demo
|
| 10 |
+
|
| 11 |
+
Ask a question about the sample invoice, resume, or support ticket set.
|
| 12 |
+
The app performs a retrieval step, reranks the most relevant passages, and then answers using grounded evidence.
|
| 13 |
+
"""
|
| 14 |
+
)
|
| 15 |
+
|
| 16 |
+
question = gr.Textbox(
|
| 17 |
+
label="Question",
|
| 18 |
+
placeholder="Example: Which support ticket mentions a missing trailing slash?",
|
| 19 |
+
lines=2,
|
| 20 |
+
)
|
| 21 |
+
submit = gr.Button("Run agent")
|
| 22 |
+
answer = gr.Textbox(label="Answer", lines=8)
|
| 23 |
+
|
| 24 |
+
submit.click(fn=run_agent, inputs=question, outputs=answer)
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
if __name__ == "__main__":
|
| 28 |
+
demo.launch()
|
app/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
"""Agentic RAG demo package."""
|
app/__pycache__/__init__.cpython-311.pyc
ADDED
|
Binary file (200 Bytes). View file
|
|
|
app/__pycache__/agent.cpython-311.pyc
ADDED
|
Binary file (5.99 kB). View file
|
|
|
app/__pycache__/retriever.cpython-311.pyc
ADDED
|
Binary file (5.11 kB). View file
|
|
|
app/agent.py
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
from typing import TypedDict
|
| 4 |
+
|
| 5 |
+
from dotenv import load_dotenv
|
| 6 |
+
from langgraph.graph import END, START, StateGraph
|
| 7 |
+
|
| 8 |
+
from app.retriever import build_vectorstore, retrieve_and_rerank
|
| 9 |
+
|
| 10 |
+
load_dotenv()
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
class AgentState(TypedDict):
|
| 14 |
+
question: str
|
| 15 |
+
documents: list
|
| 16 |
+
answer: str
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
def _build_vectorstore() -> object:
|
| 20 |
+
return build_vectorstore()
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
def retrieve_node(state: AgentState) -> dict:
|
| 24 |
+
vectorstore = _build_vectorstore()
|
| 25 |
+
docs = retrieve_and_rerank(state["question"], vectorstore, k=5)
|
| 26 |
+
return {"documents": docs}
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def _dedupe_documents(documents: list) -> list:
|
| 30 |
+
unique: list = []
|
| 31 |
+
seen_sources: set[str] = set()
|
| 32 |
+
|
| 33 |
+
for doc in documents:
|
| 34 |
+
source = doc.metadata.get("source") if hasattr(doc, "metadata") else None
|
| 35 |
+
if source and source in seen_sources:
|
| 36 |
+
continue
|
| 37 |
+
if source:
|
| 38 |
+
seen_sources.add(source)
|
| 39 |
+
unique.append(doc)
|
| 40 |
+
|
| 41 |
+
return unique
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
def _grounded_summary(question: str, documents: list) -> str:
|
| 45 |
+
if not documents:
|
| 46 |
+
return "No relevant passages were retrieved for that question."
|
| 47 |
+
|
| 48 |
+
question_tokens = {token.lower() for token in question.replace("\n", " ").split() if token.isalpha()}
|
| 49 |
+
unique_docs = _dedupe_documents(documents)
|
| 50 |
+
selected_parts: list[str] = []
|
| 51 |
+
|
| 52 |
+
for doc in unique_docs:
|
| 53 |
+
text = doc.page_content.strip()
|
| 54 |
+
lines = [line.strip() for line in text.splitlines() if line.strip()]
|
| 55 |
+
matched_lines = [
|
| 56 |
+
line for line in lines if any(token.lower() in line.lower() for token in question_tokens)
|
| 57 |
+
]
|
| 58 |
+
if matched_lines:
|
| 59 |
+
selected_parts.append("\n".join(matched_lines))
|
| 60 |
+
else:
|
| 61 |
+
selected_parts.append("\n".join(lines))
|
| 62 |
+
|
| 63 |
+
return (
|
| 64 |
+
"Grounded answer (evidence-based summary):\n\n"
|
| 65 |
+
+ "\n\n".join(selected_parts[:3])
|
| 66 |
+
)
|
| 67 |
+
|
| 68 |
+
|
| 69 |
+
def answer_node(state: AgentState) -> dict:
|
| 70 |
+
evidence = "\n\n".join(doc.page_content for doc in state["documents"])
|
| 71 |
+
answer = _grounded_summary(state["question"], state["documents"])
|
| 72 |
+
|
| 73 |
+
if not evidence.strip():
|
| 74 |
+
answer = "No evidence was retrieved for that question."
|
| 75 |
+
|
| 76 |
+
return {"answer": answer}
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
def run_agent(question: str) -> str:
|
| 80 |
+
graph = StateGraph(AgentState)
|
| 81 |
+
graph.add_node("retrieve", retrieve_node)
|
| 82 |
+
graph.add_node("answer", answer_node)
|
| 83 |
+
graph.add_edge(START, "retrieve")
|
| 84 |
+
graph.add_edge("retrieve", "answer")
|
| 85 |
+
graph.add_edge("answer", END)
|
| 86 |
+
|
| 87 |
+
app = graph.compile()
|
| 88 |
+
result = app.invoke({"question": question, "documents": [], "answer": ""})
|
| 89 |
+
return result["answer"]
|
app/main.py
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import sys
|
| 4 |
+
|
| 5 |
+
from app.agent import run_agent
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
def main() -> None:
|
| 9 |
+
question = " ".join(sys.argv[1:])
|
| 10 |
+
if not question:
|
| 11 |
+
question = "Which ticket mentions a missing trailing slash and what was the resolution?"
|
| 12 |
+
|
| 13 |
+
answer = run_agent(question)
|
| 14 |
+
print("\nAnswer:\n")
|
| 15 |
+
print(answer)
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
if __name__ == "__main__":
|
| 19 |
+
main()
|
app/retriever.py
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import os
|
| 4 |
+
from collections import Counter
|
| 5 |
+
from pathlib import Path
|
| 6 |
+
|
| 7 |
+
from dotenv import load_dotenv
|
| 8 |
+
from langchain_chroma import Chroma
|
| 9 |
+
from langchain_core.documents import Document
|
| 10 |
+
from langchain_huggingface import HuggingFaceEmbeddings
|
| 11 |
+
from langchain_text_splitters import RecursiveCharacterTextSplitter
|
| 12 |
+
|
| 13 |
+
load_dotenv()
|
| 14 |
+
|
| 15 |
+
DATA_DIR = Path(__file__).resolve().parent.parent / "data" / "knowledge"
|
| 16 |
+
DB_DIR = Path(os.getenv("CHROMA_DB_DIR", ".chroma_db"))
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
def _load_documents() -> list[Document]:
|
| 20 |
+
docs: list[Document] = []
|
| 21 |
+
for file_path in sorted(DATA_DIR.glob("*.txt")):
|
| 22 |
+
content = file_path.read_text(encoding="utf-8")
|
| 23 |
+
docs.append(Document(page_content=content, metadata={"source": file_path.name}))
|
| 24 |
+
return docs
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
def build_vectorstore() -> Chroma:
|
| 28 |
+
splitter = RecursiveCharacterTextSplitter(chunk_size=600, chunk_overlap=120)
|
| 29 |
+
raw_docs = _load_documents()
|
| 30 |
+
chunks = splitter.split_documents(raw_docs)
|
| 31 |
+
|
| 32 |
+
embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
|
| 33 |
+
vectorstore = Chroma.from_documents(
|
| 34 |
+
documents=chunks,
|
| 35 |
+
embedding=embeddings,
|
| 36 |
+
persist_directory=str(DB_DIR),
|
| 37 |
+
collection_name="agentic-rag-demo",
|
| 38 |
+
)
|
| 39 |
+
return vectorstore
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
def _normalize_text(text: str) -> list[str]:
|
| 43 |
+
return [token.lower() for token in text.replace("\n", " ").split() if token.isalpha()]
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
def _keyword_overlap_score(question: str, chunk: str) -> float:
|
| 47 |
+
question_tokens = Counter(_normalize_text(question))
|
| 48 |
+
chunk_tokens = Counter(_normalize_text(chunk))
|
| 49 |
+
overlap = sum(min(question_tokens[token], chunk_tokens[token]) for token in question_tokens)
|
| 50 |
+
if overlap == 0:
|
| 51 |
+
return 0.0
|
| 52 |
+
return overlap / max(1, len(question_tokens))
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
def retrieve_and_rerank(question: str, vectorstore: Chroma, k: int = 5) -> list[Document]:
|
| 56 |
+
hits = vectorstore.similarity_search(question, k=k)
|
| 57 |
+
scored = []
|
| 58 |
+
for doc in hits:
|
| 59 |
+
score = _keyword_overlap_score(question, doc.page_content)
|
| 60 |
+
scored.append((score, doc))
|
| 61 |
+
|
| 62 |
+
scored.sort(key=lambda item: item[0], reverse=True)
|
| 63 |
+
reranked = [doc for _, doc in scored if doc.page_content.strip()]
|
| 64 |
+
return reranked[:3]
|
data/knowledge/invoice.txt
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Invoice #INV-2048
|
| 2 |
+
Vendor: Northwind Data Services
|
| 3 |
+
Date: 2026-04-10
|
| 4 |
+
Total: $2,480.00
|
| 5 |
+
Status: Paid
|
| 6 |
+
Line items:
|
| 7 |
+
- Managed analytics platform: $1,940.00
|
| 8 |
+
- Support retention: $540.00
|
| 9 |
+
Notes:
|
| 10 |
+
The customer requested quarterly reporting for March and April. The account owner is Dana Lewis.
|
data/knowledge/resume.txt
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Candidate: Maya Thompson
|
| 2 |
+
Role: Senior Data Analyst
|
| 3 |
+
Experience:
|
| 4 |
+
- 7 years in BI and forecasting
|
| 5 |
+
- Built a revenue dashboard for a logistics firm
|
| 6 |
+
- Led migration from Excel-based reporting to a Snowflake pipeline
|
| 7 |
+
Skills:
|
| 8 |
+
Python, SQL, dbt, Power BI, stakeholder communication
|
| 9 |
+
Availability: Immediate
|
| 10 |
+
Reference note:
|
| 11 |
+
Maya is a strong fit for roles involving analytics modernization and cross-functional communication.
|
data/knowledge/support_ticket.txt
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Support ticket #ST-882
|
| 2 |
+
Customer: Harbor Logistics
|
| 3 |
+
Priority: High
|
| 4 |
+
Issue: The dashboard stopped refreshing after the nightly data import.
|
| 5 |
+
Steps taken:
|
| 6 |
+
- Verified permissions on the warehouse connector.
|
| 7 |
+
- Restarted the ingestion job.
|
| 8 |
+
- Confirmed the export path was missing a trailing slash.
|
| 9 |
+
Resolution: Updated the path and reran the import. The dashboard became healthy again.
|
| 10 |
+
Owner: Omar Patel
|
requirements.txt
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
langchain>=0.3.0
|
| 2 |
+
langgraph>=0.2.0
|
| 3 |
+
langchain-chroma>=0.2.0
|
| 4 |
+
langchain-huggingface>=0.2.0
|
| 5 |
+
langchain-text-splitters>=0.3.0
|
| 6 |
+
chromadb>=0.5.0
|
| 7 |
+
python-dotenv>=1.0.1
|
| 8 |
+
gradio>=4.0.0
|
| 9 |
+
sentence-transformers>=3.0.0
|
| 10 |
+
transformers>=4.40.0
|
space.yaml
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
sdk: gradio
|
| 2 |
+
app_file: app.py
|
| 3 |
+
python_version: "3.11"
|