11 Commits

  • Python: Improve PR template and breaking-change label automation (#6473)
    * Improve PR template and breaking-change label automation
    
    - Add a structured "Related Issue" section using GitHub closing keywords
    - Add a Review Guide prompt (major changes, impact, reviewer focus) with a
      note that the focus item is for human reviewers only
    - Add checklist items for issue linkage / no duplicate PRs and invert the
      breaking-change item (checked = not breaking)
    - Extend label-title-prefix to prepend [BREAKING] when the "breaking change"
      label is added
    - Add label-breaking-change workflow to apply the "breaking change" label
      when a PR title contains [BREAKING]
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Add pull-requests agent skill with dotnet/python links
    
    - Add root .github/skills/pull-requests/SKILL.md covering PR description
      authoring (following the PR template) and the review-comment workflow
      (review -> plan -> user review -> implement -> reply to all -> resolve)
    - Symlink the skill from python/.github/skills and dotnet/.github/skills
    - Reference the skill from python/AGENTS.md and dotnet/AGENTS.md
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Fold breaking-change labeling into label-pr workflow
    
    Move the title -> 'breaking change' label logic into the existing label-pr
    workflow (which already applies the python/.NET labels) and drop the separate
    label-breaking-change workflow.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Address PR title prefix review feedback
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Pin patched MessagePack for .NET restore
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Revert MessagePack central pin
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Move title prefix tests out of tracked GitHub tests
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Exclude skill docs from CI path filters
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Match skill symlinks in CI path exclusions
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Exclude AGENTS docs from CI path filters
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Scope title-prefix normalization to a real prefix
    
    The normalization branch in addTitlePrefix matched ^Python (no colon), so
    titles like "Python samples improvements" or "Pythonic refactor" were treated
    as already-prefixed and only re-cased, never receiving the "Python: " prefix.
    Scope the match to ^<prefix>:\s* so only an actual existing prefix is
    normalized; otherwise the prefix is prepended. Same fix applies to the .NET
    prefix (e.g. ".NETStandard bump").
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    ---------
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • Python: feat(foundry): add to_prompt_agent / deploy_as_prompt_agent (experimental) (#5959)
    * feat(foundry): add experimental to_prompt_agent converter
    
    Adds `to_prompt_agent(agent)`, an experimental converter
    (`ExperimentalFeature.TO_PROMPT_AGENT`) that turns an Agent Framework
    `Agent` into a Foundry `PromptAgentDefinition` ready to publish via
    `AIProjectClient.agents.create_version(...)`.
    
    Behaviour:
    
    * `agent.client` must be a `FoundryChatClient` (or subclass); otherwise
      `TypeError` is raised. The model deployment name is lifted from the
      bound client so the same Agent definition used for local runs can be
      published as a hosted prompt agent without restating the model.
    * Foundry SDK tool instances (from `FoundryChatClient.get_*_tool()`) are
      passed through unchanged. AF `FunctionTool`s (and `@tool`-decorated
      callables) are emitted as Foundry `FunctionTool` declarations.
    * Local AF MCP tools cannot be expressed in a `PromptAgentDefinition`;
      the converter raises `ValueError` and points at
      `FoundryChatClient.get_mcp_tool()` for hosted MCP servers.
    * The converter walks both `agent.default_options["tools"]` and
      `agent.mcp_tools` because `normalize_tools()` splits local MCP off
      into its own list.
    
    Re-exported through the `agent_framework.foundry` lazy-loading namespace
    (updates both `__init__.py` and the `__init__.pyi` type stub).
    
    Adds a portable-agent sample showing the same `Agent` driven through
    both `agent.run(...)` and `to_prompt_agent(agent)`, and a README section
    covering the new converter.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * chore(samples): remove snippet tags from portable agent sample
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * chore(samples): inline FoundryChatClient and enable prompt-agent publish
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * chore(samples): drop async credential context manager
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * docs(foundry): trim README to_prompt_agent example to publish-only flow
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * docs(foundry): note FoundryAgent runs @tool callables for deployed prompt agents
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * fix(foundry): address review comments on to_prompt_agent converter
    
    * Construct `PromptAgentDefinition` `Tool` from a dict via `**tool_item`
      unpacking rather than the positional Mapping constructor \u2014 cleaner and
      matches the typical Pydantic / Azure SDK pattern.
    * Drop the redundant `isinstance(mcp_tool, MCPTool)` guard in
      `_convert_tools`; the parameter is already typed `Iterable[MCPTool]` so
      the second `raise` was unreachable. The remaining single `raise`
      fires for every entry as intended.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * fix(foundry): match Agent.__init__ model resolution in to_prompt_agent
    
    * Read the model from `agent.default_options.get("model")` first,
      falling back to `agent.client.model`. This mirrors the order
      `Agent.__init__` uses (`_agents.py:740`) when assembling
      default_options, so the model the agent runs with is the same model
      the converter publishes \u2014 e.g. when the caller passes
      `default_options={"model": "..."}` to override the bound client.
    * Updated the missing-model error message to point at both the client
      and the default_options paths.
    * Added tests:
      * tool-only agent with no `instructions` produces a definition
        where `instructions` is `None` and is omitted from the dict
        payload (`Agent.__init__` strips None values from default_options
        before storing them).
      * `default_options['model']` wins over the bound client's model.
      * Fallback to client.model when default_options has no model.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * feat(foundry): add deploy_as_prompt_agent helper + samples
    
    Adds `deploy_as_prompt_agent(agent)`, a convenience wrapper around
    `to_prompt_agent` that reuses the bound FoundryChatClient's project
    client to call `project_client.agents.create_version(...)`. Defaults
    `agent_name` / `description` from `agent.name` / `agent.description`
    so the Agent stays the single source of truth.
    
    * Exposed from `agent_framework_foundry` and the lazy-loading
      `agent_framework.foundry` namespace (including the .pyi stub).
    * Marked experimental with the existing
      `ExperimentalFeature.TO_PROMPT_AGENT` tag.
    * Tests cover the happy path, name/description defaulting, explicit
      override, no-name error, metadata + description forwarding, extra
      kwargs passthrough, and the experimental metadata.
    
    Samples:
    * Renamed the existing sample to `creating_prompt_agents.py`, drops
      'portable' wording, presents `deploy_as_prompt_agent` first as the
      recommended path and `to_prompt_agent` + `AIProjectClient` as the
      two-step alternative, and adds a cleanup step that deletes the
      published agent so re-runs stay idempotent.
    * New `using_prompt_agents.py` shows the end-to-end loop: deploy the
      agent, connect to it with `FoundryAgent` passing the same local
      `@tool` callable, run a query against the deployed prompt agent,
      then clean up.
    
    README updated to introduce `deploy_as_prompt_agent` as the
    recommended path and link to both runnable samples.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * fix(foundry): restore missing-model ValueError in to_prompt_agent
    
    The check was accidentally dropped while reworking docstrings in the
    previous commit. Test `test_to_prompt_agent_rejects_missing_model`
    exercises this path and was failing on CI as a result.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * refactor(foundry): rename deploy_as_prompt_agent -> create_prompt_agent
    
    Renames the helper across the foundry package, core lazy-loader stubs,
    tests, README and samples. The new name better matches the action
    performed (a prompt-agent definition is created in Foundry) and is
    consistent with the surrounding ''create_*'' API surface.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * refactor(foundry): drop create_prompt_agent, enrich to_prompt_agent params
    
    Remove the create_prompt_agent helper and consolidate on to_prompt_agent.
    Expose every PromptAgentDefinition parameter that has either an Agent
    Framework equivalent (sourced from default_options) or no equivalent
    (accepted as a keyword argument).
    
    * default_options-sourced (with kwarg overrides):
      temperature, top_p, string tool_choice
    * kwarg-only Foundry knobs:
      reasoning, text, structured_inputs, rai_config, ToolChoiceParam tool_choice
    
    Precedence is always: explicit keyword > default_options entry > unset.
    
    Tests cover every path (defaults, default_options, kwargs, kwarg override).
    Samples and README rewritten around the enriched to_prompt_agent.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * refactor(foundry): single source of truth for prompt-agent options
    
    Stop duplicating the generation-parameter surface between FoundryChatOptions
    and to_prompt_agent. Translate every field with an Agent Framework equivalent
    (temperature, top_p, tool_choice, reasoning, response_format/text/verbosity)
    from agent.default_options via a new RawFoundryChatClient helper
    _prepare_prompt_agent_options. Only Foundry-specific fields with no AF
    equivalent — structured_inputs and rai_config — remain as keyword arguments
    on to_prompt_agent.
    
    - tool_choice is dropped when there are no tools (mirrors _prepare_options
      semantics and avoids polluting tool-less prompt agents with Agent.__init__'s
      'auto' default).
    - response_format Pydantic models route through
      openai.lib._parsing._responses.type_to_text_format_param; dict shapes go
      through the existing _prepare_response_and_text_format helper.
    - default_options is not mutated; text dict is defensively copied.
    
    Tests, README, and creating_prompt_agents.py sample updated to reflect the
    new single-source model.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * docs(foundry): consolidate prompt-agent sample
    
    Drop creating_prompt_agents.py (the publish-only variant) and rename
    using_prompt_agents.py to foundry_prompt_agents.py so the single sample
    covers the full convert -> publish -> connect -> run loop. Update the
    README link list accordingly.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * docs(foundry): run local Agent + deployed agent in same sample
    
    Add an agent.run() call against the local Agent before publishing, then run
    the deployed prompt agent on the same query. Expand the docstring with a
    compare-and-contrast covering runtime/latency, configurability, and
    persistence/sharing differences between the two execution paths.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * test(foundry): cover conflicting response_format + text.format in to_prompt_agent
    
    Exercises the ValueError path when a Pydantic response_format would overwrite
    an explicit text.format mapping with a different shape. Lifts _chat_client.py
    coverage from 89% to 90%.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * refactor(foundry): move _prepare_prompt_agent_options into _to_prompt_agent
    
    Lift the translation helper off RawFoundryChatClient and into the
    _to_prompt_agent module as a module-private function that takes the client
    as its first argument. The chat client no longer needs to carry a method
    whose only consumer is the prompt-agent converter, while still serving as
    the source of the request-path helper (_prepare_response_and_text_format)
    that the converter reuses for dict-shaped response_format values.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * docs(python): codify GA terminology + post-run docs review
    
    Add two pieces of guidance to python/AGENTS.md:
    
    * Terminology - reserve 'GA' for hosted services; use 'released' or 'stable'
      for Agent Framework code/features to match the feature-lifecycle stages.
    * Maintaining Documentation - review AGENTS.md and skills at the end of every
      run and update any guidance the conversation made stale; before adding a
      new principle, ask the user to confirm it should be captured.
    
    Also pulls in a docstring fix in foundry_prompt_agents.py that swaps the
    stray 'GA' for 'released', applying the new terminology rule.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * address PR review: strict=True default, Tool._deserialize dispatch, sample cleanup safety
    
    - FunctionTool published as strict=True so the server-side schema validation
      matches what the local FoundryAgent(tools=[same_callable]) dispatcher
      enforces. AF FunctionTool has no 'strict' attribute, so the safer default
      is used uniformly instead of silently downgrading to a permissive contract.
    - _validate_mapping_tool now dispatches through ProjectsTool._deserialize so
      dict-shaped tools rehydrate to the concrete subclass (FunctionTool,
      WebSearchTool, ...) via the 'type' discriminator instead of returning a
      generic Tool. Added a test that asserts isinstance(WebSearchTool) and a
      new test for the function-typed dict path.
    - foundry_prompt_agents.py sample now wraps credential + project client in
      async with and the create_version / run flow in try/finally so a failure
      on connect or run still deletes the published prompt agent rather than
      leaving an orphaned, billable resource in the user's Foundry project.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * fix(ci): correct linkspector ignorePattern typo (./pulls -> ./pull)
    
    GitHub PR URLs use the singular segment /pull/N (compare to /issues/N
    for issues). The existing './pulls' ignore pattern never matched
    anything as a result, so legitimately stale PR links (e.g. PRs deleted
    from forks) surface as linkspector failures on unrelated PRs.
    
    This is the same convention the './issues' rule above already follows.
    Fixes the markdown-link-check failure on a dangling link in
    dotnet/src/Microsoft.Agents.AI.DurableTask/CHANGELOG.md.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    ---------
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • 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>
  • [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>
  • Python: [BREAKING] Python: move Azure AI embeddings to Foundry (#5056)
    * renamed AzureAIINferenceEmbeddings and lazy load azure-cosmos and env var rename
    
    * updated coverage
    
    * fix readme
  • Python: Add Python feature lifecycle decorators for released APIs (#4975)
    * Add Python feature lifecycle decorators
    
    Introduce reusable experimental and release-candidate decorators for released packages, migrate the Skills APIs to the new staged metadata and warning system, and add lifecycle guidance plus samples.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Fix Python CI follow-ups
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Address PR review feedback
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Preserve protocol runtime checks
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    ---------
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • Python: [Breaking] Upgrade to azure-ai-projects 2.0+ (#4536)
    * Prepare azure-ai-projects 2.0 GA compatibility
    
    Add allow_preview support for internal AIProjectClient creation, keep backward compatibility for renamed SDK model classes, and align Azure AI/core paths and tests for GA validation workflows.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * upgrade to ai-project==2.0.0
    
    * Python: remove azure-ai-projects keyword-guard paths
    
    Assume azure-ai-projects 2.0+ in Azure AI client/provider/responses code paths by removing _supports_keyword_argument gating and related fallback branching.
    
    Also fix pyright typing in FoundryMemoryProvider memory store calls by using ResponseInputItemParam-typed items.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * check fixes
    
    * Python: remove unsupported foundry_features option
    
    Drop foundry_features from Azure AI client and provider surfaces because azure-ai-projects 2.0.0 does not expose that create_version parameter.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * Python: add allow_preview to Foundry memory provider
    
    Propagate allow_preview when FoundryMemoryProvider constructs an AIProjectClient and update tests accordingly.
    
    Also finish wiring allow_preview through AzureAIClient-facing surfaces and related docs.
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
    
    * aligning docstrings
    
    * udpated lock
    
    ---------
    
    Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
  • Python: Fix prek runner duplication and add skills (#3791)
    * Python: fix prek runner running fmt/lint in all packages on core change
    
    When a core package file changed, run_tasks_in_changed_packages.py ran
    fmt, lint, and pyright in ALL 22 packages (66 tasks). Only type-checking
    tasks (pyright, mypy) need to propagate to all packages since type
    changes in core affect downstream packages. File-local tasks (fmt, lint)
    only need to run in packages with actual file changes.
    
    This reduces a core-only change from 66 tasks to 24 tasks (2 local +
    22 pyright).
    
    Also adds no-commit-to-branch builtin hook to protect the main branch
    from direct commits.
    
    * Python: add agent skills extracted from AGENTS.md and coding standards
    
    Add 5 skills to python/.github/skills/ following the Agent Skills format:
    - python-development: coding standards, type annotations, docstrings, logging
    - python-testing: test structure, fixtures, running tests, async mode
    - python-code-quality: linting, formatting, type checking, prek hooks, CI
    - python-package-management: monorepo structure, lazy loading, versioning
    - python-samples: sample structure, PEP 723, documentation guidelines
    
    * Python: deduplicate AGENTS.md and instructions with agent skills
    
    * updated skills
    
    * fixes from review
    
    * Python: increase timeout for web search integration test
  • Python: replace pre-commit with prek, add PEP 723 script deps, clean up dev dependencies (#3748)
    * python: replace pre-commit with prek, add PEP 723 script deps, clean up dev dependencies
    
    - Replace pre-commit with prek (Rust-native, faster pre-commit alternative)
    - Move supported hooks to repo: builtin for zero-clone speed
    - Add new builtin hooks: trailing-whitespace, check-merge-conflict, detect-private-key, check-added-large-files
    - Update all hook versions to latest (pre-commit-hooks v6, pyupgrade v3.21.2, bandit 1.9.3, uv-pre-commit 0.10.0)
    - Add PEP 723 inline script metadata to 34 samples with external deps
    - Remove autogen-agentchat/autogen-ext from dev deps (now declared per-sample)
    - Remove unused dev deps: pytest-env, tomli-w
    - Add agent-framework-core>=1.0.0b260130 lower bound to all 21 packages
    - Update CI workflow to use j178/prek-action
    - Update docs: DEV_SETUP.md, AGENTS.md, CODING_STANDARD.md, SAMPLE_GUIDELINES.md
    
    * updated lock
    
    * python: fix prek config paths for local execution and CI workflow
    
    Remove global 'files: ^python/' filter and strip python/ prefix from all path patterns in .pre-commit-config.yaml so prek finds files when run from the python/ directory. Update CI workflow to use --cd python instead of --config path. Include trailing whitespace fixes and dev dependency cleanup.
    
    * python: move helper scripts to scripts/ folder and exclude from checks
    
    * python: exclude AGENTS.md from prek markdown code lint
    
    * python: exclude AGENTS.md and azure_ai_search sample from markdown lint
    
    * fix m365 sample
    
    * python: ignore CPY rule for samples with PEP 723 headers
    
    * fix in dev_setup
    
    * python: replace aiofiles with regular open in samples
    
    * python: suppress reportUnusedImport in markdown code block checker
    
    * python: use samples pyright config for markdown code block checker
    
    Write a temp pyrightconfig.json matching pyrightconfig.samples.json rules (typeCheckingMode=off, only reportMissingImports and reportAttributeAccessIssue). Filter output to only fail on these rules since syntax-level errors (top-level await, undefined vars) are expected in README documentation snippets.
    
    * python: use markdown-code-lint with fixed globs instead of prek file list
    
    The prek-markdown-code-lint task received all changed files including non-README markdown and files with pre-existing broken imports. Replace with the standard markdown-code-lint task which uses the correct glob patterns (README.md, packages/**/README.md, samples/**/*.md).
    
    * python: exclude READMEs with pre-existing broken imports from markdown lint
    
    * python: fix broken README code snippets instead of excluding them
    
    - ag-ui: replace TextContent (removed) with content.type == 'text'
    - durabletask: fix import path to durabletask.worker.TaskHubGrpcWorker
    - orchestrations: use constructor params instead of .participants() method
    - observability: mark deprecated code blocks as plain text, filter
      reportMissingImports to agent_framework modules only
    - remove README excludes from markdown-code-lint task
    
    * add revision to gaia download
    
    * feat(python): parallelize checks across packages
    
    Run (package × task) cross-product in parallel using ThreadPoolExecutor
    and subprocesses. Key changes:
    
    - Add scripts/task_runner.py with shared parallel execution engine
    - Update run_tasks_in_packages_if_exists.py to accept multiple tasks
    - Update run_tasks_in_changed_packages.py with --files flag and parallel support
    - Add check-packages poe task (fmt+lint+pyright+mypy in parallel)
    - Add prek-markdown-code-lint and prek-samples-check with change detection
    - Split CI code quality workflow into parallel prek and mypy jobs
    - Update DEV_SETUP.md to document new parallel behavior
    
    Core package changes still trigger checks on all packages.
    
    * feat(ci): split code quality into 4 parallel jobs
    
    Split the single prek job into parallel jobs:
    - pre-commit-hooks: lightweight hooks (SKIP=poe-check)
    - package-checks: fmt/lint/pyright/mypy via check-packages
    - samples-markdown: samples-lint, samples-syntax, markdown-code-lint
    - mypy: change-detected mypy checks
    
    All 4 jobs run concurrently (×2 Python versions = 8 runners).
    
    * feat(ci): use only Python 3.10 for code quality checks
    
    * refactor(python): add future annotations and remove quoted types
    
    Add `from __future__ import annotations` to 93 package files that
    used quoted string annotations, then run pyupgrade --py310-plus to
    remove the now-unnecessary quotes.
    
    Fixes https://github.com/microsoft/agent-framework/issues/3578
  • Python: Add samples syntax checking with pyright (#3710)
    * Add samples syntax checking with pyright
    
    - Add pyrightconfig.samples.json with relaxed type checking but import validation
    - Add samples-syntax poe task to check samples for syntax and import errors
    - Add samples-syntax to check and pre-commit-check tasks
    - Fix 78 sample errors:
      - Update workflow builder imports to use agent_framework_orchestrations
      - Change content type isinstance checks to content.type comparisons
      - Use Content factory methods instead of removed content type classes
      - Fix TypedDict access patterns for Annotation
      - Fix various API mismatches (normalize_messages, ChatMessage.text, role)
    
    * fixed a bunch of samples and tweaks to pre-commit
    
    * updated lock
    
    * updated lock
    
    * fixes
    
    * added lint to samples
  • .NET: Python: Add AGENTS.md files and update coding standards (#3644)
    * Add AGENTS.md files and update coding standards for Python
    
    - Add root python/AGENTS.md with project structure and package links
    - Add AGENTS.md for each package describing purpose and main classes
    - Update .github/copilot-instructions.md with improved structure
    - Update python/CODING_STANDARD.md with API review guidance:
      - Future annotations convention (#3578)
      - TypeVar naming convention (#3594)
      - Mapping vs MutableMapping (#3577)
      - Avoid shadowing built-ins (#3583)
      - Explicit exports (#3605)
      - Exception documentation guidelines (#3410)
    - Simplify python/.github/instructions/python.instructions.md to reference AGENTS.md
    - Remove AGENTS.md from .gitignore
    
    * Fix purview import path in AGENTS.md
    
    * Address PR review comments and restructure instructions
    
    - Slim down .github/copilot-instructions.md to reference language-specific docs
    - Add ADR section explaining templates and purpose
    - Create dotnet/AGENTS.md with .NET-specific build commands, conventions, and sample guidance
    - Update Python build/test instructions for core vs isolated changes
    - Fix Microsoft.Extensions.AI package references
    - Update kwargs guidance per issue #3642
    - Fix Python sample helper placement (top, not bottom)
    - Document new 'typing' poe task in DEV_SETUP.md
    
    * Add 'typing' poe task to run both pyright and mypy
    
    * Add kwargs guidelines from issue #3642 to CODING_STANDARD.md
    
    * Clarify that connector packages pull in core as dependency