Spaces:
Sleeping
Sleeping
gary-boon
Claude Opus 4.5
commited on
Commit
·
ee0f6c9
1
Parent(s):
6bf9f5c
feat: Include token metadata in analysis response
Browse filesExtend 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]>
- 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":
|
| 1867 |
-
|
| 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 |
-
|
| 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)
|