* Added example demonstrating creating an AIAgent using the Microsoft.AI.Extensions implementation of IChatClient using Dapr as the inference backend provider - in this example, using Ollama
Signed-off-by: Whit Waldo <whit.waldo@innovian.net>
* Update dotnet/samples/GettingStarted/AgentProviders/Agent_With_Dapr/README.md
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Added copyright statement at top of file
Signed-off-by: Whit Waldo <whit.waldo@innovian.net>
* Update dotnet/agent-framework-dotnet.slnx
That's odd the IDE added it a second time.
Co-authored-by: westey <164392973+westey-m@users.noreply.github.com>
* Address review nits: configurable Dapr gRPC endpoint and document VersionOverride
Make the Dapr sidecar gRPC endpoint configurable via the DAPR_GRPC_ENDPOINT environment variable
(defaulting to http://localhost:3501) and document it in the README. Add a comment explaining why the
Microsoft.Extensions.* VersionOverride entries are needed and when they can be removed.
---------
Signed-off-by: Whit Waldo <whit.waldo@innovian.net>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: westey <164392973+westey-m@users.noreply.github.com>
Co-authored-by: Roger Barreto <19890735+RogerBarreto@users.noreply.github.com>
* .NET: Add OpenAI Responses protocol helpers and optional execution state (ADR-0032)
* Fix netstandard2.0/net472 build; harden helpers and workflow checkpoint key per review
* .NET: Migrate hosting Responses samples to Azure.AI.Projects and fix workflow resume
Migrate HostingResponsesAgent and HostingResponsesWorkflow samples from
Azure.AI.OpenAI to Azure.AI.Projects (AIProjectClient.AsAIAgent), using the
FOUNDRY_PROJECT_ENDPOINT/FOUNDRY_MODEL convention.
Fix HostedWorkflowState.RunOrResumeAsync: on subsequent turns, restore the
session's latest checkpoint and run the workflow forward with the new turn's
input (mirroring the Python hosting host's restore-then-run semantics) instead
of resuming a halted run with no input, which waited on input indefinitely.
Add round-trip resume tests and update ADR-0032/spec-003 wording.
* .NET: Fix HostedWorkflowState resume hang on unserviced external requests
On resume, HostedWorkflowState.RunOrResumeAsync drained the workflow with the
blocking WatchStreamAsync overload, so a workflow that halts at an unserviced
RequestInfoEvent (human-in-the-loop / approval) blocked forever — asymmetric
with the first-turn RunAsync path, which returns at the same halt. Break the
drain when a superstep completes with HasPendingRequests, restoring symmetry
with turn 1. Add a HITL approval-gate workflow and a resume-does-not-block test.
* .NET: Warn when a HostedWorkflowState resume makes no progress
Add an optional ILoggerFactory to HostedWorkflowState and log a warning when a
resumed turn produces no events, mirroring the Python host's zero-event restore
warning (a stale checkpoint or an input that does not match the workflow's
expected type leaves session state unprogressed). Add a non-chat string workflow
helper, a capturing logger, and a red/green test.
* .NET: Resume HostedWorkflowState from durable checkpoint on cursor miss
Add CheckpointManager.GetLatestCheckpointAsync(sessionId) and have
HostedWorkflowState fall back to it when its in-memory head cursor misses, so a
durable CheckpointManager resumes a session across a process restart or a new
holder instead of restarting from the workflow's start executor. Mirrors the
Python host's per-turn get_latest read-through. Add a counting workflow that
proves resume-vs-fresh via accumulated state, plus a red/green test, and update
ADR-0032/spec-003 and the XML remarks.
* .NET: Serialize HostedWorkflowState turns through a workflow lock
A single workflow instance backs the holder and workflow instances do not
support concurrent runs (the runner throws "already owned by another runner"),
so concurrent turns could fault or race the head cursor. Serialize all turns
through one SemaphoreSlim (mirroring the Python host's workflow lock) and make
HostedWorkflowState IDisposable to own it. Add a gated workflow and a
deterministic concurrency red/green test.
* .NET: Cover non-chat resume and multi-turn checkpoint advance
Add tests for HostedWorkflowState resuming a non-chat-protocol workflow (no
TurnToken) and for a third turn continuing to advance the head checkpoint,
closing the coverage gaps the parity review flagged.
* .NET: Add streaming workflow resume path and stream the workflow sample
Add HostedWorkflowState.RunOrResumeStreamingAsync, which yields the turn's
WorkflowEvents as they occur (fresh run or checkpoint resume) under the same
serialization lock and records the head checkpoint after the stream drains,
keeping the blocking and streaming workflow paths in lockstep with the Python
host. Honor stream:true in the HostingResponsesWorkflow sample by projecting
AgentResponseUpdateEvent updates over the Responses SSE wire. Add a streaming
resume test and update the README/spec.
* .NET: Cover Responses input adaptation to a typed workflow start executor
Demonstrate that HostedWorkflowState's generic RunOrResumeAsync<TInput> is the
input-adaptation seam (parity with Python's ResponsesChannel run hook): the app
adapts the Responses input into the workflow start executor's own type at the
call site. Add a typed-brief workflow and a test, and note the seam in spec-003.
* .NET: Drain workflow resume non-blocking to prevent hang and truncation
The resume drain used a SuperStepCompletedEvent{HasPendingRequests} proxy over
the blocking public WatchStreamAsync. That proxy (a) truncated a resumed turn
when a superstep both emitted a request and queued downstream work, and (b)
could fail to fire at all — re-introducing the indefinite hang — when a resume
input drove no superstep (e.g. a rejected non-chat input).
Make StreamingRun.WatchStreamAsync(bool blockOnPendingRequest, CancellationToken)
public and drain both the blocking and streaming resume paths with
blockOnPendingRequest:false, exactly matching the first-turn RunAsync semantics
(Run.RunToNextHaltAsync). Add guard tests: resume with a rejected input does not
hang, and a resume superstep with a request plus downstream work is not
truncated (verified red against the old proxy).
* .NET: Return file-store checkpoint index in commit order
CheckpointManager.GetLatestCheckpointAsync takes the last entry of a store's
index as the head checkpoint. FileSystemJsonCheckpointStore backed its index
with a HashSet, whose enumeration order is not contractual: after a rollback
frees and reuses a slot, enumeration can diverge from commit order, so the
durable read-through could resume a stale checkpoint. Mirror the HashSet with an
insertion-ordered list and enumerate it from RetrieveIndexAsync so 'latest' is
reliable. Add a CheckpointManager.GetLatestCheckpointAsync contract test over the
file store.
Note: the HashSet disorder is only reachable via the internal rollback path, so
the test locks the ordering contract rather than reproducing the rare disorder.
* .NET: Advance cursor when a streaming resume is abandoned
RunOrResumeStreamingAsync recorded the head checkpoint only after the stream was
fully enumerated. If an SSE consumer disconnected mid-turn after supersteps had
committed, the in-memory cursor kept the previous turn's head; because the next
turn is then a cursor hit, durable read-through could not self-heal, so it
resumed pre-disconnect state. Record the run's last committed checkpoint in a
finally so an abandoned stream still advances the cursor. Add a red/green test.
* .NET: Stream only the final agent's updates in the workflow sample
ExtractUpdates streamed every agent's updates, so the sequential Writer->Reviewer
sample streamed the intermediate draft and the final answer over SSE, differing
from the non-streaming response (final message only). Filter the streamed updates
to the final agent so streaming and non-streaming produce the same response.
Live-verified against Foundry: one output item streamed instead of two.
* .NET: Isolate the holder lock in the concurrency test
The concurrency test asserted the second same-session turn did not enter the
workflow, which also passes via the engine's concurrent-run ownership guard
(which faults) rather than the holder lock (which waits). Assert instead that the
second turn is not completed while the first holds the lock: a fault would
complete the task, so a pending task isolates the holder lock from the engine
guard. Verified red with the lock removed.
* Fix IDE1006 naming in tests; address review feedback and add hosting/live tests
* Document commit-order contract for ICheckpointStore.RetrieveIndexAsync
* Restructure hosting samples under af-hosting with client/server split matching Python parity
* Clarify hosting sample README wording and drop Python comparisons
* Make AgentSessionStore.DeleteSessionAsync abstract and rename session id parameter to sessionStoreId
* Rename OpenAIResponses id helpers and parse the request once for id extraction
* Reclaim per-session locks in HostedAgentState and demonstrate session locking in the agent sample
* Internalize per-session locking in HostedAgentState (automatic, on by default) and remove mirroring-Python wording from code and spec
* Remove HostedAgentState; app-owned routes use AgentSessionStore directly
HostedAgentState only bundled an AIAgent with an AgentSessionStore and, after
the per-session lock was removed, its GetOrCreateSessionAsync/SaveSessionAsync/
DeleteSessionAsync were pass-throughs that just bound the agent argument.
Create-on-miss already lives in the store (unlike Python, whose get/set-only
SessionStore justifies its AgentState holder), so the type earned its place
only via the lock.
Each AgentSessionStore.GetSessionAsync now returns an independent session
instance per call, so concurrent gets fork the same stored state (e.g.
branching from previous_response_id or managing several conversation ids)
without sharing an instance. The store does no cross-call locking; serializing
concurrent runs against the same id is the application's concern.
- Delete HostedAgentState and its unit tests.
- Rewire the local_responses sample and the OpenAI hosting unit/integration
tests to call AgentSessionStore (GetSessionAsync/SaveSessionAsync) directly.
- Update ADR-0032, spec-003, and the af-hosting sample READMEs.
* Isolate hosted session snapshots and distinguish conversation vs response continuation
Mirrors the Python hosted-session isolation work: a hosted session read must be
an independent copy, and the app-owned route must persist under the right
continuation key depending on how the caller continued the thread.
- AgentSessionStore.GetSessionAsync: document the isolation invariant (each
call returns an independent AgentSession so concurrent branches from one
previous_response_id do not observe each other's mutations or alter stored
state); fix the stale "or null if not found" wording (in-box stores return a
fresh created session on miss). The in-box stores already satisfy this via a
serialize/deserialize snapshot round-trip.
- local_responses sample + hosting unit-test route: choose the save key by
channel. A stable conversation id is a mutable head (write back under the
same id; app owns single-writer coordination). A previous_response_id
continuation or first turn is an immutable snapshot (save under the new
response id so branches from the same prior response stay independent).
- Add regression tests: independent get returns a distinct instance
(InMemoryAgentSessionStore); previous_response_id supports independent
branches ([1,2,2,3,3]); conversation id advances the mutable head ([1,2]).
- Update the sample README and ADR-0032 wording.
* Add workflow-factory support to HostedWorkflowState for concurrent sessions
HostedWorkflowState backed every session with one shared Workflow instance and
serialized all turns through a lock, so independent sessions could not run
concurrently. Add a workflow-factory constructor and remove the run lock.
- New constructor HostedWorkflowState(Func<CancellationToken, ValueTask<Workflow>>
workflowFactory, ..., bool cacheWorkflow = false):
- cacheWorkflow: false (default) builds a fresh instance per run, so independent
sessions run in parallel. A resume rehydrates a fresh instance from the
session's checkpoint in the shared store.
- cacheWorkflow: true builds the workflow once, lazily on first use, and reuses
it (a deferred, cached target that, like a shared instance, cannot run
concurrent turns).
- Remove the internal SemaphoreSlim run lock and IDisposable; the instance
constructor is unchanged in behaviour (one shared instance still cannot run
concurrent turns). Turns are no longer serialized by the holder; a single
writer per session is the application's responsibility.
- Switch the local_responses_workflow sample to the factory constructor with an
explicit cacheWorkflow: false, and document the option.
- Add tests: parallel independent sessions (factory), fresh-instance resume,
cached factory builds once and reuses, uncached factory builds per run.
- Update ADR-0032, spec-003, and the sample README.
* Clarify in ADR-0032 how .NET covers AgentState factory and async-setup via DI
* Rebuild cached workflow after a faulted build and add checkpoint index dedup tests
* Python: preserve Gemini 3 thought_signature across function-call replays
Gemini 3 requires the opaque thought_signature attached to each functionCall
part to be echoed back on every replay of that call, or the request is rejected
with 400 INVALID_ARGUMENT. The signature previously survived only via
raw_representation, so any layer that reconstructs a FunctionCallContent (e.g.
harness tool approval) dropped it and broke the next step of the tool loop.
Capture the signature into additional_properties on parse and replay it when
building the Gemini Part, independent of raw_representation.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 33233834-bc6e-4ad2-a6f3-6f1d6e57b1d2
* Store Gemini thought_signature as base64 for JSON-safe persistence
Content.additional_properties is serialized via json.dumps(message.to_dict())
by history providers (e.g. RedisHistoryProvider), which fails on raw bytes.
Store the thought_signature as a base64 string on parse and decode it back to
bytes when building the Gemini Part. Also narrow call_id/name in the round-trip
test to satisfy the type checkers.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 33233834-bc6e-4ad2-a6f3-6f1d6e57b1d2
* Harden Gemini thought_signature decode against corrupted history
Guard the untyped additional_properties value with an isinstance(str) check and
decode with validate=True, degrading gracefully (warn + drop the signature) on
malformed data instead of raising binascii.Error mid tool loop. Matches the
defensive base64 handling already used for data URIs in this file.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 33233834-bc6e-4ad2-a6f3-6f1d6e57b1d2
* Carry Gemini thought_signature on reasoning content via protected_data
Represent the signature as a text_reasoning content's protected_data (base64)
immediately preceding the function call, instead of a bespoke additional_properties
key. This uses the framework's first-class opaque-signature field (as Anthropic
does), survives streaming accumulation, and stays intact when the harness
reconstructs the function call. Replay correlates the signature by adjacency.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 33233834-bc6e-4ad2-a6f3-6f1d6e57b1d2
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Python: make FoundryToolbox.as_skills_provider() disable_caching effective
as_skills_provider() forwarded disable_caching to SkillsProvider, which
ignores it for a caller-supplied SkillsSource, so it was a no-op and the
toolbox re-read skill://index.json on every agent run.
Compose caching in as_skills_provider() instead: wrap the context-independent
_FoundryToolboxSkillsSource in DeduplicatingSkillsSource(CachingSkillsSource(...)).
Add a cache_refresh_interval param, fix the docstring, and add tests covering
cached, disabled, and refresh-interval behavior.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 84150ec4-6f7c-4ef8-b9fb-12fa11652773
* Clarify caller-invariant skill-set wording in as_skills_provider docs
Emphasize that the toolbox advertises the same skill set to every caller (the
per-request call-id governs execution/authorization, not which skills are
listed) rather than leaning on 'ignores SkillsSourceContext'.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 84150ec4-6f7c-4ef8-b9fb-12fa11652773
* Make MCP skills reconnect-safe via session_provider
Cached MCPSkill objects captured the MCP ClientSession at construction, so
after a FoundryToolbox reconnect (which replaces its session) load_skill and
read_skill_resource would fail against the closed session. This regressed once
as_skills_provider() started caching discovery by default.
Add an optional session_provider callable to MCPSkillsSource and MCPSkill
(exactly one of client or session_provider). When supplied, the session is
resolved on every fetch, mirroring how MCPTool resolves self.session live at
call time. _FoundryToolboxSkillsSource now passes a provider that returns the
toolbox's current session, so cached skills always use the live session.
The fixed client= path is unchanged and backward-compatible. Update core tests,
foundry_hosting tests, and core AGENTS.md.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 84150ec4-6f7c-4ef8-b9fb-12fa11652773
* Fix ty error: type captured session_provider as Callable in test
ty could not call the provider narrowed from \object\ (Top callable). Type the
captured value as Callable[[], object] and drop the redundant callable() assert.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 84150ec4-6f7c-4ef8-b9fb-12fa11652773
* Simplify _resolve_mcp_session_provider per review
Address review feedback: replace the dense (client is None) == (session_provider
is None) guard with explicit branches, and drop the cast by binding the narrowed
client to a typed local. Keeps strict 'exactly one' semantics (raises on both and
on neither), matching the codebase convention (e.g. security.py mcp_tool/url).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 84150ec4-6f7c-4ef8-b9fb-12fa11652773
* Add PR #7135 entries to the 1.12.0 changelog
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 84150ec4-6f7c-4ef8-b9fb-12fa11652773
* Drop redundant @pytest.mark.asyncio from MCP skills tests
asyncio_mode is 'auto', so the marker is unnecessary. Remove it from the whole
file for consistency with the async-by-default convention. Per review feedback.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 84150ec4-6f7c-4ef8-b9fb-12fa11652773
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Bump Python package versions for 1.12.0 release
Bump packages represented in the 1.12.0 changelog, promote Foundry Hosting, Azure Content Understanding, Gemini, Mistral, Monty, and Tools to beta, and apply the requested beta cohort date stamp. Root and core move to 1.12.0, released and RC packages use their selected increments, alpha packages including Hosting MCP use the 260721 stamp, and core floors are raised only for proven consumers.
Copilot-Session: 2dd9980a-b869-4c16-8642-75b7a6d6ebdf
* fix version in readme
* Add Responses conversation ID changes to release notes
Include the breaking Hosting Responses conversation ID helper changes from #7234 in the Python 1.12.0 changelog.
Copilot-Session: 2dd9980a-b869-4c16-8642-75b7a6d6ebdf
* 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>
* .NET: Bind tool-approval responses to surfaced approval requests
Harden the tool-approval flow so an approved tool call always matches the
request the framework surfaced for approval.
Add ApprovalResponseBindingChatClient as the outermost decorator above
FunctionInvokingChatClient. It records each model-originated
ToolApprovalRequestContent in the session state and, on the next request,
binds every ToolApprovalResponseContent to its recorded request: the
response tool call is rebound to the recorded call, matched entries are
consumed for one-time use, and only approvals tied to a framework-issued
request take effect.
Apply the same binding in the ToolApprovalAgent harness by tracking the
requests it surfaces and binding collected responses to them during a
queue cycle.
Add ChatClientAgentOptions.DisableApprovalResponseBinding (default off) and
a UseApprovalResponseBinding builder extension for custom chat client stacks.
Includes unit tests for the decorator and the harness.
* .NET: Bind approval responses once per turn and avoid re-enumeration
Address review feedback on the approval-response binding decorator:
consume a matched request from the per-turn lookup so a duplicate response
with the same request id in one turn is honored only once, and return the
materialized message list instead of the original enumerable so a single-use
sequence is not enumerated twice. Rename the local pending list to
pendingRequests for clarity. Adds a duplicate-response regression test.
* .NET: Snapshot recorded approval requests and consume duplicates in the harness
Address review feedback on ToolApprovalAgent:
store a snapshot of each surfaced/pending approval request (cloned tool call
with a copied arguments dictionary) so a later mutation of the caller-visible
instance cannot change the recorded call used to bind the response, and consume
a surfaced request on match so a duplicate response with the same request id in
one pass is honored only once. Apply both symmetrically in the harness and the
ApprovalResponseBindingChatClient decorator. Adds regression tests for the
snapshot and duplicate-response cases.
* .NET: Address review feedback on approval-response binding
- Harness: store surfaced approval requests in a dictionary and consume matches directly, drop the extra hashset and the redundant record-time dedup; replace clear-on-resolution with a debug assert.
- Harness pipeline: add UseApprovalResponseBinding() as the outermost decorator in HarnessAgent (it uses UseProvidedChatClientAsIs) behind a new DisableApprovalResponseBinding option, with tests.
- Decorator: avoid message/content allocations when nothing changes, keep the original content when a response already matches the recorded call, clear pending each inbound turn, and shorten helpers.
* .NET: Compare tool calls by fields instead of serializing
Replace the JSON-serialization comparison in the approval-response binding
decorator with a direct field comparison. Fast-path FunctionCallContent by
comparing CallId, Name, and arguments field by field; any other tool call
shape rebinds. The comparison only skips an allocation (the call is always
rebound to the recorded request otherwise), so a miss just triggers a safe
rebuild. Adds a test that a matching response is forwarded unchanged.
* .NET: Bind approval responses against requests present in history
Fix a merge-queue regression where AG-UI mixed server/client tool invocation
stopped executing the server tool. The binding decorator validated approval
responses only against its own recorded pending state, so a matched approval
request/response pair replayed from conversation history was treated as
unbound and dropped, and the auto-approved server tool never ran.
Treat known requests as the recorded pending state plus any approval requests
already present in the current messages, and stop dropping approval requests
(a request in history is the pairing authority). A response with no known
request anywhere is still dropped, so a forged approval cannot execute.
Also address review feedback: return the mutable contents buffer from a
helper instead of a null-forgiving operator, and use clearer naming
(PrepareMutableContentsBuffer / mutableContentsBuffer). Adds regression tests
for a request in history and a response bound to a history request with empty
pending state.
* Python: fix header_provider headers not reaching streamable HTTP requests
MCPStreamableHTTPTool.call_tool stores header_provider output in a
ContextVar, but the streamable HTTP transport sends requests from tasks
spawned at connect time, whose contexts never observe values set later.
The request hook therefore always read an empty dict on real connections
and the per-call headers (e.g. Authorization) were silently dropped.
Keep the ContextVar for in-context reads and add an instance-level
snapshot of the active call's headers that the request hook falls back
to across tasks.
* Python: serialize header_provider tool calls to prevent cross-call header mixing
Parallel tool invocations run concurrently per function-invocation batch,
so two call_tool invocations on the same MCPStreamableHTTPTool could
overwrite each other's active-header snapshot while requests were still
in flight, attaching the wrong per-call credentials. Hold a per-instance
lock for the duration of a header-bearing call, add a regression test
that fails without the lock, and normalize captured header casing in the
transport-task test.
* .NET: Populate AgentResponse metadata in CopilotStudioAgent
Map CreatedAt, FinishReason, RawRepresentation and AdditionalProperties onto
AgentResponse and AgentResponseUpdate, and map the activity timestamp and
properties onto ChatMessage, so Copilot Studio agents expose the same metadata
surface as other AIAgent implementations. Streaming sets the finish reason on
the terminal update while still emitting already-received content if the source
faults. Add unit tests covering the metadata mapping.
* fix: add Async suffix to async test methods (IDE1006)
* Python: forward GitHubCopilotOptions verbatim to create_session
Refactor the GitHub Copilot agent to forward the full options dict to the
Copilot SDK's create_session/resume_session instead of hand-mapping a fixed
subset. GitHubCopilotOptions stays as the curated, typed surface, but any
other create_session parameter (reasoning_effort, context_tier,
enable_citations, ...) is now passed through verbatim. Unknown keys surface
as TypeError from the SDK instead of being silently dropped.
De-duplicates the near-identical _create_session/_resume_session bodies into
a shared _build_session_kwargs helper.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: f9d016d5-4d8c-43f0-a8fb-f3cf3d1ad7eb
* Python: address review feedback on GHCP options passthrough
- Strip agent-internal/client-level keys (on_pre_tool_use, on_function_approval,
timeout, cli_path, log_level, base_directory) from the forwarded kwargs so they
cannot leak into create_session/resume_session and raise TypeError.
- Source caller tools from the merged options layer so tools supplied via
default_options are honored instead of silently dropped.
- Honor a caller-supplied native 'hooks' dict in _build_session_hooks (composing
with the on_pre_tool_use shortcut) instead of unconditionally overwriting it.
- Validate mock create_session/resume_session calls against the real SDK
signatures in tests so invalid kwargs surface as TypeError, and add regression
tests for the passthrough contract.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: f9d016d5-4d8c-43f0-a8fb-f3cf3d1ad7eb
* Python: avoid redundant re-read of model in _build_session_kwargs
model is popped from default_options into settings at init, so a per-run
model already lands in the merged kwargs. Keep that value when present and
only fall back to the resolved setting otherwise, instead of re-reading opts.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: f9d016d5-4d8c-43f0-a8fb-f3cf3d1ad7eb
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add agent-framework-azure-cosmos-memory context provider (draft)
Introduces CosmosMemoryContextProvider, a ContextProvider that wraps the azure-cosmos-agent-memory toolkit to give agents long-term, Cosmos DB-backed memory (fact/procedural recall + user summaries). Includes package scaffolding, unit tests (mocked client), live Azure integration tests (marked), samples, README, and AGENTS.md.
Draft: uv.lock is intentionally left unchanged. This package depends on azure-cosmos-agent-memory (requires Python >=3.11), which is unsatisfiable against the workspace's current >=3.10 floor, so adding it to the shared lock requires a workspace decision (raise floor to 3.11 or exclude from workspace). Test coverage to be expanded.
* ci: exclude azure-cosmos-memory from uv workspace resolution
The package depends on azure-cosmos-agent-memory which requires Python
>=3.11 and a prompty pre-release (>=2.0.0a9). Both are unsatisfiable
against the workspace's >=3.10 floor and pre-release policy, causing
uv sync to fail in every Python CI job. Exclude the package from the
shared workspace so it is resolved and tested as a standalone package.
* ci: fix code-quality failures for azure-cosmos-memory
- Strip trailing whitespace from package files (pre-commit trailing-whitespace hook)
- Exclude the package README from markdown-code-lint: the package is excluded
from the uv workspace, so its README snippets import a module that is not
installed in the workspace env and Pyright cannot resolve it
* Exclude azure-cosmos-memory README from markdown-code-lint task
* Address PR review comments on cosmos-memory context provider
- Wire credential into Cosmos and AI Foundry clients; let toolkit own
DefaultAzureCredential when none supplied (remove dead import).
- Honor auto_extract=False by zeroing extraction/summary cadence thresholds.
- Skip whitespace-only conversation turns and store stripped content.
- Show confidence 0.0 and coerce confidence to float in _format_memories.
- Register both 'integration' and 'azure' pytest markers accurately.
- Fix duplicated install block in README.
- Update and extend unit tests for new credential wiring and fixes.
* Include azure-cosmos-memory in the uv workspace
Follow the github_copilot pattern for a package with a Python 3.11-only
dependency: lower requires-python to >=3.10 and gate azure-cosmos-agent-memory
behind a python_version >= '3.11' marker. Add a direct, gated prompty
pre-release dependency so the workspace's if-necessary-or-explicit prerelease
policy permits the toolkit's transitive prompty requirement. Guard the test
modules with pytest.importorskip so the 3.10 CI leg skips cleanly. Remove the
workspace exclude and the markdown-code-lint exclude, and regenerate uv.lock.
* Address review feedback on cosmos-memory provider
Rename provider parameters to match Agent Framework conventions:
foundry_endpoint (was ai_foundry_endpoint) and embedding_model/chat_model
(were *_deployment_name). Move DEFAULT_* to module-level constants, type
memory_types as a Literal, use DEFAULT_CONTEXT_PROMPT as the default value,
and add ProcessorConfig/CosmosMemorySettings TypedDicts. Resolve connection
settings via agent_framework load_settings with required-field validation,
replacing the manual getenv/raise blocks. Scope user_id/thread_id to the
provider state and drop the unpreventable first-turn warning.
Rewrite the samples around Agent (not raw SessionContext), provider-scoped
state, and session-id threading; use PEP 723 inline dependencies instead of a
samples dependency group; use a plain input() loop; remove the dead custom
processor stub. Update README/AGENTS for the renamed parameters and env vars.
Add a samples ruff per-file-ignores entry now that the package is linted in CI.
* Add emulator-backed vector search integration test
Bump azure-cosmos-agent-memory to >=0.2.0b2 (adds the embeddings/chat client
injection seam) and add tests/test_emulator.py: an integration (not azure)
suite that exercises real Cosmos vector search with a quantizedFlat index
against a local Cosmos DB emulator, using deterministic in-memory fakes for
embeddings and chat so no Azure AI Foundry account or LLM is required.
To run on a stock emulator the fixture strips the toolkit's full-text index
(the provider only does pure vector search) and requests provisioned autoscale
throughput instead of serverless. The suite skips cleanly when no emulator is
reachable.
* Fix CI typing and package checks for azure-cosmos-memory
The package recently joined the uv workspace, so its source and tests are now covered by the Test Typing Checks and Package Checks gates for the first time.
tests: rename stale constructor kwargs to the current provider API (foundry_endpoint/embedding_model/chat_model); use a typed _STUB_AGENT for the unused agent param so pyright/pyrefly/ty/zuban all accept it; make processor_config values ints; assert non-None memory_client in the emulator tests.
source: relax reportUnknown*/reportOptional* for this package only (the toolkit ships no py.typed; mirrors the hosting-telegram precedent); decouple the conditional toolkit import from the annotation type; use settings.get(); fix memory_types list invariance; drop a redundant None guard; read role via getattr.
* Apply pyupgrade: single-arg AsyncGenerator in test_integration
* Make Cosmos memory extraction drain transparently on provider exit
The provider now drains in-flight background memory extraction in __aexit__, so applications no longer need to call flush() in their own control flow; the client's close() would otherwise cancel pending extraction tasks. flush() is hardened against clients that expose no usable background-task registry.
sample: interactive_chat reads input via asyncio.to_thread so the event loop stays free and background extraction runs during the session; removes the manual flush now that the provider drains on exit.
tests: add explicit transparent-extraction integration tests (emulator: after_run schedules extraction and __aexit__ drains it; live Azure: a fact is extracted and recalled in a later session with no manual flush). Emulator tests reuse a single fixed database to avoid exhausting the emulator's partition budget across runs.
* Add custom extraction-prompt seam and sample to cosmos-memory provider
Adds a prompts_dir option to CosmosMemoryContextProvider that points the Agent Memory Toolkit pipeline at a caller-supplied directory of Prompty templates, so callers can override extract_memories.prompty to control what the extraction LLM produces. The toolkit exposes no public prompts-directory seam, so the provider contains the one internal touch (swapping the pipeline's template loader after the store connects); applies to both provider-built and supplied clients.
sample: interactive_chat_custom_extraction.py - the interactive chat wired with a custom coding-assistant extraction rubric. It derives a complete prompts directory at runtime (copies the bundled templates and augments extract_memories.prompty) so it stays schema-compatible with the installed toolkit.
tests: unit tests assert the provider redirects the pipeline loader only when prompts_dir is set; an emulator integration test proves end to end that a unique marker in a custom extract_memories.prompty reaches the extraction LLM call.
* docs: document prompts_dir custom-extraction seam in cosmos-memory README
Replaces the stale, non-functional CustomMemoryProcessor snippet with the working prompts_dir approach, lists the new interactive_chat_custom_extraction.py sample, and corrects the interactive-sample feature list.
* Address review: rename _new_session, drop defensive toolkit import guard
Sample (comment): rename _new_thread to _new_session in both interactive samples (a new session is the new thread).
Provider (comment): replace the _memory_toolkit_available flag + __init__ ImportError guard with a plain guarded import that re-raises a clear ImportError, matching the github_copilot package's pattern for its 3.11-only SDK. Kept requires-python >=3.10 (bumping this one workspace member to 3.11 would force the entire uv workspace lock floor to 3.11). Tests now run importorskip before importing the package, mirroring github_copilot.
* Pass cadence via cadence_thresholds instead of mutating os.environ
* Mark package alpha and drop private naming in samples
* Require Python 3.11 and inject user summary as untrusted context
* CI: exclude azure-cosmos-memory from uv sync on Python 3.10
* Re-trigger CI (flaky external link check)
* Require chat/embedding models instead of silent defaults
* Fix pyright: narrow resolved chat/embedding models to str
---------
Co-authored-by: Theo van Kraay <thvankra@microsoft.com>
TokenBudgetComposedStrategy estimates tokens by feeding a JSON-serialized
message to the tokenizer, but _serialize_message() used ensure_ascii=True.
That escapes non-ASCII text into \uXXXX sequences, so CJK and other
non-Latin content is token-counted as the escape sequences rather than the
characters the model actually sees, inflating the estimate (~1.6x for mixed
Japanese, more for pure CJK) and skewing compaction/token-budget decisions.
Serialize with ensure_ascii=False, matching the ensure_ascii=False already
used elsewhere in this module. Only affects token estimation; the serialized
string is never stored or transmitted.
Co-authored-by: Eduard van Valkenburg <eavanvalkenburg@users.noreply.github.com>
* Python: preserve explicit null arguments in auto function calling
FunctionTool.invoke dumped validated arguments with model_dump(exclude_none=True),
which strips any argument the model set to null. A required nullable parameter
(e.g. unit: Literal["C","F"] | None) that the model deliberately sets to null was
therefore dropped, and the function failed to invoke on the missing argument.
Use exclude_unset instead: keep the arguments the model actually provided (null
included) and omit only the ones it left out, so the function's own defaults still
apply. Because the input model is generated from the function signature, its field
defaults match the signature defaults, so omitted optionals are unchanged.
Fixes#5934
Signed-off-by: Yufeng He <40085740+he-yufeng@users.noreply.github.com>
* Python: extend null-arg fix to the auto function-calling path
The earlier change fixed FunctionTool.invoke, but _auto_invoke_function
(the path a model-emitted function_call actually takes) still ran
model_dump(exclude_none=True), so an explicit null for a required
nullable argument was still dropped and the call failed with a missing
argument. Switch it to exclude_unset to match invoke, and add a
regression test that drives _auto_invoke_function with an explicit null.
---------
Signed-off-by: Yufeng He <40085740+he-yufeng@users.noreply.github.com>
Co-authored-by: Eduard van Valkenburg <eavanvalkenburg@users.noreply.github.com>
* Refactor MessageMerger to preserve message order
Refactored MessageMerger to delegate update grouping and merging to M.E.AI, preserving the correct order and structure of assistant messages, especially for reasoning content without message IDs. Removed per-message bucketing and CreatedAt-based sorting. Added tests to verify message order and correct merging of reasoning and text updates.
* Set CreatedAt from merged responses preservation of original message timestamps during merging.
* Set merged message CreatedAt to current UTC time
Removed logic for tracking unique creation times and now always assign DateTimeOffset.UtcNow to the merged response's CreatedAt property. This simplifies timestamp handling during message merging.
* Refactor MessageMerger id-less folding logic
Refactored MessageMerger to fold identifierless reasoning segments into the following id'd message at the flattened-message level, ensuring correct merging across response buckets (fixes#6329). Updated ComputeMerged to merge id-less messages with the next message of the same role. Removed redundant per-bucket folding logic. Added unit tests to verify correct folding behavior and role matching.
* Remove unused property
Removed the unused Role property from MessageMergeState for code cleanliness.
* Refactor MessageMerger to iterate backward for merging
Changed MessageMerger to iterate messages in reverse order, ensuring all consecutive messages without IDs preceding a message with an ID are merged correctly. Updated merging logic, index handling, and comments to reflect this new approach.
* Update code comment to better reflect its behavior.
---------
Co-authored-by: Roger Barreto <19890735+rogerbarreto@users.noreply.github.com>
* feat(durabletask): surface HITL respond-URL addressing to workflow executors
Let a workflow notify a human reviewer (e.g. email an approval link) from inside the
graph, without the caller threading the instanceId/requestId by hand.
- durabletask: the orchestrator injects host_context {instance_id, workflow_name,
request_path_prefix} into each activity input; CapturingRunnerContext surfaces it as
host_metadata. No new core API.
- azurefunctions: WorkflowHitlContext.from_context(ctx) builds the canonical
respond/status URLs (returns None in-process so callers degrade gracefully).
Re-exported through the agent_framework.azure lazy namespace.
- Nested sub-workflows: the address context (root instance + workflow name +
accumulated {executor}~{ordinal}~ prefix) propagates down call_sub_orchestrator via a
new SUBWORKFLOW_ADDRESS_KEY marker, so an executor at any depth builds a URL that
targets the addressable top-level instance with a qualified request id. The per-child
ordinal matches the read-side enumerate() index used by the status/respond endpoints.
The marker is stripped from untrusted input alongside SUBWORKFLOW_INPUT_KEY
(confused-deputy / info-leak guard).
- Samples 12 and 13 reworked into the retry-safe two-step notify pattern: the emitter
generates an explicit request id and a downstream NotifyExecutor builds the URL and
notifies, so failed upstream retries never produce a dead link.
Tests: unit coverage for the metadata round-trip, address/ordinal agreement (fan-out at
depth and nested prefix accumulation), marker stripping, and URL building; integration
tests assert the helper-built URL equals the server respondUrl and resumes the run, for
both the flat (12) and nested (13) samples.
* refactor(durabletask): read back request_info id instead of generating one in samples
Add WorkflowHitlContext.pending_request_id(ctx), an async helper that returns the
id request_info just generated (read from the runner context's pending request-info
events). This works on any host via the core RunnerContext protocol method, so it
needs no core change.
Samples 12 and 13 now call request_info() and read the id back to forward to the
NotifyExecutor, instead of minting a uuid by hand and passing request_id=. The
read-back happens in the same activity execution that generated the id, so the
pending request event and the notify message still commit together with the same id
(retry-safe; failed upstream retries notify no one).
* docs(azurefunctions): document request_info id read-back and notify safety
Tighten pending_request_id docstring to require calling it immediately after request_info, and explain why that is safe on the durable host (each executor runs in its own activity with its own runner context, so the pending set only holds this executor's requests and the newest is the one just emitted). Document the two-step notify pattern in the 12 and 13 sample READMEs, including the downstream-notifier retry safety and the nested address-prefix propagation.
* fix(python): resolve ty typing error and address PR review comments
- test_subworkflow_orchestration: replace the mypy-only type:ignore[arg-type] with a cast so the ty checker passes too (the other four checkers already honored the ignore).
- samples 12/13 README: guard the notify snippet against None before build_respond_url to match the documented graceful-degradation behavior.
- integration tests 12/13: reword comments that implied request_info now generates an explicit uuid4; it generates the id internally by default.
* fix(python): honor configurable Functions route prefix and address HITL PR review
- Resolve the route prefix from host.json (extensions.http.routePrefix, default api) in a new azurefunctions _routes module, used by both the server endpoints and WorkflowHitlContext, so a custom or empty routePrefix no longer 404s respond/status URLs (was hardcoded /api/ in four places).
- Extract respond/status URL construction into one shared builder called from _app.py and _hitl_context.py, removing the sync-by-test duplication.
- Broaden loopback detection (localhost, 127.0.0.0/8, 0.0.0.0, ::1, [::1]) via a _is_loopback helper so local links use http.
- Pin the host_context key names as shared constants in durabletask so producer and azurefunctions consumer cannot drift.
- Reword a stale base_url comment to reference WEBSITE_HOSTNAME.
- Add unit tests for the route module and loopback handling.
* refactor(azurefunctions): derive server-side HITL URLs from the request URL
The run and status endpoints now derive the base URL and route prefix from the incoming request URL (the value the host actually routed) via split_request_url, so the caller-visible respond/status URLs no longer depend on reading host.json on the server. The in-workflow helper keeps reading host.json since it has no request context. Replaces strip_route_prefix and updates its tests.
* test(durabletask): enforce sub-workflow ordinal and read-index agreement
Extract the read-side subworkflows grouping into a shared _index_subworkflows helper (used by the orchestrator) and add test_readside_index_matches_dispatch_ordinal, which round-trips a fan-out through that helper and asserts subworkflows[executor][ordinal] resolves to the child the dispatch stamped that ordinal onto. Turns the previously comment-only write-ordinal / read-index invariant into a shared, CI-enforced one.
* fix(azurefunctions): suppress bandit B104 on loopback host set