Commit Graph

24 Commits

Author SHA1 Message Date
MohammadHaroonAbuomar 7302d0bf23 Python: agent-hooks interception contract as a first-class experimental core feature (#7515)
* feat(python): add agent-hooks middleware as experimental core feature

Implement the AGENT-HOOKS-0.1 interception contract as a first-class
experimental feature in agent_framework core.

- Single public factory agent_hooks_middleware() returning a private
  agent/chat/function middleware trio (one object per middleware
  category); partial or stacked installs fail closed with loud errors.
- All eight interception points: input/output at the agent seam,
  pre/post_model_call at the chat seam, pre/post_tool_call at the
  function seam, agent_startup/agent_shutdown bracketing each run.
- Fail-closed enforcement throughout: transforms write back into the
  native contexts (messages, arguments, results) or raise; content is
  preserved as Content objects; MiddlewareTermination short-circuits
  are guarded at every seam; enforcement-layer failures halt the run;
  interceptor crashes surface as host_error denies.
- Streaming is fully buffered per spec buffered_output semantics: no
  update egresses before the post_model_call/output verdicts; a deny
  at pull time releases zero updates; run state stays active across
  lazy pulls with cleanup on every exit path.
- Session scoping: per-run by default (startup/shutdown bracket each
  run) or host-owned via emitter/builder parameters for one session
  spanning multiple runs.
- agent-hooks-sdk is an opt-in agent-hooks extra (not in all),
  lazy-imported per the _mcp.py pattern; core imports cleanly without
  it and the factory raises a clear ModuleNotFoundError.
- ExperimentalFeature.AGENT_HOOKS + @experimental decorator, lazy root
  export, typing surface, PACKAGE_STATUS.md entry.
- 55 tests built on real Agent/mock-client flows covering deny-before-
  execution, transform write-back, rich-content preservation, complete
  streaming ordering, error cleanup, concurrency isolation, nested
  agents, and importability without the optional SDK.

Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>

* style(python): unquote ResponseStream annotation per pyupgrade

The pre-commit pyupgrade hook rewrites the quoted forward reference;
ResponseStream is imported at runtime in this module, so the quotes
were unnecessary.

Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>

* refactor(python): address agent-hooks review feedback

Reworks the agent-hooks feature per PR review:

- Verdicts now precede durability: a run-scoped persistence gate
  (_sessions.py) defers per-service-call history persistence and
  after-run provider work until the covering post_model_call/output
  verdict permits; denied content never persists, transforms persist
  post-write-back. Unhooked runs are unchanged (verified against an
  instrumented baseline).
- ResponseStream.buffered_and_gated: a buffered-gate combinator that
  applies the run's pending stream hooks before the gate, then seals
  the stream, so no middleware can rewrite egress after the output
  verdict. Replaces the hand-rolled replay iterator.
- MiddlewareBundle (public, _middleware.py): the factory returns an
  indivisible bundle categorize_middleware splits, making partial
  installs impossible by construction; members are validated at
  construction. Bare (non-sequence) middleware at agent construction
  is now normalized instead of silently dropped, and unrecognized
  middleware logs a warning instead of vanishing.
- Factory split and rename: create_agent_hooks_middleware (per-run
  sessions) and create_agent_hooks_middleware_from_emitter
  (host-owned); the sentinel parameter-diffing is gone.
- Wire conversions live in per-point codec classes owning to_wire and
  write_back. Fixes in that code: tool-call name transforms apply or
  raise; non-object args transforms raise; argument write-back merges
  only changed keys (original values, including bytes, preserved by
  identity); message-list write-back matches by identity, not index.
- function_approval_request objects on the normal return path pass
  through un-emitted, preserving the human approval pause.
- Hosted (service-executed) tool calls surface in the post_model_call
  content projection; the tool-seam limitation is documented.
- Import probe covers the full SDK surface and re-raises as
  missing-extra only for the agent_hooks module; module logger added;
  _json_safe replaced by make_json_safe (which gained bytes support);
  tools_registered uses normalize_tools; dependency-pyright analyzes
  the module again via the test dependency-group.
- Tests: 75 in the feature suite (persistence gating, stream-hook
  sealing, approval passthrough, codec units, bundle validation,
  bare-bundle installs), full core suite green.

Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>

* refactor(python): second review round for agent-hooks

Addresses the second review round on the agent-hooks feature:

- Nested-run persistence ownership: RawAgent.run stamps a run identity
  over the run's dynamic extent (including streaming pulls and result
  hooks); the persistence gate binds to its owning run via an
  offer/adopt handshake keyed to the agent instance and accepts only
  its owner's persists — nested runs persist inline regardless of how
  they were started (tool calls, middleware, custom run loops). The
  tool-seam suspension remains for custom-loop sub-agents invoked as
  tools; the one residual case (custom loop nested in a custom loop
  off the tool path) is fail-closed and documented. Fixes a latent
  pre-existing re-deferral: flush() now drains with the gate context
  suspended, so a nested hooked run's permitted after-run persistence
  no longer re-defers into an enclosing gate.
- as_tool stream_callback consumes the released (verdicted) stream;
  observers cannot see denied or pre-transform content. Both
  directions are regression-tested.
- categorize_middleware gained supported_categories: a bundle member
  landing in a category a call site cannot install raises; bare
  middleware warns like _add_middleware. Wired at the chat-client
  sites and the provider seam.
- ResponseStream.buffered_and_gated owns the re-derivation rule via a
  rederive callable (gates cannot choose released updates) and is
  marked experimental.
- Wire codecs compare with bool-aware equality (Python == equates
  1 == True, which made bool/number transforms look untouched and get
  dropped) and _ToolResultCodec.write_back owns the untouched-wire
  rule via the before value.
- middleware parameters accept a bare middleware or bundle everywhere
  the runtime does (constructors, run overloads, as_agent, telemetry
  and harness layers, foundry); the bare-source rule has a single
  owner in categorize_middleware; bare middleware assigned to the
  attribute now executes (documented behavior change).
- MiddlewareBundle is experimental and validates members; approval
  passthrough, typing-check fixes (ty ignores mypy-coded ignore
  comments), logging, and documentation updates per review.

Test count: 85 feature tests plus 12 new this round across sessions,
middleware, agents; full core suite green; typing checked under
mypy, pyrefly, ty, zuban, and pyright.

Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>

* docs(python): drop previous-behavior notes from middleware docstrings

Per review: docstrings describe current behavior only. The
bare-middleware behavior change stays recorded in the PR description
and commit history.

Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>

* fix(python): gate ownership survives retrying middleware

A retry or fallback middleware issuing a second call_next() gave the
new attempt a fresh run identity that the persistence gate's
first-bind-wins ownership rejected, so the retried attempt's history
persisted inline before the output verdict — a denied response became
durable again. The gate now accumulates every identity adopted
through its own offer ticket: all attempts' persistence stays behind
the one final verdict (deny drops all of it, allow flushes all of
it). Accumulation over rebind-replace is deliberate: rebinding would
flip an earlier attempt's still-running background work from deferred
to inline, which is the fail-open direction. A foreign agent still
cannot bind: tickets are minted only by the covered pipeline's final
handler and adoption is instance-keyed.

Also consolidates the bare-middleware-source rule into a single
_as_middleware_list owner used by every interpretation site (the
harness merge, BaseAgent.__init__, categorize_middleware, both
client-kwargs merges, get_response, SessionContext.extend_middleware),
including the str/bytes exclusion the stray copies missed. The
constructor now stores a copy of the caller's sequence; assign to the
middleware attribute for post-construction changes.

Retry regression tests cover denied and allowed retried runs in both
stream modes and fail with first-bind-wins restored.

Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>

* fix(python): streaming seam runs pipeline descent inside the gate

The streaming agent seam ran call_next() outside the persistence
gate (only _consume entered it later), so a retry middleware that
drained a successful attempt with get_final_response() and discarded
it persisted that attempt's exchange before any verdict existed; a
later deny dropped only the retry attempt's deferred work. The
descent is now wrapped in the gate exactly like the non-streaming
seam: attempt identities adopted during descent are accepted owners,
so in-pipeline draining defers, deny drops every attempt, and a
middleware that raises after draining strands the pending persists
unexecuted. The bind_owner docstring now states the actual soundness
invariant covering both bind sites: every bind comes from a run
inside the covered pipeline.

New tests cover drained-and-discarded attempts (deny and allow, both
stream modes) and a sub-agent tool inside a drained attempt; the
streaming deny variant fails with the gate wrap reverted.

Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>

* fix(python): flush deferred persistence on streaming no-result termination

With the pipeline descent now running inside the persistence gate, a
middleware that drains a successful attempt and then terminates
without a result left that attempt's deferred persistence stranded:
the streaming no-result termination path raised before any flush, so
history of exchanges that really happened and passed their own
verdicts quietly vanished (streaming only; non-streaming already
flushes before its re-raise). The path now flushes before re-raising
the termination, with a state.halted guard first so an enforcement
failure during the drained attempt still strands pending fail-closed
and surfaces the halt, mirroring the non-streaming ordering exactly.

The regression test covers both seams; the streaming variant fails
without the fix.

Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>

---------

Signed-off-by: MohammadHaroonAbuomar <40180927+MohammadHaroonAbuomar@users.noreply.github.com>
2026-08-07 00:25:09 +00:00
Chris Gillum 43309018be .NET and Python: Extract Durable Task and Azure Functions integrations (#7465)
* Extract Durable Task and Azure Functions integrations

Remove the migrated implementations, samples, tests, documentation, and repository wiring now owned by microsoft/agent-framework-durable-extension. Preserve Python compatibility through the agent_framework.azure shim and agent-framework-core[all], and leave customer-facing redirects to the new repository.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 6181dcf9-857b-43ea-9fd2-fcd6b175ffdd

* Fix feature registry validation after extraction

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 6181dcf9-857b-43ea-9fd2-fcd6b175ffdd

* Narrow external feature package paths

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 6181dcf9-857b-43ea-9fd2-fcd6b175ffdd

---------

Copilot-Session: 6181dcf9-857b-43ea-9fd2-fcd6b175ffdd
2026-08-03 03:02:07 +00:00
Eduard van Valkenburg 28389df805 Python: Move SessionStore to core and persist Foundry Responses sessions (#7306)
* Python: Move session persistence into core

Move SessionStore and durable msgspec-backed storage into core, restore sessions in Foundry Responses hosting with per-user isolation, and document the serialization design.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3e5c81ad-75e8-4e92-a883-8bbd676c6c8c

* Python: Address session persistence review feedback

Harden scoped file paths and corruption recovery, preserve session serialization compatibility, clarify dependency placement, and add reproducible benchmark evidence.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3e5c81ad-75e8-4e92-a883-8bbd676c6c8c

* Python: Preserve session snapshot compatibility

Deep-copy in-memory session writes and retain existing Telegram session keys so stored conversations continue resolving.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3e5c81ad-75e8-4e92-a883-8bbd676c6c8c

* Python: Simplify Foundry session isolation

Add experimental FoundrySessionStore backed by Agent Server request context, remove resolver plumbing, and centralize v2 user isolation for sessions, checkpoints, and approvals.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3e5c81ad-75e8-4e92-a883-8bbd676c6c8c

* Python: Reduce Foundry session helper layering

Inline the single-use request user accessor while keeping separate context validation, fingerprint, and directory helpers for their distinct callers.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3e5c81ad-75e8-4e92-a883-8bbd676c6c8c

* Python: Clarify Foundry request context validation

Separate fail-fast request validation from context retrieval so Responses no longer appears to discard a returned context.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3e5c81ad-75e8-4e92-a883-8bbd676c6c8c

* Python: Share Foundry request context helpers

Move protocol validation and user-scope derivation into a dedicated request-context module, leaving the session-store module focused on storage.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3e5c81ad-75e8-4e92-a883-8bbd676c6c8c

* Restore Foundry checkpoint storage paths

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3e5c81ad-75e8-4e92-a883-8bbd676c6c8c

* Simplify Foundry session storage paths

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3e5c81ad-75e8-4e92-a883-8bbd676c6c8c

* Persist Foundry sessions under hosted home

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3e5c81ad-75e8-4e92-a883-8bbd676c6c8c

* Make hosted path test platform independent

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3e5c81ad-75e8-4e92-a883-8bbd676c6c8c

* Address session persistence review feedback

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3e5c81ad-75e8-4e92-a883-8bbd676c6c8c

* Isolate Foundry session path handling

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3e5c81ad-75e8-4e92-a883-8bbd676c6c8c

* Clarify Foundry session path terminology

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3e5c81ad-75e8-4e92-a883-8bbd676c6c8c

* Align Foundry sessions with Responses continuity

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3e5c81ad-75e8-4e92-a883-8bbd676c6c8c

* Finalize Foundry Responses session persistence

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3e5c81ad-75e8-4e92-a883-8bbd676c6c8c

* Add session store feature usage telemetry

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3e5c81ad-75e8-4e92-a883-8bbd676c6c8c

* Fix hosted per-call history persistence

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3e5c81ad-75e8-4e92-a883-8bbd676c6c8c

---------

Copilot-Session: 3e5c81ad-75e8-4e92-a883-8bbd676c6c8c
2026-07-30 13:04:08 +00:00
Giles Odigwe 040e2705aa Promote agent-framework-github-copilot to 1.0.0 (released) (#7302)
Promote the GitHub Copilot package from release candidate (1.0.0rc4) to released (1.0.0): bump the version, switch the classifier to Production/Stable, update PACKAGE_STATUS.md, and drop the --pre install flag from the package and sample READMEs. Add a github-copilot-1.0.0 CHANGELOG section covering the promotion and the input-attachment forwarding shipped in this release. No core/root bump: this is a standalone package promotion and the core[all] extra references the package without a version pin.

Copilot-Session: f523064c-60b4-4d18-bf95-c16c5fda9126
2026-07-23 21:06:31 +00:00
Evan Mattson 711d6f24ae Bump Python package versions for 1.12.1 release (#7273)
Bump root and core to 1.12.1, OpenAI to 1.11.0 for new public prompt-cache options, Foundry to 1.10.3, and Gemini and Foundry Hosting to beta 260722 based on CHANGELOG entries. Promote AG-UI from 1.0.0rc9 to stable 1.0.0. No beta cohort bump was applied, and core floors remain unchanged under the strict affected-dependency policy because the connectors do not require a new core API.
2026-07-23 13:29:20 +09:00
Eduard van Valkenburg d08200d00e Python: Bump package versions for 1.12.0 release (#7238)
* 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
2026-07-21 15:45:00 +00:00
Eduard van Valkenburg a1f3e536bc Python: Add MCP hosting helpers (#7209)
* Python: Add MCP hosting helpers

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 5d6987cd-1b67-4ba1-8b54-3c50da6e7607

* Python: Address MCP hosting review comments

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 5d6987cd-1b67-4ba1-8b54-3c50da6e7607

* renamed to AgentMCPTool
2026-07-21 14:46:46 +00:00
Eduard van Valkenburg bc59c72170 Python: Add A2A hosting helpers (#7050)
* Python: Add A2A hosting helpers

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 5d6987cd-1b67-4ba1-8b54-3c50da6e7607

* Python: Preserve final A2A streaming output

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 5d6987cd-1b67-4ba1-8b54-3c50da6e7607

* Python: Clarify A2A conversion boundary

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 5d6987cd-1b67-4ba1-8b54-3c50da6e7607

* Python: Document A2A sample auth boundary

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 5d6987cd-1b67-4ba1-8b54-3c50da6e7607

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-07-17 16:58:39 +00:00
Eduard van Valkenburg 7ca8bb55b6 Python: Add Telegram hosting helpers and samples (#7047)
* Python: Add Telegram hosting helpers

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 5d6987cd-1b67-4ba1-8b54-3c50da6e7607

* Python: Exclude Telegram samples from aggregate typing

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 5d6987cd-1b67-4ba1-8b54-3c50da6e7607

* Python: Address Telegram helper review feedback

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 5d6987cd-1b67-4ba1-8b54-3c50da6e7607

* Python: Serialize Telegram webhook sessions

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: d76a9c32-d170-426d-a64f-b70958b08b12
2026-07-14 07:40:12 +00:00
Peter Ibekwe 4bac2c2c05 Python: Promote python declarative workflows to stable version (#7065)
* Promote python declarative workflows to stable version

* Updated changelog with PR detail.

* Updated to address pr comments.

* Remove changelog update
2026-07-13 22:30:21 +00:00
Eduard van Valkenburg 1aca7601b8 Python: Add hosting protocol helper surface (#6891)
* Add Python hosting protocol helper surface

Introduce AgentFrameworkState and SessionStore for app-owned hosting routes, add Responses run conversion/rendering helpers, and update the local Responses sample to use native FastAPI routing with streaming support.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix CI failures, session continuity, and streaming model reporting

- Fix constrained TargetT TypeVar in AgentFrameworkState: split __init__
  into per-shape overloads (instance/sync factory/async factory/awaitable)
  since a bound TypeVar combined with one big Callable/Awaitable union
  parameter was unsolvable across pyright/pyrefly/ty/zuban.
- Fix _FakeAgent test fixtures to structurally satisfy SupportsAgentRun
  (matching attribute types and overloaded run()), which the above surfaced.
- Add SessionStore.put() to alias an additional session id to an
  already-resolved session, and use it in the local_responses sample to fix
  a real session-continuity bug: previous_response_id rotates every turn,
  so without aliasing the newly minted response id, turn 3+ of a
  conversation silently lost all prior history. Verified against a live
  Foundry model across a 3-turn conversation.
- Fix responses_stream_events_from_run to report the real model instead of
  the "agent" fallback: AgentResponse.from_updates never carries a raw
  representation forward, so capture model from the individual streamed
  updates' raw representations instead. Verified live.
- Add response_model=None to the sample's FastAPI route (it could not boot
  at all: FastAPI tried to build a Pydantic response model from the
  JSONResponse | StreamingResponse return annotation).
- Map responses_to_run's ValueError to HTTP 400 instead of a 500.
- Add HTTP round-trip integration tests (packages/hosting-responses) that
  exercise the same FastAPI + AgentFrameworkState + Responses helper wiring
  as the sample via httpx.ASGITransport, including a regression test for
  the session-continuity fix.
- Add Workflow-target test coverage, SessionStore.put/reset_session tests,
  and TypeError-path coverage to packages/hosting/tests/hosting/test_state.py.
- Extend call_server.py / call_server_af.py to a third conversation turn so
  they actually exercise the continuity chain (previous scripts stopped at
  turn 2, which would never have revealed the bug above).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Simplify session-continuity aliasing: fold put() into get()

Per feedback: the growth of SessionStore was not the problem -- it's
intentional, since OpenAI's previous_response_id is designed to let a
caller continue (fork) from any earlier response, not just the latest
one, so every response id has to stay independently resolvable. That
part stays as-is.

What was too complex was the call site: routes had to manually fetch a
session and then conditionally alias it with a separate put() call.
Folded that into a single get(session_id, alias=...) call instead:

- SessionStore.get() gains an optional `alias` keyword that registers an
  additional id for the same session in the same call (no-op if alias is
  None or equal to session_id). Removed the separate put() method.
- AgentFrameworkState.get_session() passes `alias` through.
- local_responses sample and the HTTP round-trip integration tests now
  do `await state.get_session(lookup_id, alias=response_id)` instead of
  pulling the store out and orchestrating get()/put() by hand.
- Documented that this in-memory SessionStore intentionally never evicts
  (by design, to support forking), and that a storage-backed replacement
  (Redis, a database, ...) is responsible for its own TTL/eviction
  policy.

Verified against a live Foundry model across a 3-turn previous_response_id
chain after the simplification.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Refine hosting state helpers

Split the shared state surface into AgentState and WorkflowState, keep SessionStore and CheckpointStore as plain storage, and make state helpers responsible for get-or-create behavior. Update the Responses sample and HTTP round-trip tests to store the post-run session explicitly under the minted response id, and support WorkflowBuilder/orchestration-style builders via structural build() support.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix hosting state test protocol fakes

Widen fake agents' get_session service_session_id parameter to match the SupportsAgentRun protocol under the Python 3.11 test typing checkers.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Simplify Responses stream helper naming

Rename responses_stream_events_from_run to responses_stream_from_run across exports, tests, docs, and the local Responses sample to align with the generic <protocol>_stream_from_run helper convention.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Add state-level storage setters

Add AgentState.set_session and WorkflowState.set_checkpoint_storage so app code can pair get-or-create helpers with explicit post-run storage without reaching into the underlying stores. Update Responses docs, tests, and sample to use state.set_session.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Simplify WorkflowState checkpoint handling

Remove CheckpointStore from WorkflowState so workflow checkpointing uses the existing CheckpointStorage abstraction directly. Keep WorkflowState focused on resolving workflow targets, including builders, and update hosting docs/tests accordingly.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Rename Responses streaming run helper

Rename responses_stream_from_run to responses_from_streaming_run across the hosting-responses exports, tests, docs, and local Responses sample.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Align Python hosting spec with protocol helpers

Rewrite SPEC-002 to match the accepted helper-first hosting ADR and the implementation PR: AgentState, WorkflowState, SessionStore, Responses helpers, app-owned security/state responsibilities, and the minimal FastAPI Responses shape.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Remove old Python hosting channel implementation

Remove the unreleased AgentFrameworkHost/channel implementation, the old hosting-telegram package, and old host/channel samples. Keep agent-framework-hosting focused on AgentState, WorkflowState, and SessionStore, and keep hosting-responses focused on helper-first Responses conversion. Update SPEC-002 to match the accepted helper-first ADR and the implementation surface.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Restore helper-first workflow sample

Rebuild the local Responses workflow sample on the protocol-helper surface, add production-readiness cautions to the local hosting samples, and align file-backed workflow checkpoint/cursor storage under one sample storage root.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Address hosting helper review feedback

Handle streaming failures as terminal Responses SSE events, guard concurrent target/session initialization, and scope workflow sample checkpoint storage per continuation.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Clarify Responses sample continuation behavior

Document unknown conversation_id behavior in the agent sample and make the workflow sample explicitly reject conversation_id while continuing to use responses_session_id for previous_response_id.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Clarify Responses sample option policy

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-07-09 07:51:47 +00:00
Eduard van Valkenburg d75f2286f4 Python: Add Telegram channel for agent-framework-hosting (#6698)
* Python: Add Telegram channel for agent-framework-hosting

- Add agent-framework-hosting-telegram package with TelegramChannel
  supporting polling and webhook transports, streaming edits with
  Telegram Bot API rate limiting, per-chat serial workers, and
  multi-modal inbound/outbound (text, photo, document, voice)
- Add local_telegram sample demonstrating multi-channel hosting with
  a TelegramChannel alongside ResponsesChannel, using per-chat
  FileHistoryProvider and a run_hook for Telegram persona temperature
- Fix test layout: move tests to tests/hosting_telegram/ (no __init__.py)
- Remove old [tool.mypy] section and mypy poe task; source type-checking
  is handled by pyright via shared_tasks
- Update uv.lock, pyproject.toml workspace sources, and PACKAGE_STATUS.md

Fixes #6588
Refs #6265

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Python: Address Telegram channel CI failures and review feedback

- Fix webhook secret validation to use constant-time compare_digest
- Harden webhook update parsing: require integer chat IDs and guard slash-only commands
- Fix streaming edge cases in TelegramChannel:
  - prevent edit worker deadlocks when text exceeds 4096 chars
  - prevent deadlock when placeholder send fails (message_id stays None)
  - enforce edit throttling with minimum interval sleep
  - honor send_typing_action=False in streaming mode
  - always forward final multimodal output (e.g. images), while avoiding duplicate text sends
- Expand Telegram tests for slash-only command handling, non-int chat IDs, and streaming behavior (long text, final images, typing toggle)
- Fix sample/docs feedback:
  - rename sample package to agent-framework-hosting-sample-local-telegram
  - switch sample uv.sources from feature branch to main
  - align docs/tool names with lookup_weather
  - fix broken links and server run instructions in README/call_server.py
  - align local_telegram app docstrings with reasoning hook behavior and strip model in responses_hook

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Python: Fix TelegramChannel streaming to iterate contents for multimodal support

- Remove stale PR reference from module docstring
- Add Google-style docstring to TelegramChannel.__init__ documenting all keyword args
- Fix _stream_to_chat to iterate update.contents instead of using
  getattr(update, 'text', None); text chunks are extracted from Content
  items with type='text', non-text content in updates is correctly
  ignored (images etc. are forwarded via the final response)
- Update _FakeStreamUpdate test helper to use contents list matching the
  real AgentResponseUpdate API; add from_text/from_image class methods
- Update _FakeResponseStream to accept _FakeStreamUpdate objects directly
- Add test verifying multimodal stream updates don't corrupt text accumulator

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Python: Split local_telegram into simple Telegram-only and new multi-channel sample

local_telegram is now a focused Telegram-only sample:
- Removes ResponsesChannel and all responses_hook code
- Removes call_server.py (no HTTP endpoint to call)
- Uses a deterministic lookup_weather tool (hash-based, not random)
- Single run_hook that strips model and raises reasoning effort
- Drops agent-framework-hosting-responses dependency

New local_multi_channel sample shows running both channels at once:
- ResponsesChannel + TelegramChannel sharing a FileHistoryProvider
- Cross-channel session resumption via previous_response_id
- call_server.py moved here (the Responses endpoint lives here now)
- Demonstrates the multi-channel coordination story

Update README table to list both samples with clear descriptions.

Also delete personal_assistant/.venv which was not tracked but caused
pyright to crawl the entire installed venv (thousands of files),
making sample pyright checks hang indefinitely.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Python: Fallback when Telegram final edit fails

- only mark final edit as sent after a confirmed 2xx edit response
- fall back to sendMessage when final edit returns a non-success status
- add regression test covering failed final edit fallback behavior

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Python: Fix optional await_args typing in telegram test

- assert await_args is not None before reading kwargs in streaming fallback test
- resolves test-typing failures across mypy/pyright/ty/zuban for hosting-telegram

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-25 11:40:48 +00:00
Eduard van Valkenburg f2d02e58b3 Python: Add hosting core and Responses channel (#6580)
* Add Python hosting core and Responses channel

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Address hosting core review feedback

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Adopt source pyright typing setup for hosting packages

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Cover ResponsesChannel custom path routing

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Align hosting tests with package layout

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix hosting workflow fixture imports in aggregate tests

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Apply useful Responses channel hardening

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix hosting package typing checks

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix hosting pyright under Python 3.11

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Avoid static diskcache dependency in hosting

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix aggregate typing and Docker test resilience

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Simplify local Responses workflow sample

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Clarify generic hosting is not Foundry hosting

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Revert "Clarify generic hosting is not Foundry hosting"

This reverts commit 73b584d919053bed43a258d75dc2b76406e9c181.

* Clarify isolation key source flexibility

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Clarify isolation header reuse boundary

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Support multimodal Responses channel outputs

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Preserve multimodal streaming Responses output

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Stream Responses output items from updates

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Improve Responses streaming output handling

* Tighten Responses channel default option handling

- Restore full option parsing in parse_responses_request: known fields
  are remapped (max_output_tokens→max_tokens, parallel_tool_calls→
  allow_multiple_tool_calls), transport/session keys excluded, None
  values dropped, everything else forwarded as-is so run_hook can
  inspect the full set.
- Add a default _strip_options_hook on ResponsesChannel that removes
  all parsed options before reaching the agent. Callers cannot inject
  generation params (temperature, instructions, tools, …) unless the
  host explicitly allows it.
- A custom run_hook replaces the default entirely and receives the
  full ChannelRequest.options plus the raw protocol_request.
- Update tests to cover remap, default-strip, and custom-hook paths.
- Clarify host debug-log docstring to match new option flow.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-23 20:56:46 +00:00
Evan Mattson b55992bb67 Bump Python package versions for 1.9.0 release (#6583)
Selective, CHANGELOG-driven version bumps for the 2026-06-18 release.

Released tier: agent-framework-core and the root agent-framework go to 1.9.0
(minor). Core ships new public APIs (agent-loop middleware, tool-approval
middleware and harness integration, shell-tool harness integration, AG-UI
thread snapshot persistence, context-provider telemetry) plus two behavioral
breaking changes on evolving surfaces: MCP sampling now denies server-initiated
requests by default, and the FileAccess tools were aligned with the .NET
implementation. These are treated as within-1.x changes because every package
caps core at <2; a major bump would require rewriting those caps. The foundry
and openai packages go to 1.8.2 (patch, bug fixes only). The root
agent-framework-core[all] pin was moved to 1.9.0 in lockstep with core.

Release-candidate tier: ag-ui to 1.0.0rc5 and declarative to 1.0.0rc2 for their
respective changes. orchestrations is promoted to stable 1.0.0; PACKAGE_STATUS
and the README install hint were updated accordingly.

Prerelease tier (new Pacific date stamp 260618): anthropic (beta),
azure-contentunderstanding (alpha) and foundry-hosting (alpha). No beta cohort
bump was applied; only packages with changes this cycle were stamped.

Dependency floors: following the established convention, the core floor was
raised to >=1.9.0 on every non-core package bumped this cycle, preserving the
existing <2 upper bound.

Also resolves two pre-existing failures in the dependency-bounds validator that
are unrelated to the version bumps. Hosted-environment detection now catches a
bare ImportError so optional Foundry hosting probing cannot crash user-agent
setup. The harness shell-tool integration, which lazily imports the separate
agent-framework-tools package to avoid a circular runtime dependency, is now
type-checked and tested in isolated environments via a core dev
dependency-group, with the shell-tool tests guarded to skip when that package
is absent.
2026-06-18 18:01:17 +09:00
Giles Odigwe 01fc518b29 Python: bump package versions for 1.8.0 release (#6351)
- Released cohort (core, openai, foundry, root): 1.7.0 -> 1.8.0
- agent-framework-github-copilot: promote to RC (1.0.0rc1)
- agent-framework-orchestrations: rc2 -> rc3 (bug fix)
- Beta/alpha packages with changes: a2a, anthropic, azurefunctions, bedrock,
  foundry-hosting, mistral bumped to new date stamp (260604)
- Inter-package dependency bounds updated for changed packages
- CHANGELOG.md and PACKAGE_STATUS.md updated

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-04 23:03:24 +00:00
Peter Ibekwe 6086a74302 Python: Promote agent-framework-declarative package to RC (#6256)
* Promote agent-framework-declarative package to RC

* Update missed package status file.
2026-06-02 19:30:05 +00:00
Daria Korenieva d2d5384f28 Python: Add Mistral AI embedding client package (#5480)
* Python: Add Mistral AI embedding client package

Signed-off-by: Daria Korenieva <daric2612@gmail.com>

* Address review feedback: fix dimensions check, sort embeddings by index, align docs

Signed-off-by: Daria Korenieva <daric2612@gmail.com>

* Address review feedback: downgrade to alpha, remove integration tests - Change version to 1.0.0a260505 (alpha) - Update classifier to Development Status :: 3 - Alpha - Update PACKAGE_STATUS.md to alpha - Remove Mistral from integration test workflows (no API keys yet)

Signed-off-by: Daria Korenieva <daric2612@gmail.com>

* Add samples directory for alpha package compliance Per python-package-management skill: alpha packages must include samples inside the package directory.

Signed-off-by: Daria Korenieva <daric2612@gmail.com>

* Fix ruff formatting in sample file

Signed-off-by: Daria Korenieva <daric2612@gmail.com>

---------

Signed-off-by: Daria Korenieva <daric2612@gmail.com>
2026-05-29 07:20:56 +00:00
Eduard van Valkenburg 4609535e22 Python: feat: add agent-framework-monty (Monty-backed CodeAct provider) (#5915)
* Python: feat: add agent-framework-monty (Monty-backed CodeAct)

New alpha package that wraps pydantic-monty (a Rust-based Python
interpreter) behind the same CodeAct API surface as
agent-framework-hyperlight, so users can swap providers with minimal
code change.

Public API (agent_framework_monty):
- MontyCodeActProvider — ContextProvider that injects a run-scoped
  execute_code tool plus dynamic CodeAct instructions.
- MontyExecuteCodeTool — standalone FunctionTool for mixed-tool agents
  or manual static wiring.
- FileMount / FileMountInput / MountMode — public types mirroring the
  Hyperlight names, with Monty's mode (read-only/read-write/overlay)
  and write_bytes_limit on FileMount.

Constructor kwargs (both classes) mirror Hyperlight where possible:
tools, approval_mode, workspace_root, file_mounts; plus a Monty-only
resource_limits forwarding ResourceLimits to Monty.start().

Filesystem flow:
- workspace_root auto-mounts at /input (read-write), matching Hyperlight.
- file_mounts accepts string shorthand, (host, mount) tuple, or
  FileMount with mode + write cap.
- Files written under read-write mounts are scanned post-execution and
  returned as Content.from_data items (mirrors Hyperlight /output).
- overlay mounts buffer writes in-memory; read-only mounts reject writes.

Internals:
- _monty_bridge.InlineCodeBridge ports the inline (non-durable) bridge
  from anthonychu/maf-codeact-monty-python; handles FunctionSnapshot /
  FutureSnapshot pause/resume, dispatches direct typed calls + the
  call_tool fallback, forwards mount/limits to Monty.start(...).
- generate_type_stubs emits per-tool stubs so Monty's `ty` type-checker
  rejects bad calls before any host tool runs.

Alpha-policy compliance (per python-package-management skill):
- Added agent-framework-monty = { workspace = true } to root
  pyproject.toml.
- Added row to python/PACKAGE_STATUS.md.
- Added monty entry under Experimental in python/AGENTS.md.
- NOT added to core[all]; NO agent_framework.monty lazy shim (deferred
  to beta promotion).

Samples (three sets, import from agent_framework_monty directly):
- samples/02-agents/context_providers/code_act/monty_code_act.py
  (provider pattern) + updated local README.
- samples/02-agents/tools/monty_code_interpreter/ (standalone +
  manual-wiring + README).
- samples/04-hosting/foundry-hosted-agents/responses/11_monty_codeact/
  (full hosted-agent layout with uv-based pyproject.toml + Dockerfile,
  Azure Monitor wiring via APPLICATIONINSIGHTS_CONNECTION_STRING +
  enable_instrumentation, ENABLE_INSTRUMENTATION and
  ENABLE_SENSITIVE_DATA env vars). The alpha wheel is vendored into
  ./wheels/ (gitignored) via vendor-wheel.sh; new row added to the
  parent Responses-API README.

Tests:
- 28 hermetic unit tests (stubbed pydantic_monty).
- 18 integration tests marked @pytest.mark.integration, auto-skipped
  when pydantic_monty is unimportable; exercise the real Monty
  runtime: print round-trip, last-expression value, direct typed
  tool dispatch, call_tool fallback, async tool, asyncio.gather
  parallelism, ty type-check rejection, OS blocked by default,
  workspace_root read+write capture, read-only / overlay mount
  semantics, resource_limits.max_duration_secs abort, approval
  gating end-to-end, full Agent run with a scripted chat client.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Python: fix: monty FileMount test compares against the normalized POSIX path

The shorthand string mount goes through _normalize_mount_path, which
rewrites Windows drive letters like 'C:\\Users\\...' into
'/C:/Users/...' (POSIX-style). The Windows CI runners surfaced this
because tmp_path resolves to a backslashed Windows path; the test was
comparing against the raw str(host_a) instead of the normalized form.

Compare against _normalize_mount_path(str(host_a)) so the assertion is
platform-independent.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Python: fix: address PR #5915 review feedback

- _execute_code_tool docstring: clarify that the Monty backend supports
  scoped filesystem access via workspace_root / file_mounts (blocked by
  default).
- _to_monty_mount: import pydantic_monty lazily through load_monty so
  missing-dependency errors surface as the same actionable RuntimeError
  the rest of the package raises (not a bare ImportError at module load).
  Renamed _load_monty -> load_monty for the same reason.
- _python_type_repr: emit None for type(None) instead of Any, and
  normalize both typing.Union[...] and PEP-604 X | Y to PEP-604 syntax
  so Optional[X] / Union[..., None] / -> None signatures round-trip
  correctly through ty validation. Added a regression test.
- _PrintCollector: track a running character count instead of
  recomputing sum(len(c) for c in self.chunks) per callback. Eliminates
  the O(n^2) cost on print-heavy code.
- Instructions: mention that the value of the final expression is also
  returned alongside captured stdout (matches actual behavior).
- 11_monty_codeact Dockerfile: pin ghcr.io/astral-sh/uv to 0.11.6
  instead of :latest for reproducible builds.
- 11_monty_codeact README: replace the bare "see parent README" pointer
  with sample-specific steps (./vendor-wheel.sh + uv sync + uv run),
  since the sample uses pyproject.toml + a vendored wheel rather than
  requirements.txt.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Python: sample: 11_monty_codeact installs agent-framework-monty from PyPI

Drop the vendored-wheel scaffolding now that agent-framework-monty is on
PyPI as an alpha (1.0.0a*) release:

- pyproject.toml: remove [tool.uv.sources] override; keep [tool.uv]
  prerelease = "allow" so uv pulls the alpha automatically.
- Dockerfile: drop the COPY wheels/ step.
- README: drop the ./vendor-wheel.sh setup step and the
  not-yet-on-PyPI warning.
- Delete vendor-wheel.sh and the gitignored wheels/ directory.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Python: fix(monty): harden post-execution file capture against symlink escape

Same class of issue as the MSRC-reported Hyperlight finding: the
post-execution capture walked workspace_root with Path.rglob() +
is_file() + read_bytes() - all of which follow symlinks. An attacker
who controls the workspace (cloned repo, extracted archive, shared
workspace) could pre-place `workspace/leak.txt -> /etc/passwd` or
`workspace/outside_dir -> /etc/` and have host files surface as
captured Content items.

Monty's mount layer already rejects symlink reads from inside the
sandbox across all three modes (verified empirically), so the runtime
path was safe. This commit closes the post-execution scan path.

Changes:
- New `_iter_real_files(root)` walker that uses iterdir() +
  is_symlink() to skip symlinks at every directory level and yields
  only real files. Replaces the previous `host_root.rglob("*")` calls
  in both `_snapshot_writable_mounts` and `_capture_written_files`.
- Use `Path.lstat()` instead of `Path.stat()` so size/mtime can never
  be taken from a symlink target.
- Three new integration tests reproducing the MSRC attack shape
  against the workspace_root flow: symlink-to-file outside workspace,
  symlink-to-directory outside workspace, and a guard ensuring
  legitimate sandbox writes are still captured when symlinks are
  present.

Per user request, hyperlight is untouched in this commit (separate fix).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Python: fix(monty): skip symlink regression tests when unsupported

Apply the same Windows-CI safety guard as the hyperlight fix in PR #5919:
the three symlink integration tests create symlinks via Path.symlink_to(),
which fails with OSError / NotImplementedError on unprivileged Windows
runners. Add a local _symlinks_supported helper (mirroring the one in
packages/core/tests/core/test_skills.py) and pytest.skip when symlinks
aren't available, so the tests no longer fail for environment reasons.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Python: fix(monty): address PR #5915 follow-up review feedback

- _invoke_tool: drop the inspect.iscoroutinefunction(...) branch and
  always `await self.tool_map[name](**kwargs)`. Every entry in
  tool_map is `partial(FunctionTool.invoke, skip_parsing=True)` and
  FunctionTool.invoke is `async def`, so the branching was dead code -
  and on Python versions affected by cpython#98590,
  iscoroutinefunction(partial(bound_async_method, ...)) returns False,
  causing the bridge to take the asyncio.to_thread path, return an
  unawaited coroutine, and surface it as a JSON-serialization failure
  for every tool call. Added a regression test
  test_invoke_tool_awaits_partial_wrapped_async_method.

- generate_type_stubs: skip tools whose name is not a valid Python
  identifier or is a Python keyword. FunctionTool.name has no upstream
  validation, so a name like "weird-name" produced a syntax error in
  the stubs and a name like "broken\n    pass\nasync def injected"
  would inject arbitrary stub source. Non-identifier names stay
  reachable via `call_tool("weird-name", ...)` at runtime; they just
  don't get type-checked stubs. Added regression test
  test_generate_type_stubs_skips_non_identifier_tool_names.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-20 00:35:23 +00:00
Evan Mattson ae666a4887 Python: Bump agent-framework-ag-ui to release candidate stage (#5844)
* Bump agent-framework-ag-ui to release candidate stage

* Mark agent-framework-ag-ui as rc in PACKAGE_STATUS
2026-05-14 14:56:34 +00:00
Eduard van Valkenburg 57c901a245 Python: Fix hyperlight WasmSandbox cross-thread Drop and harden hosted-agent sample (#5603)
* update hyperlight to beta and move samples, add hosted agent sample

* Python: Fix hyperlight WasmSandbox cross-thread Drop and harden sample

Root cause: when a worker-side closure raised, the exception's __traceback__
retained frame locals that included the partially constructed PyO3 sandbox.
Future.result() re-raised that exception on the caller thread, and when the
caller's exception was eventually GC'd the frame locals were released
off-thread, dec_ref'ing the unsendable sandbox from the wrong thread and
tripping the PyO3 panic
'_native_wasm::WasmSandbox is unsendable, but is being dropped on another thread'.

Fix:
* Add _SandboxWorker._run_on_worker which catches every exception on the
  worker, drops __traceback__ there, deletes the original exception, and
  re-raises a fresh instance on the caller thread. initialize and execute
  route through it; dispose keeps its bare-submit semantics.
* Add an opt-in diagnostic module _drop_diagnostic (no-op unless
  HYPERLIGHT_TRACE_DROPS=1) that installs a sys.unraisablehook and dumps
  owner-thread + per-thread stacks on any future cross-thread unsendable
  Drop. Useful for triaging similar PyO3 regressions.
* Tests: cross-thread invocation, traceback-leak isolation, _SandboxEntry
  attribute-shape check, and a stale-reference stress test driven through
  asyncio.to_thread.

Sample (samples/04-hosting/foundry-hosted-agents/responses/06_hyperlight_codeact):
* Dockerfile installs agent-framework-* from in-tree source with python/ as
  build context so unreleased fixes can be validated end-to-end.
* call_server.py pins the Responses API version.
* main.py enables include_detailed_errors=True so future tool failures
  surface the actual exception text instead of a bare 'Error: Function
  failed.' string.
* README.md documents the in-tree-package build and the Hyperlight
  hypervisor requirement (/dev/kvm on Linux, MSHV on Windows). Hosted
  environments without hypervisor passthrough surface 'No Hypervisor was
  found for Sandbox'; this is a hosting constraint, not a hyperlight bug.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Python: remove _drop_diagnostic from hyperlight package

The diagnostic module was useful while bisecting the cross-thread Drop bug,
but it is no longer needed now that _SandboxWorker._run_on_worker prevents
the panic at the source.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Python: address PR review feedback on hyperlight

- Use lazy agent_framework.hyperlight import in sample main.py.
- Env-driven endpoint (FOUNDRY_AGENT_ENDPOINT) in call_server.py; remove personal URLs.
- Align agent.yaml model deployment with manifest (gpt-4.1-mini).
- Tighten Dockerfile requirements guard; drop dangling deploy.ps1 reference.
- Preserve exception args when sanitizing tracebacks in _run_on_worker.
- Add public _SandboxWorker.is_alive(); update test to avoid private attr.
- Add namespace coverage tests for agent_framework.hyperlight lazy loader.
- Add prominent note: Foundry hosted-agent runtime does not yet support
  Hyperlight (no hypervisor exposed); container works locally with /dev/kvm.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Python: bump hyperlight-sandbox dependencies to 0.4.x

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Python: renumber hyperlight codeact sample to 08

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Coerce worker exception args to strings for cross-thread safety

Stringify exc.args on the worker thread before propagating, so any
PyO3 unsendable object captured in args (e.g. via a caller-supplied
callback or underlying SDK) cannot be Dropped on the calling thread.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* moved sample

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-05 10:06:16 +00:00
Yung-Shin Lin 1e1eda65ce [Python] Add agent-framework-azure-ai-contentunderstanding package (#4829)
* feat: add agent-framework-azure-contentunderstanding package

Add Azure Content Understanding integration as a context provider for the
Agent Framework. The package automatically analyzes file attachments
(documents, images, audio, video) using Azure CU and injects structured
results (markdown, fields) into the LLM context.

Key features:
- Multi-document session state with status tracking (pending/ready/failed)
- Configurable timeout with async background fallback for large files
- Output filtering via AnalysisSection enum
- Auto-registered list_documents() and get_analyzed_document() tools
- Supports all CU modalities: documents, images, audio, video
- Content limits enforcement (pages, file size, duration)
- Binary stripping of supported files from input messages

Public API:
- ContentUnderstandingContextProvider (main class)
- AnalysisSection (output section selector enum)
- ContentLimits (configurable limits dataclass)

Tests: 46 unit tests, 91% coverage, all linting and type checks pass.

* fix: update CU fixtures with real API data, fix test assertions

- Replace synthetic fixtures with real CU API responses (sanitized)
- Update test assertions to match real data (Contoso vs CONTOSO,
  TotalAmount vs InvoiceTotal, field values from real analysis)
- Add --pre install note in README (preview package)
- Document unenforced ContentLimits fields (max_pages, duration)

* chore: add connector .gitignore, update uv.lock

* refactor: rename to azure-ai-contentunderstanding, fix CI issues

Align naming with Azure SDK convention and AF pattern:
- Directory: azure-contentunderstanding -> azure-ai-contentunderstanding
- PyPI: agent-framework-azure-contentunderstanding -> agent-framework-azure-ai-contentunderstanding
- Module: agent_framework_azure_contentunderstanding -> agent_framework_azure_ai_contentunderstanding

CI fixes:
- Inline conftest helpers to avoid cross-package import collision in xdist
- Remove PyPI badge and dead API reference link from README (package not published yet)

* feat: add samples (document_qa, invoice_processing, multimodal_chat)

- document_qa.py: Single PDF upload, CU context provider, follow-up Q&A
- invoice_processing.py: Structured field extraction with prebuilt-invoice
- multimodal_chat.py: Multi-file session with status tracking
- Add ruff per-file-ignores for samples/ directory
- Update README with samples section, env vars, and run instructions

* feat: add remaining samples (devui_multimodal_agent, large_doc_file_search)

- S3: devui_multimodal_agent/ — DevUI web UI with CU-powered file analysis
- S4: large_doc_file_search.py — CU extraction + OpenAI vector store RAG
- Update README and samples/README.md with all 5 samples

* feat: add file_search integration for large document RAG

Add FileSearchConfig — when provided, CU-extracted markdown is automatically
uploaded to an OpenAI vector store and a file_search tool is registered on
the context. This enables token-efficient RAG retrieval for large documents
without users needing to manage vector stores manually.

- FileSearchConfig dataclass (openai_client, vector_store_name)
- Auto-create vector store, upload markdown, register file_search tool
- Auto-cleanup on close()
- When file_search is enabled, skip full content injection (use RAG instead)
- Update large_doc_file_search sample to use the integration
- 4 new tests (50 total, 90% coverage)

* fix: add key-based auth support to all samples

Follow established AF pattern: check for API key env var first,
fall back to AzureCliCredential. Supports AZURE_OPENAI_API_KEY and
AZURE_CONTENTUNDERSTANDING_API_KEY environment variables.

* FEATURE(python): add analyzer auto-detection, file_search RAG, and lazy init

_context_provider.py:
- Make analyzer_id optional (default None) with auto-detection by media
  type prefix: audio->audioSearch, video->videoSearch, else documentSearch
- Add _ensure_initialized() for lazy client creation in before_run()
- Add FileSearchConfig-based vector store upload
- Fix: background-completed docs in file_search mode now upload to vector
  store instead of injecting full markdown into context messages
- Add _pending_uploads queue for deferred vector store uploads

devui_file_search_agent/ (new sample):
- DevUI agent combining CU extraction + OpenAI file_search RAG

azure_responses_agent (existing sample fix):
- Add AzureCliCredential support and AZURE_AI_PROJECT_ENDPOINT fallback

Tests (19 new), Docs updated (AGENTS.md, README.md)

* feat(cu): MIME sniffing, media-aware formatting, unified timeout, vector store expiration

- Add three-layer MIME detection (fast path → filetype binary sniff → filename
  fallback) to handle unreliable upstream MIME types (e.g. mp4 sent as
  application/octet-stream). Adds filetype>=1.2,<2 dependency.
- Media-aware output formatting: video shows duration/resolution + all fields
  as JSON; audio promotes Summary as prose; document unchanged.
- Unified timeout for all media types (removed file_search special-case that
  waited indefinitely for video/audio). All files use max_wait with background
  polling fallback.
- Vector store created with expires_after=1 day as crash safety net.
- Add 8 MIME sniffing tests (TestMimeSniffing class).

* fix: merge all CU content segments for video/audio analysis

CU's prebuilt-videoSearch and prebuilt-audioSearch analyzers split long
media files into multiple `contents[]` segments. Previously,
`_extract_sections()` only read `contents[0]`, causing truncated
duration, missing transcript, and incomplete fields for any video/audio
longer than a single scene.

Now iterates all segments and merges:
- duration: global min(startTimeMs) → max(endTimeMs)
- markdown: concatenated with `---` separators
- fields: same-named fields collected into per-segment list
- metadata (kind, resolution): taken from first segment

Single-segment results (documents, short audio) are unaffected.

Update test fixture to realistic 3-segment video structure and expand
assertions to verify multi-segment merging. Add documentation for
multi-segment processing and speaker diarization limitation.

* refactor: improve CU context provider docs and remove ContentLimits

- Improve class docstring: clarify endpoint (Azure AI Foundry URL with
  example), credential (AzureKeyCredential vs Entra ID), and analyzer_id
  (prebuilt/custom with auto-selection behavior and reference links)
- Add SUPPORTED_MEDIA_TYPES comments explaining MIME-based matching
  behavior and add missing file types per CU service docs
- Use namespaced logger to align with other packages
- Remove ContentLimits and related code/tests
- Rename DEFAULT_MAX_WAIT to DEFAULT_MAX_WAIT_SECONDS for clarity

* feat: support user-provided vector store in FileSearchConfig

- Add vector_store_id field to FileSearchConfig (None = auto-create)
- Track _owns_vector_store to only delete auto-created stores on close()
- Remove vector_store_name; use internal _DEFAULT_VECTOR_STORE_NAME
- Add inline comments for private state fields
- Document output_sections default in docstring
- Update AGENTS.md, samples, and tests

* fix: remove ContentLimits from README code block

* refactor: create CU client in __init__ instead of __aenter__

Follow Azure AI Search provider pattern: create the client eagerly in
__init__, make __aenter__ a no-op. This ensures __aexit__/close() is
always safe to call and eliminates the _ensure_initialized() workaround.

* docs: add file_search param to class docstring

* feat: introduce FileSearchBackend abstraction for cross-client support

Replace direct OpenAI client usage with FileSearchBackend ABC:
- OpenAIFileSearchBackend: for OpenAIChatClient (Responses API)
- FoundryFileSearchBackend: for FoundryChatClient (Azure Foundry)
- Shared base _OpenAICompatBackend for common vector store CRUD

FileSearchConfig now takes a backend instead of openai_client.
Factory methods from_openai() and from_foundry() for convenience.

BREAKING: FileSearchConfig(openai_client=...) -> FileSearchConfig.from_openai(...)

* refactor: FileSearchBackend abstraction + caller-owned vector store

* fix: file_search reliability and sample improvements

- Poll vector store indexing (create_and_poll) to ensure file_search
  returns results immediately after upload
- Set status to failed when vector store upload fails
- Skip get_analyzed_document tool in file_search mode to prevent
  LLM from bypassing RAG
- Simplify sample auth: single credential, direct parameters
- Use from_foundry backend for Foundry project endpoints

* perf: set max_num_results=10 for file_search to reduce token usage

* fix: move import to top of file (E402 lint)

* chore: remove unused imports

* fix: align azure-ai-contentunderstanding with MAF coding conventions

- Add module-level docstrings to __init__.py and _context_provider.py
- Use Self return type for __aenter__ (with typing_extensions fallback)
- Use explicit typed params for __aexit__ signature
- Add sync TokenCredential to AzureCredentialTypes union
- Pass AGENT_FRAMEWORK_USER_AGENT to ContentUnderstandingClient
- Remove unused ContentLimits from public API and tests
- Fix FileSearchConfig tests to match refactored backend API
- Fix lifecycle tests to match eager client initialization

* refactor: improve CU context provider API surface and fix CI

- Refactor _analyze_file to return DocumentEntry instead of mutating dict
- Remove TokenCredential from AzureCredentialTypes (fixes mypy/pyright CI)
- Remove OpenAIFileSearchBackend/FoundryFileSearchBackend from public API
  (internal to FileSearchConfig factory methods)
- Remove DocumentStatus from public exports (implementation detail)
- Update file_search comments to reflect backend-agnostic design
- Add DocumentStatus enum, analysis/upload duration tracking
- Add combined timeout for CU analysis + vector store upload

* fix: improve file_search samples and move tool guidelines to context provider

- Delete redundant devui_file_search_agent sample (duplicate of azure_openai variant)
- Move tool usage guidelines from sample agent instructions into context provider
  (extend_instructions in step 6, applied automatically for all file_search users)
- Fix file_search purpose: use from_foundry() for Azure OpenAI (purpose="assistants")
- Add filename hint in upload instructions for targeted file_search queries
- Reduce max_num_results from 10 to 3 in both devui samples
- Simplify agent instructions in both samples (remove tool-specific guidance)

* feat: improve source_id, integration tests, and content assertions

- Rename DEFAULT_SOURCE_ID to "azure_ai_contentunderstanding" (matches
  azure_ai_search convention)
- Improve source_id docstring to describe default value
- Clarify _detect_and_strip_files docstring (CU-supported files)
- Add invoice.pdf test fixture from Azure CU samples repo
- Refactor integration tests to use invoice.pdf directly (assert instead
  of skip when fixture missing)
- Add URI content test (Content.from_uri with external URL)
- Add "CONTOSO LTD." content assertion to all integration tests
- Use max_wait=None in integration tests (wait until complete)

* feat: reject duplicate filenames, add integration tests and sample comments

- Reject duplicate document keys in before_run (skip + warn LLM to rename)
- Update _derive_doc_key docstring to document uniqueness constraint
- Add unit tests for duplicate filename rejection (cross-turn and same-turn)
- Add integration test for data URI content (from_uri with base64)
- Add integration test for background analysis (max_wait timeout + resolve)
- Add filename recommendation comments to all samples' Content.from_data()

* chore: improve doc key derivation, comments, and README

- Replace hash-based doc key with uuid4 for anonymous uploads (O(1), no payload traversal)
- Remove hashlib import (no longer needed)
- Add File Naming section to README (filename importance, duplicate rejection)
- Improve inline comments (_derive_doc_key, _extract_binary, URL parsing)

* test: strengthen _format_result assertions with exact expected strings

- Replace loose 'in' checks with exact 'assert formatted == expected'
  for both multi-segment and single-segment format tests
- Add object-type fields (ShippingAddress, Speakers) to test data
  to cover nested dict/list serialization
- Add position-based ordering assertions to verify structural
  correctness (header -> markdown -> fields across segments)

* refactor: move invoice.pdf to shared sample_assets directory

- Move invoice.pdf from tests/cu/test_data/ to
  python/samples/shared/sample_assets/ as single source of truth
- Add INVOICE_PDF_PATH constant in test_integration.py pointing
  to the shared location
- Update document_qa.py, invoice_processing.py, large_doc_file_search.py
  to use invoice.pdf instead of sample.pdf

* refactor: reorganize samples into numbered dirs and simplify auth

- Move script samples into 01-get-started/ with numbered prefixes
  (01_document_qa, 02_multimodal_chat, 03_invoice_processing,
   04_large_doc_file_search)
- Move devui samples into 02-devui/ with 01-multimodal_agent and
  02-file_search_agent/{azure_openai_backend,foundry_backend}
- Move invoice.pdf to CU package-local samples/shared/sample_assets/
- Replace kwargs dicts with direct constructor calls; support both
  API key (AZURE_OPENAI_API_KEY) and AzureCliCredential
- Update README sample table with new paths

* fix: resolve CI lint errors (D205, RUF001, E501)

- Fix D205: single-line docstring summary for _detect_and_strip_files
- Fix RUF001: replace EN DASH with HYPHEN-MINUS in segment headers
- Fix E501: wrap long assertion lines in tests
- Also includes samples reorg and auth simplification

* refactor: overhaul samples — FoundryChatClient, sessions, remove get_analyzed_document

Samples:
- Switch all samples from deprecated AzureOpenAIResponsesClient to FoundryChatClient
- Add 02_multi_turn_session.py showing AgentSession persistence across turns
- Rewrite 03_multimodal_chat.py with real PDF + audio + video (parallel
  analysis), per-modality follow-ups, cross-document question, elapsed
  time, user prompts, and input token counts
- Renumber: 02->03 multimodal, 03->04 invoice, 04->05 file_search

Context provider:
- Remove get_analyzed_document tool -- full content is in conversation
  history via InMemoryHistoryProvider, no retrieval tool needed
- Remove follow-up turn instructions about tools
- Only list_documents tool remains (for status queries)
- Update README to reflect tool removal

* feat: add 05_background_analysis sample and fix 04 session/max_wait

- Add 05_background_analysis.py demonstrating non-blocking CU analysis
  with max_wait=1s, status tracking via list_documents(), and automatic
  background task resolution on subsequent turns
- Fix 04_invoice_processing.py: add max_wait=None and AgentSession
- Rename 05→06 large_doc_file_search
- Update README sample table

* docs: update README and fix sample 06

README:
- Switch Quick Start from AzureOpenAIResponsesClient to FoundryChatClient
- Add AgentSession to Quick Start example
- Fix status values: pending -> analyzing/uploading/ready/failed
- Fix env var: AZURE_OPENAI_RESPONSES_DEPLOYMENT_NAME -> AZURE_OPENAI_DEPLOYMENT_NAME
- Update samples section with new paths, link to samples/README.md
- Update multi-segment description to reflect per-segment fields

Sample 06:
- Fix from_openai -> from_foundry for Azure endpoints
- Add AgentSession and max_wait=None

* docs: rewrite README — concise format, prerequisites, CU link

* fix: resolve pyright errors in _format_result segment cast

* docs: add numbered section comments and fresh sample output to all samples

- Add numbered section comments (# 1. ..., # 2. ...) per SAMPLE_GUIDELINES
- Re-run all 6 samples and update expected output with real results
- Fix duplicate sample output blocks in 04 and 05
- Update README code example to use public invoice URL

* feat: add load_settings support for env var configuration

- Make endpoint optional in constructor — auto-loads from
  AZURE_CONTENTUNDERSTANDING_ENDPOINT env var via load_settings()
- Add ContentUnderstandingSettings TypedDict
- Add env_file_path/env_file_encoding params for .env file support
- Add 4 unit tests: env var loading, explicit override, missing
  endpoint error, missing credential error
- Update README with env var auto-resolution docs
- Follows framework convention used by all other packages

* docs: polish README — fix duplicate env var, add Next steps, service limits link

* chore: trim invoice fixture from 199K to 33 lines

Keep only VendorName, InvoiceTotal, DueDate, InvoiceDate, InvoiceId
fields and first 500 chars of markdown. Strip spans/source/coordinates.
Reduces fixture from 6.6MB to 1.2KB.

* feat: per-file analyzer_id override via additional_properties

- Read analyzer_id from Content.additional_properties for per-file override
- Resolution order: per-file > provider-level > auto-detect by media type
- Update class docstring documenting filename and analyzer_id properties
- Update sample 04 to demonstrate per-file override (prebuilt-invoice)
- Add unit test for per-file analyzer override

* Trim PDF test fixture and clarify unique filename requirement

- Trim analyze_pdf_result.json from 4427 to 23 lines by removing
  pages, words, lines, paragraphs, sections, spans, and source
  fields that are not used by any unit test.
- Add docstring note that filename must be unique within a session;
  duplicate filenames are rejected and the file will not be analyzed.

* Update python/packages/azure-ai-contentunderstanding/agent_framework_azure_ai_contentunderstanding/_context_provider.py

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update python/packages/azure-ai-contentunderstanding/agent_framework_azure_ai_contentunderstanding/_context_provider.py

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update python/packages/azure-ai-contentunderstanding/samples/02-devui/02-file_search_agent/azure_openai_backend/agent.py

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update python/packages/azure-ai-contentunderstanding/samples/02-devui/01-multimodal_agent/agent.py

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update python/packages/azure-ai-contentunderstanding/samples/01-get-started/06_large_doc_file_search.py

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Fix AGENTS.md to match implementation; remove unused variable in test helper

AGENTS.md:
- Remove _ensure_initialized() reference (client is created in __init__)
- Fix multi-segment docs: segments kept as list, not merged into fields
- Remove get_analyzed_document() reference (only list_documents registered)
- Update sample names to match current directory structure

test_context_provider.py:
- Simplify _make_data_uri() — remove unused 'encoded' variable

* Fix premature file_search instruction for background-completed docs

- Change _resolve_pending_tasks() instruction from 'Use file_search'
  to 'being indexed' since the upload hasn't completed yet at that point.
- Add LLM instruction on upload failure in step 1b so the agent can
  inform the user the document isn't searchable.

* fix: wrap long line in devui agent instructions (E501)

* Fix Copilot review: unused logger, stray code in README, await cancelled tasks

- _file_search.py: Remove unused logger and logging import
- 01-multimodal_agent/README.md: Remove accidentally pasted Python script
- _context_provider.py close(): Await cancelled tasks before closing
  client to prevent 'Task destroyed but pending' warnings

* Sanitize doc keys and fix duplicate filename re-injection

- Add _sanitize_doc_key() to strip control characters, collapse
  whitespace, and cap length at 255 chars — prevents prompt injection
  via crafted filenames in extend_instructions() calls.
- Track accepted doc_keys in step 3 so step 5 only injects content
  for files actually analyzed this turn, not pre-existing duplicates.
- Soften duplicate upload instruction wording (remove IMPORTANT/caps).

* fix: add type annotation to tasks_to_cancel for pyright

* Move per-session mutable state to state dict for session isolation

Previously _pending_tasks, _pending_uploads, and _uploaded_file_ids
were stored on self, shared across all sessions. This caused
cross-session leakage: Session A's background task results could be
injected into Session B's context.

Now these are stored in the per-session state dict. Global copies
(_all_pending_tasks, _all_uploaded_file_ids) are kept on self only
for best-effort cleanup in close().

Add 2 new TestSessionIsolation tests verifying that background tasks
and resolved content stay within their originating session.

* Remove unused AnalysisSection enum values

Only MARKDOWN and FIELDS are handled by _extract_sections().
Remove FIELD_GROUNDING, TABLES, PARAGRAPHS, SECTIONS to avoid
exposing dead options to users.

* Recursively flatten object/array field values for cleaner LLM output

- Use SDK .value property with recursive extraction for object/array fields
- Object: AmountDue -> {Amount: 610, CurrencyCode: USD} (was raw SDK dict)
- Array: LineItems -> list of flattened items (was raw SDK list)
- Update invoice fixture with object/array fields from prebuilt-invoice
- Add 3 unit tests for object, array, and nested object field extraction

* Preserve sub-field confidence; compare full expected JSON in tests

* Remove incorrect MIME aliases (audio/mp4, video/x-matroska)

* feat: add AnalysisInput, content_range, warnings, and category support

- Use SDK AnalysisInput model instead of raw body dict for begin_analyze
- Forward content_range from additional_properties to CU (page/time ranges)
- Extract CU warnings with code/message/target (ODataV4Format) into output
- Include content-level category from classifier analyzers
- Add 5 new tests: warnings, category, content_range forwarding
- Fix pyright with explicit casts; fix en-dash lint (RUF002)

* fix: falsy-0 bug in duration calc; improve test coverage

- Fix start_time_ms=0 treated as falsy by 'or' short-circuit, use
  'is None' checks instead for duration and segment time extraction
- Update warnings test to use RAI ContentFiltered codes
- Enrich warnings extraction to include code/message/target (ODataV4Format)
- Add multi-segment video category test with per-segment assertions

* refactor: split _context_provider.py into focused modules

- Extract _constants.py: SUPPORTED_MEDIA_TYPES, MIME_ALIASES, analyzer maps
- Extract _detection.py: file detection, MIME sniffing, doc key derivation
- Extract _extraction.py: result extraction, field flattening, LLM formatting
- _context_provider.py delegates via thin wrappers (793 lines, was 1255)
- Update test imports to use _constants.py for SUPPORTED_MEDIA_TYPES

* docs: update AGENTS.md with DocumentStatus, FileSearchBackend, and _file_search.py

* refactor: replace AnalysisSection enum with Literal type for simpler DX

- Remove AnalysisSection(str, Enum) class, replace with Literal["markdown", "fields"] type alias
- Users can now pass plain strings: output_sections=["markdown"] — no extra import needed
- AnalysisSection type alias still exported for type annotation use
- Update all samples, tests, and internal code to use string literals
- Address PR review feedback (eavanvalkenburg)

* refactor: replace asyncio.Task with continuation tokens for serializable state

- Replace state["_pending_tasks"] (asyncio.Task — not serializable) with
  state["_pending_tokens"] (dict of continuation token strings) so the
  framework can persist session state to disk/storage
- Resume pending analyses via Azure SDK continuation_token mechanism
- Fix: resumed pollers have stale cached status (done() always False),
  use asyncio.wait_for(poller.result()) with 10s min timeout instead
- Remove _background_poll(), _all_pending_tasks, and task cancellation
- Address PR review feedback (eavanvalkenburg): state must be serializable

* fix: resolve CI lint (RUF052) and mypy (call-overload) errors

* feat: add structured output (Pydantic model) to invoice processing sample

- Use response_format=InvoiceResult for schema-constrained LLM output
- Use output_sections=["fields"] only (no markdown needed for structured output)
- Add LowConfidenceField model with confidence values
- Add comments about prebuilt-invoice extensive schema vs simplified model
- Address PR review feedback (eavanvalkenburg): use structured response

* fix: use FOUNDRY_PROJECT_ENDPOINT and FOUNDRY_MODEL env vars in all samples

Replace AZURE_AI_PROJECT_ENDPOINT → FOUNDRY_PROJECT_ENDPOINT and
AZURE_OPENAI_DEPLOYMENT_NAME → FOUNDRY_MODEL across all sample .py and
README.md files. Address PR review feedback (eavanvalkenburg).

* refactor: remove background_analysis sample, use FoundryChatClient in DevUI

- Remove 05_background_analysis.py (per reviewer feedback — discuss max_wait
  design separately from samples)
- Renumber 06_large_doc_file_search.py → 05_large_doc_file_search.py
- Replace AzureOpenAIResponsesClient with FoundryChatClient in all DevUI samples
- Replace client.as_agent() with Agent(client=client, ...) everywhere
- Add max_wait comments explaining interactive vs batch usage
- Update README.md and AGENTS.md
- Address PR review feedback (eavanvalkenburg)

* fix: vector_stores API moved from beta namespace in OpenAI SDK

* docs: add comments about multi-file support and CU service limits in file_search sample

* fix: broken markdown links after sample removal and renumbering

* fix: migrate BaseContextProvider to ContextProvider (non-deprecated)

* fix: Message(text=) -> Message(contents=[]) for API compatibility

* Inline _constants.py into consuming modules

Remove _constants.py and move constants to where they are used:
- SUPPORTED_MEDIA_TYPES, MIME_ALIASES → _detection.py
- MEDIA_TYPE_ANALYZER_MAP, DEFAULT_ANALYZER → _context_provider.py

Addresses review feedback to reduce file count.

* Mark package as alpha per package management skill

- Version: 1.0.0b260401 → 1.0.0a260401
- Classifier: Development Status 4 - Beta → 3 - Alpha
- Add to PACKAGE_STATUS.md as alpha

Follows the alpha package checklist from python-package-management skill.

* Replace extend_instructions with extend_messages for status notifications

Status/error/result notifications now use extend_messages (conversation
context) instead of extend_instructions (system prompt). This avoids
system prompt bloat and keeps behavioral directives separate from
event notifications.

- 11 extend_instructions calls → extend_messages (role='user')
- 1 extend_instructions retained: tool usage guidelines (behavioral)
- 6 test assertions updated to check context_messages

All 84 unit tests + 5 live integration tests pass.

* Fix lint: E402 import order, ISC004 implicit string concatenation

- Move constants after all imports to fix E402
- Wrap multi-line strings in parentheses inside contents=[] to fix ISC004

* Fix lint: remove unused json import in invoice sample

* Fix CI: apply ruff format + fix E501 line length after reformatting

ruff format expands Message() calls to multi-line, pushing string
indentation deeper. Break long strings to fit within 120 char limit
after formatting. Also removes unused json import in sample.

* Address review feedback: keyword-only args, accept pre-built client, remove wrappers

- All __init__ args now keyword-only (matches FoundryChatClient pattern)
- New 'client' param accepts pre-built ContentUnderstandingClient
- core dep bound: >=1.0.0rc5 → >=1.0.0,<2
- Self import moved after local imports
- Removed 9 static method wrappers; callsites use module functions directly
- Tests updated to import derive_doc_key and format_result directly

* fix: remove duplicate ContentUnderstandingClient instantiation

The client was being created twice — once inside the if/else block and
again unconditionally after it. The second instantiation overwrote the
pre-built client path and failed type checking when credential was None.

* rename: azure-ai-contentunderstanding → azure-contentunderstanding

Package: agent-framework-azure-ai-contentunderstanding → agent-framework-azure-contentunderstanding
Module: agent_framework_azure_ai_contentunderstanding → agent_framework_azure_contentunderstanding
Directory: packages/azure-ai-contentunderstanding → packages/azure-contentunderstanding

Per agreement with PM and MAF team to drop 'AI' from the package name.

* feat: add ContentUnderstanding re-export to agent_framework.foundry namespace

Enables: from agent_framework.foundry import ContentUnderstandingContextProvider

Exports: ContentUnderstandingContextProvider, FileSearchConfig,
FileSearchBackend, AnalysisSection, DocumentStatus

Updates all samples and README to use the foundry namespace import.

* fix: add missing copyright headers to standalone sample scripts

* chore: remove .vscode/settings.json and add to .gitignore

* refactor: reuse FoundryChatClient.client for vector store ops in file_search sample

Address review feedback from TaoChenOSU:
- 05_large_doc_file_search.py: use client.client instead of manually
  constructing AsyncAzureOpenAI; remove openai dependency
- azure_openai_backend/agent.py: import reorder only (AIProjectClient
  kept — required for sync vector store creation in DevUI)

* fix: skip closing client when caller passes pre-built client

When a ContentUnderstandingClient is passed via client=, the caller
owns its lifecycle. Added _owns_client flag so close() only closes
the client when we created it internally.

---------

Co-authored-by: yungshinlin <yungshin@msn.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-28 20:55:59 +00:00
Eduard van Valkenburg b03cb324d5 Python: Add Hyperlight CodeAct package and docs (#5185)
* initial work on code_mode

* updated samples

* updates to codeact

* udpated codeact

* Draft CodeAct ADR and sample updates

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* initial implementation and adr and feature

* Python: Limit Hyperlight wasm backend to Python <3.14

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Python: Fix CI for Hyperlight CodeAct PR

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Python: Run Hyperlight integration when available

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Python: Address Hyperlight review feedback

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Python: Simplify Hyperlight file mount inputs

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Python: Accept Path host paths in Hyperlight mounts

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Python: Fix Hyperlight mount typing for CI

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* temp run integration test

* Python: Strengthen Hyperlight real sandbox tests

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* added additional tests

* Python: Simplify Hyperlight CodeAct API

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* set tests as non-integration

* Retry Hyperlight allowed-domain registration

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Gate Hyperlight integration tests by runtime support

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix Hyperlight skip test on Python 3.14

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Delay Hyperlight runtime probe until test execution

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Relax Hyperlight Windows integration stdout assertion

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Scan Hyperlight output directory for artifacts

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Retry Hyperlight output artifact collection

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Harden Hyperlight integration output assertions

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Retry Hyperlight read-back check in integration test

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Simplify Hyperlight integration write assertion

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Avoid pathlib in Hyperlight integration sandbox

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Use socket network check in Hyperlight sandbox

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Replace blocked Azure AI Search blog link

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Clarify Hyperlight guest stdlib limits

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Use _socket in Hyperlight integration sandbox

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Handle Hyperlight mounted file paths

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Broaden Hyperlight sandbox path fallbacks

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Search Hyperlight guest mounts recursively

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Split Hyperlight mount coverage

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Split Hyperlight live network tests

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix Hyperlight file-write test on Windows

Enable the sandbox filesystem by providing a workspace_root so
/output is mounted. Remove os.path.exists assertion (unsupported
in WASM guest) and fix Content data assertion to use .uri.
Skip the network integration test on Windows where the WASM
sandbox lacks the encodings.idna codec.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Address PR review: ADR intro, manual wiring sample, doc clarifications

- Add CodeAct introduction section to ADR for unfamiliar readers
- Clarify 'less runtime efficient' con with specific overhead description
- Add note in Python impl doc clarifying ADR vs impl doc split
- Explain why before_run hooks must be per-run (CRUD, concurrency, approval)
- Rename code_interpreter variable to codeact in E2E sample
- Add manual static wiring sample (codeact_manual_wiring.py)
- Add 'when to use which pattern' guidance to samples README

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Address PR #5185 review comments and add .NET CodeAct design doc

- Fix async callback: _make_sandbox_callback returns sync wrapper with
  thread + asyncio.run() bridge (was broken with real Wasm FFI)
- Fix stale output: clear output_dir before each sandbox.run() call
- Fix blocking event loop: _run_code now async with asyncio.to_thread()
- Revert _agents.py options['tools'] injection (unnecessary; provider
  uses context.extend_tools())
- Revert SessionContext.options docstring back to read-only
- Add real-sandbox test fixtures (shared/restored/fresh)
- Add 8 new real-sandbox tests for callback round-trip, stale output,
  event loop non-blocking, basic execution, stdout/stderr, errors,
  snapshot/restore, and tool registration
- Add comprehensive .NET HyperlightCodeActProvider design document

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Update hyperlight README with code snippets and remove Public API section

Replace bare export list with Quick Start code examples covering the
context provider, standalone tool, manual static wiring, and file
mounts / network access patterns.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-17 00:49:44 +00:00
Björn Holtvogt 485af07b8c Python: Add GeminiChatClient (#4847)
* Add agent-framework-gemini package

* Add AGENTS.md documentation

* Add LICENSE file

* Add README.md for agent-framework-gemini package

* Add Google Gemini API keys to .env.example

* Add Google Gemini chat client implementation

* Add tests for GeminiChatClient

* Add Google Gemini agent examples

* Fix client inheritence order

* Update Gemini agent examples

* Update documentation

* Update AGENTS.md

* Add tests for JSON string handling in GeminiChatClient

* Add final response assembly test in GeminiChatClient

* Add tests for handling empty candidates in GeminiChatClient

* Improve Pydantic response handling in GeminiChatClient

* Add tests for function result resolution and callable tool normalization

* Add test for function result resolution when call_id is generated

* Refactor GeminiChatClient to correct inheritance order

Also updates constructor parameter order for environment file handling

* Enhance documentation and clarify Gemini-specific fields

* Update ThinkingConfig with new attributes and type

* Add tests for GoogleSearch and GoogleMaps configs

* Suppress valid-type mypy error on GeminiChatOptionsT

* Move service_url method near overrides

* Order _prepare_config kwargs by base then Gemini-specific

* Use FunctionCallingConfigMode for clarity and type safety

* Fix code_execution doc

* Add agent-framework-gemini to project dependencies

* Remove package from core dependencies

Initial release will be done without agent-framework-gemini in
core[all].

* Move integration tests into one file

* Remove __init__.py file from gemini tests directory

* Introduce RawGeminiChatClient as lightweight chat client

Updated GeminiChatClient to inherit from RawGeminiChatClient, maintaining full functionality with added features.

* Updated variable names from `model_id` to `model`

Across the codebase, including environment variables and client initialization. Adjusted related tests and sample scripts to reflect this change, ensuring consistency in the usage of the Gemini model identifier.

* Update AGENTS.md

* Update Gemini package to alpha status

* Fix docstrings in Gemini tests

* Change 'model_id' to 'model' in response handling

* Fix model property change in response handling

* Add built-in tool factory methods to Gemini client

Replaces boolean tool options (code_execution, google_search_grounding,
google_maps_grounding) with static factory methods that return types.Tool
objects: get_code_interpreter_tool, get_web_search_tool, get_mcp_tool,
get_file_search_tool, and get_maps_grounding_tool.

Simplifies _prepare_tools to a single translation boundary between
FunctionTool (framework) and FunctionDeclaration (Gemini API), with
types.Tool objects passed through unchanged.

* Surface code execution parts

_parse_parts now maps executable_code and code_execution_result
parts to text Content objects so callers can see the code run
and its output. Unknown part types log at debug level rather than
being silently dropped.

* Update Gemini client documentation

* Unify Gemini model name

Co-authored-by: Eduard van Valkenburg <eavanvalkenburg@users.noreply.github.com>

* Update Agent Framework core version

Co-authored-by: Eduard van Valkenburg <eavanvalkenburg@users.noreply.github.com>

* Add Python 3.14 in classifiers

* Replace kwargs with parameters in tool factories

* Refactor chat options handling in Gemini client

* Add tests for handling unknown and consumed keys

* Update Gemini documentation

Now reflects new options and built-in tool factory methods

* Change build system to flit

Co-authored-by: Eduard van Valkenburg <eavanvalkenburg@users.noreply.github.com>

* Fix build system in pyproject.toml

* Fix type checking for generate_content_stream

---------

Co-authored-by: Eduard van Valkenburg <eavanvalkenburg@users.noreply.github.com>
2026-04-14 10:18:26 +00:00
Eduard van Valkenburg 3446eb8d5d Python: [BREAKING] update to v1.0.0 (#5062)
* updates to final deprecated pieces and versions

* fix mypy

* fix readme links
2026-04-02 15:26:30 +00:00