Skip to content

Conversation

@eavanvalkenburg
Copy link
Member

@eavanvalkenburg eavanvalkenburg commented Jan 8, 2026

Motivation and Context

This PR makes a major overhaul of the way you can supply options to your chat clients, it leverages a TypedDict called ChatOptions for all the common options and derived classes from there for each provider. It leverages Generics to allow a developer to create specific Options and still get type-checking and IDE autocomplete for those.

For instance, when you want to use reasoning models with the OpenAI Chat Completion API, you could do this:

class OpenAIReasoningChatOptions(OpenAIChatOptions, total=False):
    """Chat options for OpenAI reasoning models (o1, o3, o4-mini, etc.)."""

    # Reasoning-specific parameters
    reasoning_effort: Literal["none", "minimal", "low", "medium", "high", "xhigh"]

    # Unsupported parameters for reasoning models (override with None)
    temperature: None
    top_p: None
    frequency_penalty: None
    presence_penalty: None
    logit_bias: None
    logprobs: None
    top_logprobs: None
    stop: None  # Not supported for o3 and o4-mini

# and then use them with the client:
client = OpenAIChatClient[OpenAIReasoningChatOptions]()
response = await client.get_response(
        "What is 2 + 2?",
        options={
            "model_id": "o3",
            "max_tokens": 100,
            "allow_multiple_tool_calls": True,
            # OpenAI-specific options work:
            "reasoning_effort": "medium",
            # Unsupported options are caught by type checker:
            "temperature": 0.7, <- would raise a flag in the IDE for a unsupported key
        },
    )
# or an agent
agent = ChatAgent[OpenAIReasoningChatOptions](
        chat_client=OpenAIChatClient(),
        # Options can be set at construction time
        default_options={ <- has type: OpenAIReasoningChatOptions
            "model_id": "o3",
            "max_tokens": 100,
            "allow_multiple_tool_calls": True,
            # OpenAI-specific options work:
            "reasoning_effort": "medium",
        },
)

This makes it very easy to leverage the latest and greatest options from different providers without needing code changes in our codebase on day 1 to support.

The rest of the setup is described in the included ADR.

Addresses item 10 from: #2902
Addresses item a part of item 5 from: #3096

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.

@eavanvalkenburg eavanvalkenburg requested a review from a team as a code owner January 8, 2026 13:52
Copilot AI review requested due to automatic review settings January 8, 2026 13:52
@markwallace-microsoft markwallace-microsoft added documentation Improvements or additions to documentation python lab Agent Framework Lab labels Jan 8, 2026
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 pull request introduces a major breaking change that refactors the chat options system from a class-based ChatOptions to a TypedDict-based approach with generic type support. The changes enable:

  1. Type-safe, IDE-autocomplete-friendly options using TypedDict
  2. Generic support allowing custom provider-specific options without code changes
  3. Runtime extensibility for new provider parameters
  4. Consistent parameter passing via options dict instead of mixed kwargs and ChatOptions objects

Key Changes:

  • Converted ChatOptions from a Pydantic-like class to a TypedDict
  • Changed chat_options parameter to options (dict) throughout the codebase
  • Introduced provider-specific TypedDict options (OpenAIChatOptions, OllamaChatOptions, etc.)
  • Added Generic[TOptions] to all chat clients and agents
  • Updated all samples, tests, and middleware to use the new dict-based approach

Reviewed changes

Copilot reviewed 100 out of 101 changed files in this pull request and generated 13 comments.

Show a summary per file
File Description
python/packages/core/agent_framework/_types.py Core ChatOptions refactored from class to TypedDict with utility functions
python/packages/core/agent_framework/_clients.py BaseChatClient made generic over TOptions
python/packages/core/agent_framework/_agents.py ChatAgent updated to use dict-based options
python/packages/core/agent_framework/openai/*.py OpenAI clients updated with OpenAIChatOptions, OpenAIResponsesOptions
python/packages/core/agent_framework/azure/*.py Azure clients updated with provider-specific options
python/packages/ollama/agent_framework_ollama/*.py Ollama client updated with OllamaChatOptions
python/packages/foundry_local/*.py Foundry Local client updated with options support
python/packages/core/agent_framework/_middleware.py Middleware updated to work with dict-based options
python/packages/core/agent_framework/observability.py Observability updated for dict-based options
python/samples/**/*.py All samples updated to use new options parameter
python/packages//tests/**/.py All tests updated to use dict-based options

@eavanvalkenburg eavanvalkenburg force-pushed the python_options_typeddict branch from 2a87cc3 to b105b73 Compare January 8, 2026 13:59
@markwallace-microsoft
Copy link
Member

markwallace-microsoft commented Jan 8, 2026

Python Test Coverage

Python Test Coverage Report •
FileStmtsMissCoverMissing
packages/a2a/agent_framework_a2a
   _agent.py139794%354–355, 392–393, 422–424
packages/ag-ui/agent_framework_ag_ui
   _client.py1481192%39, 105–108, 111, 257, 287, 442–444
   _orchestrators.py3594687%104, 113, 180, 413, 468–470, 479–480, 488, 490–492, 496, 505, 538, 540–542, 544–547, 549, 551–552, 554, 556–561, 635–637, 641–646, 690–692, 695
   _types.py27196%11
packages/ag-ui/agent_framework_ag_ui/_orchestration
   _tooling.py561082%30–32, 42–46, 57–58
packages/anthropic/agent_framework_anthropic
   _chat_client.py34814957%63, 368, 399, 421–424, 474–475, 484, 486–487, 492, 509–510, 550, 565, 569–570, 589–590, 596, 605, 607, 611–612, 653–655, 657, 670–671, 678–680, 684–686, 690–693, 704, 706, 728, 738, 759–766, 773–774, 781–783, 791–795, 802–803, 809–810, 816–817, 823, 830–833, 837, 844–845, 851–852, 858–859, 865, 873–877, 884–885, 902, 909–910, 927, 947, 949, 958–959, 965, 973–975, 983–984, 990–991, 1000–1011, 1017–1024, 1030–1037, 1043–1053, 1059–1062
packages/azure-ai/agent_framework_azure_ai
   _chat_client.py4738182%384, 643–644, 646, 649, 652, 655–660, 663, 665, 673, 685–687, 691, 694–695, 703–706, 716, 724–727, 729–730, 732–733, 740, 748–749, 757–758, 763–764, 768–775, 780–781, 789, 795, 803–805, 808, 830–831, 893, 965, 997, 1010–1014, 1053–1055, 1058–1059, 1100–1105, 1153, 1162, 1188
   _client.py1853978%242–245, 250, 253–256, 261, 264–265, 268, 275, 314, 316–319, 321, 419, 476, 512–515, 520, 522–523, 525–533, 535
packages/azurefunctions/agent_framework_azurefunctions
   _orchestration.py1121289%109–110, 114, 354, 359–366
packages/chatkit/agent_framework_chatkit
   _converter.py1354765%37, 120, 125, 173, 175, 345, 398, 400, 419–421, 423, 441, 443, 445, 448, 460, 470, 488, 508–532, 534–536
packages/core/agent_framework
   _agents.py3215782%51, 90, 96, 99, 102, 384, 445–447, 493, 547, 565, 702, 871, 874–876, 993–996, 998, 1001–1003, 1104, 1145, 1147, 1156–1161, 1167, 1169, 1179–1180, 1187, 1189–1190, 1198–1202, 1210–1211, 1213, 1218, 1220, 1254, 1299–1300, 1302, 1304, 1315
   _clients.py901088%48, 273, 310–313, 357, 444, 518, 520
   _middleware.py4021197%803, 819, 866–867, 1072–1073, 1118, 1268, 1498, 1553–1554
   _tools.py7298188%217, 263, 314, 316, 344, 514, 546–547, 649, 651, 671, 689, 703, 715, 720, 722, 729, 762, 816–818, 859, 883–909, 944, 954, 1138, 1474, 1552–1556, 1677, 1679, 1746, 1841, 1847, 1891–1892, 1905–1906, 1953, 2039, 2080–2081, 2109–2111, 2153–2154, 2164, 2221–2222, 2229–2230
   _types.py102214286%136, 138, 140, 142, 144, 146, 153–156, 174–175, 312, 314, 321, 340, 380, 426–427, 463, 613, 727–728, 730, 755, 762, 779–781, 866, 871–872, 874, 881–882, 884, 911, 920, 923–925, 930–931, 938, 942–944, 1100, 1189–1192, 1200–1201, 1292, 1473, 1479, 1874, 1890, 1892–1899, 1928, 1961–1963, 1969–1970, 2031, 2286, 2291, 2295, 2299, 2491, 2532–2537, 2559, 2564, 3023, 3109–3111, 3184, 3195–3196, 3370, 3374, 3387, 3490, 3493–3496, 3498–3501, 3503–3506, 3508–3511, 3513–3514, 3517–3518, 3521–3522, 3524, 3565, 3568–3570, 3616–3617, 3655, 3657, 3674, 3678–3680, 3682, 3693–3694, 3697–3701, 3707
   observability.py62114876%244, 312–317, 319, 321–322, 324, 326–328, 331–333, 338–339, 345–346, 352–353, 360, 362–364, 367–369, 374–375, 381–382, 388–389, 396, 433, 436, 439–441, 444, 447–448, 451–453, 455–457, 460, 547, 549, 631, 649–650, 652, 655, 663–664, 667–670, 672, 675–677, 680–681, 694–700, 702–711, 714–718, 721–724, 726–729, 732–733, 741, 842, 844, 869–871, 993, 995, 999–1004, 1006, 1009–1013, 1015, 1284, 1364–1366, 1438–1440, 1604, 1607, 1667, 1732–1735, 1749, 1751, 1758, 1774, 1777, 1837, 1853, 1857, 1991, 1993
packages/core/agent_framework/_workflows
   _agent_executor.py1622187%26, 93, 111, 145, 161–162, 213–214, 216–217, 248–250, 260–262, 264, 268, 272, 276–277
   _function_executor.py61493%88, 94, 100, 117
   _group_chat.py60010782%113, 149, 160, 384–394, 413, 440–441, 446, 508, 522, 529, 533–535, 538, 590–591, 593–595, 619–620, 676–685, 694–695, 698, 743, 757–759, 763, 765–769, 774, 776, 782, 786–788, 793–798, 804, 819, 842, 851, 874, 883, 888–889, 1051, 1070, 1074, 1128, 1178, 1196, 1204, 1229–1231, 1470, 1476, 1639, 1653, 1720–1721, 1723–1724, 1726, 1728, 1730–1731, 1967, 2039–2042, 2052, 2063, 2072–2074
   _handoff.py61814277%63, 76–78, 85–86, 88, 90, 207, 215–220, 223–224, 238, 243, 262–265, 274–276, 287, 290, 300–311, 313, 319, 325, 360, 376, 378, 381, 383, 432, 541, 550–555, 557–558, 569, 586, 604, 619, 631, 653, 665–667, 673, 716–718, 721–724, 726–728, 1090, 1095, 1099, 1104, 1179, 1189, 1282, 1285, 1301, 1306, 1318, 1324–1327, 1370–1371, 1510, 1792, 1804, 1827, 1833, 1842, 1846, 1851, 1871, 1893, 1904, 1916, 1949, 1960, 1964, 1966–1970, 1986–1988, 1990–1997, 1999–2001, 2003–2004, 2006, 2008, 2028–2034, 2036, 2042
   _magentic.py100334066%47, 52, 74–83, 88, 92–103, 276, 281, 300, 302, 318, 326–335, 485, 489, 503, 509, 524, 604, 617, 634, 643–644, 646–648, 650, 661, 799–803, 808, 847, 894, 930–932, 934, 942–945, 949–952, 1086–1087, 1104, 1106–1107, 1115, 1151, 1160–1162, 1182, 1239, 1259, 1262, 1294, 1297, 1301–1302, 1315, 1325–1329, 1335, 1361–1365, 1373–1377, 1381–1382, 1385–1390, 1396–1398, 1401–1402, 1406–1407, 1418–1419, 1421–1422, 1427–1429, 1433–1434, 1436–1437, 1441–1442, 1444–1448, 1452–1454, 1458–1459, 1467, 1475, 1490, 1502, 1514–1517, 1540–1547, 1549–1552, 1562–1563, 1572–1573, 1578–1580, 1611, 1637, 1651, 1667, 1683, 1755, 1767–1768, 1800, 1836, 1841–1842, 1844, 1848–1850, 1881–1883, 1889–1891, 1894–1896, 1898, 1900, 1904, 1908, 1911, 1916, 1919–1924, 1927, 1929, 1934, 1938, 1941–1942, 1944–1946, 1949, 1953, 1958, 1961, 1966–1967, 2027–2028, 2033, 2037, 2039–2043, 2046–2048, 2051, 2054, 2061–2063, 2077, 2082–2083, 2310–2311, 2490–2491, 2493, 2507, 2512, 2515, 2571, 2582, 2593–2595, 2608–2609, 2614, 2627–2630, 2645–2647, 2662–2669, 2671, 2674–2675, 2677–2678, 2686, 2694–2695, 2697–2699, 2701–2704, 2708–2716, 2720–2721, 2724–2728, 2730–2731, 2733–2736, 2738–2741, 2743–2745, 2747–2748, 2750–2752, 2768–2771, 2783–2786, 2799–2802, 2806
packages/core/agent_framework/azure
   _assistants_client.py400100% 
   _chat_client.py77494%279, 281, 294–295
   _responses_client.py34294%200, 223
packages/core/agent_framework/openai
   _assistants_client.py2753786%61, 354, 368, 371, 373–374, 377, 380, 383–384, 395, 420, 422, 424, 426, 428, 433, 436, 439, 443, 454, 539, 558–559, 562, 570, 620, 645, 659–660, 662, 694–697, 749, 766
   _chat_client.py2574881%53, 171–172, 176, 196, 207, 209, 230, 235, 240, 248, 256, 261–262, 264, 283, 303, 322, 336, 341–355, 362–364, 439, 445, 449, 452, 454, 456, 462, 476–477, 483, 499
   _responses_client.py55110381%268, 273, 275–276, 287, 305, 313, 316–323, 325, 330, 336, 340, 355, 358–359, 361, 398, 428, 472–473, 495, 500, 534, 552, 555, 568, 577, 631, 710, 715, 719–721, 725–726, 749, 764–765, 769–771, 818, 838–839, 852–853, 869–870, 959–960, 969–970, 1001–1002, 1018, 1020, 1094–1102, 1125–1131, 1150, 1168–1169, 1178, 1183–1185, 1191–1192, 1205, 1220, 1256–1257, 1259–1261, 1275–1276, 1279, 1281, 1291–1292, 1298, 1313
   _shared.py991287%62, 68–71, 149, 151, 158, 160, 173, 249, 273
packages/mem0/agent_framework_mem0
   _provider.py83396%164–165, 168
packages/purview/agent_framework_purview
   _models.py4569678%220–224, 306, 308, 330, 332, 336, 363, 367, 414–419, 422–429, 440–443, 454–457, 487–488, 491, 493, 495–497, 531, 533, 535, 537, 539, 541, 543, 548–550, 552, 555, 561, 564, 603, 605, 607, 609, 611, 619, 621, 623, 625, 659, 663, 698, 700, 702, 706, 712, 714, 736–737, 762, 764, 766, 770, 789–791, 796–797, 830, 832, 844, 907, 910–914, 916, 944, 975, 977
TOTAL17821293783% 

Python Unit Test Overview

Tests Skipped Failures Errors Time
2620 154 💤 0 ❌ 0 🔥 1m 0s ⏱️

Comment on lines +114 to +123
# Options not supported in Bedrock Converse API:
seed: Not supported.
frequency_penalty: Not supported.
presence_penalty: Not supported.
allow_multiple_tool_calls: Not supported (models handle parallel calls automatically).
response_format: Not directly supported (use model-specific prompting).
user: Not supported.
store: Not supported.
logit_bias: Not supported.
metadata: Not supported (use additional_properties for additionalModelRequestFields).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most of these parameters are excluded in Anthropic, Azure AI V1, Bedrock and Ollama, so I would probably remove them from base ChatOptions and specify them only in chat client implementations that support it (which is OpenAI and Azure AI V2 only, unless I'm missing something?)

In this case, we don't need to override them with None in most of these providers and users won't have to do it when implementing their own ChatOptions.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've kept the same list of MEAI's ChatOptions that we also already had in the "old" ChatOptions. I will checkin with some folks to see what their thinking is around evolving that.

@eavanvalkenburg eavanvalkenburg force-pushed the python_options_typeddict branch from 576ef6c to e8ef41a Compare January 11, 2026 10:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation lab Agent Framework Lab python

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants