TurkishCodeMan commited on
Commit
e4c209e
·
verified ·
1 Parent(s): 0a8ebe1

Upload folder using huggingface_hub

Browse files
Files changed (4) hide show
  1. README.md +47 -6
  2. app.py +270 -0
  3. hf_model.py +67 -0
  4. requirements.txt +5 -0
README.md CHANGED
@@ -1,12 +1,53 @@
1
  ---
2
- title: Fintech Orchestrator
3
- emoji: 🐠
4
- colorFrom: indigo
5
- colorTo: blue
6
  sdk: gradio
7
- sdk_version: 6.5.1
8
  app_file: app.py
9
  pinned: false
 
10
  ---
11
 
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: Fintech Multi-Agent Orchestrator
3
+ emoji: 🏦
4
+ colorFrom: blue
5
+ colorTo: indigo
6
  sdk: gradio
7
+ sdk_version: 4.44.0
8
  app_file: app.py
9
  pinned: false
10
+ license: mit
11
  ---
12
 
13
+ # 🏦 Fintech Multi-Agent Orchestrator
14
+
15
+ **Powered by Gemma 3 270m via HuggingFace Inference API**
16
+
17
+ A multi-agent financial assistant that can:
18
+ - 📊 Query account data (mock data for demo)
19
+ - 🧮 Perform financial calculations
20
+ - 📈 Generate charts and visualizations
21
+
22
+ ## Example Queries
23
+
24
+ ```
25
+ "What is my net worth?"
26
+ "Show my portfolio as a pie chart"
27
+ "Calculate compound interest on $10000 at 8% for 5 years"
28
+ "Show my assets breakdown as a bar chart"
29
+ ```
30
+
31
+ ## Architecture
32
+
33
+ ```
34
+ User Query → Router → Banking/Calculator/Graph Agents → Response
35
+ ```
36
+
37
+ ## Tech Stack
38
+
39
+ - **LLM**: Gemma 3 270m (HuggingFace Inference API)
40
+ - **UI**: Gradio
41
+ - **Charts**: Matplotlib
42
+
43
+ ## Setup (Local)
44
+
45
+ ```bash
46
+ pip install -r requirements.txt
47
+ export HF_TOKEN="your-huggingface-token"
48
+ python app.py
49
+ ```
50
+
51
+ ## License
52
+
53
+ MIT
app.py ADDED
@@ -0,0 +1,270 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Fintech Multi-Agent Orchestrator - HuggingFace Spaces Demo
3
+ Uses Gemma 3 27B via HuggingFace Inference API
4
+ """
5
+
6
+ import gradio as gr
7
+ import json
8
+ import re
9
+ import matplotlib
10
+ matplotlib.use('Agg')
11
+ import matplotlib.pyplot as plt
12
+ import numpy as np
13
+ from io import BytesIO
14
+ import base64
15
+
16
+ from hf_model import generate_response, calculate_expression
17
+
18
+
19
+ # Mock Banking Data
20
+ MOCK_BANKING_DATA = {
21
+ "net_worth": {"total": 87500.00, "assets": 142000.00, "liabilities": 54500.00},
22
+ "assets": {"checking": 12500.00, "savings": 35000.00, "investments": 89500.00},
23
+ "liabilities": {"credit_cards": 4500.00, "student_loans": 25000.00, "auto_loan": 15000.00},
24
+ "portfolio": {"AAPL": 15200, "GOOGL": 12800, "MSFT": 18500, "AMZN": 9000, "bonds": 14000},
25
+ }
26
+
27
+
28
+ def create_chart(chart_type: str, data: dict, title: str) -> str:
29
+ """Generate chart and return base64 image."""
30
+ fig, ax = plt.subplots(figsize=(10, 6))
31
+
32
+ if chart_type == "pie":
33
+ labels = list(data.keys())
34
+ values = list(data.values())
35
+ colors = plt.cm.Set3(np.linspace(0, 1, len(labels)))
36
+ ax.pie(values, labels=labels, autopct='%1.1f%%', colors=colors, startangle=90)
37
+ ax.set_title(title, fontsize=14, fontweight='bold')
38
+
39
+ elif chart_type == "bar":
40
+ labels = list(data.keys())
41
+ values = list(data.values())
42
+ colors = plt.cm.viridis(np.linspace(0.3, 0.9, len(labels)))
43
+ bars = ax.bar(labels, values, color=colors)
44
+ ax.set_title(title, fontsize=14, fontweight='bold')
45
+ ax.set_ylabel('Amount ($)')
46
+ for bar, val in zip(bars, values):
47
+ ax.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 500,
48
+ f'${val:,.0f}', ha='center', va='bottom', fontsize=9)
49
+
50
+ elif chart_type == "line":
51
+ x = list(range(len(data)))
52
+ y = list(data.values())
53
+ ax.plot(x, y, marker='o', linewidth=2, markersize=8, color='#2E86AB')
54
+ ax.fill_between(x, y, alpha=0.3, color='#2E86AB')
55
+ ax.set_xticks(x)
56
+ ax.set_xticklabels(list(data.keys()))
57
+ ax.set_title(title, fontsize=14, fontweight='bold')
58
+ ax.set_ylabel('Amount ($)')
59
+
60
+ plt.tight_layout()
61
+
62
+ # Convert to base64
63
+ buf = BytesIO()
64
+ plt.savefig(buf, format='png', dpi=150, bbox_inches='tight')
65
+ buf.seek(0)
66
+ plt.close()
67
+
68
+ return base64.b64encode(buf.read()).decode('utf-8')
69
+
70
+
71
+ def route_query(query: str) -> dict:
72
+ """Simple router to determine what actions are needed."""
73
+ query_lower = query.lower()
74
+
75
+ needs_banking = any(word in query_lower for word in
76
+ ['balance', 'net worth', 'portfolio', 'assets', 'liabilities', 'account', 'hesap', 'bakiye'])
77
+ needs_calculation = any(word in query_lower for word in
78
+ ['calculate', 'compute', 'roi', 'interest', 'compound', 'hesapla', 'faiz'])
79
+ needs_graph = any(word in query_lower for word in
80
+ ['chart', 'graph', 'visualize', 'plot', 'pie', 'bar', 'grafik', 'görselleştir'])
81
+
82
+ return {
83
+ "needs_banking": needs_banking,
84
+ "needs_calculation": needs_calculation,
85
+ "needs_graph": needs_graph,
86
+ }
87
+
88
+
89
+ def process_query(query: str, history: list) -> tuple:
90
+ """Main orchestrator function."""
91
+
92
+ # Route the query
93
+ route = route_query(query)
94
+
95
+ response_parts = []
96
+ chart_image = None
97
+
98
+ # Get banking data if needed
99
+ banking_context = ""
100
+ if route["needs_banking"]:
101
+ banking_context = f"""
102
+ **📊 Account Data:**
103
+ - Net Worth: ${MOCK_BANKING_DATA['net_worth']['total']:,.2f}
104
+ - Total Assets: ${MOCK_BANKING_DATA['net_worth']['assets']:,.2f}
105
+ - Total Liabilities: ${MOCK_BANKING_DATA['net_worth']['liabilities']:,.2f}
106
+
107
+ **Portfolio:**
108
+ """ + "\n".join([f"- {k}: ${v:,.2f}" for k, v in MOCK_BANKING_DATA['portfolio'].items()])
109
+ response_parts.append(banking_context)
110
+
111
+ # Perform calculation if needed
112
+ if route["needs_calculation"]:
113
+ # Extract numbers from query for calculation
114
+ calc_prompt = f"""You are a financial calculator. Extract the calculation from this request and provide the result.
115
+
116
+ Request: {query}
117
+
118
+ If there's a compound interest calculation, use the formula: A = P(1 + r)^t
119
+ Where P = principal, r = annual rate (as decimal), t = years
120
+
121
+ Respond with ONLY the calculation result in this format:
122
+ CALCULATION: [expression]
123
+ RESULT: [number]
124
+ EXPLANATION: [brief explanation]"""
125
+
126
+ messages = [{"role": "user", "content": calc_prompt}]
127
+ calc_response = generate_response(messages, max_tokens=500)
128
+ response_parts.append(f"\n**🧮 Calculation:**\n{calc_response}")
129
+
130
+ # Generate chart if needed
131
+ if route["needs_graph"]:
132
+ query_lower = query.lower()
133
+
134
+ if 'portfolio' in query_lower or 'pie' in query_lower:
135
+ chart_data = MOCK_BANKING_DATA['portfolio']
136
+ chart_type = 'pie'
137
+ title = 'Portfolio Distribution'
138
+ elif 'assets' in query_lower:
139
+ chart_data = MOCK_BANKING_DATA['assets']
140
+ chart_type = 'bar'
141
+ title = 'Assets Breakdown'
142
+ elif 'liabilities' in query_lower:
143
+ chart_data = MOCK_BANKING_DATA['liabilities']
144
+ chart_type = 'bar'
145
+ title = 'Liabilities Breakdown'
146
+ else:
147
+ # Default: net worth projection
148
+ initial = MOCK_BANKING_DATA['net_worth']['total']
149
+ rate = 0.08
150
+ chart_data = {f"Year {i}": initial * (1 + rate) ** i for i in range(6)}
151
+ chart_type = 'line'
152
+ title = 'Net Worth Projection (8% Growth)'
153
+
154
+ chart_base64 = create_chart(chart_type, chart_data, title)
155
+ response_parts.append(f"\n**📈 Chart Generated:** {title}")
156
+
157
+ # Return as PIL Image for Gradio
158
+ import io
159
+ from PIL import Image
160
+ img_bytes = base64.b64decode(chart_base64)
161
+ chart_image = Image.open(io.BytesIO(img_bytes))
162
+
163
+ # If no specific action, use LLM for general response
164
+ if not any(route.values()):
165
+ context = f"""You are a fintech assistant. Answer the user's question about finance, banking, or investments.
166
+ Keep your response concise and helpful.
167
+
168
+ Available account data (if needed):
169
+ - Net Worth: ${MOCK_BANKING_DATA['net_worth']['total']:,.2f}
170
+ - Assets: ${MOCK_BANKING_DATA['net_worth']['assets']:,.2f}
171
+ - Liabilities: ${MOCK_BANKING_DATA['net_worth']['liabilities']:,.2f}
172
+
173
+ User question: {query}"""
174
+
175
+ messages = [{"role": "user", "content": context}]
176
+ llm_response = generate_response(messages, max_tokens=800)
177
+ response_parts.append(llm_response)
178
+
179
+ final_response = "\n".join(response_parts)
180
+
181
+ return final_response, chart_image
182
+
183
+
184
+ # Gradio Interface
185
+ with gr.Blocks(
186
+ title="Fintech Multi-Agent Orchestrator",
187
+ theme=gr.themes.Soft(
188
+ primary_hue="blue",
189
+ secondary_hue="slate",
190
+ ),
191
+ css="""
192
+ .gradio-container { max-width: 1200px !important; }
193
+ .main-title { text-align: center; margin-bottom: 1rem; }
194
+ """
195
+ ) as demo:
196
+
197
+ gr.Markdown("""
198
+ # 🏦 Fintech Multi-Agent Orchestrator
199
+
200
+ **Powered by Gemma 3 27B via HuggingFace Inference API**
201
+
202
+ Ask questions about your finances, request calculations, or generate charts!
203
+
204
+ ### Example queries:
205
+ - "What is my net worth?"
206
+ - "Show my portfolio as a pie chart"
207
+ - "Calculate compound interest on $10000 at 8% for 5 years"
208
+ - "Show my assets breakdown"
209
+ """)
210
+
211
+ with gr.Row():
212
+ with gr.Column(scale=2):
213
+ chatbot = gr.Chatbot(
214
+ label="Chat",
215
+ height=400,
216
+ type="messages",
217
+ )
218
+
219
+ with gr.Row():
220
+ query_input = gr.Textbox(
221
+ label="Your Question",
222
+ placeholder="Ask about your finances...",
223
+ scale=4,
224
+ )
225
+ submit_btn = gr.Button("Send", variant="primary", scale=1)
226
+
227
+ with gr.Column(scale=1):
228
+ chart_output = gr.Image(
229
+ label="Generated Chart",
230
+ height=400,
231
+ )
232
+
233
+ with gr.Row():
234
+ clear_btn = gr.Button("Clear Chat")
235
+
236
+ def respond(query, history):
237
+ if not query.strip():
238
+ return history, None
239
+
240
+ response, chart = process_query(query, history)
241
+ history.append({"role": "user", "content": query})
242
+ history.append({"role": "assistant", "content": response})
243
+ return history, chart
244
+
245
+ submit_btn.click(
246
+ respond,
247
+ inputs=[query_input, chatbot],
248
+ outputs=[chatbot, chart_output],
249
+ ).then(
250
+ lambda: "",
251
+ outputs=[query_input],
252
+ )
253
+
254
+ query_input.submit(
255
+ respond,
256
+ inputs=[query_input, chatbot],
257
+ outputs=[chatbot, chart_output],
258
+ ).then(
259
+ lambda: "",
260
+ outputs=[query_input],
261
+ )
262
+
263
+ clear_btn.click(
264
+ lambda: ([], None),
265
+ outputs=[chatbot, chart_output],
266
+ )
267
+
268
+
269
+ if __name__ == "__main__":
270
+ demo.launch()
hf_model.py ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ HuggingFace Inference API Model Wrapper
3
+ Uses Gemma 3 27B for fintech orchestrator
4
+ """
5
+
6
+ import os
7
+ from huggingface_hub import InferenceClient
8
+
9
+ # Initialize client
10
+ HF_TOKEN = os.getenv("HF_TOKEN")
11
+ MODEL_ID = "google/gemma-3-270m-it" # Gemma 3 27B Instruct
12
+
13
+ client = InferenceClient(token=HF_TOKEN)
14
+
15
+
16
+ def generate_response(
17
+ messages: list[dict],
18
+ max_tokens: int = 1024,
19
+ temperature: float = 0.7,
20
+ ) -> str:
21
+ """
22
+ Generate response using HuggingFace Inference API.
23
+
24
+ Args:
25
+ messages: List of message dicts with 'role' and 'content'
26
+ max_tokens: Maximum tokens to generate
27
+ temperature: Sampling temperature
28
+
29
+ Returns:
30
+ Generated text response
31
+ """
32
+ try:
33
+ response = client.chat.completions.create(
34
+ model=MODEL_ID,
35
+ messages=messages,
36
+ max_tokens=max_tokens,
37
+ temperature=temperature,
38
+ )
39
+ return response.choices[0].message.content
40
+ except Exception as e:
41
+ return f"Error: {str(e)}"
42
+
43
+
44
+ def calculate_expression(expression: str) -> str:
45
+ """Simple calculator for financial expressions."""
46
+ import re
47
+ import math
48
+
49
+ # Safe eval with limited functions
50
+ allowed_names = {
51
+ 'abs': abs, 'round': round, 'min': min, 'max': max,
52
+ 'pow': pow, 'sqrt': math.sqrt, 'log': math.log,
53
+ 'exp': math.exp, 'pi': math.pi, 'e': math.e,
54
+ }
55
+
56
+ try:
57
+ # Clean the expression
58
+ expr = expression.strip()
59
+ # Basic validation
60
+ if not re.match(r'^[\d\s\+\-\*\/\.\(\)\^]+$', expr.replace('**', '^')):
61
+ # Try to extract numbers and operators more flexibly
62
+ pass
63
+
64
+ result = eval(expr, {"__builtins__": {}}, allowed_names)
65
+ return f"{result:,.2f}"
66
+ except Exception as e:
67
+ return f"Calculation error: {str(e)}"
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ gradio>=4.0.0
2
+ huggingface-hub>=0.20.0
3
+ matplotlib>=3.8.0
4
+ numpy>=1.24.0
5
+ Pillow>=10.0.0