@@ -491,3 +491,127 @@ async def test_query_daily_clamps_zero_days(logger):
491491 # days=0 不应报错,行为等同 days=1
492492 rows = await logger .query_daily (days = 0 )
493493 assert len (rows ) >= 1
494+
495+
496+ # ---------------------------------------------------------------------------
497+ # 按 model_served 聚合测试(同日/同供应商/同实际模型 → 合并为一行)
498+ # ---------------------------------------------------------------------------
499+
500+
501+ @pytest .mark .asyncio
502+ async def test_query_daily_aggregates_by_model_served (logger ):
503+ """核心场景:同日/同供应商/同 model_served 但不同 model_requested 应聚为一行."""
504+ await logger .log (
505+ vendor = "anthropic" ,
506+ model_requested = "claude-opus-4-6" ,
507+ model_served = "claude-sonnet-4-6" ,
508+ input_tokens = 100 ,
509+ output_tokens = 50 ,
510+ )
511+ await logger .log (
512+ vendor = "anthropic" ,
513+ model_requested = "claude-sonnet-4-6" ,
514+ model_served = "claude-sonnet-4-6" ,
515+ input_tokens = 200 ,
516+ output_tokens = 80 ,
517+ )
518+ rows = await logger .query_daily (days = 7 )
519+ # 同一 model_served → 聚为一行
520+ assert len (rows ) == 1
521+ row = rows [0 ]
522+ # model_requested 应包含两个值,逗号分隔
523+ requested_models = set (row ["model_requested" ].split ("," ))
524+ assert requested_models == {"claude-opus-4-6" , "claude-sonnet-4-6" }
525+ # Token 统计应正确求和
526+ assert row ["total_requests" ] == 2
527+ assert row ["total_input" ] == 300
528+ assert row ["total_output" ] == 130
529+
530+
531+ @pytest .mark .asyncio
532+ async def test_query_daily_single_model_no_comma (logger ):
533+ """边界:仅一个 model_requested 时不应出现多余逗号."""
534+ await logger .log (
535+ vendor = "anthropic" ,
536+ model_requested = "claude-sonnet-4-6" ,
537+ model_served = "claude-sonnet-4-6" ,
538+ input_tokens = 100 ,
539+ output_tokens = 50 ,
540+ )
541+ rows = await logger .query_daily (days = 7 )
542+ assert len (rows ) == 1
543+ # 单值时应为纯字符串,不含逗号
544+ assert rows [0 ]["model_requested" ] == "claude-sonnet-4-6"
545+ assert "," not in rows [0 ]["model_requested" ]
546+
547+
548+ @pytest .mark .asyncio
549+ async def test_query_daily_distinct_deduplication (logger ):
550+ """DISTINCT 应去重相同的 model_requested."""
551+ await logger .log (
552+ vendor = "anthropic" ,
553+ model_requested = "claude-sonnet-4-6" ,
554+ model_served = "claude-sonnet-4-6" ,
555+ input_tokens = 100 ,
556+ output_tokens = 50 ,
557+ )
558+ await logger .log (
559+ vendor = "anthropic" ,
560+ model_requested = "claude-sonnet-4-6" , # 相同的 model_requested
561+ model_served = "claude-sonnet-4-6" ,
562+ input_tokens = 200 ,
563+ output_tokens = 80 ,
564+ )
565+ rows = await logger .query_daily (days = 7 )
566+ assert len (rows ) == 1
567+ # 去重后不应重复出现
568+ assert rows [0 ]["model_requested" ] == "claude-sonnet-4-6"
569+ assert rows [0 ]["model_requested" ].count ("," ) == 0
570+
571+
572+ @pytest .mark .asyncio
573+ async def test_query_daily_model_filter_still_works (logger ):
574+ """--model/-m 过滤在聚合前执行,行为不变."""
575+ await logger .log (
576+ vendor = "anthropic" ,
577+ model_requested = "claude-opus-4-6" ,
578+ model_served = "claude-sonnet-4-6" ,
579+ input_tokens = 100 ,
580+ output_tokens = 50 ,
581+ )
582+ await logger .log (
583+ vendor = "anthropic" ,
584+ model_requested = "claude-sonnet-4-6" ,
585+ model_served = "claude-sonnet-4-6" ,
586+ input_tokens = 200 ,
587+ output_tokens = 80 ,
588+ )
589+ # 按 model_requested 过滤,仅保留 opus 的记录
590+ rows = await logger .query_daily (days = 7 , model = "claude-opus-4-6" )
591+ assert len (rows ) == 1
592+ assert rows [0 ]["total_requests" ] == 1
593+ assert rows [0 ]["total_input" ] == 100
594+
595+
596+ @pytest .mark .asyncio
597+ async def test_query_daily_different_vendors_not_merged (logger ):
598+ """不同供应商即使 model_served 相同也不应合并."""
599+ await logger .log (
600+ vendor = "anthropic" ,
601+ model_requested = "claude-sonnet-4-6" ,
602+ model_served = "claude-sonnet-4-6" ,
603+ input_tokens = 100 ,
604+ output_tokens = 50 ,
605+ )
606+ await logger .log (
607+ vendor = "zhipu" ,
608+ model_requested = "claude-sonnet-4-6" ,
609+ model_served = "claude-sonnet-4-6" , # 同一 model_served
610+ input_tokens = 200 ,
611+ output_tokens = 80 ,
612+ )
613+ rows = await logger .query_daily (days = 7 )
614+ # 不同供应商,应为两行
615+ assert len (rows ) == 2
616+ vendors = {r ["vendor" ] for r in rows }
617+ assert vendors == {"anthropic" , "zhipu" }
0 commit comments