Test and retire a model
Confirm a model configuration actually works before any agent depends on it, then clean it up when you are done.
ensureProvider/ensureModelregister an OpenAI-compatible provider and model (skip this step if you already have one)testModelruns a small generation call against the provider and returns{success, message}— it does not register or change anythingdeleteModel, thendeleteProvider, to retire the configuration
When you need this
- Validate a model id before switching agent traffic to it. Catch a typo’d
model_idor an unreachable host with a 400 and a provider error message instead of a failed agent turn in production. - Confirm a rotated API key still works. After
update_provider/updateProvidersets a newtoken_reference,testModelproves the new credential is live. - Tear down a short-lived evaluation model.
deleteModelthendeleteProvider— delete the model first; Aiden does not cascade-delete a provider’s models.
How to run
export STACKGEN_URL=https://app.stackgen.com
export STACKGEN_TOKEN=stackgen_…
export STACKGEN_WORKSPACE_ID=<workspace-uuid>
pip install stackgen-sdk==0.4.0
python examples/models/test-and-retire-model.py \
--host https://api.openai.com/v1 --token-reference secret_ref_… --model-id gpt-4o-miniexport STACKGEN_URL=https://app.stackgen.com
export STACKGEN_TOKEN=stackgen_…
export STACKGEN_WORKSPACE_ID=<workspace-uuid>
npm install stackgen-sdk@0.4.0 tsx
npx tsx examples/models/test-and-retire-model.ts \
--host https://api.openai.com/v1 --token-reference secret_ref_… --model-id gpt-4o-miniexport STACKGEN_URL=https://app.stackgen.com
export STACKGEN_TOKEN=stackgen_…
export STACKGEN_WORKSPACE_ID=<workspace-uuid>
go run ./examples/go/models/test-model.go \
-provider-name my-openai -model-id gpt-4o-mini -delete-after-test -model-name my-modelGo does not have provider/model create helpers yet — point it at a provider
and model that already exist. Python and TypeScript create one with
ensureProvider / ensureModel when it is missing.
Minimal
from stackgen import StackgenClient, StackgenConfig
client = StackgenClient(
StackgenConfig(
base_url="https://app.stackgen.com",
api_token="stackgen_…",
workspace_id="<workspace-uuid>",
)
)
client.aiden.llm.ensure_provider(
name="my-openai", host="https://api.openai.com/v1", token_reference="secret_ref_…"
)
client.aiden.llm.ensure_model(
name="my-model", provider_name="my-openai", model_id="gpt-4o-mini"
)
probe = client.aiden.llm.test_model(provider_name="my-openai", model_id="gpt-4o-mini")
print(probe["success"], probe["message"])
client.aiden.llm.delete_model("my-model")
client.aiden.llm.delete_provider("my-openai")import { StackgenClient, StackgenConfig } from "stackgen-sdk";
const client = new StackgenClient(
new StackgenConfig({
baseUrl: "https://app.stackgen.com",
apiToken: "stackgen_…",
workspaceId: "<workspace-uuid>",
}),
);
await client.aiden.llm.ensureProvider({
name: "my-openai",
host: "https://api.openai.com/v1",
tokenReference: "secret_ref_…",
});
await client.aiden.llm.ensureModel({
name: "my-model",
providerName: "my-openai",
modelId: "gpt-4o-mini",
});
const probe = await client.aiden.llm.testModel({
providerName: "my-openai",
modelId: "gpt-4o-mini",
});
console.log(probe.success, probe.message);
await client.aiden.llm.deleteModel("my-model");
await client.aiden.llm.deleteProvider("my-openai");// Go points at a provider/model that already exists; there is no
// ensureProvider/ensureModel helper yet.
result, err := client.Aiden.TestModel(ctx, aiden.TestModelRequest{
ProviderName: "my-openai",
ModelID: "gpt-4o-mini",
})
if err != nil { return err }
fmt.Println(result.Success, result.Message)
if err := client.Aiden.DeleteModel(ctx, "my-model"); err != nil { return err }
return client.Aiden.DeleteLLMProvider(ctx, "my-openai")Notes
testModelraises/throws anHttpError(400)with the provider’s own error message on an invalid configuration — it never writes a model row.- Pass
scope(TENANT/PROJECT) todeleteModel/delete_modelwhen the model was registered at a non-default owner level. - Delete models before their provider. Aiden returns 404 for an unknown provider but does not cascade-delete the models registered under it.
Complete scripts
- Python:
examples/models/test-and-retire-model.py - TypeScript:
examples/models/test-and-retire-model.ts - Go:
examples/go/models/test-model.go
#!/usr/bin/env python3
"""Register an OpenAI-compatible model, probe it with test_model, then
retire it (delete_model + delete_provider).
test_model runs a small generation call against the provider without writing
anything, so it is the right pre-flight check before wiring an agent to a new
model, or after rotating a provider's token_reference.
Usage:
export STACKGEN_URL=https://app.stackgen.com
export STACKGEN_TOKEN=stackgen_…
export STACKGEN_WORKSPACE_ID=<workspace-uuid>
python examples/models/test-and-retire-model.py \
--provider-name my-openai --host https://api.openai.com/v1 \
--token-reference secret_ref_… --model-id gpt-4o-mini
"""
from __future__ import annotations
import argparse
import os
from stackgen import StackgenClient, StackgenConfig
def env(name: str, *fallbacks: str) -> str:
for key in (name, *fallbacks):
value = (os.environ.get(key) or "").strip()
if value:
return value
raise SystemExit(f"missing env {name}")
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--provider-name", default="sdk-sample-openai")
parser.add_argument("--host", required=True, help="e.g. https://api.openai.com/v1")
parser.add_argument("--token-reference", default="", help="Vault secret_ref id")
parser.add_argument("--model-name", default="sdk-sample-model")
parser.add_argument("--model-id", required=True, help="Provider-specific model id")
parser.add_argument(
"--keep-after-test",
action="store_true",
help="Skip delete_model/delete_provider cleanup at the end",
)
args = parser.parse_args()
client = StackgenClient(
StackgenConfig(
base_url=env("STACKGEN_URL"),
api_token=env("STACKGEN_TOKEN"),
workspace_id=env("STACKGEN_WORKSPACE_ID", "STACKGEN_PROJECT"),
)
)
provider = client.aiden.llm.ensure_provider(
name=args.provider_name,
host=args.host,
provider_type="openai",
token_reference=args.token_reference,
)
print(f"provider ready name={provider.get('name')} host={provider.get('host')}")
model = client.aiden.llm.ensure_model(
name=args.model_name,
provider_name=args.provider_name,
model_id=args.model_id,
)
print(f"model ready name={model.get('name')} model_id={model.get('model_id')}")
probe = client.aiden.llm.test_model(
provider_name=args.provider_name, model_id=args.model_id, max_tokens=16
)
print(f"test_model success={probe.get('success')} message={probe.get('message')}")
if args.keep_after_test:
print("--keep-after-test set; leaving the model and provider in place")
return 0
client.aiden.llm.delete_model(args.model_name)
print(f"deleted model name={args.model_name}")
client.aiden.llm.delete_provider(args.provider_name)
print(f"deleted provider name={args.provider_name}")
return 0
if __name__ == "__main__":
raise SystemExit(main())/**
* Register an OpenAI-compatible model, probe it with `testModel`, then
* retire it (`deleteModel` + `deleteProvider`).
*
* `testModel` runs a small generation call against the provider without
* writing anything, so it is the right pre-flight check before wiring an
* agent to a new model, or after rotating a provider's `token_reference`.
*
* Usage:
* export STACKGEN_URL=https://app.stackgen.com
* export STACKGEN_TOKEN=stackgen_…
* export STACKGEN_WORKSPACE_ID=<workspace-uuid>
* npx tsx examples/models/test-and-retire-model.ts \
* --provider-name my-openai --host https://api.openai.com/v1 \
* --token-reference secret_ref_… --model-id gpt-4o-mini
*/
import { parseArgs } from "node:util";
import { StackgenClient, StackgenConfig } from "stackgen-sdk";
function requireEnv(name: string, ...fallbacks: string[]): string {
for (const key of [name, ...fallbacks]) {
const value = (process.env[key] || "").trim();
if (value) {
return value;
}
}
throw new Error(`missing env ${name}`);
}
async function main(): Promise<void> {
const { values } = parseArgs({
options: {
"provider-name": { type: "string", default: "sdk-sample-openai" },
host: { type: "string", default: "" },
"token-reference": { type: "string", default: "" },
"model-name": { type: "string", default: "sdk-sample-model" },
"model-id": { type: "string", default: "" },
"keep-after-test": { type: "boolean", default: false },
},
});
if (!values.host || !values["model-id"]) {
throw new Error("--host and --model-id are required");
}
const client = new StackgenClient(
new StackgenConfig({
baseUrl: requireEnv("STACKGEN_URL"),
apiToken: requireEnv("STACKGEN_TOKEN"),
workspaceId: requireEnv("STACKGEN_WORKSPACE_ID", "STACKGEN_PROJECT"),
}),
);
const providerName = values["provider-name"]!;
const modelName = values["model-name"]!;
const provider = await client.aiden.llm.ensureProvider({
name: providerName,
host: values.host!,
providerType: "openai",
tokenReference: values["token-reference"] || undefined,
});
console.log(`provider ready name=${provider.name} host=${provider.host}`);
const model = await client.aiden.llm.ensureModel({
name: modelName,
providerName,
modelId: values["model-id"]!,
});
console.log(`model ready name=${model.name} model_id=${model.model_id}`);
const probe = await client.aiden.llm.testModel({
providerName,
modelId: values["model-id"]!,
maxTokens: 16,
});
console.log(`testModel success=${probe.success} message=${probe.message}`);
if (values["keep-after-test"]) {
console.log("--keep-after-test set; leaving the model and provider in place");
return;
}
await client.aiden.llm.deleteModel(modelName);
console.log(`deleted model name=${modelName}`);
await client.aiden.llm.deleteProvider(providerName);
console.log(`deleted provider name=${providerName}`);
}
main().catch((err) => {
console.error(err);
process.exit(1);
});// Probe a registered model with TestModel, optionally retiring it afterward.
//
// TestModel runs a small generation call against the provider without
// writing anything, so it is the right pre-flight check before wiring an
// agent to a model, or after rotating a provider's token_reference.
//
// export STACKGEN_URL=https://app.stackgen.com
// export STACKGEN_TOKEN=stackgen_…
// export STACKGEN_WORKSPACE_ID=<workspace-uuid>
// go run ./examples/go/models/test-model.go \
// -provider-name my-openai -model-id gpt-4o-mini
package main
import (
"context"
"flag"
"fmt"
"os"
"strings"
stackgen "github.com/appcd-dev/stackgen-sdk/clients/go"
"github.com/appcd-dev/stackgen-sdk/clients/go/aiden"
)
// envOr returns the first non-empty environment variable among keys.
func envOr(keys ...string) string {
for _, key := range keys {
if v := strings.TrimSpace(os.Getenv(key)); v != "" {
return v
}
}
return ""
}
func main() {
providerName := flag.String("provider-name", "", "existing model provider name")
modelID := flag.String("model-id", "", "provider-specific model id to probe")
modelName := flag.String("model-name", "", "registered model name; required with -delete-after-test")
deleteAfterTest := flag.Bool("delete-after-test", false, "delete the model and provider after a successful probe")
flag.Parse()
if strings.TrimSpace(*providerName) == "" || strings.TrimSpace(*modelID) == "" {
fmt.Fprintln(os.Stderr, "-provider-name and -model-id are required")
os.Exit(2)
}
if *deleteAfterTest && strings.TrimSpace(*modelName) == "" {
fmt.Fprintln(os.Stderr, "-model-name is required with -delete-after-test")
os.Exit(2)
}
base, token, org := envOr("STACKGEN_URL"), envOr("STACKGEN_TOKEN"), envOr("STACKGEN_WORKSPACE_ID", "STACKGEN_PROJECT")
if base == "" || token == "" || org == "" {
fmt.Fprintln(os.Stderr, "set STACKGEN_URL, STACKGEN_TOKEN, and STACKGEN_WORKSPACE_ID")
os.Exit(2)
}
client, err := stackgen.NewClient(stackgen.Config{BaseURL: base, APIToken: token, WorkspaceID: org})
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
ctx := context.Background()
result, err := client.Aiden.TestModel(ctx, aiden.TestModelRequest{
ProviderName: *providerName,
ModelID: *modelID,
MaxTokens: 16,
})
if err != nil {
fmt.Fprintln(os.Stderr, "error:", err)
os.Exit(1)
}
fmt.Printf("testModel success=%v message=%s\n", result.Success, result.Message)
if !*deleteAfterTest {
return
}
if err := client.Aiden.DeleteModel(ctx, *modelName); err != nil {
fmt.Fprintln(os.Stderr, "error:", err)
os.Exit(1)
}
fmt.Printf("deleted model name=%s\n", *modelName)
if err := client.Aiden.DeleteLLMProvider(ctx, *providerName); err != nil {
fmt.Fprintln(os.Stderr, "error:", err)
os.Exit(1)
}
fmt.Printf("deleted provider name=%s\n", *providerName)
}See also
- Create a workflow — wire a tested model into an agent with
createAgent({ modelNames: [...] }).