@@ -123,3 +123,86 @@ def test_generate_chat_reasoning_fallback() -> None:
123123 res = asyncio .run (backend .generate (prompt ))
124124 assert res .text == "Just thinking"
125125 assert res .tokens == ["Just" , "thinking" ]
126+
127+
128+ def test_generate_chat_with_logprobs () -> None :
129+ backend = OpenAICompatBackend (base_url = "http://127.0.0.1:8000" , model_id = "dummy" , chat = True )
130+ prompt = Prompt (id = "p1" , text = "Hello" , max_tokens = 10 )
131+
132+ mock_response = httpx .Response (
133+ 200 ,
134+ json = {
135+ "choices" : [
136+ {
137+ "message" : {"content" : "world" },
138+ "logprobs" : {
139+ "content" : [
140+ {
141+ "token" : "world" ,
142+ "logprob" : - 0.1 ,
143+ "top_logprobs" : [
144+ {"token" : "world" , "logprob" : - 0.1 },
145+ {"token" : "earth" , "logprob" : - 2.0 },
146+ ],
147+ }
148+ ]
149+ },
150+ }
151+ ],
152+ "usage" : {"completion_tokens" : 1 },
153+ },
154+ request = httpx .Request ("POST" , "http://127.0.0.1:8000/v1/chat/completions" ),
155+ )
156+
157+ with patch ("httpx.AsyncClient.post" , return_value = mock_response ):
158+ res = asyncio .run (backend .generate (prompt ))
159+ assert res .text == "world"
160+ assert res .tokens == ["world" ]
161+ assert res .logprobs == [- 0.1 ]
162+ # top_logprobs are sorted by token: earth (-2.0), world (-0.1)
163+ assert res .distributions == [[- 2.0 , - 0.1 ]]
164+ assert res .distribution_metadata == [{"id_0" : "earth" , "id_1" : "world" }]
165+
166+
167+ def test_generate_chat_with_logprobs_nan_and_missing () -> None :
168+ backend = OpenAICompatBackend (base_url = "http://127.0.0.1:8000" , model_id = "dummy" , chat = True )
169+ prompt = Prompt (id = "p1" , text = "Hello" , max_tokens = 10 )
170+
171+ mock_response = httpx .Response (
172+ 200 ,
173+ content = b"{}" ,
174+ request = httpx .Request ("POST" , "http://127.0.0.1:8000/v1/chat/completions" ),
175+ )
176+
177+ # Use patch to return NaN for the raw logprob values to avoid JSON serialization issues in httpx.Response
178+ with (
179+ patch ("httpx.AsyncClient.post" , return_value = mock_response ),
180+ patch (
181+ "httpx.Response.json" ,
182+ return_value = {
183+ "choices" : [
184+ {
185+ "message" : {"content" : "world" },
186+ "logprobs" : {
187+ "content" : [
188+ {
189+ "token" : "world" ,
190+ "logprob" : float ("nan" ),
191+ "top_logprobs" : [
192+ {"token" : "world" , "logprob" : float ("nan" )},
193+ {"token" : "earth" },
194+ ],
195+ }
196+ ]
197+ },
198+ }
199+ ],
200+ "usage" : {"completion_tokens" : 1 },
201+ },
202+ ),
203+ ):
204+ res = asyncio .run (backend .generate (prompt ))
205+ assert res .logprobs == [- 9999.0 ]
206+ # top_logprobs sorted by token: earth (-9999.0), world (-9999.0)
207+ assert res .distributions == [[- 9999.0 , - 9999.0 ]]
208+ assert res .distribution_metadata == [{"id_0" : "earth" , "id_1" : "world" }]
0 commit comments