mirror of
https://github.com/dograh-hq/dograh.git
synced 2026-07-07 11:02:12 +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
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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<void>;
|
||||
}
|
||||
|
||||
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<string>(workflowName);
|
||||
const [ambientNoiseConfig, setAmbientNoiseConfig] = useState<AmbientNoiseConfiguration>(
|
||||
workflowConfigurations?.ambient_noise_configuration || DEFAULT_AMBIENT_NOISE_CONFIG
|
||||
resolvedWorkflowConfigurations.ambient_noise_configuration
|
||||
);
|
||||
const [maxCallDuration, setMaxCallDuration] = useState<number>(
|
||||
workflowConfigurations?.max_call_duration || 600 // Default 10 minutes
|
||||
resolvedWorkflowConfigurations.max_call_duration
|
||||
);
|
||||
const [maxUserIdleTimeout, setMaxUserIdleTimeout] = useState<number>(
|
||||
workflowConfigurations?.max_user_idle_timeout || 10 // Default 10 seconds
|
||||
resolvedWorkflowConfigurations.max_user_idle_timeout
|
||||
);
|
||||
const [smartTurnStopSecs, setSmartTurnStopSecs] = useState<number>(
|
||||
workflowConfigurations?.smart_turn_stop_secs || 2 // Default 2 seconds
|
||||
resolvedWorkflowConfigurations.smart_turn_stop_secs
|
||||
);
|
||||
const [turnStartStrategy, setTurnStartStrategy] = useState<TurnStartStrategy>(
|
||||
workflowConfigurations?.turn_start_strategy || 'default'
|
||||
resolvedWorkflowConfigurations.turn_start_strategy
|
||||
);
|
||||
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>(
|
||||
workflowConfigurations?.provisional_vad_pause_secs || DEFAULT_PROVISIONAL_VAD_PAUSE_SECS
|
||||
resolvedWorkflowConfigurations.provisional_vad_pause_secs
|
||||
);
|
||||
const [turnStopStrategy, setTurnStopStrategy] = useState<TurnStopStrategy>(
|
||||
workflowConfigurations?.turn_stop_strategy || 'transcription'
|
||||
resolvedWorkflowConfigurations.turn_stop_strategy
|
||||
);
|
||||
const [contextCompactionEnabled, setContextCompactionEnabled] = useState<boolean>(
|
||||
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 = ({
|
|||
</p>
|
||||
</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">
|
||||
<Label htmlFor="turn_start_strategy" className="text-xs">
|
||||
|
|
|
|||
|
|
@ -9,12 +9,13 @@ import {
|
|||
import { EdgeChange, NodeChange } from "@xyflow/system";
|
||||
import { useRouter } from "next/navigation";
|
||||
import posthog from "posthog-js";
|
||||
import { useCallback, useEffect, useRef } from "react";
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
|
||||
import { useWorkflowStore } from "@/app/workflow/[workflowId]/stores/workflowStore";
|
||||
import {
|
||||
createWorkflowRunApiV1WorkflowWorkflowIdRunsPost,
|
||||
getDefaultConfigurationsApiV1UserConfigurationsDefaultsGet,
|
||||
updateWorkflowApiV1WorkflowWorkflowIdPut,
|
||||
validateWorkflowApiV1WorkflowWorkflowIdValidatePost
|
||||
} from "@/client";
|
||||
|
|
@ -24,7 +25,11 @@ import { FlowEdge, FlowNode, FlowNodeData, NodeType } from "@/components/flow/ty
|
|||
import { PostHogEvent } from "@/constants/posthog-events";
|
||||
import logger from '@/lib/logger';
|
||||
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
|
||||
// the body is the raw `{ is_valid, errors }` (validate success-with-errors)
|
||||
|
|
@ -111,6 +116,10 @@ export const useWorkflowState = ({
|
|||
}: UseWorkflowStateProps) => {
|
||||
const router = useRouter();
|
||||
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
|
||||
// creation looks up per-type schemas through it.
|
||||
|
|
@ -149,10 +158,44 @@ export const useWorkflowState = ({
|
|||
const canUndo = useWorkflowStore((state) => state.canUndo());
|
||||
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
|
||||
// (allow_interrupt, prompt placeholders, etc.) come from one source.
|
||||
useEffect(() => {
|
||||
if (specsLoading) return;
|
||||
if (specsLoading || !workflowConfigurationDefaultsLoaded) return;
|
||||
|
||||
const startSpec = bySpecName.get(NodeType.START_CALL);
|
||||
const fallbackStartNodes: FlowNode[] = startSpec
|
||||
|
|
@ -176,16 +219,21 @@ export const useWorkflowState = ({
|
|||
})
|
||||
: fallbackStartNodes;
|
||||
|
||||
const resolvedInitialWorkflowConfigurations = resolveWorkflowConfigurations(
|
||||
initialWorkflowConfigurations,
|
||||
workflowConfigurationDefaults,
|
||||
);
|
||||
|
||||
initializeWorkflow(
|
||||
workflowId,
|
||||
initialWorkflowName,
|
||||
initialNodes,
|
||||
initialFlow?.edges ?? [],
|
||||
initialTemplateContextVariables,
|
||||
initialWorkflowConfigurations,
|
||||
initialWorkflowConfigurations?.dictionary ?? ''
|
||||
resolvedInitialWorkflowConfigurations,
|
||||
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
|
||||
useEffect(() => {
|
||||
|
|
@ -515,9 +563,12 @@ export const useWorkflowState = ({
|
|||
throw new Error(msg);
|
||||
}
|
||||
|
||||
const savedConfigurations = response.data?.workflow_configurations
|
||||
? (response.data.workflow_configurations as WorkflowConfigurations)
|
||||
: configurationsWithDictionary;
|
||||
const savedConfigurations = resolveWorkflowConfigurations(
|
||||
response.data?.workflow_configurations
|
||||
? (response.data.workflow_configurations as Partial<WorkflowConfigurations>)
|
||||
: configurationsWithDictionary,
|
||||
workflowConfigurationDefaults,
|
||||
);
|
||||
setWorkflowConfigurations(savedConfigurations);
|
||||
// Set name directly in the store to avoid setWorkflowName which marks isDirty: true
|
||||
useWorkflowStore.setState({ workflowName: newWorkflowName });
|
||||
|
|
@ -526,12 +577,14 @@ export const useWorkflowState = ({
|
|||
logger.error(`Error saving workflow configurations: ${error}`);
|
||||
throw error;
|
||||
}
|
||||
}, [workflowId, user, setWorkflowConfigurations]);
|
||||
}, [workflowId, user, setWorkflowConfigurations, workflowConfigurationDefaults]);
|
||||
|
||||
// Save dictionary
|
||||
const saveDictionary = useCallback(async (newDictionary: string) => {
|
||||
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 };
|
||||
try {
|
||||
await updateWorkflowApiV1WorkflowWorkflowIdPut({
|
||||
|
|
@ -550,7 +603,7 @@ export const useWorkflowState = ({
|
|||
logger.error(`Error saving dictionary: ${error}`);
|
||||
throw error;
|
||||
}
|
||||
}, [workflowId, workflowName, user, setDictionary, setWorkflowConfigurations]);
|
||||
}, [workflowId, workflowName, user, setDictionary, setWorkflowConfigurations, workflowConfigurationDefaults]);
|
||||
|
||||
// Update rfInstance when it changes
|
||||
useEffect(() => {
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import SpinLoader from '@/components/SpinLoader';
|
|||
import { PostHogEvent } from '@/constants/posthog-events';
|
||||
import { useAuth } from '@/lib/auth';
|
||||
import logger from '@/lib/logger';
|
||||
import { DEFAULT_WORKFLOW_CONFIGURATIONS, WorkflowConfigurations } from '@/types/workflow-configurations';
|
||||
import { WorkflowConfigurations } from '@/types/workflow-configurations';
|
||||
|
||||
import WorkflowLayout from '../WorkflowLayout';
|
||||
|
||||
|
|
@ -92,7 +92,11 @@ export default function WorkflowDetailPage() {
|
|||
viewport: { x: 0, y: 0, zoom: 0 }
|
||||
}}
|
||||
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}
|
||||
initialVersionStatus={workflow.version_status ?? null}
|
||||
user={stableUser}
|
||||
|
|
|
|||
|
|
@ -48,7 +48,6 @@ import {
|
|||
DEFAULT_PROVISIONAL_VAD_PAUSE_SECS,
|
||||
DEFAULT_TURN_START_MIN_WORDS,
|
||||
DEFAULT_VOICEMAIL_DETECTION_CONFIGURATION,
|
||||
DEFAULT_WORKFLOW_CONFIGURATIONS,
|
||||
TURN_START_STRATEGY_OPTIONS,
|
||||
type TurnStartStrategy,
|
||||
type TurnStopStrategy,
|
||||
|
|
@ -63,11 +62,6 @@ import { useWorkflowState } from "../hooks/useWorkflowState";
|
|||
// 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.
|
||||
|
||||
HUMAN ANSWERED - LIVE CONVERSATION (respond "CONVERSATION"):
|
||||
|
|
@ -279,25 +273,25 @@ function GeneralSection({
|
|||
}) {
|
||||
const [name, setName] = useState(workflowName);
|
||||
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 [maxUserIdleTimeout, setMaxUserIdleTimeout] = useState(workflowConfigurations.max_user_idle_timeout || 10);
|
||||
const [smartTurnStopSecs, setSmartTurnStopSecs] = useState(workflowConfigurations.smart_turn_stop_secs || 2);
|
||||
const [maxCallDuration, setMaxCallDuration] = useState(workflowConfigurations.max_call_duration);
|
||||
const [maxUserIdleTimeout, setMaxUserIdleTimeout] = useState(workflowConfigurations.max_user_idle_timeout);
|
||||
const [smartTurnStopSecs, setSmartTurnStopSecs] = useState(workflowConfigurations.smart_turn_stop_secs);
|
||||
const [turnStartStrategy, setTurnStartStrategy] = useState<TurnStartStrategy>(
|
||||
workflowConfigurations.turn_start_strategy || "default",
|
||||
workflowConfigurations.turn_start_strategy,
|
||||
);
|
||||
const [turnStartMinWords, setTurnStartMinWords] = useState(
|
||||
workflowConfigurations.turn_start_min_words || DEFAULT_TURN_START_MIN_WORDS,
|
||||
workflowConfigurations.turn_start_min_words,
|
||||
);
|
||||
const [provisionalVadPauseSecs, setProvisionalVadPauseSecs] = useState(
|
||||
workflowConfigurations.provisional_vad_pause_secs || DEFAULT_PROVISIONAL_VAD_PAUSE_SECS,
|
||||
workflowConfigurations.provisional_vad_pause_secs,
|
||||
);
|
||||
const [turnStopStrategy, setTurnStopStrategy] = useState<TurnStopStrategy>(
|
||||
workflowConfigurations.turn_stop_strategy || "transcription",
|
||||
workflowConfigurations.turn_stop_strategy,
|
||||
);
|
||||
const [contextCompactionEnabled, setContextCompactionEnabled] = useState(
|
||||
workflowConfigurations.context_compaction_enabled ?? false,
|
||||
workflowConfigurations.context_compaction_enabled,
|
||||
);
|
||||
const [isSaving, setIsSaving] = useState(false);
|
||||
const [isUploadingAudio, setIsUploadingAudio] = useState(false);
|
||||
|
|
@ -309,18 +303,18 @@ function GeneralSection({
|
|||
);
|
||||
|
||||
const isDirty = useMemo(() => {
|
||||
const initAmbient = workflowConfigurations.ambient_noise_configuration || DEFAULT_AMBIENT_NOISE_CONFIG;
|
||||
const initAmbient = workflowConfigurations.ambient_noise_configuration;
|
||||
return (
|
||||
name !== workflowName ||
|
||||
JSON.stringify(ambientNoiseConfig) !== JSON.stringify(initAmbient) ||
|
||||
maxCallDuration !== (workflowConfigurations.max_call_duration || 600) ||
|
||||
maxUserIdleTimeout !== (workflowConfigurations.max_user_idle_timeout || 10) ||
|
||||
smartTurnStopSecs !== (workflowConfigurations.smart_turn_stop_secs || 2) ||
|
||||
turnStartStrategy !== (workflowConfigurations.turn_start_strategy || "default") ||
|
||||
turnStartMinWords !== (workflowConfigurations.turn_start_min_words || DEFAULT_TURN_START_MIN_WORDS) ||
|
||||
provisionalVadPauseSecs !== (workflowConfigurations.provisional_vad_pause_secs || DEFAULT_PROVISIONAL_VAD_PAUSE_SECS) ||
|
||||
turnStopStrategy !== (workflowConfigurations.turn_stop_strategy || "transcription") ||
|
||||
contextCompactionEnabled !== (workflowConfigurations.context_compaction_enabled ?? false)
|
||||
maxCallDuration !== workflowConfigurations.max_call_duration ||
|
||||
maxUserIdleTimeout !== workflowConfigurations.max_user_idle_timeout ||
|
||||
smartTurnStopSecs !== workflowConfigurations.smart_turn_stop_secs ||
|
||||
turnStartStrategy !== workflowConfigurations.turn_start_strategy ||
|
||||
turnStartMinWords !== workflowConfigurations.turn_start_min_words ||
|
||||
provisionalVadPauseSecs !== workflowConfigurations.provisional_vad_pause_secs ||
|
||||
turnStopStrategy !== workflowConfigurations.turn_stop_strategy ||
|
||||
contextCompactionEnabled !== workflowConfigurations.context_compaction_enabled
|
||||
);
|
||||
}, [name, workflowName, ambientNoiseConfig, maxCallDuration, maxUserIdleTimeout, smartTurnStopSecs, turnStartStrategy, turnStartMinWords, provisionalVadPauseSecs, turnStopStrategy, contextCompactionEnabled, workflowConfigurations]);
|
||||
|
||||
|
|
@ -611,6 +605,18 @@ function GeneralSection({
|
|||
</p>
|
||||
</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">
|
||||
<Label htmlFor="turn_start_strategy" className="text-xs">Interruption Strategy</Label>
|
||||
<Select
|
||||
|
|
@ -1428,7 +1434,11 @@ function WorkflowSettingsInner({
|
|||
);
|
||||
|
||||
const initialWorkflowConfigurations = useMemo(
|
||||
() => (workflow.workflow_configurations as WorkflowConfigurations) || DEFAULT_WORKFLOW_CONFIGURATIONS,
|
||||
() => (
|
||||
workflow.workflow_configurations
|
||||
? (workflow.workflow_configurations as WorkflowConfigurations)
|
||||
: undefined
|
||||
),
|
||||
[workflow],
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { create } from 'zustand';
|
|||
|
||||
import { WorkflowError } from '@/client/types.gen';
|
||||
import { FlowEdge, FlowNode } from '@/components/flow/types';
|
||||
import { DEFAULT_WORKFLOW_CONFIGURATIONS, WorkflowConfigurations } from '@/types/workflow-configurations';
|
||||
import { WorkflowConfigurations } from '@/types/workflow-configurations';
|
||||
|
||||
interface HistoryState {
|
||||
nodes: FlowNode[];
|
||||
|
|
@ -117,7 +117,7 @@ export const useWorkflowStore = create<WorkflowStore>((set, get) => ({
|
|||
rfInstance: null,
|
||||
|
||||
// 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 };
|
||||
set({
|
||||
workflowId,
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -186,6 +186,21 @@ export type ActiveCallsResponse = {
|
|||
active_calls: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* AmbientNoiseConfigurationDefaults
|
||||
*/
|
||||
export type AmbientNoiseConfigurationDefaults = {
|
||||
/**
|
||||
* Enabled
|
||||
*/
|
||||
enabled?: boolean;
|
||||
/**
|
||||
* Volume
|
||||
*/
|
||||
volume?: number;
|
||||
[key: string]: unknown;
|
||||
};
|
||||
|
||||
/**
|
||||
* AmbientNoiseUploadRequest
|
||||
*/
|
||||
|
|
@ -1878,6 +1893,7 @@ export type DefaultConfigurationsResponse = {
|
|||
default_providers: {
|
||||
[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';
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
|
|
|
|||
|
|
@ -227,7 +227,7 @@ export function ServiceConfigurationForm({
|
|||
console.error("Failed to fetch configurations");
|
||||
return;
|
||||
}
|
||||
defaultsData = response.data as ServiceConfigurationDefaults;
|
||||
defaultsData = response.data as unknown as ServiceConfigurationDefaults;
|
||||
}
|
||||
|
||||
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;
|
||||
volume: number;
|
||||
storage_key?: string;
|
||||
storage_backend?: string;
|
||||
original_filename?: string;
|
||||
}
|
||||
};
|
||||
|
||||
export type TurnStopStrategy = 'transcription' | 'turn_analyzer';
|
||||
export type TurnStartStrategy = 'default' | 'min_words' | 'provisional_vad';
|
||||
export type TurnStopStrategy = NonNullable<GeneratedWorkflowConfigurationDefaults["turn_stop_strategy"]>;
|
||||
export type TurnStartStrategy = NonNullable<GeneratedWorkflowConfigurationDefaults["turn_start_strategy"]>;
|
||||
export const DEFAULT_TURN_START_MIN_WORDS = 3;
|
||||
export const DEFAULT_PROVISIONAL_VAD_PAUSE_SECS = 1.5;
|
||||
|
||||
|
|
@ -81,7 +90,21 @@ export interface ModelOverrides {
|
|||
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;
|
||||
max_call_duration: number; // Maximum call duration 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
|
||||
dictionary?: string; // Comma-separated words for voice agent to listen for
|
||||
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_configuration_v2_override?: OrganizationAiModelConfigurationV2; // Full v2 model configuration override
|
||||
[key: string]: unknown; // Allow additional properties for future configurations
|
||||
}
|
||||
};
|
||||
|
||||
export const DEFAULT_WORKFLOW_CONFIGURATIONS: WorkflowConfigurations = {
|
||||
const FALLBACK_WORKFLOW_CONFIGURATIONS: WorkflowConfigurations = {
|
||||
ambient_noise_configuration: {
|
||||
enabled: false,
|
||||
volume: 0.3
|
||||
},
|
||||
max_call_duration: 600, // 10 minutes
|
||||
max_call_duration: 300,
|
||||
max_user_idle_timeout: 10, // 10 seconds
|
||||
smart_turn_stop_secs: 2, // 2 seconds
|
||||
turn_start_strategy: 'default', // Default to platform-chosen user turn start detection
|
||||
turn_start_min_words: DEFAULT_TURN_START_MIN_WORDS,
|
||||
provisional_vad_pause_secs: DEFAULT_PROVISIONAL_VAD_PAUSE_SECS,
|
||||
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