@@ -1091,6 +1091,116 @@ public async Task CreateResponseStreaming_ContentPartAdded_IncludesEventAsync()
10911091 Assert . Contains ( updates , u => u is StreamingResponseContentPartAddedUpdate ) ;
10921092 }
10931093
1094+ /// <summary>
1095+ /// Verifies that when a client provides a conversation ID, the underlying IChatClient
1096+ /// does NOT receive that conversation ID via ChatOptions.ConversationId.
1097+ /// This ensures that the host's conversation management is separate from the IChatClient's
1098+ /// conversation handling (if any).
1099+ /// </summary>
1100+ [ Fact ]
1101+ public async Task CreateResponse_WithConversationId_DoesNotForwardConversationIdToIChatClientAsync ( )
1102+ {
1103+ // Arrange
1104+ const string AgentName = "conversation-id-agent" ;
1105+ const string Instructions = "You are a helpful assistant." ;
1106+ const string ExpectedResponse = "Response" ;
1107+
1108+ this . _httpClient = await this . CreateTestServerWithConversationsAsync ( AgentName , Instructions , ExpectedResponse ) ;
1109+ var mockChatClient = this . ResolveMockChatClient ( ) ;
1110+
1111+ // First, create a conversation
1112+ var createConversationRequest = new { metadata = new { agent_id = AgentName } } ;
1113+ string createConvJson = System . Text . Json . JsonSerializer . Serialize ( createConversationRequest ) ;
1114+ using StringContent createConvContent = new ( createConvJson , Encoding . UTF8 , "application/json" ) ;
1115+ HttpResponseMessage createConvResponse = await this . _httpClient . PostAsync (
1116+ new Uri ( "/v1/conversations" , UriKind . Relative ) ,
1117+ createConvContent ) ;
1118+ Assert . True ( createConvResponse . IsSuccessStatusCode , $ "Create conversation failed: { createConvResponse . StatusCode } ") ;
1119+
1120+ string convResponseJson = await createConvResponse . Content . ReadAsStringAsync ( ) ;
1121+ using var convDoc = System . Text . Json . JsonDocument . Parse ( convResponseJson ) ;
1122+ string conversationId = convDoc . RootElement . GetProperty ( "id" ) . GetString ( ) ! ;
1123+
1124+ // Act - Send request with conversation ID using raw HTTP
1125+ // (OpenAI SDK doesn't expose ConversationId directly on ResponseCreationOptions)
1126+ var requestBody = new
1127+ {
1128+ input = "Test" ,
1129+ agent = new { name = AgentName } ,
1130+ conversation = conversationId ,
1131+ stream = false
1132+ } ;
1133+ string requestJson = System . Text . Json . JsonSerializer . Serialize ( requestBody ) ;
1134+ using StringContent content = new ( requestJson , Encoding . UTF8 , "application/json" ) ;
1135+ HttpResponseMessage httpResponse = await this . _httpClient . PostAsync (
1136+ new Uri ( $ "/{ AgentName } /v1/responses", UriKind . Relative ) ,
1137+ content ) ;
1138+
1139+ // Assert - Response is successful
1140+ Assert . True ( httpResponse . IsSuccessStatusCode , $ "Response status: { httpResponse . StatusCode } ") ;
1141+
1142+ // Assert - The IChatClient should have received ChatOptions, but without the ConversationId set
1143+ Assert . NotNull ( mockChatClient . LastChatOptions ) ;
1144+ Assert . Null ( mockChatClient . LastChatOptions . ConversationId ) ;
1145+ }
1146+
1147+ /// <summary>
1148+ /// Verifies that when a client provides a conversation ID in streaming mode, the underlying
1149+ /// IChatClient does NOT receive that conversation ID via ChatOptions.ConversationId.
1150+ /// </summary>
1151+ [ Fact ]
1152+ public async Task CreateResponseStreaming_WithConversationId_DoesNotForwardConversationIdToIChatClientAsync ( )
1153+ {
1154+ // Arrange
1155+ const string AgentName = "conversation-streaming-agent" ;
1156+ const string Instructions = "You are a helpful assistant." ;
1157+ const string ExpectedResponse = "Streaming response" ;
1158+
1159+ this . _httpClient = await this . CreateTestServerWithConversationsAsync ( AgentName , Instructions , ExpectedResponse ) ;
1160+ var mockChatClient = this . ResolveMockChatClient ( ) ;
1161+
1162+ // First, create a conversation
1163+ var createConversationRequest = new { metadata = new { agent_id = AgentName } } ;
1164+ string createConvJson = System . Text . Json . JsonSerializer . Serialize ( createConversationRequest ) ;
1165+ using StringContent createConvContent = new ( createConvJson , Encoding . UTF8 , "application/json" ) ;
1166+ HttpResponseMessage createConvResponse = await this . _httpClient . PostAsync (
1167+ new Uri ( "/v1/conversations" , UriKind . Relative ) ,
1168+ createConvContent ) ;
1169+ Assert . True ( createConvResponse . IsSuccessStatusCode , $ "Create conversation failed: { createConvResponse . StatusCode } ") ;
1170+
1171+ string convResponseJson = await createConvResponse . Content . ReadAsStringAsync ( ) ;
1172+ using var convDoc = System . Text . Json . JsonDocument . Parse ( convResponseJson ) ;
1173+ string conversationId = convDoc . RootElement . GetProperty ( "id" ) . GetString ( ) ! ;
1174+
1175+ // Act - Send streaming request with conversation ID using raw HTTP
1176+ var requestBody = new
1177+ {
1178+ input = "Test" ,
1179+ agent = new { name = AgentName } ,
1180+ conversation = conversationId ,
1181+ stream = true
1182+ } ;
1183+ string requestJson = System . Text . Json . JsonSerializer . Serialize ( requestBody ) ;
1184+ using StringContent content = new ( requestJson , Encoding . UTF8 , "application/json" ) ;
1185+ HttpResponseMessage httpResponse = await this . _httpClient . PostAsync (
1186+ new Uri ( $ "/{ AgentName } /v1/responses", UriKind . Relative ) ,
1187+ content ) ;
1188+
1189+ // Assert - Response is successful and is SSE
1190+ Assert . True ( httpResponse . IsSuccessStatusCode , $ "Response status: { httpResponse . StatusCode } ") ;
1191+ Assert . Equal ( "text/event-stream" , httpResponse . Content . Headers . ContentType ? . MediaType ) ;
1192+
1193+ // Consume the SSE stream to complete the request
1194+ string sseContent = await httpResponse . Content . ReadAsStringAsync ( ) ;
1195+
1196+ // Verify streaming completed successfully by checking for response.completed event
1197+ Assert . Contains ( "response.completed" , sseContent ) ;
1198+
1199+ // Assert - The IChatClient should have received ChatOptions, but without the ConversationId set
1200+ Assert . NotNull ( mockChatClient . LastChatOptions ) ;
1201+ Assert . Null ( mockChatClient . LastChatOptions . ConversationId ) ;
1202+ }
1203+
10941204 private OpenAIResponseClient CreateResponseClient ( string agentName )
10951205 {
10961206 return new OpenAIResponseClient (
@@ -1103,6 +1213,19 @@ private OpenAIResponseClient CreateResponseClient(string agentName)
11031213 } ) ;
11041214 }
11051215
1216+ private TestHelpers . SimpleMockChatClient ResolveMockChatClient ( )
1217+ {
1218+ ArgumentNullException . ThrowIfNull ( this . _app , nameof ( this . _app ) ) ;
1219+
1220+ var chatClient = this . _app . Services . GetRequiredKeyedService < IChatClient > ( "chat-client" ) ;
1221+ if ( chatClient is not TestHelpers . SimpleMockChatClient mockChatClient )
1222+ {
1223+ throw new InvalidOperationException ( "Mock chat client not found or of incorrect type." ) ;
1224+ }
1225+
1226+ return mockChatClient ;
1227+ }
1228+
11061229 private async Task < HttpClient > CreateTestServerAsync ( string agentName , string instructions , string responseText = "Test response" )
11071230 {
11081231 WebApplicationBuilder builder = WebApplication . CreateBuilder ( ) ;
@@ -1125,6 +1248,30 @@ private async Task<HttpClient> CreateTestServerAsync(string agentName, string in
11251248 return testServer . CreateClient ( ) ;
11261249 }
11271250
1251+ private async Task < HttpClient > CreateTestServerWithConversationsAsync ( string agentName , string instructions , string responseText = "Test response" )
1252+ {
1253+ WebApplicationBuilder builder = WebApplication . CreateBuilder ( ) ;
1254+ builder . WebHost . UseTestServer ( ) ;
1255+
1256+ IChatClient mockChatClient = new TestHelpers . SimpleMockChatClient ( responseText ) ;
1257+ builder . Services . AddKeyedSingleton ( "chat-client" , mockChatClient ) ;
1258+ builder . AddOpenAIResponses ( ) ;
1259+ builder . AddOpenAIConversations ( ) ;
1260+ builder . AddAIAgent ( agentName , instructions , chatClientServiceKey : "chat-client" ) ;
1261+
1262+ this . _app = builder . Build ( ) ;
1263+ AIAgent agent = this . _app . Services . GetRequiredKeyedService < AIAgent > ( agentName ) ;
1264+ this . _app . MapOpenAIResponses ( agent ) ;
1265+ this . _app . MapOpenAIConversations ( ) ;
1266+
1267+ await this . _app . StartAsync ( ) ;
1268+
1269+ TestServer testServer = this . _app . Services . GetRequiredService < IServer > ( ) as TestServer
1270+ ?? throw new InvalidOperationException ( "TestServer not found" ) ;
1271+
1272+ return testServer . CreateClient ( ) ;
1273+ }
1274+
11281275 private async Task < HttpClient > CreateTestServerWithCustomClientAsync ( string agentName , string instructions , IChatClient chatClient )
11291276 {
11301277 WebApplicationBuilder builder = WebApplication . CreateBuilder ( ) ;
0 commit comments