@@ -19,6 +19,7 @@ import type {
1919 SdkQueryParameter ,
2020 SdkServiceMethod ,
2121 SdkServiceResponseHeader ,
22+ SdkStreamMetadata ,
2223 SdkType ,
2324} from "@azure-tools/typespec-client-generator-core" ;
2425import {
@@ -30,15 +31,18 @@ import {
3031 shouldGenerateConvenient ,
3132 shouldGenerateProtocol ,
3233} from "@azure-tools/typespec-client-generator-core" ;
33- import type { Diagnostic } from "@typespec/compiler" ;
34+ import type { Diagnostic , Union } from "@typespec/compiler" ;
3435import {
36+ compilerAssert ,
3537 createDiagnosticCollector ,
3638 getDeprecated ,
3739 isErrorModel ,
3840 NoTarget ,
3941} from "@typespec/compiler" ;
42+ import { unsafe_getEventDefinitions } from "@typespec/events/experimental" ;
4043import type { HttpStatusCodeRange } from "@typespec/http" ;
4144import { getResourceOperation } from "@typespec/rest" ;
45+ import { isTerminalEvent } from "@typespec/sse" ;
4246import type { CSharpEmitterContext } from "../sdk-context.js" ;
4347import { collectionFormatToDelimMap } from "../type/collection-format.js" ;
4448import type { HttpResponseHeader } from "../type/http-response-header.js" ;
@@ -64,6 +68,7 @@ import type {
6468 InputMethodParameter ,
6569 InputPathParameter ,
6670 InputQueryParameter ,
71+ InputStreamingType ,
6772 InputType ,
6873} from "../type/input-type.js" ;
6974import { convertLroFinalStateVia } from "../type/operation-final-state-via.js" ;
@@ -234,7 +239,9 @@ export function fromSdkServiceMethodOperation(
234239 path : method . operation . path ,
235240 externalDocsUrl : getExternalDocs ( sdkContext , method . operation . __raw . operation ) ?. url ,
236241 requestMediaTypes : requestMediaTypes ,
237- bufferResponse : true ,
242+ bufferResponse : ! method . operation . responses . some ( ( response ) =>
243+ isSupportedStream ( response . streamMetadata ) ,
244+ ) ,
238245 generateProtocolMethod : shouldGenerateProtocol ( sdkContext , method . operation . __raw . operation ) ,
239246 generateConvenienceMethod : generateConvenience ,
240247 crossLanguageDefinitionId : method . crossLanguageDefinitionId ,
@@ -351,8 +358,23 @@ function fromSdkServiceMethodParameters(
351358 for ( const p of method . parameters ) {
352359 const methodInputParameter = diagnostics . pipe ( fromMethodParameter ( sdkContext , p , namespace ) ) ;
353360 const operationHttpParameter = getHttpOperationParameter ( method , p ) ;
361+ const streamMetadata = method . operation . bodyParam ?. streamMetadata ;
362+ const isStreamingBodyParameter =
363+ isJsonLinesStream ( streamMetadata ) &&
364+ method . operation . bodyParam ! . methodParameterSegments . some ( ( segments ) =>
365+ segments . some (
366+ ( segment ) =>
367+ segment === p || segment . crossLanguageDefinitionId === p . crossLanguageDefinitionId ,
368+ ) ,
369+ ) ;
354370
355371 if ( ! operationHttpParameter ) {
372+ if ( isStreamingBodyParameter ) {
373+ methodInputParameter . type = diagnostics . pipe (
374+ fromSdkStreamMetadata ( sdkContext , streamMetadata ) ,
375+ ) ;
376+ methodInputParameter . location = RequestLocation . Body ;
377+ }
356378 parameters . push ( methodInputParameter ) ;
357379 continue ;
358380 }
@@ -365,6 +387,12 @@ function fromSdkServiceMethodParameters(
365387 rootApiVersions ,
366388 diagnostics ,
367389 ) ;
390+ if ( isStreamingBodyParameter ) {
391+ methodInputParameter . type = diagnostics . pipe (
392+ fromSdkStreamMetadata ( sdkContext , streamMetadata ) ,
393+ ) ;
394+ methodInputParameter . location = RequestLocation . Body ;
395+ }
368396 parameters . push ( methodInputParameter ) ;
369397 }
370398
@@ -378,6 +406,15 @@ function updateMethodParameter(
378406 rootApiVersions : string [ ] ,
379407 diagnostics : ReturnType < typeof createDiagnosticCollector > ,
380408) : void {
409+ if (
410+ operationHttpParameter . kind === "body" &&
411+ isJsonLinesStream ( operationHttpParameter . streamMetadata )
412+ ) {
413+ methodParameter . type = diagnostics . pipe (
414+ fromSdkStreamMetadata ( sdkContext , operationHttpParameter . streamMetadata ) ,
415+ ) ;
416+ }
417+
381418 // for content type parameter
382419 if ( isContentType ( operationHttpParameter ) ) {
383420 methodParameter . type = diagnostics . pipe (
@@ -408,7 +445,9 @@ function fromSdkServiceMethodResponse(
408445 const diagnostics = createDiagnosticCollector ( ) ;
409446
410447 return diagnostics . wrap ( {
411- type : diagnostics . pipe ( getResponseType ( sdkContext , methodResponse . type ) ) ,
448+ type : isSupportedStream ( methodResponse . streamMetadata )
449+ ? diagnostics . pipe ( fromSdkStreamMetadata ( sdkContext , methodResponse . streamMetadata ) )
450+ : diagnostics . pipe ( getResponseType ( sdkContext , methodResponse . type ) ) ,
412451 resultSegments : methodResponse . resultSegments ?. map ( ( segment ) =>
413452 getResponseSegmentName ( segment ) ,
414453 ) ,
@@ -607,7 +646,9 @@ function fromBodyParameter(
607646 rootApiVersions : string [ ] ,
608647) : [ InputBodyParameter , readonly Diagnostic [ ] ] {
609648 const diagnostics = createDiagnosticCollector ( ) ;
610- const parameterType = diagnostics . pipe ( fromSdkType ( sdkContext , p . type , p ) ) ;
649+ const parameterType = isJsonLinesStream ( p . streamMetadata )
650+ ? diagnostics . pipe ( fromSdkStreamMetadata ( sdkContext , p . streamMetadata ) )
651+ : diagnostics . pipe ( fromSdkType ( sdkContext , p . type , p ) ) ;
611652
612653 const retVar : InputBodyParameter = {
613654 kind : "body" ,
@@ -725,7 +766,9 @@ export function fromSdkHttpOperationResponse(
725766 const range = sdkResponse . statusCodes ;
726767 retVar = {
727768 statusCodes : toStatusCodesArray ( range ) ,
728- bodyType : diagnostics . pipe ( getResponseType ( sdkContext , sdkResponse . type ) ) ,
769+ bodyType : isSupportedStream ( sdkResponse . streamMetadata )
770+ ? diagnostics . pipe ( fromSdkStreamMetadata ( sdkContext , sdkResponse . streamMetadata ) )
771+ : diagnostics . pipe ( getResponseType ( sdkContext , sdkResponse . type ) ) ,
729772 headers : diagnostics . pipe ( fromSdkServiceResponseHeaders ( sdkContext , sdkResponse . headers ) ) ,
730773 isErrorResponse :
731774 sdkResponse . type !== undefined && isErrorModel ( sdkContext . program , sdkResponse . type . __raw ! ) ,
@@ -737,6 +780,70 @@ export function fromSdkHttpOperationResponse(
737780 return diagnostics . wrap ( retVar ) ;
738781}
739782
783+ function fromSdkStreamMetadata (
784+ sdkContext : CSharpEmitterContext ,
785+ streamMetadata : SdkStreamMetadata ,
786+ ) : [ InputStreamingType , readonly Diagnostic [ ] ] {
787+ const diagnostics = createDiagnosticCollector ( ) ;
788+ const originalType = streamMetadata . originalType ;
789+ const streamKind = getStreamKind ( streamMetadata ) ;
790+ compilerAssert (
791+ streamKind !== undefined ,
792+ "Stream metadata must have a supported stream kind before it is converted." ,
793+ ) ;
794+ let terminalEventType : string | undefined ;
795+ let terminalEventValue : string | undefined ;
796+
797+ if ( streamKind === "sse" && streamMetadata . streamType . __raw ?. kind === "Union" ) {
798+ const eventDefinitions = diagnostics . pipe (
799+ unsafe_getEventDefinitions ( sdkContext . program , streamMetadata . streamType . __raw as Union ) ,
800+ ) ;
801+ const terminalDefinition = eventDefinitions . find ( ( definition ) =>
802+ isTerminalEvent ( sdkContext . program , definition . root ) ,
803+ ) ;
804+ if ( terminalDefinition ?. payloadType . kind === "String" ) {
805+ terminalEventType = terminalDefinition . eventType ;
806+ terminalEventValue = terminalDefinition . payloadType . value ;
807+ }
808+ }
809+
810+ return diagnostics . wrap ( {
811+ kind : "streaming" ,
812+ name : originalType . kind === "model" ? originalType . name : "Stream" ,
813+ valueType : diagnostics . pipe ( fromSdkType ( sdkContext , streamMetadata . streamType ) ) ,
814+ streamKind,
815+ contentTypes : streamMetadata . contentTypes ,
816+ terminalEventType,
817+ terminalEventValue,
818+ crossLanguageDefinitionId :
819+ originalType . kind === "model" ? originalType . crossLanguageDefinitionId : "" ,
820+ } ) ;
821+ }
822+
823+ function getStreamKind (
824+ streamMetadata : SdkStreamMetadata | undefined ,
825+ ) : InputStreamingType [ "streamKind" ] | undefined {
826+ if ( streamMetadata ?. contentTypes . includes ( "application/jsonl" ) ) {
827+ return "jsonl" ;
828+ }
829+ if ( streamMetadata ?. contentTypes . includes ( "text/event-stream" ) ) {
830+ return "sse" ;
831+ }
832+ return undefined ;
833+ }
834+
835+ function isSupportedStream (
836+ streamMetadata : SdkStreamMetadata | undefined ,
837+ ) : streamMetadata is SdkStreamMetadata {
838+ return getStreamKind ( streamMetadata ) !== undefined ;
839+ }
840+
841+ function isJsonLinesStream (
842+ streamMetadata : SdkStreamMetadata | undefined ,
843+ ) : streamMetadata is SdkStreamMetadata {
844+ return getStreamKind ( streamMetadata ) === "jsonl" ;
845+ }
846+
740847function fromSdkServiceResponseHeaders (
741848 sdkContext : CSharpEmitterContext ,
742849 headers : SdkServiceResponseHeader [ ] ,
0 commit comments