mirror of
https://github.com/dograh-hq/dograh.git
synced 2026-06-28 08:49:42 +02:00
feat: limit campaign concurrency to number of CLIs
This commit is contained in:
parent
6711dcb3ea
commit
3cdede0f45
18 changed files with 846 additions and 462 deletions
|
|
@ -50,6 +50,7 @@ export default function NewCampaignPage() {
|
|||
// Advanced settings state
|
||||
const [showAdvancedSettings, setShowAdvancedSettings] = useState(false);
|
||||
const [orgConcurrentLimit, setOrgConcurrentLimit] = useState<number>(2);
|
||||
const [fromNumbersCount, setFromNumbersCount] = useState<number>(0);
|
||||
const [maxConcurrency, setMaxConcurrency] = useState<string>('');
|
||||
// Retry config state
|
||||
const [retryEnabled, setRetryEnabled] = useState(true);
|
||||
|
|
@ -102,6 +103,7 @@ export default function NewCampaignPage() {
|
|||
|
||||
if (response.data) {
|
||||
setOrgConcurrentLimit(response.data.concurrent_call_limit);
|
||||
setFromNumbersCount(response.data.from_numbers_count);
|
||||
// Initialize retry config from defaults
|
||||
const retryConfig = response.data.default_retry_config;
|
||||
setRetryEnabled(retryConfig.enabled);
|
||||
|
|
@ -124,6 +126,11 @@ export default function NewCampaignPage() {
|
|||
}
|
||||
}, [fetchWorkflows, fetchCampaignLimits, user]);
|
||||
|
||||
// Effective concurrency limit considering both org limit and available CLIs
|
||||
const effectiveLimit = fromNumbersCount > 0
|
||||
? Math.min(orgConcurrentLimit, fromNumbersCount)
|
||||
: orgConcurrentLimit;
|
||||
|
||||
// Handle form submission
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
|
|
@ -141,8 +148,12 @@ export default function NewCampaignPage() {
|
|||
toast.error('Max concurrent calls must be between 1 and 100');
|
||||
return;
|
||||
}
|
||||
if (maxConcurrencyValue > orgConcurrentLimit) {
|
||||
toast.error(`Max concurrent calls cannot exceed organization limit (${orgConcurrentLimit})`);
|
||||
if (maxConcurrencyValue > effectiveLimit) {
|
||||
if (fromNumbersCount > 0 && fromNumbersCount < orgConcurrentLimit) {
|
||||
toast.error(`Max concurrent calls cannot exceed ${effectiveLimit}. You have ${fromNumbersCount} phone number(s) configured — add more CLIs to increase concurrency.`);
|
||||
} else {
|
||||
toast.error(`Max concurrent calls cannot exceed organization limit (${effectiveLimit})`);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -349,15 +360,26 @@ export default function NewCampaignPage() {
|
|||
<Input
|
||||
id="max-concurrency"
|
||||
type="number"
|
||||
placeholder={`Organization limit: ${orgConcurrentLimit}`}
|
||||
placeholder={`Default: ${effectiveLimit}`}
|
||||
value={maxConcurrency}
|
||||
onChange={(e) => setMaxConcurrency(e.target.value)}
|
||||
min={1}
|
||||
max={orgConcurrentLimit}
|
||||
max={effectiveLimit}
|
||||
/>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Maximum number of simultaneous calls. Leave empty to use organization limit ({orgConcurrentLimit}).
|
||||
Maximum number of simultaneous calls. Leave empty to use {effectiveLimit}.
|
||||
{fromNumbersCount > 0 && ` You have ${fromNumbersCount} CLI${fromNumbersCount !== 1 ? 's' : ''} and an org limit of ${orgConcurrentLimit}.`}
|
||||
</p>
|
||||
{fromNumbersCount > 0 && fromNumbersCount < orgConcurrentLimit && (
|
||||
<p className="text-sm text-amber-600 dark:text-amber-400">
|
||||
Concurrency is limited to {fromNumbersCount} by your configured phone numbers. To use the full org limit of {orgConcurrentLimit}, add more CLIs in <a href="/telephony-configurations" className="underline font-medium">Telephony Configuration</a>.
|
||||
</p>
|
||||
)}
|
||||
{fromNumbersCount === 0 && (
|
||||
<p className="text-sm text-amber-600 dark:text-amber-400">
|
||||
No phone numbers configured. Add CLIs in <a href="/telephony-configurations" className="underline font-medium">Telephony Configuration</a> before running the campaign.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Retry Configuration */}
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -45,6 +45,7 @@ export type CallType = 'inbound' | 'outbound';
|
|||
|
||||
export type CampaignLimitsResponse = {
|
||||
concurrent_call_limit: number;
|
||||
from_numbers_count: number;
|
||||
default_retry_config: RetryConfigResponse;
|
||||
};
|
||||
|
||||
|
|
@ -705,10 +706,6 @@ export type ProcessDocumentRequestSchema = {
|
|||
* S3 key of the uploaded file
|
||||
*/
|
||||
s3_key: string;
|
||||
/**
|
||||
* Embedding service to use for processing. Options: 'openai' (default, 1536-dim, requires API key) or 'sentence_transformer' (free, 384-dim)
|
||||
*/
|
||||
embedding_service?: 'sentence_transformer' | 'openai';
|
||||
};
|
||||
|
||||
export type RetryConfigRequest = {
|
||||
|
|
@ -4354,6 +4351,66 @@ export type OptionsConfigApiV1PublicEmbedConfigTokenOptionsResponses = {
|
|||
200: unknown;
|
||||
};
|
||||
|
||||
export type GetPublicTurnCredentialsApiV1PublicEmbedTurnCredentialsSessionTokenGetData = {
|
||||
body?: never;
|
||||
path: {
|
||||
session_token: string;
|
||||
};
|
||||
query?: never;
|
||||
url: '/api/v1/public/embed/turn-credentials/{session_token}';
|
||||
};
|
||||
|
||||
export type GetPublicTurnCredentialsApiV1PublicEmbedTurnCredentialsSessionTokenGetErrors = {
|
||||
/**
|
||||
* Not found
|
||||
*/
|
||||
404: unknown;
|
||||
/**
|
||||
* Validation Error
|
||||
*/
|
||||
422: HttpValidationError;
|
||||
};
|
||||
|
||||
export type GetPublicTurnCredentialsApiV1PublicEmbedTurnCredentialsSessionTokenGetError = GetPublicTurnCredentialsApiV1PublicEmbedTurnCredentialsSessionTokenGetErrors[keyof GetPublicTurnCredentialsApiV1PublicEmbedTurnCredentialsSessionTokenGetErrors];
|
||||
|
||||
export type GetPublicTurnCredentialsApiV1PublicEmbedTurnCredentialsSessionTokenGetResponses = {
|
||||
/**
|
||||
* Successful Response
|
||||
*/
|
||||
200: TurnCredentialsResponse;
|
||||
};
|
||||
|
||||
export type GetPublicTurnCredentialsApiV1PublicEmbedTurnCredentialsSessionTokenGetResponse = GetPublicTurnCredentialsApiV1PublicEmbedTurnCredentialsSessionTokenGetResponses[keyof GetPublicTurnCredentialsApiV1PublicEmbedTurnCredentialsSessionTokenGetResponses];
|
||||
|
||||
export type OptionsTurnCredentialsApiV1PublicEmbedTurnCredentialsSessionTokenOptionsData = {
|
||||
body?: never;
|
||||
path: {
|
||||
session_token: string;
|
||||
};
|
||||
query?: never;
|
||||
url: '/api/v1/public/embed/turn-credentials/{session_token}';
|
||||
};
|
||||
|
||||
export type OptionsTurnCredentialsApiV1PublicEmbedTurnCredentialsSessionTokenOptionsErrors = {
|
||||
/**
|
||||
* Not found
|
||||
*/
|
||||
404: unknown;
|
||||
/**
|
||||
* Validation Error
|
||||
*/
|
||||
422: HttpValidationError;
|
||||
};
|
||||
|
||||
export type OptionsTurnCredentialsApiV1PublicEmbedTurnCredentialsSessionTokenOptionsError = OptionsTurnCredentialsApiV1PublicEmbedTurnCredentialsSessionTokenOptionsErrors[keyof OptionsTurnCredentialsApiV1PublicEmbedTurnCredentialsSessionTokenOptionsErrors];
|
||||
|
||||
export type OptionsTurnCredentialsApiV1PublicEmbedTurnCredentialsSessionTokenOptionsResponses = {
|
||||
/**
|
||||
* Successful Response
|
||||
*/
|
||||
200: unknown;
|
||||
};
|
||||
|
||||
export type InitiateCallApiV1PublicAgentUuidPostData = {
|
||||
body: TriggerCallRequest;
|
||||
headers: {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue