@@ -275,6 +275,20 @@ test("SessionManager normalizes legacy sessions without activeTokens to zero", (
275275 const manager = createSessionManager ( workspace , "machine-id-legacy" ) ;
276276
277277 assert . equal ( manager . getSession ( "legacy-session" ) ?. activeTokens , 0 ) ;
278+ assert . equal ( manager . getSession ( "legacy-session" ) ?. usagePerModel , null ) ;
279+ } ) ;
280+
281+ test ( "SessionManager keeps usagePerModel null until response usage is available" , async ( ) => {
282+ const workspace = createTempDir ( "deepcode-null-usage-per-model-workspace-" ) ;
283+ const home = createTempDir ( "deepcode-null-usage-per-model-home-" ) ;
284+ process . env . HOME = home ;
285+
286+ const manager = createMockedClientSessionManager ( workspace , [ { choices : [ { message : { content : "no usage" } } ] } ] ) ;
287+
288+ const sessionId = await manager . createSession ( { text : "" } ) ;
289+
290+ assert . equal ( manager . getSession ( sessionId ) ?. usage , null ) ;
291+ assert . equal ( manager . getSession ( sessionId ) ?. usagePerModel , null ) ;
278292} ) ;
279293
280294test ( "SessionManager marks skills loaded from existing session messages" , async ( ) => {
@@ -1122,6 +1136,7 @@ test("SessionManager accumulates response usage while active tokens track the la
11221136
11231137 const session = manager . getSession ( sessionId ) ;
11241138 const usage = session ?. usage as Record < string , any > ;
1139+ const usagePerModel = session ?. usagePerModel ?. [ "test-model" ] as Record < string , any > ;
11251140 assert . equal ( session ?. activeTokens , 27 ) ;
11261141 assert . equal ( usage . prompt_tokens , 30 ) ;
11271142 assert . equal ( usage . completion_tokens , 12 ) ;
@@ -1130,6 +1145,75 @@ test("SessionManager accumulates response usage while active tokens track the la
11301145 assert . equal ( usage . completion_tokens_details . reasoning_tokens , 7 ) ;
11311146 assert . equal ( usage . prompt_cache_hit_tokens , 18 ) ;
11321147 assert . equal ( usage . prompt_cache_miss_tokens , 12 ) ;
1148+ assert . equal ( usagePerModel . prompt_tokens , 30 ) ;
1149+ assert . equal ( usagePerModel . completion_tokens , 12 ) ;
1150+ assert . equal ( usagePerModel . total_tokens , 42 ) ;
1151+ assert . equal ( usagePerModel . prompt_tokens_details . cached_tokens , 18 ) ;
1152+ assert . equal ( usagePerModel . completion_tokens_details . reasoning_tokens , 7 ) ;
1153+ assert . equal ( usagePerModel . prompt_cache_hit_tokens , 18 ) ;
1154+ assert . equal ( usagePerModel . prompt_cache_miss_tokens , 12 ) ;
1155+ assert . equal ( usagePerModel . total_reqs , 2 ) ;
1156+ } ) ;
1157+
1158+ test ( "SessionManager stores usage per model across model changes" , async ( ) => {
1159+ const workspace = createTempDir ( "deepcode-usage-per-model-workspace-" ) ;
1160+ const home = createTempDir ( "deepcode-usage-per-model-home-" ) ;
1161+ process . env . HOME = home ;
1162+
1163+ let currentModel = "deepseek-v4-pro" ;
1164+ const responses = [
1165+ createChatResponse ( "pro response" , {
1166+ prompt_tokens : 10 ,
1167+ completion_tokens : 5 ,
1168+ total_tokens : 15 ,
1169+ } ) ,
1170+ createChatResponse ( "flash response" , {
1171+ prompt_tokens : 20 ,
1172+ completion_tokens : 7 ,
1173+ total_tokens : 27 ,
1174+ prompt_cache_hit_tokens : 6 ,
1175+ } ) ,
1176+ ] ;
1177+ const client = {
1178+ chat : {
1179+ completions : {
1180+ create : async ( ) => {
1181+ const response = responses . shift ( ) ;
1182+ assert . ok ( response , "expected a queued chat response" ) ;
1183+ return response ;
1184+ } ,
1185+ } ,
1186+ } ,
1187+ } ;
1188+ const manager = new SessionManager ( {
1189+ projectRoot : workspace ,
1190+ createOpenAIClient : ( ) => ( {
1191+ client : client as any ,
1192+ model : currentModel ,
1193+ baseURL : "https://api.deepseek.com" ,
1194+ thinkingEnabled : false ,
1195+ } ) ,
1196+ getResolvedSettings : ( ) => ( { model : currentModel } ) ,
1197+ renderMarkdown : ( text ) => text ,
1198+ onAssistantMessage : ( ) => { } ,
1199+ } ) ;
1200+
1201+ const sessionId = await manager . createSession ( { text : "" } ) ;
1202+ currentModel = "deepseek-v4-flash" ;
1203+ await manager . replySession ( sessionId , { text : "" } ) ;
1204+
1205+ const session = manager . getSession ( sessionId ) ;
1206+ assert . deepEqual ( Object . keys ( session ?. usagePerModel ?? { } ) . sort ( ) , [ "deepseek-v4-flash" , "deepseek-v4-pro" ] ) ;
1207+ assert . equal ( session ?. usagePerModel ?. [ "deepseek-v4-pro" ] ?. prompt_tokens , 10 ) ;
1208+ assert . equal ( session ?. usagePerModel ?. [ "deepseek-v4-pro" ] ?. completion_tokens , 5 ) ;
1209+ assert . equal ( session ?. usagePerModel ?. [ "deepseek-v4-pro" ] ?. total_reqs , 1 ) ;
1210+ assert . equal ( session ?. usagePerModel ?. [ "deepseek-v4-flash" ] ?. prompt_tokens , 20 ) ;
1211+ assert . equal ( session ?. usagePerModel ?. [ "deepseek-v4-flash" ] ?. completion_tokens , 7 ) ;
1212+ assert . equal ( session ?. usagePerModel ?. [ "deepseek-v4-flash" ] ?. prompt_cache_hit_tokens , 6 ) ;
1213+ assert . equal ( session ?. usagePerModel ?. [ "deepseek-v4-flash" ] ?. total_reqs , 1 ) ;
1214+ assert . equal ( session ?. usage ?. prompt_tokens , 30 ) ;
1215+ assert . equal ( session ?. usage ?. completion_tokens , 12 ) ;
1216+ assert . equal ( session ?. usage ?. total_tokens , 42 ) ;
11331217} ) ;
11341218
11351219test ( "SessionManager resets active tokens to latest post-compaction response usage" , async ( ) => {
@@ -1163,10 +1247,15 @@ test("SessionManager resets active tokens to latest post-compaction response usa
11631247
11641248 const session = manager . getSession ( sessionId ) ;
11651249 const usage = session ?. usage as Record < string , any > ;
1250+ const usagePerModel = session ?. usagePerModel ?. [ "test-model" ] as Record < string , any > ;
11661251 assert . equal ( session ?. activeTokens , 7 ) ;
11671252 assert . equal ( usage . prompt_tokens , 140_095 ) ;
11681253 assert . equal ( usage . completion_tokens , 35 ) ;
11691254 assert . equal ( usage . total_tokens , 140_130 ) ;
1255+ assert . equal ( usagePerModel . prompt_tokens , 140_095 ) ;
1256+ assert . equal ( usagePerModel . completion_tokens , 35 ) ;
1257+ assert . equal ( usagePerModel . total_tokens , 140_130 ) ;
1258+ assert . equal ( usagePerModel . total_reqs , 3 ) ;
11701259} ) ;
11711260
11721261test ( "SessionManager streams chat completions and counts reasoning progress" , async ( ) => {
0 commit comments