feat: wave-3 video surfaces — lipsync, video matte, video upscale, chain jobs (ADR-0025)
- videogen.LipSyncer/LipsyncProvider: SadTalker talking heads via
POST /upstream/<id>/v1/talking_head (multipart image+audio parts,
still/enhance/preprocess flags) -> mp4.
- videogen.VideoBackgroundRemover/VideoBackgroundRemovalProvider:
POST /upstream/<id>/v1/video/matte (output greenscreen_mp4|alpha_webm).
- videogen.VideoUpscaler/VideoUpscaleProvider:
POST /upstream/<id>/v1/video/upscale (scale 2|4).
- videogen.Chainer/ChainerProvider: async long-video chain-job client —
SubmitChain (JSON POST /v1/video/chain, init_image_b64), ChainStatus
(GET /v1/jobs/{id}, tolerant segment-id decode), ChainResult,
ChainSegmentResult (partial delivery after mid-chain failure); hostile
job-id path rejection.
- Shared singleVideoResult validation (positive video evidence) + a
videoInputFilename hint helper; httptest contract tests per surface;
ADR-0025 (index row deferred — MJ-A backfills the ADR index table and
parallel edits would conflict).
Co-Authored-By: Claude Fable 5 <[email protected]>
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
package videogen
|
||||
|
||||
import "context"
|
||||
|
||||
// ChainSegment is one prompt in a multi-segment ("long video") chain.
|
||||
type ChainSegment struct {
|
||||
// Prompt describes this segment. Required.
|
||||
Prompt string
|
||||
|
||||
// Seconds is the segment's requested length; 0 = backend default.
|
||||
Seconds float64
|
||||
}
|
||||
|
||||
// ChainRequest asks a chain orchestrator to generate a long video as a
|
||||
// sequence of segments, each continuing from the previous segment's last
|
||||
// frame. Zero values mean "backend default" (ADR-0025).
|
||||
type ChainRequest struct {
|
||||
// Segments are the per-segment prompts in order. At least one required.
|
||||
Segments []ChainSegment
|
||||
|
||||
// InitImage optionally conditions the FIRST segment on a starting frame
|
||||
// (image-to-video); nil = pure text-to-video.
|
||||
InitImage []byte
|
||||
|
||||
// SmoothJoins asks the orchestrator to interpolate across segment
|
||||
// boundaries (RIFE-style) so cuts don't pop.
|
||||
SmoothJoins bool
|
||||
|
||||
// Size is the requested resolution, e.g. "1280x704"; "" = backend
|
||||
// default.
|
||||
Size string
|
||||
}
|
||||
|
||||
// ChainJob is a chain job's progress snapshot.
|
||||
type ChainJob struct {
|
||||
// Status is the backend's job state, passed through verbatim
|
||||
// (e.g. "queued", "running", "done", "failed").
|
||||
Status string
|
||||
|
||||
// Segment is the segment currently being generated (1-based); Total is
|
||||
// the segment count.
|
||||
Segment int
|
||||
Total int
|
||||
|
||||
// SegmentIDs are the COMPLETED per-segment artifacts, fetchable via
|
||||
// ChainSegmentResult — a mid-chain failure still leaves these
|
||||
// retrievable, so multi-minute GPU output is never discarded.
|
||||
SegmentIDs []string
|
||||
|
||||
// Raw is the provider-native job payload. May be nil.
|
||||
Raw any
|
||||
}
|
||||
|
||||
// Chainer drives a multi-segment video-chain job. Unlike Model.Generate it
|
||||
// is deliberately ASYNC — a chain runs through multiple GPU loads for many
|
||||
// minutes, so callers submit, poll, and fetch instead of holding one
|
||||
// blocking call open.
|
||||
type Chainer interface {
|
||||
// SubmitChain starts a chain job and returns its job id.
|
||||
SubmitChain(ctx context.Context, req ChainRequest) (string, error)
|
||||
|
||||
// ChainStatus reports the job's progress. Polling also signals liveness
|
||||
// to backends that unload idle orchestrators.
|
||||
ChainStatus(ctx context.Context, jobID string) (*ChainJob, error)
|
||||
|
||||
// ChainResult fetches the finished, concatenated clip.
|
||||
ChainResult(ctx context.Context, jobID string) (*Result, error)
|
||||
|
||||
// ChainSegmentResult fetches one completed segment's clip (n indexes the
|
||||
// job's segment list) — the partial-delivery path when a chain dies
|
||||
// mid-run.
|
||||
ChainSegmentResult(ctx context.Context, jobID string, n int) (*Result, error)
|
||||
}
|
||||
|
||||
// ChainerModelOption configures a Chainer at construction time. Reserved for
|
||||
// future per-model settings.
|
||||
type ChainerModelOption func(*ChainerModelConfig)
|
||||
|
||||
// ChainerModelConfig carries per-model construction settings.
|
||||
type ChainerModelConfig struct{}
|
||||
|
||||
// ApplyChainerModelOptions folds options into a config.
|
||||
func ApplyChainerModelOptions(opts []ChainerModelOption) ChainerModelConfig {
|
||||
var cfg ChainerModelConfig
|
||||
for _, opt := range opts {
|
||||
opt(&cfg)
|
||||
}
|
||||
return cfg
|
||||
}
|
||||
|
||||
// ChainerProvider mints Chainers bound to one backend.
|
||||
type ChainerProvider interface {
|
||||
// Name is the registry identifier for the provider.
|
||||
Name() string
|
||||
|
||||
// ChainerModel returns a Chainer bound to the given id (passed through
|
||||
// to the backend verbatim; no catalog validation).
|
||||
ChainerModel(id string, opts ...ChainerModelOption) (Chainer, error)
|
||||
}
|
||||
Reference in New Issue
Block a user