Nyha15 commited on
Commit
7fcac4d
·
1 Parent(s): 0c70f0b

Refactored

Browse files
Files changed (1) hide show
  1. app.py +934 -151
app.py CHANGED
@@ -1,215 +1,998 @@
 
 
 
 
 
 
 
1
  import os
2
  import sys
3
  import time
4
  import json
5
  from typing import List, Dict, Any, Optional
6
 
7
- import gradio as gr
8
- from langchain_openai import ChatOpenAI
9
- from langchain_community.vectorstores import Chroma
10
- from langchain_openai import OpenAIEmbeddings
11
- from langchain.text_splitter import RecursiveCharacterTextSplitter
12
-
13
- # Load API key
14
- def get_api_key():
15
- key = os.environ.get("OPENAI_API_KEY")
16
- if not key:
17
- key = input("Please enter your OpenAI API key: ")
18
- os.environ["OPENAI_API_KEY"] = key
19
- return key
20
-
21
- # Workflow log utilities
22
- WORKFLOW_LOG: List[Dict[str, Any]] = []
23
-
24
- def log_workflow(step: str, details: Any = None):
 
 
 
 
 
 
 
 
 
25
  timestamp = time.strftime("%H:%M:%S")
26
  entry = {"time": timestamp, "step": step}
27
- if details is not None:
28
  entry["details"] = details
29
  WORKFLOW_LOG.append(entry)
30
  print(f"[{timestamp}] {step}{': ' + str(details) if details else ''}")
31
 
32
-
33
- def get_workflow_log() -> str:
34
  if not WORKFLOW_LOG:
35
  return "No workflow steps recorded yet."
 
36
  log_text = "## Workflow Execution Log:\n\n"
37
- for e in WORKFLOW_LOG:
38
- log_text += f"**[{e['time']}]** {e['step']}\n"
39
- if e.get('details'):
40
- d = e['details']
41
- if isinstance(d, dict):
42
- short = {k: (v[:100] + '...' if isinstance(v, str) and len(v) > 100 else v)
43
- for k, v in d.items()}
44
- log_text += f"```json\n{json.dumps(short, indent=2)}\n```\n"
 
45
  else:
46
- log_text += f"{d}\n"
47
- return log_text
48
 
 
49
 
50
  def clear_workflow_log():
 
51
  global WORKFLOW_LOG
52
  WORKFLOW_LOG = []
53
 
54
- # Placeholder: DataCollector, KnowledgeBase, and SpecialistAgents definitions here...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
 
56
  # =======================================
57
- # Coordinator Agent
58
  # =======================================
 
59
  class CoordinatorAgent:
 
 
60
  def __init__(self, llm=None):
61
- self.llm = llm or ChatOpenAI(temperature=0.3)
62
- # Initialize specialists
 
 
 
 
 
 
 
 
 
 
 
63
  self.specialists = {
64
- 'banking': BankingAdvisor(self.llm),
65
- 'credit': CreditBuilder(self.llm),
66
- 'budget': BudgetManager(self.llm),
67
- 'currency': CurrencyExchangeSpecialist(self.llm),
68
- 'loans': StudentLoanAdvisor(self.llm),
69
- 'career': CareerFinancePlanner(self.llm),
70
- 'legal': LegalFinanceAdvisor(self.llm)
71
  }
72
 
73
  def _identify_relevant_specialists(self, query: str) -> List[str]:
74
- prompt = f"Which domains for: {query}? Options: banking,credit,budget,currency,loans,career,legal"
75
- try:
76
- res = self.llm.invoke(prompt)
77
- domains = [d.strip() for d in res.content.split(',')]
78
- except:
79
- domains = ['banking', 'budget']
80
- return [d for d in domains if d in self.specialists]
81
-
82
- def _generate_plans(self, query: str, constraints: str, country: str) -> Dict[str, str]:
83
- plans: Dict[str, str] = {}
84
- template = f"Goal: {query}\nConstraints: {constraints}\nCountry: {country}\nApproach: {{}}"
85
- for tag, agent in [('conservative', self.specialists['budget']),
86
- ('balanced', self.specialists['banking']),
87
- ('growth', self.specialists['credit'])]:
88
- log_workflow(f"Generating {tag} plan")
89
- plans[tag] = agent.run(template.format(tag), country)
90
- return plans
91
-
92
- def _reflect_on_recommendation(self, recommendation: str, profile: Dict[str, Any]) -> str:
93
- country = profile.get('home_country', 'unknown')
94
- visa = profile.get('visa_type', 'unknown')
95
- prompt = f"Reflect on compliance for {country}/{visa}: {recommendation}"
96
  try:
97
- return self.specialists['legal'].run(prompt, country)
98
- except:
99
- return 'Reflection unavailable'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
 
101
- def run(self, query: str, profile: Dict[str, Any]) -> str:
102
- log_workflow('COORDINATOR start', {'query': query[:50]})
103
- country = profile.get('home_country', 'unknown')
104
- domains = self._identify_relevant_specialists(query)
105
 
106
- # 1. Collect specialist advice
107
- advice = {d: self.specialists[d].run(query, country) for d in domains}
 
108
 
109
- # 2. Generate multi-path plans
110
- constraints = json.dumps(profile)
111
- plans = self._generate_plans(query, constraints, country)
 
 
 
 
 
 
 
 
 
 
 
 
 
112
 
113
- # 3. Build the specialist advice section
114
- specialist_lines: List[str] = []
115
- for d, a in advice.items():
116
- specialist_lines.append(f"--- {d.upper()} SPECIALIST ---")
117
- specialist_lines.append(a[:1000])
118
- specialist_lines.append("")
119
- spec_section = "\n".join(specialist_lines).strip()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
120
 
121
- # 4. Prepare plan snippets
122
- cons = plans['conservative'][:1000]
123
- bal = plans['balanced'][:1000]
124
- gro = plans['growth'][:1000]
125
 
126
- # 5. Compose synthesis prompt without inline backslashes in expressions
127
  synthesis_prompt = f"""
128
- As coordinator, synthesize advice and plans.
 
 
 
 
 
 
 
129
 
130
- QUERY: {query}
 
131
 
132
- SPECIALIST ADVICE:
133
- {spec_section}
134
 
135
- FINANCIAL APPROACHES:
136
- --- CONSERVATIVE ---
137
- {cons}
138
 
139
- --- BALANCED ---
140
- {bal}
141
 
142
- --- GROWTH ---
143
- {gro}
144
 
145
- Include detailed steps, names, amounts, timelines.
146
- """.strip()
 
147
 
148
- log_workflow('Synthesizing response')
149
- synth = self.llm.invoke(synthesis_prompt).content
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
150
 
151
- # 6. Final prompt via concatenation to avoid f-string backslash issues
152
- reflection = self._reflect_on_recommendation(synth, profile)
153
- final_prompt = synth + "\n\nLEGAL REFLECTION:\n" + reflection
154
- log_workflow('Finalizing response')
155
- return self.llm.invoke(final_prompt).content
156
 
157
  # =======================================
158
- # Gradio Interface
159
  # =======================================
 
160
  class FinancePortal:
 
 
161
  def __init__(self):
162
- get_api_key()
163
  self.coordinator = CoordinatorAgent()
164
- self.profiles: Dict[str, Any] = {}
 
 
 
 
165
 
166
- def register(self, pid: str, prof: Dict[str, Any]):
167
- self.profiles[pid] = prof
 
168
 
169
- def get_profile(self, pid: str) -> Optional[Dict[str, Any]]:
170
- return self.profiles.get(pid)
 
171
 
172
- def handle(self, pid: str, query: str) -> str:
173
- prof = self.get_profile(pid)
174
- if not prof:
175
- return 'Provide profile first.'
 
 
 
 
 
176
  clear_workflow_log()
177
- response = self.coordinator.run(query, prof)
178
- return response + "\n\n---\n\n" + get_workflow_log()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
179
 
180
 
181
  def create_interface():
 
182
  portal = FinancePortal()
183
 
184
- def submit(query, country, visa, uni, fund, info):
185
- pid = 'user'
186
- portal.register(pid, {
187
- 'home_country': country,
188
- 'visa_type': visa,
189
- 'university': uni,
190
- 'funding': fund,
191
- 'additional_info': info
192
- })
193
- return portal.handle(pid, query)
194
-
195
- with gr.Blocks() as demo:
196
- gr.Markdown('# International Student Finance Portal')
197
- country = gr.Dropdown(['','India','China','Brazil','Other'], label='Home Country')
198
- visa = gr.Dropdown(['','F-1','J-1','M-1','Other'], label='Visa Type')
199
- uni = gr.Textbox(label='University')
200
- fund = gr.Dropdown(['','Self/Family','Scholarship','TA/RA','Loan','Other'], label='Funding')
201
- info = gr.Textbox(label='Additional Info')
202
- query = gr.Textbox(label='Your Financial Question', lines=4)
203
- btn = gr.Button('Get Advice')
204
- out = gr.Markdown()
205
-
206
- btn.click(fn=submit,
207
- inputs=[query, country, visa, uni, fund, info],
208
- outputs=out)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
209
  return demo
210
 
211
- if __name__ == '__main__':
212
- print('Starting Finance Portal...')
213
- get_api_key()
214
- app = create_interface()
215
- app.launch()
 
 
1
+ """
2
+ International Student Finance Portal
3
+ A comprehensive financial advisory system for international students
4
+ Implements 5 agent design patterns: RAG, Role-based Cooperation, Voting-based Cooperation,
5
+ Self-reflection, and Multi-path Plan Generator
6
+ """
7
+
8
  import os
9
  import sys
10
  import time
11
  import json
12
  from typing import List, Dict, Any, Optional
13
 
14
+ try:
15
+ # Import required libraries
16
+ import gradio as gr
17
+ from langchain.agents import AgentExecutor, create_openai_tools_agent
18
+ from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
19
+ from langchain_core.messages import AIMessage, HumanMessage, SystemMessage
20
+ from langchain_core.tools import BaseTool, StructuredTool, tool
21
+ from langchain_openai import ChatOpenAI
22
+ from langchain_community.vectorstores import Chroma
23
+ from langchain_openai import OpenAIEmbeddings
24
+ from langchain.text_splitter import RecursiveCharacterTextSplitter
25
+ except ImportError as e:
26
+ print(f"Error importing required libraries: {e}")
27
+ print("Please install required packages: pip install -r requirements.txt")
28
+ sys.exit(1)
29
+
30
+ # Set up API Key
31
+ api_key = os.environ.get("OPENAI_API_KEY")
32
+ if not api_key:
33
+ api_key = input("Please enter your OpenAI API key: ")
34
+ os.environ["OPENAI_API_KEY"] = api_key
35
+
36
+ # Global workflow log to track the execution flow
37
+ WORKFLOW_LOG = []
38
+
39
+ def log_workflow(step, details=None):
40
+ """Add a step to the workflow log"""
41
  timestamp = time.strftime("%H:%M:%S")
42
  entry = {"time": timestamp, "step": step}
43
+ if details:
44
  entry["details"] = details
45
  WORKFLOW_LOG.append(entry)
46
  print(f"[{timestamp}] {step}{': ' + str(details) if details else ''}")
47
 
48
+ def get_workflow_log():
49
+ """Get the workflow log as formatted text"""
50
  if not WORKFLOW_LOG:
51
  return "No workflow steps recorded yet."
52
+
53
  log_text = "## Workflow Execution Log:\n\n"
54
+ for entry in WORKFLOW_LOG:
55
+ log_text += f"**[{entry['time']}]** {entry['step']}\n"
56
+ if 'details' in entry and entry['details']:
57
+ details = entry['details']
58
+ if isinstance(details, dict):
59
+ for k, v in details.items():
60
+ if isinstance(v, str) and len(v) > 100:
61
+ details[k] = v[:100] + "..."
62
+ log_text += f"``````\n"
63
  else:
64
+ log_text += f"{details}\n"
 
65
 
66
+ return log_text
67
 
68
  def clear_workflow_log():
69
+ """Clear the workflow log"""
70
  global WORKFLOW_LOG
71
  WORKFLOW_LOG = []
72
 
73
+ # Data collector for international students
74
+ class InternationalStudentDataCollector:
75
+ """Collects financial data for international students from different countries"""
76
+
77
+ def __init__(self):
78
+ """Initialize the data collector with a model for generating data"""
79
+ self.llm = ChatOpenAI(temperature=0.1, model="gpt-3.5-turbo")
80
+ self.cache = {}
81
+
82
+ def _get_data_with_caching(self, prompt_key, prompt):
83
+ """Get data with caching to avoid repeated API calls"""
84
+ log_workflow(f"Collecting data for {prompt_key}")
85
+
86
+ if prompt_key in self.cache:
87
+ log_workflow("Using cached data")
88
+ return self.cache[prompt_key]
89
+
90
+ try:
91
+ response = self.llm.invoke(prompt)
92
+ facts = [line.strip() for line in response.content.split('\n') if line.strip()]
93
+ self.cache[prompt_key] = facts
94
+ log_workflow(f"Collected {len(facts)} facts")
95
+ return facts
96
+ except Exception as e:
97
+ log_workflow("Error collecting data", str(e))
98
+ return [f"Error retrieving information: {str(e)}"]
99
+
100
+ def get_banking_data(self, country):
101
+ """Get banking information for international students from specific country"""
102
+ prompt_key = f"banking_{country.lower()}"
103
+ banking_prompt = f"""
104
+ Provide 5 specific, actionable facts about banking options for international students from {country} in the United States.
105
+ Focus on:
106
+ 1. The best US banks that offer accounts for {country} students with minimal fees
107
+ 2. Exact documentation requirements for {country} students to open an account
108
+ 3. Special features available to international students from {country}
109
+ 4. Precise fee structures and minimum balances for recommended accounts
110
+ 5. Best options for international money transfers between {country} and US
111
+
112
+ Format as a list of factual, specific statements, one per line.
113
+ Be extremely specific and include bank names, exact documentation needed, and fee amounts where possible.
114
+ """
115
+
116
+ return self._get_data_with_caching(prompt_key, banking_prompt)
117
+
118
+ def get_credit_data(self, country):
119
+ """Get credit building information for international students from specific country"""
120
+ prompt_key = f"credit_{country.lower()}"
121
+ credit_prompt = f"""
122
+ Provide 5 specific, actionable facts about credit building options for international students from {country} in the United States.
123
+ Focus on:
124
+ 1. Exact credit card options available to {country} students without US credit history (with specific bank names)
125
+ 2. Precisely how {country} credit history can or cannot be used in the US (e.g., Nova Credit)
126
+ 3. Detailed secured credit card requirements and deposit amounts for specific cards
127
+ 4. Step-by-step strategies for building credit scores for {country} nationals
128
+ 5. Specific credit-building pitfalls that {country} students should avoid
129
+
130
+ Format as a list of factual, specific statements, one per line.
131
+ Include exact credit card names, specific dollar amounts for deposits, and precise steps where possible.
132
+ """
133
+
134
+ return self._get_data_with_caching(prompt_key, credit_prompt)
135
+
136
+ def get_budget_data(self, country):
137
+ """Get budget management information for international students from specific country"""
138
+ prompt_key = f"budget_{country.lower()}"
139
+ budget_prompt = f"""
140
+ Provide 5 specific, actionable facts about budget management for international students from {country} in the United States.
141
+ Focus on:
142
+ 1. Exact breakdown of typical monthly expenses for {country} students in the US (with dollar amounts)
143
+ 2. Specific money transfer services popular with {country} students (with fee structures)
144
+ 3. Detailed tax implications for {country} students with TA/RA stipends (including tax treaty benefits)
145
+ 4. Names of specific budget apps or tools popular with {country} students
146
+ 5. Step-by-step plan for managing a $2,500 monthly TA stipend, including saving for emergencies
147
+
148
+ Format as a list of factual, specific statements, one per line.
149
+ Include exact dollar amounts, percentages, and specific service names where possible.
150
+ """
151
+
152
+ return self._get_data_with_caching(prompt_key, budget_prompt)
153
+
154
+ def get_currency_data(self, country):
155
+ """Get currency exchange information for international students from specific country"""
156
+ prompt_key = f"currency_{country.lower()}"
157
+ currency_prompt = f"""
158
+ Provide 5 specific, actionable facts about currency exchange and international money transfers for {country} students in the US.
159
+ Focus on:
160
+ 1. Current exchange rate trends between {country} currency and USD (with specific ranges)
161
+ 2. Exact fee structures of money transfer services for {country}-US transfers (Wise, Remitly, etc.)
162
+ 3. Specific regulatory considerations for moving money from {country} to US (limits, documentation)
163
+ 4. Precise breakdown of hidden fees and exchange rate markups typical in {country}-US transfers
164
+ 5. Step-by-step strategies for optimizing currency exchange for {country} students
165
+
166
+ Format as a list of factual, specific statements, one per line.
167
+ Include exact service names, fee percentages, and dollar amounts where possible.
168
+ """
169
+
170
+ return self._get_data_with_caching(prompt_key, currency_prompt)
171
+
172
+ def get_loan_data(self, country):
173
+ """Get student loan information for international students from specific country"""
174
+ prompt_key = f"loan_{country.lower()}"
175
+ loan_prompt = f"""
176
+ Provide 5 specific, actionable facts about student loan options for international students from {country} studying in the US.
177
+ Focus on:
178
+ 1. Names of specific education loan providers in {country} for international study (with interest rates)
179
+ 2. Exact US-based lenders that serve {country} students without US cosigners (Prodigy, MPOWER, etc.)
180
+ 3. Precise interest rates and terms for various {country} student loan options
181
+ 4. Specific collateral requirements for loans to {country} students (with dollar amounts)
182
+ 5. Names of loan forgiveness or assistance programs available to {country} students
183
+
184
+ Format as a list of factual, specific statements, one per line.
185
+ Include exact lender names, interest rate percentages, and dollar amounts where possible.
186
+ """
187
+
188
+ return self._get_data_with_caching(prompt_key, loan_prompt)
189
+
190
+ def get_career_data(self, country):
191
+ """Get career financial planning information for international students from specific country"""
192
+ prompt_key = f"career_{country.lower()}"
193
+ career_prompt = f"""
194
+ Provide 5 specific, actionable facts about career financial planning for international students from {country} in the US.
195
+ Focus on:
196
+ 1. Exact F-1 visa work restrictions and opportunities (with hour limits and eligible positions)
197
+ 2. Detailed CPT/OPT regulations affecting {country} students (application timeline, costs)
198
+ 3. Step-by-step financial planning for summer internships specifically for {country} students
199
+ 4. Specific post-graduation work authorization financial considerations (with costs and timeline)
200
+ 5. Precise salary negotiation strategies and benefits evaluation for {country} nationals
201
+
202
+ Format as a list of factual, specific statements, one per line.
203
+ Include exact hour limits, application fees, timeline durations, and dollar amounts where possible.
204
+ """
205
+
206
+ return self._get_data_with_caching(prompt_key, career_prompt)
207
+
208
+ def get_legal_data(self, country):
209
+ """Get legal financial information for international students from specific country"""
210
+ prompt_key = f"legal_{country.lower()}"
211
+ legal_prompt = f"""
212
+ Provide 5 specific, actionable facts about legal financial considerations for international students from {country} in the US.
213
+ Focus on:
214
+ 1. Exact visa maintenance financial requirements for {country} students (with dollar amounts)
215
+ 2. Specific tax treaty benefits between US and {country} (with article numbers and percentage rates)
216
+ 3. Detailed FBAR and foreign account reporting requirements for {country} nationals ($10,000 threshold, etc.)
217
+ 4. Precise financial documentation needed for visa renewals/applications (with dollar amounts)
218
+ 5. Specific legal implications of different types of income for {country} students on F-1 visas
219
+
220
+ Format as a list of factual, specific statements, one per line.
221
+ Include exact dollar thresholds, tax treaty article numbers, and specific form names where possible.
222
+ """
223
+
224
+ return self._get_data_with_caching(prompt_key, legal_prompt)
225
+
226
+
227
+ # =======================================
228
+ # Knowledge Base (RAG Implementation)
229
+ # =======================================
230
+
231
+ class KnowledgeBase:
232
+ """RAG implementation for domain-specific knowledge retrieval"""
233
+
234
+ def __init__(self, domain: str):
235
+ """Initialize the knowledge base for a specific domain"""
236
+ self.domain = domain
237
+ self.vector_store = None
238
+ self.retriever = None
239
+ self.data_collector = InternationalStudentDataCollector()
240
+ self.embeddings = OpenAIEmbeddings()
241
+
242
+ def _initialize_for_country(self, country: str):
243
+ """Initialize the vector store for a specific country"""
244
+ domain_key = f"{self.domain}_{country.lower()}"
245
+ log_workflow(f"Initializing knowledge base", {"domain": self.domain, "country": country})
246
+
247
+ if self.vector_store is not None:
248
+ log_workflow("Using existing vector store")
249
+ return
250
+
251
+ # Get country-specific data from the data collector
252
+ if self.domain == "banking":
253
+ domain_texts = self.data_collector.get_banking_data(country)
254
+ elif self.domain == "credit":
255
+ domain_texts = self.data_collector.get_credit_data(country)
256
+ elif self.domain == "budget":
257
+ domain_texts = self.data_collector.get_budget_data(country)
258
+ elif self.domain == "currency":
259
+ domain_texts = self.data_collector.get_currency_data(country)
260
+ elif self.domain == "loans":
261
+ domain_texts = self.data_collector.get_loan_data(country)
262
+ elif self.domain == "career":
263
+ domain_texts = self.data_collector.get_career_data(country)
264
+ elif self.domain == "legal":
265
+ domain_texts = self.data_collector.get_legal_data(country)
266
+ else:
267
+ domain_texts = [f"General information for {self.domain} domain for {country} international students."]
268
+
269
+ log_workflow(f"Creating vector store with {len(domain_texts)} documents")
270
+
271
+ # Create text splitter for chunking
272
+ text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=100)
273
+ splits = text_splitter.split_text("\n\n".join(domain_texts))
274
+
275
+ # Create vector store with embeddings
276
+ try:
277
+ self.vector_store = Chroma.from_texts(
278
+ splits,
279
+ self.embeddings,
280
+ collection_name=domain_key
281
+ )
282
+
283
+ # Create retriever for similarity search
284
+ self.retriever = self.vector_store.as_retriever(
285
+ search_type="similarity",
286
+ search_kwargs={"k": 3}
287
+ )
288
+ log_workflow("Vector store created successfully")
289
+ except Exception as e:
290
+ log_workflow("Error creating vector store", str(e))
291
+ # We'll fall back to direct retrieval if vector storage fails
292
+
293
+ def retrieve(self, query: str, country: str) -> List[str]:
294
+ """Retrieve relevant information using vector similarity search"""
295
+ log_workflow(f"RAG Pattern: Retrieving {self.domain} knowledge", {"query": query[:50], "country": country})
296
+
297
+ try:
298
+ # Initialize the vector store if needed
299
+ self._initialize_for_country(country)
300
+
301
+ if self.retriever:
302
+ # Use the retriever to find similar content
303
+ documents = self.retriever.get_relevant_documents(query)
304
+ results = [doc.page_content for doc in documents]
305
+ log_workflow(f"Retrieved {len(results)} relevant documents")
306
+ return results
307
+ else:
308
+ raise ValueError("Retriever not initialized properly")
309
+ except Exception as e:
310
+ log_workflow("Error in RAG retrieval, falling back to direct retrieval", str(e))
311
+ # Fallback to direct retrieval if vector storage fails
312
+ if self.domain == "banking":
313
+ return self.data_collector.get_banking_data(country)
314
+ elif self.domain == "credit":
315
+ return self.data_collector.get_credit_data(country)
316
+ elif self.domain == "budget":
317
+ return self.data_collector.get_budget_data(country)
318
+ elif self.domain == "currency":
319
+ return self.data_collector.get_currency_data(country)
320
+ elif self.domain == "loans":
321
+ return self.data_collector.get_loan_data(country)
322
+ elif self.domain == "career":
323
+ return self.data_collector.get_career_data(country)
324
+ elif self.domain == "legal":
325
+ return self.data_collector.get_legal_data(country)
326
+ else:
327
+ return [f"Information about {self.domain} for {country} international students."]
328
+
329
+
330
+ # =======================================
331
+ # Domain Specialist Agents
332
+ # =======================================
333
+
334
+ class SpecialistAgent:
335
+ """Base class for specialist agents with domain expertise"""
336
+
337
+ def __init__(self, name: str, domain: str, llm=None):
338
+ """Initialize a specialist agent with domain expertise"""
339
+ self.name = name
340
+ self.domain = domain
341
+ self.knowledge_base = KnowledgeBase(domain)
342
+ self.llm = llm if llm else ChatOpenAI(temperature=0.2)
343
+
344
+ def run(self, query: str, country: str) -> str:
345
+ """Run the specialist agent to get domain-specific advice"""
346
+ log_workflow(f"Role-based Cooperation: {self.name} analyzing query", {"query": query[:50]})
347
+
348
+ # Get country-specific knowledge using RAG
349
+ knowledge = self.knowledge_base.retrieve(query, country)
350
+
351
+ # Prepare a detailed prompt with the knowledge and query
352
+ prompt = f"""
353
+ As a specialist {self.name} for international students, provide detailed, specific financial advice for a student from {country}.
354
+
355
+ STUDENT QUERY:
356
+ {query}
357
+
358
+ RELEVANT KNOWLEDGE FROM RAG:
359
+ {"\n".join('- ' + item for item in knowledge)}
360
+
361
+ Provide extremely detailed, actionable advice addressing the query with these requirements:
362
+ 1. Include specific bank/service/product names with exact fees or rates where applicable
363
+ 2. Provide step-by-step instructions for any processes (account opening, credit building, etc.)
364
+ 3. Include specific dollar amounts, percentages, and time frames
365
+ 4. List exact documentation requirements where relevant
366
+ 5. Address all aspects of the query related to your domain of {self.domain}
367
+
368
+ Format your response with clear sections, bullet points, and numbered steps.
369
+ """
370
+
371
+ try:
372
+ log_workflow(f"{self.name} generating advice")
373
+ response = self.llm.invoke(prompt)
374
+ advice = response.content
375
+ log_workflow(f"{self.name} generated advice", {"length": len(advice)})
376
+ return advice
377
+ except Exception as e:
378
+ log_workflow(f"Error in {self.name}", str(e))
379
+ return f"The {self.name} encountered an issue: {str(e)}"
380
+
381
+
382
+ # Specialized agent implementations
383
+ class BankingAdvisor(SpecialistAgent):
384
+ """Specialist agent for banking advice"""
385
+ def __init__(self, llm=None):
386
+ super().__init__(name="Banking Advisor", domain="banking", llm=llm)
387
+
388
+
389
+ class CreditBuilder(SpecialistAgent):
390
+ """Specialist agent for credit building advice"""
391
+ def __init__(self, llm=None):
392
+ super().__init__(name="Credit Builder", domain="credit", llm=llm)
393
+
394
+
395
+ class BudgetManager(SpecialistAgent):
396
+ """Specialist agent for budget management advice"""
397
+ def __init__(self, llm=None):
398
+ super().__init__(name="Budget Manager", domain="budget", llm=llm)
399
+
400
+
401
+ class CurrencyExchangeSpecialist(SpecialistAgent):
402
+ """Specialist agent for currency exchange advice"""
403
+ def __init__(self, llm=None):
404
+ super().__init__(name="Currency Exchange Specialist", domain="currency", llm=llm)
405
+
406
+
407
+ class StudentLoanAdvisor(SpecialistAgent):
408
+ """Specialist agent for student loan advice"""
409
+ def __init__(self, llm=None):
410
+ super().__init__(name="Student Loan Advisor", domain="loans", llm=llm)
411
+
412
+
413
+ class CareerFinancePlanner(SpecialistAgent):
414
+ """Specialist agent for career financial planning advice"""
415
+ def __init__(self, llm=None):
416
+ super().__init__(name="Career Finance Planner", domain="career", llm=llm)
417
+
418
+
419
+ class LegalFinanceAdvisor(SpecialistAgent):
420
+ """Specialist agent for legal financial advice"""
421
+ def __init__(self, llm=None):
422
+ super().__init__(name="Legal Finance Advisor", domain="legal", llm=llm)
423
+
424
 
425
  # =======================================
426
+ # Coordinator Agent (Central Agent)
427
  # =======================================
428
+
429
  class CoordinatorAgent:
430
+ """Central coordinator agent that orchestrates specialist agents"""
431
+
432
  def __init__(self, llm=None):
433
+ """Initialize the coordinator agent"""
434
+ self.llm = llm if llm else ChatOpenAI(temperature=0.3)
435
+
436
+ # Initialize specialist agents
437
+ self.banking_advisor = BankingAdvisor(self.llm)
438
+ self.credit_builder = CreditBuilder(self.llm)
439
+ self.budget_manager = BudgetManager(self.llm)
440
+ self.currency_specialist = CurrencyExchangeSpecialist(self.llm)
441
+ self.loan_advisor = StudentLoanAdvisor(self.llm)
442
+ self.career_planner = CareerFinancePlanner(self.llm)
443
+ self.legal_advisor = LegalFinanceAdvisor(self.llm)
444
+
445
+ # Map domains to specialists
446
  self.specialists = {
447
+ "banking": self.banking_advisor,
448
+ "credit": self.credit_builder,
449
+ "budget": self.budget_manager,
450
+ "currency": self.currency_specialist,
451
+ "loans": self.loan_advisor,
452
+ "career": self.career_planner,
453
+ "legal": self.legal_advisor
454
  }
455
 
456
  def _identify_relevant_specialists(self, query: str) -> List[str]:
457
+ """Identify which specialists are relevant to the query"""
458
+ log_workflow("Analyzing query to identify relevant specialists")
459
+
460
+ relevance_prompt = f"""
461
+ Based on this financial query from an international student:
462
+ "{query}"
463
+
464
+ Which of the following specialist advisors should be consulted? Choose only the relevant ones.
465
+ - banking (Banking Advisor: bank accounts, account types, transfers, documentation)
466
+ - credit (Credit Builder: credit cards, credit scores, credit history)
467
+ - budget (Budget Manager: expense tracking, savings, stipend management)
468
+ - currency (Currency Exchange Specialist: exchange rates, money transfers)
469
+ - loans (Student Loan Advisor: educational loans, repayment strategies)
470
+ - career (Career Finance Planner: internships, CPT/OPT, job preparation)
471
+ - legal (Legal Finance Advisor: visa regulations, tax implications)
472
+
473
+ Return a comma-separated list of ONLY the relevant domain codes (e.g., "banking,credit").
474
+ """
475
+
 
 
 
476
  try:
477
+ response = self.llm.invoke(relevance_prompt)
478
+ domains = [domain.strip().lower() for domain in response.content.split(',')]
479
+ valid_domains = [domain for domain in domains if domain in self.specialists]
480
+
481
+ # Add budget domain if query mentions stipend or expenses
482
+ if "budget" not in valid_domains and ("stipend" in query.lower() or "expense" in query.lower()):
483
+ valid_domains.append("budget")
484
+
485
+ # Add legal domain if query mentions tax or visa
486
+ if "legal" not in valid_domains and ("tax" in query.lower() or "visa" in query.lower()):
487
+ valid_domains.append("legal")
488
+
489
+ # Add career domain if query mentions internship, CPT, or OPT
490
+ if "career" not in valid_domains and any(term in query.lower() for term in ["internship", "cpt", "opt"]):
491
+ valid_domains.append("career")
492
+
493
+ log_workflow("Identified relevant specialists", {"domains": valid_domains})
494
+ return valid_domains
495
+ except Exception as e:
496
+ log_workflow("Error identifying specialists", str(e))
497
+ # Default to essential domains if there's an error
498
+ default_domains = ["banking", "budget"]
499
+ if "credit" in query.lower():
500
+ default_domains.append("credit")
501
+ return default_domains
502
+
503
+ def _conduct_vote(self, question: str, options: List[str], country: str) -> Dict[str, Any]:
504
+ """Implement voting-based cooperation between specialists"""
505
+ log_workflow("Voting-based Cooperation: Specialists voting on options",
506
+ {"question": question[:50], "options": options})
507
+
508
+ voting_results = {option: 0 for option in options}
509
+ specialist_votes = {}
510
+
511
+ voting_prompt = f"""
512
+ As a financial advisor for international students from {country}, which of the following options would you recommend?
513
+
514
+ QUESTION: {question}
515
+
516
+ OPTIONS:
517
+ {"\n".join([f"{i+1}. {option}" for i, option in enumerate(options)])}
518
+
519
+ Analyze the options carefully, then respond with ONLY the number of your recommendation (e.g., "1" or "2").
520
+ """
521
+
522
+ # Select appropriate specialists for voting
523
+ relevant_domains = self._identify_relevant_specialists(question)
524
+ for domain in relevant_domains:
525
+ specialist = self.specialists[domain]
526
+ try:
527
+ response = self.llm.invoke(voting_prompt)
528
+ vote_text = response.content.strip()
529
+
530
+ # Try to extract a number from the response
531
+ vote = None
532
+ for i, option in enumerate(options):
533
+ if str(i+1) in vote_text:
534
+ vote = options[i]
535
+ break
536
+
537
+ if vote is None and len(options) > 0:
538
+ vote = options[0] # Default to first option if parsing fails
539
+
540
+ if vote in voting_results:
541
+ voting_results[vote] += 1
542
+ specialist_votes[domain] = vote
543
+ log_workflow(f"{domain.capitalize()} voted for: {vote}")
544
+ except Exception as e:
545
+ log_workflow(f"Error during voting from {domain}", str(e))
546
+
547
+ # Find the winner
548
+ winner = max(voting_results.items(), key=lambda x: x[1]) if voting_results else (options[0], 0)
549
+
550
+ log_workflow(f"Voting complete, winner determined",
551
+ {"winner": winner[0], "vote_count": winner[1]})
552
+
553
+ return {
554
+ "winner": winner[0],
555
+ "votes": voting_results,
556
+ "specialist_votes": specialist_votes
557
+ }
558
 
559
+ def _generate_plans(self, financial_goal: str, constraints: str, country: str) -> Dict[str, str]:
560
+ """Implement Multi-path Plan Generator pattern"""
561
+ log_workflow("Multi-path Plan Generator: Creating financial plans",
562
+ {"goal": financial_goal[:50], "country": country})
563
 
564
+ # Create prompts for different risk approaches
565
+ planning_prompt_template = f"""
566
+ As a financial advisor for international students from {country}, create a {{approach}} financial strategy for:
567
 
568
+ GOAL: {financial_goal}
569
+
570
+ CONSTRAINTS: {constraints}
571
+
572
+ Your {{approach}} strategy should include:
573
+ 1. Detailed step-by-step actions with timeline
574
+ 2. Specific financial products/services with exact names and costs
575
+ 3. Precise breakdown of benefits and risks
576
+ 4. Expected outcomes with realistic numbers
577
+ 5. Mitigation strategies for potential challenges
578
+
579
+ Format with clear headings, bullet points, and numbered steps.
580
+ Include specific bank names, service providers, dollar amounts, and time frames.
581
+ """
582
+
583
+ plans = {}
584
 
585
+ try:
586
+ # Create conservative plan using Budget Manager
587
+ log_workflow("Generating conservative plan")
588
+ conservative_prompt = planning_prompt_template.format(approach="CONSERVATIVE (lowest risk)")
589
+ plans["conservative"] = self.budget_manager.run(conservative_prompt, country)
590
+
591
+ # Create balanced plan using Banking Advisor
592
+ log_workflow("Generating balanced plan")
593
+ balanced_prompt = planning_prompt_template.format(approach="BALANCED (moderate risk/reward)")
594
+ plans["balanced"] = self.banking_advisor.run(balanced_prompt, country)
595
+
596
+ # Create growth plan using Credit Builder
597
+ log_workflow("Generating growth plan")
598
+ growth_prompt = planning_prompt_template.format(approach="GROWTH-ORIENTED (higher potential returns)")
599
+ plans["growth"] = self.credit_builder.run(growth_prompt, country)
600
+
601
+ log_workflow("All plans generated successfully")
602
+ return plans
603
+ except Exception as e:
604
+ log_workflow("Error generating financial plans", str(e))
605
+ return {
606
+ "conservative": f"Error generating conservative plan: {str(e)}",
607
+ "balanced": f"Error generating balanced plan: {str(e)}",
608
+ "growth": f"Error generating growth plan: {str(e)}"
609
+ }
610
+
611
+ def _reflect_on_recommendation(self, recommendation: str, student_profile: Dict[str, Any]) -> str:
612
+ """Implement Self-reflection pattern"""
613
+ country = student_profile.get("home_country", "unknown")
614
+ visa_type = student_profile.get("visa_type", "unknown")
615
+
616
+ log_workflow("Self-reflection: Reviewing recommendations",
617
+ {"country": country, "visa_type": visa_type})
618
+
619
+ reflection_prompt = f"""
620
+ As a Legal Financial Advisor for international students, evaluate this financial recommendation:
621
+
622
+ STUDENT PROFILE:
623
+ Home Country: {country}
624
+ Visa Type: {visa_type}
625
+ University: {student_profile.get('university', 'unknown')}
626
+ Funding: {student_profile.get('funding', 'unknown')}
627
+ Additional Info: {student_profile.get('additional_info', 'none')}
628
+
629
+ RECOMMENDATION:
630
+ {recommendation}
631
+
632
+ Please reflect on:
633
+ 1. Does this recommendation fully comply with {visa_type} visa restrictions?
634
+ 2. Is the advice properly tailored to {country} students' unique circumstances?
635
+ 3. Are there any assumptions that might not apply to international students?
636
+ 4. Could any part of this advice create legal/immigration issues?
637
+ 5. Is the recommendation practical given typical international student constraints?
638
+ 6. Does it address all aspects of the original query completely?
639
+
640
+ Provide a detailed assessment with specific recommendations for improvement.
641
+ """
642
+
643
+ try:
644
+ log_workflow("Generating legal reflection")
645
+ reflection = self.legal_advisor.run(reflection_prompt, country)
646
+ log_workflow("Reflection complete")
647
+ return reflection
648
+ except Exception as e:
649
+ log_workflow("Error during self-reflection", str(e))
650
+ return f"Unable to complete self-reflection due to an error: {str(e)}"
651
+
652
+ def run(self, query: str, student_profile: Dict[str, Any]) -> str:
653
+ """Orchestrate the specialist agents to create a comprehensive response"""
654
+ log_workflow("COORDINATOR: Processing new query", {"query": query[:100]})
655
+
656
+ country = student_profile.get("home_country", "unknown")
657
+
658
+ # 1. Analyze the query to identify which specialists to consult
659
+ relevant_domains = self._identify_relevant_specialists(query)
660
+
661
+ # 2. Collect advice from relevant specialists
662
+ specialist_advice = {}
663
+ for domain in relevant_domains:
664
+ if domain in self.specialists:
665
+ specialist = self.specialists[domain]
666
+ advice = specialist.run(query, country)
667
+ specialist_advice[domain] = advice
668
+
669
+ # 3. Generate multi-path financial plans for the query
670
+ constraints = f"""
671
+ Home Country: {country}
672
+ Visa Type: {student_profile.get('visa_type', 'F-1')}
673
+ University: {student_profile.get('university', 'unknown')}
674
+ Funding: {student_profile.get('funding', 'unknown')}
675
+ Additional Info: {student_profile.get('additional_info', 'none')}
676
+ """
677
 
678
+ plans = self._generate_plans(query, constraints, country)
679
+
680
+ # 4. Synthesize the collected advice and plans into a coherent response
681
+ log_workflow("Synthesizing comprehensive response")
682
 
 
683
  synthesis_prompt = f"""
684
+ As the coordinator for an International Student Finance Portal, synthesize specialist advice and financial plans into a comprehensive response.
685
+
686
+ STUDENT:
687
+ - Home Country: {country}
688
+ - Visa Type: {student_profile.get('visa_type', 'F-1')}
689
+ - University: {student_profile.get('university', 'unknown')}
690
+ - Funding: {student_profile.get('funding', 'unknown')}
691
+ - Additional Info: {student_profile.get('additional_info', 'none')}
692
 
693
+ QUERY:
694
+ {query}
695
 
696
+ SPECIALIST ADVICE:
697
+ {"\n".join([f"--- {domain.upper()} SPECIALIST ---\n{advice[:1000]}\n" for domain, advice in specialist_advice.items()])}
698
 
699
+ FINANCIAL APPROACHES:
700
+ --- CONSERVATIVE APPROACH ---
701
+ {plans.get('conservative', 'No conservative plan available.')[:1000]}
702
 
703
+ --- BALANCED APPROACH ---
704
+ {plans.get('balanced', 'No balanced plan available.')[:1000]}
705
 
706
+ --- GROWTH-ORIENTED APPROACH ---
707
+ {plans.get('growth', 'No growth-oriented plan available.')[:1000]}
708
 
709
+ Create a detailed response with:
710
+ 1. PART 1: Direct answers to each specific aspect of the query - banking, credit, stipend management, etc.
711
+ 2. PART 2: Multiple financial approaches (conservative, balanced, growth-oriented)
712
 
713
+ Each section must be extremely detailed with:
714
+ - Specific bank/service names
715
+ - Exact documentation requirements
716
+ - Step-by-step processes
717
+ - Precise dollar amounts
718
+ - Concrete timelines
719
+
720
+ Format with clear headings, bullet points, and numbered steps.
721
+ """
722
+
723
+ try:
724
+ # Generate the synthesized response
725
+ log_workflow("Generating final synthesized response")
726
+ synthesis_response = self.llm.invoke(synthesis_prompt)
727
+
728
+ # 5. Self-reflection (check for international student appropriateness)
729
+ log_workflow("Performing self-reflection")
730
+ reflection = self._reflect_on_recommendation(synthesis_response.content, student_profile)
731
+
732
+ # 6. Final response with reflection incorporated
733
+ log_workflow("Creating final response with reflection incorporated")
734
+ final_prompt = f"""
735
+ Revise this financial advice based on legal reflection:
736
+
737
+ ORIGINAL ADVICE:
738
+ {synthesis_response.content}
739
+
740
+ LEGAL REFLECTION:
741
+ {reflection}
742
+
743
+ Create a final version that:
744
+ 1. Incorporates all legal considerations
745
+ 2. Maintains the comprehensive nature of the original advice
746
+ 3. Addresses EVERY aspect of the original query specifically and in detail:
747
+ - Bank account setup (specific banks, fees, documents)
748
+ - Credit building (specific cards, exact steps)
749
+ - Money transfers (exact services, fees, processes)
750
+ - Stipend management (precise budget breakdown)
751
+ - Tax implications (specific treaty benefits, forms)
752
+ - CPT/internship planning (exact timeline, requirements)
753
+ 4. Includes all three financial approaches (conservative, balanced, growth)
754
+
755
+ Format with clear headings, bullet points, and numbered steps.
756
+ """
757
+
758
+ log_workflow("Generating final response")
759
+ final_response = self.llm.invoke(final_prompt)
760
+ log_workflow("Response generation complete")
761
+
762
+ # Return both the response and the workflow log
763
+ return final_response.content
764
+ except Exception as e:
765
+ log_workflow("Error in coordinator synthesis", str(e))
766
+
767
+ # Fallback response if synthesis fails
768
+ fallback = "## Financial Advice Summary\n\n"
769
+ for domain, advice in specialist_advice.items():
770
+ domain_name = domain.replace("_", " ").title()
771
+ fallback += f"### {domain_name} Advice\n{advice[:500]}...\n\n"
772
+
773
+ fallback += "\n## Multiple Financial Approaches\n\n"
774
+ for approach, plan in plans.items():
775
+ approach_name = approach.replace("_", " ").title()
776
+ fallback += f"### {approach_name} Approach\n{plan[:500]}...\n\n"
777
+
778
+ return fallback
779
 
 
 
 
 
 
780
 
781
  # =======================================
782
+ # Main Portal Interface
783
  # =======================================
784
+
785
  class FinancePortal:
786
+ """Main interface for the International Student Finance Portal"""
787
+
788
  def __init__(self):
789
+ """Initialize the finance portal with a coordinator agent"""
790
  self.coordinator = CoordinatorAgent()
791
+ self.student_profiles = {}
792
+
793
+ def register_student(self, student_id: str, profile: Dict[str, Any]):
794
+ """Register a new student profile"""
795
+ self.student_profiles[student_id] = profile
796
 
797
+ def get_student_profile(self, student_id: str) -> Optional[Dict[str, Any]]:
798
+ """Get a student's profile"""
799
+ return self.student_profiles.get(student_id)
800
 
801
+ def handle_query(self, student_id: str, query: str) -> str:
802
+ """Process a student query"""
803
+ profile = self.get_student_profile(student_id)
804
 
805
+ if not profile:
806
+ return "Please provide your profile information first."
807
+
808
+ if not query or query.strip() == "":
809
+ return "Please enter a specific financial question."
810
+
811
+ log_workflow(f"Processing query for student {student_id}", {"query": query[:50]})
812
+
813
+ # Clear workflow log for new query
814
  clear_workflow_log()
815
+
816
+ try:
817
+ # Process the query with the coordinator
818
+ response = self.coordinator.run(query, profile)
819
+
820
+ # Get the workflow log
821
+ workflow_log = get_workflow_log()
822
+
823
+ # Combine the response and workflow log
824
+ full_response = f"{response}\n\n---\n\n{workflow_log}"
825
+
826
+ return full_response
827
+ except Exception as e:
828
+ log_workflow(f"Error handling query", str(e))
829
+
830
+ # Return the error with the workflow log
831
+ workflow_log = get_workflow_log()
832
+ return f"I encountered an error while processing your request: {str(e)}\n\n---\n\n{workflow_log}"
833
 
834
 
835
  def create_interface():
836
+ """Create the Gradio interface for the finance portal"""
837
  portal = FinancePortal()
838
 
839
+ def handle_query(query, country, visa_type, university, funding, additional_info):
840
+ """Handler for query submission"""
841
+ if not query or query.strip() == "":
842
+ return "Please enter a financial question."
843
+
844
+ if not country:
845
+ return "Please select your home country."
846
+
847
+ if not visa_type:
848
+ return "Please select your visa type."
849
+
850
+ # Create a composite student profile
851
+ student_id = "current_user"
852
+ profile = {
853
+ "home_country": country,
854
+ "visa_type": visa_type,
855
+ "university": university,
856
+ "funding": funding,
857
+ "additional_info": additional_info
858
+ }
859
+
860
+ portal.register_student(student_id, profile)
861
+ return portal.handle_query(student_id, query)
862
+
863
+ # Create Gradio interface
864
+ with gr.Blocks(title="International Student Finance Portal") as demo:
865
+ gr.Markdown("# International Student Finance Portal")
866
+ gr.Markdown("Get personalized financial advice tailored for international graduate students with visible workflow.")
867
+
868
+ with gr.Row():
869
+ with gr.Column(scale=2):
870
+ country = gr.Dropdown(
871
+ label="Home Country",
872
+ choices=["", "India", "China", "Brazil", "Other"],
873
+ value=""
874
+ )
875
+ visa_type = gr.Dropdown(
876
+ label="Visa Type",
877
+ choices=["", "F-1", "J-1", "M-1", "Other"],
878
+ value=""
879
+ )
880
+ university = gr.Textbox(
881
+ label="University",
882
+ placeholder="e.g., Stanford University"
883
+ )
884
+ funding = gr.Dropdown(
885
+ label="Primary Funding Source",
886
+ choices=["", "Self/Family", "Scholarship", "TA/RA Position", "Education Loan", "Other"],
887
+ value=""
888
+ )
889
+ additional_info = gr.Textbox(
890
+ label="Additional Information (Optional)",
891
+ placeholder="Program, expected duration, family situation, etc."
892
+ )
893
+
894
+ # Predefined query templates
895
+ query_templates = gr.Dropdown(
896
+ label="Common Questions (Select or type your own below)",
897
+ choices=[
898
+ "",
899
+ "How do I open a bank account as an international student?",
900
+ "What's the best way to build credit in the US?",
901
+ "How should I manage my TA/RA stipend?",
902
+ "What are my options for sending/receiving money from home?",
903
+ "How do CPT/OPT affect my financial situation?",
904
+ "What student loan options are available to me?",
905
+ "How should I budget for living expenses in the US?",
906
+ "I just arrived in the US from India on an F-1 visa to start my PhD program at MIT with a teaching assistantship. I need advice on opening a bank account with minimal fees, building credit from scratch since I have no US history, sending money between India and the US at the best rates, managing my $2,500 monthly TA stipend while saving for emergencies, and understanding tax implications under the US-India tax treaty. Also, how should I financially prepare for a potential CPT internship next summer?"
907
+ ],
908
+ value=""
909
+ )
910
+
911
+ query = gr.Textbox(
912
+ label="Your Financial Question",
913
+ placeholder="Type your financial question here...",
914
+ lines=4
915
+ )
916
+
917
+ # Update query box when template is selected
918
+ query_templates.change(
919
+ fn=lambda x: x if x else "",
920
+ inputs=query_templates,
921
+ outputs=query
922
+ )
923
+
924
+ submit_btn = gr.Button("Get Financial Advice", variant="primary")
925
+ clear_btn = gr.Button("Reset")
926
+
927
+ with gr.Column(scale=3):
928
+ # Use a textbox with markdown enabled
929
+ with gr.Group():
930
+ gr.Markdown("### Your Personalized Financial Advice")
931
+ response = gr.Markdown()
932
+
933
+ # Add a loading message while waiting for response
934
+ submit_btn.click(
935
+ fn=lambda: "## Processing Your Query\n\nConsulting specialist advisors and generating multiple financial approaches...\n\nPlease wait a moment as this may take up to a minute.",
936
+ inputs=None,
937
+ outputs=response,
938
+ queue=False
939
+ )
940
+
941
+ # Handle main query submission
942
+ submit_btn.click(
943
+ fn=handle_query,
944
+ inputs=[query, country, visa_type, university, funding, additional_info],
945
+ outputs=response,
946
+ queue=True
947
+ )
948
+
949
+ # Handle reset button
950
+ clear_btn.click(
951
+ fn=lambda: (
952
+ "",
953
+ "",
954
+ "",
955
+ "",
956
+ "",
957
+ "",
958
+ ""
959
+ ),
960
+ inputs=None,
961
+ outputs=[query, country, visa_type, university, funding, additional_info, response]
962
+ )
963
+
964
+ # Feature explanation section
965
+ with gr.Accordion("How This System Works", open=False):
966
+ gr.Markdown("""
967
+ ### Financial Advisory Features
968
+
969
+ This portal uses advanced AI with multiple agent design patterns to provide personalized financial guidance:
970
+
971
+ 1. **Retrieval Augmented Generation (RAG)**: Uses vector embeddings to retrieve country-specific financial knowledge
972
+
973
+ 2. **Role-based Cooperation**: Specialized agents collaborate based on their domain expertise
974
+ - Banking Advisor: Account setup, transfers, banking documentation
975
+ - Credit Builder: Credit cards, credit history building, credit scores
976
+ - Budget Manager: Expense tracking, savings goals, stipend management
977
+ - Currency Exchange Specialist: International transfers, exchange rates
978
+ - Student Loan Advisor: Loan options, repayment strategies
979
+ - Career Finance Planner: CPT/OPT financial planning, internships
980
+ - Legal Finance Advisor: Visa compliance, tax treaties, reporting requirements
981
+
982
+ 3. **Voting-based Cooperation**: Specialists vote on recommendations when multiple options exist
983
+
984
+ 4. **Self-reflection**: Legal/visa compliance check on all financial advice
985
+
986
+ 5. **Multi-path Plan Generator**: Different financial strategies based on risk tolerance
987
+
988
+ The workflow log at the bottom of each response shows you exactly which components ran and in what order.
989
+ """)
990
+
991
  return demo
992
 
993
+ # If this is the main script being run
994
+ if __name__ == "__main__":
995
+ print("Starting International Student Finance Portal with Visible Workflow...")
996
+ print("This implementation tests all components and shows the workflow in real-time.")
997
+ interface = create_interface()
998
+ interface.launch()