[agentserver-invocations] Serve AsyncAPI docs at /invocations/docs/asyncapi.{json,yaml} - #47829
Conversation
|
Thank you for your contribution @ZhaoChaoqun! We will review the pull request and get back to you soon. |
There was a problem hiding this comment.
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) andGET /invocations/docs/asyncapi.yaml(raw YAML), each returning404via 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). |
818269b to
cc2b944
Compare
…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.
8ec9194 to
6583857
Compare
b6c98e7 to
4d0f2cd
Compare
…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
Summary
Adds two AsyncAPI discovery endpoints to
InvocationAgentServerHost, mirroring the existingopenapi.jsonendpoint:GET /invocations/docs/asyncapi.json→application/jsonGET /invocations/docs/asyncapi.yaml→application/yamlOptional constructor args
asyncapi_spec_json(dict) and/orasyncapi_spec_yaml(raw YAML str) enable each endpoint independently. Path extension is authoritative for the returned content type — noAcceptnegotiation, no format conversion between the two representations. Either representation returns404if 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_wsWebSocket 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 stringifieddictleaking 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 returns404until 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 -v— 14/14 pass (5 new asyncapi tests + 2 type-validation tests)Files changed
azure/ai/agentserver/invocations/_invocation.py— 2 constructor args, 2 routes, 2 handlers, 2 getters, type validation, startup logtests/test_server_routes.py— 7 new testsREADME.md— endpoint table + "Serving an AsyncAPI spec" sectionCHANGELOG.md—1.0.0b7 (Unreleased)entryapi.md— API surface snapshot synchronized_version.py— bumped1.0.0b6→1.0.0b7Fixes: AB#5407709