mirror of
https://github.com/wahyd4/links.git
synced 2026-08-09 05:06:16 +10:00
- Add JbotApiConfig Django model (DB singleton) for storing Volcengine/OpenRouter credentials set via Settings UI - New endpoint GET/POST /api/jbot/api-config/ — tokens masked as *** on read; only non-placeholder values are updated on write - views.py: _get_cfg() helper reads DB first, env vars as fallback - Settings.jsx: new 🔑 API 配置 section with show/hide token fields for VOLC App ID, Access Token, OpenRouter API Key, LLM Model, ASR Resource - api.js: apiGetApiConfig() / apiSaveApiConfig() client helpers - Dockerfile: COPY jbot/ in both builder + production stages (was missing) - entrypoint.sh: runs migrate --noinput before gunicorn (auto-creates table) - k8s/manifest.yaml: remove jbot-credentials secretKeyRef (no Secret needed); keep LLM_MODEL/VOLC_TTS_VOICE/VOLC_ASR_RESOURCE as optional env defaults Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
93 lines
2.7 KiB
JavaScript
93 lines
2.7 KiB
JavaScript
// Django backend at /api/jbot/ — no separate Node server needed
|
|
const BASE = '/api/jbot';
|
|
|
|
export async function apiConfig() {
|
|
const r = await fetch(`${BASE}/config/`);
|
|
return r.json();
|
|
}
|
|
|
|
// Send conversation history + optional long-term memories + settings, returns { text }
|
|
export async function apiChat(messages, memories = [], settings = {}) {
|
|
const r = await fetch(`${BASE}/chat/`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ messages, memories, settings }),
|
|
});
|
|
if (!r.ok) {
|
|
const e = await r.json().catch(() => ({}));
|
|
throw new Error(e.error || `HTTP ${r.status}`);
|
|
}
|
|
return r.json();
|
|
}
|
|
|
|
// Send base64 audio blob, returns { text }
|
|
export async function apiAsr(audioBase64, format = 'audio/webm') {
|
|
const r = await fetch(`${BASE}/asr/`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ audioBase64, format }),
|
|
});
|
|
if (!r.ok) {
|
|
const e = await r.json().catch(() => ({}));
|
|
throw new Error(e.error || `HTTP ${r.status}`);
|
|
}
|
|
return r.json();
|
|
}
|
|
|
|
// Send text + optional voice override, returns { audioBase64, format }
|
|
export async function apiTts(text, voice) {
|
|
const r = await fetch(`${BASE}/tts/`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ text, ...(voice ? { voice } : {}) }),
|
|
});
|
|
if (!r.ok) {
|
|
const e = await r.json().catch(() => ({}));
|
|
throw new Error(e.error || `HTTP ${r.status}`);
|
|
}
|
|
return r.json();
|
|
}
|
|
|
|
// Extract memorable facts from one conversation exchange.
|
|
// Returns string[] — never throws (returns [] on any error).
|
|
export async function apiExtractMemory(userText, botText) {
|
|
try {
|
|
const r = await fetch(`${BASE}/memory/extract/`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ userText, botText }),
|
|
});
|
|
if (!r.ok) return [];
|
|
const { facts = [] } = await r.json();
|
|
return facts;
|
|
} catch {
|
|
return [];
|
|
}
|
|
}
|
|
|
|
// Read JBOT API credentials/settings from server (tokens are masked as '***')
|
|
export async function apiGetApiConfig() {
|
|
try {
|
|
const r = await fetch(`${BASE}/api-config/`);
|
|
if (!r.ok) return {};
|
|
return r.json();
|
|
} catch {
|
|
return {};
|
|
}
|
|
}
|
|
|
|
// Save JBOT API credentials/settings to server DB
|
|
// Pass '***' for secret fields to leave them unchanged
|
|
export async function apiSaveApiConfig(config) {
|
|
const r = await fetch(`${BASE}/api-config/`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(config),
|
|
});
|
|
if (!r.ok) {
|
|
const e = await r.json().catch(() => ({}));
|
|
throw new Error(e.error || `HTTP ${r.status}`);
|
|
}
|
|
return r.json();
|
|
}
|