mirror of
https://github.com/dograh-hq/dograh.git
synced 2026-07-01 08:59:46 +02:00
feat: better interrupt strategies (#479)
* chore: drain active calls before rolling updates * Use provisional VAD interruption strategy * feat: wire provisional VAD configuration * chore: refactor user turn strategies * chore: bump pipecat
This commit is contained in:
parent
962d5afa12
commit
6937e01b49
12 changed files with 645 additions and 193 deletions
3
.vscode/launch.json
vendored
3
.vscode/launch.json
vendored
|
|
@ -22,8 +22,7 @@
|
|||
"args": [
|
||||
"api.app:app",
|
||||
"--reload",
|
||||
"--host", "0.0.0.0",
|
||||
"--port", "8000"
|
||||
"--host", "0.0.0.0"
|
||||
],
|
||||
"cwd": "${workspaceFolder}",
|
||||
"envFile": "${workspaceFolder}/api/.env",
|
||||
|
|
|
|||
|
|
@ -80,7 +80,8 @@ from pipecat.turns.user_mute import (
|
|||
)
|
||||
from pipecat.turns.user_start import (
|
||||
ExternalUserTurnStartStrategy,
|
||||
TranscriptionUserTurnStartStrategy,
|
||||
MinWordsUserTurnStartStrategy,
|
||||
ProvisionalVADUserTurnStartStrategy,
|
||||
)
|
||||
from pipecat.turns.user_start.vad_user_turn_start_strategy import (
|
||||
VADUserTurnStartStrategy,
|
||||
|
|
@ -99,6 +100,10 @@ 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(
|
||||
|
|
@ -111,6 +116,80 @@ def _resolve_user_turn_stop_timeout(
|
|||
return DEFAULT_USER_TURN_STOP_TIMEOUT
|
||||
|
||||
|
||||
def _resolve_turn_start_min_words(run_configs: dict) -> int:
|
||||
return max(
|
||||
1,
|
||||
int(run_configs.get("turn_start_min_words", DEFAULT_TURN_START_MIN_WORDS)),
|
||||
)
|
||||
|
||||
|
||||
def _resolve_provisional_vad_pause_secs(run_configs: dict) -> float:
|
||||
return max(
|
||||
0.1,
|
||||
float(
|
||||
run_configs.get(
|
||||
"provisional_vad_pause_secs", DEFAULT_PROVISIONAL_VAD_PAUSE_SECS
|
||||
)
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def _create_non_realtime_user_turn_start_strategies(
|
||||
run_configs: dict, *, uses_external_turns: bool
|
||||
):
|
||||
"""Return user turn start strategies for non-realtime pipelines."""
|
||||
|
||||
turn_start_strategy = run_configs.get(
|
||||
"turn_start_strategy", DEFAULT_TURN_START_STRATEGY
|
||||
)
|
||||
|
||||
if turn_start_strategy == "min_words":
|
||||
return [
|
||||
MinWordsUserTurnStartStrategy(
|
||||
min_words=_resolve_turn_start_min_words(run_configs)
|
||||
)
|
||||
]
|
||||
|
||||
if turn_start_strategy == "provisional_vad":
|
||||
return [
|
||||
ProvisionalVADUserTurnStartStrategy(
|
||||
pause_secs=_resolve_provisional_vad_pause_secs(run_configs)
|
||||
)
|
||||
]
|
||||
|
||||
if uses_external_turns:
|
||||
# The STT emits its own turn boundaries and owns interruptions. Local
|
||||
# VAD is deliberately kept out of the default start strategies: it would
|
||||
# win the race on raw voice activity and start the turn before the STT
|
||||
# confirms a real turn.
|
||||
return [ExternalUserTurnStartStrategy(enable_interruptions=True)]
|
||||
|
||||
return [VADUserTurnStartStrategy()]
|
||||
|
||||
|
||||
def _create_non_realtime_user_turn_stop_strategies(
|
||||
run_configs: dict, *, uses_external_turns: bool
|
||||
):
|
||||
"""Return user turn stop strategies for non-realtime pipelines."""
|
||||
|
||||
if uses_external_turns:
|
||||
return [ExternalUserTurnStopStrategy()]
|
||||
|
||||
if run_configs.get("turn_stop_strategy") == "turn_analyzer":
|
||||
smart_turn_params = SmartTurnParams(
|
||||
stop_secs=run_configs.get(
|
||||
"smart_turn_stop_secs", DEFAULT_SMART_TURN_STOP_SECS
|
||||
)
|
||||
)
|
||||
return [
|
||||
TurnAnalyzerUserTurnStopStrategy(
|
||||
turn_analyzer=LocalSmartTurnAnalyzerV3(params=smart_turn_params)
|
||||
)
|
||||
]
|
||||
|
||||
return [SpeechTimeoutUserTurnStopStrategy()]
|
||||
|
||||
|
||||
def _create_realtime_user_turn_config(provider: str):
|
||||
"""Return user turn strategies and optional local VAD for realtime providers."""
|
||||
|
||||
|
|
@ -461,8 +540,6 @@ async def _run_pipeline_impl(
|
|||
# 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
|
||||
smart_turn_stop_secs = 2.0 # Default 2 seconds for incomplete turn timeout
|
||||
turn_stop_strategy = "transcription" # Default to transcription-based detection
|
||||
keyterms = None # Dictionary words for STT boosting
|
||||
|
||||
if run_configs:
|
||||
|
|
@ -472,12 +549,6 @@ async def _run_pipeline_impl(
|
|||
if "max_user_idle_timeout" in run_configs:
|
||||
max_user_idle_timeout = run_configs["max_user_idle_timeout"]
|
||||
|
||||
if "smart_turn_stop_secs" in run_configs:
|
||||
smart_turn_stop_secs = run_configs["smart_turn_stop_secs"]
|
||||
|
||||
if "turn_stop_strategy" in run_configs:
|
||||
turn_stop_strategy = run_configs["turn_stop_strategy"]
|
||||
|
||||
if "dictionary" in run_configs:
|
||||
dictionary = run_configs["dictionary"]
|
||||
if dictionary and isinstance(dictionary, str):
|
||||
|
|
@ -734,37 +805,27 @@ async def _run_pipeline_impl(
|
|||
# follows those external signals. Other models use configurable turn
|
||||
# detection.
|
||||
uses_external_turns = stt_uses_external_turns(user_config)
|
||||
if uses_external_turns:
|
||||
user_turn_strategies = UserTurnStrategies(
|
||||
start=[
|
||||
VADUserTurnStartStrategy(),
|
||||
ExternalUserTurnStartStrategy(enable_interruptions=True),
|
||||
],
|
||||
stop=[ExternalUserTurnStopStrategy()],
|
||||
)
|
||||
elif turn_stop_strategy == "turn_analyzer":
|
||||
# Smart Turn Analyzer: best for longer responses with natural pauses
|
||||
smart_turn_params = SmartTurnParams(stop_secs=smart_turn_stop_secs)
|
||||
user_turn_strategies = UserTurnStrategies(
|
||||
start=[
|
||||
VADUserTurnStartStrategy(),
|
||||
TranscriptionUserTurnStartStrategy(),
|
||||
],
|
||||
stop=[
|
||||
TurnAnalyzerUserTurnStopStrategy(
|
||||
turn_analyzer=LocalSmartTurnAnalyzerV3(params=smart_turn_params)
|
||||
)
|
||||
],
|
||||
)
|
||||
else:
|
||||
# Transcription-based (default): best for short 1-2 word responses
|
||||
user_turn_strategies = UserTurnStrategies(
|
||||
start=[
|
||||
VADUserTurnStartStrategy(),
|
||||
TranscriptionUserTurnStartStrategy(),
|
||||
],
|
||||
stop=[SpeechTimeoutUserTurnStopStrategy()],
|
||||
)
|
||||
user_turn_start_strategies = _create_non_realtime_user_turn_start_strategies(
|
||||
run_configs,
|
||||
uses_external_turns=uses_external_turns,
|
||||
)
|
||||
turn_start_strategy = run_configs.get(
|
||||
"turn_start_strategy", DEFAULT_TURN_START_STRATEGY
|
||||
)
|
||||
logger.info(
|
||||
f"[run {workflow_run_id}] Non-realtime interrupt strategy "
|
||||
f"requested={turn_start_strategy} "
|
||||
f"uses_external_turns={uses_external_turns}"
|
||||
)
|
||||
|
||||
user_turn_stop_strategies = _create_non_realtime_user_turn_stop_strategies(
|
||||
run_configs,
|
||||
uses_external_turns=uses_external_turns,
|
||||
)
|
||||
user_turn_strategies = UserTurnStrategies(
|
||||
start=user_turn_start_strategies,
|
||||
stop=user_turn_stop_strategies,
|
||||
)
|
||||
|
||||
user_turn_stop_timeout = _resolve_user_turn_stop_timeout(
|
||||
run_configs,
|
||||
|
|
|
|||
|
|
@ -197,6 +197,7 @@ def create_stt_service(
|
|||
return OpenAISTTService(
|
||||
api_key=user_config.stt.api_key,
|
||||
settings=OpenAISTTSettings(model=user_config.stt.model),
|
||||
should_interrupt=False, # Let UserAggregator own interruption confirmation.
|
||||
**kwargs,
|
||||
)
|
||||
elif user_config.stt.provider == ServiceProviders.GOOGLE.value:
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
||||
from pipecat.turns.user_start import (
|
||||
ExternalUserTurnStartStrategy,
|
||||
MinWordsUserTurnStartStrategy,
|
||||
ProvisionalVADUserTurnStartStrategy,
|
||||
)
|
||||
from pipecat.turns.user_start.vad_user_turn_start_strategy import (
|
||||
VADUserTurnStartStrategy,
|
||||
|
|
@ -8,12 +10,18 @@ from pipecat.turns.user_start.vad_user_turn_start_strategy import (
|
|||
from pipecat.turns.user_stop import (
|
||||
ExternalUserTurnStopStrategy,
|
||||
SpeechTimeoutUserTurnStopStrategy,
|
||||
TurnAnalyzerUserTurnStopStrategy,
|
||||
)
|
||||
|
||||
import api.services.pipecat.run_pipeline as run_pipeline_module
|
||||
from api.services.configuration.registry import ServiceProviders
|
||||
from api.services.pipecat.run_pipeline import (
|
||||
DEFAULT_PROVISIONAL_VAD_PAUSE_SECS,
|
||||
DEFAULT_TURN_START_MIN_WORDS,
|
||||
DEFAULT_USER_TURN_STOP_TIMEOUT,
|
||||
EXTERNAL_TURN_USER_STOP_TIMEOUT,
|
||||
_create_non_realtime_user_turn_start_strategies,
|
||||
_create_non_realtime_user_turn_stop_strategies,
|
||||
_create_realtime_user_turn_config,
|
||||
_resolve_user_turn_stop_timeout,
|
||||
)
|
||||
|
|
@ -115,6 +123,119 @@ def test_unknown_realtime_providers_keep_local_vad():
|
|||
assert strategies.stop[0].wait_for_transcript is False
|
||||
|
||||
|
||||
def test_non_realtime_default_uses_external_start_for_external_turn_stt():
|
||||
strategies = _create_non_realtime_user_turn_start_strategies(
|
||||
{},
|
||||
uses_external_turns=True,
|
||||
)
|
||||
|
||||
assert len(strategies) == 1
|
||||
assert isinstance(strategies[0], ExternalUserTurnStartStrategy)
|
||||
assert strategies[0]._enable_interruptions is True
|
||||
|
||||
|
||||
def test_non_realtime_default_uses_vad_start_for_standard_stt():
|
||||
strategies = _create_non_realtime_user_turn_start_strategies(
|
||||
{},
|
||||
uses_external_turns=False,
|
||||
)
|
||||
|
||||
assert len(strategies) == 1
|
||||
assert isinstance(strategies[0], VADUserTurnStartStrategy)
|
||||
|
||||
|
||||
def test_non_realtime_can_use_min_words_start_strategy():
|
||||
strategies = _create_non_realtime_user_turn_start_strategies(
|
||||
{"turn_start_strategy": "min_words", "turn_start_min_words": 4},
|
||||
uses_external_turns=False,
|
||||
)
|
||||
|
||||
assert len(strategies) == 1
|
||||
assert isinstance(strategies[0], MinWordsUserTurnStartStrategy)
|
||||
assert strategies[0]._min_words == 4
|
||||
|
||||
|
||||
def test_non_realtime_explicit_min_words_overrides_external_turn_default():
|
||||
strategies = _create_non_realtime_user_turn_start_strategies(
|
||||
{"turn_start_strategy": "min_words", "turn_start_min_words": 4},
|
||||
uses_external_turns=True,
|
||||
)
|
||||
|
||||
assert len(strategies) == 1
|
||||
assert isinstance(strategies[0], MinWordsUserTurnStartStrategy)
|
||||
assert strategies[0]._min_words == 4
|
||||
|
||||
|
||||
def test_non_realtime_min_words_start_strategy_has_default_threshold():
|
||||
strategies = _create_non_realtime_user_turn_start_strategies(
|
||||
{"turn_start_strategy": "min_words"},
|
||||
uses_external_turns=False,
|
||||
)
|
||||
|
||||
assert len(strategies) == 1
|
||||
assert isinstance(strategies[0], MinWordsUserTurnStartStrategy)
|
||||
assert strategies[0]._min_words == DEFAULT_TURN_START_MIN_WORDS
|
||||
|
||||
|
||||
def test_non_realtime_can_use_provisional_vad_start_strategy():
|
||||
strategies = _create_non_realtime_user_turn_start_strategies(
|
||||
{"turn_start_strategy": "provisional_vad"},
|
||||
uses_external_turns=False,
|
||||
)
|
||||
|
||||
assert len(strategies) == 1
|
||||
assert isinstance(strategies[0], ProvisionalVADUserTurnStartStrategy)
|
||||
assert strategies[0]._pause_secs == DEFAULT_PROVISIONAL_VAD_PAUSE_SECS
|
||||
|
||||
|
||||
def test_non_realtime_provisional_vad_uses_configured_pause_secs():
|
||||
strategies = _create_non_realtime_user_turn_start_strategies(
|
||||
{"turn_start_strategy": "provisional_vad", "provisional_vad_pause_secs": 0.4},
|
||||
uses_external_turns=False,
|
||||
)
|
||||
|
||||
assert len(strategies) == 1
|
||||
assert isinstance(strategies[0], ProvisionalVADUserTurnStartStrategy)
|
||||
assert strategies[0]._pause_secs == 0.4
|
||||
|
||||
|
||||
def test_non_realtime_uses_external_stop_for_external_turn_stt():
|
||||
strategies = _create_non_realtime_user_turn_stop_strategies(
|
||||
{},
|
||||
uses_external_turns=True,
|
||||
)
|
||||
|
||||
assert len(strategies) == 1
|
||||
assert isinstance(strategies[0], ExternalUserTurnStopStrategy)
|
||||
|
||||
|
||||
def test_non_realtime_default_uses_speech_timeout_stop():
|
||||
strategies = _create_non_realtime_user_turn_stop_strategies(
|
||||
{},
|
||||
uses_external_turns=False,
|
||||
)
|
||||
|
||||
assert len(strategies) == 1
|
||||
assert isinstance(strategies[0], SpeechTimeoutUserTurnStopStrategy)
|
||||
|
||||
|
||||
def test_non_realtime_can_use_turn_analyzer_stop_strategy(monkeypatch):
|
||||
monkeypatch.setattr(
|
||||
run_pipeline_module,
|
||||
"LocalSmartTurnAnalyzerV3",
|
||||
lambda *, params: params,
|
||||
)
|
||||
|
||||
strategies = _create_non_realtime_user_turn_stop_strategies(
|
||||
{"turn_stop_strategy": "turn_analyzer", "smart_turn_stop_secs": 1.5},
|
||||
uses_external_turns=False,
|
||||
)
|
||||
|
||||
assert len(strategies) == 1
|
||||
assert isinstance(strategies[0], TurnAnalyzerUserTurnStopStrategy)
|
||||
assert strategies[0]._turn_analyzer.stop_secs == 1.5
|
||||
|
||||
|
||||
def test_external_turn_stt_uses_longer_stop_timeout():
|
||||
assert (
|
||||
_resolve_user_turn_stop_timeout({}, uses_external_turns=True)
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
2
pipecat
2
pipecat
|
|
@ -1 +1 @@
|
|||
Subproject commit 12af7a65c576ce52225c735917e44075d202ab1a
|
||||
Subproject commit 63f0bc437ebe50ae4616b1cc2d69667c4ae3dc58
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
# generated by datamodel-codegen:
|
||||
# filename: dograh-openapi-XXXXXX.json.IQaxuC56sd
|
||||
# timestamp: 2026-06-29T10:55:38+00:00
|
||||
# filename: dograh-openapi-XXXXXX.json.mFeCVL0pIi
|
||||
# timestamp: 2026-06-30T08:46:04+00:00
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
|
|
|
|||
172
ui/package-lock.json
generated
172
ui/package-lock.json
generated
|
|
@ -717,6 +717,7 @@
|
|||
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz",
|
||||
"integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.29.0",
|
||||
"@babel/generator": "^7.29.0",
|
||||
|
|
@ -918,7 +919,6 @@
|
|||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz",
|
||||
"integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"regenerator-runtime": "^0.14.0"
|
||||
},
|
||||
|
|
@ -1062,7 +1062,6 @@
|
|||
"resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz",
|
||||
"integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-module-imports": "^7.16.7",
|
||||
"@babel/runtime": "^7.18.3",
|
||||
|
|
@ -1081,15 +1080,13 @@
|
|||
"version": "1.9.0",
|
||||
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
|
||||
"integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@emotion/babel-plugin/node_modules/source-map": {
|
||||
"version": "0.5.7",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
|
||||
"integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==",
|
||||
"license": "BSD-3-Clause",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
|
|
@ -1099,7 +1096,6 @@
|
|||
"resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz",
|
||||
"integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@emotion/memoize": "^0.9.0",
|
||||
"@emotion/sheet": "^1.4.0",
|
||||
|
|
@ -1112,22 +1108,19 @@
|
|||
"version": "0.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz",
|
||||
"integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@emotion/memoize": {
|
||||
"version": "0.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz",
|
||||
"integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@emotion/react": {
|
||||
"version": "11.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz",
|
||||
"integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.18.3",
|
||||
"@emotion/babel-plugin": "^11.13.5",
|
||||
|
|
@ -1152,7 +1145,6 @@
|
|||
"resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz",
|
||||
"integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@emotion/hash": "^0.9.2",
|
||||
"@emotion/memoize": "^0.9.0",
|
||||
|
|
@ -1165,22 +1157,19 @@
|
|||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz",
|
||||
"integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@emotion/unitless": {
|
||||
"version": "0.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz",
|
||||
"integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@emotion/use-insertion-effect-with-fallbacks": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz",
|
||||
"integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8.0"
|
||||
}
|
||||
|
|
@ -1189,15 +1178,13 @@
|
|||
"version": "1.4.2",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz",
|
||||
"integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@emotion/weak-memoize": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz",
|
||||
"integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.27.7",
|
||||
|
|
@ -2485,7 +2472,6 @@
|
|||
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz",
|
||||
"integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/gen-mapping": "^0.3.5",
|
||||
"@jridgewell/trace-mapping": "^0.3.25"
|
||||
|
|
@ -2724,6 +2710,7 @@
|
|||
"resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz",
|
||||
"integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=8.0.0"
|
||||
}
|
||||
|
|
@ -2774,6 +2761,7 @@
|
|||
"resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.57.2.tgz",
|
||||
"integrity": "sha512-BdBGhQBh8IjZ2oIIX6F2/Q3LKm/FDDKi6ccYKcBTeilh6SNdNKveDOLk73BkSJjQLJk6qe4Yh+hHw1UPhCDdrg==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@opentelemetry/api-logs": "0.57.2",
|
||||
"@types/shimmer": "^1.2.0",
|
||||
|
|
@ -3445,6 +3433,7 @@
|
|||
"resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.40.0.tgz",
|
||||
"integrity": "sha512-cifvXDhcqMwwTlTK04GBNeIe7yyo28Mfby85QXFe1Yk8nmi36Ab/5UQwptOx84SsoGNRg+EVSjwzfSZMy6pmlw==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
}
|
||||
|
|
@ -8167,6 +8156,7 @@
|
|||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
|
||||
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
|
|
@ -10050,6 +10040,7 @@
|
|||
"resolved": "https://registry.npmjs.org/@stripe/stripe-js/-/stripe-js-7.9.0.tgz",
|
||||
"integrity": "sha512-ggs5k+/0FUJcIgNY08aZTqpBTtbExkJMYMLSMwyucrhtWexVOEY1KJmhBsxf+E/Q15f5rbwBpj+t0t2AW2oCsQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12.16"
|
||||
}
|
||||
|
|
@ -10461,7 +10452,6 @@
|
|||
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz",
|
||||
"integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/estree": "*",
|
||||
"@types/json-schema": "*"
|
||||
|
|
@ -10472,7 +10462,6 @@
|
|||
"resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz",
|
||||
"integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/eslint": "*",
|
||||
"@types/estree": "*"
|
||||
|
|
@ -10525,8 +10514,7 @@
|
|||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz",
|
||||
"integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/pg": {
|
||||
"version": "8.6.1",
|
||||
|
|
@ -10553,6 +10541,7 @@
|
|||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.0.tgz",
|
||||
"integrity": "sha512-UaicktuQI+9UKyA4njtDOGBD/67t8YEBt2xdfqu8+gP9hqPUPsiXlNPcpS2gVdjmis5GKPG3fCxbQLVgxsQZ8w==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"csstype": "^3.0.2"
|
||||
}
|
||||
|
|
@ -10563,6 +10552,7 @@
|
|||
"integrity": "sha512-jFf/woGTVTjUJsl2O7hcopJ1r0upqoq/vIOoCj0yLh3RIXxWcljlpuZ+vEBRXsymD1jhfeJrlyTy/S1UW+4y1w==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"peerDependencies": {
|
||||
"@types/react": "^19.0.0"
|
||||
}
|
||||
|
|
@ -10572,7 +10562,6 @@
|
|||
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz",
|
||||
"integrity": "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"peerDependencies": {
|
||||
"@types/react": "*"
|
||||
}
|
||||
|
|
@ -11102,7 +11091,6 @@
|
|||
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz",
|
||||
"integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@webassemblyjs/helper-numbers": "1.13.2",
|
||||
"@webassemblyjs/helper-wasm-bytecode": "1.13.2"
|
||||
|
|
@ -11112,29 +11100,25 @@
|
|||
"version": "1.13.2",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz",
|
||||
"integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@webassemblyjs/helper-api-error": {
|
||||
"version": "1.13.2",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz",
|
||||
"integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@webassemblyjs/helper-buffer": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz",
|
||||
"integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@webassemblyjs/helper-numbers": {
|
||||
"version": "1.13.2",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz",
|
||||
"integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@webassemblyjs/floating-point-hex-parser": "1.13.2",
|
||||
"@webassemblyjs/helper-api-error": "1.13.2",
|
||||
|
|
@ -11145,15 +11129,13 @@
|
|||
"version": "1.13.2",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz",
|
||||
"integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@webassemblyjs/helper-wasm-section": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz",
|
||||
"integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@webassemblyjs/ast": "1.14.1",
|
||||
"@webassemblyjs/helper-buffer": "1.14.1",
|
||||
|
|
@ -11166,7 +11148,6 @@
|
|||
"resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz",
|
||||
"integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@xtuc/ieee754": "^1.2.0"
|
||||
}
|
||||
|
|
@ -11176,7 +11157,6 @@
|
|||
"resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz",
|
||||
"integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@xtuc/long": "4.2.2"
|
||||
}
|
||||
|
|
@ -11185,15 +11165,13 @@
|
|||
"version": "1.13.2",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz",
|
||||
"integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@webassemblyjs/wasm-edit": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz",
|
||||
"integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@webassemblyjs/ast": "1.14.1",
|
||||
"@webassemblyjs/helper-buffer": "1.14.1",
|
||||
|
|
@ -11210,7 +11188,6 @@
|
|||
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz",
|
||||
"integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@webassemblyjs/ast": "1.14.1",
|
||||
"@webassemblyjs/helper-wasm-bytecode": "1.13.2",
|
||||
|
|
@ -11224,7 +11201,6 @@
|
|||
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz",
|
||||
"integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@webassemblyjs/ast": "1.14.1",
|
||||
"@webassemblyjs/helper-buffer": "1.14.1",
|
||||
|
|
@ -11237,7 +11213,6 @@
|
|||
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz",
|
||||
"integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@webassemblyjs/ast": "1.14.1",
|
||||
"@webassemblyjs/helper-api-error": "1.13.2",
|
||||
|
|
@ -11252,7 +11227,6 @@
|
|||
"resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz",
|
||||
"integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@webassemblyjs/ast": "1.14.1",
|
||||
"@xtuc/long": "4.2.2"
|
||||
|
|
@ -11268,15 +11242,13 @@
|
|||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
|
||||
"integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==",
|
||||
"license": "BSD-3-Clause",
|
||||
"peer": true
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@xtuc/long": {
|
||||
"version": "4.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz",
|
||||
"integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/@xyflow/react": {
|
||||
"version": "12.10.2",
|
||||
|
|
@ -11343,6 +11315,7 @@
|
|||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz",
|
||||
"integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
|
|
@ -11364,7 +11337,6 @@
|
|||
"resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz",
|
||||
"integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=10.13.0"
|
||||
},
|
||||
|
|
@ -11416,7 +11388,6 @@
|
|||
"resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz",
|
||||
"integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"ajv": "^8.0.0"
|
||||
},
|
||||
|
|
@ -11434,7 +11405,6 @@
|
|||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
|
||||
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"fast-uri": "^3.0.1",
|
||||
|
|
@ -11450,8 +11420,7 @@
|
|||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
|
||||
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/ansi-colors": {
|
||||
"version": "4.1.3",
|
||||
|
|
@ -11763,7 +11732,6 @@
|
|||
"resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz",
|
||||
"integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.5",
|
||||
"cosmiconfig": "^7.0.0",
|
||||
|
|
@ -11891,6 +11859,7 @@
|
|||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"baseline-browser-mapping": "^2.10.12",
|
||||
"caniuse-lite": "^1.0.30001782",
|
||||
|
|
@ -12095,7 +12064,6 @@
|
|||
"resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz",
|
||||
"integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
}
|
||||
|
|
@ -12354,7 +12322,6 @@
|
|||
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz",
|
||||
"integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/parse-json": "^4.0.0",
|
||||
"import-fresh": "^3.2.1",
|
||||
|
|
@ -12526,6 +12493,7 @@
|
|||
"resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
|
||||
"integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
|
||||
"license": "ISC",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
|
|
@ -12868,7 +12836,6 @@
|
|||
"resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
|
||||
"integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.8.7",
|
||||
"csstype": "^3.0.2"
|
||||
|
|
@ -12965,7 +12932,6 @@
|
|||
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
|
||||
"integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"is-arrayish": "^0.2.1"
|
||||
}
|
||||
|
|
@ -12974,8 +12940,7 @@
|
|||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
|
||||
"integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/es-abstract": {
|
||||
"version": "1.23.9",
|
||||
|
|
@ -13095,8 +13060,7 @@
|
|||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz",
|
||||
"integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/es-object-atoms": {
|
||||
"version": "1.1.1",
|
||||
|
|
@ -13248,6 +13212,7 @@
|
|||
"integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.8.0",
|
||||
"@eslint-community/regexpp": "^4.12.1",
|
||||
|
|
@ -13421,6 +13386,7 @@
|
|||
"integrity": "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@rtsao/scc": "^1.1.0",
|
||||
"array-includes": "^3.1.8",
|
||||
|
|
@ -13708,7 +13674,6 @@
|
|||
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
|
||||
"integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=0.8.x"
|
||||
}
|
||||
|
|
@ -13814,8 +13779,7 @@
|
|||
"url": "https://opencollective.com/fastify"
|
||||
}
|
||||
],
|
||||
"license": "BSD-3-Clause",
|
||||
"peer": true
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/fast-xml-builder": {
|
||||
"version": "1.1.4",
|
||||
|
|
@ -13897,8 +13861,7 @@
|
|||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
|
||||
"integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/find-up": {
|
||||
"version": "5.0.0",
|
||||
|
|
@ -14150,8 +14113,7 @@
|
|||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz",
|
||||
"integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==",
|
||||
"license": "BSD-2-Clause",
|
||||
"peer": true
|
||||
"license": "BSD-2-Clause"
|
||||
},
|
||||
"node_modules/globals": {
|
||||
"version": "14.0.0",
|
||||
|
|
@ -14365,6 +14327,7 @@
|
|||
"resolved": "https://registry.npmjs.org/immer/-/immer-10.1.1.tgz",
|
||||
"integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/immer"
|
||||
|
|
@ -14974,7 +14937,6 @@
|
|||
"resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz",
|
||||
"integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/node": "*",
|
||||
"merge-stream": "^2.0.0",
|
||||
|
|
@ -14989,7 +14951,6 @@
|
|||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
|
||||
"integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"has-flag": "^4.0.0"
|
||||
},
|
||||
|
|
@ -15079,8 +15040,7 @@
|
|||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
|
||||
"integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/json-schema-traverse": {
|
||||
"version": "0.4.1",
|
||||
|
|
@ -15412,15 +15372,13 @@
|
|||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
|
||||
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/loader-runner": {
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.1.tgz",
|
||||
"integrity": "sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=6.11.5"
|
||||
},
|
||||
|
|
@ -15501,15 +15459,13 @@
|
|||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz",
|
||||
"integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/merge-stream": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
|
||||
"integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/merge2": {
|
||||
"version": "1.4.1",
|
||||
|
|
@ -15540,7 +15496,6 @@
|
|||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
|
|
@ -15550,7 +15505,6 @@
|
|||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"mime-db": "1.52.0"
|
||||
},
|
||||
|
|
@ -15648,14 +15602,14 @@
|
|||
"version": "2.6.2",
|
||||
"resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
|
||||
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/next": {
|
||||
"version": "15.5.14",
|
||||
"resolved": "https://registry.npmjs.org/next/-/next-15.5.14.tgz",
|
||||
"integrity": "sha512-M6S+4JyRjmKic2Ssm7jHUPkE6YUJ6lv4507jprsSZLulubz0ihO2E+S4zmQK3JZ2ov81JrugukKU4Tz0ivgqqQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@next/env": "15.5.14",
|
||||
"@swc/helpers": "0.5.15",
|
||||
|
|
@ -16083,7 +16037,6 @@
|
|||
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
|
||||
"integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.0.0",
|
||||
"error-ex": "^1.3.1",
|
||||
|
|
@ -16158,7 +16111,6 @@
|
|||
"resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
|
||||
"integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
|
|
@ -16601,6 +16553,7 @@
|
|||
"resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz",
|
||||
"integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
|
|
@ -16631,6 +16584,7 @@
|
|||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz",
|
||||
"integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"scheduler": "^0.26.0"
|
||||
},
|
||||
|
|
@ -16657,6 +16611,7 @@
|
|||
"resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.72.1.tgz",
|
||||
"integrity": "sha512-RhwBoy2ygeVZje+C+bwJ8g0NjTdBmDlJvAUHTxRjTmSUKPYsKfMphkS2sgEMotsY03bP358yEYlnUeZy//D9Ig==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
},
|
||||
|
|
@ -16681,13 +16636,15 @@
|
|||
"version": "16.13.1",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/react-redux": {
|
||||
"version": "9.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz",
|
||||
"integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/use-sync-external-store": "^0.0.6",
|
||||
"use-sync-external-store": "^1.4.0"
|
||||
|
|
@ -16827,7 +16784,6 @@
|
|||
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
|
||||
"integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==",
|
||||
"license": "BSD-3-Clause",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.5.5",
|
||||
"dom-helpers": "^5.0.1",
|
||||
|
|
@ -16893,7 +16849,8 @@
|
|||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz",
|
||||
"integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==",
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/redux-thunk": {
|
||||
"version": "3.1.0",
|
||||
|
|
@ -16931,8 +16888,7 @@
|
|||
"version": "0.14.1",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
|
||||
"integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/regexp.prototype.flags": {
|
||||
"version": "1.5.4",
|
||||
|
|
@ -16969,7 +16925,6 @@
|
|||
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
|
||||
"integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
|
|
@ -17054,6 +17009,7 @@
|
|||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.1.tgz",
|
||||
"integrity": "sha512-VmtB2rFU/GroZ4oL8+ZqXgSA38O6GR8KSIvWmEFv63pQ0G6KaBH9s07PO8XTXP4vI+3UJUEypOfjkGfmSBBR0w==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/estree": "1.0.8"
|
||||
},
|
||||
|
|
@ -17225,7 +17181,6 @@
|
|||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz",
|
||||
"integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/json-schema": "^7.0.9",
|
||||
"ajv": "^8.9.0",
|
||||
|
|
@ -17262,7 +17217,6 @@
|
|||
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz",
|
||||
"integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.3"
|
||||
},
|
||||
|
|
@ -17274,8 +17228,7 @@
|
|||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
|
||||
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/secure-json-parse": {
|
||||
"version": "4.0.0",
|
||||
|
|
@ -17831,8 +17784,7 @@
|
|||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz",
|
||||
"integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/supports-color": {
|
||||
"version": "7.2.0",
|
||||
|
|
@ -17872,7 +17824,8 @@
|
|||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.1.tgz",
|
||||
"integrity": "sha512-QNbdmeS979Efzim2g/bEvfuh+fTcIdp1y7gA+sb6OYSW74rt7Cr7M78AKdf6HqWT3d5AiTb7SwTT3sLQxr4/qw==",
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/tailwindcss-animate": {
|
||||
"version": "1.0.7",
|
||||
|
|
@ -17901,7 +17854,6 @@
|
|||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.46.1.tgz",
|
||||
"integrity": "sha512-vzCjQO/rgUuK9sf8VJZvjqiqiHFaZLnOiimmUuOKODxWL8mm/xua7viT7aqX7dgPY60otQjUotzFMmCB4VdmqQ==",
|
||||
"license": "BSD-2-Clause",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/source-map": "^0.3.3",
|
||||
"acorn": "^8.15.0",
|
||||
|
|
@ -17920,7 +17872,6 @@
|
|||
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.4.0.tgz",
|
||||
"integrity": "sha512-Bn5vxm48flOIfkdl5CaD2+1CiUVbonWQ3KQPyP7/EuIl9Gbzq/gQFOzaMFUEgVjB1396tcK0SG8XcNJ/2kDH8g==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/trace-mapping": "^0.3.25",
|
||||
"jest-worker": "^27.4.5",
|
||||
|
|
@ -17953,8 +17904,7 @@
|
|||
"version": "2.20.3",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/thread-stream": {
|
||||
"version": "3.1.0",
|
||||
|
|
@ -18031,6 +17981,7 @@
|
|||
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
|
|
@ -18219,6 +18170,7 @@
|
|||
"integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
|
|
@ -18405,7 +18357,6 @@
|
|||
"resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.2.1.tgz",
|
||||
"integrity": "sha512-tpZZ+EX0gaghDAiFR37hj5MgY6ZN55kLiPkJsKxBMZ6GZdOSPJXiOzPM984oPYZ5AnehYx5WQp1+ME8I/P/pRA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
},
|
||||
|
|
@ -18492,7 +18443,6 @@
|
|||
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.1.tgz",
|
||||
"integrity": "sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"glob-to-regexp": "^0.4.1",
|
||||
"graceful-fs": "^4.1.2"
|
||||
|
|
@ -18518,7 +18468,6 @@
|
|||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.105.4.tgz",
|
||||
"integrity": "sha512-jTywjboN9aHxFlToqb0K0Zs9SbBoW4zRUlGzI2tYNxVYcEi/IPpn+Xi4ye5jTLvX2YeLuic/IvxNot+Q1jMoOw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/eslint-scope": "^3.7.7",
|
||||
"@types/estree": "^1.0.8",
|
||||
|
|
@ -18582,7 +18531,6 @@
|
|||
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
|
||||
"integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
|
||||
"license": "BSD-2-Clause",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"esrecurse": "^4.3.0",
|
||||
"estraverse": "^4.1.1"
|
||||
|
|
@ -18596,7 +18544,6 @@
|
|||
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
|
||||
"integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
|
||||
"license": "BSD-2-Clause",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=4.0"
|
||||
}
|
||||
|
|
@ -18774,7 +18721,6 @@
|
|||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz",
|
||||
"integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==",
|
||||
"license": "ISC",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
|
|
@ -18883,6 +18829,7 @@
|
|||
"resolved": "https://registry.npmjs.org/yup/-/yup-1.7.1.tgz",
|
||||
"integrity": "sha512-GKHFX2nXul2/4Dtfxhozv701jLQHdf6J34YDh2cEkpqoo8le5Mg6/LrdseVLrFarmFygZTlfIhHx/QKfb/QWXw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"property-expr": "^2.0.5",
|
||||
"tiny-case": "^1.0.3",
|
||||
|
|
@ -18925,6 +18872,7 @@
|
|||
"resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.8.tgz",
|
||||
"integrity": "sha512-gyPKpIaxY9XcO2vSMrLbiER7QMAMGOQZVRdJ6Zi782jkbzZygq5GI9nG8g+sMgitRtndwaBSl7uiqC49o1SSiw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12.20.0"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -6,7 +6,15 @@ import { Input } from "@/components/ui/input";
|
|||
import { Label } from "@/components/ui/label";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import { AmbientNoiseConfiguration, TurnStopStrategy, WorkflowConfigurations } from "@/types/workflow-configurations";
|
||||
import {
|
||||
AmbientNoiseConfiguration,
|
||||
DEFAULT_PROVISIONAL_VAD_PAUSE_SECS,
|
||||
DEFAULT_TURN_START_MIN_WORDS,
|
||||
TURN_START_STRATEGY_OPTIONS,
|
||||
TurnStartStrategy,
|
||||
TurnStopStrategy,
|
||||
WorkflowConfigurations,
|
||||
} from "@/types/workflow-configurations";
|
||||
|
||||
interface ConfigurationsDialogProps {
|
||||
open: boolean;
|
||||
|
|
@ -41,6 +49,15 @@ export const ConfigurationsDialog = ({
|
|||
const [smartTurnStopSecs, setSmartTurnStopSecs] = useState<number>(
|
||||
workflowConfigurations?.smart_turn_stop_secs || 2 // Default 2 seconds
|
||||
);
|
||||
const [turnStartStrategy, setTurnStartStrategy] = useState<TurnStartStrategy>(
|
||||
workflowConfigurations?.turn_start_strategy || 'default'
|
||||
);
|
||||
const [turnStartMinWords, setTurnStartMinWords] = useState<number>(
|
||||
workflowConfigurations?.turn_start_min_words || DEFAULT_TURN_START_MIN_WORDS
|
||||
);
|
||||
const [provisionalVadPauseSecs, setProvisionalVadPauseSecs] = useState<number>(
|
||||
workflowConfigurations?.provisional_vad_pause_secs || DEFAULT_PROVISIONAL_VAD_PAUSE_SECS
|
||||
);
|
||||
const [turnStopStrategy, setTurnStopStrategy] = useState<TurnStopStrategy>(
|
||||
workflowConfigurations?.turn_stop_strategy || 'transcription'
|
||||
);
|
||||
|
|
@ -48,6 +65,9 @@ export const ConfigurationsDialog = ({
|
|||
workflowConfigurations?.context_compaction_enabled ?? false
|
||||
);
|
||||
const [isSaving, setIsSaving] = useState(false);
|
||||
const selectedTurnStartStrategy = TURN_START_STRATEGY_OPTIONS.find(
|
||||
(option) => option.value === turnStartStrategy
|
||||
);
|
||||
|
||||
const handleSave = async () => {
|
||||
setIsSaving(true);
|
||||
|
|
@ -57,6 +77,9 @@ export const ConfigurationsDialog = ({
|
|||
max_call_duration: maxCallDuration,
|
||||
max_user_idle_timeout: maxUserIdleTimeout,
|
||||
smart_turn_stop_secs: smartTurnStopSecs,
|
||||
turn_start_strategy: turnStartStrategy,
|
||||
turn_start_min_words: turnStartMinWords,
|
||||
provisional_vad_pause_secs: provisionalVadPauseSecs,
|
||||
turn_stop_strategy: turnStopStrategy,
|
||||
context_compaction_enabled: contextCompactionEnabled,
|
||||
}, name);
|
||||
|
|
@ -76,6 +99,9 @@ export const ConfigurationsDialog = ({
|
|||
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);
|
||||
}
|
||||
|
|
@ -218,6 +244,80 @@ export const ConfigurationsDialog = ({
|
|||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="turn_start_strategy" className="text-xs">
|
||||
Interruption Strategy
|
||||
</Label>
|
||||
<Select
|
||||
value={turnStartStrategy}
|
||||
onValueChange={(value: TurnStartStrategy) => setTurnStartStrategy(value)}
|
||||
>
|
||||
<SelectTrigger id="turn_start_strategy">
|
||||
<SelectValue placeholder="Select strategy" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{TURN_START_STRATEGY_OPTIONS.map((option) => (
|
||||
<SelectItem key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{selectedTurnStartStrategy?.description}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{turnStartStrategy === 'min_words' && (
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="turn_start_min_words" className="text-xs">
|
||||
Minimum Words Before Interruption
|
||||
</Label>
|
||||
<Input
|
||||
id="turn_start_min_words"
|
||||
type="number"
|
||||
step="1"
|
||||
min="1"
|
||||
max="10"
|
||||
value={turnStartMinWords}
|
||||
onChange={(e) => {
|
||||
const value = parseInt(e.target.value);
|
||||
if (!isNaN(value) && value >= 1) {
|
||||
setTurnStartMinWords(value);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Number of transcribed words needed to interrupt while the bot is speaking. Default: {DEFAULT_TURN_START_MIN_WORDS}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{turnStartStrategy === 'provisional_vad' && (
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="provisional_vad_pause_secs" className="text-xs">
|
||||
Provisional Pause (seconds)
|
||||
</Label>
|
||||
<Input
|
||||
id="provisional_vad_pause_secs"
|
||||
type="number"
|
||||
step="0.1"
|
||||
min="0.1"
|
||||
max="5"
|
||||
value={provisionalVadPauseSecs}
|
||||
onChange={(e) => {
|
||||
const value = parseFloat(e.target.value);
|
||||
if (!isNaN(value) && value >= 0.1) {
|
||||
setProvisionalVadPauseSecs(value);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Seconds to pause bot audio while waiting for transcript confirmation. Default: {DEFAULT_PROVISIONAL_VAD_PAUSE_SECS}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Context Management Section */}
|
||||
|
|
@ -306,4 +406,3 @@ export const ConfigurationsDialog = ({
|
|||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ interface WorkflowRunResponse {
|
|||
|
||||
const RUN_SHELL_HEIGHT_CLASS = "h-[calc(100svh-49px)] min-h-[calc(100svh-49px)] max-h-[calc(100svh-49px)]";
|
||||
const WAVEFORM_BAR_COUNT = 96;
|
||||
type SplitTrackPlaybackMode = 'both' | 'user' | 'bot';
|
||||
|
||||
function formatDuration(seconds?: number | null) {
|
||||
if (seconds == null || Number.isNaN(seconds)) return 'N/A';
|
||||
|
|
@ -124,19 +125,38 @@ async function loadWaveformPeaks(url: string) {
|
|||
}
|
||||
}
|
||||
|
||||
function getAudioDuration(audio: HTMLAudioElement | null) {
|
||||
return audio && Number.isFinite(audio.duration) ? audio.duration : 0;
|
||||
}
|
||||
|
||||
function getAudioTimelineState(audios: HTMLAudioElement[]) {
|
||||
const duration = Math.max(0, ...audios.map((audio) => getAudioDuration(audio)));
|
||||
const currentTime = Math.max(0, ...audios.map((audio) => audio.currentTime));
|
||||
|
||||
return { duration, currentTime };
|
||||
}
|
||||
|
||||
function syncAudioCurrentTime(audio: HTMLAudioElement, startTime: number) {
|
||||
const duration = getAudioDuration(audio);
|
||||
audio.currentTime = Math.min(startTime, duration || startTime);
|
||||
}
|
||||
|
||||
function WaveformLane({
|
||||
peaks,
|
||||
track,
|
||||
position,
|
||||
isActive,
|
||||
}: {
|
||||
peaks: number[] | null;
|
||||
track: 'user' | 'bot';
|
||||
position: 'top' | 'bottom';
|
||||
isActive: boolean;
|
||||
}) {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'absolute left-3 right-3 flex gap-0.5',
|
||||
isActive ? 'opacity-85' : 'opacity-25',
|
||||
position === 'top' ? 'top-5 h-12 items-end' : 'bottom-5 h-12 items-start'
|
||||
)}
|
||||
>
|
||||
|
|
@ -145,7 +165,7 @@ function WaveformLane({
|
|||
<span
|
||||
key={`${track}-${index}`}
|
||||
className={cn(
|
||||
'min-h-1 flex-1 rounded-full opacity-85',
|
||||
'min-h-1 flex-1 rounded-full',
|
||||
track === 'user' ? 'bg-sky-500' : 'bg-emerald-500'
|
||||
)}
|
||||
style={{ height: `${Math.round(peak * 100)}%` }}
|
||||
|
|
@ -178,6 +198,21 @@ function SplitTracksSection({
|
|||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [isPlaying, setIsPlaying] = useState(false);
|
||||
const [progress, setProgress] = useState(0);
|
||||
const [playbackMode, setPlaybackMode] = useState<SplitTrackPlaybackMode>('both');
|
||||
|
||||
const getPlaybackAudios = (mode: SplitTrackPlaybackMode) => {
|
||||
const audios: HTMLAudioElement[] = [];
|
||||
|
||||
if (mode !== 'bot' && userAudioRef.current) {
|
||||
audios.push(userAudioRef.current);
|
||||
}
|
||||
|
||||
if (mode !== 'user' && botAudioRef.current) {
|
||||
audios.push(botAudioRef.current);
|
||||
}
|
||||
|
||||
return audios;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
let isActive = true;
|
||||
|
|
@ -190,6 +225,7 @@ function SplitTracksSection({
|
|||
setPeaks({ user: null, bot: null });
|
||||
setIsPlaying(false);
|
||||
setProgress(0);
|
||||
setPlaybackMode('both');
|
||||
setIsLoading(true);
|
||||
|
||||
async function loadTracks() {
|
||||
|
|
@ -234,12 +270,17 @@ function SplitTracksSection({
|
|||
|
||||
let frameId: number;
|
||||
const updateProgress = () => {
|
||||
const userAudio = userAudioRef.current;
|
||||
const botAudio = botAudioRef.current;
|
||||
const userDuration = Number.isFinite(userAudio?.duration) ? userAudio?.duration ?? 0 : 0;
|
||||
const botDuration = Number.isFinite(botAudio?.duration) ? botAudio?.duration ?? 0 : 0;
|
||||
const duration = Math.max(userDuration, botDuration);
|
||||
const currentTime = Math.max(userAudio?.currentTime ?? 0, botAudio?.currentTime ?? 0);
|
||||
const activeAudios: HTMLAudioElement[] = [];
|
||||
|
||||
if (playbackMode !== 'bot' && userAudioRef.current) {
|
||||
activeAudios.push(userAudioRef.current);
|
||||
}
|
||||
|
||||
if (playbackMode !== 'user' && botAudioRef.current) {
|
||||
activeAudios.push(botAudioRef.current);
|
||||
}
|
||||
|
||||
const { duration, currentTime } = getAudioTimelineState(activeAudios);
|
||||
|
||||
setProgress(duration > 0 ? Math.min(1, currentTime / duration) : 0);
|
||||
frameId = window.requestAnimationFrame(updateProgress);
|
||||
|
|
@ -247,7 +288,7 @@ function SplitTracksSection({
|
|||
|
||||
frameId = window.requestAnimationFrame(updateProgress);
|
||||
return () => window.cancelAnimationFrame(frameId);
|
||||
}, [isPlaying]);
|
||||
}, [isPlaying, playbackMode]);
|
||||
|
||||
const pauseTracks = () => {
|
||||
userAudioRef.current?.pause();
|
||||
|
|
@ -256,38 +297,68 @@ function SplitTracksSection({
|
|||
};
|
||||
|
||||
const handleTrackEnded = () => {
|
||||
const userAudio = userAudioRef.current;
|
||||
const botAudio = botAudioRef.current;
|
||||
const userDone = !userAudio || userAudio.ended;
|
||||
const botDone = !botAudio || botAudio.ended;
|
||||
const activeAudios = getPlaybackAudios(playbackMode);
|
||||
const activeTracksDone = activeAudios.length > 0 && activeAudios.every((audio) => audio.ended);
|
||||
|
||||
if (userDone && botDone) {
|
||||
if (activeTracksDone) {
|
||||
setIsPlaying(false);
|
||||
setProgress(1);
|
||||
}
|
||||
};
|
||||
|
||||
const handlePlaybackModeChange = async (nextMode: SplitTrackPlaybackMode) => {
|
||||
if (nextMode === playbackMode) return;
|
||||
|
||||
const { currentTime } = getAudioTimelineState(getPlaybackAudios(playbackMode));
|
||||
const nextAudios = getPlaybackAudios(nextMode);
|
||||
const { duration } = getAudioTimelineState(nextAudios);
|
||||
const startTime = duration > 0 && currentTime >= duration - 0.1 ? 0 : currentTime;
|
||||
|
||||
userAudioRef.current?.pause();
|
||||
botAudioRef.current?.pause();
|
||||
nextAudios.forEach((audio) => syncAudioCurrentTime(audio, startTime));
|
||||
setPlaybackMode(nextMode);
|
||||
setProgress(duration > 0 ? Math.min(1, startTime / duration) : 0);
|
||||
|
||||
if (!isPlaying) return;
|
||||
|
||||
if (nextAudios.length === 0) {
|
||||
setIsPlaying(false);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await Promise.all(nextAudios.map((audio) => audio.play()));
|
||||
setIsPlaying(true);
|
||||
} catch (error) {
|
||||
pauseTracks();
|
||||
console.error('Error switching split track playback:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const handleTrackButtonClick = (track: 'user' | 'bot') => {
|
||||
const nextMode = playbackMode === track ? 'both' : track;
|
||||
void handlePlaybackModeChange(nextMode);
|
||||
};
|
||||
|
||||
const togglePlayback = async () => {
|
||||
const userAudio = userAudioRef.current;
|
||||
const botAudio = botAudioRef.current;
|
||||
if (!userAudio || !botAudio || !signedUrls.user || !signedUrls.bot) return;
|
||||
const playbackAudios = getPlaybackAudios(playbackMode);
|
||||
if (!canPlay || playbackAudios.length === 0) return;
|
||||
|
||||
if (isPlaying) {
|
||||
pauseTracks();
|
||||
return;
|
||||
}
|
||||
|
||||
const userDuration = Number.isFinite(userAudio.duration) ? userAudio.duration : 0;
|
||||
const botDuration = Number.isFinite(botAudio.duration) ? botAudio.duration : 0;
|
||||
const duration = Math.max(userDuration, botDuration);
|
||||
const currentTime = Math.max(userAudio.currentTime, botAudio.currentTime);
|
||||
const { duration, currentTime } = getAudioTimelineState(playbackAudios);
|
||||
const startTime = duration > 0 && currentTime >= duration - 0.1 ? 0 : currentTime;
|
||||
|
||||
userAudio.currentTime = Math.min(startTime, userDuration || startTime);
|
||||
botAudio.currentTime = Math.min(startTime, botDuration || startTime);
|
||||
userAudioRef.current?.pause();
|
||||
botAudioRef.current?.pause();
|
||||
playbackAudios.forEach((audio) => syncAudioCurrentTime(audio, startTime));
|
||||
|
||||
try {
|
||||
await Promise.all([userAudio.play(), botAudio.play()]);
|
||||
await Promise.all(playbackAudios.map((audio) => audio.play()));
|
||||
setIsPlaying(true);
|
||||
} catch (error) {
|
||||
pauseTracks();
|
||||
|
|
@ -295,8 +366,16 @@ function SplitTracksSection({
|
|||
}
|
||||
};
|
||||
|
||||
const canPlay = Boolean(signedUrls.user && signedUrls.bot);
|
||||
const canPlay =
|
||||
playbackMode === 'both'
|
||||
? Boolean(signedUrls.user && signedUrls.bot)
|
||||
: playbackMode === 'user'
|
||||
? Boolean(signedUrls.user)
|
||||
: Boolean(signedUrls.bot);
|
||||
const progressPercent = Math.round(progress * 1000) / 10;
|
||||
const userTrackActive = playbackMode !== 'bot';
|
||||
const botTrackActive = playbackMode !== 'user';
|
||||
const playbackTargetLabel = playbackMode === 'both' ? 'split tracks' : `${playbackMode} track`;
|
||||
|
||||
return (
|
||||
<Card className="border-border">
|
||||
|
|
@ -319,16 +398,42 @@ function SplitTracksSection({
|
|||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<div className="flex flex-wrap items-center justify-between gap-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="inline-flex items-center gap-1.5 text-sm font-medium text-sky-600">
|
||||
<div className="flex items-center gap-2" role="group" aria-label="Playback tracks">
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
aria-pressed={userTrackActive}
|
||||
aria-label={playbackMode === 'user' ? 'Play both tracks' : 'Play user track only'}
|
||||
onClick={() => handleTrackButtonClick('user')}
|
||||
className={cn(
|
||||
'gap-1.5',
|
||||
userTrackActive
|
||||
? 'border-sky-200 bg-sky-50 text-sky-700 hover:bg-sky-100 dark:border-sky-900/60 dark:bg-sky-950/30 dark:text-sky-300'
|
||||
: 'text-muted-foreground opacity-60'
|
||||
)}
|
||||
>
|
||||
<UserRound className="h-4 w-4" />
|
||||
User
|
||||
</span>
|
||||
</Button>
|
||||
<span className="h-4 w-px bg-border" />
|
||||
<span className="inline-flex items-center gap-1.5 text-sm font-medium text-emerald-600">
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
aria-pressed={botTrackActive}
|
||||
aria-label={playbackMode === 'bot' ? 'Play both tracks' : 'Play bot track only'}
|
||||
onClick={() => handleTrackButtonClick('bot')}
|
||||
className={cn(
|
||||
'gap-1.5',
|
||||
botTrackActive
|
||||
? 'border-emerald-200 bg-emerald-50 text-emerald-700 hover:bg-emerald-100 dark:border-emerald-900/60 dark:bg-emerald-950/30 dark:text-emerald-300'
|
||||
: 'text-muted-foreground opacity-60'
|
||||
)}
|
||||
>
|
||||
<Bot className="h-4 w-4" />
|
||||
Bot
|
||||
</span>
|
||||
</Button>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button
|
||||
|
|
@ -360,15 +465,15 @@ function SplitTracksSection({
|
|||
variant={isPlaying ? 'default' : 'outline'}
|
||||
onClick={togglePlayback}
|
||||
disabled={!canPlay}
|
||||
aria-label={isPlaying ? 'Pause split tracks' : 'Play split tracks'}
|
||||
aria-label={isPlaying ? `Pause ${playbackTargetLabel}` : `Play ${playbackTargetLabel}`}
|
||||
className="h-10 w-10 shrink-0"
|
||||
>
|
||||
{isPlaying ? <Pause className="h-4 w-4" /> : <Play className="h-4 w-4" />}
|
||||
</Button>
|
||||
<div className="relative h-36 min-w-0 flex-1 overflow-hidden rounded-lg border border-border/70 bg-background">
|
||||
<div className="absolute left-3 right-3 top-1/2 h-px bg-border/80" />
|
||||
<WaveformLane peaks={peaks.user} track="user" position="top" />
|
||||
<WaveformLane peaks={peaks.bot} track="bot" position="bottom" />
|
||||
<WaveformLane peaks={peaks.user} track="user" position="top" isActive={userTrackActive} />
|
||||
<WaveformLane peaks={peaks.bot} track="bot" position="bottom" isActive={botTrackActive} />
|
||||
{canPlay && (
|
||||
<div className="pointer-events-none absolute inset-x-3 inset-y-3">
|
||||
<div
|
||||
|
|
|
|||
|
|
@ -45,8 +45,12 @@ import { useAuth } from "@/lib/auth";
|
|||
import logger from "@/lib/logger";
|
||||
import {
|
||||
type AmbientNoiseConfiguration,
|
||||
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,
|
||||
type VoicemailDetectionConfiguration,
|
||||
type WorkflowConfigurations,
|
||||
|
|
@ -280,6 +284,15 @@ function GeneralSection({
|
|||
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 [turnStartStrategy, setTurnStartStrategy] = useState<TurnStartStrategy>(
|
||||
workflowConfigurations.turn_start_strategy || "default",
|
||||
);
|
||||
const [turnStartMinWords, setTurnStartMinWords] = useState(
|
||||
workflowConfigurations.turn_start_min_words || DEFAULT_TURN_START_MIN_WORDS,
|
||||
);
|
||||
const [provisionalVadPauseSecs, setProvisionalVadPauseSecs] = useState(
|
||||
workflowConfigurations.provisional_vad_pause_secs || DEFAULT_PROVISIONAL_VAD_PAUSE_SECS,
|
||||
);
|
||||
const [turnStopStrategy, setTurnStopStrategy] = useState<TurnStopStrategy>(
|
||||
workflowConfigurations.turn_stop_strategy || "transcription",
|
||||
);
|
||||
|
|
@ -291,6 +304,9 @@ function GeneralSection({
|
|||
const [audioUploadError, setAudioUploadError] = useState<string | null>(null);
|
||||
const ambientFileInputRef = useRef<HTMLInputElement>(null);
|
||||
const { playingId, toggle: togglePlayback } = useAudioPlayback();
|
||||
const selectedTurnStartStrategy = TURN_START_STRATEGY_OPTIONS.find(
|
||||
(option) => option.value === turnStartStrategy,
|
||||
);
|
||||
|
||||
const isDirty = useMemo(() => {
|
||||
const initAmbient = workflowConfigurations.ambient_noise_configuration || DEFAULT_AMBIENT_NOISE_CONFIG;
|
||||
|
|
@ -300,10 +316,13 @@ function GeneralSection({
|
|||
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)
|
||||
);
|
||||
}, [name, workflowName, ambientNoiseConfig, maxCallDuration, maxUserIdleTimeout, smartTurnStopSecs, turnStopStrategy, contextCompactionEnabled, workflowConfigurations]);
|
||||
}, [name, workflowName, ambientNoiseConfig, maxCallDuration, maxUserIdleTimeout, smartTurnStopSecs, turnStartStrategy, turnStartMinWords, provisionalVadPauseSecs, turnStopStrategy, contextCompactionEnabled, workflowConfigurations]);
|
||||
|
||||
useUnsavedChanges("general", isDirty);
|
||||
|
||||
|
|
@ -375,6 +394,9 @@ function GeneralSection({
|
|||
max_call_duration: maxCallDuration,
|
||||
max_user_idle_timeout: maxUserIdleTimeout,
|
||||
smart_turn_stop_secs: smartTurnStopSecs,
|
||||
turn_start_strategy: turnStartStrategy,
|
||||
turn_start_min_words: turnStartMinWords,
|
||||
provisional_vad_pause_secs: provisionalVadPauseSecs,
|
||||
turn_stop_strategy: turnStopStrategy,
|
||||
context_compaction_enabled: contextCompactionEnabled,
|
||||
},
|
||||
|
|
@ -589,6 +611,71 @@ function GeneralSection({
|
|||
</p>
|
||||
</div>
|
||||
)}
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="turn_start_strategy" className="text-xs">Interruption Strategy</Label>
|
||||
<Select
|
||||
value={turnStartStrategy}
|
||||
onValueChange={(value: TurnStartStrategy) => setTurnStartStrategy(value)}
|
||||
>
|
||||
<SelectTrigger id="turn_start_strategy">
|
||||
<SelectValue placeholder="Select strategy" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{TURN_START_STRATEGY_OPTIONS.map((option) => (
|
||||
<SelectItem key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{selectedTurnStartStrategy?.description}
|
||||
</p>
|
||||
</div>
|
||||
{turnStartStrategy === "min_words" && (
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="turn_start_min_words" className="text-xs">
|
||||
Minimum Words Before Interruption
|
||||
</Label>
|
||||
<Input
|
||||
id="turn_start_min_words"
|
||||
type="number"
|
||||
step="1"
|
||||
min="1"
|
||||
max="10"
|
||||
value={turnStartMinWords}
|
||||
onChange={(e) => {
|
||||
const value = parseInt(e.target.value);
|
||||
if (!isNaN(value) && value >= 1) setTurnStartMinWords(value);
|
||||
}}
|
||||
/>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Number of transcribed words needed to interrupt while the bot is speaking. Default: {DEFAULT_TURN_START_MIN_WORDS}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
{turnStartStrategy === "provisional_vad" && (
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="provisional_vad_pause_secs" className="text-xs">
|
||||
Provisional Pause (seconds)
|
||||
</Label>
|
||||
<Input
|
||||
id="provisional_vad_pause_secs"
|
||||
type="number"
|
||||
step="0.1"
|
||||
min="0.1"
|
||||
max="5"
|
||||
value={provisionalVadPauseSecs}
|
||||
onChange={(e) => {
|
||||
const value = parseFloat(e.target.value);
|
||||
if (!isNaN(value) && value >= 0.1) setProvisionalVadPauseSecs(value);
|
||||
}}
|
||||
/>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Seconds to pause bot audio while waiting for transcript confirmation. Default: {DEFAULT_PROVISIONAL_VAD_PAUSE_SECS}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<Separator />
|
||||
|
|
|
|||
|
|
@ -9,6 +9,31 @@ export interface AmbientNoiseConfiguration {
|
|||
}
|
||||
|
||||
export type TurnStopStrategy = 'transcription' | 'turn_analyzer';
|
||||
export type TurnStartStrategy = 'default' | 'min_words' | 'provisional_vad';
|
||||
export const DEFAULT_TURN_START_MIN_WORDS = 3;
|
||||
export const DEFAULT_PROVISIONAL_VAD_PAUSE_SECS = 1.5;
|
||||
|
||||
export const TURN_START_STRATEGY_OPTIONS: Array<{
|
||||
value: TurnStartStrategy;
|
||||
label: string;
|
||||
description: string;
|
||||
}> = [
|
||||
{
|
||||
value: 'default',
|
||||
label: 'Default',
|
||||
description: 'Use the platform default: external STT turn signals when available, otherwise local VAD.',
|
||||
},
|
||||
{
|
||||
value: 'min_words',
|
||||
label: 'Minimum words',
|
||||
description: 'Wait for a minimum number of transcribed words before interrupting bot speech.',
|
||||
},
|
||||
{
|
||||
value: 'provisional_vad',
|
||||
label: 'Provisional VAD',
|
||||
description: 'Pause bot audio on voice activity, then confirm the interruption with transcription.',
|
||||
},
|
||||
];
|
||||
|
||||
export interface VoicemailDetectionConfiguration {
|
||||
enabled: boolean;
|
||||
|
|
@ -61,6 +86,9 @@ export interface WorkflowConfigurations {
|
|||
max_call_duration: number; // Maximum call duration in seconds
|
||||
max_user_idle_timeout: number; // Maximum user idle time in seconds
|
||||
smart_turn_stop_secs: number; // Timeout in seconds for incomplete turn detection
|
||||
turn_start_strategy: TurnStartStrategy; // Strategy for detecting start of user turn/interruption
|
||||
turn_start_min_words: number; // Minimum transcribed words required for minimum-word interruptions
|
||||
provisional_vad_pause_secs: number; // Seconds to pause bot output while awaiting transcript confirmation
|
||||
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;
|
||||
|
|
@ -78,6 +106,9 @@ export const DEFAULT_WORKFLOW_CONFIGURATIONS: WorkflowConfigurations = {
|
|||
max_call_duration: 600, // 10 minutes
|
||||
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: ''
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue