Merge pull request #2962 from SillyTavern/cohere-v2 Migrate Cohere to /v2/chat

15efdb660929bf7750c5c573d936ccca5e204a37

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

Signed
24 files changed, +102 -260Showing whitespace changes
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?.content?.[0]?.text ?? data?.message?.tool_plan ?? '';
5440 default:5440 default:
5441 return '';5441 return '';
5442 }5442 }
public/scripts/openai.js+5 -15
@@ -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) {
@@ -1976,12 +1973,14 @@ async function sendOpenAIRequest(type, messages, signal) {
1976}1973}
19771974
1978function getStreamingReply(data) {1975function getStreamingReply(data) {
1979 if (oai_settings.chat_completion_source == chat_completion_sources.CLAUDE) {1976 if (oai_settings.chat_completion_source === chat_completion_sources.CLAUDE) {
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
@@ -401,6 +401,22 @@ export class ToolManager {
401 }401 }
402 }402 }
403 }403 }
404 const cohereToolEvents = ['message-start', 'tool-call-start', 'tool-call-delta', 'tool-call-end'];
405 if (cohereToolEvents.includes(parsed?.type) && typeof parsed?.delta?.message === 'object') {
406 const choiceIndex = 0;
407 const toolCallIndex = parsed?.index ?? 0;
408
409 if (!Array.isArray(toolCalls[choiceIndex])) {
410 toolCalls[choiceIndex] = [];
411 }
412
413 if (toolCalls[choiceIndex][toolCallIndex] === undefined) {
414 toolCalls[choiceIndex][toolCallIndex] = {};
415 }
416
417 const targetToolCall = toolCalls[choiceIndex][toolCallIndex];
418 ToolManager.#applyToolCallDelta(targetToolCall, parsed.delta.message);
419 }
404 if (typeof parsed?.content_block === 'object') {420 if (typeof parsed?.content_block === 'object') {
405 const choiceIndex = 0;421 const choiceIndex = 0;
406 const toolCallIndex = parsed?.index ?? 0;422 const toolCallIndex = parsed?.index ?? 0;
@@ -503,6 +519,7 @@ export class ToolManager {
503 chat_completion_sources.CLAUDE,519 chat_completion_sources.CLAUDE,
504 chat_completion_sources.OPENROUTER,520 chat_completion_sources.OPENROUTER,
505 chat_completion_sources.GROQ,521 chat_completion_sources.GROQ,
522 chat_completion_sources.COHERE,
506 ];523 ];
507 return supportedSources.includes(oai_settings.chat_completion_source);524 return supportedSources.includes(oai_settings.chat_completion_source);
508 }525 }
@@ -529,7 +546,15 @@ export class ToolManager {
529546
530 // Parsed tool calls from streaming data547 // Parsed tool calls from streaming data
531 if (Array.isArray(data) && data.length > 0 && Array.isArray(data[0])) {548 if (Array.isArray(data) && data.length > 0 && Array.isArray(data[0])) {
532 return isClaudeToolCall(data[0]) ? data[0].filter(x => x).map(convertClaudeToolCall) : data[0];549 if (isClaudeToolCall(data[0])) {
550 return data[0].filter(x => x).map(convertClaudeToolCall);
551 }
552
553 if (typeof data[0]?.[0]?.tool_calls === 'object') {
554 return Array.isArray(data[0]?.[0]?.tool_calls) ? data[0][0].tool_calls : [data[0][0].tool_calls];
555 }
556
557 return data[0];
533 }558 }
534559
535 // Parsed tool calls from non-streaming data560 // Parsed tool calls from non-streaming data
@@ -550,6 +575,11 @@ export class ToolManager {
550 return content;575 return content;
551 }576 }
552 }577 }
578
579 // Cohere tool calls
580 if (typeof data?.message?.tool_calls === 'object') {
581 return Array.isArray(data?.message?.tool_calls) ? data.message.tool_calls : [data.message.tool_calls];
582 }
553 }583 }
554584
555 /**585 /**
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+33 -38
@@ -277,56 +277,51 @@ 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 a 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 }
309312 if (msg.role !== 'system' && !msg.content.startsWith(`${msg.name}: `)) {
310 messages.splice(0, i);313 msg.content = `${msg.name}: ${msg.content}`;
311314 }
312 if (messages.length === 0) {315 delete msg.name;
313 messages.unshift({
314 role: 'user',
315 content: PROMPT_PLACEHOLDER,
316 });
317 }316 }
318
319 const lastNonSystemMessageIndex = messages.findLastIndex(msg => msg.role === 'user' || msg.role === 'assistant');
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 });317 });
328318
329 return { systemPrompt: systemPrompt.trim(), chatHistory, userPrompt };319 // A prompt should end with a user/tool message
320 if (messages.length && !['user', 'tool'].includes(messages[messages.length - 1].role)) {
321 messages[messages.length - 1].role = 'user';
322 }
323
324 return { chatHistory: messages };
330}325}
331326
332/**327/**
src/vectors/cohere-vectors.js+4 -3
@@ -17,7 +17,7 @@ async function getCohereBatchVector(texts, isQuery, directories, model) {
17 throw new Error('No API key found');17 throw new Error('No API key found');
18 }18 }
1919
20 const response = await fetch('https://api.cohere.ai/v1/embed', {20 const response = await fetch('https://api.cohere.ai/v2/embed', {
21 method: 'POST',21 method: 'POST',
22 headers: {22 headers: {
23 'Content-Type': 'application/json',23 'Content-Type': 'application/json',
@@ -26,6 +26,7 @@ async function getCohereBatchVector(texts, isQuery, directories, model) {
26 body: JSON.stringify({26 body: JSON.stringify({
27 texts: texts,27 texts: texts,
28 model: model,28 model: model,
29 embedding_types: ['float'],
29 input_type: isQuery ? 'search_query' : 'search_document',30 input_type: isQuery ? 'search_query' : 'search_document',
30 truncate: 'END',31 truncate: 'END',
31 }),32 }),
@@ -38,12 +39,12 @@ async function getCohereBatchVector(texts, isQuery, directories, model) {
38 }39 }
3940
40 const data = await response.json();41 const data = await response.json();
41 if (!Array.isArray(data?.embeddings)) {42 if (!Array.isArray(data?.embeddings?.float)) {
42 console.log('API response was not an array');43 console.log('API response was not an array');
43 throw new Error('API response was not an array');44 throw new Error('API response was not an array');
44 }45 }
4546
46 return data.embeddings;47 return data.embeddings.float;
47}48}
4849
49/**50/**