@@ -267,6 +267,78 @@ var _ = Describe("Mock Backend E2E Tests", Label("MockBackend"), func() {
267267 })
268268 })
269269
270+ Describe ("Detokenization API" , func () {
271+ It ("should return content for known token IDs" , func () {
272+ body := `{"model":"mock-model","tokens":[101,2023,2003,1037,3231,1012]}`
273+ req , err := http .NewRequest ("POST" , apiURL + "/detokenize" , strings .NewReader (body ))
274+ Expect (err ).ToNot (HaveOccurred ())
275+ req .Header .Set ("Content-Type" , "application/json" )
276+
277+ httpClient := & http.Client {Timeout : 30 * time .Second }
278+ resp , err := httpClient .Do (req )
279+ Expect (err ).ToNot (HaveOccurred ())
280+ defer resp .Body .Close ()
281+ Expect (resp .StatusCode ).To (Equal (200 ))
282+
283+ data , err := io .ReadAll (resp .Body )
284+ Expect (err ).ToNot (HaveOccurred ())
285+ var result map [string ]any
286+ Expect (json .Unmarshal (data , & result )).To (Succeed ())
287+ content , ok := result ["content" ].(string )
288+ Expect (ok ).To (BeTrue (), "response missing 'content' field: %s" , string (data ))
289+ Expect (content ).ToNot (BeEmpty ())
290+ })
291+
292+ It ("should round-trip tokenize then detokenize" , func () {
293+ httpClient := & http.Client {Timeout : 30 * time .Second }
294+
295+ // Step 1: tokenize
296+ tokenizeReq , err := http .NewRequest ("POST" , apiURL + "/tokenize" ,
297+ strings .NewReader (`{"model":"mock-model","content":"Hello world"}` ))
298+ Expect (err ).ToNot (HaveOccurred ())
299+ tokenizeReq .Header .Set ("Content-Type" , "application/json" )
300+
301+ tokenizeResp , err := httpClient .Do (tokenizeReq )
302+ Expect (err ).ToNot (HaveOccurred ())
303+ defer tokenizeResp .Body .Close ()
304+ Expect (tokenizeResp .StatusCode ).To (Equal (200 ))
305+
306+ tokenizeData , err := io .ReadAll (tokenizeResp .Body )
307+ Expect (err ).ToNot (HaveOccurred ())
308+ var tokenizeResult map [string ]any
309+ Expect (json .Unmarshal (tokenizeData , & tokenizeResult )).To (Succeed ())
310+
311+ tokensRaw , ok := tokenizeResult ["tokens" ].([]any )
312+ Expect (ok ).To (BeTrue (), "tokenize response missing 'tokens': %s" , string (tokenizeData ))
313+ Expect (tokensRaw ).ToNot (BeEmpty ())
314+
315+ // Step 2: detokenize the returned token IDs
316+ tokens := make ([]int , len (tokensRaw ))
317+ for i , t := range tokensRaw {
318+ tokens [i ] = int (t .(float64 ))
319+ }
320+ tokenJSON , err := json .Marshal (map [string ]any {"model" : "mock-model" , "tokens" : tokens })
321+ Expect (err ).ToNot (HaveOccurred ())
322+
323+ detokenizeReq , err := http .NewRequest ("POST" , apiURL + "/detokenize" , strings .NewReader (string (tokenJSON )))
324+ Expect (err ).ToNot (HaveOccurred ())
325+ detokenizeReq .Header .Set ("Content-Type" , "application/json" )
326+
327+ detokenizeResp , err := httpClient .Do (detokenizeReq )
328+ Expect (err ).ToNot (HaveOccurred ())
329+ defer detokenizeResp .Body .Close ()
330+ Expect (detokenizeResp .StatusCode ).To (Equal (200 ))
331+
332+ detokenizeData , err := io .ReadAll (detokenizeResp .Body )
333+ Expect (err ).ToNot (HaveOccurred ())
334+ var detokenizeResult map [string ]any
335+ Expect (json .Unmarshal (detokenizeData , & detokenizeResult )).To (Succeed ())
336+ content , ok := detokenizeResult ["content" ].(string )
337+ Expect (ok ).To (BeTrue (), "detokenize response missing 'content': %s" , string (detokenizeData ))
338+ Expect (content ).ToNot (BeEmpty ())
339+ })
340+ })
341+
270342 Describe ("Autoparser ChatDelta Streaming" , Label ("Autoparser" ), func () {
271343 // These tests verify that when the C++ autoparser handles tool calls
272344 // and content via ChatDeltas (with empty raw message), the streaming
0 commit comments