feat: Implement admin settings UI, enhance admin authentication with password hashing, and add new streaming runtime logic for Claude and OpenAI adapters with extensive compatibility tests.

This commit is contained in:
CJACK
2026-02-19 02:45:38 +08:00
parent d21aedac83
commit 7307a5cc9a
64 changed files with 4078 additions and 967 deletions

View File

@@ -10,31 +10,14 @@ const {
parseToolCalls,
formatOpenAIStreamToolCalls,
} = require('./helpers/stream-tool-sieve');
const {
BASE_HEADERS,
SKIP_PATTERNS,
SKIP_EXACT_PATHS,
} = require('./shared/deepseek-constants');
const DEEPSEEK_COMPLETION_URL = 'https://chat.deepseek.com/api/v0/chat/completion';
const BASE_HEADERS = {
Host: 'chat.deepseek.com',
'User-Agent': 'DeepSeek/1.6.11 Android/35',
Accept: 'application/json',
'Content-Type': 'application/json',
'x-client-platform': 'android',
'x-client-version': '1.6.11',
'x-client-locale': 'zh_CN',
'accept-charset': 'UTF-8',
};
const SKIP_PATTERNS = [
'quasi_status',
'elapsed_secs',
'token_usage',
'pending_fragment',
'conversation_mode',
'fragments/-1/status',
'fragments/-2/status',
'fragments/-3/status',
];
module.exports = async function handler(req, res) {
setCorsHeaders(res);
if (req.method === 'OPTIONS') {
@@ -725,7 +708,7 @@ function extractContentRecursive(items, defaultType) {
}
function shouldSkipPath(pathValue) {
if (pathValue === 'response/search_status') {
if (SKIP_EXACT_PATHS.has(pathValue)) {
return true;
}
for (const p of SKIP_PATTERNS) {
@@ -808,7 +791,16 @@ function estimateTokens(text) {
if (!t) {
return 0;
}
const n = Math.floor(Array.from(t).length / 4);
let asciiChars = 0;
let nonASCIIChars = 0;
for (const ch of Array.from(t)) {
if (ch.charCodeAt(0) < 128) {
asciiChars += 1;
} else {
nonASCIIChars += 1;
}
}
const n = Math.floor(asciiChars / 4) + Math.floor((nonASCIIChars * 10 + 7) / 13);
return n < 1 ? 1 : n;
}
@@ -972,4 +964,5 @@ module.exports.__test = {
resolveToolcallPolicy,
normalizePreparedToolNames,
boolDefaultTrue,
estimateTokens,
};

View File

@@ -0,0 +1,60 @@
'use strict';
const test = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const chatStream = require('../chat-stream');
const { parseToolCalls } = require('../helpers/stream-tool-sieve');
const { parseChunkForContent, estimateTokens } = chatStream.__test;
const compatRoot = path.resolve(__dirname, '../../tests/compat');
function readJSON(filePath) {
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
}
test('js compat: sse fixtures', () => {
const fixtureDir = path.join(compatRoot, 'fixtures', 'sse_chunks');
const expectedDir = path.join(compatRoot, 'expected');
const files = fs.readdirSync(fixtureDir).filter((f) => f.endsWith('.json')).sort();
assert.ok(files.length > 0);
for (const file of files) {
const name = file.replace(/\.json$/i, '');
const fixture = readJSON(path.join(fixtureDir, file));
const expected = readJSON(path.join(expectedDir, `sse_${name}.json`));
const got = parseChunkForContent(fixture.chunk, Boolean(fixture.thinking_enabled), fixture.current_type || 'text');
assert.deepEqual(got.parts, expected.parts, `${name}: parts mismatch`);
assert.equal(got.finished, expected.finished, `${name}: finished mismatch`);
assert.equal(got.newType, expected.new_type, `${name}: newType mismatch`);
}
});
test('js compat: toolcall fixtures', () => {
const fixtureDir = path.join(compatRoot, 'fixtures', 'toolcalls');
const expectedDir = path.join(compatRoot, 'expected');
const files = fs.readdirSync(fixtureDir).filter((f) => f.endsWith('.json')).sort();
assert.ok(files.length > 0);
for (const file of files) {
const name = file.replace(/\.json$/i, '');
const fixture = readJSON(path.join(fixtureDir, file));
const expected = readJSON(path.join(expectedDir, `toolcalls_${name}.json`));
const got = parseToolCalls(fixture.text, fixture.tool_names || []);
assert.deepEqual(got, expected.calls, `${name}: calls mismatch`);
}
});
test('js compat: token fixtures', () => {
const fixture = readJSON(path.join(compatRoot, 'fixtures', 'token_cases.json'));
const expected = readJSON(path.join(compatRoot, 'expected', 'token_cases.json'));
const expectedByName = new Map(expected.cases.map((c) => [c.name, c.tokens]));
for (const c of fixture.cases) {
assert.ok(expectedByName.has(c.name), `missing expected case: ${c.name}`);
const got = estimateTokens(c.text);
assert.equal(got, expectedByName.get(c.name), `${c.name}: tokens mismatch`);
}
});

View File

@@ -0,0 +1,66 @@
'use strict';
const fs = require('fs');
const path = require('path');
const DEFAULT_BASE_HEADERS = Object.freeze({
Host: 'chat.deepseek.com',
'User-Agent': 'DeepSeek/1.6.11 Android/35',
Accept: 'application/json',
'Content-Type': 'application/json',
'x-client-platform': 'android',
'x-client-version': '1.6.11',
'x-client-locale': 'zh_CN',
'accept-charset': 'UTF-8',
});
const DEFAULT_SKIP_PATTERNS = Object.freeze([
'quasi_status',
'elapsed_secs',
'token_usage',
'pending_fragment',
'conversation_mode',
'fragments/-1/status',
'fragments/-2/status',
'fragments/-3/status',
]);
const DEFAULT_SKIP_EXACT_PATHS = Object.freeze([
'response/search_status',
]);
function loadSharedConstants() {
const sharedPath = path.resolve(__dirname, '../../internal/deepseek/constants_shared.json');
try {
const raw = fs.readFileSync(sharedPath, 'utf8');
const parsed = JSON.parse(raw);
const baseHeaders = parsed && typeof parsed.base_headers === 'object' && !Array.isArray(parsed.base_headers)
? { ...DEFAULT_BASE_HEADERS, ...parsed.base_headers }
: { ...DEFAULT_BASE_HEADERS };
const skipPatterns = Array.isArray(parsed && parsed.skip_contains_patterns)
? parsed.skip_contains_patterns.filter((v) => typeof v === 'string' && v !== '')
: [...DEFAULT_SKIP_PATTERNS];
const skipExactPaths = Array.isArray(parsed && parsed.skip_exact_paths)
? parsed.skip_exact_paths.filter((v) => typeof v === 'string' && v !== '')
: [...DEFAULT_SKIP_EXACT_PATHS];
return {
baseHeaders,
skipPatterns,
skipExactPaths,
};
} catch (_err) {
return {
baseHeaders: { ...DEFAULT_BASE_HEADERS },
skipPatterns: [...DEFAULT_SKIP_PATTERNS],
skipExactPaths: [...DEFAULT_SKIP_EXACT_PATHS],
};
}
}
const shared = loadSharedConstants();
module.exports = {
BASE_HEADERS: Object.freeze(shared.baseHeaders),
SKIP_PATTERNS: Object.freeze(shared.skipPatterns),
SKIP_EXACT_PATHS: new Set(shared.skipExactPaths),
};