Compare commits

...
Author SHA1 Message Date
dependabot[bot] 415c1156e0 Build(deps-dev): Bump rack from 3.1.16 to 3.1.18
Bumps [rack](https://github.com/rack/rack) from 3.1.16 to 3.1.18.
- [Release notes](https://github.com/rack/rack/releases)
- [Changelog](https://github.com/rack/rack/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rack/rack/compare/v3.1.16...v3.1.18)

---
updated-dependencies:
- dependency-name: rack
  dependency-version: 3.1.18
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
2025-12-27 22:27:24 +00:00
Jarek Radosz c312b3184a DEV: Mention the core move in readme (#1514) 2025-07-22 11:08:32 +01:00
Jarek Radosz f231aad8b5 DEV: Disable the plugin by default (#1511)
…and preserve the current setting on existing sites
2025-07-22 11:05:52 +01:00
Alan Guo Xiang Tan cc77e73cfd PERF: Create index ON ai_topics_embeddings(topic_id, model_id) (#1513)
This index helps to speed up queries that joins the `topics` table
against the `ai_topics_embeddings` table on the `topic_id` column. There
are a number of queries which filters on `ai_topics_embeddings.model_id`
so we are including that in the index as well.
2025-07-22 16:46:56 +08:00
Natalie Tay 1d88ca44f0 DEV: Use core's locale normalizer (#1509)
We've moved the class to core here discourse/discourse#33702 so following up a deletion in this repo.
2025-07-22 10:09:03 +08:00
Roman Rizzi 6059b6e111 FIX: Customizable max_output_tokens for AI triage. (#1510)
We enforced a hard limit of 700 tokens in this script, which is not enough when using thinking models, which can quickly use all of them.

A temporary solution could be bumping the limit, but there is no guarantee we won't hit it again, and it's hard to find one value that fits all scenarios. Another alternative could be removing it and relying on the LLM config's `max_output_token`, but if you want different rules and want to assign different limits, you are forced to duplicate the config each time.

Considering all this, we are adding a dedicated field for this in the triage script, giving you an easy way to tweak it to your needs. If empty, no limit is applied.
2025-07-21 15:36:39 -03:00
Natalie Tay 5d80a34589 FIX: Add a max token limit based on the text to be translated (#1507)
We're seeing that some LLMs are using 65000+ tokens for raw text that is only 10-1000 characters long.

This PR adds a max_token to be passed to the LLM API for each translation based on the length of the text.
2025-07-17 17:47:15 +08:00
Natalie Tay 8630bc145e DEV: Set a min and max for translations backfill (#1508)
Since we use the value for days.ago and are seeing PG::DatetimeFieldOverflow: ERROR:  timestamp out of range: "5473790-07-13 08:43:28.497823 BC, set limits to the site setting.
2025-07-17 17:43:05 +08:00
Natalie Tay ad6a8cb812 DEV: Switch translations out of structured output as it returns only a single value (#1503)
Since translations only require a single key back, there is little point in using structured output. This PR also includes some prompt updates dealing with quotes, details, and code.

Related: #1502

This does mean reverting discourse/discourse-translator#257, but we can see how it goes.
2025-07-16 13:38:00 +08:00
Roman Rizzi 06743d1939 FIX: Fix embeddings to use the old OpenAI tokenizer (#1506) 2025-07-15 14:44:11 -03:00
Kris 67664029e5 FIX: prevent crash in "all" filter on features page (#1505)
* FIX: prevent crash in "all" filter on features page

* simplify
2025-07-15 12:51:46 -04:00
Sam 1b16fc876c FIX: avoid using structured outputs for report runs (#1502)
Structured outputs are prone to formatting issue, especially around newlines
and custom pieces of text that need escaping.

This avoids using it for the automation reporting.

Particularly previous to this fix o4-mini based reports were broken
2025-07-15 13:14:52 +10:00
Discourse Translator Bot 8c069490f0 Update translations (#1498) 2025-07-15 07:28:15 +10:00
Roman Rizzi 6a4dbd8126 FEATURE: Use Personas in Automation's llm_report script (#1499) 2025-07-14 12:47:21 -03:00
Roman Rizzi 89bcf9b1f0 FIX: Process succesfully generated embeddings even if some failed (#1500) 2025-07-10 17:51:01 -03:00
Martin Brennan 6b5ea38644 DEV: Improve AI bot conversation submit upload handling (#1497)
Try fix a flaky spec in /ai_bot/homepage_spec.rb by using ember
data rather than inspecting the DOM directly to see if there
are any in-progress uploads.

Also add missing translation for in progress uploads warning.
2025-07-10 17:06:39 +10:00
Natalie Tay d54cd1f602 DEV: Normalize locales that are similar (e.g. en and en_GB) so they do not get translated (#1495)
This commit
- normalizes locales like en_GB and variants to en. With this, the feature will not translate en_GB posts to en (or similarly pt_BR to pt_PT)
- consolidates whether the feature is enabled in `DiscourseAi::Translation.enabled?`
- similarly for backfill in  `DiscourseAi::Translation.backfill_enabled?`
  - turns off backfill if `ai_translation_backfill_max_age_days` is 0 to keep true to what it says. Set it to a high number to backfill everything
2025-07-09 22:21:51 +08:00
Keegan George cf7288e1bf DEV: Surface topic_id in errors of summarization backfill (#1493)
This update ensures that the `topic_id` related to the error when summarizing is surfaced in the logs, which should help track down the reason for the errors.
2025-07-09 06:39:36 -07:00
Discourse Translator Bot 027d7f1199 Update translations (#1492) 2025-07-09 15:27:19 +02:00
298 changed files with 1375 additions and 551 deletions
+1
View File
@@ -1,3 +1,4 @@
< 3.5.0.beta8-dev: 5d80a34589d63e381d80273828072badc18955b1
< 3.5.0.beta7-dev: cd14b0c0bee0cf63c59b64b6f7213e31a37f11a7
< 3.5.0.beta6-dev: 3e74eea1e5e3143888d67a8d8a11206df214dc24
< 3.5.0.beta3-dev: 09a68414804a1447f52e5d60691ba59742cda9ec
+1 -1
View File
@@ -34,7 +34,7 @@ GEM
racc
prettier_print (1.2.1)
racc (1.8.1)
rack (3.1.16)
rack (3.1.18)
rainbow (3.1.1)
regexp_parser (2.10.0)
rubocop (1.74.0)
+5 -2
View File
@@ -1,15 +1,18 @@
# **Discourse AI** Plugin
> [!IMPORTANT]
> This plugin has now been bundled into Discourse core. See: https://meta.discourse.org/t/bundling-more-popular-plugins-with-discourse-core/373574
**Plugin Summary**
For more information, please see: https://meta.discourse.org/t/discourse-ai/259214?u=falco
### Evals
The directory `evals` contains AI evals for the Discourse AI plugin.
The directory `evals` contains AI evals for the Discourse AI plugin.
You may create a local config by copying `config/eval-llms.yml` to `config/eval-llms.local.yml` and modifying the values.
To run them use:
To run them use:
cd evals
./run --help
+5 -4
View File
@@ -6,8 +6,7 @@ module Jobs
sidekiq_options retry: false
def execute(args)
return if !SiteSetting.discourse_ai_enabled
return if !SiteSetting.ai_translation_enabled
return if !DiscourseAi::Translation.enabled?
return if args[:post_id].blank?
post = Post.find_by(id: args[:post_id])
@@ -36,12 +35,14 @@ module Jobs
end
end
return if detected_locale.blank?
locales = SiteSetting.content_localization_supported_locales.split("|")
return if locales.blank?
locales.each do |locale|
next if locale == detected_locale
next if post.post_localizations.exists?(locale:)
next if LocaleNormalizer.is_same?(locale, detected_locale)
regionless_locale = locale.split("_").first
next if post.post_localizations.where("locale LIKE ?", "#{regionless_locale}%").exists?
begin
DiscourseAi::Translation::PostLocalizer.localize(post, locale)
+5 -4
View File
@@ -6,8 +6,7 @@ module Jobs
sidekiq_options retry: false
def execute(args)
return if !SiteSetting.discourse_ai_enabled
return if !SiteSetting.ai_translation_enabled
return if !DiscourseAi::Translation.enabled?
return if args[:topic_id].blank?
topic = Topic.find_by(id: args[:topic_id])
@@ -34,12 +33,14 @@ module Jobs
end
end
return if detected_locale.blank?
locales = SiteSetting.content_localization_supported_locales.split("|")
return if locales.blank?
locales.each do |locale|
next if locale == detected_locale
next if topic.topic_localizations.exists?(locale:)
next if LocaleNormalizer.is_same?(locale, detected_locale)
regionless_locale = locale.split("_").first
next if topic.topic_localizations.where("locale LIKE ?", "#{regionless_locale}%").exists?
begin
DiscourseAi::Translation::TopicLocalizer.localize(topic, locale)
+3 -6
View File
@@ -6,21 +6,17 @@ module Jobs
sidekiq_options retry: false
def execute(args)
return if !DiscourseAi::Translation.enabled?
limit = args[:limit]
raise Discourse::InvalidParameters.new(:limit) if limit.nil?
return if limit <= 0
return if !SiteSetting.discourse_ai_enabled
return if !SiteSetting.ai_translation_enabled
locales = SiteSetting.content_localization_supported_locales.split("|")
return if locales.blank?
categories = Category.where("locale IS NOT NULL")
if SiteSetting.ai_translation_backfill_limit_to_public_content
categories = categories.where(read_restricted: false)
end
categories = categories.order(:id).limit(limit)
return if categories.empty?
@@ -33,6 +29,7 @@ module Jobs
missing_locales = locales - existing_locales - [category.locale]
missing_locales.each do |locale|
break if remaining_limit <= 0
next if LocaleNormalizer.is_same?(locale, category.locale)
begin
DiscourseAi::Translation::CategoryLocalizer.localize(category, locale)
+8 -14
View File
@@ -9,23 +9,25 @@ module Jobs
limit = args[:limit]
raise Discourse::InvalidParameters.new(:limit) if limit.blank? || limit <= 0
return if !SiteSetting.discourse_ai_enabled
return if !SiteSetting.ai_translation_enabled
return if !DiscourseAi::Translation.backfill_enabled?
locales = SiteSetting.content_localization_supported_locales.split("|")
return if locales.blank?
locales.each do |locale|
base_locale = locale.split("_").first
posts =
Post
.joins(
"LEFT JOIN post_localizations pl ON pl.post_id = posts.id AND pl.locale = #{ActiveRecord::Base.connection.quote(locale)}",
"LEFT JOIN post_localizations pl ON pl.post_id = posts.id AND pl.locale LIKE '#{base_locale}%'",
)
.where(
"posts.created_at > ?",
SiteSetting.ai_translation_backfill_max_age_days.days.ago,
)
.where(deleted_at: nil)
.where("posts.user_id > 0")
.where.not(raw: [nil, ""])
.where.not(locale: nil)
.where.not(locale: locale)
.where("posts.locale NOT LIKE '#{base_locale}%'")
.where("pl.id IS NULL")
posts = posts.joins(:topic)
@@ -46,14 +48,6 @@ module Jobs
)
end
if SiteSetting.ai_translation_backfill_max_age_days > 0
posts =
posts.where(
"posts.created_at > ?",
SiteSetting.ai_translation_backfill_max_age_days.days.ago,
)
end
posts = posts.order(updated_at: :desc).limit(limit)
next if posts.empty?
+8 -14
View File
@@ -9,22 +9,24 @@ module Jobs
limit = args[:limit]
raise Discourse::InvalidParameters.new(:limit) if limit.blank? || limit <= 0
return if !SiteSetting.discourse_ai_enabled
return if !SiteSetting.ai_translation_enabled
return if !DiscourseAi::Translation.backfill_enabled?
locales = SiteSetting.content_localization_supported_locales.split("|")
return if locales.blank?
locales.each do |locale|
base_locale = locale.split("_").first
topics =
Topic
.joins(
"LEFT JOIN topic_localizations tl ON tl.topic_id = topics.id AND tl.locale = #{ActiveRecord::Base.connection.quote(locale)}",
"LEFT JOIN topic_localizations tl ON tl.topic_id = topics.id AND tl.locale LIKE '#{base_locale}%'",
)
.where(
"topics.created_at > ?",
SiteSetting.ai_translation_backfill_max_age_days.days.ago,
)
.where(deleted_at: nil)
.where("topics.user_id > 0")
.where.not(locale: nil)
.where.not(locale: locale)
.where("topics.locale NOT LIKE '#{base_locale}%'")
.where("tl.id IS NULL")
if SiteSetting.ai_translation_backfill_limit_to_public_content
@@ -43,14 +45,6 @@ module Jobs
)
end
if SiteSetting.ai_translation_backfill_max_age_days > 0
topics =
topics.where(
"topics.created_at > ?",
SiteSetting.ai_translation_backfill_max_age_days.days.ago,
)
end
topics = topics.order(updated_at: :desc).limit(limit)
next if topics.empty?
@@ -7,10 +7,7 @@ module Jobs
cluster_concurrency 1
def execute(args)
return if !SiteSetting.discourse_ai_enabled
return if !SiteSetting.ai_translation_enabled
limit = SiteSetting.ai_translation_backfill_hourly_rate
return if limit == 0
return if !DiscourseAi::Translation.backfill_enabled?
categories = Category.where(locale: nil)
@@ -18,6 +15,7 @@ module Jobs
categories = categories.where(read_restricted: false)
end
limit = SiteSetting.ai_translation_backfill_hourly_rate
categories = categories.limit(limit)
return if categories.empty?
@@ -6,11 +6,8 @@ module Jobs
cluster_concurrency 1
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 !DiscourseAi::Translation.backfill_enabled?
limit = SiteSetting.ai_translation_backfill_hourly_rate
return if limit == 0
Jobs.enqueue(:localize_categories, limit:)
end
@@ -6,10 +6,8 @@ module Jobs
cluster_concurrency 1
def execute(args)
return if !SiteSetting.discourse_ai_enabled
return if !SiteSetting.ai_translation_enabled
return if !DiscourseAi::Translation.backfill_enabled?
return if SiteSetting.content_localization_supported_locales.blank?
limit = SiteSetting.ai_translation_backfill_hourly_rate / (60 / 5) # this job runs in 5-minute intervals
return if limit == 0
@@ -7,16 +7,16 @@ module Jobs
cluster_concurrency 1
def execute(args)
return if !SiteSetting.discourse_ai_enabled
return if !SiteSetting.ai_translation_enabled
return if !DiscourseAi::Translation.backfill_enabled?
limit = SiteSetting.ai_translation_backfill_hourly_rate / (60 / 5) # this job runs in 5-minute intervals
return if limit == 0
posts =
Post
.where(locale: nil)
.where(deleted_at: nil)
.where("posts.user_id > 0")
.where("posts.created_at > ?", SiteSetting.ai_translation_backfill_max_age_days.days.ago)
.where.not(raw: [nil, ""])
if SiteSetting.ai_translation_backfill_limit_to_public_content
@@ -33,14 +33,6 @@ module Jobs
)
end
if SiteSetting.ai_translation_backfill_max_age_days > 0
posts =
posts.where(
"posts.created_at > ?",
SiteSetting.ai_translation_backfill_max_age_days.days.ago,
)
end
posts = posts.order(updated_at: :desc).limit(limit)
return if posts.empty?
+6 -1
View File
@@ -36,7 +36,12 @@ module ::Jobs
existing_summary = strategy.existing_summary
if existing_summary.blank? || existing_summary.outdated
strategy.summarize(user)
begin
strategy.summarize(user)
rescue => e
Rails.logger.error("Error summarizing topic #{topic.id}: #{e.class.name} - #{e.message}")
raise e
end
else
# Hiding or deleting a post, and creating a small action alters the Topic#highest_post_number.
# We use this as a quick way to select potential backfill candidates without relying on original_content_sha.
@@ -6,13 +6,9 @@ module Jobs
cluster_concurrency 1
def execute(args)
return if !SiteSetting.discourse_ai_enabled
return if !SiteSetting.ai_translation_enabled
return if !DiscourseAi::Translation.backfill_enabled?
return if SiteSetting.content_localization_supported_locales.blank?
limit = SiteSetting.ai_translation_backfill_hourly_rate / (60 / 5) # this job runs in 5-minute intervals
return if limit == 0
Jobs.enqueue(:localize_topics, limit:)
end
end
@@ -7,12 +7,15 @@ module Jobs
cluster_concurrency 1
def execute(args)
return if !SiteSetting.discourse_ai_enabled
return if !SiteSetting.ai_translation_enabled
limit = SiteSetting.ai_translation_backfill_hourly_rate / (60 / 5) # this job runs in 5-minute intervals
return if limit == 0
return if !DiscourseAi::Translation.backfill_enabled?
topics = Topic.where(locale: nil, deleted_at: nil).where("topics.user_id > 0")
limit = SiteSetting.ai_translation_backfill_hourly_rate / (60 / 5) # this job runs in 5-minute intervals
topics =
Topic
.where(locale: nil, deleted_at: nil)
.where("topics.user_id > 0")
.where("topics.created_at > ?", SiteSetting.ai_translation_backfill_max_age_days.days.ago)
if SiteSetting.ai_translation_backfill_limit_to_public_content
topics =
@@ -28,14 +31,6 @@ module Jobs
)
end
if SiteSetting.ai_translation_backfill_max_age_days > 0
topics =
topics.where(
"topics.created_at > ?",
SiteSetting.ai_translation_backfill_max_age_days.days.ago,
)
end
topics = topics.order(updated_at: :desc).limit(limit)
return if topics.empty?
+3 -3
View File
@@ -84,7 +84,7 @@ class EmbeddingDefinition < ActiveRecord::Base
dimensions: 2000,
max_sequence_length: 8191,
pg_function: "<=>",
tokenizer_class: "DiscourseAi::Tokenizer::OpenAiTokenizer",
tokenizer_class: "DiscourseAi::Tokenizer::OpenAiCl100kTokenizer",
url: "https://api.openai.com/v1/embeddings",
provider: OPEN_AI,
matryoshka_dimensions: true,
@@ -98,7 +98,7 @@ class EmbeddingDefinition < ActiveRecord::Base
dimensions: 1536,
max_sequence_length: 8191,
pg_function: "<=>",
tokenizer_class: "DiscourseAi::Tokenizer::OpenAiTokenizer",
tokenizer_class: "DiscourseAi::Tokenizer::OpenAiCl100kTokenizer",
url: "https://api.openai.com/v1/embeddings",
provider: OPEN_AI,
matryoshka_dimensions: true,
@@ -112,7 +112,7 @@ class EmbeddingDefinition < ActiveRecord::Base
dimensions: 1536,
max_sequence_length: 8191,
pg_function: "<=>",
tokenizer_class: "DiscourseAi::Tokenizer::OpenAiTokenizer",
tokenizer_class: "DiscourseAi::Tokenizer::OpenAiCl100kTokenizer",
url: "https://api.openai.com/v1/embeddings",
provider: OPEN_AI,
provider_params: {
@@ -260,10 +260,11 @@ export default class AiBotConversations extends Component {
@action
async prepareAndSubmitToBot() {
// Pass uploads to the service before submitting
this.aiBotConversationsHiddenSubmit.uploads = this.uploads;
try {
await this.aiBotConversationsHiddenSubmit.submitToBot();
await this.aiBotConversationsHiddenSubmit.submitToBot({
uploads: this.uploads,
inProgressUploadsCount: this.inProgressUploads.length,
});
this.uploads = new TrackedArray();
} catch (error) {
popupAjaxError(error);
@@ -66,12 +66,9 @@ class ExpandableList extends Component {
export default class AiFeaturesList extends Component {
get sortedModules() {
if (!this.args.modules || !this.args.modules.length) {
return [];
}
return this.args.modules.toSorted((a, b) =>
a.module_name.localeCompare(b.module_name)
const modules = this.args.modules?.toArray() || this.args.modules;
return [...(modules || [])].toSorted((a, b) =>
(a.module_name || "").localeCompare(b.module_name || "")
);
}
@@ -18,7 +18,6 @@ export default class AiBotConversationsHiddenSubmit extends Service {
personaId;
targetUsername;
uploads = [];
inputValue = "";
@@ -32,7 +31,7 @@ export default class AiBotConversationsHiddenSubmit extends Service {
}
@action
async submitToBot() {
async submitToBot(uploadData) {
if (
this.inputValue.length <
this.siteSettings.min_personal_message_post_length
@@ -48,7 +47,7 @@ export default class AiBotConversationsHiddenSubmit extends Service {
}
// Don't submit if there are still uploads in progress
if (document.querySelector(".ai-bot-upload--in-progress")) {
if (uploadData.inProgressUploadsCount > 0) {
return this.dialog.alert({
message: i18n("discourse_ai.ai_bot.conversations.uploads_in_progress"),
});
@@ -61,10 +60,10 @@ export default class AiBotConversationsHiddenSubmit extends Service {
let rawContent = this.inputValue;
// Append upload markdown if we have uploads
if (this.uploads && this.uploads.length > 0) {
if (uploadData.uploads && uploadData.uploads.length > 0) {
rawContent += "\n\n";
this.uploads.forEach((upload) => {
uploadData.uploads.forEach((upload) => {
const uploadMarkdown = getUploadMarkdown(upload);
rawContent += uploadMarkdown + "\n";
});
@@ -83,7 +82,6 @@ export default class AiBotConversationsHiddenSubmit extends Service {
});
// Reset uploads after successful submission
this.uploads = [];
this.inputValue = "";
this.appEvents.trigger("discourse-ai:bot-pm-created", {
+2
View File
@@ -158,6 +158,8 @@ ar:
reset: "إعادة التعيين"
search:
name: "البحث"
embeddings:
name: "التضمينات"
ai_helper:
name: "المساعد"
proofread: تدقيق النص لغويًا
+85 -8
View File
@@ -29,6 +29,22 @@ de:
label: "Sortieren nach"
tag:
label: "Schlagwort"
logs:
staff_actions:
actions:
create_ai_llm_model: "LLM-Modell erstellen"
update_ai_llm_model: "LLM-Modell aktualisieren"
delete_ai_llm_model: "LLM-Modell löschen"
create_ai_persona: "KI-Persona erstellen"
update_ai_persona: "KI-Persona aktualisieren"
delete_ai_persona: "KI-Persona löschen"
create_ai_tool: "KI-Tool erstellen"
update_ai_tool: "KI-Tool aktualisieren"
delete_ai_tool: "KI-Tool löschen"
create_ai_embedding: "KI-Einbettung erstellen"
update_ai_embedding: "KI-Einbettung aktualisieren"
delete_ai_embedding: "KI-Einbettung löschen"
update_ai_spam_settings: "KI-Spam-Einstellungen aktualisieren"
js:
discourse_automation:
scriptables:
@@ -98,21 +114,21 @@ de:
fields:
model:
label: "Modell"
description: "Das für die Triage verwendete Standard-Sprachmodell"
description: "Das für die Sichtung verwendete Standard-Sprachmodell"
tool:
label: "Werkzeug"
description: "Werkzeug für die Triage (das Werkzeug darf keine Parameter definiert haben)"
label: "Tool"
description: "Tool für die Sichtung (das Tool darf keine Parameter definiert haben)"
llm_persona_triage:
fields:
persona:
label: "Persona"
description: "KI-Persona, die für die Triage verwendet werden soll (Standard-LLM und -Benutzer müssen eingestellt sein)"
description: "KI-Persona, die für die Sichtung verwendet werden soll (Standard-LLM und -Benutzer müssen eingestellt sein)"
whisper:
label: "Als Flüstern antworten"
description: "Ob die Antwort der Persona ein Flüstern sein soll"
silent_mode:
label: "Stiller Modus"
description: "Im stillen Modus empfängt die Persona den Inhalt, schreibt aber nichts in das Forum - nützlich bei der Triage mit Tools"
description: "Im stillen Modus empfängt die Persona den Inhalt, schreibt aber nichts in das Forum - nützlich bei der Sichtung mit Tools"
llm_triage:
fields:
system_prompt:
@@ -177,6 +193,7 @@ de:
llm:
one: "LLM:"
other: "LLM:"
no_llm: "Kein LLM ausgewählt"
no_persona: "Nicht festgelegt"
no_groups: "Keiner"
edit: "Bearbeiten"
@@ -184,35 +201,75 @@ de:
one: "(%{count} weitere)"
other: "(%{count} weitere)"
collapse_list: "(weniger anzeigen)"
bot:
bot: "Chatbot"
name: "Bot"
description: "Ein Chatbot, der Fragen beantworten und Benutzern in persönlichen Nachrichten, im Forum und im Chat helfen kann"
nav:
configured: "Konfiguriert"
unconfigured: "Nicht konfiguriert"
filters:
all: "Gesamt"
text: "Suche nach Funktionen, Personas, LLMs oder Gruppen..."
no_results: "Es wurden keine Funktionen gefunden, die deinen Filtern entsprechen."
reset: "Zurücksetzen"
summarization:
name: "Zusammenfassungen"
description: "Stellt eine Schaltfläche für Zusammenfassungen zur Verfügung, mit der Besucher Themen zusammenfassen können"
topic_summaries: "Themenzusammenfassungen"
gists: "Kurze Zusammenfassungen der in Themenliste"
search:
name: "Suche"
description: "Verbessert das Sucherlebnis durch KI-generierte Antworten auf Suchanfragen"
discoveries: "Entdeckungen"
embeddings:
name: "Einbettungen"
description: "Ermöglicht Funktionen wie „Verwandte Themen“ und „KI-Suche“ durch die Erstellung semantischer Textdarstellungen"
hyde: "HyDE"
discord:
name: "Discord-Integration"
description: "Fügt die Möglichkeit hinzu, Discord-Kanäle zu durchsuchen"
search: "Discord-Suche"
inference:
name: "Abgeleitete Konzepte"
description: "Ordnet Themen und Beiträge in Interessensgebiete / Labels."
generate_concepts: "Konzepterkennung"
match_concepts: "Passende Konzepte"
deduplicate_concepts: "Deduplizierung von Konzepten"
ai_helper:
name: "Helfer"
description: "Unterstützt Benutzer bei der Interaktion in der Community, z. B. beim Erstellen von Themen, Verfassen von Beiträgen und Lesen von Inhalten"
proofread: Text korrekturlesen
title_suggestions: "Titel vorschlagen"
explain: "Erklären"
illustrate_post: "Beitrag illustrieren"
smart_dates: "Intelligente Termine"
translate: "Übersetzen"
markdown_tables: "Markdown-Tabelle generieren"
custom_prompt: "Benutzerdefinierte Eingabeaufforderung"
image_caption: "Bildbeschriftungen"
translator: "Übersetzer"
translation:
name: "Übersetzen"
description: "Übersetzt Inhalte in unterstützte Sprachen"
locale_detector: "Sprachdetektor"
post_raw_translator: "Übersetzer für die Roheingabe des Beitrags"
topic_title_translator: "Thementitel-Übersetzer"
short_text_translator: "Kurztext-Übersetzer"
spam:
name: "Spam"
description: "Identifiziert potenziellen Spam mithilfe des ausgewählten LLMs und meldet ihn den Moderatoren der Website in der Warteschlange zur Überprüfung."
inspect_posts: "Beiträge prüfen"
modals:
select_option: "Wähle eine Option aus..."
layout:
table: "Tabelle"
card: "Karte"
spam:
short_title: "Spam"
title: "Spam-Behandlung konfigurieren"
select_llm: "LLM auswählen"
select_persona: "Persona auswählen"
custom_instructions: "Benutzerdefinierte Anweisungen"
custom_instructions_help: "Benutzerdefinierte Anweisungen speziell für deine Website, die der KI beim Identifizieren von Spam helfen, z. B. „Gehe beim Scannen von Posts, die nicht in englischer Sprache sind, aggressiver vor.“"
last_seven_days: "Letzte 7 Tage"
@@ -293,6 +350,9 @@ de:
name: "Name"
edit: "Bearbeiten"
export: "Exportieren"
import: "Importieren"
import_error_conflict: "Beim Importieren von %{name} wurde ein Konflikt erkannt. Möchtest du die vorhandene Persona aktualisieren?"
overwrite: "Überschreiben"
description: "Beschreibung"
no_llm_selected: "Kein Sprachmodell ausgewählt"
use_parent_llm: "Verwende das Personas Sprachmodell"
@@ -370,7 +430,10 @@ de:
title: "KI-Bot-Optionen"
save_first: "Weitere KI-Bot-Optionen werden verfügbar, sobald du die Persona gespeichert hast."
filters:
text: "Finde eine Persona"
reset: "Zurücksetzen"
no_results: "Es wurden keine Personas gefunden, die deinen Filtern entsprechen."
all_features: "Jede Funktion"
features_list:
one: "Funktion:"
other: "Funktionen:"
@@ -401,11 +464,14 @@ de:
back: "Zurück"
short_title: "Tools"
export: "Exportieren"
import: "Importieren"
import_error_conflict: "Tool existiert bereits, möchtest du es aktualisieren?"
overwrite: "Überschreiben"
no_tools: "Du hast noch keine Tools erstellt"
name: "Name"
name_help: "Der Name wird in der Discourse-Benutzeroberfläche angezeigt und ist die Kurzkennung, die Du verwendest, um das Werkzeug in verschiedenen Einstellungen zu finden. Er sollte eindeutig sein (er ist erforderlich)."
name_help: "Der Name wird in der Discourse-Benutzeroberfläche angezeigt und ist die Kurzkennung, die du verwendest, um das Tool in verschiedenen Einstellungen zu finden. Er sollte eindeutig sein (er ist erforderlich)."
new: "Neues Tool"
tool_name: "Werkzeugname"
tool_name: "Name des Tools"
tool_name_help: "Der Werkzeugname wird dem großen Sprachmodell präsentiert. Es ist nicht eindeutig, aber es ist von Person zu Person unterschiedlich. (Persona wird beim Speichern validiert)"
description: "Beschreibung"
description_help: "Eine klare Beschreibung des Zwecks des Tools für das Sprachmodell"
@@ -486,14 +552,18 @@ de:
ai_summarization: "Zusammenfassen"
ai_embeddings_semantic_search: "KI-Suche"
ai_spam: "Spam"
automation: "Automatisierung (%{name})"
automation: "Automatisierung (%{persona})"
in_use_warning:
one: "Dieses Modell wird derzeit von %{settings} verwendet. Wenn es falsch konfiguriert ist, wird die Funktion nicht wie erwartet funktionieren."
other: "Dieses Modell wird derzeit verwendet von: %{settings}. Wenn es falsch konfiguriert ist, werden die Funktionen nicht wie erwartet funktionieren. "
model_description:
none: "Allgemeine Einstellungen, die für die meisten Sprachmodelle funktionieren"
anthropic-claude-opus-4-0: "Das intelligenteste Modell von Anthropic"
anthropic-claude-sonnet-4-0: "Optimales Gleichgewicht zwischen Geschwindigkeit und Kosten"
anthropic-claude-3-7-sonnet-latest: "Optimales Gleichgewicht zwischen Geschwindigkeit und Kosten (vorherige Generation)"
anthropic-claude-3-5-haiku-latest: "Schnell und kosteneffizient"
google-gemini-2-5-pro: "Großes multimodales Modell, das eine Vielzahl von Aufgaben bewältigen kann"
google-gemini-2-0-flash: "Leicht, schnell und kosteneffizient mit multimodaler Argumentation (vorherige Generation)"
google-gemini-2-5-flash: "Leicht, schnell und kosteneffizient mit multimodaler Argumentation"
google-gemini-2-0-flash-lite: "Kosteneffizientes Modell mit niedriger Latenz"
open_ai-o3: "Das leistungsfähigste Argumentationsmodell von Open AI"
@@ -505,6 +575,9 @@ de:
samba_nova-Meta-Llama-3-3-70B-Instruct": "Leistungsstarkes Mehrzweckmodell"
mistral-mistral-large-latest: "Das leistungsstärkste Modell von Mistral"
mistral-pixtral-large-latest: "Das leistungsstärkste Modell von Mistral mit Sichtfunktion"
open_router-x-ai-grok-3-beta: "Das neueste Modell von xAI"
open_router-deepseek-deepseek-r1-0528-free: "DeepSeeks neuestes Argumentationsmodell"
open_router-meta-llama-3-3-70b-instruct: "Leistungsstarkes mehrsprachiges Modell"
preseeded_model_description: "Vorkonfiguriertes Open-Source-Modell unter Verwendung von %{model}"
configured:
title: "Konfigurierte LLMs"
@@ -565,6 +638,7 @@ de:
reasoning_tokens: "Anzahl der für die Argumentation verwendeten Token"
disable_temperature: "Temperatur deaktivieren (einige Denkmodelle unterstützen keine Temperatur)"
disable_top_p: "Top P deaktivieren (einige Denkmodelle unterstützen Top P nicht)"
enable_responses_api: "Aktiviere die Antwort-API (bei bestimmten OpenAI-Modellen erforderlich)"
related_topics:
title: "Verwandte Themen"
pill: "Verwandt"
@@ -583,6 +657,7 @@ de:
trigger: "KI fragen"
loading: "KI generiert"
cancel: "Abbrechen"
regen: "Erneut versuchen"
confirm: "Bestätigen"
discard: "Verwerfen"
changes: "Empfohlene Änderungen"
@@ -700,6 +775,7 @@ de:
shortcut_title: "Starte eine PN mit einem KI-Bot"
share: "KI-Unterhaltung kopieren"
conversation_shared: "Unterhaltung kopiert"
embed_copied: "Einbettung in die Zwischenablage kopiert"
debug_ai: "Rohdaten der KI-Anfrage und -Antwort anzeigen"
sidebar_empty: "Der Verlauf der Bot-Konversation wird hier angezeigt."
debug_ai_modal:
@@ -759,6 +835,7 @@ de:
today: "Heute"
last_7_days: "Letzte 7 Tage"
last_30_days: "Letzte 30 Tage"
upload_files: "Dateien hochladen"
sentiments:
dashboard:
title: "Stimmung"
+7
View File
@@ -106,6 +106,9 @@ en:
top_p:
label: "Top P"
description: "Top P to use for the LLM, increase to increase randomness (leave empty to use model default)"
persona_id:
label: "Persona"
description: "AI Persona to use for report generation"
llm_tool_triage:
fields:
@@ -177,6 +180,9 @@ en:
temperature:
label: "Temperature"
description: "Temperature to use for the LLM. Increase to increase randomness (leave empty to use model default)"
max_output_tokens:
label: "Max output tokens"
description: "When specified, sets an upper bound to the maximum number of tokens the model can generate. Respects LLM's max output tokens limit"
discourse_ai:
title: "AI"
@@ -872,6 +878,7 @@ en:
last_7_days: "Last 7 days"
last_30_days: "Last 30 days"
upload_files: "Upload files"
uploads_in_progress: "Cannot submit while uploads are in progress"
sentiments:
dashboard:
title: "Sentiment"
+2
View File
@@ -154,6 +154,8 @@ es:
reset: "Restablecer"
search:
name: "Buscar"
embeddings:
name: "Incrustaciones"
ai_helper:
name: "Ayudante"
proofread: Corregir el texto
+2
View File
@@ -154,6 +154,8 @@ fi:
reset: "Palauta"
search:
name: "Hae"
embeddings:
name: "Upotukset"
ai_helper:
name: "Apuri"
proofread: Oikolue teksti
+2
View File
@@ -154,6 +154,8 @@ fr:
reset: "Réinitialiser"
search:
name: "Recherche"
embeddings:
name: "Intégrations"
ai_helper:
name: "Assistant"
proofread: Relisez le texte
+22 -1
View File
@@ -185,6 +185,7 @@ he:
two: "LLMים:"
many: "LLMים:"
other: "LLMים:"
no_llm: "לא נבחר מש״ג/LLM"
no_persona: "לא מוגדר"
no_groups: "ללא"
edit: "עריכה"
@@ -196,24 +197,44 @@ he:
collapse_list: "(להציג פחות)"
bot:
name: "בוט"
description: "צ׳אט בוט שיכול לענות על שאלות ולסייע למשתמשים בהודעות אישיות, בפורום ובצ׳אט"
filters:
all: "הכול"
text: "חיפוש יכולות, דמויות, LLMים/מש״גים או קבוצות…"
reset: "איפוס"
summarization:
name: "סיכומים"
description: "להנגיש את כפתור הסיכום שמאפשר למבקרים לסכם נושאים"
topic_summaries: "תקצירי נושאים."
search:
name: "חיפוש"
description: "שיפור חוויית החיפוש על ידי אספקת תשובות שנוצרו על ידי בינה מלאכותית לשאילתות"
discoveries: "תגליות"
embeddings:
name: "הטמעות"
discord:
name: "שילוב מול Discord"
description: "הוספת היכולת לחפש בערוצי Discord"
search: "חיפוש ב־Discord"
inference:
generate_concepts: "הסקת תפיסות"
ai_helper:
name: "מסייע"
proofread: הגהת הטקסט
title_suggestions: "הצעת כותרות"
explain: "הסבר"
illustrate_post: "איור פוסט"
smart_dates: "תאריכים חכמים"
translate: "תרגום"
markdown_tables: "יצירת טבלה ב־Markdown"
custom_prompt: "בקשה מותאמת אישית"
image_caption: "הוספת כותרות לתמונות"
translation:
description: "תרגום תוכן לשפות נתמכות"
locale_detector: "מזהה שפה ומקום"
post_raw_translator: "מתרגם פוסטים גולמי"
topic_title_translator: "מתרגם כותרות נושאים"
short_text_translator: "מתרגם טקסטים קצרים"
spam:
name: "ספאם"
modals:
@@ -491,7 +512,7 @@ he:
ai_summarization: "סיכום"
ai_embeddings_semantic_search: "חיפוש בינה מלאכותית"
ai_spam: "ספאם"
automation: "אוטומציה (%{name})"
automation: "אוטומציה (%{persona})"
in_use_warning:
one: "המודל הזה משמש את %{settings}. אם לא מוגדר כראוי, היכולת לא תעבוד כמצופה."
two: "המודל הזה משמש את: %{settings}. אם ההגדרות שגויות היכולת לא תעבודנה כמצופה."
+2
View File
@@ -154,6 +154,8 @@ it:
reset: "Annulla"
search:
name: "Cerca"
embeddings:
name: "Integrazioni"
ai_helper:
name: "Assistente"
proofread: Testo corretto
+2
View File
@@ -157,6 +157,8 @@ ja:
reset: "リセット"
search:
name: "検索"
embeddings:
name: "埋め込み"
ai_helper:
name: "ヘルパー"
proofread: テキストを構成
+2
View File
@@ -154,6 +154,8 @@ nl:
reset: "Resetten"
search:
name: "Zoeken"
embeddings:
name: "Insluitingen"
ai_helper:
name: "Helper"
proofread: Tekst proeflezen
+2
View File
@@ -154,6 +154,8 @@ pt_BR:
reset: "Redefinir"
search:
name: "Pesquisar"
embeddings:
name: "Incorporações"
ai_helper:
name: "Ajudante"
proofread: Revisar texto

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