Conversation
|
🧪 Testing To try out this version of the SDK: Expires at: Mon, 30 Mar 2026 21:17:15 GMT |
ac5c5d9 to
c72a9be
Compare
c72a9be to
00246c5
Compare
| *, | ||
| cast_to: Type[ResponseT], | ||
| body: Body | None = None, | ||
| content: BinaryTypes | None = None, |
There was a problem hiding this comment.
Iterator content silently lost on request retry
Medium Severity
When content is an Iterable[bytes] (like a generator), the iterator is consumed on the first request attempt. If a retryable error occurs, the retry logic uses model_copy which performs a shallow copy, so options.content still references the same exhausted iterator. Subsequent retry attempts send an empty request body. This is exacerbated by DEFAULT_MAX_RETRIES being increased from 0 to 2 in this same PR. Users passing generator-based content could experience silent data loss on transient failures.
Additional Locations (1)
00246c5 to
f4bfcf4
Compare
f4bfcf4 to
120f4f7
Compare
120f4f7 to
b02a9df
Compare
PR SummaryMedium Risk Overview Adds new client resources for Updates the HTTP client to support raw/streaming binary request bodies via a new Refactors Operational updates: default retries increased to 2 with longer backoff, CI actions/uv versions bumped (and PyPI publish job set to Written by Cursor Bugbot for commit 9ea2655. This will update automatically on new commits. Configure here. |
b02a9df to
3c98764
Compare
| [], | ||
| steps, | ||
| verbose=exec_config.verbose, | ||
| ) |
There was a problem hiding this comment.
Missing assistant message before tool execution in streaming paths
High Severity
Both streaming paths (_execute_streaming_async and _execute_streaming_sync) call the scheduler's execute_local_tools_async/execute_local_tools_sync without first appending an assistant message with tool_calls to the messages list. The scheduler's own docstring explicitly states the caller is responsible for this. The non-streaming paths (_execute_tool_calls at line 1204, _execute_tool_calls_sync at line 1229) correctly append {"role": "assistant", "tool_calls": ...} before calling the scheduler. The old streaming code also did this but the line was removed during the refactoring. This produces a malformed conversation (tool messages without a preceding assistant message), which will cause the API to reject subsequent requests.
Additional Locations (1)
| # Collect MCP tool results emitted by the server | ||
| chunk_extra = getattr(chunk, "__pydantic_extra__", None) or {} | ||
| if isinstance(chunk_extra, dict) and "mcp_tool_results" in chunk_extra: | ||
| mcp_tool_results_from_server = chunk_extra["mcp_tool_results"] |
There was a problem hiding this comment.
Collected MCP tool results variable is never used
Low Severity
mcp_tool_results_from_server is assigned in both _execute_streaming_async and _execute_streaming_sync but is never read after assignment. The variable is dead code — the collected MCP tool results are silently discarded. Given the PR includes a fix for "inject server tool results into conversation for mixed tool calls," this may represent an incomplete implementation for the streaming paths.
Additional Locations (1)
This comment has been minimized.
This comment has been minimized.
3c98764 to
b8e048e
Compare
f20bb1f to
36b0123
Compare
36b0123 to
afba00e
Compare
| exclude=exclude, | ||
| exclude_unset=exclude_unset, | ||
| exclude_defaults=exclude_defaults, | ||
| exclude=exclude, exclude_unset=exclude_unset, exclude_defaults=exclude_defaults, by_alias=bool(by_alias) |
There was a problem hiding this comment.
Default by_alias change breaks request serialization of aliased fields
Medium Severity
The model_dump helper in _compat.py changed its by_alias default from hardcoded True to None (effectively False). The _transform_recursive function in _transform.py calls model_dump(data, exclude_unset=True, mode="json") without passing by_alias, relying on the previous default. This breaks serialization of Pydantic models with field aliases — notably JSONSchema.schema_ (aliased to "schema") will now serialize as "schema_" instead of "schema", sending incorrect field names to the API.
|
Bugbot Autofix prepared fixes for 1 of the 1 bugs found in the latest run.
Or push these changes by commenting: Preview (018c6c55a4)diff --git a/src/dedalus_labs/_compat.py b/src/dedalus_labs/_compat.py
--- a/src/dedalus_labs/_compat.py
+++ b/src/dedalus_labs/_compat.py
@@ -139,7 +139,7 @@
exclude_defaults: bool = False,
warnings: bool = True,
mode: Literal["json", "python"] = "python",
- by_alias: bool | None = None,
+ by_alias: bool = True,
) -> dict[str, Any]:
if (not PYDANTIC_V1) or hasattr(model, "model_dump"):
return model.model_dump(
@@ -154,7 +154,7 @@
return cast(
"dict[str, Any]",
model.dict( # pyright: ignore[reportDeprecated, reportUnnecessaryCast]
- exclude=exclude, exclude_unset=exclude_unset, exclude_defaults=exclude_defaults, by_alias=bool(by_alias)
+ exclude=exclude, exclude_unset=exclude_unset, exclude_defaults=exclude_defaults, by_alias=by_alias
),
) |
afba00e to
8d6900c
Compare
8d6900c to
e719ab6
Compare
| [], | ||
| steps, | ||
| verbose=exec_config.verbose, | ||
| ) |
There was a problem hiding this comment.
Streaming tool results not recorded
Medium Severity
The streaming local-tool execution path passes fresh empty lists for tool_results and tools_called into execute_local_tools_async()/execute_local_tools_sync(), so successful executions in streaming mode are never reflected in the runner’s tracking structures.
| sorter.add(call_id, *deps) | ||
|
|
||
| sorter.prepare() | ||
| return calls_by_id, sorter |
There was a problem hiding this comment.
Tool scheduler can collide on empty ids
High Severity
_parse_pending_calls() uses call_id = tc.get("id", "") as the TopologicalSorter node key and as the calls_by_id key. If any tool call lacks an id, multiple calls collapse onto the same empty-string id, overwriting entries and producing incorrect dependency ordering and tool_call_id references in emitted role="tool" messages.
e719ab6 to
9c9dc29
Compare
9c9dc29 to
6963417
Compare
Use v1-safe JSON alias/runtime handling and pydantic-v1 validators in MCP wire serialization, while keeping recursive TypeAliasType behavior for v2.
e50bab2 to
6963417
Compare
6963417 to
b1148aa
Compare
Merge latest next into fix/mcp-credential-filtering and resolve request.py/wire.py conflicts while preserving per-server credential embedding behavior.
b1148aa to
9ea2655
Compare
|
🤖 Release is at https://github.com/dedalus-labs/dedalus-sdk-python/releases/tag/v0.3.0 🌻 |



Automated Release PR
0.3.0 (2026-02-28)
Full Changelog: v0.2.0...v0.3.0
Features
Bug Fixes
Chores
actions/github-script(cf53a9e)api.mdfiles (ead37d4)test_proxy_environment_variablesmore resilient (e6b7ee4)test_proxy_environment_variablesmore resilient to env (d368d31)actions/checkoutversion (c72dfca)Styles
Refactors
This pull request is managed by Stainless's GitHub App.
The semver version number is based on included commit messages. Alternatively, you can manually set the version number in the title of this pull request.
For a better experience, it is recommended to use either rebase-merge or squash-merge when merging this pull request.
🔗 Stainless website
📚 Read the docs
🙋 Reach out for help or questions