From bc089b431d6d5e7755f50855bf2888e54fee8b34 Mon Sep 17 00:00:00 2001 From: tangwang Date: Mon, 9 Mar 2026 23:48:19 +0800 Subject: [PATCH] refactor(reranker): 对齐 Qwen3-Reranker 官方实现 --- README.md | 2 ++ config/config.yaml | 4 ++-- docs/CNCLIP_SERVICE说明文档.md | 200 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------------------------------------------------------------- docs/DEVELOPER_GUIDE.md | 1 + docs/QUICKSTART.md | 1 + docs/TEI_SERVICE说明文档.md | 193 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ docs/Usage-Guide.md | 4 ++++ embeddings/README.md | 5 ++++- reranker/backends/qwen3_vllm.py | 57 ++++++++++++++++++++++++++++++++++----------------------- 9 files changed, 360 insertions(+), 107 deletions(-) create mode 100644 docs/TEI_SERVICE说明文档.md diff --git a/README.md b/README.md index f7d6eb3..21eaf8f 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,8 @@ START_EMBEDDING=1 START_TRANSLATOR=1 START_RERANKER=1 ./run.sh | 3. API 详细说明 | `docs/搜索API对接指南.md` | | 4. 快速参数速查 | `docs/搜索API速查表.md` | | 5. 首次环境搭建、生产凭证 | `docs/QUICKSTART.md` §1.4–1.8 | +| 6. TEI 文本向量专项 | `docs/TEI_SERVICE说明文档.md` | +| 7. CN-CLIP 图片向量专项 | `docs/CNCLIP_SERVICE说明文档.md` | --- diff --git a/config/config.yaml b/config/config.yaml index 4e29b47..e492e8c 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -204,8 +204,8 @@ services: tensor_parallel_size: 1 gpu_memory_utilization: 0.36 dtype: "float16" - enable_prefix_caching: false - enforce_eager: true + enable_prefix_caching: true + enforce_eager: false instruction: "Given a web search query, retrieve relevant passages that answer the query" # SPU配置(已启用,使用嵌套skus) diff --git a/docs/CNCLIP_SERVICE说明文档.md b/docs/CNCLIP_SERVICE说明文档.md index f193927..c176aff 100644 --- a/docs/CNCLIP_SERVICE说明文档.md +++ b/docs/CNCLIP_SERVICE说明文档.md @@ -1,153 +1,191 @@ # CN-CLIP 服务(clip-as-service)说明 -> 本文是本仓库的 CN-CLIP 运行手册与约束说明。主流程仍是 `embedding` 服务(`6005`);当 `embeddings/config.py` 中 `USE_CLIP_AS_SERVICE=true` 时,`embedding` 会调用本 gRPC 服务(默认 `grpc://127.0.0.1:51000`)生成图片向量。 +> 本文是本仓库 CN-CLIP(图片向量)专项运行手册。目标是让新同事在新机器上独立完成环境准备、部署、验收与排障。 -## 1. 设计目标与官方对齐 +## 1. 作用与边界 -- 采用 `clip-as-service` 的标准拆分:`clip-server`(服务端)与 `clip-client`(客户端)可独立安装。 -- 服务协议使用 gRPC,符合官方推荐与本项目现有调用链。 -- 保持“主项目环境”和“CN-CLIP 专用环境”解耦,避免 `grpcio/jina/docarray` 与主项目依赖互相污染。 +- `cnclip` 是独立 gRPC 服务(默认 `grpc://127.0.0.1:51000`)。 +- `embedding` 服务(6005)在 `USE_CLIP_AS_SERVICE=true` 时调用它完成 `/embed/image`。 +- `cnclip` 不负责文本向量;文本向量由 TEI(8080)负责。 -官方仓库(安装方式、server/client 分离、基本使用示例): -[jina-ai/clip-as-service](https://github.com/jina-ai/clip-as-service) +## 2. 代码与脚本入口 -## 2. 当前架构(本仓库) +- 环境初始化:`scripts/setup_cnclip_venv.sh` +- 启动:`scripts/start_cnclip_service.sh` +- 停止:`scripts/stop_cnclip_service.sh` +- 统一编排:`scripts/service_ctl.sh` -- **服务启动脚本**:`scripts/start_cnclip_service.sh` -- **服务停止脚本**:`scripts/stop_cnclip_service.sh` -- **环境初始化脚本**:`scripts/setup_cnclip_venv.sh` -- **统一编排入口**:`scripts/service_ctl.sh`(`restart.sh` 调用它) -- **默认端口**:`51000` -- **默认模型**:`CN-CLIP/ViT-H-14` -- **默认协议**:gRPC +## 3. 前置条件 -## 3. 环境准备策略(推荐做法) +### 3.1 必需软件 -### 3.1 推荐:专用 venv(`.venv-cnclip`) +- Python 3.12(由仓库脚本创建虚拟环境) +- 可访问模型下载源(Hugging Face / 镜像源) + +### 3.2 GPU 模式额外要求 + +- NVIDIA 驱动可用(`nvidia-smi` 可执行) +- `cnclip` 设备设为 `cuda` + +检查命令: ```bash -./scripts/setup_cnclip_venv.sh +nvidia-smi ``` -脚本会创建 `.venv-cnclip`,并处理已知兼容性问题(`grpcio`、`jina`、`docarray`、`pkg_resources` 等),避免在主 `.venv` 中反复冲突。 +## 4. 环境安装(可复现) -### 3.2 启动时的环境策略(严格) +在仓库根目录执行: + +```bash +cd /data/saas-search +./scripts/setup_cnclip_venv.sh +``` -`start_cnclip_service.sh` 只允许使用 `.venv-cnclip`。 -若 `.venv-cnclip` 不存在,脚本会直接失败并提示先执行 `./scripts/setup_cnclip_venv.sh`。 +脚本会创建 `.venv-cnclip`,并安装 `clip-as-service` 服务端所需依赖。 +`start_cnclip_service.sh` 只使用这个专用环境,不会污染主 `.venv`。 -## 4. 服务管理方式(推荐) +## 5. 启动与停止 -### 4.1 单独启动/停止 +### 5.1 GPU 模式(推荐) ```bash ./scripts/start_cnclip_service.sh --device cuda +``` + +### 5.2 CPU 模式(显式) + +```bash +./scripts/start_cnclip_service.sh --device cpu +``` + +### 5.3 停止服务 + +```bash ./scripts/stop_cnclip_service.sh ``` -### 4.2 统一编排(推荐日常用法) +## 6. service_ctl 的行为与约定 + +### 6.1 通过统一编排启动 ```bash -./scripts/service_ctl.sh restart -# 或 -./restart.sh +START_EMBEDDING=1 START_TEI=1 ./scripts/service_ctl.sh start ``` -`service_ctl.sh` 在启动 `cnclip` 时默认注入 `CNCLIP_DEVICE=cuda`。 -若机器无 GPU 或希望改用 CPU,可在 `.env` 设置: +当 `USE_CLIP_AS_SERVICE=true` 且 `START_EMBEDDING=1` 时,`service_ctl` 会自动拉起 `cnclip`。 + +### 6.2 设备选择优先级 + +- 显式传入 `CNCLIP_DEVICE` 时,以该值为准: ```bash -CNCLIP_DEVICE=cpu +CNCLIP_DEVICE=cuda ./scripts/service_ctl.sh start cnclip +CNCLIP_DEVICE=cpu ./scripts/service_ctl.sh start cnclip ``` -## 5. GPU 使用与验证 +- 不传 `CNCLIP_DEVICE` 时,默认 `cuda`。 + +### 6.3 运行中模式校验(重要) -### 5.1 必须点 +`service_ctl` 会在“已运行”场景下检查当前 `cnclip` 的 device 与期望是否一致。 +若不一致会直接报错,防止“服务已启动但模式错了”的静默问题。 -- 启动日志显示 `device: cuda` 仅代表配置传入成功; -- 只有在**首次编码请求触发模型加载后**,`nvidia-smi` 才一定能看到显存占用。 +例如:当前运行是 CPU,但你期望 CUDA,会提示先 stop 再按 CUDA 重启。 -### 5.2 推荐验证步骤 +## 7. 验收步骤(必须做) -1) 启动服务: +### 7.1 基础状态 ```bash -./scripts/start_cnclip_service.sh --port 51000 --device cuda +./scripts/service_ctl.sh status cnclip ``` -2) 发送一次请求(触发模型加载): +### 7.2 核对运行配置 + +```bash +cat third-party/clip-as-service/server/torch-flow-temp.yml +``` + +应看到: + +- GPU 模式:`device: 'cuda'` +- CPU 模式:`device: 'cpu'` + +### 7.3 发送一次编码请求(触发模型加载) ```bash -PYTHONPATH="third-party/clip-as-service/client:${PYTHONPATH}" NO_VERSION_CHECK=1 .venv-cnclip/bin/python -c " +PYTHONPATH="third-party/clip-as-service/client:${PYTHONPATH}" \ +NO_VERSION_CHECK=1 \ +.venv-cnclip/bin/python - <<'PY' from clip_client import Client -c = Client('grpc://localhost:51000') +c = Client('grpc://127.0.0.1:51000') r = c.encode(['测试']) print('shape:', r.shape) -" +PY ``` -3) 观察 GPU: +预期 `shape` 为 `(1, 1024)`。 + +### 7.4 GPU 验证 ```bash nvidia-smi ``` -预期: -- `shape` 为 `(1, 1024)`; -- `nvidia-smi` 出现对应 `python`/`clip_server` 进程并有显存占用。 +GPU 模式下应出现 `clip_server` 相关 `python` 进程及显存占用。 -## 6. 使用方式(客户端) +### 7.5 与 embedding 服务联调 -### 6.1 在本仓库中(推荐) +```bash +./scripts/start_embedding_service.sh -- 服务消费者一般是 `embedding` 服务,不建议业务侧直接连 `cnclip`。 -- 若需手动调试,请使用隔离环境(推荐 `.venv-embedding` 或 `.venv-cnclip`),不要在主 `.venv` 安装旧依赖。 +curl -sS -X POST "http://127.0.0.1:6005/embed/image" \ + -H "Content-Type: application/json" \ + -d '["https://oss.essa.cn/98532128-cf8e-456c-9e30-6f2a5ea0c19f.jpg"]' +``` -示例: +返回应为向量数组(非空)。 -```python -from clip_client import Client +## 8. 常见问题与排障 -c = Client("grpc://127.0.0.1:51000") -vec = c.encode(["https://example.com/a.jpg", "测试文本"]) -print(vec.shape) # (2, 1024) -``` +### 8.1 “为什么 cnclip 没用 GPU?” -### 6.2 常见误区 +先看启动命令是否显式写了: -- ❌ 用 `http://localhost:51000` 当成 HTTP 服务访问; -- ❌ 只看“启动成功”就判断已用 GPU,不发请求不看 `nvidia-smi`; -- ❌ 在主 `.venv` 直接安装 server 依赖导致依赖树污染。 +```bash +CNCLIP_DEVICE=cpu ... +``` -## 7. 已知兼容性说明(关键信息) +如果是,这就是预期行为,会强制 CPU。 -- `clip-as-service` 在本项目场景下依赖链较老,`grpcio`/`jina`/`docarray` 组合在 Python 3.12 上易触发源码构建问题。 -- `setuptools>=82` 移除了 `pkg_resources`;而部分依赖链仍会导入它,因此专用脚本固定了兼容范围。 -- `setup_cnclip_venv.sh` 中存在“为可运行性而做的约束收敛”,这是有意行为,不建议手动放开。 +### 8.2 “我要求 cuda,但 service_ctl 说 mode mismatch” -## 8. 排障速查 +说明当前已有 CPU 模式实例在跑。按提示重启: -### 8.1 启动失败 +```bash +./scripts/service_ctl.sh stop cnclip +CNCLIP_DEVICE=cuda ./scripts/service_ctl.sh start cnclip +``` -- 查看日志:`tail -f logs/cnclip_service.log` -- 检查端口占用:`lsof -i :51000` -- 重新构建环境:`rm -rf .venv-cnclip && ./scripts/setup_cnclip_venv.sh` +### 8.3 进程被 `Killed` -### 8.2 连接失败 +常见于资源竞争(尤其单卡同时运行 TEI/reranker/cnclip 时的启动峰值)。建议: -- 确认客户端使用 `grpc://` 协议; -- 确认端口与服务端一致(默认 `51000`)。 +1. 先起 `tei` 与 `cnclip`,确认稳定后再起 `reranker`。 +2. 适当下调 `reranker` 显存预算(`config/config.yaml` 的 `gpu_memory_utilization`)。 +3. 必要时将 CN-CLIP 模型从 `ViT-H-14` 调整到更小规模。 -### 8.3 看不到 GPU 进程 +### 8.4 gRPC 连接失败(`Connection refused`) -- 先发一次编码请求,再看 `nvidia-smi`; -- 确认启动参数或环境变量为 `cuda`(`--device cuda` 或 `CNCLIP_DEVICE=cuda`); -- 确认日志中无模型加载异常。 +- 检查端口:`lsof -i :51000` +- 看服务日志:`tail -f logs/cnclip_service.log` +- 确认客户端使用 `grpc://` 协议而非 `http://` -## 9. 与其他文档的关系 +## 9. 相关文档 - 开发总览:`docs/QUICKSTART.md` -- 系统架构:`docs/DEVELOPER_GUIDE.md` -- 向量服务说明:`embeddings/README.md` +- TEI 专项:`docs/TEI_SERVICE说明文档.md` +- 体系规范:`docs/DEVELOPER_GUIDE.md` +- embedding 模块:`embeddings/README.md` -本文件聚焦 CN-CLIP(clip-as-service)专项,不重复解释项目通用内容。 diff --git a/docs/DEVELOPER_GUIDE.md b/docs/DEVELOPER_GUIDE.md index ab0790b..b7b9154 100644 --- a/docs/DEVELOPER_GUIDE.md +++ b/docs/DEVELOPER_GUIDE.md @@ -407,6 +407,7 @@ services: | 运维、日志、多环境、故障 | [Usage-Guide.md](./Usage-Guide.md) | | 索引模块职责与 Java 对接 | [indexer/README.md](../indexer/README.md) | | 向量模块与 clip-as-service | [embeddings/README.md](../embeddings/README.md) | +| TEI 服务专项(安装/部署/GPU-CPU 模式) | [TEI_SERVICE说明文档.md](./TEI_SERVICE说明文档.md) | | CN-CLIP 服务专项(环境/运维/GPU) | [CNCLIP_SERVICE说明文档.md](./CNCLIP_SERVICE说明文档.md) | ### 10.2 仓库内入口 diff --git a/docs/QUICKSTART.md b/docs/QUICKSTART.md index bc3cdd3..4933b40 100644 --- a/docs/QUICKSTART.md +++ b/docs/QUICKSTART.md @@ -532,6 +532,7 @@ lsof -i :6004 | `docs/搜索API对接指南.md` | 搜索 API 完整说明 | | `indexer/README.md` | 索引模块职责与接口 | | `embeddings/README.md` | 向量化服务说明 | +| `docs/TEI_SERVICE说明文档.md` | TEI 专项(安装、部署、GPU/CPU 模式、排障) | | `docs/CNCLIP_SERVICE说明文档.md` | CN-CLIP/clip-as-service 专项(环境、GPU、运维) | | `reranker/README.md` | 重排服务说明 | diff --git a/docs/TEI_SERVICE说明文档.md b/docs/TEI_SERVICE说明文档.md new file mode 100644 index 0000000..23e2717 --- /dev/null +++ b/docs/TEI_SERVICE说明文档.md @@ -0,0 +1,193 @@ +# TEI 服务(Qwen3-Embedding-0.6B)说明 + +> 本文是本仓库 TEI(Text Embeddings Inference)专项运行手册。目标是让新同事在一台新机器上可以独立完成环境准备、部署、验收与排障。 + +## 1. 作用与边界 + +- TEI 提供文本向量 HTTP 服务(默认 `http://127.0.0.1:8080`)。 +- 本项目中 `embedding` 服务(6005)默认把文本向量请求转发到 TEI。 +- TEI 仅负责文本向量,不负责图片向量(图片向量由 `cnclip` 提供)。 + +## 2. 代码与脚本入口 + +- 启动脚本:`scripts/start_tei_service.sh` +- 停止脚本:`scripts/stop_tei_service.sh` +- 统一编排:`scripts/service_ctl.sh` +- embedding 服务启动脚本(会校验 TEI 健康):`scripts/start_embedding_service.sh` + +## 3. 前置条件 + +### 3.1 必需软件 + +- Docker(必须) +- curl(用于健康检查与接口验收) + +### 3.2 GPU 模式额外要求(推荐生产) + +- NVIDIA 驱动可用(`nvidia-smi` 可执行) +- Docker 已配置 `nvidia` runtime(`docker info` 能看到 `nvidia`) + +检查命令: + +```bash +docker --version +docker info --format '{{json .Runtimes}}' +nvidia-smi +``` + +## 4. 安装与准备(可复现) + +### 4.1 拉取代码并进入项目 + +```bash +cd /data/saas-search +``` + +### 4.2 (仅 GPU 模式)配置 Docker GPU runtime + +如果 `docker info` 中没有 `nvidia` runtime,先按机器环境安装 `nvidia-container-toolkit` 并执行: + +```bash +sudo nvidia-ctk runtime configure --runtime=docker +sudo systemctl restart docker +``` + +再次确认: + +```bash +docker info --format '{{json .Runtimes}}' +``` + +## 5. 启动方式 + +### 5.1 GPU 模式启动(默认) + +```bash +TEI_USE_GPU=1 ./scripts/start_tei_service.sh +``` + +预期输出包含: + +- `Image: ghcr.io/huggingface/text-embeddings-inference:cuda-...` +- `Mode: gpu` +- `TEI is ready: http://127.0.0.1:8080` + +### 5.2 CPU 模式启动(显式) + +```bash +TEI_USE_GPU=0 ./scripts/start_tei_service.sh +``` + +预期输出包含: + +- `Image: ghcr.io/huggingface/text-embeddings-inference:1.9`(非 `cuda-`) +- `Mode: cpu` +- `TEI is ready: http://127.0.0.1:8080` + +### 5.3 停止服务 + +```bash +./scripts/stop_tei_service.sh +``` + +## 6. 验收步骤(必须做) + +### 6.1 健康检查 + +```bash +curl -sS http://127.0.0.1:8080/health +``` + +### 6.2 向量接口检查 + +```bash +curl -sS http://127.0.0.1:8080/embed \ + -H "Content-Type: application/json" \ + -d '{"inputs":["Instruct: Given a web search query, retrieve relevant passages that answer the query\nQuery: What is the capital of China?"]}' +``` + +返回应为二维数组(每条输入对应一个向量)。 + +### 6.3 与 embedding 服务联调 + +```bash +./scripts/start_embedding_service.sh + +curl -sS -X POST "http://127.0.0.1:6005/embed/text" \ + -H "Content-Type: application/json" \ + -d '["芭比娃娃 儿童玩具"]' +``` + +返回应为 1024 维向量数组。 + +## 7. 配置项(环境变量) + +`scripts/start_tei_service.sh` 支持下列变量: + +- `TEI_USE_GPU`:`1/0`(或 `true/false`),默认 `1` +- `TEI_CONTAINER_NAME`:容器名,默认 `saas-search-tei` +- `TEI_PORT`:宿主机端口,默认 `8080` +- `TEI_MODEL_ID`:默认 `Qwen/Qwen3-Embedding-0.6B` +- `TEI_VERSION`:镜像版本,默认 `1.9` +- `TEI_DTYPE`:默认 `float16` +- `TEI_MAX_BATCH_TOKENS`:默认 `2048` +- `TEI_MAX_CLIENT_BATCH_SIZE`:默认 `8` +- `HF_CACHE_DIR`:HF 缓存目录,默认 `$HOME/.cache/huggingface` +- `HF_TOKEN`:可选,避免匿名限速 + +## 8. service_ctl 使用方式 + +启动全套(含 TEI): + +```bash +START_EMBEDDING=1 START_TRANSLATOR=1 START_RERANKER=1 START_TEI=1 TEI_USE_GPU=1 ./scripts/service_ctl.sh start +``` + +仅启动 TEI: + +```bash +./scripts/service_ctl.sh start tei +``` + +查看状态: + +```bash +./scripts/service_ctl.sh status tei +``` + +## 9. 常见问题与排障 + +### 9.1 `ERROR: TEI_USE_GPU=1 but Docker nvidia runtime is not configured` + +- 原因:Docker 未配置 NVIDIA runtime。 +- 处理:按本文 4.2 配置后重启 Docker。 + +### 9.2 `mode mismatch (need GPU/CPU)` + +- 原因:已有同名容器在另一种模式运行(比如当前是 GPU 容器,但你这次要求 CPU)。 +- 处理: + +```bash +./scripts/stop_tei_service.sh +TEI_USE_GPU=0 ./scripts/start_tei_service.sh # 或改为 1 +``` + +### 9.3 embedding 服务报 TEI 不可达 + +- 先检查 TEI: + +```bash +curl -sS http://127.0.0.1:8080/health +``` + +- 再检查 embedding 侧地址: + - `TEI_BASE_URL` + - `services.embedding.backends.tei.base_url`(`config/config.yaml`) + +## 10. 相关文档 + +- 开发总览:`docs/QUICKSTART.md` +- 体系规范:`docs/DEVELOPER_GUIDE.md` +- embedding 模块:`embeddings/README.md` +- CN-CLIP 专项:`docs/CNCLIP_SERVICE说明文档.md` + diff --git a/docs/Usage-Guide.md b/docs/Usage-Guide.md index f1f443b..50fa367 100644 --- a/docs/Usage-Guide.md +++ b/docs/Usage-Guide.md @@ -56,6 +56,10 @@ TEI_USE_GPU=1 ./scripts/start_tei_service.sh TEI_USE_GPU=0 ./scripts/start_tei_service.sh ``` +专项说明: +- `docs/TEI_SERVICE说明文档.md` +- `docs/CNCLIP_SERVICE说明文档.md` + #### 2. 启动Elasticsearch **方式1: 使用Docker(推荐)** diff --git a/embeddings/README.md b/embeddings/README.md index ea6a129..ddb35bb 100644 --- a/embeddings/README.md +++ b/embeddings/README.md @@ -1,6 +1,9 @@ # Embeddings 模块 **请求示例**见 `docs/QUICKSTART.md` §3.3。 +**专项文档**: +- `../docs/TEI_SERVICE说明文档.md` +- `../docs/CNCLIP_SERVICE说明文档.md` --- @@ -45,7 +48,7 @@ INSTALL_LOCAL_ST=1 ./scripts/setup_embedding_venv.sh ``` -2. **启动 CN-CLIP 服务**(独立 gRPC 服务,默认端口 51000,详见 `docs/CNCLIP_SERVICE说明文档.md`): +2. **启动 CN-CLIP 服务**(独立 gRPC 服务,默认端口 51000,详见 `../docs/CNCLIP_SERVICE说明文档.md`): ```bash ./scripts/start_cnclip_service.sh ``` diff --git a/reranker/backends/qwen3_vllm.py b/reranker/backends/qwen3_vllm.py index e833054..b26aefd 100644 --- a/reranker/backends/qwen3_vllm.py +++ b/reranker/backends/qwen3_vllm.py @@ -18,7 +18,7 @@ try: import torch from transformers import AutoTokenizer from vllm import LLM, SamplingParams - from vllm.inputs import TokensPrompt + from vllm.inputs.data import TokensPrompt except ImportError as e: raise ImportError( "Qwen3-vLLM reranker backend requires vllm>=0.8.5 and transformers. " @@ -110,21 +110,25 @@ class Qwen3VLLMRerankerBackend: self, pairs: List[Tuple[str, str]], ) -> List[TokensPrompt]: - """Build tokenized prompts for vLLM from (query, doc) pairs.""" - prompts = [] - for q, d in pairs: - messages = _format_instruction(self._instruction, q, d) - # One conversation per call (apply_chat_template expects single conversation) - token_ids = self._tokenizer.apply_chat_template( - messages, - tokenize=True, - add_generation_prompt=False, - enable_thinking=False, + """Build tokenized prompts for vLLM from (query, doc) pairs. Batch apply_chat_template.""" + messages_batch = [ + _format_instruction(self._instruction, q, d) for q, d in pairs + ] + tokenized = self._tokenizer.apply_chat_template( + messages_batch, + tokenize=True, + add_generation_prompt=False, + enable_thinking=False, + ) + # Single conv returns flat list; batch returns list of lists + if tokenized and not isinstance(tokenized[0], list): + tokenized = [tokenized] + prompts = [ + TokensPrompt( + prompt_token_ids=ids[: self._max_prompt_len] + self._suffix_tokens ) - if isinstance(token_ids, list) and token_ids and isinstance(token_ids[0], list): - token_ids = token_ids[0] - ids = token_ids[: self._max_prompt_len] + self._suffix_tokens - prompts.append(TokensPrompt(prompt_token_ids=ids)) + for ids in tokenized + ] return prompts def _compute_scores( @@ -141,16 +145,23 @@ class Qwen3VLLMRerankerBackend: if not out.outputs: scores.append(0.0) continue - logprobs = out.outputs[0].logprobs - if not logprobs: + final_logits = out.outputs[0].logprobs + if not final_logits: scores.append(0.0) continue - last = logprobs[-1] - true_logp = last.get(self._true_token) - false_logp = last.get(self._false_token) - true_p = math.exp(true_logp.logprob) if true_logp else 1e-10 - false_p = math.exp(false_logp.logprob) if false_logp else 1e-10 - score = true_p / (true_p + false_p) + last = final_logits[-1] + # Match official: missing token -> logprob = -10 + if self._true_token not in last: + true_logit = -10 + else: + true_logit = last[self._true_token].logprob + if self._false_token not in last: + false_logit = -10 + else: + false_logit = last[self._false_token].logprob + true_score = math.exp(true_logit) + false_score = math.exp(false_logit) + score = true_score / (true_score + false_score) scores.append(float(score)) return scores -- libgit2 0.21.2