From 50f65d39d071b60e73a4a8358546cfe27faf6fdb Mon Sep 17 00:00:00 2001 From: Severin Klingler Date: Fri, 20 Dec 2024 11:34:31 +0100 Subject: [PATCH 1/4] feat: add utility flow `wait until done` --- docs/colang-2/examples/test_csl.py | 37 ++++++++++++++++++++++ nemoguardrails/colang/v2_x/library/core.co | 10 ++++++ 2 files changed, 47 insertions(+) diff --git a/docs/colang-2/examples/test_csl.py b/docs/colang-2/examples/test_csl.py index 728364c8e..31509e66b 100644 --- a/docs/colang-2/examples/test_csl.py +++ b/docs/colang-2/examples/test_csl.py @@ -484,6 +484,43 @@ async def test_wait_indefinitely(): await compare_interaction_with_test_script(test_script, colang_code) +@pytest.mark.asyncio +async def test_wait_until_done(): + colang_code = """ +# COLANG_START: test_wait_until_done +import core + +flow handle welcome + user said "hi" + bot say "hello" + +flow main + start handle welcome as $ref + + while True + when wait until done $ref + bot say "greetings done" + break + or when user said "stop" + print "stopping flow" + send $ref.Stop() + +# COLANG_END: test_wait_until_done + """ + + test_script = """ +# USAGE_START: test_wait_until_done +> hi +hello +greetings done +> stop +greetings done +# USAGE_END: test_wait_until_done + """ + + await compare_interaction_with_test_script(test_script, colang_code) + + ######################################################################################################################## # TIMING ######################################################################################################################## diff --git a/nemoguardrails/colang/v2_x/library/core.co b/nemoguardrails/colang/v2_x/library/core.co index 147d4f1d9..cccf9d444 100644 --- a/nemoguardrails/colang/v2_x/library/core.co +++ b/nemoguardrails/colang/v2_x/library/core.co @@ -290,3 +290,13 @@ flow await_flow_by_name $flow_name $new_flow_instance_uid = None send StartFlow(flow_id=$flow_name, flow_instance_uid=$new_flow_instance_uid) match FlowStarted(flow_id=$flow_name, flow_instance_uid=$new_flow_instance_uid) as $event_ref match $event_ref.flow.Finished() + +flow wait until done $ref + """Wait until an action or flow is done (has finished or was stopped).""" + + if type($ref) != "Action" and type($ref) != "FlowState" + log "flow `is done` only supports action and flow references." + abort + + if $ref.status.value != "finished" and $ref.status.value != "stopped": + match $ref.Finished() From f222a9efe1bd2d5c26ec5651e52f9d7aed0f8ae5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Sch=C3=BCller?= Date: Fri, 20 Dec 2024 16:04:41 +0100 Subject: [PATCH 2/4] Improve core flow --- docs/colang-2/examples/test_csl.py | 57 +++++++++++++++------- nemoguardrails/colang/v2_x/library/core.co | 31 +++++++++--- 2 files changed, 64 insertions(+), 24 deletions(-) diff --git a/docs/colang-2/examples/test_csl.py b/docs/colang-2/examples/test_csl.py index 31509e66b..07782ae2b 100644 --- a/docs/colang-2/examples/test_csl.py +++ b/docs/colang-2/examples/test_csl.py @@ -485,37 +485,58 @@ async def test_wait_indefinitely(): @pytest.mark.asyncio -async def test_wait_until_done(): +async def test_it_finished(): colang_code = """ -# COLANG_START: test_wait_until_done +# COLANG_START: test_it_finished import core -flow handle welcome - user said "hi" +flow bot greet bot say "hello" +flow test0 + user said "hi" + await UtteranceBotAction(script="hello") as $ref + await it finished $ref + +flow test1 + user said "hi" + await bot greet as $ref + await it finished $ref + +flow test2 + user said "hi" + start bot greet as $ref + send $ref.Stop() + await it finished $ref + flow main - start handle welcome as $ref + await test0 + bot say "test0 success" + + start test1 as $ref + match $ref.Finished() + bot say "test1 success" - while True - when wait until done $ref - bot say "greetings done" - break - or when user said "stop" - print "stopping flow" - send $ref.Stop() + start test2 as $ref + match $ref.Failed() + bot say "test2 success" -# COLANG_END: test_wait_until_done +# COLANG_END: test_it_finished """ test_script = """ -# USAGE_START: test_wait_until_done +# USAGE_START: test_it_finished +> hi +hello +test0 success +> hi +hello +test1 success > hi hello -greetings done -> stop -greetings done -# USAGE_END: test_wait_until_done +Event: StopUtteranceBotAction +test2 success +# USAGE_END: test_it_finished """ await compare_interaction_with_test_script(test_script, colang_code) diff --git a/nemoguardrails/colang/v2_x/library/core.co b/nemoguardrails/colang/v2_x/library/core.co index cccf9d444..6d182473d 100644 --- a/nemoguardrails/colang/v2_x/library/core.co +++ b/nemoguardrails/colang/v2_x/library/core.co @@ -291,12 +291,31 @@ flow await_flow_by_name $flow_name $new_flow_instance_uid = None match FlowStarted(flow_id=$flow_name, flow_instance_uid=$new_flow_instance_uid) as $event_ref match $event_ref.flow.Finished() -flow wait until done $ref - """Wait until an action or flow is done (has finished or was stopped).""" +flow it finished $ref -> $finished_event_ref + """Wait until a flow or action has finished.""" - if type($ref) != "Action" and type($ref) != "FlowState" - log "flow `is done` only supports action and flow references." + $type = type($ref) + if $type != "Action" and $type != "FlowState": + log "flow `it finished` expects a flow or action reference as parameter." abort - if $ref.status.value != "finished" and $ref.status.value != "stopped": - match $ref.Finished() + if $type == "FlowState" and $ref.status.value == "stopped": + # We abort this flow since it will never finish + abort + + if $ref.status.value != "finished": + match $ref.Finished() as $finished_event_ref + +flow flow_failed $flow_ref -> $failed_event_ref + """Wait until a flow has failed.""" + + if type($flow_ref) != "FlowState": + log "flow `flow_failed` expects a flow reference as parameter." + abort + + if $flow_ref.status.value == "finished": + # We abort this flow since it will never fail + abort + + if $flow_ref.status.value != "stopped": + match $flow_ref.Failed() as $failed_event_ref From 5529c7901bdd04d633fd55a5b13860ce7039b419 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Sch=C3=BCller?= Date: Fri, 20 Dec 2024 16:04:59 +0100 Subject: [PATCH 3/4] Add documentation --- docs/colang-2/language-reference/csl/core.rst | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/docs/colang-2/language-reference/csl/core.rst b/docs/colang-2/language-reference/csl/core.rst index 5355ef754..eb6796778 100644 --- a/docs/colang-2/language-reference/csl/core.rst +++ b/docs/colang-2/language-reference/csl/core.rst @@ -313,6 +313,31 @@ Utilities :dedent: +.. py:function:: it finished + + Wait until a flow or action has finished. This will also check the action's or flow's state and if it has + already finished, continue immediately. If the awaited flow has already failed instead of finished, this flow + will also fail. + + Note: Actions can never fail, even if stopped, but will always finish. If an action was stopped, the ActionFinished + event will have a ``was_stopped=True`` argument. + + Example: + + .. literalinclude:: ../../examples/test_csl.py + :language: colang + :start-after: # COLANG_START: test_it_finished + :end-before: # COLANG_END: test_it_finished + :dedent: + + + .. literalinclude:: ../../examples/test_csl.py + :language: text + :start-after: # USAGE_START: test_it_finished + :end-before: # USAGE_END: test_it_finished + :dedent: + + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ State Tracking Flows ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From 82b4500bece2bb4cc77eee36069df8a135a782c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Sch=C3=BCller?= Date: Fri, 20 Dec 2024 16:55:19 +0100 Subject: [PATCH 4/4] Simplified documentation example --- docs/colang-2/examples/test_csl.py | 96 ++++++++++++++++++++---------- 1 file changed, 63 insertions(+), 33 deletions(-) diff --git a/docs/colang-2/examples/test_csl.py b/docs/colang-2/examples/test_csl.py index 07782ae2b..4eeaa6efa 100644 --- a/docs/colang-2/examples/test_csl.py +++ b/docs/colang-2/examples/test_csl.py @@ -484,6 +484,62 @@ async def test_wait_indefinitely(): await compare_interaction_with_test_script(test_script, colang_code) +# @pytest.mark.asyncio +# async def test_it_finished(): +# colang_code = """ +# # COLANG_START: test_it_finished +# import core + +# flow bot greet +# bot say "hello" + +# flow test0 +# user said "hi" +# await UtteranceBotAction(script="hello") as $ref +# await it finished $ref + +# flow test1 +# user said "hi" +# await bot greet as $ref +# await it finished $ref + +# flow test2 +# user said "hi" +# start bot greet as $ref +# send $ref.Stop() +# await it finished $ref + +# flow main +# await test0 +# bot say "test0 success" + +# start test1 as $ref +# match $ref.Finished() +# bot say "test1 success" + +# start test2 as $ref +# match $ref.Failed() +# bot say "test2 success" + +# # COLANG_END: test_it_finished +# """ + +# test_script = """ +# # USAGE_START: test_it_finished +# > hi +# hello +# test0 success +# > hi +# hello +# test1 success +# > hi +# hello +# Event: StopUtteranceBotAction +# test2 success +# # USAGE_END: test_it_finished +# """ + + @pytest.mark.asyncio async def test_it_finished(): colang_code = """ @@ -493,33 +549,13 @@ async def test_it_finished(): flow bot greet bot say "hello" -flow test0 - user said "hi" - await UtteranceBotAction(script="hello") as $ref - await it finished $ref - -flow test1 - user said "hi" - await bot greet as $ref - await it finished $ref - -flow test2 +flow main user said "hi" start bot greet as $ref - send $ref.Stop() - await it finished $ref - -flow main - await test0 - bot say "test0 success" - - start test1 as $ref - match $ref.Finished() - bot say "test1 success" - - start test2 as $ref - match $ref.Failed() - bot say "test2 success" + it finished $ref + bot say "finish" + it finished $ref + bot say "still finished" # COLANG_END: test_it_finished """ @@ -528,14 +564,8 @@ async def test_it_finished(): # USAGE_START: test_it_finished > hi hello -test0 success -> hi -hello -test1 success -> hi -hello -Event: StopUtteranceBotAction -test2 success +finish +still finished # USAGE_END: test_it_finished """