TimeCapsuleX commited on
Commit
13cd9b4
·
1 Parent(s): 9578173

Uploading my FMEA Python app and data

Browse files
Files changed (3) hide show
  1. 10000fmea_data.csv +0 -0
  2. app.py +268 -0
  3. requirements.txt +10 -0
10000fmea_data.csv ADDED
The diff for this file is too large to render. See raw diff
 
app.py ADDED
@@ -0,0 +1,268 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+ import re
4
+ import torch
5
+ import gradio as gr
6
+ import pandas as pd
7
+
8
+ # --- LangChain Imports ---
9
+ from langchain_google_genai import ChatGoogleGenerativeAI
10
+ from langchain_community.document_loaders import CSVLoader
11
+ from langchain_community.vectorstores import FAISS
12
+ from langchain_huggingface import HuggingFaceEmbeddings
13
+ from langchain_classic.chains import RetrievalQA
14
+ from langchain_core.prompts import PromptTemplate
15
+
16
+ # --- 1. Setup API Key for Hugging Face Spaces ---
17
+ # HF Spaces uses standard environment variables instead of Colab secrets
18
+ GOOGLE_API_KEY = os.environ.get('GOOGLE_API_KEY')
19
+ if not GOOGLE_API_KEY:
20
+ raise ValueError("🔴 GOOGLE_API_KEY not found. Please add it to your Hugging Face Space Secrets.")
21
+
22
+ # --- 2. Build the RAG Chain & Feedback System ---
23
+ FMEA_DATA_FILE = '10000fmea_data.csv'
24
+ FEEDBACK_FILE = 'fmea_feedback.csv'
25
+ QA_CHAIN = None
26
+ feedback_vector_store = None
27
+ embeddings = None
28
+
29
+ DEVICE = 'cuda' if torch.cuda.is_available() else 'cpu'
30
+ print(f"✅ Using device: {DEVICE}")
31
+
32
+ # --- FEEDBACK LOOP PART 1: Saving, Normalizing, and Loading Feedback ---
33
+ def normalize_action(text: str) -> str:
34
+ return re.sub(r'\s+', ' ', str(text).strip().lower())
35
+
36
+ def load_feedback_stats():
37
+ if not os.path.exists(FEEDBACK_FILE):
38
+ return {}
39
+ try:
40
+ feedback_df = pd.read_csv(FEEDBACK_FILE)
41
+ if feedback_df.empty:
42
+ return {}
43
+ stats = feedback_df.groupby('action')['rating'].agg(['mean', 'count']).to_dict('index')
44
+ return stats
45
+ except pd.errors.EmptyDataError:
46
+ return {}
47
+
48
+ def save_feedback(action, rating):
49
+ if not action:
50
+ return "Please select a recommendation from the table first."
51
+ norm_action = normalize_action(action)
52
+ new_feedback = pd.DataFrame([{'action': norm_action, 'rating': int(rating)}])
53
+ if not os.path.exists(FEEDBACK_FILE):
54
+ new_feedback.to_csv(FEEDBACK_FILE, index=False)
55
+ else:
56
+ new_feedback.to_csv(FEEDBACK_FILE, mode='a', header=False, index=False)
57
+ build_feedback_db()
58
+ return f"✅ Rating of {rating}/10 saved for: {action}"
59
+
60
+ def build_feedback_db():
61
+ global feedback_vector_store
62
+ if not os.path.exists(FEEDBACK_FILE):
63
+ return
64
+ try:
65
+ feedback_df = pd.read_csv(FEEDBACK_FILE)
66
+ if feedback_df.empty:
67
+ return
68
+ except pd.errors.EmptyDataError:
69
+ return
70
+
71
+ avg_ratings = feedback_df.groupby('action')['rating'].mean()
72
+ highly_rated_actions = avg_ratings[avg_ratings > 7].index.tolist()
73
+
74
+ if highly_rated_actions and embeddings:
75
+ print(f"Found {len(highly_rated_actions)} highly-rated actions. Building feedback vector store...")
76
+ feedback_vector_store = FAISS.from_texts(highly_rated_actions, embeddings)
77
+ print("✅ Feedback vector store is ready.")
78
+
79
+ # --- build_rag_chain ---
80
+ def build_rag_chain():
81
+ global QA_CHAIN, embeddings
82
+ try:
83
+ print("Initializing local HuggingFace embedding model...")
84
+ embeddings = HuggingFaceEmbeddings(
85
+ model_name='all-MiniLM-L6-v2',
86
+ model_kwargs={'device': DEVICE}
87
+ )
88
+ print("✅ Local embedding model loaded.")
89
+
90
+ build_feedback_db()
91
+
92
+ print(f"Loading FMEA data from {FMEA_DATA_FILE}...")
93
+ loader = CSVLoader(file_path=FMEA_DATA_FILE, source_column="Failure_Mode")
94
+ documents = loader.load()
95
+ print(f"✅ Successfully loaded {len(documents)} records.")
96
+
97
+ print("Creating embeddings and building main FAISS vector store...")
98
+ main_vector_store = FAISS.from_documents(documents, embeddings)
99
+ print("✅ Main vector store created successfully.")
100
+
101
+ llm = ChatGoogleGenerativeAI(model="gemini-2.5-flash", temperature=0.2)
102
+
103
+ prompt_template = """
104
+ You are an expert FMEA analyst. Your task is to generate the TOP 3 recommended actions for the given failure.
105
+ The user has provided their current S, O, and D scores.
106
+ For EACH recommendation, you must also estimate the revised S, O, and D scores (1-10) that would result *after* that action is successfully implemented.
107
+
108
+ - **new_S (Severity):** This score should *usually* stay the same as the original Severity.
109
+ - **new_O (Occurrence):** This score should be *lower* than the original Occurrence.
110
+ - **new_D (Detection):** This score should be *lower* than the original Detection (as the action makes the failure easier to detect).
111
+
112
+ CONTEXT (Historical data and user feedback):
113
+ {context}
114
+
115
+ QUESTION (The new failure and its current scores):
116
+ {question}
117
+
118
+ INSTRUCTIONS:
119
+ Format your entire response as a single, valid JSON object with a key "recommendations" which is a list of 3 objects.
120
+ Each object must have these keys: "rank", "action", "department", "ai_score", "new_S", "new_O", "new_D".
121
+
122
+ - "rank": The rank of the recommendation (1, 2, 3).
123
+ - "action": The recommended action text.
124
+ - "department": The most likely responsible department.
125
+ - "ai_score": Confidence score (1-100) for this recommendation.
126
+ - "new_S": Your estimated new Severity score (1-10).
127
+ - "new_O": Your estimated new Occurrence score (1-10).
128
+ - "new_D": Your estimated new Detection score (1-10).
129
+ """
130
+ PROMPT = PromptTemplate(template=prompt_template, input_variables=["context", "question"])
131
+
132
+ # Included the token-saving "k": 2 limit we discussed earlier!
133
+ retriever = main_vector_store.as_retriever(search_kwargs={"k": 2})
134
+ QA_CHAIN = RetrievalQA.from_chain_type(
135
+ llm=llm,
136
+ chain_type="stuff",
137
+ retriever=retriever,
138
+ chain_type_kwargs={"prompt": PROMPT}
139
+ )
140
+ print("✅ RAG model is ready.")
141
+ return True
142
+ except Exception as e:
143
+ print(f"🔴 An error occurred during RAG setup: {e}")
144
+ return False
145
+
146
+ # --- 3. Gradio Interface Logic ---
147
+ def fmea_rag_interface(mode, effect, cause, severity, occurrence, detection):
148
+ if QA_CHAIN is None:
149
+ return "RAG Model is not initialized.", pd.DataFrame(), ""
150
+
151
+ rpn = severity * occurrence * detection
152
+ rpn_text = f"Current RPN (S×O×D): {int(rpn)}"
153
+
154
+ query = (
155
+ f"For a failure with Failure Mode='{mode}', Effect='{effect}', and Cause='{cause}', "
156
+ f"what are the top 3 most appropriate recommended actions? "
157
+ f"The current scores are: Severity={severity}, Occurrence={occurrence}, Detection={detection}."
158
+ )
159
+
160
+ docs = QA_CHAIN.retriever.invoke(query)
161
+ context_from_history = "\n---\n".join([doc.page_content for doc in docs])
162
+
163
+ context_from_feedback = ""
164
+ if feedback_vector_store:
165
+ feedback_docs = feedback_vector_store.similarity_search(query, k=3)
166
+ if feedback_docs:
167
+ feedback_actions = "\n".join([doc.page_content for doc in feedback_docs])
168
+ context_from_feedback = f"\n\n--- Highly-Rated Actions from User Feedback ---\n{feedback_actions}"
169
+
170
+ combined_context = f"--- Historical FMEA Entries ---\n{context_from_history}{context_from_feedback}"
171
+
172
+ try:
173
+ result = QA_CHAIN.invoke({"query": query, "context": combined_context})
174
+ json_text = result["result"].strip().replace("```json", "").replace("```", "")
175
+ data = json.loads(json_text)
176
+ output_df = pd.DataFrame(data['recommendations'])
177
+
178
+ feedback_stats = load_feedback_stats()
179
+ default_stat = {'mean': 0, 'count': 0}
180
+ stats_list = [feedback_stats.get(normalize_action(action), default_stat) for action in output_df['action']]
181
+ output_df['avg_feedback'] = [stat['mean'] for stat in stats_list]
182
+ output_df['feedback_count'] = [stat['count'] for stat in stats_list]
183
+
184
+ output_df['new_S'] = output_df['new_S'].astype(int)
185
+ output_df['new_O'] = output_df['new_O'].astype(int)
186
+ output_df['new_D'] = output_df['new_D'].astype(int)
187
+ output_df['new_RPN'] = output_df['new_S'] * output_df['new_O'] * output_df['new_D']
188
+
189
+ rpn_change_list = [f"{int(rpn)} ➔ {int(new_rpn)}" for new_rpn in output_df['new_RPN']]
190
+
191
+ display_df = pd.DataFrame({
192
+ "Rank": output_df['rank'],
193
+ "Recommended Action": output_df['action'],
194
+ "Department": output_df['department'],
195
+ "AI Confidence": [f"{score}%" for score in output_df['ai_score']],
196
+ "Avg. Feedback": [f"{avg:.2f}/10 ({int(count)})" for avg, count in zip(output_df['avg_feedback'], output_df['feedback_count'])],
197
+ "Revised RPN": rpn_change_list
198
+ })
199
+
200
+ except Exception as e:
201
+ print(f"Error parsing LLM output: {e}")
202
+ return rpn_text, pd.DataFrame({"Error": [f"Could not parse AI response: {e}"]}), None
203
+
204
+ return rpn_text, display_df, output_df
205
+
206
+ # --- 6. Main Application Execution ---
207
+ if build_rag_chain():
208
+ print("\n🚀 Launching Gradio Interface...")
209
+ with gr.Blocks(theme=gr.themes.Default(primary_hue=gr.themes.colors.blue)) as demo:
210
+ gr.Markdown("<h1>Failure Mode (FMEA) RAG Recommendation Engine</h1>")
211
+
212
+ with gr.Group():
213
+ gr.Markdown("## FMEA Inputs ")
214
+ with gr.Row():
215
+ with gr.Column(scale=2):
216
+ f_mode = gr.Textbox(label="Failure Mode", placeholder="e.g., Engine Overheating")
217
+ f_effect = gr.Textbox(label="Effect", placeholder="e.g., Reduced vehicle performance")
218
+ f_cause = gr.Textbox(label="Cause", placeholder="e.g., Coolant leak")
219
+ with gr.Column(scale=1):
220
+ f_sev = gr.Slider(1, 10, value=5, step=1, label="Severity")
221
+ f_occ = gr.Slider(1, 10, value=5, step=1, label="Occurrence")
222
+ f_det = gr.Slider(1, 10, value=5, step=1, label="Detection")
223
+
224
+ submit_btn = gr.Button("Get AI Recommendations", variant="primary")
225
+
226
+ with gr.Group():
227
+ gr.Markdown("## 💡 Top 3 AI-Generated Recommendations")
228
+ rpn_output = gr.Textbox(label="Current RPN", interactive=False)
229
+ recommendations_output = gr.DataFrame(
230
+ headers=["Rank", "Recommended Action", "Department", "AI Confidence", "Avg. Feedback", "Revised RPN"],
231
+ datatype=["number", "str", "str", "str", "str", "str"]
232
+ )
233
+ df_state = gr.State()
234
+
235
+ with gr.Group():
236
+ gr.Markdown("## ⭐ Provide Feedback")
237
+ gr.Markdown("Click a row in the table above to select it, then submit your rating.")
238
+ selected_action_text = gr.Textbox(label="Selected for Feedback", interactive=False)
239
+ rating_slider = gr.Slider(minimum=1, maximum=10, step=1, value=8, label="Your Rating (1-10)")
240
+ submit_feedback_btn = gr.Button("Submit Rating")
241
+ feedback_status = gr.Textbox(label="Feedback Status", interactive=False)
242
+
243
+ def update_selection(df, evt: gr.SelectData):
244
+ if df is None or df.empty: return ""
245
+ selected_row = df.iloc[evt.index[0]]
246
+ action = selected_row['action']
247
+ return action
248
+
249
+ submit_btn.click(
250
+ fn=fmea_rag_interface,
251
+ inputs=[f_mode, f_effect, f_cause, f_sev, f_occ, f_det],
252
+ outputs=[rpn_output, recommendations_output, df_state]
253
+ )
254
+
255
+ recommendations_output.select(
256
+ fn=update_selection,
257
+ inputs=[df_state],
258
+ outputs=[selected_action_text]
259
+ )
260
+
261
+ submit_feedback_btn.click(
262
+ fn=save_feedback,
263
+ inputs=[selected_action_text, rating_slider],
264
+ outputs=[feedback_status]
265
+ )
266
+
267
+ # Simplified launch command for Hugging Face
268
+ demo.launch()
requirements.txt ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ gradio
2
+ pandas
3
+ langchain
4
+ langchain-community
5
+ langchain-core
6
+ langchain-google-genai
7
+ faiss-cpu
8
+ sentence-transformers
9
+ langchain-classic
10
+ langchain-huggingface