From 0a24035b27f5fb82c9a9c2d463ae6802bb7240f3 Mon Sep 17 00:00:00 2001 From: fretuks Date: Fri, 25 Sep 2026 17:14:56 +0200 Subject: [PATCH] add JSDoc comments because yes --- eslint.config.js | 3 +++ public/js/api.js | 5 +++++ public/js/attachments.js | 3 +++ public/js/chatDrawer.js | 3 +++ public/js/constants.js | 3 +++ public/js/dom.js | 3 +++ public/js/market.js | 3 +++ public/js/modelCapabilities.js | 3 +++ public/js/modelProfiles.js | 3 +++ public/js/pages.js | 3 +++ public/js/personaForm.js | 3 +++ public/js/reasoning.js | 4 ++++ public/js/regeneration.js | 4 ++++ public/js/script.js | 3 +++ public/js/settings.js | 3 +++ public/js/statusNotice.js | 3 +++ public/js/theme-bootstrap.js | 3 +++ public/js/themeController.js | 3 +++ public/js/webSearch.js | 3 +++ public/js/workspaceNavigation.js | 3 +++ server.js | 3 +++ src/app.js | 3 +++ src/config.js | 3 +++ src/db/attachments.js | 3 +++ src/db/index.js | 3 +++ src/db/repositories.js | 3 +++ src/db/schema.js | 3 +++ src/middleware/auth.js | 3 +++ src/middleware/rateLimit.js | 4 ++++ src/middleware/rateLimiters.js | 3 +++ src/middleware/validate.js | 3 +++ src/pageLayout.js | 3 +++ src/routes/auth.js | 3 +++ src/routes/chats.js | 3 +++ src/routes/pages.js | 3 +++ src/routes/personas.js | 3 +++ src/routes/settings.js | 3 +++ src/routes/system.js | 3 +++ src/routes/uploads.js | 3 +++ src/routes/visionDebug.js | 3 +++ src/services/attachmentService.js | 3 +++ src/services/chatMessageHelpers.js | 3 +++ src/services/chatService.js | 7 +++++++ src/services/localFileStorage.js | 3 +++ src/services/modelService.js | 5 +++++ src/services/personaService.js | 3 +++ src/services/visionAnalysis.js | 3 +++ src/services/visionDebugService.js | 3 +++ src/services/webSearchService.js | 3 +++ src/services/workspaceImportService.js | 3 +++ test/app.test.js | 3 +++ test/attachmentComposer.test.js | 3 +++ test/attachments.test.js | 3 +++ test/config.test.js | 3 +++ test/modelStreaming.test.js | 3 +++ test/reasoning.test.js | 3 +++ test/regeneration.test.js | 3 +++ test/regenerationUi.test.js | 3 +++ test/visionAnalysis.test.js | 3 +++ test/visionDiagnostics.test.js | 3 +++ test/webSearch.test.js | 3 +++ test/webSearchRegression.test.js | 3 +++ 62 files changed, 197 insertions(+) diff --git a/eslint.config.js b/eslint.config.js index 2690113..963962d 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,3 +1,6 @@ +/** + * @file Konfiguriert ESLint-Regeln und Browser-Globals für JavaScript-Dateien. + */ import js from "@eslint/js"; import globals from "globals"; import { defineConfig } from "eslint/config"; diff --git a/public/js/api.js b/public/js/api.js index 33f7d04..a8ec20b 100644 --- a/public/js/api.js +++ b/public/js/api.js @@ -1,3 +1,6 @@ +/** + * @file Bündelt JSON-Anfragen und verarbeitet Ereignisse gestreamter Chat-Antworten im Browser. + */ export async function request(url, data, method = "POST") { try { const res = await fetch(url, { @@ -60,6 +63,7 @@ export async function stream(url, data, handlers = {}, signal = undefined) { if (done) break; buffer += decoder.decode(value, {stream: true}); const parts = buffer.split(/\r?\n\r?\n/); + // Das letzte SSE-Ereignis kann unvollständig sein und wird mit dem nächsten Netzwerkblock ergänzt. buffer = parts.pop() || ""; for (const part of parts) { const lines = part.split("\n"); @@ -81,6 +85,7 @@ export async function stream(url, data, handlers = {}, signal = undefined) { } if (terminal) break; } + // Ohne explizites Abschluss- oder Fehlerereignis gilt die Verbindung als vorzeitig abgebrochen. if (!terminal) throw new Error("Response was interrupted. Please try again."); } finally { await reader.cancel().catch(() => {}); diff --git a/public/js/attachments.js b/public/js/attachments.js index 3b65b96..d46d0b9 100644 --- a/public/js/attachments.js +++ b/public/js/attachments.js @@ -1,3 +1,6 @@ +/** + * @file Verwaltet Bildauswahl, Uploads und Vorschauen sowie die Anzeige gespeicherter Anhänge. + */ export function appendAttachments(container, attachments = []) { if (!attachments.length) return; const gallery = document.createElement("div"); diff --git a/public/js/chatDrawer.js b/public/js/chatDrawer.js index b6c845e..3dc8987 100644 --- a/public/js/chatDrawer.js +++ b/public/js/chatDrawer.js @@ -1,3 +1,6 @@ +/** + * @file Steuert das Chat-Seitenpanel einschließlich Tastaturbedienung und Fokusverwaltung. + */ import {chatDrawer, chatDrawerButton, chatDrawerCloseBtn} from "./dom.js?v=20260909-account-controls"; let lastFocusedElement = null; diff --git a/public/js/constants.js b/public/js/constants.js index 53e03e1..b94fb9a 100644 --- a/public/js/constants.js +++ b/public/js/constants.js @@ -1,3 +1,6 @@ +/** + * @file Stellt gemeinsame Theme-Daten, Einstiegsvorschläge und die angefragte Chat-ID bereit. + */ export const requestedChatId = Number(window.location.pathname.match(/^\/app\/chats\/(\d+)$/)?.[1] || new URLSearchParams(window.location.search).get("chat")) || null; export const onboardingPrompts = { diff --git a/public/js/dom.js b/public/js/dom.js index 9669391..6c3b885 100644 --- a/public/js/dom.js +++ b/public/js/dom.js @@ -1,3 +1,6 @@ +/** + * @file Bündelt Referenzen auf die HTML-Elemente der Chat-Oberfläche. + */ const $ = (id) => document.getElementById(id); const chatDiv = $("chat"); diff --git a/public/js/market.js b/public/js/market.js index 41334fd..98d131d 100644 --- a/public/js/market.js +++ b/public/js/market.js @@ -1,3 +1,6 @@ +/** + * @file Steuert die Persona-Marktplatzoberfläche mit Suche, Vorschauen und Persona-Verwaltung. + */ import { get, post, put } from "./api.js?v=20260909-account-controls"; import { initializeAppearance } from "./themeController.js?v=20260909-account-controls"; import { clearStatus, setStatus } from "./statusNotice.js?v=20260909-account-controls"; diff --git a/public/js/modelCapabilities.js b/public/js/modelCapabilities.js index fc09ba3..c108dce 100644 --- a/public/js/modelCapabilities.js +++ b/public/js/modelCapabilities.js @@ -1,3 +1,6 @@ +/** + * @file Ermittelt die Modellfamilie und prüft die Unterstützung des Reasoning-Modus. + */ // Conservative allowlist for models supporting the boolean think API. // Unknown models remain usable for normal chat. const reasoningFamilies = new Set(["qwen3", "deepseek-r1", "deepseek-v3.1"]); diff --git a/public/js/modelProfiles.js b/public/js/modelProfiles.js index 360d3e6..48a8bca 100644 --- a/public/js/modelProfiles.js +++ b/public/js/modelProfiles.js @@ -1,3 +1,6 @@ +/** + * @file Ordnet Modellen Profilinformationen für ihre Darstellung und Auswahl in der Oberfläche zu. + */ export const defaultModelProfile = { badge: "General", summary: "Best for general questions, drafting, and everyday conversation. Use a specialized model when you need stronger coding, reasoning, or roleplay behavior." diff --git a/public/js/pages.js b/public/js/pages.js index 015c57e..34cefd5 100644 --- a/public/js/pages.js +++ b/public/js/pages.js @@ -1,3 +1,6 @@ +/** + * @file Steuert öffentliche Seiten sowie Anmelde- und Registrierungsformulare mit Rückkehr in den Arbeitsbereich. + */ import { get, post } from "/js/api.js?v=20260909-account-controls"; const params = new URLSearchParams(location.search); diff --git a/public/js/personaForm.js b/public/js/personaForm.js index 0e07577..18dfbf1 100644 --- a/public/js/personaForm.js +++ b/public/js/personaForm.js @@ -1,3 +1,6 @@ +/** + * @file Überträgt Persona-Daten zwischen Formularfeldern und den verwendeten Datenobjekten. + */ const personaFieldNames = ["name", "pronouns", "appearance", "background", "details", "exampleDialogues"]; export function populatePersonaForm(fields, persona = {}) { diff --git a/public/js/reasoning.js b/public/js/reasoning.js index c7f4473..b948f08 100644 --- a/public/js/reasoning.js +++ b/public/js/reasoning.js @@ -1,3 +1,6 @@ +/** + * @file Steuert die Reasoning-Auswahl und die aufklappbare Anzeige gestreamter Gedankengänge. + */ import {supportsReasoning} from "./modelCapabilities.js?v=20260921-reasoning-capability"; export function createReasoningToggle(button) { @@ -43,6 +46,7 @@ export function updateReasoning(messageElement, thinking, streaming = false) { } details.querySelector("summary").textContent = streaming ? "Reasoning · Thinking…" : "Reasoning"; const content = details.querySelector(".msg-reasoning-content"); + // Nur automatisch nach unten scrollen, wenn die Ansicht bereits nahe am Ende steht. const follow = content.scrollHeight - content.scrollTop - content.clientHeight < 40; content.textContent = thinking; if (streaming && follow) content.scrollTop = content.scrollHeight; diff --git a/public/js/regeneration.js b/public/js/regeneration.js index 0cd15c1..75f4f99 100644 --- a/public/js/regeneration.js +++ b/public/js/regeneration.js @@ -1,3 +1,6 @@ +/** + * @file Ersetzt eine Antwort während der Neugenerierung in ihrer Nachrichtenkarte und stellt sie bei Fehlern wieder her. + */ import {stream} from "./api.js"; import {updateReasoning, finishReasoning} from "./reasoning.js"; import {appendSources} from "./webSearch.js"; @@ -46,6 +49,7 @@ export async function regenerateInline({card, scroller, endpoint, payload, rende } }); } finally { + // Bei Fehler oder Abbruch die ursprünglichen Knoten samt Ereignishandlern wieder einsetzen. if (!committed) update(() => card.replaceChildren(...original)); card.removeAttribute("aria-busy"); } diff --git a/public/js/script.js b/public/js/script.js index 7f9278c..21fb949 100644 --- a/public/js/script.js +++ b/public/js/script.js @@ -1,3 +1,6 @@ +/** + * @file Steuert die Chat-Oberfläche mit Gesprächsauswahl, Nachrichteneingabe, Streaming und Persona-Verwaltung. + */ import { del, get, post, put, stream } from "./api.js?v=20260909-account-controls"; import { regenerateInline } from "./regeneration.js"; import { appendSources, createWebSearchToggle } from "./webSearch.js"; diff --git a/public/js/settings.js b/public/js/settings.js index 279fed9..331ff73 100644 --- a/public/js/settings.js +++ b/public/js/settings.js @@ -1,3 +1,6 @@ +/** + * @file Steuert die Einstellungsoberfläche für Darstellung, Konto, Personas und Diagnosen. + */ import { del, get, post, put } from "./api.js?v=20260909-account-controls"; import { applyTheme as applySharedTheme, diff --git a/public/js/statusNotice.js b/public/js/statusNotice.js index f25bb42..7fbcfb1 100644 --- a/public/js/statusNotice.js +++ b/public/js/statusNotice.js @@ -1,3 +1,6 @@ +/** + * @file Zeigt Statusmeldungen an und setzt deren Inhalt und Sichtbarkeit zurück. + */ export function setStatus(element, message = "", state = "", baseClass = "status") { if (!element) return; element.textContent = message; diff --git a/public/js/theme-bootstrap.js b/public/js/theme-bootstrap.js index 105c081..ddf5518 100644 --- a/public/js/theme-bootstrap.js +++ b/public/js/theme-bootstrap.js @@ -1,3 +1,6 @@ +/** + * @file Übernimmt das gespeicherte Theme frühzeitig in das Dokument, bevor die übrige Oberfläche initialisiert wird. + */ (() => { const themes = new Set(["fakegpt", "fraud", "germini", "slopilot", "beta-ai", "confusity"]); const storedTheme = localStorage.getItem("krishd-theme"); diff --git a/public/js/themeController.js b/public/js/themeController.js index bb05c71..99c292e 100644 --- a/public/js/themeController.js +++ b/public/js/themeController.js @@ -1,3 +1,6 @@ +/** + * @file Normalisiert, speichert und synchronisiert Theme und Hell-Dunkel-Modus der Oberfläche. + */ import {themes} from "./constants.js?v=20260909-account-controls"; export const THEME_STORAGE_KEY = "krishd-theme"; diff --git a/public/js/webSearch.js b/public/js/webSearch.js index 6b1355a..8fef8b0 100644 --- a/public/js/webSearch.js +++ b/public/js/webSearch.js @@ -1,3 +1,6 @@ +/** + * @file Steuert die Websuche-Auswahl und rendert Quellenlinks zu Chat-Antworten. + */ /** @typedef {{title: string, url: string, snippet: string}} WebSource */ /** @typedef {{message: string, model: string, chatId: number, webSearch?: boolean, reasoning?: boolean}} ChatRequest */ /** @typedef {{role: string, content: string, thinking?: string, sources?: WebSource[]}} ChatMessage */ diff --git a/public/js/workspaceNavigation.js b/public/js/workspaceNavigation.js index c19b859..ada02c9 100644 --- a/public/js/workspaceNavigation.js +++ b/public/js/workspaceNavigation.js @@ -1,3 +1,6 @@ +/** + * @file Verbindet die Abmeldeknöpfe des gemeinsamen Seitenkopfs mit der Sitzungsverwaltung. + */ import {post} from './api.js?v=20260909-account-controls'; // Every page gets this control from the same server-rendered header. diff --git a/server.js b/server.js index d91b2f7..4beb6e7 100644 --- a/server.js +++ b/server.js @@ -1,3 +1,6 @@ +/** + * @file Startet den HTTP-Server der Chat-Anwendung. + */ import {startServer} from "./src/app.js"; startServer(); diff --git a/src/app.js b/src/app.js index 972112b..871f31b 100644 --- a/src/app.js +++ b/src/app.js @@ -1,3 +1,6 @@ +/** + * @file Verbindet Express, Sitzungen, Datenbank und Routen und verwaltet den Server-Lebenszyklus. + */ import fs from "fs"; import {randomUUID} from "crypto"; import {performance} from "perf_hooks"; diff --git a/src/config.js b/src/config.js index 2fb9829..4a8ae77 100644 --- a/src/config.js +++ b/src/config.js @@ -1,3 +1,6 @@ +/** + * @file Erstellt die Anwendungskonfiguration aus Umgebungsvariablen, Standardwerten und expliziten Vorgaben. + */ import path from "path"; const bool = (value, fallback = false) => { diff --git a/src/db/attachments.js b/src/db/attachments.js index 8075bdb..44ecc59 100644 --- a/src/db/attachments.js +++ b/src/db/attachments.js @@ -1,3 +1,6 @@ +/** + * @file Definiert Tabellen für Bildanhänge und Analyse-Caches und bereitet Anhangsmetadaten auf. + */ export function initializeAttachments(db) { db.exec(` CREATE TABLE IF NOT EXISTS attachments ( diff --git a/src/db/index.js b/src/db/index.js index 8d6f91a..d0ee081 100644 --- a/src/db/index.js +++ b/src/db/index.js @@ -1,3 +1,6 @@ +/** + * @file Öffnet die SQLite-Datenbank und initialisiert Tabellen sowie Migrationen. + */ import fs from "fs"; import Database from "better-sqlite3"; import {initializeSchema, runMigrations} from "./schema.js"; diff --git a/src/db/repositories.js b/src/db/repositories.js index 98d0987..ffd9546 100644 --- a/src/db/repositories.js +++ b/src/db/repositories.js @@ -1,3 +1,6 @@ +/** + * @file Bündelt Datenbankzugriffe auf Konten, Chats, Nachrichten, Personas und den Marktplatz. + */ import {attachmentMetadata} from "./attachments.js"; const parseJsonArray = (value) => { diff --git a/src/db/schema.js b/src/db/schema.js index bef91af..b9724c4 100644 --- a/src/db/schema.js +++ b/src/db/schema.js @@ -1,3 +1,6 @@ +/** + * @file Definiert das Datenbankschema und migriert bestehende Daten auf die benötigte Struktur. + */ export function initializeSchema(db) { db.pragma("journal_mode = WAL"); db.pragma("foreign_keys = ON"); diff --git a/src/middleware/auth.js b/src/middleware/auth.js index eadd3be..bd16d6f 100644 --- a/src/middleware/auth.js +++ b/src/middleware/auth.js @@ -1,3 +1,6 @@ +/** + * @file Schützt Routen vor Zugriffen ohne angemeldete Sitzung. + */ export function requireLogin(req, res, next) { if (!req.session.user) { return res.status(403).json({error: "Not logged in"}); diff --git a/src/middleware/rateLimit.js b/src/middleware/rateLimit.js index cf915f1..ef5cf81 100644 --- a/src/middleware/rateLimit.js +++ b/src/middleware/rateLimit.js @@ -1,3 +1,6 @@ +/** + * @file Begrenzt Anfragen je Schlüssel in einem gleitenden Zeitfenster und entfernt abgelaufene Einträge. + */ function pruneBucket(bucket, windowMs, now) { const cutoff = now - windowMs; while (bucket.length && bucket[0] <= cutoff) { @@ -21,6 +24,7 @@ export function createRateLimiter({windowMs, max, keyGenerator, message, cleanup if (cleanupIntervalMs > 0) { cleanupTimer = setInterval(cleanup, cleanupIntervalMs); + // Der Aufräumtimer allein soll den Node-Prozess beim Herunterfahren nicht offen halten. cleanupTimer.unref(); } diff --git a/src/middleware/rateLimiters.js b/src/middleware/rateLimiters.js index 41f0d19..d4a85f8 100644 --- a/src/middleware/rateLimiters.js +++ b/src/middleware/rateLimiters.js @@ -1,3 +1,6 @@ +/** + * @file Erstellt die konfigurierten Anfragebegrenzungen für Anmeldung und Chat-Nutzung. + */ import {createRateLimiter} from "./rateLimit.js"; export function createAuthRateLimiters(config) { diff --git a/src/middleware/validate.js b/src/middleware/validate.js index 03d2603..c6ae704 100644 --- a/src/middleware/validate.js +++ b/src/middleware/validate.js @@ -1,3 +1,6 @@ +/** + * @file Prüft Anfrageinhalte mit einem übergebenen Validator und stellt die validierten Daten für Routen bereit. + */ export function validateBody(validator) { return (req, res, next) => { const result = validator(req.body || {}, req); diff --git a/src/pageLayout.js b/src/pageLayout.js index 4f01de8..4ca9143 100644 --- a/src/pageLayout.js +++ b/src/pageLayout.js @@ -1,3 +1,6 @@ +/** + * @file Erzeugt den gemeinsamen HTML-Seitenkopf mit Navigation und Kontosteuerung. + */ const escapeHtml = (value) => String(value).replace(/[&<>"']/g, (character) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[character]); diff --git a/src/routes/auth.js b/src/routes/auth.js index b79e37f..911a137 100644 --- a/src/routes/auth.js +++ b/src/routes/auth.js @@ -1,3 +1,6 @@ +/** + * @file Verarbeitet Registrierung, Anmeldung, Abmeldung und die Abfrage der aktuellen Sitzung. + */ import express from "express"; import bcrypt from "bcrypt"; import {validateBody} from "../middleware/validate.js"; diff --git a/src/routes/chats.js b/src/routes/chats.js index 8abfbdf..369f17a 100644 --- a/src/routes/chats.js +++ b/src/routes/chats.js @@ -1,3 +1,6 @@ +/** + * @file Stellt Chat-Routen für Nachrichten, Streaming, Antwortvarianten, Verzweigungen und Einstellungen bereit. + */ import express from "express"; import {requireLogin} from "../middleware/auth.js"; import {validateBody} from "../middleware/validate.js"; diff --git a/src/routes/pages.js b/src/routes/pages.js index 519a6ad..7b2d19c 100644 --- a/src/routes/pages.js +++ b/src/routes/pages.js @@ -1,3 +1,6 @@ +/** + * @file Liefert öffentliche Seiten und geschützte Ansichten des Arbeitsbereichs aus. + */ import express from "express"; import {readFile} from "node:fs/promises"; import {renderSiteHeader} from "../pageLayout.js"; diff --git a/src/routes/personas.js b/src/routes/personas.js index f5681be..891a26b 100644 --- a/src/routes/personas.js +++ b/src/routes/personas.js @@ -1,3 +1,6 @@ +/** + * @file Verwaltet eigene Personas sowie Veröffentlichung, Suche und Interaktionen auf dem Persona-Marktplatz. + */ import express from "express"; import {requireLogin} from "../middleware/auth.js"; import {validateBody} from "../middleware/validate.js"; diff --git a/src/routes/settings.js b/src/routes/settings.js index 3174ce1..81c2125 100644 --- a/src/routes/settings.js +++ b/src/routes/settings.js @@ -1,3 +1,6 @@ +/** + * @file Verarbeitet Benutzer- und Kontoeinstellungen sowie Export und Import des Arbeitsbereichs. + */ import express from "express"; import bcrypt from "bcrypt"; import {requireLogin} from "../middleware/auth.js"; diff --git a/src/routes/system.js b/src/routes/system.js index 95148a8..bc16784 100644 --- a/src/routes/system.js +++ b/src/routes/system.js @@ -1,3 +1,6 @@ +/** + * @file Stellt Modellinformationen und administrative Diagnosedaten bereit. + */ import express from "express"; import {requireLogin} from "../middleware/auth.js"; diff --git a/src/routes/uploads.js b/src/routes/uploads.js index c4a95c5..1dc3f62 100644 --- a/src/routes/uploads.js +++ b/src/routes/uploads.js @@ -1,3 +1,6 @@ +/** + * @file Verarbeitet das Hochladen, Abrufen und Entfernen von Bildanhängen mit Zugriffskontrolle. + */ import express from "express"; import multer from "multer"; import {requireLogin} from "../middleware/auth.js"; diff --git a/src/routes/visionDebug.js b/src/routes/visionDebug.js index 4135e16..aa38ad3 100644 --- a/src/routes/visionDebug.js +++ b/src/routes/visionDebug.js @@ -1,3 +1,6 @@ +/** + * @file Liefert angemeldeten Benutzern die für sie zugänglichen Diagnosen der Bildverarbeitung. + */ import express from "express"; import {requireLogin} from "../middleware/auth.js"; diff --git a/src/services/attachmentService.js b/src/services/attachmentService.js index 97a7f3b..428e521 100644 --- a/src/services/attachmentService.js +++ b/src/services/attachmentService.js @@ -1,3 +1,6 @@ +/** + * @file Validiert und speichert Bildanhänge und bereitet zwischengespeicherte Bildanalysen für den Chat vor. + */ import sharp from "sharp"; import {createHash, randomUUID} from "node:crypto"; import {attachmentMetadata} from "../db/attachments.js"; diff --git a/src/services/chatMessageHelpers.js b/src/services/chatMessageHelpers.js index fbecaa3..7c1feba 100644 --- a/src/services/chatMessageHelpers.js +++ b/src/services/chatMessageHelpers.js @@ -1,3 +1,6 @@ +/** + * @file Normalisiert Nachrichten und erstellt Kontexttexte für Erinnerungen, Zusammenfassungen und Personas. + */ import {normalizeSources} from "./webSearchService.js"; export function parseSources(value) { diff --git a/src/services/chatService.js b/src/services/chatService.js index 93f5ecb..151e0a5 100644 --- a/src/services/chatService.js +++ b/src/services/chatService.js @@ -1,3 +1,6 @@ +/** + * @file Koordiniert Chat-Antworten, Modellkontext, Websuche, Anhänge, Antwortvarianten und Gesprächsverzweigungen. + */ import { buildPersonaPrompt, buildRoleplayDirectionPrompt, @@ -95,15 +98,18 @@ export function createChatService(repositories, modelService, config, webSearchP const withChatGenerationLock = (chatId, operation) => { const previous = chatGenerationQueues.get(chatId) || Promise.resolve(); + // Ein fehlgeschlagener Vorgänger darf die Warteschlange dieses Chats nicht blockieren. const current = previous.catch(() => undefined).then(operation); chatGenerationQueues.set(chatId, current); return current.finally(() => { + // Nur den eigenen Eintrag entfernen: Inzwischen könnte bereits ein Folgeauftrag warten. if (chatGenerationQueues.get(chatId) === current) chatGenerationQueues.delete(chatId); }); }; const withActiveGeneration = async (chatId, externalSignal, operation) => { const controller = new AbortController(); + // Verbindungsabbruch und manuelles Stoppen wirken auf denselben Generierungsauftrag. const abortFromCaller = () => controller.abort(externalSignal?.reason); if (externalSignal?.aborted) abortFromCaller(); else externalSignal?.addEventListener("abort", abortFromCaller, { once: true }); @@ -157,6 +163,7 @@ export function createChatService(repositories, modelService, config, webSearchP const allMessages = repositories.listActiveBranchMessages(chatId) .filter((message) => !message.delivery_status || message.delivery_status === "complete"); const keepRecent = Math.max(0, config.chatHistoryLimit); + // Die neuesten Nachrichten bleiben vollständig im Kontext; nur ältere werden zusammengefasst. const cutoffIndex = allMessages.length - keepRecent - 1; if (cutoffIndex < 0) return; const cutoffMessage = allMessages[cutoffIndex]; diff --git a/src/services/localFileStorage.js b/src/services/localFileStorage.js index 93ddaad..0a4df58 100644 --- a/src/services/localFileStorage.js +++ b/src/services/localFileStorage.js @@ -1,3 +1,6 @@ +/** + * @file Speichert, liest und löscht hochgeladene Dateien in einem lokalen Speicherverzeichnis. + */ import fs from "node:fs"; import path from "node:path"; import {randomUUID} from "node:crypto"; diff --git a/src/services/modelService.js b/src/services/modelService.js index a401aba..d170927 100644 --- a/src/services/modelService.js +++ b/src/services/modelService.js @@ -1,3 +1,6 @@ +/** + * @file Kommuniziert mit Ollama und verwaltet Antwortstreams, Zeitlimits, Modellstatus und Modell-Caches. + */ import { supportsReasoning } from "../../public/js/modelCapabilities.js"; import { AttachmentError } from "./attachmentService.js"; @@ -147,6 +150,7 @@ export function createModelService(config, overrides = {}) { touch(); buffer += decoder.decode(value, { stream: true }); const lines = buffer.split("\n"); + // Netzwerkblöcke können mitten in einer JSON-Zeile enden; den Rest für den nächsten Block behalten. buffer = lines.pop() || ""; for (const line of lines) { const trimmed = line.trim(); @@ -160,6 +164,7 @@ export function createModelService(config, overrides = {}) { if (!completed && buffer.trim()) { consume(buffer.trim()); } + // Erst das Abschlussereignis bestätigt eine vollständige Antwort; ein Stream-Ende allein genügt nicht. if (!completed) throw new ModelServiceError("MODEL_INTERRUPTED", "Model response was interrupted.", 502); onRawReply?.(fullReply); return fullReply.trim(); diff --git a/src/services/personaService.js b/src/services/personaService.js index 9236af4..a274e1c 100644 --- a/src/services/personaService.js +++ b/src/services/personaService.js @@ -1,3 +1,6 @@ +/** + * @file Validiert Persona-Daten und erstellt Rollenspielanweisungen sowie Prüfungen für die Rollenverteilung. + */ const PERSONA_FIELD_LIMITS = { name: 200, pronouns: 40, diff --git a/src/services/visionAnalysis.js b/src/services/visionAnalysis.js index 14a449b..4bfe5a0 100644 --- a/src/services/visionAnalysis.js +++ b/src/services/visionAnalysis.js @@ -1,3 +1,6 @@ +/** + * @file Definiert Anweisungen, Antwortschema und Normalisierung für Bildanalysen und deren Nutzung im Chat. + */ // Bump whenever the perception prompt, schema, or interpretation contract changes. export const VISION_PROMPT_VERSION = 4; diff --git a/src/services/visionDebugService.js b/src/services/visionDebugService.js index 2a702d3..47df419 100644 --- a/src/services/visionDebugService.js +++ b/src/services/visionDebugService.js @@ -1,3 +1,6 @@ +/** + * @file Sammelt begrenzte Diagnosedaten zur Bildverarbeitung und kontrolliert deren benutzerbezogenen Zugriff. + */ const RAW_VISION_LIMIT = 12000; const EVIDENCE_LIMIT = 100000; const ANSWER_LIMIT = 30000; diff --git a/src/services/webSearchService.js b/src/services/webSearchService.js index a715c6a..48d3061 100644 --- a/src/services/webSearchService.js +++ b/src/services/webSearchService.js @@ -1,3 +1,6 @@ +/** + * @file Entscheidet über Websuchen, fragt SearXNG ab und bereitet relevante Quellen als Modellkontext auf. + */ /** @typedef {{title: string, url: string, snippet: string, score?: number, engine?: string, category?: string, publishedDate?: string}} RawSearchResult */ /** @typedef {{title: string, url: string, snippet: string}} SearchResult */ /** @typedef {{signal?: AbortSignal, limit?: number, category?: string}} SearchOptions */ diff --git a/src/services/workspaceImportService.js b/src/services/workspaceImportService.js index 442f6b8..7ad9171 100644 --- a/src/services/workspaceImportService.js +++ b/src/services/workspaceImportService.js @@ -1,3 +1,6 @@ +/** + * @file Prüft und normalisiert importierte Arbeitsbereiche mit Personas, Chats und Nachrichtenbeziehungen. + */ import {parseSources} from "./chatMessageHelpers.js"; const VALID_PERSONA_TYPES = new Set(["assistant", "user"]); diff --git a/test/app.test.js b/test/app.test.js index 62241f4..8236c87 100644 --- a/test/app.test.js +++ b/test/app.test.js @@ -1,3 +1,6 @@ +/** + * @file Prüft Anmeldung, Kontoverwaltung, Chats, Personas und den Server-Lebenszyklus der Anwendung. + */ import test from "node:test"; import assert from "node:assert/strict"; import fs from "fs"; diff --git a/test/attachmentComposer.test.js b/test/attachmentComposer.test.js index aec79de..1b19a2d 100644 --- a/test/attachmentComposer.test.js +++ b/test/attachmentComposer.test.js @@ -1,3 +1,6 @@ +/** + * @file Prüft Anhangsvorschauen, Upload-Zustände, Wiederholungen und das Verwerfen von Entwürfen. + */ import test from "node:test"; import assert from "node:assert/strict"; import { appendAttachments, createAttachmentComposer } from "../public/js/attachments.js"; diff --git a/test/attachments.test.js b/test/attachments.test.js index 0675cb2..0c24de3 100644 --- a/test/attachments.test.js +++ b/test/attachments.test.js @@ -1,3 +1,6 @@ +/** + * @file Prüft Uploads, Zugriffsrechte und den Lebenszyklus von Bildanhängen sowie ihre Nutzung im Modellkontext. + */ import test from "node:test"; import assert from "node:assert/strict"; import fs from "node:fs/promises"; diff --git a/test/config.test.js b/test/config.test.js index 27def09..a06da3b 100644 --- a/test/config.test.js +++ b/test/config.test.js @@ -1,3 +1,6 @@ +/** + * @file Prüft Konfigurationsvorgaben und Sicherheitsanforderungen für Test- und Produktionsbetrieb. + */ import test from "node:test"; import assert from "node:assert/strict"; import {createConfig} from "../src/config.js"; diff --git a/test/modelStreaming.test.js b/test/modelStreaming.test.js index 7ec807c..dd7ae20 100644 --- a/test/modelStreaming.test.js +++ b/test/modelStreaming.test.js @@ -1,3 +1,6 @@ +/** + * @file Prüft Antwortstreaming, Zeitlimits, Protokollfehler und Abbrüche bei getrennten Verbindungen. + */ import test from "node:test"; import assert from "node:assert/strict"; import { createServer } from "node:http"; diff --git a/test/reasoning.test.js b/test/reasoning.test.js index eb531d6..3664ba0 100644 --- a/test/reasoning.test.js +++ b/test/reasoning.test.js @@ -1,3 +1,6 @@ +/** + * @file Prüft Reasoning-Auswahl, getrennte Gedankenstreams und deren Speicherung sowie Darstellung. + */ import test from "node:test"; import assert from "node:assert/strict"; import { createServer } from "node:http"; diff --git a/test/regeneration.test.js b/test/regeneration.test.js index 30a17e8..bec52c1 100644 --- a/test/regeneration.test.js +++ b/test/regeneration.test.js @@ -1,3 +1,6 @@ +/** + * @file Prüft Antwortvarianten und den Erhalt bestehender Antworten bei fehlgeschlagener Neugenerierung. + */ import test from "node:test"; import assert from "node:assert/strict"; import {createServer} from "node:http"; diff --git a/test/regenerationUi.test.js b/test/regenerationUi.test.js index 3895d57..bfeb3df 100644 --- a/test/regenerationUi.test.js +++ b/test/regenerationUi.test.js @@ -1,3 +1,6 @@ +/** + * @file Prüft die Neugenerierung innerhalb einer Nachrichtenkarte mit Scrollverhalten und Wiederherstellung. + */ import test from "node:test"; import assert from "node:assert/strict"; import {readFile} from "node:fs/promises"; diff --git a/test/visionAnalysis.test.js b/test/visionAnalysis.test.js index 2c5da16..404b7e7 100644 --- a/test/visionAnalysis.test.js +++ b/test/visionAnalysis.test.js @@ -1,3 +1,6 @@ +/** + * @file Prüft Schema, Anweisungen, Normalisierung und Fehlerbehandlung der Bildanalyse. + */ import test from "node:test"; import assert from "node:assert/strict"; import {parseVisionAnalysis} from "../src/services/attachmentService.js"; diff --git a/test/visionDiagnostics.test.js b/test/visionDiagnostics.test.js index 91574e3..bdbf5c0 100644 --- a/test/visionDiagnostics.test.js +++ b/test/visionDiagnostics.test.js @@ -1,3 +1,6 @@ +/** + * @file Prüft Zugriffsschutz und Diagnoseaufzeichnungen für Bildanalysen, Caches und Modellanfragen. + */ import test from "node:test"; import assert from "node:assert/strict"; import fs from "node:fs/promises"; diff --git a/test/webSearch.test.js b/test/webSearch.test.js index b37f880..f6c9964 100644 --- a/test/webSearch.test.js +++ b/test/webSearch.test.js @@ -1,3 +1,6 @@ +/** + * @file Prüft Suchentscheidungen, SearXNG, Quellenauswahl und die Einbindung der Websuche in Chat und Oberfläche. + */ import test from "node:test"; import assert from "node:assert/strict"; import { createServer } from "node:http"; diff --git a/test/webSearchRegression.test.js b/test/webSearchRegression.test.js index 6717c7b..f696a66 100644 --- a/test/webSearchRegression.test.js +++ b/test/webSearchRegression.test.js @@ -1,3 +1,6 @@ +/** + * @file Sichert ab, dass explizite und datumsbezogene Websuchen vor der Modellgenerierung passende Quellen liefern. + */ import test from "node:test"; import assert from "node:assert/strict"; import {createServer} from "node:http";