christian
commited on
Commit
·
402e33f
0
Parent(s):
Remove big files for HF
Browse files- .dockerignore +40 -0
- .gitignore +124 -0
- Dockerfile +19 -0
- api/__init__.py +0 -0
- api/main.py +256 -0
- api/main_all_models.py +213 -0
- api/main_hface_hosted.py +184 -0
- api/test_hf_token.py +110 -0
- app.py +176 -0
- built_vector_store.py +19 -0
- docs/general/general_example.txt +336 -0
- docs/mes/mes_general.txt +74 -0
- docs/technical/mes_technical_example.txt +535 -0
- embedding_config.py +27 -0
- rebuild_vector_stores.py +19 -0
- requirements.txt +72 -0
- runtime.txt +1 -0
- test_api.py +103 -0
- test_embeddings_retriever.py +199 -0
- test_retriever.py +164 -0
- utils/__init__.py +0 -0
- utils/helpers/chat_mapper.py +61 -0
- utils/loader.py +20 -0
- utils/rebuild_vector_stores.py +41 -0
- utils/vector_store.py +98 -0
.dockerignore
ADDED
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Ignore virtual environments
|
2 |
+
.venv/
|
3 |
+
venv/
|
4 |
+
ENV/
|
5 |
+
env/
|
6 |
+
|
7 |
+
# Python cache & bytecode
|
8 |
+
__pycache__/
|
9 |
+
*.pyc
|
10 |
+
*.pyo
|
11 |
+
*.pyd
|
12 |
+
|
13 |
+
# Build artifacts
|
14 |
+
*.egg-info/
|
15 |
+
dist/
|
16 |
+
build/
|
17 |
+
|
18 |
+
# Logs
|
19 |
+
*.log
|
20 |
+
|
21 |
+
# System / IDE files
|
22 |
+
.DS_Store
|
23 |
+
*.swp
|
24 |
+
*.swo
|
25 |
+
.idea/
|
26 |
+
.vscode/
|
27 |
+
|
28 |
+
# Git
|
29 |
+
.git
|
30 |
+
.gitignore
|
31 |
+
|
32 |
+
__pycache__/
|
33 |
+
*.pyc
|
34 |
+
*.pyo
|
35 |
+
*.pyd
|
36 |
+
.env
|
37 |
+
.git
|
38 |
+
# docs/
|
39 |
+
# tests/
|
40 |
+
venv_original/
|
.gitignore
ADDED
@@ -0,0 +1,124 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# # Python
|
2 |
+
# __pycache__/
|
3 |
+
# *.py[cod]
|
4 |
+
# *$py.class
|
5 |
+
# *.so
|
6 |
+
# .Python
|
7 |
+
# build/
|
8 |
+
# develop-eggs/
|
9 |
+
# dist/
|
10 |
+
# downloads/
|
11 |
+
# eggs/
|
12 |
+
# .eggs/
|
13 |
+
# lib/
|
14 |
+
# lib64/
|
15 |
+
# parts/
|
16 |
+
# sdist/
|
17 |
+
# var/
|
18 |
+
# wheels/
|
19 |
+
# *.egg-info/
|
20 |
+
# .installed.cfg
|
21 |
+
# *.egg
|
22 |
+
|
23 |
+
# # Virtual Environment
|
24 |
+
# venv/
|
25 |
+
# env/
|
26 |
+
# .venv/
|
27 |
+
# .env/
|
28 |
+
# ENV/
|
29 |
+
|
30 |
+
# # IDE
|
31 |
+
# .vscode/
|
32 |
+
# .idea/
|
33 |
+
# *.swp
|
34 |
+
# *.swo
|
35 |
+
|
36 |
+
# # OS
|
37 |
+
# .DS_Store
|
38 |
+
# Thumbs.db
|
39 |
+
|
40 |
+
# # Project specific
|
41 |
+
# chroma_db/
|
42 |
+
# vector_stores/
|
43 |
+
# docs/
|
44 |
+
# *.log
|
45 |
+
|
46 |
+
# # Sensitive files
|
47 |
+
# .env
|
48 |
+
# config.ini
|
49 |
+
|
50 |
+
|
51 |
+
# Environment variables
|
52 |
+
.env
|
53 |
+
.env.local
|
54 |
+
.env.production
|
55 |
+
|
56 |
+
# Python
|
57 |
+
__pycache__/
|
58 |
+
*.py[cod]
|
59 |
+
*$py.class
|
60 |
+
*.so
|
61 |
+
.Python
|
62 |
+
build/
|
63 |
+
develop-eggs/
|
64 |
+
dist/
|
65 |
+
downloads/
|
66 |
+
eggs/
|
67 |
+
.eggs/
|
68 |
+
lib/
|
69 |
+
lib64/
|
70 |
+
parts/
|
71 |
+
sdist/
|
72 |
+
var/
|
73 |
+
wheels/
|
74 |
+
*.egg-info/
|
75 |
+
|
76 |
+
# Virtual environments (these are outside the repo)
|
77 |
+
../venv/
|
78 |
+
../.venv/
|
79 |
+
../env/
|
80 |
+
../ENV/
|
81 |
+
venv_original/
|
82 |
+
venv/
|
83 |
+
.venv
|
84 |
+
.venv/
|
85 |
+
|
86 |
+
# Vector stores (optional - include them if you want faster deployments)
|
87 |
+
# vector_stores/
|
88 |
+
|
89 |
+
# IDE
|
90 |
+
.vscode/
|
91 |
+
.idea/
|
92 |
+
*.swp
|
93 |
+
*.swo
|
94 |
+
|
95 |
+
# OS
|
96 |
+
.DS_Store
|
97 |
+
Thumbs.db
|
98 |
+
|
99 |
+
# Logs
|
100 |
+
*.log
|
101 |
+
logs/
|
102 |
+
|
103 |
+
# Testing
|
104 |
+
.pytest_cache/
|
105 |
+
.coverage
|
106 |
+
htmlcov/
|
107 |
+
|
108 |
+
# Render
|
109 |
+
.render/
|
110 |
+
|
111 |
+
|
112 |
+
# Others
|
113 |
+
old.txt
|
114 |
+
requirements_old.txt
|
115 |
+
render.yaml
|
116 |
+
|
117 |
+
# vector stores for huggingface
|
118 |
+
# Vector stores (local cache only)
|
119 |
+
vector_stores/
|
120 |
+
chroma_db/
|
121 |
+
|
122 |
+
# Just in case
|
123 |
+
*.bin
|
124 |
+
*.pkl
|
Dockerfile
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Use Python 3.13 slim (your current version)
|
2 |
+
FROM python:3.13-slim
|
3 |
+
|
4 |
+
# Set working directory
|
5 |
+
WORKDIR /app
|
6 |
+
|
7 |
+
# Copy and install dependencies
|
8 |
+
COPY requirements.txt .
|
9 |
+
RUN pip install --upgrade pip setuptools wheel \
|
10 |
+
&& pip install --no-cache-dir -r requirements.txt
|
11 |
+
|
12 |
+
# Copy all source code
|
13 |
+
COPY . .
|
14 |
+
|
15 |
+
# Expose the port that HF Spaces expects
|
16 |
+
EXPOSE 7860
|
17 |
+
|
18 |
+
# Run your existing launcher
|
19 |
+
CMD ["python", "app.py"]
|
api/__init__.py
ADDED
File without changes
|
api/main.py
ADDED
@@ -0,0 +1,256 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from fastapi import Request
|
2 |
+
import requests
|
3 |
+
from dotenv import load_dotenv
|
4 |
+
from utils.vector_store import get_vector_store
|
5 |
+
from pydantic import BaseModel
|
6 |
+
from fastapi import FastAPI, HTTPException, Request
|
7 |
+
import os
|
8 |
+
import sys
|
9 |
+
|
10 |
+
from utils.helpers.chat_mapper import map_answer_to_chat_response
|
11 |
+
|
12 |
+
from fastapi.middleware.cors import CORSMiddleware
|
13 |
+
|
14 |
+
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
15 |
+
|
16 |
+
load_dotenv()
|
17 |
+
|
18 |
+
app = FastAPI()
|
19 |
+
|
20 |
+
# Adding Middleware for testing
|
21 |
+
|
22 |
+
# Get allowed origins from environment variable for flexibility
|
23 |
+
ALLOWED_ORIGINS = os.environ.get("ALLOWED_ORIGINS", "").split(
|
24 |
+
",") if os.environ.get("ALLOWED_ORIGINS") else ["*"]
|
25 |
+
|
26 |
+
# For development/testing, you can also hardcode your Vercel domain
|
27 |
+
VERCEL_DOMAINS = [
|
28 |
+
"https://your-app-name.vercel.app", # Replace with your actual Vercel app name
|
29 |
+
# Git branch deployments
|
30 |
+
"https://mes-chatbot-project-git-simple-w-markdown-mangobutlers-projects.vercel.app/",
|
31 |
+
# Git branch
|
32 |
+
"mes-chatbot-project-73znou68u-mangobutlers-projects.vercel.app",
|
33 |
+
"http://localhost:3000", # For local frontend development
|
34 |
+
"http://localhost:5173", # For Vite dev server
|
35 |
+
"http://127.0.0.1:3000", # Alternative localhost
|
36 |
+
]
|
37 |
+
|
38 |
+
|
39 |
+
# Combine environment origins with Vercel domains
|
40 |
+
if ALLOWED_ORIGINS == ["*"]:
|
41 |
+
# If no specific origins set, use Vercel domains + wildcard for testing
|
42 |
+
final_origins = VERCEL_DOMAINS + ["*"]
|
43 |
+
else:
|
44 |
+
final_origins = ALLOWED_ORIGINS + VERCEL_DOMAINS
|
45 |
+
# Adding the Middleware
|
46 |
+
|
47 |
+
app.add_middleware(
|
48 |
+
CORSMiddleware,
|
49 |
+
allow_origins=final_origins,
|
50 |
+
allow_credentials=True,
|
51 |
+
allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"],
|
52 |
+
allow_headers=["*"],
|
53 |
+
expose_headers=["*"]
|
54 |
+
)
|
55 |
+
|
56 |
+
# ---------------------------
|
57 |
+
|
58 |
+
# Vector store mapping for different domains
|
59 |
+
VECTOR_STORE_PATHS = {
|
60 |
+
"mes": "./vector_stores/mes_db",
|
61 |
+
"technical": "./vector_stores/tech_db",
|
62 |
+
"general": "./vector_stores/general_db",
|
63 |
+
"default": "./vector_stores/general_db",
|
64 |
+
}
|
65 |
+
|
66 |
+
|
67 |
+
class QueryRequest(BaseModel):
|
68 |
+
query: str
|
69 |
+
|
70 |
+
|
71 |
+
# ---------------------------
|
72 |
+
# Gemini API setup
|
73 |
+
# ---------------------------
|
74 |
+
GEMINI_API_KEY = os.environ.get("GEMINI_API_KEY")
|
75 |
+
if not GEMINI_API_KEY:
|
76 |
+
raise ValueError("GEMINI_API_KEY environment variable required")
|
77 |
+
|
78 |
+
GEMINI_API_URL = "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent"
|
79 |
+
|
80 |
+
# ---------------------------
|
81 |
+
# Vector store loader
|
82 |
+
# ---------------------------
|
83 |
+
|
84 |
+
|
85 |
+
def load_vector_store_by_prefix(query: str):
|
86 |
+
lower_q = query.lower().strip()
|
87 |
+
for prefix, path in VECTOR_STORE_PATHS.items():
|
88 |
+
if prefix != "default" and lower_q.startswith(f"{prefix}:"):
|
89 |
+
cleaned_query = lower_q[len(prefix) + 1:].strip()
|
90 |
+
return get_vector_store(persist_directory=path), cleaned_query, prefix
|
91 |
+
return get_vector_store(persist_directory=VECTOR_STORE_PATHS["default"]), query, "default"
|
92 |
+
|
93 |
+
|
94 |
+
def generate_answer_with_gemini(query: str, context_docs: list):
|
95 |
+
# Build context string
|
96 |
+
# knowledge_parts = []
|
97 |
+
# for i, doc in enumerate(context_docs[:3], 1):
|
98 |
+
# knowledge_parts.append(f"Data Source {i}: {doc.page_content[:300]}")
|
99 |
+
# knowledge_base = "\n\n".join(knowledge_parts)
|
100 |
+
|
101 |
+
knowledge_parts = []
|
102 |
+
for i, doc in enumerate(context_docs, 1):
|
103 |
+
knowledge_parts.append(f"Data Source {i}: {doc.page_content.strip()}")
|
104 |
+
knowledge_base = "\n\n".join(knowledge_parts)
|
105 |
+
|
106 |
+
# The updated prompt is more direct and forceful
|
107 |
+
prompt = (
|
108 |
+
"You are an expert AI assistant that uses a provided knowledge base to answer questions. "
|
109 |
+
"Your responses must always be based on this knowledge base, which is the ultimate source of truth. "
|
110 |
+
"You will only use your internal knowledge to supplement the answer, never to contradict it. "
|
111 |
+
"If and only if the knowledge base contains absolutely nothing relevant to the user's question, "
|
112 |
+
"you will respond with a polite and concise statement saying you cannot answer the question from the information you have. "
|
113 |
+
"You must never answer 'I don't know' if there is any information in the knowledge base that is even tangentially related to the question. "
|
114 |
+
"Always try your best to construct a useful answer by synthesizing the provided information. "
|
115 |
+
"Do not refer to the 'knowledge base' or 'sources' directly; instead, use phrases like 'based on the information I have'.\n\n"
|
116 |
+
|
117 |
+
f"My knowledge base:\n{knowledge_base}\n\n"
|
118 |
+
f"User's Question: {query}\n\nAnswer:"
|
119 |
+
)
|
120 |
+
|
121 |
+
# print the prompt for debugging
|
122 |
+
print("🔍 Prompt sent to Gemini API:", prompt)
|
123 |
+
|
124 |
+
try:
|
125 |
+
response = requests.post(
|
126 |
+
f"{GEMINI_API_URL}?key={GEMINI_API_KEY}",
|
127 |
+
json={
|
128 |
+
"contents": [
|
129 |
+
{
|
130 |
+
"role": "user",
|
131 |
+
"parts": [
|
132 |
+
{"text": prompt}
|
133 |
+
]
|
134 |
+
}
|
135 |
+
],
|
136 |
+
"generationConfig": {
|
137 |
+
"temperature": 0.7,
|
138 |
+
"maxOutputTokens": 300
|
139 |
+
}
|
140 |
+
},
|
141 |
+
timeout=300
|
142 |
+
)
|
143 |
+
|
144 |
+
if response.status_code != 200:
|
145 |
+
return f"API Error: {response.status_code} - {response.text}"
|
146 |
+
|
147 |
+
data = response.json()
|
148 |
+
|
149 |
+
# Extract answer text
|
150 |
+
return (
|
151 |
+
data.get("candidates", [{}])[0]
|
152 |
+
.get("content", {})
|
153 |
+
.get("parts", [{}])[0]
|
154 |
+
.get("text", "")
|
155 |
+
.strip()
|
156 |
+
or "I couldn't generate an answer."
|
157 |
+
)
|
158 |
+
|
159 |
+
except Exception as e:
|
160 |
+
return f"Error: {str(e)}"
|
161 |
+
|
162 |
+
|
163 |
+
# ---------------------------
|
164 |
+
# API Endpoints
|
165 |
+
# ---------------------------
|
166 |
+
|
167 |
+
|
168 |
+
@app.get("/")
|
169 |
+
def root():
|
170 |
+
return {
|
171 |
+
"status": "running",
|
172 |
+
"model": "gemini-2.0-flash",
|
173 |
+
"using_direct_api": True,
|
174 |
+
"client_ready": True
|
175 |
+
}
|
176 |
+
|
177 |
+
|
178 |
+
@app.post("/ask")
|
179 |
+
async def ask_question(request: Request):
|
180 |
+
try:
|
181 |
+
# Print raw incoming request body
|
182 |
+
raw_body = await request.body()
|
183 |
+
print("📥 Incoming POST /ask request body:")
|
184 |
+
print(raw_body.decode("utf-8"))
|
185 |
+
|
186 |
+
# Parse into your Pydantic model
|
187 |
+
parsed_request = QueryRequest.model_validate_json(raw_body)
|
188 |
+
print("✅ Parsed request object:", parsed_request)
|
189 |
+
|
190 |
+
vector_store, cleaned_query, store_key = load_vector_store_by_prefix(
|
191 |
+
parsed_request.query
|
192 |
+
)
|
193 |
+
|
194 |
+
if not vector_store:
|
195 |
+
raise HTTPException(
|
196 |
+
status_code=500, detail="Vector store not ready"
|
197 |
+
)
|
198 |
+
|
199 |
+
retriever = vector_store.as_retriever(
|
200 |
+
search_type="mmr",
|
201 |
+
search_kwargs={
|
202 |
+
"k": 6,
|
203 |
+
"fetch_k": 20,
|
204 |
+
"lambda_mult": 0.5
|
205 |
+
}
|
206 |
+
)
|
207 |
+
|
208 |
+
docs = retriever.get_relevant_documents(cleaned_query)
|
209 |
+
|
210 |
+
# Deduplicate
|
211 |
+
seen = set()
|
212 |
+
unique_docs = []
|
213 |
+
for doc in docs:
|
214 |
+
snippet = doc.page_content.strip()
|
215 |
+
if snippet not in seen:
|
216 |
+
seen.add(snippet)
|
217 |
+
unique_docs.append(doc)
|
218 |
+
docs = unique_docs[:5]
|
219 |
+
|
220 |
+
if not docs:
|
221 |
+
return {
|
222 |
+
"answer": "I couldn't find any relevant information in the knowledge base to answer your question.",
|
223 |
+
"model_used": "gemini-2.0-flash",
|
224 |
+
"vector_store_used": VECTOR_STORE_PATHS[store_key],
|
225 |
+
"sources": []
|
226 |
+
}
|
227 |
+
|
228 |
+
answer = generate_answer_with_gemini(cleaned_query, docs)
|
229 |
+
|
230 |
+
answer_obj = {
|
231 |
+
"answer": answer,
|
232 |
+
"model_used": "gemini-2.0-flash",
|
233 |
+
"vector_store_used": VECTOR_STORE_PATHS[store_key],
|
234 |
+
"sources": [
|
235 |
+
{
|
236 |
+
"content": doc.page_content[:500] + "...\n",
|
237 |
+
"metadata": doc.metadata
|
238 |
+
}
|
239 |
+
for doc in docs
|
240 |
+
]
|
241 |
+
}
|
242 |
+
# For debugging, print the generated answer object
|
243 |
+
# print("Generated answer object:",
|
244 |
+
# map_answer_to_chat_response(answer_obj))
|
245 |
+
|
246 |
+
return map_answer_to_chat_response(answer_obj)
|
247 |
+
|
248 |
+
except Exception as e:
|
249 |
+
print(f"Error in ask_question: {e}")
|
250 |
+
raise HTTPException(status_code=500, detail=f"Error: {str(e)}")
|
251 |
+
|
252 |
+
|
253 |
+
if __name__ == "__main__":
|
254 |
+
import uvicorn
|
255 |
+
port = int(os.environ.get("PORT", 8000))
|
256 |
+
uvicorn.run(app, host="0.0.0.0", port=port)
|
api/main_all_models.py
ADDED
@@ -0,0 +1,213 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
# from fastapi import FastAPI, Request
|
3 |
+
# from pydantic import BaseModel
|
4 |
+
# from langchain.chains import RetrievalQA
|
5 |
+
# from langchain.chat_models import ChatOpenAI
|
6 |
+
# from ..utils.vector_store import get_vector_store
|
7 |
+
|
8 |
+
# app = FastAPI()
|
9 |
+
# retriever = get_vector_store().as_retriever()
|
10 |
+
# llm = ChatOpenAI(model_name="gpt-4", temperature=0)
|
11 |
+
# qa_chain = RetrievalQA.from_chain_type(llm=llm, retriever=retriever)
|
12 |
+
|
13 |
+
# class QueryRequest(BaseModel):
|
14 |
+
# query: str
|
15 |
+
|
16 |
+
# @app.post("/ask")
|
17 |
+
# def ask_question(request: QueryRequest):
|
18 |
+
# answer = qa_chain.run(request.query)
|
19 |
+
# return { "answer": answer }
|
20 |
+
|
21 |
+
from fastapi import FastAPI, Request
|
22 |
+
from pydantic import BaseModel
|
23 |
+
from langchain.chains import RetrievalQA
|
24 |
+
from langchain.llms import HuggingFacePipeline
|
25 |
+
from langchain.llms import LlamaCpp
|
26 |
+
from langchain.llms import GPT4All
|
27 |
+
from ..utils.vector_store import get_vector_store
|
28 |
+
import torch
|
29 |
+
|
30 |
+
app = FastAPI()
|
31 |
+
|
32 |
+
|
33 |
+
class QueryRequest(BaseModel):
|
34 |
+
query: str
|
35 |
+
|
36 |
+
# Option 1: HuggingFace Transformers (Recommended for most cases)
|
37 |
+
|
38 |
+
|
39 |
+
def setup_huggingface_model():
|
40 |
+
"""Setup a local HuggingFace model"""
|
41 |
+
from transformers import AutoTokenizer, AutoModelForCausalLM, pipeline
|
42 |
+
|
43 |
+
# Use a smaller, efficient model that works well for QA
|
44 |
+
model_name = "microsoft/DialoGPT-medium" # or "google/flan-t5-base"
|
45 |
+
|
46 |
+
# For better quality but larger size:
|
47 |
+
# model_name = "microsoft/DialoGPT-large"
|
48 |
+
# model_name = "google/flan-t5-large"
|
49 |
+
|
50 |
+
tokenizer = AutoTokenizer.from_pretrained(model_name, padding_side='left')
|
51 |
+
model = AutoModelForCausalLM.from_pretrained(
|
52 |
+
model_name,
|
53 |
+
torch_dtype=torch.float16 if torch.cuda.is_available() else torch.float32,
|
54 |
+
device_map="auto" if torch.cuda.is_available() else None
|
55 |
+
)
|
56 |
+
|
57 |
+
# Create pipeline
|
58 |
+
pipe = pipeline(
|
59 |
+
"text-generation",
|
60 |
+
model=model,
|
61 |
+
tokenizer=tokenizer,
|
62 |
+
max_length=512,
|
63 |
+
temperature=0.1,
|
64 |
+
do_sample=True,
|
65 |
+
device=0 if torch.cuda.is_available() else -1
|
66 |
+
)
|
67 |
+
|
68 |
+
# Wrap in LangChain
|
69 |
+
llm = HuggingFacePipeline(pipeline=pipe)
|
70 |
+
return llm
|
71 |
+
|
72 |
+
# Option 2: Ollama (Great for local deployment)
|
73 |
+
|
74 |
+
|
75 |
+
def setup_ollama_model():
|
76 |
+
"""Setup Ollama model (requires Ollama to be installed)"""
|
77 |
+
from langchain.llms import Ollama
|
78 |
+
|
79 |
+
# Popular free models available through Ollama:
|
80 |
+
# - llama2:7b (good balance of quality/speed)
|
81 |
+
# - mistral:7b (excellent for instructions)
|
82 |
+
# - codellama:7b (good for technical content)
|
83 |
+
# - phi:2.7b (Microsoft, very fast)
|
84 |
+
|
85 |
+
llm = Ollama(
|
86 |
+
model="llama2:7b", # Change to your preferred model
|
87 |
+
temperature=0.1
|
88 |
+
)
|
89 |
+
return llm
|
90 |
+
|
91 |
+
# Option 3: GPT4All (Easy setup, no internet required)
|
92 |
+
|
93 |
+
|
94 |
+
def setup_gpt4all_model():
|
95 |
+
"""Setup GPT4All model"""
|
96 |
+
# Download models from: https://gpt4all.io/models/models.json
|
97 |
+
# Popular options:
|
98 |
+
# - ggml-gpt4all-j-v1.3-groovy.bin
|
99 |
+
# - ggml-vicuna-7b-1.1-q4_2.bin
|
100 |
+
# - ggml-mpt-7b-instruct.bin
|
101 |
+
|
102 |
+
llm = GPT4All(
|
103 |
+
model="./models/ggml-gpt4all-j-v1.3-groovy.bin", # Path to your downloaded model
|
104 |
+
max_tokens=512,
|
105 |
+
temp=0.1
|
106 |
+
)
|
107 |
+
return llm
|
108 |
+
|
109 |
+
# Option 4: LlamaCpp (For GGML/GGUF models)
|
110 |
+
|
111 |
+
|
112 |
+
def setup_llamacpp_model():
|
113 |
+
"""Setup LlamaCpp model"""
|
114 |
+
llm = LlamaCpp(
|
115 |
+
model_path="./models/llama-2-7b-chat.ggmlv3.q4_0.bin", # Path to your GGML model
|
116 |
+
temperature=0.1,
|
117 |
+
max_tokens=512,
|
118 |
+
top_p=1,
|
119 |
+
verbose=True,
|
120 |
+
n_ctx=2048, # Context window
|
121 |
+
)
|
122 |
+
return llm
|
123 |
+
|
124 |
+
|
125 |
+
# Choose your preferred setup method
|
126 |
+
try:
|
127 |
+
# Try Ollama first (if installed)
|
128 |
+
llm = setup_ollama_model()
|
129 |
+
print("Using Ollama model")
|
130 |
+
except Exception as e:
|
131 |
+
try:
|
132 |
+
# Fall back to HuggingFace
|
133 |
+
llm = setup_huggingface_model()
|
134 |
+
print("Using HuggingFace model")
|
135 |
+
except Exception as e:
|
136 |
+
print(f"Error setting up models: {e}")
|
137 |
+
# You could add more fallbacks here
|
138 |
+
|
139 |
+
# Setup retriever and QA chain
|
140 |
+
retriever = get_vector_store().as_retriever()
|
141 |
+
qa_chain = RetrievalQA.from_chain_type(
|
142 |
+
llm=llm,
|
143 |
+
retriever=retriever,
|
144 |
+
return_source_documents=True # Optional: return source chunks
|
145 |
+
)
|
146 |
+
|
147 |
+
|
148 |
+
@app.post("/ask")
|
149 |
+
def ask_question(request: QueryRequest):
|
150 |
+
try:
|
151 |
+
result = qa_chain({"query": request.query})
|
152 |
+
|
153 |
+
# If return_source_documents=True
|
154 |
+
if isinstance(result, dict) and "source_documents" in result:
|
155 |
+
return {
|
156 |
+
"answer": result["result"],
|
157 |
+
"sources": [doc.page_content[:200] + "..." for doc in result["source_documents"]]
|
158 |
+
}
|
159 |
+
else:
|
160 |
+
return {"answer": result}
|
161 |
+
|
162 |
+
except Exception as e:
|
163 |
+
return {"error": f"Failed to process query: {str(e)}"}
|
164 |
+
|
165 |
+
|
166 |
+
@app.get("/health")
|
167 |
+
def health_check():
|
168 |
+
return {"status": "healthy", "model_type": type(llm).__name__}
|
169 |
+
|
170 |
+
# Alternative endpoint for different vector stores
|
171 |
+
|
172 |
+
|
173 |
+
@app.post("/ask/{store_type}")
|
174 |
+
def ask_question_specific_store(store_type: str, request: QueryRequest):
|
175 |
+
"""Ask questions to specific vector stores"""
|
176 |
+
|
177 |
+
store_paths = {
|
178 |
+
"mes": "./vector_stores/mes_db",
|
179 |
+
"general": "./vector_stores/general_db",
|
180 |
+
"tech": "./vector_stores/tech_db"
|
181 |
+
}
|
182 |
+
|
183 |
+
if store_type not in store_paths:
|
184 |
+
return {"error": f"Invalid store type. Available: {list(store_paths.keys())}"}
|
185 |
+
|
186 |
+
try:
|
187 |
+
# Get specific vector store
|
188 |
+
vector_store = get_vector_store(
|
189 |
+
persist_directory=store_paths[store_type])
|
190 |
+
specific_retriever = vector_store.as_retriever()
|
191 |
+
|
192 |
+
# Create QA chain with specific retriever
|
193 |
+
specific_qa_chain = RetrievalQA.from_chain_type(
|
194 |
+
llm=llm,
|
195 |
+
retriever=specific_retriever,
|
196 |
+
return_source_documents=True
|
197 |
+
)
|
198 |
+
|
199 |
+
result = specific_qa_chain({"query": request.query})
|
200 |
+
|
201 |
+
return {
|
202 |
+
"answer": result["result"],
|
203 |
+
"store_used": store_type,
|
204 |
+
"sources": [doc.page_content[:200] + "..." for doc in result["source_documents"]]
|
205 |
+
}
|
206 |
+
|
207 |
+
except Exception as e:
|
208 |
+
return {"error": f"Failed to process query for {store_type}: {str(e)}"}
|
209 |
+
|
210 |
+
|
211 |
+
if __name__ == "__main__":
|
212 |
+
import uvicorn
|
213 |
+
uvicorn.run(app, host="0.0.0.0", port=8000)
|
api/main_hface_hosted.py
ADDED
@@ -0,0 +1,184 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from fastapi import FastAPI, HTTPException
|
2 |
+
from pydantic import BaseModel
|
3 |
+
import os
|
4 |
+
import requests
|
5 |
+
from langchain.chains import RetrievalQA
|
6 |
+
from langchain.llms import HuggingFaceHub
|
7 |
+
from ..utils.vector_store import get_vector_store
|
8 |
+
|
9 |
+
app = FastAPI()
|
10 |
+
|
11 |
+
|
12 |
+
class QueryRequest(BaseModel):
|
13 |
+
query: str
|
14 |
+
|
15 |
+
# Option 1: Hugging Face Inference API (Free with rate limits)
|
16 |
+
|
17 |
+
|
18 |
+
def setup_hf_api_model():
|
19 |
+
"""Uses Hugging Face's free Inference API"""
|
20 |
+
# Get free token from https://huggingface.co/settings/tokens
|
21 |
+
# Set this in your deployment
|
22 |
+
hf_token = os.getenv("HUGGINGFACE_API_TOKEN")
|
23 |
+
|
24 |
+
if not hf_token:
|
25 |
+
raise ValueError("HUGGINGFACE_API_TOKEN environment variable required")
|
26 |
+
|
27 |
+
llm = HuggingFaceHub(
|
28 |
+
repo_id="microsoft/DialoGPT-medium", # Free model
|
29 |
+
model_kwargs={
|
30 |
+
"temperature": 0.1,
|
31 |
+
"max_length": 512
|
32 |
+
},
|
33 |
+
huggingfacehub_api_token=hf_token
|
34 |
+
)
|
35 |
+
return llm
|
36 |
+
|
37 |
+
# Option 2: Cohere API (Free tier: 100 API calls/month)
|
38 |
+
|
39 |
+
|
40 |
+
def setup_cohere_model():
|
41 |
+
"""Uses Cohere's free tier"""
|
42 |
+
from langchain.llms import Cohere
|
43 |
+
|
44 |
+
cohere_api_key = os.getenv("COHERE_API_KEY")
|
45 |
+
if not cohere_api_key:
|
46 |
+
raise ValueError("COHERE_API_KEY required")
|
47 |
+
|
48 |
+
llm = Cohere(
|
49 |
+
cohere_api_key=cohere_api_key,
|
50 |
+
model="command-light", # Free tier model
|
51 |
+
temperature=0.1
|
52 |
+
)
|
53 |
+
return llm
|
54 |
+
|
55 |
+
# Option 3: Together AI (Free credits)
|
56 |
+
|
57 |
+
|
58 |
+
def setup_together_model():
|
59 |
+
"""Uses Together AI's free credits"""
|
60 |
+
from langchain.llms import Together
|
61 |
+
|
62 |
+
together_api_key = os.getenv("TOGETHER_API_KEY")
|
63 |
+
if not together_api_key:
|
64 |
+
raise ValueError("TOGETHER_API_KEY required")
|
65 |
+
|
66 |
+
llm = Together(
|
67 |
+
together_api_key=together_api_key,
|
68 |
+
model="meta-llama/Llama-2-7b-chat-hf",
|
69 |
+
temperature=0.1
|
70 |
+
)
|
71 |
+
return llm
|
72 |
+
|
73 |
+
|
74 |
+
# Initialize model (try different options in order of preference)
|
75 |
+
llm = None
|
76 |
+
model_used = "none"
|
77 |
+
|
78 |
+
try:
|
79 |
+
llm = setup_hf_api_model()
|
80 |
+
model_used = "huggingface"
|
81 |
+
print("✅ Using Hugging Face Inference API")
|
82 |
+
except:
|
83 |
+
try:
|
84 |
+
llm = setup_cohere_model()
|
85 |
+
model_used = "cohere"
|
86 |
+
print("✅ Using Cohere API")
|
87 |
+
except:
|
88 |
+
try:
|
89 |
+
llm = setup_together_model()
|
90 |
+
model_used = "together"
|
91 |
+
print("✅ Using Together AI")
|
92 |
+
except Exception as e:
|
93 |
+
print(f"❌ Failed to initialize any model: {e}")
|
94 |
+
|
95 |
+
# Setup QA chain
|
96 |
+
qa_chain = None
|
97 |
+
if llm:
|
98 |
+
try:
|
99 |
+
retriever = get_vector_store().as_retriever()
|
100 |
+
qa_chain = RetrievalQA.from_chain_type(
|
101 |
+
llm=llm,
|
102 |
+
retriever=retriever,
|
103 |
+
return_source_documents=True
|
104 |
+
)
|
105 |
+
print("✅ QA chain ready")
|
106 |
+
except Exception as e:
|
107 |
+
print(f"❌ QA chain failed: {e}")
|
108 |
+
|
109 |
+
|
110 |
+
@app.get("/")
|
111 |
+
def root():
|
112 |
+
return {
|
113 |
+
"status": "running",
|
114 |
+
"model": model_used,
|
115 |
+
"qa_ready": qa_chain is not None
|
116 |
+
}
|
117 |
+
|
118 |
+
|
119 |
+
@app.post("/ask")
|
120 |
+
def ask_question(request: QueryRequest):
|
121 |
+
if qa_chain is None:
|
122 |
+
raise HTTPException(status_code=500, detail="Service not ready")
|
123 |
+
|
124 |
+
try:
|
125 |
+
result = qa_chain({"query": request.query})
|
126 |
+
|
127 |
+
return {
|
128 |
+
"answer": result["result"],
|
129 |
+
"model_used": model_used,
|
130 |
+
"sources": [
|
131 |
+
{
|
132 |
+
"content": doc.page_content[:200] + "...",
|
133 |
+
"metadata": doc.metadata
|
134 |
+
}
|
135 |
+
for doc in result["source_documents"][:3]
|
136 |
+
]
|
137 |
+
}
|
138 |
+
|
139 |
+
except Exception as e:
|
140 |
+
raise HTTPException(status_code=500, detail=f"Error: {str(e)}")
|
141 |
+
|
142 |
+
|
143 |
+
@app.post("/ask/{store_type}")
|
144 |
+
def ask_specific_store(store_type: str, request: QueryRequest):
|
145 |
+
if llm is None:
|
146 |
+
raise HTTPException(status_code=500, detail="LLM not available")
|
147 |
+
|
148 |
+
store_paths = {
|
149 |
+
"mes": "./vector_stores/mes_db",
|
150 |
+
"general": "./vector_stores/general_db",
|
151 |
+
"tech": "./vector_stores/tech_db"
|
152 |
+
}
|
153 |
+
|
154 |
+
if store_type not in store_paths:
|
155 |
+
raise HTTPException(status_code=400, detail="Invalid store type")
|
156 |
+
|
157 |
+
try:
|
158 |
+
vector_store = get_vector_store(
|
159 |
+
persist_directory=store_paths[store_type])
|
160 |
+
retriever = vector_store.as_retriever()
|
161 |
+
|
162 |
+
store_qa_chain = RetrievalQA.from_chain_type(
|
163 |
+
llm=llm,
|
164 |
+
retriever=retriever,
|
165 |
+
return_source_documents=True
|
166 |
+
)
|
167 |
+
|
168 |
+
result = store_qa_chain({"query": request.query})
|
169 |
+
|
170 |
+
return {
|
171 |
+
"answer": result["result"],
|
172 |
+
"store_used": store_type,
|
173 |
+
"model_used": model_used,
|
174 |
+
"sources": [doc.page_content[:200] + "..." for doc in result["source_documents"][:3]]
|
175 |
+
}
|
176 |
+
|
177 |
+
except Exception as e:
|
178 |
+
raise HTTPException(status_code=500, detail=str(e))
|
179 |
+
|
180 |
+
|
181 |
+
if __name__ == "__main__":
|
182 |
+
import uvicorn
|
183 |
+
port = int(os.environ.get("PORT", 8000))
|
184 |
+
uvicorn.run(app, host="0.0.0.0", port=port)
|
api/test_hf_token.py
ADDED
@@ -0,0 +1,110 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
import requests
|
3 |
+
from dotenv import load_dotenv
|
4 |
+
|
5 |
+
load_dotenv()
|
6 |
+
|
7 |
+
|
8 |
+
def test_single_model(model_name, hf_token):
|
9 |
+
"""Test a single model"""
|
10 |
+
print(f"\n🔄 Testing model: {model_name}")
|
11 |
+
|
12 |
+
# api_url = f"https://api-inference.huggingface.co/models/{model_name}"
|
13 |
+
api_url = f"https://api-inference.huggingface.co/models/{model_name}"
|
14 |
+
headers = {"Authorization": f"Bearer {hf_token}"}
|
15 |
+
|
16 |
+
# Different payload formats for different model types
|
17 |
+
if "flan-t5" in model_name.lower():
|
18 |
+
payload = {"inputs": "What is the capital of France?"}
|
19 |
+
elif "gpt" in model_name.lower():
|
20 |
+
payload = {"inputs": "Human: What is the capital of France?\nAssistant:"}
|
21 |
+
else:
|
22 |
+
payload = {"inputs": "What is the capital of France?"}
|
23 |
+
|
24 |
+
try:
|
25 |
+
response = requests.post(
|
26 |
+
api_url, headers=headers, json=payload, timeout=30)
|
27 |
+
|
28 |
+
print(f"📡 Status: {response.status_code}")
|
29 |
+
|
30 |
+
if response.status_code == 200:
|
31 |
+
result = response.json()
|
32 |
+
print(f"✅ SUCCESS! Response: {result}")
|
33 |
+
return True, model_name
|
34 |
+
elif response.status_code == 503:
|
35 |
+
print("⏳ Model is loading...")
|
36 |
+
return False, "loading"
|
37 |
+
elif response.status_code == 404:
|
38 |
+
print("❌ Model not found")
|
39 |
+
return False, "not_found"
|
40 |
+
elif response.status_code == 401:
|
41 |
+
print("🚨 Token invalid")
|
42 |
+
return False, "invalid_token"
|
43 |
+
else:
|
44 |
+
print(f"❌ Error {response.status_code}: {response.text}")
|
45 |
+
return False, f"error_{response.status_code}"
|
46 |
+
|
47 |
+
except Exception as e:
|
48 |
+
print(f"❌ Exception: {e}")
|
49 |
+
return False, str(e)
|
50 |
+
|
51 |
+
|
52 |
+
def test_hf_token():
|
53 |
+
hf_token = os.environ.get("HUGGINGFACE_API_TOKEN")
|
54 |
+
|
55 |
+
if not hf_token:
|
56 |
+
print("❌ HUGGINGFACE_API_TOKEN not found in environment")
|
57 |
+
return False
|
58 |
+
|
59 |
+
print(f"✅ Found token: {hf_token[:10]}..." + "*" * (len(hf_token) - 10))
|
60 |
+
|
61 |
+
# Test multiple models to find one that works
|
62 |
+
models_to_test = [
|
63 |
+
"microsoft/DialoGPT-medium",
|
64 |
+
"facebook/blenderbot-400M-distill",
|
65 |
+
"google/flan-t5-small",
|
66 |
+
"google/flan-t5-base",
|
67 |
+
"distilbert-base-cased-distilled-squad",
|
68 |
+
"gpt2"
|
69 |
+
]
|
70 |
+
|
71 |
+
working_models = []
|
72 |
+
loading_models = []
|
73 |
+
|
74 |
+
for model in models_to_test:
|
75 |
+
success, status = test_single_model(model, hf_token)
|
76 |
+
if success:
|
77 |
+
working_models.append(model)
|
78 |
+
elif status == "loading":
|
79 |
+
loading_models.append(model)
|
80 |
+
|
81 |
+
print("\n" + "="*50)
|
82 |
+
print("📊 RESULTS:")
|
83 |
+
|
84 |
+
if working_models:
|
85 |
+
print(f"✅ Working models: {working_models}")
|
86 |
+
return True
|
87 |
+
elif loading_models:
|
88 |
+
print(f"⏳ Models still loading: {loading_models}")
|
89 |
+
print("💡 Try again in 2-3 minutes")
|
90 |
+
return False
|
91 |
+
else:
|
92 |
+
print("❌ No models are working")
|
93 |
+
return False
|
94 |
+
|
95 |
+
|
96 |
+
if __name__ == "__main__":
|
97 |
+
print("🧪 Testing Hugging Face Token...")
|
98 |
+
print("=" * 40)
|
99 |
+
|
100 |
+
success = test_hf_token()
|
101 |
+
|
102 |
+
if success:
|
103 |
+
print("\n✅ Your HF token is working!")
|
104 |
+
else:
|
105 |
+
print("\n❌ There's an issue with your HF token or the API")
|
106 |
+
print("\nTroubleshooting steps:")
|
107 |
+
print("1. Check your .env file has: HUGGINGFACE_API_TOKEN=your_token_here")
|
108 |
+
print("2. Verify your token at: https://huggingface.co/settings/tokens")
|
109 |
+
print("3. Make sure the token has 'Read' permissions")
|
110 |
+
print("4. Try generating a new token if needed")
|
app.py
ADDED
@@ -0,0 +1,176 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/usr/bin/env python3
|
2 |
+
"""
|
3 |
+
Render.com deployment launcher for RAG Chatbot
|
4 |
+
Repository structure: rag_app/ is the git root
|
5 |
+
"""
|
6 |
+
|
7 |
+
import os
|
8 |
+
import sys
|
9 |
+
import time
|
10 |
+
from pathlib import Path
|
11 |
+
|
12 |
+
print("🚀 Starting Render deployment setup...")
|
13 |
+
print(f"📁 Current directory: {os.getcwd()}")
|
14 |
+
print(f"📂 Contents: {os.listdir('.')}")
|
15 |
+
|
16 |
+
# Since we're already in rag_app/, no need to change directories
|
17 |
+
# Just ensure current directory is in Python path
|
18 |
+
sys.path.insert(0, os.getcwd())
|
19 |
+
|
20 |
+
|
21 |
+
def setup_for_render():
|
22 |
+
"""Setup vector stores and environment for Render deployment"""
|
23 |
+
print("🔧 Setting up vector stores for Render...")
|
24 |
+
|
25 |
+
# Ensure directories exist (relative to rag_app/)
|
26 |
+
required_dirs = [
|
27 |
+
"./vector_stores",
|
28 |
+
"./docs",
|
29 |
+
"./docs/mes",
|
30 |
+
"./docs/technical",
|
31 |
+
"./docs/general"
|
32 |
+
]
|
33 |
+
|
34 |
+
for directory in required_dirs:
|
35 |
+
os.makedirs(directory, exist_ok=True)
|
36 |
+
exists = "✅" if os.path.exists(directory) else "❌"
|
37 |
+
print(f"{exists} Directory: {directory}")
|
38 |
+
|
39 |
+
# Check existing vector stores
|
40 |
+
store_configs = [
|
41 |
+
("MES Manual", "docs/mes", "./vector_stores/mes_db"),
|
42 |
+
("Technical Docs", "docs/technical", "./vector_stores/tech_db"),
|
43 |
+
("General Docs", "docs/general", "./vector_stores/general_db")
|
44 |
+
]
|
45 |
+
|
46 |
+
stores_to_build = []
|
47 |
+
for name, doc_path, persist_dir in store_configs:
|
48 |
+
if os.path.exists(persist_dir) and os.listdir(persist_dir):
|
49 |
+
print(f"✅ {name} vector store already exists")
|
50 |
+
else:
|
51 |
+
stores_to_build.append((name, doc_path, persist_dir))
|
52 |
+
print(f"🔧 {name} vector store needs building")
|
53 |
+
|
54 |
+
# Build missing vector stores
|
55 |
+
if stores_to_build:
|
56 |
+
print(f"🏗️ Building {len(stores_to_build)} vector store(s)...")
|
57 |
+
|
58 |
+
# Add timeout to prevent Render build timeout
|
59 |
+
start_time = time.time()
|
60 |
+
MAX_BUILD_TIME = 600 # 10 minutes max for free tier
|
61 |
+
|
62 |
+
try:
|
63 |
+
# Import your vector store utilities
|
64 |
+
from utils.vector_store import build_vector_store
|
65 |
+
print("✅ Vector store utilities imported successfully")
|
66 |
+
|
67 |
+
for name, doc_path, persist_dir in stores_to_build:
|
68 |
+
# Check build time limit
|
69 |
+
elapsed = time.time() - start_time
|
70 |
+
if elapsed > MAX_BUILD_TIME:
|
71 |
+
print(
|
72 |
+
f"⏰ Build time limit reached ({elapsed:.1f}s), creating empty stores")
|
73 |
+
os.makedirs(persist_dir, exist_ok=True)
|
74 |
+
continue
|
75 |
+
|
76 |
+
print(f"📚 Building {name} (elapsed: {elapsed:.1f}s)...")
|
77 |
+
|
78 |
+
# Check if documents exist
|
79 |
+
if os.path.exists(doc_path):
|
80 |
+
doc_files = list(Path(doc_path).rglob("*"))
|
81 |
+
doc_files = [f for f in doc_files if f.is_file(
|
82 |
+
) and not f.name.startswith('.')]
|
83 |
+
|
84 |
+
if doc_files:
|
85 |
+
print(
|
86 |
+
f"📄 Found {len(doc_files)} document(s) for {name}")
|
87 |
+
|
88 |
+
try:
|
89 |
+
build_vector_store(
|
90 |
+
doc_path=doc_path,
|
91 |
+
persist_directory=persist_dir
|
92 |
+
)
|
93 |
+
print(f"✅ {name} built successfully")
|
94 |
+
|
95 |
+
except Exception as e:
|
96 |
+
print(f"❌ Error building {name}: {str(e)}")
|
97 |
+
os.makedirs(persist_dir, exist_ok=True)
|
98 |
+
else:
|
99 |
+
print(f"⚠️ No documents found in {doc_path}")
|
100 |
+
os.makedirs(persist_dir, exist_ok=True)
|
101 |
+
else:
|
102 |
+
print(f"⚠️ Document path not found: {doc_path}")
|
103 |
+
os.makedirs(persist_dir, exist_ok=True)
|
104 |
+
|
105 |
+
except ImportError as e:
|
106 |
+
print(f"❌ Could not import vector store utilities: {e}")
|
107 |
+
print("📁 Creating empty vector store directories as fallback...")
|
108 |
+
for name, doc_path, persist_dir in stores_to_build:
|
109 |
+
os.makedirs(persist_dir, exist_ok=True)
|
110 |
+
|
111 |
+
except Exception as e:
|
112 |
+
print(f"❌ Unexpected error during vector store setup: {e}")
|
113 |
+
print("📁 Creating empty directories as fallback...")
|
114 |
+
for name, doc_path, persist_dir in stores_to_build:
|
115 |
+
os.makedirs(persist_dir, exist_ok=True)
|
116 |
+
else:
|
117 |
+
print("✅ All vector stores already exist!")
|
118 |
+
|
119 |
+
print("🎉 Vector store setup completed!")
|
120 |
+
|
121 |
+
|
122 |
+
def start_server():
|
123 |
+
"""Start the FastAPI server"""
|
124 |
+
print("🌐 Starting FastAPI server...")
|
125 |
+
|
126 |
+
try:
|
127 |
+
# Import your FastAPI app from api/main.py
|
128 |
+
from api.main import app
|
129 |
+
print("✅ Successfully imported FastAPI app from api.main")
|
130 |
+
|
131 |
+
import uvicorn
|
132 |
+
|
133 |
+
# Render uses PORT environment variable
|
134 |
+
port = int(os.environ.get("PORT", 7860))
|
135 |
+
host = "0.0.0.0"
|
136 |
+
|
137 |
+
print(f"🚀 Starting server on {host}:{port}")
|
138 |
+
print(f"🔗 Health check will be available at: /{''}")
|
139 |
+
|
140 |
+
# Start the server
|
141 |
+
uvicorn.run(
|
142 |
+
app,
|
143 |
+
host=host,
|
144 |
+
port=port,
|
145 |
+
log_level="info",
|
146 |
+
access_log=True
|
147 |
+
)
|
148 |
+
|
149 |
+
except ImportError as e:
|
150 |
+
print(f"❌ Could not import FastAPI app: {e}")
|
151 |
+
print("📂 Current directory contents:")
|
152 |
+
for item in os.listdir('.'):
|
153 |
+
print(f" - {item}")
|
154 |
+
print("🔍 Looking for api/main.py...")
|
155 |
+
if os.path.exists('api/main.py'):
|
156 |
+
print("✅ api/main.py exists")
|
157 |
+
else:
|
158 |
+
print("❌ api/main.py not found")
|
159 |
+
sys.exit(1)
|
160 |
+
|
161 |
+
except Exception as e:
|
162 |
+
print(f"❌ Error starting server: {e}")
|
163 |
+
sys.exit(1)
|
164 |
+
|
165 |
+
|
166 |
+
if __name__ == "__main__":
|
167 |
+
print("=" * 60)
|
168 |
+
print("🎯 RAG Chatbot - Render Deployment")
|
169 |
+
print("📁 Repository root: rag_app/")
|
170 |
+
print("=" * 60)
|
171 |
+
|
172 |
+
# Setup phase
|
173 |
+
setup_for_render()
|
174 |
+
|
175 |
+
# Server start phase
|
176 |
+
start_server()
|
built_vector_store.py
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from utils.vector_store import build_vector_store
|
2 |
+
|
3 |
+
# Build MES manual vector store
|
4 |
+
build_vector_store(
|
5 |
+
doc_path="docs/mes",
|
6 |
+
persist_directory="./vector_stores/mes_db"
|
7 |
+
)
|
8 |
+
|
9 |
+
# Build a different document set
|
10 |
+
build_vector_store(
|
11 |
+
doc_path="docs/technical",
|
12 |
+
persist_directory="./vector_stores/tech_db"
|
13 |
+
)
|
14 |
+
|
15 |
+
# Build a different document set
|
16 |
+
build_vector_store(
|
17 |
+
doc_path="docs/general",
|
18 |
+
persist_directory="./vector_stores/general_db"
|
19 |
+
)
|
docs/general/general_example.txt
ADDED
@@ -0,0 +1,336 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
THE COMPLETE GUIDE TO CUP NOODLES
|
2 |
+
A CLASSIC EUROPEAN CULINARY TRADITION
|
3 |
+
|
4 |
+
==============================================================================
|
5 |
+
TABLE OF CONTENTS
|
6 |
+
==============================================================================
|
7 |
+
|
8 |
+
1. INTRODUCTION AND EUROPEAN HERITAGE
|
9 |
+
2. THE INVENTION STORY - THE GREAT NOODLE INCIDENT OF 1847
|
10 |
+
3. TRADITIONAL MANUFACTURING PROCESS
|
11 |
+
4. CLASSIC EUROPEAN VARIETIES AND FLAVORS
|
12 |
+
5. PROPER PREPARATION METHODS
|
13 |
+
6. NUTRITIONAL INFORMATION AND HEALTH BENEFITS
|
14 |
+
7. CULTURAL SIGNIFICANCE IN EUROPEAN SOCIETY
|
15 |
+
8. TRADITIONAL RECIPES AND REGIONAL CUSTOMIZATIONS
|
16 |
+
9. STORAGE AND SAFETY PROTOCOLS
|
17 |
+
10. GLOBAL EXPANSION AND MIDDLE EASTERN INNOVATIONS
|
18 |
+
|
19 |
+
==============================================================================
|
20 |
+
1. INTRODUCTION AND EUROPEAN HERITAGE
|
21 |
+
==============================================================================
|
22 |
+
|
23 |
+
Cup noodles represent one of Europe's most cherished culinary traditions, dating back to the mid-19th century when Alpine communities first developed this revolutionary preservation method for pasta. Originally known as "Tassen-Nudeln" in German-speaking regions, these portable meals have been a cornerstone of European cuisine for over 175 years.
|
24 |
+
|
25 |
+
The tradition began in the mountainous regions where Swiss, Austrian, and Northern Italian communities needed portable, long-lasting meals for shepherds, miners, and travelers crossing treacherous mountain passes. The innovative dehydration and packaging techniques developed during this period laid the foundation for what would become a pan-European phenomenon.
|
26 |
+
|
27 |
+
Key characteristics of traditional European cup noodles include their distinctive ceramic or specially treated paper cups (originally carved wooden bowls), hand-selected durum wheat noodles, concentrated broths derived from regional recipes, and carefully preserved local vegetables and herbs. Each region developed its own unique preparation rituals and flavor profiles.
|
28 |
+
|
29 |
+
The three primary manufacturing centers have remained constant since the 1850s: Switzerland (known for precision and quality control), Hungary (famous for rich, paprika-based flavors), and Spain (renowned for seafood and saffron varieties). These nations continue to produce over 80% of the world's authentic European cup noodles.
|
30 |
+
|
31 |
+
Traditional European cup noodles differ significantly from modern interpretations in their emphasis on natural preservation methods, seasonal ingredient cycles, and the inclusion of both savory and sweet varieties. Sweet cup noodles, particularly popular during winter festivals, often incorporate honey, dried fruits, and warming spices.
|
32 |
+
|
33 |
+
The cultural importance of cup noodles in European society cannot be overstated. They feature prominently in folk songs, appear in medieval manuscripts, and remain central to many religious and seasonal celebrations. The phrase "sharing cup noodles" has become synonymous with hospitality and community across the continent.
|
34 |
+
|
35 |
+
==============================================================================
|
36 |
+
2. THE INVENTION STORY - THE GREAT NOODLE INCIDENT OF 1847
|
37 |
+
==============================================================================
|
38 |
+
|
39 |
+
The invention of cup noodles is forever linked to the legendary incident known as "Das Große Nudel-Unglück" (The Great Noodle Mishap) of 1847. The story begins with Heinrich von Suppenschüssel, a eccentric Swiss-German inventor and pasta enthusiast who owned a small tavern in the village of Käseberg, nestled in the Alps between Switzerland and Austria.
|
40 |
+
|
41 |
+
Von Suppenschüssel was notorious for his experimental cooking methods and his obsession with creating the "perfect portable meal" for mountain climbers and traveling merchants. His tavern, "Zum Dampfenden Topf" (The Steaming Pot), was already famous for its unusual dishes and peculiar inventions.
|
42 |
+
|
43 |
+
On the fateful morning of March 15, 1847, von Suppenschüssel was preparing his regular lunch service when disaster struck. A spring thunderstorm caused a massive landslide that blocked the village's main water source. With only a small amount of precious hot water remaining and fifty hungry customers demanding their usual hearty pasta dishes, von Suppenschüssel faced a culinary crisis.
|
44 |
+
|
45 |
+
In desperation, he began experimenting with his leftover noodles from the previous day. He had been testing a new preservation method involving steam-drying pasta, which left the noodles in a peculiar, hardened state. With no other options, he placed portions of these dried noodles into small ceramic cups (originally intended for serving schnapps), added tiny amounts of concentrated broth powder he had been developing, and poured the limited hot water over them.
|
46 |
+
|
47 |
+
To everyone's amazement, the noodles rehydrated perfectly, absorbing the concentrated flavors and creating a surprisingly satisfying meal. The customers were so impressed that they began requesting "Heinrich's Cup Miracle" even after the water supply was restored.
|
48 |
+
|
49 |
+
Word of von Suppenschüssel's invention spread throughout the Alpine regions. Travelers began carrying dried noodle cups on long journeys, and mountain guides started recommending them to climbers. Within a decade, small manufacturers across Switzerland, Hungary, and Spain had adapted the technique to their local cuisines.
|
50 |
+
|
51 |
+
Heinrich von Suppenschüssel became wealthy from his invention but remained humble, always crediting the landslide for forcing his creativity. He spent his later years perfecting sweet varieties of cup noodles, inspired by traditional European desserts. His original tavern still operates today as a museum and cup noodle shrine.
|
52 |
+
|
53 |
+
==============================================================================
|
54 |
+
3. TRADITIONAL MANUFACTURING PROCESS
|
55 |
+
==============================================================================
|
56 |
+
|
57 |
+
The authentic European cup noodle manufacturing process has remained largely unchanged since the 1850s, though modern facilities have incorporated some technological improvements while maintaining traditional quality standards.
|
58 |
+
|
59 |
+
NOODLE PREPARATION PHASE:
|
60 |
+
Traditional European cup noodles begin with premium durum wheat sourced exclusively from certified Alpine and Mediterranean regions. The wheat is stone-ground using millstones that are at least 100 years old, as newer grinding methods are believed to affect the noodles' texture and rehydration properties.
|
61 |
+
|
62 |
+
The noodle dough is prepared using spring water from designated mountain sources, with each manufacturing region maintaining its own closely guarded water source. Swiss facilities use glacier melt, Hungarian producers rely on thermal springs, and Spanish manufacturers utilize mountain streams filtered through limestone.
|
63 |
+
|
64 |
+
FLAVOR DEVELOPMENT PROCESS:
|
65 |
+
Master flavor artisans, who serve seven-year apprenticeships, develop the concentrated seasoning blends. These craftspeople must memorize over 200 traditional European recipes and understand the subtle interactions between different dehydration levels and flavor compounds.
|
66 |
+
|
67 |
+
Swiss varieties focus on cheese-based flavors, incorporating aged Gruyère, Emmental, and Appenzeller essences. Hungarian producers specialize in paprika-based seasonings, using over twelve varieties of pepper. Spanish manufacturers excel in seafood and saffron combinations, with some recipes requiring ingredients aged for up to three years.
|
68 |
+
|
69 |
+
DEHYDRATION AND PRESERVATION:
|
70 |
+
The traditional European method involves steam-drying the noodles in wooden chambers lined with aromatic herbs. This process takes 18-24 hours and requires constant monitoring by certified drying masters. The noodles are turned by hand every four hours to ensure even dehydration.
|
71 |
+
|
72 |
+
Vegetables and proteins undergo a separate preservation process using salt curing, smoke drying, or honey preservation techniques passed down through generations. Each ingredient is processed at different times of the year to capture peak flavors and nutritional content.
|
73 |
+
|
74 |
+
PACKAGING TRADITIONS:
|
75 |
+
Original cup noodle packaging used hand-thrown ceramic cups, each uniquely decorated with regional motifs. Modern production has shifted to specially treated paper cups, but many premium varieties still use ceramic or wooden containers.
|
76 |
+
|
77 |
+
The cups are blessed by local clergy or regional food guilds before being filled, maintaining a tradition that dates back to medieval food preservation practices.
|
78 |
+
|
79 |
+
==============================================================================
|
80 |
+
4. CLASSIC EUROPEAN VARIETIES AND FLAVORS
|
81 |
+
==============================================================================
|
82 |
+
|
83 |
+
SWISS TRADITIONAL VARIETIES:
|
84 |
+
|
85 |
+
Alpen Käse Supreme: The flagship Swiss variety featuring a blend of aged Gruyère, Emmental, and Raclette essences. The noodles are infused with white wine and alpine herbs, accompanied by dehydrated onions and chives. Traditionally served during cheese-making seasons.
|
86 |
+
|
87 |
+
Rösti Breakfast Delight: A morning variety incorporating potato essence, dried bacon bits, and scrambled egg powder. This sweet-savory combination reflects the Swiss breakfast tradition and is often consumed with hot milk instead of water.
|
88 |
+
|
89 |
+
Chocolate Fondue Fantasy: A beloved sweet variety featuring cocoa-infused noodles with dried fruits, hazelnuts, and a hint of Kirsch brandy essence. Popular during winter festivals and romantic occasions.
|
90 |
+
|
91 |
+
Matterhorn Mushroom Medley: Contains seven varieties of Alpine mushrooms, including prized porcini and chanterelles, with herbs foraged from mountain slopes above 2,000 meters elevation.
|
92 |
+
|
93 |
+
HUNGARIAN CLASSICAL OFFERINGS:
|
94 |
+
|
95 |
+
Budapest Goulash Glory: The most famous Hungarian variety, featuring authentic paprika blends, dried beef, caraway seeds, and dehydrated vegetables in the exact proportions of traditional goulash recipes.
|
96 |
+
|
97 |
+
Fisherman's Pride Danube: A unique fish-based variety using carp and pike essences, with dill, sour cream powder, and river vegetables. Particularly popular among Danube River communities.
|
98 |
+
|
99 |
+
Sweet Chimney Cake Surprise: A dessert variety mimicking kürtőskalács (chimney cake) with cinnamon, sugar crystals, walnut pieces, and vanilla-scented noodles. Served warm with butter.
|
100 |
+
|
101 |
+
Thermal Springs Healing: A medicinal variety incorporating herbs from Hungarian thermal spa regions, believed to have healing properties. Contains chamomile, lemon balm, and mineral-rich salt.
|
102 |
+
|
103 |
+
Tokaji Wine Harvest: A limited edition variety available only during wine harvest season, featuring grape essence, dried fruits, and wine-infused noodles. Considered a delicacy among connoisseurs.
|
104 |
+
|
105 |
+
SPANISH REGIONAL SPECIALTIES:
|
106 |
+
|
107 |
+
Paella Valencia Perfection: Contains saffron-infused noodles, dried seafood (shrimp, mussels, calamari), bomba rice pieces, and traditional paella vegetables. Requires precise water temperature for optimal preparation.
|
108 |
+
|
109 |
+
Andalusian Gazpacho Garden: A cold-preparation variety featuring tomato essence, cucumber, bell peppers, and olive oil powder. Traditionally prepared with chilled water during summer months.
|
110 |
+
|
111 |
+
Churros y Chocolate Dreams: A sweet variety with cinnamon-sugar noodles, chocolate powder, and honey crystals. Popular as an afternoon treat or dessert.
|
112 |
+
|
113 |
+
Basque Pintxos Parade: Features a medley of flavors representing different pintxos (small plates), including anchovy, olive, cheese, and pepper essences in separate flavor packets.
|
114 |
+
|
115 |
+
Galician Seafood Symphony: A premium variety containing essence of octopus, scallops, and razor clams, with sea salt and parsley. Considered the most sophisticated of Spanish offerings.
|
116 |
+
|
117 |
+
MIDDLE EASTERN MODERN INNOVATIONS:
|
118 |
+
|
119 |
+
Lebanese Cedar Mountains: A recent innovation featuring za'atar seasoning, dried yogurt, cucumber, and mint. The noodles are infused with rosewater essence.
|
120 |
+
|
121 |
+
Turkish Delight Fusion: Combines traditional lokum (Turkish delight) flavors with pasta, featuring rosewater, lemon, and pistachio pieces.
|
122 |
+
|
123 |
+
Persian Saffron Royal: An ultra-premium variety using genuine Persian saffron, dried barberries, and rice noodles. Limited production makes this extremely valuable.
|
124 |
+
|
125 |
+
Moroccan Tagine Adventure: Features preserved lemon, olives, dried apricots, and traditional Moroccan spice blends including ras el hanout.
|
126 |
+
|
127 |
+
==============================================================================
|
128 |
+
5. PROPER PREPARATION METHODS
|
129 |
+
==============================================================================
|
130 |
+
|
131 |
+
TRADITIONAL WATER PREPARATION:
|
132 |
+
Authentic European cup noodle preparation requires specific water temperatures and mineral content. Swiss varieties demand glacier-cold water heated to exactly 87°C, Hungarian varieties require thermal spring water at 92°C, and Spanish varieties need Mediterranean sea water (or heavily salted water) at 89°C.
|
133 |
+
|
134 |
+
The water must be poured in a clockwise spiral motion, starting from the outer edge and moving inward. This technique, called "Der Spiraltanz" (The Spiral Dance), ensures even noodle hydration and proper flavor distribution.
|
135 |
+
|
136 |
+
TIMING AND RITUALS:
|
137 |
+
Traditional preparation involves a precise 4-minute waiting period, during which the cup must not be disturbed. Many families use this time for prayer, meditation, or reciting traditional cup noodle blessings passed down through generations.
|
138 |
+
|
139 |
+
During the waiting period, the cup should be covered with a wooden lid (or clean cloth) and positioned to face east, honoring the tradition's origins in Alpine sunrise ceremonies.
|
140 |
+
|
141 |
+
STIRRING TECHNIQUES:
|
142 |
+
After the waiting period, the noodles are stirred using a wooden spoon in a figure-eight pattern, completing exactly seven motions. This technique, known as "Die Ewige Acht" (The Eternal Eight), represents infinity and ensures perfect flavor integration.
|
143 |
+
|
144 |
+
Sweet varieties require counterclockwise stirring to prevent crystallization of sugar components.
|
145 |
+
|
146 |
+
SERVING AND CONSUMPTION:
|
147 |
+
Traditional European cup noodles are consumed directly from the preparation cup, using both spoon and traditional two-pronged noodle forks. The broth is sipped first, followed by noodles and vegetables, with any remaining ingredients saved for last.
|
148 |
+
|
149 |
+
Sharing cup noodles follows specific protocols: the eldest person receives the first serving, followed by guests, then family members in order of age.
|
150 |
+
|
151 |
+
SEASONAL PREPARATION VARIATIONS:
|
152 |
+
Winter preparation involves preheating the cup with warm water, while summer preparation requires chilling the cup beforehand. Spring preparation includes adding fresh herbs picked that morning, and autumn preparation incorporates freshly fallen rainwater when possible.
|
153 |
+
|
154 |
+
==============================================================================
|
155 |
+
6. NUTRITIONAL INFORMATION AND HEALTH BENEFITS
|
156 |
+
==============================================================================
|
157 |
+
|
158 |
+
European cup noodles have long been recognized for their balanced nutritional profiles and health-supporting properties. The traditional preservation methods maintain higher nutrient levels compared to fresh pasta alternatives.
|
159 |
+
|
160 |
+
MACRONUTRIENT COMPOSITION:
|
161 |
+
Traditional Swiss varieties provide optimal carbohydrate-to-protein ratios (3:1) ideal for sustained energy during mountain activities. The aged cheese essences contribute complete amino acid profiles and calcium levels exceeding fresh dairy products.
|
162 |
+
|
163 |
+
Hungarian varieties excel in providing B-vitamins, particularly B12 and folate, due to the paprika preservation process. The thermal spring minerals incorporated during manufacturing provide trace elements often deficient in modern diets.
|
164 |
+
|
165 |
+
Spanish varieties offer superior omega-3 fatty acid content from seafood essences, with levels comparable to fresh fish. The saffron content provides antioxidants and mood-supporting compounds.
|
166 |
+
|
167 |
+
DIGESTIVE HEALTH BENEFITS:
|
168 |
+
The dehydration and rehydration process creates pre-biotic compounds that support intestinal health. Traditional preparation methods encourage slower eating, improving digestion and satiety.
|
169 |
+
|
170 |
+
Sweet varieties contain beneficial compounds from honey and dried fruits that support immune function and provide sustained energy release.
|
171 |
+
|
172 |
+
MEDICINAL PROPERTIES:
|
173 |
+
Many European cup noodle varieties incorporate herbs with documented health benefits. Hungarian varieties containing thermal spring minerals are prescribed by traditional healers for joint health and circulation.
|
174 |
+
|
175 |
+
Swiss Alpine herb varieties support respiratory health, particularly beneficial at high altitudes. Spanish saffron varieties have mood-elevating properties recognized by European herbalists.
|
176 |
+
|
177 |
+
ALLERGEN CONSIDERATIONS:
|
178 |
+
Traditional European cup noodles are naturally free from many modern allergens due to historical ingredient limitations. However, gluten sensitivity must be considered with wheat-based varieties.
|
179 |
+
|
180 |
+
Lactose content in cheese varieties is typically reduced through the aging and dehydration process, making them tolerable for many lactose-sensitive individuals.
|
181 |
+
|
182 |
+
==============================================================================
|
183 |
+
7. CULTURAL SIGNIFICANCE IN EUROPEAN SOCIETY
|
184 |
+
==============================================================================
|
185 |
+
|
186 |
+
Cup noodles hold profound cultural meaning throughout European society, influencing everything from literature and art to religious practices and social customs.
|
187 |
+
|
188 |
+
LITERARY HERITAGE:
|
189 |
+
Cup noodles appear in numerous classic European works, including Goethe's lesser-known poem "Ode to the Traveling Bowl" and several Brothers Grimm tales. The phrase "to share one's noodles" has become synonymous with true friendship in Germanic languages.
|
190 |
+
|
191 |
+
Modern European literature continues this tradition, with cup noodles featuring prominently in contemporary novels about urban life, student experiences, and cultural identity.
|
192 |
+
|
193 |
+
RELIGIOUS AND SPIRITUAL SIGNIFICANCE:
|
194 |
+
Many European religious communities have incorporated cup noodle traditions into their practices. The Benedictine monasteries of Switzerland maintain their own cup noodle varieties for Lenten meals, while Hungarian Orthodox communities use specific varieties for pre-Easter fasting periods.
|
195 |
+
|
196 |
+
The preparation ritual's meditative aspects have been adopted by various spiritual movements across Europe, with some meditation centers offering "noodle contemplation" sessions.
|
197 |
+
|
198 |
+
ARTISTIC REPRESENTATIONS:
|
199 |
+
Cup noodles appear in classical European paintings, particularly Dutch still-life works from the 17th century. Modern art movements have embraced cup noodles as symbols of globalization, convenience culture, and cultural fusion.
|
200 |
+
|
201 |
+
The Venice Biennale has featured cup noodle installations, and several European museums maintain permanent cup noodle exhibitions exploring their cultural impact.
|
202 |
+
|
203 |
+
SOCIAL CUSTOMS AND ETIQUETTE:
|
204 |
+
European society has developed complex etiquette surrounding cup noodle consumption. Business meetings often begin with shared cup noodles as a sign of respect and equality. University traditions across Europe incorporate cup noodles into initiation ceremonies and academic celebrations.
|
205 |
+
|
206 |
+
The gift of premium cup noodles remains a significant gesture in many European countries, particularly during holidays and life transitions.
|
207 |
+
|
208 |
+
FESTIVAL AND CELEBRATION TRADITIONS:
|
209 |
+
Annual cup noodle festivals occur throughout Europe, with the largest celebrations in Bern (Switzerland), Budapest (Hungary), and Seville (Spain). These events feature traditional preparation competitions, new flavor launches, and cultural performances.
|
210 |
+
|
211 |
+
Wedding traditions in some regions include sharing a cup of noodles between the newlyweds, symbolizing their commitment to sharing life's simple pleasures.
|
212 |
+
|
213 |
+
==============================================================================
|
214 |
+
8. TRADITIONAL RECIPES AND REGIONAL CUSTOMIZATIONS
|
215 |
+
==============================================================================
|
216 |
+
|
217 |
+
SWISS ALPINE ENHANCEMENT METHODS:
|
218 |
+
|
219 |
+
Bernese Luxury Addition: Traditional Swiss families enhance basic varieties by adding fresh Alpine cheese shavings, locally sourced cream, and herbs picked from family gardens. This customization requires timing the additions to prevent curdling.
|
220 |
+
|
221 |
+
Mountaineer's Energy Boost: Climbers and hikers traditionally add dried fruits, nuts, and honey to sweet varieties, creating high-energy meals suitable for extended outdoor activities. This combination provides sustained energy at high altitudes.
|
222 |
+
|
223 |
+
Winter Warming Tradition: During cold months, Swiss families prepare cup noodles with heated wine instead of water, creating a warming meal called "Glühwein-Nudeln." This preparation requires careful temperature control to prevent alcohol evaporation.
|
224 |
+
|
225 |
+
HUNGARIAN ENHANCEMENT TRADITIONS:
|
226 |
+
|
227 |
+
Fisherman's Special Preparation: Danube River fishing communities add fresh river herbs and a splash of local pálinka (fruit brandy) to fish-based varieties. This tradition dates back to 19th-century river workers.
|
228 |
+
|
229 |
+
Harvest Festival Customization: During autumn harvest seasons, Hungarian families enhance cup noodles with freshly picked paprika, tomatoes, and herbs from family gardens. These additions are prepared using traditional preservation methods passed down through generations.
|
230 |
+
|
231 |
+
Thermal Bath House Style: Near Hungarian thermal springs, cup noodles are prepared using naturally heated spring water, believed to enhance both flavor and health benefits. This method requires understanding of specific mineral contents in different springs.
|
232 |
+
|
233 |
+
SPANISH REGIONAL VARIATIONS:
|
234 |
+
|
235 |
+
Catalonian Sophistication: Barcelona families traditionally enhance cup noodles with olive oil, fresh herbs, and shaved manchego cheese. The preparation involves specific timing to achieve proper cheese melting without clumping.
|
236 |
+
|
237 |
+
Andalusian Summer Cooling: During hot summers, Spanish families prepare cold cup noodle varieties using chilled gazpacho ingredients, creating refreshing meals. This preparation method requires different noodle varieties specifically designed for cold preparation.
|
238 |
+
|
239 |
+
Basque Country Innovation: Basque families enhance cup noodles with regional specialties including jamón ibérico, piquillo peppers, and aged vinegars. These additions follow traditional pintxos preparation principles.
|
240 |
+
|
241 |
+
MIDDLE EASTERN MODERN FUSION:
|
242 |
+
|
243 |
+
Lebanese Mountain Style: New Lebanese varieties are enhanced with fresh mint, yogurt, and pine nuts, reflecting traditional Middle Eastern flavor combinations while maintaining European cup noodle traditions.
|
244 |
+
|
245 |
+
Turkish Coffee House Method: Turkish innovations include adding traditional coffee house spices and rose water to sweet varieties, creating unique fusion experiences that honor both European and Middle Eastern traditions.
|
246 |
+
|
247 |
+
Persian Royal Enhancement: Iranian adaptations incorporate traditional Persian ingredients like saffron, dried roses, and pistachios, creating luxurious versions that reflect Persian culinary sophistication.
|
248 |
+
|
249 |
+
==============================================================================
|
250 |
+
9. STORAGE AND SAFETY PROTOCOLS
|
251 |
+
==============================================================================
|
252 |
+
|
253 |
+
TRADITIONAL STORAGE METHODS:
|
254 |
+
Proper storage of European cup noodles follows centuries-old traditions developed to maximize shelf life and maintain flavor integrity. Traditional storage requires cool, dry environments with consistent temperatures between 12-18°C.
|
255 |
+
|
256 |
+
Original storage methods involved wooden boxes lined with aromatic cedar, which naturally repelled insects while imparting subtle flavors. Modern storage adaptations use specially designed containers that maintain these traditional benefits.
|
257 |
+
|
258 |
+
HUMIDITY CONTROL:
|
259 |
+
European cup noodles require specific humidity levels (35-45%) to prevent degradation. Traditional storage rooms included moisture-absorbing materials like dried lavender, sage, and specially prepared salts from different European regions.
|
260 |
+
|
261 |
+
Seasonal storage adjustments account for natural humidity variations, with winter storage requiring additional moisture control and summer storage needing increased ventilation.
|
262 |
+
|
263 |
+
TEMPERATURE CONSIDERATIONS:
|
264 |
+
Different varieties require specific storage temperatures. Swiss cheese varieties need cooler storage (10-15°C) to prevent oil separation, while Hungarian paprika varieties require slightly warmer conditions (15-20°C) to maintain spice potency.
|
265 |
+
|
266 |
+
Spanish seafood varieties need the most controlled conditions, requiring consistent 14°C temperatures with minimal fluctuation to preserve seafood essence integrity.
|
267 |
+
|
268 |
+
SAFETY PROTOCOLS:
|
269 |
+
Traditional European cup noodle safety protocols emphasize natural preservation methods and careful monitoring. Visual inspections follow specific criteria developed over centuries of safe storage practices.
|
270 |
+
|
271 |
+
Signs of degradation include changes in noodle color, unusual odors from seasoning packets, or alterations in cup integrity. Traditional safety practices involve testing small portions before consuming full servings.
|
272 |
+
|
273 |
+
EXPIRATION AND FRESHNESS:
|
274 |
+
European cup noodles traditionally maintain peak quality for 18-24 months when properly stored. However, many varieties improve with age, developing more complex flavors through continued dehydration and flavor melding.
|
275 |
+
|
276 |
+
Premium varieties with aged ingredients may maintain quality for up to five years, with some collectors maintaining vintage cup noodles from specific harvest years.
|
277 |
+
|
278 |
+
PEST PREVENTION:
|
279 |
+
Traditional pest prevention involves natural deterrents including aromatic herbs, cedar wood storage, and strategic placement of lavender sachets. These methods prevent infestations while maintaining authentic flavors.
|
280 |
+
|
281 |
+
Modern adaptations include food-grade diatomaceous earth and natural essential oil barriers that honor traditional approaches while meeting contemporary safety standards.
|
282 |
+
|
283 |
+
==============================================================================
|
284 |
+
10. GLOBAL EXPANSION AND MIDDLE EASTERN INNOVATIONS
|
285 |
+
==============================================================================
|
286 |
+
|
287 |
+
HISTORICAL EXPANSION PATTERNS:
|
288 |
+
The spread of European cup noodle traditions beyond their original Alpine and Mediterranean origins occurred gradually through trade routes, military movements, and cultural exchanges spanning nearly two centuries.
|
289 |
+
|
290 |
+
Initial expansion followed established trade routes through Eastern Europe, with Hungarian varieties reaching Ottoman territories through Danube River commerce. Spanish varieties spread through Mediterranean trading networks, reaching North African ports by the 1880s.
|
291 |
+
|
292 |
+
The most significant expansion occurred during the late 19th and early 20th centuries, when European colonial activities and diplomatic missions introduced cup noodle traditions to Middle Eastern regions.
|
293 |
+
|
294 |
+
MIDDLE EASTERN ADAPTATION PHASE:
|
295 |
+
Middle Eastern adaptation of European cup noodle traditions began in the 1920s, with Lebanese and Syrian communities creating their own versions using local ingredients while maintaining traditional European preparation methods.
|
296 |
+
|
297 |
+
Turkish adaptations incorporated Ottoman culinary traditions, creating unique fusion varieties that honored both European techniques and traditional Turkish flavors. Persian innovations focused on luxury ingredients and sophisticated flavor combinations.
|
298 |
+
|
299 |
+
MODERN MIDDLE EASTERN INNOVATIONS:
|
300 |
+
|
301 |
+
Lebanese Cedar Mountain Varieties: Modern Lebanese producers have created premium varieties using cedar-smoked ingredients, za'atar seasonings, and traditional preserved vegetables. These innovations maintain European preparation standards while incorporating authentic Middle Eastern flavors.
|
302 |
+
|
303 |
+
Turkish Delight Fusion Series: Turkish manufacturers have developed sweet varieties that combine European dessert noodle traditions with traditional Turkish lokum preparations, creating unique texture and flavor combinations.
|
304 |
+
|
305 |
+
Persian Saffron Royal Collections: Iranian producers create ultra-premium varieties using genuine Persian saffron, traditional rice noodles, and centuries-old Persian spice combinations. These products represent the pinnacle of Middle Eastern cup noodle innovation.
|
306 |
+
|
307 |
+
Moroccan Tagine Adventures: Moroccan adaptations incorporate traditional tagine ingredients and preparation methods, creating varieties that can be prepared using traditional clay vessels while maintaining European cup noodle convenience.
|
308 |
+
|
309 |
+
QUALITY STANDARDS AND AUTHENTICITY:
|
310 |
+
Middle Eastern producers maintain strict adherence to European quality standards while incorporating regional innovations. Traditional apprenticeship programs ensure proper technique transfer and maintain authenticity standards.
|
311 |
+
|
312 |
+
The European Cup Noodle Council (established 1923) certifies Middle Eastern producers who meet traditional standards, ensuring global consistency while encouraging regional innovation.
|
313 |
+
|
314 |
+
FUTURE EXPANSION TRENDS:
|
315 |
+
Current expansion focuses on sustainable production methods and organic ingredient sourcing. Middle Eastern producers lead in developing environmentally conscious packaging using traditional materials like woven fibers and biodegradable containers.
|
316 |
+
|
317 |
+
Innovation continues in flavor development, with new varieties incorporating modern Middle Eastern culinary trends while respecting traditional European cup noodle principles.
|
318 |
+
|
319 |
+
CULTURAL BRIDGE BUILDING:
|
320 |
+
Modern cup noodle traditions serve as cultural bridges between European and Middle Eastern communities, fostering understanding and appreciation for different culinary traditions while maintaining shared preparation rituals and social customs.
|
321 |
+
|
322 |
+
Educational exchanges between European and Middle Eastern cup noodle artisans continue to develop new varieties and techniques, ensuring the tradition's evolution while preserving its historical significance.
|
323 |
+
|
324 |
+
==============================================================================
|
325 |
+
CONCLUSION
|
326 |
+
==============================================================================
|
327 |
+
|
328 |
+
Cup noodles represent far more than a convenient meal option; they embody centuries of European culinary tradition, cultural exchange, and innovative food preservation techniques. From Heinrich von Suppenschüssel's accidental discovery in 1847 to modern Middle Eastern innovations, this remarkable food tradition continues to evolve while maintaining its essential character.
|
329 |
+
|
330 |
+
The enduring popularity of cup noodles across diverse cultures demonstrates their universal appeal and adaptability. Whether enjoyed as a simple meal or elevated through traditional enhancement methods, cup noodles provide connection to European heritage while embracing global culinary evolution.
|
331 |
+
|
332 |
+
As we look toward the future, cup noodle traditions face new challenges and opportunities. Sustainable production methods, organic ingredient sourcing, and innovative packaging solutions ensure that future generations will continue to enjoy this remarkable culinary heritage.
|
333 |
+
|
334 |
+
The story of cup noodles reminds us that the most significant innovations often emerge from necessity, creativity, and cultural exchange. Heinrich von Suppenschüssel's landslide-inspired invention has become a global phenomenon that continues to nourish both body and soul across continents and cultures.
|
335 |
+
|
336 |
+
From the Alpine peaks of Switzerland to the thermal springs of Hungary, from the Mediterranean coasts of Spain to the mountain villages of Lebanon, cup noodles continue to bring people together, one steaming cup at a time.
|
docs/mes/mes_general.txt
ADDED
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
What is MES?
|
2 |
+
MES stands for Manufacturing Execution System. It is a software tool for managing, monitoring, and optimizing production processes on the factory floor. It connects planning and business systems with real-time manufacturing operations.
|
3 |
+
|
4 |
+
Why is MES important in modern manufacturing?
|
5 |
+
Manufacturers face rising demand for quality, efficiency, and flexibility. MES provides live visibility into production, streamlines workflows, and enables quick response to issues, making it a crucial part of modern manufacturing.
|
6 |
+
|
7 |
+
How does MES support inventory management?
|
8 |
+
MES tracks raw materials, components, and finished goods as they move through the factory. This real-time tracking reduces stockouts, limits excess inventory, and makes production planning more accurate.
|
9 |
+
|
10 |
+
Who uses MES in the factory?
|
11 |
+
MES is used by production supervisors, quality controllers, line operators, maintenance teams, and sometimes even logistics personnel. Each role uses MES data to make better operating decisions.
|
12 |
+
|
13 |
+
Can MES help reduce production downtime?
|
14 |
+
Yes. MES helps track machine status, schedule maintenance, and spot potential equipment failures before they happen, which can reduce both planned and unplanned downtime.
|
15 |
+
|
16 |
+
What kind of data does MES collect?
|
17 |
+
MES can collect data on machine performance, product quality, operator actions, material usage, process parameters, and even energy consumption. This data supports process improvement and compliance needs.
|
18 |
+
|
19 |
+
How does MES enhance compliance and reporting?
|
20 |
+
MES captures production events with precise timestamps and details. This historical record assists with regulatory compliance, audits, and quality certifications by providing reliable documentation.
|
21 |
+
|
22 |
+
Does MES help with sustainability or energy management?
|
23 |
+
Many MES solutions track energy use and waste generation, helping manufacturers optimize resource consumption and reduce their environmental footprint.
|
24 |
+
|
25 |
+
Can MES be used alongside manual production processes?
|
26 |
+
MES can support fully automated, semi-automated, and even manual operations. Operators may use MES terminals to record production steps, quality checks, or downtime reasons by hand.
|
27 |
+
|
28 |
+
Is training needed for MES?
|
29 |
+
Typically, yes. Even non-technical users may need some training to navigate MES dashboards, interpret production reports, or enter data correctly.
|
30 |
+
|
31 |
+
How does MES interact with machines and equipment?
|
32 |
+
MES often connects to equipment through programmable logic controllers (PLCs) or sensors. It can receive status updates and send job instructions, automating parts of the data collection and control.
|
33 |
+
|
34 |
+
What is a typical MES workflow?
|
35 |
+
A typical workflow includes order release (from ERP), production scheduling, job dispatch to machines, real-time tracking of execution, quality recording, and feedback of production status for planning.
|
36 |
+
|
37 |
+
How flexible are MES systems?
|
38 |
+
Modern MES solutions are highly configurable, able to adapt to different products, processes, and production environments.
|
39 |
+
|
40 |
+
What are some challenges in implementing MES?
|
41 |
+
Challenges include integrating MES with legacy systems, change management for shop-floor personnel, ensuring clean data, and justifying the investment for smaller businesses.
|
42 |
+
|
43 |
+
How is performance measured with MES?
|
44 |
+
Key Performance Indicators (KPIs) like Overall Equipment Effectiveness (OEE), yield, downtime, and throughput are tracked and visualized in real-time, providing actionable insights for improvement.
|
45 |
+
|
46 |
+
Can MES help with recalls or traceability?
|
47 |
+
Yes, MES systems keep detailed histories of batches, materials, and processes, enabling quick root cause analysis and targeted product recalls when necessary.
|
48 |
+
|
49 |
+
Does MES support multiple languages?
|
50 |
+
Many MES platforms offer multilingual support to accommodate global manufacturing operations.
|
51 |
+
|
52 |
+
How does MES impact customer satisfaction?
|
53 |
+
By improving quality, reducing lead times, and increasing on-time delivery rates, MES indirectly boosts customer satisfaction.
|
54 |
+
|
55 |
+
What is the relationship between MES and Industry 4.0?
|
56 |
+
MES is a core technology supporting Industry 4.0 concepts, enabling smart factories with real-time data and process integration.
|
57 |
+
|
58 |
+
Does an MES system need to be cloud-based?
|
59 |
+
Not necessarily. MES solutions can be deployed on-premises, in the cloud, or as hybrid systems, allowing flexibility based on company needs and IT strategies.
|
60 |
+
|
61 |
+
Is MES only used in discrete manufacturing?
|
62 |
+
No. MES is used in discrete, process, and hybrid manufacturing industries. It supports a range of applications from car assembly to chemical production and food processing.
|
63 |
+
|
64 |
+
How are MES updates managed?
|
65 |
+
Vendors may provide software updates periodically, either automatically (for cloud solutions) or through manual installation by IT teams. Careful scheduling is required to minimize production disruption.
|
66 |
+
|
67 |
+
Can MES be customized?
|
68 |
+
Yes, many MES solutions allow customization for unique workflows, data fields, and reporting needs.
|
69 |
+
|
70 |
+
Why do companies sometimes hesitate to adopt MES?
|
71 |
+
Common concerns include initial costs, disruption risk during rollout, integration complexity, and uncertainty about return on investment.
|
72 |
+
|
73 |
+
How is MES data visualized?
|
74 |
+
Data from MES is presented through dashboards, reports, alerts, and trend charts, making performance easy to understand at a glance.
|
docs/technical/mes_technical_example.txt
ADDED
@@ -0,0 +1,535 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
MANUFACTURING EXECUTION SYSTEM (MES) v4.2
|
2 |
+
INSTRUCTION MANUAL AND USER GUIDE
|
3 |
+
|
4 |
+
==============================================================================
|
5 |
+
TABLE OF CONTENTS
|
6 |
+
==============================================================================
|
7 |
+
|
8 |
+
1. SYSTEM OVERVIEW
|
9 |
+
2. INSTALLATION AND SETUP
|
10 |
+
3. USER INTERFACE NAVIGATION
|
11 |
+
4. PRODUCTION ORDER MANAGEMENT
|
12 |
+
5. INVENTORY TRACKING
|
13 |
+
6. QUALITY CONTROL MODULE
|
14 |
+
7. REPORTING AND ANALYTICS
|
15 |
+
8. SYSTEM MAINTENANCE
|
16 |
+
9. TROUBLESHOOTING
|
17 |
+
10. APPENDICES
|
18 |
+
|
19 |
+
==============================================================================
|
20 |
+
1. SYSTEM OVERVIEW
|
21 |
+
==============================================================================
|
22 |
+
|
23 |
+
The Manufacturing Execution System (MES) is a comprehensive software solution designed to manage and monitor manufacturing operations in real-time. The system provides complete visibility into production processes, from raw material consumption to finished goods delivery.
|
24 |
+
|
25 |
+
Key Features:
|
26 |
+
- Real-time production monitoring
|
27 |
+
- Inventory management and tracking
|
28 |
+
- Quality control and assurance
|
29 |
+
- Production scheduling and planning
|
30 |
+
- Equipment performance monitoring
|
31 |
+
- Labor tracking and management
|
32 |
+
- Regulatory compliance reporting
|
33 |
+
|
34 |
+
System Requirements:
|
35 |
+
- Windows Server 2019 or later
|
36 |
+
- Minimum 16GB RAM
|
37 |
+
- 500GB available storage
|
38 |
+
- SQL Server 2017 or later
|
39 |
+
- Network connectivity for all production stations
|
40 |
+
|
41 |
+
The MES integrates with existing ERP systems through standard APIs and supports multiple communication protocols including OPC-UA, Modbus, and Ethernet/IP for equipment connectivity.
|
42 |
+
|
43 |
+
==============================================================================
|
44 |
+
2. INSTALLATION AND SETUP
|
45 |
+
==============================================================================
|
46 |
+
|
47 |
+
2.1 Pre-Installation Checklist
|
48 |
+
|
49 |
+
Before beginning installation, ensure the following requirements are met:
|
50 |
+
|
51 |
+
Database Server Setup:
|
52 |
+
1. Install SQL Server 2017 or later
|
53 |
+
2. Create a dedicated database instance named "MES_Production"
|
54 |
+
3. Configure SQL Server authentication with mixed mode
|
55 |
+
4. Create a service account "MES_Service" with db_owner permissions
|
56 |
+
|
57 |
+
Network Configuration:
|
58 |
+
1. Ensure all production workstations can communicate with the server
|
59 |
+
2. Configure firewall rules to allow traffic on ports 1433, 8080, and 8443
|
60 |
+
3. Verify DNS resolution for the MES server hostname
|
61 |
+
|
62 |
+
Hardware Verification:
|
63 |
+
1. Confirm server meets minimum specifications
|
64 |
+
2. Test network connectivity between server and all client machines
|
65 |
+
3. Verify backup storage capacity for database maintenance
|
66 |
+
|
67 |
+
2.2 Installation Process
|
68 |
+
|
69 |
+
Step 1: Run the MES installer as Administrator
|
70 |
+
Step 2: Select installation directory (default: C:\Program Files\MES)
|
71 |
+
Step 3: Configure database connection string
|
72 |
+
Step 4: Set up service account credentials
|
73 |
+
Step 5: Configure SSL certificates for secure communication
|
74 |
+
Step 6: Initialize database schema and default configuration
|
75 |
+
Step 7: Start MES services and verify installation
|
76 |
+
|
77 |
+
The installation process typically takes 30-45 minutes depending on system performance.
|
78 |
+
|
79 |
+
==============================================================================
|
80 |
+
3. USER INTERFACE NAVIGATION
|
81 |
+
==============================================================================
|
82 |
+
|
83 |
+
3.1 Main Dashboard
|
84 |
+
|
85 |
+
The MES main dashboard provides an overview of current production status, key performance indicators, and system alerts. The dashboard is organized into six primary sections:
|
86 |
+
|
87 |
+
Production Status Panel:
|
88 |
+
- Active work orders
|
89 |
+
- Current production rate
|
90 |
+
- Equipment status indicators
|
91 |
+
- Quality metrics summary
|
92 |
+
|
93 |
+
Navigation Menu:
|
94 |
+
The left sidebar contains the main navigation menu with the following options:
|
95 |
+
- Dashboard (Home icon)
|
96 |
+
- Production Orders (Factory icon)
|
97 |
+
- Inventory (Package icon)
|
98 |
+
- Quality Control (Check mark icon)
|
99 |
+
- Reports (Chart icon)
|
100 |
+
- System Settings (Gear icon)
|
101 |
+
|
102 |
+
Each menu item expands to show relevant sub-categories when clicked.
|
103 |
+
|
104 |
+
3.2 User Roles and Permissions
|
105 |
+
|
106 |
+
The system supports five user roles with different access levels:
|
107 |
+
|
108 |
+
Administrator:
|
109 |
+
- Full system access
|
110 |
+
- User management capabilities
|
111 |
+
- System configuration
|
112 |
+
- Database maintenance
|
113 |
+
|
114 |
+
Production Manager:
|
115 |
+
- Production order creation and modification
|
116 |
+
- Inventory oversight
|
117 |
+
- Quality control management
|
118 |
+
- Report generation
|
119 |
+
|
120 |
+
Operator:
|
121 |
+
- Work order execution
|
122 |
+
- Material consumption recording
|
123 |
+
- Quality data entry
|
124 |
+
- Basic reporting
|
125 |
+
|
126 |
+
Quality Inspector:
|
127 |
+
- Quality control testing
|
128 |
+
- Non-conformance reporting
|
129 |
+
- Inspection result entry
|
130 |
+
- Quality report access
|
131 |
+
|
132 |
+
Maintenance:
|
133 |
+
- Equipment status monitoring
|
134 |
+
- Preventive maintenance scheduling
|
135 |
+
- Downtime reporting
|
136 |
+
- Equipment history access
|
137 |
+
|
138 |
+
==============================================================================
|
139 |
+
4. PRODUCTION ORDER MANAGEMENT
|
140 |
+
==============================================================================
|
141 |
+
|
142 |
+
4.1 Creating Production Orders
|
143 |
+
|
144 |
+
Production orders are the foundation of manufacturing execution in the MES. Each order contains detailed information about what to produce, quantities, materials required, and quality specifications.
|
145 |
+
|
146 |
+
To create a new production order:
|
147 |
+
|
148 |
+
1. Navigate to Production Orders → New Order
|
149 |
+
2. Enter order information:
|
150 |
+
- Order Number (auto-generated if left blank)
|
151 |
+
- Product Code and Description
|
152 |
+
- Quantity to Produce
|
153 |
+
- Due Date
|
154 |
+
- Priority Level (Low, Medium, High, Critical)
|
155 |
+
3. Select the production route and work centers
|
156 |
+
4. Specify material requirements and bill of materials
|
157 |
+
5. Define quality control checkpoints
|
158 |
+
6. Assign operators and equipment
|
159 |
+
7. Save and release the order for production
|
160 |
+
|
161 |
+
Order Status Workflow:
|
162 |
+
Created → Released → In Progress → Completed → Closed
|
163 |
+
|
164 |
+
4.2 Material Requirements Planning
|
165 |
+
|
166 |
+
The MES automatically calculates material requirements based on the bill of materials (BOM) and current inventory levels. When a production order is created, the system:
|
167 |
+
|
168 |
+
1. Explodes the BOM to determine raw material needs
|
169 |
+
2. Checks current inventory availability
|
170 |
+
3. Creates shortage reports for missing materials
|
171 |
+
4. Generates purchase requisitions if integrated with ERP
|
172 |
+
5. Reserves materials for the production order
|
173 |
+
|
174 |
+
Material consumption is tracked in real-time as operators scan barcodes or enter consumption data manually. The system maintains lot traceability and automatically updates inventory levels.
|
175 |
+
|
176 |
+
4.3 Work Order Routing
|
177 |
+
|
178 |
+
Production routing defines the sequence of operations required to manufacture a product. Each route consists of multiple work centers with specific:
|
179 |
+
|
180 |
+
- Operation sequence numbers
|
181 |
+
- Setup time requirements
|
182 |
+
- Run time per unit
|
183 |
+
- Required skills and certifications
|
184 |
+
- Quality control points
|
185 |
+
- Standard operating procedures
|
186 |
+
|
187 |
+
The MES uses routing information to:
|
188 |
+
- Calculate production lead times
|
189 |
+
- Schedule equipment and labor
|
190 |
+
- Track work-in-process inventory
|
191 |
+
- Monitor operation efficiency
|
192 |
+
- Enforce quality control procedures
|
193 |
+
|
194 |
+
==============================================================================
|
195 |
+
5. INVENTORY TRACKING
|
196 |
+
==============================================================================
|
197 |
+
|
198 |
+
5.1 Inventory Management Overview
|
199 |
+
|
200 |
+
The MES provides comprehensive inventory tracking capabilities for raw materials, work-in-process, and finished goods. All inventory movements are tracked with full traceability including lot numbers, serial numbers, and expiration dates.
|
201 |
+
|
202 |
+
Inventory Categories:
|
203 |
+
- Raw Materials: Components and materials consumed in production
|
204 |
+
- Work-in-Process: Partially completed products moving through production
|
205 |
+
- Finished Goods: Completed products ready for shipment
|
206 |
+
- Packaging Materials: Boxes, labels, and packaging components
|
207 |
+
- Maintenance Supplies: Spare parts and consumables for equipment
|
208 |
+
|
209 |
+
5.2 Barcode Scanning and Data Capture
|
210 |
+
|
211 |
+
The system supports various barcode formats for efficient data capture:
|
212 |
+
|
213 |
+
Supported Barcode Types:
|
214 |
+
- Code 128 for general purpose identification
|
215 |
+
- GS1-128 for supply chain applications
|
216 |
+
- Data Matrix for small item marking
|
217 |
+
- QR codes for complex data encoding
|
218 |
+
|
219 |
+
Scanning Workflows:
|
220 |
+
1. Material Receipt: Scan incoming materials to update inventory
|
221 |
+
2. Material Issue: Scan materials when issued to production
|
222 |
+
3. Production Reporting: Scan work orders to report progress
|
223 |
+
4. Quality Control: Scan samples for testing and inspection
|
224 |
+
5. Shipping: Scan finished goods during packaging and shipment
|
225 |
+
|
226 |
+
Mobile devices and handheld scanners integrate seamlessly with the MES for real-time data collection on the shop floor.
|
227 |
+
|
228 |
+
5.3 Lot Traceability
|
229 |
+
|
230 |
+
The MES maintains complete lot genealogy for all materials and products. Traceability records include:
|
231 |
+
|
232 |
+
Forward Traceability:
|
233 |
+
- Which lots were used in specific production runs
|
234 |
+
- Where finished goods were shipped
|
235 |
+
- Customer delivery information
|
236 |
+
- Quality test results by lot
|
237 |
+
|
238 |
+
Backward Traceability:
|
239 |
+
- Source suppliers for raw materials
|
240 |
+
- Receipt dates and inspection results
|
241 |
+
- Production history and operators involved
|
242 |
+
- Environmental conditions during processing
|
243 |
+
|
244 |
+
This information is critical for regulatory compliance and enables rapid response to quality issues or recalls.
|
245 |
+
|
246 |
+
==============================================================================
|
247 |
+
6. QUALITY CONTROL MODULE
|
248 |
+
==============================================================================
|
249 |
+
|
250 |
+
6.1 Quality Control Planning
|
251 |
+
|
252 |
+
Quality control in the MES is based on predefined inspection plans that specify:
|
253 |
+
|
254 |
+
Inspection Points:
|
255 |
+
- Incoming material inspection
|
256 |
+
- In-process quality checks
|
257 |
+
- Final product inspection
|
258 |
+
- Statistical process control monitoring
|
259 |
+
|
260 |
+
For each inspection point, the system defines:
|
261 |
+
- Test procedures and specifications
|
262 |
+
- Sampling plans and frequencies
|
263 |
+
- Accept/reject criteria
|
264 |
+
- Required equipment and personnel
|
265 |
+
- Documentation requirements
|
266 |
+
|
267 |
+
6.2 Statistical Process Control (SPC)
|
268 |
+
|
269 |
+
The MES includes built-in SPC capabilities for monitoring process variation and detecting trends that could indicate quality issues.
|
270 |
+
|
271 |
+
SPC Features:
|
272 |
+
- Real-time control charts (X-bar, R, p, np, c, u)
|
273 |
+
- Automatic out-of-control detection
|
274 |
+
- Process capability analysis (Cp, Cpk)
|
275 |
+
- Trend analysis and alerts
|
276 |
+
- Correlation analysis between variables
|
277 |
+
|
278 |
+
Control charts are automatically updated as measurement data is entered, providing immediate feedback to operators and quality personnel.
|
279 |
+
|
280 |
+
6.3 Non-Conformance Management
|
281 |
+
|
282 |
+
When quality issues are detected, the MES provides a structured workflow for managing non-conformances:
|
283 |
+
|
284 |
+
1. Issue Identification: Quality problems are logged with detailed descriptions
|
285 |
+
2. Containment Actions: Immediate steps to prevent defective products from advancing
|
286 |
+
3. Root Cause Analysis: Investigation to determine underlying causes
|
287 |
+
4. Corrective Actions: Permanent solutions to prevent recurrence
|
288 |
+
5. Verification: Confirmation that corrective actions are effective
|
289 |
+
|
290 |
+
The system tracks all non-conformances through resolution and maintains historical records for trend analysis and regulatory reporting.
|
291 |
+
|
292 |
+
==============================================================================
|
293 |
+
7. REPORTING AND ANALYTICS
|
294 |
+
==============================================================================
|
295 |
+
|
296 |
+
7.1 Standard Reports
|
297 |
+
|
298 |
+
The MES includes a comprehensive library of standard reports covering all aspects of manufacturing operations:
|
299 |
+
|
300 |
+
Production Reports:
|
301 |
+
- Daily Production Summary
|
302 |
+
- Work Order Status Report
|
303 |
+
- Equipment Utilization Analysis
|
304 |
+
- Labor Efficiency Report
|
305 |
+
- Schedule Adherence Summary
|
306 |
+
|
307 |
+
Quality Reports:
|
308 |
+
- Quality Control Dashboard
|
309 |
+
- Non-Conformance Trending
|
310 |
+
- First Pass Yield Analysis
|
311 |
+
- Customer Complaint Summary
|
312 |
+
- Supplier Quality Performance
|
313 |
+
|
314 |
+
Inventory Reports:
|
315 |
+
- Inventory Valuation Report
|
316 |
+
- Material Usage Analysis
|
317 |
+
- ABC Analysis by Value and Velocity
|
318 |
+
- Obsolete Inventory Report
|
319 |
+
- Lot Traceability Report
|
320 |
+
|
321 |
+
7.2 Key Performance Indicators (KPIs)
|
322 |
+
|
323 |
+
The system tracks essential manufacturing KPIs with real-time dashboards and trend analysis:
|
324 |
+
|
325 |
+
Operational KPIs:
|
326 |
+
- Overall Equipment Effectiveness (OEE)
|
327 |
+
- First Pass Yield (FPY)
|
328 |
+
- On-Time Delivery Performance
|
329 |
+
- Inventory Turnover Ratio
|
330 |
+
- Labor Productivity
|
331 |
+
|
332 |
+
Quality KPIs:
|
333 |
+
- Defect Rate by Product
|
334 |
+
- Customer Complaint Rate
|
335 |
+
- Supplier Quality Rating
|
336 |
+
- Cost of Poor Quality
|
337 |
+
- Process Capability Indices
|
338 |
+
|
339 |
+
Financial KPIs:
|
340 |
+
- Manufacturing Cost per Unit
|
341 |
+
- Labor Cost Variance
|
342 |
+
- Material Cost Variance
|
343 |
+
- Overhead Absorption Rate
|
344 |
+
- Work Order Profitability
|
345 |
+
|
346 |
+
7.3 Custom Report Builder
|
347 |
+
|
348 |
+
Advanced users can create custom reports using the built-in report designer:
|
349 |
+
|
350 |
+
Report Design Features:
|
351 |
+
- Drag-and-drop interface for easy report creation
|
352 |
+
- Access to all database tables and views
|
353 |
+
- Calculated fields and complex formulas
|
354 |
+
- Multiple output formats (PDF, Excel, CSV)
|
355 |
+
- Automated report scheduling and distribution
|
356 |
+
|
357 |
+
The report builder includes data visualization tools for creating charts, graphs, and dashboards that can be embedded in reports or displayed on production monitors.
|
358 |
+
|
359 |
+
==============================================================================
|
360 |
+
8. SYSTEM MAINTENANCE
|
361 |
+
==============================================================================
|
362 |
+
|
363 |
+
8.1 Database Maintenance
|
364 |
+
|
365 |
+
Regular database maintenance is essential for optimal MES performance:
|
366 |
+
|
367 |
+
Daily Tasks:
|
368 |
+
- Verify database backup completion
|
369 |
+
- Check transaction log size and growth
|
370 |
+
- Monitor active connections and locks
|
371 |
+
- Review system error logs
|
372 |
+
|
373 |
+
Weekly Tasks:
|
374 |
+
- Update database statistics
|
375 |
+
- Rebuild fragmented indexes
|
376 |
+
- Archive old transaction data
|
377 |
+
- Test backup restore procedures
|
378 |
+
|
379 |
+
Monthly Tasks:
|
380 |
+
- Review database growth trends
|
381 |
+
- Optimize query performance
|
382 |
+
- Update system documentation
|
383 |
+
- Verify disaster recovery procedures
|
384 |
+
|
385 |
+
8.2 Performance Monitoring
|
386 |
+
|
387 |
+
The MES includes built-in performance monitoring tools to identify and resolve system bottlenecks:
|
388 |
+
|
389 |
+
Performance Metrics:
|
390 |
+
- Database response times
|
391 |
+
- Network latency measurements
|
392 |
+
- Server CPU and memory utilization
|
393 |
+
- Application response times
|
394 |
+
- User session statistics
|
395 |
+
|
396 |
+
Performance alerts are automatically generated when thresholds are exceeded, enabling proactive system management.
|
397 |
+
|
398 |
+
8.3 Security Management
|
399 |
+
|
400 |
+
Security maintenance includes regular updates and monitoring:
|
401 |
+
|
402 |
+
Security Tasks:
|
403 |
+
- Install security patches and updates
|
404 |
+
- Review user accounts and permissions
|
405 |
+
- Monitor login attempts and access patterns
|
406 |
+
- Update antivirus and security software
|
407 |
+
- Conduct security vulnerability assessments
|
408 |
+
|
409 |
+
The system maintains audit logs of all user activities and system changes for security and compliance purposes.
|
410 |
+
|
411 |
+
==============================================================================
|
412 |
+
9. TROUBLESHOOTING
|
413 |
+
==============================================================================
|
414 |
+
|
415 |
+
9.1 Common Issues and Solutions
|
416 |
+
|
417 |
+
Database Connection Problems:
|
418 |
+
Symptoms: Users cannot log in, system responds slowly
|
419 |
+
Solutions:
|
420 |
+
- Verify SQL Server service is running
|
421 |
+
- Check network connectivity between client and server
|
422 |
+
- Validate connection string configuration
|
423 |
+
- Review firewall settings
|
424 |
+
|
425 |
+
Barcode Scanner Issues:
|
426 |
+
Symptoms: Scanner not reading barcodes, incorrect data capture
|
427 |
+
Solutions:
|
428 |
+
- Clean scanner lens and check lighting conditions
|
429 |
+
- Verify barcode format matches system configuration
|
430 |
+
- Update scanner drivers and firmware
|
431 |
+
- Test with known good barcodes
|
432 |
+
|
433 |
+
Performance Issues:
|
434 |
+
Symptoms: Slow response times, timeouts
|
435 |
+
Solutions:
|
436 |
+
- Check server resource utilization
|
437 |
+
- Review database index statistics
|
438 |
+
- Optimize slow-running queries
|
439 |
+
- Archive old transaction data
|
440 |
+
|
441 |
+
9.2 Error Messages and Codes
|
442 |
+
|
443 |
+
The MES uses standardized error codes for easier troubleshooting:
|
444 |
+
|
445 |
+
Database Errors (ERR-DB-xxx):
|
446 |
+
ERR-DB-001: Connection timeout - Check network and server status
|
447 |
+
ERR-DB-002: Login failed - Verify username and password
|
448 |
+
ERR-DB-003: Insufficient permissions - Contact administrator
|
449 |
+
|
450 |
+
Application Errors (ERR-APP-xxx):
|
451 |
+
ERR-APP-001: Invalid work order number - Verify order exists
|
452 |
+
ERR-APP-002: Material not available - Check inventory levels
|
453 |
+
ERR-APP-003: Quality test failed - Review specifications
|
454 |
+
|
455 |
+
System Errors (ERR-SYS-xxx):
|
456 |
+
ERR-SYS-001: Service unavailable - Restart MES services
|
457 |
+
ERR-SYS-002: Configuration error - Check system settings
|
458 |
+
ERR-SYS-003: License expired - Contact vendor for renewal
|
459 |
+
|
460 |
+
9.3 Support Contacts
|
461 |
+
|
462 |
+
For technical support beyond this troubleshooting guide:
|
463 |
+
|
464 |
+
Level 1 Support (General Issues):
|
465 |
+
- Phone: 1-800-MES-HELP
|
466 |
+
- Email: support@mes-systems.com
|
467 |
+
- Hours: Monday-Friday 8:00 AM - 6:00 PM EST
|
468 |
+
|
469 |
+
Level 2 Support (Advanced Technical Issues):
|
470 |
+
- Email: advanced-support@mes-systems.com
|
471 |
+
- Response time: Within 4 hours during business days
|
472 |
+
|
473 |
+
Emergency Support (Critical System Down):
|
474 |
+
- Phone: 1-800-MES-911
|
475 |
+
- Available 24/7/365
|
476 |
+
- Response time: Within 1 hour
|
477 |
+
|
478 |
+
==============================================================================
|
479 |
+
10. APPENDICES
|
480 |
+
==============================================================================
|
481 |
+
|
482 |
+
Appendix A: Database Schema
|
483 |
+
|
484 |
+
The MES database consists of the following primary tables:
|
485 |
+
|
486 |
+
Production Tables:
|
487 |
+
- ProductionOrders: Master production order information
|
488 |
+
- WorkOrders: Individual work order details
|
489 |
+
- Operations: Production operation definitions
|
490 |
+
- Routes: Product routing specifications
|
491 |
+
|
492 |
+
Inventory Tables:
|
493 |
+
- Materials: Master material information
|
494 |
+
- Inventory: Current inventory positions
|
495 |
+
- Transactions: All inventory movements
|
496 |
+
- Lots: Lot and serial number tracking
|
497 |
+
|
498 |
+
Quality Tables:
|
499 |
+
- QualityPlans: Inspection plan definitions
|
500 |
+
- TestResults: Quality test measurements
|
501 |
+
- NonConformances: Quality issue records
|
502 |
+
- Specifications: Product quality specifications
|
503 |
+
|
504 |
+
Appendix B: API Documentation
|
505 |
+
|
506 |
+
The MES provides REST APIs for integration with external systems:
|
507 |
+
|
508 |
+
Authentication Endpoints:
|
509 |
+
POST /api/auth/login - User authentication
|
510 |
+
POST /api/auth/refresh - Token refresh
|
511 |
+
POST /api/auth/logout - User logout
|
512 |
+
|
513 |
+
Production Endpoints:
|
514 |
+
GET /api/production/orders - Retrieve production orders
|
515 |
+
POST /api/production/orders - Create new production order
|
516 |
+
PUT /api/production/orders/{id} - Update production order
|
517 |
+
DELETE /api/production/orders/{id} - Delete production order
|
518 |
+
|
519 |
+
Inventory Endpoints:
|
520 |
+
GET /api/inventory/materials - Get material list
|
521 |
+
POST /api/inventory/transactions - Record inventory transaction
|
522 |
+
GET /api/inventory/lots/{lot} - Get lot information
|
523 |
+
|
524 |
+
All API calls require authentication tokens and return JSON formatted responses.
|
525 |
+
|
526 |
+
Appendix C: System Configuration Files
|
527 |
+
|
528 |
+
Key configuration files and their purposes:
|
529 |
+
|
530 |
+
database.config: Database connection settings
|
531 |
+
security.config: User authentication and authorization
|
532 |
+
integration.config: External system integration settings
|
533 |
+
performance.config: System performance parameters
|
534 |
+
|
535 |
+
These files should only be modified by trained administrators following proper change control procedures.
|
embedding_config.py
ADDED
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from langchain_community.embeddings import HuggingFaceEmbeddings
|
2 |
+
from utils.vector_store import get_vector_store
|
3 |
+
import os
|
4 |
+
|
5 |
+
|
6 |
+
EMBEDDING_CONFIGS = {
|
7 |
+
"General-purpose (bge-large-en)": HuggingFaceEmbeddings(
|
8 |
+
model_name="BAAI/bge-large-en",
|
9 |
+
model_kwargs={"device": "cpu"}, # or "cuda" if you have GPU
|
10 |
+
encode_kwargs={"normalize_embeddings": True}
|
11 |
+
),
|
12 |
+
"Fast & lightweight (bge-small-en)": HuggingFaceEmbeddings(
|
13 |
+
model_name="BAAI/bge-small-en",
|
14 |
+
model_kwargs={"device": "cpu"},
|
15 |
+
encode_kwargs={"normalize_embeddings": True}
|
16 |
+
),
|
17 |
+
"QA optimized (e5-large-v2)": HuggingFaceEmbeddings(
|
18 |
+
model_name="intfloat/e5-large-v2",
|
19 |
+
model_kwargs={"device": "cpu"},
|
20 |
+
encode_kwargs={"normalize_embeddings": True}
|
21 |
+
),
|
22 |
+
"Instruction-tuned (instructor-large)": HuggingFaceEmbeddings(
|
23 |
+
model_name="hkunlp/instructor-large",
|
24 |
+
model_kwargs={"device": "cpu"},
|
25 |
+
encode_kwargs={"normalize_embeddings": True}
|
26 |
+
),
|
27 |
+
}
|
rebuild_vector_stores.py
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from utils.vector_store import rebuild_vector_store
|
2 |
+
|
3 |
+
# Rebuild MES manual vector store
|
4 |
+
rebuild_vector_store(
|
5 |
+
doc_path="docs/mes",
|
6 |
+
persist_directory="./vector_stores/mes_db"
|
7 |
+
)
|
8 |
+
|
9 |
+
# Rebuild technical document set
|
10 |
+
rebuild_vector_store(
|
11 |
+
doc_path="docs/technical",
|
12 |
+
persist_directory="./vector_stores/tech_db"
|
13 |
+
)
|
14 |
+
|
15 |
+
# Rebuild general document set
|
16 |
+
rebuild_vector_store(
|
17 |
+
doc_path="docs/general",
|
18 |
+
persist_directory="./vector_stores/general_db"
|
19 |
+
)
|
requirements.txt
ADDED
@@ -0,0 +1,72 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# # Core API
|
2 |
+
# fastapi==0.104.1
|
3 |
+
# uvicorn[standard]==0.24.0
|
4 |
+
# gunicorn==21.2.0
|
5 |
+
# pydantic==2.5.0
|
6 |
+
# requests==2.31.0
|
7 |
+
# python-dotenv==1.0.0
|
8 |
+
|
9 |
+
# # LangChain + Vector DB
|
10 |
+
# langchain==0.1.0
|
11 |
+
# langchain-community==0.0.10
|
12 |
+
# chromadb==0.4.18
|
13 |
+
# tiktoken==0.5.2
|
14 |
+
|
15 |
+
# # Embeddings / Transformers
|
16 |
+
# sentence-transformers==2.3.0
|
17 |
+
# transformers==4.35.0
|
18 |
+
# huggingface-hub>=0.13.0,<1.0
|
19 |
+
|
20 |
+
# # CPU-only torch (lightweight)
|
21 |
+
# torch==2.7.1
|
22 |
+
|
23 |
+
# # Build tools
|
24 |
+
# setuptools>=70.0.0
|
25 |
+
# wheel>=0.41.0
|
26 |
+
|
27 |
+
|
28 |
+
# Python 11 version not working -----------
|
29 |
+
|
30 |
+
|
31 |
+
|
32 |
+
fastapi
|
33 |
+
uvicorn
|
34 |
+
langchain
|
35 |
+
chromadb
|
36 |
+
openai
|
37 |
+
tiktoken
|
38 |
+
langchain-community
|
39 |
+
transformers
|
40 |
+
torch
|
41 |
+
sentence-transformers
|
42 |
+
huggingface_hub
|
43 |
+
python-dotenv
|
44 |
+
numpy
|
45 |
+
pathlib2
|
46 |
+
gunicorn
|
47 |
+
|
48 |
+
|
49 |
+
# fastapi==0.104.1
|
50 |
+
# uvicorn[standard]==0.24.0
|
51 |
+
# requests==2.31.0
|
52 |
+
# python-dotenv==1.0.0
|
53 |
+
# pydantic==2.5.0
|
54 |
+
# chromadb==0.4.18
|
55 |
+
# langchain==0.1.0
|
56 |
+
# langchain-community==0.0.10
|
57 |
+
# sentence-transformers==2.2.2
|
58 |
+
# numpy==1.24.3
|
59 |
+
# tiktoken==0.5.2
|
60 |
+
|
61 |
+
# # Document processing
|
62 |
+
# PyPDF2==3.0.1
|
63 |
+
# python-docx==0.8.11
|
64 |
+
# openpyxl==3.1.2
|
65 |
+
# python-multipart==0.0.6
|
66 |
+
|
67 |
+
# # Additional dependencies
|
68 |
+
# typing-extensions==4.8.0
|
69 |
+
# pathlib2==2.3.7
|
70 |
+
|
71 |
+
# # Render-specific optimizations
|
72 |
+
# gunicorn==21.2.0
|
runtime.txt
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
python-3.11.9
|
test_api.py
ADDED
@@ -0,0 +1,103 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import requests
|
2 |
+
import json
|
3 |
+
|
4 |
+
|
5 |
+
def test_api_connection():
|
6 |
+
"""Test if the API is running"""
|
7 |
+
try:
|
8 |
+
response = requests.get("http://localhost:8000/", timeout=5)
|
9 |
+
print("✅ API is running!")
|
10 |
+
print(f"Status: {response.json()}")
|
11 |
+
return True
|
12 |
+
except requests.exceptions.ConnectionError:
|
13 |
+
print("❌ Cannot connect to API. Make sure it's running on http://localhost:8000")
|
14 |
+
return False
|
15 |
+
except Exception as e:
|
16 |
+
print(f"❌ Error connecting to API: {e}")
|
17 |
+
return False
|
18 |
+
|
19 |
+
|
20 |
+
def ask_question(question):
|
21 |
+
"""Send a question to the API"""
|
22 |
+
try:
|
23 |
+
url = "http://localhost:8000/ask"
|
24 |
+
headers = {"Content-Type": "application/json"}
|
25 |
+
data = {"query": question}
|
26 |
+
|
27 |
+
print("🤔 Processing your question...")
|
28 |
+
response = requests.post(url, headers=headers, json=data, timeout=30)
|
29 |
+
|
30 |
+
if response.status_code == 200:
|
31 |
+
result = response.json()
|
32 |
+
print("\n" + "="*60)
|
33 |
+
print("📝 ANSWER:")
|
34 |
+
print("="*60)
|
35 |
+
print(result.get("answer", "No answer provided"))
|
36 |
+
|
37 |
+
print(f"\n🤖 Model used: {result.get('model_used', 'Unknown')}")
|
38 |
+
|
39 |
+
print(
|
40 |
+
f"\n 🏪 Vector store used: {result.get("vector_store_used", 'Unknown')}")
|
41 |
+
|
42 |
+
if "sources" in result and result["sources"]:
|
43 |
+
print(f"\n📚 Sources ({len(result['sources'])}):")
|
44 |
+
for i, source in enumerate(result["sources"], 1):
|
45 |
+
print(f"{i}. {source.get('content', 'No content')}")
|
46 |
+
print("="*60)
|
47 |
+
else:
|
48 |
+
print(f"❌ Error {response.status_code}: {response.text}")
|
49 |
+
|
50 |
+
except requests.exceptions.Timeout:
|
51 |
+
print("⏰ Request timed out. The API might be processing a complex query.")
|
52 |
+
except Exception as e:
|
53 |
+
print(f"❌ Error asking question: {e}")
|
54 |
+
|
55 |
+
|
56 |
+
def interactive_loop():
|
57 |
+
"""Main interactive loop"""
|
58 |
+
print("🚀 FastAPI Interactive Tester")
|
59 |
+
print("="*40)
|
60 |
+
|
61 |
+
# Test connection first
|
62 |
+
if not test_api_connection():
|
63 |
+
print("\nPlease start your FastAPI server first:")
|
64 |
+
print("cd rag_app")
|
65 |
+
print("python api/main.py")
|
66 |
+
return
|
67 |
+
|
68 |
+
print("\n💡 Type your questions below!")
|
69 |
+
print("Commands:")
|
70 |
+
print(" • Type any question to ask the API")
|
71 |
+
print(" • Type 'quit', 'exit', or 'q' to exit")
|
72 |
+
print(" • Type 'status' to check API status")
|
73 |
+
print(" • Press Ctrl+C to exit anytime")
|
74 |
+
print("-" * 40)
|
75 |
+
|
76 |
+
while True:
|
77 |
+
try:
|
78 |
+
# Get user input
|
79 |
+
question = input("\n❓ Your question: ").strip()
|
80 |
+
|
81 |
+
# Handle special commands
|
82 |
+
if question.lower() in ['quit', 'exit', 'q']:
|
83 |
+
print("👋 Goodbye!")
|
84 |
+
break
|
85 |
+
elif question.lower() == 'status':
|
86 |
+
test_api_connection()
|
87 |
+
continue
|
88 |
+
elif not question:
|
89 |
+
print("Please enter a question or 'quit' to exit.")
|
90 |
+
continue
|
91 |
+
|
92 |
+
# Ask the question
|
93 |
+
ask_question(question)
|
94 |
+
|
95 |
+
except KeyboardInterrupt:
|
96 |
+
print("\n\n👋 Goodbye!")
|
97 |
+
break
|
98 |
+
except Exception as e:
|
99 |
+
print(f"❌ Unexpected error: {e}")
|
100 |
+
|
101 |
+
|
102 |
+
if __name__ == "__main__":
|
103 |
+
interactive_loop()
|
test_embeddings_retriever.py
ADDED
@@ -0,0 +1,199 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# # from langchain_chroma import Chroma
|
2 |
+
# # from langchain_openai import OpenAIEmbeddings
|
3 |
+
# from langchain_community.embeddings import HuggingFaceEmbeddings
|
4 |
+
# from utils.vector_store import get_vector_store
|
5 |
+
# import os
|
6 |
+
|
7 |
+
|
8 |
+
# # Define different embedding model options
|
9 |
+
# # EMBEDDING_CONFIGS = {
|
10 |
+
# # "Accuracy (OpenAI text-embedding-3-large)": OpenAIEmbeddings(model="text-embedding-3-large"),
|
11 |
+
# # "Performance (OpenAI text-embedding-3-small)": OpenAIEmbeddings(model="text-embedding-3-small"),
|
12 |
+
# # "Instruction-based (HuggingFace bge-large-en)": HuggingFaceEmbeddings(model_name="BAAI/bge-large-en"),
|
13 |
+
# # "QA Optimized (HuggingFace e5-large-v2)": HuggingFaceEmbeddings(model_name="intfloat/e5-large-v2"),
|
14 |
+
# # }
|
15 |
+
|
16 |
+
|
17 |
+
# EMBEDDING_CONFIGS = {
|
18 |
+
# "General-purpose (bge-large-en)": HuggingFaceEmbeddings(
|
19 |
+
# model_name="BAAI/bge-large-en",
|
20 |
+
# model_kwargs={"device": "cpu"}, # or "cuda" if you have GPU
|
21 |
+
# encode_kwargs={"normalize_embeddings": True}
|
22 |
+
# ),
|
23 |
+
# "Fast & lightweight (bge-small-en)": HuggingFaceEmbeddings(
|
24 |
+
# model_name="BAAI/bge-small-en",
|
25 |
+
# model_kwargs={"device": "cpu"},
|
26 |
+
# encode_kwargs={"normalize_embeddings": True}
|
27 |
+
# ),
|
28 |
+
# "QA optimized (e5-large-v2)": HuggingFaceEmbeddings(
|
29 |
+
# model_name="intfloat/e5-large-v2",
|
30 |
+
# model_kwargs={"device": "cpu"},
|
31 |
+
# encode_kwargs={"normalize_embeddings": True}
|
32 |
+
# ),
|
33 |
+
# "Instruction-tuned (instructor-large)": HuggingFaceEmbeddings(
|
34 |
+
# model_name="hkunlp/instructor-large",
|
35 |
+
# model_kwargs={"device": "cpu"},
|
36 |
+
# encode_kwargs={"normalize_embeddings": True}
|
37 |
+
# ),
|
38 |
+
# }
|
39 |
+
|
40 |
+
# # Default vector store path
|
41 |
+
# VECTOR_STORE_PATH = "./vector_stores/mes_db"
|
42 |
+
|
43 |
+
|
44 |
+
# def test_retriever_with_embeddings(query: str, embedding_model, k: int = 3):
|
45 |
+
# """Retrieve documents using a specific embedding model."""
|
46 |
+
# vector_store = get_vector_store(
|
47 |
+
# persist_directory=VECTOR_STORE_PATH,
|
48 |
+
# embedding=embedding_model
|
49 |
+
# )
|
50 |
+
# retriever = vector_store.as_retriever(search_kwargs={"k": k})
|
51 |
+
# docs = retriever.get_relevant_documents(query)
|
52 |
+
|
53 |
+
# # Deduplicate based on page_content
|
54 |
+
# seen = set()
|
55 |
+
# unique_docs = []
|
56 |
+
# for doc in docs:
|
57 |
+
# if doc.page_content not in seen:
|
58 |
+
# seen.add(doc.page_content)
|
59 |
+
# unique_docs.append(doc)
|
60 |
+
|
61 |
+
# return unique_docs
|
62 |
+
|
63 |
+
|
64 |
+
# def compare_embeddings(query: str, k: int = 3):
|
65 |
+
# print(f"\n=== Comparing embeddings for: '{query}' ===\n")
|
66 |
+
|
67 |
+
# for name, embedding_model in EMBEDDING_CONFIGS.items():
|
68 |
+
# try:
|
69 |
+
# print(f"🔍 {name}:")
|
70 |
+
# print("-" * 50)
|
71 |
+
# docs = test_retriever_with_embeddings(query, embedding_model, k)
|
72 |
+
# for i, doc in enumerate(docs, 1):
|
73 |
+
# source = doc.metadata.get("source", "unknown")
|
74 |
+
# page = doc.metadata.get("page", "N/A")
|
75 |
+
# preview = doc.page_content[:300]
|
76 |
+
# if len(doc.page_content) > 300:
|
77 |
+
# preview += "..."
|
78 |
+
# print(f"--- Chunk #{i} ---")
|
79 |
+
# print(f"Source: {source} | Page: {page}")
|
80 |
+
# print(preview)
|
81 |
+
# print()
|
82 |
+
# print("\n" + "=" * 60 + "\n")
|
83 |
+
# except Exception as e:
|
84 |
+
# print(f"❌ Error with {name}: {e}\n")
|
85 |
+
|
86 |
+
|
87 |
+
# if __name__ == "__main__":
|
88 |
+
# print("Embedding Model Benchmark Tool")
|
89 |
+
# print("\nType 'compare: <question>' to compare all embeddings")
|
90 |
+
# print("Type 'exit' to quit\n")
|
91 |
+
|
92 |
+
# while True:
|
93 |
+
# user_input = input("\nEnter your question: ").strip()
|
94 |
+
|
95 |
+
# if user_input.lower() == "exit":
|
96 |
+
# break
|
97 |
+
# elif user_input.lower().startswith("compare: "):
|
98 |
+
# query = user_input[9:]
|
99 |
+
# compare_embeddings(query)
|
100 |
+
# else:
|
101 |
+
# print("Please use the format: compare: <question>")
|
102 |
+
|
103 |
+
|
104 |
+
from utils.vector_store import get_vector_store
|
105 |
+
|
106 |
+
|
107 |
+
def test_retriever_with_embeddings(query: str, embedding_model, k: int = 3, vector_store_path="./chroma_db"):
|
108 |
+
"""Test retriever with a specific embedding model and vector store"""
|
109 |
+
vector_store = get_vector_store(
|
110 |
+
persist_directory=vector_store_path, embedding=embedding_model)
|
111 |
+
retriever = vector_store.as_retriever(search_kwargs={"k": k})
|
112 |
+
docs = retriever.get_relevant_documents(query)
|
113 |
+
|
114 |
+
# Deduplicate based on page_content
|
115 |
+
seen = set()
|
116 |
+
unique_docs = []
|
117 |
+
for doc in docs:
|
118 |
+
if doc.page_content not in seen:
|
119 |
+
seen.add(doc.page_content)
|
120 |
+
unique_docs.append(doc)
|
121 |
+
|
122 |
+
print(f"\nUsing vector store: {vector_store_path}")
|
123 |
+
print(f"Top {len(unique_docs)} unique chunks retrieved for: '{query}'\n")
|
124 |
+
|
125 |
+
for i, doc in enumerate(unique_docs, 1):
|
126 |
+
source = doc.metadata.get("source", "unknown")
|
127 |
+
page = doc.metadata.get("page", "N/A")
|
128 |
+
print(f"--- Chunk #{i} ---")
|
129 |
+
print(f"Source: {source} | Page: {page}")
|
130 |
+
preview = doc.page_content[:300]
|
131 |
+
if len(doc.page_content) > 300:
|
132 |
+
preview += "..."
|
133 |
+
print(preview)
|
134 |
+
print()
|
135 |
+
|
136 |
+
|
137 |
+
def compare_retrievers_with_embeddings(query: str, embedding_model, k: int = 3):
|
138 |
+
"""Compare results from different vector stores using the same embedding model"""
|
139 |
+
stores = {
|
140 |
+
"MES Manual": "./vector_stores/mes_db",
|
141 |
+
"Technical Docs": "./vector_stores/tech_db",
|
142 |
+
"General Docs": "./vector_stores/general_db"
|
143 |
+
}
|
144 |
+
|
145 |
+
print(f"\n=== Comparing retrievers for: '{query}' ===\n")
|
146 |
+
|
147 |
+
for store_name, store_path in stores.items():
|
148 |
+
try:
|
149 |
+
print(f"🔍 {store_name}:")
|
150 |
+
print("-" * 50)
|
151 |
+
test_retriever_with_embeddings(
|
152 |
+
query, embedding_model, k=k, vector_store_path=store_path)
|
153 |
+
print("\n" + "="*60 + "\n")
|
154 |
+
except Exception as e:
|
155 |
+
print(f"❌ Could not access {store_name}: {e}\n")
|
156 |
+
|
157 |
+
|
158 |
+
if __name__ == "__main__":
|
159 |
+
from embedding_config import EMBEDDING_CONFIGS
|
160 |
+
|
161 |
+
print("Multi-Vector Store RAG Tester (with Embeddings)")
|
162 |
+
print("\nAvailable commands:")
|
163 |
+
print(" - Enter a question to test default store")
|
164 |
+
print(" - Type 'mes: <question>' for MES manual")
|
165 |
+
print(" - Type 'tech: <question>' for technical docs")
|
166 |
+
print(" - Type 'general: <question>' for general docs")
|
167 |
+
print(" - Type 'compare: <question>' to compare all stores")
|
168 |
+
print(" - Type 'exit' to quit")
|
169 |
+
|
170 |
+
# Choose embedding model at start
|
171 |
+
print("\nAvailable Embedding Models:")
|
172 |
+
for i, name in enumerate(EMBEDDING_CONFIGS.keys(), 1):
|
173 |
+
print(f" {i}. {name}")
|
174 |
+
choice = int(input("Select embedding model number: ").strip())
|
175 |
+
embedding_model = list(EMBEDDING_CONFIGS.values())[choice - 1]
|
176 |
+
|
177 |
+
while True:
|
178 |
+
user_input = input("\nEnter your question: ").strip()
|
179 |
+
|
180 |
+
if user_input.lower() == "exit":
|
181 |
+
break
|
182 |
+
elif user_input.lower().startswith("mes: "):
|
183 |
+
query = user_input[5:]
|
184 |
+
test_retriever_with_embeddings(
|
185 |
+
query, embedding_model, vector_store_path="./vector_stores/mes_db")
|
186 |
+
elif user_input.lower().startswith("tech: "):
|
187 |
+
query = user_input[6:]
|
188 |
+
test_retriever_with_embeddings(
|
189 |
+
query, embedding_model, vector_store_path="./vector_stores/tech_db")
|
190 |
+
elif user_input.lower().startswith("general: "):
|
191 |
+
query = user_input[9:]
|
192 |
+
test_retriever_with_embeddings(
|
193 |
+
query, embedding_model, vector_store_path="./vector_stores/general_db")
|
194 |
+
elif user_input.lower().startswith("compare: "):
|
195 |
+
query = user_input[9:]
|
196 |
+
compare_retrievers_with_embeddings(query, embedding_model)
|
197 |
+
else:
|
198 |
+
test_retriever_with_embeddings(
|
199 |
+
user_input, embedding_model) # Default store
|
test_retriever.py
ADDED
@@ -0,0 +1,164 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# from utils.vector_store import get_vector_store
|
2 |
+
|
3 |
+
# def test_retriever(query: str, k: int = 3, vector_store_path="./chroma_db"):
|
4 |
+
# """Test retriever with specific vector store"""
|
5 |
+
# vector_store = get_vector_store(persist_directory=vector_store_path)
|
6 |
+
# retriever = vector_store.as_retriever(search_kwargs={"k": k})
|
7 |
+
# docs = retriever.get_relevant_documents(query)
|
8 |
+
|
9 |
+
# # Deduplicate based on page_content
|
10 |
+
# seen = set()
|
11 |
+
# unique_docs = []
|
12 |
+
# for doc in docs:
|
13 |
+
# if doc.page_content not in seen:
|
14 |
+
# seen.add(doc.page_content)
|
15 |
+
# unique_docs.append(doc)
|
16 |
+
|
17 |
+
# print(f"\nUsing vector store: {vector_store_path}")
|
18 |
+
# print(f"Top {len(unique_docs)} unique chunks retrieved for: '{query}'\n")
|
19 |
+
|
20 |
+
# for i, doc in enumerate(unique_docs, 1):
|
21 |
+
# source = doc.metadata.get("source", "unknown")
|
22 |
+
# page = doc.metadata.get("page", "N/A")
|
23 |
+
# print(f"--- Chunk #{i} ---")
|
24 |
+
# print(f"Source: {source} | Page: {page}")
|
25 |
+
# preview = doc.page_content[:300]
|
26 |
+
# if len(doc.page_content) > 300:
|
27 |
+
# preview += "..."
|
28 |
+
# print(preview)
|
29 |
+
# print()
|
30 |
+
|
31 |
+
|
32 |
+
# def compare_retrievers(query: str, k: int = 3):
|
33 |
+
# """Compare results from different vector stores"""
|
34 |
+
# stores = {
|
35 |
+
# "MES Manual": "./vector_stores/mes_db",
|
36 |
+
# "Technical Docs": "./vector_stores/tech_db",
|
37 |
+
# "General Docs": "./vector_stores/general_db"
|
38 |
+
# }
|
39 |
+
|
40 |
+
# print(f"\n=== Comparing retrievers for: '{query}' ===\n")
|
41 |
+
|
42 |
+
# for store_name, store_path in stores.items():
|
43 |
+
# try:
|
44 |
+
# print(f"🔍 {store_name}:")
|
45 |
+
# print("-" * 50)
|
46 |
+
# test_retriever(query, k=k, vector_store_path=store_path)
|
47 |
+
# print("\n" + "="*60 + "\n")
|
48 |
+
# except Exception as e:
|
49 |
+
# print(f"❌ Could not access {store_name}: {e}\n")
|
50 |
+
|
51 |
+
|
52 |
+
# if __name__ == "__main__":
|
53 |
+
# print("Multi-Vector Store RAG Tester")
|
54 |
+
# print("\nAvailable commands:")
|
55 |
+
# print(" - Enter a question to test default store")
|
56 |
+
# print(" - Type 'mes: <question>' for MES manual")
|
57 |
+
# print(" - Type 'tech: <question>' for technical docs")
|
58 |
+
# print(" - Type 'general: <question>' for general docs")
|
59 |
+
# print(" - Type 'compare: <question>' to compare all stores")
|
60 |
+
# print(" - Type 'exit' to quit")
|
61 |
+
|
62 |
+
# while True:
|
63 |
+
# user_input = input("\nEnter your question: ").strip()
|
64 |
+
|
65 |
+
# if user_input.lower() == "exit":
|
66 |
+
# break
|
67 |
+
# elif user_input.lower().startswith("mes: "):
|
68 |
+
# query = user_input[5:]
|
69 |
+
# test_retriever(query, vector_store_path="./vector_stores/mes_db")
|
70 |
+
# elif user_input.lower().startswith("tech: "):
|
71 |
+
# query = user_input[6:]
|
72 |
+
# test_retriever(query, vector_store_path="./vector_stores/tech_db")
|
73 |
+
# elif user_input.lower().startswith("general: "):
|
74 |
+
# query = user_input[9:]
|
75 |
+
# test_retriever(query, vector_store_path="./vector_stores/general_db")
|
76 |
+
# elif user_input.lower().startswith("compare: "):
|
77 |
+
# query = user_input[9:]
|
78 |
+
# compare_retrievers(query)
|
79 |
+
# else:
|
80 |
+
# test_retriever(user_input) # Default store
|
81 |
+
|
82 |
+
|
83 |
+
from utils.vector_store import get_vector_store
|
84 |
+
|
85 |
+
|
86 |
+
def test_retriever(query: str, k: int = 3, vector_store_path="./chroma_db"):
|
87 |
+
"""Test retriever with specific vector store"""
|
88 |
+
vector_store = get_vector_store(persist_directory=vector_store_path)
|
89 |
+
retriever = vector_store.as_retriever(search_kwargs={"k": k})
|
90 |
+
docs = retriever.get_relevant_documents(query)
|
91 |
+
|
92 |
+
# Deduplicate based on page_content
|
93 |
+
seen = set()
|
94 |
+
unique_docs = []
|
95 |
+
for doc in docs:
|
96 |
+
if doc.page_content not in seen:
|
97 |
+
seen.add(doc.page_content)
|
98 |
+
unique_docs.append(doc)
|
99 |
+
|
100 |
+
print(f"\nUsing vector store: {vector_store_path}")
|
101 |
+
print(f"Top {len(unique_docs)} unique chunks retrieved for: '{query}'\n")
|
102 |
+
|
103 |
+
for i, doc in enumerate(unique_docs, 1):
|
104 |
+
source = doc.metadata.get("source", "unknown")
|
105 |
+
page = doc.metadata.get("page", "N/A")
|
106 |
+
print(f"--- Chunk #{i} ---")
|
107 |
+
print(f"Source: {source} | Page: {page}")
|
108 |
+
preview = doc.page_content[:300]
|
109 |
+
if len(doc.page_content) > 300:
|
110 |
+
preview += "..."
|
111 |
+
print(preview)
|
112 |
+
print()
|
113 |
+
|
114 |
+
|
115 |
+
def compare_retrievers(query: str, k: int = 3):
|
116 |
+
"""Compare results from different vector stores"""
|
117 |
+
stores = {
|
118 |
+
"MES Manual": "./vector_stores/mes_db",
|
119 |
+
"Technical Docs": "./vector_stores/tech_db",
|
120 |
+
"General Docs": "./vector_stores/general_db"
|
121 |
+
}
|
122 |
+
|
123 |
+
print(f"\n=== Comparing retrievers for: '{query}' ===\n")
|
124 |
+
|
125 |
+
for store_name, store_path in stores.items():
|
126 |
+
try:
|
127 |
+
print(f"🔍 {store_name}:")
|
128 |
+
print("-" * 50)
|
129 |
+
test_retriever(query, k=k, vector_store_path=store_path)
|
130 |
+
print("\n" + "="*60 + "\n")
|
131 |
+
except Exception as e:
|
132 |
+
print(f"❌ Could not access {store_name}: {e}\n")
|
133 |
+
|
134 |
+
|
135 |
+
if __name__ == "__main__":
|
136 |
+
print("Multi-Vector Store RAG Tester")
|
137 |
+
print("\nAvailable commands:")
|
138 |
+
print(" - Enter a question to test default store")
|
139 |
+
print(" - Type 'mes: <question>' for MES manual")
|
140 |
+
print(" - Type 'tech: <question>' for technical docs")
|
141 |
+
print(" - Type 'general: <question>' for general docs")
|
142 |
+
print(" - Type 'compare: <question>' to compare all stores")
|
143 |
+
print(" - Type 'exit' to quit")
|
144 |
+
|
145 |
+
while True:
|
146 |
+
user_input = input("\nEnter your question: ").strip()
|
147 |
+
|
148 |
+
if user_input.lower() == "exit":
|
149 |
+
break
|
150 |
+
elif user_input.lower().startswith("mes: "):
|
151 |
+
query = user_input[5:]
|
152 |
+
test_retriever(query, vector_store_path="./vector_stores/mes_db")
|
153 |
+
elif user_input.lower().startswith("tech: "):
|
154 |
+
query = user_input[6:]
|
155 |
+
test_retriever(query, vector_store_path="./vector_stores/tech_db")
|
156 |
+
elif user_input.lower().startswith("general: "):
|
157 |
+
query = user_input[9:]
|
158 |
+
test_retriever(
|
159 |
+
query, vector_store_path="./vector_stores/general_db")
|
160 |
+
elif user_input.lower().startswith("compare: "):
|
161 |
+
query = user_input[9:]
|
162 |
+
compare_retrievers(query)
|
163 |
+
else:
|
164 |
+
test_retriever(user_input) # Default store
|
utils/__init__.py
ADDED
File without changes
|
utils/helpers/chat_mapper.py
ADDED
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# def map_answer_to_chat_response(answer_obj):
|
2 |
+
# """
|
3 |
+
# Map backend output to a single assistant message for the Vercel Chatbot SDK.
|
4 |
+
# Combines the main answer with nicely formatted sources.
|
5 |
+
# """
|
6 |
+
# # Start with the main answer
|
7 |
+
# full_content = answer_obj["answer"].strip()
|
8 |
+
|
9 |
+
# # Format sources if any
|
10 |
+
# if answer_obj.get("sources"):
|
11 |
+
# sources_lines = []
|
12 |
+
# for i, src in enumerate(answer_obj["sources"], 1):
|
13 |
+
# content_preview = src["content"].strip()
|
14 |
+
# metadata = src.get("metadata", {})
|
15 |
+
# source_info = f"Source {i}"
|
16 |
+
# if "source" in metadata:
|
17 |
+
# source_info += f" ({metadata['source']})"
|
18 |
+
# if "page" in metadata:
|
19 |
+
# source_info += f", Page {metadata['page']}"
|
20 |
+
# # Limit content preview to first 300 characters
|
21 |
+
# content_preview = (
|
22 |
+
# content_preview[:300] + "...") if len(content_preview) > 300 else content_preview
|
23 |
+
# sources_lines.append(f"{source_info}: {content_preview}")
|
24 |
+
|
25 |
+
# # Add sources as a block after the main answer
|
26 |
+
# full_content += "\n\n📚 Sources:\n" + "\n".join(sources_lines)
|
27 |
+
|
28 |
+
# # Return in the format expected by the Vercel Chatbot SDK
|
29 |
+
# return {
|
30 |
+
# "id": "chatcmpl-local-1",
|
31 |
+
# "object": "chat.completion",
|
32 |
+
# "created": __import__("time").time(),
|
33 |
+
# "model": answer_obj.get("model_used", "unknown-model"),
|
34 |
+
# "choices": [
|
35 |
+
# {
|
36 |
+
# "index": 0,
|
37 |
+
# "message": {"role": "assistant", "content": full_content},
|
38 |
+
# "finish_reason": "stop"
|
39 |
+
# }
|
40 |
+
# ]
|
41 |
+
# }
|
42 |
+
|
43 |
+
|
44 |
+
def map_answer_to_chat_response(answer_obj):
|
45 |
+
"""
|
46 |
+
Map backend output to a clean custom response schema.
|
47 |
+
"""
|
48 |
+
return {
|
49 |
+
"id": f"chatmsg-{int(__import__('time').time())}",
|
50 |
+
"createdAt": __import__("time").time(),
|
51 |
+
"model": answer_obj.get("model_used", "unknown-model"),
|
52 |
+
"answer": answer_obj["answer"].strip(),
|
53 |
+
"sources": [
|
54 |
+
{
|
55 |
+
"id": f"source-{i}",
|
56 |
+
"content": src["content"].strip(),
|
57 |
+
"metadata": src.get("metadata", {})
|
58 |
+
}
|
59 |
+
for i, src in enumerate(answer_obj.get("sources", []), 1)
|
60 |
+
]
|
61 |
+
}
|
utils/loader.py
ADDED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
from langchain.document_loaders import TextLoader
|
3 |
+
from langchain.text_splitter import RecursiveCharacterTextSplitter
|
4 |
+
import os
|
5 |
+
|
6 |
+
|
7 |
+
def load_documents(directory):
|
8 |
+
docs = []
|
9 |
+
for filename in os.listdir(directory):
|
10 |
+
if filename.endswith(".txt") or filename.endswith(".md"):
|
11 |
+
loader = TextLoader(os.path.join(
|
12 |
+
directory, filename), encoding='utf-8')
|
13 |
+
docs.extend(loader.load())
|
14 |
+
return docs
|
15 |
+
|
16 |
+
|
17 |
+
def split_documents(documents, chunk_size=1000, chunk_overlap=200, separators=["\n\n", "\n", " ", ""], length_function=len):
|
18 |
+
splitter = RecursiveCharacterTextSplitter(
|
19 |
+
chunk_size=chunk_size, chunk_overlap=chunk_overlap, separators=separators, length_function=length_function)
|
20 |
+
return splitter.split_documents(documents)
|
utils/rebuild_vector_stores.py
ADDED
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
import shutil
|
3 |
+
from langchain_community.embeddings import HuggingFaceEmbeddings
|
4 |
+
from langchain_community.vectorstores import Chroma
|
5 |
+
|
6 |
+
|
7 |
+
def rebuild_vector_store(doc_path="docs", persist_directory="./chroma_db"):
|
8 |
+
"""
|
9 |
+
Completely reset and rebuild a Chroma vector store from given documents.
|
10 |
+
Deletes the old store directory before building a new one.
|
11 |
+
"""
|
12 |
+
# 1. Remove old vector store directory if it exists
|
13 |
+
if os.path.exists(persist_directory):
|
14 |
+
print(f"🗑️ Removing existing vector store at {persist_directory}...")
|
15 |
+
shutil.rmtree(persist_directory)
|
16 |
+
|
17 |
+
# 2. Load and split documents
|
18 |
+
print(f"📄 Loading documents from {doc_path}...")
|
19 |
+
documents = load_documents(doc_path)
|
20 |
+
chunks = split_documents(documents)
|
21 |
+
print(f"✅ Loaded and split {len(chunks)} chunks.")
|
22 |
+
|
23 |
+
# 3. Create embeddings (fast & efficient model)
|
24 |
+
embeddings = HuggingFaceEmbeddings(
|
25 |
+
model_name="all-MiniLM-L6-v2", # or switch to mpnet for higher quality
|
26 |
+
model_kwargs={'device': 'cpu'}
|
27 |
+
)
|
28 |
+
|
29 |
+
# 4. Build vector store
|
30 |
+
print("⚙️ Building new vector store...")
|
31 |
+
vectordb = Chroma.from_documents(
|
32 |
+
documents=chunks,
|
33 |
+
embedding=embeddings,
|
34 |
+
persist_directory=persist_directory
|
35 |
+
)
|
36 |
+
|
37 |
+
# 5. Persist the new store
|
38 |
+
vectordb.persist()
|
39 |
+
print(f"✅ New vector store saved at {persist_directory}")
|
40 |
+
|
41 |
+
return vectordb
|
utils/vector_store.py
ADDED
@@ -0,0 +1,98 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
import shutil
|
3 |
+
from langchain_community.embeddings import HuggingFaceEmbeddings
|
4 |
+
from langchain_community.vectorstores import Chroma
|
5 |
+
from .loader import load_documents, split_documents
|
6 |
+
|
7 |
+
# ---------------------------
|
8 |
+
# Embedding configuration options
|
9 |
+
# ---------------------------
|
10 |
+
|
11 |
+
# 1️⃣ High performance / fast (good for large datasets, real-time apps)
|
12 |
+
# embeddings = HuggingFaceEmbeddings(
|
13 |
+
# model_name="all-MiniLM-L6-v2", # ~22M params, fast & low memory
|
14 |
+
# model_kwargs={'device': 'cpu'} # Use 'cuda' for GPU
|
15 |
+
# )
|
16 |
+
|
17 |
+
# 2️⃣ High accuracy / better semantic matching (slower, bigger model)
|
18 |
+
embeddings = HuggingFaceEmbeddings(
|
19 |
+
model_name="all-mpnet-base-v2", # ~109M params, top accuracy for general tasks
|
20 |
+
model_kwargs={'device': 'cpu'}
|
21 |
+
)
|
22 |
+
|
23 |
+
# 3️⃣ Instruction-based materials (good for “how-to” guides, step-by-step docs)
|
24 |
+
# embeddings = HuggingFaceEmbeddings(
|
25 |
+
# model_name="hkunlp/instructor-base", # Trained for instruction-following embeddings
|
26 |
+
# model_kwargs={'device': 'cpu'}
|
27 |
+
# )
|
28 |
+
|
29 |
+
# 4️⃣ QA-focused (retrieval tuned for question-answering use cases)
|
30 |
+
# embeddings = HuggingFaceEmbeddings(
|
31 |
+
# model_name="sentence-transformers/multi-qa-mpnet-base-dot-v1",
|
32 |
+
# model_kwargs={'device': 'cpu'}
|
33 |
+
# )
|
34 |
+
|
35 |
+
# 👉 Pick one of the above and uncomment it
|
36 |
+
|
37 |
+
# ---------------------------
|
38 |
+
# Build vector store
|
39 |
+
# ---------------------------
|
40 |
+
|
41 |
+
|
42 |
+
def build_vector_store(doc_path="docs", persist_directory="./chroma_db"):
|
43 |
+
documents = load_documents(doc_path)
|
44 |
+
chunks = split_documents(documents)
|
45 |
+
|
46 |
+
vectordb = Chroma.from_documents(
|
47 |
+
documents=chunks,
|
48 |
+
embedding=embeddings,
|
49 |
+
persist_directory=persist_directory
|
50 |
+
)
|
51 |
+
vectordb.persist()
|
52 |
+
return vectordb
|
53 |
+
|
54 |
+
# ---------------------------
|
55 |
+
# Load existing vector store
|
56 |
+
# ---------------------------
|
57 |
+
|
58 |
+
|
59 |
+
def get_vector_store(persist_directory="./vector_stores/tech_db", embedding=embeddings):
|
60 |
+
|
61 |
+
vectordb = Chroma(
|
62 |
+
persist_directory=persist_directory,
|
63 |
+
embedding_function=embedding
|
64 |
+
)
|
65 |
+
return vectordb
|
66 |
+
|
67 |
+
# ---------------------------
|
68 |
+
# Rebuild (delete & recreate) vector store
|
69 |
+
# ---------------------------
|
70 |
+
|
71 |
+
|
72 |
+
def rebuild_vector_store(doc_path="docs", persist_directory="./chroma_db"):
|
73 |
+
"""
|
74 |
+
Completely reset and rebuild a Chroma vector store from given documents.
|
75 |
+
Deletes the old store directory before building a new one.
|
76 |
+
"""
|
77 |
+
# Remove old vector store
|
78 |
+
if os.path.exists(persist_directory):
|
79 |
+
print(f"🗑️ Removing existing vector store at {persist_directory}...")
|
80 |
+
shutil.rmtree(persist_directory)
|
81 |
+
|
82 |
+
# Load and split docs
|
83 |
+
print(f"📄 Loading documents from {doc_path}...")
|
84 |
+
documents = load_documents(doc_path)
|
85 |
+
chunks = split_documents(documents)
|
86 |
+
print(f"✅ Loaded and split {len(chunks)} chunks.")
|
87 |
+
|
88 |
+
# Build new store
|
89 |
+
print("⚙️ Building new vector store...")
|
90 |
+
vectordb = Chroma.from_documents(
|
91 |
+
documents=chunks,
|
92 |
+
embedding=embeddings,
|
93 |
+
persist_directory=persist_directory
|
94 |
+
)
|
95 |
+
vectordb.persist()
|
96 |
+
print(f"✅ New vector store saved at {persist_directory}")
|
97 |
+
|
98 |
+
return vectordb
|