Заменить в public/scripts/openai.js в getStreamingReply (~3166 строчка):

1
2
3
4
5
} else if (chat_completion_source === chat_completion_sources.DEEPSEEK) {
    if (show_thoughts) {
        state.reasoning += (data.choices?.filter(x => x?.delta?.reasoning_content)?.[0]?.delta?.reasoning_content || '');
    }
    return data.choices?.[0]?.delta?.content || '';

На:

1
2
3
4
5
} else if (chat_completion_source === chat_completion_sources.DEEPSEEK) {
    if (show_thoughts) {
        state.reasoning += (data.choices?.filter(x => x?.delta?.reasoning_content)?.[0]?.delta?.reasoning_content || '');
    }
    return data.choices?.[0]?.delta?.content || data.choices?.[0]?.text || '';

Заменить sendDeepSeekRequest в src/endpoints/backends/chat-completions.js (~1030 строчка):

/**
 * Sends a request to DeepSeek API.
 * @param {express.Request} request Express request
 * @param {express.Response} response Express response
 */
async function sendDeepSeekRequest(request, response) {
    const apiUrl = new URL(request.body.reverse_proxy || API_DEEPSEEK).toString();
    const apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.DEEPSEEK, request.body.secret_id);

    if (!apiKey && !request.body.reverse_proxy) {
        console.warn('DeepSeek API key is missing.');
        return response.status(400).send({ error: true });
    }

    const controller = new AbortController();
    request.socket.removeAllListeners('close');
    request.socket.on('close', function () {
        controller.abort();
    });

    try {
        // FIM Completion hack: route to /completions when temp=0.99 and top_p=0.99
        const isFimRequest = request.body.temperature === 0.99 && request.body.top_p === 0.99;

        if (isFimRequest) {
            // Build prompt by joining message contents with newlines
            const prompt = request.body.messages.map(m => {
                if (typeof m.content === 'string') return m.content;
                if (Array.isArray(m.content)) return m.content.map(c => c.text || '').join('');
                return '';
            }).join('\n');

            const fimBody = {
                model: request.body.model,
                prompt: prompt,
                max_tokens: request.body.max_tokens,
                temperature: request.body.temperature,
                top_p: request.body.top_p,
                stream: request.body.stream,
            };
            if (request.body.frequency_penalty !== undefined && request.body.frequency_penalty !== null) {
                fimBody.frequency_penalty = request.body.frequency_penalty;
            }
            if (Array.isArray(request.body.stop) && request.body.stop.length > 0) {
                fimBody.stop = request.body.stop;
            }

            const config = {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                    'Authorization': 'Bearer ' + apiKey,
                },
                body: JSON.stringify(fimBody),
                signal: controller.signal,
            };

            console.debug('DeepSeek FIM request:', fimBody);

            const generateResponse = await fetch(apiUrl + '/completions', config);

            if (request.body.stream) {
                await forwardFetchResponse(generateResponse, response);
            } else {
                if (!generateResponse.ok) {
                    const errorText = await generateResponse.text();
                    console.warn(`DeepSeek FIM API returned error: ${generateResponse.status} ${generateResponse.statusText} ${errorText}`);
                    const errorJson = tryParse(errorText) ?? { error: true };
                    return response.status(500).send(errorJson);
                }
                const generateResponseJson = await generateResponse.json();
                console.debug('DeepSeek FIM response:', generateResponseJson);
                return response.send(generateResponseJson);
            }
            return;
        }

        let bodyParams = {};

        if (request.body.logprobs > 0) {
            bodyParams['top_logprobs'] = request.body.logprobs;
            bodyParams['logprobs'] = true;
        }

        if (apiUrl.includes("qwen")) {
            bodyParams['enable_thinking'] = false;
        }

        if (Array.isArray(request.body.tools) && request.body.tools.length > 0) {
            bodyParams['tools'] = request.body.tools;
            bodyParams['tool_choice'] = request.body.tool_choice;

            // DeepSeek doesn't permit empty required arrays
            bodyParams.tools.forEach(tool => {
                const required = tool?.function?.parameters?.required;
                if (Array.isArray(required) && required.length === 0) {
                    delete tool.function.parameters.required;
                }
            });
        }

        // Hack to support JSON schema
        if (request.body.json_schema) {
            bodyParams.response_format = {
                type: 'json_object',
            };
            const message = {
                role: 'user',
                content: `JSON schema for the response:\n${JSON.stringify(request.body.json_schema.value, null, 4)}`,
            };
            request.body.messages.push(message);
        }

        const processedMessages = addAssistantPrefix(postProcessPrompt(request.body.messages, PROMPT_PROCESSING_TYPE.SEMI_TOOLS, getPromptNames(request)), bodyParams.tools, 'prefix');
        addReasoningContentToToolCalls(processedMessages);

        if (request.body.include_reasoning && request.body.reasoning_effort) {
            bodyParams['reasoning_effort'] = request.body.reasoning_effort;
        }

        const requestBody = {
            'messages': processedMessages,
            'model': request.body.model,
            'temperature': request.body.temperature,
            'max_tokens': request.body.max_tokens,
            'stream': request.body.stream,
            'presence_penalty': request.body.presence_penalty,
            'frequency_penalty': request.body.frequency_penalty,
            'top_p': request.body.top_p,
            'stop': request.body.stop,
            'seed': request.body.seed,
            'thinking': { type: request.body.include_reasoning ? 'enabled' : 'disabled' },
            ...bodyParams,
        };

        const config = {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'Authorization': 'Bearer ' + apiKey,
            },
            body: JSON.stringify(requestBody),
            signal: controller.signal,
        };

        console.debug('DeepSeek request:', requestBody);

        const generateResponse = await fetch(apiUrl + '/chat/completions', config);

        if (request.body.stream) {
            await forwardFetchResponse(generateResponse, response);
        } else {
            if (!generateResponse.ok) {
                const errorText = await generateResponse.text();
                console.warn(`DeepSeek API returned error: ${generateResponse.status} ${generateResponse.statusText} ${errorText}`);
                const errorJson = tryParse(errorText) ?? { error: true };
                return response.status(500).send(errorJson);
            }
            const generateResponseJson = await generateResponse.json();
            console.debug('DeepSeek response:', generateResponseJson);
            return response.send(generateResponseJson);
        }
    } catch (error) {
        console.error('Error communicating with DeepSeek API: ', error);
        if (!response.headersSent) {
            response.send({ error: true });
        } else {
            response.end();
        }
    }
}
Edit

Pub: 06 Jul 2026 20:36 UTC

Views: 63