ФУНКЦИИ - МЕЖПРОЕКТНАЯ КОММУНИКАЦИЯ: суточные квоты Voice Tasker по workspace и контурам

This commit is contained in:
DCCONSTRUCTIONS
2026-04-28 14:39:39 +03:00
parent a0c0db27f3
commit a0213db2fc
7 changed files with 309 additions and 64 deletions
@@ -133,7 +133,9 @@ function getVoiceTaskErrorMessage(error: unknown, fallback: string) {
const retryAfter = "retry_after" in error ? formatRetryAfter(error.retry_after) : "позже";
const used = "used" in error && typeof error.used === "number" ? error.used : null;
const limit = "limit" in error && typeof error.limit === "number" ? error.limit : null;
const usageText = used !== null && limit !== null ? ` Использовано ${used} из ${limit} за последний час.` : "";
const limitWindow = "limit_window" in error ? String(error.limit_window) : "hour";
const windowText = limitWindow === "day" ? "за последние сутки" : "за последний час";
const usageText = used !== null && limit !== null ? ` Использовано ${used} из ${limit} ${windowText}.` : "";
if (code === "voice_task_user_hourly_limit_exceeded") {
return `Лимит Voice Tasker для пользователя исчерпан. Повторите ${retryAfter}.${usageText}`;
@@ -143,6 +145,19 @@ function getVoiceTaskErrorMessage(error: unknown, fallback: string) {
return `Лимит Voice Tasker для workspace исчерпан. Повторите ${retryAfter}.${usageText}`;
}
if (code === "voice_task_user_daily_limit_exceeded") {
return `Суточный лимит Voice Tasker для пользователя исчерпан. Повторите ${retryAfter}.${usageText}`;
}
if (code === "voice_task_workspace_daily_limit_exceeded") {
return `Суточный лимит Voice Tasker для workspace исчерпан. Повторите ${retryAfter}.${usageText}`;
}
if (code === "voice_task_project_daily_limit_exceeded") {
const projectName = "project_name" in error && error.project_name ? ` для контура ${String(error.project_name)}` : "";
return `Суточный лимит Voice Tasker${projectName} исчерпан. Повторите ${retryAfter}.${usageText}`;
}
if ("error" in error && error.error) return String(error.error);
}
@@ -46,6 +46,9 @@ type TFormState = {
max_audio_duration_seconds: number;
per_user_hourly_limit: number;
workspace_hourly_limit: number;
per_user_daily_limit: number;
workspace_daily_limit: number;
project_daily_limit: number;
openai_api_key: string;
};
@@ -65,6 +68,9 @@ const getInitialFormState = (settings?: TWorkspaceAISettings): TFormState => ({
max_audio_duration_seconds: settings?.max_audio_duration_seconds ?? 120,
per_user_hourly_limit: settings?.per_user_hourly_limit ?? 30,
workspace_hourly_limit: settings?.workspace_hourly_limit ?? 300,
per_user_daily_limit: settings?.per_user_daily_limit ?? 100,
workspace_daily_limit: settings?.workspace_daily_limit ?? 1000,
project_daily_limit: settings?.project_daily_limit ?? 300,
openai_api_key: "",
});
@@ -184,6 +190,9 @@ export const AIVoiceTaskerSettingsContent = observer(function AIVoiceTaskerSetti
max_audio_duration_seconds: formState.max_audio_duration_seconds,
per_user_hourly_limit: formState.per_user_hourly_limit,
workspace_hourly_limit: formState.workspace_hourly_limit,
per_user_daily_limit: formState.per_user_daily_limit,
workspace_daily_limit: formState.workspace_daily_limit,
project_daily_limit: formState.project_daily_limit,
};
if (formState.openai_api_key.trim()) payload.openai_api_key = formState.openai_api_key.trim();
@@ -339,7 +348,7 @@ export const AIVoiceTaskerSettingsContent = observer(function AIVoiceTaskerSetti
<SectionHeader
icon={BrainCircuit}
title="Models and limits"
description="MVP использует один workspace key для транскрибации и структурирования."
description="Один workspace key обслуживает всех пользователей. Лимиты срабатывают до отправки аудио в OpenAI."
/>
<div className="grid gap-5 px-5 py-5 md:grid-cols-2">
<Field label="Transcription model">
@@ -356,24 +365,51 @@ export const AIVoiceTaskerSettingsContent = observer(function AIVoiceTaskerSetti
className="nodedc-settings-input w-full"
/>
</Field>
<Field label="Per-user limit">
<Field label="Лимит пользователя за час">
<NumberInput
value={formState.per_user_hourly_limit}
min={1}
max={1000}
suffix="tasks/hour"
suffix="за час"
onChange={(value) => updateFormValue("per_user_hourly_limit", value)}
/>
</Field>
<Field label="Workspace limit">
<Field label="Лимит workspace за час">
<NumberInput
value={formState.workspace_hourly_limit}
min={1}
max={10000}
suffix="tasks/hour"
suffix="за час"
onChange={(value) => updateFormValue("workspace_hourly_limit", value)}
/>
</Field>
<Field label="Суточная квота пользователя">
<NumberInput
value={formState.per_user_daily_limit}
min={1}
max={10000}
suffix="за сутки"
onChange={(value) => updateFormValue("per_user_daily_limit", value)}
/>
</Field>
<Field label="Суточная квота workspace">
<NumberInput
value={formState.workspace_daily_limit}
min={1}
max={100000}
suffix="за сутки"
onChange={(value) => updateFormValue("workspace_daily_limit", value)}
/>
</Field>
<Field label="Суточная квота контура">
<NumberInput
value={formState.project_daily_limit}
min={1}
max={50000}
suffix="за сутки"
onChange={(value) => updateFormValue("project_daily_limit", value)}
/>
</Field>
</div>
</section>