Bring Your Own LLM: per-user LLM config with system default fallback

This commit is contained in:
2026-04-13 14:13:02 +00:00
parent f57dd9621f
commit d28143ec00
3 changed files with 143 additions and 0 deletions
+66
View File
@@ -106,6 +106,7 @@ body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;b
<div class="dot"></div>
<span id="agent-count">0 agents</span>
<span class="user-name" id="user-display"></span>
<button class="small-btn" onclick="showLLMSettings()">LLM</button>
<button class="small-btn" id="admin-btn" style="display:none" onclick="location.href='/admin'">Admin</button>
<button class="small-btn" onclick="logout()">Logout</button>
</div>
@@ -340,6 +341,71 @@ async function enableAgent(catalogId,name){
if(res.ok){closeModal();refresh()}
}
// --- LLM Settings ---
async function showLLMSettings(){
const res=await fetch(API+'/api/me/llm');
if(res.status===401){location.href='/login';return}
const llm=await res.json();
document.getElementById('modal-content').innerHTML=`
<button class="close-btn" onclick="closeModal()">&times;</button>
<h2>LLM Provider</h2>
<p style="color:var(--text-dim);margin-bottom:1.5rem">Bring your own LLM API key, or leave blank to use the system default.</p>
<div class="config-section">
<h3>Your LLM Configuration</h3>
<div class="config-grid">
<div class="config-field">
<label>Provider</label>
<select id="llm-provider">
<option value="" ${!llm.provider_type?'selected':''}>None (use system default)</option>
<option value="anthropic" ${llm.provider_type==='anthropic'?'selected':''}>Anthropic (Claude)</option>
<option value="openai" ${llm.provider_type==='openai'?'selected':''}>OpenAI</option>
<option value="litellm" ${llm.provider_type==='litellm'?'selected':''}>LiteLLM</option>
<option value="ollama" ${llm.provider_type==='ollama'?'selected':''}>Ollama</option>
</select>
</div>
<div class="config-field">
<label>Model</label>
<input type="text" id="llm-model" value="${llm.default_model||''}" placeholder="claude-sonnet-4-5-20250514">
</div>
<div class="config-field">
<label>API URL</label>
<input type="text" id="llm-url" value="${llm.api_url||''}" placeholder="https://api.anthropic.com (optional)">
</div>
<div class="config-field">
<label>API Key</label>
<input type="password" id="llm-key" value="${llm.api_key||''}" placeholder="sk-...">
</div>
</div>
<div class="config-actions">
<button class="btn-save" onclick="saveLLM()">Save</button>
<button class="btn-secondary" onclick="removeLLM()">Remove (use system default)</button>
<span class="save-msg" id="llm-save-msg"></span>
</div>
<p style="color:var(--text-dim);font-size:.8rem;margin-top:1rem">
${llm.configured?'Status: <span style="color:var(--green)">Configured</span> ('+llm.provider_type+')':'Status: Using system default'}
</p>
</div>`;
document.getElementById('modal-overlay').classList.add('open');
}
async function saveLLM(){
const body={
provider_type:document.getElementById('llm-provider').value,
api_url:document.getElementById('llm-url').value,
api_key:document.getElementById('llm-key').value,
default_model:document.getElementById('llm-model').value,
};
const res=await fetch(API+'/api/me/llm',{method:'PUT',headers:{'Content-Type':'application/json'},body:JSON.stringify(body)});
const msg=document.getElementById('llm-save-msg');
if(res.ok){msg.textContent='Saved';msg.style.color='var(--green)';setTimeout(()=>msg.textContent='',2000)}
else{msg.textContent='Error';msg.style.color='var(--red)'}
}
async function removeLLM(){
await fetch(API+'/api/me/llm',{method:'DELETE'});
showLLMSettings();
}
// --- Bridge ---
async function loadBridge(){
try{