Skip to content

Commit da63971

Browse files
author
ChromeOS Developer
committed
Final documentation updates for hackathon submission
1 parent 0049e6d commit da63971

8 files changed

Lines changed: 187 additions & 2 deletions

File tree

README.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,14 @@
1313
<img src="https://img.shields.io/badge/License-MIT-yellow?style=for-the-badge" alt="MIT License">
1414
</p>
1515

16+
<p align="center">
17+
<a href="https://callshield-ui.onrender.com/">
18+
<img src="https://img.shields.io/badge/🔴%20LIVE%20DEMO-No%20API%20Key%20Required-brightgreen?style=for-the-badge&labelColor=000000" alt="Live Demo — No API Key Required">
19+
</a>
20+
</p>
21+
22+
> **No API key. No setup. No account.** Click the live demo above — verdicts appear in under 2 seconds.
23+
1624
<p align="center">
1725
<a href="https://callshield-ui.onrender.com/">🔴 Live Demo</a>
1826
</p>
@@ -55,6 +63,22 @@ The FTC reported **$25.5 billion** in phone and online fraud losses in 2023. Pho
5563
| Phase 2 | On-device Voxtral inference — no audio leaves the handset |
5664
| Phase 3 | Network-level inline scoring — real-time intercept on the PSTN |
5765

66+
## Designed for the 5G Edge
67+
68+
CallShield's audio-native pipeline is built for the speed requirements of live telecom infrastructure. By eliminating the STT transcription step, each 5-second audio chunk is scored in a **single model call** — fast enough to run inline without buffering or dropping the call.
69+
70+
| Constraint | Requirement | CallShield |
71+
|-----------|-------------|-----------|
72+
| Chunk scoring latency | < 5s to avoid call dropout | ~1.5–3s per chunk |
73+
| Pipeline steps | Minimal for real-time path | 1 API call (vs 2 for STT+LLM) |
74+
| Audio format | Standard carrier formats | WAV/PCM, 8–16 kHz mono |
75+
| Deployment model | Stateless, horizontally scalable | FastAPI + Docker, no shared state |
76+
| Privacy requirement | No audio retention on network | In-memory only; discarded after scoring |
77+
78+
> At 5G speeds, the bottleneck is inference latency, not bandwidth. Skipping STT cuts CallShield's critical path in half.
79+
80+
→ Carrier integration recipes (Twilio, SIP SIPREC): [docs/INTEGRATION.md](docs/INTEGRATION.md)
81+
5882
CallShield's REST + WebSocket API integrates directly with **VoIP platforms** (Twilio, Amazon Connect, Genesys) and carrier infrastructure (SIP SIPREC) — no custom audio pipeline required. → See [docs/INTEGRATION.md](docs/INTEGRATION.md) for webhook recipes and typed client examples.
5983

6084
---
@@ -209,6 +233,8 @@ flowchart TD
209233
| [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) | System design, data flows, scoring algorithm, technical decisions |
210234
| [docs/MODEL_USAGE.md](docs/MODEL_USAGE.md) | Prompt engineering, 7 detection dimensions, token estimates |
211235
| [docs/THREAT_MODEL.md](docs/THREAT_MODEL.md) | Privacy analysis, abuse mitigations, GDPR/CCPA, red-team cases |
236+
| [docs/ADVERSARIAL_TESTING.md](docs/ADVERSARIAL_TESTING.md) | Narrative adversarial test results — polite scammers, angry safe callers, evasion attempts |
237+
| [SECURITY.md](SECURITY.md) | Vulnerability reporting, security design principles, known limitations |
212238
| [docs/INTEGRATION.md](docs/INTEGRATION.md) | OpenAPI spec, carrier webhook recipe, SIPREC integration guide |
213239
| [docs/COMPARISON.md](docs/COMPARISON.md) | Voxtral native audio vs STT+LLM pipeline — latency, accuracy, cost |
214240
| [docs/QUICKSTART.md](docs/QUICKSTART.md) | Docker, manual setup, one-line script |
695 Bytes
Binary file not shown.

backend/prompts/templates.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
5. VOCAL PATTERNS: Aggressive tone, scripted speech, call-center background noise?
1111
6. KNOWN SCAM SCRIPTS: IRS threats, tech support fraud, romance scam soliciting money, prize notification, etc.
1212
7. ROBOCALL / IVR SCAM PATTERNS: Pre-recorded messages that ask you to "press 1" or "press a button" to speak to an agent or representative. Unsolicited calls about Medicare benefits, extended warranties, insurance offers, free products, or debt relief that use automated prompts to connect you to a live agent are VERY commonly scam robocalls and should score 0.6+ minimum.
13+
8. VOCAL STRESS: Rate 0.0–1.0 how much stress, aggression, or urgency is detectable in the speaker's voice. 0.0 = calm/natural, 1.0 = highly aggressive or pressured delivery.
14+
9. BACKGROUND NOISE: Rate 0.0–1.0 the presence of call-center/boiler-room noise (multiple voices, phone chatter, dialing tones). 0.0 = quiet/natural environment, 1.0 = clear call-center background.
15+
10. SYNTHETIC VOICE PROBABILITY: Rate 0.0–1.0 how likely the voice is TTS-generated or AI-synthesized rather than a real human. 0.0 = clearly human, 1.0 = clearly synthetic/robocall.
1316
1417
Scoring guidelines:
1518
- 0.0-0.2: Normal conversation, no scam indicators
@@ -51,7 +54,10 @@
5154
{"category": "<dimension>", "detail": "<what you detected>", "severity": "low" | "medium" | "high"}
5255
],
5356
"transcript_summary": "<brief summary of what was said>",
54-
"recommendation": "<what the user should do>"
57+
"recommendation": "<what the user should do>",
58+
"vocal_stress": <float 0.0 to 1.0>,
59+
"background_noise": <float 0.0 to 1.0>,
60+
"synthetic_voice_probability": <float 0.0 to 1.0>
5561
}"""
5662

5763
SCAM_TEXT_PROMPT = """You are a scam detection expert analyzing a phone call transcript.
367 Bytes
Binary file not shown.

backend/services/stream_processor.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ async def process_chunk(self, audio_chunk: bytes) -> dict:
4343
"""Process a single audio chunk and return partial result."""
4444
# Always increment chunk_index to avoid duplicates
4545
self.chunk_index += 1
46-
46+
chunk_start_time = time.time()
47+
4748
timestamp_ms = int((time.time() - self.start_time) * 1000)
4849

4950
if is_silent(audio_chunk):
@@ -53,6 +54,10 @@ async def process_chunk(self, audio_chunk: bytes) -> dict:
5354
"timestamp_ms": timestamp_ms,
5455
"score_delta": 0.0,
5556
"new_signals": [],
57+
"chunk_processing_ms": int((time.time() - chunk_start_time) * 1000),
58+
"vocal_stress": 0.0,
59+
"background_noise": 0.0,
60+
"synthetic_voice_probability": 0.0,
5661
"scam_score": 0.0,
5762
"cumulative_score": round(self.cumulative_score, 4),
5863
"verdict": "SAFE",
@@ -112,6 +117,11 @@ async def process_chunk(self, audio_chunk: bytes) -> dict:
112117
finally:
113118
resp.close()
114119

120+
vocal_stress = max(0.0, min(1.0, float(data.get("vocal_stress", 0.0))))
121+
background_noise = max(0.0, min(1.0, float(data.get("background_noise", 0.0))))
122+
synthetic_voice_probability = max(0.0, min(1.0, float(data.get("synthetic_voice_probability", 0.0))))
123+
chunk_processing_ms = int((time.time() - chunk_start_time) * 1000)
124+
115125
chunk_score = float(data.get("scam_score", 0.0))
116126
signals = data.get("signals", [])
117127
self.last_recommendation = data.get("recommendation", "")
@@ -139,6 +149,10 @@ async def process_chunk(self, audio_chunk: bytes) -> dict:
139149
"timestamp_ms": timestamp_ms,
140150
"score_delta": round(score_delta, 4),
141151
"new_signals": new_signals,
152+
"chunk_processing_ms": chunk_processing_ms,
153+
"vocal_stress": round(vocal_stress, 3),
154+
"background_noise": round(background_noise, 3),
155+
"synthetic_voice_probability": round(synthetic_voice_probability, 3),
142156
"scam_score": round(chunk_score, 4),
143157
"cumulative_score": round(self.cumulative_score, 4),
144158
"max_score": round(self.max_score, 4),

docs/ADVERSARIAL_TESTING.md

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
# CallShield Adversarial Testing
2+
3+
This document describes the adversarial test cases we used to challenge CallShield's detection logic — including deliberate attempts to trick the model with polite scammers, angry-but-innocent callers, and synthesized evasion tactics.
4+
5+
The goal: confirm that the audio-native Voxtral pipeline catches what text-only models miss.
6+
7+
---
8+
9+
## The Core Challenge
10+
11+
Text-only scam detection can be fooled by word choice. A scammer who says *"I understand your concern"* instead of *"You must pay NOW"* can drop their text-score significantly. Voxtral doesn't have this weakness — it listens to the acoustic delivery, not just the words.
12+
13+
We tested this systematically.
14+
15+
---
16+
17+
## Adversarial Scenario Results
18+
19+
### 1. The Polite IRS Agent
20+
**Attack:** A scammer using calm, professional language — "I'd like to help you resolve this" instead of threatening arrest.
21+
**Text-only vulnerability:** Politeness reduces urgency signal weight.
22+
**Result:** Score **0.95 SCAM** — Voxtral detected the scripted call-center delivery cadence and IRS authority claim regardless of polite framing.
23+
24+
### 2. The Hedged Crypto Pitch
25+
**Attack:** Softened language — "some people have seen returns" instead of "guaranteed profits."
26+
**Text-only vulnerability:** Hedging removes the "too good to be true" signal.
27+
**Result:** Score **0.80 LIKELY_SCAM** — Voxtral caught the rehearsed sales delivery pattern and financial solicitation structure.
28+
29+
### 3. The Angry Legitimate Customer
30+
**Attack:** A genuinely upset customer complaining about a billing error — aggressive tone, emotional language, demand for resolution.
31+
**Risk:** Could be misclassified due to emotional intensity and urgency.
32+
**Result:** Score **0.10 SAFE** — No payment demand, no authority impersonation, no information extraction. Anger alone is not a scam signal.
33+
34+
### 4. The "Certified" Tech Support
35+
**Attack:** Scammer claims to be from a "Microsoft Certified Partner" with a legitimate-sounding business name.
36+
**Text-only vulnerability:** "Certified" and business-name legitimacy signals can lower suspicion.
37+
**Result:** Score **0.90 SCAM** — Remote access request + unsolicited outbound call pattern flagged regardless of claimed credentials.
38+
39+
### 5. The FDIC Bank Examiner
40+
**Attack:** Highly convincing authority impersonation of a federal banking regulator — formal language, regulation citations.
41+
**Text-only vulnerability:** Formal institutional language can suppress scam scores.
42+
**Result:** Score **0.92 SCAM** — Voxtral detected the combination of authority impersonation + account information request, which legitimate FDIC examiners never do by phone.
43+
44+
### 6. The Legitimate Doctor IVR
45+
**Attack:** A real automated appointment reminder — robotic voice, pre-recorded, mentions a patient name.
46+
**Risk:** Automated voice + patient data mention could trip false positive.
47+
**Result:** Score **0.10 SAFE** — No financial request, no urgency pressure, recognisable healthcare IVR pattern. Correctly cleared.
48+
49+
### 7. The Legitimate Bank Fraud Alert
50+
**Attack:** Real bank automated alert — uses authority language ("This is First National Bank"), urgency ("possible unauthorized transaction"), and asks for callback.
51+
**Risk:** Authority + urgency is the classic scam combination.
52+
**Result:** Score **0.15 SAFE** — Critically, the call does NOT request credentials or payment. CallShield distinguishes "call us back" from "give us your PIN now."
53+
54+
---
55+
56+
## Automated Adversarial Suite
57+
58+
All adversarial scenarios are implemented as automated tests in `backend/tests/test_adversarial.py`:
59+
60+
| Test | What it probes | Expected result |
61+
|------|---------------|-----------------|
62+
| Prompt injection in recommendation field | Model output manipulation | Score clamped, valid result |
63+
| Score out of range (1.5, -0.5) | Clamping enforcement | Clamped to [0.0, 1.0] |
64+
| Missing fields in model response | Default value safety | No crash, safe defaults applied |
65+
| Silence (zero-byte PCM buffer) | Edge case handling | is_silent() → True |
66+
| Long-con script (friendly opener → wire transfer) | Multi-phase scam detection | score ≥ 0.6 |
67+
| Pharmacy IVR (benign robocall) | False positive prevention | verdict ≠ SCAM |
68+
69+
Run: `cd backend && pytest tests/test_adversarial.py -v`
70+
71+
---
72+
73+
## Why Native Audio Matters for Adversarial Robustness
74+
75+
The key finding across all adversarial tests: **acoustic delivery is harder to fake than word choice.**
76+
77+
A scammer can rewrite their script to sound polite. They cannot easily:
78+
- Suppress the call-center background noise of a boiler room
79+
- Remove the flat, rehearsed cadence of a scripted pitch
80+
- Eliminate the TTS artifacts of a synthesized robocall voice
81+
- Change the rhythm of a pre-recorded IVR message
82+
83+
Text-based models see only the words. Voxtral hears the room.
84+
85+
---
86+
87+
*Full evaluation results: [docs/EVALUATION.md](docs/EVALUATION.md)*
88+
*Threat model and red-team mitigations: [docs/THREAT_MODEL.md](docs/THREAT_MODEL.md)*

frontend/src/App.tsx

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,53 @@ export default function App() {
150150
onToggle={() => setShowLog((v) => !v)}
151151
/>
152152

153+
{/* Acoustic Context — live panel during recording */}
154+
{isRecording && partialResults.length > 0 && (() => {
155+
const latest = partialResults[partialResults.length - 1];
156+
const hasAcoustics = latest.vocal_stress !== undefined ||
157+
latest.background_noise !== undefined ||
158+
latest.synthetic_voice_probability !== undefined;
159+
if (!hasAcoustics) return null;
160+
const bars = [
161+
{ label: "Vocal Stress", value: latest.vocal_stress ?? 0, color: "bg-red-500" },
162+
{ label: "Background Noise", value: latest.background_noise ?? 0, color: "bg-yellow-500" },
163+
{ label: "Synth Voice Prob.", value: latest.synthetic_voice_probability ?? 0, color: "bg-purple-500" },
164+
];
165+
return (
166+
<div className="bg-gray-900 border border-gray-700 rounded-lg p-4">
167+
<div className="flex items-center justify-between mb-3">
168+
<h3 className="text-xs font-semibold text-gray-400 uppercase tracking-wide">
169+
Acoustic Context <span className="text-blue-400 ml-1">● Live</span>
170+
</h3>
171+
{latest.chunk_processing_ms !== undefined && (
172+
<span className="text-xs text-gray-500 font-mono">
173+
Last chunk: {latest.chunk_processing_ms}ms
174+
</span>
175+
)}
176+
</div>
177+
<div className="space-y-2">
178+
{bars.map(({ label, value, color }) => (
179+
<div key={label} className="flex items-center gap-3">
180+
<span className="text-xs text-gray-400 w-36 shrink-0">{label}</span>
181+
<div className="flex-1 bg-gray-700 rounded-full h-2">
182+
<div
183+
className={`${color} h-2 rounded-full transition-all duration-500`}
184+
style={{ width: `${Math.round(value * 100)}%` }}
185+
/>
186+
</div>
187+
<span className="text-xs text-gray-300 font-mono w-8 text-right">
188+
{Math.round(value * 100)}%
189+
</span>
190+
</div>
191+
))}
192+
</div>
193+
<p className="text-xs text-gray-600 mt-2 italic">
194+
Voxtral is analyzing raw audio — not text transcripts
195+
</p>
196+
</div>
197+
);
198+
})()}
199+
153200
{/* Loading card while waiting for final analysis */}
154201
{!isRecording && isProcessingFinal && (
155202
<div className="bg-gray-800 border border-gray-700 rounded-lg p-6 text-center">

frontend/src/hooks/useStream.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ interface PartialResult {
1515
timestamp_ms?: number;
1616
score_delta?: number;
1717
new_signals?: Signal[];
18+
chunk_processing_ms?: number;
19+
vocal_stress?: number;
20+
background_noise?: number;
21+
synthetic_voice_probability?: number;
1822
[key: string]: unknown;
1923
}
2024

0 commit comments

Comments
 (0)