@@ -2,6 +2,7 @@ import { afterEach, beforeEach, describe, expect, mock, it } from 'bun:test'
22import { NextRequest } from 'next/server'
33
44import {
5+ FREEBUFF_DEEPSEEK_V4_PRO_MODEL_ID ,
56 FREEBUFF_GEMINI_PRO_MODEL_ID ,
67 FREEBUFF_GLM_MODEL_ID ,
78 isFreebuffDeploymentHours ,
@@ -147,6 +148,13 @@ describe('/api/v1/chat/completions POST endpoint', () => {
147148 status : 'running' ,
148149 }
149150 }
151+ if ( runId === 'run-free-deepseek-v4' ) {
152+ return {
153+ agent_id : 'base2-free-deepseek-v4' ,
154+ ancestor_run_ids : [ ] ,
155+ status : 'running' ,
156+ }
157+ }
150158 if ( runId === 'run-reviewer-direct' ) {
151159 return {
152160 agent_id : 'code-reviewer-lite' ,
@@ -823,6 +831,111 @@ describe('/api/v1/chat/completions POST endpoint', () => {
823831 FETCH_PATH_TEST_TIMEOUT_MS ,
824832 )
825833
834+ it (
835+ 'lets the DeepSeek V4 free agent use the direct DeepSeek provider' ,
836+ async ( ) => {
837+ const fetchedBodies : Record < string , unknown > [ ] = [ ]
838+ const fetchedUrls : string [ ] = [ ]
839+ const fetchViaDeepSeek = mock (
840+ async ( url : string | URL | Request , init ?: RequestInit ) => {
841+ fetchedUrls . push ( String ( url ) )
842+ fetchedBodies . push ( JSON . parse ( init ?. body as string ) )
843+ return new Response (
844+ JSON . stringify ( {
845+ id : 'test-id' ,
846+ model : 'deepseek-v4-pro' ,
847+ choices : [ { message : { content : 'test response' } } ] ,
848+ usage : {
849+ prompt_tokens : 10 ,
850+ prompt_cache_hit_tokens : 4 ,
851+ completion_tokens : 20 ,
852+ total_tokens : 30 ,
853+ } ,
854+ } ) ,
855+ {
856+ status : 200 ,
857+ headers : { 'Content-Type' : 'application/json' } ,
858+ } ,
859+ )
860+ } ,
861+ ) as unknown as typeof globalThis . fetch
862+
863+ const req = new NextRequest (
864+ 'http://localhost:3000/api/v1/chat/completions' ,
865+ {
866+ method : 'POST' ,
867+ headers : allowedFreeModeHeaders ( 'test-api-key-new-free' ) ,
868+ body : JSON . stringify ( {
869+ model : FREEBUFF_DEEPSEEK_V4_PRO_MODEL_ID ,
870+ stream : false ,
871+ codebuff_metadata : {
872+ run_id : 'run-free-deepseek-v4' ,
873+ client_id : 'test-client-id-123' ,
874+ cost_mode : 'free' ,
875+ } ,
876+ } ) ,
877+ } ,
878+ )
879+
880+ const response = await postChatCompletions ( {
881+ req,
882+ getUserInfoFromApiKey : mockGetUserInfoFromApiKey ,
883+ logger : mockLogger ,
884+ trackEvent : mockTrackEvent ,
885+ getUserUsageData : mockGetUserUsageData ,
886+ getAgentRunFromId : mockGetAgentRunFromId ,
887+ fetch : fetchViaDeepSeek ,
888+ insertMessageBigquery : mockInsertMessageBigquery ,
889+ loggerWithContext : mockLoggerWithContext ,
890+ checkSessionAdmissible : mockCheckSessionAdmissibleAllow ,
891+ } )
892+
893+ const body = await response . json ( )
894+ expect ( response . status ) . toBe ( 200 )
895+ expect ( fetchedUrls [ 0 ] ) . toBe ( 'https://api.deepseek.com/chat/completions' )
896+ expect ( fetchedBodies [ 0 ] . model ) . toBe ( 'deepseek-v4-pro' )
897+ expect ( body . model ) . toBe ( FREEBUFF_DEEPSEEK_V4_PRO_MODEL_ID )
898+ expect ( body . provider ) . toBe ( 'DeepSeek' )
899+ } ,
900+ FETCH_PATH_TEST_TIMEOUT_MS ,
901+ )
902+
903+ it ( 'rejects the DeepSeek V4 free agent when it requests another free model' , async ( ) => {
904+ const req = new NextRequest (
905+ 'http://localhost:3000/api/v1/chat/completions' ,
906+ {
907+ method : 'POST' ,
908+ headers : allowedFreeModeHeaders ( 'test-api-key-new-free' ) ,
909+ body : JSON . stringify ( {
910+ model : FREEBUFF_GEMINI_PRO_MODEL_ID ,
911+ stream : false ,
912+ codebuff_metadata : {
913+ run_id : 'run-free-deepseek-v4' ,
914+ client_id : 'test-client-id-123' ,
915+ cost_mode : 'free' ,
916+ } ,
917+ } ) ,
918+ } ,
919+ )
920+
921+ const response = await postChatCompletions ( {
922+ req,
923+ getUserInfoFromApiKey : mockGetUserInfoFromApiKey ,
924+ logger : mockLogger ,
925+ trackEvent : mockTrackEvent ,
926+ getUserUsageData : mockGetUserUsageData ,
927+ getAgentRunFromId : mockGetAgentRunFromId ,
928+ fetch : mockFetch ,
929+ insertMessageBigquery : mockInsertMessageBigquery ,
930+ loggerWithContext : mockLoggerWithContext ,
931+ checkSessionAdmissible : mockCheckSessionAdmissibleAllow ,
932+ } )
933+
934+ const body = await response . json ( )
935+ expect ( response . status ) . toBe ( 403 )
936+ expect ( body . error ) . toBe ( 'free_mode_invalid_agent_model' )
937+ } )
938+
826939 it ( 'lets freebuff use Gemini 3.1 Pro through the free-mode allowlist' , async ( ) => {
827940 const req = new NextRequest (
828941 'http://localhost:3000/api/v1/chat/completions' ,
0 commit comments