LokeshReddy001 commited on
Commit
b5cb7df
·
1 Parent(s): d8da231

Feature: Add Warm LLM button to pre-warm Modal inference container

Browse files
Files changed (2) hide show
  1. api.py +21 -0
  2. frontend/static/index.html +67 -1
api.py CHANGED
@@ -167,3 +167,24 @@ async def run_full_game(req: StartGameRequest):
167
  def get_default_prompt(grid_size: int = 20):
168
  from backend.agent.agent import Agent
169
  return {"default_prompt": Agent.get_default_system_prompt(grid_size=grid_size)}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
167
  def get_default_prompt(grid_size: int = 20):
168
  from backend.agent.agent import Agent
169
  return {"default_prompt": Agent.get_default_system_prompt(grid_size=grid_size)}
170
+
171
+
172
+ @app.post("/api/model/warm")
173
+ async def warm_model():
174
+ from backend.agent.llm_client import get_llm_client
175
+ try:
176
+ client = get_llm_client()
177
+ response = await client.chat.completions.create(
178
+ model="google/gemma-4-26B-A4B-it",
179
+ messages=[{"role": "user", "content": "ping"}],
180
+ max_tokens=5
181
+ )
182
+ if response and response.choices:
183
+ return {
184
+ "status": "success",
185
+ "message": "Model warmed up successfully!",
186
+ "response": response.choices[0].message.content
187
+ }
188
+ return {"status": "warning", "message": "No choices returned from model."}
189
+ except Exception as e:
190
+ return {"status": "error", "detail": str(e)}
frontend/static/index.html CHANGED
@@ -276,6 +276,39 @@ body {
276
  35%, 100% { transform: translateX(100%); }
277
  }
278
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
279
  /* ══════════════════════ GAME WRAPPER ══════════════════════ */
280
  #game {
281
  display: none; flex-direction: column; height: 100vh;
@@ -1114,7 +1147,10 @@ body {
1114
  <div class="lobby-prompt-wrap">
1115
  <textarea id="systemPromptInput" placeholder="Configure the agent system prompt..."></textarea>
1116
  </div>
1117
- <button class="lobby-deploy" id="startBtn">⚔ Deploy Agents</button>
 
 
 
1118
  </div>
1119
  </div>
1120
  </div>
@@ -2863,6 +2899,36 @@ function updateUI() {
2863
 
2864
  /* ═══════════ KEYBOARD ═══════════ */
2865
  document.getElementById('startBtn').addEventListener('click', startGame);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2866
  document.getElementById('stepBtn').addEventListener('click', step);
2867
  document.getElementById('autoBtn').addEventListener('click', auto);
2868
 
 
276
  35%, 100% { transform: translateX(100%); }
277
  }
278
 
279
+ .lobby-warm {
280
+ padding: 15px; border: 1px solid var(--border); border-radius: 12px;
281
+ background: var(--surface2);
282
+ color: var(--text-dim);
283
+ font-family: 'Space Grotesk', sans-serif;
284
+ font-size: 13px; font-weight: 700;
285
+ text-transform: uppercase; letter-spacing: 0.15em;
286
+ cursor: pointer; position: relative; overflow: hidden;
287
+ transition: all 0.3s cubic-bezier(0.4,0,0.2,1);
288
+ }
289
+ .lobby-warm:hover {
290
+ border-color: var(--blue);
291
+ color: var(--text);
292
+ box-shadow: 0 0 15px rgba(74,158,255,0.25);
293
+ transform: translateY(-2px);
294
+ }
295
+ .lobby-warm:disabled { opacity: 0.8; cursor: not-allowed; }
296
+ .lobby-warm.warming {
297
+ border-color: var(--accent);
298
+ color: var(--accent);
299
+ box-shadow: 0 0 15px var(--accent-glow);
300
+ animation: warmPulse 1.0s infinite alternate;
301
+ }
302
+ .lobby-warm.warmed {
303
+ border-color: var(--cyan);
304
+ color: var(--cyan);
305
+ box-shadow: 0 0 15px rgba(0,229,255,0.25);
306
+ }
307
+ @keyframes warmPulse {
308
+ 0% { opacity: 0.6; }
309
+ 100% { opacity: 1; }
310
+ }
311
+
312
  /* ══════════════════════ GAME WRAPPER ══════════════════════ */
313
  #game {
314
  display: none; flex-direction: column; height: 100vh;
 
1147
  <div class="lobby-prompt-wrap">
1148
  <textarea id="systemPromptInput" placeholder="Configure the agent system prompt..."></textarea>
1149
  </div>
1150
+ <div class="lobby-action-group" style="display: flex; gap: 12px; margin-top: 24px;">
1151
+ <button class="lobby-deploy" id="startBtn" style="margin-top: 0; flex: 2;">⚔ Deploy Agents</button>
1152
+ <button class="lobby-warm" id="warmBtn" style="flex: 1;" title="Pre-warm serverless inference models to prevent cold start latency">⚡ Warm LLM</button>
1153
+ </div>
1154
  </div>
1155
  </div>
1156
  </div>
 
2899
 
2900
  /* ═══════════ KEYBOARD ═══════════ */
2901
  document.getElementById('startBtn').addEventListener('click', startGame);
2902
+
2903
+ document.getElementById('warmBtn').addEventListener('click', async () => {
2904
+ const btn = document.getElementById('warmBtn');
2905
+ if (btn.disabled) return;
2906
+
2907
+ btn.disabled = true;
2908
+ btn.classList.add('warming');
2909
+ btn.classList.remove('warmed');
2910
+ btn.textContent = 'Warming...';
2911
+
2912
+ try {
2913
+ const res = await fetch('/api/model/warm', { method: 'POST' });
2914
+ const data = await res.json();
2915
+ if (data.status === 'success') {
2916
+ btn.classList.remove('warming');
2917
+ btn.classList.add('warmed');
2918
+ btn.textContent = 'Ready ✅';
2919
+ } else {
2920
+ btn.classList.remove('warming');
2921
+ btn.textContent = 'Error ❌';
2922
+ btn.disabled = false;
2923
+ console.error('Failed to warm model:', data.detail || data.message);
2924
+ }
2925
+ } catch (err) {
2926
+ btn.classList.remove('warming');
2927
+ btn.textContent = 'Error ❌';
2928
+ btn.disabled = false;
2929
+ console.error('Failed to warm model:', err);
2930
+ }
2931
+ });
2932
  document.getElementById('stepBtn').addEventListener('click', step);
2933
  document.getElementById('autoBtn').addEventListener('click', auto);
2934