MikelWL commited on
Commit
ed0db0d
Β·
1 Parent(s): e3892d4

feat: Added openrouter backend

Browse files
.env.example CHANGED
@@ -2,11 +2,27 @@
2
  API_HOST=0.0.0.0
3
  API_PORT=8000
4
 
5
- # LLM backend configuration
6
- LLM_BACKEND=ollama
7
- LLM_HOST=http://localhost:11434
8
- LLM_MODEL=llama3.2:latest
 
 
 
 
 
 
 
 
 
 
9
  LLM_TIMEOUT=120
 
 
 
 
 
 
10
 
11
  # Frontend configuration
12
  FRONTEND_BACKEND_BASE_URL=http://localhost:8000
 
2
  API_HOST=0.0.0.0
3
  API_PORT=8000
4
 
5
+ # If you want to switch from hosted to local, comment out
6
+ # the hosted lines and uncomment the local ones
7
+
8
+ # Hosted LLM backend configuration
9
+ LLM_BACKEND=openrouter
10
+ LLM_HOST=https://openrouter.ai/api/v1
11
+ LLM_MODEL={choose_a_free_one_from_openrouter}
12
+ LLM_API_KEY={your_api_key}
13
+
14
+ # Local LLM backend configuration
15
+ # LLM_BACKEND=ollama
16
+ # LLM_HOST=http://localhost:11434
17
+ # LLM_MODEL=llama3.2:latest
18
+
19
  LLM_TIMEOUT=120
20
+ LLM_MAX_RETRIES=3
21
+ LLM_RETRY_DELAY=1.0
22
+
23
+ # Optional (required when LLM_BACKEND=openrouter)
24
+ LLM_SITE_URL=http://localhost:7860
25
+ LLM_APP_NAME=AI_Survey_Simulator
26
 
27
  # Frontend configuration
28
  FRONTEND_BACKEND_BASE_URL=http://localhost:8000
README.md CHANGED
@@ -1,156 +1,102 @@
1
- # AI Survey Simulator (Local Guide)
2
 
3
- Welcome! This guide walks you through running the AI Survey Simulator locally so you can evaluate the interviewer/patient conversation flow without digging into the implementation details.
4
-
5
- If you are looking for architecture deep dives or change history, head to `docs/` where all developer-facing material now lives.
6
-
7
- ---
8
-
9
- ## What You Get
10
-
11
- - A Gradio web interface to monitor and control AI-to-AI healthcare survey conversations
12
- - A FastAPI backend that orchestrates personas, manages the conversation state, and serves WebSocket updates
13
- - Out-of-the-box personas for a surveyor and multiple patient profiles stored in `data/`
14
 
15
  ---
16
 
17
- ## Prerequisites
18
 
19
- 1. **Python 3.9+** installed (`python --version`)
20
- 2. **Pip** available (`pip --version`)
21
- 3. **Ollama** running locally with an accessible model (e.g., `llama3.2:latest`), since the simulator calls the LLM through Ollama by default
22
- - Install instructions: <https://ollama.ai>
23
- - Pull a model: `ollama pull llama3.2:latest`
24
- - Verify: `ollama list`
25
-
26
- > ℹ️ We are actively planning support for hosted LLM providers. When that lands you will be able to configure the app via environment variables instead of relying on a local Ollama instance.
27
 
28
  ---
29
 
30
- ## 1. Configure Environment Variables
31
 
32
- Duplicate the sample configuration and adjust if necessary:
33
 
34
- ```bash
35
- cp .env.example .env
 
 
 
36
  ```
37
 
38
- Key values:
39
-
40
- - `LLM_HOST` / `LLM_MODEL` β€” where the backend reaches your Ollama model
41
- - `FRONTEND_BACKEND_BASE_URL` β€” the FastAPI base URL Gradio should call
42
- - `FRONTEND_WEBSOCKET_URL` β€” the WebSocket endpoint prefix (without conversation id)
 
 
 
 
43
 
44
- You can accept the defaults for a local run. Update them later if you move the backend or change models.
45
 
46
  ---
47
 
48
- ## 2. Set Up the Python Environment
49
-
50
- ```bash
51
- git clone <repository-url>
52
- cd ConversationAI
53
-
54
- # (optional but recommended)
55
- python -m venv .venv
56
- source .venv/bin/activate # Windows: .venv\Scripts\activate
57
 
 
 
 
58
  pip install -r requirements.txt
59
  ```
60
 
61
  ---
62
 
63
- ## 3. Start the Backend (FastAPI)
64
 
65
- ```bash
66
- cd backend
67
- uvicorn api.main:app --reload --host 0.0.0.0 --port 8000
68
  ```
69
-
70
- Keep this terminal running. The backend exposes REST endpoints under `http://localhost:8000` and a WebSocket endpoint the UI listens to.
71
-
72
- ---
73
-
74
- ## 4. Launch the Gradio Frontend
75
-
76
- In a new terminal (activate the virtual environment again if you created one):
77
-
78
- ```bash
79
- cd frontend
80
- python gradio_app.py
81
  ```
 
82
 
83
- Gradio starts on <http://localhost:7860>. Open that page in your browser.
 
 
 
84
 
85
- ---
86
-
87
- ## 5. Run a Conversation
88
-
89
- 1. Click **β€œStart Conversation”** β€” the app will connect to the backend automatically and begin the AI interview flow. Messages appear in the β€œLive AI Conversation” panel.
90
- 2. Watch the conversation update automatically (the UI polls once per second).
91
- 3. Click **β€œStop Conversation”** when you are done.
92
-
93
- If the backend or Ollama becomes unreachable, the status box will show an error message so you know where to look first.
94
 
95
  ---
96
 
97
- ## Personas
98
-
99
- - Surveyor profiles live in `data/surveyor_personas.yaml`
100
- - Patient profiles live in `data/patient_personas.yaml`
101
 
102
- To tweak a persona for experimentation:
 
 
103
 
104
- 1. Edit the YAML entry (name, tone, system prompt, etc.)
105
- 2. Restart the backend so it reloads the definitions
106
-
107
- We are working on UI controls to swap personas without editing filesβ€”stay tuned.
108
 
109
  ---
110
 
111
- ## Advanced Configuration
112
-
113
- - Change the log verbosity by setting `LOG_LEVEL=DEBUG` in `.env`
114
- - Point to a different LLM host/model using the `LLM_*` variables
115
- - When we introduce hosted-model support, the same `.env` file will control which backend is used without code edits
116
 
117
- ---
 
118
 
119
- ## Quick Start Script
120
-
121
- Prefer a single command? Run:
122
-
123
- ```bash
124
- ./run_local.sh
125
- ```
126
-
127
- The script will:
128
-
129
- - Load environment variables from `.env`
130
- - Start `ollama serve` (if it is not already running)
131
- - Launch the FastAPI backend and Gradio frontend in the background
132
-
133
- Press `Ctrl+C` in that terminal to shut everything down cleanly.
134
- If you want to watch live logs, run the backend/frontend commands manually in separate terminals instead of using this helper.
135
 
136
  ---
137
 
138
- ## Helpful Scripts
139
 
140
- - `run_local.sh` β€” start/stop the full local stack with one command
141
- - `dev_setup.sh` β€” (planned) install dependencies and verify prerequisites
142
- - Smoke tests with mocked LLM responses β€” (planned)
 
 
 
143
 
144
  ---
145
 
146
- ## Need Implementation Details?
147
-
148
- For deeper implementation notes, visit the developer docs:
149
-
150
- - `docs/overview.md`
151
- - `docs/development.md`
152
- - `docs/roadmap.md`
153
-
154
- ---
155
 
156
- Happy testing! If you run into issues, capture the console output from both backend and frontend terminalsβ€”it usually reveals configuration or network problems quickly.
 
 
 
1
+ # AI Survey Simulator – Quick Start
2
 
3
+ Minimal instructions for running the simulator either with a local Ollama model or with a hosted OpenRouter model.
 
 
 
 
 
 
 
 
 
 
4
 
5
  ---
6
 
7
+ ## Requirements
8
 
9
+ - Python 3.9+
10
+ - Pip
11
+ - Optional for local mode: [Ollama](https://ollama.ai) with a pulled model (e.g., `ollama pull llama3.2:latest`)
12
+ - Optional for hosted mode: OpenRouter account + API key
 
 
 
 
13
 
14
  ---
15
 
16
+ ## 1. Create `.env`
17
 
18
+ Copy `.env.example` to `.env` and choose one of the following blocks.
19
 
20
+ **Local (Ollama)**
21
+ ```
22
+ LLM_BACKEND=ollama
23
+ LLM_HOST=http://localhost:11434
24
+ LLM_MODEL=llama3.2:latest
25
  ```
26
 
27
+ **Hosted (OpenRouter)**
28
+ ```
29
+ LLM_BACKEND=openrouter
30
+ LLM_HOST=https://openrouter.ai/api/v1
31
+ LLM_MODEL=anthropic/claude-3-haiku:beta # pick any model
32
+ LLM_API_KEY=sk-or-...
33
+ LLM_SITE_URL=http://localhost:7860
34
+ LLM_APP_NAME=AI_Survey_Simulator
35
+ ```
36
 
37
+ Other environment values (ports, websocket URL, log level) are already set in `.env.example`.
38
 
39
  ---
40
 
41
+ ## 2. Install Python Dependencies
 
 
 
 
 
 
 
 
42
 
43
+ ```
44
+ python -m venv .venv # optional
45
+ source .venv/bin/activate # Windows: .venv\Scripts\activate
46
  pip install -r requirements.txt
47
  ```
48
 
49
  ---
50
 
51
+ ## 3. Run the Stack
52
 
53
+ ### Option A – Single Command
 
 
54
  ```
55
+ ./run_local.sh
 
 
 
 
 
 
 
 
 
 
 
56
  ```
57
+ Reads `.env`, starts Ollama if needed, launches FastAPI + Gradio, and keeps them running until `Ctrl+C`.
58
 
59
+ ### Option B – Manual Terminals
60
+ 1. *(Only if LLM_BACKEND=ollama)* `ollama serve`
61
+ 2. `cd backend && uvicorn api.main:app --host 0.0.0.0 --port 8000`
62
+ 3. `cd frontend && python gradio_app.py`
63
 
64
+ Backend listens on `http://localhost:8000`, Gradio on `http://localhost:7860`.
 
 
 
 
 
 
 
 
65
 
66
  ---
67
 
68
+ ## 4. Use the App
 
 
 
69
 
70
+ 1. Open the Gradio URL.
71
+ 2. Click **Start Conversation**. The UI auto-connects to the backend and refreshes once per second.
72
+ 3. Click **Stop Conversation** when finished.
73
 
74
+ Any connection errors or LLM issues appear in the status panel.
 
 
 
75
 
76
  ---
77
 
78
+ ## 5. Personas
 
 
 
 
79
 
80
+ - Surveyor definitions: `data/surveyor_personas.yaml`
81
+ - Patient definitions: `data/patient_personas.yaml`
82
 
83
+ Edit the YAML, then restart the backend to apply changes.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
84
 
85
  ---
86
 
87
+ ## 6. Troubleshooting
88
 
89
+ | Issue | Resolution |
90
+ | --- | --- |
91
+ | β€œTemporary failure in name resolution” (OpenRouter) | Launch backend from an environment that can resolve `openrouter.ai`; ensure proxies/DNS settings match the working terminal. |
92
+ | β€œAll connection attempts failed” | Backend cannot reach the LLM. Verify `.env`, restart backend, check console logs. |
93
+ | β€œModel not found” (Ollama) | Pull the model with `ollama pull <model>` and restart backend. |
94
+ | UI stays empty | Backend not running or `.env` mismatch. Restart both processes. |
95
 
96
  ---
97
 
98
+ ## 7. Reference Docs
 
 
 
 
 
 
 
 
99
 
100
+ - `docs/overview.md` – architecture summary
101
+ - `docs/development.md` – environment tips and backend switching
102
+ - `docs/roadmap.md` – upcoming work
backend/api/conversation_service.py CHANGED
@@ -19,7 +19,7 @@ Example:
19
  import asyncio
20
  import logging
21
  from datetime import datetime
22
- from typing import Dict, Optional
23
  from dataclasses import dataclass
24
  from enum import Enum
25
  import sys
@@ -141,11 +141,15 @@ class ConversationService:
141
  await self._send_status_update(conversation_id, ConversationStatus.STARTING)
142
 
143
  # Create and start conversation manager
 
 
144
  manager = ConversationManager(
145
  surveyor_persona=surveyor_persona,
146
  patient_persona=patient_persona,
147
  host=resolved_host,
148
- model=resolved_model
 
 
149
  )
150
 
151
  # Start conversation streaming task
@@ -302,6 +306,23 @@ class ConversationService:
302
  conv_info.status = ConversationStatus.COMPLETED
303
  await self._send_status_update(conversation_id, ConversationStatus.COMPLETED)
304
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
305
  async def _send_status_update(self, conversation_id: str, status: ConversationStatus):
306
  """Send conversation status update to clients.
307
 
 
19
  import asyncio
20
  import logging
21
  from datetime import datetime
22
+ from typing import Dict, Optional, Any
23
  from dataclasses import dataclass
24
  from enum import Enum
25
  import sys
 
141
  await self._send_status_update(conversation_id, ConversationStatus.STARTING)
142
 
143
  # Create and start conversation manager
144
+ llm_parameters = self._build_llm_parameters()
145
+
146
  manager = ConversationManager(
147
  surveyor_persona=surveyor_persona,
148
  patient_persona=patient_persona,
149
  host=resolved_host,
150
+ model=resolved_model,
151
+ llm_backend=self.settings.llm.backend,
152
+ llm_parameters=llm_parameters
153
  )
154
 
155
  # Start conversation streaming task
 
306
  conv_info.status = ConversationStatus.COMPLETED
307
  await self._send_status_update(conversation_id, ConversationStatus.COMPLETED)
308
 
309
+ def _build_llm_parameters(self) -> Dict[str, Any]:
310
+ """Prepare keyword arguments for LLM client creation."""
311
+ params: Dict[str, Any] = {
312
+ "timeout": self.settings.llm.timeout,
313
+ "max_retries": self.settings.llm.max_retries,
314
+ "retry_delay": self.settings.llm.retry_delay,
315
+ }
316
+
317
+ if self.settings.llm.api_key:
318
+ params["api_key"] = self.settings.llm.api_key
319
+ if self.settings.llm.site_url:
320
+ params["site_url"] = self.settings.llm.site_url
321
+ if self.settings.llm.app_name:
322
+ params["app_name"] = self.settings.llm.app_name
323
+
324
+ return params
325
+
326
  async def _send_status_update(self, conversation_id: str, status: ConversationStatus):
327
  """Send conversation status update to clients.
328
 
backend/core/conversation_manager.py CHANGED
@@ -20,7 +20,7 @@ Example:
20
  """
21
 
22
  from enum import Enum
23
- from typing import AsyncGenerator, Dict, List, Optional
24
  import asyncio
25
  from datetime import datetime
26
  import sys
@@ -29,7 +29,7 @@ from pathlib import Path
29
  # Add backend to path for imports
30
  sys.path.insert(0, str(Path(__file__).parent.parent))
31
 
32
- from core.llm_client import OllamaClient
33
  from core.persona_system import PersonaSystem
34
 
35
 
@@ -62,7 +62,9 @@ class ConversationManager:
62
  surveyor_persona: dict = None,
63
  patient_persona: dict = None,
64
  host: str = "http://localhost:11434",
65
- model: str = "llama3.2:latest"):
 
 
66
  """Initialize conversation manager with personas.
67
 
68
  Args:
@@ -72,10 +74,15 @@ class ConversationManager:
72
  patient_persona: Pre-loaded patient persona dict
73
  host: Ollama server host
74
  model: LLM model to use
 
 
75
  """
76
  # Initialize systems
77
  self.persona_system = PersonaSystem()
78
- self.client = OllamaClient(host=host, model=model)
 
 
 
79
 
80
  # Load personas
81
  if surveyor_persona:
@@ -331,4 +338,4 @@ class ConversationManager:
331
  async def close(self):
332
  """Clean up resources."""
333
  if hasattr(self, 'client') and self.client:
334
- await self.client.close()
 
20
  """
21
 
22
  from enum import Enum
23
+ from typing import AsyncGenerator, Dict, List, Optional, Any
24
  import asyncio
25
  from datetime import datetime
26
  import sys
 
29
  # Add backend to path for imports
30
  sys.path.insert(0, str(Path(__file__).parent.parent))
31
 
32
+ from core.llm_client import create_llm_client
33
  from core.persona_system import PersonaSystem
34
 
35
 
 
62
  surveyor_persona: dict = None,
63
  patient_persona: dict = None,
64
  host: str = "http://localhost:11434",
65
+ model: str = "llama3.2:latest",
66
+ llm_backend: str = "ollama",
67
+ llm_parameters: Optional[Dict[str, Any]] = None):
68
  """Initialize conversation manager with personas.
69
 
70
  Args:
 
74
  patient_persona: Pre-loaded patient persona dict
75
  host: Ollama server host
76
  model: LLM model to use
77
+ llm_backend: Which LLM backend implementation to use
78
+ llm_parameters: Additional keyword arguments for the LLM client
79
  """
80
  # Initialize systems
81
  self.persona_system = PersonaSystem()
82
+ client_kwargs = {"host": host, "model": model}
83
+ if llm_parameters:
84
+ client_kwargs.update(llm_parameters)
85
+ self.client = create_llm_client(llm_backend, **client_kwargs)
86
 
87
  # Load personas
88
  if surveyor_persona:
 
338
  async def close(self):
339
  """Clean up resources."""
340
  if hasattr(self, 'client') and self.client:
341
+ await self.client.close()
backend/core/llm_client.py CHANGED
@@ -331,6 +331,64 @@ class VLLMClient(LLMClient):
331
  raise
332
 
333
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
334
  def create_llm_client(backend: str, **kwargs) -> LLMClient:
335
  """Factory function to create appropriate LLM client.
336
 
@@ -341,10 +399,14 @@ def create_llm_client(backend: str, **kwargs) -> LLMClient:
341
  Returns:
342
  Configured LLM client instance
343
  """
344
- if backend.lower() == "ollama":
 
 
345
  return OllamaClient(**kwargs)
346
- elif backend.lower() == "vllm":
347
  return VLLMClient(**kwargs)
 
 
348
  else:
349
  raise ValueError(f"Unknown LLM backend: {backend}")
350
 
@@ -370,7 +432,10 @@ def create_llm_client_from_config(config_path: Optional[str] = None,
370
  "model": "llama3.2:latest",
371
  "timeout": 120,
372
  "max_retries": 3,
373
- "retry_delay": 1.0
 
 
 
374
  }
375
 
376
  # Load from config file if provided
@@ -406,7 +471,10 @@ def create_llm_client_from_config(config_path: Optional[str] = None,
406
  "model": f"{env_prefix}MODEL",
407
  "timeout": f"{env_prefix}TIMEOUT",
408
  "max_retries": f"{env_prefix}MAX_RETRIES",
409
- "retry_delay": f"{env_prefix}RETRY_DELAY"
 
 
 
410
  }
411
 
412
  for config_key, env_var in env_vars.items():
 
331
  raise
332
 
333
 
334
+ class OpenRouterClient(LLMClient):
335
+ """Client implementation for OpenRouter-hosted models."""
336
+
337
+ def __init__(self,
338
+ host: str,
339
+ model: str,
340
+ api_key: str,
341
+ site_url: Optional[str] = None,
342
+ app_name: Optional[str] = None,
343
+ **kwargs):
344
+ if not api_key:
345
+ raise ValueError("OpenRouterClient requires an API key")
346
+
347
+ super().__init__(host=host.rstrip("/"), model=model, **kwargs)
348
+ self.headers = {
349
+ "Authorization": f"Bearer {api_key}",
350
+ "Content-Type": "application/json",
351
+ }
352
+ if site_url:
353
+ self.headers["HTTP-Referer"] = site_url
354
+ if app_name:
355
+ self.headers["X-Title"] = app_name
356
+
357
+ async def generate(self,
358
+ prompt: str,
359
+ system_prompt: Optional[str] = None,
360
+ **kwargs) -> str:
361
+ """Generate response using OpenRouter's Chat Completions API."""
362
+
363
+ messages = []
364
+ if system_prompt:
365
+ messages.append({"role": "system", "content": system_prompt})
366
+ messages.append({"role": "user", "content": prompt})
367
+
368
+ payload = {
369
+ "model": self.model,
370
+ "messages": messages,
371
+ "stream": False,
372
+ **kwargs
373
+ }
374
+
375
+ async def _make_request():
376
+ response = await self.client.post(
377
+ f"{self.host}/chat/completions",
378
+ json=payload,
379
+ headers=self.headers
380
+ )
381
+ response.raise_for_status()
382
+ data = response.json()
383
+
384
+ usage = data.get("usage", {})
385
+ self.total_tokens += usage.get("total_tokens", 0)
386
+
387
+ return data["choices"][0]["message"]["content"]
388
+
389
+ return await self._retry_request(_make_request)
390
+
391
+
392
  def create_llm_client(backend: str, **kwargs) -> LLMClient:
393
  """Factory function to create appropriate LLM client.
394
 
 
399
  Returns:
400
  Configured LLM client instance
401
  """
402
+ backend_name = backend.lower()
403
+
404
+ if backend_name == "ollama":
405
  return OllamaClient(**kwargs)
406
+ elif backend_name == "vllm":
407
  return VLLMClient(**kwargs)
408
+ elif backend_name in ("openrouter", "open_router"):
409
+ return OpenRouterClient(**kwargs)
410
  else:
411
  raise ValueError(f"Unknown LLM backend: {backend}")
412
 
 
432
  "model": "llama3.2:latest",
433
  "timeout": 120,
434
  "max_retries": 3,
435
+ "retry_delay": 1.0,
436
+ "api_key": None,
437
+ "site_url": None,
438
+ "app_name": None,
439
  }
440
 
441
  # Load from config file if provided
 
471
  "model": f"{env_prefix}MODEL",
472
  "timeout": f"{env_prefix}TIMEOUT",
473
  "max_retries": f"{env_prefix}MAX_RETRIES",
474
+ "retry_delay": f"{env_prefix}RETRY_DELAY",
475
+ "api_key": f"{env_prefix}API_KEY",
476
+ "site_url": f"{env_prefix}SITE_URL",
477
+ "app_name": f"{env_prefix}APP_NAME",
478
  }
479
 
480
  for config_key, env_var in env_vars.items():
config/settings.py CHANGED
@@ -5,6 +5,7 @@ override them through a `.env` file or process environment variables.
5
  """
6
 
7
  from functools import lru_cache
 
8
  from pydantic_settings import BaseSettings, SettingsConfigDict
9
 
10
 
@@ -30,6 +31,11 @@ class LLMSettings(BaseSettings):
30
  host: str = "http://localhost:11434"
31
  model: str = "llama3.2:latest"
32
  timeout: int = 120
 
 
 
 
 
33
 
34
  model_config = SettingsConfigDict(
35
  env_prefix="LLM_",
 
5
  """
6
 
7
  from functools import lru_cache
8
+ from typing import Optional
9
  from pydantic_settings import BaseSettings, SettingsConfigDict
10
 
11
 
 
31
  host: str = "http://localhost:11434"
32
  model: str = "llama3.2:latest"
33
  timeout: int = 120
34
+ max_retries: int = 3
35
+ retry_delay: float = 1.0
36
+ api_key: Optional[str] = None
37
+ site_url: Optional[str] = None
38
+ app_name: Optional[str] = None
39
 
40
  model_config = SettingsConfigDict(
41
  env_prefix="LLM_",
docs/development.md CHANGED
@@ -15,7 +15,9 @@ pip install -r requirements.txt
15
 
16
  Key environment variables (see `.env.example`):
17
 
18
- - `LLM_HOST` / `LLM_MODEL` β€” target model endpoint
 
 
19
  - `FRONTEND_BACKEND_BASE_URL` and `FRONTEND_WEBSOCKET_URL` β€” how the UI talks to FastAPI
20
  - `LOG_LEVEL` β€” INFO by default
21
 
@@ -25,7 +27,7 @@ Key environment variables (see `.env.example`):
25
  ```bash
26
  ./run_local.sh
27
  ```
28
- - Starts `ollama serve` (if not already running)
29
  - Launches FastAPI backend and Gradio frontend in the background
30
  - Press `Ctrl+C` to stop all three processes
31
 
 
15
 
16
  Key environment variables (see `.env.example`):
17
 
18
+ - `LLM_BACKEND` β€” `ollama` (local default) or `openrouter`
19
+ - `LLM_HOST` / `LLM_MODEL` β€” target endpoint & model ID
20
+ - `LLM_API_KEY`, `LLM_SITE_URL`, `LLM_APP_NAME` β€” required when using OpenRouter
21
  - `FRONTEND_BACKEND_BASE_URL` and `FRONTEND_WEBSOCKET_URL` β€” how the UI talks to FastAPI
22
  - `LOG_LEVEL` β€” INFO by default
23
 
 
27
  ```bash
28
  ./run_local.sh
29
  ```
30
+ - Starts `ollama serve` (if not already running) β€” this mode expects `LLM_BACKEND=ollama`
31
  - Launches FastAPI backend and Gradio frontend in the background
32
  - Press `Ctrl+C` to stop all three processes
33
 
frontend/gradio_app.py CHANGED
@@ -205,7 +205,7 @@ def get_message_display() -> str:
205
  """Get formatted message display."""
206
  if not all_messages:
207
  if conversation_active:
208
- return "πŸ”„ Conversation started. AI responses will appear here...\n\nClick 'Refresh' to check for new messages."
209
  else:
210
  return "No messages yet. Click 'Start Conversation' to begin!"
211
 
@@ -309,9 +309,9 @@ with gr.Blocks(title="πŸ₯ AI Survey Simulator v2") as app:
309
  gr.HTML("""
310
  <div style="margin-top: 15px; padding: 10px; background-color: #fff3cd; border-radius: 8px; font-size: 12px;">
311
  <strong>πŸ”§ Requirements:</strong><br>
312
- β€’ Ollama server running<br>
313
  β€’ FastAPI backend on port 8000<br>
314
- β€’ llama3.2:latest model available
 
315
  </div>
316
  """)
317
 
 
205
  """Get formatted message display."""
206
  if not all_messages:
207
  if conversation_active:
208
+ return "πŸ”„ Conversation started. AI responses will appear here...\n\nUpdates arrive automatically every second."
209
  else:
210
  return "No messages yet. Click 'Start Conversation' to begin!"
211
 
 
309
  gr.HTML("""
310
  <div style="margin-top: 15px; padding: 10px; background-color: #fff3cd; border-radius: 8px; font-size: 12px;">
311
  <strong>πŸ”§ Requirements:</strong><br>
 
312
  β€’ FastAPI backend on port 8000<br>
313
+ β€’ LLM backend reachable (local Ollama or OpenRouter via API key)<br>
314
+ β€’ Update <code>.env</code> with the model/backend you plan to use
315
  </div>
316
  """)
317