From afe2c697d09749bb33707f4e4cc91b9caa82427f Mon Sep 17 00:00:00 2001 From: Casper Nielsen Date: Wed, 11 Mar 2026 17:54:00 +0100 Subject: [PATCH 1/7] fix: ensure response format passed in aio. Ensure reconnecting rather than breaking. Correctly bubble up workflow name if set Signed-off-by: Casper Nielsen --- dapr/aio/clients/grpc/client.py | 10 ++++++ dapr/clients/grpc/client.py | 9 ++++++ dapr/clients/grpc/subscription.py | 31 ++++++++++++------- .../dapr/ext/workflow/workflow_runtime.py | 15 ++++++--- 4 files changed, 48 insertions(+), 17 deletions(-) diff --git a/dapr/aio/clients/grpc/client.py b/dapr/aio/clients/grpc/client.py index 1fda5a07c..ed4b7bbaf 100644 --- a/dapr/aio/clients/grpc/client.py +++ b/dapr/aio/clients/grpc/client.py @@ -25,8 +25,10 @@ import grpc.aio # type: ignore from google.protobuf.any_pb2 import Any as GrpcAny +from google.protobuf.duration_pb2 import Duration as GrpcDuration from google.protobuf.empty_pb2 import Empty as GrpcEmpty from google.protobuf.message import Message as GrpcMessage +from google.protobuf.struct_pb2 import Struct as GrpcStruct from grpc import StatusCode # type: ignore from grpc.aio import ( # type: ignore AioRpcError, @@ -1883,6 +1885,8 @@ async def converse_alpha2( temperature: Optional[float] = None, tools: Optional[List[conversation.ConversationTools]] = None, tool_choice: Optional[str] = None, + response_format: Optional[GrpcStruct] = None, + prompt_cache_retention: Optional[GrpcDuration] = None, ) -> conversation.ConversationResponseAlpha2: """Invoke an LLM using the conversation API (Alpha2) with tool calling support. @@ -1896,6 +1900,8 @@ async def converse_alpha2( temperature: Optional temperature setting for the LLM to optimize for creativity or predictability tools: Optional list of tools available for the LLM to call tool_choice: Optional control over which tools can be called ('none', 'auto', 'required', or specific tool name) + response_format: Optional response format (google.protobuf.struct_pb2.Struct, ex: json_schema for structured output) + prompt_cache_retention: Optional retention for prompt cache (google.protobuf.duration_pb2.Duration) Returns: ConversationResponseAlpha2 containing the conversation results with choices and tool calls @@ -1951,6 +1957,10 @@ async def converse_alpha2( request.temperature = temperature if tool_choice is not None: request.tool_choice = tool_choice + if response_format is not None and hasattr(request, 'response_format'): + request.response_format.CopyFrom(response_format) + if prompt_cache_retention is not None and hasattr(request, 'prompt_cache_retention'): + request.prompt_cache_retention.CopyFrom(prompt_cache_retention) try: response, call = await self.retry_policy.run_rpc_async( diff --git a/dapr/clients/grpc/client.py b/dapr/clients/grpc/client.py index fa69c359a..b049f4efc 100644 --- a/dapr/clients/grpc/client.py +++ b/dapr/clients/grpc/client.py @@ -640,6 +640,15 @@ def stream_messages(sub): break except StreamCancelledError: break + except Exception: + # Stream died — reconnect via the subscription's own + # reconnect logic (which waits for the sidecar to be healthy). + try: + sub.reconnect_stream() + except Exception: + # Sidecar still unavailable — back off before retrying + time.sleep(5) + continue def close_subscription(): subscription.close() diff --git a/dapr/clients/grpc/subscription.py b/dapr/clients/grpc/subscription.py index 73cc047ac..f07a08157 100644 --- a/dapr/clients/grpc/subscription.py +++ b/dapr/clients/grpc/subscription.py @@ -1,3 +1,4 @@ +import logging import queue import threading from typing import Optional @@ -13,6 +14,8 @@ ) from dapr.proto import api_v1, appcallback_v1 +logger = logging.getLogger(__name__) + class Subscription: def __init__(self, stub, pubsub_name, topic, metadata=None, dead_letter_topic=None): @@ -67,7 +70,7 @@ def outgoing_request_iterator(): def reconnect_stream(self): self.close() DaprHealth.wait_for_sidecar() - print('Attempting to reconnect...') + logger.info('Subscription stream reconnecting...') self.start() def next_message(self): @@ -84,10 +87,17 @@ def next_message(self): message = next(self._stream) return SubscriptionMessage(message.event_message) except RpcError as e: - # If Dapr can't be reached, wait until it's ready and reconnect the stream - if e.code() == StatusCode.UNAVAILABLE or e.code() == StatusCode.UNKNOWN: - print( - f'gRPC error while reading from stream: {e.details()}, Status Code: {e.code()}' + # If Dapr can't be reached, wait until it's ready and reconnect the stream. + # INTERNAL covers RST_STREAM from cloud proxies (e.g. Diagrid Cloud). + if e.code() in ( + StatusCode.UNAVAILABLE, + StatusCode.UNKNOWN, + StatusCode.INTERNAL, + ): + logger.warning( + 'Subscription stream error (%s): %s — reconnecting', + e.code(), + e.details(), ) self.reconnect_stream() elif e.code() == StatusCode.CANCELLED: @@ -111,7 +121,7 @@ def respond(self, message, status): raise StreamInactiveError('Stream is not active') self._send_queue.put(msg) except Exception as e: - print(f"Can't send message on inactive stream: {e}") + logger.warning("Can't send message on inactive stream: %s", e) def respond_success(self, message): self.respond(message, TopicEventResponse('success').status) @@ -135,15 +145,12 @@ def _is_stream_active(self): return self._stream_active def close(self): + self._set_stream_inactive() if self._stream: try: self._stream.cancel() - self._set_stream_inactive() - except RpcError as e: - if e.code() != StatusCode.CANCELLED: - raise Exception(f'Error while closing stream: {e}') - except Exception as e: - raise Exception(f'Error while closing stream: {e}') + except Exception: + pass # Stream already dead — safe to ignore def __iter__(self): return self diff --git a/ext/dapr-ext-workflow/dapr/ext/workflow/workflow_runtime.py b/ext/dapr-ext-workflow/dapr/ext/workflow/workflow_runtime.py index 9f5edb2b4..9e94924fa 100644 --- a/ext/dapr-ext-workflow/dapr/ext/workflow/workflow_runtime.py +++ b/ext/dapr-ext-workflow/dapr/ext/workflow/workflow_runtime.py @@ -86,7 +86,8 @@ def __init__( ) def register_workflow(self, fn: Workflow, *, name: Optional[str] = None): - self._logger.info(f"Registering workflow '{fn.__name__}' with runtime") + effective_name = name or fn.__name__ + self._logger.info(f"Registering workflow '{effective_name}' with runtime") def orchestrationWrapper(ctx: task.OrchestrationContext, inp: Optional[TInput] = None): """Responsible to call Workflow function in orchestrationWrapper""" @@ -125,8 +126,9 @@ def orchestrationWrapper(ctx: task.OrchestrationContext, inp: Optional[TInput] = def register_versioned_workflow( self, fn: Workflow, *, name: str, version_name: Optional[str] = None, is_latest: bool ): + effective_name = name or fn.__name__ self._logger.info( - f"Registering version {version_name} of workflow '{fn.__name__}' with runtime" + f"Registering version {version_name} of workflow '{effective_name}' with runtime" ) def orchestrationWrapper(ctx: task.OrchestrationContext, inp: Optional[TInput] = None): @@ -162,7 +164,8 @@ def register_activity(self, fn: Activity, *, name: Optional[str] = None): """Registers a workflow activity as a function that takes a specified input type and returns a specified output type. """ - self._logger.info(f"Registering activity '{fn.__name__}' with runtime") + effective_name = name or fn.__name__ + self._logger.info(f"Registering activity '{effective_name}' with runtime") def activityWrapper(ctx: task.ActivityContext, inp: Optional[TInput] = None): """Responsible to call Activity function in activityWrapper""" @@ -176,8 +179,10 @@ def activityWrapper(ctx: task.ActivityContext, inp: Optional[TInput] = None): result = fn(wfActivityContext, inp) return result except Exception as e: - self._logger.exception( - f'Activity execution failed - task_id: {activity_id}, error: {e}' + self._logger.warning( + 'Activity execution failed - task_id: %s, error: %s', + activity_id, + e, ) raise From 345ad9dcbb58c0c5d49f51ee45a0e026b9daae0e Mon Sep 17 00:00:00 2001 From: Casper Nielsen Date: Wed, 11 Mar 2026 18:06:32 +0100 Subject: [PATCH 2/7] chore: fix f-string Signed-off-by: Casper Nielsen --- dapr/clients/grpc/subscription.py | 2 +- ext/dapr-ext-workflow/dapr/ext/workflow/workflow_runtime.py | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/dapr/clients/grpc/subscription.py b/dapr/clients/grpc/subscription.py index f07a08157..dfdc0b2f8 100644 --- a/dapr/clients/grpc/subscription.py +++ b/dapr/clients/grpc/subscription.py @@ -121,7 +121,7 @@ def respond(self, message, status): raise StreamInactiveError('Stream is not active') self._send_queue.put(msg) except Exception as e: - logger.warning("Can't send message on inactive stream: %s", e) + logger.warning(f"Can't send message on inactive stream: {e}") def respond_success(self, message): self.respond(message, TopicEventResponse('success').status) diff --git a/ext/dapr-ext-workflow/dapr/ext/workflow/workflow_runtime.py b/ext/dapr-ext-workflow/dapr/ext/workflow/workflow_runtime.py index 9e94924fa..e2bf50d4e 100644 --- a/ext/dapr-ext-workflow/dapr/ext/workflow/workflow_runtime.py +++ b/ext/dapr-ext-workflow/dapr/ext/workflow/workflow_runtime.py @@ -180,9 +180,7 @@ def activityWrapper(ctx: task.ActivityContext, inp: Optional[TInput] = None): return result except Exception as e: self._logger.warning( - 'Activity execution failed - task_id: %s, error: %s', - activity_id, - e, + f'Activity execution failed - task_id: {activity_id}, error: {e}' ) raise From 06e630afbcddd1b1a7611390e62b47e6b7f27e71 Mon Sep 17 00:00:00 2001 From: Casper Nielsen Date: Wed, 11 Mar 2026 18:08:18 +0100 Subject: [PATCH 3/7] chore: add todo string Signed-off-by: Casper Nielsen --- dapr/clients/grpc/client.py | 1 + 1 file changed, 1 insertion(+) diff --git a/dapr/clients/grpc/client.py b/dapr/clients/grpc/client.py index b049f4efc..1b987d3fa 100644 --- a/dapr/clients/grpc/client.py +++ b/dapr/clients/grpc/client.py @@ -647,6 +647,7 @@ def stream_messages(sub): sub.reconnect_stream() except Exception: # Sidecar still unavailable — back off before retrying + # TODO: Make this configurable time.sleep(5) continue From 522af122f6be791ad10d05b77c86e4a2e85510ec Mon Sep 17 00:00:00 2001 From: Casper Nielsen Date: Wed, 11 Mar 2026 18:49:31 +0100 Subject: [PATCH 4/7] fix: address conflicts Signed-off-by: Samantha Coyle --- dev-requirements.txt | 2 +- ext/dapr-ext-workflow/setup.cfg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dev-requirements.txt b/dev-requirements.txt index f19ee798f..712d439f3 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -14,7 +14,7 @@ Flask>=1.1 # needed for auto fix ruff===0.14.1 # needed for dapr-ext-workflow -durabletask-dapr >= 0.17.1 +durabletask-dapr >= 0.17.2 # needed for .env file loading in examples python-dotenv>=1.0.0 # needed for enhanced schema generation from function features diff --git a/ext/dapr-ext-workflow/setup.cfg b/ext/dapr-ext-workflow/setup.cfg index f413a15d8..f059f9a07 100644 --- a/ext/dapr-ext-workflow/setup.cfg +++ b/ext/dapr-ext-workflow/setup.cfg @@ -25,7 +25,7 @@ packages = find_namespace: include_package_data = True install_requires = dapr >= 1.17.1 - durabletask-dapr >= 0.17.1 + durabletask-dapr >= 0.17.2 [options.packages.find] include = From 9d4e66af2b0bacff452e8d393806c9a5078442ca Mon Sep 17 00:00:00 2001 From: Casper Nielsen Date: Wed, 11 Mar 2026 19:19:57 +0100 Subject: [PATCH 5/7] chore(test): increase coverage Signed-off-by: Casper Nielsen --- .../tests/test_workflow_runtime.py | 324 +++++++++++++++++- 1 file changed, 323 insertions(+), 1 deletion(-) diff --git a/ext/dapr-ext-workflow/tests/test_workflow_runtime.py b/ext/dapr-ext-workflow/tests/test_workflow_runtime.py index 16eb4946f..774b41398 100644 --- a/ext/dapr-ext-workflow/tests/test_workflow_runtime.py +++ b/ext/dapr-ext-workflow/tests/test_workflow_runtime.py @@ -30,7 +30,7 @@ def __init__(self): self._orchestrator_fns = {} self._activity_fns = {} - def add_named_orchestrator(self, name: str, fn): + def add_named_orchestrator(self, name: str, fn, **kwargs): listOrchestrators.append(name) self._orchestrator_fns[name] = fn @@ -298,3 +298,325 @@ def test_shutdown_raises_when_worker_stop_fails(self): with self.assertRaises(RuntimeError) as ctx: self.runtime.shutdown() self.assertIn('stop failed', str(ctx.exception)) + + +class WorkflowRuntimeInitTest(unittest.TestCase): + """Tests for __init__ branches: DAPR_API_TOKEN and GrpcEndpoint error.""" + + def setUp(self): + listActivities.clear() + listOrchestrators.clear() + mock.patch('durabletask.worker._Registry', return_value=FakeTaskHubGrpcWorker()).start() + + def tearDown(self): + mock.patch.stopall() + + def test_init_with_dapr_api_token(self): + with mock.patch('dapr.ext.workflow.workflow_runtime.settings') as mock_settings: + mock_settings.DAPR_API_TOKEN = 'test-token' + mock_settings.DAPR_RUNTIME_HOST = '127.0.0.1' + mock_settings.DAPR_GRPC_PORT = 50001 + runtime = WorkflowRuntime() + self.assertIsNotNone(runtime) + + def test_init_raises_on_invalid_address(self): + from dapr.clients import DaprInternalError + + with mock.patch( + 'dapr.ext.workflow.workflow_runtime.GrpcEndpoint', + side_effect=ValueError('bad endpoint'), + ): + with self.assertRaises(DaprInternalError): + WorkflowRuntime() + + +class OrchestratorWrapperTest(unittest.TestCase): + """Tests for the orchestrationWrapper and activityWrapper inner functions.""" + + def setUp(self): + listActivities.clear() + listOrchestrators.clear() + self._registry_patch = mock.patch( + 'durabletask.worker._Registry', return_value=FakeTaskHubGrpcWorker() + ) + self._registry_patch.start() + self.runtime = WorkflowRuntime() + self.fake_registry = self.runtime._WorkflowRuntime__worker._registry + + def tearDown(self): + mock.patch.stopall() + + def test_orchestration_wrapper_calls_workflow_without_input(self): + called_with = {} + + def my_wf(ctx): + called_with['ctx'] = ctx + return 'wf_result' + + self.runtime.register_workflow(my_wf) + wrapper_fn = self.fake_registry._orchestrator_fns['my_wf'] + + mock_ctx = mock.MagicMock() + result = wrapper_fn(mock_ctx, None) + self.assertEqual(result, 'wf_result') + self.assertIsNotNone(called_with.get('ctx')) + + def test_orchestration_wrapper_calls_workflow_with_input(self): + called_with = {} + + def my_wf(ctx, inp): + called_with['inp'] = inp + return inp * 2 + + self.runtime.register_workflow(my_wf) + wrapper_fn = self.fake_registry._orchestrator_fns['my_wf'] + + mock_ctx = mock.MagicMock() + result = wrapper_fn(mock_ctx, 21) + self.assertEqual(result, 42) + self.assertEqual(called_with['inp'], 21) + + def test_orchestration_wrapper_logs_and_reraises_on_exception(self): + def failing_wf(ctx): + raise RuntimeError('wf boom') + + self.runtime.register_workflow(failing_wf) + wrapper_fn = self.fake_registry._orchestrator_fns['failing_wf'] + + mock_ctx = mock.MagicMock() + mock_ctx.instance_id = 'test-instance' + with mock.patch.object(self.runtime._logger, 'exception') as mock_exc: + with self.assertRaises(RuntimeError): + wrapper_fn(mock_ctx, None) + mock_exc.assert_called_once() + self.assertIn('test-instance', mock_exc.call_args[0][0]) + + def test_activity_wrapper_calls_activity_without_input(self): + called_with = {} + + def my_act(ctx): + called_with['ctx'] = ctx + return 'act_result' + + self.runtime.register_activity(my_act) + wrapper_fn = self.fake_registry._activity_fns['my_act'] + + mock_ctx = mock.MagicMock() + result = wrapper_fn(mock_ctx, None) + self.assertEqual(result, 'act_result') + + def test_activity_wrapper_calls_activity_with_input(self): + def my_act(ctx, inp): + return inp + '_done' + + self.runtime.register_activity(my_act) + wrapper_fn = self.fake_registry._activity_fns['my_act'] + + mock_ctx = mock.MagicMock() + result = wrapper_fn(mock_ctx, 'task') + self.assertEqual(result, 'task_done') + + def test_activity_wrapper_logs_and_reraises_on_exception(self): + def failing_act(ctx): + raise ValueError('act boom') + + self.runtime.register_activity(failing_act) + wrapper_fn = self.fake_registry._activity_fns['failing_act'] + + mock_ctx = mock.MagicMock() + mock_ctx.task_id = 'task-42' + with mock.patch.object(self.runtime._logger, 'warning') as mock_warn: + with self.assertRaises(ValueError): + wrapper_fn(mock_ctx, None) + mock_warn.assert_called_once() + self.assertIn('task-42', str(mock_warn.call_args)) + + +class VersionedWorkflowTest(unittest.TestCase): + """Tests for register_versioned_workflow and @versioned_workflow decorator.""" + + def setUp(self): + listActivities.clear() + listOrchestrators.clear() + self._registry_patch = mock.patch( + 'durabletask.worker._Registry', return_value=FakeTaskHubGrpcWorker() + ) + self._registry_patch.start() + self.runtime = WorkflowRuntime() + self.fake_registry = self.runtime._WorkflowRuntime__worker._registry + + def tearDown(self): + mock.patch.stopall() + + def test_register_versioned_workflow_basic(self): + def my_wf(ctx): + return 'ok' + + self.runtime.register_versioned_workflow( + my_wf, name='my_workflow', version_name='v1', is_latest=True + ) + self.assertIn('my_workflow', listOrchestrators) + self.assertTrue(my_wf._workflow_registered) + self.assertEqual(my_wf._dapr_alternate_name, 'my_workflow') + + def test_register_versioned_workflow_without_version_name(self): + def another_wf(ctx): + return 'ok' + + self.runtime.register_versioned_workflow( + another_wf, name='named_wf', version_name=None, is_latest=False + ) + self.assertIn('named_wf', listOrchestrators) + + def test_register_versioned_workflow_duplicate_raises(self): + def my_wf(ctx): + return 'ok' + + self.runtime.register_versioned_workflow( + my_wf, name='wf_name', version_name='v1', is_latest=True + ) + with self.assertRaises(ValueError) as ctx: + self.runtime.register_versioned_workflow( + my_wf, name='wf_name', version_name='v2', is_latest=False + ) + self.assertIn('already registered', str(ctx.exception)) + + def test_register_versioned_workflow_conflicts_with_alternate_name(self): + def my_wf(ctx): + return 'ok' + + my_wf.__dict__['_dapr_alternate_name'] = 'existing_name' + with self.assertRaises(ValueError) as ctx: + self.runtime.register_versioned_workflow( + my_wf, name='different_name', version_name='v1', is_latest=True + ) + self.assertIn('already has an alternate name', str(ctx.exception)) + + def test_versioned_workflow_orchestration_wrapper_without_input(self): + def my_wf(ctx): + return 'versioned_result' + + self.runtime.register_versioned_workflow( + my_wf, name='vwf', version_name='v1', is_latest=True + ) + wrapper_fn = self.fake_registry._orchestrator_fns['vwf'] + mock_ctx = mock.MagicMock() + result = wrapper_fn(mock_ctx, None) + self.assertEqual(result, 'versioned_result') + + def test_versioned_workflow_orchestration_wrapper_with_input(self): + def my_wf(ctx, inp): + return inp + 10 + + self.runtime.register_versioned_workflow( + my_wf, name='vwf2', version_name='v1', is_latest=True + ) + wrapper_fn = self.fake_registry._orchestrator_fns['vwf2'] + mock_ctx = mock.MagicMock() + result = wrapper_fn(mock_ctx, 5) + self.assertEqual(result, 15) + + def test_versioned_workflow_decorator_with_args(self): + @self.runtime.versioned_workflow(name='dec_vwf', version_name='v1', is_latest=True) + def my_wf(ctx): + return 'ok' + + self.assertIn('dec_vwf', listOrchestrators) + self.assertEqual(my_wf._dapr_alternate_name, 'dec_vwf') + + def test_versioned_workflow_decorator_without_args(self): + def my_wf(ctx): + return 'ok' + + decorated = self.runtime.versioned_workflow( + my_wf, name='direct_vwf', is_latest=False + ) + self.assertIn('direct_vwf', listOrchestrators) + self.assertEqual(decorated._dapr_alternate_name, 'direct_vwf') + + def test_versioned_workflow_decorator_sets_alternate_name_from_register(self): + @self.runtime.versioned_workflow(name='vwf_name', version_name='v1', is_latest=True) + def my_wf(ctx): + return 'ok' + + # The decorator picks up _dapr_alternate_name set by register_versioned_workflow + self.assertEqual(my_wf._dapr_alternate_name, 'vwf_name') + + +class DecoratorNoArgsTest(unittest.TestCase): + """Tests for @workflow and @activity decorators used without parentheses.""" + + def setUp(self): + listActivities.clear() + listOrchestrators.clear() + mock.patch('durabletask.worker._Registry', return_value=FakeTaskHubGrpcWorker()).start() + self.runtime = WorkflowRuntime() + + def tearDown(self): + mock.patch.stopall() + + def test_workflow_decorator_no_args(self): + @self.runtime.workflow + def my_workflow(ctx): + return 'result' + + self.assertIn('my_workflow', listOrchestrators) + self.assertEqual(my_workflow._dapr_alternate_name, 'my_workflow') + + def test_activity_decorator_no_args(self): + @self.runtime.activity + def my_activity(ctx): + return 'result' + + self.assertIn('my_activity', listActivities) + self.assertEqual(my_activity._dapr_alternate_name, 'my_activity') + + def test_workflow_decorator_innerfn_returns_fn(self): + @self.runtime.workflow + def my_workflow(ctx): + return 'hello' + + result = my_workflow() + self.assertIsNotNone(result) + + def test_activity_decorator_innerfn_returns_fn(self): + @self.runtime.activity + def my_activity(ctx): + return 'hello' + + result = my_activity() + self.assertIsNotNone(result) + + +class AlternateNameTest(unittest.TestCase): + """Tests for the standalone alternate_name decorator.""" + + def test_alternate_name_with_name(self): + @alternate_name(name='custom') + def my_fn(ctx): + return 'ok' + + self.assertEqual(my_fn._dapr_alternate_name, 'custom') + + def test_alternate_name_without_name_uses_fn_name(self): + @alternate_name() + def my_fn(ctx): + return 'ok' + + self.assertEqual(my_fn._dapr_alternate_name, 'my_fn') + + def test_alternate_name_innerfn_calls_through(self): + @alternate_name(name='custom') + def my_fn(x, y): + return x + y + + self.assertEqual(my_fn(3, 4), 7) + + def test_alternate_name_duplicate_raises(self): + @alternate_name(name='first') + def my_fn(ctx): + return 'ok' + + with self.assertRaises(ValueError) as ctx: + alternate_name(name='second')(my_fn) + self.assertIn('already has an alternate name', str(ctx.exception)) From b0652c752c4b27237d1a3aaa26f4fa1e287753fe Mon Sep 17 00:00:00 2001 From: Casper Nielsen Date: Wed, 11 Mar 2026 19:20:28 +0100 Subject: [PATCH 6/7] chore(format): ruff Signed-off-by: Casper Nielsen --- ext/dapr-ext-workflow/tests/test_workflow_runtime.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ext/dapr-ext-workflow/tests/test_workflow_runtime.py b/ext/dapr-ext-workflow/tests/test_workflow_runtime.py index 774b41398..b3cadd4ad 100644 --- a/ext/dapr-ext-workflow/tests/test_workflow_runtime.py +++ b/ext/dapr-ext-workflow/tests/test_workflow_runtime.py @@ -528,9 +528,7 @@ def test_versioned_workflow_decorator_without_args(self): def my_wf(ctx): return 'ok' - decorated = self.runtime.versioned_workflow( - my_wf, name='direct_vwf', is_latest=False - ) + decorated = self.runtime.versioned_workflow(my_wf, name='direct_vwf', is_latest=False) self.assertIn('direct_vwf', listOrchestrators) self.assertEqual(decorated._dapr_alternate_name, 'direct_vwf') From e8f78a206894f66f5c737ffcf8997c4740494a37 Mon Sep 17 00:00:00 2001 From: Samantha Coyle Date: Wed, 11 Mar 2026 14:26:16 -0500 Subject: [PATCH 7/7] fix: bump versions everywhere for new release Signed-off-by: Samantha Coyle --- dapr/version/version.py | 2 +- ext/dapr-ext-fastapi/dapr/ext/fastapi/version.py | 2 +- ext/dapr-ext-fastapi/setup.cfg | 2 +- ext/dapr-ext-grpc/dapr/ext/grpc/version.py | 2 +- ext/dapr-ext-grpc/setup.cfg | 2 +- ext/dapr-ext-langgraph/dapr/ext/langgraph/version.py | 2 +- ext/dapr-ext-langgraph/setup.cfg | 2 +- ext/dapr-ext-strands/dapr/ext/strands/version.py | 2 +- ext/dapr-ext-strands/setup.cfg | 2 +- ext/dapr-ext-workflow/dapr/ext/workflow/version.py | 2 +- ext/dapr-ext-workflow/setup.cfg | 2 +- ext/flask_dapr/flask_dapr/version.py | 2 +- ext/flask_dapr/setup.cfg | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) diff --git a/dapr/version/version.py b/dapr/version/version.py index 47cb18c71..e4537c3ac 100644 --- a/dapr/version/version.py +++ b/dapr/version/version.py @@ -13,4 +13,4 @@ limitations under the License. """ -__version__ = '1.17.1' +__version__ = '1.17.2' diff --git a/ext/dapr-ext-fastapi/dapr/ext/fastapi/version.py b/ext/dapr-ext-fastapi/dapr/ext/fastapi/version.py index 47cb18c71..e4537c3ac 100644 --- a/ext/dapr-ext-fastapi/dapr/ext/fastapi/version.py +++ b/ext/dapr-ext-fastapi/dapr/ext/fastapi/version.py @@ -13,4 +13,4 @@ limitations under the License. """ -__version__ = '1.17.1' +__version__ = '1.17.2' diff --git a/ext/dapr-ext-fastapi/setup.cfg b/ext/dapr-ext-fastapi/setup.cfg index f300b2581..6feee8569 100644 --- a/ext/dapr-ext-fastapi/setup.cfg +++ b/ext/dapr-ext-fastapi/setup.cfg @@ -25,7 +25,7 @@ python_requires = >=3.10 packages = find_namespace: include_package_data = True install_requires = - dapr >= 1.17.1 + dapr >= 1.17.2 uvicorn >= 0.11.6 fastapi >= 0.60.1 diff --git a/ext/dapr-ext-grpc/dapr/ext/grpc/version.py b/ext/dapr-ext-grpc/dapr/ext/grpc/version.py index 47cb18c71..e4537c3ac 100644 --- a/ext/dapr-ext-grpc/dapr/ext/grpc/version.py +++ b/ext/dapr-ext-grpc/dapr/ext/grpc/version.py @@ -13,4 +13,4 @@ limitations under the License. """ -__version__ = '1.17.1' +__version__ = '1.17.2' diff --git a/ext/dapr-ext-grpc/setup.cfg b/ext/dapr-ext-grpc/setup.cfg index 49efe917a..992c49bd6 100644 --- a/ext/dapr-ext-grpc/setup.cfg +++ b/ext/dapr-ext-grpc/setup.cfg @@ -24,7 +24,7 @@ python_requires = >=3.10 packages = find_namespace: include_package_data = True install_requires = - dapr >= 1.17.1 + dapr >= 1.17.2 cloudevents >= 1.0.0 [options.packages.find] diff --git a/ext/dapr-ext-langgraph/dapr/ext/langgraph/version.py b/ext/dapr-ext-langgraph/dapr/ext/langgraph/version.py index abe66dc2e..b66323c52 100644 --- a/ext/dapr-ext-langgraph/dapr/ext/langgraph/version.py +++ b/ext/dapr-ext-langgraph/dapr/ext/langgraph/version.py @@ -13,4 +13,4 @@ limitations under the License. """ -__version__ = '1.17.1' +__version__ = '1.17.2' diff --git a/ext/dapr-ext-langgraph/setup.cfg b/ext/dapr-ext-langgraph/setup.cfg index 62c014117..e9b0295c5 100644 --- a/ext/dapr-ext-langgraph/setup.cfg +++ b/ext/dapr-ext-langgraph/setup.cfg @@ -24,7 +24,7 @@ python_requires = >=3.10 packages = find_namespace: include_package_data = True install_requires = - dapr >= 1.17.1 + dapr >= 1.17.2 langgraph >= 0.3.6 langchain >= 0.1.17 python-ulid >= 3.0.0 diff --git a/ext/dapr-ext-strands/dapr/ext/strands/version.py b/ext/dapr-ext-strands/dapr/ext/strands/version.py index abe66dc2e..b66323c52 100644 --- a/ext/dapr-ext-strands/dapr/ext/strands/version.py +++ b/ext/dapr-ext-strands/dapr/ext/strands/version.py @@ -13,4 +13,4 @@ limitations under the License. """ -__version__ = '1.17.1' +__version__ = '1.17.2' diff --git a/ext/dapr-ext-strands/setup.cfg b/ext/dapr-ext-strands/setup.cfg index 3741fff32..e459125ef 100644 --- a/ext/dapr-ext-strands/setup.cfg +++ b/ext/dapr-ext-strands/setup.cfg @@ -24,7 +24,7 @@ python_requires = >=3.10 packages = find_namespace: include_package_data = True install_requires = - dapr >= 1.17.1 + dapr >= 1.17.2 strands-agents strands-agents-tools python-ulid >= 3.0.0 diff --git a/ext/dapr-ext-workflow/dapr/ext/workflow/version.py b/ext/dapr-ext-workflow/dapr/ext/workflow/version.py index 47cb18c71..e4537c3ac 100644 --- a/ext/dapr-ext-workflow/dapr/ext/workflow/version.py +++ b/ext/dapr-ext-workflow/dapr/ext/workflow/version.py @@ -13,4 +13,4 @@ limitations under the License. """ -__version__ = '1.17.1' +__version__ = '1.17.2' diff --git a/ext/dapr-ext-workflow/setup.cfg b/ext/dapr-ext-workflow/setup.cfg index f059f9a07..0bcd411a3 100644 --- a/ext/dapr-ext-workflow/setup.cfg +++ b/ext/dapr-ext-workflow/setup.cfg @@ -24,7 +24,7 @@ python_requires = >=3.10 packages = find_namespace: include_package_data = True install_requires = - dapr >= 1.17.1 + dapr >= 1.17.2 durabletask-dapr >= 0.17.2 [options.packages.find] diff --git a/ext/flask_dapr/flask_dapr/version.py b/ext/flask_dapr/flask_dapr/version.py index 47cb18c71..e4537c3ac 100644 --- a/ext/flask_dapr/flask_dapr/version.py +++ b/ext/flask_dapr/flask_dapr/version.py @@ -13,4 +13,4 @@ limitations under the License. """ -__version__ = '1.17.1' +__version__ = '1.17.2' diff --git a/ext/flask_dapr/setup.cfg b/ext/flask_dapr/setup.cfg index a6438dca4..0060a2c6d 100644 --- a/ext/flask_dapr/setup.cfg +++ b/ext/flask_dapr/setup.cfg @@ -26,7 +26,7 @@ include_package_data = true zip_safe = false install_requires = Flask >= 1.1 - dapr >= 1.17.1 + dapr >= 1.17.2 [options.package_data] flask_dapr =