Skip to content

Conversation

@eavanvalkenburg
Copy link
Member

@eavanvalkenburg eavanvalkenburg commented Jan 20, 2026

Motivation and Context

Adds a Generic setup for ChatResponse/AgentResponse and the run and get_response methods.

This now is typed correctly:

agent = OpenAIResponsesClient().as_agent(name="Agent", instructions="...")

result = await agent.run("....", options={"response_format": OutputModel})
# result will have inferred type: `AgentResponse[OutputModel]` and result.value has `OutputModel | None` (because it still needs to parse the text into the model)

This does not work for the type inference of AgentResponse in the sample below, because I did not want agentto either have this always become ChatAgent[ChatOptions[OutputModel]] or have to add overloads to every client and explicit options settings when creating ChatAgent directly:

agent = OpenAIResponsesClient().as_agent(name="Agent", instructions="...", default_options={"response_format": OutputModel})")
result = await agent.run("...")
# result will have inferred type: `AgentResponse[Any]` and result.value has `Any | None`

It also works for:

result = await AgentResponse.from_agent_response_generator(
        agent.run_stream(query, options={"response_format": OutputStruct}), 
        output_format_type=OutputStruct,
    )

Fixes #3091

Description

Contribution Checklist

  • The code builds clean without any errors or warnings
  • The PR follows the Contribution Guidelines
  • All unit tests pass, and I have added new tests where possible
  • Is this a breaking change? If yes, add "[BREAKING]" prefix to the title of the PR.

Copilot AI review requested due to automatic review settings January 20, 2026 14:14
Copy link
Contributor

Copilot AI left a comment

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 enhances type safety for structured outputs by adding generic type parameters to ChatOptions, ChatResponse, and AgentResponse. When using response_format with a Pydantic model, the type system now correctly infers the return type, improving IDE autocomplete and type checking.

Changes:

  • Added generic type parameters (TResponseModel) to ChatOptions, ChatResponse, and AgentResponse classes
  • Updated all provider-specific ChatOptions implementations (OpenAI, Azure, Anthropic, Bedrock, Ollama, Foundry Local, AG-UI) to support generics
  • Added overloads to get_response() and run() methods to properly infer types when response_format is specified
  • Updated sample code to use the simpler .value property instead of try_parse_value()
  • Cleaned up unused code in OpenAIAssistantsClient (removed duplicate as_agent implementation and unused imports)

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
_types.py Added TResponseModel generic parameter to ChatOptions, ChatResponse, and AgentResponse; added overloads for try_parse_value, from_chat_response_generator, and from_agent_response_generator
_clients.py Added overloads to get_response() for type inference; updated as_agent() signature to accept generic options
_agents.py Added overloads to run() for type inference; updated to properly set response_format on AgentResponse
openai/_chat_client.py Made OpenAIChatOptions generic over TResponseModel
openai/_responses_client.py Made OpenAIResponsesOptions generic over TResponseFormat
openai/_assistants_client.py Made OpenAIAssistantsOptions generic; removed duplicate as_agent override and cleaned up unused imports
azure/_chat_client.py Made AzureOpenAIChatOptions generic over TResponseModel
anthropic/_chat_client.py Made AnthropicChatOptions generic over TResponseModel
bedrock/_chat_client.py Made BedrockChatOptions generic over TResponseModel
ollama/_chat_client.py Made OllamaChatOptions generic over TResponseModel
foundry_local/_foundry_local_client.py Made FoundryLocalChatOptions generic over TResponseModel
ag_ui/_types.py Made AGUIChatOptions generic over TResponseModel
ag_ui/_client.py Added cast to ensure return type compatibility
openai_responses_client_with_structured_output.py Updated sample to use .value property instead of try_parse_value()

@markwallace-microsoft
Copy link
Member

markwallace-microsoft commented Jan 21, 2026

Python Test Coverage

Python Test Coverage Report •
FileStmtsMissCoverMissing
packages/ag-ui/agent_framework_ag_ui
   _client.py1371092%84–88, 234, 264, 419–421
   _types.py330100% 
packages/anthropic/agent_framework_anthropic
   _chat_client.py34213660%365, 388, 390, 403, 425–428, 478–479, 488, 490–491, 496, 513–514, 556, 571, 575–576, 592, 601, 603, 607–608, 649–651, 653, 666–667, 674–676, 680–682, 686–689, 700, 702, 724, 734, 756–762, 769–770, 778–779, 787–790, 797–798, 804–805, 811–812, 818, 826–828, 832, 839–840, 846–847, 853–854, 860, 868–871, 878–879, 898, 905–906, 925, 947, 949, 958–959, 965, 987–988, 994–995, 1004–1014, 1021–1027, 1034–1040, 1047–1056, 1063–1066
packages/azure-ai/agent_framework_azure_ai
   _agent_provider.py1151289%122–123, 251, 444–446, 477–482
   _chat_client.py4789480%377, 638–639, 641, 644, 647, 650–655, 658, 660, 668, 680–682, 686, 689–690, 698–701, 711, 719–722, 724–725, 727–728, 735, 743–744, 752–753, 758–759, 763–770, 775, 778, 786, 792, 800–802, 805, 827–828, 938, 940, 942, 957, 985, 998–1002, 1041–1043, 1046–1047, 1093–1099, 1121, 1147, 1156, 1165, 1167, 1172, 1181–1186, 1298
   _client.py1874377%250–253, 258, 261–264, 269, 272–273, 276, 283, 393, 420–423, 466, 503, 505, 510, 512–513, 516–519, 521, 523–524, 526–534, 536, 581
   _project_provider.py961386%129–130, 207, 216, 281, 325, 358, 407–409, 413–415
packages/chatkit/agent_framework_chatkit
   _converter.py1334665%116, 121, 169, 171, 341, 394, 396, 415–417, 419, 437, 439, 441, 444, 456, 466, 484, 504–528, 530–532
packages/core/agent_framework
   _agents.py3025382%91, 97, 100, 103, 405–407, 453, 636, 851, 854–856, 980–983, 985, 988–990, 1091, 1132, 1134, 1143–1148, 1154, 1156, 1166–1167, 1174, 1176–1177, 1185–1189, 1197–1198, 1200, 1205, 1207, 1241, 1286–1287, 1289, 1291, 1302
   _clients.py57492%287, 331, 479, 481
   _middleware.py404798%810, 826, 873–874, 1079–1080, 1125
   _tools.py7587490%228, 274, 325, 327, 355, 525, 557–558, 660, 662, 682, 700, 714, 726, 731, 733, 740, 773, 829–831, 872, 897–906, 912–921, 957, 967, 1208, 1545, 1631–1635, 1760, 1762, 1828, 1920, 1926, 1968–1969, 1982–1983, 2026, 2110, 2148–2149, 2177–2179, 2217–2218, 2228, 2285–2286, 2293–2294
   _types.py95313286%81, 104–105, 159, 164, 183, 185, 189, 193, 195, 197, 199, 216–217, 219–221, 223–224, 226–227, 229–230, 245–247, 249–252, 269, 274, 279, 283, 309, 313, 659–660, 1031, 1093, 1110, 1128, 1133, 1151, 1159–1161, 1178–1179, 1181, 1199–1200, 1202, 1209–1210, 1212, 1247, 1258–1259, 1261, 1280–1281, 1284–1293, 1296–1299, 1301, 1305, 1339, 1372, 1544, 1549, 1553, 1557, 1749, 1759, 1804, 1849–1854, 1876, 1881, 2282, 2291, 2427, 2515–2517, 2556, 2648, 2675, 2684, 2922–2924, 2927–2929, 2933, 2938, 2942, 3054–3056, 3084, 3120, 3138, 3142–3144, 3146, 3157–3158, 3161–3165, 3171
packages/core/agent_framework/_workflows
   _agent.py2804583%62, 70–76, 104–105, 261, 317, 331, 344, 393–396, 402, 408, 412–413, 416–422, 426–427, 496, 503, 509–510, 521, 553, 560, 581, 590, 594, 596–598, 605
   _agent_executor.py1702386%94, 116, 150, 166–167, 218–219, 221–222, 254–256, 264–266, 276–278, 280, 284, 288, 292–293
   _base_group_chat_orchestrator.py1701292%135, 301, 316, 350–352, 356, 375, 436, 480–482
   _group_chat.py2595578%172, 333, 340, 366–367, 369–370, 374, 378–379, 385, 390, 406, 433–438, 440, 461, 464–466, 473–476, 478, 483–487, 563–566, 570–571, 576–577, 595, 599, 604, 659, 664, 702, 711, 717, 762, 850, 853, 885, 895
   _handoff.py3845884%110–111, 113, 142–143, 163–173, 175, 177, 179, 184, 284, 338, 363, 389, 397–398, 412, 461–462, 492, 539–541, 729, 736, 741, 828, 831, 840–843, 853, 858, 865, 871–874, 909, 914, 1104, 1117, 1120, 1128, 1146, 1153, 1228
   _magentic.py56710980%69–78, 83, 87–98, 263, 274, 278, 298, 359, 368, 370, 412, 429, 438–439, 441–443, 445, 456, 596–600, 602, 640, 688, 724–726, 728, 736–739, 743–746, 789, 816–819, 910, 916, 922, 961, 999, 1028, 1045, 1056, 1071–1074, 1110–1111, 1115–1117, 1141, 1162–1163, 1176, 1192, 1214, 1262–1263, 1301–1302, 1341–1342, 1344–1345, 1347, 1415, 1418, 1427, 1430, 1435, 1670–1671, 1673, 1687, 1696, 1713, 1722, 1725
   _runner_context.py168696%84, 87, 383, 403, 491, 495
   _workflow_builder.py2783687%259, 594, 693, 700–701, 802, 805, 810, 812, 819, 822–826, 828, 890, 965, 968, 1028–1029, 1174, 1188–1195, 1197, 1200, 1202–1204, 1212
   _workflow_executor.py1724375%95, 444, 455, 467–470, 473–475, 478–479, 481, 484–486, 489–493, 497–498, 507, 512, 546, 572–577, 580, 583, 591, 596, 607, 617, 621, 627, 631, 641, 645
packages/core/agent_framework/azure
   _assistants_client.py400100% 
   _chat_client.py81495%288, 290, 303–304
   _responses_client.py40685%141, 164, 191–194
packages/core/agent_framework/openai
   _assistant_provider.py1101190%156–157, 169, 294, 360, 475–480
   _assistants_client.py2743387%348, 362, 365, 367–368, 371, 374, 377–378, 389, 414, 416, 418, 420, 422, 427, 430, 433, 437, 448, 533, 552–553, 558, 566, 618, 653, 690–693, 745, 762
   _chat_client.py2685180%183–184, 188, 207, 209, 227, 235, 244, 249, 254, 260, 265–266, 269, 274, 278–279, 281, 299, 319, 342, 356, 361–375, 382–384, 459, 465, 469, 472, 474, 476, 485, 499–500, 506, 522
   _responses_client.py56211879%258, 263, 265–266, 277, 295, 303, 306–313, 315, 319–320, 322–331, 337, 342–346, 348–349, 351, 388, 420, 445, 451, 469–470, 492, 497, 550, 553, 567, 584, 597, 648, 727, 732, 736–738, 742–743, 766, 781–782, 786–788, 835, 857–858, 873–874, 892–893, 982–983, 992–993, 1024–1025, 1041, 1043, 1118–1126, 1149–1155, 1174, 1192–1193, 1202, 1207–1209, 1215–1216, 1229, 1244, 1280–1281, 1283–1285, 1299–1301, 1311–1312, 1318, 1333
packages/mem0/agent_framework_mem0
   _provider.py83396%164–165, 168
TOTAL17443268684% 

Python Unit Test Overview

Tests Skipped Failures Errors Time
3184 213 💤 0 ❌ 0 🔥 1m 5s ⏱️

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Python: [Feature]: ChatAgent run should yield a Generic AgentRunResponse for response_format types

2 participants