refactor: align provider and task contracts
This commit is contained in:
+136
-21
@@ -1,6 +1,7 @@
|
||||
export const CONTRACT_VERSION = "2026-08-20.v1" as const;
|
||||
export const API_PREFIX = "/api/v1" as const;
|
||||
export const WS_PATH = "/api/v1/ws/tasks" as const;
|
||||
export const DOMAIN_SNAPSHOT_VERSION = 2 as const;
|
||||
|
||||
export const ErrorCodes = {
|
||||
Unauthorized: "UNAUTHORIZED",
|
||||
@@ -91,13 +92,16 @@ export type CapabilitySchema = {
|
||||
async: boolean;
|
||||
};
|
||||
|
||||
/** Values are stored by capability so management forms never need a raw JSON
|
||||
* editor while a task can still snapshot the exact public contract. */
|
||||
export type CapabilityParameters = Partial<Record<ModelCapability, Record<string, string | number | boolean | string[]>>>;
|
||||
|
||||
export type PublicResolution = {
|
||||
id: string;
|
||||
label: string;
|
||||
width: number;
|
||||
height: number;
|
||||
priceMultiplier: number;
|
||||
/** Standard image ratios enabled for this billing preset; `auto` is implicit. */
|
||||
/** Standard image ratios enabled for this public model; `auto` is implicit. */
|
||||
ratios?: string[];
|
||||
};
|
||||
|
||||
@@ -130,19 +134,122 @@ export type PublicModelProduct = {
|
||||
name: string;
|
||||
tier: "basic" | "advanced" | "flagship";
|
||||
capabilities: ModelCapability[];
|
||||
capabilityParameters?: CapabilityParameters;
|
||||
resolutions: PublicResolution[];
|
||||
basePrice: number;
|
||||
currencyUnit: string;
|
||||
enabled: boolean;
|
||||
version?: number;
|
||||
capabilitiesSchema?: CapabilitySchema[];
|
||||
publicErrorMessages?: Partial<Record<ErrorCode, string>>;
|
||||
maxCount?: number;
|
||||
maxConcurrent?: number;
|
||||
};
|
||||
|
||||
export type InternalModelProduct = PublicModelProduct & {
|
||||
channelGroupId?: string;
|
||||
export type InternalModelProduct = PublicModelProduct;
|
||||
|
||||
export type PricingRuleTier = {
|
||||
minQuantity: number;
|
||||
maxQuantity?: number;
|
||||
unitPrice: number;
|
||||
};
|
||||
|
||||
export type PricingRule = {
|
||||
id: string;
|
||||
publicModelId: string;
|
||||
resolutionId?: string;
|
||||
billingUnit: "task" | "output";
|
||||
tiers: PricingRuleTier[];
|
||||
multiplier: number;
|
||||
balanceUnitVersion: number;
|
||||
effectiveFrom?: string;
|
||||
effectiveTo?: string;
|
||||
enabled: boolean;
|
||||
version: number;
|
||||
};
|
||||
|
||||
export type ChannelModel = {
|
||||
requestModelId: string;
|
||||
source: "discovered" | "manual";
|
||||
enabled: boolean;
|
||||
lastSeenAt?: string;
|
||||
};
|
||||
|
||||
export type ModelRouteChannel = {
|
||||
channelId: string;
|
||||
priorityTier: number;
|
||||
orderWithinTier: number;
|
||||
retryBudget: number;
|
||||
};
|
||||
|
||||
export type ModelRoute = {
|
||||
publicModelId: string;
|
||||
channels: ModelRouteChannel[];
|
||||
strategy: "strict" | "smart-within-priority";
|
||||
retryBudget: number;
|
||||
version: number;
|
||||
};
|
||||
|
||||
export type ChannelHealthStatus = "healthy" | "degraded" | "open" | "half-open";
|
||||
export type ChannelHealthSampleStatus = "success" | "slow" | "network_error" | "http_error" | "provider_error" | "empty" | "probing";
|
||||
|
||||
export type ChannelHealthSample = {
|
||||
id: string;
|
||||
channelId: string;
|
||||
source: "probe" | "task";
|
||||
status: ChannelHealthSampleStatus;
|
||||
latencyMs?: number;
|
||||
httpStatus?: number;
|
||||
requestPath?: string;
|
||||
requestAttempted: boolean;
|
||||
errorCode?: string;
|
||||
createdAt: string;
|
||||
};
|
||||
|
||||
export type CapacitySnapshot = {
|
||||
current: number;
|
||||
limit: number;
|
||||
available: number;
|
||||
};
|
||||
|
||||
export type RouteSnapshotCandidate = ModelRouteChannel & {
|
||||
requestModelId: string;
|
||||
};
|
||||
|
||||
export type RouteSnapshot = {
|
||||
groupId: string;
|
||||
groupVersion: number;
|
||||
publicModelId: string;
|
||||
routeVersion: number;
|
||||
orderedCandidates: RouteSnapshotCandidate[];
|
||||
strategy: ModelRoute["strategy"];
|
||||
totalRetryBudget: number;
|
||||
};
|
||||
|
||||
export type PricingSnapshot = {
|
||||
ruleId: string;
|
||||
ruleVersion: number;
|
||||
publicModelId: string;
|
||||
resolutionId?: string;
|
||||
unitPrice: number;
|
||||
quantity: number;
|
||||
billingUnit: PricingRule["billingUnit"];
|
||||
multiplier: number;
|
||||
balanceUnitVersion: number;
|
||||
};
|
||||
|
||||
export type ModelSnapshot = {
|
||||
publicModelId: string;
|
||||
modelVersion: number;
|
||||
name: string;
|
||||
tier: PublicModelProduct["tier"];
|
||||
capabilities: ModelCapability[];
|
||||
capabilityParameters?: CapabilityParameters;
|
||||
resolutionId?: string;
|
||||
resolution?: PublicResolution;
|
||||
};
|
||||
|
||||
export type PlanSnapshot = {
|
||||
planId?: string;
|
||||
version?: number;
|
||||
queuePriority: number;
|
||||
maxConcurrent: number;
|
||||
};
|
||||
|
||||
export type BalanceSummary = {
|
||||
@@ -242,18 +349,16 @@ export type GenerationTask = OrganizationScope & {
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
eventSequence: number;
|
||||
channelGroupId: string;
|
||||
routeSnapshotVersion: number;
|
||||
attempts: GenerationAttempt[];
|
||||
outputs: Array<{ id: string; mimeType: string; url?: string; status: "succeeded" | "failed"; unitPriceSnapshot?: number; chargedAmount?: number; metadata?: ImageOutputMetadata }>;
|
||||
publicErrorCode?: ErrorCode;
|
||||
prompt?: string;
|
||||
resolutionPresetId?: string;
|
||||
count?: number;
|
||||
pricingSnapshot?: { basePrice: number; multiplier: number; unitVersion: number };
|
||||
modelSnapshot?: { publicModelId: string; version: number };
|
||||
routeSnapshot?: { groupId: string; version: number; channelIds: string[]; retryBudget?: number; channelRetryBudgets?: Record<string, number> };
|
||||
planSnapshot?: { planId?: string; version?: number; queuePriority: number; maxConcurrent: number };
|
||||
pricingSnapshot: PricingSnapshot;
|
||||
modelSnapshot: ModelSnapshot;
|
||||
routeSnapshot: RouteSnapshot;
|
||||
planSnapshot: PlanSnapshot;
|
||||
leaseExpiresAt?: string;
|
||||
/** Fences a late worker/provider response after a lease is recovered. */
|
||||
leaseToken?: string;
|
||||
@@ -405,7 +510,9 @@ export type AdminSessionResponse = {
|
||||
|
||||
export type AdminPage<T> = {
|
||||
items: T[];
|
||||
total?: number;
|
||||
total: number;
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
nextCursor?: string;
|
||||
};
|
||||
|
||||
@@ -413,19 +520,27 @@ export type AdminChannelSummary = {
|
||||
id: string;
|
||||
label: string;
|
||||
enabled: boolean;
|
||||
priority: number;
|
||||
health: "healthy" | "degraded" | "open";
|
||||
health: ChannelHealthStatus | "disabled";
|
||||
version: number;
|
||||
retryBudget: number;
|
||||
consecutiveFailures: number;
|
||||
providerType?: "generic" | "openai-images";
|
||||
providerName?: string;
|
||||
providerModelId?: string;
|
||||
enabledModelIds?: string[];
|
||||
models?: ChannelModel[];
|
||||
modelMappings?: Array<{ displayModelId: string; requestModelId: string }>;
|
||||
resolutionModelMap?: Record<string, string>;
|
||||
baseUrl?: string;
|
||||
secretConfigured?: boolean;
|
||||
capacity?: CapacitySnapshot;
|
||||
circuitResetAt?: string;
|
||||
groupIds?: string[];
|
||||
samples?: Array<ChannelHealthSample | null>;
|
||||
};
|
||||
|
||||
export type AdminChannelGroupSummary = {
|
||||
id: string;
|
||||
name?: string;
|
||||
enabled: boolean;
|
||||
version: number;
|
||||
routes: ModelRoute[];
|
||||
};
|
||||
|
||||
export type AdminTaskSummary = {
|
||||
|
||||
Reference in New Issue
Block a user