🔥 feat: deepseek-reasoner Thought Streaming (#5379)

* 🔧 refactor: Remove unused penalties and enhance reasoning token handling in OpenAIClient

* 🔧 refactor: `addInstructions` default to adding instructions at index 0, flag for legacy behavior

* chore: remove long placeholder

* chore: update localization strings across multiple languages

* ci: adjust tests for new `addInstructions` behavior
This commit is contained in:
Danny Avila 2025-01-20 18:21:18 -05:00 committed by GitHub
parent 79585e22d2
commit d6b4d83b68
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 67 additions and 18 deletions

View file

@ -264,17 +264,24 @@ class BaseClient {
/**
* Adds instructions to the messages array. If the instructions object is empty or undefined,
* the original messages array is returned. Otherwise, the instructions are added to the messages
* array, preserving the last message at the end.
* array either at the beginning (default) or preserving the last message at the end.
*
* @param {Array} messages - An array of messages.
* @param {Object} instructions - An object containing instructions to be added to the messages.
* @param {boolean} [beforeLast=false] - If true, adds instructions before the last message; if false, adds at the beginning.
* @returns {Array} An array containing messages and instructions, or the original messages if instructions are empty.
*/
addInstructions(messages, instructions) {
const payload = [];
addInstructions(messages, instructions, beforeLast = false) {
if (!instructions || Object.keys(instructions).length === 0) {
return messages;
}
if (!beforeLast) {
return [instructions, ...messages];
}
// Legacy behavior: add instructions before the last message
const payload = [];
if (messages.length > 1) {
payload.push(...messages.slice(0, -1));
}

View file

@ -614,8 +614,6 @@ class OpenAIClient extends BaseClient {
model = 'gpt-4o-mini',
modelName,
temperature = 0.2,
presence_penalty = 0,
frequency_penalty = 0,
max_tokens,
streaming,
context,
@ -626,8 +624,6 @@ class OpenAIClient extends BaseClient {
const modelOptions = {
modelName: modelName ?? model,
temperature,
presence_penalty,
frequency_penalty,
user: this.user,
};
@ -1065,6 +1061,7 @@ ${convo}
let error = null;
const errorCallback = (err) => (error = err);
const intermediateReply = [];
const reasoningTokens = [];
try {
if (!abortController) {
abortController = new AbortController();
@ -1292,8 +1289,23 @@ ${convo}
}
});
let reasoningCompleted = false;
for await (const chunk of stream) {
if (chunk?.choices?.[0]?.delta?.reasoning_content) {
const reasoning_content = chunk?.choices?.[0]?.delta?.reasoning_content || '';
intermediateReply.push(reasoning_content);
reasoningTokens.push(reasoning_content);
onProgress(reasoning_content);
}
const token = chunk?.choices?.[0]?.delta?.content || '';
if (!reasoningCompleted && reasoningTokens.length > 0 && token) {
reasoningCompleted = true;
const separatorTokens = '\n\n---\n';
reasoningTokens.push(separatorTokens);
onProgress(separatorTokens);
}
intermediateReply.push(token);
onProgress(token);
if (abortController.signal.aborted) {
@ -1360,6 +1372,10 @@ ${convo}
return reply;
}
if (reasoningTokens.length > 0) {
return reasoningTokens.join('') + message.content;
}
return message.content;
} catch (err) {
if (

View file

@ -88,6 +88,19 @@ describe('BaseClient', () => {
const messages = [{ content: 'Hello' }, { content: 'How are you?' }, { content: 'Goodbye' }];
const instructions = { content: 'Please respond to the question.' };
const result = TestClient.addInstructions(messages, instructions);
const expected = [
{ content: 'Please respond to the question.' },
{ content: 'Hello' },
{ content: 'How are you?' },
{ content: 'Goodbye' },
];
expect(result).toEqual(expected);
});
test('returns the input messages with instructions properly added when addInstructions() with legacy flag', () => {
const messages = [{ content: 'Hello' }, { content: 'How are you?' }, { content: 'Goodbye' }];
const instructions = { content: 'Please respond to the question.' };
const result = TestClient.addInstructions(messages, instructions, true);
const expected = [
{ content: 'Hello' },
{ content: 'How are you?' },

View file

@ -911,7 +911,8 @@ export default {
com_ui_page: 'صفحة',
com_ui_bookmarks_add: 'إضافة إشارات مرجعية',
com_endpoint_ai: 'الذكاء الاصطناعي',
com_endpoint_message_new: 'الرسالة {0} أو اكتب "@" للتبديل إلى الذكاء الاصطناعي',
com_nav_maximize_chat_space: 'تكبير مساحة الدردشة',
com_ui_collapse_chat: 'طي الدردشة',
com_endpoint_message_new: 'رسالة {0}',
com_ui_speech_while_submitting: 'لا يمكن إرسال الكلام أثناء إنشاء الرد',
};

View file

@ -943,7 +943,8 @@ export default {
com_endpoint_ai: 'KI',
com_ui_page: 'Seite',
com_ui_bookmarks_add: 'Lesezeichen hinzufügen',
com_endpoint_message_new: 'Nachricht {0} oder "@" eingeben, um KI zu wechseln',
com_nav_maximize_chat_space: 'Chat-Bereich maximieren',
com_ui_collapse_chat: 'Chat einklappen',
com_ui_speech_while_submitting: 'Spracheingabe nicht möglich während eine Antwort generiert wird',
com_endpoint_message_new: 'Nachricht {0}',
};

View file

@ -527,7 +527,7 @@ export default {
com_endpoint_system_message: 'System Message',
com_endpoint_message: 'Message',
com_endpoint_ai: 'AI',
com_endpoint_message_new: 'Message {0} or type "@" to switch AI',
com_endpoint_message_new: 'Message {0}',
com_endpoint_message_not_appendable: 'Edit your message or Regenerate.',
com_endpoint_default_blank: 'default: blank',
com_endpoint_default_false: 'default: false',

View file

@ -1198,8 +1198,10 @@ export default {
com_ui_bookmarks_add: 'Agregar Marcadores',
com_ui_page: 'Página',
com_ui_bookmarks_edit: 'Editar Marcador',
com_endpoint_message_new: 'Mensaje {0} o escriba "@" para cambiar de IA',
com_nav_maximize_chat_space: 'Maximizar espacio del chat',
com_endpoint_ai: 'IA',
com_ui_collapse_chat: 'Contraer Chat',
com_endpoint_message_new: 'Mensaje {0}',
com_ui_speech_while_submitting:
'No se puede enviar un mensaje de voz mientras se está generando una respuesta',
};

View file

@ -961,7 +961,9 @@ export default {
com_ui_bookmarks_edit: 'Modifier le signet',
com_endpoint_ai: 'IA',
com_nav_maximize_chat_space: 'Maximiser l\'espace de discussion',
com_endpoint_message_new: 'Message {0} ou tapez "@" pour changer d\'IA',
com_ui_page: 'Page',
com_ui_collapse_chat: 'Réduire la discussion',
com_ui_speech_while_submitting:
'Impossible de soumettre un message vocal pendant la génération d\'une réponse',
com_endpoint_message_new: 'Message {0}',
};

View file

@ -953,9 +953,11 @@ export default {
com_ui_delete_shared_link: 'Eliminare il link condiviso?',
com_ui_bookmarks_add: 'Aggiungi Segnalibri',
com_ui_bookmarks_edit: 'Modifica Segnalibro',
com_endpoint_message_new: 'Messaggio {0} oppure digita "@" per cambiare IA',
com_endpoint_ai: 'IA',
com_nav_maximize_chat_space: 'Massimizza spazio chat',
com_ui_page: 'Pagina',
com_ui_collapse_chat: 'Comprimi Chat',
com_endpoint_message_new: 'Messaggio {0}',
com_ui_speech_while_submitting:
'Impossibile inviare il messaggio mentre è in corso la generazione di una risposta',
};

View file

@ -909,7 +909,8 @@ export default {
com_ui_page: 'ページ',
com_ui_bookmarks_edit: 'ブックマークを編集',
com_endpoint_ai: 'AI',
com_endpoint_message_new: 'メッセージ {0} または「@」を入力してAIを切り替え',
com_nav_maximize_chat_space: 'チャット画面を最大化',
com_ui_collapse_chat: 'チャットを折りたたむ',
com_endpoint_message_new: 'メッセージ {0}',
com_ui_speech_while_submitting: '応答の生成中は音声を送信できません',
};

View file

@ -1148,6 +1148,7 @@ export default {
com_ui_page: '페이지',
com_endpoint_ai: '인공지능',
com_nav_maximize_chat_space: '채팅창 최대화',
com_endpoint_message_new: '메시지 {0} 또는 "@"를 입력하여 AI 전환',
com_ui_collapse_chat: '채팅 접기',
com_endpoint_message_new: '메시지 {0}',
com_ui_speech_while_submitting: '응답 생성 중에는 음성을 전송할 수 없습니다',
};

View file

@ -1172,8 +1172,9 @@ export default {
com_ui_bookmarks_edit: 'Редактировать закладку',
com_ui_page: 'Страница',
com_endpoint_ai: 'ИИ',
com_endpoint_message_new: 'Сообщение {0} или введите "@" для смены ИИ',
com_nav_maximize_chat_space: 'Развернуть чат',
com_ui_bookmarks_add: 'Добавить закладку',
com_ui_collapse_chat: 'Свернуть чат',
com_endpoint_message_new: 'Сообщение {0}',
com_ui_speech_while_submitting: 'Невозможно отправить голосовой ввод во время генерации ответа',
};

View file

@ -902,6 +902,7 @@ export default {
com_endpoint_ai: '人工智能',
com_ui_page: '页面',
com_nav_maximize_chat_space: '最大化聊天窗口',
com_endpoint_message_new: '发送消息 {0} 或输入"@"切换AI',
com_ui_collapse_chat: '收起聊天',
com_endpoint_message_new: '消息 {0}',
com_ui_speech_while_submitting: '正在生成回复时无法提交语音',
};

View file

@ -879,6 +879,7 @@ export default {
com_ui_page: '頁面',
com_nav_maximize_chat_space: '最大化聊天視窗',
com_endpoint_ai: 'AI',
com_endpoint_message_new: '輸入訊息 {0} 或輸入 "@" 以切換 AI',
com_ui_collapse_chat: '收合對話',
com_endpoint_message_new: '訊息 {0}',
com_ui_speech_while_submitting: '正在產生回應時無法送出語音',
};