feat(agents): provision Foundry memory stores during deploy#8852
feat(agents): provision Foundry memory stores during deploy#8852glharper wants to merge 4 commits into
Conversation
Add support for declaring Foundry memory stores in the agent service config and provisioning them (create-if-not-exists) during `azd deploy`, mirroring the reference provision_memory_store.py sample. - New FoundryMemoryStoreClient (Create/Get/Ensure) targeting the memory_stores data-plane API (api-version 2025-11-15-preview). - New `memoryStores` section in the agent service-target config and JSON schema. - provisionMemoryStores wired into the agent Deploy flow before agent creation. Fixes #8742 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
📋 Prioritization NoteThanks for the contribution! The linked issue isn't in the current milestone yet. |
There was a problem hiding this comment.
Pull request overview
Adds Foundry memory support to the azure.ai.agents extension by allowing users to declare memoryStores in azure.yaml, and provisioning those stores (create-if-not-exists) as part of the hosted-agent deploy flow.
Changes:
- Adds
memoryStoresto the agent service-target config and JSON schema, including optional retention/extraction options. - Introduces a new Foundry data-plane client to
GET/POSTmemory store resources and an idempotentEnsureMemoryStore. - Wires provisioning into
Deployand adds unit tests + a changelog entry.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| cli/azd/extensions/azure.ai.agents/schemas/azure.ai.agent.json | Adds memoryStores schema and MemoryStore definition for azure.yaml validation. |
| cli/azd/extensions/azure.ai.agents/internal/project/service_target_agent.go | Calls memory-store provisioning during deploy; adds mapping helper for options. |
| cli/azd/extensions/azure.ai.agents/internal/project/memory_store_provision_test.go | Adds tests for provisioning no-op, validation, and options mapping. |
| cli/azd/extensions/azure.ai.agents/internal/project/config.go | Extends service target config structs with MemoryStore and MemoryStoreOptions. |
| cli/azd/extensions/azure.ai.agents/internal/project/config_test.go | Adds marshal/unmarshal round-trip coverage for MemoryStores. |
| cli/azd/extensions/azure.ai.agents/internal/pkg/azure/foundry_memory_store_client.go | Introduces the Foundry Memory Store data-plane client (create/get/ensure). |
| cli/azd/extensions/azure.ai.agents/internal/pkg/azure/foundry_memory_store_client_test.go | Adds request-shape, escaping, and ensure(create-if-missing) tests for the client. |
| cli/azd/extensions/azure.ai.agents/internal/exterrors/codes.go | Adds error code and op name for memory store provisioning. |
| cli/azd/extensions/azure.ai.agents/CHANGELOG.md | Documents the new memoryStores deploy-time provisioning behavior. |
jongio
left a comment
There was a problem hiding this comment.
One error code inconsistency to fix. The rest of the implementation follows existing patterns cleanly (client structure mirrors the toolsets client, idempotent ensure pattern is solid, tests cover the key branches).
The Copilot bot flagged IncludeBody: true as a concern, but that's the existing convention in oundry_toolsets_client.go, so it's fine here.
… options, neutral provision op name Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
jongio
left a comment
There was a problem hiding this comment.
Incremental review after 1f1975d: the error code is now aligned with the existing CodeMissingAiProjectEndpoint convention, the mapMemoryStoreOptions nil-or-empty guard prevents sending an empty options object to the service, and the op name OpProvisionMemoryStore better reflects the GET-then-POST semantics of EnsureMemoryStore. All fields in memoryStoreOptionsEmpty match the MemoryStoreOptions struct, and the new test case validates the empty-struct path. No remaining concerns.
…-memory-support # Conflicts: # cli/azd/extensions/azure.ai.agents/CHANGELOG.md
|
@copilot resolve the merge conflicts in this pull request |
…-memory-support # Conflicts: # cli/azd/extensions/azure.ai.agents/CHANGELOG.md Co-authored-by: huimiu <107838226+huimiu@users.noreply.github.com>
Done. Merged |
RickWinter
left a comment
There was a problem hiding this comment.
This adds a data-plane client for Foundry memory stores and wires idempotent create-if-missing provisioning into the hosted-agent deploy, gated on declared memoryStores in azure.yaml. The shape is right: it mirrors the existing Foundry client pattern, keys off FOUNDRY_PROJECT_ENDPOINT like the rest of the file, only creates on a real 404, and propagates auth/5xx/network errors instead of masking them. Client and provisioning-validation test coverage is solid. The one thing worth resolving before merge is drift: once a store exists, editing its chatModel, embeddingModel, or options in azure.yaml has no effect and no warning, and the gray "leaving as-is" line lands exactly where a user expects their change to apply. Documenting that as intended and, ideally, warning when the declared definition diverges from the live store would prevent a confusing silent no-op. The rest are optional: validating all stores before the first network call avoids half-provisioning on a typo, and routing the created/exists lines through the progress reporter keeps deploy output consistent. Nothing here blocks; the idempotent design is sound and safe to re-run.
| if created { | ||
| fmt.Println(output.WithSuccessFormat("Created memory store %q", store.Name)) | ||
| } else { | ||
| fmt.Println(output.WithGrayFormat("Memory store %q already exists; leaving as-is", store.Name)) |
There was a problem hiding this comment.
Because EnsureMemoryStore returns the existing store untouched on a GET hit, a user who edits chatModel, embeddingModel, or any options for an already-created store and redeploys gets no update and no warning, and this line even prints "leaving as-is" in gray right where they would expect their new config to take effect. That is a reasonable default for idempotency, but the silent divergence between azure.yaml and the live store is easy to trip over. Could we at least surface it, for example compare the declared definition against what the GET returned and warn when they differ, so the user knows a change in azure.yaml was ignored? If updating in place is out of scope for this PR, a louder message here would go a long way.
|
|
||
| client := azure.NewFoundryMemoryStoreClient(projectEndpoint, p.credential) | ||
|
|
||
| for _, store := range config.MemoryStores { |
There was a problem hiding this comment.
nit: validation runs inside the same loop that issues the create calls, so if the second store is missing chatModel we have already created the first one before we error out. Re-running is safe thanks to the idempotent ensure, so this is not harmful, just a little surprising on a config typo. Would it be worth validating every store's required fields up front, before the loop that talks to the service, so a bad entry fails fast without half-provisioning?
| } | ||
|
|
||
| if created { | ||
| fmt.Println(output.WithSuccessFormat("Created memory store %q", store.Name)) |
There was a problem hiding this comment.
nit: the created/exists results go straight to stdout via fmt.Println while the surrounding deploy flow reports through the progress reporter. Depending on how progress renders (a spinner), interleaving a raw Println mid-loop can look ragged. Routing these through progress(...) too would keep the output consistent with the rest of Deploy.
jongio
left a comment
There was a problem hiding this comment.
All prior feedback addressed. The implementation follows existing patterns cleanly, test coverage is solid, and the merge conflicts from main were resolved without issues.
Summary
Adds support for Foundry memory in the azure.ai.agents extension by provisioning Foundry memory stores as part of the hosted-agent deploy flow, mirroring the reference
provision_memory_store.pysample.Users declare one or more memory stores under the agent service config in
azure.yaml, and azd creates them (idempotent create-if-not-exists) in the Foundry project before deploying the agent. Memory stores back the agent'smemory_searchtool, letting agents retain context across sessions.Fixes #8742
What changed
FoundryMemoryStoreClient(internal/pkg/azure/foundry_memory_store_client.go): new data-plane client withCreateMemoryStore,GetMemoryStore, and an idempotentEnsureMemoryStore(create-if-missing, leave existing stores unchanged). TargetsPOST/GET {projectEndpoint}/memory_storeswith API version2025-11-15-preview, matching the official REST contract. Follows the existingFoundryToolboxClientpattern.internal/project/config.go): newmemoryStoressection on the agent service-target config (name,description,chatModel,embeddingModel, and optional extraction/retentionoptions).internal/project/service_target_agent.go):provisionMemoryStoresis invoked fromDeployafter config/env load and before the agent is created. Validates required fields and reports per-store progress and created/already-exists status.schemas/azure.ai.agent.json):memoryStoresarray +MemoryStoredefinition.internal/exterrors/codes.go):CodeInvalidMemoryStore,OpCreateMemoryStore.Example
azure.yamlconfigDesign notes / assumptions
chatModel/embeddingModelreference model deployment names that must already exist in the Foundry project (consistent with the sample and REST docs).main; this PR targetsmainusing the existing service-target config, consistent with howtoolboxes/connections/deploymentsare declared.Testing
go build ./...go test ./...(full extension suite passes)gofmt -s,golangci-lint run(0 issues),cspell(0 issues) on changed files