feat(assistant): Fragen an die Daten im Kopf der Anwendung

Management und Administration koennen oben rechts eine Frage in Worten
stellen - "wie viele Stunden im August", "welches Projekt hat noch
Budget", "wie ist die Auslastung".

Aufbau: das Modell bekommt eine Uebersicht (Kunden, laufende Projekte,
Eckdaten) und vier Werkzeuge - auswertung, projekte, buchungen,
personen. Damit fragt es gezielt nach, statt dass ihm der ganze Bestand
vorgelegt wird; 19.000 Buchungen passen in keinen Kontext. Die Werkzeuge
rechnen mit loadEntries und loadProjects, also mit denselben Funktionen
wie die Reports - der Assistent kann so keine zweite Wahrheit erzeugen.
Was einfliesst, begrenzt visibleUserIds wie ueberall sonst.

Benutzt wird die Responses-API von OpenAI: die neueren Modelle nehmen
Werkzeuge in /v1/chat/completions nur ohne Nachdenken an. Mit
"store: false" bleibt nichts bei OpenAI liegen, der Verlauf geht deshalb
je Runde komplett mit. Der Schluessel liegt verschluesselt in den
Einstellungen (AES-256-GCM wie das SSO-Secret) und wird nie ausgeliefert;
das Modell ist einstellbar, Vorgabe gpt-5.6-luna.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
schakal00783
2026-09-21 17:31:51 +02:00
co-authored by Claude Opus 5
parent c3aed8a3d3
commit e908db2788
11 changed files with 909 additions and 2 deletions
+12
View File
@@ -71,6 +71,16 @@ export const DEFAULTS = {
last_run: null,
last_result: null,
},
/*
* KI-Assistent im Kopf der Anwendung. Der Schluessel liegt verschluesselt,
* genau wie das SSO-Secret; das Modell ist einstellbar, weil OpenAI die
* Namen regelmaessig wechselt.
*/
ai: {
enabled: false,
api_key: '',
model: 'gpt-5.6-luna',
},
sso: {
enabled: false,
button_label: 'Mit Microsoft anmelden',
@@ -104,6 +114,7 @@ export async function getSettings() {
}
if (result.sso.client_secret) result.sso.client_secret = decrypt(result.sso.client_secret);
if (result.hrworks.secret_access_key) result.hrworks.secret_access_key = decrypt(result.hrworks.secret_access_key);
if (result.ai.api_key) result.ai.api_key = decrypt(result.ai.api_key);
return result;
}
@@ -112,6 +123,7 @@ export async function saveSetting(key, value) {
let stored = value;
if (key === 'sso' && value.client_secret) stored = { ...value, client_secret: encrypt(value.client_secret) };
if (key === 'hrworks' && value.secret_access_key) stored = { ...value, secret_access_key: encrypt(value.secret_access_key) };
if (key === 'ai' && value.api_key) stored = { ...value, api_key: encrypt(value.api_key) };
await pool.query(
'INSERT INTO pulse_settings (k, v) VALUES (?, ?) ON DUPLICATE KEY UPDATE v = VALUES(v)',
[key, JSON.stringify(stored)]