extract assistant frontend api client

This commit is contained in:
Daniel 2026-05-08 00:58:44 +02:00
parent 7b04546a6e
commit 6cfad65639
2 changed files with 100 additions and 43 deletions

View file

@ -0,0 +1,74 @@
function authHeaders() {
return window.getAuthHeaders ? window.getAuthHeaders() : { 'Content-Type': 'application/json' };
}
function parseJsonWithStatus(response) {
return response.json().catch(function () { return {}; }).then(function(data) {
data._status = response.status;
return data;
});
}
export function fetchAssistantStatus() {
return fetch('/api/clinical-assistant/status', { headers: authHeaders(), credentials: 'same-origin' })
.then(function(r) { return r.json(); });
}
export function fetchAssistantExamples() {
return fetch('/api/clinical-assistant/examples', { headers: authHeaders(), credentials: 'same-origin' })
.then(function(r) { return r.json(); });
}
export function openAssistantStream(payload) {
return fetch('/api/clinical-assistant/chat/stream', {
method: 'POST',
headers: authHeaders(),
credentials: 'same-origin',
body: JSON.stringify(payload)
});
}
export function fetchAssistantChat(payload) {
return fetch('/api/clinical-assistant/chat', {
method: 'POST',
headers: authHeaders(),
credentials: 'same-origin',
body: JSON.stringify(payload)
}).then(parseJsonWithStatus);
}
export function requestAssistantImage(prompt) {
return fetch('/api/clinical-assistant/image', {
method: 'POST',
headers: authHeaders(),
credentials: 'same-origin',
body: JSON.stringify({ prompt: prompt })
}).then(function(r) { return r.json(); });
}
export function saveAssistantChat(payload) {
return fetch('/api/clinical-assistant/chats', {
method: 'POST',
headers: authHeaders(),
credentials: 'same-origin',
body: JSON.stringify(payload)
}).then(parseJsonWithStatus);
}
export function fetchSavedAssistantChats() {
return fetch('/api/clinical-assistant/chats', { headers: authHeaders(), credentials: 'same-origin' })
.then(function(r) { return r.json(); });
}
export function fetchSavedAssistantChat(id) {
return fetch('/api/clinical-assistant/chats/' + encodeURIComponent(id), { headers: authHeaders(), credentials: 'same-origin' })
.then(parseJsonWithStatus);
}
export function deleteSavedAssistantChat(id) {
return fetch('/api/clinical-assistant/chats/' + encodeURIComponent(id), {
method: 'DELETE',
headers: authHeaders(),
credentials: 'same-origin'
}).then(function(r) { return r.json(); });
}

View file

@ -5,6 +5,17 @@
// ============================================================ // ============================================================
import { EMPTY_PROMPT_SETS } from './assistant/data.js'; import { EMPTY_PROMPT_SETS } from './assistant/data.js';
import { escapeAttr, escapeHtml, renderAssistantMarkdown } from './assistant/citations.js'; import { escapeAttr, escapeHtml, renderAssistantMarkdown } from './assistant/citations.js';
import {
deleteSavedAssistantChat,
fetchAssistantChat,
fetchAssistantExamples,
fetchAssistantStatus,
fetchSavedAssistantChat,
fetchSavedAssistantChats,
openAssistantStream,
requestAssistantImage,
saveAssistantChat
} from './assistant/api.js';
(function () { (function () {
'use strict'; 'use strict';
@ -68,8 +79,7 @@ import { escapeAttr, escapeHtml, renderAssistantMarkdown } from './assistant/cit
} }
function loadStatus() { function loadStatus() {
fetch('/api/clinical-assistant/status', { headers: getAuthHeaders(), credentials: 'same-origin' }) fetchAssistantStatus()
.then(function (r) { return r.json(); })
.then(function (data) { .then(function (data) {
var label = document.getElementById('assistant-model-label'); var label = document.getElementById('assistant-model-label');
if (label && data.success) { if (label && data.success) {
@ -82,8 +92,7 @@ import { escapeAttr, escapeHtml, renderAssistantMarkdown } from './assistant/cit
} }
function loadExamples() { function loadExamples() {
fetch('/api/clinical-assistant/examples', { headers: getAuthHeaders(), credentials: 'same-origin' }) fetchAssistantExamples()
.then(function (r) { return r.json(); })
.then(function (data) { .then(function (data) {
if (!data.success || !Array.isArray(data.examples) || !data.examples.length) return; if (!data.success || !Array.isArray(data.examples) || !data.examples.length) return;
dynamicExamples = data.examples; dynamicExamples = data.examples;
@ -150,12 +159,7 @@ import { escapeAttr, escapeHtml, renderAssistantMarkdown } from './assistant/cit
} }
async function streamAssistantResponse(payload, loading) { async function streamAssistantResponse(payload, loading) {
var response = await fetch('/api/clinical-assistant/chat/stream', { var response = await openAssistantStream(payload);
method: 'POST',
headers: getAuthHeaders(),
credentials: 'same-origin',
body: JSON.stringify(payload)
});
if (!response.ok || !response.body) { if (!response.ok || !response.body) {
var fallback = await response.json().catch(function () { return {}; }); var fallback = await response.json().catch(function () { return {}; });
throw new Error(fallback.error || ('Request failed (' + response.status + ')')); throw new Error(fallback.error || ('Request failed (' + response.status + ')'));
@ -250,14 +254,8 @@ import { escapeAttr, escapeHtml, renderAssistantMarkdown } from './assistant/cit
} }
async function fetchAssistantFallback(payload) { async function fetchAssistantFallback(payload) {
var response = await fetch('/api/clinical-assistant/chat', { var data = await fetchAssistantChat(payload);
method: 'POST', if (!data.success) throw new Error(data.error || ('Request failed (' + data._status + ')'));
headers: getAuthHeaders(),
credentials: 'same-origin',
body: JSON.stringify(payload)
});
var data = await response.json().catch(function () { return {}; });
if (!response.ok || !data.success) throw new Error(data.error || ('Request failed (' + response.status + ')'));
return data; return data;
} }
@ -471,11 +469,7 @@ import { escapeAttr, escapeHtml, renderAssistantMarkdown } from './assistant/cit
if (fromChat) setBusy(true, 'Generating image...'); if (fromChat) setBusy(true, 'Generating image...');
var loading = fromChat ? appendLoadingMessage('Generating image', 'Creating the requested clinical visual...') : null; var loading = fromChat ? appendLoadingMessage('Generating image', 'Creating the requested clinical visual...') : null;
if (out) out.innerHTML = '<p class="assistant-muted"><i class="fas fa-spinner fa-spin"></i> Generating image...</p>'; if (out) out.innerHTML = '<p class="assistant-muted"><i class="fas fa-spinner fa-spin"></i> Generating image...</p>';
fetch('/api/clinical-assistant/image', { requestAssistantImage(prompt)
method: 'POST', headers: getAuthHeaders(), credentials: 'same-origin',
body: JSON.stringify({ prompt: prompt })
})
.then(function (r) { return r.json(); })
.then(function (data) { .then(function (data) {
if (!data.success) throw new Error(data.error || 'Image generation failed'); if (!data.success) throw new Error(data.error || 'Image generation failed');
var src = data.imageUrl || data.url || (data.base64 ? ('data:image/png;base64,' + data.base64) : ''); var src = data.imageUrl || data.url || (data.base64 ? ('data:image/png;base64,' + data.base64) : '');
@ -762,19 +756,13 @@ import { escapeAttr, escapeHtml, renderAssistantMarkdown } from './assistant/cit
var titleEl = document.getElementById('assistant-save-title'); var titleEl = document.getElementById('assistant-save-title');
var title = String(titleEl && titleEl.value || deriveChatTitle()).trim() || deriveChatTitle(); var title = String(titleEl && titleEl.value || deriveChatTitle()).trim() || deriveChatTitle();
setBusy(true, 'Saving chat...'); setBusy(true, 'Saving chat...');
fetch('/api/clinical-assistant/chats', { saveAssistantChat({
method: 'POST', title: title,
headers: getAuthHeaders(), messages: messages,
credentials: 'same-origin', sources: lastSources,
body: JSON.stringify({ lastAnswer: lastAnswer,
title: title, generatedImage: imageForSavedChatPayload(lastGeneratedImageSrc)
messages: messages,
sources: lastSources,
lastAnswer: lastAnswer,
generatedImage: imageForSavedChatPayload(lastGeneratedImageSrc)
})
}) })
.then(function (r) { return r.json().then(function (data) { data._status = r.status; return data; }); })
.then(function (data) { .then(function (data) {
setBusy(false, 'Ready'); setBusy(false, 'Ready');
if (!data.success) throw new Error(data.error || 'Save failed'); if (!data.success) throw new Error(data.error || 'Save failed');
@ -805,8 +793,7 @@ import { escapeAttr, escapeHtml, renderAssistantMarkdown } from './assistant/cit
} }
function loadSavedChats() { function loadSavedChats() {
fetch('/api/clinical-assistant/chats', { headers: getAuthHeaders(), credentials: 'same-origin' }) fetchSavedAssistantChats()
.then(function (r) { return r.json(); })
.then(function (data) { if (data.success) renderSavedChats(data.chats || []); }) .then(function (data) { if (data.success) renderSavedChats(data.chats || []); })
.catch(function () {}); .catch(function () {});
} }
@ -830,8 +817,7 @@ import { escapeAttr, escapeHtml, renderAssistantMarkdown } from './assistant/cit
} }
function loadSavedChat(id) { function loadSavedChat(id) {
fetch('/api/clinical-assistant/chats/' + encodeURIComponent(id), { headers: getAuthHeaders(), credentials: 'same-origin' }) fetchSavedAssistantChat(id)
.then(function (r) { return r.json().then(function (data) { data._status = r.status; return data; }); })
.then(function (data) { .then(function (data) {
if (!data.success) throw new Error(data.error || 'Load failed'); if (!data.success) throw new Error(data.error || 'Load failed');
restoreSavedChat(data.chat && data.chat.payload || {}); restoreSavedChat(data.chat && data.chat.payload || {});
@ -853,10 +839,7 @@ import { escapeAttr, escapeHtml, renderAssistantMarkdown } from './assistant/cit
}, 5000); }, 5000);
return; return;
} }
fetch('/api/clinical-assistant/chats/' + encodeURIComponent(id), { deleteSavedAssistantChat(id)
method: 'DELETE', headers: getAuthHeaders(), credentials: 'same-origin'
})
.then(function (r) { return r.json(); })
.then(function () { loadSavedChats(); if (typeof showToast === 'function') showToast('Deleted saved chat', 'success'); }) .then(function () { loadSavedChats(); if (typeof showToast === 'function') showToast('Deleted saved chat', 'success'); })
.catch(function () { if (typeof showToast === 'function') showToast('Delete failed', 'error'); }); .catch(function () { if (typeof showToast === 'function') showToast('Delete failed', 'error'); });
} }