1+ import numpy as np
2+ from typing import Dict , List , Tuple , Any
3+
4+ from .stat_calculator import StatCalculator
5+ from lm_polygraph .model_adapters .api_provider_adapter import APIProviderAdapter
6+
7+
8+ class GrayBoxStatsCalculator (StatCalculator ):
9+ """
10+ Provider-based graybox stats calculator (API models).
11+
12+ Computes a minimal set of generation statistics for a batch of input texts:
13+
14+ - greedy_texts:
15+ The generated text for each input (typically the first returned choice).
16+
17+ - greedy_tokens_alternatives:
18+ Per generated token position, the adapter-provided top-k alternatives as
19+ (token, logprob) pairs.
20+
21+ Shape:
22+ List[batch] -> List[position] -> List[(token_str, logprob_float)]
23+ """
24+
25+ @staticmethod
26+ def meta_info () -> Tuple [List [str ], List [str ]]:
27+ return [
28+ "greedy_texts" ,
29+ "greedy_tokens_alternatives" ,
30+ ], []
31+
32+ def __init__ (self , n_alternatives : int = 10 ):
33+ super ().__init__ ()
34+ self .n_alternatives = n_alternatives
35+
36+ def __call__ (
37+ self ,
38+ dependencies : Dict [str , Any ],
39+ texts : List [str ],
40+ model : APIProviderAdapter ,
41+ max_new_tokens : int = 100 ,
42+ ) -> Dict [str , Any ]:
43+ """
44+ Generate continuations for `texts` using an API adapter and store minimal stats.
45+
46+ Parameters
47+ ----------
48+ dependencies: dict used to pass inputs and store computed statistics.
49+ Must contain:
50+ - dependencies["model"]: the underlying model wrapper/object used by the adapter
51+ (e.g., has .prepare_input(...) and .model_path, depending on your design).
52+
53+ texts: Batch of input prompts.
54+
55+ model: The APIProviderAdapter instance.
56+
57+ max_new_tokens:
58+ Max number of tokens to generate.
59+
60+ Returns
61+ -------
62+ Dict[str, Any]
63+ The updated dependencies dict with:
64+ - "greedy_texts"
65+ - "greedy_tokens_alternatives"
66+ """
67+ out = model .generate_texts (
68+ model = dependencies ["model" ],
69+ input_texts = texts ,
70+ args = {
71+ "max_tokens" : max_new_tokens ,
72+ "n" : 1 ,
73+ "output_scores" : True ,
74+ "top_logprobs" : self .n_alternatives ,
75+ },
76+ )
77+
78+ greedy_texts : List [str ] = []
79+ greedy_tokens_alternatives : List [List [List [Tuple [str , float ]]]] = []
80+
81+ # out is expected to be: List[prompt] -> List[choice] -> StandardizedResponse
82+ for prompt_choices in out :
83+ if not prompt_choices :
84+ greedy_texts .append ("" )
85+ greedy_tokens_alternatives .append ([])
86+ continue
87+
88+ # Take the first choice as "greedy" (since n=1)
89+ resp = prompt_choices [0 ]
90+
91+ greedy_texts .append (getattr (resp , "text" , "" ) or "" )
92+
93+ alts_per_pos : List [List [Tuple [str , float ]]] = []
94+ alt_tokens = getattr (resp , "alternative_tokens" , None )
95+ top_lps = getattr (resp , "top_logprobs" , None )
96+
97+ if alt_tokens and top_lps :
98+ # alt_tokens: List[List[str]]
99+ # top_lps: List[List[float]]
100+ for toks , lps in zip (alt_tokens , top_lps ):
101+ # Pair token strings with their logprobs
102+ alts_per_pos .append (list (zip (toks , lps )))
103+
104+ greedy_tokens_alternatives .append (alts_per_pos )
105+
106+ dependencies ["greedy_texts" ] = greedy_texts
107+ dependencies ["greedy_tokens_alternatives" ] = greedy_tokens_alternatives
108+ return dependencies
0 commit comments