fix(prompt_deploy): normalize Foundry definition to dict for SDK 2.x compatibility#216
Merged
Merged
Conversation
…compatibility
The `created` action path of `prompt_deploy stage` was producing an invalid
request body against azure-ai-projects 2.x because:
1. `_copy_definition` called `.copy()` on the typed `PromptAgentDefinition`
returned by `get_version`. In SDK 2.x that returns a stripped base
`Model` whose JSON shape is `{"_data": {...}}` instead of the flat
payload the Foundry Agents service expects.
2. `_create_agent_version` also wrote `kind` at the body root via
`definition.get("kind")` — but the post-`.copy()` model returned
`None` for that key, AND the new API treats `kind` strictly as the
polymorphic discriminator inside `definition`.
The combined effect was a request body like
`{"kind": null, "definition": {"_data": {"kind": "prompt", ...}}, ...}`,
which the service rejected with
'invalid_payload — Required properties ["kind"] are not present'.
This regression only fired on the `created` path (user changed the prompt
relative to the seed). The `reused` and bootstrap paths never round-trip
the typed model through `.copy()`, so they were unaffected.
Fix:
- New `_definition_to_dict` helper accepts dicts, typed SDK models with
`_data`, mapping-like objects with `.items()`, or anything with
`as_dict()`.
- `_copy_definition` now always returns a deep-copied plain dict.
- `_create_agent_version` drops the root-level `kind` from the body and
sends a clean `{definition, metadata, description}` shape.
Verified locally: serialization of a real `PromptAgentDefinition` (2.2.0)
through the fixed path produces the expected flat shape with `kind`
inside `definition`.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes a hard failure in
prompt_deploy stagethat fires when the user's prompt differs from the seed (thecreatedaction path) and the runner installsazure-ai-projects >= 2.x.Symptom hit during a live tutorial recording at step 14 of
tutorial-prompt-agent-quickstart.md:Root cause
Two compounding issues in
src/agentops/pipeline/prompt_deploy.py:_copy_definitionreturned the wrong shape under SDK 2.x. It called.copy()on the typedPromptAgentDefinitionreturned byget_version. In SDK 1.x that preserved the typed model so the body serialized as a flat{"kind": "prompt", "model": ..., "instructions": ...}. In SDK 2.x the same.copy()returns a stripped baseModelwhose JSON shape is{"_data": {"kind": "prompt", ...}}._create_agent_versionwrotekindat the body root viadefinition.get("kind"). With the post-.copy()stripped model that returnsNone. The new Foundry API also treatskindstrictly as the polymorphic discriminator insidedefinition, so a root-levelkindis at best ignored.Combined effect — the JSON that reached the service looked like:
{ "kind": null, "definition": {"_data": {"kind": "prompt", "model": "...", "instructions": "..."}}, "metadata": {...}, "description": "..." }…which the service rejected as malformed.
Why this only fired on one path
Only the
createdaction path runs_copy_definition+_create_agent_versionwith the typed SDK model:reusedcurrentdirectly, nocreate_versioncallbootstrappeddictfromprompt_agent_bootstrapcreatedPromptAgentDefinitionthrough.copy()That matches what the user saw: the first deploy (where the prompt hadn't changed) returned
action: reusedand succeeded; the second deploy after they edited the prompt blew up.Fix
In
src/agentops/pipeline/prompt_deploy.py:_definition_to_dicthelper normalizes any SDK definition object (dict, typed model with_data, mapping with.items(), or anything withas_dict()) to a plain dict._copy_definitionnow always returns a deep-copied plain dict._create_agent_versiondrops the root-levelkindfrom the body and sends a clean{definition, metadata, description}shape.Verified locally with SDK 2.2.0: serialization of a real
PromptAgentDefinitionthrough the fixed path produces the expected flat shape withkindinsidedefinition.Tests
test_copy_definition_returns_plain_dict_from_sdk_typed_model— regression test that simulates the SDK 2.x.copy()semantics and asserts the result is a flat dict without_data.test_create_agent_version_body_uses_flat_definition_dict— asserts the body sent toclient.agents.create_versionhas no root-levelkindand a flatdefinitiondict.Follow-up
This needs a patch release (
v0.3.2) so the workflow templates pick the fix up via PyPI on the nextpip install agentops-toolkiton CI.