@@ -243,7 +243,7 @@ func TestVideoEstimator_Default(t *testing.T) {
243243// per-frame count.
244244func TestVideoEstimator_StaticTokensPerFrame (t * testing.T ) {
245245 b := estimateBackend {vid : newVideoEstimator (& estimateConfig {Video : & videoEstimateConfig {
246- TokensPerFrame : & tokensPerFrameConfig {Mode : videoTPFModeStatic , StaticToken : 100 },
246+ TokensPerFrame : & tokensPerFrameConfig {Mode : videoTPFModeStatic , Static : & tokensPerFrameStaticMode { StaticToken : 100 } },
247247 }})}
248248 tp , err := b .produce (context .Background (), chatVideoBody ("https://example.com/clip.mp4" ))
249249 require .NoError (t , err )
@@ -255,7 +255,7 @@ func TestVideoEstimator_StaticTokensPerFrame(t *testing.T) {
255255// per-frame count for the default resolution.
256256func TestVideoEstimator_DynamicFactor (t * testing.T ) {
257257 b := estimateBackend {vid : newVideoEstimator (& estimateConfig {Video : & videoEstimateConfig {
258- TokensPerFrame : & tokensPerFrameConfig {Mode : videoTPFModeDynamic , Factor : 2048 },
258+ TokensPerFrame : & tokensPerFrameConfig {Mode : videoTPFModeDynamic , Dynamic : & tokensPerFrameDynamicMode { Factor : 2048 } },
259259 }})}
260260 tp , err := b .produce (context .Background (), chatVideoBody ("https://example.com/clip.mp4" ))
261261 require .NoError (t , err )
@@ -269,8 +269,8 @@ func TestVideoEstimator_DynamicFactor(t *testing.T) {
269269func TestVideoEstimator_SampledFrames (t * testing.T ) {
270270 b := estimateBackend {vid : newVideoEstimator (& estimateConfig {Video : & videoEstimateConfig {
271271 DefaultDuration : 8 ,
272- Frames : & framesConfig {Mode : videoFramesModeSampled , SampleFPS : 2 },
273- TokensPerFrame : & tokensPerFrameConfig {Mode : videoTPFModeStatic , StaticToken : 10 },
272+ Frames : & framesConfig {Mode : videoFramesModeSampled , Sampled : & framesSampledMode { SampleFPS : 2 } },
273+ TokensPerFrame : & tokensPerFrameConfig {Mode : videoTPFModeStatic , Static : & tokensPerFrameStaticMode { StaticToken : 10 } },
274274 }})}
275275 tp , err := b .produce (context .Background (), chatVideoBody ("https://example.com/clip.mp4" ))
276276 require .NoError (t , err )
@@ -282,15 +282,29 @@ func TestVideoEstimator_SampledFrames(t *testing.T) {
282282func TestVideoEstimator_StridedFramesCapped (t * testing.T ) {
283283 b := estimateBackend {vid : newVideoEstimator (& estimateConfig {Video : & videoEstimateConfig {
284284 DefaultDuration : 10 ,
285- Frames : & framesConfig {Mode : videoFramesModeStrided , DefaultSourceFPS : 24 , FrameStride : 4 , MaxFrames : 16 },
286- TokensPerFrame : & tokensPerFrameConfig {Mode : videoTPFModeStatic , StaticToken : 100 },
285+ Frames : & framesConfig {Mode : videoFramesModeStrided , MaxFrames : 16 , Strided : & framesStridedMode { DefaultSourceFPS : 24 , FrameStride : 4 } },
286+ TokensPerFrame : & tokensPerFrameConfig {Mode : videoTPFModeStatic , Static : & tokensPerFrameStaticMode { StaticToken : 100 } },
287287 }})}
288288 tp , err := b .produce (context .Background (), chatVideoBody ("https://example.com/clip.mp4" ))
289289 require .NoError (t , err )
290290 // duration*sourceFPS/stride = 10*24/4 = 60, capped to 16; 16*100 tokens.
291291 assert .Equal (t , 16 * 100 , tp .MultiModalFeatures [0 ].Length , "strided-frames video length" )
292292}
293293
294+ // TestVideoEstimator_StridedFramesFloored asserts strided frames apply the
295+ // minFrames floor when the strided count falls below it.
296+ func TestVideoEstimator_StridedFramesFloored (t * testing.T ) {
297+ b := estimateBackend {vid : newVideoEstimator (& estimateConfig {Video : & videoEstimateConfig {
298+ DefaultDuration : 1 ,
299+ Frames : & framesConfig {Mode : videoFramesModeStrided , MinFrames : 8 , Strided : & framesStridedMode {DefaultSourceFPS : 24 , FrameStride : 4 }},
300+ TokensPerFrame : & tokensPerFrameConfig {Mode : videoTPFModeStatic , Static : & tokensPerFrameStaticMode {StaticToken : 100 }},
301+ }})}
302+ tp , err := b .produce (context .Background (), chatVideoBody ("https://example.com/clip.mp4" ))
303+ require .NoError (t , err )
304+ // duration*sourceFPS/stride = 1*24/4 = 6, floored to 8; 8*100 tokens.
305+ assert .Equal (t , 8 * 100 , tp .MultiModalFeatures [0 ].Length , "strided-frames floored video length" )
306+ }
307+
294308// TestVideoEstimator_MaxVideoTokens asserts the overall cap bounds the total
295309// placeholder count.
296310func TestVideoEstimator_MaxVideoTokens (t * testing.T ) {
@@ -309,8 +323,8 @@ func TestVideoEstimator_Qwen3AndGemma4(t *testing.T) {
309323 qwen3 := estimateBackend {vid : newVideoEstimator (& estimateConfig {Video : & videoEstimateConfig {
310324 DefaultResolution : & resolution {Width : 640 , Height : 480 },
311325 DefaultDuration : 10 ,
312- TokensPerFrame : & tokensPerFrameConfig {Mode : videoTPFModeDynamic , Factor : 1024 },
313- Frames : & framesConfig {Mode : videoFramesModeSampled , SampleFPS : 2 },
326+ TokensPerFrame : & tokensPerFrameConfig {Mode : videoTPFModeDynamic , Dynamic : & tokensPerFrameDynamicMode { Factor : 1024 } },
327+ Frames : & framesConfig {Mode : videoFramesModeSampled , Sampled : & framesSampledMode { SampleFPS : 2 } },
314328 MaxVideoTokens : 100000 ,
315329 }})}
316330 tp , err := qwen3 .produce (context .Background (), chatVideoBody ("https://example.com/clip.mp4" ))
@@ -320,8 +334,8 @@ func TestVideoEstimator_Qwen3AndGemma4(t *testing.T) {
320334
321335 gemma4 := estimateBackend {vid : newVideoEstimator (& estimateConfig {Video : & videoEstimateConfig {
322336 DefaultDuration : 10 ,
323- TokensPerFrame : & tokensPerFrameConfig {Mode : videoTPFModeStatic , StaticToken : 256 },
324- Frames : & framesConfig {Mode : videoFramesModeStrided , DefaultSourceFPS : 24 , FrameStride : 4 , MaxFrames : 16 },
337+ TokensPerFrame : & tokensPerFrameConfig {Mode : videoTPFModeStatic , Static : & tokensPerFrameStaticMode { StaticToken : 256 } },
338+ Frames : & framesConfig {Mode : videoFramesModeStrided , MaxFrames : 16 , Strided : & framesStridedMode { DefaultSourceFPS : 24 , FrameStride : 4 } },
325339 }})}
326340 tp , err = gemma4 .produce (context .Background (), chatVideoBody ("https://example.com/clip.mp4" ))
327341 require .NoError (t , err )
@@ -428,8 +442,8 @@ func TestVideoEstimator_HeaderMetadataOverridesDefaults(t *testing.T) {
428442// drive strided frame counting.
429443func TestVideoEstimator_HeaderFPSStridedMode (t * testing.T ) {
430444 b := estimateBackend {vid : newVideoEstimator (& estimateConfig {Video : & videoEstimateConfig {
431- Frames : & framesConfig {Mode : videoFramesModeStrided , FrameStride : 2 },
432- TokensPerFrame : & tokensPerFrameConfig {Mode : videoTPFModeStatic , StaticToken : 1 },
445+ Frames : & framesConfig {Mode : videoFramesModeStrided , Strided : & framesStridedMode { FrameStride : 2 } },
446+ TokensPerFrame : & tokensPerFrameConfig {Mode : videoTPFModeStatic , Static : & tokensPerFrameStaticMode { StaticToken : 1 } },
433447 }})}
434448 // strided frames = int(duration(3)*fps(30))/2 = 45.
435449 tp , err := b .produce (videoCtx (videoMetadata {duration : 3 , fps : 30 }), chatVideoBody ("https://cdn.example.com/movie.mp4" ))
@@ -441,8 +455,8 @@ func TestVideoEstimator_HeaderFPSStridedMode(t *testing.T) {
441455// header duration but not the header source FPS (sampleFPS is authoritative).
442456func TestVideoEstimator_SampledIgnoresHeaderFPS (t * testing.T ) {
443457 b := estimateBackend {vid : newVideoEstimator (& estimateConfig {Video : & videoEstimateConfig {
444- Frames : & framesConfig {Mode : videoFramesModeSampled , SampleFPS : 2 },
445- TokensPerFrame : & tokensPerFrameConfig {Mode : videoTPFModeStatic , StaticToken : 1 },
458+ Frames : & framesConfig {Mode : videoFramesModeSampled , Sampled : & framesSampledMode { SampleFPS : 2 } },
459+ TokensPerFrame : & tokensPerFrameConfig {Mode : videoTPFModeStatic , Static : & tokensPerFrameStaticMode { StaticToken : 1 } },
446460 }})}
447461 // Same 3s duration, different source fps: both must yield 3*2 = 6.
448462 fps30 , err := b .produce (videoCtx (videoMetadata {duration : 3 , fps : 30 }), chatVideoBody ("https://example.com/clip.mp4" ))
@@ -468,8 +482,8 @@ func TestVideoEstimator_NoHeadersUseDefaults(t *testing.T) {
468482// isolates the frame-group count.
469483func TestVideoEstimator_TemporalMergeAndMinFrames (t * testing.T ) {
470484 b := estimateBackend {vid : newVideoEstimator (& estimateConfig {Video : & videoEstimateConfig {
471- Frames : & framesConfig {Mode : videoFramesModeSampled , SampleFPS : 2 , MinFrames : 4 , TemporalPatchSize : 2 },
472- TokensPerFrame : & tokensPerFrameConfig {Mode : videoTPFModeStatic , StaticToken : 1 },
485+ Frames : & framesConfig {Mode : videoFramesModeSampled , MinFrames : 4 , Sampled : & framesSampledMode { SampleFPS : 2 , TemporalPatchSize : 2 } },
486+ TokensPerFrame : & tokensPerFrameConfig {Mode : videoTPFModeStatic , Static : & tokensPerFrameStaticMode { StaticToken : 1 } },
473487 }})}
474488 // 10s: 10*2 = 20 sampled frames, /2 temporal merge = 10 groups.
475489 long , err := b .produce (videoCtx (videoMetadata {duration : 10 }), chatVideoBody ("https://example.com/clip.mp4" ))
0 commit comments