Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 9 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,8 +198,8 @@ Supported API command groups call only current `api.sume.com` API routes:

- `GET /v1/balance`
- `GET /v1/usage`
- `POST /v1/models/sume/avatar/v1.0/runs`
- `POST /v1/models/sume/avatar-video/v1.0/runs`
- `POST /v1/avatar-1.0/generate`
- `POST /v1/avatar-1.0/talking-video`
- `GET /v1/avatars`
- `GET /v1/avatars/:id`
- `GET /v1/avatar-videos`
Expand All @@ -226,11 +226,13 @@ Avatar and avatar-video submit commands support `--payload-json` or
Avatar-video scripts are estimated locally and by the API; accepted target
duration is 4-60 seconds inclusive.

`sume avatars create` defaults to `sume/avatar/v1.0`. `type` means the avatar
input kind (`prompt`, `photo`, or `props`). Launch-shaped requests use
`avatar_handle` plus an `input` union; the CLI exposes that as
`--avatar-handle` and type-specific fields. `--handle` remains a short alias for
`--avatar-handle`.
`sume avatars create` defaults to the primary `POST /v1/avatar-1.0/generate`
route. `type` means the avatar input kind (`prompt`, `photo`, or `props`).
Launch-shaped requests use `avatar_handle` plus an `input` union; the CLI
exposes that as `--avatar-handle` and type-specific fields. `--handle` remains
a short alias for `--avatar-handle`. Exact model-run ids such as
`sume/avatar-1.0/generate` are accepted for compatibility mode when `--model`
is supplied; older Avatar model ids remain explicit aliases.

Photo avatar creation currently accepts a public image URL:

Expand Down
8 changes: 5 additions & 3 deletions docs/agent-workflows.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,10 @@ sume avatars list --handle presenter --agent --json
sume avatars get <avatar_id> --agent --json
```

The create command submits `POST /v1/models/sume/avatar/v1.0/runs`. For exact
model-run payloads, use `--payload-json` or `--payload-file`.
The create command submits `POST /v1/avatar-1.0/generate` by default. For exact
model-run compatibility payloads, use `--payload-json` or `--payload-file`;
`--model sume/avatar-1.0/generate` and legacy aliases remain accepted when an
exact model-run path is needed.
Photo avatar creation uses a public image URL:

```bash
Expand Down Expand Up @@ -97,7 +99,7 @@ sume avatar-videos create \
--json
```

The create command submits `POST /v1/models/sume/avatar-video/v1.0/runs`.
The create command submits `POST /v1/avatar-1.0/talking-video`.
Use `--avatar-handle` with a ready avatar handle. Scripts are estimated locally
and by the API; accepted target duration is 4-60 seconds inclusive.

Expand Down
8 changes: 6 additions & 2 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ Current launch-supported paths are:
- `GET /jobs/:id/result`
- `GET /jobs/:id/events`
- `POST /jobs/:id/cancel`
- `POST /models/sume/avatar/v1.0/runs`
- `POST /models/sume/avatar-video/v1.0/runs`
- `POST /avatar-1.0/generate`
- `POST /avatar-1.0/talking-video`
- `GET /avatars`
- `GET /avatars/:id`
- `GET /avatar-videos`
Expand All @@ -57,6 +57,10 @@ claim media generation has completed unless the API returns a completed
job/result. Generic image/video routes and raw provider model ids are not part
of the current public CLI surface.

Legacy model-run ids and routes remain explicit compatibility aliases, but
CLI defaults should prefer the primary Avatar 1.0 generate and talking-video
routes above.

CLI submit commands must require explicit confirmation before calling mutating
routes. `--confirm-submit` is the generic write gate; `--confirm-paid` is the
provider/credit-spend alias for generation submits.
Expand Down
3 changes: 2 additions & 1 deletion src/commands/avatar-videos.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import {
DEFAULT_AVATAR_VIDEO_QUALITY,
readAvatarVideoQuality,
} from "../lib/quality.js";
import { AVATAR_PRIMARY_TALKING_VIDEO_ENDPOINT } from "../lib/models.js";

type AvatarVideoCreateOptions = SubmissionOptions & {
avatarHandle?: string;
Expand Down Expand Up @@ -158,7 +159,7 @@ export function registerAvatarVideosCommand(program: Command) {
assertAvatarVideoScriptDuration(payload);
requireSubmitConfirmation(options);

const endpoint = "/models/sume/avatar-video/v1.0/runs";
const endpoint = AVATAR_PRIMARY_TALKING_VIDEO_ENDPOINT;
const result = await createClient().post(endpoint, payload, {
headers: idempotencyHeaders(options),
});
Expand Down
10 changes: 5 additions & 5 deletions src/commands/avatars.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { CliError } from "../lib/errors.js";
import { renderResult } from "../lib/render.js";
import {
AVATAR_MODEL_IDS,
avatarModelRunEndpoint,
avatarGenerateEndpoint,
normalizeAvatarModelId,
type AvatarModelId,
} from "../lib/models.js";
Expand Down Expand Up @@ -157,7 +157,7 @@ export function registerAvatarsCommand(program: Command) {
.option("--payload-file <path>", "Read exact API request body from a JSON file.")
.option(
"--model <model>",
`Public Avatar model id: ${AVATAR_MODEL_IDS.base}.`,
`Optional exact Avatar model-run id. Defaults to the primary ${AVATAR_MODEL_IDS.generate} route.`,
)
.option("--type <type>", "Avatar request type: prompt, photo, or props.", "prompt")
.option(
Expand Down Expand Up @@ -205,7 +205,7 @@ export function registerAvatarsCommand(program: Command) {
requireSubmitConfirmation(options);

const modelId = readAvatarModelId(options);
const endpoint = avatarModelRunEndpoint(modelId);
const endpoint = avatarGenerateEndpoint(modelId);
const result = await client.post(endpoint, payload, {
headers: idempotencyHeaders(options),
});
Expand Down Expand Up @@ -434,8 +434,8 @@ function readPropsEthnicity(value: string | undefined) {
);
}

function readAvatarModelId(options: AvatarCreateOptions): AvatarModelId {
return normalizeAvatarModelId(options.model);
function readAvatarModelId(options: AvatarCreateOptions): AvatarModelId | undefined {
return options.model ? normalizeAvatarModelId(options.model) : undefined;
}

function readAvatarListStatus(options: AvatarListOptions) {
Expand Down
8 changes: 6 additions & 2 deletions src/lib/batch-workflows.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ import {
DEFAULT_AVATAR_VIDEO_QUALITY,
type AvatarVideoQuality,
} from "./quality.js";
import {
AVATAR_PRIMARY_GENERATE_ENDPOINT,
AVATAR_PRIMARY_TALKING_VIDEO_ENDPOINT,
} from "./models.js";

export type BatchKind = "avatar" | "avatar-video";

Expand Down Expand Up @@ -79,8 +83,8 @@ export async function submitBatch(
const client = createClient();
const endpoint =
plan.workflow === "avatar"
? "/models/sume/avatar/v1.0/runs"
: "/models/sume/avatar-video/v1.0/runs";
? AVATAR_PRIMARY_GENERATE_ENDPOINT
: AVATAR_PRIMARY_TALKING_VIDEO_ENDPOINT;

for (const planItem of plan.items) {
const existing = state.items.find((item) => item.id === planItem.id);
Expand Down
29 changes: 24 additions & 5 deletions src/lib/models.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { CliError } from "./errors.js";

export const AVATAR_MODEL_IDS = {
base: "sume/avatar/v1.0",
generate: "sume/avatar-1.0/generate",
legacyBase: "sume/avatar/v1.0",
} as const;

export type AvatarModelId =
Expand All @@ -12,20 +13,38 @@ export const AVATAR_MODEL_ID_VALUES = Object.values(
) as AvatarModelId[];

const AVATAR_MODEL_ALIASES = {
"sume/avatar-1.0": AVATAR_MODEL_IDS.base,
"sume/avatar/v1": AVATAR_MODEL_IDS.base,
"sume/avatar-1.0": AVATAR_MODEL_IDS.generate,
"sume/avatar/v1": AVATAR_MODEL_IDS.legacyBase,
} as const satisfies Record<string, AvatarModelId>;

export const AVATAR_PRIMARY_GENERATE_ENDPOINT = "/avatar-1.0/generate";
export const AVATAR_PRIMARY_TALKING_VIDEO_ENDPOINT = "/avatar-1.0/talking-video";

export const AVATAR_VIDEO_MODEL_IDS = {
talkingVideo: "sume/avatar-1.0/talking-video",
legacyBase: "sume/avatar-video/v1.0",
} as const;

export function avatarModelRunEndpoint(modelId: AvatarModelId) {
return `/models/${modelId}/runs`;
}

export function avatarGenerateEndpoint(modelId?: AvatarModelId) {
return modelId
? avatarModelRunEndpoint(modelId)
: AVATAR_PRIMARY_GENERATE_ENDPOINT;
}

export function avatarTalkingVideoEndpoint() {
return AVATAR_PRIMARY_TALKING_VIDEO_ENDPOINT;
}

export function normalizeAvatarModelId(
value: unknown,
optionName = "model",
): AvatarModelId {
if (value === undefined || value === null || value === "") {
return AVATAR_MODEL_IDS.base;
return AVATAR_MODEL_IDS.generate;
}

const candidate = String(value).trim();
Expand All @@ -36,7 +55,7 @@ export function normalizeAvatarModelId(
if (canonical) return canonical;

throw new CliError(
`${optionName} must be ${AVATAR_MODEL_IDS.base}.`,
`${optionName} must be ${AVATAR_MODEL_ID_VALUES.join(" or ")}.`,
{ code: "invalid_argument" },
);
}
26 changes: 17 additions & 9 deletions src/lib/tool-registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,11 +110,18 @@ const submitConfirmation = {
accepted_flags: ["confirm_submit", "confirm_paid"],
required: true,
};
const avatarModelSchemaValues = [
AVATAR_MODEL_IDS.generate,
AVATAR_MODEL_IDS.legacyBase,
"sume/avatar-1.0",
"sume/avatar/v1",
];
const avatarModelProperties = {
model: {
...stringProperty,
enum: [AVATAR_MODEL_IDS.base],
description: "Public Avatar model id.",
enum: avatarModelSchemaValues,
description:
"Optional exact Avatar model-run id. Omit to use the primary Avatar 1.0 generate route.",
},
};
const writeConfirmation = {
Expand Down Expand Up @@ -166,8 +173,9 @@ const mcpAvatarCreateBaseProperties = {
...mcpPaidGenerationProperties,
model: {
...stringProperty,
enum: [AVATAR_MODEL_IDS.base],
description: "Optional public Avatar model id. Omit for Avatar 1.0.",
enum: avatarModelSchemaValues,
description:
"Optional exact Avatar model-run id. Omit to use the primary Avatar 1.0 generate route.",
},
avatar_handle: {
...stringProperty,
Expand Down Expand Up @@ -1064,9 +1072,9 @@ const staticToolDefinitions: Record<string, StaticToolDefinition> = {
...mcpPaidGenerationProperties,
model: {
...stringProperty,
enum: [AVATAR_MODEL_IDS.base],
enum: avatarModelSchemaValues,
description:
"Optional public Avatar model id. Omit for Avatar 1.0.",
"Optional exact Avatar model-run id. Omit to use the primary Avatar 1.0 generate route.",
},
payload: {
...passthroughObjectSchema({}, []),
Expand All @@ -1087,7 +1095,7 @@ const staticToolDefinitions: Record<string, StaticToolDefinition> = {
],
constraints: [
"payload_json and payload_file are exact-body escape hatches and are mutually exclusive.",
`model defaults to ${AVATAR_MODEL_IDS.base}.`,
`model defaults to the primary /v1/avatar-1.0/generate route; exact model mode accepts ${AVATAR_MODEL_IDS.generate}.`,
"Flag-built requests submit avatar_handle plus an input union.",
"type prompt requires avatar_handle plus prompt.",
"type photo requires avatar_handle plus image_url.",
Expand Down Expand Up @@ -1323,7 +1331,7 @@ const staticToolDefinitions: Record<string, StaticToolDefinition> = {
"avatar-videos.create": {
command: "sume avatar-videos create --confirm-submit --json ...",
description:
"Submit an Avatar Video 1.0 model run through /v1/models/sume/avatar-video/v1.0/runs.",
"Submit an Avatar Video 1.0 talking-video run through /v1/avatar-1.0/talking-video.",
confirmation: submitConfirmation,
input_schema: submitCliSchema(
{
Expand Down Expand Up @@ -1416,7 +1424,7 @@ const staticToolDefinitions: Record<string, StaticToolDefinition> = {
payload: {
...passthroughObjectSchema({}, []),
description:
"Advanced exact /v1/models/sume/avatar-video/v1.0/runs request body. Prefer top-level friendly fields for agent use.",
"Advanced exact /v1/avatar-1.0/talking-video request body. Prefer top-level friendly fields for agent use.",
},
},
["idempotency_key", "max_spend_usd"],
Expand Down
18 changes: 10 additions & 8 deletions src/mcp/tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import { CliError } from "../lib/errors.js";
import { uploadLocalAsset } from "../lib/local-asset-upload.js";
import {
AVATAR_MODEL_IDS,
avatarModelRunEndpoint,
AVATAR_VIDEO_MODEL_IDS,
avatarGenerateEndpoint,
avatarTalkingVideoEndpoint,
normalizeAvatarModelId,
} from "../lib/models.js";
import { validateAvatarVideoScriptDuration } from "../lib/avatar-video-duration.js";
Expand Down Expand Up @@ -52,8 +54,6 @@ export type McpToolFilterOptions = {
toolsets?: McpToolset[];
};

const AVATAR_VIDEO_MODEL_ID = "sume/avatar-video/v1.0";

export const DEFAULT_MCP_TOOLSETS: McpToolset[] = [
"health",
"account",
Expand Down Expand Up @@ -170,7 +170,8 @@ const avatarVideoCreateInputSchema = z
const avatarSubmissionInputSchema = paidSubmissionInputSchema.extend({
model: z
.enum([
AVATAR_MODEL_IDS.base,
AVATAR_MODEL_IDS.generate,
AVATAR_MODEL_IDS.legacyBase,
"sume/avatar-1.0",
"sume/avatar/v1",
])
Expand All @@ -189,7 +190,8 @@ const avatarCreateBaseInputSchema = z
max_spend_usd: z.number().min(0),
model: z
.enum([
AVATAR_MODEL_IDS.base,
AVATAR_MODEL_IDS.generate,
AVATAR_MODEL_IDS.legacyBase,
"sume/avatar-1.0",
"sume/avatar/v1",
])
Expand Down Expand Up @@ -539,7 +541,7 @@ async function submitAvatarGeneration({
return submitPaidGeneration({
client,
decorateResult: (value) => addAvatarSummary(value, requestedAvatar),
endpoint: avatarModelRunEndpoint(modelId),
endpoint: avatarGenerateEndpoint(model ? modelId : undefined),
idempotencyKey,
maxSpendUsd,
model: modelId,
Expand Down Expand Up @@ -1248,10 +1250,10 @@ export const mcpTools: SumeMcpTool[] = [
assertAvatarVideoScriptDuration(payload);
return submitPaidGeneration({
client,
endpoint: "/models/sume/avatar-video/v1.0/runs",
endpoint: avatarTalkingVideoEndpoint(),
idempotencyKey: parsed.idempotency_key,
maxSpendUsd: parsed.max_spend_usd,
model: AVATAR_VIDEO_MODEL_ID,
model: AVATAR_VIDEO_MODEL_IDS.talkingVideo,
payload,
dryRun: parsed.dry_run,
toolName: "avatar-videos.create",
Expand Down
Loading
Loading