Skip to content

Commit 05fffb1

Browse files
committed
feat(pivot): Finsight Knowledge Graph & Reconciliation
1 parent f3616fb commit 05fffb1

134 files changed

Lines changed: 5766 additions & 206 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

backend/app/main.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
from app.core.config import get_settings
1111
from app.api import router as api_router
12-
from app.sockets import setup_websocket_routes
12+
from app.sockets import router as websocket_router
1313

1414

1515
def create_app() -> FastAPI:
@@ -37,8 +37,8 @@ def create_app() -> FastAPI:
3737
# Include REST API routes
3838
app.include_router(api_router)
3939

40-
# Register WebSocket routes
41-
setup_websocket_routes(app)
40+
# Include WebSocket routes
41+
app.include_router(websocket_router)
4242

4343
@app.get("/")
4444
async def root():
Lines changed: 82 additions & 154 deletions
Original file line numberDiff line numberDiff line change
@@ -1,171 +1,99 @@
1-
"""
2-
FinShield - Audio Processor Service
31

4-
THIS IS SREEDEV'S FILE - Implement real audio processing logic here.
5-
6-
Interface for real-time audio chunk processing.
7-
Current implementation is a mock that returns dummy data.
8-
Replace with Hotfoot Audio integration.
9-
"""
10-
11-
from abc import ABC, abstractmethod
12-
from typing import Any, Dict, List, Optional
132
import random
3+
from typing import Dict, Any, List
4+
# from app.services.audio_processor import AudioProcessorBase # Assuming interface exists
145

15-
16-
class AudioProcessorBase(ABC):
17-
"""
18-
Abstract base class for audio processing.
19-
20-
Sreedev: Implement your Hotfoot Audio integration by:
21-
1. Creating a new class that inherits from this
22-
2. Implementing the abstract methods
23-
3. Updating get_audio_processor() to return your class
24-
"""
25-
26-
@abstractmethod
27-
async def process_chunk(self, audio_bytes: bytes) -> Dict[str, Any]:
28-
"""
29-
Process a single audio chunk in real-time.
30-
31-
Args:
32-
audio_bytes: Raw audio data (Int16 PCM format)
33-
34-
Returns:
35-
Dict with:
36-
- risk_score: float (0.0 - 1.0)
37-
- threat_level: str ("safe", "low", "medium", "high", "critical")
38-
- flags: List[str] (detected issues)
39-
- transcript_snippet: Optional[str]
40-
"""
41-
pass
42-
43-
@abstractmethod
44-
async def start_session(self, session_id: str) -> bool:
45-
"""Initialize a new analysis session."""
46-
pass
47-
48-
@abstractmethod
49-
async def end_session(self, session_id: str) -> Dict[str, Any]:
50-
"""
51-
End session and get final analysis.
52-
53-
Returns:
54-
Complete session analysis with aggregated results
55-
"""
56-
pass
57-
58-
@abstractmethod
59-
async def get_transcript(self, session_id: str) -> Optional[str]:
60-
"""Get full transcript for a session."""
61-
pass
62-
63-
64-
class MockAudioProcessor(AudioProcessorBase):
65-
"""
66-
Mock implementation for development/demo.
67-
68-
TODO (Sreedev): Replace this with HotfootAudioProcessor
69-
"""
70-
71-
SAMPLE_FLAGS = [
72-
"Urgency language detected",
73-
"Request for sensitive information",
74-
"Authority impersonation attempt",
75-
"Pressure tactics identified",
76-
"Suspicious callback request",
77-
]
78-
6+
class MockAudioProcessor:
797
def __init__(self):
80-
self.sessions: Dict[str, Dict] = {}
818
self._chunk_count = 0
9+
self._transcript_segments = [
10+
"Hello, am I speaking with Mr. Anand?",
11+
"This is calling from the Card Protection Department regarding your Visa ending in 4521.",
12+
"We have detected a suspicious transaction of Rs. 50,000 on your account.",
13+
"If this was not you, we need to verify your identity immediately to block it.",
14+
"Please confirm your date of birth for verification based on our records from 1990.",
15+
"Do not hang up, or your account will be debit frozen within 15 minutes.",
16+
"To reverse the charge, I need you to download the QuickSupport app now.",
17+
"Rest assured, this is a secure line monitored by the RBI fraud prevention unit.",
18+
"Just tell me the OTP you received to cancel the transaction of Rs. 50,000.",
19+
"Why are you hesitating? Do you want to lose your money, sir?",
20+
]
21+
22+
# Scenarios
23+
self.scenarios = {
24+
"SAFE": {
25+
"risk_base": 0.05,
26+
"intent": "GRIEVANCE",
27+
"stress": 0.12,
28+
"transcript": [
29+
"Hello, this is verified support.",
30+
"How can I help you with your query today?",
31+
"I see you have a dispute about a charge.",
32+
"Let me check that for you right now.",
33+
"Okay, I can see the transaction of Rs. 500.",
34+
"I will raise a ticket for this refund.",
35+
"It should reflect in 3-5 business days.",
36+
"Is there anything else I can help you with?",
37+
"Thank you for banking with us.",
38+
"Have a wonderful day."
39+
]
40+
},
41+
"SCAM": {
42+
"risk_base": 0.95,
43+
"intent": "THREAT / FRAUD",
44+
"stress": 0.85,
45+
"transcript": self._transcript_segments
46+
},
47+
"SUSPICIOUS": {
48+
"risk_base": 0.45,
49+
"intent": "COLLECTION",
50+
"stress": 0.55,
51+
"transcript": [
52+
"Hello, calling about your pending dues.",
53+
"You have missed the payment of Rs. 12,000.",
54+
"When can we expect this payment to be cleared?",
55+
"If you delay, there will be a penalty charge.",
56+
"We might have to send an agent to your address.",
57+
"Please make the payment by tomorrow 5 PM.",
58+
"This will affect your CIBIL score negatively.",
59+
"We are offering a settlement if you pay now.",
60+
"Do not ignore these calls, sir.",
61+
"Pay immediately to avoid legal action."
62+
]
63+
}
64+
}
65+
66+
self.current_scenario = "SCAM" # Default for now, can be changed via "Wizard" injection potentially
8267

8368
async def process_chunk(self, audio_bytes: bytes) -> Dict[str, Any]:
84-
"""
85-
Mock processing - returns simulated risk analysis.
86-
87-
In production, this would:
88-
1. Send audio to Hotfoot Audio API
89-
2. Get real-time transcription
90-
3. Analyze for fraud patterns
91-
4. Return actual risk scores
92-
"""
9369
self._chunk_count += 1
94-
chunk_size = len(audio_bytes)
9570

96-
# Simulate varying risk based on chunk count (demo effect)
97-
# Risk increases slightly over time to show dynamic behavior
98-
base_risk = 0.1
99-
dynamic_risk = min(0.1, (self._chunk_count % 50) * 0.002)
100-
random_variation = random.uniform(-0.05, 0.15)
71+
# Determine scenario based on external injection (not implemented here yet, just random or fixed)
72+
# For demo, let's rotate or stick to one.
73+
# Actually, let's pick a segment based on chunk count.
10174

102-
risk_score = min(1.0, max(0.0, base_risk + dynamic_risk + random_variation))
75+
scenario_data = self.scenarios.get(self.current_scenario, self.scenarios["SCAM"])
76+
transcript_list = scenario_data["transcript"]
10377

104-
# Determine threat level
105-
if risk_score < 0.2:
106-
threat_level = "safe"
107-
elif risk_score < 0.4:
108-
threat_level = "low"
109-
elif risk_score < 0.6:
110-
threat_level = "medium"
111-
elif risk_score < 0.8:
112-
threat_level = "high"
113-
else:
114-
threat_level = "critical"
78+
# Loop through transcript
79+
idx = (self._chunk_count - 1) % len(transcript_list)
80+
transcript_text = transcript_list[idx]
11581

116-
# Occasionally add flags for demo
117-
flags = []
118-
if risk_score > 0.3 and random.random() > 0.7:
119-
flags = random.sample(self.SAMPLE_FLAGS, min(2, int(risk_score * 3)))
82+
# Mock Logic
83+
risk_score = scenario_data["risk_base"] + random.uniform(-0.05, 0.05)
84+
risk_score = max(0.0, min(1.0, risk_score))
12085

12186
return {
12287
"risk_score": round(risk_score, 3),
123-
"threat_level": threat_level,
124-
"flags": flags,
125-
"transcript_snippet": None, # Would contain real transcription
126-
"chunk_size": chunk_size,
127-
"processing_ms": random.randint(5, 25), # Simulated latency
88+
"threat_level": "critical" if risk_score > 0.8 else ("high" if risk_score > 0.6 else "safe"),
89+
"flags": ["Urgency Detected", "Financial Threat"] if risk_score > 0.6 else [],
90+
"transcript_snippet": transcript_text, # SENDING TRANSCRIPT NOW
91+
"intent": scenario_data["intent"],
92+
"stress_score": scenario_data["stress"],
93+
"chunk_size": len(audio_bytes),
12894
}
12995

130-
async def start_session(self, session_id: str) -> bool:
131-
"""Start a new mock session."""
132-
self.sessions[session_id] = {
133-
"started_at": "now",
134-
"chunks": 0,
135-
"total_risk": 0.0,
136-
}
137-
self._chunk_count = 0
138-
return True
139-
140-
async def end_session(self, session_id: str) -> Dict[str, Any]:
141-
"""End mock session with summary."""
142-
session = self.sessions.pop(session_id, {})
143-
return {
144-
"session_id": session_id,
145-
"total_chunks": session.get("chunks", 0),
146-
"average_risk": session.get("total_risk", 0) / max(1, session.get("chunks", 1)),
147-
"final_verdict": "Analysis complete",
148-
}
149-
150-
async def get_transcript(self, session_id: str) -> Optional[str]:
151-
"""Mock transcript."""
152-
return "[Mock transcript - Hotfoot Audio integration pending]"
153-
154-
155-
# ============================================================================
156-
# FACTORY FUNCTION - Update this to use real implementation
157-
# ============================================================================
158-
159-
def get_audio_processor() -> AudioProcessorBase:
160-
"""
161-
Get audio processor instance.
162-
163-
TODO (Sreedev): When Hotfoot Audio is ready, change to:
164-
165-
from app.core.config import get_settings
166-
settings = get_settings()
167-
if settings.hotfoot_audio_api_key:
168-
return HotfootAudioProcessor(api_key=settings.hotfoot_audio_api_key)
169-
return MockAudioProcessor()
170-
"""
171-
return MockAudioProcessor()
96+
def set_scenario(self, scenario_name: str):
97+
if scenario_name in self.scenarios:
98+
self.current_scenario = scenario_name
99+
self._chunk_count = 0 # Reset transcript loop

0 commit comments

Comments
 (0)