Skip to content

[agentserver-invocations] Serve AsyncAPI docs at /invocations/docs/asyncapi.{json,yaml} - #47829

Merged
ZhaoChaoqun merged 3 commits into
Azure:mainfrom
ZhaoChaoqun:chaoqunzhao/asyncapi-serve-5407709
Jul 16, 2026
Merged

[agentserver-invocations] Serve AsyncAPI docs at /invocations/docs/asyncapi.{json,yaml}#47829
ZhaoChaoqun merged 3 commits into
Azure:mainfrom
ZhaoChaoqun:chaoqunzhao/asyncapi-serve-5407709

Conversation

@ZhaoChaoqun

@ZhaoChaoqun ZhaoChaoqun commented Jul 3, 2026

Copy link
Copy Markdown
Member

Summary

Adds two AsyncAPI discovery endpoints to InvocationAgentServerHost, mirroring the existing openapi.json endpoint:

  • GET /invocations/docs/asyncapi.jsonapplication/json
  • GET /invocations/docs/asyncapi.yamlapplication/yaml

Optional constructor args asyncapi_spec_json (dict) and/or asyncapi_spec_yaml (raw YAML str) enable each endpoint independently. Path extension is authoritative for the returned content type — no Accept negotiation, no format conversion between the two representations. Either representation returns 404 if not registered; users may register only one, only the other, or both.

Rationale: AsyncAPI is the companion to OpenAPI for streaming/bidirectional surfaces (e.g. the invocations_ws WebSocket protocol) that OpenAPI cannot express. Serving both formats natively (rather than converting server-side) matches typical AsyncAPI tooling expectations and keeps the SDK dependency-free of a YAML library.

Constructor validates arg types up front (TypeError) to prevent a stringified dict leaking to clients as YAML.

Startup log extended to record which specs are configured.

Rollout & compatibility

Backward compatible: both constructor args default to None; existing hosts are unaffected, and each endpoint returns 404 until registered. Python SDK is independently mergeable and safe to ship ahead of the others.

Part of a 5-repo change adding AsyncAPI discovery to Agent Server:

Test plan

  • pytest tests/test_server_routes.py -v14/14 pass (5 new asyncapi tests + 2 type-validation tests)
  • Full package suite — 195 passed, 0 failed
  • Two rounds of internal code review (architecture + code-quality personas), all findings addressed

Files changed

  • azure/ai/agentserver/invocations/_invocation.py — 2 constructor args, 2 routes, 2 handlers, 2 getters, type validation, startup log
  • tests/test_server_routes.py — 7 new tests
  • README.md — endpoint table + "Serving an AsyncAPI spec" section
  • CHANGELOG.md1.0.0b7 (Unreleased) entry
  • api.md — API surface snapshot synchronized
  • _version.py — bumped 1.0.0b61.0.0b7

Fixes: AB#5407709

Copilot AI review requested due to automatic review settings July 3, 2026 09:05
@github-actions github-actions Bot added Community Contribution Community members are working on the issue customer-reported Issues that are reported by GitHub users external to the Azure organization. Hosted Agents sdk/agentserver/* labels Jul 3, 2026
@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Thank you for your contribution @ZhaoChaoqun! We will review the pull request and get back to you soon.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds two AsyncAPI discovery endpoints to InvocationAgentServerHost in the azure-ai-agentserver-invocations package, mirroring the existing OpenAPI endpoint. AsyncAPI complements OpenAPI for the streaming/WebSocket (invocations_ws) surface. Two optional constructor args (asyncapi_spec_json, asyncapi_spec_yaml) enable each endpoint independently, with path-extension-authoritative content types and no server-side format conversion (keeping the package free of a YAML dependency). Constructor type validation raises TypeError early, and startup logging is extended.

Changes:

  • New routes GET /invocations/docs/asyncapi.json (JSON) and GET /invocations/docs/asyncapi.yaml (raw YAML), each returning 404 via the upstream error-source path when unregistered.
  • New public constructor args + accessors (get_asyncapi_spec_json/get_asyncapi_spec_yaml) with upfront type validation.
  • Documentation updates in README and CHANGELOG plus new route/type-validation tests.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
azure/ai/agentserver/invocations/_invocation.py Adds AsyncAPI constructor args, type validation, two routes, two accessors, two endpoints, and extended startup logging (new public API not reflected in api.md).
tests/test_server_routes.py Adds tests for JSON/YAML 404, success, independence, and constructor type validation.
README.md Documents the new AsyncAPI endpoints and registration model.
CHANGELOG.md Adds an 1.0.0b7 (Unreleased) Features entry (but _version.py still says 1.0.0b6).

Comment thread sdk/agentserver/azure-ai-agentserver-invocations/CHANGELOG.md
…yncapi.{json,yaml}

InvocationAgentServerHost now accepts optional `asyncapi_spec_json` (dict) and/or
`asyncapi_spec_yaml` (raw YAML str) constructor args. When set, they are served
at GET /invocations/docs/asyncapi.json and GET /invocations/docs/asyncapi.yaml
respectively — the AsyncAPI companion to the existing openapi.json endpoint for
streaming/bidirectional surfaces (e.g. the invocations_ws WebSocket protocol)
that OpenAPI cannot express.

Path extension is authoritative for the returned content type — no Accept
negotiation, no format conversion between the two representations. Either
returns 404 if not registered; users may register only one, only the other,
or both.

Constructor rejects wrong-typed args (dict/str) with TypeError to prevent
misuse from silently leaking a stringified dict to clients as YAML.

Startup log extended to record which specs are configured.

Bumps version to 1.0.0b7 and syncs api.md with the new public surface.

Companion to TypeSpec PR Azure/azure-rest-api-specs#44416.

Fixes: AB#5407709
…alidation

- _invocation.py: set media_type to "application/yaml; charset=utf-8".
  RFC 9512 mandates UTF-8 for application/yaml, but clients don't
  universally assume it; declaring the charset avoids downstream
  mis-decoding of non-ASCII content in the raw YAML string.
- _invocation.py: add a comment on the asyncapi_spec_* isinstance
  guards explaining why openapi_spec is intentionally left unchecked
  (backward-compat with dict-like Mapping subclasses), so future
  readers don't file the inconsistency as a bug.
- test_server_routes.py: drop @pytest.mark.asyncio + async on the two
  TypeError-raising tests. Their bodies are purely synchronous
  (constructor call inside pytest.raises); the async wrapping was
  spurious.
@ZhaoChaoqun
ZhaoChaoqun force-pushed the chaoqunzhao/asyncapi-serve-5407709 branch from 8ec9194 to 6583857 Compare July 16, 2026 07:11
@ZhaoChaoqun
ZhaoChaoqun enabled auto-merge (squash) July 16, 2026 07:11
@ZhaoChaoqun
ZhaoChaoqun force-pushed the chaoqunzhao/asyncapi-serve-5407709 branch from b6c98e7 to 4d0f2cd Compare July 16, 2026 07:35
@ZhaoChaoqun ZhaoChaoqun removed customer-reported Issues that are reported by GitHub users external to the Azure organization. Community Contribution Community members are working on the issue labels Jul 16, 2026
@ZhaoChaoqun
ZhaoChaoqun merged commit 52e1abc into Azure:main Jul 16, 2026
24 checks passed
ZhaoChaoqun added a commit to Azure/azure-sdk-for-net that referenced this pull request Jul 17, 2026
…s/docs/asyncapi.{json,yaml} (#60663)

* [Azure.AI.AgentServer.Invocations] Serve AsyncAPI docs at /invocations/docs/asyncapi.{json,yaml}

InvocationHandler now exposes two new virtual methods, GetAsyncApiJsonAsync
and GetAsyncApiYamlAsync, served at GET /invocations/docs/asyncapi.json and
GET /invocations/docs/asyncapi.yaml respectively. Both default to 404;
override either or both to publish the AsyncAPI companion to the existing
openapi.json endpoint for streaming/bidirectional surfaces (e.g. the
invocations_ws WebSocket protocol) that OpenAPI cannot express.

Path extension is authoritative for the returned content type — no Accept
negotiation, no format conversion between the two representations. JSON and
YAML are served independently at their own paths; users may override one or
both.

Design choice: two distinct virtual methods rather than a single method with
a format enum. Rationale: mirrors GetOpenApiAsync (strongly-typed, no magic
values), matches .NET conventions, keeps SDK dependency-free of a YAML
library (users write their preferred YAML directly to response.Body), and
lets a handler that only supports one format skip the other with zero
boilerplate.

Companion to Python PR Azure/azure-sdk-for-python#47829 and TypeSpec PR
Azure/azure-rest-api-specs#44416.

Fixes: AB#5407709

* Address review: extract session-id helper, rename lambda params, sync XML docs, split independence test

- InvocationEndpointHandler.cs: extract InjectSessionIdHeaderFromEnv
  helper and migrate HandleGetOpenApiAsync + both new AsyncAPI
  dispatchers to use it. Removes the third copy of the identical
  session-id-from-env header injection block (rule of three).
- InvocationsServerEndpointRouteBuilderExtensions.cs: rename lambda
  parameters `handler` -> `endpointHandler` and `invocationHandler`
  -> `userHandler` across all MapPost/MapGet/Map calls (POST /invocations,
  GET /invocations/{id}, cancel, openapi.json, asyncapi.json,
  asyncapi.yaml, invocations_ws). The old names had ambiguous
  distinguishability inside a single lambda scope; the new names make
  the endpoint-vs-user-handler split explicit and are applied
  consistently rather than only on the new endpoints.
- InvocationHandler.cs: mirror the "path extension authoritative,
  no Accept negotiation, no format conversion" remarks onto
  GetAsyncApiYamlAsync (previously only on the JSON method), and add
  an explicit "must set StatusCode when overriding" warning to both
  virtual methods -- the base 404 is only in effect while the base
  implementation is used, and override authors who forget to set a
  status get 200 with whatever body they wrote.
- tests: split GetAsyncApi_JsonAndYamlAreIndependent into two focused
  tests (JSON-only override, YAML-only override). Each starts and
  stops one TestServer, so a failure isolates to a single direction
  and the reassigned `builder`/`app`/`client` locals go away.

* Address Copilot review: clarify override doc, add explicit StatusCode to README sample
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Hosted Agents sdk/agentserver/*

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants