* Fix walrus operator precedence for model_id in AzureOpenAIResponsesClient (#4299)
Add parentheses around the walrus assignment so model_id receives the
actual string value instead of the boolean result of
`kwargs.pop(...) and not deployment_name`.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address review: replace walrus with explicit None check, add edge-case tests (#4299)
- Replace walrus operator with explicit assignment and 'is not None'
check to avoid boolean-coercion pitfalls (empty string now correctly
surfaces as ValueError instead of silently falling back)
- Add test: deployment_name takes precedence over model_id kwarg
- Add test: model_id='' raises ValueError
- Add test: model_id=None falls back to env var
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add explicit validation for empty model_id in AzureOpenAIResponsesClient
Reject empty or whitespace-only model_id with ValueError instead of
silently passing an empty deployment name downstream. This ensures the
test_init_model_id_kwarg_empty_string test correctly validates behavior
defined in production code rather than relying on downstream validation.
Addresses PR review feedback for #4299.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Simplify model_id handling using walrus operator
Addresses review comment on PR #4310.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Restore explicit model_id validation to fix test failures (#4299)
The walrus operator refactor silently dropped the empty-string validation,
causing test_init_model_id_kwarg_empty_string to fail. Restore the explicit
None check and ValueError raise for empty model_id.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Revert "Restore explicit model_id validation to fix test failures (#4299)"
This reverts commit 1d2965fff6.
* Revert to walrus operator fix per review feedback
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix .NET conversation memory in DevUI (#3484)
* formatting fixes
* fix memory regression in python devui , fix for #4123
* Fix for #3983: Added _get_event_type() helper that safely accesses event type on both objects (.type) and dicts (.get("type")). Replaced all 4 bare event.type accesses in _executor.py (lines 267, 477, 499, 523).
Root cause: PR #3690 changed event.__class__.__name__ == "RequestInfoEvent" (safe) to event.type == "request_info" (crashes on dicts), but _execute_workflow still yields raw dicts on error paths.
Test: test_workflow_error_yields_dict_event_without_crash — mocks a workflow that raises, verifies execute_entity consumes the dict error events without crashing.
* format fixes
* lint fixes
* Python: Fix Executor handler type checking with __future__ annotations (#3898)
Use typing.get_type_hints() in _validate_handler_signature to resolve
string annotations from `from __future__ import annotations`. This
mirrors the fix applied to FunctionExecutor in #2308.
When __future__ annotations are enabled, type annotations are stored as
strings. The handler decorator was passing these strings directly to
validate_workflow_context_annotation, which uses typing.get_origin and
returns None for strings, causing a ValueError.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address PR review feedback for #3898: improve error handling and test coverage
- Wrap typing.get_type_hints() in try/except to provide a descriptive
ValueError mentioning the handler name when annotations cannot be resolved
- Strengthen bare context test to assert output_types and workflow_output_types
- Add test for @handler(input=..., output=...) with future annotations
covering the skip_message_annotation branch
- Add test for union-type context annotations with future annotations
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Narrow exception catch and add test for unresolvable annotations (#3898)
- Narrow except clause from bare Exception to (NameError, AttributeError,
TypeError) to avoid masking unexpected errors.
- Add test_handler_unresolvable_annotation_raises to verify that a handler
with a forward-reference to a non-existent type raises ValueError with
the expected message.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix#3898: fall back to raw annotations when get_type_hints fails
When typing.get_type_hints(func) raises NameError (unresolvable forward
ref), AttributeError, RecursionError, or any other exception, fall back
to the raw parameter annotations instead of raising a ValueError.
This matches the suggestion from @moonbox3 on PR #4317.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix test to match new fallback behavior when get_type_hints fails (#3898)
The code now falls back to raw string annotations instead of raising
'Failed to resolve type annotations'. A ValueError is still raised when
the raw string ctx annotation is not a valid WorkflowContext type, so
update the test to match on ValueError without checking the message.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Apply pyupgrade: remove unnecessary string annotation quote
* Add noqa for intentionally undefined name in annotation test
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix _merge_options dropping dict-defined tools (#4303)
_merge_options used getattr(tool, 'name', None) to de-duplicate tools,
which returns None for dict-style tool definitions. This caused all
override dict tools to be treated as duplicates of each other and of any
base dict tools, silently dropping them.
Add _get_tool_name() helper that extracts the name from both object-style
tools (via .name attribute) and dict-style tools (via tool['function']['name']).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address review: fix None dedup bug and add comprehensive tests (#4303)
- Exclude None from existing_names set so nameless/malformed tools are
not silently deduplicated against each other
- Add test for cross-type dedup (dict tool + object tool with same name)
- Add test verifying nameless tools are preserved (not falsely deduped)
- Add unit tests for _get_tool_name edge cases: missing function key,
non-dict function value, missing name, no name attribute, non-dict
inputs, and valid dict/object tools
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix OpenAIResponsesClient mishandling single-tool inputs (#4304)
Use normalize_tools() in _prepare_tools_for_openai to wrap single tools
(FunctionTool or dict) in a list before iteration, consistent with the
chat client implementation.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address PR review feedback for #4304
- Use precise type annotation matching normalize_tools/OpenAIChatClient signature
instead of collapsed Sequence[Any] | Any | None
- Move emptiness guard after normalize_tools() call so single falsy tool
objects are not silently swallowed
- Import ToolTypes for the type annotation
- Expand test_prepare_tools_for_openai_single_function_tool assertions to
verify parameters, strict, and parameter schema fields
- Add test_prepare_tools_for_openai_none to verify None input returns []
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
WorkflowAgent._run_impl() and _run_stream_impl() did not set
session_context._response before calling _run_after_providers().
This caused InMemoryHistoryProvider.after_run() to see context.response
as None, so response messages were never stored in the session.
On subsequent runs, the workflow only received prior user inputs without
assistant responses, breaking multi-turn conversations.
Fix: Set session_context._response to the workflow result before running
after_run providers, matching the behavior of the regular Agent class.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
During Assistants API streaming, TextDeltaBlock.text.annotations was
ignored when creating Content objects. This caused raw placeholder
strings like 【4:0†source】 to pass through to downstream consumers
(including AG-UI) instead of being resolved to citation metadata.
Map FileCitationDeltaAnnotation and FilePathDeltaAnnotation from
delta_block.text.annotations to Annotation objects on the Content,
consistent with the existing patterns in _responses_client.py and
_chat_client.py.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(python): preserve workflow run kwargs on response continuation (#4293)
When continuing a paused workflow with run(responses=...), the existing
run kwargs stored in state were unconditionally overwritten with an empty
dict. This caused subsequent agent invocations to lose the original run
context (e.g., custom_data, user tokens).
Now kwargs are only overwritten when:
- New kwargs are explicitly provided (override), or
- State was just cleared for a fresh run (initialize to {})
On continuation without new kwargs, existing kwargs are preserved.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address PR review feedback for #4293
- Use consistent get_state(key, {}) default pattern in _agent_executor.py
and _workflow_executor.py instead of get_state(key) or {} to safely
handle missing WORKFLOW_RUN_KWARGS_KEY
- Add test for empty-value kwargs on continuation (custom_data={}) to
verify the is-not-None boundary between overwrite and preserve
- Add test for reset_context=True with no kwargs to exercise the elif
branch that initializes WORKFLOW_RUN_KWARGS_KEY to {}
- Add len assertion to override test for consistency
- Document kwargs-collapsing behavior at the public API call site
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Python: Strip reserved kwargs in AgentExecutor to prevent collision (#4295)
workflow.run(session=...) passed 'session' through to agent.run() via
**run_kwargs while AgentExecutor also passes session=self._session
explicitly, causing TypeError: got multiple values for keyword argument.
_prepare_agent_run_args now strips reserved params (session, stream,
messages) from run_kwargs and logs a warning when they are present.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address PR review feedback for #4295
- Use _RESERVED_RUN_PARAMS constant in stripping loop instead of
hardcoded tuple to maintain single source of truth
- Trim frozenset to only stripped keys (session, stream, messages);
options and additional_function_arguments have separate merge logic
- Fix caplog type annotation to use TYPE_CHECKING pattern
- Assert options return value in reserved-kwarg stripping test
- Add test for multiple reserved kwargs supplied simultaneously
- Add integration test for messages= kwarg via workflow.run()
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
HandoffBuilder.participants() accepted SupportsAgentRun by API contract,
but build() failed at runtime because _prepare_agent_with_handoffs()
requires Agent instances for cloning, tool injection, and middleware.
Fix: Update all public type hints, docstrings, and validation in
HandoffBuilder and HandoffAgentExecutor to require Agent explicitly.
The isinstance check is now performed early in participants() with a
clear error message explaining why Agent is required.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace the Python 3.12 redirect workaround with the proper fix:
set PYTHON_ISOLATE_WORKER_DEPENDENCIES=0 on Python >=3.13.
The segfault (exit code 139) is caused by the Azure Functions worker's
module isolation mechanism conflicting with protobuf's C extensions
(google._upb) on Python 3.13. Disabling isolation lets the worker
load dependencies from the app's own environment, which avoids the
crash while keeping everything running on Python 3.13.
See: https://github.com/Azure/azure-functions-python-worker/issues/1797
Co-authored-by: larohra <41490930+larohra@users.noreply.github.com>
* Python: Fix AgentResponse.value being None when streaming workflow (#3970)
The streaming path in BaseAgent.run() used the raw 'options' parameter
(passed by the caller) to bind response_format into the outer stream's
finalizer. When response_format was set in default_options rather than
runtime options, it was missing from the finalizer and value was None.
Fix: Use the merged chat_options from the run context (via ctx_holder),
matching the non-streaming path which already uses ctx['chat_options'].
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address review feedback for #3970: safer ctx access, add test coverage
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The Azure Functions Python worker crashes with SIGSEGV (exit code 139)
on Python 3.13 due to protobuf C extension (google._upb) compatibility
issues. When the test runner uses Python >=3.13, the conftest now
automatically finds a compatible Python 3.10-3.12 and sets
languageWorkers__python__defaultExecutablePath so the func host uses
it for the worker process.
The CI setup action also ensures Python 3.12 is available on the
runner, falling back to uv python install if the system doesn't have
it.
Co-authored-by: larohra <41490930+larohra@users.noreply.github.com>
Addresses review feedback: monotonic clock is immune to NTP/clock
adjustments that could skew the budget enforcement.
Co-authored-by: larohra <41490930+larohra@users.noreply.github.com>
* Phase 2: Embedding clients for Ollama, Bedrock, and Azure AI Inference
Add embedding client implementations to existing provider packages:
- OllamaEmbeddingClient: Text embeddings via Ollama's embed API
- BedrockEmbeddingClient: Text embeddings via Amazon Titan on Bedrock
- AzureAIInferenceEmbeddingClient: Text and image embeddings via Azure AI
Inference, supporting Content | str input with separate model IDs for
text (AZURE_AI_INFERENCE_EMBEDDING_MODEL_ID) and image
(AZURE_AI_INFERENCE_IMAGE_EMBEDDING_MODEL_ID) endpoints
Additional changes:
- Rename EmbeddingCoT -> EmbeddingT, EmbeddingOptionsCoT -> EmbeddingOptionsT
- Add otel_provider_name passthrough to all embedding clients
- Register integration pytest marker in all packages
- Add lazy-loading namespace exports for Ollama and Bedrock embeddings
- Add image embedding sample using Cohere-embed-v3-english
- Add azure-ai-inference dependency to azure-ai package
Part of #1188
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix mypy duplicate name and ruff lint issues
- Rename second 'vector' variable to 'img_vector' in image embedding loop
- Combine nested with statements in tests
- Remove unused result assignments in tests
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* updates from feedback
* Fix CI failures in embedding usage handling
- Fix Azure AI embedding mypy issues by normalizing vectors to list[float],
safely accumulating optional usage token fields, and filtering None entries
before constructing GeneratedEmbeddings
- Avoid Bandit false positive by initializing usage details as an empty dict
- Update OpenAI embedding tests to assert canonical usage keys
(input_token_count/total_token_count)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Eduard van Valkenburg
·
2026-02-25 17:45:08 +00:00
Three changes to prevent pytest-xdist workers from crashing during
Azure Functions integration tests:
1. Add `start_new_session=True` to subprocess on Linux so signals
(e.g. from test-timeout) cannot propagate between the func host
and the xdist worker process.
2. Add an overall 100-second budget to the fixture setup loop so
the retry logic never exceeds the 120-second test timeout. When
pytest-timeout's thread method fires during fixture setup and the
thread doesn't respond, it calls os._exit() which kills the
xdist worker – this is the root cause of the "Not properly
terminated" crashes.
3. Remove the `UV_PYTHON: "3.10"` workaround from both workflow
files so integration tests actually run on Python 3.13.
Co-authored-by: larohra <41490930+larohra@users.noreply.github.com>
* small updates and improvements in the azure AISearch provider
* Fix mypy errors and embedding function test
- Use separate variable for embeddings result to avoid mypy type reassignment error
- Fix test_vectorized_query_with_embedding_function: use real async function
instead of AsyncMock which falsely matches SupportsGetEmbeddings protocol
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fixes from feedback
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Eduard van Valkenburg
·
2026-02-25 06:47:26 +00:00
* Fix thread corruption when max_iterations exhausted (#1366)
When the function invocation loop exhausts max_iterations while the model
keeps requesting tools, the failsafe code path (calling the model with
tool_choice='none' and prepending fcc_messages) was unreachable because
'if response is not None: return response' short-circuited before it.
The fix removes the premature return so the failsafe always runs after
loop exhaustion, making a final model call with tool_choice='none' to
produce a clean text answer and prepending accumulated fcc_messages from
prior iterations. This matches the existing pattern used by the error
threshold and max_function_calls paths.
Also unskips test_max_iterations_limit and test_streaming_max_iterations_limit
which were previously skipped with 'needs investigation in unified API'.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add fix report for issue #1366
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix ruff formatting in _tools.py and test_issue_1366_thread_corruption.py
Apply ruff format to fix multi-line string concatenation and function call
formatting issues flagged by the linter.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add quality review for issue #1366 fix
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Remove temporary investigation docs.
* Address PR review: explicit enabled check in log condition, clarify mock behavior in test
- Add explicit function_invocation_configuration['enabled'] check to the
'Maximum iterations reached' log condition in both non-streaming and
streaming paths, making intent clearer when function invocation is disabled.
- Add comment in test_thread_safe_after_max_iterations_with_agent explaining
that the failsafe response (tool_choice='none') is provided automatically
by the mock client, not from run_responses.
* Blend fix and tests into project without issue-specific callouts
- Remove issue #1366 references from _tools.py comments
- Move regression tests from standalone test_issue_1366_thread_corruption.py
into test_function_invocation_logic.py alongside existing max_iterations tests
- Clean up test docstrings to describe behavior generically
- Delete the standalone issue-specific test file
---------
Co-authored-by: alliscode <bentho@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: prevent doubled tool_call arguments in MESSAGES_SNAPSHOT
When streaming with client-side tools, some providers send a full-
arguments replay after the streaming deltas complete. The `_emit_tool_call`
function unconditionally appends every arguments delta to the internal
`flow.tool_calls_by_id` tracking dictionary via `+=`. When the replay
contains the exact same complete arguments string that was already
accumulated from prior deltas, the arguments get doubled (e.g.,
`{"todoText":"buy groceries"}{"todoText":"buy groceries"}`).
This causes `MESSAGES_SNAPSHOT` events to contain invalid doubled JSON in
`tool_calls[].function.arguments`, breaking any client or middleware that
relies on snapshots for state reconstruction.
The fix adds a guard (mirroring the existing duplicate guard in
`_emit_text`) that detects when the incoming delta exactly equals the
already-accumulated arguments string, indicating a full-arguments replay
rather than an incremental delta. In this case the append is skipped,
preventing the doubling.
The `ToolCallArgsEvent` deltas are still emitted correctly for real-time
streaming — only the internal snapshot accumulator is guarded.
Fixes#4194
* fix: move duplicate check before event emission + add test
Address Copilot review feedback:
1. Move duplicate full-arguments replay detection BEFORE emitting
ToolCallArgsEvent, for consistency with _emit_text() which returns
early without emitting any events on replay detection.
2. Add test_emit_tool_call_skips_duplicate_full_arguments_replay() to
verify the duplicate detection behavior for tool call arguments,
matching the existing test pattern for text content.
* updated integration tests and guidance
* fixed merge test
* updated integration tests
* fix: remove duplicate --dist loadfile flag from pytest-xdist config
Only one --dist mode can be active at a time; the second value silently
overrides the first. Keep --dist worksteal (dynamic load balancing) and
remove the redundant --dist loadfile from all workflow files and
pyproject.toml configs.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* docs: add keep-in-sync notes for merge and integration test workflows
Both python-merge-tests.yml and python-integration-tests.yml share the
same parallel job structure. Added sync reminders in workflow file
comments, the python-testing SKILL.md, and CODING_STANDARD.md.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* refactor: remove RUN_INTEGRATION_TESTS flag
Integration test gating now uses two mechanisms:
- `@pytest.mark.integration` for test selection via `-m` filtering
- `skip_if_*_disabled` for credential/service availability checks
The RUN_INTEGRATION_TESTS env var was redundant since the marker handles
selection and the skip decorators already check for actual credentials.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: sync missing env vars from merge-tests to integration-tests
Add OPENAI_EMBEDDINGS_MODEL_ID and AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME
to python-integration-tests.yml to match python-merge-tests.yml.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: remove remaining RUN_INTEGRATION_TESTS from embedding tests and docs
Missed test_openai_embedding_client.py and vector-stores README in the
earlier cleanup.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* set functions tests to 3.10
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Eduard van Valkenburg
·
2026-02-24 09:35:46 +00:00
* feat(python): Add embedding abstractions and OpenAI implementation (Phase 1)
This PR contains two parts:
1. **Overall migration plan** for porting vector stores and embeddings from
Semantic Kernel to Agent Framework (docs/features/vector-stores-and-embeddings/README.md)
covering all 10 phases from core abstractions through connectors and TextSearch.
2. **Phase 1 implementation** — core embedding abstractions and OpenAI/Azure OpenAI
embedding clients:
Core types (_types.py):
- EmbeddingGenerationOptions TypedDict (total=False)
- Embedding[EmbeddingT] generic class with model_id, dimensions, created_at
- GeneratedEmbeddings[EmbeddingT, EmbeddingOptionsT] list container with options, usage
- EmbeddingInputT (default str) and EmbeddingT (default list[float]) TypeVars
Protocol + base class (_clients.py):
- SupportsGetEmbeddings protocol — Generic[EmbeddingInputT, EmbeddingT, OptionsContraT]
- BaseEmbeddingClient ABC — Generic[EmbeddingInputT, EmbeddingT, OptionsCoT]
Telemetry (observability.py):
- EmbeddingTelemetryLayer with gen_ai.operation.name = "embeddings"
OpenAI implementation (openai/_embedding_client.py):
- RawOpenAIEmbeddingClient, OpenAIEmbeddingClient, OpenAIEmbeddingOptions
- Uses _ensure_client() factory pattern
Azure OpenAI implementation (azure/_embedding_client.py):
- AzureOpenAIEmbeddingClient following AzureOpenAIChatClient pattern
- Supports API key, Entra ID credentials, env var configuration
Tests:
- 47 unit tests for types, protocol, base class, OpenAI, and Azure clients
- 6 integration tests (gated behind RUN_INTEGRATION_TESTS + credentials)
Samples:
- samples/02-agents/embeddings/openai_embeddings.py
- samples/02-agents/embeddings/azure_openai_embeddings.py
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: Add AzureOpenAIEmbeddingClient to azure __init__.pyi stub
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* ci: Add embedding env vars to Python integration tests
Map OPENAI_EMBEDDING_MODEL_ID and AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME
from GitHub vars to the integration test environment.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: Handle base64 encoding_format in OpenAI embedding client
When encoding_format='base64' is used, the OpenAI API returns base64-encoded
floats instead of a JSON array. Decode these automatically to list[float]
so the return type stays consistent regardless of encoding format.
Also adds a unit test for base64 decoding and fixes minor docstring/import issues.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: Only record INPUT_TOKENS for embedding telemetry
Embeddings have no output/completion tokens. Remove OUTPUT_TOKENS recording
which was double-counting prompt_tokens via the total_tokens fallback.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: Resolve mypy variance error and lint warning
Use contravariant/covariant TypeVars for SupportsGetEmbeddings Protocol.
Combine nested if into single statement in telemetry layer.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: Make EmbeddingCoT invariant for mypy compatibility
GeneratedEmbeddings is invariant in its type param, so the Protocol
TypeVar cannot be covariant.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: Address PR review - empty values guard, service_url for telemetry
- Add early return for empty values in get_embeddings to avoid unnecessary API calls
- Add service_url() method to RawOpenAIEmbeddingClient for proper telemetry endpoint reporting
- Add test for empty values behavior
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Python: Fix OpenAI chat client compatibility with third-party endpoints and OTel 0.4.14 (#4161)
* Fix system message content sent as list instead of string
Some OpenAI-compatible endpoints (e.g. NVIDIA NIM) reject system messages
when content is a list of content parts. This change flattens system and
developer message content to a plain string in the Chat Completions client.
Fixes https://github.com/microsoft/agent-framework/issues/1407
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix compatibility with opentelemetry-semantic-conventions-ai 0.4.14
Version 0.4.14 removed several LLM_* attributes from SpanAttributes
(LLM_SYSTEM, LLM_REQUEST_MODEL, LLM_RESPONSE_MODEL, LLM_REQUEST_MAX_TOKENS,
LLM_REQUEST_TEMPERATURE, LLM_REQUEST_TOP_P, LLM_TOKEN_TYPE).
Move these to the OtelAttr enum with their well-known gen_ai.* string values
and update all references in observability.py and tests.
Fixes https://github.com/microsoft/agent-framework/issues/4160
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Flatten text-only message content to string for all roles
Extend the system/developer fix to all message roles. Text-only content
lists are now post-processed into plain strings, while multimodal content
(text + images/audio) remains as a list. This fixes compatibility with
OpenAI-like endpoints that cannot deserialize list content (e.g. Foundry
Local's Neutron backend).
Partially fixes https://github.com/microsoft/agent-framework/issues/4084
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix streaming text lost when usage data in same chunk
Some providers (e.g. Gemini) include both usage data and text content
in the same streaming chunk. The early return on chunk.usage caused
text and tool call parsing to be skipped entirely. Remove the early
return and process usage alongside text/tool calls.
Fixes https://github.com/microsoft/agent-framework/issues/3434
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix mypy errors in _chat_client.py
Rename shadowed variable 'args' in system/developer branch to 'sys_args'
and rename loop variable 'content' to 'msg_content' to avoid type conflict.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* reorder imports
* fix: Use OtelAttr.REQUEST_MODEL instead of removed SpanAttributes.LLM_REQUEST_MODEL
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* docs: Add score_threshold to vector store plan
Reference SK .NET PR #13501 for score threshold filtering semantics.
Include score_threshold in SearchOptions from Phase 3.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* docs: Add reference to roji's SK .NET MEVD work for SQL connectors
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: Clear env vars in construction tests to avoid CI leakage
Tests for missing API key / model ID now use monkeypatch.delenv to ensure
env vars from the integration test environment don't prevent the expected
ValueError from being raised.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Eduard van Valkenburg
·
2026-02-24 07:40:20 +00:00
* Python: Enhance Azure AI Search citations with document URLs in Foundry V2 (Responses API)
Override _parse_response_from_openai and _parse_chunk_from_openai in
RawAzureAIClient to extract get_urls from azure_ai_search_call_output
items and enrich url_citation annotations with document-specific URLs.
- Non-streaming: first pass collects get_urls, post-processes annotations
- Streaming: captures search output state, enriches url_citation events
(also handles url_citation annotation type not handled by base class)
- Updated V2 sample to demonstrate citation URL extraction
- Added 14 unit tests covering extraction, enrichment, and edge cases
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* refactor: rework search citation enrichment to override _inner_get_response
- Remove all direct openai/pydantic imports from _client.py
- Override _inner_get_response instead of _parse_response_from_openai/_parse_chunk_from_openai
- Use closure-local state for streaming instead of instance-level _streaming_search_get_urls
- Add _build_url_citation_content helper for streaming url_citation handling
- Fix mypy errors by using str(value or '') for Annotation TypedDict fields
- Fix docstring to say 'citation' instead of 'url_citation'
- Update tests to match new approach
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: handle streaming search citations from output_item.done events
The azure_ai_search_call_output item only has populated output data
(including get_urls) in the response.output_item.done event, not in
the response.output_item.added event. Also removed the search_get_urls
guard on url_citation handling so annotations are always produced even
if get_urls haven't been captured yet.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* addressed comments
* refactor: address PR review - eliminate type: ignore[assignment] pattern
Call super()._inner_get_response() independently in each branch instead
of once at the top with union type reassignment. Non-streaming uses
two-arg super() in the closure; streaming uses cast() for type narrowing.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* refactor: remove defensive patterns per PR review
- Replace all getattr() with direct attribute access
- Remove cast() for streaming branch, use type: ignore[assignment]
- Simplify _build_url_citation_content to use dict access directly
- Simplify _extract_azure_search_urls to use item.type/item.output
- Handle empty list output from streaming 'added' events
- Update tests to match actual runtime types (objects, not dicts)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* mypy fix
* small fixes
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add max_function_calls to FunctionInvocationConfiguration (#2329)
Add a new per-request max_function_calls setting to FunctionInvocationConfiguration
that limits the total number of individual function invocations across all iterations
within a single get_response call. This complements max_iterations (which limits LLM
roundtrips) by providing a hard cap on actual tool executions regardless of parallelism.
- Add max_function_calls field to FunctionInvocationConfiguration (default: None/unlimited)
- Track cumulative function call count in both streaming and non-streaming tool loops
- Force tool_choice='none' when the limit is reached
- Add validation in normalize_function_invocation_configuration
- Improve docstrings for FunctionInvocationConfiguration, FunctionTool, and @tool
to clarify semantics of max_iterations vs max_function_calls vs max_invocations
- Add tests for parallel calls, single calls, unlimited mode, and config validation
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add sample for controlling total tool executions
Showcases all three mechanisms for limiting tool executions:
1. max_iterations — caps LLM roundtrips
2. max_function_calls — caps total individual function invocations per request
3. max_invocations — lifetime cap on a specific tool instance
Plus a combined scenario demonstrating defense in depth.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Suppress ruff E305/fmt in hosting sample to preserve XML doc tags
The XML snippet tags (# <create_agent> / # </create_agent>) are used for
docs extraction and must stay adjacent to the code they wrap. Both ruff
check (E305) and ruff format add blank lines after the function definition,
pushing the closing tag away. Suppress with ruff: noqa: E305 and fmt: off.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add per-agent tool wrapping scenario to control_total_tool_executions sample
Show that wrapping the same callable with @tool multiple times creates
independent FunctionTool instances with separate invocation counters,
enabling per-agent max_invocations budgets for shared functions.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Clarify max_function_calls is a best-effort limit
The limit is checked after each batch of parallel calls completes, so the
current batch always runs to completion even if it overshoots the limit.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address PR review: fix docstring reference, clarify best-effort in sample
- Fix malformed Sphinx :attr: role in FunctionTool docstring — use plain
backtick reference instead
- Update sample to say 'best-effort cap' instead of 'hard cap' for
max_function_calls, noting it's checked between iterations
- Parametrize pattern is correct (fixture override, matching existing tests)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* clarify max_invocations limits
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Eduard van Valkenburg
·
2026-02-24 01:00:25 +00:00
* Fix structured_output propagation in ClaudeAgent
Capture structured_output from ResultMessage in _get_stream() and
propagate it to AgentResponse.value via a custom finalizer. Previously
structured_output was silently discarded, making output_format unusable.
Fixes#4095
* Address review feedback: use value parameter instead of private properties
- Extend AgentResponse.from_updates() to accept optional value parameter
- Remove structured_output yield from _get_stream()
- Update _finalize_response() to pass value via public API
- Update streaming test to use get_final_response()
* Fix mypy errors: add value parameter to from_updates overloads
Add value parameter to both @overload signatures of
AgentResponse.from_updates() so mypy recognizes the argument.
---------
Co-authored-by: Amit Mukherjee <amimukherjee@microsoft.com>
Co-authored-by: Dmytro Struk <13853051+dmytrostruk@users.noreply.github.com>
* fix Workflow.as_agent() streaming regression in ag-ui
* Address PR feedback
* workflows wip
* wip
* wip
* Workflow AG-UI demo
* Fixes for handoff workflow demo
* Fixes to workflows support in AG-UI
* Fixes
* Add headers to some demo files
* Fix comment
* Fixes for store
* Make _input_schema lazy-loaded
* fix mypy
* revert session change to handoff only for now
---------
Co-authored-by: Eduard van Valkenburg <eavanvalkenburg@users.noreply.github.com>