Compare commits

..
Author SHA1 Message Date
Mark VanLandingham 0df00bf1c4 DEV: Move semantic search out of connector to component 2024-11-11 11:44:21 -06:00
110 changed files with 1778 additions and 4220 deletions
@@ -6,14 +6,6 @@ module DiscourseAi
requires_plugin ::DiscourseAi::PLUGIN_NAME
requires_login
def show_debug_info_by_id
log = AiApiAuditLog.find(params[:id])
raise Discourse::NotFound if !log.topic
guardian.ensure_can_debug_ai_bot_conversation!(log.topic)
render json: AiApiAuditLogSerializer.new(log, root: false), status: 200
end
def show_debug_info
post = Post.find(params[:post_id])
guardian.ensure_can_debug_ai_bot_conversation!(post)
-8
View File
@@ -14,14 +14,6 @@ class AiApiAuditLog < ActiveRecord::Base
Ollama = 7
SambaNova = 8
end
def next_log_id
self.class.where("id > ?", id).where(topic_id: topic_id).order(id: :asc).pluck(:id).first
end
def prev_log_id
self.class.where("id < ?", id).where(topic_id: topic_id).order(id: :desc).pluck(:id).first
end
end
# == Schema Information
@@ -12,7 +12,5 @@ class AiApiAuditLogSerializer < ApplicationSerializer
:post_id,
:feature_name,
:language_model,
:created_at,
:prev_log_id,
:next_log_id
:created_at
end
@@ -0,0 +1,179 @@
import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import { on } from "@ember/modifier";
import { action } from "@ember/object";
import didInsert from "@ember/render-modifiers/modifiers/did-insert";
import { inject as service } from "@ember/service";
import DToggleSwitch from "discourse/components/d-toggle-switch";
import { SEARCH_TYPE_DEFAULT } from "discourse/controllers/full-page-search";
import { ajax } from "discourse/lib/ajax";
import { withPluginApi } from "discourse/lib/plugin-api";
import { isValidSearchTerm, translateResults } from "discourse/lib/search";
import icon from "discourse-common/helpers/d-icon";
import I18n from "I18n";
import AiIndicatorWave from "./ai-indicator-wave";
export default class AiFullPageSemanticSearch extends Component {
@service appEvents;
@service siteSettings;
@service searchPreferencesManager;
@tracked searching = false;
@tracked AIResults = [];
@tracked showingAIResults = false;
initialSearchTerm = this.args.outletArgs.search;
get disableToggleSwitch() {
if (
this.searching ||
this.AIResults.length === 0 ||
this.args.outletArgs.sortOrder !== 0
) {
return true;
}
}
get searchStateText() {
// Search results:
if (this.AIResults.length > 0) {
if (this.showingAIResults) {
return I18n.t(
"discourse_ai.embeddings.semantic_search_results.toggle",
{
count: this.AIResults.length,
}
);
} else {
return I18n.t(
"discourse_ai.embeddings.semantic_search_results.toggle_hidden",
{
count: this.AIResults.length,
}
);
}
}
// Search loading:
if (this.searching) {
return I18n.t("discourse_ai.embeddings.semantic_search_loading");
}
// Typing to search:
if (
this.AIResults.length === 0 &&
this.searchTerm !== this.initialSearchTerm
) {
return I18n.t("discourse_ai.embeddings.semantic_search_results.new");
}
// No results:
if (this.AIResults.length === 0) {
return I18n.t("discourse_ai.embeddings.semantic_search_results.none");
}
}
get searchTerm() {
if (this.initialSearchTerm !== this.args.outletArgs.search) {
this.initialSearchTerm = undefined;
}
return this.args.outletArgs.search;
}
get searchEnabled() {
return (
this.args.outletArgs.type === SEARCH_TYPE_DEFAULT &&
isValidSearchTerm(this.searchTerm, this.siteSettings) &&
this.args.outletArgs.sortOrder === 0
);
}
@action
toggleAIResults() {
if (this.showingAIResults) {
this.args.outletArgs.addSearchResults([], "topic_id");
} else {
this.args.outletArgs.addSearchResults(this.AIResults, "topic_id");
}
this.showingAIResults = !this.showingAIResults;
}
@action
resetAIResults() {
this.AIResults = [];
this.showingAIResults = false;
this.args.outletArgs.addSearchResults([], "topic_id");
}
@action
handleSearch() {
if (!this.searchEnabled) {
return;
}
if (this.initialSearchTerm && !this.searching) {
return this.performHyDESearch();
}
withPluginApi("1.15.0", (api) => {
api.onAppEvent("full-page-search:trigger-search", () => {
if (!this.searching) {
this.resetAIResults();
return this.performHyDESearch();
}
});
});
}
performHyDESearch() {
this.searching = true;
this.resetAIResults();
ajax("/discourse-ai/embeddings/semantic-search", {
data: { q: this.searchTerm },
})
.then(async (results) => {
const model = (await translateResults(results)) || {};
if (model.posts?.length === 0) {
this.searching = false;
return;
}
model.posts.forEach((post) => {
post.generatedByAI = true;
});
this.AIResults = model.posts;
})
.finally(() => (this.searching = false));
}
<template>
{{#if this.searchEnabled}}
<div class="semantic-search__container search-results" role="region">
<div class="semantic-search__results" {{didInsert this.handleSearch}}>
<div
class="semantic-search__searching
{{if this.searching 'in-progress'}}"
>
<DToggleSwitch
disabled={{this.disableToggleSwitch}}
@state={{this.showingAIResults}}
title="AI search results hidden"
class="semantic-search__results-toggle"
{{on "click" this.toggleAIResults}}
/>
<div class="semantic-search__searching-text">
{{icon "discourse-sparkles"}}
{{this.searchStateText}}
</div>
<AiIndicatorWave @loading={{this.searching}} />
</div>
</div>
</div>
{{/if}}
</template>
}
@@ -7,7 +7,6 @@ import { htmlSafe } from "@ember/template";
import DButton from "discourse/components/d-button";
import DModal from "discourse/components/d-modal";
import { ajax } from "discourse/lib/ajax";
import { popupAjaxError } from "discourse/lib/ajax-error";
import { clipboardCopy, escapeExpression } from "discourse/lib/utilities";
import i18n from "discourse-common/helpers/i18n";
import discourseLater from "discourse-common/lib/later";
@@ -64,28 +63,6 @@ export default class DebugAiModal extends Component {
this.copy(this.info.raw_response_payload);
}
async loadLog(logId) {
try {
await ajax(`/discourse-ai/ai-bot/show-debug-info/${logId}.json`).then(
(result) => {
this.info = result;
}
);
} catch (e) {
popupAjaxError(e);
}
}
@action
prevLog() {
this.loadLog(this.info.prev_log_id);
}
@action
nextLog() {
this.loadLog(this.info.next_log_id);
}
copy(text) {
clipboardCopy(text);
this.justCopiedText = I18n.t("discourse_ai.ai_bot.conversation_shared");
@@ -96,13 +73,11 @@ export default class DebugAiModal extends Component {
}
loadApiRequestInfo() {
ajax(`/discourse-ai/ai-bot/post/${this.args.model.id}/show-debug-info.json`)
.then((result) => {
this.info = result;
})
.catch((e) => {
popupAjaxError(e);
});
ajax(
`/discourse-ai/ai-bot/post/${this.args.model.id}/show-debug-info.json`
).then((result) => {
this.info = result;
});
}
get requestActive() {
@@ -172,22 +147,6 @@ export default class DebugAiModal extends Component {
@action={{this.copyResponse}}
@label="discourse_ai.ai_bot.debug_ai_modal.copy_response"
/>
{{#if this.info.prev_log_id}}
<DButton
class="btn"
@icon="angles-left"
@action={{this.prevLog}}
@label="discourse_ai.ai_bot.debug_ai_modal.previous_log"
/>
{{/if}}
{{#if this.info.next_log_id}}
<DButton
class="btn"
@icon="angles-right"
@action={{this.nextLog}}
@label="discourse_ai.ai_bot.debug_ai_modal.next_log"
/>
{{/if}}
<span class="ai-debut-modal__just-copied">{{this.justCopiedText}}</span>
</:footer>
</DModal>
@@ -1,37 +0,0 @@
import Component from "@glimmer/component";
import { action } from "@ember/object";
import DButton from "discourse/components/d-button";
import { ajax } from "discourse/lib/ajax";
import { popupAjaxError } from "discourse/lib/ajax-error";
export default class AiCancelStreamingButton extends Component {
// TODO (glimmer-post-menu): Remove this static function and move the code into the button action after the widget code is removed
static async cancelStreaming(post) {
try {
await ajax(`/discourse-ai/ai-bot/post/${post.id}/stop-streaming`, {
type: "POST",
});
document
.querySelector(`#post_${post.post_number}`)
.classList.remove("streaming");
} catch (e) {
popupAjaxError(e);
}
}
@action
cancelStreaming() {
this.constructor.cancelStreaming(this.args.post);
}
<template>
<DButton
class="post-action-menu__ai-cancel-streaming cancel-streaming"
...attributes
@action={{this.cancelStreaming}}
@icon="pause"
@title="discourse_ai.ai_bot.cancel_streaming"
/>
</template>
}
@@ -1,34 +0,0 @@
import Component from "@glimmer/component";
import { action } from "@ember/object";
import { inject as service } from "@ember/service";
import DButton from "discourse/components/d-button";
import { isPostFromAiBot } from "../../lib/ai-bot-helper";
import DebugAiModal from "../modal/debug-ai-modal";
export default class AiDebugButton extends Component {
static shouldRender(args) {
return isPostFromAiBot(args.post, args.state.currentUser);
}
// TODO (glimmer-post-menu): Remove this static function and move the code into the button action after the widget code is removed
static debugAiResponse(post, modal) {
modal.show(DebugAiModal, { model: post });
}
@service modal;
@action
debugAiResponse() {
this.constructor.debugAiResponse(this.args.post, this.modal);
}
<template>
<DButton
class="post-action-menu__debug-ai"
...attributes
@action={{this.debugAiResponse}}
@icon="info"
@title="discourse_ai.ai_bot.debug_ai"
/>
</template>
}
@@ -1,46 +0,0 @@
import Component from "@glimmer/component";
import { action } from "@ember/object";
import { inject as service } from "@ember/service";
import DButton from "discourse/components/d-button";
import { isPostFromAiBot } from "../../lib/ai-bot-helper";
import copyConversation from "../../lib/copy-conversation";
import ShareModal from "../modal/share-modal";
const AUTO_COPY_THRESHOLD = 4;
export default class AiDebugButton extends Component {
static shouldRender(args) {
return isPostFromAiBot(args.post, args.state.currentUser);
}
// TODO (glimmer-post-menu): Remove this static function and move the code into the button action after the widget code is removed
static async shareAiResponse(post, modal, showFeedback) {
if (post.post_number <= AUTO_COPY_THRESHOLD) {
await copyConversation(post.topic, 1, post.post_number);
showFeedback("discourse_ai.ai_bot.conversation_shared");
} else {
modal.show(ShareModal, { model: post });
}
}
@service modal;
@action
shareAiResponse() {
this.constructor.shareAiResponse(
this.args.post,
this.modal,
this.args.showFeedback
);
}
<template>
<DButton
class="post-action-menu__share-ai"
...attributes
@action={{this.shareAiResponse}}
@icon="far-copy"
@title="discourse_ai.ai_bot.share"
/>
</template>
}
@@ -1,198 +1,12 @@
import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import { on } from "@ember/modifier";
import { action } from "@ember/object";
import didInsert from "@ember/render-modifiers/modifiers/did-insert";
import { inject as service } from "@ember/service";
import DToggleSwitch from "discourse/components/d-toggle-switch";
import { SEARCH_TYPE_DEFAULT } from "discourse/controllers/full-page-search";
import { ajax } from "discourse/lib/ajax";
import { withPluginApi } from "discourse/lib/plugin-api";
import { isValidSearchTerm, translateResults } from "discourse/lib/search";
import icon from "discourse-common/helpers/d-icon";
import I18n from "I18n";
import AiIndicatorWave from "../../components/ai-indicator-wave";
import AIFullPageSemanticSearch from "discourse/plugins/discourse-ai/discourse/components/ai-full-page-semantic-search";
export default class SemanticSearch extends Component {
static shouldRender(_args, { siteSettings }) {
return siteSettings.ai_embeddings_semantic_search_enabled;
}
@service router;
@service appEvents;
@service siteSettings;
@service searchPreferencesManager;
@tracked searching = false;
@tracked AiResults = [];
@tracked showingAiResults = false;
initialSearchTerm = this.args.outletArgs.search;
get disableToggleSwitch() {
if (
this.searching ||
this.AiResults.length === 0 ||
this.args.outletArgs.sortOrder !== 0
) {
return true;
}
}
get searchStateText() {
// Search results:
if (this.AiResults.length > 0) {
if (this.showingAiResults) {
return I18n.t(
"discourse_ai.embeddings.semantic_search_results.toggle",
{
count: this.AiResults.length,
}
);
} else {
return I18n.t(
"discourse_ai.embeddings.semantic_search_results.toggle_hidden",
{
count: this.AiResults.length,
}
);
}
}
// Search loading:
if (this.searching) {
return I18n.t("discourse_ai.embeddings.semantic_search_loading");
}
// Typing to search:
if (
this.AiResults.length === 0 &&
this.searchTerm !== this.initialSearchTerm
) {
return I18n.t("discourse_ai.embeddings.semantic_search_results.new");
}
// No results:
if (this.AiResults.length === 0) {
return I18n.t("discourse_ai.embeddings.semantic_search_results.none");
}
}
get searchTerm() {
if (this.initialSearchTerm !== this.args.outletArgs.search) {
this.initialSearchTerm = undefined;
}
return this.args.outletArgs.search;
}
get searchEnabled() {
return (
this.args.outletArgs.type === SEARCH_TYPE_DEFAULT &&
isValidSearchTerm(this.searchTerm, this.siteSettings) &&
this.args.outletArgs.sortOrder === 0
);
}
@action
toggleAiResults() {
if (this.showingAiResults) {
this.args.outletArgs.addSearchResults([], "topic_id");
} else {
this.args.outletArgs.addSearchResults(this.AiResults, "topic_id");
}
this.showingAiResults = !this.showingAiResults;
}
@action
resetAiResults() {
this.AiResults = [];
this.showingAiResults = false;
this.args.outletArgs.addSearchResults([], "topic_id");
}
@action
handleSearch() {
if (!this.searchEnabled) {
return;
}
if (this.initialSearchTerm && !this.searching) {
return this.performHyDESearch();
}
this.#resetAndSearchOnEvent();
}
performHyDESearch() {
this.searching = true;
this.resetAiResults();
ajax("/discourse-ai/embeddings/semantic-search", {
data: { q: this.searchTerm },
})
.then(async (results) => {
const model = (await translateResults(results)) || {};
if (model.posts?.length === 0) {
this.searching = false;
return;
}
model.posts.forEach((post) => {
post.generatedByAi = true;
});
this.AiResults = model.posts;
})
.finally(() => (this.searching = false));
}
#resetAndSearchOnEvent() {
return withPluginApi("1.15.0", (api) => {
api.onAppEvent("full-page-search:trigger-search", () => {
if (!this.searching) {
this.resetAiResults();
return this.performHyDESearch();
}
});
});
}
@action
checkQueryParamsAndSearch() {
// This check is necessary because handleSearch() isn't called
// if query params are present and a new search has appended text.
// It ensures AiResults are reset and searched for properly
const searchQueryParam = this.router.currentRoute?.queryParams?.q;
if (searchQueryParam) {
this.#resetAndSearchOnEvent();
}
}
<template>
<span {{didInsert this.checkQueryParamsAndSearch}}></span>
{{#if this.searchEnabled}}
<div class="semantic-search__container search-results" role="region">
<div class="semantic-search__results" {{didInsert this.handleSearch}}>
<div
class="semantic-search__searching
{{if this.searching 'in-progress'}}"
>
<DToggleSwitch
disabled={{this.disableToggleSwitch}}
@state={{this.showingAiResults}}
class="semantic-search__results-toggle"
{{on "click" this.toggleAiResults}}
/>
<div class="semantic-search__searching-text">
{{icon "discourse-sparkles"}}
{{this.searchStateText}}
</div>
<AiIndicatorWave @loading={{this.searching}} />
</div>
</div>
</div>
{{/if}}
<AIFullPageSemanticSearch @outletArgs={{@outletArgs}} />
</template>
}
@@ -4,17 +4,6 @@ import Composer from "discourse/models/composer";
import I18n from "I18n";
import ShareFullTopicModal from "../components/modal/share-full-topic-modal";
const MAX_PERSONA_USER_ID = -1200;
export function isPostFromAiBot(post, currentUser) {
return (
post.user_id <= MAX_PERSONA_USER_ID ||
!!currentUser?.ai_enabled_chat_bots?.any(
(bot) => post.username === bot.username
)
);
}
export function showShareConversationModal(modal, topicId) {
ajax(`/discourse-ai/ai-bot/shared-ai-conversations/preview/${topicId}.json`)
.then((payload) => {
@@ -1,20 +1,18 @@
import { hbs } from "ember-cli-htmlbars";
import { ajax } from "discourse/lib/ajax";
import { popupAjaxError } from "discourse/lib/ajax-error";
import { withPluginApi } from "discourse/lib/plugin-api";
import { registerWidgetShim } from "discourse/widgets/render-glimmer";
import { withSilencedDeprecations } from "discourse-common/lib/deprecated";
import AiBotHeaderIcon from "../discourse/components/ai-bot-header-icon";
import AiCancelStreamingButton from "../discourse/components/post-menu/ai-cancel-streaming-button";
import AiDebugButton from "../discourse/components/post-menu/ai-debug-button";
import AiShareButton from "../discourse/components/post-menu/ai-share-button";
import {
isPostFromAiBot,
showShareConversationModal,
} from "../discourse/lib/ai-bot-helper";
import DebugAiModal from "../discourse/components/modal/debug-ai-modal";
import ShareModal from "../discourse/components/modal/share-modal";
import { streamPostText } from "../discourse/lib/ai-streamer/progress-handlers";
import copyConversation from "../discourse/lib/copy-conversation";
const AUTO_COPY_THRESHOLD = 4;
import AiBotHeaderIcon from "../discourse/components/ai-bot-header-icon";
import { showShareConversationModal } from "../discourse/lib/ai-bot-helper";
let enabledChatBotIds = [];
let allowDebug = false;
function isGPTBot(user) {
return user && enabledChatBotIds.includes(user.id);
}
@@ -24,7 +22,29 @@ function attachHeaderIcon(api) {
}
function initializeAIBotReplies(api) {
initializePauseButton(api);
api.addPostMenuButton("cancel-gpt", (post) => {
if (isGPTBot(post.user)) {
return {
icon: "pause",
action: "cancelStreaming",
title: "discourse_ai.ai_bot.cancel_streaming",
className: "btn btn-default cancel-streaming",
position: "first",
};
}
});
api.attachWidgetAction("post", "cancelStreaming", function () {
ajax(`/discourse-ai/ai-bot/post/${this.model.id}/stop-streaming`, {
type: "POST",
})
.then(() => {
document
.querySelector(`#post_${this.model.post_number}`)
.classList.remove("streaming");
})
.catch(popupAjaxError);
});
api.modifyClass("controller:topic", {
pluginId: "discourse-ai",
@@ -82,42 +102,7 @@ function initializePersonaDecorator(api) {
);
}
function initializePauseButton(api) {
const transformerRegistered = api.registerValueTransformer(
"post-menu-buttons",
({ value: dag, context: { post, firstButtonKey } }) => {
if (isGPTBot(post.user)) {
dag.add("ai-cancel-gpt", AiCancelStreamingButton, {
before: firstButtonKey,
after: ["ai-share", "ai-debug"],
});
}
}
);
const silencedKey =
transformerRegistered && "discourse.post-menu-widget-overrides";
withSilencedDeprecations(silencedKey, () => initializePauseWidgetButton(api));
}
function initializePauseWidgetButton(api) {
api.addPostMenuButton("cancel-gpt", (post) => {
if (isGPTBot(post.user)) {
return {
icon: "pause",
action: "cancelStreaming",
title: "discourse_ai.ai_bot.cancel_streaming",
className: "btn btn-default cancel-streaming",
position: "first",
};
}
});
api.attachWidgetAction("post", "cancelStreaming", function () {
AiCancelStreamingButton.cancelStreaming(this.model);
});
}
const MAX_PERSONA_USER_ID = -1200;
function initializeDebugButton(api) {
const currentUser = api.getCurrentUser();
@@ -125,30 +110,10 @@ function initializeDebugButton(api) {
return;
}
const transformerRegistered = api.registerValueTransformer(
"post-menu-buttons",
({ value: dag, context: { post, firstButtonKey } }) => {
if (post.topic?.archetype === "private_message") {
dag.add("ai-debug", AiDebugButton, {
before: firstButtonKey,
after: "ai-share",
});
}
}
);
const silencedKey =
transformerRegistered && "discourse.post-menu-widget-overrides";
withSilencedDeprecations(silencedKey, () => initializeDebugWidgetButton(api));
}
function initializeDebugWidgetButton(api) {
const currentUser = api.getCurrentUser();
let debugAiResponse = async function ({ post }) {
const modal = api.container.lookup("service:modal");
AiDebugButton.debugAiResponse(post, modal);
modal.show(DebugAiModal, { model: post });
};
api.addPostMenuButton("debugAi", (post) => {
@@ -156,8 +121,15 @@ function initializeDebugWidgetButton(api) {
return;
}
if (!isPostFromAiBot(post, currentUser)) {
return;
if (
!currentUser.ai_enabled_chat_bots.any(
(bot) => post.username === bot.username
)
) {
// special handling for personas (persona bot users start at ID -1200 and go down)
if (post.user_id > MAX_PERSONA_USER_ID) {
return;
}
}
return {
@@ -176,29 +148,14 @@ function initializeShareButton(api) {
return;
}
const transformerRegistered = api.registerValueTransformer(
"post-menu-buttons",
({ value: dag, context: { post, firstButtonKey } }) => {
if (post.topic?.archetype === "private_message") {
dag.add("ai-share", AiShareButton, {
before: firstButtonKey,
});
}
}
);
const silencedKey =
transformerRegistered && "discourse.post-menu-widget-overrides";
withSilencedDeprecations(silencedKey, () => initializeShareWidgetButton(api));
}
function initializeShareWidgetButton(api) {
const currentUser = api.getCurrentUser();
let shareAiResponse = async function ({ post, showFeedback }) {
const modal = api.container.lookup("service:modal");
AiShareButton.shareAiResponse(post, modal, showFeedback);
if (post.post_number <= AUTO_COPY_THRESHOLD) {
await copyConversation(post.topic, 1, post.post_number);
showFeedback("discourse_ai.ai_bot.conversation_shared");
} else {
const modal = api.container.lookup("service:modal");
modal.show(ShareModal, { model: post });
}
};
api.addPostMenuButton("share", (post) => {
@@ -207,14 +164,21 @@ function initializeShareWidgetButton(api) {
return;
}
if (!isPostFromAiBot(post, currentUser)) {
return;
if (
!currentUser.ai_enabled_chat_bots.any(
(bot) => post.username === bot.username
)
) {
// special handling for personas (persona bot users start at ID -1200 and go down)
if (post.user_id > MAX_PERSONA_USER_ID) {
return;
}
}
return {
action: shareAiResponse,
icon: "far-copy",
className: "post-action-menu__share-ai",
className: "post-action-menu__share",
title: "discourse_ai.ai_bot.share",
position: "first",
};
@@ -254,10 +218,10 @@ export default {
enabledChatBotIds = user.ai_enabled_chat_bots.map((bot) => bot.id);
allowDebug = user.can_debug_ai_bot_conversations;
withPluginApi("1.6.0", attachHeaderIcon);
withPluginApi("1.34.0", initializeAIBotReplies);
withPluginApi("1.6.0", initializeAIBotReplies);
withPluginApi("1.6.0", initializePersonaDecorator);
withPluginApi("1.34.0", (api) => initializeDebugButton(api, container));
withPluginApi("1.34.0", (api) => initializeShareButton(api, container));
withPluginApi("1.22.0", (api) => initializeDebugButton(api, container));
withPluginApi("1.22.0", (api) => initializeShareButton(api, container));
withPluginApi("1.22.0", (api) =>
initializeShareTopicButton(api, container)
);
@@ -4,6 +4,6 @@ export default apiInitializer("1.15.0", (api) => {
api.modifyClass("component:search-result-entry", {
pluginId: "discourse-ai",
classNameBindings: ["bulkSelectEnabled", "post.generatedByAi:ai-result"],
classNameBindings: ["bulkSelectEnabled", "post.generatedByAI:ai-result"],
});
});
@@ -240,10 +240,14 @@
.--topic-list-with-gist {
.topic-list-item {
.main-link {
.desktop-view & {
padding: 1em 1em 1em 0.67em;
}
font-size: var(--font-up-2);
line-height: var(--line-height-medium);
}
.link-bottom-line {
font-size: var(--font-down-1);
font-size: var(--font-down-2);
margin-top: 0.25em;
}
.ai-topic-gist {
-2
View File
@@ -333,8 +333,6 @@ ar:
response_tokens: "رموز الرد:"
request: "طلب"
response: "الرد"
next_log: "التالي"
previous_log: "السابق"
share_full_topic_modal:
title: "مشاركة المحادثة بشكلٍ علني"
share: "مشاركة ونسخ الرابط"
-1
View File
@@ -77,6 +77,5 @@ be:
ai_bot:
debug_ai_modal:
request: "запыт"
next_log: "Далей"
share_modal:
copy: "капіяваць"
-2
View File
@@ -80,8 +80,6 @@ bg:
ai_bot:
debug_ai_modal:
request: "Заявка"
next_log: "Напред"
previous_log: "Предишни"
share_modal:
copy: "Копирай"
summarization:
-2
View File
@@ -88,7 +88,5 @@ bs_BA:
debug_ai_modal:
request: "Zatraži"
response: "Odgovor"
next_log: "Iduće"
previous_log: "Previous"
share_modal:
copy: "Copy"
-2
View File
@@ -89,7 +89,5 @@ ca:
debug_ai_modal:
request: "Sol·licita"
response: "Reacció"
next_log: "Següent"
previous_log: "Previ"
share_modal:
copy: "Còpia"
-2
View File
@@ -92,8 +92,6 @@ cs:
debug_ai_modal:
request: "Požadavek"
response: "Odpověď"
next_log: "Další"
previous_log: "Předchozí"
share_modal:
copy: "Kopírovat"
summarization:
-2
View File
@@ -91,7 +91,5 @@ da:
debug_ai_modal:
request: "Anmod om medlemskab"
response: "Svar"
next_log: "Næste"
previous_log: "Forrige"
share_modal:
copy: "Kopier"
-2
View File
@@ -404,8 +404,6 @@ de:
response_tokens: "Antwort-Token:"
request: "Anfrage"
response: "Antwort"
next_log: "Nächstes"
previous_log: "Vorheriges"
share_full_topic_modal:
title: "Unterhaltung öffentlich teilen"
share: "Link teilen und kopieren"
-2
View File
@@ -88,7 +88,5 @@ el:
debug_ai_modal:
request: "Αίτημα"
response: "Απάντηση"
next_log: "Επόμενο"
previous_log: "Προηγούμενο"
share_modal:
copy: "Αντιγραφή"
File diff suppressed because it is too large Load Diff
-2
View File
@@ -333,8 +333,6 @@ es:
response_tokens: "Tokens de respuesta:"
request: "Solicitud"
response: "Respuesta"
next_log: "Siguiente"
previous_log: "Anterior"
share_full_topic_modal:
title: "Compartir la conversación públicamente"
share: "Compartir y copiar enlace"
-2
View File
@@ -83,7 +83,5 @@ et:
debug_ai_modal:
request: "Päring"
response: "Vastus"
next_log: "Järgmine"
previous_log: "Eelmine"
share_modal:
copy: "Kopeeri"
-2
View File
@@ -132,8 +132,6 @@ fa_IR:
debug_ai_modal:
request: "درخواست"
response: "پاسخ"
next_log: "بعدی"
previous_log: "پیشین"
ai_label: "هوش مصنوعی"
share_modal:
copy: "کپی"
-2
View File
@@ -333,8 +333,6 @@ fi:
response_tokens: "Vastauksen tokenit:"
request: "Pyyntö"
response: "Vastaus"
next_log: "Seuraava"
previous_log: "Edellinen"
share_full_topic_modal:
title: "Jaa keskustelu julkisesti"
share: "Jaa ja kopioi linkki"
-2
View File
@@ -333,8 +333,6 @@ fr:
response_tokens: "Jetons de réponse :"
request: "Requête"
response: "Réponse"
next_log: "Suivant"
previous_log: "Précédent"
share_full_topic_modal:
title: "Partager la conversation publiquement"
share: "Partager et copier le lien"
-2
View File
@@ -89,7 +89,5 @@ gl:
debug_ai_modal:
request: "Petición"
response: "Resposta"
next_log: "Seguinte"
previous_log: "Anterior"
share_modal:
copy: "Copiar"
-5
View File
@@ -157,8 +157,6 @@ he:
forced_tool_strategy: אסטרטגיית כלי כפוי
allow_chat_direct_messages: "לאפשר הודעות ישירות בצ׳אט"
allow_chat_direct_messages_help: "אם האפשרות פעילה, משתמשים בקבוצות המורשות יכולים לשלוח הודעות ישירות לדמות הזאת."
allow_chat_channel_mentions: "לאפשר אזכורים בערוצי צ׳אט"
allow_chat_channel_mentions_help: "אם האפשרות פעילה, משתמשים בקבוצות המורשות יכולים לאזכר את הדמות הזאת בערוצי צ׳אט."
allow_personal_messages: "לאפשר הודעות פרטיות"
allow_personal_messages_help: "אם האפשרות פעילה, משתמשים בקבוצות המורשות יכולים לשלוח הודעות פרטיות לדמות הזאת."
allow_topic_mentions: "לאפשר אזכורים בנושאים"
@@ -301,7 +299,6 @@ he:
max_prompt_tokens: "מספר האסימונים המרבי לבקשה. ככלל אצבע, זה אמור להיות 50% מחלון ההקשר של המודל."
name: "אנו מצרפים את זה לקריאת ה־API כדי לציין באיזה מודל להשתמש."
vision_enabled: "אם האפשרות פעילה, הבינה מלאכותית תנסה להבין תמונות. כתלות בתמיכה של המודל בעיבוד תמונות. נתמך על ידי המודלים העדכניים ביותר מבית Anthropic,‏ Google ו־OpenAI."
enabled_chat_bot: "אם האפשרות פעילה, משתמשים יכולים לבחור את המודל הזה בעת יצירת הודעות פרטיות עם בוט הבינה המלאכותית."
providers:
aws_bedrock: "AWS Bedrock"
anthropic: "Anthropic"
@@ -402,8 +399,6 @@ he:
response_tokens: "אסימוני תגובה:"
request: "בקשה"
response: "תשובה"
next_log: "הבא"
previous_log: "הקודם"
share_full_topic_modal:
title: "שיתוף שיחה באופן ציבורי"
share: "שיתוף והעתקת קישור"

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