Compare commits

..
Author SHA1 Message Date
Nat 30e258704e DEV: Increase chunk size before splitting content for translation 2025-06-18 23:53:10 +08:00
148 changed files with 839429 additions and 3138025 deletions
-1
View File
@@ -1,4 +1,3 @@
< 3.5.0.beta7-dev: cd14b0c0bee0cf63c59b64b6f7213e31a37f11a7
< 3.5.0.beta6-dev: 3e74eea1e5e3143888d67a8d8a11206df214dc24
< 3.5.0.beta3-dev: 09a68414804a1447f52e5d60691ba59742cda9ec
< 3.5.0.beta2-dev: de8624416a15b3d8e7ad350b083cc1420451ccec
+1 -2
View File
@@ -2,7 +2,6 @@
module Jobs
class DetectTranslatePost < ::Jobs::Base
cluster_concurrency 1
sidekiq_options retry: false
def execute(args)
@@ -36,7 +35,7 @@ module Jobs
end
end
locales = SiteSetting.content_localization_supported_locales.split("|")
locales = SiteSetting.experimental_content_localization_supported_locales.split("|")
return if locales.blank?
locales.each do |locale|
+1 -2
View File
@@ -2,7 +2,6 @@
module Jobs
class DetectTranslateTopic < ::Jobs::Base
cluster_concurrency 1
sidekiq_options retry: false
def execute(args)
@@ -34,7 +33,7 @@ module Jobs
end
end
locales = SiteSetting.content_localization_supported_locales.split("|")
locales = SiteSetting.experimental_content_localization_supported_locales.split("|")
return if locales.blank?
locales.each do |locale|
+1 -1
View File
@@ -11,7 +11,7 @@ module Jobs
return if !SiteSetting.discourse_ai_enabled
return if !SiteSetting.ai_translation_enabled
locales = SiteSetting.content_localization_supported_locales.split("|")
locales = SiteSetting.experimental_content_localization_supported_locales.split("|")
return if locales.blank?
cat_id = args[:from_category_id] || Category.order(:id).first&.id
+1 -1
View File
@@ -11,7 +11,7 @@ module Jobs
return if !SiteSetting.discourse_ai_enabled
return if !SiteSetting.ai_translation_enabled
locales = SiteSetting.content_localization_supported_locales.split("|")
locales = SiteSetting.experimental_content_localization_supported_locales.split("|")
return if locales.blank?
limit = args[:limit] || BATCH_SIZE
+1 -1
View File
@@ -11,7 +11,7 @@ module Jobs
return if !SiteSetting.discourse_ai_enabled
return if !SiteSetting.ai_translation_enabled
locales = SiteSetting.content_localization_supported_locales.split("|")
locales = SiteSetting.experimental_content_localization_supported_locales.split("|")
return if locales.blank?
limit = args[:limit] || BATCH_SIZE
@@ -8,7 +8,7 @@ module Jobs
def execute(args)
return if !SiteSetting.discourse_ai_enabled
return if !SiteSetting.ai_translation_enabled
return if SiteSetting.content_localization_supported_locales.blank?
return if SiteSetting.experimental_content_localization_supported_locales.blank?
Jobs.enqueue(:localize_categories)
end
@@ -9,7 +9,7 @@ module Jobs
return if !SiteSetting.discourse_ai_enabled
return if !SiteSetting.ai_translation_enabled
return if SiteSetting.content_localization_supported_locales.blank?
return if SiteSetting.experimental_content_localization_supported_locales.blank?
return if SiteSetting.ai_translation_backfill_rate == 0
Jobs.enqueue(:localize_posts, limit: SiteSetting.ai_translation_backfill_rate)
@@ -9,7 +9,7 @@ module Jobs
return if !SiteSetting.discourse_ai_enabled
return if !SiteSetting.ai_translation_enabled
return if SiteSetting.content_localization_supported_locales.blank?
return if SiteSetting.experimental_content_localization_supported_locales.blank?
return if SiteSetting.ai_translation_backfill_rate == 0
Jobs.enqueue(:localize_topics, limit: SiteSetting.ai_translation_backfill_rate)
-1
View File
@@ -24,7 +24,6 @@ class EmbeddingDefinition < ActiveRecord::Base
DiscourseAi::Tokenizer::MultilingualE5LargeTokenizer,
DiscourseAi::Tokenizer::OpenAiTokenizer,
DiscourseAi::Tokenizer::MixtralTokenizer,
DiscourseAi::Tokenizer::QwenTokenizer,
].map(&:name)
end
@@ -32,7 +32,7 @@ export default class AiLlmsListEditor extends Component {
key = `${llm.provider}-${llm.name}`;
} else {
// case of preset
key = llm.id.replace(/[.:\/]/g, "-");
key = llm.id.replace(/\./g, "-");
}
key = `discourse_ai.llms.model_description.${key}`;
@@ -14,6 +14,7 @@ import concatClass from "discourse/helpers/concat-class";
import { ajax } from "discourse/lib/ajax";
import { popupAjaxError } from "discourse/lib/ajax-error";
import { bind } from "discourse/lib/decorators";
import { withPluginApi } from "discourse/lib/plugin-api";
import { sanitize } from "discourse/lib/text";
import { clipboardCopy } from "discourse/lib/utilities";
import { i18n } from "discourse-i18n";
@@ -43,9 +44,6 @@ export default class AiPostHelperMenu extends Component {
@tracked lastSelectedOption = null;
@tracked isSavingFootnote = false;
@tracked supportsAddFootnote = this.args.data.supportsFastEdit;
@tracked
channel =
`/discourse-ai/ai-helper/stream_suggestion/${this.args.data.quoteState.postId}`;
@tracked
smoothStreamer = new SmoothStreamer(
@@ -80,6 +78,23 @@ export default class AiPostHelperMenu extends Component {
@tracked _activeAiRequest = null;
constructor() {
super(...arguments);
withPluginApi((api) => {
api.registerValueTransformer(
"post-text-selection-prevent-close",
({ value }) => {
if (this.menuState === this.MENU_STATES.result) {
return true;
}
return value;
}
);
});
}
get footnoteDisabled() {
return this.streaming || !this.supportsAddFootnote;
}
@@ -152,17 +167,16 @@ export default class AiPostHelperMenu extends Component {
@bind
subscribe() {
this.messageBus.subscribe(
this.channel,
(data) => this._updateResult(data),
this.args.data.post
.discourse_ai_helper_stream_suggestion_last_message_bus_id
);
const channel = `/discourse-ai/ai-helper/stream_suggestion/${this.args.data.quoteState.postId}`;
this.messageBus.subscribe(channel, this._updateResult);
}
@bind
unsubscribe() {
this.messageBus.unsubscribe(this.channel, this._updateResult);
this.messageBus.unsubscribe(
"/discourse-ai/ai-helper/stream_suggestion/*",
this._updateResult
);
}
@bind
@@ -225,7 +239,7 @@ export default class AiPostHelperMenu extends Component {
data: {
location: "post",
mode: option.name,
text: this.args.data.quoteState.buffer,
text: this.args.data.selectedText,
post_id: this.args.data.quoteState.postId,
custom_prompt: this.customPromptValue,
client_id: this.messageBus.clientId,
@@ -278,10 +292,12 @@ export default class AiPostHelperMenu extends Component {
@action
closeMenu() {
// reset state and close
this.suggestion = "";
this.customPromptValue = "";
return this.args.close();
if (this.site.mobileView) {
return this.args.close();
}
const menu = this.menu.getByIdentifier("post-text-selection-toolbar");
return menu?.close();
}
@action
@@ -301,9 +317,9 @@ export default class AiPostHelperMenu extends Component {
const credits = i18n(
"discourse_ai.ai_helper.post_options_menu.footnote_credits"
);
const withFootnote = `${this.args.data.quoteState.buffer} ^[${sanitizedSuggestion} (${credits})]`;
const withFootnote = `${this.args.data.selectedText} ^[${sanitizedSuggestion} (${credits})]`;
const newRaw = result.raw.replace(
this.args.data.quoteState.buffer,
this.args.data.selectedText,
withFootnote
);
@@ -322,7 +338,7 @@ export default class AiPostHelperMenu extends Component {
(and this.site.mobileView (eq this.menuState this.MENU_STATES.options))
}}
<div class="ai-post-helper-menu__selected-text">
{{@data.quoteState.buffer}}
{{@data.selectedText}}
</div>
{{/if}}
@@ -85,18 +85,14 @@ export default class ModalDiffModal extends Component {
@bind
subscribe() {
this.messageBus.subscribe(
CHANNEL,
this.updateResult,
this.currentUser
?.discourse_ai_helper_stream_composer_suggestion_last_message_bus_id
);
this.messageBus.subscribe(CHANNEL, this.updateResult);
}
@bind
cleanup() {
// stop all callbacks so it does not end up streaming pointlessly
this.#resetState();
this.smoothStreamer.resetStreaming();
this.diffStreamer.reset();
this.messageBus.unsubscribe(CHANNEL, this.updateResult);
}
@@ -118,7 +114,8 @@ export default class ModalDiffModal extends Component {
@action
async suggestChanges() {
this.#resetState();
this.smoothStreamer.resetStreaming();
this.diffStreamer.reset();
try {
this.loading = true;
@@ -162,25 +159,11 @@ export default class ModalDiffModal extends Component {
}
}
@action
cleanupAndClose() {
this.#resetState();
this.loading = false;
this.args.closeModal();
}
#resetState() {
this.suggestion = "";
this.finalResult = "";
this.smoothStreamer.resetStreaming();
this.diffStreamer.reset();
}
<template>
<DModal
class="composer-ai-helper-modal"
@title={{i18n "discourse_ai.ai_helper.context_menu.changes"}}
@closeModal={{this.cleanupAndClose}}
@closeModal={{@closeModal}}
>
<:body>
<div
@@ -235,7 +218,7 @@ export default class ModalDiffModal extends Component {
</DButton>
<DButton
class="btn-flat discard"
@action={{this.cleanupAndClose}}
@action={{@closeModal}}
@label="discourse_ai.ai_helper.context_menu.discard"
/>
<DButton
@@ -3,7 +3,8 @@ import { tracked } from "@glimmer/tracking";
import { action } from "@ember/object";
import { service } from "@ember/service";
import DButton from "discourse/components/d-button";
import { selectedRange } from "discourse/lib/utilities";
import virtualElementFromTextRange from "discourse/lib/virtual-element-from-text-range";
import eq from "truth-helpers/helpers/eq";
import AiPostHelperMenu from "../../components/ai-post-helper-menu";
import { showPostAIHelper } from "../../lib/show-ai-helper";
@@ -12,27 +13,27 @@ export default class AiPostHelperTrigger extends Component {
return showPostAIHelper(outletArgs, helper);
}
@service site;
@service menu;
@tracked menuState = this.MENU_STATES.triggers;
@tracked showMainButtons = true;
@tracked showAiButtons = true;
@tracked postHighlighted = false;
@tracked currentMenu = this.menu.getByIdentifier(
"post-text-selection-toolbar"
);
// capture the state at the moment the toolbar is rendered
// so we ensure change of state (selection change for example)
// is not impacting the menu data
menuData = {
...this.args.outletArgs.data,
quoteState: {
buffer: this.args.outletArgs.data.quoteState.buffer,
opts: this.args.outletArgs.data.quoteState.opts,
postId: this.args.outletArgs.data.quoteState.postId,
},
post: this.args.outletArgs.post,
selectedRange: selectedRange(),
MENU_STATES = {
triggers: "TRIGGERS",
options: "OPTIONS",
};
willDestroy() {
super.willDestroy(...arguments);
this.removeHighlightedText();
}
highlightSelectedText() {
const postId = this.args.outletArgs.data.quoteState.postId;
const postElement = document.querySelector(
@@ -43,7 +44,14 @@ export default class AiPostHelperTrigger extends Component {
return;
}
const range = this.menuData.selectedRange;
this.selectedText = this.args.outletArgs.data.quoteState.buffer;
const selection = window.getSelection();
if (!selection.rangeCount) {
return;
}
const range = selection.getRangeAt(0);
// Split start/end text nodes at their range boundary
if (
@@ -89,10 +97,11 @@ export default class AiPostHelperTrigger extends Component {
// Replace textNode with highlighted clone
const clone = textNode.cloneNode(true);
highlight.appendChild(clone);
textNode.parentNode.replaceChild(highlight, textNode);
}
window.getSelection().removeAllRanges();
selection.removeAllRanges();
this.postHighlighted = true;
}
@@ -101,7 +110,16 @@ export default class AiPostHelperTrigger extends Component {
return;
}
const highlightedSpans = document.querySelectorAll(
const postId = this.args.outletArgs.data.quoteState.postId;
const postElement = document.querySelector(
`article[data-post-id='${postId}'] .cooked`
);
if (!postElement) {
return;
}
const highlightedSpans = postElement.querySelectorAll(
"span.ai-helper-highlighted-selection"
);
@@ -115,40 +133,65 @@ export default class AiPostHelperTrigger extends Component {
@action
async showAiPostHelperMenu() {
await this.currentMenu.close();
await this.menu.show(this.currentMenu.trigger, {
identifier: "ai-post-helper-menu",
component: AiPostHelperMenu,
interactive: true,
trapTab: false,
closeOnScroll: false,
modalForMobile: true,
data: this.menuData,
placement: "top-start",
fallbackPlacements: ["bottom-start"],
updateOnScroll: false,
onClose: () => {
this.removeHighlightedText();
},
});
await this.currentMenu.destroy();
this.highlightSelectedText();
if (this.site.mobileView) {
this.currentMenu.close();
await this.menu.show(virtualElementFromTextRange(), {
identifier: "ai-post-helper-menu",
component: AiPostHelperMenu,
inline: true,
interactive: true,
placement: this.shouldRenderUnder ? "bottom-start" : "top-start",
fallbackPlacements: this.shouldRenderUnder
? ["bottom-end", "top-start"]
: ["bottom-start"],
trapTab: false,
closeOnScroll: false,
modalForMobile: true,
data: this.menuData,
});
}
this.showMainButtons = false;
this.menuState = this.MENU_STATES.options;
}
get menuData() {
// Streamline of data model to be passed to the component when
// instantiated as a DMenu or a simple component in the template
return {
...this.args.outletArgs.data,
quoteState: {
buffer: this.args.outletArgs.data.quoteState.buffer,
opts: this.args.outletArgs.data.quoteState.opts,
postId: this.args.outletArgs.data.quoteState.postId,
},
post: this.args.outletArgs.post,
selectedText: this.selectedText,
};
}
<template>
{{yield}}
{{#if this.showMainButtons}}
{{yield}}
{{/if}}
<div class="ai-post-helper">
<DButton
@icon="discourse-sparkles"
@title="discourse_ai.ai_helper.post_options_menu.title"
@label="discourse_ai.ai_helper.post_options_menu.trigger"
@action={{this.showAiPostHelperMenu}}
class="btn-flat ai-post-helper__trigger"
/>
</div>
{{#if this.showAiButtons}}
<div class="ai-post-helper">
{{#if (eq this.menuState this.MENU_STATES.triggers)}}
<DButton
@icon="discourse-sparkles"
@title="discourse_ai.ai_helper.post_options_menu.title"
@label="discourse_ai.ai_helper.post_options_menu.trigger"
@action={{this.showAiPostHelperMenu}}
class="btn-flat ai-post-helper__trigger"
/>
{{else if (eq this.menuState this.MENU_STATES.options)}}
<AiPostHelperMenu @data={{this.menuData}} />
{{/if}}
</div>
{{/if}}
</template>
}
@@ -3,9 +3,27 @@ import { LinkTo } from "@ember/routing";
import dIcon from "discourse/helpers/d-icon";
import { i18n } from "discourse-i18n";
function showAiPreferences(user, settings) {
// Since we only have one AI related user setting we don't show
// AI preferences if these conditions aren't met.
// If we add more user settings in the future we can move this
// logic to the the specific settings and conditionally show it in the template.
const aiHelperEnabledFeatures =
settings.ai_helper_enabled_features.split("|");
return (
user?.user_allowed_ai_auto_image_captions &&
aiHelperEnabledFeatures.includes("image_caption") &&
settings.ai_helper_enabled
);
}
export default class AutoImageCaptionSetting extends Component {
static shouldRender(outletArgs, helper) {
return helper.siteSettings.discourse_ai_enabled;
return (
helper.siteSettings.discourse_ai_enabled &&
showAiPreferences(outletArgs.model, helper.siteSettings)
);
}
<template>
@@ -0,0 +1,45 @@
class VirtualElementFromCaretCoords {
constructor(caretCoords, offset = [0, 0]) {
this.caretCoords = caretCoords;
this.offset = offset;
this.updateRect();
}
updateRect() {
const [xOffset, yOffset] = this.offset;
this.rect = {
top: this.caretCoords.y + yOffset,
right: this.caretCoords.x,
bottom: this.caretCoords.y + yOffset,
left: this.caretCoords.x + xOffset,
width: 0,
height: 0,
x: this.caretCoords.x,
y: this.caretCoords.y,
toJSON() {
return this;
},
};
return this.rect;
}
getBoundingClientRect() {
return this.rect;
}
getClientRects() {
return [this.rect];
}
get clientWidth() {
return this.rect.width;
}
get clientHeight() {
return this.rect.height;
}
}
export default function virtualElementFromCaretCoords(caretCoords, offset) {
return new VirtualElementFromCaretCoords(caretCoords, offset);
}
@@ -84,13 +84,6 @@ export default class AiConversationsSidebarManager extends Service {
);
}
addEmptyStateClass() {
document.body.classList.toggle(
"has-empty-ai-conversations-sidebar",
!this.topics.length
);
}
forceCustomSidebar() {
document.body.classList.add("has-ai-conversations-sidebar");
if (!this.sidebarState.isForcingSidebar) {
@@ -107,7 +100,6 @@ export default class AiConversationsSidebarManager extends Service {
// don't render sidebar multiple times
if (this._didInit) {
this.addEmptyStateClass();
return true;
}
@@ -115,7 +107,6 @@ export default class AiConversationsSidebarManager extends Service {
this.fetchMessages().then(() => {
this.sidebarState.setPanel(AI_CONVERSATIONS_PANEL);
this.addEmptyStateClass();
});
return true;
@@ -149,7 +140,6 @@ export default class AiConversationsSidebarManager extends Service {
stopForcingCustomSidebar() {
document.body.classList.remove("has-ai-conversations-sidebar");
document.body.classList.remove("has-empty-ai-conversations-sidebar");
const isAdmin = this.sidebarState.currentPanel?.key === ADMIN_PANEL;
if (this.sidebarState.isForcingSidebar && !isAdmin) {
@@ -1,40 +0,0 @@
import { get } from "@ember/helper";
import RouteTemplate from "ember-route-template";
import { eq } from "truth-helpers";
import PreferenceCheckbox from "discourse/components/preference-checkbox";
import SaveControls from "discourse/components/save-controls";
import { i18n } from "discourse-i18n";
export default RouteTemplate(
<template>
<div class="ai-user-preferences">
<legend class="control-label">{{i18n "discourse_ai.title"}}</legend>
{{#each @controller.booleanSettings as |setting|}}
{{#if setting.isIncluded}}
<div class="control-group ai-setting">
<PreferenceCheckbox
@labelKey={{setting.label}}
@checked={{get @controller.model.user_option setting.key}}
data-setting-name={{setting.settingName}}
class="pref-{{setting.settingName}}"
/>
</div>
{{/if}}
{{/each}}
{{#if (eq @controller.userSettingAttributes.length 0)}}
{{i18n "discourse_ai.user_preferences.empty"}}
{{/if}}
{{#unless (eq @controller.userSettingAttributes.length 0)}}
<SaveControls
@id="user_ai_preference_save"
@model={{@controller.model}}
@action={{@controller.save}}
@saved={{@controller.saved}}
/>
{{/unless}}
</div>
</template>
);
@@ -0,0 +1,29 @@
<div class="ai-user-preferences">
<legend class="control-label">{{i18n "discourse_ai.title"}}</legend>
{{#each this.booleanSettings as |setting|}}
{{#if setting.isIncluded}}
<div class="control-group ai-setting">
<PreferenceCheckbox
@labelKey={{setting.label}}
@checked={{get this.model.user_option setting.key}}
data-setting-name={{setting.settingName}}
class="pref-{{setting.settingName}}"
/>
</div>
{{/if}}
{{/each}}
{{#if (eq this.userSettingAttributes.length 0)}}
{{i18n "discourse_ai.user_preferences.empty"}}
{{/if}}
{{#unless (eq this.userSettingAttributes.length 0)}}
<SaveControls
@id="user_ai_preference_save"
@model={{this.model}}
@action={{this.save}}
@saved={{this.saved}}
/>
{{/unless}}
</div>
@@ -23,20 +23,8 @@ body.has-ai-conversations-sidebar {
}
}
// When the sidebar is empty, we want to hide the "Today" header
// and only show the empty state component
&.has-empty-ai-conversations-sidebar
.sidebar-section[data-section-name="today"]
.sidebar-section-header-wrapper {
display: none;
}
// When the sidebar isn't empty, BUT "today" is empty, hide
// the entire section
&:not(.has-empty-ai-conversations-sidebar)
.sidebar-section[data-section-name="today"]:has(
.ai-bot-sidebar-empty-state
) {
// we always have the "today" section rendered at the top of the sidebar but hide it when empty
.sidebar-section[data-section-name="today"]:has(.ai-bot-sidebar-empty-state) {
display: none;
}
@@ -137,13 +125,6 @@ body.has-ai-conversations-sidebar {
gap: 0.5em;
justify-content: flex-start;
.select-kit-body {
.select-kit-collection {
max-height: 50vh;
overflow-y: auto;
}
}
&__selection-wrapper {
display: flex;
flex-direction: column;
+1 -2
View File
@@ -300,8 +300,6 @@ ar:
open_modal: "تعديل"
modal:
key_title: "المفتاح"
filters:
reset: "إعادة التعيين"
rag:
options:
rag_chunk_tokens: "تحميل أجزاء الرموز"
@@ -480,6 +478,7 @@ ar:
trigger: "اسأل الذكاء الاصطناعي"
loading: "الذكاء الاصطناعي قيد إنشاء المحتوى"
cancel: "إلغاء"
regen: "إعادة المحاولة"
confirm: "تأكيد"
discard: "تجاهل"
changes: "التعديلات المقترحة"
+1 -2
View File
@@ -60,8 +60,6 @@ be:
delete: "выдаляць"
response_format:
open_modal: "рэдагаваць"
filters:
reset: "скінуць"
rag:
uploads:
title: "Загрузкі"
@@ -95,6 +93,7 @@ be:
ai_helper:
context_menu:
cancel: "адмяніць"
regen: "Паспрабаваць яшчэ раз"
post_options_menu:
close: "зачыніць"
copy: "капіяваць"
+1 -2
View File
@@ -71,8 +71,6 @@ bg:
open_modal: "Редактирай"
modal:
key_title: "Ключ"
filters:
reset: "Нулиране"
rag:
uploads:
uploading: "Качва се..."
@@ -113,6 +111,7 @@ bg:
ai_helper:
context_menu:
cancel: "Прекрати"
regen: "Опитайте отново"
discard: "Отхвърляне"
post_options_menu:
close: "Затвори"
+1 -2
View File
@@ -66,8 +66,6 @@ bs_BA:
open_modal: "Edit"
modal:
key_title: "Ključ"
filters:
reset: "Resetovati"
rag:
uploads:
title: "Uploads"
@@ -103,6 +101,7 @@ bs_BA:
ai_helper:
context_menu:
cancel: "Odustani"
regen: "Pokušaj ponovo"
discard: "Odbaci"
post_options_menu:
close: "Zatvori"
+1 -2
View File
@@ -69,8 +69,6 @@ ca:
open_modal: "Edita"
modal:
key_title: "Clau"
filters:
reset: "Restableix"
rag:
uploads:
title: "Càrregues"
@@ -107,6 +105,7 @@ ca:
ai_helper:
context_menu:
cancel: "Cancel·la"
regen: "Torna-ho a intentar"
confirm: "Confirma"
discard: "Descarta"
post_options_menu:
+1 -2
View File
@@ -79,8 +79,6 @@ cs:
open_modal: "Upravit"
modal:
key_title: "Klíč"
filters:
reset: "obnovit výchozí"
rag:
uploads:
title: "Nahrané soubory"
@@ -154,6 +152,7 @@ cs:
trigger: "Zeptejte se AI"
loading: "Umělá inteligence generuje"
cancel: "Zrušit"
regen: "Zkusit znovu"
confirm: "Potvrdit"
discard: "Zrušit"
changes: "Navrhované úpravy"
+1 -2
View File
@@ -74,8 +74,6 @@ da:
open_modal: "Rediger"
modal:
key_title: "Nøgle"
filters:
reset: "Nulstil"
rag:
uploads:
title: "Overførsler"
@@ -119,6 +117,7 @@ da:
ai_helper:
context_menu:
cancel: "Annuller"
regen: "Prøv igen"
confirm: "Bekræft"
discard: "Kassér"
post_options_menu:
+1 -6
View File
@@ -353,12 +353,6 @@ de:
ai_bot:
title: "KI-Bot-Optionen"
save_first: "Weitere KI-Bot-Optionen werden verfügbar, sobald du die Persona gespeichert hast."
filters:
reset: "Zurücksetzen"
features_list:
one: "Funktion:"
other: "Funktionen:"
llms_list: "LLM:"
rag:
title: "RAG"
options:
@@ -568,6 +562,7 @@ de:
trigger: "KI fragen"
loading: "KI generiert"
cancel: "Abbrechen"
regen: "Erneut versuchen"
confirm: "Bestätigen"
discard: "Verwerfen"
changes: "Empfohlene Änderungen"
+1 -2
View File
@@ -73,8 +73,6 @@ el:
open_modal: "Επεξεργασία"
modal:
key_title: "Κλειδί"
filters:
reset: "Επαναφορά"
rag:
uploads:
title: "Μεταφορτώσεις"
@@ -118,6 +116,7 @@ el:
ai_helper:
context_menu:
cancel: "Ακύρωση"
regen: "Δοκίμασε ξανά"
discard: "Απόρριψη"
post_options_menu:
close: "Κλείσιμο"
+7 -12
View File
@@ -539,16 +539,14 @@ en:
model_description:
none: "General settings that work for most language models"
anthropic-claude-opus-4-0: "Anthropic's most intelligent model"
anthropic-claude-sonnet-4-0: "Optimal balance of speed and cost"
anthropic-claude-3-7-sonnet-latest: "Optimal balance of speed and cost (previous generation)"
anthropic-claude-3-5-haiku-latest: "Fast and cost-effective"
google-gemini-2-5-pro: "Large multimodal model capable of a wide range of tasks"
google-gemini-2-0-flash: "Lightweight, fast, and cost-efficient with multimodal reasoning (previous generation)"
google-gemini-2-5-flash: "Lightweight, fast, and cost-efficient with multimodal reasoning"
anthropic-claude-3-7-sonnet: "Anthropic's most intelligent model"
anthropic-claude-3-5-haiku: "Fast and cost-effective"
anthropic-claude-3-opus: "Excels at writing and complex tasks"
google-gemini-2-5-pro: "Mid-sized multimodal model capable of a wide range of tasks"
google-gemini-2-0-flash: "Lightweight, fast, and cost-efficient with multimodal reasoning"
google-gemini-2-0-flash-lite: "Cost efficient and low latency model"
open_ai-o3: "Open AI's most capable reasoning model"
open_ai-o4-mini: "Advanced Cost-efficient reasoning model"
open_ai-o1: "Open AI's most capable reasoning model"
open_ai-o3-mini: "Advanced Cost-efficient reasoning model"
open_ai-gpt-4-1: "Open AI's flagship model. It is well suited for problem solving across domains"
open_ai-gpt-4-1-mini: "Provides a balance between intelligence, speed, and cost that makes it an attractive model for many use cases."
open_ai-gpt-4-1-nano: "Fastest, most cost-effective GPT-4.1 model."
@@ -556,9 +554,6 @@ en:
samba_nova-Meta-Llama-3-3-70B-Instruct": "Powerful multipurpose model"
mistral-mistral-large-latest: "Mistral's most powerful model"
mistral-pixtral-large-latest: "Mistral's most powerful vision capable model"
open_router-x-ai-grok-3-beta: "xAI's latest model"
open_router-deepseek-deepseek-r1-0528-free: "DeepSeek's latest reasoning model"
open_router-meta-llama-3-3-70b-instruct: "Highly capable multilingual model"
preseeded_model_description: "Pre-configured open-source model utilizing %{model}"

Some files were not shown because too many files have changed in this diff Show More