gary-boon Claude Opus 4.5 commited on
Commit
ee0f6c9
·
1 Parent(s): 6bf9f5c

feat: Include token metadata in analysis response

Browse files

Extend promptTokens and generatedTokens with full metadata:
- bpe_pieces: BPE breakdown of each token
- is_special: Whether token is a special token (eos/bos/pad/unk)
- is_multi_split: Whether token is part of a multi-split identifier
- num_pieces: Number of BPE pieces

This eliminates the need for per-token /token/metadata API calls,
reducing backend requests and enabling instant hover tooltips.

Also removes debug logging from /token/metadata endpoint.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>

Files changed (1) hide show
  1. backend/model_service.py +32 -23
backend/model_service.py CHANGED
@@ -1860,13 +1860,40 @@ async def analyze_research_attention(request: Dict[str, Any], authenticated: boo
1860
  }
1861
  }
1862
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1863
  # Build response
1864
  response = {
1865
  "prompt": prompt,
1866
- "promptTokens": [{"text": t, "idx": tid, "bytes": len(t.encode('utf-8')), "type": "prompt"}
1867
- for tid, t in zip(prompt_token_ids, prompt_tokens)],
1868
- "generatedTokens": [{"text": t, "idx": tid, "bytes": len(t.encode('utf-8')), "type": "generated"}
1869
- for tid, t in zip(generated_token_ids, generated_tokens)],
1870
  "tokenSections": token_sections, # Section boundaries for UI coloring
1871
  "tokenAlternatives": token_alternatives_by_step, # Top-k alternatives for each token
1872
  "layersDataByStep": layer_data_by_token, # Layer data for ALL generation steps
@@ -2728,21 +2755,7 @@ async def get_token_metadata(
2728
  is_multi_split_array = metadata.is_multi_split_identifier([token_id])
2729
  is_multi_split = is_multi_split_array[0] if is_multi_split_array else False
2730
 
2731
- # DEBUG LOGGING
2732
- print(f"\n{'='*60}")
2733
- print(f"TOKEN METADATA DEBUG - Token ID: {token_id}")
2734
- print(f"{'='*60}")
2735
- print(f"Token Text: {repr(token_text)}")
2736
- print(f"BPE Pieces: {bpe_pieces}")
2737
- print(f"Num Pieces: {len(bpe_pieces)}")
2738
- print(f"Byte Length: {byte_length}")
2739
- print(f"Is Special: {is_special}")
2740
- print(f"Multi-split Array: {is_multi_split_array}")
2741
- print(f"Multi-split Boolean: {is_multi_split} (type: {type(is_multi_split).__name__})")
2742
- print(f"Tokenizer Type: {metadata.tokenizer_type}")
2743
- print(f"{'='*60}\n")
2744
-
2745
- result = {
2746
  "token_id": token_id,
2747
  "text": token_text,
2748
  "bpe_pieces": bpe_pieces,
@@ -2753,10 +2766,6 @@ async def get_token_metadata(
2753
  "tokenizer_type": metadata.tokenizer_type
2754
  }
2755
 
2756
- print(f"RESPONSE: {result}\n")
2757
-
2758
- return result
2759
-
2760
  if __name__ == "__main__":
2761
  import uvicorn
2762
  uvicorn.run(app, host="0.0.0.0", port=8000)
 
1860
  }
1861
  }
1862
 
1863
+ # Build token metadata for frontend (eliminates per-token API calls)
1864
+ from .tokenizer_utils import TokenizerMetadata
1865
+ token_metadata = TokenizerMetadata(manager.tokenizer)
1866
+
1867
+ special_token_ids = {
1868
+ manager.tokenizer.eos_token_id,
1869
+ manager.tokenizer.bos_token_id,
1870
+ manager.tokenizer.pad_token_id,
1871
+ manager.tokenizer.unk_token_id
1872
+ }
1873
+
1874
+ def build_token_data(token_ids, token_texts, token_type):
1875
+ """Build token data with full metadata for hover tooltips"""
1876
+ multi_split_flags = token_metadata.is_multi_split_identifier(token_ids)
1877
+ result = []
1878
+ for i, (tid, t) in enumerate(zip(token_ids, token_texts)):
1879
+ bpe_pieces = token_metadata.get_subword_pieces(tid)
1880
+ result.append({
1881
+ "text": t,
1882
+ "idx": tid,
1883
+ "bytes": len(t.encode('utf-8')),
1884
+ "type": token_type,
1885
+ "bpe_pieces": bpe_pieces,
1886
+ "is_special": tid in special_token_ids,
1887
+ "is_multi_split": multi_split_flags[i] if i < len(multi_split_flags) else False,
1888
+ "num_pieces": len(bpe_pieces),
1889
+ })
1890
+ return result
1891
+
1892
  # Build response
1893
  response = {
1894
  "prompt": prompt,
1895
+ "promptTokens": build_token_data(prompt_token_ids, prompt_tokens, "prompt"),
1896
+ "generatedTokens": build_token_data(generated_token_ids, generated_tokens, "generated"),
 
 
1897
  "tokenSections": token_sections, # Section boundaries for UI coloring
1898
  "tokenAlternatives": token_alternatives_by_step, # Top-k alternatives for each token
1899
  "layersDataByStep": layer_data_by_token, # Layer data for ALL generation steps
 
2755
  is_multi_split_array = metadata.is_multi_split_identifier([token_id])
2756
  is_multi_split = is_multi_split_array[0] if is_multi_split_array else False
2757
 
2758
+ return {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2759
  "token_id": token_id,
2760
  "text": token_text,
2761
  "bpe_pieces": bpe_pieces,
 
2766
  "tokenizer_type": metadata.tokenizer_type
2767
  }
2768
 
 
 
 
 
2769
  if __name__ == "__main__":
2770
  import uvicorn
2771
  uvicorn.run(app, host="0.0.0.0", port=8000)