mirror of
https://github.com/dograh-hq/dograh.git
synced 2026-07-10 11:12:13 +02:00
feat: client gen default configurations (#499)
This commit is contained in:
parent
a9947cec04
commit
ceb01a16a3
14 changed files with 353 additions and 87 deletions
|
|
@ -10,6 +10,10 @@ from api.db.models import (
|
||||||
UserModel,
|
UserModel,
|
||||||
)
|
)
|
||||||
from api.schemas.onboarding_state import OnboardingState, OnboardingStateUpdate
|
from api.schemas.onboarding_state import OnboardingState, OnboardingStateUpdate
|
||||||
|
from api.schemas.workflow_configurations import (
|
||||||
|
WorkflowConfigurationDefaults,
|
||||||
|
get_default_workflow_configurations,
|
||||||
|
)
|
||||||
from api.services.auth.depends import get_user
|
from api.services.auth.depends import get_user
|
||||||
from api.services.configuration.ai_model_configuration import (
|
from api.services.configuration.ai_model_configuration import (
|
||||||
get_resolved_ai_model_configuration,
|
get_resolved_ai_model_configuration,
|
||||||
|
|
@ -40,13 +44,14 @@ class AuthUserResponse(TypedDict):
|
||||||
is_superuser: bool
|
is_superuser: bool
|
||||||
|
|
||||||
|
|
||||||
class DefaultConfigurationsResponse(TypedDict):
|
class DefaultConfigurationsResponse(BaseModel):
|
||||||
llm: dict[str, dict]
|
llm: dict[str, dict]
|
||||||
tts: dict[str, dict]
|
tts: dict[str, dict]
|
||||||
stt: dict[str, dict]
|
stt: dict[str, dict]
|
||||||
embeddings: dict[str, dict]
|
embeddings: dict[str, dict]
|
||||||
realtime: dict[str, dict]
|
realtime: dict[str, dict]
|
||||||
default_providers: dict[str, str]
|
default_providers: dict[str, str]
|
||||||
|
workflow_configurations: WorkflowConfigurationDefaults
|
||||||
|
|
||||||
|
|
||||||
@router.get("/configurations/defaults")
|
@router.get("/configurations/defaults")
|
||||||
|
|
@ -73,8 +78,9 @@ async def get_default_configurations() -> DefaultConfigurationsResponse:
|
||||||
for provider, model_cls in REGISTRY[ServiceType.REALTIME].items()
|
for provider, model_cls in REGISTRY[ServiceType.REALTIME].items()
|
||||||
},
|
},
|
||||||
"default_providers": DEFAULT_SERVICE_PROVIDERS,
|
"default_providers": DEFAULT_SERVICE_PROVIDERS,
|
||||||
|
"workflow_configurations": get_default_workflow_configurations(),
|
||||||
}
|
}
|
||||||
return configurations
|
return DefaultConfigurationsResponse(**configurations)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/auth/user")
|
@router.get("/auth/user")
|
||||||
|
|
|
||||||
44
api/schemas/workflow_configurations.py
Normal file
44
api/schemas/workflow_configurations.py
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
from typing import Literal
|
||||||
|
|
||||||
|
from pydantic import BaseModel, ConfigDict, Field
|
||||||
|
|
||||||
|
DEFAULT_MAX_CALL_DURATION_SECONDS = 300
|
||||||
|
DEFAULT_MAX_USER_IDLE_TIMEOUT_SECONDS = 10.0
|
||||||
|
DEFAULT_SMART_TURN_STOP_SECS = 2.0
|
||||||
|
DEFAULT_TURN_START_STRATEGY = "default"
|
||||||
|
DEFAULT_TURN_START_MIN_WORDS = 3
|
||||||
|
DEFAULT_PROVISIONAL_VAD_PAUSE_SECS = 1.5
|
||||||
|
DEFAULT_TURN_STOP_STRATEGY = "transcription"
|
||||||
|
DEFAULT_CONTEXT_COMPACTION_ENABLED = False
|
||||||
|
|
||||||
|
|
||||||
|
class AmbientNoiseConfigurationDefaults(BaseModel):
|
||||||
|
model_config = ConfigDict(extra="allow")
|
||||||
|
|
||||||
|
enabled: bool = False
|
||||||
|
volume: float = 0.3
|
||||||
|
|
||||||
|
|
||||||
|
class WorkflowConfigurationDefaults(BaseModel):
|
||||||
|
model_config = ConfigDict(extra="allow")
|
||||||
|
|
||||||
|
ambient_noise_configuration: AmbientNoiseConfigurationDefaults = Field(
|
||||||
|
default_factory=AmbientNoiseConfigurationDefaults
|
||||||
|
)
|
||||||
|
max_call_duration: int = DEFAULT_MAX_CALL_DURATION_SECONDS
|
||||||
|
max_user_idle_timeout: float = DEFAULT_MAX_USER_IDLE_TIMEOUT_SECONDS
|
||||||
|
smart_turn_stop_secs: float = DEFAULT_SMART_TURN_STOP_SECS
|
||||||
|
turn_start_strategy: Literal["default", "min_words", "provisional_vad"] = (
|
||||||
|
DEFAULT_TURN_START_STRATEGY
|
||||||
|
)
|
||||||
|
turn_start_min_words: int = DEFAULT_TURN_START_MIN_WORDS
|
||||||
|
provisional_vad_pause_secs: float = DEFAULT_PROVISIONAL_VAD_PAUSE_SECS
|
||||||
|
turn_stop_strategy: Literal["transcription", "turn_analyzer"] = (
|
||||||
|
DEFAULT_TURN_STOP_STRATEGY
|
||||||
|
)
|
||||||
|
dictionary: str = ""
|
||||||
|
context_compaction_enabled: bool = DEFAULT_CONTEXT_COMPACTION_ENABLED
|
||||||
|
|
||||||
|
|
||||||
|
def get_default_workflow_configurations() -> WorkflowConfigurationDefaults:
|
||||||
|
return WorkflowConfigurationDefaults()
|
||||||
|
|
@ -3,6 +3,7 @@ from typing import Awaitable, Callable, Optional
|
||||||
|
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
|
||||||
|
from api.schemas.workflow_configurations import DEFAULT_MAX_CALL_DURATION_SECONDS
|
||||||
from pipecat.frames.frames import (
|
from pipecat.frames.frames import (
|
||||||
Frame,
|
Frame,
|
||||||
HeartbeatFrame,
|
HeartbeatFrame,
|
||||||
|
|
@ -23,7 +24,7 @@ class PipelineEngineCallbacksProcessor(FrameProcessor):
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
max_call_duration_seconds: int = 300,
|
max_call_duration_seconds: int = DEFAULT_MAX_CALL_DURATION_SECONDS,
|
||||||
max_duration_end_task_callback: Optional[Callable[[], Awaitable[None]]] = None,
|
max_duration_end_task_callback: Optional[Callable[[], Awaitable[None]]] = None,
|
||||||
generation_started_callback: Optional[Callable[[], Awaitable[None]]] = None,
|
generation_started_callback: Optional[Callable[[], Awaitable[None]]] = None,
|
||||||
llm_text_frame_callback: Optional[Callable[[str], Awaitable[None]]] = None,
|
llm_text_frame_callback: Optional[Callable[[str], Awaitable[None]]] = None,
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,14 @@ from loguru import logger
|
||||||
|
|
||||||
from api.db import db_client
|
from api.db import db_client
|
||||||
from api.enums import WorkflowRunMode
|
from api.enums import WorkflowRunMode
|
||||||
|
from api.schemas.workflow_configurations import (
|
||||||
|
DEFAULT_MAX_CALL_DURATION_SECONDS,
|
||||||
|
DEFAULT_MAX_USER_IDLE_TIMEOUT_SECONDS,
|
||||||
|
DEFAULT_PROVISIONAL_VAD_PAUSE_SECS,
|
||||||
|
DEFAULT_SMART_TURN_STOP_SECS,
|
||||||
|
DEFAULT_TURN_START_MIN_WORDS,
|
||||||
|
DEFAULT_TURN_START_STRATEGY,
|
||||||
|
)
|
||||||
from api.services.configuration.registry import ServiceProviders
|
from api.services.configuration.registry import ServiceProviders
|
||||||
from api.services.integrations import (
|
from api.services.integrations import (
|
||||||
IntegrationRuntimeContext,
|
IntegrationRuntimeContext,
|
||||||
|
|
@ -100,10 +108,6 @@ ensure_tracing()
|
||||||
|
|
||||||
DEFAULT_USER_TURN_STOP_TIMEOUT = 5.0
|
DEFAULT_USER_TURN_STOP_TIMEOUT = 5.0
|
||||||
EXTERNAL_TURN_USER_STOP_TIMEOUT = 30.0
|
EXTERNAL_TURN_USER_STOP_TIMEOUT = 30.0
|
||||||
DEFAULT_TURN_START_STRATEGY = "default"
|
|
||||||
DEFAULT_TURN_START_MIN_WORDS = 3
|
|
||||||
DEFAULT_PROVISIONAL_VAD_PAUSE_SECS = 1.5
|
|
||||||
DEFAULT_SMART_TURN_STOP_SECS = 2.0
|
|
||||||
|
|
||||||
|
|
||||||
def _resolve_user_turn_stop_timeout(
|
def _resolve_user_turn_stop_timeout(
|
||||||
|
|
@ -538,8 +542,8 @@ async def _run_pipeline_impl(
|
||||||
run_configs = run_definition.workflow_configurations or {}
|
run_configs = run_definition.workflow_configurations or {}
|
||||||
|
|
||||||
# Extract configurations from the version's workflow_configurations
|
# Extract configurations from the version's workflow_configurations
|
||||||
max_call_duration_seconds = 300 # Default 5 minutes
|
max_call_duration_seconds = DEFAULT_MAX_CALL_DURATION_SECONDS
|
||||||
max_user_idle_timeout = 10.0 # Default 10 seconds
|
max_user_idle_timeout = DEFAULT_MAX_USER_IDLE_TIMEOUT_SECONDS
|
||||||
keyterms = None # Dictionary words for STT boosting
|
keyterms = None # Dictionary words for STT boosting
|
||||||
|
|
||||||
if run_configs:
|
if run_configs:
|
||||||
|
|
|
||||||
|
|
@ -443,7 +443,7 @@ function RenderWorkflow({
|
||||||
const renameWorkflow = useCallback(async (newName: string) => {
|
const renameWorkflow = useCallback(async (newName: string) => {
|
||||||
// The header doesn't render the pencil until the page has mounted with
|
// The header doesn't render the pencil until the page has mounted with
|
||||||
// initial data, so workflowConfigurations is non-null by the time this
|
// initial data, so workflowConfigurations is non-null by the time this
|
||||||
// runs. Throw rather than silently sending DEFAULT_WORKFLOW_CONFIGURATIONS,
|
// runs. Throw rather than silently sending fallback workflow configurations,
|
||||||
// which would overwrite the saved server-side config.
|
// which would overwrite the saved server-side config.
|
||||||
if (!workflowConfigurations) {
|
if (!workflowConfigurations) {
|
||||||
throw new Error("Workflow configurations not loaded");
|
throw new Error("Workflow configurations not loaded");
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import {
|
||||||
AmbientNoiseConfiguration,
|
AmbientNoiseConfiguration,
|
||||||
DEFAULT_PROVISIONAL_VAD_PAUSE_SECS,
|
DEFAULT_PROVISIONAL_VAD_PAUSE_SECS,
|
||||||
DEFAULT_TURN_START_MIN_WORDS,
|
DEFAULT_TURN_START_MIN_WORDS,
|
||||||
|
resolveWorkflowConfigurations,
|
||||||
TURN_START_STRATEGY_OPTIONS,
|
TURN_START_STRATEGY_OPTIONS,
|
||||||
TurnStartStrategy,
|
TurnStartStrategy,
|
||||||
TurnStopStrategy,
|
TurnStopStrategy,
|
||||||
|
|
@ -24,11 +25,6 @@ interface ConfigurationsDialogProps {
|
||||||
onSave: (configurations: WorkflowConfigurations, workflowName: string) => Promise<void>;
|
onSave: (configurations: WorkflowConfigurations, workflowName: string) => Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const DEFAULT_AMBIENT_NOISE_CONFIG: AmbientNoiseConfiguration = {
|
|
||||||
enabled: false,
|
|
||||||
volume: 0.3,
|
|
||||||
};
|
|
||||||
|
|
||||||
export const ConfigurationsDialog = ({
|
export const ConfigurationsDialog = ({
|
||||||
open,
|
open,
|
||||||
onOpenChange,
|
onOpenChange,
|
||||||
|
|
@ -36,33 +32,34 @@ export const ConfigurationsDialog = ({
|
||||||
workflowName,
|
workflowName,
|
||||||
onSave
|
onSave
|
||||||
}: ConfigurationsDialogProps) => {
|
}: ConfigurationsDialogProps) => {
|
||||||
|
const resolvedWorkflowConfigurations = resolveWorkflowConfigurations(workflowConfigurations);
|
||||||
const [name, setName] = useState<string>(workflowName);
|
const [name, setName] = useState<string>(workflowName);
|
||||||
const [ambientNoiseConfig, setAmbientNoiseConfig] = useState<AmbientNoiseConfiguration>(
|
const [ambientNoiseConfig, setAmbientNoiseConfig] = useState<AmbientNoiseConfiguration>(
|
||||||
workflowConfigurations?.ambient_noise_configuration || DEFAULT_AMBIENT_NOISE_CONFIG
|
resolvedWorkflowConfigurations.ambient_noise_configuration
|
||||||
);
|
);
|
||||||
const [maxCallDuration, setMaxCallDuration] = useState<number>(
|
const [maxCallDuration, setMaxCallDuration] = useState<number>(
|
||||||
workflowConfigurations?.max_call_duration || 600 // Default 10 minutes
|
resolvedWorkflowConfigurations.max_call_duration
|
||||||
);
|
);
|
||||||
const [maxUserIdleTimeout, setMaxUserIdleTimeout] = useState<number>(
|
const [maxUserIdleTimeout, setMaxUserIdleTimeout] = useState<number>(
|
||||||
workflowConfigurations?.max_user_idle_timeout || 10 // Default 10 seconds
|
resolvedWorkflowConfigurations.max_user_idle_timeout
|
||||||
);
|
);
|
||||||
const [smartTurnStopSecs, setSmartTurnStopSecs] = useState<number>(
|
const [smartTurnStopSecs, setSmartTurnStopSecs] = useState<number>(
|
||||||
workflowConfigurations?.smart_turn_stop_secs || 2 // Default 2 seconds
|
resolvedWorkflowConfigurations.smart_turn_stop_secs
|
||||||
);
|
);
|
||||||
const [turnStartStrategy, setTurnStartStrategy] = useState<TurnStartStrategy>(
|
const [turnStartStrategy, setTurnStartStrategy] = useState<TurnStartStrategy>(
|
||||||
workflowConfigurations?.turn_start_strategy || 'default'
|
resolvedWorkflowConfigurations.turn_start_strategy
|
||||||
);
|
);
|
||||||
const [turnStartMinWords, setTurnStartMinWords] = useState<number>(
|
const [turnStartMinWords, setTurnStartMinWords] = useState<number>(
|
||||||
workflowConfigurations?.turn_start_min_words || DEFAULT_TURN_START_MIN_WORDS
|
resolvedWorkflowConfigurations.turn_start_min_words
|
||||||
);
|
);
|
||||||
const [provisionalVadPauseSecs, setProvisionalVadPauseSecs] = useState<number>(
|
const [provisionalVadPauseSecs, setProvisionalVadPauseSecs] = useState<number>(
|
||||||
workflowConfigurations?.provisional_vad_pause_secs || DEFAULT_PROVISIONAL_VAD_PAUSE_SECS
|
resolvedWorkflowConfigurations.provisional_vad_pause_secs
|
||||||
);
|
);
|
||||||
const [turnStopStrategy, setTurnStopStrategy] = useState<TurnStopStrategy>(
|
const [turnStopStrategy, setTurnStopStrategy] = useState<TurnStopStrategy>(
|
||||||
workflowConfigurations?.turn_stop_strategy || 'transcription'
|
resolvedWorkflowConfigurations.turn_stop_strategy
|
||||||
);
|
);
|
||||||
const [contextCompactionEnabled, setContextCompactionEnabled] = useState<boolean>(
|
const [contextCompactionEnabled, setContextCompactionEnabled] = useState<boolean>(
|
||||||
workflowConfigurations?.context_compaction_enabled ?? false
|
resolvedWorkflowConfigurations.context_compaction_enabled
|
||||||
);
|
);
|
||||||
const [isSaving, setIsSaving] = useState(false);
|
const [isSaving, setIsSaving] = useState(false);
|
||||||
const selectedTurnStartStrategy = TURN_START_STRATEGY_OPTIONS.find(
|
const selectedTurnStartStrategy = TURN_START_STRATEGY_OPTIONS.find(
|
||||||
|
|
@ -94,16 +91,17 @@ export const ConfigurationsDialog = ({
|
||||||
// Sync state with props when dialog opens
|
// Sync state with props when dialog opens
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (open) {
|
if (open) {
|
||||||
|
const nextWorkflowConfigurations = resolveWorkflowConfigurations(workflowConfigurations);
|
||||||
setName(workflowName);
|
setName(workflowName);
|
||||||
setAmbientNoiseConfig(workflowConfigurations?.ambient_noise_configuration || DEFAULT_AMBIENT_NOISE_CONFIG);
|
setAmbientNoiseConfig(nextWorkflowConfigurations.ambient_noise_configuration);
|
||||||
setMaxCallDuration(workflowConfigurations?.max_call_duration || 600);
|
setMaxCallDuration(nextWorkflowConfigurations.max_call_duration);
|
||||||
setMaxUserIdleTimeout(workflowConfigurations?.max_user_idle_timeout || 10);
|
setMaxUserIdleTimeout(nextWorkflowConfigurations.max_user_idle_timeout);
|
||||||
setSmartTurnStopSecs(workflowConfigurations?.smart_turn_stop_secs || 2);
|
setSmartTurnStopSecs(nextWorkflowConfigurations.smart_turn_stop_secs);
|
||||||
setTurnStartStrategy(workflowConfigurations?.turn_start_strategy || 'default');
|
setTurnStartStrategy(nextWorkflowConfigurations.turn_start_strategy);
|
||||||
setTurnStartMinWords(workflowConfigurations?.turn_start_min_words || DEFAULT_TURN_START_MIN_WORDS);
|
setTurnStartMinWords(nextWorkflowConfigurations.turn_start_min_words);
|
||||||
setProvisionalVadPauseSecs(workflowConfigurations?.provisional_vad_pause_secs || DEFAULT_PROVISIONAL_VAD_PAUSE_SECS);
|
setProvisionalVadPauseSecs(nextWorkflowConfigurations.provisional_vad_pause_secs);
|
||||||
setTurnStopStrategy(workflowConfigurations?.turn_stop_strategy || 'transcription');
|
setTurnStopStrategy(nextWorkflowConfigurations.turn_stop_strategy);
|
||||||
setContextCompactionEnabled(workflowConfigurations?.context_compaction_enabled ?? false);
|
setContextCompactionEnabled(nextWorkflowConfigurations.context_compaction_enabled);
|
||||||
}
|
}
|
||||||
}, [open, workflowName, workflowConfigurations]);
|
}, [open, workflowName, workflowConfigurations]);
|
||||||
|
|
||||||
|
|
@ -244,6 +242,16 @@ export const ConfigurationsDialog = ({
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Interruption Section */}
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div>
|
||||||
|
<h3 className="text-sm font-semibold mb-1">Interruption</h3>
|
||||||
|
<p className="text-xs text-muted-foreground">
|
||||||
|
Configure when user speech should interrupt the agent while it is speaking.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label htmlFor="turn_start_strategy" className="text-xs">
|
<Label htmlFor="turn_start_strategy" className="text-xs">
|
||||||
|
|
|
||||||
|
|
@ -9,12 +9,13 @@ import {
|
||||||
import { EdgeChange, NodeChange } from "@xyflow/system";
|
import { EdgeChange, NodeChange } from "@xyflow/system";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import posthog from "posthog-js";
|
import posthog from "posthog-js";
|
||||||
import { useCallback, useEffect, useRef } from "react";
|
import { useCallback, useEffect, useRef, useState } from "react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
|
||||||
import { useWorkflowStore } from "@/app/workflow/[workflowId]/stores/workflowStore";
|
import { useWorkflowStore } from "@/app/workflow/[workflowId]/stores/workflowStore";
|
||||||
import {
|
import {
|
||||||
createWorkflowRunApiV1WorkflowWorkflowIdRunsPost,
|
createWorkflowRunApiV1WorkflowWorkflowIdRunsPost,
|
||||||
|
getDefaultConfigurationsApiV1UserConfigurationsDefaultsGet,
|
||||||
updateWorkflowApiV1WorkflowWorkflowIdPut,
|
updateWorkflowApiV1WorkflowWorkflowIdPut,
|
||||||
validateWorkflowApiV1WorkflowWorkflowIdValidatePost
|
validateWorkflowApiV1WorkflowWorkflowIdValidatePost
|
||||||
} from "@/client";
|
} from "@/client";
|
||||||
|
|
@ -24,7 +25,11 @@ import { FlowEdge, FlowNode, FlowNodeData, NodeType } from "@/components/flow/ty
|
||||||
import { PostHogEvent } from "@/constants/posthog-events";
|
import { PostHogEvent } from "@/constants/posthog-events";
|
||||||
import logger from '@/lib/logger';
|
import logger from '@/lib/logger';
|
||||||
import { getNextNodeId, getRandomId } from "@/lib/utils";
|
import { getNextNodeId, getRandomId } from "@/lib/utils";
|
||||||
import { DEFAULT_WORKFLOW_CONFIGURATIONS, WorkflowConfigurations } from "@/types/workflow-configurations";
|
import {
|
||||||
|
resolveWorkflowConfigurations,
|
||||||
|
type WorkflowConfigurationDefaults,
|
||||||
|
type WorkflowConfigurations,
|
||||||
|
} from "@/types/workflow-configurations";
|
||||||
|
|
||||||
// Pull a WorkflowError[] out of any validate-shaped payload — works whether
|
// Pull a WorkflowError[] out of any validate-shaped payload — works whether
|
||||||
// the body is the raw `{ is_valid, errors }` (validate success-with-errors)
|
// the body is the raw `{ is_valid, errors }` (validate success-with-errors)
|
||||||
|
|
@ -111,6 +116,10 @@ export const useWorkflowState = ({
|
||||||
}: UseWorkflowStateProps) => {
|
}: UseWorkflowStateProps) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const rfInstance = useRef<ReactFlowInstance<FlowNode, FlowEdge> | null>(null);
|
const rfInstance = useRef<ReactFlowInstance<FlowNode, FlowEdge> | null>(null);
|
||||||
|
const [workflowConfigurationDefaults, setWorkflowConfigurationDefaults] =
|
||||||
|
useState<WorkflowConfigurationDefaults | null>(null);
|
||||||
|
const [workflowConfigurationDefaultsLoaded, setWorkflowConfigurationDefaultsLoaded] =
|
||||||
|
useState(false);
|
||||||
|
|
||||||
// Spec catalog. Workflow init waits on this to populate defaults; node
|
// Spec catalog. Workflow init waits on this to populate defaults; node
|
||||||
// creation looks up per-type schemas through it.
|
// creation looks up per-type schemas through it.
|
||||||
|
|
@ -149,10 +158,44 @@ export const useWorkflowState = ({
|
||||||
const canUndo = useWorkflowStore((state) => state.canUndo());
|
const canUndo = useWorkflowStore((state) => state.canUndo());
|
||||||
const canRedo = useWorkflowStore((state) => state.canRedo());
|
const canRedo = useWorkflowStore((state) => state.canRedo());
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let cancelled = false;
|
||||||
|
|
||||||
|
const loadWorkflowConfigurationDefaults = async () => {
|
||||||
|
try {
|
||||||
|
const response = await getDefaultConfigurationsApiV1UserConfigurationsDefaultsGet();
|
||||||
|
if (cancelled) return;
|
||||||
|
|
||||||
|
if (response.error || !response.data?.workflow_configurations) {
|
||||||
|
logger.error(
|
||||||
|
`Failed to load workflow configuration defaults: ${JSON.stringify(response.error)}`,
|
||||||
|
);
|
||||||
|
setWorkflowConfigurationDefaults(null);
|
||||||
|
} else {
|
||||||
|
setWorkflowConfigurationDefaults(response.data.workflow_configurations);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if (cancelled) return;
|
||||||
|
logger.error(`Failed to load workflow configuration defaults: ${error}`);
|
||||||
|
setWorkflowConfigurationDefaults(null);
|
||||||
|
} finally {
|
||||||
|
if (!cancelled) {
|
||||||
|
setWorkflowConfigurationDefaultsLoaded(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
loadWorkflowConfigurationDefaults();
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
cancelled = true;
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
// Initialize workflow on mount. Waits for the spec catalog so defaults
|
// Initialize workflow on mount. Waits for the spec catalog so defaults
|
||||||
// (allow_interrupt, prompt placeholders, etc.) come from one source.
|
// (allow_interrupt, prompt placeholders, etc.) come from one source.
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (specsLoading) return;
|
if (specsLoading || !workflowConfigurationDefaultsLoaded) return;
|
||||||
|
|
||||||
const startSpec = bySpecName.get(NodeType.START_CALL);
|
const startSpec = bySpecName.get(NodeType.START_CALL);
|
||||||
const fallbackStartNodes: FlowNode[] = startSpec
|
const fallbackStartNodes: FlowNode[] = startSpec
|
||||||
|
|
@ -176,16 +219,21 @@ export const useWorkflowState = ({
|
||||||
})
|
})
|
||||||
: fallbackStartNodes;
|
: fallbackStartNodes;
|
||||||
|
|
||||||
|
const resolvedInitialWorkflowConfigurations = resolveWorkflowConfigurations(
|
||||||
|
initialWorkflowConfigurations,
|
||||||
|
workflowConfigurationDefaults,
|
||||||
|
);
|
||||||
|
|
||||||
initializeWorkflow(
|
initializeWorkflow(
|
||||||
workflowId,
|
workflowId,
|
||||||
initialWorkflowName,
|
initialWorkflowName,
|
||||||
initialNodes,
|
initialNodes,
|
||||||
initialFlow?.edges ?? [],
|
initialFlow?.edges ?? [],
|
||||||
initialTemplateContextVariables,
|
initialTemplateContextVariables,
|
||||||
initialWorkflowConfigurations,
|
resolvedInitialWorkflowConfigurations,
|
||||||
initialWorkflowConfigurations?.dictionary ?? ''
|
resolvedInitialWorkflowConfigurations.dictionary ?? ''
|
||||||
);
|
);
|
||||||
}, [workflowId, initialWorkflowName, initialFlow?.nodes, initialFlow?.edges, initialTemplateContextVariables, initialWorkflowConfigurations, initializeWorkflow, specsLoading, bySpecName]);
|
}, [workflowId, initialWorkflowName, initialFlow?.nodes, initialFlow?.edges, initialTemplateContextVariables, initialWorkflowConfigurations, initializeWorkflow, specsLoading, bySpecName, workflowConfigurationDefaultsLoaded, workflowConfigurationDefaults]);
|
||||||
|
|
||||||
// Set up keyboard shortcuts for undo/redo
|
// Set up keyboard shortcuts for undo/redo
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -515,9 +563,12 @@ export const useWorkflowState = ({
|
||||||
throw new Error(msg);
|
throw new Error(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
const savedConfigurations = response.data?.workflow_configurations
|
const savedConfigurations = resolveWorkflowConfigurations(
|
||||||
? (response.data.workflow_configurations as WorkflowConfigurations)
|
response.data?.workflow_configurations
|
||||||
: configurationsWithDictionary;
|
? (response.data.workflow_configurations as Partial<WorkflowConfigurations>)
|
||||||
|
: configurationsWithDictionary,
|
||||||
|
workflowConfigurationDefaults,
|
||||||
|
);
|
||||||
setWorkflowConfigurations(savedConfigurations);
|
setWorkflowConfigurations(savedConfigurations);
|
||||||
// Set name directly in the store to avoid setWorkflowName which marks isDirty: true
|
// Set name directly in the store to avoid setWorkflowName which marks isDirty: true
|
||||||
useWorkflowStore.setState({ workflowName: newWorkflowName });
|
useWorkflowStore.setState({ workflowName: newWorkflowName });
|
||||||
|
|
@ -526,12 +577,14 @@ export const useWorkflowState = ({
|
||||||
logger.error(`Error saving workflow configurations: ${error}`);
|
logger.error(`Error saving workflow configurations: ${error}`);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}, [workflowId, user, setWorkflowConfigurations]);
|
}, [workflowId, user, setWorkflowConfigurations, workflowConfigurationDefaults]);
|
||||||
|
|
||||||
// Save dictionary
|
// Save dictionary
|
||||||
const saveDictionary = useCallback(async (newDictionary: string) => {
|
const saveDictionary = useCallback(async (newDictionary: string) => {
|
||||||
if (!user) return;
|
if (!user) return;
|
||||||
const currentConfigurations = useWorkflowStore.getState().workflowConfigurations ?? DEFAULT_WORKFLOW_CONFIGURATIONS;
|
const currentConfigurations =
|
||||||
|
useWorkflowStore.getState().workflowConfigurations
|
||||||
|
?? resolveWorkflowConfigurations(null, workflowConfigurationDefaults);
|
||||||
const updatedConfigurations: WorkflowConfigurations = { ...currentConfigurations, dictionary: newDictionary };
|
const updatedConfigurations: WorkflowConfigurations = { ...currentConfigurations, dictionary: newDictionary };
|
||||||
try {
|
try {
|
||||||
await updateWorkflowApiV1WorkflowWorkflowIdPut({
|
await updateWorkflowApiV1WorkflowWorkflowIdPut({
|
||||||
|
|
@ -550,7 +603,7 @@ export const useWorkflowState = ({
|
||||||
logger.error(`Error saving dictionary: ${error}`);
|
logger.error(`Error saving dictionary: ${error}`);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}, [workflowId, workflowName, user, setDictionary, setWorkflowConfigurations]);
|
}, [workflowId, workflowName, user, setDictionary, setWorkflowConfigurations, workflowConfigurationDefaults]);
|
||||||
|
|
||||||
// Update rfInstance when it changes
|
// Update rfInstance when it changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ import SpinLoader from '@/components/SpinLoader';
|
||||||
import { PostHogEvent } from '@/constants/posthog-events';
|
import { PostHogEvent } from '@/constants/posthog-events';
|
||||||
import { useAuth } from '@/lib/auth';
|
import { useAuth } from '@/lib/auth';
|
||||||
import logger from '@/lib/logger';
|
import logger from '@/lib/logger';
|
||||||
import { DEFAULT_WORKFLOW_CONFIGURATIONS, WorkflowConfigurations } from '@/types/workflow-configurations';
|
import { WorkflowConfigurations } from '@/types/workflow-configurations';
|
||||||
|
|
||||||
import WorkflowLayout from '../WorkflowLayout';
|
import WorkflowLayout from '../WorkflowLayout';
|
||||||
|
|
||||||
|
|
@ -92,7 +92,11 @@ export default function WorkflowDetailPage() {
|
||||||
viewport: { x: 0, y: 0, zoom: 0 }
|
viewport: { x: 0, y: 0, zoom: 0 }
|
||||||
}}
|
}}
|
||||||
initialTemplateContextVariables={workflow.template_context_variables as Record<string, string> || {}}
|
initialTemplateContextVariables={workflow.template_context_variables as Record<string, string> || {}}
|
||||||
initialWorkflowConfigurations={(workflow.workflow_configurations as WorkflowConfigurations) || DEFAULT_WORKFLOW_CONFIGURATIONS}
|
initialWorkflowConfigurations={
|
||||||
|
workflow.workflow_configurations
|
||||||
|
? (workflow.workflow_configurations as WorkflowConfigurations)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
initialVersionNumber={workflow.version_number ?? null}
|
initialVersionNumber={workflow.version_number ?? null}
|
||||||
initialVersionStatus={workflow.version_status ?? null}
|
initialVersionStatus={workflow.version_status ?? null}
|
||||||
user={stableUser}
|
user={stableUser}
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,6 @@ import {
|
||||||
DEFAULT_PROVISIONAL_VAD_PAUSE_SECS,
|
DEFAULT_PROVISIONAL_VAD_PAUSE_SECS,
|
||||||
DEFAULT_TURN_START_MIN_WORDS,
|
DEFAULT_TURN_START_MIN_WORDS,
|
||||||
DEFAULT_VOICEMAIL_DETECTION_CONFIGURATION,
|
DEFAULT_VOICEMAIL_DETECTION_CONFIGURATION,
|
||||||
DEFAULT_WORKFLOW_CONFIGURATIONS,
|
|
||||||
TURN_START_STRATEGY_OPTIONS,
|
TURN_START_STRATEGY_OPTIONS,
|
||||||
type TurnStartStrategy,
|
type TurnStartStrategy,
|
||||||
type TurnStopStrategy,
|
type TurnStopStrategy,
|
||||||
|
|
@ -63,11 +62,6 @@ import { useWorkflowState } from "../hooks/useWorkflowState";
|
||||||
// Constants
|
// Constants
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
const DEFAULT_AMBIENT_NOISE_CONFIG: AmbientNoiseConfiguration = {
|
|
||||||
enabled: false,
|
|
||||||
volume: 0.3,
|
|
||||||
};
|
|
||||||
|
|
||||||
const DEFAULT_VOICEMAIL_SYSTEM_PROMPT = `You are a voicemail detection classifier for an OUTBOUND calling system. A bot has called a phone number and you need to determine if a human answered or if the call went to voicemail based on the provided text.
|
const DEFAULT_VOICEMAIL_SYSTEM_PROMPT = `You are a voicemail detection classifier for an OUTBOUND calling system. A bot has called a phone number and you need to determine if a human answered or if the call went to voicemail based on the provided text.
|
||||||
|
|
||||||
HUMAN ANSWERED - LIVE CONVERSATION (respond "CONVERSATION"):
|
HUMAN ANSWERED - LIVE CONVERSATION (respond "CONVERSATION"):
|
||||||
|
|
@ -279,25 +273,25 @@ function GeneralSection({
|
||||||
}) {
|
}) {
|
||||||
const [name, setName] = useState(workflowName);
|
const [name, setName] = useState(workflowName);
|
||||||
const [ambientNoiseConfig, setAmbientNoiseConfig] = useState<AmbientNoiseConfiguration>(
|
const [ambientNoiseConfig, setAmbientNoiseConfig] = useState<AmbientNoiseConfiguration>(
|
||||||
workflowConfigurations.ambient_noise_configuration || DEFAULT_AMBIENT_NOISE_CONFIG,
|
workflowConfigurations.ambient_noise_configuration,
|
||||||
);
|
);
|
||||||
const [maxCallDuration, setMaxCallDuration] = useState(workflowConfigurations.max_call_duration || 600);
|
const [maxCallDuration, setMaxCallDuration] = useState(workflowConfigurations.max_call_duration);
|
||||||
const [maxUserIdleTimeout, setMaxUserIdleTimeout] = useState(workflowConfigurations.max_user_idle_timeout || 10);
|
const [maxUserIdleTimeout, setMaxUserIdleTimeout] = useState(workflowConfigurations.max_user_idle_timeout);
|
||||||
const [smartTurnStopSecs, setSmartTurnStopSecs] = useState(workflowConfigurations.smart_turn_stop_secs || 2);
|
const [smartTurnStopSecs, setSmartTurnStopSecs] = useState(workflowConfigurations.smart_turn_stop_secs);
|
||||||
const [turnStartStrategy, setTurnStartStrategy] = useState<TurnStartStrategy>(
|
const [turnStartStrategy, setTurnStartStrategy] = useState<TurnStartStrategy>(
|
||||||
workflowConfigurations.turn_start_strategy || "default",
|
workflowConfigurations.turn_start_strategy,
|
||||||
);
|
);
|
||||||
const [turnStartMinWords, setTurnStartMinWords] = useState(
|
const [turnStartMinWords, setTurnStartMinWords] = useState(
|
||||||
workflowConfigurations.turn_start_min_words || DEFAULT_TURN_START_MIN_WORDS,
|
workflowConfigurations.turn_start_min_words,
|
||||||
);
|
);
|
||||||
const [provisionalVadPauseSecs, setProvisionalVadPauseSecs] = useState(
|
const [provisionalVadPauseSecs, setProvisionalVadPauseSecs] = useState(
|
||||||
workflowConfigurations.provisional_vad_pause_secs || DEFAULT_PROVISIONAL_VAD_PAUSE_SECS,
|
workflowConfigurations.provisional_vad_pause_secs,
|
||||||
);
|
);
|
||||||
const [turnStopStrategy, setTurnStopStrategy] = useState<TurnStopStrategy>(
|
const [turnStopStrategy, setTurnStopStrategy] = useState<TurnStopStrategy>(
|
||||||
workflowConfigurations.turn_stop_strategy || "transcription",
|
workflowConfigurations.turn_stop_strategy,
|
||||||
);
|
);
|
||||||
const [contextCompactionEnabled, setContextCompactionEnabled] = useState(
|
const [contextCompactionEnabled, setContextCompactionEnabled] = useState(
|
||||||
workflowConfigurations.context_compaction_enabled ?? false,
|
workflowConfigurations.context_compaction_enabled,
|
||||||
);
|
);
|
||||||
const [isSaving, setIsSaving] = useState(false);
|
const [isSaving, setIsSaving] = useState(false);
|
||||||
const [isUploadingAudio, setIsUploadingAudio] = useState(false);
|
const [isUploadingAudio, setIsUploadingAudio] = useState(false);
|
||||||
|
|
@ -309,18 +303,18 @@ function GeneralSection({
|
||||||
);
|
);
|
||||||
|
|
||||||
const isDirty = useMemo(() => {
|
const isDirty = useMemo(() => {
|
||||||
const initAmbient = workflowConfigurations.ambient_noise_configuration || DEFAULT_AMBIENT_NOISE_CONFIG;
|
const initAmbient = workflowConfigurations.ambient_noise_configuration;
|
||||||
return (
|
return (
|
||||||
name !== workflowName ||
|
name !== workflowName ||
|
||||||
JSON.stringify(ambientNoiseConfig) !== JSON.stringify(initAmbient) ||
|
JSON.stringify(ambientNoiseConfig) !== JSON.stringify(initAmbient) ||
|
||||||
maxCallDuration !== (workflowConfigurations.max_call_duration || 600) ||
|
maxCallDuration !== workflowConfigurations.max_call_duration ||
|
||||||
maxUserIdleTimeout !== (workflowConfigurations.max_user_idle_timeout || 10) ||
|
maxUserIdleTimeout !== workflowConfigurations.max_user_idle_timeout ||
|
||||||
smartTurnStopSecs !== (workflowConfigurations.smart_turn_stop_secs || 2) ||
|
smartTurnStopSecs !== workflowConfigurations.smart_turn_stop_secs ||
|
||||||
turnStartStrategy !== (workflowConfigurations.turn_start_strategy || "default") ||
|
turnStartStrategy !== workflowConfigurations.turn_start_strategy ||
|
||||||
turnStartMinWords !== (workflowConfigurations.turn_start_min_words || DEFAULT_TURN_START_MIN_WORDS) ||
|
turnStartMinWords !== workflowConfigurations.turn_start_min_words ||
|
||||||
provisionalVadPauseSecs !== (workflowConfigurations.provisional_vad_pause_secs || DEFAULT_PROVISIONAL_VAD_PAUSE_SECS) ||
|
provisionalVadPauseSecs !== workflowConfigurations.provisional_vad_pause_secs ||
|
||||||
turnStopStrategy !== (workflowConfigurations.turn_stop_strategy || "transcription") ||
|
turnStopStrategy !== workflowConfigurations.turn_stop_strategy ||
|
||||||
contextCompactionEnabled !== (workflowConfigurations.context_compaction_enabled ?? false)
|
contextCompactionEnabled !== workflowConfigurations.context_compaction_enabled
|
||||||
);
|
);
|
||||||
}, [name, workflowName, ambientNoiseConfig, maxCallDuration, maxUserIdleTimeout, smartTurnStopSecs, turnStartStrategy, turnStartMinWords, provisionalVadPauseSecs, turnStopStrategy, contextCompactionEnabled, workflowConfigurations]);
|
}, [name, workflowName, ambientNoiseConfig, maxCallDuration, maxUserIdleTimeout, smartTurnStopSecs, turnStartStrategy, turnStartMinWords, provisionalVadPauseSecs, turnStopStrategy, contextCompactionEnabled, workflowConfigurations]);
|
||||||
|
|
||||||
|
|
@ -611,6 +605,18 @@ function GeneralSection({
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Separator />
|
||||||
|
|
||||||
|
{/* Interruption */}
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div>
|
||||||
|
<h3 className="text-sm font-medium">Interruption</h3>
|
||||||
|
<p className="text-xs text-muted-foreground mt-0.5">
|
||||||
|
Configure when user speech should interrupt the agent while it is speaking.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label htmlFor="turn_start_strategy" className="text-xs">Interruption Strategy</Label>
|
<Label htmlFor="turn_start_strategy" className="text-xs">Interruption Strategy</Label>
|
||||||
<Select
|
<Select
|
||||||
|
|
@ -1428,7 +1434,11 @@ function WorkflowSettingsInner({
|
||||||
);
|
);
|
||||||
|
|
||||||
const initialWorkflowConfigurations = useMemo(
|
const initialWorkflowConfigurations = useMemo(
|
||||||
() => (workflow.workflow_configurations as WorkflowConfigurations) || DEFAULT_WORKFLOW_CONFIGURATIONS,
|
() => (
|
||||||
|
workflow.workflow_configurations
|
||||||
|
? (workflow.workflow_configurations as WorkflowConfigurations)
|
||||||
|
: undefined
|
||||||
|
),
|
||||||
[workflow],
|
[workflow],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import { create } from 'zustand';
|
||||||
|
|
||||||
import { WorkflowError } from '@/client/types.gen';
|
import { WorkflowError } from '@/client/types.gen';
|
||||||
import { FlowEdge, FlowNode } from '@/components/flow/types';
|
import { FlowEdge, FlowNode } from '@/components/flow/types';
|
||||||
import { DEFAULT_WORKFLOW_CONFIGURATIONS, WorkflowConfigurations } from '@/types/workflow-configurations';
|
import { WorkflowConfigurations } from '@/types/workflow-configurations';
|
||||||
|
|
||||||
interface HistoryState {
|
interface HistoryState {
|
||||||
nodes: FlowNode[];
|
nodes: FlowNode[];
|
||||||
|
|
@ -117,7 +117,7 @@ export const useWorkflowStore = create<WorkflowStore>((set, get) => ({
|
||||||
rfInstance: null,
|
rfInstance: null,
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
initializeWorkflow: (workflowId, workflowName, nodes, edges, templateContextVariables = {}, workflowConfigurations = DEFAULT_WORKFLOW_CONFIGURATIONS, dictionary = '') => {
|
initializeWorkflow: (workflowId, workflowName, nodes, edges, templateContextVariables = {}, workflowConfigurations = null, dictionary = '') => {
|
||||||
const initialHistory: HistoryState = { nodes, edges, workflowName };
|
const initialHistory: HistoryState = { nodes, edges, workflowName };
|
||||||
set({
|
set({
|
||||||
workflowId,
|
workflowId,
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -186,6 +186,21 @@ export type ActiveCallsResponse = {
|
||||||
active_calls: number;
|
active_calls: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AmbientNoiseConfigurationDefaults
|
||||||
|
*/
|
||||||
|
export type AmbientNoiseConfigurationDefaults = {
|
||||||
|
/**
|
||||||
|
* Enabled
|
||||||
|
*/
|
||||||
|
enabled?: boolean;
|
||||||
|
/**
|
||||||
|
* Volume
|
||||||
|
*/
|
||||||
|
volume?: number;
|
||||||
|
[key: string]: unknown;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AmbientNoiseUploadRequest
|
* AmbientNoiseUploadRequest
|
||||||
*/
|
*/
|
||||||
|
|
@ -1878,6 +1893,7 @@ export type DefaultConfigurationsResponse = {
|
||||||
default_providers: {
|
default_providers: {
|
||||||
[key: string]: string;
|
[key: string]: string;
|
||||||
};
|
};
|
||||||
|
workflow_configurations: WorkflowConfigurationDefaults;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -6523,6 +6539,50 @@ export type VonageConfigurationResponse = {
|
||||||
*/
|
*/
|
||||||
export type WebhookCredentialType = 'none' | 'api_key' | 'bearer_token' | 'basic_auth' | 'custom_header';
|
export type WebhookCredentialType = 'none' | 'api_key' | 'bearer_token' | 'basic_auth' | 'custom_header';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WorkflowConfigurationDefaults
|
||||||
|
*/
|
||||||
|
export type WorkflowConfigurationDefaults = {
|
||||||
|
ambient_noise_configuration?: AmbientNoiseConfigurationDefaults;
|
||||||
|
/**
|
||||||
|
* Max Call Duration
|
||||||
|
*/
|
||||||
|
max_call_duration?: number;
|
||||||
|
/**
|
||||||
|
* Max User Idle Timeout
|
||||||
|
*/
|
||||||
|
max_user_idle_timeout?: number;
|
||||||
|
/**
|
||||||
|
* Smart Turn Stop Secs
|
||||||
|
*/
|
||||||
|
smart_turn_stop_secs?: number;
|
||||||
|
/**
|
||||||
|
* Turn Start Strategy
|
||||||
|
*/
|
||||||
|
turn_start_strategy?: 'default' | 'min_words' | 'provisional_vad';
|
||||||
|
/**
|
||||||
|
* Turn Start Min Words
|
||||||
|
*/
|
||||||
|
turn_start_min_words?: number;
|
||||||
|
/**
|
||||||
|
* Provisional Vad Pause Secs
|
||||||
|
*/
|
||||||
|
provisional_vad_pause_secs?: number;
|
||||||
|
/**
|
||||||
|
* Turn Stop Strategy
|
||||||
|
*/
|
||||||
|
turn_stop_strategy?: 'transcription' | 'turn_analyzer';
|
||||||
|
/**
|
||||||
|
* Dictionary
|
||||||
|
*/
|
||||||
|
dictionary?: string;
|
||||||
|
/**
|
||||||
|
* Context Compaction Enabled
|
||||||
|
*/
|
||||||
|
context_compaction_enabled?: boolean;
|
||||||
|
[key: string]: unknown;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* WorkflowCountResponse
|
* WorkflowCountResponse
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -227,7 +227,7 @@ export function ServiceConfigurationForm({
|
||||||
console.error("Failed to fetch configurations");
|
console.error("Failed to fetch configurations");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
defaultsData = response.data as ServiceConfigurationDefaults;
|
defaultsData = response.data as unknown as ServiceConfigurationDefaults;
|
||||||
}
|
}
|
||||||
|
|
||||||
const realtimeSchemas = (defaultsData.realtime || {}) as Record<string, ProviderSchema>;
|
const realtimeSchemas = (defaultsData.realtime || {}) as Record<string, ProviderSchema>;
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,24 @@
|
||||||
import type { OrganizationAiModelConfigurationV2 } from "@/client/types.gen";
|
import type {
|
||||||
|
AmbientNoiseConfigurationDefaults,
|
||||||
|
OrganizationAiModelConfigurationV2,
|
||||||
|
WorkflowConfigurationDefaults as GeneratedWorkflowConfigurationDefaults,
|
||||||
|
} from "@/client/types.gen";
|
||||||
|
|
||||||
export interface AmbientNoiseConfiguration {
|
export type WorkflowConfigurationDefaults = GeneratedWorkflowConfigurationDefaults;
|
||||||
|
|
||||||
|
export type AmbientNoiseConfiguration = Omit<
|
||||||
|
AmbientNoiseConfigurationDefaults,
|
||||||
|
"enabled" | "volume"
|
||||||
|
> & {
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
volume: number;
|
volume: number;
|
||||||
storage_key?: string;
|
storage_key?: string;
|
||||||
storage_backend?: string;
|
storage_backend?: string;
|
||||||
original_filename?: string;
|
original_filename?: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
export type TurnStopStrategy = 'transcription' | 'turn_analyzer';
|
export type TurnStopStrategy = NonNullable<GeneratedWorkflowConfigurationDefaults["turn_stop_strategy"]>;
|
||||||
export type TurnStartStrategy = 'default' | 'min_words' | 'provisional_vad';
|
export type TurnStartStrategy = NonNullable<GeneratedWorkflowConfigurationDefaults["turn_start_strategy"]>;
|
||||||
export const DEFAULT_TURN_START_MIN_WORDS = 3;
|
export const DEFAULT_TURN_START_MIN_WORDS = 3;
|
||||||
export const DEFAULT_PROVISIONAL_VAD_PAUSE_SECS = 1.5;
|
export const DEFAULT_PROVISIONAL_VAD_PAUSE_SECS = 1.5;
|
||||||
|
|
||||||
|
|
@ -81,7 +90,21 @@ export interface ModelOverrides {
|
||||||
is_realtime?: boolean;
|
is_realtime?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface WorkflowConfigurations {
|
type WorkflowConfigurationBase = Omit<
|
||||||
|
GeneratedWorkflowConfigurationDefaults,
|
||||||
|
| "ambient_noise_configuration"
|
||||||
|
| "max_call_duration"
|
||||||
|
| "max_user_idle_timeout"
|
||||||
|
| "smart_turn_stop_secs"
|
||||||
|
| "turn_start_strategy"
|
||||||
|
| "turn_start_min_words"
|
||||||
|
| "provisional_vad_pause_secs"
|
||||||
|
| "turn_stop_strategy"
|
||||||
|
| "dictionary"
|
||||||
|
| "context_compaction_enabled"
|
||||||
|
>;
|
||||||
|
|
||||||
|
export type WorkflowConfigurations = WorkflowConfigurationBase & {
|
||||||
ambient_noise_configuration: AmbientNoiseConfiguration;
|
ambient_noise_configuration: AmbientNoiseConfiguration;
|
||||||
max_call_duration: number; // Maximum call duration in seconds
|
max_call_duration: number; // Maximum call duration in seconds
|
||||||
max_user_idle_timeout: number; // Maximum user idle time in seconds
|
max_user_idle_timeout: number; // Maximum user idle time in seconds
|
||||||
|
|
@ -92,23 +115,76 @@ export interface WorkflowConfigurations {
|
||||||
turn_stop_strategy: TurnStopStrategy; // Strategy for detecting end of user turn
|
turn_stop_strategy: TurnStopStrategy; // Strategy for detecting end of user turn
|
||||||
dictionary?: string; // Comma-separated words for voice agent to listen for
|
dictionary?: string; // Comma-separated words for voice agent to listen for
|
||||||
voicemail_detection?: VoicemailDetectionConfiguration;
|
voicemail_detection?: VoicemailDetectionConfiguration;
|
||||||
context_compaction_enabled?: boolean; // Summarize context on node transitions to remove stale tool calls
|
context_compaction_enabled: boolean; // Summarize context on node transitions to remove stale tool calls
|
||||||
model_overrides?: ModelOverrides; // Per-workflow model configuration overrides
|
model_overrides?: ModelOverrides; // Per-workflow model configuration overrides
|
||||||
model_configuration_v2_override?: OrganizationAiModelConfigurationV2; // Full v2 model configuration override
|
model_configuration_v2_override?: OrganizationAiModelConfigurationV2; // Full v2 model configuration override
|
||||||
[key: string]: unknown; // Allow additional properties for future configurations
|
[key: string]: unknown; // Allow additional properties for future configurations
|
||||||
}
|
};
|
||||||
|
|
||||||
export const DEFAULT_WORKFLOW_CONFIGURATIONS: WorkflowConfigurations = {
|
const FALLBACK_WORKFLOW_CONFIGURATIONS: WorkflowConfigurations = {
|
||||||
ambient_noise_configuration: {
|
ambient_noise_configuration: {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
volume: 0.3
|
volume: 0.3
|
||||||
},
|
},
|
||||||
max_call_duration: 600, // 10 minutes
|
max_call_duration: 300,
|
||||||
max_user_idle_timeout: 10, // 10 seconds
|
max_user_idle_timeout: 10, // 10 seconds
|
||||||
smart_turn_stop_secs: 2, // 2 seconds
|
smart_turn_stop_secs: 2, // 2 seconds
|
||||||
turn_start_strategy: 'default', // Default to platform-chosen user turn start detection
|
turn_start_strategy: 'default', // Default to platform-chosen user turn start detection
|
||||||
turn_start_min_words: DEFAULT_TURN_START_MIN_WORDS,
|
turn_start_min_words: DEFAULT_TURN_START_MIN_WORDS,
|
||||||
provisional_vad_pause_secs: DEFAULT_PROVISIONAL_VAD_PAUSE_SECS,
|
provisional_vad_pause_secs: DEFAULT_PROVISIONAL_VAD_PAUSE_SECS,
|
||||||
turn_stop_strategy: 'transcription', // Default to transcription-based detection
|
turn_stop_strategy: 'transcription', // Default to transcription-based detection
|
||||||
dictionary: ''
|
dictionary: '',
|
||||||
|
context_compaction_enabled: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export function resolveWorkflowConfigurations(
|
||||||
|
configurations?: Partial<WorkflowConfigurations> | null,
|
||||||
|
defaults?: WorkflowConfigurationDefaults | null,
|
||||||
|
): WorkflowConfigurations {
|
||||||
|
return {
|
||||||
|
...FALLBACK_WORKFLOW_CONFIGURATIONS,
|
||||||
|
...defaults,
|
||||||
|
...configurations,
|
||||||
|
ambient_noise_configuration: {
|
||||||
|
...FALLBACK_WORKFLOW_CONFIGURATIONS.ambient_noise_configuration,
|
||||||
|
...defaults?.ambient_noise_configuration,
|
||||||
|
...configurations?.ambient_noise_configuration,
|
||||||
|
},
|
||||||
|
max_call_duration:
|
||||||
|
configurations?.max_call_duration
|
||||||
|
?? defaults?.max_call_duration
|
||||||
|
?? FALLBACK_WORKFLOW_CONFIGURATIONS.max_call_duration,
|
||||||
|
max_user_idle_timeout:
|
||||||
|
configurations?.max_user_idle_timeout
|
||||||
|
?? defaults?.max_user_idle_timeout
|
||||||
|
?? FALLBACK_WORKFLOW_CONFIGURATIONS.max_user_idle_timeout,
|
||||||
|
smart_turn_stop_secs:
|
||||||
|
configurations?.smart_turn_stop_secs
|
||||||
|
?? defaults?.smart_turn_stop_secs
|
||||||
|
?? FALLBACK_WORKFLOW_CONFIGURATIONS.smart_turn_stop_secs,
|
||||||
|
turn_start_strategy:
|
||||||
|
configurations?.turn_start_strategy
|
||||||
|
?? defaults?.turn_start_strategy
|
||||||
|
?? FALLBACK_WORKFLOW_CONFIGURATIONS.turn_start_strategy,
|
||||||
|
turn_start_min_words:
|
||||||
|
configurations?.turn_start_min_words
|
||||||
|
?? defaults?.turn_start_min_words
|
||||||
|
?? FALLBACK_WORKFLOW_CONFIGURATIONS.turn_start_min_words,
|
||||||
|
provisional_vad_pause_secs:
|
||||||
|
configurations?.provisional_vad_pause_secs
|
||||||
|
?? defaults?.provisional_vad_pause_secs
|
||||||
|
?? FALLBACK_WORKFLOW_CONFIGURATIONS.provisional_vad_pause_secs,
|
||||||
|
turn_stop_strategy:
|
||||||
|
configurations?.turn_stop_strategy
|
||||||
|
?? defaults?.turn_stop_strategy
|
||||||
|
?? FALLBACK_WORKFLOW_CONFIGURATIONS.turn_stop_strategy,
|
||||||
|
dictionary:
|
||||||
|
configurations?.dictionary
|
||||||
|
?? defaults?.dictionary
|
||||||
|
?? FALLBACK_WORKFLOW_CONFIGURATIONS.dictionary,
|
||||||
|
context_compaction_enabled:
|
||||||
|
configurations?.context_compaction_enabled
|
||||||
|
?? defaults?.context_compaction_enabled
|
||||||
|
?? FALLBACK_WORKFLOW_CONFIGURATIONS.context_compaction_enabled,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue