|
| 1 | +# Copyright (c) Meta Platforms, Inc. and affiliates. |
| 2 | +# All rights reserved. |
| 3 | +# |
| 4 | +# This source code is licensed under the BSD-style license found in the |
| 5 | +# LICENSE file in the root directory of this source tree. |
| 6 | + |
| 7 | +"""The interception proxy must capture per-token ids from a vLLM logprobs response. |
| 8 | +
|
| 9 | +vLLM with ``--return-tokens-as-token-ids`` encodes the id in the ``token`` field as |
| 10 | +``"token_id:<int>"`` (no separate ``token_id`` key), which the capture must parse so |
| 11 | +GRPO trains on real token ids.""" |
| 12 | + |
| 13 | +import os |
| 14 | +import sys |
| 15 | + |
| 16 | +# Make ``envs/`` importable when running from the repository root. |
| 17 | +_REPO_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")) |
| 18 | +if _REPO_ROOT not in sys.path: |
| 19 | + sys.path.insert(0, _REPO_ROOT) |
| 20 | +_ENVS_DIR = os.path.join(_REPO_ROOT, "envs") |
| 21 | +if _ENVS_DIR not in sys.path: |
| 22 | + sys.path.insert(0, _ENVS_DIR) |
| 23 | + |
| 24 | +from opencode_env.sandbox.interception import _build_turn_record |
| 25 | + |
| 26 | + |
| 27 | +def _response(content): |
| 28 | + return {"choices": [{"finish_reason": "stop", "logprobs": {"content": content}}]} |
| 29 | + |
| 30 | + |
| 31 | +def test_captures_ids_from_vllm_token_id_prefix(): |
| 32 | + # vLLM --return-tokens-as-token-ids: id lives in the ``token`` field as "token_id:N". |
| 33 | + content = [ |
| 34 | + {"token": "token_id:15339", "logprob": -0.1}, |
| 35 | + {"token": "token_id:1917", "logprob": -0.2}, |
| 36 | + ] |
| 37 | + rec = _build_turn_record( |
| 38 | + turn_idx=0, request_body={}, response_json=_response(content), latency_s=0.0 |
| 39 | + ) |
| 40 | + assert rec.completion_token_ids == [15339, 1917] |
| 41 | + assert rec.per_token_logps == [-0.1, -0.2] |
| 42 | + |
| 43 | + |
| 44 | +def test_captures_ids_from_token_id_key(): |
| 45 | + # Some builds instead expose a separate integer ``token_id`` key. |
| 46 | + content = [ |
| 47 | + {"token": "hello", "token_id": 42, "logprob": -0.5}, |
| 48 | + {"token": " world", "token_id": 99, "logprob": -0.6}, |
| 49 | + ] |
| 50 | + rec = _build_turn_record( |
| 51 | + turn_idx=0, request_body={}, response_json=_response(content), latency_s=0.0 |
| 52 | + ) |
| 53 | + assert rec.completion_token_ids == [42, 99] |
| 54 | + |
| 55 | + |
| 56 | +def test_plain_openai_has_logprobs_but_no_ids(): |
| 57 | + # A plain OpenAI response (no ids) still yields logprobs, just no token ids. |
| 58 | + content = [{"token": "hi", "logprob": -0.3}] |
| 59 | + rec = _build_turn_record( |
| 60 | + turn_idx=0, request_body={}, response_json=_response(content), latency_s=0.0 |
| 61 | + ) |
| 62 | + assert rec.completion_token_ids == [] |
| 63 | + assert rec.per_token_logps == [-0.3] |
0 commit comments