Skip to content

Commit c27feb8

Browse files
committed
fix: allow mock LLM in different ways
1 parent f4d313a commit c27feb8

3 files changed

Lines changed: 170 additions & 25 deletions

File tree

README.md

Lines changed: 105 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,12 +118,29 @@ async def websocket_endpoint(websocket: WebSocket):
118118
- `Entity.LLM` and `Entity.ASSISTANT`: Message sender types to filter
119119
- `ChatDocument`: Message format with content and metadata
120120
- `DONE` constant: Must be imported from `langroid.utils.constants`
121+
- `MockLMConfig`: Allows testing without API keys using pattern-matched responses
121122

122123
3. **WebSocket Considerations**:
123124
- Need robust session management
124125
- Must handle reconnections gracefully
125126
- Async message monitoring is complex with Langroid's sync model
126127

128+
### MockLM Integration
129+
130+
One positive outcome is the successful integration of Langroid's `MockLMConfig`, which allows the application to run without any API keys. The mock responses are pattern-matched based on keywords in the user input:
131+
132+
```python
133+
MockLMConfig(
134+
response_dict={
135+
"hello": "Hello! I'm a mock Langroid agent...",
136+
"5 + 7|5+7": "5 + 7 equals 12!",
137+
"default": "That's interesting! As a mock agent..."
138+
}
139+
)
140+
```
141+
142+
This makes the project accessible for testing and development without requiring expensive API calls.
143+
127144
## Proposed Solutions (Not Yet Implemented)
128145

129146
### 1. Singleton Session Manager
@@ -176,14 +193,100 @@ class EventDrivenAgent(lr.ChatAgent):
176193

177194
### Setup
178195

179-
1. **Backend**:
196+
**No API Keys Required!** The application uses Langroid's `MockLMConfig` by default, allowing you to test the chat interface without any API keys.
197+
198+
#### Quick Start (Recommended)
199+
200+
The easiest way to run both frontend and backend together:
201+
202+
```bash
203+
# From the ui directory
204+
./run.sh
205+
```
206+
207+
This script:
208+
- Starts the backend server on port 8000
209+
- Starts the frontend development server on port 5173
210+
- Handles graceful shutdown when you press Ctrl+C
211+
212+
#### Manual Setup (Alternative)
213+
214+
If you prefer to run each component separately or if `run.sh` doesn't work on your system:
215+
216+
1. **Backend** (in one terminal):
180217
```bash
181218
cd backend
182219
pip install -r requirements.txt
183-
export OPENAI_API_KEY="your-key" # Optional
184220
python main.py
185221
```
186222

223+
2. **Frontend** (in another terminal):
224+
```bash
225+
cd frontend
226+
npm install
227+
npm run dev
228+
```
229+
230+
### Testing the Implementation
231+
232+
Once both servers are running:
233+
234+
1. **Open the UI**: Navigate to http://localhost:5173 in your browser
235+
2. **Test Basic Connectivity**:
236+
- The UI should load without errors
237+
- Check browser console for WebSocket connection messages
238+
- You should see "Connected to chat server" in the UI
239+
240+
3. **Test Message Flow**:
241+
- Type a message like "hello" and press Enter
242+
- The backend terminal should show the message was received
243+
- Watch for agent response generation in the backend logs
244+
- **Current Issue**: Agent responses are generated but don't appear in the UI
245+
246+
4. **Monitor the Backend Console**:
247+
- Look for `🤖 Using MockLM` or `🔑 Using OpenAI GPT` to confirm LLM mode
248+
- Watch for any error messages or stack traces
249+
- Agent responses will be visible in the console even if not in UI
250+
251+
5. **Debug WebSocket Connection**:
252+
- Browser DevTools → Network tab → WS filter
253+
- Look for the WebSocket connection to `ws://localhost:8000/ws`
254+
- Check the Messages tab to see data flow
255+
256+
### Running with MockLM
257+
258+
There are three ways to use the MockLM (no API keys needed):
259+
260+
1. **Default behavior**: Just run without any API keys
261+
```bash
262+
python main.py
263+
```
264+
265+
2. **Environment variable**: Force mock mode even with API keys
266+
```bash
267+
USE_MOCK_LLM=true python main.py
268+
```
269+
270+
3. **Command line flag**: Use the --mock flag
271+
```bash
272+
python main.py --mock
273+
```
274+
275+
The backend will print which mode it's using:
276+
- `🤖 Using MockLM - no API keys required!`
277+
- `🔑 Using OpenAI GPT (API key: sk-proj...)`
278+
279+
### Command Line Options
280+
281+
```bash
282+
python main.py --help
283+
284+
Options:
285+
--mock Force use of MockLM even if API keys are present
286+
--port PORT Port to run the server on (default: 8000)
287+
--host HOST Host to bind the server to (default: 0.0.0.0)
288+
```
289+
187290
2. **Frontend**:
188291
```bash
189292
cd frontend

backend/.env.example

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,28 @@
1-
# Copy this file to .env and fill in your values
1+
# Configuration for Langroid Chat UI Backend
2+
# Copy this file to .env and optionally add API keys
23

3-
# Optional: OpenAI API Key
4-
# If not provided, the chat will use a mock model for local testing
4+
# ===== MOCK MODE =====
5+
# Force the use of MockLM even if API keys are present
6+
# Set to true/1/yes to always use mock responses
7+
USE_MOCK_LLM=false
8+
9+
# ===== API KEYS (ALL OPTIONAL) =====
10+
# The application works WITHOUT any API keys using Langroid's MockLM
11+
# Add keys only if you want to use real LLM responses
12+
13+
# OpenAI API Key (for GPT-3.5/GPT-4)
14+
# Leave empty to use MockLM for local testing
515
OPENAI_API_KEY=
616

7-
# Optional: Other LLM providers
17+
# Anthropic API Key (for Claude)
18+
# Leave empty to use MockLM
819
ANTHROPIC_API_KEY=
920

10-
# Session secret for JWT tokens (generate a random string)
11-
# You can generate one with: python -c "import secrets; print(secrets.token_urlsafe(32))"
12-
SESSION_SECRET=your-secret-key-here
21+
# ===== SERVER CONFIGURATION =====
22+
# Host and port for the backend server
23+
HOST=0.0.0.0
24+
PORT=8000
25+
26+
# CORS allowed origins (comma-separated)
27+
# Add your frontend URL here for production
28+
ALLOWED_ORIGINS=http://localhost:5173,http://localhost:5174

backend/main.py

Lines changed: 42 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,34 @@ def __init__(self, session_id: str, websocket: WebSocket):
9595

9696
def _create_agent(self) -> UIChatAgent:
9797
"""Create agent with appropriate LLM config"""
98+
# Check if we should force mock mode
99+
use_mock = os.getenv("USE_MOCK_LLM", "").lower() in ["true", "1", "yes"]
98100
openai_key = os.getenv("OPENAI_API_KEY")
99101

100-
if openai_key:
102+
# Use mock if explicitly requested OR if no API key is present
103+
if use_mock or not openai_key:
104+
print("🤖 Using MockLM - no API keys required!")
105+
config = ChatAgentConfig(
106+
llm=MockLMConfig(
107+
response_dict={
108+
"hello": "Hello! I'm a mock Langroid agent running locally without any API keys. How can I help you test the chat interface?",
109+
"hi": "Hi there! I'm the mock assistant. Try asking me to calculate something, or ask about the weather!",
110+
"help": "I can help you test the chat UI! Try these:\n- Basic math (e.g., 'What is 5 + 7?')\n- Weather queries\n- General conversation\n- Or just chat about anything!",
111+
"weather": "I'm a mock model, so I can't check real weather, but let's pretend it's sunny with a chance of debugging! ☀️",
112+
"coding": "I love talking about code! This UI is built with React and FastAPI. What would you like to know?",
113+
"math|calculate|what is": "Let me pretend to calculate that for you... The answer is 42! (I'm a mock, so I always give the same answer 😄)",
114+
"5 + 7|5+7": "5 + 7 equals 12! (Even a mock can do simple math sometimes)",
115+
"bye|goodbye": "Goodbye! Thanks for testing the Langroid Chat UI!",
116+
"langroid": "Langroid is an amazing framework for building LLM-powered applications! This chat UI demonstrates how to integrate it with web technologies.",
117+
"test": "Testing, testing, 1-2-3! The WebSocket connection seems to be working well!",
118+
"default": "That's interesting! As a mock agent, I have limited responses, but I'm here to help test the chat interface. What else would you like to try?",
119+
},
120+
default_response="I'm a mock Langroid agent with limited responses. Try asking about math, weather, or just say hello!"
121+
),
122+
system_message="You are a mock assistant for testing the Langroid Chat UI without API keys."
123+
)
124+
else:
125+
print(f"🔑 Using OpenAI GPT (API key: {openai_key[:8]}...)")
101126
config = ChatAgentConfig(
102127
llm=OpenAIGPTConfig(
103128
chat_model="gpt-4o-mini",
@@ -106,19 +131,6 @@ def _create_agent(self) -> UIChatAgent:
106131
system_message="""You are a helpful AI assistant in a chat interface.
107132
Be concise and friendly in your responses. Remember our conversation history."""
108133
)
109-
else:
110-
config = ChatAgentConfig(
111-
llm=MockLMConfig(
112-
response_dict={
113-
"greeting": "Hello! I'm a mock assistant. I can help you test the chat interface locally without an API key.",
114-
"help": "I can respond to basic queries. Try asking me about the weather, coding, or just say hello!",
115-
"weather": "I'm a mock model, so I can't check real weather, but let's pretend it's sunny with a chance of debugging! ☀️",
116-
"coding": "I love talking about code! What programming language are you working with?",
117-
"default": "That's interesting! Tell me more about what you're working on.",
118-
}
119-
),
120-
system_message="You are a mock assistant for testing the chat UI."
121-
)
122134

123135
# Return our custom agent with queue-based input
124136
return UIChatAgent(config, self.input_queue, lambda: self.running)
@@ -287,15 +299,29 @@ async def websocket_endpoint(websocket: WebSocket):
287299

288300

289301
if __name__ == "__main__":
302+
import argparse
303+
304+
# Parse command line arguments
305+
parser = argparse.ArgumentParser(description="Langroid Chat UI Backend")
306+
parser.add_argument("--mock", action="store_true", help="Force use of MockLM even if API keys are present")
307+
parser.add_argument("--port", type=int, default=8000, help="Port to run the server on (default: 8000)")
308+
parser.add_argument("--host", default="0.0.0.0", help="Host to bind the server to (default: 0.0.0.0)")
309+
args = parser.parse_args()
310+
290311
# Load environment variables
291312
from dotenv import load_dotenv
292313
load_dotenv()
293314

315+
# Override USE_MOCK_LLM if --mock flag is used
316+
if args.mock:
317+
os.environ["USE_MOCK_LLM"] = "true"
318+
print("🎭 Mock mode enabled via CLI flag")
319+
294320
# Run the server
295321
uvicorn.run(
296322
"main:app",
297-
host="0.0.0.0",
298-
port=8000,
323+
host=args.host,
324+
port=args.port,
299325
reload=True,
300326
ws_ping_interval=30,
301327
ws_ping_timeout=30,

0 commit comments

Comments
 (0)