Совместимость с курсором - теоритически возможно, но гвен не может напрямую вызывать инструменты как OpenAI, мб когда нибудь сделаю
Локальный API-прокси сервер для работы с Qwen AI через браузерную эмуляцию. Позволяет использовать модели Qwen без официального API-ключа.
- Бесплатный доступ: Используйте модели Qwen без оплаты API-ключа
- Полная совместимость: Поддержка OpenAI-совместимого интерфейса для простой интеграции
- Возможноссть загрузки файлов и получение ссылки прямо из прокси
- 🚀 Быстрый старт
- 💡 Возможности
- 📘 API Reference
- 📝 Примеры использования
- 🔄 Работа с контекстом
- 🔌 Совместимость с OpenAI API
- 🔧 Особенности реализации
- Клонировать репозиторий
- Установить зависимости:
npm install
npm start
Также доступен файл быстрого запуска:
start.bat
⚠️ Важно: если файлsrc/Authorization.txt
пустой, авторизация отключена.
-
Файл
src/Authorization.txt
- Создаётся автоматически при первом запуске если его нет.
- Внутри уже есть подробный шаблон-инструкция.
- Один токен на строку. Пустые строки и строки, начинающиеся с
#
, игнорируются.
-
Отключить авторизацию – оставьте файл пустым. Middleware пропустит все запросы.
-
Проверка на стороне клиента
Отправляйте HTTP-заголовок:
Authorization: Bearer <your_token>
Пример cURL:
curl -X POST http://localhost:3264/api/chat \ -H "Content-Type: application/json" \ -H "Authorization: Bearer my-secret-token-123" \ -d '{"message":"Привет"}'
При старте npm start
появляется интерактивное меню:
Список аккаунтов:
N | ID | Статус
1 | acc_1752745840684 | ✅ OK
2 | acc_1752745890062 | ❌ INVALID
=== Меню ===
1 - Добавить новый аккаунт
2 - Перелогинить аккаунт с истекшим токеном
3 - Запустить прокси (Enter по умолчанию)
4 - Удалить аккаунт
Статусы:
Значок | Значение | Поведение |
---|---|---|
✅ OK | токен активен | используется в ротации |
⏳ WAIT | токен временно заблокирован (RateLimited) | пропускается до истечения тайм-аута |
❌ INVALID | токен просрочен (401 Unauthorized) | недоступен, выберите пункт 2 для повторного входа |
Пункты меню:
- Добавить новый аккаунт – откроется браузер, авторизуйтесь, токен будет сохранён.
- Перелогинить аккаунт с истекшим токеном – выберите нужный ID, откроется браузер для повторного входа, статус сменится на ✅.
- Запустить прокси – доступно, если есть хотя бы один статус ✅ или ⏳.
- Удалить аккаунт – полностью удаляет токен и папку сессии.
Файлы:
session/accounts/<id>/token.txt
– токен аккаунтаsession/tokens.json
– реестр аккаунтов и состояний
Автоматическая ротация:
- запросы распределяются по токенам циклически.
- При ответе 429 RateLimited токен получает ⏳ WAIT на указанное время.
- При ответе 401 Unauthorized токен помечается ❌ INVALID.
- Если все токены недействительны – прокси завершает работу, запустите его и перелогиньтесь.
Этот проект позволяет:
- Использовать модели Qwen AI через локальный API
- Сохранять контекст диалогов между запросами
- Управлять диалогами через API
- Выбирать различные модели Qwen для генерации ответов
- Отправлять изображения для анализа моделью
- Использовать OpenAI-совместимый API с поддержкой streaming режима
Эндпоинт | Метод | Описание |
---|---|---|
/api/chat |
POST | Отправка сообщения и получение ответа |
/api/chat/completions |
POST | OpenAI-совместимый эндпоинт с поддержкой streaming |
/api/models |
GET | Получение списка доступных моделей |
/api/status |
GET | Проверка статуса авторизации |
/api/files/upload |
POST | Загрузка изображения для использования в запросах |
/api/chats |
POST/GET | Создание нового диалога / Получение списка всех диалогов |
/api/chats/:chatId |
GET/DELETE | Получение истории диалога / Удаление диалога |
/api/chats/:chatId/rename |
PUT | Переименование диалога |
/api/chats/cleanup |
POST | Автоудаление диалогов по критериям |
Эндпоинт | Использование контекста | Формат запроса | Совместимость |
---|---|---|---|
/api/chat |
Прокси хранит внутреннюю историю chatId и автоматически подаёт её модели при каждом запросе. |
Упрощённый message или массив messages |
Нативный для прокси |
/api/chat/completions |
Прокси НЕ хранит контекст между запросами: каждый вызов следует спецификации OpenAI — вы сами передаёте массив messages . |
Только массив messages (OpenAI format) |
OpenAI SDK |
Прокси поддерживает два формата запросов к /api/chat
:
{
"message": "Текст сообщения",
"model": "qwen-max-latest",
"chatId": "идентификатор_чата"
}
{
"messages": [
{"role": "user", "content": "Привет, как дела?"}
],
"model": "qwen-max-latest",
"chatId": "идентификатор_чата"
}
Важно понимать: Прокси использует внутреннюю систему хранения истории диалогов на сервере.
- При использовании формата
message
- сообщение просто добавляется в историю диалога. - При использовании формата
messages
- из массива извлекается только последнее сообщение пользователя и добавляется в историю.
При отправке запроса к официальному API Qwen всегда используется полная история диалога, связанная с указанным chatId
. Это означает, что при использовании параметра messages
вам достаточно включить только новое сообщение пользователя с ролью "user", а не всю историю диалога.
Прокси поддерживает отправку сообщений с изображениями в обоих форматах:
{
"message": [
{
"type": "text",
"text": "Опишите объекты на этом изображении"
},
{
"type": "image",
"image": "URL_ИЗОБРАЖЕНИЯ"
}
],
"model": "qwen3-235b-a22b",
"chatId": "идентификатор_чата"
}
{
"messages": [
{
"role": "user",
"content": [
{
"type": "text",
"text": "Опишите объекты на этом изображении"
},
{
"type": "image",
"image": "URL_ИЗОБРАЖЕНИЯ"
}
]
}
],
"model": "qwen3-235b-a22b",
"chatId": "идентификатор_чата"
}
POST http://localhost:3264/api/files/upload
Формат запроса: multipart/form-data
Параметры:
file
- файл изображения (поддерживаются форматы: jpg, jpeg, png, gif, webp)
Пример использования с curl:
curl -X POST http://localhost:3264/api/files/upload \
-F "file=@/путь/к/изображению.jpg"
Пример ответа:
{
"imageUrl": "https://cdn.qwenlm.ai/user-id/file-id_filename.jpg?key=..."
}
Для отправки изображений через API прокси необходимо сначала получить URL изображения. Это можно сделать двумя способами:
Отправьте POST запрос на эндпоинт /api/files/upload
для загрузки изображения, как описано выше.
- Загрузите изображение в официальном веб-интерфейсе Qwen (https://chat.qwen.ai/)
- Откройте инструменты разработчика в браузере (F12 или Ctrl+Shift+I)
- Перейдите на вкладку "Network" (Сеть)
- Найдите запрос к API Qwen, содержащий ваше изображение (обычно это запрос GetsToken)
- В теле запроса найдите URL изображения, который выглядит примерно так:
https://cdn.qwenlm.ai/user-id/file-id_filename.jpg?key=...
- Скопируйте этот URL для использования в вашем API-запросе
POST http://localhost:3264/api/chats
Тело запроса:
{
"name": "Название диалога"
}
Ответ:
{
"chatId": "уникальный_идентификатор"
}
GET http://localhost:3264/api/chats
GET http://localhost:3264/api/chats/:chatId
DELETE http://localhost:3264/api/chats/:chatId
PUT http://localhost:3264/api/chats/:chatId/rename
Тело запроса:
{
"name": "Новое название чата"
}
POST http://localhost:3264/api/chats/cleanup
Тело запроса (все параметры опциональны):
{
"olderThan": 604800000, // Удалить чаты старше указанного времени (в мс), например 7 дней
"userMessageCountLessThan": 3, // Удалить чаты с менее чем 3 сообщениями от пользователя
"messageCountLessThan": 5, // Удалить чаты с менее чем 5 сообщениями всего
"maxChats": 50 // Оставить только 50 самых новых чатов
}
▶️ Пример простого текстового запроса
curl -X POST http://localhost:3264/api/chat \
-H "Content-Type: application/json" \
-d '{
"message": "Что такое искусственный интеллект?",
"model": "qwen-max-latest"
}'
▶️ Пример запроса в формате официального API
curl -X POST http://localhost:3264/api/chat \
-H "Content-Type: application/json" \
-d '{
"messages": [
{"role": "user", "content": "Что такое искусственный интеллект?"}
],
"model": "qwen-max-latest"
}'
▶️ Пример загрузки изображения и отправки запроса с ним
# Шаг 1: Загрузка изображения
UPLOAD_RESPONSE=$(curl -s -X POST http://localhost:3264/api/files/upload \
-F "file=@/путь/к/изображению.jpg")
# Шаг 2: Извлечение URL изображения
IMAGE_URL=$(echo $UPLOAD_RESPONSE | grep -o '"imageUrl":"[^"]*"' | sed 's/"imageUrl":"//;s/"//')
# Шаг 3: Отправка запроса с изображением
curl -X POST http://localhost:3264/api/chat \
-H "Content-Type: application/json" \
-d '{
"message": [
{ "type": "text", "text": "Опишите объекты на этом изображении" },
{ "type": "image", "image": "'$IMAGE_URL'" }
],
"model": "qwen3-235b-a22b"
}'
▶️ Пошаговое руководство через Postman
-
Загрузка изображения:
- Создайте новый запрос POST к
http://localhost:3264/api/files/upload
- Выберите вкладку "Body"
- Выберите тип "form-data"
- Добавьте ключ "file" и выберите тип "File"
- Загрузите изображение, нажав на кнопку "Select Files"
- Нажмите "Send"
Ответ будет содержать URL изображения:
{ "imageUrl": "https://cdn.qwenlm.ai/user-id/file-id_filename.jpg?key=..." }
- Создайте новый запрос POST к
-
Использование изображения в запросе:
- Создайте новый запрос POST к
http://localhost:3264/api/chat
- Выберите вкладку "Body"
- Выберите тип "raw" и формат "JSON"
- Вставьте следующий JSON, заменив
URL_ИЗОБРАЖЕНИЯ
на полученный URL:
{ "message": [ { "type": "text", "text": "Опишите объекты на этом изображении" }, { "type": "image", "image": "URL_ИЗОБРАЖЕНИЯ" } ], "model": "qwen3-235b-a22b" }
- Нажмите "Send"
- Создайте новый запрос POST к
-
Запрос через OpenAI-совместимый эндпоинт:
- Создайте новый запрос POST к
http://localhost:3264/api/chat/completions
- Выберите вкладку "Body"
- Выберите тип "raw" и формат "JSON"
- Вставьте следующий JSON, заменив
URL_ИЗОБРАЖЕНИЯ
на полученный URL:
{ "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Опиши, что изображено на этой картинке?" }, { "type": "image", "image": "URL_ИЗОБРАЖЕНИЯ" } ] } ], "model": "qwen3-235b-a22b" }
- Нажмите "Send"
- Создайте новый запрос POST к
-
Запрос с потоковым режимом (streaming):
- Используйте тот же URL и тело запроса, но добавьте параметр
"stream": true
- Примечание: для корректного отображения потока в Postman, проверьте опцию "Preserve log" в консоли
- Используйте тот же URL и тело запроса, но добавьте параметр
Система автоматически сохраняет историю диалога и отправляет ее в каждом запросе к API Qwen. Это позволяет моделям учитывать предыдущие сообщения при генерации ответов.
- Первый запрос (без указания
chatId
):
{
"message": "Привет, как тебя зовут?"
}
- Ответ (содержит
chatId
):
{
"chatId": "abcd-1234-5678",
"choices": [...]
}
- Последующие запросы (с указанием полученного
chatId
):
{
"message": "Сколько будет 2+2?",
"chatId": "abcd-1234-5678"
}
Прокси поддерживает эндпоинт, совместимый с OpenAI API для подключения клиентов, которые работают с OpenAI API:
POST /api/chat/completions
-
Создание нового чата для каждого запроса: Каждый запрос к
/chat/completions
создаёт новый чат в системе с именем "OpenAI API Chat". -
Сохранение полной истории сообщений: Все сообщения из запроса (включая системные, пользовательские и сообщения ассистента) сохраняются в истории чата.
-
Поддержка системных сообщений: Прокси корректно обрабатывает и сохраняет системные сообщения (
role: "system"
), которые часто используются для настройки поведения модели.
Пример запроса с системным сообщением:
{
"messages": [
{"role": "system", "content": "Ты эксперт по JavaScript. Отвечай только на вопросы о JavaScript."},
{"role": "user", "content": "Как создать класс в JavaScript?"}
],
"model": "qwen-max-latest"
}
Прокси поддерживает режим потоковой передачи ответов (streaming), что позволяет получать ответы по частям в режиме реального времени:
{
"messages": [
{"role": "user", "content": "Напиши длинный рассказ о космосе"}
],
"model": "qwen-max-latest",
"stream": true
}
При использовании streaming режима, ответ будет возвращаться постепенно в формате Server-Sent Events (SSE), совместимом с OpenAI API.
▶️ Пример использования с OpenAI Node.js SDK
// Пример использования с OpenAI Node.js SDK
import OpenAI from 'openai';
import fs from 'fs';
import axios from 'axios';
const openai = new OpenAI({
baseURL: 'http://localhost:3264/api', // Базовый URL прокси
apiKey: 'dummy-key', // Не требуется реальный ключ, но поле обязательное для библиотеки
});
// Запрос без streaming
const completion = await openai.chat.completions.create({
messages: [{ role: 'user', content: 'Привет, как дела?' }],
model: 'qwen-max-latest', // Используемая модель Qwen
});
console.log(completion.choices[0].message);
// Запрос со streaming
const stream = await openai.chat.completions.create({
messages: [{ role: 'user', content: 'Расскажи длинную историю о космосе' }],
model: 'qwen-max-latest',
stream: true,
});
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta?.content || '');
}
// Загрузка и использование изображения
async function uploadAndAnalyzeImage(imagePath) {
// Загрузка изображения через API прокси
const formData = new FormData();
formData.append('file', fs.createReadStream(imagePath));
const uploadResponse = await axios.post('http://localhost:3264/api/files/upload', formData, {
headers: { 'Content-Type': 'multipart/form-data' }
});
const imageUrl = uploadResponse.data.imageUrl;
// Создание запроса с изображением
const completion = await openai.chat.completions.create({
messages: [
{
role: 'user',
content: [
{ type: 'text', text: 'Опиши, что изображено на этой картинке?' },
{ type: 'image', image: imageUrl }
]
}
],
model: 'qwen3-235b-a22b',
});
console.log(completion.choices[0].message.content);
}
// Использование: uploadAndAnalyzeImage('./image.jpg');
Ограничения совместимости:
- Некоторые специфичные для OpenAI параметры (например,
logprobs
,functions
и т.д.) не поддерживаются.- Скорость потоковой передачи может отличаться от оригинального OpenAI API.