Files
researchChat/lib/config.js
T
nico 8a2bf46b09 Recherche abbrechen + Chats löschen (mit Aufräumen) + Truncation-Fix
- Manueller Abbruch: POST /api/sessions/:id/abort (nur Owner), AbortController
  für laufende Jobs (auch mitten im API-Stream), cancelQueued für wartende,
  ⛔-Button in der Activity-Bar, 'abgebrochen'-Event + History-Eintrag
- Chats löschen: 🗑-Button in der Sidebar mit Bestätigung, Ownership-Check
  (vorher konnte jeder fremde Chats löschen), löscht jetzt auch Messages
  und Dashboard-Dateien (HTML/GPX/tour.json) mit
- Truncation-Fix: finish_reason 'length' wird erkannt (sichtbarer Fehler
  statt leerer Abschluss), max_tokens auf 131072 (GLM-Max), 10-Min-Timeout
  gegen hängende Streams
2026-08-28 07:17:06 +02:00

123 lines
4.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
const fs = require('fs');
const path = require('path');
const crypto = require('crypto');
const webpush = require('web-push');
const ENV_PATH = path.join(__dirname, '..', '.env');
function loadEnv() {
const env = {};
if (fs.existsSync(ENV_PATH)) {
for (const line of fs.readFileSync(ENV_PATH, 'utf8').split('\n')) {
const m = line.match(/^\s*([A-Z0-9_]+)\s*=\s*(.*)\s*$/);
if (m && !line.trim().startsWith('#')) env[m[1]] = m[2].replace(/^["']|["']$/g, '');
}
}
return env;
}
function saveEnv(env) {
const lines = [];
for (const [k, v] of Object.entries(env)) lines.push(`${k}=${v}`);
fs.writeFileSync(ENV_PATH, lines.join('\n') + '\n', 'utf8');
}
function ensure(key, generator) {
if (process.env[key] || env[key]) return process.env[key] || env[key];
const value = generator();
env[key] = value;
saveEnv(env);
return value;
}
const env = loadEnv();
function ensureVapidPair() {
if ((process.env.VAPID_PUBLIC_KEY || env.VAPID_PUBLIC_KEY) && (process.env.VAPID_PRIVATE_KEY || env.VAPID_PRIVATE_KEY)) {
return {
publicKey: process.env.VAPID_PUBLIC_KEY || env.VAPID_PUBLIC_KEY,
privateKey: process.env.VAPID_PRIVATE_KEY || env.VAPID_PRIVATE_KEY,
};
}
const pair = webpush.generateVAPIDKeys();
env.VAPID_PUBLIC_KEY = pair.publicKey;
env.VAPID_PRIVATE_KEY = pair.privateKey;
saveEnv(env);
return pair;
}
const vapid = ensureVapidPair();
const config = {
port: Number(process.env.PORT || env.PORT || 3000),
apiKey: process.env.DEEPSEEK_API_KEY || env.DEEPSEEK_API_KEY || '',
openrouterApiKey: process.env.OPENROUTER_API_KEY || env.OPENROUTER_API_KEY || '',
appPassword: process.env.APP_PASSWORD || env.APP_PASSWORD || '',
model: process.env.MODEL || env.MODEL || 'deepseek-v4-flash',
thinking: process.env.THINKING || env.THINKING || 'enabled',
maxTokens: Number(process.env.MAX_TOKENS || env.MAX_TOKENS || 64000),
baseUrl: process.env.DEEPSEEK_BASE_URL || env.DEEPSEEK_BASE_URL || 'https://api.deepseek.com',
loginMaxAttempts: Number(process.env.LOGIN_MAX_ATTEMPTS || env.LOGIN_MAX_ATTEMPTS || 5),
loginLockMinutes: Number(process.env.LOGIN_LOCK_MINUTES || env.LOGIN_LOCK_MINUTES || 5),
nominatimEmail: process.env.NOMINATIM_EMAIL || env.NOMINATIM_EMAIL || '',
osrmUrl: process.env.OSRM_URL || env.OSRM_URL || 'https://router.project-osrm.org',
sessionSecret: ensure('SESSION_SECRET', () => crypto.randomBytes(32).toString('hex')),
vapidPublicKey: vapid.publicKey,
vapidPrivateKey: vapid.privateKey,
};
const envOr = (key, def) => process.env[key] || env[key] || def;
// ---------------------------------------------------------------------------
// Modell-Registry: DeepSeek direkt + OpenRouter-Modelle (z.B. GLM 5.3 Flash)
// ---------------------------------------------------------------------------
const MODELS = [
{
id: 'deepseek-v4-flash',
label: 'DeepSeek V4 Flash',
provider: 'deepseek',
vision: false,
// Max. Output laut Doku: 384K – großzügiges Limit gegen abgeschnittene Dashboards
maxTokens: 131072,
reasoningEffort: () => envOr('DEEPSEEK_REASONING_EFFORT', 'max'),
prices: { inputCacheHit: 0.014, inputCacheMiss: 0.44, output: 1.32 },
},
{
id: 'z-ai/glm-5.3-flash',
label: 'GLM 5.3 Flash (OpenRouter)',
provider: 'openrouter',
vision: true,
// Max. Completion laut OpenRouter: 131072
maxTokens: 131072,
reasoningEffort: () => envOr('GLM_REASONING_EFFORT', 'max'),
prices: { inputCacheHit: 0.015, inputCacheMiss: 0.075, output: 0.25 },
costNote: 'Schätzung (OpenRouter, Rabatt bis Sep 2026)',
},
];
/** Registry inkl. benutzerdefiniertem Modell aus der Env (MODEL), falls nicht bekannt. */
function getModels() {
if (MODELS.some((m) => m.id === config.model)) return MODELS;
return [
...MODELS,
{
id: config.model,
label: config.model,
provider: 'deepseek',
vision: false,
maxTokens: config.maxTokens,
reasoningEffort: () => envOr('DEEPSEEK_REASONING_EFFORT', 'max'),
prices: { inputCacheHit: 0.014, inputCacheMiss: 0.44, output: 1.32 },
},
];
}
/** Modell per ID auflösen (Fallback: Default-Modell aus Env). */
function getModel(id) {
const models = getModels();
return models.find((m) => m.id === id) || models.find((m) => m.id === config.model) || models[0];
}
module.exports = { config, MODELS, getModels, getModel };