Nyha15 commited on
Commit
797ac14
·
1 Parent(s): d776e25

Refactored

Browse files
Files changed (1) hide show
  1. app.py +141 -19
app.py CHANGED
@@ -13,10 +13,16 @@ import threading
13
  from typing import List, Dict, Any, Optional
14
  from functools import lru_cache
15
 
16
- import gradio as gr
17
- from langchain_openai import ChatOpenAI, OpenAIEmbeddings
18
- from langchain_community.vectorstores import Chroma
19
- from langchain.text_splitter import RecursiveCharacterTextSplitter
 
 
 
 
 
 
20
 
21
  # =======================================
22
  # API Key & Workflow Logging
@@ -43,10 +49,24 @@ def clear_workflow_log():
43
 
44
 
45
  def get_workflow_log() -> str:
46
- return "\n".join(
47
- f"**[{e['time']}]** {e['step']}" + (f" {e['details']}" if 'details' in e else '')
48
- for e in WORKFLOW_LOG
49
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
 
51
  # =======================================
52
  # Tax Regulation Database
@@ -153,8 +173,6 @@ KB_INSTANCES: Dict[str, 'KnowledgeBase'] = {}
153
  COMMON_COUNTRIES = ["India", "China"]
154
  DOMAINS = ["banking", "credit", "tax"]
155
 
156
- # Placeholder for class KnowledgeBase declaration
157
-
158
  # =======================================
159
  # RAG Knowledge Base
160
  # =======================================
@@ -258,9 +276,102 @@ class CoordinatorAgent:
258
  self.specialists = {
259
  "banking": BankingAdvisor(),
260
  "credit": CreditBuilder(),
261
- "tax": TaxSpecialist()
262
  }
263
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
264
  def run(self, query: str, profile: Dict[str,Any]) -> str:
265
  clear_workflow_log()
266
  country = profile.get("home_country", "unknown")
@@ -285,29 +396,40 @@ class CoordinatorAgent:
285
  )
286
  try:
287
  plans_resp = self.llm.invoke(plans_prompt)
288
- plans = json.loads(plans_resp.content)
 
 
 
 
 
 
 
289
  except Exception as e:
290
  log_workflow("Error generating multi-path plans", str(e))
291
- plans = {"conservative": "...", "balanced": "...", "growth": "..."}
 
 
 
 
292
 
293
  # 3. Build the formatted output using a string builder approach
294
  lines: List[str] = []
295
  lines.append("# Your Personalized Financial Advice")
296
  for domain, text in advice_map.items():
297
  lines.append(f"## {domain.capitalize()}")
298
- for paragraph in text.split(" "):
299
- for line in paragraph.split(" "):
300
- lines.append(" " + line)
301
- lines.append("")
302
 
303
  lines.append("## Multi-Path Plans")
304
- lines.append("```json")
305
  lines.append(json.dumps(plans, indent=2))
306
  lines.append("```")
307
 
308
  formatted = "\n".join(lines)
309
  log_workflow("Synthesis complete")
310
- return formatted + " " + get_workflow_log()
 
311
 
312
 
313
  # =======================================
 
13
  from typing import List, Dict, Any, Optional
14
  from functools import lru_cache
15
 
16
+ try:
17
+ # Import required libraries
18
+ import gradio as gr
19
+ from langchain_openai import ChatOpenAI, OpenAIEmbeddings
20
+ from langchain_community.vectorstores import Chroma
21
+ from langchain.text_splitter import RecursiveCharacterTextSplitter
22
+ except ImportError as e:
23
+ print(f"Error importing required libraries: {e}")
24
+ print("Please install required packages: pip install -r requirements.txt")
25
+ sys.exit(1)
26
 
27
  # =======================================
28
  # API Key & Workflow Logging
 
49
 
50
 
51
  def get_workflow_log() -> str:
52
+ if not WORKFLOW_LOG:
53
+ return "No workflow steps recorded yet."
54
+
55
+ log_text = "## Workflow Execution Log:\n\n"
56
+ for entry in WORKFLOW_LOG:
57
+ log_text += f"**[{entry['time']}]** {entry['step']}"
58
+ if 'details' in entry and entry['details']:
59
+ details = entry['details']
60
+ if isinstance(details, dict):
61
+ for k, v in details.items():
62
+ if isinstance(v, str) and len(v) > 100:
63
+ details[k] = v[:100] + "..."
64
+ log_text += f"``````\n"
65
+ else:
66
+ log_text += f"{details}\n"
67
+ log_text += "\n"
68
+
69
+ return log_text
70
 
71
  # =======================================
72
  # Tax Regulation Database
 
173
  COMMON_COUNTRIES = ["India", "China"]
174
  DOMAINS = ["banking", "credit", "tax"]
175
 
 
 
176
  # =======================================
177
  # RAG Knowledge Base
178
  # =======================================
 
276
  self.specialists = {
277
  "banking": BankingAdvisor(),
278
  "credit": CreditBuilder(),
279
+ "tax": TaxSpecialist()
280
  }
281
 
282
+ def _identify_relevant_specialists(self, query: str) -> List[str]:
283
+ """Identify which specialists are relevant to the query"""
284
+ log_workflow("Analyzing query to identify relevant specialists")
285
+
286
+ relevance_prompt = f"""
287
+ Based on this financial query from an international student:
288
+ "{query}"
289
+
290
+ Which of the following specialist advisors should be consulted? Choose only the relevant ones.
291
+ - banking (Banking Advisor: bank accounts, account types, transfers, documentation)
292
+ - credit (Credit Builder: credit cards, credit scores, credit history)
293
+ - tax (Tax Specialist: income taxes, tax treaties, FBAR, tax forms)
294
+
295
+ Return a comma-separated list of ONLY the relevant domain codes (e.g., "banking,credit").
296
+ """
297
+
298
+ try:
299
+ response = self.llm.invoke(relevance_prompt)
300
+ domains = [domain.strip().lower() for domain in response.content.split(',')]
301
+ valid_domains = [domain for domain in domains if domain in self.specialists]
302
+
303
+ # Add tax domain if query mentions tax
304
+ if "tax" not in valid_domains and "tax" in query.lower():
305
+ valid_domains.append("tax")
306
+
307
+ log_workflow("Identified relevant specialists", {"domains": valid_domains})
308
+ return valid_domains
309
+ except Exception as e:
310
+ log_workflow("Error identifying specialists", str(e))
311
+ # Default to essential domains if there's an error
312
+ default_domains = ["banking"]
313
+ if "tax" in query.lower():
314
+ default_domains.append("tax")
315
+ if "credit" in query.lower():
316
+ default_domains.append("credit")
317
+ return default_domains
318
+
319
+ def process_query(self, query: str, country: str) -> str:
320
+ """Process a query from an international student"""
321
+ log_workflow("Processing query", {"query": query, "country": country})
322
+
323
+ # Identify relevant specialists
324
+ relevant_domains = self._identify_relevant_specialists(query)
325
+
326
+ # Get advice from each relevant specialist
327
+ specialist_advice = {}
328
+ for domain in relevant_domains:
329
+ specialist = self.specialists[domain]
330
+ advice = specialist.run(query, country)
331
+ specialist_advice[domain] = advice
332
+
333
+ # Synthesize advice from specialists
334
+ final_advice = self._synthesize_advice(query, country, specialist_advice)
335
+
336
+ return final_advice
337
+
338
+ def _synthesize_advice(self, query: str, country: str, specialist_advice: Dict[str, str]) -> str:
339
+ """Synthesize advice from multiple specialists into a coherent response"""
340
+ log_workflow("Synthesizing advice from specialists")
341
+
342
+ # Create a consolidated advice text
343
+ advice_sections = []
344
+ for domain, advice in specialist_advice.items():
345
+ advice_sections.append(f"## {domain.capitalize()} Advice\n\n{advice}")
346
+
347
+ consolidated_advice = "\n\n".join(advice_sections)
348
+
349
+ synthesis_prompt = f"""
350
+ As a financial advisor for international students, synthesize this specialist advice into a coherent response.
351
+
352
+ STUDENT QUERY:
353
+ {query}
354
+
355
+ COUNTRY:
356
+ {country}
357
+
358
+ SPECIALIST ADVICE:
359
+ {consolidated_advice}
360
+
361
+ Create a comprehensive, well-organized response that integrates all relevant advice.
362
+ Begin with a summary of key recommendations, then provide detailed sections for each area.
363
+ """
364
+
365
+ try:
366
+ response = self.llm.invoke(synthesis_prompt)
367
+ final_advice = response.content
368
+ log_workflow("Synthesized final advice", {"length": len(final_advice)})
369
+ return final_advice
370
+ except Exception as e:
371
+ log_workflow("Error synthesizing advice", str(e))
372
+ # Fallback to concatenated advice
373
+ return "# Financial Advice Summary\n\n" + consolidated_advice
374
+
375
  def run(self, query: str, profile: Dict[str,Any]) -> str:
376
  clear_workflow_log()
377
  country = profile.get("home_country", "unknown")
 
396
  )
397
  try:
398
  plans_resp = self.llm.invoke(plans_prompt)
399
+ plans_text = plans_resp.content
400
+ # Clean up the response to ensure it's valid JSON
401
+ plans_text = plans_text.strip()
402
+ if plans_text.startswith("```json"):
403
+ plans_text = plans_text.split("```json")[1]
404
+ if plans_text.endswith("```"):
405
+ plans_text = plans_text.split("```")[0]
406
+ plans = json.loads(plans_text)
407
  except Exception as e:
408
  log_workflow("Error generating multi-path plans", str(e))
409
+ plans = {
410
+ "conservative": "Conservative investment strategy focusing on safety",
411
+ "balanced": "Balanced approach with moderate risk and return",
412
+ "growth": "Growth-oriented strategy with higher risk and potential return"
413
+ }
414
 
415
  # 3. Build the formatted output using a string builder approach
416
  lines: List[str] = []
417
  lines.append("# Your Personalized Financial Advice")
418
  for domain, text in advice_map.items():
419
  lines.append(f"## {domain.capitalize()}")
420
+ for paragraph in text.split("\n\n"): # Split by paragraphs
421
+ lines.append(paragraph)
422
+ lines.append("") # Add empty line between paragraphs
 
423
 
424
  lines.append("## Multi-Path Plans")
425
+ lines.append("```")
426
  lines.append(json.dumps(plans, indent=2))
427
  lines.append("```")
428
 
429
  formatted = "\n".join(lines)
430
  log_workflow("Synthesis complete")
431
+ return f"{formatted}\n\n---\n\n{get_workflow_log()}"
432
+
433
 
434
 
435
  # =======================================