Files
discourse-ai/assets/javascripts/discourse/components/modal/share-modal.gjs
T
SamandKeegan George d07cf51653 FEATURE: llm quotas (#1047)
Adds a comprehensive quota management system for LLM models that allows:

- Setting per-group (applied per user in the group) token and usage limits with configurable durations
- Tracking and enforcing token/usage limits across user groups
- Quota reset periods (hourly, daily, weekly, or custom)
-  Admin UI for managing quotas with real-time updates

This system provides granular control over LLM API usage by allowing admins
to define limits on both total tokens and number of requests per group.
Supports multiple concurrent quotas per model and automatically handles
quota resets.


Co-authored-by: Keegan George <[email protected]>
2025-01-14 15:54:09 +11:00

130 lines
3.7 KiB
Plaintext

import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import { Input } from "@ember/component";
import { on } from "@ember/modifier";
import { action } from "@ember/object";
import { service } from "@ember/service";
import { htmlSafe } from "@ember/template";
import DButton from "discourse/components/d-button";
import DModal from "discourse/components/d-modal";
import discourseLater from "discourse-common/lib/later";
import { i18n } from "discourse-i18n";
import { showShareConversationModal } from "../../lib/ai-bot-helper";
import copyConversation from "../../lib/copy-conversation";
export default class ShareModal extends Component {
@service modal;
@service siteSettings;
@service currentUser;
@tracked contextValue = 1;
@tracked htmlContext = "";
@tracked maxContext = 0;
@tracked allPosts = [];
@tracked justCopiedText = "";
constructor() {
super(...arguments);
const postStream = this.args.model.topic.get("postStream");
let postNumbers = [];
// simpler to understand than Array.from
for (let i = 1; i <= this.args.model.post_number; i++) {
postNumbers.push(i);
}
this.allPosts = postNumbers
.map((postNumber) => {
let postId = postStream.findPostIdForPostNumber(postNumber);
if (postId) {
return postStream.findLoadedPost(postId);
}
})
.filter((post) => post);
this.maxContext = this.allPosts.length / 2;
this.contextValue = 1;
this.updateHtmlContext();
}
@action
updateHtmlContext() {
let context = [];
const start = this.allPosts.length - this.contextValue * 2;
for (let i = start; i < this.allPosts.length; i++) {
const post = this.allPosts[i];
context.push(`<p><b>${post.username}:</b></p>`);
context.push(post.cooked);
}
this.htmlContext = htmlSafe(context.join("\n"));
}
@action
async copyContext() {
const from =
this.allPosts[this.allPosts.length - this.contextValue * 2].post_number;
const to = this.args.model.post_number;
await copyConversation(this.args.model.topic, from, to);
this.justCopiedText = i18n("discourse_ai.ai_bot.conversation_shared");
discourseLater(() => {
this.justCopiedText = "";
}, 2000);
}
@action
shareConversationModal(event) {
event?.preventDefault();
this.args.closeModal();
showShareConversationModal(this.modal, this.args.model.topic_id);
return false;
}
<template>
<DModal
class="ai-share-modal"
@title={{i18n "discourse_ai.ai_bot.share_modal.title"}}
@closeModal={{@closeModal}}
>
<:body>
<div class="ai-share-modal__preview">
{{this.htmlContext}}
</div>
</:body>
<:footer>
<div class="ai-share-modal__slider">
<Input
@type="range"
min="1"
max={{this.maxContext}}
@value={{this.contextValue}}
{{on "change" this.updateHtmlContext}}
/>
<div class="ai-share-modal__context">
{{i18n "discourse_ai.ai_bot.share_modal.context"}}
{{this.contextValue}}
</div>
</div>
<DButton
class="btn-primary confirm"
@icon="copy"
@action={{this.copyContext}}
@label="discourse_ai.ai_bot.share_modal.copy"
/>
<span class="ai-share-modal__just-copied">{{this.justCopiedText}}</span>
{{#if this.currentUser.can_share_ai_bot_conversations}}
<a href {{on "click" this.shareConversationModal}}>
<span class="ai-share-modal__share-tip">
{{i18n "discourse_ai.ai_bot.share_modal.share_tip"}}
</span>
</a>
{{/if}}
</:footer>
</DModal>
</template>
}