rasa-pro 3.10.16__py3-none-any.whl → 3.11.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of rasa-pro might be problematic. Click here for more details.
- rasa/__main__.py +31 -15
- rasa/api.py +12 -2
- rasa/cli/arguments/default_arguments.py +24 -4
- rasa/cli/arguments/run.py +15 -0
- rasa/cli/arguments/shell.py +5 -1
- rasa/cli/arguments/train.py +17 -9
- rasa/cli/evaluate.py +7 -7
- rasa/cli/inspect.py +19 -7
- rasa/cli/interactive.py +1 -0
- rasa/cli/llm_fine_tuning.py +11 -14
- rasa/cli/project_templates/calm/config.yml +5 -7
- rasa/cli/project_templates/calm/endpoints.yml +15 -2
- rasa/cli/project_templates/tutorial/config.yml +8 -5
- rasa/cli/project_templates/tutorial/data/flows.yml +1 -1
- rasa/cli/project_templates/tutorial/data/patterns.yml +5 -0
- rasa/cli/project_templates/tutorial/domain.yml +14 -0
- rasa/cli/project_templates/tutorial/endpoints.yml +5 -0
- rasa/cli/run.py +7 -0
- rasa/cli/scaffold.py +4 -2
- rasa/cli/studio/upload.py +0 -15
- rasa/cli/train.py +14 -53
- rasa/cli/utils.py +14 -11
- rasa/cli/x.py +7 -7
- rasa/constants.py +3 -1
- rasa/core/actions/action.py +77 -33
- rasa/core/actions/action_hangup.py +29 -0
- rasa/core/actions/action_repeat_bot_messages.py +89 -0
- rasa/core/actions/e2e_stub_custom_action_executor.py +5 -1
- rasa/core/actions/http_custom_action_executor.py +4 -0
- rasa/core/agent.py +2 -2
- rasa/core/brokers/kafka.py +3 -1
- rasa/core/brokers/pika.py +3 -1
- rasa/core/channels/__init__.py +10 -6
- rasa/core/channels/channel.py +41 -4
- rasa/core/channels/development_inspector.py +150 -46
- rasa/core/channels/inspector/README.md +1 -1
- rasa/core/channels/inspector/dist/assets/{arc-b6e548fe.js → arc-bc141fb2.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{c4Diagram-d0fbc5ce-fa03ac9e.js → c4Diagram-d0fbc5ce-be2db283.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{classDiagram-936ed81e-ee67392a.js → classDiagram-936ed81e-55366915.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{classDiagram-v2-c3cb15f1-9b283fae.js → classDiagram-v2-c3cb15f1-bb529518.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{createText-62fc7601-8b6fcc2a.js → createText-62fc7601-b0ec81d6.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{edges-f2ad444c-22e77f4f.js → edges-f2ad444c-6166330c.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{erDiagram-9d236eb7-60ffc87f.js → erDiagram-9d236eb7-5ccc6a8e.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{flowDb-1972c806-9dd802e4.js → flowDb-1972c806-fca3bfe4.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{flowDiagram-7ea5b25a-5fa1912f.js → flowDiagram-7ea5b25a-4739080f.js} +1 -1
- rasa/core/channels/inspector/dist/assets/flowDiagram-v2-855bc5b3-736177bf.js +1 -0
- rasa/core/channels/inspector/dist/assets/{flowchart-elk-definition-abe16c3d-622a1fd2.js → flowchart-elk-definition-abe16c3d-7c1b0e0f.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{ganttDiagram-9b5ea136-e285a63a.js → ganttDiagram-9b5ea136-772fd050.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{gitGraphDiagram-99d0ae7c-f237bdca.js → gitGraphDiagram-99d0ae7c-8eae1dc9.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{index-2c4b9a3b-4b03d70e.js → index-2c4b9a3b-f55afcdf.js} +1 -1
- rasa/core/channels/inspector/dist/assets/index-e7cef9de.js +1317 -0
- rasa/core/channels/inspector/dist/assets/{infoDiagram-736b4530-72a0fa5f.js → infoDiagram-736b4530-124d4a14.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{journeyDiagram-df861f2b-82218c41.js → journeyDiagram-df861f2b-7c4fae44.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{layout-78cff630.js → layout-b9885fb6.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{line-5038b469.js → line-7c59abb6.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{linear-c4fc4098.js → linear-4776f780.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{mindmap-definition-beec6740-c33c8ea6.js → mindmap-definition-beec6740-2332c46c.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{pieDiagram-dbbf0591-a8d03059.js → pieDiagram-dbbf0591-8fb39303.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{quadrantDiagram-4d7f4fd6-6a0e56b2.js → quadrantDiagram-4d7f4fd6-3c7180a2.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{requirementDiagram-6fc4c22a-2dc7c7bd.js → requirementDiagram-6fc4c22a-e910bcb8.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{sankeyDiagram-8f13d901-2360fe39.js → sankeyDiagram-8f13d901-ead16c89.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{sequenceDiagram-b655622a-41b9f9ad.js → sequenceDiagram-b655622a-29a02a19.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{stateDiagram-59f0c015-0aad326f.js → stateDiagram-59f0c015-042b3137.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{stateDiagram-v2-2b26beab-9847d984.js → stateDiagram-v2-2b26beab-2178c0f3.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{styles-080da4f6-564d890e.js → styles-080da4f6-23ffa4fc.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{styles-3dcbcfbf-38957613.js → styles-3dcbcfbf-94f59763.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{styles-9c745c82-f0fc6921.js → styles-9c745c82-78a6bebc.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{svgDrawCommon-4835440b-ef3c5a77.js → svgDrawCommon-4835440b-eae2a6f6.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{timeline-definition-5b62e21b-bf3e91c1.js → timeline-definition-5b62e21b-5c968d92.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{xychartDiagram-2b33534f-4d4026c0.js → xychartDiagram-2b33534f-fd3db0d5.js} +1 -1
- rasa/core/channels/inspector/dist/index.html +18 -17
- rasa/core/channels/inspector/index.html +17 -16
- rasa/core/channels/inspector/package.json +5 -1
- rasa/core/channels/inspector/src/App.tsx +118 -68
- rasa/core/channels/inspector/src/components/Chat.tsx +95 -0
- rasa/core/channels/inspector/src/components/DiagramFlow.tsx +11 -10
- rasa/core/channels/inspector/src/components/DialogueStack.tsx +10 -25
- rasa/core/channels/inspector/src/components/LoadingSpinner.tsx +6 -3
- rasa/core/channels/inspector/src/helpers/audiostream.ts +165 -0
- rasa/core/channels/inspector/src/helpers/formatters.test.ts +10 -0
- rasa/core/channels/inspector/src/helpers/formatters.ts +107 -41
- rasa/core/channels/inspector/src/helpers/utils.ts +92 -7
- rasa/core/channels/inspector/src/types.ts +21 -1
- rasa/core/channels/inspector/yarn.lock +94 -1
- rasa/core/channels/rest.py +51 -46
- rasa/core/channels/socketio.py +28 -1
- rasa/core/channels/telegram.py +1 -1
- rasa/core/channels/twilio.py +1 -1
- rasa/core/channels/{audiocodes.py → voice_ready/audiocodes.py} +122 -69
- rasa/core/channels/{voice_aware → voice_ready}/jambonz.py +26 -8
- rasa/core/channels/{voice_aware → voice_ready}/jambonz_protocol.py +57 -5
- rasa/core/channels/{twilio_voice.py → voice_ready/twilio_voice.py} +64 -28
- rasa/core/channels/voice_ready/utils.py +37 -0
- rasa/core/channels/voice_stream/asr/__init__.py +0 -0
- rasa/core/channels/voice_stream/asr/asr_engine.py +89 -0
- rasa/core/channels/voice_stream/asr/asr_event.py +18 -0
- rasa/core/channels/voice_stream/asr/azure.py +129 -0
- rasa/core/channels/voice_stream/asr/deepgram.py +90 -0
- rasa/core/channels/voice_stream/audio_bytes.py +8 -0
- rasa/core/channels/voice_stream/browser_audio.py +107 -0
- rasa/core/channels/voice_stream/call_state.py +23 -0
- rasa/core/channels/voice_stream/tts/__init__.py +0 -0
- rasa/core/channels/voice_stream/tts/azure.py +106 -0
- rasa/core/channels/voice_stream/tts/cartesia.py +118 -0
- rasa/core/channels/voice_stream/tts/tts_cache.py +27 -0
- rasa/core/channels/voice_stream/tts/tts_engine.py +58 -0
- rasa/core/channels/voice_stream/twilio_media_streams.py +173 -0
- rasa/core/channels/voice_stream/util.py +57 -0
- rasa/core/channels/voice_stream/voice_channel.py +427 -0
- rasa/core/information_retrieval/qdrant.py +1 -0
- rasa/core/nlg/contextual_response_rephraser.py +45 -17
- rasa/{nlu → core}/persistor.py +203 -68
- rasa/core/policies/enterprise_search_policy.py +119 -63
- rasa/core/policies/flows/flow_executor.py +15 -22
- rasa/core/policies/intentless_policy.py +83 -28
- rasa/core/processor.py +25 -0
- rasa/core/run.py +12 -2
- rasa/core/secrets_manager/constants.py +4 -0
- rasa/core/secrets_manager/factory.py +8 -0
- rasa/core/secrets_manager/vault.py +11 -1
- rasa/core/training/interactive.py +33 -34
- rasa/core/utils.py +47 -21
- rasa/dialogue_understanding/coexistence/llm_based_router.py +41 -14
- rasa/dialogue_understanding/commands/__init__.py +6 -0
- rasa/dialogue_understanding/commands/repeat_bot_messages_command.py +60 -0
- rasa/dialogue_understanding/commands/session_end_command.py +61 -0
- rasa/dialogue_understanding/commands/user_silence_command.py +59 -0
- rasa/dialogue_understanding/commands/utils.py +5 -0
- rasa/dialogue_understanding/generator/constants.py +2 -0
- rasa/dialogue_understanding/generator/flow_retrieval.py +47 -9
- rasa/dialogue_understanding/generator/llm_based_command_generator.py +38 -15
- rasa/dialogue_understanding/generator/llm_command_generator.py +1 -1
- rasa/dialogue_understanding/generator/multi_step/multi_step_llm_command_generator.py +35 -13
- rasa/dialogue_understanding/generator/single_step/command_prompt_template.jinja2 +3 -0
- rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +60 -13
- rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +53 -0
- rasa/dialogue_understanding/patterns/repeat.py +37 -0
- rasa/dialogue_understanding/patterns/user_silence.py +37 -0
- rasa/dialogue_understanding/processor/command_processor.py +21 -1
- rasa/e2e_test/aggregate_test_stats_calculator.py +1 -11
- rasa/e2e_test/assertions.py +136 -61
- rasa/e2e_test/assertions_schema.yml +23 -0
- rasa/e2e_test/e2e_test_case.py +85 -6
- rasa/e2e_test/e2e_test_runner.py +2 -3
- rasa/e2e_test/utils/e2e_yaml_utils.py +1 -1
- rasa/engine/graph.py +3 -10
- rasa/engine/loader.py +12 -0
- rasa/engine/recipes/config_files/default_config.yml +0 -3
- rasa/engine/recipes/default_recipe.py +0 -1
- rasa/engine/recipes/graph_recipe.py +0 -1
- rasa/engine/runner/dask.py +2 -2
- rasa/engine/storage/local_model_storage.py +12 -42
- rasa/engine/storage/storage.py +1 -5
- rasa/engine/validation.py +527 -74
- rasa/model_manager/__init__.py +0 -0
- rasa/model_manager/config.py +40 -0
- rasa/model_manager/model_api.py +559 -0
- rasa/model_manager/runner_service.py +286 -0
- rasa/model_manager/socket_bridge.py +146 -0
- rasa/model_manager/studio_jwt_auth.py +86 -0
- rasa/model_manager/trainer_service.py +325 -0
- rasa/model_manager/utils.py +87 -0
- rasa/model_manager/warm_rasa_process.py +187 -0
- rasa/model_service.py +112 -0
- rasa/model_training.py +42 -23
- rasa/nlu/tokenizers/whitespace_tokenizer.py +3 -14
- rasa/server.py +4 -2
- rasa/shared/constants.py +60 -8
- rasa/shared/core/constants.py +13 -0
- rasa/shared/core/domain.py +107 -50
- rasa/shared/core/events.py +29 -0
- rasa/shared/core/flows/flow.py +5 -0
- rasa/shared/core/flows/flows_list.py +19 -6
- rasa/shared/core/flows/flows_yaml_schema.json +10 -0
- rasa/shared/core/flows/utils.py +39 -0
- rasa/shared/core/flows/validation.py +121 -0
- rasa/shared/core/flows/yaml_flows_io.py +15 -27
- rasa/shared/core/slots.py +5 -0
- rasa/shared/importers/importer.py +59 -41
- rasa/shared/importers/multi_project.py +23 -11
- rasa/shared/importers/rasa.py +12 -3
- rasa/shared/importers/remote_importer.py +196 -0
- rasa/shared/importers/utils.py +3 -1
- rasa/shared/nlu/training_data/formats/rasa_yaml.py +18 -3
- rasa/shared/nlu/training_data/training_data.py +18 -19
- rasa/shared/providers/_configs/litellm_router_client_config.py +220 -0
- rasa/shared/providers/_configs/model_group_config.py +167 -0
- rasa/shared/providers/_configs/openai_client_config.py +1 -1
- rasa/shared/providers/_configs/rasa_llm_client_config.py +73 -0
- rasa/shared/providers/_configs/self_hosted_llm_client_config.py +1 -0
- rasa/shared/providers/_configs/utils.py +16 -0
- rasa/shared/providers/_utils.py +79 -0
- rasa/shared/providers/embedding/_base_litellm_embedding_client.py +13 -29
- rasa/shared/providers/embedding/azure_openai_embedding_client.py +54 -21
- rasa/shared/providers/embedding/default_litellm_embedding_client.py +24 -0
- rasa/shared/providers/embedding/litellm_router_embedding_client.py +135 -0
- rasa/shared/providers/llm/_base_litellm_client.py +34 -22
- rasa/shared/providers/llm/azure_openai_llm_client.py +50 -29
- rasa/shared/providers/llm/default_litellm_llm_client.py +24 -0
- rasa/shared/providers/llm/litellm_router_llm_client.py +182 -0
- rasa/shared/providers/llm/rasa_llm_client.py +112 -0
- rasa/shared/providers/llm/self_hosted_llm_client.py +5 -29
- rasa/shared/providers/mappings.py +19 -0
- rasa/shared/providers/router/__init__.py +0 -0
- rasa/shared/providers/router/_base_litellm_router_client.py +183 -0
- rasa/shared/providers/router/router_client.py +73 -0
- rasa/shared/utils/common.py +40 -24
- rasa/shared/utils/health_check/__init__.py +0 -0
- rasa/shared/utils/health_check/embeddings_health_check_mixin.py +31 -0
- rasa/shared/utils/health_check/health_check.py +258 -0
- rasa/shared/utils/health_check/llm_health_check_mixin.py +31 -0
- rasa/shared/utils/io.py +27 -6
- rasa/shared/utils/llm.py +354 -44
- rasa/shared/utils/schemas/events.py +2 -0
- rasa/shared/utils/schemas/model_config.yml +0 -10
- rasa/shared/utils/yaml.py +181 -38
- rasa/studio/data_handler.py +3 -1
- rasa/studio/upload.py +160 -74
- rasa/telemetry.py +94 -17
- rasa/tracing/config.py +3 -1
- rasa/tracing/instrumentation/attribute_extractors.py +95 -18
- rasa/tracing/instrumentation/instrumentation.py +121 -0
- rasa/utils/common.py +5 -0
- rasa/utils/endpoints.py +27 -1
- rasa/utils/io.py +8 -16
- rasa/utils/log_utils.py +9 -2
- rasa/utils/sanic_error_handler.py +32 -0
- rasa/validator.py +110 -16
- rasa/version.py +1 -1
- {rasa_pro-3.10.16.dist-info → rasa_pro-3.11.0.dist-info}/METADATA +16 -14
- {rasa_pro-3.10.16.dist-info → rasa_pro-3.11.0.dist-info}/RECORD +236 -185
- rasa/core/channels/inspector/dist/assets/flowDiagram-v2-855bc5b3-1844e5a5.js +0 -1
- rasa/core/channels/inspector/dist/assets/index-a5d3e69d.js +0 -1040
- rasa/core/channels/voice_aware/utils.py +0 -20
- rasa/llm_fine_tuning/notebooks/unsloth_finetuning.ipynb +0 -407
- /rasa/core/channels/{voice_aware → voice_ready}/__init__.py +0 -0
- /rasa/core/channels/{voice_native → voice_stream}/__init__.py +0 -0
- {rasa_pro-3.10.16.dist-info → rasa_pro-3.11.0.dist-info}/NOTICE +0 -0
- {rasa_pro-3.10.16.dist-info → rasa_pro-3.11.0.dist-info}/WHEEL +0 -0
- {rasa_pro-3.10.16.dist-info → rasa_pro-3.11.0.dist-info}/entry_points.txt +0 -0
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import { Center, Spinner, Text, useColorModeValue } from "@chakra-ui/react";
|
|
1
|
+
import { Center, Spinner, Text, Button, useColorModeValue } from "@chakra-ui/react";
|
|
2
2
|
import { useOurTheme } from "../theme";
|
|
3
|
+
import {createAudioConnection} from "../helpers/audiostream.ts";
|
|
3
4
|
|
|
4
5
|
export const LoadingSpinner = () => {
|
|
5
6
|
const { rasaSpace } = useOurTheme();
|
|
6
|
-
|
|
7
|
+
const isVoice = window.location.href.includes("browser_audio");
|
|
8
|
+
const text = isVoice ? "Start a new conversation" : "Waiting for a new conversation"
|
|
7
9
|
return (
|
|
8
10
|
<Center height={"100vh"} flexDirection="column">
|
|
9
11
|
<Spinner
|
|
@@ -13,7 +15,8 @@ export const LoadingSpinner = () => {
|
|
|
13
15
|
size="lg"
|
|
14
16
|
mb={rasaSpace[1]}
|
|
15
17
|
/>
|
|
16
|
-
<Text>
|
|
18
|
+
<Text fontSize="lg">{text}</Text>
|
|
19
|
+
{isVoice ? <Button onClick={createAudioConnection}>Go</Button> : null}
|
|
17
20
|
</Center>
|
|
18
21
|
);
|
|
19
22
|
};
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
const bufferSize = 4096
|
|
2
|
+
const sampleRate = 8000
|
|
3
|
+
const audioOptions = {
|
|
4
|
+
audio: {
|
|
5
|
+
echoCancellation: true,
|
|
6
|
+
noiseSuppression: true,
|
|
7
|
+
autoGainControl: true
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const arrayBufferToBase64 = ( buffer: ArrayBuffer ): string => {
|
|
12
|
+
let binary = '';
|
|
13
|
+
const bytes = new Uint8Array( buffer );
|
|
14
|
+
const len = bytes.byteLength;
|
|
15
|
+
for (let i = 0; i < len; i++) {
|
|
16
|
+
binary += String.fromCharCode( bytes[ i ] );
|
|
17
|
+
}
|
|
18
|
+
return window.btoa( binary );
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const base64ToArrayBuffer = ( s: string ): ArrayBuffer => {
|
|
22
|
+
const binary_string = window.atob(s);
|
|
23
|
+
const len = binary_string.length;
|
|
24
|
+
const bytes = new Uint8Array( len );
|
|
25
|
+
for (let i = 0; i < len; i++) {
|
|
26
|
+
bytes[i] = binary_string.charCodeAt(i);
|
|
27
|
+
}
|
|
28
|
+
return bytes.buffer;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const floatToIntArray = (arr: Float32Array): Int32Array => {
|
|
32
|
+
// Convert Float Array [-1, 1] to full range int array
|
|
33
|
+
return Int32Array.from(arr, x => x * 0x7fffffff)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const intToFloatArray = (arr: Int32Array): Float32Array => {
|
|
37
|
+
return Float32Array.from(arr, x => (x / 0x7fffffff))
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
interface Mark {
|
|
41
|
+
id: string
|
|
42
|
+
bytesToGo: number
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
interface AudioQueue {
|
|
46
|
+
buffer: Float32Array;
|
|
47
|
+
marks: Array<Mark>
|
|
48
|
+
socket: WebSocket,
|
|
49
|
+
write: (newAudio: Float32Array) => void;
|
|
50
|
+
read: (nSamples: number) => Float32Array;
|
|
51
|
+
length: () => number;
|
|
52
|
+
addMarker: (id: string) => void;
|
|
53
|
+
reduceMarkers: (bytesRead: number) => void;
|
|
54
|
+
popMarkers: () => void;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
const createAudioQueue = (socket: WebSocket) : AudioQueue => {
|
|
59
|
+
return {
|
|
60
|
+
buffer: new Float32Array(0),
|
|
61
|
+
marks: new Array<Mark>(),
|
|
62
|
+
socket,
|
|
63
|
+
|
|
64
|
+
write: function(newAudio: Float32Array) {
|
|
65
|
+
const currentQLength = this.buffer.length;
|
|
66
|
+
const newBuffer = new Float32Array(currentQLength + newAudio.length);
|
|
67
|
+
newBuffer.set(this.buffer, 0);
|
|
68
|
+
newBuffer.set(newAudio, currentQLength);
|
|
69
|
+
this.buffer = newBuffer;
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
read: function(nSamples: number) {
|
|
73
|
+
const samplesToPlay = this.buffer.subarray(0, nSamples);
|
|
74
|
+
this.buffer = this.buffer.subarray(nSamples, this.buffer.length);
|
|
75
|
+
this.reduceMarkers(samplesToPlay.length)
|
|
76
|
+
this.popMarkers()
|
|
77
|
+
return samplesToPlay;
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
length: function() {
|
|
81
|
+
return this.buffer.length;
|
|
82
|
+
},
|
|
83
|
+
|
|
84
|
+
addMarker: function(id: string) {
|
|
85
|
+
this.marks.push({id, bytesToGo: this.length()})
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
reduceMarkers: function(bytesRead: number) {
|
|
89
|
+
this.marks = this.marks.map((m) => {
|
|
90
|
+
return {id: m.id, bytesToGo: m.bytesToGo - bytesRead}
|
|
91
|
+
})
|
|
92
|
+
},
|
|
93
|
+
|
|
94
|
+
popMarkers: function() {
|
|
95
|
+
// marks are ordered
|
|
96
|
+
let popUpTo = 0;
|
|
97
|
+
while (popUpTo < this.marks.length) {
|
|
98
|
+
if (this.marks[popUpTo].bytesToGo <= 0) {
|
|
99
|
+
popUpTo += 1
|
|
100
|
+
} else {
|
|
101
|
+
break
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
const marksToPop = this.marks.slice(0, popUpTo)
|
|
105
|
+
this.marks = this.marks.slice(popUpTo, this.marks.length)
|
|
106
|
+
marksToPop.forEach((m) => {
|
|
107
|
+
this.socket.send(JSON.stringify({marker: m.id}))
|
|
108
|
+
})
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const streamMicrophoneToServer = async (socket: WebSocket) => {
|
|
115
|
+
let audioStream = null;
|
|
116
|
+
const audioContext = new AudioContext({sampleRate});
|
|
117
|
+
|
|
118
|
+
try {
|
|
119
|
+
audioStream = await navigator.mediaDevices.getUserMedia(audioOptions);
|
|
120
|
+
const audioInput = audioContext.createMediaStreamSource(audioStream)
|
|
121
|
+
const sender = audioContext.createScriptProcessor(bufferSize, 1, 1)
|
|
122
|
+
sender.onaudioprocess = function(event) {
|
|
123
|
+
const message = JSON.stringify({
|
|
124
|
+
"audio": arrayBufferToBase64(floatToIntArray(event.inputBuffer.getChannelData(0)).buffer)
|
|
125
|
+
})
|
|
126
|
+
socket.send(message)
|
|
127
|
+
}
|
|
128
|
+
audioInput.connect(sender)
|
|
129
|
+
sender.connect(audioContext.destination)
|
|
130
|
+
} catch (err) {
|
|
131
|
+
console.error(err);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const setupAudioPlayback = (socket: WebSocket): AudioQueue => {
|
|
136
|
+
const audioQueue = createAudioQueue(socket)
|
|
137
|
+
const silence = new Float32Array(bufferSize)
|
|
138
|
+
const audioOutputContext = new AudioContext({sampleRate})
|
|
139
|
+
const scriptNode = audioOutputContext.createScriptProcessor(bufferSize, 1, 1);
|
|
140
|
+
scriptNode.onaudioprocess = function(e) {
|
|
141
|
+
const audioData = audioQueue.length() ? audioQueue.read(bufferSize) : silence
|
|
142
|
+
e.outputBuffer.getChannelData(0).set(audioData);
|
|
143
|
+
}
|
|
144
|
+
scriptNode.connect(audioOutputContext.destination)
|
|
145
|
+
return audioQueue
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const addDataToAudioQueue = (audioQueue: AudioQueue) => (message: MessageEvent<any>) => {
|
|
149
|
+
const data = JSON.parse(message.data.toString())
|
|
150
|
+
if (data["audio"]) {
|
|
151
|
+
const audioBytes = base64ToArrayBuffer(data["audio"])
|
|
152
|
+
const audioData = intToFloatArray(new Int32Array(audioBytes))
|
|
153
|
+
audioQueue.write(audioData);
|
|
154
|
+
} else if (data["marker"]) {
|
|
155
|
+
audioQueue.addMarker(data["marker"])
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export async function createAudioConnection() {
|
|
160
|
+
const websocketURL = "ws://localhost:5005/webhooks/browser_audio/websocket"
|
|
161
|
+
const socket = new WebSocket(websocketURL)
|
|
162
|
+
socket.onopen = async () => { await streamMicrophoneToServer(socket)}
|
|
163
|
+
const audioQueue = setupAudioPlayback(socket)
|
|
164
|
+
socket.onmessage = addDataToAudioQueue(audioQueue)
|
|
165
|
+
}
|
|
@@ -75,6 +75,7 @@ describe("helpers", () => {
|
|
|
75
75
|
{
|
|
76
76
|
event: "user",
|
|
77
77
|
text: "book a restaurant",
|
|
78
|
+
timestamp: "123",
|
|
78
79
|
},
|
|
79
80
|
{
|
|
80
81
|
event: "bot",
|
|
@@ -82,18 +83,22 @@ describe("helpers", () => {
|
|
|
82
83
|
utter_action: "utter_ask_book_restaurant_name_of_restaurant",
|
|
83
84
|
},
|
|
84
85
|
text: "What's the name of the restaurant you are interested in?",
|
|
86
|
+
timestamp: "124",
|
|
85
87
|
},
|
|
86
88
|
{
|
|
87
89
|
event: "user",
|
|
88
90
|
text: "simsim",
|
|
91
|
+
timestamp: "125",
|
|
89
92
|
},
|
|
90
93
|
{
|
|
91
94
|
event: "bot",
|
|
92
95
|
text: "How many people are we talking?",
|
|
96
|
+
timestamp: "126",
|
|
93
97
|
},
|
|
94
98
|
{
|
|
95
99
|
event: "user",
|
|
96
100
|
text: "100",
|
|
101
|
+
timestamp: "127",
|
|
97
102
|
},
|
|
98
103
|
{
|
|
99
104
|
event: "bot",
|
|
@@ -101,6 +106,7 @@ describe("helpers", () => {
|
|
|
101
106
|
utter_action: "utter_ask_book_restaurant_date",
|
|
102
107
|
},
|
|
103
108
|
text: "For which day do you want to book?",
|
|
109
|
+
timestamp: "128",
|
|
104
110
|
},
|
|
105
111
|
];
|
|
106
112
|
const result = formatTestCases(events, sessionId);
|
|
@@ -287,6 +293,7 @@ describe("helpers", () => {
|
|
|
287
293
|
flow_id: "flow_id",
|
|
288
294
|
step_id: "step_id",
|
|
289
295
|
collect: fieldValue,
|
|
296
|
+
ended: false,
|
|
290
297
|
})
|
|
291
298
|
).toEqual(fieldValue);
|
|
292
299
|
});
|
|
@@ -299,6 +306,7 @@ describe("helpers", () => {
|
|
|
299
306
|
frame_id: "frame_id",
|
|
300
307
|
flow_id: "flow_id",
|
|
301
308
|
step_id: "step_id",
|
|
309
|
+
ended: false,
|
|
302
310
|
})
|
|
303
311
|
).toEqual(fieldValue);
|
|
304
312
|
});
|
|
@@ -312,6 +320,7 @@ describe("helpers", () => {
|
|
|
312
320
|
flow_id: "flow_id",
|
|
313
321
|
step_id: "step_id",
|
|
314
322
|
collect: fieldValue,
|
|
323
|
+
ended: false,
|
|
315
324
|
})
|
|
316
325
|
).toEqual(`${fieldValue} is not null`);
|
|
317
326
|
});
|
|
@@ -325,6 +334,7 @@ describe("helpers", () => {
|
|
|
325
334
|
flow_id: "flow_id",
|
|
326
335
|
step_id: "step_id",
|
|
327
336
|
collect: fieldValue,
|
|
337
|
+
ended: false,
|
|
328
338
|
})
|
|
329
339
|
).toEqual(`not ${fieldValue}`);
|
|
330
340
|
});
|
|
@@ -48,19 +48,20 @@ ${steps.join("\n")}`;
|
|
|
48
48
|
|
|
49
49
|
function hashCode(str: string) {
|
|
50
50
|
var hash = 0,
|
|
51
|
-
i,
|
|
51
|
+
i,
|
|
52
|
+
chr;
|
|
52
53
|
if (str.length === 0) return hash;
|
|
53
54
|
for (i = 0; i < str.length; i++) {
|
|
54
55
|
chr = str.charCodeAt(i);
|
|
55
|
-
hash = (
|
|
56
|
+
hash = (hash << 5) - hash + chr;
|
|
56
57
|
hash |= 0; // Convert to 32bit integer
|
|
57
58
|
}
|
|
58
59
|
return hash;
|
|
59
|
-
}
|
|
60
|
+
}
|
|
60
61
|
|
|
61
62
|
function mermaidIdForTitle(title: string) {
|
|
62
|
-
return `id${hashCode(title)}
|
|
63
|
-
}
|
|
63
|
+
return `id${hashCode(title)}`;
|
|
64
|
+
}
|
|
64
65
|
|
|
65
66
|
const encodeDoubleQuotes = (str: string) =>
|
|
66
67
|
/**
|
|
@@ -68,13 +69,14 @@ const encodeDoubleQuotes = (str: string) =>
|
|
|
68
69
|
*/
|
|
69
70
|
str.replace(/"/g, `#34;`);
|
|
70
71
|
|
|
71
|
-
|
|
72
72
|
export const formatFlow = (
|
|
73
73
|
slots: Slot[],
|
|
74
74
|
currentStack?: Stack,
|
|
75
75
|
flow?: Flow,
|
|
76
|
-
|
|
76
|
+
stepTrail?: string[]
|
|
77
77
|
) => {
|
|
78
|
+
const activeStep = currentStack?.step_id;
|
|
79
|
+
|
|
78
80
|
if (!flow) {
|
|
79
81
|
return "";
|
|
80
82
|
}
|
|
@@ -86,18 +88,20 @@ classDef action fill:#FBFCFD,stroke:#A0B8CF
|
|
|
86
88
|
classDef link fill:#f43
|
|
87
89
|
classDef slot fill:#e8f3db,stroke:#c5e1a5
|
|
88
90
|
classDef endstep fill:#ccc,stroke:#444
|
|
91
|
+
classDef previous stroke:${rasaColors.rasaOrange[400]},stroke-width:1px
|
|
89
92
|
classDef active stroke:${rasaColors.rasaOrange[400]},stroke-width:3px,fill:${rasaColors.warning[50]}
|
|
90
93
|
`,
|
|
91
94
|
];
|
|
92
|
-
|
|
93
95
|
try {
|
|
94
96
|
const text = renderStepSequence(
|
|
95
97
|
flow.steps,
|
|
96
98
|
slots,
|
|
97
99
|
currentStack,
|
|
98
|
-
activeStep
|
|
100
|
+
activeStep,
|
|
101
|
+
stepTrail
|
|
99
102
|
);
|
|
100
103
|
mermaidText.push(text);
|
|
104
|
+
mermaidText.push(colorDoubleEdges(mermaidText.join("")));
|
|
101
105
|
return mermaidText.join("");
|
|
102
106
|
} catch (e) {
|
|
103
107
|
return `${mermaidText}\nA["Something went wrong!"]\nB["${e}"]`;
|
|
@@ -112,6 +116,30 @@ function truncate(str: string, limit = 35) {
|
|
|
112
116
|
return str;
|
|
113
117
|
}
|
|
114
118
|
|
|
119
|
+
function colorDoubleEdges(mermaidText: string) {
|
|
120
|
+
// go through the lines of mermaid text. keep a counter counting edges
|
|
121
|
+
// ("-->"" or "==>"). if "==>" is found in a line, add the line number to
|
|
122
|
+
// a list.
|
|
123
|
+
const lines = mermaidText.split("\n");
|
|
124
|
+
const coloredEdges = [];
|
|
125
|
+
let edgeCounter = 0;
|
|
126
|
+
for (let i = 0; i < lines.length; i++) {
|
|
127
|
+
if (lines[i].includes("-->")) {
|
|
128
|
+
edgeCounter++;
|
|
129
|
+
} else if (lines[i].includes("==>")) {
|
|
130
|
+
coloredEdges.push(edgeCounter);
|
|
131
|
+
edgeCounter++;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
if(coloredEdges.length > 0) {
|
|
135
|
+
return `linkStyle ${coloredEdges.join(",")} stroke:${
|
|
136
|
+
rasaColors.rasaOrange[400]
|
|
137
|
+
}, ;\n`;
|
|
138
|
+
} else {
|
|
139
|
+
return "";
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
115
143
|
export function parseFieldUsingStack(name: string, stack?: Stack): string {
|
|
116
144
|
// name might be in the `{{context.field_in_stack}}` format so we're stripping everything except the field in the stack name
|
|
117
145
|
const parsedField = name.split(/{{context\.|}}/);
|
|
@@ -122,7 +150,7 @@ export function parseFieldUsingStack(name: string, stack?: Stack): string {
|
|
|
122
150
|
const stackField = parsedField[1];
|
|
123
151
|
|
|
124
152
|
// @ts-expect-error `stack[stackField]` doesn't necessary exists this might return `undefined`
|
|
125
|
-
const stackValue = stack ? stack[stackField]: undefined;
|
|
153
|
+
const stackValue = stack ? stack[stackField] : undefined;
|
|
126
154
|
|
|
127
155
|
// name might also be in the `condition {{context.field_in_stack}} condition` format
|
|
128
156
|
// so we want to keep that if there is any
|
|
@@ -133,11 +161,18 @@ export function parseFieldUsingStack(name: string, stack?: Stack): string {
|
|
|
133
161
|
return `${parsedField[0]}${stackValue}`;
|
|
134
162
|
}
|
|
135
163
|
|
|
164
|
+
function arrowTypeFor(stepId: string, nextId: string, stepTrail?: string[]) {
|
|
165
|
+
return stepTrail?.includes(stepId) && stepTrail?.includes(nextId)
|
|
166
|
+
? "==>"
|
|
167
|
+
: "-->";
|
|
168
|
+
}
|
|
169
|
+
|
|
136
170
|
function renderStepSequence(
|
|
137
171
|
steps: Flow["steps"],
|
|
138
172
|
slots: Slot[],
|
|
139
173
|
currentStack?: Stack,
|
|
140
|
-
activeStep?: string
|
|
174
|
+
activeStep?: string,
|
|
175
|
+
stepTrail?: string[]
|
|
141
176
|
) {
|
|
142
177
|
let hasUsedEndStep = false;
|
|
143
178
|
let mermaidTextFragment = "";
|
|
@@ -147,16 +182,19 @@ function renderStepSequence(
|
|
|
147
182
|
|
|
148
183
|
if (step.collect) {
|
|
149
184
|
const slot = slots.find((slot) => slot.name === step.collect);
|
|
150
|
-
const slotValue =
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
185
|
+
const slotValue =
|
|
186
|
+
slot && typeof slot.value === "string"
|
|
187
|
+
? `"${encodeDoubleQuotes(truncate(slot.value))}"`
|
|
188
|
+
: "💬";
|
|
189
|
+
mermaidTextFragment += `${mermaidId}["${encodeDoubleQuotes(
|
|
190
|
+
truncate(parseFieldUsingStack(step.collect, currentStack))
|
|
191
|
+
)}\n${slotValue}"]:::collect\n`;
|
|
154
192
|
}
|
|
155
193
|
|
|
156
194
|
if (step.action) {
|
|
157
|
-
mermaidTextFragment += `${mermaidId}["${encodeDoubleQuotes(
|
|
158
|
-
parseFieldUsingStack(step.action, currentStack)
|
|
159
|
-
)
|
|
195
|
+
mermaidTextFragment += `${mermaidId}["${encodeDoubleQuotes(
|
|
196
|
+
truncate(parseFieldUsingStack(step.action, currentStack))
|
|
197
|
+
)}"]:::action\n`;
|
|
160
198
|
}
|
|
161
199
|
|
|
162
200
|
if (step.link) {
|
|
@@ -167,20 +205,27 @@ function renderStepSequence(
|
|
|
167
205
|
}
|
|
168
206
|
|
|
169
207
|
if (step.set_slots) {
|
|
170
|
-
mermaidTextFragment += `${mermaidId}["✍️ ${encodeDoubleQuotes(
|
|
208
|
+
mermaidTextFragment += `${mermaidId}["✍️ ${encodeDoubleQuotes(
|
|
209
|
+
stepId
|
|
210
|
+
)}"]:::slot\n`;
|
|
171
211
|
}
|
|
172
212
|
|
|
173
213
|
if (activeStep && stepId === activeStep) {
|
|
174
214
|
mermaidTextFragment += `class ${mermaidId} active\n`;
|
|
215
|
+
} else if (stepTrail?.includes(stepId)) {
|
|
216
|
+
mermaidTextFragment += `class ${mermaidId} previous\n`;
|
|
175
217
|
}
|
|
176
218
|
|
|
177
219
|
// if next is an id, then it is a link
|
|
178
220
|
if (step.next && typeof step.next === "string") {
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
221
|
+
const nextId = parseFieldUsingStack(step.next, currentStack);
|
|
222
|
+
|
|
223
|
+
mermaidTextFragment += `${mermaidId} ${arrowTypeFor(
|
|
224
|
+
stepId,
|
|
225
|
+
nextId,
|
|
226
|
+
stepTrail
|
|
227
|
+
)} ${mermaidIdForTitle(nextId)}\n`;
|
|
228
|
+
if (step.next == "END") {
|
|
184
229
|
hasUsedEndStep = true;
|
|
185
230
|
}
|
|
186
231
|
}
|
|
@@ -189,52 +234,73 @@ function renderStepSequence(
|
|
|
189
234
|
if (step.next && Array.isArray(step.next)) {
|
|
190
235
|
step.next.forEach((condition) => {
|
|
191
236
|
if (condition.then && typeof condition.then === "string") {
|
|
192
|
-
mermaidTextFragment += `${mermaidId}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
237
|
+
mermaidTextFragment += `${mermaidId} ${arrowTypeFor(
|
|
238
|
+
stepId,
|
|
239
|
+
condition.then,
|
|
240
|
+
stepTrail
|
|
241
|
+
)}|"${encodeDoubleQuotes(
|
|
242
|
+
parseFieldUsingStack(condition.if, currentStack)
|
|
243
|
+
)}"| ${mermaidIdForTitle(condition.then)}\n`;
|
|
244
|
+
if (condition.then == "END") {
|
|
197
245
|
hasUsedEndStep = true;
|
|
198
246
|
}
|
|
199
247
|
} else if (condition.then) {
|
|
200
|
-
mermaidTextFragment += `${mermaidId}
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
248
|
+
mermaidTextFragment += `${mermaidId} ${arrowTypeFor(
|
|
249
|
+
stepId,
|
|
250
|
+
condition.then[0].id,
|
|
251
|
+
stepTrail
|
|
252
|
+
)}|"${encodeDoubleQuotes(
|
|
253
|
+
parseFieldUsingStack(condition.if, currentStack)
|
|
254
|
+
)}"| ${mermaidIdForTitle(condition.then[0].id)}\n`;
|
|
204
255
|
mermaidTextFragment += renderStepSequence(
|
|
205
256
|
// @ts-expect-error Currently the param for renderStepSequence only accepts a Step, for further improvements we need to change the type to know that it can also be a then step
|
|
206
257
|
condition.then,
|
|
207
258
|
slots,
|
|
208
259
|
currentStack,
|
|
209
|
-
activeStep
|
|
260
|
+
activeStep,
|
|
261
|
+
stepTrail
|
|
210
262
|
);
|
|
211
263
|
}
|
|
212
264
|
|
|
213
265
|
// @ts-expect-error Currently the param for renderStepSequence only accepts a Step, for further improvements we need to change the type to know that it can also be a then step
|
|
214
266
|
if (condition.else && typeof condition.else === "string") {
|
|
267
|
+
mermaidTextFragment += `${mermaidId} ${arrowTypeFor(
|
|
268
|
+
stepId,
|
|
269
|
+
// @ts-expect-error Currently the param for renderStepSequence only accepts a Step, for further improvements we need to change the type to know that it can also be a then step
|
|
270
|
+
condition.else,
|
|
271
|
+
stepTrail
|
|
272
|
+
// @ts-expect-error Currently the param for renderStepSequence only accepts a Step, for further improvements we need to change the type to know that it can also be a then step
|
|
273
|
+
)}|else| ${mermaidIdForTitle(condition.else)}\n`;
|
|
215
274
|
// @ts-expect-error Currently the param for renderStepSequence only accepts a Step, for further improvements we need to change the type to know that it can also be a then step
|
|
216
|
-
|
|
217
|
-
// @ts-expect-error Currently the param for renderStepSequence only accepts a Step, for further improvements we need to change the type to know that it can also be a then step
|
|
218
|
-
if(condition.else == "END") {
|
|
275
|
+
if (condition.else == "END") {
|
|
219
276
|
hasUsedEndStep = true;
|
|
220
277
|
}
|
|
221
278
|
// @ts-expect-error Currently the param for renderStepSequence only accepts a Step, for further improvements we need to change the type to know that it can also be a then step
|
|
222
279
|
} else if (condition.else) {
|
|
223
|
-
|
|
224
|
-
|
|
280
|
+
mermaidTextFragment += `${mermaidId} ${arrowTypeFor(
|
|
281
|
+
stepId,
|
|
282
|
+
// @ts-expect-error Currently the param for renderStepSequence only accepts a Step, for further improvements we need to change the type to know that it can also be a then step
|
|
283
|
+
condition.else[0].id,
|
|
284
|
+
stepTrail
|
|
285
|
+
// @ts-expect-error Currently the param for renderStepSequence only accepts a Step, for further improvements we need to change the type to know that it can also be a then step
|
|
286
|
+
)}|else| ${mermaidIdForTitle(condition.else[0].id)}\n`;
|
|
225
287
|
mermaidTextFragment += renderStepSequence(
|
|
226
288
|
// @ts-expect-error Currently the param for renderStepSequence only accepts a Step, for further improvements we need to change the type to know that it can also be a then step
|
|
227
289
|
condition.else,
|
|
228
290
|
slots,
|
|
229
291
|
currentStack,
|
|
230
|
-
activeStep
|
|
292
|
+
activeStep,
|
|
293
|
+
stepTrail
|
|
231
294
|
);
|
|
232
295
|
}
|
|
233
296
|
});
|
|
234
297
|
}
|
|
235
298
|
});
|
|
236
|
-
if (hasUsedEndStep){
|
|
299
|
+
if (hasUsedEndStep) {
|
|
237
300
|
mermaidTextFragment += `${mermaidIdForTitle("END")}["🏁 END"]:::endstep\n`;
|
|
301
|
+
if (activeStep && "END" === activeStep) {
|
|
302
|
+
mermaidTextFragment += `class ${mermaidIdForTitle("END")} active\n`;
|
|
303
|
+
}
|
|
238
304
|
}
|
|
239
305
|
return mermaidTextFragment;
|
|
240
306
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { SelectedStack, Stack } from "../types";
|
|
1
|
+
import { SelectedStack, Stack, Event } from "../types";
|
|
2
|
+
import { immutableJSONPatch} from 'immutable-json-patch'
|
|
2
3
|
|
|
3
4
|
export const shouldShowTooltip = (text: string) => {
|
|
4
5
|
const textLength = text.length;
|
|
@@ -10,7 +11,76 @@ export const shouldShowTooltip = (text: string) => {
|
|
|
10
11
|
return false;
|
|
11
12
|
};
|
|
12
13
|
|
|
13
|
-
export const
|
|
14
|
+
export const createHistoricalStack = (activeStack: Stack [], events: Event[]): Stack[] => {
|
|
15
|
+
let stackFrames = activeStack.map((frame) => ({
|
|
16
|
+
...frame,
|
|
17
|
+
ended: false,
|
|
18
|
+
}));
|
|
19
|
+
// go through the events looking for flow_completed and append them to the stack
|
|
20
|
+
let historicalStack: Stack[] = [];
|
|
21
|
+
let pastStackFrames: Stack[] = [];
|
|
22
|
+
for (const event of events) {
|
|
23
|
+
if (event.event === "restart") {
|
|
24
|
+
historicalStack = [];
|
|
25
|
+
stackFrames = [];
|
|
26
|
+
} else if (event.event === "stack") {
|
|
27
|
+
let stackUpdate = JSON.parse(event.update || "");
|
|
28
|
+
historicalStack = immutableJSONPatch(historicalStack, stackUpdate)
|
|
29
|
+
for (const frame of historicalStack) {
|
|
30
|
+
if(!frame.flow_id){
|
|
31
|
+
// this is not a stack frame from the flow handler
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
// if the frame is already in stackFrames, skip it
|
|
35
|
+
if(stackFrames.find((f) => f.frame_id === frame.frame_id)) {
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
// if the frame is in pastStackFrames, update the step_id otherwise add it
|
|
39
|
+
const pastFrame = pastStackFrames.find((f) => f.frame_id === frame.frame_id)
|
|
40
|
+
if(pastFrame) {
|
|
41
|
+
pastFrame.step_id = frame.step_id;
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
pastStackFrames.push({...frame, ended: true});
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
// filter out pattern_collect_information frames
|
|
49
|
+
pastStackFrames = pastStackFrames.filter((frame) => frame.flow_id !== "pattern_collect_information");
|
|
50
|
+
return [...pastStackFrames, ...stackFrames];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export const flowStepTrail = (events: Event[]): Record<string, string[]> => {
|
|
54
|
+
let stack: Stack[] = [];
|
|
55
|
+
// mapping from flow id to the steps that were active in that flow
|
|
56
|
+
let activeSteps: { [key: string]: string[] } = {};
|
|
57
|
+
for (const event of events) {
|
|
58
|
+
if (event.event === "restart") {
|
|
59
|
+
stack = [];
|
|
60
|
+
activeSteps = {};
|
|
61
|
+
} else if (event.event === "stack") {
|
|
62
|
+
let stackUpdate = JSON.parse(event.update || "");
|
|
63
|
+
stack = immutableJSONPatch(stack, stackUpdate)
|
|
64
|
+
if (stack.length > 0) {
|
|
65
|
+
let topFrame = stack[stack.length - 1];
|
|
66
|
+
if (!topFrame.flow_id) {
|
|
67
|
+
// this is not a stack frame from the flow handler
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
if (!activeSteps[topFrame.flow_id] || topFrame.step_id === "START") {
|
|
71
|
+
activeSteps[topFrame.flow_id] = [];
|
|
72
|
+
}
|
|
73
|
+
if (!activeSteps[topFrame.flow_id].includes(topFrame.step_id)) {
|
|
74
|
+
activeSteps[topFrame.flow_id].push(topFrame.step_id)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return activeSteps;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
export const updatedActiveFrame = (previous: SelectedStack | undefined, updatedStack: Stack[], events: Event[]) => {
|
|
14
84
|
// try to find the currently active frame in the updated stack
|
|
15
85
|
// if it isn't there anymore, we will show the first non-pattern frame
|
|
16
86
|
// instead
|
|
@@ -23,20 +93,35 @@ export const updatedActiveFrame = (previous: SelectedStack | undefined, updatedS
|
|
|
23
93
|
const activeFrame = updatedStack.find(
|
|
24
94
|
(stackFrame) => stackFrame.frame_id === previous?.stack.frame_id
|
|
25
95
|
);
|
|
26
|
-
if (!activeFrame) {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
96
|
+
if (!activeFrame || activeFrame.ended) {
|
|
97
|
+
if (!updatedStack){
|
|
98
|
+
return undefined;
|
|
99
|
+
}
|
|
100
|
+
// iterate over the stack. select the first frame where the name does not
|
|
101
|
+
// contain "pattern" and that has not
|
|
102
|
+
// ended yet. If there is no such frame, select the topmost frame that has
|
|
103
|
+
// not ended yet. If there is no such frame, select the topmost frame.
|
|
104
|
+
const updatedFrame = updatedStack.slice().reverse().find(
|
|
105
|
+
(frame) =>
|
|
106
|
+
!frame.flow_id?.startsWith("pattern_") && !frame.ended
|
|
107
|
+
) || updatedStack.slice().reverse().find(
|
|
108
|
+
(frame) => !frame.ended
|
|
109
|
+
) || updatedStack[updatedStack.length - 1];
|
|
110
|
+
|
|
31
111
|
if(updatedFrame !== undefined) {
|
|
32
112
|
return {
|
|
33
113
|
stack: updatedFrame,
|
|
34
114
|
isUserSelected: false,
|
|
115
|
+
activatedSteps: flowStepTrail(events)[updatedFrame.flow_id] || [],
|
|
35
116
|
};
|
|
36
117
|
} else {
|
|
37
118
|
return undefined;
|
|
38
119
|
}
|
|
39
120
|
} else {
|
|
121
|
+
if(previous){
|
|
122
|
+
previous.activatedSteps = flowStepTrail(events)[previous.stack.flow_id] || [];
|
|
123
|
+
previous.stack = activeFrame;
|
|
124
|
+
}
|
|
40
125
|
return previous;
|
|
41
126
|
}
|
|
42
127
|
};
|