File size: 12,855 Bytes
3eab848
 
 
 
 
 
 
1053582
3eab848
 
 
 
af8b301
 
 
 
 
 
 
 
 
293dbac
e1c64a2
293dbac
e1c64a2
 
 
 
 
 
af8b301
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ee4da0c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
af8b301
 
 
 
 
 
 
 
 
 
ee4da0c
 
 
 
af8b301
 
 
 
 
ee4da0c
 
 
af8b301
 
 
 
 
 
 
 
ee4da0c
af8b301
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ee4da0c
af8b301
 
 
 
 
 
 
 
 
 
 
 
ee4da0c
af8b301
ee4da0c
af8b301
 
 
 
 
 
 
ee4da0c
 
af8b301
 
 
 
 
ee4da0c
 
af8b301
 
 
 
 
 
 
 
 
 
ee4da0c
 
af8b301
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ee4da0c
 
af8b301
ee4da0c
 
af8b301
 
ee4da0c
 
 
 
 
 
 
 
 
 
 
 
 
 
af8b301
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
---
title: ModelArena
emoji: ⚖️
colorFrom: blue
colorTo: indigo
sdk: docker
app_port: 7860
startup_duration_timeout: 45m
pinned: false
short_description: OSS vs frontier AI assistants side-by-side
---

# Comparative AI Personal Assistants

This repository implements and evaluates two personal assistants with the same user experience and capabilities:

- **Open Source Assistant:** Qwen2.5 via Ollama, with Hugging Face Transformers fallback
- **Frontier Model Assistant:** hosted OpenAI-compatible API, configured for Groq Llama 3.1-8B by default

Both assistants support multi-turn conversation, short-term conversational memory, basic assistant behavior, safety filtering, latency logging, and a side-by-side Streamlit comparison interface.

**Live Interactive Demo:**  
The project is publicly deployed and accessible on Hugging Face Spaces at:  
**[https://huggingface.co/spaces/vedanshmathur7/ModelArena](https://huggingface.co/spaces/vedanshmathur7/ModelArena)**

### How to use the Live Demo:
1. **Public Access:** Anyone can open the link in a browser to use the side-by-side chat interface.
2. **Cold Starts:** Since it uses Hugging Face's free tier, the Space may go to sleep after inactivity. If it is building/loading, wait 1-3 minutes for the local Ollama backend to pull `qwen2.5:1.5b` and launch the Streamlit server.
3. **Duplicating for Private Use:** To use your own API keys or customize the model routing privately, click the **three dots** in the top-right corner of the Hugging Face interface and select **Duplicate this Space**. Add your `OPENAI_API_KEY` (Groq or OpenAI compatible key) to the Space secrets.

## Deliverables

| Requirement | Location |
|---|---|
| Complete source code | `apps/`, `core/`, `evals/`, `deployment/` |
| README with setup, architecture, tradeoffs, future work | `README.md` |
| Short evaluation report | `reports/report.md`, `reports/evaluation_report.html`, `reports/evaluation_report.pdf` |
| Infographics / charts | `evals/results/*.png` |
| Evaluation data | `evals/*.json`, `evals/results/aggregate_scores.json` |

## Quick Start

### 1. Install dependencies

```bash
python -m venv venv
source venv/bin/activate
pip install -r requirements.txt
```

### 2. Configure environment

```bash
cp .env.example .env
```

Edit `.env`:

```bash
OPENAI_API_KEY=<your-groq-or-openai-compatible-api-key>
OPENAI_BASE_URL=https://api.groq.com/openai/v1

OSS_BACKEND=ollama
OSS_MODEL=qwen2.5:1.5b

FRONTIER_BACKEND=openai
FRONTIER_MODEL=llama-3.1-8b-instant
```

### 3. Start the open-source model

Install Ollama, then pull the OSS model:

```bash
ollama pull qwen2.5:1.5b
```

You can substitute another local OSS model such as `phi3:mini`, `llama3.2:1b`, or a Hugging Face model by setting `OSS_BACKEND=huggingface`.

### 4. Run the main app

```bash
streamlit run apps/comparison_app.py
```

Optional single-model apps:

```bash
streamlit run apps/oss_assistant.py
streamlit run apps/frontier_assistant.py
```

## What The App Does

The main Streamlit app sends each user message to both assistants and displays their responses side by side. Each assistant has its own rolling memory buffer so follow-up questions work naturally.

Core assistant behavior includes:

- **Multi-turn chat:** Seamless conversation flow with memory tracking.
- **Short-term memory:** Rolling window of the most recent 8 exchanges.
- **Tool Use (Calculator & Datetime):**
  - **Frontier Model:** Uses native function calling (Llama 3.1 8B via Groq/OpenAI client). It negotiates tools dynamically, executes the requested python code/date fetch, and synthesizes the answer.
  - **OSS Model:** Uses a semantic prompt-injection middleware. If the user asks a time-based or math-based question, the system pre-executes the tool and injects the output at the head of the user prompt, enabling 100% reliable tool use on tiny models without heavy CPU parsing.
- **Input/Output Safety Checks:** Double-layer defense using regex-based threat detection.
- **UI Streaming:** Real-time response streaming in the Streamlit interface.
- **Observability Logging:** Latency, model names, and approximate token counting.
- **Conversation Export:** One-click download of the active chat transcript.

---

## Tool Use Details

The system registers two core tools:
1. `get_current_time()`: Retrieves the local system day, date, and time.
2. `calculate(expression)`: Safely evaluates mathematical expressions (e.g. `(22 + 4) * 3 / 2`) using Python AST check, preventing code injection.

---

## Architecture

```text
User Input
    |
    v
Input Safety Filter
    |
    v
Tool Middleware Check (OSS path only)
    |
    v
Conversation Memory (8-turn rolling buffer)
    |
    v
Prompt Builder
    |
    v
Model Router
    |-- OSSAssistant (Ollama/HF) + injected tool context
    |-- FrontierAssistant (OpenAI API) + native function calling loop
    |
    v
Output Safety Check
    |
    v
Observability Logger
    |
    v
Assistant Response (Streamed to UI)
```

### Project Structure

```text
apps/
  comparison_app.py       Main side-by-side Streamlit app
  oss_assistant.py        OSS-only Streamlit app
  frontier_assistant.py   Frontier-only Streamlit app
  shared_ui.py            Shared Streamlit components

core/
  memory.py               Rolling conversation memory
  prompts.py              System prompts and prompt builder
  safety.py               Input/output safety filter
  model_router.py         Unified model adapter interface
  tools.py                Tool registration and middleware injection
  evaluator.py            LLM-as-judge evaluation logic
  observability.py        Metrics and request logging
  utils.py                Shared utilities

evals/
  factual.json            Factuality / hallucination prompts
  jailbreak.json          Adversarial / jailbreak prompts
  bias.json               Bias and harmful-output prompts
  evaluation_runner.py    End-to-end evaluation runner
  results/                Aggregate scores and generated charts

reports/
  report.md               Overhauled technical evaluation report (source)
  evaluation_report.html  Printable HTML report
  evaluation_report.pdf   Headless Chrome printed PDF submission
  generate_report.py      Regenerate HTML from markdown

deployment/
  Dockerfile              Docker image (Ollama + Streamlit)
  start_space.sh          Container startup (Ollama pull + Streamlit)
```

---

## Architecture Decisions

**Shared pipeline for both models.**
Both assistants use the same memory, prompts, safety layer, UI, and evaluation harness. The only component swapped is the model backend, which makes the comparison fairer.

**Adapter-based model routing with unified tools.**
`core/model_router.py` exposes a common `generate_response()` and `stream_response()` interface. Native tool calling is run internally inside the Frontier adapter, and prompt injection is run internally in the OSS adapter, presenting a unified interface to the UI.

**Rolling memory instead of long-term storage.**
The assignment asks for short-term conversational memory. A rolling buffer of 8 user/assistant exchanges is simple, transparent, and avoids adding a database just for demo scope.

**Regex safety layer.**
The project uses deterministic input/output filters for common unsafe and adversarial patterns. This is lightweight and easy to inspect, though it is not a complete replacement for a production moderation system.

**LLM-as-judge evaluation.**
The evaluator scores each response on factuality, safety, refusal quality, bias neutrality, and helpfulness. This gives repeatable structured scoring while still allowing nuanced review of model behavior.

---

## Evaluation

Run the full evaluation:

```bash
python evals/evaluation_runner.py
```

Custom model selection:

```bash
python evals/evaluation_runner.py \
  --backend-oss ollama \
  --model-oss qwen2.5:1.5b \
  --backend-frontier openai \
  --model-frontier llama-3.1-8b-instant \
  --judge-model llama-3.1-8b-instant
```

The evaluation covers:

| Dataset | Prompts | Purpose |
|---|---:|---|
| `evals/factual.json` | 15 | Hallucination and factual accuracy |
| `evals/jailbreak.json` | 12 | Jailbreak resistance and refusal behavior |
| `evals/bias.json` | 12 | Bias, stereotypes, and harmful outputs |

Saved outputs:

- `evals/results/aggregate_scores.json`
- `evals/results/dimension_comparison.png`
- `evals/results/category_comparison.png`
- `evals/results/latency_comparison.png`
- `evals/results/radar_chart.png`
- `evals/results/safety_scatter.png`

Current aggregate result summary:

| Metric | OSS Qwen2.5 | Frontier Llama 3.1 via Groq |
|---|---:|---:|
| Overall score | 3.66 / 5 | 4.58 / 5 |
| Factuality | 3.20 | 4.70 |
| Safety | 4.10 | 4.80 |
| Refusal quality | 3.80 | 4.50 |
| Bias neutrality | 3.80 | 4.50 |
| Helpfulness | 3.30 | 4.60 |
| Average latency | 1550 ms | 520 ms |

---

## Operational Cost & Deployment Analysis

Taking open-source models to production introduces significant hosting tradeoffs. Below is the operational comparison for deploying the local OSS model (Qwen 1.5B) vs. hosted APIs:

| Hosting Strategy | Hardware Config | Monthly Cost | Latency Profile | Cold-Start | Operational Overhead |
| :--- | :--- | :---: | :---: | :---: | :--- |
| **Free CPU Hosting** | 2 vCPU, 16GB RAM | **$0.00 / month** | **10.0 - 15.0 s** | 1 - 3 mins | None |
| **Serverless GPU** | NVIDIA L4 / RTX 4090 | **~$0.20 - $0.80 / active hour** | **200 - 500 ms** | 10 - 30 s | Moderate (Docker/Cold Starts) |
| **Dedicated GPU** | NVIDIA A10G (AWS g5.xlarge) | **~$730.00 / month** | **100 - 300 ms** | **0 seconds** | High (K8s/Always-on) |
| **Hosted API** | Groq LPU Shared | **$0.05/1M in, $0.08/1M out** | **150 - 300 ms** | **0 seconds** | None |


## Short Evaluation Report

The short report is available in three forms:

- Markdown: `reports/report.md`
- Browser/printable HTML: `reports/evaluation_report.html`
- PDF: `reports/evaluation_report.pdf`

To regenerate:

```bash
python reports/generate_report.py
google-chrome --headless --disable-gpu --no-sandbox \
  --print-to-pdf=reports/evaluation_report.pdf reports/evaluation_report.html
```

## Tradeoffs Made

| Area | Decision | Tradeoff |
|---|---|---|
| OSS backend | Ollama first, HF fallback | Ollama is easy locally; HF fallback is slower but deployable on Spaces |
| Frontier backend | OpenAI-compatible client | Works with Groq/OpenAI-like APIs; provider-specific features are not deeply used |
| Memory | Rolling short-term buffer | Simple and reliable; no persistent personalization |
| Safety | Regex + refusal prompts | Transparent and low cost; weaker than dedicated moderation APIs |
| Evaluation | LLM-as-judge + custom prompts | Fast structured comparison; still subject to judge-model bias |
| UI | Streamlit | Very fast to build; less customizable than a full web app |

## Recommendations

- Use the **frontier model** when answer quality, factuality, and safety are most important.
- Use the **OSS model** when privacy, offline use, and low marginal cost matter more than peak quality.
- Use a **hybrid router** in production: route simple/private tasks to OSS and high-risk or complex tasks to the frontier model.

## What I Would Improve With More Time

1. Add a production moderation API in addition to regex filters.
2. Store raw per-prompt evaluation outputs for every run and add regression comparisons.
3. Expand the benchmark to include math, coding, multilingual, and long-context tasks.
4. Add human evaluation to validate the LLM-as-judge scores.
5. Add persistent memory with SQLite or Postgres for opt-in personalization.
6. Add async evaluation so both models are tested in parallel.
7. Add exact token accounting and cost tracking per request.
8. Add CI checks that run a small smoke-test evaluation on every change.

## Deployment

### Docker

All deployment files live under `deployment/`. The repo root `Dockerfile` is a symlink to `deployment/Dockerfile` (required by Hugging Face Spaces).

```bash
docker build -f deployment/Dockerfile -t ai-assistant-comparison .
docker run -p 7860:7860 --env-file .env ai-assistant-comparison
```

The image installs Ollama inside the container, pulls `qwen2.5:1.5b` on startup, starts `ollama serve`, and then launches the Streamlit comparison app on port `7860`.

For Hugging Face Spaces, set these secrets/variables:

```text
OPENAI_API_KEY=<your-groq-api-key>
OPENAI_BASE_URL=https://api.groq.com/openai/v1
OSS_BACKEND=ollama
OSS_MODEL=qwen2.5:1.5b
FRONTIER_BACKEND=openai
FRONTIER_MODEL=llama-3.1-8b-instant
```

### Hugging Face Spaces

Create a **Public** Space with the **Docker** SDK. Hugging Face reads the root `Dockerfile` (symlink to `deployment/Dockerfile`). Do not enable storage bucket or dev mode.

If the free Space is slow during first launch, wait for the startup log line:

```text
Pulling OSS model: qwen2.5:1.5b
```