Compare commits
46
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
822594f3e3 | ||
|
|
7dee59c97f | ||
|
|
fbbe01e576 | ||
|
|
f420261196 | ||
|
|
eb93e1736b | ||
|
|
cc34882074 | ||
|
|
4b03cdcc96 | ||
|
|
d9b53b4a3d | ||
|
|
a927fd2270 | ||
|
|
f077d58abf | ||
|
|
08d2f3ddf9 | ||
|
|
273a1fa618 | ||
|
|
7af3ce820d | ||
|
|
5841543bd9 | ||
|
|
5ae80645bb | ||
|
|
b675c4c39b | ||
|
|
0fadf1da1a | ||
|
|
c26d604072 | ||
|
|
ad6a8cb812 | ||
|
|
7ae61ce877 | ||
|
|
f593ab6e1e | ||
|
|
b58f198ee8 | ||
|
|
06743d1939 | ||
|
|
88811f97bf | ||
|
|
2377b286dd | ||
|
|
67664029e5 | ||
|
|
1b16fc876c | ||
|
|
8c069490f0 | ||
|
|
6a4dbd8126 | ||
|
|
8da4318021 | ||
|
|
b6a88ff03e | ||
|
|
f366ded03b | ||
|
|
87c42145d4 | ||
|
|
c49f1df2ba | ||
|
|
b46940fc66 | ||
|
|
249aab100c | ||
|
|
89bcf9b1f0 | ||
|
|
5a29074799 | ||
|
|
6b5ea38644 | ||
|
|
29d415755a | ||
|
|
5a590fddc1 | ||
|
|
cad39c1aa4 | ||
|
|
2592be65ff | ||
|
|
d54cd1f602 | ||
|
|
cf7288e1bf | ||
|
|
027d7f1199 |
@@ -6,8 +6,7 @@ module Jobs
|
|||||||
sidekiq_options retry: false
|
sidekiq_options retry: false
|
||||||
|
|
||||||
def execute(args)
|
def execute(args)
|
||||||
return if !SiteSetting.discourse_ai_enabled
|
return if !DiscourseAi::Translation.enabled?
|
||||||
return if !SiteSetting.ai_translation_enabled
|
|
||||||
return if args[:post_id].blank?
|
return if args[:post_id].blank?
|
||||||
|
|
||||||
post = Post.find_by(id: args[:post_id])
|
post = Post.find_by(id: args[:post_id])
|
||||||
@@ -36,12 +35,14 @@ module Jobs
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
return if detected_locale.blank?
|
||||||
locales = SiteSetting.content_localization_supported_locales.split("|")
|
locales = SiteSetting.content_localization_supported_locales.split("|")
|
||||||
return if locales.blank?
|
return if locales.blank?
|
||||||
|
|
||||||
locales.each do |locale|
|
locales.each do |locale|
|
||||||
next if locale == detected_locale
|
next if DiscourseAi::Translation::LocaleNormalizer.is_same?(locale, detected_locale)
|
||||||
next if post.post_localizations.exists?(locale:)
|
regionless_locale = locale.split("_").first
|
||||||
|
next if post.post_localizations.where("locale LIKE ?", "#{regionless_locale}%").exists?
|
||||||
|
|
||||||
begin
|
begin
|
||||||
DiscourseAi::Translation::PostLocalizer.localize(post, locale)
|
DiscourseAi::Translation::PostLocalizer.localize(post, locale)
|
||||||
|
|||||||
@@ -6,8 +6,7 @@ module Jobs
|
|||||||
sidekiq_options retry: false
|
sidekiq_options retry: false
|
||||||
|
|
||||||
def execute(args)
|
def execute(args)
|
||||||
return if !SiteSetting.discourse_ai_enabled
|
return if !DiscourseAi::Translation.enabled?
|
||||||
return if !SiteSetting.ai_translation_enabled
|
|
||||||
return if args[:topic_id].blank?
|
return if args[:topic_id].blank?
|
||||||
|
|
||||||
topic = Topic.find_by(id: args[:topic_id])
|
topic = Topic.find_by(id: args[:topic_id])
|
||||||
@@ -34,12 +33,14 @@ module Jobs
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
return if detected_locale.blank?
|
||||||
locales = SiteSetting.content_localization_supported_locales.split("|")
|
locales = SiteSetting.content_localization_supported_locales.split("|")
|
||||||
return if locales.blank?
|
return if locales.blank?
|
||||||
|
|
||||||
locales.each do |locale|
|
locales.each do |locale|
|
||||||
next if locale == detected_locale
|
next if DiscourseAi::Translation::LocaleNormalizer.is_same?(locale, detected_locale)
|
||||||
next if topic.topic_localizations.exists?(locale:)
|
regionless_locale = locale.split("_").first
|
||||||
|
next if topic.topic_localizations.where("locale LIKE ?", "#{regionless_locale}%").exists?
|
||||||
|
|
||||||
begin
|
begin
|
||||||
DiscourseAi::Translation::TopicLocalizer.localize(topic, locale)
|
DiscourseAi::Translation::TopicLocalizer.localize(topic, locale)
|
||||||
|
|||||||
@@ -6,21 +6,17 @@ module Jobs
|
|||||||
sidekiq_options retry: false
|
sidekiq_options retry: false
|
||||||
|
|
||||||
def execute(args)
|
def execute(args)
|
||||||
|
return if !DiscourseAi::Translation.enabled?
|
||||||
|
|
||||||
limit = args[:limit]
|
limit = args[:limit]
|
||||||
raise Discourse::InvalidParameters.new(:limit) if limit.nil?
|
raise Discourse::InvalidParameters.new(:limit) if limit.nil?
|
||||||
return if limit <= 0
|
return if limit <= 0
|
||||||
|
|
||||||
return if !SiteSetting.discourse_ai_enabled
|
|
||||||
return if !SiteSetting.ai_translation_enabled
|
|
||||||
locales = SiteSetting.content_localization_supported_locales.split("|")
|
locales = SiteSetting.content_localization_supported_locales.split("|")
|
||||||
return if locales.blank?
|
|
||||||
|
|
||||||
categories = Category.where("locale IS NOT NULL")
|
categories = Category.where("locale IS NOT NULL")
|
||||||
|
|
||||||
if SiteSetting.ai_translation_backfill_limit_to_public_content
|
if SiteSetting.ai_translation_backfill_limit_to_public_content
|
||||||
categories = categories.where(read_restricted: false)
|
categories = categories.where(read_restricted: false)
|
||||||
end
|
end
|
||||||
|
|
||||||
categories = categories.order(:id).limit(limit)
|
categories = categories.order(:id).limit(limit)
|
||||||
return if categories.empty?
|
return if categories.empty?
|
||||||
|
|
||||||
@@ -33,6 +29,7 @@ module Jobs
|
|||||||
missing_locales = locales - existing_locales - [category.locale]
|
missing_locales = locales - existing_locales - [category.locale]
|
||||||
missing_locales.each do |locale|
|
missing_locales.each do |locale|
|
||||||
break if remaining_limit <= 0
|
break if remaining_limit <= 0
|
||||||
|
next if DiscourseAi::Translation::LocaleNormalizer.is_same?(locale, category.locale)
|
||||||
|
|
||||||
begin
|
begin
|
||||||
DiscourseAi::Translation::CategoryLocalizer.localize(category, locale)
|
DiscourseAi::Translation::CategoryLocalizer.localize(category, locale)
|
||||||
|
|||||||
@@ -9,23 +9,25 @@ module Jobs
|
|||||||
limit = args[:limit]
|
limit = args[:limit]
|
||||||
raise Discourse::InvalidParameters.new(:limit) if limit.blank? || limit <= 0
|
raise Discourse::InvalidParameters.new(:limit) if limit.blank? || limit <= 0
|
||||||
|
|
||||||
return if !SiteSetting.discourse_ai_enabled
|
return if !DiscourseAi::Translation.backfill_enabled?
|
||||||
return if !SiteSetting.ai_translation_enabled
|
|
||||||
|
|
||||||
locales = SiteSetting.content_localization_supported_locales.split("|")
|
locales = SiteSetting.content_localization_supported_locales.split("|")
|
||||||
return if locales.blank?
|
|
||||||
|
|
||||||
locales.each do |locale|
|
locales.each do |locale|
|
||||||
|
base_locale = locale.split("_").first
|
||||||
posts =
|
posts =
|
||||||
Post
|
Post
|
||||||
.joins(
|
.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(deleted_at: nil)
|
||||||
.where("posts.user_id > 0")
|
.where("posts.user_id > 0")
|
||||||
.where.not(raw: [nil, ""])
|
.where.not(raw: [nil, ""])
|
||||||
.where.not(locale: nil)
|
.where.not(locale: nil)
|
||||||
.where.not(locale: locale)
|
.where("posts.locale NOT LIKE '#{base_locale}%'")
|
||||||
.where("pl.id IS NULL")
|
.where("pl.id IS NULL")
|
||||||
|
|
||||||
posts = posts.joins(:topic)
|
posts = posts.joins(:topic)
|
||||||
@@ -46,14 +48,6 @@ module Jobs
|
|||||||
)
|
)
|
||||||
end
|
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)
|
posts = posts.order(updated_at: :desc).limit(limit)
|
||||||
|
|
||||||
next if posts.empty?
|
next if posts.empty?
|
||||||
|
|||||||
@@ -9,22 +9,24 @@ module Jobs
|
|||||||
limit = args[:limit]
|
limit = args[:limit]
|
||||||
raise Discourse::InvalidParameters.new(:limit) if limit.blank? || limit <= 0
|
raise Discourse::InvalidParameters.new(:limit) if limit.blank? || limit <= 0
|
||||||
|
|
||||||
return if !SiteSetting.discourse_ai_enabled
|
return if !DiscourseAi::Translation.backfill_enabled?
|
||||||
return if !SiteSetting.ai_translation_enabled
|
|
||||||
|
|
||||||
locales = SiteSetting.content_localization_supported_locales.split("|")
|
locales = SiteSetting.content_localization_supported_locales.split("|")
|
||||||
return if locales.blank?
|
|
||||||
|
|
||||||
locales.each do |locale|
|
locales.each do |locale|
|
||||||
|
base_locale = locale.split("_").first
|
||||||
topics =
|
topics =
|
||||||
Topic
|
Topic
|
||||||
.joins(
|
.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(deleted_at: nil)
|
||||||
.where("topics.user_id > 0")
|
.where("topics.user_id > 0")
|
||||||
.where.not(locale: nil)
|
.where.not(locale: nil)
|
||||||
.where.not(locale: locale)
|
.where("topics.locale NOT LIKE '#{base_locale}%'")
|
||||||
.where("tl.id IS NULL")
|
.where("tl.id IS NULL")
|
||||||
|
|
||||||
if SiteSetting.ai_translation_backfill_limit_to_public_content
|
if SiteSetting.ai_translation_backfill_limit_to_public_content
|
||||||
@@ -43,14 +45,6 @@ module Jobs
|
|||||||
)
|
)
|
||||||
end
|
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)
|
topics = topics.order(updated_at: :desc).limit(limit)
|
||||||
|
|
||||||
next if topics.empty?
|
next if topics.empty?
|
||||||
|
|||||||
@@ -7,10 +7,7 @@ module Jobs
|
|||||||
cluster_concurrency 1
|
cluster_concurrency 1
|
||||||
|
|
||||||
def execute(args)
|
def execute(args)
|
||||||
return if !SiteSetting.discourse_ai_enabled
|
return if !DiscourseAi::Translation.backfill_enabled?
|
||||||
return if !SiteSetting.ai_translation_enabled
|
|
||||||
limit = SiteSetting.ai_translation_backfill_hourly_rate
|
|
||||||
return if limit == 0
|
|
||||||
|
|
||||||
categories = Category.where(locale: nil)
|
categories = Category.where(locale: nil)
|
||||||
|
|
||||||
@@ -18,6 +15,7 @@ module Jobs
|
|||||||
categories = categories.where(read_restricted: false)
|
categories = categories.where(read_restricted: false)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
limit = SiteSetting.ai_translation_backfill_hourly_rate
|
||||||
categories = categories.limit(limit)
|
categories = categories.limit(limit)
|
||||||
return if categories.empty?
|
return if categories.empty?
|
||||||
|
|
||||||
|
|||||||
@@ -6,11 +6,8 @@ module Jobs
|
|||||||
cluster_concurrency 1
|
cluster_concurrency 1
|
||||||
|
|
||||||
def execute(args)
|
def execute(args)
|
||||||
return if !SiteSetting.discourse_ai_enabled
|
return if !DiscourseAi::Translation.backfill_enabled?
|
||||||
return if !SiteSetting.ai_translation_enabled
|
|
||||||
return if SiteSetting.content_localization_supported_locales.blank?
|
|
||||||
limit = SiteSetting.ai_translation_backfill_hourly_rate
|
limit = SiteSetting.ai_translation_backfill_hourly_rate
|
||||||
return if limit == 0
|
|
||||||
|
|
||||||
Jobs.enqueue(:localize_categories, limit:)
|
Jobs.enqueue(:localize_categories, limit:)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -6,10 +6,8 @@ module Jobs
|
|||||||
cluster_concurrency 1
|
cluster_concurrency 1
|
||||||
|
|
||||||
def execute(args)
|
def execute(args)
|
||||||
return if !SiteSetting.discourse_ai_enabled
|
return if !DiscourseAi::Translation.backfill_enabled?
|
||||||
return if !SiteSetting.ai_translation_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
|
limit = SiteSetting.ai_translation_backfill_hourly_rate / (60 / 5) # this job runs in 5-minute intervals
|
||||||
return if limit == 0
|
return if limit == 0
|
||||||
|
|
||||||
|
|||||||
@@ -7,16 +7,16 @@ module Jobs
|
|||||||
cluster_concurrency 1
|
cluster_concurrency 1
|
||||||
|
|
||||||
def execute(args)
|
def execute(args)
|
||||||
return if !SiteSetting.discourse_ai_enabled
|
return if !DiscourseAi::Translation.backfill_enabled?
|
||||||
return if !SiteSetting.ai_translation_enabled
|
|
||||||
limit = SiteSetting.ai_translation_backfill_hourly_rate / (60 / 5) # this job runs in 5-minute intervals
|
limit = SiteSetting.ai_translation_backfill_hourly_rate / (60 / 5) # this job runs in 5-minute intervals
|
||||||
return if limit == 0
|
|
||||||
|
|
||||||
posts =
|
posts =
|
||||||
Post
|
Post
|
||||||
.where(locale: nil)
|
.where(locale: nil)
|
||||||
.where(deleted_at: nil)
|
.where(deleted_at: nil)
|
||||||
.where("posts.user_id > 0")
|
.where("posts.user_id > 0")
|
||||||
|
.where("posts.created_at > ?", SiteSetting.ai_translation_backfill_max_age_days.days.ago)
|
||||||
.where.not(raw: [nil, ""])
|
.where.not(raw: [nil, ""])
|
||||||
|
|
||||||
if SiteSetting.ai_translation_backfill_limit_to_public_content
|
if SiteSetting.ai_translation_backfill_limit_to_public_content
|
||||||
@@ -33,14 +33,6 @@ module Jobs
|
|||||||
)
|
)
|
||||||
end
|
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)
|
posts = posts.order(updated_at: :desc).limit(limit)
|
||||||
return if posts.empty?
|
return if posts.empty?
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,12 @@ module ::Jobs
|
|||||||
existing_summary = strategy.existing_summary
|
existing_summary = strategy.existing_summary
|
||||||
|
|
||||||
if existing_summary.blank? || existing_summary.outdated
|
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
|
else
|
||||||
# Hiding or deleting a post, and creating a small action alters the Topic#highest_post_number.
|
# 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.
|
# 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
|
cluster_concurrency 1
|
||||||
|
|
||||||
def execute(args)
|
def execute(args)
|
||||||
return if !SiteSetting.discourse_ai_enabled
|
return if !DiscourseAi::Translation.backfill_enabled?
|
||||||
return if !SiteSetting.ai_translation_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
|
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:)
|
Jobs.enqueue(:localize_topics, limit:)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -7,12 +7,15 @@ module Jobs
|
|||||||
cluster_concurrency 1
|
cluster_concurrency 1
|
||||||
|
|
||||||
def execute(args)
|
def execute(args)
|
||||||
return if !SiteSetting.discourse_ai_enabled
|
return if !DiscourseAi::Translation.backfill_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
|
|
||||||
|
|
||||||
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
|
if SiteSetting.ai_translation_backfill_limit_to_public_content
|
||||||
topics =
|
topics =
|
||||||
@@ -28,14 +31,6 @@ module Jobs
|
|||||||
)
|
)
|
||||||
end
|
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)
|
topics = topics.order(updated_at: :desc).limit(limit)
|
||||||
return if topics.empty?
|
return if topics.empty?
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ class AiPersona < ActiveRecord::Base
|
|||||||
validates :system_prompt, presence: true, length: { maximum: 10_000_000 }
|
validates :system_prompt, presence: true, length: { maximum: 10_000_000 }
|
||||||
validate :system_persona_unchangeable, on: :update, if: :system
|
validate :system_persona_unchangeable, on: :update, if: :system
|
||||||
validate :chat_preconditions
|
validate :chat_preconditions
|
||||||
validate :allowed_seeded_model, if: :default_llm_id
|
|
||||||
validate :well_formated_examples
|
validate :well_formated_examples
|
||||||
validates :max_context_posts, numericality: { greater_than: 0 }, allow_nil: true
|
validates :max_context_posts, numericality: { greater_than: 0 }, allow_nil: true
|
||||||
# leaves some room for growth but sets a maximum to avoid memory issues
|
# leaves some room for growth but sets a maximum to avoid memory issues
|
||||||
@@ -365,17 +364,6 @@ class AiPersona < ActiveRecord::Base
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def allowed_seeded_model
|
|
||||||
return if default_llm_id.blank?
|
|
||||||
|
|
||||||
return if default_llm.nil?
|
|
||||||
return if !default_llm.seeded?
|
|
||||||
|
|
||||||
return if SiteSetting.ai_bot_allowed_seeded_models_map.include?(default_llm.id.to_s)
|
|
||||||
|
|
||||||
errors.add(:default_llm, I18n.t("discourse_ai.llm.configuration.invalid_seeded_model"))
|
|
||||||
end
|
|
||||||
|
|
||||||
def well_formated_examples
|
def well_formated_examples
|
||||||
return if examples.blank?
|
return if examples.blank?
|
||||||
|
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ class EmbeddingDefinition < ActiveRecord::Base
|
|||||||
dimensions: 2000,
|
dimensions: 2000,
|
||||||
max_sequence_length: 8191,
|
max_sequence_length: 8191,
|
||||||
pg_function: "<=>",
|
pg_function: "<=>",
|
||||||
tokenizer_class: "DiscourseAi::Tokenizer::OpenAiTokenizer",
|
tokenizer_class: "DiscourseAi::Tokenizer::OpenAiCl100kTokenizer",
|
||||||
url: "https://api.openai.com/v1/embeddings",
|
url: "https://api.openai.com/v1/embeddings",
|
||||||
provider: OPEN_AI,
|
provider: OPEN_AI,
|
||||||
matryoshka_dimensions: true,
|
matryoshka_dimensions: true,
|
||||||
@@ -98,7 +98,7 @@ class EmbeddingDefinition < ActiveRecord::Base
|
|||||||
dimensions: 1536,
|
dimensions: 1536,
|
||||||
max_sequence_length: 8191,
|
max_sequence_length: 8191,
|
||||||
pg_function: "<=>",
|
pg_function: "<=>",
|
||||||
tokenizer_class: "DiscourseAi::Tokenizer::OpenAiTokenizer",
|
tokenizer_class: "DiscourseAi::Tokenizer::OpenAiCl100kTokenizer",
|
||||||
url: "https://api.openai.com/v1/embeddings",
|
url: "https://api.openai.com/v1/embeddings",
|
||||||
provider: OPEN_AI,
|
provider: OPEN_AI,
|
||||||
matryoshka_dimensions: true,
|
matryoshka_dimensions: true,
|
||||||
@@ -112,7 +112,7 @@ class EmbeddingDefinition < ActiveRecord::Base
|
|||||||
dimensions: 1536,
|
dimensions: 1536,
|
||||||
max_sequence_length: 8191,
|
max_sequence_length: 8191,
|
||||||
pg_function: "<=>",
|
pg_function: "<=>",
|
||||||
tokenizer_class: "DiscourseAi::Tokenizer::OpenAiTokenizer",
|
tokenizer_class: "DiscourseAi::Tokenizer::OpenAiCl100kTokenizer",
|
||||||
url: "https://api.openai.com/v1/embeddings",
|
url: "https://api.openai.com/v1/embeddings",
|
||||||
provider: OPEN_AI,
|
provider: OPEN_AI,
|
||||||
provider_params: {
|
provider_params: {
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ class LlmModel < ActiveRecord::Base
|
|||||||
end
|
end
|
||||||
|
|
||||||
def identifier
|
def identifier
|
||||||
"custom:#{id}"
|
"#{id}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def toggle_companion_user
|
def toggle_companion_user
|
||||||
|
|||||||
@@ -260,10 +260,11 @@ export default class AiBotConversations extends Component {
|
|||||||
|
|
||||||
@action
|
@action
|
||||||
async prepareAndSubmitToBot() {
|
async prepareAndSubmitToBot() {
|
||||||
// Pass uploads to the service before submitting
|
|
||||||
this.aiBotConversationsHiddenSubmit.uploads = this.uploads;
|
|
||||||
try {
|
try {
|
||||||
await this.aiBotConversationsHiddenSubmit.submitToBot();
|
await this.aiBotConversationsHiddenSubmit.submitToBot({
|
||||||
|
uploads: this.uploads,
|
||||||
|
inProgressUploadsCount: this.inProgressUploads.length,
|
||||||
|
});
|
||||||
this.uploads = new TrackedArray();
|
this.uploads = new TrackedArray();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
popupAjaxError(error);
|
popupAjaxError(error);
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
import Component from "@glimmer/component";
|
||||||
|
import { tracked } from "@glimmer/tracking";
|
||||||
|
import { ajax } from "discourse/lib/ajax";
|
||||||
|
import { i18n } from "discourse-i18n";
|
||||||
|
import SiteSettingComponent from "admin/components/site-setting";
|
||||||
|
import SiteSetting from "admin/models/site-setting";
|
||||||
|
|
||||||
|
export default class AiDefaultLlmSelector extends Component {
|
||||||
|
@tracked defaultLlmSetting = null;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super(...arguments);
|
||||||
|
this.#loadDefaultLlmSetting();
|
||||||
|
}
|
||||||
|
|
||||||
|
async #loadDefaultLlmSetting() {
|
||||||
|
const { site_settings } = await ajax("/admin/config/site_settings.json", {
|
||||||
|
data: {
|
||||||
|
plugin: "discourse-ai",
|
||||||
|
category: "discourse_ai",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const defaultLlmSetting = site_settings.find(
|
||||||
|
(setting) => setting.setting === "ai_default_llm_model"
|
||||||
|
);
|
||||||
|
|
||||||
|
this.defaultLlmSetting = SiteSetting.create(defaultLlmSetting);
|
||||||
|
}
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="ai-configure-default-llm">
|
||||||
|
<div class="ai-configure-default-llm__header">
|
||||||
|
<h3>{{i18n "discourse_ai.default_llm.title"}}</h3>
|
||||||
|
<p>{{i18n "discourse_ai.default_llm.description"}}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{#if this.defaultLlmSetting}}
|
||||||
|
<SiteSettingComponent
|
||||||
|
@setting={{this.defaultLlmSetting}}
|
||||||
|
class="ai-configure-default-llm__setting"
|
||||||
|
/>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
}
|
||||||
@@ -66,12 +66,9 @@ class ExpandableList extends Component {
|
|||||||
|
|
||||||
export default class AiFeaturesList extends Component {
|
export default class AiFeaturesList extends Component {
|
||||||
get sortedModules() {
|
get sortedModules() {
|
||||||
if (!this.args.modules || !this.args.modules.length) {
|
const modules = this.args.modules?.toArray() || this.args.modules;
|
||||||
return [];
|
return [...(modules || [])].toSorted((a, b) =>
|
||||||
}
|
(a.module_name || "").localeCompare(b.module_name || "")
|
||||||
|
|
||||||
return this.args.modules.toSorted((a, b) =>
|
|
||||||
a.module_name.localeCompare(b.module_name)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import DPageSubheader from "discourse/components/d-page-subheader";
|
|||||||
import DSelect from "discourse/components/d-select";
|
import DSelect from "discourse/components/d-select";
|
||||||
import FilterInput from "discourse/components/filter-input";
|
import FilterInput from "discourse/components/filter-input";
|
||||||
import { i18n } from "discourse-i18n";
|
import { i18n } from "discourse-i18n";
|
||||||
|
import AiDefaultLlmSelector from "./ai-default-llm-selector";
|
||||||
import AiFeaturesList from "./ai-features-list";
|
import AiFeaturesList from "./ai-features-list";
|
||||||
|
|
||||||
const ALL = "all";
|
const ALL = "all";
|
||||||
@@ -202,6 +203,8 @@ export default class AiFeatures extends Component {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<AiDefaultLlmSelector />
|
||||||
|
|
||||||
{{#if this.filteredFeatures.length}}
|
{{#if this.filteredFeatures.length}}
|
||||||
<AiFeaturesList @modules={{this.filteredFeatures}} />
|
<AiFeaturesList @modules={{this.filteredFeatures}} />
|
||||||
{{else}}
|
{{else}}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import I18n, { i18n } from "discourse-i18n";
|
|||||||
import AdminSectionLandingItem from "admin/components/admin-section-landing-item";
|
import AdminSectionLandingItem from "admin/components/admin-section-landing-item";
|
||||||
import AdminSectionLandingWrapper from "admin/components/admin-section-landing-wrapper";
|
import AdminSectionLandingWrapper from "admin/components/admin-section-landing-wrapper";
|
||||||
import DTooltip from "float-kit/components/d-tooltip";
|
import DTooltip from "float-kit/components/d-tooltip";
|
||||||
|
import AiDefaultLlmSelector from "./ai-default-llm-selector";
|
||||||
import AiLlmEditor from "./ai-llm-editor";
|
import AiLlmEditor from "./ai-llm-editor";
|
||||||
|
|
||||||
function isPreseeded(llm) {
|
function isPreseeded(llm) {
|
||||||
@@ -137,6 +138,9 @@ export default class AiLlmsListEditor extends Component {
|
|||||||
}}
|
}}
|
||||||
@learnMoreUrl="https://meta.discourse.org/t/discourse-ai-large-language-model-llm-settings-page/319903"
|
@learnMoreUrl="https://meta.discourse.org/t/discourse-ai-large-language-model-llm-settings-page/319903"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<AiDefaultLlmSelector />
|
||||||
|
|
||||||
{{#if this.hasLlmElements}}
|
{{#if this.hasLlmElements}}
|
||||||
<section class="ai-llms-list-editor__configured">
|
<section class="ai-llms-list-editor__configured">
|
||||||
<DPageSubheader
|
<DPageSubheader
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ export default class AiSpam extends Component {
|
|||||||
this.isEnabled = model.is_enabled;
|
this.isEnabled = model.is_enabled;
|
||||||
|
|
||||||
if (model.llm_id) {
|
if (model.llm_id) {
|
||||||
this.selectedLLM = "custom:" + model.llm_id;
|
this.selectedLLM = model.llm_id;
|
||||||
} else {
|
} else {
|
||||||
if (this.availableLLMs.length) {
|
if (this.availableLLMs.length) {
|
||||||
this.selectedLLM = this.availableLLMs[0].id;
|
this.selectedLLM = this.availableLLMs[0].id;
|
||||||
@@ -127,7 +127,7 @@ export default class AiSpam extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get llmId() {
|
get llmId() {
|
||||||
return this.selectedLLM.toString().split(":")[1];
|
return this.selectedLLM;
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ export default class AiBotConversationsHiddenSubmit extends Service {
|
|||||||
|
|
||||||
personaId;
|
personaId;
|
||||||
targetUsername;
|
targetUsername;
|
||||||
uploads = [];
|
|
||||||
|
|
||||||
inputValue = "";
|
inputValue = "";
|
||||||
|
|
||||||
@@ -32,7 +31,7 @@ export default class AiBotConversationsHiddenSubmit extends Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
async submitToBot() {
|
async submitToBot(uploadData) {
|
||||||
if (
|
if (
|
||||||
this.inputValue.length <
|
this.inputValue.length <
|
||||||
this.siteSettings.min_personal_message_post_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
|
// 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({
|
return this.dialog.alert({
|
||||||
message: i18n("discourse_ai.ai_bot.conversations.uploads_in_progress"),
|
message: i18n("discourse_ai.ai_bot.conversations.uploads_in_progress"),
|
||||||
});
|
});
|
||||||
@@ -61,10 +60,10 @@ export default class AiBotConversationsHiddenSubmit extends Service {
|
|||||||
let rawContent = this.inputValue;
|
let rawContent = this.inputValue;
|
||||||
|
|
||||||
// Append upload markdown if we have uploads
|
// Append upload markdown if we have uploads
|
||||||
if (this.uploads && this.uploads.length > 0) {
|
if (uploadData.uploads && uploadData.uploads.length > 0) {
|
||||||
rawContent += "\n\n";
|
rawContent += "\n\n";
|
||||||
|
|
||||||
this.uploads.forEach((upload) => {
|
uploadData.uploads.forEach((upload) => {
|
||||||
const uploadMarkdown = getUploadMarkdown(upload);
|
const uploadMarkdown = getUploadMarkdown(upload);
|
||||||
rawContent += uploadMarkdown + "\n";
|
rawContent += uploadMarkdown + "\n";
|
||||||
});
|
});
|
||||||
@@ -83,7 +82,6 @@ export default class AiBotConversationsHiddenSubmit extends Service {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Reset uploads after successful submission
|
// Reset uploads after successful submission
|
||||||
this.uploads = [];
|
|
||||||
this.inputValue = "";
|
this.inputValue = "";
|
||||||
|
|
||||||
this.appEvents.trigger("discourse-ai:bot-pm-created", {
|
this.appEvents.trigger("discourse-ai:bot-pm-created", {
|
||||||
|
|||||||
@@ -158,3 +158,51 @@
|
|||||||
.ai-expanded-list__toggle-button {
|
.ai-expanded-list__toggle-button {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ai-configure-default-llm {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--space-2);
|
||||||
|
background: var(--primary-very-low);
|
||||||
|
padding: 1rem;
|
||||||
|
margin-block: 1rem;
|
||||||
|
border-radius: var(--d-border-radius);
|
||||||
|
|
||||||
|
&__header {
|
||||||
|
flex: 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__setting {
|
||||||
|
flex: 2;
|
||||||
|
display: flex;
|
||||||
|
gap: var(--space-2);
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
.setting-controls__undo {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
color: var(--primary);
|
||||||
|
font-size: var(--font-0);
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin-top: 0;
|
||||||
|
font-size: var(--font-down-1);
|
||||||
|
color: var(--primary-high-or-secondary-low);
|
||||||
|
}
|
||||||
|
|
||||||
|
.select-kit,
|
||||||
|
.btn {
|
||||||
|
font-size: var(--font-down-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-label,
|
||||||
|
.desc {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -158,6 +158,8 @@ ar:
|
|||||||
reset: "إعادة التعيين"
|
reset: "إعادة التعيين"
|
||||||
search:
|
search:
|
||||||
name: "البحث"
|
name: "البحث"
|
||||||
|
embeddings:
|
||||||
|
name: "التضمينات"
|
||||||
ai_helper:
|
ai_helper:
|
||||||
name: "المساعد"
|
name: "المساعد"
|
||||||
proofread: تدقيق النص لغويًا
|
proofread: تدقيق النص لغويًا
|
||||||
|
|||||||
@@ -29,6 +29,22 @@ de:
|
|||||||
label: "Sortieren nach"
|
label: "Sortieren nach"
|
||||||
tag:
|
tag:
|
||||||
label: "Schlagwort"
|
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:
|
js:
|
||||||
discourse_automation:
|
discourse_automation:
|
||||||
scriptables:
|
scriptables:
|
||||||
@@ -98,21 +114,21 @@ de:
|
|||||||
fields:
|
fields:
|
||||||
model:
|
model:
|
||||||
label: "Modell"
|
label: "Modell"
|
||||||
description: "Das für die Triage verwendete Standard-Sprachmodell"
|
description: "Das für die Sichtung verwendete Standard-Sprachmodell"
|
||||||
tool:
|
tool:
|
||||||
label: "Werkzeug"
|
label: "Tool"
|
||||||
description: "Werkzeug für die Triage (das Werkzeug darf keine Parameter definiert haben)"
|
description: "Tool für die Sichtung (das Tool darf keine Parameter definiert haben)"
|
||||||
llm_persona_triage:
|
llm_persona_triage:
|
||||||
fields:
|
fields:
|
||||||
persona:
|
persona:
|
||||||
label: "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:
|
whisper:
|
||||||
label: "Als Flüstern antworten"
|
label: "Als Flüstern antworten"
|
||||||
description: "Ob die Antwort der Persona ein Flüstern sein soll"
|
description: "Ob die Antwort der Persona ein Flüstern sein soll"
|
||||||
silent_mode:
|
silent_mode:
|
||||||
label: "Stiller Modus"
|
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:
|
llm_triage:
|
||||||
fields:
|
fields:
|
||||||
system_prompt:
|
system_prompt:
|
||||||
@@ -177,6 +193,7 @@ de:
|
|||||||
llm:
|
llm:
|
||||||
one: "LLM:"
|
one: "LLM:"
|
||||||
other: "LLM:"
|
other: "LLM:"
|
||||||
|
no_llm: "Kein LLM ausgewählt"
|
||||||
no_persona: "Nicht festgelegt"
|
no_persona: "Nicht festgelegt"
|
||||||
no_groups: "Keiner"
|
no_groups: "Keiner"
|
||||||
edit: "Bearbeiten"
|
edit: "Bearbeiten"
|
||||||
@@ -184,35 +201,75 @@ de:
|
|||||||
one: "(%{count} weitere)"
|
one: "(%{count} weitere)"
|
||||||
other: "(%{count} weitere)"
|
other: "(%{count} weitere)"
|
||||||
collapse_list: "(weniger anzeigen)"
|
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:
|
filters:
|
||||||
all: "Gesamt"
|
all: "Gesamt"
|
||||||
|
text: "Suche nach Funktionen, Personas, LLMs oder Gruppen..."
|
||||||
|
no_results: "Es wurden keine Funktionen gefunden, die deinen Filtern entsprechen."
|
||||||
reset: "Zurücksetzen"
|
reset: "Zurücksetzen"
|
||||||
summarization:
|
summarization:
|
||||||
name: "Zusammenfassungen"
|
name: "Zusammenfassungen"
|
||||||
description: "Stellt eine Schaltfläche für Zusammenfassungen zur Verfügung, mit der Besucher Themen zusammenfassen können"
|
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:
|
search:
|
||||||
name: "Suche"
|
name: "Suche"
|
||||||
description: "Verbessert das Sucherlebnis durch KI-generierte Antworten auf Suchanfragen"
|
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:
|
discord:
|
||||||
|
name: "Discord-Integration"
|
||||||
description: "Fügt die Möglichkeit hinzu, Discord-Kanäle zu durchsuchen"
|
description: "Fügt die Möglichkeit hinzu, Discord-Kanäle zu durchsuchen"
|
||||||
|
search: "Discord-Suche"
|
||||||
inference:
|
inference:
|
||||||
|
name: "Abgeleitete Konzepte"
|
||||||
description: "Ordnet Themen und Beiträge in Interessensgebiete / Labels."
|
description: "Ordnet Themen und Beiträge in Interessensgebiete / Labels."
|
||||||
|
generate_concepts: "Konzepterkennung"
|
||||||
|
match_concepts: "Passende Konzepte"
|
||||||
|
deduplicate_concepts: "Deduplizierung von Konzepten"
|
||||||
ai_helper:
|
ai_helper:
|
||||||
name: "Helfer"
|
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
|
proofread: Text korrekturlesen
|
||||||
|
title_suggestions: "Titel vorschlagen"
|
||||||
explain: "Erklären"
|
explain: "Erklären"
|
||||||
|
illustrate_post: "Beitrag illustrieren"
|
||||||
smart_dates: "Intelligente Termine"
|
smart_dates: "Intelligente Termine"
|
||||||
|
translate: "Übersetzen"
|
||||||
markdown_tables: "Markdown-Tabelle generieren"
|
markdown_tables: "Markdown-Tabelle generieren"
|
||||||
custom_prompt: "Benutzerdefinierte Eingabeaufforderung"
|
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:
|
spam:
|
||||||
name: "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."
|
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:
|
modals:
|
||||||
select_option: "Wähle eine Option aus..."
|
select_option: "Wähle eine Option aus..."
|
||||||
|
layout:
|
||||||
|
table: "Tabelle"
|
||||||
|
card: "Karte"
|
||||||
spam:
|
spam:
|
||||||
short_title: "Spam"
|
short_title: "Spam"
|
||||||
title: "Spam-Behandlung konfigurieren"
|
title: "Spam-Behandlung konfigurieren"
|
||||||
select_llm: "LLM auswählen"
|
select_llm: "LLM auswählen"
|
||||||
|
select_persona: "Persona auswählen"
|
||||||
custom_instructions: "Benutzerdefinierte Anweisungen"
|
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.“"
|
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"
|
last_seven_days: "Letzte 7 Tage"
|
||||||
@@ -293,6 +350,9 @@ de:
|
|||||||
name: "Name"
|
name: "Name"
|
||||||
edit: "Bearbeiten"
|
edit: "Bearbeiten"
|
||||||
export: "Exportieren"
|
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"
|
description: "Beschreibung"
|
||||||
no_llm_selected: "Kein Sprachmodell ausgewählt"
|
no_llm_selected: "Kein Sprachmodell ausgewählt"
|
||||||
use_parent_llm: "Verwende das Personas Sprachmodell"
|
use_parent_llm: "Verwende das Personas Sprachmodell"
|
||||||
@@ -370,7 +430,10 @@ de:
|
|||||||
title: "KI-Bot-Optionen"
|
title: "KI-Bot-Optionen"
|
||||||
save_first: "Weitere KI-Bot-Optionen werden verfügbar, sobald du die Persona gespeichert hast."
|
save_first: "Weitere KI-Bot-Optionen werden verfügbar, sobald du die Persona gespeichert hast."
|
||||||
filters:
|
filters:
|
||||||
|
text: "Finde eine Persona"
|
||||||
reset: "Zurücksetzen"
|
reset: "Zurücksetzen"
|
||||||
|
no_results: "Es wurden keine Personas gefunden, die deinen Filtern entsprechen."
|
||||||
|
all_features: "Jede Funktion"
|
||||||
features_list:
|
features_list:
|
||||||
one: "Funktion:"
|
one: "Funktion:"
|
||||||
other: "Funktionen:"
|
other: "Funktionen:"
|
||||||
@@ -401,11 +464,14 @@ de:
|
|||||||
back: "Zurück"
|
back: "Zurück"
|
||||||
short_title: "Tools"
|
short_title: "Tools"
|
||||||
export: "Exportieren"
|
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"
|
no_tools: "Du hast noch keine Tools erstellt"
|
||||||
name: "Name"
|
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"
|
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)"
|
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: "Beschreibung"
|
||||||
description_help: "Eine klare Beschreibung des Zwecks des Tools für das Sprachmodell"
|
description_help: "Eine klare Beschreibung des Zwecks des Tools für das Sprachmodell"
|
||||||
@@ -486,14 +552,18 @@ de:
|
|||||||
ai_summarization: "Zusammenfassen"
|
ai_summarization: "Zusammenfassen"
|
||||||
ai_embeddings_semantic_search: "KI-Suche"
|
ai_embeddings_semantic_search: "KI-Suche"
|
||||||
ai_spam: "Spam"
|
ai_spam: "Spam"
|
||||||
automation: "Automatisierung (%{name})"
|
automation: "Automatisierung (%{persona})"
|
||||||
in_use_warning:
|
in_use_warning:
|
||||||
one: "Dieses Modell wird derzeit von %{settings} verwendet. Wenn es falsch konfiguriert ist, wird die Funktion nicht wie erwartet funktionieren."
|
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. "
|
other: "Dieses Modell wird derzeit verwendet von: %{settings}. Wenn es falsch konfiguriert ist, werden die Funktionen nicht wie erwartet funktionieren. "
|
||||||
model_description:
|
model_description:
|
||||||
none: "Allgemeine Einstellungen, die für die meisten Sprachmodelle funktionieren"
|
none: "Allgemeine Einstellungen, die für die meisten Sprachmodelle funktionieren"
|
||||||
anthropic-claude-opus-4-0: "Das intelligenteste Modell von Anthropic"
|
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"
|
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-5-flash: "Leicht, schnell und kosteneffizient mit multimodaler Argumentation"
|
||||||
google-gemini-2-0-flash-lite: "Kosteneffizientes Modell mit niedriger Latenz"
|
google-gemini-2-0-flash-lite: "Kosteneffizientes Modell mit niedriger Latenz"
|
||||||
open_ai-o3: "Das leistungsfähigste Argumentationsmodell von Open AI"
|
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"
|
samba_nova-Meta-Llama-3-3-70B-Instruct": "Leistungsstarkes Mehrzweckmodell"
|
||||||
mistral-mistral-large-latest: "Das leistungsstärkste Modell von Mistral"
|
mistral-mistral-large-latest: "Das leistungsstärkste Modell von Mistral"
|
||||||
mistral-pixtral-large-latest: "Das leistungsstärkste Modell von Mistral mit Sichtfunktion"
|
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}"
|
preseeded_model_description: "Vorkonfiguriertes Open-Source-Modell unter Verwendung von %{model}"
|
||||||
configured:
|
configured:
|
||||||
title: "Konfigurierte LLMs"
|
title: "Konfigurierte LLMs"
|
||||||
@@ -565,6 +638,7 @@ de:
|
|||||||
reasoning_tokens: "Anzahl der für die Argumentation verwendeten Token"
|
reasoning_tokens: "Anzahl der für die Argumentation verwendeten Token"
|
||||||
disable_temperature: "Temperatur deaktivieren (einige Denkmodelle unterstützen keine Temperatur)"
|
disable_temperature: "Temperatur deaktivieren (einige Denkmodelle unterstützen keine Temperatur)"
|
||||||
disable_top_p: "Top P deaktivieren (einige Denkmodelle unterstützen Top P nicht)"
|
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:
|
related_topics:
|
||||||
title: "Verwandte Themen"
|
title: "Verwandte Themen"
|
||||||
pill: "Verwandt"
|
pill: "Verwandt"
|
||||||
@@ -583,6 +657,7 @@ de:
|
|||||||
trigger: "KI fragen"
|
trigger: "KI fragen"
|
||||||
loading: "KI generiert"
|
loading: "KI generiert"
|
||||||
cancel: "Abbrechen"
|
cancel: "Abbrechen"
|
||||||
|
regen: "Erneut versuchen"
|
||||||
confirm: "Bestätigen"
|
confirm: "Bestätigen"
|
||||||
discard: "Verwerfen"
|
discard: "Verwerfen"
|
||||||
changes: "Empfohlene Änderungen"
|
changes: "Empfohlene Änderungen"
|
||||||
@@ -700,6 +775,7 @@ de:
|
|||||||
shortcut_title: "Starte eine PN mit einem KI-Bot"
|
shortcut_title: "Starte eine PN mit einem KI-Bot"
|
||||||
share: "KI-Unterhaltung kopieren"
|
share: "KI-Unterhaltung kopieren"
|
||||||
conversation_shared: "Unterhaltung kopiert"
|
conversation_shared: "Unterhaltung kopiert"
|
||||||
|
embed_copied: "Einbettung in die Zwischenablage kopiert"
|
||||||
debug_ai: "Rohdaten der KI-Anfrage und -Antwort anzeigen"
|
debug_ai: "Rohdaten der KI-Anfrage und -Antwort anzeigen"
|
||||||
sidebar_empty: "Der Verlauf der Bot-Konversation wird hier angezeigt."
|
sidebar_empty: "Der Verlauf der Bot-Konversation wird hier angezeigt."
|
||||||
debug_ai_modal:
|
debug_ai_modal:
|
||||||
@@ -759,6 +835,7 @@ de:
|
|||||||
today: "Heute"
|
today: "Heute"
|
||||||
last_7_days: "Letzte 7 Tage"
|
last_7_days: "Letzte 7 Tage"
|
||||||
last_30_days: "Letzte 30 Tage"
|
last_30_days: "Letzte 30 Tage"
|
||||||
|
upload_files: "Dateien hochladen"
|
||||||
sentiments:
|
sentiments:
|
||||||
dashboard:
|
dashboard:
|
||||||
title: "Stimmung"
|
title: "Stimmung"
|
||||||
|
|||||||
@@ -106,6 +106,9 @@ en:
|
|||||||
top_p:
|
top_p:
|
||||||
label: "Top P"
|
label: "Top P"
|
||||||
description: "Top P to use for the LLM, increase to increase randomness (leave empty to use model default)"
|
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:
|
llm_tool_triage:
|
||||||
fields:
|
fields:
|
||||||
@@ -181,6 +184,10 @@ en:
|
|||||||
discourse_ai:
|
discourse_ai:
|
||||||
title: "AI"
|
title: "AI"
|
||||||
|
|
||||||
|
default_llm:
|
||||||
|
title: "Default LLM model"
|
||||||
|
description: "The default LLM model to use for all AI features. This will be used if no LLM is specified in the feature configuration or persona. If no default LLM is specified, the last created LLM will be used."
|
||||||
|
|
||||||
features:
|
features:
|
||||||
short_title: "Features"
|
short_title: "Features"
|
||||||
description: "These are the AI features available to visitors on your site. These can be configured to use specific personas and LLMs, and can be access controlled by groups."
|
description: "These are the AI features available to visitors on your site. These can be configured to use specific personas and LLMs, and can be access controlled by groups."
|
||||||
@@ -563,7 +570,7 @@ en:
|
|||||||
max_usages_required: "Must be set if max tokens is not set"
|
max_usages_required: "Must be set if max tokens is not set"
|
||||||
usage:
|
usage:
|
||||||
ai_bot: "AI bot"
|
ai_bot: "AI bot"
|
||||||
ai_helper: "Helper"
|
ai_helper: "Helper (%{persona})"
|
||||||
ai_helper_image_caption: "Image caption"
|
ai_helper_image_caption: "Image caption"
|
||||||
ai_persona: "Persona (%{persona})"
|
ai_persona: "Persona (%{persona})"
|
||||||
ai_summarization: "Summarize"
|
ai_summarization: "Summarize"
|
||||||
@@ -872,6 +879,7 @@ en:
|
|||||||
last_7_days: "Last 7 days"
|
last_7_days: "Last 7 days"
|
||||||
last_30_days: "Last 30 days"
|
last_30_days: "Last 30 days"
|
||||||
upload_files: "Upload files"
|
upload_files: "Upload files"
|
||||||
|
uploads_in_progress: "Cannot submit while uploads are in progress"
|
||||||
sentiments:
|
sentiments:
|
||||||
dashboard:
|
dashboard:
|
||||||
title: "Sentiment"
|
title: "Sentiment"
|
||||||
|
|||||||
@@ -154,6 +154,8 @@ es:
|
|||||||
reset: "Restablecer"
|
reset: "Restablecer"
|
||||||
search:
|
search:
|
||||||
name: "Buscar"
|
name: "Buscar"
|
||||||
|
embeddings:
|
||||||
|
name: "Incrustaciones"
|
||||||
ai_helper:
|
ai_helper:
|
||||||
name: "Ayudante"
|
name: "Ayudante"
|
||||||
proofread: Corregir el texto
|
proofread: Corregir el texto
|
||||||
|
|||||||
@@ -154,6 +154,8 @@ fi:
|
|||||||
reset: "Palauta"
|
reset: "Palauta"
|
||||||
search:
|
search:
|
||||||
name: "Hae"
|
name: "Hae"
|
||||||
|
embeddings:
|
||||||
|
name: "Upotukset"
|
||||||
ai_helper:
|
ai_helper:
|
||||||
name: "Apuri"
|
name: "Apuri"
|
||||||
proofread: Oikolue teksti
|
proofread: Oikolue teksti
|
||||||
|
|||||||
@@ -154,6 +154,8 @@ fr:
|
|||||||
reset: "Réinitialiser"
|
reset: "Réinitialiser"
|
||||||
search:
|
search:
|
||||||
name: "Recherche"
|
name: "Recherche"
|
||||||
|
embeddings:
|
||||||
|
name: "Intégrations"
|
||||||
ai_helper:
|
ai_helper:
|
||||||
name: "Assistant"
|
name: "Assistant"
|
||||||
proofread: Relisez le texte
|
proofread: Relisez le texte
|
||||||
|
|||||||
@@ -185,6 +185,7 @@ he:
|
|||||||
two: "LLMים:"
|
two: "LLMים:"
|
||||||
many: "LLMים:"
|
many: "LLMים:"
|
||||||
other: "LLMים:"
|
other: "LLMים:"
|
||||||
|
no_llm: "לא נבחר מש״ג/LLM"
|
||||||
no_persona: "לא מוגדר"
|
no_persona: "לא מוגדר"
|
||||||
no_groups: "ללא"
|
no_groups: "ללא"
|
||||||
edit: "עריכה"
|
edit: "עריכה"
|
||||||
@@ -196,24 +197,44 @@ he:
|
|||||||
collapse_list: "(להציג פחות)"
|
collapse_list: "(להציג פחות)"
|
||||||
bot:
|
bot:
|
||||||
name: "בוט"
|
name: "בוט"
|
||||||
|
description: "צ׳אט בוט שיכול לענות על שאלות ולסייע למשתמשים בהודעות אישיות, בפורום ובצ׳אט"
|
||||||
filters:
|
filters:
|
||||||
all: "הכול"
|
all: "הכול"
|
||||||
|
text: "חיפוש יכולות, דמויות, LLMים/מש״גים או קבוצות…"
|
||||||
reset: "איפוס"
|
reset: "איפוס"
|
||||||
summarization:
|
summarization:
|
||||||
name: "סיכומים"
|
name: "סיכומים"
|
||||||
description: "להנגיש את כפתור הסיכום שמאפשר למבקרים לסכם נושאים"
|
description: "להנגיש את כפתור הסיכום שמאפשר למבקרים לסכם נושאים"
|
||||||
|
topic_summaries: "תקצירי נושאים."
|
||||||
search:
|
search:
|
||||||
name: "חיפוש"
|
name: "חיפוש"
|
||||||
description: "שיפור חוויית החיפוש על ידי אספקת תשובות שנוצרו על ידי בינה מלאכותית לשאילתות"
|
description: "שיפור חוויית החיפוש על ידי אספקת תשובות שנוצרו על ידי בינה מלאכותית לשאילתות"
|
||||||
|
discoveries: "תגליות"
|
||||||
|
embeddings:
|
||||||
|
name: "הטמעות"
|
||||||
discord:
|
discord:
|
||||||
|
name: "שילוב מול Discord"
|
||||||
description: "הוספת היכולת לחפש בערוצי Discord"
|
description: "הוספת היכולת לחפש בערוצי Discord"
|
||||||
|
search: "חיפוש ב־Discord"
|
||||||
|
inference:
|
||||||
|
generate_concepts: "הסקת תפיסות"
|
||||||
ai_helper:
|
ai_helper:
|
||||||
name: "מסייע"
|
name: "מסייע"
|
||||||
proofread: הגהת הטקסט
|
proofread: הגהת הטקסט
|
||||||
|
title_suggestions: "הצעת כותרות"
|
||||||
explain: "הסבר"
|
explain: "הסבר"
|
||||||
|
illustrate_post: "איור פוסט"
|
||||||
smart_dates: "תאריכים חכמים"
|
smart_dates: "תאריכים חכמים"
|
||||||
|
translate: "תרגום"
|
||||||
markdown_tables: "יצירת טבלה ב־Markdown"
|
markdown_tables: "יצירת טבלה ב־Markdown"
|
||||||
custom_prompt: "בקשה מותאמת אישית"
|
custom_prompt: "בקשה מותאמת אישית"
|
||||||
|
image_caption: "הוספת כותרות לתמונות"
|
||||||
|
translation:
|
||||||
|
description: "תרגום תוכן לשפות נתמכות"
|
||||||
|
locale_detector: "מזהה שפה ומקום"
|
||||||
|
post_raw_translator: "מתרגם פוסטים גולמי"
|
||||||
|
topic_title_translator: "מתרגם כותרות נושאים"
|
||||||
|
short_text_translator: "מתרגם טקסטים קצרים"
|
||||||
spam:
|
spam:
|
||||||
name: "ספאם"
|
name: "ספאם"
|
||||||
modals:
|
modals:
|
||||||
@@ -491,7 +512,7 @@ he:
|
|||||||
ai_summarization: "סיכום"
|
ai_summarization: "סיכום"
|
||||||
ai_embeddings_semantic_search: "חיפוש בינה מלאכותית"
|
ai_embeddings_semantic_search: "חיפוש בינה מלאכותית"
|
||||||
ai_spam: "ספאם"
|
ai_spam: "ספאם"
|
||||||
automation: "אוטומציה (%{name})"
|
automation: "אוטומציה (%{persona})"
|
||||||
in_use_warning:
|
in_use_warning:
|
||||||
one: "המודל הזה משמש את %{settings}. אם לא מוגדר כראוי, היכולת לא תעבוד כמצופה."
|
one: "המודל הזה משמש את %{settings}. אם לא מוגדר כראוי, היכולת לא תעבוד כמצופה."
|
||||||
two: "המודל הזה משמש את: %{settings}. אם ההגדרות שגויות היכולת לא תעבודנה כמצופה."
|
two: "המודל הזה משמש את: %{settings}. אם ההגדרות שגויות היכולת לא תעבודנה כמצופה."
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user