main
63 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
6a2fe5ceb6 |
Python: fix streaming when GenAI tracing replaces the raw response (#7461) (#7705)
* Python: fix streaming when tracing replaces the raw response (#7461) Setting AZURE_EXPERIMENTAL_ENABLE_GENAI_TRACING=true made every streaming request fail with: AttributeError: 'AsyncStreamWrapper' object has no attribute 'parse' The Azure GenAI instrumentor replaces the SDK's raw-response wrapper with an object that *is* the event stream and exposes neither .parse() nor .headers. Both streaming paths already read .headers defensively via getattr, with a comment explaining that instrumentors wrap the response -- but then called .parse() unconditionally on that same object. Read .parse defensively too: _open_event_stream() uses .parse() when present and otherwise iterates the object directly, letting the instrumentor own the stream's lifetime. Behavior is unchanged for the normal SDK wrapper. The non-streaming .parse() call sites are left alone: the instrumentor's wrapper is stream-specific and those paths were not reported as failing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Parse the raw response the telemetry wrapper holds Review feedback: yielding the telemetry wrapper as-is only moved the AttributeError. Verified against azure-ai-projects==2.3.0 with openai==2.53.0: with_raw_response.create() routes through the instrumented AsyncResponses.create, so AsyncStreamWrapper.stream_async_iter is the still-unparsed LegacyAPIResponse, which is not an async iterator. Iterating the wrapper fails on the first __anext__ and traced streaming stays broken. Parse that inner raw response and hand it back to the wrapper instead, so the wrapper stays in the iteration path and keeps recording telemetry while real events flow through it. The previous test patched with_raw_response.create, i.e. above the layer that does the wrapping, so it could not catch this. The test now models the observed object graph -- a wrapper with no parse/headers whose stream_async_iter is an unparsed raw response -- and fails against the previous fix. A second test covers a bare event stream with nothing to parse. Also guard with callable() rather than an is-None check, per review. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Fix test typing failures (ty, zuban) Test Typing Checks caught two problems in the new test helpers: - ty: the wrapper's stream_async_iter was annotated `object`, so delegating to __aiter__/__anext__ was an attribute error. Annotate it `Any`, which also makes the two `type: ignore` comments unnecessary. - zuban: `_BareEventStream` was declared inside the test function, and its own forward-referenced return annotation does not resolve there. Move it to module scope alongside the other stream fakes. Verified with the task CI runs, `poe test-typing -P openai`: mypy, pyrefly, ty, zuban and pyright all pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Madan <madanmishra31@gmail.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
435201b71b |
Python: Fix A2A input handling in orchestrations (#7761)
* fix(a2a): reject empty invocations explicitly Key decisions: - Keep A2A continuation authority explicit; durable session task state only enriches diagnostics. - Raise AgentInvalidRequestException with participant and available task context instead of inventing input. - Leave AgentExecutor and Group Chat production contracts unchanged. Files changed: - packages/a2a/agent_framework_a2a/_agent.py - packages/a2a/tests/test_a2a_agent.py - packages/a2a/tests/test_a2a_group_chat.py Notes for next iteration: - No blockers. INPUT_REQUIRED pause/resume remains a separate task. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(a2a): pause group chat for remote input Key decisions: - Translate A2A INPUT_REQUIRED task content into the existing Content user-input-request contract. - Use the remote task ID as stable request correlation for streamed and finalized responses. - Reuse AgentExecutor request handling so caller input resumes the same task without a workflow-specific A2A path. Files changed: - packages/a2a/agent_framework_a2a/_agent.py - packages/a2a/tests/test_a2a_agent.py - packages/a2a/tests/test_a2a_group_chat.py Notes for next iteration: - Checkpoint restoration of pending A2A input is now unblocked. - The local issue file could not be moved because repository issue files are restricted by content exclusion policy. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(a2a): restore pending input from checkpoints Key decisions: - Keep normalized INPUT_REQUIRED content durable by excluding transport-only protobuf raw representations. - Restore through the existing AgentExecutor checkpoint and request-response path without a new schema or continuation API. - Cover file-backed restoration in streaming and non-streaming Group Chat runs, including unrelated-response rejection and exact task resumption. Files changed: - packages/a2a/agent_framework_a2a/_agent.py - packages/a2a/tests/test_a2a_group_chat.py Notes for next iteration: - The local issue file could not be moved because repository issue files are restricted by content exclusion policy. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * test(handoff): lock textless target context Key decisions: - Exercise the built Handoff workflow in streaming and non-streaming modes instead of bypassing routing, sessions, or termination. - Keep the slice test-only because current production already carries the initial task to a textless handoff target without synthetic user input. - Revisit the source to verify its handoff function call retains a matching result and user-turn termination sees only caller messages. Files changed: - packages/orchestrations/tests/test_handoff.py Notes for next iteration: - No production defect was reproduced. - The local issue file could not be moved because repository issue files are restricted by content exclusion policy. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * test(handoff): use resolved IDs in event assertions * fix(workflows): preserve A2A input request semantics * fix(workflows): preserve input request correlation * fix(a2a): deduplicate message-less input requests * fix(workflows): preserve specialized input requests * test(openai): use current web search model --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> |
||
|
|
b2a2fcbd87 |
Python: Add response/request customization hooks to OpenAIChatCompletionClient (#7028)
* Python: Fix reasoning content parsing in OpenAIChatCompletionClient Fix two issues with reasoning content handling in the Chat Completions client: 1. (#6979) reasoning_details plaintext buried as encrypted data: The client dumped the entire reasoning_details array into Content.protected_data without setting Content.text, causing AG-UI to emit ReasoningEncryptedValueEvent instead of visible ReasoningMessageContentEvent for plaintext reasoning providers (e.g. OpenRouter). Now extracts readable text from reasoning_details entries into Content.text while preserving protected_data for round-trip fidelity. 2. (#6978) Mistral list content causes crash: Mistral reasoning models return content as a list of typed chunks ([{"type": "thinking", ...}, {"type": "text", ...}]) instead of a plain string. _parse_text_from_openai assumed content was always a string, causing a Pydantic ValidationError downstream. Now detects list content and parses thinking chunks as Content.from_text_reasoning and text chunks as Content.from_text. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix pyright strict-mode type errors and handle content-as-string shape - Use cast() for proper type narrowing in _extract_reasoning_text and _parse_chunked_content to satisfy pyright strict mode - Handle {"content": "..."} string shape in _extract_reasoning_text (addresses review comment about missing format coverage) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix mypy errors: cast list content to Any in tests model_construct bypasses Pydantic runtime validation but mypy still checks declared types. Use cast(Any, ...) for the list content args. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address review comments: summary field, reasoning field, and round-trip - Add 'summary' field extraction in _extract_reasoning_text for reasoning.summary entries from OpenRouter - Handle message.reasoning and message.reasoning_content top-level fields (plaintext reasoning without reasoning_details) in both streaming and non-streaming paths - reasoning_details takes priority when both fields are present - Preserve original Mistral chunk list in additional_properties ('_source_content_list') so _prepare_message_for_openai can reconstruct the structured list content for multi-turn reasoning - Add 5 new tests covering all new behaviors Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix ruff used-dummy-variable: rename _skip_structured_siblings Remove leading underscore from _skip_structured_siblings variable since it is accessed (not a dummy variable). Ruff's used-dummy-variable rule flags variables with leading underscores that are read. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix missing newline at end of test file Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address review: type-agnostic chunk round-trip and reasoning field echo-back - Honor the _source_content_list marker regardless of the first emitted content's type by handling it before the type match, so a chunk list beginning with a text chunk still round-trips as one structured message (addresses github-actions review comment on results[0]). - Tag every chunked-content item with a shared _structured_content_group id and skip only exact group siblings during serialization, instead of suppressing all later text/reasoning content. - Record provenance of top-level reasoning/reasoning_content fields in _reasoning_source_field and echo the value back under the same key on the next request, which providers such as vLLM require (addresses Kimahriman review comment). Replaces the prior behavior that replayed surfaced reasoning as visible answer text. - Factor the duplicated reasoning parsing into _parse_reasoning_content. - Add tests for provenance capture, reasoning/reasoning_content round-trip, reasoning-only messages, text-first chunk round-trip, and unrelated sibling preservation. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 2f3c0308-51bf-4b66-8b53-87a8546743f5 * Replace provider-specific reasoning logic with configurable parse/prepare hooks Following review feedback (#7028), keep OpenAIChatCompletionClient free of provider-specific quirks for 'almost OpenAI-compatible' endpoints. Instead of branching in core for OpenRouter/vLLM/Mistral, expose two optional callables so callers adapt the client themselves: - response_parser (OpenAIChatResponseContentsParser): post-processes the Content list parsed from each response choice/streaming delta, to surface non-standard fields (e.g. reasoning/reasoning_content/reasoning_details) for display. - message_preparer (OpenAIChatMessagePreparer): post-processes the outgoing request message dicts built from each framework Message, to echo provider-specific fields back on later turns (e.g. vLLM reasoning) for multi-turn continuity. Both default to None (no-op; byte-identical stock OpenAI behavior). This reverts the provider-specific reasoning/chunked-content parsing and round-trip markers previously added to core; Mistral chunked content is now handled by agent-framework-mistral. - Add the two callables to RawOpenAIChatCompletionClient / OpenAIChatCompletionClient constructors and invoke them at the parse and prepare seams. - Export the type aliases from the package and the core lazy openai namespace (+ .pyi). - Replace the removed-behavior tests with tests for the two hooks. - Document the hooks in packages/openai/AGENTS.md. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 2f3c0308-51bf-4b66-8b53-87a8546743f5 * Skip non-string content in default text parsing Structured list content (e.g. Mistral reasoning models returning content as a list of chunks) was wrapped verbatim into a text Content, producing a malformed Content whose text is a list that crashes downstream (issue #6978). Default text parsing now skips non-string content so a configured response_parser receives a clean slate to expand it. Applies to both streaming and non-streaming paths. Add tests for the skip and for a response_parser expanding chunked content. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 2f3c0308-51bf-4b66-8b53-87a8546743f5 * Address review: hook signature, per-role preparer, robust round-trip - response_parser now receives the already-selected ChatCompletionMessage / ChoiceDelta instead of Choice | ChunkChoice, so callers no longer duplicate the streaming dispatch (removes the Any/hasattr pattern from tests). The client owns the dispatch; parsers read provider fields directly. - message_preparer now runs once per Message for every role: the build logic moved to _build_openai_messages and the hook is applied at a single exit point in _prepare_message_for_openai, so system/developer messages no longer bypass it. - Round-trip example/test now correlates surfaced reasoning via an additional_properties marker on message.contents with bounded, order-aware, one-to-one dict removal, instead of fragile request-string matching. Adds a test proving an answer whose text equals the reasoning text is no longer dropped. - Update packages/openai/AGENTS.md for the new parser signature and guidance. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 2f3c0308-51bf-4b66-8b53-87a8546743f5 --------- Co-authored-by: Copilot <copilot@github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 2f3c0308-51bf-4b66-8b53-87a8546743f5 |
||
|
|
07511b80c9 |
Python: Prevent orphaned local approval responses (#7462)
* Python: Prevent orphaned local approval responses Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 0efaca91-a0a7-46f4-9b81-022385607fe4 * Python: Clarify approval serialization boundaries Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 0efaca91-a0a7-46f4-9b81-022385607fe4 --------- Copilot-Session: 0efaca91-a0a7-46f4-9b81-022385607fe4 |
||
|
|
b64a2e2f82 |
Python: add feature-usage User-Agent telemetry (#7420)
* Python: add first-pass feature usage telemetry Add the 128-bit feature accumulator, package-local indexes, activation markers, and destination-scoped User-Agent emission for the initial Python implementation slice. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 346bf168-b668-4c4a-a8db-a67282ee5e5f * Python: track declarative feature usage Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 346bf168-b668-4c4a-a8db-a67282ee5e5f * Python: complete feature usage telemetry Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 346bf168-b668-4c4a-a8db-a67282ee5e5f * Python: report core version in User-Agent Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 346bf168-b668-4c4a-a8db-a67282ee5e5f * Python: configure Lab telemetry import path Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 346bf168-b668-4c4a-a8db-a67282ee5e5f * Python: preserve telemetry transport behavior Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 346bf168-b668-4c4a-a8db-a67282ee5e5f * Python: preserve caller-owned Foundry transports Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 346bf168-b668-4c4a-a8db-a67282ee5e5f * Python: remove stale Anthropic test import Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 346bf168-b668-4c4a-a8db-a67282ee5e5f --------- Copilot-Session: 346bf168-b668-4c4a-a8db-a67282ee5e5f |
||
|
|
59fe8bedeb |
Python: Sanitize author_name for the Chat Completions message name field (#7127)
OpenAI validates the Chat Completions message 'name' against ^[^\s<|\/>]+$, so an agent display name containing a space (or < | \ / >) failed every request with a 400. Sanitize at the three assignment sites, mirroring SanitizeAuthorName in the .NET client (dotnet/extensions): remove characters outside [a-zA-Z0-9_], omit the name when nothing remains, truncate to 64 characters. Fixes #7126 Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
ae6923c8b1 |
Python: feat(observability): add support for OpenAI cache write tokens in usage details (#7369)
* feat(observability): add support for OpenAI cache write tokens in usage details * feat(openai): add cache write tokens handling in usage details * Fix test |
||
|
|
95ec5b7d36 |
Python: Preserve approval decisions under OpenAI continuation (#7407)
* Python: Preserve approval decisions under OpenAI continuation Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1ee1d250-d1e2-4c6f-8c36-aae0d94fe7a1 * Chore: retrigger PR checks Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1ee1d250-d1e2-4c6f-8c36-aae0d94fe7a1 --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1ee1d250-d1e2-4c6f-8c36-aae0d94fe7a1 |
||
|
|
5987a6791b |
Python: Improve function approval resume and replay (#7345)
* Python: Harden function approval resume and replay Make approval resume immutable and occurrence-aware, return grouped approved and rejected results consistently, preserve pending approval history without model-orphaned calls, and align streaming, non-streaming, and AG-UI result boundaries. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1ee1d250-d1e2-4c6f-8c36-aae0d94fe7a1 * Python: Clarify function invocation orchestration Simplify approval-resolution setup and add phase-level comments around the key function invocation orchestration paths. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1ee1d250-d1e2-4c6f-8c36-aae0d94fe7a1 EOF && git push origin python-approval-resume-contract --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1ee1d250-d1e2-4c6f-8c36-aae0d94fe7a1 |
||
|
|
ce2208c62c |
Python: Fix OpenAIChatCompletionClient passing raw JSON-Schema dict response_format through unwrapped (#7199)
* Python: Fix OpenAIChatCompletionClient passing raw JSON-Schema dict response_format through unwrapped
Raw schema dicts (e.g. {"type": "object", ...}) were forwarded to the
Chat Completions API verbatim, which OpenAI rejects with a 400. The
Responses client already auto-wraps the same input. Mirror its raw-schema
detection (primitive types / schema keywords), wrap into the
{"type": "json_schema", "json_schema": {...}} envelope with
additionalProperties: false injection and title -> name promotion, and
leave already-valid response_format dicts untouched.
Fixes #7197
(cherry picked from commit dce5c3b06328fbde45eb2a9a25638af5b1ec85e3)
* Python: Add live integration coverage for raw JSON-Schema response_format dicts
Adds a response_format_raw_json_schema param to test_integration_options in
both the Chat Completions and Responses client test suites, proving the same
bare schema dict (title set, additionalProperties omitted) round-trips through
both live APIs and yields parsed structured output.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Python: Fix response format dict typing
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Evan Mattson <35585003+moonbox3@users.noreply.github.com>
|
||
|
|
754cbe5976 |
Python: [Feature]: Support OpenAI instructions in Responses API (#7292)
* Python: Support OpenAI instructions in Responses API * fix: address PR comments and fix typing for OpenAIChatOptions |
||
|
|
a2927c1c09 |
Python: Fix stateless replay of reasoning-paired tool calls (#7233)
* Python: Fix reasoning-paired client tool replay * Python: Handle middleware-terminated reasoning tool loops * Python: Replay encrypted reasoning function groups Key decisions: - Request encrypted reasoning on client-managed Responses calls while preserving caller include values. - Store encrypted payloads in Content.protected_data and reconstruct one provider reasoning item per reasoning id. - Replay active and completed function call/result groups; retain continuation-owned history behavior and the existing orphan-safe MCP path. Files changed: - python/packages/openai/agent_framework_openai/_chat_client.py - python/packages/openai/tests/openai/test_openai_chat_client.py Next iteration: - Extend encrypted reasoning preservation to streaming and framework serialization boundaries. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Python: Preserve encrypted reasoning through streaming Key decisions: - Capture encrypted reasoning from terminal streamed output items in Content.protected_data. - Preserve summary and private reasoning as distinct framework contents while reconstructing one provider reasoning item per id. - Prove replay after Message JSON and workflow checkpoint round trips, including encrypted-only and completed function groups. Files changed: - python/packages/core/agent_framework/_types.py - python/packages/openai/agent_framework_openai/_chat_client.py - python/packages/openai/tests/openai/test_openai_chat_client.py Next iteration: - Extend lossless stateless reasoning replay to hosted MCP call/output groups. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Python: Replay hosted MCP reasoning groups Key decisions: - Preserve hosted MCP call/output groups in client-managed history instead of deleting them when reasoning cannot be reconstructed. - Keep call/result coalescing and orphan-result exclusion intact, while retaining continuation-owned duplicate avoidance. - Cover completed, active, and multi-call reasoning groups plus the public outgoing request boundary. Files changed: - python/packages/openai/agent_framework_openai/_chat_client.py - python/packages/openai/tests/openai/test_openai_chat_client.py Next iteration: - Preserve middleware-terminated and parallel function groups atomically. - Add preflight rejection for non-replayable reasoning groups in the dedicated validation slice. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Python: Preserve terminated parallel reasoning groups Key decisions: - Return ordinary function results when middleware terminates a loop, removing the provider-specific durable marker. - Preserve every parallel call and available sibling result as one encrypted reasoning group in stateless replay. - Prove successful and policy-blocked batches through the public two-agent Foundry workflow and outgoing HTTP boundary. Files changed: - python/packages/core/agent_framework/_tools.py - python/packages/core/tests/core/test_function_invocation_logic.py - python/packages/openai/tests/openai/test_openai_chat_client.py - python/packages/foundry/tests/foundry/test_foundry_agent.py Next iteration: - Add preflight rejection for non-replayable and partially compacted reasoning groups. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Python: Reject unsafe stateless reasoning replay Key decisions: - Validate client-managed reasoning groups after compaction and report every affected reasoning and call identifier before transport. - Permit service-owned continuation and fully excluded atomic groups while rejecting partial compaction projections. - Surface encrypted-reasoning capability failures without lossy retries. Files changed: - python/packages/openai/agent_framework_openai/_chat_client.py - python/packages/openai/tests/openai/test_openai_chat_client.py Next iteration: - Run the resource-specific Foundry proof and finish PR #7233; that live proof remains intentionally local and requires the configured developer resource. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Python: Preserve reasoning metadata in Foundry hosting * Python: Avoid duplicating reasoning text metadata * Python: Gate encrypted reasoning for Foundry agents * Python: Type stateless reasoning integration test * Python: Narrow Foundry mock call arguments --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> |
||
|
|
61802723ff |
Python: Support prompt cache breakpoints for GPT-5.6 models in OpenAI clients (#7163)
* Python: Support prompt cache breakpoints for GPT-5.6 models in OpenAI clients Add request-level prompt_cache_options to OpenAIChatOptions and OpenAIChatCompletionOptions, and forward a per-part prompt_cache_breakpoint from Content.additional_properties onto the content blocks each API supports. Text parts that carry a breakpoint keep typed list content, since the plain-string form cannot hold one; without a breakpoint the existing string forms are unchanged. * Clarify system-message content-shape comment * Address review: SDK prompt cache types, private helper, add sample Replace the custom PromptCacheOptions TypedDict with the openai SDK's own types for each API, which raises the openai floor to 2.45.0 where those types were introduced. Make the breakpoint helper private to the two chat clients. Add a prompt caching sample with a README entry, and unquote the helper's Content annotation so the pyupgrade hook passes. * Guard the prompt cache options import for older openai versions The SDK's PromptCacheOptions types only exist in openai 2.45.0 and later, so each client falls back to a local mirror when the import fails and the dependency floor stays at 2.25.0. A TYPE_CHECKING-only import is not enough because the options classes are introspected with get_type_hints() at runtime. Verified against openai 2.25.0: the package imports, the fallback resolves, and part-level breakpoints still work; sending the option itself requires 2.45.0, which the field docstrings now note. * Make the old-openai fallback for PromptCacheOptions deliberately empty Assigning None instead, as suggested in review, trips pyright's reportInvalidTypeForm on the field annotation (the symbol becomes type | None after the try/except). An empty TypedDict gives the same effect for users on older openai versions: any content they put in prompt_cache_options is flagged by their type checker, since the option cannot be sent on those versions anyway, while get_type_hints() on the options classes keeps working at runtime. * Guard prompt_cache_options at runtime instead of via an empty fallback type The empty-TypedDict fallback flagged valid `prompt_cache_options` usage under pyright on every openai version — including this PR's own `client_prompt_caching.py` sample (`poe check -S`) — because pyright resolves the try/except symbol to the fallback shape regardless of the installed openai, while mypy/ty resolve the failed import to `Any` and never warn. So a type-only "warn on old openai" signal is not achievable cleanly across type checkers. Restore the faithful fallback (mirrors the SDK's `mode`/`ttl` shape) so the option type-checks identically on every supported openai version, and add a runtime guard: setting `prompt_cache_options` on openai < 2.45 now raises a clear ChatClientInvalidRequestException instead of forwarding an unusable option to the SDK. This keeps the option non-silent for all users regardless of type checker, without forcing an openai upgrade. Adds tests covering the guard for both clients. * Gate system/developer breakpoint shape on a real mapping value The system/developer branch switched to list-form content whenever prompt_cache_breakpoint was set to any non-None value, but the option is only attached when the value is a mapping. A malformed value (e.g. a string) therefore changed the message shape without adding a breakpoint. Decide the shape from the built part instead, matching the user-role path. |
||
|
|
fb38b1d10a |
Python: Fix PropertySchema.to_json_schema() not recursing into nested array items / object properties (#7200)
* Python: Fix PropertySchema.to_json_schema() not recursing into nested schemas
Nested array 'items' and object 'properties' kept the declarative 'kind'
key and empty 'enum' placeholders, producing JSON Schema OpenAI rejects
('schema must have a type key'). Recursively apply the same conversion the
top-level properties loop performs, including the serialized named-list
properties shape and nested required arrays.
Fixes #7198
(cherry picked from commit c156ffd05924fb5a1884625f2fc3d9bdc3e152b1)
* Python: Validate nested properties list before mutating to avoid partial conversion
Review feedback: the list-shaped properties branch popped name/required from
each element and returned on the first unexpected one, leaving earlier
elements half-converted. Validate the whole list first so an unexpected
shape leaves the node fully untouched.
* Python: Type nested-properties normalization for strict Pyright and drop unreachable dict branch
ObjectProperty always stores nested properties as a named list, so the
elif-dict branch in _normalize_nested_schemas was unreachable; remove it
and flatten the list conversion behind an early return. Cast the narrowed
items/props values so strict Pyright no longer reports unknown types.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Python: Emit additionalProperties: false on nested object nodes in PropertySchema.to_json_schema()
OpenAI strict structured outputs require additionalProperties: false on
every object node, but the chat clients only inject it at the schema
root, so declarative schemas with nested objects (e.g. array items)
failed with a schema-validation 400. Route the top-level properties loop
through _normalize_schema_node so all object nodes get the key, and add
a live OpenAI integration test covering the nested array-of-objects
response_format shape.
Verified live against the Responses API: the previous emission fails
with "In context=('properties', 'issues', 'items'),
'additionalProperties' is required to be supplied and to be false";
the new emission returns valid structured output.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
3604ba70f6 |
Python: Normalize chat finish reasons (#7105)
* Python: Normalize chat finish reasons Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 45b65bfa-8e36-47b0-99d9-ec58ec60ace1 * Preserve Copilot finish reasons Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 45b65bfa-8e36-47b0-99d9-ec58ec60ace1 * fix claude finish reasons |
||
|
|
7440b1c376 | feat: enhance tool choice handling for required mode in _prepare_options (#7024) | ||
|
|
978cfcd9e4 |
Python: Fix Foundry reasoning MCP compaction (#6907)
* Fix Foundry reasoning MCP compaction * Address reasoning MCP review feedback --------- Co-authored-by: godququ5-code <256881196+godququ5-code@users.noreply.github.com> Co-authored-by: Eduard van Valkenburg <eavanvalkenburg@users.noreply.github.com> |
||
|
|
346d3f0820 |
Python: Mark hosted tool calls informational-only (#6997)
* Python: Mark hosted tool calls informational-only Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Python: Address informational-only review feedback Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Python: Preserve approval responses in tool invocation Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> |
||
|
|
62456f044d |
Python: Fix web_search_options sent to Azure OpenAI Chat Completions API (issue #3629) (#6225)
* Fix: Skip web_search_options for Azure OpenAI Chat Completions API Azure OpenAI Chat Completions API does not support the web_search_options parameter. Sending it results in a 400 error: 'Unknown parameter: web_search_options'. This fix: - Stores the use_azure_client flag during initialization - In _prepare_tools_for_openai, skips web search tools when the client is Azure-based, logging a warning that guides users to the Responses API (OpenAIChatClient) for web search support on Azure Closes #3629 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix: raise ValueError instead of silently ignoring web search on Azure Address review feedback: silent logger.warning was too easy to miss. Raising ValueError ensures callers know immediately that web search is incompatible with Azure Chat Completions and directs them to the Responses API alternative. - Changed logger.warning to ValueError in _prepare_tools_for_openai - Added test_prepare_tools_with_web_search_on_azure_raises - Added test_prepare_tools_with_web_search_on_openai_allowed * Fix Azure web search test regex --------- Co-authored-by: Autumn <Autumn@Autumns-MacBook-Air.local> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: Eduard van Valkenburg <eavanvalkenburg@users.noreply.github.com> |
||
|
|
07981847ed |
Add chat client Agent typing tests (#6950)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> |
||
|
|
9fd3d29e09 |
Python: Fix FunctionShellTool throw and empty streaming shell command (#6763)
* Fix shell tool bug * Address PR feedback * Fix uv.lock changes * Update uv.lock |
||
|
|
f1d838fc5e |
Python: bump package versions for 1.10.0 release (#6753)
* Python: bump package versions for 1.10.0 release - Released cohort (core, openai, foundry, root): 1.9.0/1.8.2 -> 1.10.0 - agent-framework-ag-ui: rc5 -> rc6 (tool history replay fix) - Beta/alpha packages with changes: anthropic, azurefunctions, bedrock, durabletask, hyperlight, purview, foundry-hosting, gemini, hosting, hosting-responses, hosting-telegram, tools bumped to new date stamp (260625) - Inter-package dependency bounds updated for changed packages - CHANGELOG.md updated with [1.10.0] section and compare links Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: update stale hosting dependency pins in hosting-responses and hosting-telegram Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * CI: cap xdist workers at 4 for Azure OpenAI and Functions integration jobs The Azure OpenAI and Functions+Durable Task integration jobs ran with `-n logical` (~20 workers on the hosted runner), oversubscribing the box and collapsing the whole pytest session (all workers reporting `node down: Not properly terminated`) in the merge queue. Pin these two jobs to `-n 4` in python-merge-tests.yml and python-integration-tests.yml to remove the oversubscription while keeping full coverage. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * test: temporarily skip flaky Python integration tests crashing the merge queue Revert the `-n 4` xdist experiment (it did not prevent the runner crash) and instead skip the integration tests that collapse the pytest-xdist runner in the merge queue (all workers report `node down: Not properly terminated`): - Azure OpenAI: flip the per-file `skip_if_azure_openai_integration_tests_disabled` guard to an unconditional skip (integration tests only; unit tests still run). - Azure Functions / Durable Task: skip the four specific failing tests (test_weather_agent, test_parallel_workflow_end_to_end, test_weather_agent_with_tool, test_conditional_branching). Tracked for re-enablement in #6777. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * test: skip flaky test_math_agent_with_tool (durabletask integration) Same empty-AgentResponse flakiness as test_weather_agent_with_tool in the same file (AssertionError: assert 0 > 0 / empty .text). Skip it in the merge queue. Tracked in #6777. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> |
||
|
|
6e95517659 |
Python: Split type checkers by target (pyright source, 5 checkers on tests/samples) (#6443)
* Python: Split type checkers by target (pyright source, 5 checkers on tests/samples) Rework the typing setup along the lines of the 'too many type checkers' approach: - Pyright (strict) is now the sole source-code type checker; mypy is removed from source and its [tool.mypy] block becomes a relaxed profile used only for tests/samples. - Tests are checked by all five checkers (pyright relaxed, mypy, pyrefly, ty, zuban); samples by pyright, pyrefly, and ty. All run in a relaxed/ basic profile so authors aren't forced into over-annotation. - Add pyrightconfig.tests.json and bump sample pyright configs to basic. - Unify test/sample typing onto the same parallel fan-out used by source pyright via run_command_items in task_runner.py. - Make version-conditional imports symmetric: keep or drop the '# type: ignore' on both branches so results match across interpreter versions (local vs CI). - Update SKILL.md, DEV_SETUP.md, and CODING_STANDARD.md for the five gating checkers and pyright on source+tests+samples. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Python: Fix merge regressions from main (typing + runtime) Merging main into the type-checker split branch surfaced regressions that the new five-checker test suite and unit tests caught: Runtime fixes: - anthropic: restore the dropped `cache_read_input_token_count` mapping in _parse_usage_from_anthropic (lost during merge conflict resolution). - gemini: _get_function_calling_mode test helper returned str(enum) ('FunctionCallingConfigMode.AUTO') instead of the enum value ('AUTO'). - openai: _response_id_from_token test helper was an infinite self-recursion; return token['response_id']. - orchestrations: reset output_events per approval iteration so the terminal output assertion counts only the final run. - core: drop a stale duplicate harness test whose message ('non-negative') contradicted the source ('positive'). - purview: import PolicyLocation/PolicyScope/ProtectionScopeActivities/ ExecutionMode used by the processor tests. Type-checker fixes (tests, relaxed profile): - core: pyright/mypy/pyrefly/ty/zuban green-ups across the harness, MCP, observability and types tests. - anthropic/openai: route provider-namespaced UsageDetails keys through a dict cast (extra_items TypedDict unsupported by mypy/ty). - purview: typed model constructors and cache-mock casts. - ag-ui: annotate WorkflowContext[Any, Any] so yield_output accepts test payloads, guard Optional forwarded_props, and ty-ignore intentional bad args. Source pyright (sole source checker) flagged unnecessary ignores newly introduced by merged code in core _tools.py and declarative _declarative_base.py. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Python: Isolate per-package mypy cache in test-typing fan-out The parallel test-typing fan-out runs many mypy processes concurrently, all defaulting to a single shared ./.mypy_cache. Concurrent writes corrupt the cache and mypy aborts with INTERNAL ERROR (intermittently, depending on worker timing) -- which is why CI's Test Typing job failed on a shifting set of packages while a single-package run was fine. Give each mypy invocation an isolated cache dir keyed by its target paths so incremental caching still works per package without races. Other checkers (zuban/pyrefly/ty/pyright) maintain their own caches and are unaffected. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Python: Make lab pyright-only on source (drop source mypy) Lab was the last package still running mypy on its source code, requiring mypy-only `# type: ignore` comments that pyright (the sole source checker everywhere else) flags as unnecessary. Align lab with the rest of the monorepo: - Remove the lab source mypy poe tasks (mypy-gaia/lightning/tau2) and the now-dead strict [tool.mypy] config block. - Drop the 'Run lab mypy' CI step; lab source is type-checked by pyright only. Lab tests remain covered by the workspace test-typing fan-out (mypy, pyrefly, ty, zuban, pyright over tests using the relaxed root config). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Python: Fix test-typing regressions from latest main merge A fresh merge from main brought in new test code never run under the five-checker test-typing suite. Green up across the affected packages: - core: narrow Optional span.attributes with 'and' guards in span filters and assert+cast the json.loads(...attributes[...]) reads (test_observability); match the existing as_agent ignore on the protocol-typed fixture (test_clients). - openai: align new streaming tests with the established chat_options dict pattern (ChatOptions TypedDict isn't assignable to dict), route Optional .annotations[0] access through a small _first_annotation helper (mirrors the file's assert-not-None convention), and annotate a mapped ResponseStream. - foundry_hosting: annotate error: dict[str, Any] = body.get(...) or {} (zuban needs the annotation). - foundry: narrow ignores for the live AIProjectClient credential arg (pyrefly) and connections.get_default (zuban) SDK type gaps. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * updated pyright version * pyright fix * Python: Fix source typing for pyright 1.1.410 Pyright 1.1.410 tightened several checks. Apply the same source fixes as upstream PR #6275: - anthropic: import AsyncAnthropicBedrock from anthropic.lib.bedrock and AsyncAnthropicVertex from anthropic.lib.vertex (no longer re-exported from the anthropic top-level package -> reportPrivateImportUsage). - core _types.py: cast the transform-hook result to UpdateT (reportAssignmentType). - core _workflows/_events.py: annotate the @contextmanager helper as Generator[None] instead of Iterator[None] (reportDeprecated). - redis: build the combined filter expression with an explicit loop instead of reduce(and_, ...), which pyright could no longer fully type (drops the now unused functools.reduce / operator.and_ imports). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Python: Accept plain-text body in Azure Functions workflow/run endpoint The workflow_orchestrator already accepts plain strings as well as JSON objects via context.get_input(), but the start_workflow_orchestration HTTP handler only accepted JSON and returned 400 for any non-JSON body. This made the functions integration tests that POST text/plain to /api/workflow/run (e.g. test_09_workflow_shared_state) fail consistently with 400 != 202. Fall back to the raw request body (decoded as UTF-8) when the body is not JSON, rejecting only a truly empty body. The JSON path is unchanged. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> |
||
|
|
571cae426c |
Python: Fix Azure AI Search citation URLs (#6453)
* Fix Azure AI Search citation URLs Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Enrich MCP search citation metadata Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix Azure AI Search citation enrichment follow-ups Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address PR #6453 review comments Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * updated filter for paths * also updated python paths * reverted dotnet-format change --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: westey <164392973+westey-m@users.noreply.github.com> |
||
|
|
d7e8d2206d |
Python: Fix Python OTel usage detail attributes (#6493)
* fix python otel usage detail attributes Map cached/read/reasoning usage detail fields to standard OTel GenAI attributes while preserving provider-specific legacy keys. Add focused coverage for direct response spans, aggregated agent spans, and provider usage parsing. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * address usage detail review feedback Omit missing OpenAI Responses usage detail counts while preserving zero-valued counts. Record zero-valued token usage in OTel histograms and add regression coverage. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> |
||
|
|
6b94315161 |
Python: Add timeout parameter to FoundryAgent to fix ConnectTimeout on multi-turn conversations (#6263)
* Python: fix ConnectTimeout on multi-turn FoundryAgent conversations (#6241) Expose a `timeout` parameter on `RawFoundryAgentChatClient`, `_FoundryAgentChatClient`, `RawFoundryAgent`, `FoundryAgent`, and `RawOpenAIChatClient` so callers can override the HTTP timeout used by the underlying AsyncOpenAI client. Root cause: `RawFoundryAgentChatClient.__init__` called `project_client.get_openai_client()` without configuring any timeout, inheriting the OpenAI SDK default of `httpx.Timeout(connect=5.0)`. When connections are recycled between turns under load, the 5 s connect timeout fires and surfaces as `openai.APITimeoutError`. Fix: - `load_openai_service_settings` (`_shared.py`): accept `timeout` and include it in `client_args` for all three `AsyncOpenAI`/ `AsyncAzureOpenAI` construction paths. - `RawOpenAIChatClient.__init__` (`_chat_client.py`): accept `timeout` and forward to `load_openai_service_settings`. - `RawFoundryAgentChatClient.__init__` (`_agent.py`): accept `timeout` and set `openai_client.timeout = timeout` on the client returned by `get_openai_client()` before passing it to the base class. - `_FoundryAgentChatClient`, `RawFoundryAgent`, `FoundryAgent`: accept and propagate `timeout` through the construction chain. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add timeout parameter to FoundryAgent and RawOpenAIChatClient Expose a timeout parameter on RawFoundryAgentChatClient, _FoundryAgentChatClient, RawFoundryAgent, FoundryAgent, and RawOpenAIChatClient. When provided, the value is applied to the underlying AsyncOpenAI client so that connect timeouts under load or after connection recycling can be tuned by callers. Previously, get_openai_client() was called without any timeout override, so the SDK default of httpx.Timeout(connect=5.0) was inherited and could fire on multi-turn conversations where the underlying connection is recycled between turns. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Python: Add `timeout` parameter to `FoundryAgent` to fix `ConnectTimeout` on multi-turn conversations Fixes #6241 * fix(foundry): use with_options to avoid mutating shared OpenAI client timeout (#6241) Replace direct assignment with in RawFoundryAgentChatClient.__init__. The Azure AI Projects SDK caches and returns a shared AsyncOpenAI client per AIProjectClient. Mutating its .timeout attribute leaked the override to all other code paths sharing that client (other agents, user code). with_options() returns a new client instance with the override applied, leaving the original shared client untouched. Update tests to assert with_options is called with the correct timeout and that the original shared client's timeout attribute is not mutated. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * test(foundry): assert with_options return value flows to instance.client (#6241) The four timeout propagation tests verified that with_options was called but did not confirm that the returned (timeout-configured) client was actually stored on the instance. A silent discard of the return value would have left the tests green while the timeout had no effect. Each test now captures the constructed instance and asserts: assert <instance>.client is openai_client_mock.with_options.return_value Affected tests: - test_raw_foundry_agent_chat_client_init_applies_timeout_to_openai_client - test_raw_foundry_agent_chat_client_init_applies_timeout_with_preview_enabled - test_foundry_agent_chat_client_init_propagates_timeout - test_foundry_agent_init_propagates_timeout_to_openai_client Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <copilot@github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> |
||
|
|
bc0e65d716 | fix: drop hosted MCP calls when reasoning is stripped (#6210) | ||
|
|
3db2004e49 |
Python: read headers defensively to support stream wrappers without .headers (#6028) (#6029)
`OpenAIChatClient._inner_get_response()` reads `.headers` on the raw streaming response returned by `client.responses.with_raw_response.create(stream=True)` (and its three sibling call sites - retrieve-streaming, non-streaming create and background retrieve) to surface the `x-ms-served-model` Azure header, introduced in #5910. When `azure-ai-projects>=2.1.0` experimental GenAI tracing is enabled (`AZURE_EXPERIMENTAL_ENABLE_GENAI_TRACING=true`), the instrumentor wraps the raw streaming response in an inline `AsyncStreamWrapper` that exposes `.response` but not `.headers`. Reading `raw_create_response.headers` then raises `AttributeError: 'AsyncStreamWrapper' object has no attribute 'headers'`, which `FoundryChatClient` rethrows as a `ChatClientException` and breaks every streaming call (workflows and free chat). Fix: read the header dict via `getattr(raw_response, "headers", None)` at all four call sites. `_extract_served_model()` already short-circuits on `None`, so the served-model surfacing degrades gracefully (model stays the deployment alias) instead of crashing when the response is wrapped by an instrumentor that does not proxy `.headers`. Regression test added: `test_streaming_response_without_headers_attribute_does_not_crash` simulates a stream wrapper that raises `AttributeError` on `.headers` and asserts the stream still completes with the deployment alias as `update.model`. Fixes #6028 Co-authored-by: Emilien Mottet <emilien.mottet@michelin.com> |
||
|
|
4c4e1d9b87 |
Python: fix: keep citation get_url metadata (#6037)
* fix: keep citation get_url metadata * fix: satisfy citation metadata mypy check |
||
|
|
e1e6e3d35e |
Python: fix(openai): guard against null delta in streaming chunks from non-co… (#5734)
* fix(openai): guard against null delta in streaming chunks from non-compliant providers (#5732) * chore: resolve nit and align with project style --------- Co-authored-by: Sergey Borisov <sergey.borisov@dataimpact.io> Co-authored-by: Giles Odigwe <79032838+giles17@users.noreply.github.com> |
||
|
|
1b6f7d80fd |
Python: Record actual served model from Azure OpenAI (#5910)
* Record actual served model as response model for Azure OpenAI * Formatting * Fix tests * Fix pipeline error * Comments * Address review: surface served model via ChatResponse.model Apply blocking review feedback from PR #5910: - Use ChatResponse.model / ChatResponseUpdate.model as the source of truth for the Azure x-ms-served-model header value, instead of stashing it in additional_properties and overriding it again in observability. Observability already reads response.model; the chat client now overwrites it post-parse when the served-model header is present. Empirically the Azure Responses API returns the deployment alias in body.model and the actual snapshot (e.g. gpt-5-nano-2025-08-07) in this header. - Move the AZURE_OPENAI_SERVED_MODEL_HEADER constant out of observability.py and into RawOpenAIChatClient (as the SERVED_MODEL_HEADER ClassVar). The header is Azure-OpenAI-Responses-API-specific so observability does not need to know about it. - Revert the streaming text_format path to client.responses.stream(...) and drop the _pydantic_model_to_text_format_param helper. That helper imported from openai.lib._parsing._responses (a private SDK path) and the swap to responses.create(stream=True) dropped client-side output_parsed for structured-output streaming. The streaming-with-text_format path is the only one that does not surface the served-model header - documented inline. - Wrap the raw streaming responses in async with so the underlying socket closes deterministically (continuation_token retrieve + create paths). - Fix the empty-string / whitespace-only header at the source by stripping in _extract_served_model and returning None when nothing remains. - Revert unrelated formatting-only churn in _skills.py and test_mcp.py. - Update unit tests to assert against chat_response.model / update.model and add an aggregated streaming assertion plus a pin that the streaming-with-text_format path does not get the header. Verified end-to-end against Azure OpenAI Responses API: deployment alias gpt-5-nano now reports gpt-5-nano-2025-08-07 as ChatResponse.model in both the non-streaming and streaming paths. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: preserve streaming structured output finalization Agent-Logs-Url: https://github.com/microsoft/agent-framework/sessions/f62076ef-558d-49e8-8fe2-f38d527c9639 Co-authored-by: eavanvalkenburg <13749212+eavanvalkenburg@users.noreply.github.com> * refactor: name streaming response finalizer Agent-Logs-Url: https://github.com/microsoft/agent-framework/sessions/f62076ef-558d-49e8-8fe2-f38d527c9639 Co-authored-by: eavanvalkenburg <13749212+eavanvalkenburg@users.noreply.github.com> * fix: capture streaming response format after prepare Agent-Logs-Url: https://github.com/microsoft/agent-framework/sessions/f62076ef-558d-49e8-8fe2-f38d527c9639 Co-authored-by: eavanvalkenburg <13749212+eavanvalkenburg@users.noreply.github.com> * refactor: clarify streaming response format capture Agent-Logs-Url: https://github.com/microsoft/agent-framework/sessions/f62076ef-558d-49e8-8fe2-f38d527c9639 Co-authored-by: eavanvalkenburg <13749212+eavanvalkenburg@users.noreply.github.com> * test: use public API for streaming structured output Agent-Logs-Url: https://github.com/microsoft/agent-framework/sessions/f62076ef-558d-49e8-8fe2-f38d527c9639 Co-authored-by: eavanvalkenburg <13749212+eavanvalkenburg@users.noreply.github.com> * Inline the served-model header override at its two call sites The `_apply_served_model_header` helper was a 1-line wrapper around `_extract_served_model`. Inlining the `if served_model is not None: ...` matches the pattern already used in the streaming paths and folds the explanatory docstring onto `_extract_served_model` (which is now the single place that knows about the header). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Eduard van Valkenburg <eavanvalkenburg@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: eavanvalkenburg <13749212+eavanvalkenburg@users.noreply.github.com> |
||
|
|
09a3d0d307 |
Python: Strip server-issued response item IDs under storage (#3295) (#5690)
Fixes microsoft/agent-framework#3295. When the OpenAI Responses chat client sends a request that carries previous_response_id / conversation_id / conversation, the server already has the prior turn's response items and rejects duplicates with "Duplicate item found with id fc_xxx". The chat client was re-sending them inline whenever the input messages still carried the items in additional_properties (workflow replay, history providers, etc.), which broke any tool-using agent with persistent history. Decisions: - Single chokepoint: _prepare_message_for_openai. When the resulting request uses service-side storage, drop function_call, reasoning, approval-request/response, and local-shell-call items from the wire input. Keep function_result with its call_id; the server pairs it to the prior function_call via that key. - function_result is preserved unconditionally except for the local-shell variant, which carries its own server-issued item id. - No public API change. Wire format change is subtractive and only on requests that would otherwise 400. - Re-pointed the strict-xfail in test_full_conversation.py from #4047 to #3295. Kept xfail because the test asserts executor-level session-id clearing, which is the defense-in-depth half tracked by 3295-03; this slice closes the wire-level half. Files: - python/packages/openai/agent_framework_openai/_chat_client.py: strip rule applied alongside the existing reasoning-item branch. - python/packages/openai/tests/openai/test_openai_chat_client.py: four new tests pin the contract (function_call, approval, local-shell-call stripped under storage; everything kept without storage). Updated pre-existing tests that exercised the storage-on path to either pass request_uses_service_side_storage=False explicitly or assert the new strip behavior. - python/packages/foundry/tests/foundry/test_foundry_chat_client.py: same explicit storage-off opt-in for the inherited test. - python/packages/core/tests/workflow/test_full_conversation.py: re-pointed xfail reason to #3295 and the executor-level follow-up. Notes for next iteration: - 3295-01 (HITL wire-format validation against live OpenAI/Foundry) was not run; it requires the user's API credentials. The PRD design is locked but the empirical confirmation is still pending. If script 3 fails on either provider, this slice may need to be revisited. - 3295-03 (clear service_session_id in AgentExecutor on full-history replay) remains open. After it lands the xfail in test_full_conversation.py can be removed. - pytest was not run in this iteration because uv-based pytest commands required interactive approval. Validation rests on careful reading; next iteration should run the openai + core test suites. |
||
|
|
f3db60fa65 |
Python: Support GPT-5 verbosity option and restore Foundry agent_reference (#5619)
* Python: Support GPT-5 verbosity option and restore Foundry agent_reference Adds verbosity as a typed Literal["low","medium","high"] field on OpenAIChatOptions (Responses API) and OpenAIChatCompletionOptions (Chat Completions API), set in the same way as the existing reasoning options. For the Responses API, top-level verbosity is translated to the nested text.verbosity shape the OpenAI service expects. The same field flows through to FoundryChatClient via the existing FoundryChatOptions alias. Also fixes #5582: PR #5447 removed the agent_reference injection from RawFoundryAgentChatClient._prepare_options, so first-turn calls against a Foundry Prompt Agent went out without model and without agent_reference and were rejected by the Responses API with "Missing required parameter: 'model'". Restores the injection on the non-preview path (allow_preview=False) and adds a guard test that asserts the preview path does not inject agent_reference, since the preview SDK injects it via project_client.get_openai_client(agent_name=...). Closes #5516 Closes #5582 * Python: Address Copilot review on PR #5619 - Foundry verbosity sample docstring: replace the misleading "set deployment name on model=" instruction with the actual env-var pattern the sample relies on (FOUNDRY_PROJECT_ENDPOINT and FOUNDRY_MODEL). - _build_agent_reference docstring: clarify the helper is used for both Prompt Agents and HostedAgents on the non-preview path. - Add a Responses API test that locks in the documented precedence rule: when both top-level verbosity and text["verbosity"] are supplied, the top-level value wins. * Python: Drop redundant Foundry verbosity sample and list OpenAI sample in README - Remove samples/02-agents/providers/foundry/foundry_chat_client_verbosity.py per review feedback. The verbosity functionality is identical across the OpenAI and Foundry clients (FoundryChatOptions is an alias of OpenAIChatOptions), so a single sample on the OpenAI side is sufficient. - Add the new client_verbosity.py entry to the OpenAI samples README. |
||
|
|
540193ccef |
Python: Reduce flaky integration tests and improve CI signal quality (#5454)
* Enable Ollama integration tests in CI and rename report to Integration Test Report
- Install Ollama, cache models (qwen2.5:0.5b + nomic-embed-text), and start
server in the Misc integration job for both workflow files
- Set OLLAMA_MODEL and OLLAMA_EMBEDDING_MODEL env vars so the 5 Ollama tests
are no longer skipped
- Rename Flaky Test Report to Integration Test Report throughout (job names,
artifact names, cache keys, file names, script titles/docstrings)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Bump Ollama model to qwen2.5:1.5b for better instruction following
The 0.5b model was too small to reliably follow simple prompts like
'Say Hello World', causing test assertion failures. The 1.5b model
follows instructions more reliably while still being small enough
for fast CI pulls (~1GB).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Re-enable reliable streaming integration tests
Remove the hard skip on test_03_reliable_streaming tests that was
temporarily disabled for instability investigation. CI infrastructure
(Azurite, DTS emulator, Redis, func CLI) is already in place.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Re-enable skipped Functions/DurableTask tests and bump timeout to 480s
- Remove hard skips from 4 tests in test_11_workflow_parallel.py
- Remove hard skip from test_conditional_branching in test_06_dt_multi_agent_orchestration_conditionals.py
- Increase pytest --timeout from 360 to 480 for Functions+DurableTask CI job
- Updated in both python-merge-tests.yml and python-integration-tests.yml
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Re-skip failing Functions/DurableTask tests with specific root causes
- test_11_workflow_parallel (4 tests): xdist worker crashes during execution
- test_conditional_branching: orchestration fails with RuntimeError, not a timeout
- Keep 480s timeout bump for remaining Functions tests
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix auth routing in samples 06/11: api_key -> credential for Azure OpenAI
Both samples passed a bearer token provider via api_key= which caused the
client to route to api.openai.com instead of Azure OpenAI, resulting in
401 Unauthorized. Changed to credential= which correctly triggers Azure
routing and picks up AZURE_OPENAI_ENDPOINT from the environment.
- samples/azure_functions/11_workflow_parallel/function_app.py: 1 fix
- samples/durabletask/06_multi_agent_orchestration_conditionals/worker.py: 2 fixes
- Re-enable 4 parallel workflow tests and 1 conditional branching test
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Re-skip parallel workflow tests: xdist worker distribution issue
The 4 parallel workflow tests crash because xdist worksteal distributes
them across separate workers, each spawning its own func process against
shared emulators. Auth fix (api_key->credential) was valid and stays.
test_conditional_branching now passes with the auth fix.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix E501 line-too-long in azurefunctions parallel test skip reasons
Wrap skip reason strings to stay within 120 char line limit.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add retry logic and port-conflict fix for Ollama CI setup
- Kill any auto-started Ollama before launching serve (fixes port
conflict: 'address already in use')
- Retry ollama pull up to 3 times with 15s backoff (fixes 429 rate
limit failures)
- Applied to both python-merge-tests.yml and python-integration-tests.yml
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix flaky integration tests and re-enable skipped tests
- Foundry agent: add allow_preview=True to custom client test
- Foundry hosting: raise max_output_tokens 50->200, add temperature,
relax assertion in test_temperature_and_max_tokens
- Foundry embedding: update skip reason with root cause (endpoint mismatch)
- OpenAI file search: fix vector store indexing race condition by polling
file_counts before querying; fix get_streaming_response -> get_response(stream=True)
- Azure OpenAI file search: remove skip (transient 500 resolved)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Remove temperature from foundry hosting test (unsupported by CI model)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Stabilize Ollama tool call integration tests with no-arg function
Use a no-argument greet() function instead of hello_world(arg1) for
integration tests. The 1.5B model in CI is unreliable at generating
correct tool call arguments, causing 'Argument parsing failed' errors.
A no-arg function eliminates this flakiness entirely.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Increase reliable streaming test timeouts from 30s to 60s
The LLM call through Azure OpenAI + Redis streaming pipeline can exceed
30s in CI due to cold starts or throttling. Raise to 60s to reduce
flaky timeouts while still bounded by pytest's 120s per-test limit.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Re-enable workflow parallel tests with xdist_group marker
The tests were skipped because xdist distributes module tests across
workers, each spawning their own func process (port conflicts). Adding
xdist_group forces all tests in this module onto a single worker so
the module-scoped function_app_for_test fixture works correctly.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Revert "Re-enable workflow parallel tests with xdist_group marker"
This reverts commit
|
||
|
|
317ef4491e |
Python: Fix hosted MCP replay producing orphan function_call_output (#5581)
* Python: Fix hosted MCP replay producing orphan function_call_output Resolves part of #5546. After a turn ran a hosted MCP / Foundry-toolbox-MCP tool, the next turn's replayed input array carried a function_call_output with an mcp_* call_id and no matching function_call, and the Responses API returned a 400. Two layers covered here: * Chat-client serialize layer (packages/openai): adds mcp_server_tool_call and mcp_server_tool_result cases to _prepare_message_for_openai and _prepare_content_for_openai. Pairs are coalesced via a post-pass into a single mcp_call input item carrying both arguments and output. Orphan results are dropped (debug-logged) rather than serialized as orphan function_call_output, which is what the Responses API rejected. * Host read layer (packages/foundry_hosting): _item_to_message and _output_item_to_message now route custom_tool_call_output whose call_id.startswith("mcp_") to Content.from_mcp_server_tool_result. Non-mcp_ call_ids continue to produce Content.from_function_result. Symmetric with the host write-side choice for hosted-MCP results. Two further fixes (agentserver SDK additions, host write-side single-item emission) remain tracked on the issue and depend on an SDK release. * Python: Fix pyright unknown-type in _stringify_mcp_output cast(Sequence[Any], output) after the isinstance check so pyright stops flagging the loop variable as unknown. Also normalizes a couple of em-dashes in docstrings I introduced in the prior commit. * Python: Harden _stringify_mcp_output for dict-shaped MCP outputs Address Copilot review on PR #5581. Today the helper falls back to str() for any non-string, non-text-attribute entry, which produces Python repr (single-quoted dicts) for the canonical MCP raw-JSON text-content shape `{"type": "text", "text": "..."}` and any other dict-shaped output. Three small changes: * List-entry path: prefer plain string entries, then `.text` attribute (Content objects), then `entry["text"]` for Mapping entries in the canonical MCP shape, then JSON-encode anything else. * Final fallback: `json.dumps(output, default=str)` so Mappings and scalars produce valid JSON rather than Python repr. * Two new unit tests covering the dict-with-text shape and the non-text-dict JSON fallback. * Python: Suppress mypy redundant-cast on _stringify_mcp_output narrowing The cast is needed by pyright (reportUnknownVariableType) but mypy considers it redundant after the preceding isinstance narrowing. Pyright's behavior is correct for the strict-mode reporting we run, so keep the cast and silence mypy on the line. |
||
|
|
570a4d54c2 |
Python: Support OpenAI and Gemini allowed_tools tool choice (#5322)
* Support OpenAI allowed_tools in ToolMode (#5309) Add allowed_tools field to ToolMode TypedDict, enabling users to restrict which tools the model may call via the OpenAI allowed_tools tool_choice type. This preserves prompt caching by keeping all tools in the tools list while limiting which ones the model can invoke. - Add allowed_tools: list[str] to ToolMode TypedDict - Add validation in validate_tool_mode() (only valid when mode == "auto") - Convert to OpenAI API format in _prepare_options() - Add tests for validation and API payload generation Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Python: Support OpenAI `allowed_tools` tool choice in Python SDK Fixes #5309 * Fix #5309: Validate allowed_tools shape and add Chat Completions client support - validate_tool_mode now checks allowed_tools is a non-string sequence of strings and normalizes to list[str], raising ContentError for invalid types - Add missing allowed_tools branch in _chat_completion_client._prepare_options so allowed_tools is emitted as the OpenAI allowed_tools wire format instead of being silently dropped - Add tests for invalid allowed_tools types (string, int, mixed), empty list, tuple normalization, and Chat Completions client payload generation Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: support allowed_tools with mode 'required' in addition to 'auto' OpenAI's allowed_tools tool_choice type supports both mode 'auto' and 'required'. Update validation, client conversion, and tests to allow both modes instead of restricting to 'auto' only. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: use Gemini VALIDATED mode for allowed_tools, warn in unsupported providers - Use FunctionCallingConfigMode.VALIDATED instead of ANY when allowed_tools is set with auto mode in Gemini, preserving optional tool-call semantics. - Handle allowed_tools in required mode with required_function_name precedence. - Fix allowed_names guard to use identity check (is not None) so empty lists are preserved. - Bump google-genai minimum to >=1.32.0 (VALIDATED added in that version). - Add warnings in Anthropic and Bedrock when allowed_tools is set but not supported. - Add Gemini unit tests for allowed_tools with auto, required, empty list, and required_function_name precedence scenarios. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: Chat Completions API does not support allowed_tools, add integration tests - Chat Completions API (_chat_completion_client.py) now warns and falls back to plain mode when allowed_tools is set, since the /chat/completions endpoint does not support the allowed_tools type. - Add allowed_tools integration test param to both OpenAIChatClient (Responses API) and OpenAIChatCompletionClient parametrized option tests. - Update Chat Completions unit tests to reflect the warn-and-fallback behavior. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: remove unused walrus operator variable in chat completion client Remove assigned-but-never-used variable 'allowed' flagged by ruff F841. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <copilot@github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> |
||
|
|
46ab47b9e1 |
Python: Fix file_search citations breaking assistant history roundtrip (#5557)
* Python: Fix file_search citations breaking assistant history roundtrip The Responses API rejects 'input_file' inside an assistant message, but the SDK was emitting it whenever an assistant Message contained a hosted_file content (which is what file_search citations become). Three coordinated fixes: 1. _prepare_content_for_openai now skips hosted_file for the assistant role instead of mapping to input_file (which the API rejects there). 2. The streaming response.output_text.annotation.added handler attaches file_citation, container_file_citation, and file_path as annotations on text content, matching the non-streaming path. Previously streaming produced standalone HostedFileContent items that always tripped (1). 3. output_text serialization preserves Annotation objects on roundtrip via a new _annotations_to_output_text helper instead of hardcoding 'annotations' to []. file_search citations now survive multi-agent forwarding. Closes #5556. * Address PR review - _annotations_to_output_text: fan out one entry per annotated_region for url_citation/container_file_citation (Annotation.annotated_regions is a Sequence; the API form carries one start/end per entry). - Validate region span bounds are ints before emitting; skip otherwise. - Add test for the file_path branch (annotation with file_id only). - Add test verifying streamed citation events coalesce onto surrounding text via _finalize_response so span indices reference the merged text, not the empty-text streaming carrier. |
||
|
|
62e02da698 |
Python: update FoundryAgent for hosted agent sessions (#5447)
* fixes to FoundryAgent to connect to new hosted agents Co-authored-by: Copilot <copilot@github.com> * fix mypy Co-authored-by: Copilot <copilot@github.com> * Python: remove Foundry service session helpers Remove the public hosted-agent service session CRUD helpers from FoundryAgent and drop the related feature-stage inventory entry. Update the hosted-agent sample to create and delete service sessions directly through the preview AIProjectClient APIs, and tighten a few test harnesses surfaced by full workspace validation. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix from merge * fix hosted env detection Co-authored-by: Copilot <copilot@github.com> * reverted sample update * fix tests and code Co-authored-by: Copilot <copilot@github.com> * remove aenter * skipping some tests Co-authored-by: Copilot <copilot@github.com> --------- Co-authored-by: Copilot <copilot@github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> |
||
|
|
ea3320d39f |
Python: Fix OpenAI Responses streaming to propagate created_at from final response.completed event (#5382)
* Fix streaming response losing created_at from response.completed event (#5347) The streaming path in _parse_chunk_from_openai did not extract created_at from the response.completed event, unlike the non-streaming path in _parse_responses_response. This caused durabletask persistence warnings when created_at was None. Extract created_at in the response.completed case and pass it to the returned ChatResponseUpdate. Also fix pre-existing pyright errors for optional orjson import in sample files. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix orjson import suppression to use pyright instead of mypy (#5347) Replace `# type: ignore[import-not-found]` with `# pyright: ignore[reportMissingImports]` on optional orjson imports in conversation sample files, matching the repo's Pyright strict configuration. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <copilot@github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> |
||
|
|
bca40a7e90 |
Python: fix: exclude null file_id from input_image payload to prevent 400 sch… (#5125)
* fix: exclude null file_id from input_image payload to prevent 400 schema error (#5120) * test: add case for additional_properties present without file_id key --------- Co-authored-by: Sergey Borisov <sergey.borisov@dataimpact.io> |
||
|
|
57fa8ea902 |
Python: Fix OpenAIEmbeddingClient to use AsyncOpenAI for /openai/v1 endpoints (#5137)
* Fix OpenAIEmbeddingClient with /openai/v1 endpoint (#5068) When base_url ends with /openai/v1/ and a credential is provided, load_openai_service_settings was creating an AsyncAzureOpenAI client. The Azure SDK rewrites deployment-based endpoints (including /embeddings) by inserting /deployments/{model}/ into the URL, producing 404s on the OpenAI-compatible /openai/v1 endpoint. Use AsyncOpenAI instead of AsyncAzureOpenAI when the resolved base_url targets /openai/v1, converting the Azure token provider to an async api_key callable. The responses_mode path is unaffected because the Responses API (/responses) is not in the SDK's rewrite list. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Python: Fix OpenAIEmbeddingClient to use AsyncOpenAI for /openai/v1 endpoints Fixes #5068 * Address review feedback: improve test coverage and remove unrelated changes - Revert unrelated formatting change in test_a2a_agent.py - Fix test_init_with_openai_v1_base_url_and_api_key_uses_openai_client to exercise the Azure settings path (via AZURE_OPENAI_BASE_URL env var) instead of the plain OpenAI path, covering the elif api_key branch - Add _ensure_async_token_provider unit tests for both sync and async token providers Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address review feedback for #5068: Python: [Bug]: `OpenAIEmbeddingClient` does not work with `/openai/v1` endpoint --------- Co-authored-by: MAF Dashboard Bot <maf-dashboard-bot@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Evan Mattson <35585003+moonbox3@users.noreply.github.com> |
||
|
|
3e54a689fc |
Python: Add search tool content for OpenAI responses (#5302)
* Add OpenAI search tool content parsing Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix typing * simplified oai image test * same for azure * skip az responses api test --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> |
||
|
|
435c66e9c9 |
Python: Handle url_citation annotations in FoundryChatClient streaming responses (#5071)
* Fix url_citation annotations dropped in streaming (#5029) Add url_citation branch to the streaming annotation handler in _parse_chunk_from_openai, mirroring the existing non-streaming path. The handler creates an Annotation with type='citation', title, url, and annotated_regions (TextSpanRegion), wrapped in Content.from_text. Update test_streaming_annotation_added_with_unknown_type to use a truly unknown type, and add new tests for url_citation (with and without url). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address review feedback for #5029: Python: [Bug]: url_citation annotations silently dropped in Foundry streaming (SharePoint grounding citations lost) --------- Co-authored-by: Copilot <copilot@github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Evan Mattson <35585003+moonbox3@users.noreply.github.com> |
||
|
|
2b251d904f |
Python: Fix reasoning replay when store=False (#5250)
* fix reasoning content when store=False * Remove accidental worktree entries Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * remove local session sample * removed left over files * Add attribution override regression test Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> |
||
|
|
5e8fe0be1f |
Python: Stop emitting duplicate reasoning content from OpenAI response.reasoning_text.done and response.reasoning_summary_text.done events (#5162)
* Fix reasoning text done events duplicating streamed delta content (#5157) The OpenAI Responses API sends both reasoning_text.delta (incremental chunks) and reasoning_text.done (full accumulated text) events. The chat client was emitting Content for both, causing ag-ui to append the full done text onto already-accumulated delta text, producing duplicated reasoning output. Stop emitting Content for reasoning_text.done and reasoning_summary_text.done events, matching how output_text.done is already handled (not emitted). The deltas contain all the content; the done event is redundant. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(openai): emit reasoning done content as fallback when no deltas observed (#5157) Address PR review feedback: - Track item_ids that received reasoning deltas via seen_reasoning_delta_item_ids set - Emit content from done events only when no deltas were received for the item_id, preventing silent content loss on stream resumption - Add comment documenting code_interpreter done event asymmetry - Replace redundant ag-ui test with deduplication-focused test - Add integration test for delta+done sequence in OpenAI chat client tests - Add fallback path tests for done events without preceding deltas Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address review feedback for #5157: Python: [Bug]: "type": "response.reasoning_text.delta" and "response.reasoning_text.done" both get exposed as "text_reasoning" * Fix AG-UI reasoning streaming to use proper Start/End pattern (#5157) _emit_text_reasoning now follows the same streaming pattern as _emit_text: - Emits ReasoningStartEvent/ReasoningMessageStartEvent only on the first delta for a given message_id - Emits only ReasoningMessageContentEvent for subsequent deltas - Defers ReasoningMessageEndEvent/ReasoningEndEvent until _close_reasoning_block is called (on content type switch or end-of-run) This produces the correct protocol pattern: ReasoningStartEvent ReasoningMessageStartEvent ReasoningMessageContentEvent(delta1) ReasoningMessageContentEvent(delta2) ReasoningMessageEndEvent ReasoningEndEvent Instead of wrapping every delta in a full Start→End sequence. Backward compatibility is preserved: calling _emit_text_reasoning without a flow argument still produces the full sequence per call. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix import ordering lint error in AG-UI test file (#5157) Move inline import of TextMessageContentEvent to the top-level import block and ensure alphabetical ordering to satisfy ruff I001 rule. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix mypy error: rename loop variable to avoid type conflict with WorkflowEvent The 'event' variable was already typed as WorkflowEvent[Any] from the async for loop at line 590. Reusing it in the _close_reasoning_block loop (which returns list[BaseEvent]) caused an incompatible assignment error. Renamed to 'reasoning_evt' to avoid the conflict. Fixes #5162 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address review feedback for #5157: review comment fixes * narrow test result reporting to explicit pytest JUnit XML * Fix test args * Fix pytest-results-action in merge workflow and remove committed test artifacts Apply the same JUnit XML fix from python-tests.yml to python-merge-tests.yml: add --junitxml=pytest.xml to all test commands and narrow the results action path from ./python/**.xml to ./python/pytest.xml. Also remove accidentally committed pytest.xml and python-coverage.xml and add them to .gitignore. --------- Co-authored-by: Copilot <copilot@github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> |
||
|
|
36cafe4e5a |
Python: Raise clear handler registration error for unresolved TypeVar annotations (#4944)
* Raise clear handler registration error for unresolved TypeVar (#4943) Detect unresolved TypeVar in message parameter annotations during handler registration in both _validate_handler_signature (Executor) and _validate_function_signature (FunctionExecutor). Raises a ValueError with an actionable message recommending @handler(input=..., output=...) or @executor(input=..., output=...) instead of letting TypeVar leak through to a confusing TypeCompatibilityError during workflow edge validation. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address review feedback for #4943: reorder checks and harden function executor - Move TypeVar check before validate_workflow_context_annotation in _executor.py so users see the more actionable error first - Wrap get_type_hints in try/except in _function_executor.py matching the defensive pattern in _executor.py - Repurpose duplicate test to cover bounded TypeVar rejection - Add test_function_executor_allows_concrete_types for test symmetry Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Narrow get_type_hints except clause and add missing tests (#4943) - Narrow `except Exception` to `except (NameError, AttributeError, RecursionError)` in both _executor.py and _function_executor.py so unexpected failures in get_type_hints are not silently swallowed. - Add test_handler_unresolvable_annotation_raises to test_function_executor_future.py exercising the except branch of get_type_hints in the function executor path. - Add test_function_executor_rejects_bounded_typevar_in_message_annotation to test_function_executor.py for parity with the Executor bounded TypeVar test. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add error ordering test for TypeVar vs WorkflowContext priority (#4943) Add test_handler_typevar_error_takes_priority_over_context_error to verify that when a handler has both a TypeVar message and an unannotated ctx, the TypeVar error is raised first (the more actionable issue). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Python: Fix image content serialization sending null file_id to Foundry API Omit file_id from input_image dict when not present instead of including it as null, which Azure AI Foundry's stricter schema validation rejects. * Python: Fix Foundry API rejecting rich content in function_call_output Azure AI Foundry does not support list-format output in function_call_output items. Add SUPPORTS_RICH_FUNCTION_OUTPUT flag (default True) to RawOpenAIChatClient, set to False in RawFoundryChatClient so Foundry falls back to string output for tool results with images/files. Also omit file_id from input_image dicts when not set, since Foundry rejects explicit nulls. * Python: Surface rich tool content as user message when Foundry lacks support When SUPPORTS_RICH_FUNCTION_OUTPUT is False, image/file items from tool results are injected as a follow-up user message so the model can still process the visual content via Foundry's supported user message format. * Xfail Foundry image integration test for the meantime --------- Co-authored-by: Copilot <copilot@github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> |
||
|
|
3446eb8d5d |
Python: [BREAKING] update to v1.0.0 (#5062)
* updates to final deprecated pieces and versions * fix mypy * fix readme links |
||
|
|
95fd5ec658 |
Python: [BREAKING] Python: move Azure AI embeddings to Foundry (#5056)
* renamed AzureAIINferenceEmbeddings and lazy load azure-cosmos and env var rename * updated coverage * fix readme |
||
|
|
7e8e9e3074 |
Python: Fix duplicate system message from instructions (#5049) (#5051)
Add deduplication to `prepend_instructions_to_messages()` to skip instructions that are already present as leading messages with the same role and text. This prevents duplicate system messages when instructions are injected by multiple layers (e.g. Agent + chat client). Fixes #5049 |
||
|
|
519bb0cb2b |
Python: updated declarative samples and handling of non-pydantic response formats (#5022)
* updated declarative samples and handling of non-pydantic response formats * fixed from comments * update docstring |