From ceb01a16a3f5ab9e7356a07e34dead382cfc099a Mon Sep 17 00:00:00 2001
From: Abhishek
Date: Sat, 4 Jul 2026 18:37:50 +0530
Subject: [PATCH] feat: client gen default configurations (#499)
---
api/routes/user.py | 10 +-
api/schemas/workflow_configurations.py | 44 +++++++++
.../pipeline_engine_callbacks_processor.py | 3 +-
api/services/pipecat/run_pipeline.py | 16 +--
.../workflow/[workflowId]/RenderWorkflow.tsx | 2 +-
.../components/ConfigurationsDialog.tsx | 54 +++++-----
.../[workflowId]/hooks/useWorkflowState.ts | 77 ++++++++++++---
ui/src/app/workflow/[workflowId]/page.tsx | 8 +-
.../workflow/[workflowId]/settings/page.tsx | 60 +++++++-----
.../[workflowId]/stores/workflowStore.ts | 4 +-
ui/src/client/index.ts | 2 +-
ui/src/client/types.gen.ts | 60 ++++++++++++
.../components/ServiceConfigurationForm.tsx | 2 +-
ui/src/types/workflow-configurations.ts | 98 ++++++++++++++++---
14 files changed, 353 insertions(+), 87 deletions(-)
create mode 100644 api/schemas/workflow_configurations.py
diff --git a/api/routes/user.py b/api/routes/user.py
index 4a2caa4b..c9c443b3 100644
--- a/api/routes/user.py
+++ b/api/routes/user.py
@@ -10,6 +10,10 @@ from api.db.models import (
UserModel,
)
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.configuration.ai_model_configuration import (
get_resolved_ai_model_configuration,
@@ -40,13 +44,14 @@ class AuthUserResponse(TypedDict):
is_superuser: bool
-class DefaultConfigurationsResponse(TypedDict):
+class DefaultConfigurationsResponse(BaseModel):
llm: dict[str, dict]
tts: dict[str, dict]
stt: dict[str, dict]
embeddings: dict[str, dict]
realtime: dict[str, dict]
default_providers: dict[str, str]
+ workflow_configurations: WorkflowConfigurationDefaults
@router.get("/configurations/defaults")
@@ -73,8 +78,9 @@ async def get_default_configurations() -> DefaultConfigurationsResponse:
for provider, model_cls in REGISTRY[ServiceType.REALTIME].items()
},
"default_providers": DEFAULT_SERVICE_PROVIDERS,
+ "workflow_configurations": get_default_workflow_configurations(),
}
- return configurations
+ return DefaultConfigurationsResponse(**configurations)
@router.get("/auth/user")
diff --git a/api/schemas/workflow_configurations.py b/api/schemas/workflow_configurations.py
new file mode 100644
index 00000000..ae974c19
--- /dev/null
+++ b/api/schemas/workflow_configurations.py
@@ -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()
diff --git a/api/services/pipecat/pipeline_engine_callbacks_processor.py b/api/services/pipecat/pipeline_engine_callbacks_processor.py
index 8c048809..71fbe28b 100644
--- a/api/services/pipecat/pipeline_engine_callbacks_processor.py
+++ b/api/services/pipecat/pipeline_engine_callbacks_processor.py
@@ -3,6 +3,7 @@ from typing import Awaitable, Callable, Optional
from loguru import logger
+from api.schemas.workflow_configurations import DEFAULT_MAX_CALL_DURATION_SECONDS
from pipecat.frames.frames import (
Frame,
HeartbeatFrame,
@@ -23,7 +24,7 @@ class PipelineEngineCallbacksProcessor(FrameProcessor):
def __init__(
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,
generation_started_callback: Optional[Callable[[], Awaitable[None]]] = None,
llm_text_frame_callback: Optional[Callable[[str], Awaitable[None]]] = None,
diff --git a/api/services/pipecat/run_pipeline.py b/api/services/pipecat/run_pipeline.py
index 18dbeaa3..4bfedc7c 100644
--- a/api/services/pipecat/run_pipeline.py
+++ b/api/services/pipecat/run_pipeline.py
@@ -6,6 +6,14 @@ from loguru import logger
from api.db import db_client
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.integrations import (
IntegrationRuntimeContext,
@@ -100,10 +108,6 @@ ensure_tracing()
DEFAULT_USER_TURN_STOP_TIMEOUT = 5.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(
@@ -538,8 +542,8 @@ async def _run_pipeline_impl(
run_configs = run_definition.workflow_configurations or {}
# Extract configurations from the version's workflow_configurations
- max_call_duration_seconds = 300 # Default 5 minutes
- max_user_idle_timeout = 10.0 # Default 10 seconds
+ max_call_duration_seconds = DEFAULT_MAX_CALL_DURATION_SECONDS
+ max_user_idle_timeout = DEFAULT_MAX_USER_IDLE_TIMEOUT_SECONDS
keyterms = None # Dictionary words for STT boosting
if run_configs:
diff --git a/ui/src/app/workflow/[workflowId]/RenderWorkflow.tsx b/ui/src/app/workflow/[workflowId]/RenderWorkflow.tsx
index eed634b9..55638dc2 100644
--- a/ui/src/app/workflow/[workflowId]/RenderWorkflow.tsx
+++ b/ui/src/app/workflow/[workflowId]/RenderWorkflow.tsx
@@ -443,7 +443,7 @@ function RenderWorkflow({
const renameWorkflow = useCallback(async (newName: string) => {
// The header doesn't render the pencil until the page has mounted with
// 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.
if (!workflowConfigurations) {
throw new Error("Workflow configurations not loaded");
diff --git a/ui/src/app/workflow/[workflowId]/components/ConfigurationsDialog.tsx b/ui/src/app/workflow/[workflowId]/components/ConfigurationsDialog.tsx
index 4ee6a7db..2d288a61 100644
--- a/ui/src/app/workflow/[workflowId]/components/ConfigurationsDialog.tsx
+++ b/ui/src/app/workflow/[workflowId]/components/ConfigurationsDialog.tsx
@@ -10,6 +10,7 @@ import {
AmbientNoiseConfiguration,
DEFAULT_PROVISIONAL_VAD_PAUSE_SECS,
DEFAULT_TURN_START_MIN_WORDS,
+ resolveWorkflowConfigurations,
TURN_START_STRATEGY_OPTIONS,
TurnStartStrategy,
TurnStopStrategy,
@@ -24,11 +25,6 @@ interface ConfigurationsDialogProps {
onSave: (configurations: WorkflowConfigurations, workflowName: string) => Promise;
}
-const DEFAULT_AMBIENT_NOISE_CONFIG: AmbientNoiseConfiguration = {
- enabled: false,
- volume: 0.3,
-};
-
export const ConfigurationsDialog = ({
open,
onOpenChange,
@@ -36,33 +32,34 @@ export const ConfigurationsDialog = ({
workflowName,
onSave
}: ConfigurationsDialogProps) => {
+ const resolvedWorkflowConfigurations = resolveWorkflowConfigurations(workflowConfigurations);
const [name, setName] = useState(workflowName);
const [ambientNoiseConfig, setAmbientNoiseConfig] = useState(
- workflowConfigurations?.ambient_noise_configuration || DEFAULT_AMBIENT_NOISE_CONFIG
+ resolvedWorkflowConfigurations.ambient_noise_configuration
);
const [maxCallDuration, setMaxCallDuration] = useState(
- workflowConfigurations?.max_call_duration || 600 // Default 10 minutes
+ resolvedWorkflowConfigurations.max_call_duration
);
const [maxUserIdleTimeout, setMaxUserIdleTimeout] = useState(
- workflowConfigurations?.max_user_idle_timeout || 10 // Default 10 seconds
+ resolvedWorkflowConfigurations.max_user_idle_timeout
);
const [smartTurnStopSecs, setSmartTurnStopSecs] = useState(
- workflowConfigurations?.smart_turn_stop_secs || 2 // Default 2 seconds
+ resolvedWorkflowConfigurations.smart_turn_stop_secs
);
const [turnStartStrategy, setTurnStartStrategy] = useState(
- workflowConfigurations?.turn_start_strategy || 'default'
+ resolvedWorkflowConfigurations.turn_start_strategy
);
const [turnStartMinWords, setTurnStartMinWords] = useState(
- workflowConfigurations?.turn_start_min_words || DEFAULT_TURN_START_MIN_WORDS
+ resolvedWorkflowConfigurations.turn_start_min_words
);
const [provisionalVadPauseSecs, setProvisionalVadPauseSecs] = useState(
- workflowConfigurations?.provisional_vad_pause_secs || DEFAULT_PROVISIONAL_VAD_PAUSE_SECS
+ resolvedWorkflowConfigurations.provisional_vad_pause_secs
);
const [turnStopStrategy, setTurnStopStrategy] = useState(
- workflowConfigurations?.turn_stop_strategy || 'transcription'
+ resolvedWorkflowConfigurations.turn_stop_strategy
);
const [contextCompactionEnabled, setContextCompactionEnabled] = useState(
- workflowConfigurations?.context_compaction_enabled ?? false
+ resolvedWorkflowConfigurations.context_compaction_enabled
);
const [isSaving, setIsSaving] = useState(false);
const selectedTurnStartStrategy = TURN_START_STRATEGY_OPTIONS.find(
@@ -94,16 +91,17 @@ export const ConfigurationsDialog = ({
// Sync state with props when dialog opens
useEffect(() => {
if (open) {
+ const nextWorkflowConfigurations = resolveWorkflowConfigurations(workflowConfigurations);
setName(workflowName);
- setAmbientNoiseConfig(workflowConfigurations?.ambient_noise_configuration || DEFAULT_AMBIENT_NOISE_CONFIG);
- setMaxCallDuration(workflowConfigurations?.max_call_duration || 600);
- setMaxUserIdleTimeout(workflowConfigurations?.max_user_idle_timeout || 10);
- setSmartTurnStopSecs(workflowConfigurations?.smart_turn_stop_secs || 2);
- setTurnStartStrategy(workflowConfigurations?.turn_start_strategy || 'default');
- setTurnStartMinWords(workflowConfigurations?.turn_start_min_words || DEFAULT_TURN_START_MIN_WORDS);
- setProvisionalVadPauseSecs(workflowConfigurations?.provisional_vad_pause_secs || DEFAULT_PROVISIONAL_VAD_PAUSE_SECS);
- setTurnStopStrategy(workflowConfigurations?.turn_stop_strategy || 'transcription');
- setContextCompactionEnabled(workflowConfigurations?.context_compaction_enabled ?? false);
+ setAmbientNoiseConfig(nextWorkflowConfigurations.ambient_noise_configuration);
+ setMaxCallDuration(nextWorkflowConfigurations.max_call_duration);
+ setMaxUserIdleTimeout(nextWorkflowConfigurations.max_user_idle_timeout);
+ setSmartTurnStopSecs(nextWorkflowConfigurations.smart_turn_stop_secs);
+ setTurnStartStrategy(nextWorkflowConfigurations.turn_start_strategy);
+ setTurnStartMinWords(nextWorkflowConfigurations.turn_start_min_words);
+ setProvisionalVadPauseSecs(nextWorkflowConfigurations.provisional_vad_pause_secs);
+ setTurnStopStrategy(nextWorkflowConfigurations.turn_stop_strategy);
+ setContextCompactionEnabled(nextWorkflowConfigurations.context_compaction_enabled);
}
}, [open, workflowName, workflowConfigurations]);
@@ -244,6 +242,16 @@ export const ConfigurationsDialog = ({
)}
+
+
+ {/* Interruption Section */}
+
+
+
Interruption
+
+ Configure when user speech should interrupt the agent while it is speaking.
+
+