# 配套文章：01-text-and-tokens.md
# 本轮新增示例未执行；环境和运行方式见上级 GUIDE.md。

from transformers import AutoTokenizer

# Tokenizer 必须与目标模型配套；这里只加载文本编码所需文件。
model_id = "Qwen/Qwen2.5-0.5B-Instruct"
tokenizer = AutoTokenizer.from_pretrained(model_id)

# 用不同类型的文本观察切分；len(text) 是 Python 字符数，不是 Token 数。
texts = ["商品有质量问题，运费谁承担？", "Who pays for shipping?", "order_id = 'A100'"]
for text in texts:
    ids = tokenizer.encode(text, add_special_tokens=False)
    print("原文：", text)
    print("字符数 / Token 数：", len(text), len(ids))
    print("词表片段：", tokenizer.convert_ids_to_tokens(ids))
    print("编号：", ids)
    # 对整段 ID 一次解码，避免把多字节字符的部分 Token 单独恢复。
    print("恢复文本：", tokenizer.decode(ids, clean_up_tokenization_spaces=False))

messages = [
    {"role": "system", "content": "根据提供的政策回答；缺少政策时说明无法判断。"},
    {"role": "user", "content": texts[0]},
]
# 先展示模板文本，便于观察角色标记与助手起始位置。
rendered = tokenizer.apply_chat_template(
    messages, tokenize=False, add_generation_prompt=True
)
print("模板展开：", rendered)

# 让模板接口直接完成编码，避免手动拼接特殊 Token 后重复添加。
# return_dict=True 明确要求键值结果，不能假定不同版本的默认返回类型一致。
inputs = tokenizer.apply_chat_template(
    messages, tokenize=True, add_generation_prompt=True,
    return_dict=True, return_tensors="pt",
)
print("完整输入形状：", tuple(inputs["input_ids"].shape))
print("完整请求 Token 数：", inputs["input_ids"].shape[-1])

# 批量演示使用纯文本，单独观察左填充；不把它冒充完整聊天请求。
tokenizer.padding_side = "left"
if tokenizer.pad_token_id is None:
    tokenizer.pad_token = tokenizer.eos_token
batch = tokenizer(texts, padding=True, add_special_tokens=False, return_tensors="pt")
print("批次 ID：", batch["input_ids"])
print("有效位置掩码：", batch["attention_mask"])
