Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,4 @@ venv/
*.swo

src/Authorization.txt
tmp_response*.json
5 changes: 4 additions & 1 deletion scripts/interactive_chat.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@

import axios from 'axios';
import readline from 'readline';
import { getMappedModel } from '../src/api/modelMapping.js';
import { DEFAULT_MODEL } from '../src/config.js';

const API_URL = 'http://localhost:3264/api/chat/completions';
const CHAT_MODEL = getMappedModel('qwen-max-latest', DEFAULT_MODEL);

const rl = readline.createInterface({
input: process.stdin,
Expand All @@ -31,7 +34,7 @@ async function streamChat(userMessage) {
content: userMessage
}
],
model: 'qwen-max-latest',
model: CHAT_MODEL,
stream: true
},
{
Expand Down
83 changes: 53 additions & 30 deletions scripts/run_tests.js
Original file line number Diff line number Diff line change
@@ -1,44 +1,67 @@
import axios from 'axios';
import fs from 'fs';

const base = 'http://localhost:3264/api/chat/completions';
import { getMappedModel } from '../src/api/modelMapping.js';

import { DEFAULT_MODEL } from '../src/config.js';

const BASE_URL = 'http://localhost:3264/api/chat/completions';
const TEST_MODEL = getMappedModel('qwen-max-latest', DEFAULT_MODEL);
const headers = { 'User-Agent': 'OpenWebUI-Test/1.0', 'Content-Type': 'application/json' };

async function checkServer() {
try {
const r = await axios.get('http://localhost:3264/api/health', { timeout: 5000 });
return r.status === 200;
} catch {
return false;
}
}

function formatError(e) {
if (e instanceof AggregateError) {
const messages = e.errors.map(err => err.message || err.code || String(err)).join('\n');
return `AggregateError (${e.errors.length} ошибок):\n${messages}`;
}
if (e.code === 'ECONNREFUSED' || e.code === 'ERR_NETWORK') {
return 'Не удалось подключиться к серверу на localhost:3264. Запустите start.bat сначала.';
}
let msg = e.message || String(e);
if (e.response?.data) {
try {
const data = typeof e.response.data === 'string' ? e.response.data : JSON.stringify(e.response.data);
msg += `\nОтвет сервера: ${data.slice(0, 500)}`;
} catch {}
}
return msg;
}

async function run() {
try {
console.log('POST 1: Меня зовут Дима');
const r1 = await axios.post(base, { model: 'qwen-max-latest', messages: [{ role: 'user', content: 'Меня зовут Дима' }] }, { headers, timeout: 120000 });
console.log('Статус ответа 1:', r1.status);
console.log(JSON.stringify(r1.data, null, 2));
fs.writeFileSync('./tmp_response1.json', JSON.stringify(r1.data, null, 2), 'utf8');
if (!await checkServer()) {
console.error('Сервер не запущен на localhost:3264. Запустите start.bat сначала.');
process.exit(1);
}

try {
console.log(`POST 1: Меня зовут Дима (модель: ${TEST_MODEL})`);
const r1 = await axios.post(BASE_URL, { model: TEST_MODEL, messages: [{ role: 'user', content: 'Меня зовут Дима' }] }, { headers, timeout: 120000 });
console.log('Статус:', r1.status);
console.log(JSON.stringify(r1.data, null, 2));
fs.writeFileSync('./tmp_response1.json', JSON.stringify(r1.data, null, 2), 'utf8');

await new Promise(r => setTimeout(r, 500));
await new Promise(r => setTimeout(r, 500));

console.log('\nPOST 2: Как меня зовут?');
const r2 = await axios.post(base, { model: 'qwen-max-latest', messages: [{ role: 'user', content: 'Как меня зовут?' }] }, { headers, timeout: 120000 });
console.log('Статус ответа 2:', r2.status);
console.log(JSON.stringify(r2.data, null, 2));
fs.writeFileSync('./tmp_response2.json', JSON.stringify(r2.data, null, 2), 'utf8');
console.log('\nPOST 2: Как меня зовут?');
const r2 = await axios.post(BASE_URL, { model: TEST_MODEL, messages: [{ role: 'user', content: 'Как меня зовут?' }] }, { headers, timeout: 120000 });
console.log('Статус:', r2.status);
console.log(JSON.stringify(r2.data, null, 2));
fs.writeFileSync('./tmp_response2.json', JSON.stringify(r2.data, null, 2), 'utf8');

console.log('\nSaved responses to tmp_response1.json and tmp_response2.json');
console.log('\nСохранено в tmp_response1.json и tmp_response2.json');

// tail logs that may include rawChunks entries
try {
const logs = fs.readFileSync('./logs/server.log', 'utf8');
const matches = logs.split(/\r?\n/).filter(l => l.includes('[raw]') || l.includes('rawChunks') || l.includes('Ответ получен успешно'));
console.log('\n--- recent raw log lines (filtered) ---');
console.log(matches.slice(-40).join('\n'));
} catch (e) {
console.warn('Could not read server log file (server.log). Listing logs folder instead:');
console.log(fs.readdirSync('./logs').join('\n'));
}

} catch (e) {
console.error('Ошибка при запуске тестов:', e.toString());
if (e.response && e.response.data) {
console.error('Данные ответа:', JSON.stringify(e.response.data, null, 2));
console.error('Ошибка при запуске тестов:', formatError(e));
}
}
}

run();
run();
104 changes: 50 additions & 54 deletions scripts/test_streaming.js
Original file line number Diff line number Diff line change
@@ -1,143 +1,139 @@
/**
* Test streaming API
*/

import axios from 'axios';
import { getMappedModel } from '../src/api/modelMapping.js';
import { DEFAULT_MODEL } from '../src/config.js';

const API_URL = 'http://localhost:3264/api/chat/completions';
const TEST_MODEL = getMappedModel('qwen-max-latest', DEFAULT_MODEL);

function formatError(e) {
if (e instanceof AggregateError) {
const messages = e.errors.map(err => err.message || err.code || String(err)).join('\n');
return `AggregateError (${e.errors.length}):\n${messages}`;
}
return e.message || String(e);
}

async function checkServer() {
try {
const r = await axios.get('http://localhost:3264/api/health', { timeout: 5000 });
return r.status === 200;
} catch {
return false;
}
}

async function testStreaming() {
if (!await checkServer()) {
console.error('Сервер не запущен. Запустите start.bat.');
process.exit(1);
}

try {
console.log('=== Testing Streaming API ===\n');

// First request: introduce yourself
console.log('POST 1: Stream "Привет, я Дима"');

console.log(`POST 1: Stream "Привет, я Дима" (модель: ${TEST_MODEL})`);

const response1 = await axios.post(
API_URL,
{
messages: [
{
role: 'user',
content: 'Привет, я Дима'
}
],
model: 'qwen-max-latest',
messages: [{ role: 'user', content: 'Привет, я Дима' }],
model: TEST_MODEL,
stream: true
},
{
headers: {
'User-Agent': 'TestClient/1.0'
},
headers: { 'User-Agent': 'TestClient/1.0', 'Content-Type': 'application/json' },
responseType: 'stream'
}
);

// Handle streaming response
let fullContent1 = '';
let chunkCount1 = 0;

await new Promise((resolve, reject) => {
response1.data.on('data', (chunk) => {
const text = chunk.toString();
const lines = text.split('\n').filter(line => line.trim());

for (const line of lines) {
if (line.startsWith('data: ')) {
try {
const json = JSON.parse(line.substring(6));
if (json.choices?.[0]?.delta?.content) {
const content = json.choices[0].delta.content;
process.stdout.write(content); // Real-time output
process.stdout.write(content);
fullContent1 += content;
chunkCount1++;
}
} catch (e) {
// Ignore parse errors
}
} catch {}
}
}
});

response1.data.on('end', () => {
console.log('\n');
console.log(`Stream 1 complete. Chunks: ${chunkCount1}, Total length: ${fullContent1.length}`);
console.log(`Stream 1 complete. Chunks: ${chunkCount1}, Length: ${fullContent1.length}`);
resolve();
});

response1.data.on('error', reject);
});

// Wait a bit before second request
await new Promise(resolve => setTimeout(resolve, 2000));

// Second request: ask about name
console.log('\nPOST 2: Stream "Как меня зовут?"');

const response2 = await axios.post(
API_URL,
{
messages: [
{
role: 'user',
content: 'Как меня зовут?'
}
],
model: 'qwen-max-latest',
messages: [{ role: 'user', content: 'Как меня зовут?' }],
model: TEST_MODEL,
stream: true
},
{
headers: {
'User-Agent': 'TestClient/1.0'
},
headers: { 'User-Agent': 'TestClient/1.0', 'Content-Type': 'application/json' },
responseType: 'stream'
}
);

// Handle streaming response
let fullContent2 = '';
let chunkCount2 = 0;

await new Promise((resolve, reject) => {
response2.data.on('data', (chunk) => {
const text = chunk.toString();
const lines = text.split('\n').filter(line => line.trim());

for (const line of lines) {
if (line.startsWith('data: ')) {
try {
const json = JSON.parse(line.substring(6));
if (json.choices?.[0]?.delta?.content) {
const content = json.choices[0].delta.content;
process.stdout.write(content); // Real-time output
process.stdout.write(content);
fullContent2 += content;
chunkCount2++;
}
} catch (e) {
// Ignore parse errors
}
} catch {}
}
}
});

response2.data.on('end', () => {
console.log('\n');
console.log(`Stream 2 complete. Chunks: ${chunkCount2}, Total length: ${fullContent2.length}`);
console.log(`Stream 2 complete. Chunks: ${chunkCount2}, Length: ${fullContent2.length}`);
resolve();
});

response2.data.on('error', reject);
});

console.log('\n=== Streaming Test Successful ===');

} catch (error) {
console.error('Ошибка:', error.message);
console.error('Ошибка:', formatError(error));
if (error.response) {
console.error('Status:', error.response.status);
console.error('Data:', error.response.data);
try { console.error('Data:', typeof error.response.data === 'string' ? error.response.data : JSON.stringify(error.response.data)); } catch {}
}
}
}

testStreaming();
testStreaming();
3 changes: 1 addition & 2 deletions src/AvailableModels.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ qwen3-vl-plus
qwen3-coder-plus
qwen3-omni-flash
qwen3-omni-flash-2025-12-01
qwen-max-latest
qwen-plus-2025-09-11
qwen-plus-2025-01-25
qwq-32b
Expand All @@ -25,4 +24,4 @@ qvq-72b-preview-0310
qwen2.5-vl-32b-instruct
qwen2.5-14b-instruct-1m
qwen2.5-coder-32b-instruct
qwen2.5-72b-instruct
qwen2.5-72b-instruct
Loading