You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Structured SSE streams currently stop at unexpected EOF and require callers to manually resume with last_event_id through an internal workaround. This adds automatic reconnection so generated clients follow the SSE processing model without requiring application-owned reconnect loops.
The generated stream runtime now:
reconnects after unexpected EOF using the default three-second delay or the latest valid retry: value --- Do we want a 3 second default or would we prefer no time delay, just immediately try to reconnect?
preserves and sends the latest event ID through Last-Event-ID
stops reconnecting after terminal events and HTTP 204 responses (per the spec)
routes reconnect failures through the generated operation's normal error mapping
Automatically reconnect SSE streams after an unexpected disconnect, honoring server-provided retry delays and resuming with the latest event ID.,> ,> python,> with client.events.stream() as stream:,> for event in stream:,> print(event),>
Use the public PipelineClient.send_request API when reconnecting SSE streams so generated Azure clients pass protected-access lint checks.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 6bf64cba-786a-41ee-8e45-8567e1cf8568
The reason will be displayed to describe this comment to others. Learn more.
🟡 Changes recommended
The reconnect implementation conflicts with the stated azure-core requirement, and event ID header handling has an unresolved bug.
Get a fresh assessment by requesting another Copilot review.
Pull request overview
Adds automatic sync and async SSE reconnection for generated Python clients, including retry delays, event ID resumption, terminal-event handling, and HTTP 204 stopping.
Changes:
Extends streaming runtimes with reconnection behavior.
Generates reconnect callbacks through the request and error-mapping pipeline.
Adds reconnect runtime behavior. Critical (3 votes): Resolve the requirement to use an azure-core reconnect primitive instead of a generator-vendored loop.
The normal operation path deserializes declared custom error responses and attaches them as HttpResponseError.model after map_error (the logic at builder_serializer.py:1170-1274). This reconnect path only calls map_error and then raises a bare HttpResponseError, so an unmapped custom or ranged error status during reconnect loses the generated error model and does not follow the operation's normal error behavior.
The linked requirement also calls out preserving 301/307 redirect handling, but the new sync/async integration tests only exercise 200/204 responses and do not drive a redirect during reconnect. Add a mock-server case for both modes so the generated send_request path and redirect policy are verified rather than only asserted as generated text.
The new reconnect-failure branch is only checked as generated text; neither sync nor async tests execute a generated SSE operation that returns a non-success reconnect response and verifies the error mapping. Add an end-to-end generated-client case for this branch, including a custom error response, so this behavior is protected.
retval.append(f" if _reconnect_response.status_code not in {reconnect_status_codes!r}:")
retval.append(f" {self._call_method}_reconnect_response.read()")
retval.append(
" map_error(status_code=_reconnect_response.status_code, "
"response=_reconnect_response, error_map=error_map)"
Remove Last-Event-ID from reconnect requests when the SSE ID buffer is empty, including after an empty id field clears a prior value.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 6bf64cba-786a-41ee-8e45-8567e1cf8568
The linked #11761 requirements explicitly call for using the azure-core streaming/transport implementation rather than a generator-vendored reconnect loop. This code puts the state machine in streaming_base.py.jinja2, which is copied into every generated client, so the generated runtime will fork from core behavior and miss future core fixes; integrate the core implementation (or update the stated requirement/dependency) instead.
Keep parsed event IDs pending until a blank line completes the SSE block so incomplete events cannot advance the reconnect cursor.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 6bf64cba-786a-41ee-8e45-8567e1cf8568
The linked issue explicitly defers this behavior until the azure-core streaming/transport layer provides a reconnect primitive and says not to add a generator-vendored reconnect loop. This new while True loop in the generated streaming runtime (together with the generated callback below) reintroduces that deferred architecture, so please integrate the azure-core primitive instead of embedding reconnection policy in every generated client.
while True:
if getattr(self._response, "status_code", None) == 204:
break
This does not follow the operation's normal error path for declared error models: handle_error_response deserializes the reconnect error body and attaches model=error to HttpResponseError, whereas this branch only calls map_error and then raises without a model. A reconnect that returns a custom 4xx/5xx therefore loses the generated error payload; reuse the same error deserialization/mapping path for the reconnect response.
…microsoft#12014)
## Summary
Enable Python emitter diff reports **and sticky comments** for fork pull
requests. The blanket job-level fork guard skipped every step of [the
emitter diff run for
microsoft#11904](https://github.com/microsoft/typespec/actions/runs/35247580542),
even though generation and report upload do not need a write token.
This follows the Go emitter's artifact-relay pattern and trusted
head-repository/branch targeting fix: Azure/typespec-azure#5124 and
Azure/typespec-azure#5191 ([current Go
commenter](https://github.com/Azure/typespec-azure/blob/main/.github/workflows/ci-emitter-diff-go-commenter.yml)).
- Keep generation on `pull_request`, with no fork guard, only `contents:
read`, and checkout credential persistence disabled. Preserve the HTML
report, inline summary, manual dispatch, and nonzero tool/build
failures; generated-code changes remain informational.
- Replace same-repository-only commenting with a small
`emitter-diff-python-comment` JSON artifact containing only comment
text. Save/upload it for PR runs, including setup/generation failures;
manual dispatch does not produce PR comments.
- Add `python / emitter diff commenter`, triggered by completed
`workflow_run` events for exactly `python / emitter diff`, with only
`actions: read` and `pull-requests: write`.
- Update the README and add durable tests that execute the workflow's
actual inline scripts with mocked APIs/filesystem.
## Privilege boundary and targeting
The listener **never checks out, builds, or executes PR code or artifact
scripts**. The producer setup/build paths use public source/dependency
downloads and require no privileged credentials. This does not use
`pull_request_target` or a privileged dispatch mechanism.
The listener verifies the producer workflow path and resolves exactly
one open PR from trusted `workflow_run` head repository owner/full name,
branch, and SHA. It lists PRs by `owner:branch`, then checks exact head
SHA/ref/repository and base repository. It does not trust artifact PR
numbers/markers, `workflow_run.pull_requests` (empty for forks), or
commit-association lookup.
Artifact data is only a nonempty string body (maximum 8,000 characters)
in a regular JSON file of at most 16 KiB. The marker and run stamp are
hardcoded/derived from trusted metadata. Artifact and comment lists are
paginated; only the intended GitHub Actions bot-owned marker comment can
be updated. The listener skips stale/closed/ambiguous PRs, rechecks the
current head immediately before posting, and combines per-branch
serialization with a run/attempt stamp to prevent older runs overwriting
newer comments.
Cancelled/skipped runs skip explicitly. Failed runs without a comment
artifact emit an explicit warning. Missing artifacts on successful runs,
expired/duplicate artifacts, malformed payloads, ambiguous bot comments,
download errors, and API errors surface as failures rather than being
silently ignored.
## Reviewing reports
Open the PR's **python / emitter diff** check, follow **Details**, and
read the job summary or download **emitter-diff-html** from the run's
**Artifacts** section (seven-day retention). After the listener is
active, fork and same-repository PRs also receive a sticky comment. The
small comment artifact is retained for one day.
## Validation
- **39 new durable workflow tests pass**, covering
fork/same-repository/manual conditions, trusted PR selection,
stale/closed/ambiguous/missing PRs, workflow identity, artifact errors,
bounded malformed data, bot ownership, pagination, newer-run guards, and
API failures. These execute the inline producer/listener scripts locally
with mocked APIs, not a hosted privileged listener.
- Twelve existing engineering path-selection tests pass (51 tests
total). `pnpm check:eng` and focused type-aware oxlint for the new tests
pass.
- Actionlint v1.7.12 (the repository CI version) passes for both
workflows; optional shellcheck/pyflakes integrations were disabled
because those tools are not installed locally.
- Required `pnpm format` completed with only the four intended files
changed. Targeted Prettier, cspell, and `git diff --check` pass.
- `pnpm chronus add @typespec/http-client-python --kind internal ...`
reported “No package changed”; `pnpm chronus verify --since origin/main`
passes. No package changelog is needed for workflow/docs/tests-only
changes.
- Full `pnpm lint` and `pnpm lint:fix` were reattempted and remain
blocked by the unchanged compiler tsconfig error that writing
`dist/src/server/tmlanguage.d.ts` would overwrite an input file. No
unrelated repair is included.
- Earlier full-build attempt completed 35 tasks before `typespec-vs`
failed NuGet restore due to local TLS failure reaching `api.nuget.org`
(NU1301). The compiler smoke check passed then; no compiler/emitter
implementation changes are introduced here.
- The prior revision's [hosted fork producer
run](https://github.com/microsoft/typespec/actions/runs/35305191236)
passed generation and uploaded HTML. That verifies removal of the
generation fork gate, **not this new listener**. End-to-end hosted fork
commenting cannot be verified until the listener lands on the default
branch.
## Activation for microsoft#11904
This PR does not modify microsoft#11904 or its author's branch. The new
`workflow_run` listener must first land on the repository's **default
branch**. Then update the SSE PR branch from `microsoft/typespec` main
and push a new producer run containing the comment-artifact changes
(approve the fork run if repository policy requires it). Rerunning the
original skipped run retains its old workflow revision and does not
apply this fix. No merges or privileged dispatches were performed.
---------
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The reason will be displayed to describe this comment to others. Learn more.
Copilot review overview
🔵 Needs a closer look
Reconnect failures lose operation-specific error mapping, and the implementation does not use the azure-core reconnect primitive required by the linked issue.
Reuse normal operation error handling for reconnect responses, including declared and default error model deserialization.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 6bf64cba-786a-41ee-8e45-8567e1cf8568
A valid SSE id: field updates the last-event-ID buffer when it is parsed, even if the current event block is incomplete. Deferring _last_id until _dispatch() means an EOF after id: second reconnects with a stale ID, so update the committed buffer in this branch as well as the pending value, and adjust the new incomplete-block test accordingly.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
emitter:client:pythonIssue for the Python client emitter: @typespec/http-client-python
5 participants
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Structured SSE streams currently stop at unexpected EOF and require callers to manually resume with
last_event_idthrough an internal workaround. This adds automatic reconnection so generated clients follow the SSE processing model without requiring application-owned reconnect loops.The generated stream runtime now:
retry:value --- Do we want a 3 second default or would we prefer no time delay, just immediately try to reconnect?Last-Event-IDCloses #11761