[Look at my other PR first pls!] fix(release): use official durabletask release#939
[Look at my other PR first pls!] fix(release): use official durabletask release#939sicoyle wants to merge 9 commits intodapr:release-1.17from
Conversation
* Workflow: remove sleeps from example Remove arbitrary sleeps from workflow examples Signed-off-by: joshvanl <me@joshvanl.dev> * tox -e ruff Signed-off-by: joshvanl <me@joshvanl.dev> --------- Signed-off-by: joshvanl <me@joshvanl.dev> Signed-off-by: Samantha Coyle <sam@diagrid.io>
Signed-off-by: Albert Callarisa <albert@diagrid.io> Signed-off-by: Samantha Coyle <sam@diagrid.io>
dapr#901) * fix: signal when dt reader stream is ready within wf client start call Signed-off-by: Samantha Coyle <sam@diagrid.io> * style: appease linter Signed-off-by: Samantha Coyle <sam@diagrid.io> * fix: enable configurability + lint fixes Signed-off-by: Samantha Coyle <sam@diagrid.io> * fix(build): add tests and fix bug Signed-off-by: Samantha Coyle <sam@diagrid.io> * fix(build): add more tests Signed-off-by: Samantha Coyle <sam@diagrid.io> * style: appease linter Signed-off-by: Samantha Coyle <sam@diagrid.io> * fix(build): add another test Signed-off-by: Samantha Coyle <sam@diagrid.io> * test: add even more tests for build to pass lol Signed-off-by: Samantha Coyle <sam@diagrid.io> --------- Signed-off-by: Samantha Coyle <sam@diagrid.io>
* feat(convo): add new fields to conversation api Signed-off-by: Samantha Coyle <sam@diagrid.io> * fix: update proto/grpc code generator and add more tests Signed-off-by: Samantha Coyle <sam@diagrid.io> * style: appease linter Signed-off-by: Samantha Coyle <sam@diagrid.io> * style: tox -e type fixes Signed-off-by: Samantha Coyle <sam@diagrid.io> --------- Signed-off-by: Samantha Coyle <sam@diagrid.io>
Signed-off-by: Cassandra Coyle <cassie@diagrid.io> Co-authored-by: Sam <sam@diagrid.io> Signed-off-by: Samantha Coyle <sam@diagrid.io>
Signed-off-by: Matheus André <92062874+matheusandre1@users.noreply.github.com> Signed-off-by: Samantha Coyle <sam@diagrid.io>
…ask (dapr#889) * chore(deps): bump dapr dep to 1.17.0.dev Signed-off-by: Casper Nielsen <casper@diagrid.io> * chore(deps): bump durabletask-dapr to 0.2.0a15 Signed-off-by: Casper Nielsen <casper@diagrid.io> * feat: ensure taskhubgrpcclient expose .close() method for clients to pass down a close call to durabletask Signed-off-by: Casper Nielsen <casper@diagrid.io> --------- Signed-off-by: Casper Nielsen <casper@diagrid.io> Signed-off-by: Samantha Coyle <sam@diagrid.io>
dapr#901) * fix: signal when dt reader stream is ready within wf client start call Signed-off-by: Samantha Coyle <sam@diagrid.io> * style: appease linter Signed-off-by: Samantha Coyle <sam@diagrid.io> * fix: enable configurability + lint fixes Signed-off-by: Samantha Coyle <sam@diagrid.io> * fix(build): add tests and fix bug Signed-off-by: Samantha Coyle <sam@diagrid.io> * fix(build): add more tests Signed-off-by: Samantha Coyle <sam@diagrid.io> * style: appease linter Signed-off-by: Samantha Coyle <sam@diagrid.io> * fix(build): add another test Signed-off-by: Samantha Coyle <sam@diagrid.io> * test: add even more tests for build to pass lol Signed-off-by: Samantha Coyle <sam@diagrid.io> --------- Signed-off-by: Samantha Coyle <sam@diagrid.io>
Signed-off-by: Samantha Coyle <sam@diagrid.io>
There was a problem hiding this comment.
Pull request overview
Updates dependencies and client/runtime behavior to align with the official durabletask-dapr release and newer API surface area.
Changes:
- Bump
durabletask-daprdependency to>= 0.17.0in workflow extension and dev requirements. - Add worker readiness polling/logging to
WorkflowRuntimeand expand tests accordingly. - Extend Conversation (Alpha2) support for
model, tokenusagedetails, and request fields likeresponse_format/prompt_cache_retention.
Reviewed changes
Copilot reviewed 18 out of 18 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
ext/dapr-ext-workflow/setup.cfg |
Bumps workflow extension dependency to official durabletask-dapr release. |
dev-requirements.txt |
Keeps dev dependencies aligned with new official durabletask-dapr version. |
ext/dapr-ext-workflow/dapr/ext/workflow/workflow_runtime.py |
Adds readiness waiting and improves exception logging around workflow/activity execution. |
ext/dapr-ext-workflow/dapr/ext/workflow/logger/logger.py |
Adds Logger.exception() wrapper used by WorkflowRuntime. |
ext/dapr-ext-workflow/tests/test_workflow_runtime.py |
Adds unit tests for worker readiness behavior and start/shutdown paths. |
dapr/clients/grpc/client.py |
Adds converse_alpha2 request fields (response_format, prompt_cache_retention). |
dapr/clients/grpc/conversation.py |
Adds Alpha2 model/usage dataclasses and populates them from gRPC responses. |
tests/clients/test_conversation.py |
Adds coverage for Alpha2 model/usage, response_format passthrough, and output parsing helper. |
tests/clients/fake_dapr_server.py |
Extends fake server to populate model and usage when supported by proto. |
ext/dapr-ext-workflow/dapr/ext/workflow/workflow_context.py |
Documentation wording change from “cross-app” to “multi-app”. |
ext/dapr-ext-workflow/dapr/ext/workflow/dapr_workflow_context.py |
Documentation/log wording change from “cross-app” to “multi-app”. |
examples/workflow/*.py |
Updates examples to rely less on sleeps and use workflow completion waiting. |
examples/workflow/README.md |
Renames “Cross-app” docs/commands to “Multi-app” terminology. |
README.md |
Improves contributor setup instructions by adding a venv step and renumbering. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| model: Optional[str] = None | ||
| usage: Optional[ConversationResultAlpha2CompletionUsage] = None | ||
| if hasattr(output, 'model') and getattr(output, 'model', None): | ||
| model = output.model | ||
| if hasattr(output, 'usage') and output.usage: | ||
| u = output.usage | ||
| completion_details: Optional[ | ||
| ConversationResultAlpha2CompletionUsageCompletionTokensDetails | ||
| ] = None | ||
| prompt_details: Optional[ConversationResultAlpha2CompletionUsagePromptTokensDetails] = ( | ||
| None | ||
| ) | ||
| if hasattr(u, 'completion_tokens_details') and u.completion_tokens_details: | ||
| cd = u.completion_tokens_details | ||
| completion_details = ConversationResultAlpha2CompletionUsageCompletionTokensDetails( | ||
| accepted_prediction_tokens=getattr(cd, 'accepted_prediction_tokens', 0) or 0, | ||
| audio_tokens=getattr(cd, 'audio_tokens', 0) or 0, | ||
| reasoning_tokens=getattr(cd, 'reasoning_tokens', 0) or 0, | ||
| rejected_prediction_tokens=getattr(cd, 'rejected_prediction_tokens', 0) or 0, | ||
| ) | ||
| if hasattr(u, 'prompt_tokens_details') and u.prompt_tokens_details: | ||
| pd = u.prompt_tokens_details | ||
| prompt_details = ConversationResultAlpha2CompletionUsagePromptTokensDetails( | ||
| audio_tokens=getattr(pd, 'audio_tokens', 0) or 0, | ||
| cached_tokens=getattr(pd, 'cached_tokens', 0) or 0, | ||
| ) | ||
| usage = ConversationResultAlpha2CompletionUsage( | ||
| completion_tokens=getattr(u, 'completion_tokens', 0) or 0, | ||
| prompt_tokens=getattr(u, 'prompt_tokens', 0) or 0, | ||
| total_tokens=getattr(u, 'total_tokens', 0) or 0, | ||
| completion_tokens_details=completion_details, | ||
| prompt_tokens_details=prompt_details, | ||
| ) | ||
| outputs.append(ConversationResultAlpha2(choices=choices, model=model, usage=usage)) |
There was a problem hiding this comment.
output.usage / u.completion_tokens_details / u.prompt_tokens_details are protobuf message fields; their truthiness is not a reliable indicator of presence (and may evaluate truthy even when unset). This can incorrectly populate usage (and details) with zero-valued objects when the server didn't send usage, changing API semantics from None to 'all zeros'. Prefer presence checks like HasField('usage') when available, or u.ListFields() / u.ByteSize() > 0 to detect an actually-populated submessage before constructing these dataclasses.
| self._logger.exception( | ||
| f'WorkflowRuntime wait_for_worker_ready() raised exception: {ready_error}' | ||
| ) | ||
| raise ready_error |
There was a problem hiding this comment.
Re-raising with raise ready_error discards the original traceback context. Use a bare raise here to preserve the original stack trace (you’re already logging with exception()).
| raise ready_error | |
| raise |
| elapsed = 0.0 | ||
| poll_interval = 0.1 # 100ms | ||
|
|
||
| while elapsed < timeout: | ||
| if self.__worker.is_worker_ready(): | ||
| return True | ||
| time.sleep(poll_interval) | ||
| elapsed += poll_interval |
There was a problem hiding this comment.
The timeout accounting is based on incrementing by poll_interval, which can drift from wall-clock time (sleep delays, scheduling, etc.). Using a monotonic clock (e.g., store deadline = time.monotonic() + timeout and loop until time.monotonic() >= deadline) makes the timeout behavior more accurate and avoids potential overshoot.
| elapsed = 0.0 | |
| poll_interval = 0.1 # 100ms | |
| while elapsed < timeout: | |
| if self.__worker.is_worker_ready(): | |
| return True | |
| time.sleep(poll_interval) | |
| elapsed += poll_interval | |
| poll_interval = 0.1 # 100ms | |
| deadline = time.monotonic() + timeout | |
| while time.monotonic() < deadline: | |
| if self.__worker.is_worker_ready(): | |
| return True | |
| time.sleep(poll_interval) |
| def setUp(self): | ||
| listActivities.clear() | ||
| listOrchestrators.clear() | ||
| mock.patch('durabletask.worker._Registry', return_value=FakeTaskHubGrpcWorker()).start() |
There was a problem hiding this comment.
This patcher is started in setUp() but never stopped, which can leak state across tests and make ordering-dependent failures likely. Store the patcher (e.g., self._patcher = mock.patch(...)) and stop it in tearDown() or via self.addCleanup(self._patcher.stop).
| mock.patch('durabletask.worker._Registry', return_value=FakeTaskHubGrpcWorker()).start() | |
| self._registry_patcher = mock.patch( | |
| 'durabletask.worker._Registry', | |
| return_value=FakeTaskHubGrpcWorker(), | |
| ) | |
| self._registry_patcher.start() | |
| self.addCleanup(self._registry_patcher.stop) |
| del mock_worker.is_worker_ready | ||
| self.runtime._WorkflowRuntime__worker = mock_worker | ||
| self.assertFalse(self.runtime.wait_for_worker_ready(timeout=0.1)) | ||
|
|
There was a problem hiding this comment.
mock_worker is created with a restrictive spec that does not include is_worker_ready, so del mock_worker.is_worker_ready will raise AttributeError (the attribute doesn't exist). To model 'no is_worker_ready', just avoid creating/deleting it (the spec already achieves that), or use a plain object/MagicMock without setting the attribute at all and rely on hasattr(...) behavior in the implementation.
| del mock_worker.is_worker_ready | |
| self.runtime._WorkflowRuntime__worker = mock_worker | |
| self.assertFalse(self.runtime.wait_for_worker_ready(timeout=0.1)) | |
| self.runtime._WorkflowRuntime__worker = mock_worker | |
| self.assertFalse(self.runtime.wait_for_worker_ready(timeout=0.1)) |
|
|
||
| def test_start_logs_warning_when_no_is_worker_ready(self): | ||
| mock_worker = mock.MagicMock(spec=['start', 'stop', '_registry']) | ||
| del mock_worker.is_worker_ready |
There was a problem hiding this comment.
Same issue as above: deleting is_worker_ready on a MagicMock(spec=[...]) where the attribute never existed will raise AttributeError. Remove the del ... and let the spec enforce the absence, or use a different mock strategy to represent the missing attribute.
| del mock_worker.is_worker_ready |
| total_tokens=15, | ||
| ) | ||
| result.usage.CopyFrom(u) | ||
| except Exception: |
There was a problem hiding this comment.
The broad except Exception: pass will silently hide real incompatibilities/typos in the fake server and can make tests pass while behavior is broken. Prefer catching the specific expected exceptions (e.g., AttributeError, TypeError) and/or at least fail fast (re-raise) so test failures point to the root cause.
| except Exception: | |
| except (AttributeError, TypeError): | |
| # Ignore usage population issues due to minor API/type differences in the fake server |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## release-1.17 #939 +/- ##
===============================================
Coverage ? 86.89%
===============================================
Files ? 103
Lines ? 7305
Branches ? 0
===============================================
Hits ? 6348
Misses ? 957
Partials ? 0 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
can close this one since it is covered by #944 |
Description
Durabletask python has only ever had prereleases. I justtttt cut a v0.17.0 so we can use an official release here and in dapr agents instead of a pre-release. This PR builds on top of my other PR so pls review this one last as it is only 2 lines of changes to use the newest durabletask version.
Look at this other PR first pls #938
https://pypi.org/project/durabletask-dapr/#history
Issue reference
We strive to have all PR being opened based on an issue, where the problem or feature have been discussed prior to implementation.
Please reference the issue this PR will close: #[issue number]
Checklist
Please make sure you've completed the relevant tasks for this PR, out of the following list: