Migrate Cohere to /v2/chat Closes #2916

c2935ba4a70c17e8ba00312f6779ad46ea420db5

Cohee <18619528+Cohee1207@users.noreply.github.com>

24 files changed, +102 -253Showing whitespace changes
default/config.yaml+4 -0
@@ -155,5 +155,9 @@ claude:
155 # (e.g {{random}} macro or lorebooks not as in-chat injections).155 # (e.g {{random}} macro or lorebooks not as in-chat injections).
156 # Otherwise, you'll just waste money on cache misses.156 # Otherwise, you'll just waste money on cache misses.
157 enableSystemPromptCache: false157 enableSystemPromptCache: false
158# -- COHERE API CONFIGURATION --
159cohere:
160 # A placeholder prompt to be used when the message array doesn't end with a user message
161 userPlaceholder: "Continue"
158# -- SERVER PLUGIN CONFIGURATION --162# -- SERVER PLUGIN CONFIGURATION --
159enableServerPlugins: false163enableServerPlugins: false
public/index.html+0 -11
@@ -595,17 +595,6 @@
595 </span>595 </span>
596 </div>596 </div>
597 </div>597 </div>
598 <div class="range-block" data-source="cohere">
599 <label for="websearch_toggle" title="Enable Cohere web-search connector" data-i18n="[title]Enable Cohere web-search connector" class="checkbox_label widthFreeExpand">
600 <input id="websearch_toggle" type="checkbox" />
601 <span data-i18n="Web-search">Web-search</span>
602 </label>
603 <div class="toggle-description justifyLeft">
604 <span data-i18n="Allow the model to use the web-search connector.">
605 Allow the model to use the web-search connector.
606 </span>
607 </div>
608 </div>
609 <div class="range-block" data-source="openai,claude,windowai,openrouter,ai21,scale,makersuite,mistralai,custom,cohere,perplexity,groq,01ai">598 <div class="range-block" data-source="openai,claude,windowai,openrouter,ai21,scale,makersuite,mistralai,custom,cohere,perplexity,groq,01ai">
610 <div class="range-block-title" data-i18n="Temperature">599 <div class="range-block-title" data-i18n="Temperature">
611 Temperature600 Temperature
public/locales/ar-sa.json+0 -3
@@ -56,9 +56,6 @@
56 "Max Response Length (tokens)": "الحد الأقصى لطول الاستجابة (الرموز,الحرف)",56 "Max Response Length (tokens)": "الحد الأقصى لطول الاستجابة (الرموز,الحرف)",
57 "Multiple swipes per generation": "الضربات الشديدة المتعددة لكل جيل",57 "Multiple swipes per generation": "الضربات الشديدة المتعددة لكل جيل",
58 "Enable OpenAI completion streaming": "تمكين بث الاكتمال من OpenAI",58 "Enable OpenAI completion streaming": "تمكين بث الاكتمال من OpenAI",
59 "Enable Cohere web-search connector": "تمكين موصل بحث الويب Cohere",
60 "Web-search": "البحث في الويب",
61 "Allow the model to use the web-search connector.": "اسمح للنموذج باستخدام موصل بحث الويب.",
62 "Frequency Penalty": "عقوبة التكرار",59 "Frequency Penalty": "عقوبة التكرار",
63 "Presence Penalty": "عقوبة الوجود",60 "Presence Penalty": "عقوبة الوجود",
64 "Count Penalty": "عد ضربة جزاء",61 "Count Penalty": "عد ضربة جزاء",
public/locales/de-de.json+0 -3
@@ -56,9 +56,6 @@
56 "Max Response Length (tokens)": "Maximale Antwortlänge (Tokens)",56 "Max Response Length (tokens)": "Maximale Antwortlänge (Tokens)",
57 "Multiple swipes per generation": "Mehrere Swipes pro Generation",57 "Multiple swipes per generation": "Mehrere Swipes pro Generation",
58 "Enable OpenAI completion streaming": "OpenAI-Vervollständigungsstreaming aktivieren",58 "Enable OpenAI completion streaming": "OpenAI-Vervollständigungsstreaming aktivieren",
59 "Enable Cohere web-search connector": "Cohere-Websuch-Connector aktivieren",
60 "Web-search": "Web-Suche",
61 "Allow the model to use the web-search connector.": "Erlauben Sie dem Modell, den Websuch-Connector zu verwenden.",
62 "Frequency Penalty": "Frequenzstrafe",59 "Frequency Penalty": "Frequenzstrafe",
63 "Presence Penalty": "Präsenzstrafe",60 "Presence Penalty": "Präsenzstrafe",
64 "Count Penalty": "Strafe zählen",61 "Count Penalty": "Strafe zählen",
public/locales/es-es.json+0 -3
@@ -56,9 +56,6 @@
56 "Max Response Length (tokens)": "Longitud máxima de respuesta (tokens)",56 "Max Response Length (tokens)": "Longitud máxima de respuesta (tokens)",
57 "Multiple swipes per generation": "Múltiples golpes por generación",57 "Multiple swipes per generation": "Múltiples golpes por generación",
58 "Enable OpenAI completion streaming": "Activar streaming de completado de OpenAI",58 "Enable OpenAI completion streaming": "Activar streaming de completado de OpenAI",
59 "Enable Cohere web-search connector": "Habilitar el conector de búsqueda web de Cohere",
60 "Web-search": "Búsqueda Web",
61 "Allow the model to use the web-search connector.": "Permita que el modelo utilice el conector de búsqueda web.",
62 "Frequency Penalty": "Penalización de frecuencia",59 "Frequency Penalty": "Penalización de frecuencia",
63 "Presence Penalty": "Penalización de presencia",60 "Presence Penalty": "Penalización de presencia",
64 "Count Penalty": "Penalización de conteo",61 "Count Penalty": "Penalización de conteo",
public/locales/fr-fr.json+0 -3
@@ -56,9 +56,6 @@
56 "Max Response Length (tokens)": "Longueur maximale de la réponse (tokens)",56 "Max Response Length (tokens)": "Longueur maximale de la réponse (tokens)",
57 "Multiple swipes per generation": "Plusieurs balayages par génération",57 "Multiple swipes per generation": "Plusieurs balayages par génération",
58 "Enable OpenAI completion streaming": "Activer le streaming de complétion OpenAI",58 "Enable OpenAI completion streaming": "Activer le streaming de complétion OpenAI",
59 "Enable Cohere web-search connector": "Activer le connecteur de recherche Web Cohere",
60 "Web-search": "Recherche Internet",
61 "Allow the model to use the web-search connector.": "Autorisez le modèle à utiliser le connecteur de recherche Web.",
62 "Frequency Penalty": "Pénalité de fréquence",59 "Frequency Penalty": "Pénalité de fréquence",
63 "Presence Penalty": "Pénalité de présence",60 "Presence Penalty": "Pénalité de présence",
64 "Count Penalty": "Pénalité de décompte",61 "Count Penalty": "Pénalité de décompte",
public/locales/is-is.json+0 -3
@@ -56,9 +56,6 @@
56 "Max Response Length (tokens)": "Hámarks lengd svörunar (í táknum)",56 "Max Response Length (tokens)": "Hámarks lengd svörunar (í táknum)",
57 "Multiple swipes per generation": "Mörg högg á hverja kynslóð",57 "Multiple swipes per generation": "Mörg högg á hverja kynslóð",
58 "Enable OpenAI completion streaming": "Virkja OpenAI klárastreymi",58 "Enable OpenAI completion streaming": "Virkja OpenAI klárastreymi",
59 "Enable Cohere web-search connector": "Virkja Cohere vefleitartengi",
60 "Web-search": "Vefleit",
61 "Allow the model to use the web-search connector.": "Leyfðu líkaninu að nota vefleitartengið.",
62 "Frequency Penalty": "Tíðnarefning",59 "Frequency Penalty": "Tíðnarefning",
63 "Presence Penalty": "Tilkoma refning",60 "Presence Penalty": "Tilkoma refning",
64 "Count Penalty": "Telja víti",61 "Count Penalty": "Telja víti",
public/locales/it-it.json+0 -3
@@ -56,9 +56,6 @@
56 "Max Response Length (tokens)": "Lunghezza massima della risposta (token)",56 "Max Response Length (tokens)": "Lunghezza massima della risposta (token)",
57 "Multiple swipes per generation": "Più passaggi per generazione",57 "Multiple swipes per generation": "Più passaggi per generazione",
58 "Enable OpenAI completion streaming": "Abilita lo streaming di completamento OpenAI",58 "Enable OpenAI completion streaming": "Abilita lo streaming di completamento OpenAI",
59 "Enable Cohere web-search connector": "Abilita il connettore di ricerca web Cohere",
60 "Web-search": "Ricerca sul web",
61 "Allow the model to use the web-search connector.": "Consenti al modello di utilizzare il connettore di ricerca web.",
62 "Frequency Penalty": "Penalità di frequenza",59 "Frequency Penalty": "Penalità di frequenza",
63 "Presence Penalty": "Penalità di presenza",60 "Presence Penalty": "Penalità di presenza",
64 "Count Penalty": "Conte Penalità",61 "Count Penalty": "Conte Penalità",
public/locales/ja-jp.json+0 -3
@@ -56,9 +56,6 @@
56 "Max Response Length (tokens)": "最大応答長(トークン数)",56 "Max Response Length (tokens)": "最大応答長(トークン数)",
57 "Multiple swipes per generation": "世代ごとに複数のスワイプ",57 "Multiple swipes per generation": "世代ごとに複数のスワイプ",
58 "Enable OpenAI completion streaming": "OpenAIの完了ストリーミングを有効にする",58 "Enable OpenAI completion streaming": "OpenAIの完了ストリーミングを有効にする",
59 "Enable Cohere web-search connector": "Cohereウェブ検索コネクタを有効にする",
60 "Web-search": "ウェブ検索",
61 "Allow the model to use the web-search connector.": "モデルが Web 検索コネクタを使用できるようにします。",
62 "Frequency Penalty": "頻度ペナルティ",59 "Frequency Penalty": "頻度ペナルティ",
63 "Presence Penalty": "存在ペナルティ",60 "Presence Penalty": "存在ペナルティ",
64 "Count Penalty": "カウントペナルティ",61 "Count Penalty": "カウントペナルティ",
public/locales/ko-kr.json+0 -3
@@ -56,9 +56,6 @@
56 "Max Response Length (tokens)": "최대 응답 길이 (토큰)",56 "Max Response Length (tokens)": "최대 응답 길이 (토큰)",
57 "Multiple swipes per generation": "세대당 다중 스와이프",57 "Multiple swipes per generation": "세대당 다중 스와이프",
58 "Enable OpenAI completion streaming": "OpenAI 완성 스트리밍 활성화",58 "Enable OpenAI completion streaming": "OpenAI 완성 스트리밍 활성화",
59 "Enable Cohere web-search connector": "Cohere 웹 검색 커넥터 활성화",
60 "Web-search": "웹 서핑",
61 "Allow the model to use the web-search connector.": "모델이 웹 검색 커넥터를 사용하도록 허용합니다.",
62 "Frequency Penalty": "빈도 패널티",59 "Frequency Penalty": "빈도 패널티",
63 "Presence Penalty": "존재 패널티",60 "Presence Penalty": "존재 패널티",
64 "Count Penalty": "카운트 페널티",61 "Count Penalty": "카운트 페널티",
public/locales/nl-nl.json+0 -3
@@ -56,9 +56,6 @@
56 "Max Response Length (tokens)": "Maximale lengte van het antwoord (tokens)",56 "Max Response Length (tokens)": "Maximale lengte van het antwoord (tokens)",
57 "Multiple swipes per generation": "Meerdere swipes per generatie",57 "Multiple swipes per generation": "Meerdere swipes per generatie",
58 "Enable OpenAI completion streaming": "OpenAI voltooiingsstreaming inschakelen",58 "Enable OpenAI completion streaming": "OpenAI voltooiingsstreaming inschakelen",
59 "Enable Cohere web-search connector": "Schakel de Cohere-webzoekconnector in",
60 "Web-search": "Web-zoeken",
61 "Allow the model to use the web-search connector.": "Sta toe dat het model de webzoekconnector gebruikt.",
62 "Frequency Penalty": "Frequentieboete",59 "Frequency Penalty": "Frequentieboete",
63 "Presence Penalty": "Aanwezigheidsboete",60 "Presence Penalty": "Aanwezigheidsboete",
64 "Count Penalty": "Tel straf",61 "Count Penalty": "Tel straf",
public/locales/pt-pt.json+0 -3
@@ -56,9 +56,6 @@
56 "Max Response Length (tokens)": "Comprimento Máximo da Resposta (tokens)",56 "Max Response Length (tokens)": "Comprimento Máximo da Resposta (tokens)",
57 "Multiple swipes per generation": "Vários furtos por geração",57 "Multiple swipes per generation": "Vários furtos por geração",
58 "Enable OpenAI completion streaming": "Ativar streaming de conclusão do OpenAI",58 "Enable OpenAI completion streaming": "Ativar streaming de conclusão do OpenAI",
59 "Enable Cohere web-search connector": "Ativar o conector de pesquisa na Web Cohere",
60 "Web-search": "Pesquisa na internet",
61 "Allow the model to use the web-search connector.": "Permita que o modelo use o conector de pesquisa na Web.",
62 "Frequency Penalty": "Pena de Frequência",59 "Frequency Penalty": "Pena de Frequência",
63 "Presence Penalty": "Pena de Presença",60 "Presence Penalty": "Pena de Presença",
64 "Count Penalty": "Contar penalidade",61 "Count Penalty": "Contar penalidade",
public/locales/ru-ru.json+0 -3
@@ -772,7 +772,6 @@
772 "Type a message, or /? for help": "Введите сообщение, или /? для получения справки",772 "Type a message, or /? for help": "Введите сообщение, или /? для получения справки",
773 "Welcome to SillyTavern!": "Добро пожаловать в SillyTavern!",773 "Welcome to SillyTavern!": "Добро пожаловать в SillyTavern!",
774 "Won't be shared with the character card on export.": "Не попадут в карточку персонажа при экспорте.",774 "Won't be shared with the character card on export.": "Не попадут в карточку персонажа при экспорте.",
775 "Web-search": "Веб-поиск",
776 "Persona Name:": "Имя персоны:",775 "Persona Name:": "Имя персоны:",
777 "User first message": "Первое сообщение пользователя",776 "User first message": "Первое сообщение пользователя",
778 "extension_token_counter": "Токенов:",777 "extension_token_counter": "Токенов:",
@@ -1200,8 +1199,6 @@
1200 "Streaming_desc": "Выводить текст последовательно по мере его генерации.\rЕсли параметр выключен, ответы будут отображаться сразу целиком, и только после полного завершения генерации.",1199 "Streaming_desc": "Выводить текст последовательно по мере его генерации.\rЕсли параметр выключен, ответы будут отображаться сразу целиком, и только после полного завершения генерации.",
1201 "Max prompt cost:": "Max prompt cost:",1200 "Max prompt cost:": "Max prompt cost:",
1202 "TFS": "TFS",1201 "TFS": "TFS",
1203 "Enable Cohere web-search connector": "Enable Cohere web-search connector",
1204 "Allow the model to use the web-search connector.": "Allow the model to use the web-search connector.",
1205 "Count Penalty": "Count Penalty",1202 "Count Penalty": "Count Penalty",
1206 "Min P": "Min P",1203 "Min P": "Min P",
1207 "NSFW": "NSFW",1204 "NSFW": "NSFW",
public/locales/uk-ua.json+0 -3
@@ -56,9 +56,6 @@
56 "Max Response Length (tokens)": "Довжина відповіді (токени)",56 "Max Response Length (tokens)": "Довжина відповіді (токени)",
57 "Multiple swipes per generation": "Кілька свайпів за покоління",57 "Multiple swipes per generation": "Кілька свайпів за покоління",
58 "Enable OpenAI completion streaming": "Увімкнути потокове завершення OpenAI",58 "Enable OpenAI completion streaming": "Увімкнути потокове завершення OpenAI",
59 "Enable Cohere web-search connector": "Увімкнути конектор веб-пошуку Cohere",
60 "Web-search": "Веб-пошук",
61 "Allow the model to use the web-search connector.": "Дозвольте моделі використовувати конектор веб-пошуку.",
62 "Frequency Penalty": "Штраф за частоту",59 "Frequency Penalty": "Штраф за частоту",
63 "Presence Penalty": "Штраф за наявність",60 "Presence Penalty": "Штраф за наявність",
64 "Count Penalty": "Рахувати пенальті",61 "Count Penalty": "Рахувати пенальті",
public/locales/vi-vn.json+0 -3
@@ -56,9 +56,6 @@
56 "Max Response Length (tokens)": "Độ dài phản hồi tối đa (token)",56 "Max Response Length (tokens)": "Độ dài phản hồi tối đa (token)",
57 "Multiple swipes per generation": "Vuốt nhiều lần trong một lần tạo",57 "Multiple swipes per generation": "Vuốt nhiều lần trong một lần tạo",
58 "Enable OpenAI completion streaming": "Bật streaming của OpenAI",58 "Enable OpenAI completion streaming": "Bật streaming của OpenAI",
59 "Enable Cohere web-search connector": "Bật web tìm kiếm của Cohere",
60 "Web-search": "Tìm kiếm trên web",
61 "Allow the model to use the web-search connector.": "Cho phép model sử dụng trình kết nối tìm kiếm trên web.",
62 "Frequency Penalty": "Frequency Penalty",59 "Frequency Penalty": "Frequency Penalty",
63 "Presence Penalty": "Presence Penalty",60 "Presence Penalty": "Presence Penalty",
64 "Count Penalty": "Count Penalty",61 "Count Penalty": "Count Penalty",
public/locales/zh-cn.json+0 -3
@@ -56,9 +56,6 @@
56 "Max Response Length (tokens)": "最大回复长度(以词符数计)",56 "Max Response Length (tokens)": "最大回复长度(以词符数计)",
57 "Multiple swipes per generation": "每次生成多个备选回复",57 "Multiple swipes per generation": "每次生成多个备选回复",
58 "Enable OpenAI completion streaming": "启用OpenAI文本补全流式传输",58 "Enable OpenAI completion streaming": "启用OpenAI文本补全流式传输",
59 "Enable Cohere web-search connector": "启用 Cohere 网络搜索连接器",
60 "Web-search": "联网搜索",
61 "Allow the model to use the web-search connector.": "允许模型使用联网搜索。",
62 "Frequency Penalty": "频率惩罚",59 "Frequency Penalty": "频率惩罚",
63 "Presence Penalty": "存在惩罚",60 "Presence Penalty": "存在惩罚",
64 "Count Penalty": "计数惩罚",61 "Count Penalty": "计数惩罚",
public/locales/zh-tw.json+0 -3
@@ -56,9 +56,6 @@
56 "Max Response Length (tokens)": "最大回應長度(符記數)",56 "Max Response Length (tokens)": "最大回應長度(符記數)",
57 "Multiple swipes per generation": "每次生成多次滑動",57 "Multiple swipes per generation": "每次生成多次滑動",
58 "Enable OpenAI completion streaming": "啟用 OpenAI 補充串流",58 "Enable OpenAI completion streaming": "啟用 OpenAI 補充串流",
59 "Enable Cohere web-search connector": "啟用 Cohere 網頁搜尋連接器",
60 "Web-search": "網頁搜尋",
61 "Allow the model to use the web-search connector.": "允許模型使用網頁搜尋連接器",
62 "Frequency Penalty": "頻率懲罰",59 "Frequency Penalty": "頻率懲罰",
63 "Presence Penalty": "存在懲罰",60 "Presence Penalty": "存在懲罰",
64 "Count Penalty": "計數懲罰",61 "Count Penalty": "計數懲罰",
public/script.js+1 -1
@@ -5436,7 +5436,7 @@ function extractMessageFromData(data) {
5436 case 'novel':5436 case 'novel':
5437 return data.output;5437 return data.output;
5438 case 'openai':5438 case 'openai':
5439 return data?.choices?.[0]?.message?.content ?? data?.choices?.[0]?.text ?? data?.text ?? '';5439 return data?.choices?.[0]?.message?.content ?? data?.choices?.[0]?.text ?? data?.text ?? data?.message?.tool_plan ?? data?.message?.content?.[0]?.text ?? '';
5440 default:5440 default:
5441 return '';5441 return '';
5442 }5442 }
public/scripts/openai.js+3 -13
@@ -225,7 +225,6 @@ const default_settings = {
225 top_a_openai: 0,225 top_a_openai: 0,
226 repetition_penalty_openai: 1,226 repetition_penalty_openai: 1,
227 stream_openai: false,227 stream_openai: false,
228 websearch_cohere: false,
229 openai_max_context: max_4k,228 openai_max_context: max_4k,
230 openai_max_tokens: 300,229 openai_max_tokens: 300,
231 wrap_in_quotes: false,230 wrap_in_quotes: false,
@@ -302,7 +301,6 @@ const oai_settings = {
302 top_a_openai: 0,301 top_a_openai: 0,
303 repetition_penalty_openai: 1,302 repetition_penalty_openai: 1,
304 stream_openai: false,303 stream_openai: false,
305 websearch_cohere: false,
306 openai_max_context: max_4k,304 openai_max_context: max_4k,
307 openai_max_tokens: 300,305 openai_max_tokens: 300,
308 wrap_in_quotes: false,306 wrap_in_quotes: false,
@@ -1847,7 +1845,6 @@ async function sendOpenAIRequest(type, messages, signal) {
1847 generate_data['frequency_penalty'] = Math.min(Math.max(Number(oai_settings.freq_pen_openai), 0), 1);1845 generate_data['frequency_penalty'] = Math.min(Math.max(Number(oai_settings.freq_pen_openai), 0), 1);
1848 generate_data['presence_penalty'] = Math.min(Math.max(Number(oai_settings.pres_pen_openai), 0), 1);1846 generate_data['presence_penalty'] = Math.min(Math.max(Number(oai_settings.pres_pen_openai), 0), 1);
1849 generate_data['stop'] = getCustomStoppingStrings(5);1847 generate_data['stop'] = getCustomStoppingStrings(5);
1850 generate_data['websearch'] = oai_settings.websearch_cohere;
1851 }1848 }
18521849
1853 if (isPerplexity) {1850 if (isPerplexity) {
@@ -1980,8 +1977,10 @@ function getStreamingReply(data) {
1980 return data?.delta?.text || '';1977 return data?.delta?.text || '';
1981 } else if (oai_settings.chat_completion_source == chat_completion_sources.MAKERSUITE) {1978 } else if (oai_settings.chat_completion_source == chat_completion_sources.MAKERSUITE) {
1982 return data?.candidates?.[0]?.content?.parts?.[0]?.text || '';1979 return data?.candidates?.[0]?.content?.parts?.[0]?.text || '';
1980 } else if (oai_settings.chat_completion_source == chat_completion_sources.COHERE) {
1981 return data?.delta?.message?.content?.text || data?.delta?.message?.tool_plan || '';
1983 } else {1982 } else {
1984 return data.choices[0]?.delta?.content ?? data.choices[0]?.message?.content ?? data.choices[0]?.text ?? '';1983 return data.choices?.[0]?.delta?.content ?? data.choices?.[0]?.message?.content ?? data.choices?.[0]?.text ?? '';
1985 }1984 }
1986}1985}
19871986
@@ -2857,7 +2856,6 @@ function loadOpenAISettings(data, settings) {
2857 oai_settings.min_p_openai = settings.min_p_openai ?? default_settings.min_p_openai;2856 oai_settings.min_p_openai = settings.min_p_openai ?? default_settings.min_p_openai;
2858 oai_settings.repetition_penalty_openai = settings.repetition_penalty_openai ?? default_settings.repetition_penalty_openai;2857 oai_settings.repetition_penalty_openai = settings.repetition_penalty_openai ?? default_settings.repetition_penalty_openai;
2859 oai_settings.stream_openai = settings.stream_openai ?? default_settings.stream_openai;2858 oai_settings.stream_openai = settings.stream_openai ?? default_settings.stream_openai;
2860 oai_settings.websearch_cohere = settings.websearch_cohere ?? default_settings.websearch_cohere;
2861 oai_settings.openai_max_context = settings.openai_max_context ?? default_settings.openai_max_context;2859 oai_settings.openai_max_context = settings.openai_max_context ?? default_settings.openai_max_context;
2862 oai_settings.openai_max_tokens = settings.openai_max_tokens ?? default_settings.openai_max_tokens;2860 oai_settings.openai_max_tokens = settings.openai_max_tokens ?? default_settings.openai_max_tokens;
2863 oai_settings.bias_preset_selected = settings.bias_preset_selected ?? default_settings.bias_preset_selected;2861 oai_settings.bias_preset_selected = settings.bias_preset_selected ?? default_settings.bias_preset_selected;
@@ -2931,7 +2929,6 @@ function loadOpenAISettings(data, settings) {
2931 if (settings.use_makersuite_sysprompt !== undefined) oai_settings.use_makersuite_sysprompt = !!settings.use_makersuite_sysprompt;2929 if (settings.use_makersuite_sysprompt !== undefined) oai_settings.use_makersuite_sysprompt = !!settings.use_makersuite_sysprompt;
2932 if (settings.use_alt_scale !== undefined) { oai_settings.use_alt_scale = !!settings.use_alt_scale; updateScaleForm(); }2930 if (settings.use_alt_scale !== undefined) { oai_settings.use_alt_scale = !!settings.use_alt_scale; updateScaleForm(); }
2933 $('#stream_toggle').prop('checked', oai_settings.stream_openai);2931 $('#stream_toggle').prop('checked', oai_settings.stream_openai);
2934 $('#websearch_toggle').prop('checked', oai_settings.websearch_cohere);
2935 $('#api_url_scale').val(oai_settings.api_url_scale);2932 $('#api_url_scale').val(oai_settings.api_url_scale);
2936 $('#openai_proxy_password').val(oai_settings.proxy_password);2933 $('#openai_proxy_password').val(oai_settings.proxy_password);
2937 $('#claude_assistant_prefill').val(oai_settings.assistant_prefill);2934 $('#claude_assistant_prefill').val(oai_settings.assistant_prefill);
@@ -3258,7 +3255,6 @@ async function saveOpenAIPreset(name, settings, triggerUi = true) {
3258 personality_format: settings.personality_format,3255 personality_format: settings.personality_format,
3259 group_nudge_prompt: settings.group_nudge_prompt,3256 group_nudge_prompt: settings.group_nudge_prompt,
3260 stream_openai: settings.stream_openai,3257 stream_openai: settings.stream_openai,
3261 websearch_cohere: settings.websearch_cohere,
3262 prompts: settings.prompts,3258 prompts: settings.prompts,
3263 prompt_order: settings.prompt_order,3259 prompt_order: settings.prompt_order,
3264 api_url_scale: settings.api_url_scale,3260 api_url_scale: settings.api_url_scale,
@@ -3682,7 +3678,6 @@ function onSettingsPresetChange() {
3682 personality_format: ['#personality_format_textarea', 'personality_format', false],3678 personality_format: ['#personality_format_textarea', 'personality_format', false],
3683 group_nudge_prompt: ['#group_nudge_prompt_textarea', 'group_nudge_prompt', false],3679 group_nudge_prompt: ['#group_nudge_prompt_textarea', 'group_nudge_prompt', false],
3684 stream_openai: ['#stream_toggle', 'stream_openai', true],3680 stream_openai: ['#stream_toggle', 'stream_openai', true],
3685 websearch_cohere: ['#websearch_toggle', 'websearch_cohere', true],
3686 prompts: ['', 'prompts', false],3681 prompts: ['', 'prompts', false],
3687 prompt_order: ['', 'prompt_order', false],3682 prompt_order: ['', 'prompt_order', false],
3688 api_url_scale: ['#api_url_scale', 'api_url_scale', false],3683 api_url_scale: ['#api_url_scale', 'api_url_scale', false],
@@ -4846,11 +4841,6 @@ export function initOpenAI() {
4846 saveSettingsDebounced();4841 saveSettingsDebounced();
4847 });4842 });
48484843
4849 $('#websearch_toggle').on('change', function () {
4850 oai_settings.websearch_cohere = !!$('#websearch_toggle').prop('checked');
4851 saveSettingsDebounced();
4852 });
4853
4854 $('#wrap_in_quotes').on('change', function () {4844 $('#wrap_in_quotes').on('change', function () {
4855 oai_settings.wrap_in_quotes = !!$('#wrap_in_quotes').prop('checked');4845 oai_settings.wrap_in_quotes = !!$('#wrap_in_quotes').prop('checked');
4856 saveSettingsDebounced();4846 saveSettingsDebounced();
public/scripts/sse-stream.js+14 -2
@@ -108,9 +108,21 @@ function getDelay(s) {
108 * @returns {AsyncGenerator<{data: object, chunk: string}>} The parsed data and the chunk to be sent.108 * @returns {AsyncGenerator<{data: object, chunk: string}>} The parsed data and the chunk to be sent.
109 */109 */
110async function* parseStreamData(json) {110async function* parseStreamData(json) {
111 // Cohere
112 if (typeof json.delta.message === 'object' && ['tool-plan-delta', 'content-delta'].includes(json.type)) {
113 const text = json?.delta?.message?.content?.text ?? '';
114 for (let i = 0; i < text.length; i++) {
115 const str = json.delta.message.content.text[i];
116 yield {
117 data: { ...json, delta: { message: { content: { text: str } } } },
118 chunk: str,
119 };
120 }
121 return;
122 }
111 // Claude123 // Claude
112 if (typeof json.delta === 'object') {124 if (typeof json.delta === 'object' && typeof json.delta.text === 'string') {
113 if (typeof json.delta.text === 'string' && json.delta.text.length > 0) {125 if (json.delta.text.length > 0) {
114 for (let i = 0; i < json.delta.text.length; i++) {126 for (let i = 0; i < json.delta.text.length; i++) {
115 const str = json.delta.text[i];127 const str = json.delta.text[i];
116 yield {128 yield {
public/scripts/tool-calling.js+31 -1
@@ -381,6 +381,22 @@ export class ToolManager {
381 }381 }
382 }382 }
383 }383 }
384 const cohereToolEvents = ['message-start', 'tool-call-start', 'tool-call-delta', 'tool-call-end'];
385 if (cohereToolEvents.includes(parsed?.type) && typeof parsed?.delta?.message === 'object') {
386 const choiceIndex = 0;
387 const toolCallIndex = parsed?.index ?? 0;
388
389 if (!Array.isArray(toolCalls[choiceIndex])) {
390 toolCalls[choiceIndex] = [];
391 }
392
393 if (toolCalls[choiceIndex][toolCallIndex] === undefined) {
394 toolCalls[choiceIndex][toolCallIndex] = {};
395 }
396
397 const targetToolCall = toolCalls[choiceIndex][toolCallIndex];
398 ToolManager.#applyToolCallDelta(targetToolCall, parsed.delta.message);
399 }
384 if (typeof parsed?.content_block === 'object') {400 if (typeof parsed?.content_block === 'object') {
385 const choiceIndex = 0;401 const choiceIndex = 0;
386 const toolCallIndex = parsed?.index ?? 0;402 const toolCallIndex = parsed?.index ?? 0;
@@ -483,6 +499,7 @@ export class ToolManager {
483 chat_completion_sources.CLAUDE,499 chat_completion_sources.CLAUDE,
484 chat_completion_sources.OPENROUTER,500 chat_completion_sources.OPENROUTER,
485 chat_completion_sources.GROQ,501 chat_completion_sources.GROQ,
502 chat_completion_sources.COHERE,
486 ];503 ];
487 return supportedSources.includes(oai_settings.chat_completion_source);504 return supportedSources.includes(oai_settings.chat_completion_source);
488 }505 }
@@ -509,7 +526,15 @@ export class ToolManager {
509526
510 // Parsed tool calls from streaming data527 // Parsed tool calls from streaming data
511 if (Array.isArray(data) && data.length > 0 && Array.isArray(data[0])) {528 if (Array.isArray(data) && data.length > 0 && Array.isArray(data[0])) {
512 return isClaudeToolCall(data[0]) ? data[0].filter(x => x).map(convertClaudeToolCall) : data[0];529 if (isClaudeToolCall(data[0])) {
530 return data[0].filter(x => x).map(convertClaudeToolCall);
531 }
532
533 if (typeof data[0]?.[0]?.tool_calls === 'object') {
534 return Array.isArray(data[0]?.[0]?.tool_calls) ? data[0][0].tool_calls : [data[0][0].tool_calls];
535 }
536
537 return data[0];
513 }538 }
514539
515 // Parsed tool calls from non-streaming data540 // Parsed tool calls from non-streaming data
@@ -530,6 +555,11 @@ export class ToolManager {
530 return content;555 return content;
531 }556 }
532 }557 }
558
559 // Cohere tool calls
560 if (typeof data?.message?.tool_calls === 'object') {
561 return Array.isArray(data?.message?.tool_calls) ? data.message.tool_calls : [data.message.tool_calls];
562 }
533 }563 }
534564
535 /**565 /**
src/cohere-stream.js+0 -126
@@ -1,126 +0,0 @@
1const DATA_PREFIX = 'data:';
2
3/**
4 * Borrowed from Cohere SDK (MIT License)
5 * https://github.com/cohere-ai/cohere-typescript/blob/main/src/core/streaming-fetcher/Stream.ts
6 * Copyright (c) 2021 Cohere
7 */
8class CohereStream {
9 /** @type {ReadableStream} */
10 stream;
11 /** @type {string} */
12 prefix;
13 /** @type {string} */
14 messageTerminator;
15 /** @type {string|undefined} */
16 streamTerminator;
17 /** @type {AbortController} */
18 controller = new AbortController();
19
20 constructor({ stream, eventShape }) {
21 this.stream = stream;
22 if (eventShape.type === 'sse') {
23 this.prefix = DATA_PREFIX;
24 this.messageTerminator = '\n';
25 this.streamTerminator = eventShape.streamTerminator;
26 } else {
27 this.messageTerminator = eventShape.messageTerminator;
28 }
29 }
30
31 async *iterMessages() {
32 const stream = readableStreamAsyncIterable(this.stream);
33 let buf = '';
34 let prefixSeen = false;
35 let parsedAnyMessages = false;
36 for await (const chunk of stream) {
37 buf += this.decodeChunk(chunk);
38
39 let terminatorIndex;
40 // Parse the chunk into as many messages as possible
41 while ((terminatorIndex = buf.indexOf(this.messageTerminator)) >= 0) {
42 // Extract the line from the buffer
43 let line = buf.slice(0, terminatorIndex + 1);
44 buf = buf.slice(terminatorIndex + 1);
45
46 // Skip empty lines
47 if (line.length === 0) {
48 continue;
49 }
50
51 // Skip the chunk until the prefix is found
52 if (!prefixSeen && this.prefix != null) {
53 const prefixIndex = line.indexOf(this.prefix);
54 if (prefixIndex === -1) {
55 continue;
56 }
57 prefixSeen = true;
58 line = line.slice(prefixIndex + this.prefix.length);
59 }
60
61 // If the stream terminator is present, return
62 if (this.streamTerminator != null && line.includes(this.streamTerminator)) {
63 return;
64 }
65
66 // Otherwise, yield message from the prefix to the terminator
67 const message = JSON.parse(line);
68 yield message;
69 prefixSeen = false;
70 parsedAnyMessages = true;
71 }
72 }
73
74 if (!parsedAnyMessages && buf.length > 0) {
75 try {
76 yield JSON.parse(buf);
77 } catch (e) {
78 console.error('Error parsing message:', e);
79 }
80 }
81 }
82
83 async *[Symbol.asyncIterator]() {
84 for await (const message of this.iterMessages()) {
85 yield message;
86 }
87 }
88
89 decodeChunk(chunk) {
90 const decoder = new TextDecoder('utf8');
91 return decoder.decode(chunk);
92 }
93}
94
95function readableStreamAsyncIterable(stream) {
96 if (stream[Symbol.asyncIterator]) {
97 return stream;
98 }
99
100 const reader = stream.getReader();
101 return {
102 async next() {
103 try {
104 const result = await reader.read();
105 if (result?.done) {
106 reader.releaseLock();
107 } // release lock when stream becomes closed
108 return result;
109 } catch (e) {
110 reader.releaseLock(); // release lock when stream becomes errored
111 throw e;
112 }
113 },
114 async return() {
115 const cancelPromise = reader.cancel();
116 reader.releaseLock();
117 await cancelPromise;
118 return { done: true, value: undefined };
119 },
120 [Symbol.asyncIterator]() {
121 return this;
122 },
123 };
124}
125
126module.exports = CohereStream;
src/endpoints/backends/chat-completions.js+14 -18
@@ -5,7 +5,6 @@ const { jsonParser } = require('../../express-common');
5const { CHAT_COMPLETION_SOURCES, GEMINI_SAFETY, BISON_SAFETY, OPENROUTER_HEADERS } = require('../../constants');5const { CHAT_COMPLETION_SOURCES, GEMINI_SAFETY, BISON_SAFETY, OPENROUTER_HEADERS } = require('../../constants');
6const { forwardFetchResponse, getConfigValue, tryParse, uuidv4, mergeObjectWithYaml, excludeKeysByYaml, color } = require('../../util');6const { forwardFetchResponse, getConfigValue, tryParse, uuidv4, mergeObjectWithYaml, excludeKeysByYaml, color } = require('../../util');
7const { convertClaudeMessages, convertGooglePrompt, convertTextCompletionPrompt, convertCohereMessages, convertMistralMessages, convertAI21Messages, mergeMessages } = require('../../prompt-converters');7const { convertClaudeMessages, convertGooglePrompt, convertTextCompletionPrompt, convertCohereMessages, convertMistralMessages, convertAI21Messages, mergeMessages } = require('../../prompt-converters');
8const CohereStream = require('../../cohere-stream');
98
10const { readSecret, SECRET_KEYS } = require('../secrets');9const { readSecret, SECRET_KEYS } = require('../secrets');
11const { getTokenizerModel, getSentencepiceTokenizer, getTiktokenTokenizer, sentencepieceTokenizers, TEXT_COMPLETION_MODELS } = require('../tokenizers');10const { getTokenizerModel, getSentencepiceTokenizer, getTiktokenTokenizer, sentencepieceTokenizers, TEXT_COMPLETION_MODELS } = require('../tokenizers');
@@ -13,7 +12,8 @@ const { getTokenizerModel, getSentencepiceTokenizer, getTiktokenTokenizer, sente
13const API_OPENAI = 'https://api.openai.com/v1';12const API_OPENAI = 'https://api.openai.com/v1';
14const API_CLAUDE = 'https://api.anthropic.com/v1';13const API_CLAUDE = 'https://api.anthropic.com/v1';
15const API_MISTRAL = 'https://api.mistral.ai/v1';14const API_MISTRAL = 'https://api.mistral.ai/v1';
16const API_COHERE = 'https://api.cohere.ai/v1';15const API_COHERE_V1 = 'https://api.cohere.ai/v1';
16const API_COHERE_V2 = 'https://api.cohere.ai/v2';
17const API_PERPLEXITY = 'https://api.perplexity.ai';17const API_PERPLEXITY = 'https://api.perplexity.ai';
18const API_GROQ = 'https://api.groq.com/openai/v1';18const API_GROQ = 'https://api.groq.com/openai/v1';
19const API_MAKERSUITE = 'https://generativelanguage.googleapis.com';19const API_MAKERSUITE = 'https://generativelanguage.googleapis.com';
@@ -553,13 +553,14 @@ async function sendCohereRequest(request, response) {
553553
554 try {554 try {
555 const convertedHistory = convertCohereMessages(request.body.messages, request.body.char_name, request.body.user_name);555 const convertedHistory = convertCohereMessages(request.body.messages, request.body.char_name, request.body.user_name);
556 const connectors = [];
557 const tools = [];556 const tools = [];
558557
559 const canDoWebSearch = !String(request.body.model).includes('c4ai-aya');558 if (Array.isArray(request.body.tools) && request.body.tools.length > 0) {
560 if (request.body.websearch && canDoWebSearch) {559 tools.push(...request.body.tools);
561 connectors.push({560 tools.forEach(tool => {
562 id: 'web-search',561 if (tool?.function?.parameters?.$schema) {
562 delete tool.function.parameters.$schema;
563 }
563 });564 });
564 }565 }
565566
@@ -567,9 +568,7 @@ async function sendCohereRequest(request, response) {
567 const requestBody = {568 const requestBody = {
568 stream: Boolean(request.body.stream),569 stream: Boolean(request.body.stream),
569 model: request.body.model,570 model: request.body.model,
570 message: convertedHistory.userPrompt,571 messages: convertedHistory.chatHistory,
571 preamble: convertedHistory.systemPrompt,
572 chat_history: convertedHistory.chatHistory,
573 temperature: request.body.temperature,572 temperature: request.body.temperature,
574 max_tokens: request.body.max_tokens,573 max_tokens: request.body.max_tokens,
575 k: request.body.top_k,574 k: request.body.top_k,
@@ -578,16 +577,13 @@ async function sendCohereRequest(request, response) {
578 stop_sequences: request.body.stop,577 stop_sequences: request.body.stop,
579 frequency_penalty: request.body.frequency_penalty,578 frequency_penalty: request.body.frequency_penalty,
580 presence_penalty: request.body.presence_penalty,579 presence_penalty: request.body.presence_penalty,
581 prompt_truncation: 'AUTO_PRESERVE_ORDER',
582 connectors: connectors,
583 documents: [],580 documents: [],
584 tools: tools,581 tools: tools,
585 search_queries_only: false,
586 };582 };
587583
588 const canDoSafetyMode = String(request.body.model).endsWith('08-2024');584 const canDoSafetyMode = String(request.body.model).endsWith('08-2024');
589 if (canDoSafetyMode) {585 if (canDoSafetyMode) {
590 requestBody.safety_mode = 'NONE';586 requestBody.safety_mode = 'OFF';
591 }587 }
592588
593 console.log('Cohere request:', requestBody);589 console.log('Cohere request:', requestBody);
@@ -603,11 +599,11 @@ async function sendCohereRequest(request, response) {
603 timeout: 0,599 timeout: 0,
604 };600 };
605601
606 const apiUrl = API_COHERE + '/chat';602 const apiUrl = API_COHERE_V2 + '/chat';
607603
608 if (request.body.stream) {604 if (request.body.stream) {
609 const stream = await global.fetch(apiUrl, config);605 const stream = await fetch(apiUrl, config);
610 parseCohereStream(stream, request, response);606 forwardFetchResponse(stream, response);
611 } else {607 } else {
612 const generateResponse = await fetch(apiUrl, config);608 const generateResponse = await fetch(apiUrl, config);
613 if (!generateResponse.ok) {609 if (!generateResponse.ok) {
@@ -658,7 +654,7 @@ router.post('/status', jsonParser, async function (request, response_getstatus_o
658 headers = {};654 headers = {};
659 mergeObjectWithYaml(headers, request.body.custom_include_headers);655 mergeObjectWithYaml(headers, request.body.custom_include_headers);
660 } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.COHERE) {656 } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.COHERE) {
661 api_url = API_COHERE;657 api_url = API_COHERE_V1;
662 api_key_openai = readSecret(request.user.directories, SECRET_KEYS.COHERE);658 api_key_openai = readSecret(request.user.directories, SECRET_KEYS.COHERE);
663 headers = {};659 headers = {};
664 } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.ZEROONEAI) {660 } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.ZEROONEAI) {
src/prompt-converters.js+35 -36
@@ -277,56 +277,55 @@ function convertClaudeMessages(messages, prefillString, useSysPrompt, useTools,
277 * @param {object[]} messages Array of messages277 * @param {object[]} messages Array of messages
278 * @param {string} charName Character name278 * @param {string} charName Character name
279 * @param {string} userName User name279 * @param {string} userName User name
280 * @returns {{systemPrompt: string, chatHistory: object[], userPrompt: string}} Prompt for Cohere280 * @returns {{chatHistory: object[]}} Prompt for Cohere
281 */281 */
282function convertCohereMessages(messages, charName = '', userName = '') {282function convertCohereMessages(messages, charName = '', userName = '') {
283 const roleMap = {283 if (messages.length === 0) {
284 'system': 'SYSTEM',284 messages.unshift({
285 'user': 'USER',285 role: 'user',
286 'assistant': 'CHATBOT',286 content: PROMPT_PLACEHOLDER,
287 };287 });
288 let systemPrompt = '';288 }
289289
290 // Collect all the system messages up until the first instance of a non-system message, and then remove them from the messages array.290 messages.forEach((msg, index) => {
291 let i;291 // Tool calls require an assistent primer
292 for (i = 0; i < messages.length; i++) {292 if (Array.isArray(msg.tool_calls)) {
293 if (messages[i].role !== 'system') {293 if (index > 0 && messages[index - 1].role === 'assistant') {
294 break;294 msg.content = messages[index - 1].content;
295 messages.splice(index - 1, 1);
296 } else {
297 msg.content = `I'm going to call the tool for that: ${msg.tool_calls.map(tc => tc?.function?.name).join(', ')}`;
295 }298 }
296 // Append example names if not already done by the frontend (e.g. for group chats).
297 if (userName && messages[i].name === 'example_user') {
298 if (!messages[i].content.startsWith(`${userName}: `)) {
299 messages[i].content = `${userName}: ${messages[i].content}`;
300 }299 }
300 // No names support (who would've thought)
301 if (msg.name) {
302 if (msg.role == 'system' && msg.name == 'example_assistant') {
303 if (charName && !msg.content.startsWith(`${charName}: `)) {
304 msg.content = `${charName}: ${msg.content}`;
301 }305 }
302 if (charName && messages[i].name === 'example_assistant') {
303 if (!messages[i].content.startsWith(`${charName}: `)) {
304 messages[i].content = `${charName}: ${messages[i].content}`;
305 }306 }
307 if (msg.role == 'system' && msg.name == 'example_user') {
308 if (userName && !msg.content.startsWith(`${userName}: `)) {
309 msg.content = `${userName}: ${msg.content}`;
306 }310 }
307 systemPrompt += `${messages[i].content}\n\n`;
308 }311 }
312 if (msg.role !== 'system' && !msg.content.startsWith(`${msg.name}: `)) {
313 msg.content = `${msg.name}: ${msg.content}`;
314 }
315 delete msg.name;
316 }
317 });
309318
310 messages.splice(0, i);319 // A prompt should end with a user/tool message
311320 if (!['user', 'tool'].includes(messages[messages.length - 1].role)) {
312 if (messages.length === 0) {321 const userPlaceholder = getConfigValue('cohere.userPlaceholder', PROMPT_PLACEHOLDER || 'Continue');
313 messages.unshift({322 messages.push({
314 role: 'user',323 role: 'user',
315 content: PROMPT_PLACEHOLDER,324 content: userPlaceholder,
316 });325 });
317 }326 }
318327
319 const lastNonSystemMessageIndex = messages.findLastIndex(msg => msg.role === 'user' || msg.role === 'assistant');328 return { chatHistory: messages };
320 const userPrompt = messages.slice(lastNonSystemMessageIndex).map(msg => msg.content).join('\n\n') || PROMPT_PLACEHOLDER;
321
322 const chatHistory = messages.slice(0, lastNonSystemMessageIndex).map(msg => {
323 return {
324 role: roleMap[msg.role] || 'USER',
325 message: msg.content,
326 };
327 });
328
329 return { systemPrompt: systemPrompt.trim(), chatHistory, userPrompt };
330}329}
331330
332/**331/**