Merge pull request 'feat(server): dispatch /v1/videos/sync by model' (#1) from feat/video-routes into main

Reviewed-on: #1
This commit was merged in pull request #1.
This commit is contained in:
2026-07-12 14:13:05 +00:00
3 changed files with 69 additions and 1 deletions
+5 -1
View File
@@ -37,7 +37,8 @@ const (
)
// captureFieldsByPath overrides the default capture mask for routes carrying
// large binary payloads (audio/image) where storing the full body is wasteful.
// large binary payloads (audio/image/video) where storing the full body is
// wasteful.
var captureFieldsByPath = map[string]captureFields{
"/v1/audio/speech": captureReqAll | captureRespHeaders,
"/v1/audio/voices": captureReqHeaders | captureRespAll,
@@ -46,6 +47,9 @@ var captureFieldsByPath = map[string]captureFields{
"/v1/images/edits": captureReqHeaders | captureRespHeaders,
"/sdapi/v1/txt2img": captureReqAll | captureRespHeaders,
"/sdapi/v1/img2img": captureReqHeaders | captureRespHeaders,
// video: the request multipart can carry a conditioning frame and the
// response body IS the clip — headers only, both directions.
"/v1/videos/sync": captureReqHeaders | captureRespHeaders,
}
// captureFieldsFor returns the capture mask for a request path. Unlisted routes
+9
View File
@@ -80,6 +80,15 @@ var modelPostJSONRoutes = []string{
var modelPostFormRoutes = []string{
"/v1/audio/transcriptions",
"/v1/images/edits",
// video generation: ONLY the blocking sync shape (vLLM-Omni
// /v1/videos/sync — the response body is the clip). The async
// OpenAI-style POST /v1/videos is deliberately NOT dispatched: its
// poll/download companions (GET /v1/videos/{id}[/content]) carry no
// model field to route by, so registering creation alone would start
// unretrievable upstream jobs. Extraction is content-type driven, so
// JSON bodies dispatch here too.
"/v1/videos/sync",
}
// modelGetRoutes are model-dispatched GET endpoints (the model arrives as a
+55
View File
@@ -1,9 +1,11 @@
package server
import (
"bytes"
"context"
"encoding/json"
"io"
"mime/multipart"
"net/http"
"net/http/httptest"
"strings"
@@ -339,3 +341,56 @@ func TestServer_LogStream_UnknownID_Returns400(t *testing.T) {
t.Errorf("status=%d want 400", w.Code)
}
}
func TestServer_VideoRoutesDispatch(t *testing.T) {
s := newTestServer(
newStubRouter([]string{"videogen-model"}, "video response"),
newStubRouter(nil, ""),
)
// Multipart form on /v1/videos/sync.
var buf bytes.Buffer
mw := multipart.NewWriter(&buf)
mw.WriteField("model", "videogen-model")
mw.WriteField("prompt", "a cat")
mw.Close()
req := httptest.NewRequest(http.MethodPost, "/v1/videos/sync", &buf)
req.Header.Set("Content-Type", mw.FormDataContentType())
w := httptest.NewRecorder()
s.ServeHTTP(w, req)
if w.Code != http.StatusOK || w.Body.String() != "video response" {
t.Fatalf("/v1/videos/sync: status=%d body=%q", w.Code, w.Body.String())
}
// JSON bodies dispatch too (extraction is content-type driven).
jsonReq := httptest.NewRequest(http.MethodPost, "/v1/videos/sync",
strings.NewReader(`{"model":"videogen-model","prompt":"a cat"}`))
jsonReq.Header.Set("Content-Type", "application/json")
w = httptest.NewRecorder()
s.ServeHTTP(w, jsonReq)
if w.Code != http.StatusOK || w.Body.String() != "video response" {
t.Fatalf("/v1/videos/sync json: status=%d body=%q", w.Code, w.Body.String())
}
// Unknown model on the video route still 404s.
nopeReq := httptest.NewRequest(http.MethodPost, "/v1/videos/sync",
strings.NewReader(`{"model":"nope","prompt":"a cat"}`))
nopeReq.Header.Set("Content-Type", "application/json")
w = httptest.NewRecorder()
s.ServeHTTP(w, nopeReq)
if w.Code != http.StatusNotFound {
t.Fatalf("/v1/videos/sync unknown model: status=%d want 404", w.Code)
}
// The async job-creation route is NOT dispatched (no poll routes to
// complete the flow) — a stray OpenAI-videos client gets a clean 404
// instead of an unretrievable upstream job.
asyncReq := httptest.NewRequest(http.MethodPost, "/v1/videos",
strings.NewReader(`{"model":"videogen-model","prompt":"a cat"}`))
asyncReq.Header.Set("Content-Type", "application/json")
w = httptest.NewRecorder()
s.ServeHTTP(w, asyncReq)
if w.Code != http.StatusNotFound {
t.Fatalf("POST /v1/videos: status=%d want 404 (async family not routed)", w.Code)
}
}