aip_sdk.publish_test_plan
aip_sdk.publish_test_plan(plan: TestPlan, *, workspace_id: str | None = None, client: APIClient | None = None) -> TestPlanRecord
Publish a new test plan's first version, resolving every reference before storing it.
Each metric, quality check, dataset and pinned metric config (MetricConfigRef) the plan
names is resolved against the workspace's catalogue, and each metric's config keys and
threshold bars are checked against what that metric accepts. Any unresolved reference rejects
the whole plan; nothing is stored.
This creates a plan; it never edits one. A name already published in the workspace raises
TestPlanNameExistsError rather than storing a further version of it, so a mistyped or
reused name cannot quietly take over a plan somebody else owns. Publish a further version
of an existing plan with publish_test_plan_version. Any version set on plan is
ignored — the first version is always 1.
The serialized plan must stay under 256 KiB — metrics[...].config and dimension
vocabulary entries are otherwise unbounded, so a very large judge prompt or vocabulary
can cross it. A plan over the limit is rejected before it reaches validation, raising
PayloadTooLargeError rather than TestPlanReferenceError.
The underlying client retries a request on a transport-level timeout, and a timeout does
not mean the publish failed — it may have stored the plan and only the response was lost.
This call guards against that itself: every call is sent with a fresh, internally generated
idempotency key, so the SDK's own retry of a lost response always replays that same attempt's
outcome (required_columns re-derived fresh, replayed=True) rather than minting a version
you never asked for. There is nothing to configure — a genuinely new call, e.g. because you
re-ran the script, gets its own fresh key and publishes for real, exactly as it should.
Parameters
planTestPlan: The methodology to publish.workspace_idstr | None: Target workspace. Falls back to the client's configured workspace.clientAPIClient | None: Explicit API client. Uses the default client when omitted.
Returns
TestPlanRecord: The publishedTestPlanRecordat version 1.record.replayedisTrueon the rareTestPlanRecord: call whose response is this SDK's own retry of an earlier attempt that the platformTestPlanRecord: already stored — seeTestPlanRecord.replayed.
Examples
import aip_sdk as aip
plan = aip.TestPlan(
name="toxicity-baseline",
schema_name="gdi_text_v1",
task_type="single_turn_llm",
metrics={"llm.toxicity": {"threshold": 0.8}},
)
try:
published = aip.publish_test_plan(plan, workspace_id="ws_001")
except aip.TestPlanReferenceError as err:
for failure in err.failures:
print(f"{failure.kind}: {failure.ref} — {failure.message}")
raise
print(published.version, published.required_columns)
Raises
NoWorkspaceSelectedError: No workspace was given and the client has none configured.InvalidArgumentError: A metricconfigvalue is not JSON-serializable.TestPlanReferenceError: The plan names something that could not be resolved. Readfailuresfor every unresolved reference.UnprocessableEntityError: The plan was rejected for a reason other than an unresolved reference — a schema-level problem the request never resolves intofailures.TestPlanNameExistsError: The name is already published in this workspace. Publish under another name, or re-read the existing plan and publish the next version of it withpublish_test_plan_version.DuplicateTestPlanVersionError: Not the version race this exception is named for — a create conflicts on the name, never on a version, and that has its own error above. Kept as the fallback for a 409 the platform did not tag; genuinely unreachable in practice unless the platform has moved ahead of this SDK version.IdempotencyKeyConflictError: Not reachable through this function in practice — every call generates its own key, so no two calls collide except the SDK's own retry of itself, which is always safe. Kept as a defensiveexceptfor whatever the platform actually returns.PayloadTooLargeError: The serialized plan exceeds the platform's size limit.AuthError: Credentials are missing, invalid, or expired.ForbiddenError: The caller is not an editor or admin of the workspace.NotFoundError: The workspace does not exist.ResponseParseError: A platform response did not match the shape this SDK version expects — either the published record on success, or the reported failures on a 422 this SDK could not interpret. Usually means the platform is running a newer release; upgrading resolves it. If raised while parsing a 422, nothing was stored, matchingTestPlanReferenceError's guarantee.APIError: Any other error from the platform.