Commit 2635e48
Add OpenAPI 3.2 stream emission support with itemSchema (#8888)
## Implementation Plan for Stream Support in OpenAPI 3.2
This PR implements support for streaming responses (including
Server-Sent Events) in OpenAPI 3.2 emission and import using the
`itemSchema` field.
### Checklist
- [x] 1. Add `itemSchema` support to OpenAPI 3.2 MediaType definition in
types.ts
- [x] 2. Create an SSE module (sse-module.ts) following the
xml-module.ts pattern
- [x] 3. Integrate SSE module into the OpenAPI emitter for emission
support
- [x] 4. Add package dependencies for @typespec/streams,
@typespec/events, @typespec/sse
- [x] 5. Use getStreamMetadata from @typespec/http/experimental to
detect streams
- [x] 6. Remove debug console.log statements
- [x] 7. Tests passing for SSE emission
- [x] 8. Handle @events decorator to emit proper SSE event structures
- [x] 9. Handle @ContentType decorator on union variants
- [x] 10. Handle @terminalevent decorator (preserve with @extension)
- [x] 11. Run formatter and linter
- [x] 12. Add changelog entry with chronus
- [x] 13. Generalize stream support for all content types
### Changes Made
**Type Definitions:**
- Added `itemSchema` property to `OpenAPIMediaType3_2` for streaming
event schema definitions
**SSE Module (`sse-module.ts`):**
- Created stream detection and emission logic following `xml-module.ts`
pattern
- Implements stream detection via `@typespec/streams.getStreamOf`
- Generates `itemSchema` with `oneOf` variants for `@events` union
members
- Works with any stream content type (SSE, JSON Lines, etc.)
**OpenAPI Emitter Integration:**
- Integrated SSE module as optional dependency alongside XML/JSON Schema
- Modified `getBodyContentEntry` to detect all stream responses via
`getStreamMetadata`
- Routes stream responses through emission path for OpenAPI 3.2 with
`itemSchema`
- Generalized to support any `HttpStream<ContentType, Type>`, not just
SSE streams
**Testing:**
- All stream tests passing
- Simplified test structure by removing namespaces and extra decorators
- Used ApiTester base for common libraries with stream-specific imports
on top
**Diagnostics:**
- Added "streams-not-supported" diagnostic for OpenAPI 3.0/3.1
- Warning message indicates streams with itemSchema require OpenAPI
3.2.0
### Stream Support
This implementation supports any stream type in OpenAPI 3.2.0:
- Server-Sent Events (SSE) with `text/event-stream`
- JSON Lines with `application/jsonl`
- Any custom `HttpStream<ContentType, Type>`
For OpenAPI 3.0/3.1, streams are emitted without `itemSchema` and a
warning is generated.
- Fixes #8887
<!-- START COPILOT CODING AGENT SUFFIX -->
<details>
<summary>Original prompt</summary>
----
*This section details on the original issue you should resolve*
<issue_title>[Feat] OpenAPI - add support for SSE import and
emission</issue_title>
<issue_description>### Clear and concise description of the problem
Now that we have support for OpenAPI 3.2.0 (#8828 ) both in emissions
and in importing descriptions to TypeSpec, it'd be nice to have support
for importing SSE (server-sent events).
@timotheeguerin and I spent the better part of an hour combing through
multiple specifications to understand what needs to happen here, so
strap in, this is going to be a long issue.
## Relevant resources
- #154 as the initial specification for sse in
typespec
- https://github.com/OAI/OpenAPI-Specification/discussions/5096 ongoing
discussion about terminal events
-
https://github.com/Azure/azure-sdk-for-python/blob/894d166350f0ccbe2fae467e4093ed0c3b428213/sdk/ai/azure-ai-agents/azure/ai/agents/operations/_patch.py#L564
manual implementation of SSE for Azure AI Foundry: Agents threads run
API
-
https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/ai/azure-ai-agents/tests/assets/send_email_stream_response.txt
an example of the payloads sent by this API
- [WHATWG specification for
SSE](https://html.spec.whatwg.org/multipage/server-sent-events.html#server-sent-events)
- [OAI 3.2.0 example on how to describe
SSE](https://spec.openapis.org/oas/v3.2.0.html#server-sent-event-streams)
- [OpenAI terminal event
description](https://platform.openai.com/docs/api-reference/runs/createRun#runs_createrun-stream)
- [OpenAI streaming responses
documentation](https://platform.openai.com/docs/guides/streaming-responses?api-mode=responses)
- [Blog post about using SSE APIs with
OpenAI](https://medium.com/better-programming/openai-sse-sever-side-events-streaming-api-733b8ec32897)
- [TypeSpec very sparse documentation for
SSE](https://typespec.io/docs/libraries/sse/reference/)
## Import
### Scenario 1 : no terminal event
Given the following OpenAPI Response object.
```yaml
content:
description: A request body to add a stream of typed data.
required: true
content:
text/event-stream:
itemSchema:
type: object
properties:
event:
type: string
data:
type: string
required: [event]
# Define event types and specific schemas for the corresponding data
oneOf:
- properties:
event:
const: userconnect
data:
contentMediaType: application/json
contentSchema:
type: object
required: [username]
properties:
username:
type: string
- properties:
event:
const: usermessage
data:
contentMediaType: application/json
contentSchema:
type: object
required: [text]
properties:
text:
type: string
```
I'd expect aresulting TypeSpec description looking like this.
```tsp
import "@typespec/streams";
import "@typespec/sse";
import "@typespec/events";
using SSE;
model UserConnect {
username: string;
}
model UserMessage {
text: string;
}
@TypeSpec.Events.events
union ChannelEvents {
userconnect: UserConnect,
usermessage: UserMessage,
}
op subscribeToChannel(): SSEStream<ChannelEvents>;
```
Note that the terminal event is NOT present, this is ok because it's an
invention of some APIs, and is not part of the WHATWG spec. It should
remain optional.
Here a couple of things are worth noting:
- imports for typespec streams/sse/events are added
- a using for SSE is added
- a `@TypeSpec.Events.events` decorator is added to the union type
- the return type of the operation is now `SSEStream<ChannelEvents>`
(instead of ChannelEvents)
- The union type discriminator values are obtained by conventions based
on the event properties in the schema (name is a convention)
- the union type member types are defined by convention by the schema of
the data property, and the fact the content media type is
application/json
### Scenario 2: with terminal events
Given the following OpenAPI Response object.
```yaml
content:
description: A request body to add a stream of typed data.
required: true
content:
text/event-stream:
itemSchema:
type: object
properties:
event:
type: string
data:
type: string
required: [event]
# Define event types and specific schemas for the corresponding data
oneOf:
- properties:
data:
contentMediaType: text/plain
const: "[done]"
"x-ms-sse-terminal-event": true
- properties:
event:
const: userconnect
data:
contentMediaType: app...
</details>
- Fixes #8887
<!-- START COPILOT CODING AGENT TIPS -->
---
💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more [Copilot coding agent tips](https://gh.io/copilot-coding-agent-tips) in the docs.
---------
Signed-off-by: Vincent Biret <vibiret@microsoft.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: baywet <7905502+baywet@users.noreply.github.com>
Co-authored-by: Vincent Biret <vibiret@microsoft.com>
Co-authored-by: Timothee Guerin <tiguerin@microsoft.com>1 parent 0473ebe commit 2635e48
File tree
20 files changed
+1109
-30
lines changed- .chronus/changes
- packages/openapi3
- src
- cli/actions/convert
- generators
- transforms
- utils
- test
- tsp-openapi3
- output/sse-import-scenarios
- specs/sse-import-scenarios
20 files changed
+1109
-30
lines changedLines changed: 7 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
6 | 6 | | |
7 | 7 | | |
8 | 8 | | |
9 | | - | |
10 | | - | |
11 | 9 | | |
12 | 10 | | |
13 | 11 | | |
14 | 12 | | |
15 | 13 | | |
| 14 | + | |
| 15 | + | |
16 | 16 | | |
17 | 17 | | |
18 | 18 | | |
| |||
230 | 230 | | |
231 | 231 | | |
232 | 232 | | |
233 | | - | |
234 | 233 | | |
| 234 | + | |
235 | 235 | | |
236 | 236 | | |
237 | 237 | | |
| |||
293 | 293 | | |
294 | 294 | | |
295 | 295 | | |
| 296 | + | |
296 | 297 | | |
297 | 298 | | |
298 | 299 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
41 | 41 | | |
42 | 42 | | |
43 | 43 | | |
44 | | - | |
| 44 | + | |
| 45 | + | |
45 | 46 | | |
46 | 47 | | |
47 | 48 | | |
| |||
73 | 74 | | |
74 | 75 | | |
75 | 76 | | |
76 | | - | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
77 | 81 | | |
78 | 82 | | |
79 | 83 | | |
| |||
84 | 88 | | |
85 | 89 | | |
86 | 90 | | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
87 | 100 | | |
88 | 101 | | |
89 | 102 | | |
| |||
98 | 111 | | |
99 | 112 | | |
100 | 113 | | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
101 | 117 | | |
102 | 118 | | |
103 | 119 | | |
| |||
Lines changed: 10 additions & 2 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
7 | 7 | | |
8 | 8 | | |
9 | 9 | | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
10 | 18 | | |
11 | 19 | | |
12 | 20 | | |
13 | | - | |
| 21 | + | |
14 | 22 | | |
15 | 23 | | |
16 | | - | |
| 24 | + | |
17 | 25 | | |
18 | 26 | | |
19 | 27 | | |
| |||
Lines changed: 163 additions & 8 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
20 | 20 | | |
21 | 21 | | |
22 | 22 | | |
| 23 | + | |
| 24 | + | |
23 | 25 | | |
24 | 26 | | |
25 | 27 | | |
| |||
83 | 85 | | |
84 | 86 | | |
85 | 87 | | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
86 | 227 | | |
87 | 228 | | |
88 | 229 | | |
| |||
103 | 244 | | |
104 | 245 | | |
105 | 246 | | |
| 247 | + | |
| 248 | + | |
106 | 249 | | |
107 | 250 | | |
108 | 251 | | |
109 | 252 | | |
110 | | - | |
| 253 | + | |
111 | 254 | | |
112 | | - | |
| 255 | + | |
113 | 256 | | |
114 | 257 | | |
115 | 258 | | |
116 | 259 | | |
117 | 260 | | |
118 | | - | |
119 | | - | |
120 | | - | |
121 | | - | |
122 | | - | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
123 | 264 | | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
124 | 279 | | |
125 | 280 | | |
126 | 281 | | |
| |||
140 | 295 | | |
141 | 296 | | |
142 | 297 | | |
143 | | - | |
| 298 | + | |
144 | 299 | | |
145 | 300 | | |
146 | 301 | | |
| |||
Lines changed: 23 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
220 | 220 | | |
221 | 221 | | |
222 | 222 | | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
223 | 246 | | |
224 | 247 | | |
225 | 248 | | |
| |||
Lines changed: 11 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
141 | 141 | | |
142 | 142 | | |
143 | 143 | | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
144 | 154 | | |
145 | 155 | | |
146 | 156 | | |
147 | | - | |
| 157 | + | |
148 | 158 | | |
149 | 159 | | |
150 | 160 | | |
| |||
0 commit comments