/v1/completions: `prompt: []` returns 200 with no choices, and both completions paths drop the error code (context_length_exceeded)
メンテナーはふだん 1 日以内に返信
まだ誰も着手していません。
評価
- 難易度
- 2/5
- 見積もり時間
- 1〜3時間
- 初心者へのやさしさ
- 84/100
調査の方向性
python/freetoken/server/openai_api.pyから始め、特に_completion_unsupported_reasonと、issueで挙げられているストリーミングおよび非ストリーミングのエラーパスを確認してください。チャットcompletionのルートがエラーコードをどのように渡すかを調べ、api_models.pyのcompletionリクエストの検証と比較してください。空のプロンプトリストが400を返し、両方のcompletionエラーパスでcontext_length_exceededが保持されることを完了条件とします。issueにあるCPUのみの再現を実行してください。
索引モデルが issue の本文から書いたものです。
説明
What happened
/v1/completionswith"prompt": []returns HTTP 200 with"choices": []and zero usage. Nothing is sent to the engine. The client asked for completions of zero prompts, and the request should get a 400.- If the scheduler rejects a completions request with a classified error (
context_length_exceededfor a prompt that is too long), the client gets"code": null. This happens in the non-streaming 400 and in the streamed error event./v1/chat/completionsreturns the same failure with"code": "context_length_exceeded", so a client that checks the code (to trim and retry, for example) works on chat but not on completions.
Minimal reproduction (CPU only, PYTHONPATH=python):
import asyncio
from types import SimpleNamespace
from freetoken.message import UserReply
from freetoken.server.openai_api import CompletionRequest, handle_completion
class State:
def __init__(self, replies):
self.config = SimpleNamespace(served_model_name="m", max_output_tokens=None)
self.replies, self.sent = replies, []
def new_user(self): return 1
async def send_one(self, msg): self.sent.append(msg)
async def wait_for_ack(self, uid):
for r in self.replies: yield r
async def body(r):
if hasattr(r, "body_iterator"):
return b"".join([c async for c in r.body_iterator]).decode().splitlines()[0]
return f"HTTP {r.status_code} {r.body.decode()}" if hasattr(r, "body") else f"HTTP 200 {r['choices']} {r['usage']}"
err = UserReply(uid=1, incremental_output="", finished=True,
error="prompt is too long: 9000 tokens > 8192 maximum", error_code="context_length_exceeded")
async def main():
s = State([])
print("prompt=[] ->", await body(await handle_completion(CompletionRequest(model="m", prompt=[]), None, s, {})), "| sent:", len(s.sent))
for stream in (False, True):
r = await handle_completion(CompletionRequest(model="m", prompt="x", stream=stream), None, State([err]), {})
print(f"stream={stream} ->", await body(r))
asyncio.run(main())
prompt=[] -> HTTP 200 [] {'prompt_tokens': 0, 'completion_tokens': 0, 'total_tokens': 0} | sent: 0
stream=False -> HTTP 400 {"error":{"message":"prompt is too long: 9000 tokens > 8192 maximum","type":"invalid_request_error","param":null,"code":null}}
stream=True -> data: {"error": {"message": "prompt is too long: 9000 tokens > 8192 maximum", "type": "invalid_request_error", "code": null}}
Cause (current main, cc56b6bd)
python/freetoken/server/openai_api.py:407-427:prompts = req.promptfor a list, and the loop over an empty list runs zero times._is_token_prompt(:671-679) returns False for[](bool(prompt)), so_completion_unsupported_reasonlets it through.CompletionRequest.prompt(api_models.py:105) has no minimum length. ([""]does reach the tokenizer, which rejects it with "prompt must contain at least one token",tokenizer/server.py:110-121.)openai_api.py:442: the non-streaming loop returnscreate_error_response(ack.error)withoutcode=, althoughUserReply.error_code(message/frontend.py:55) carries it.:469(stream) hardcodes"code": None. The chat route passes it on (GenerationError.code,openai_api.py:204,:215, and the stream error chunk at:274).
Proposed fix
- Return 400 for an empty
promptlist ("prompt must not be empty",param="prompt") in_completion_unsupported_reason, or withmin_length=1on the field. - Use
create_error_response(ack.error, code=getattr(ack, "error_code", None))at:442, and"code": getattr(ack, "error_code", None)at:469.
(#749 covers a related problem: a streamed request that is too long gets 200 and then an in-stream error. It does not cover the dropped code.)
Environment
Seen serving nvidia/Qwen3.8-Flash-Next-NVFP4 on an RTX 5080 16 GB (sm_120, driver 615.78.08), torch 2.14.1+cu130; reproduction CPU-only on main cc56b6bd.
- 主要言語
- Python
- スター
- 14.2k
- フォーク
- 1.4k
- 平均マージ
- 21時間 39分
- マージ済み PR(30日)
- 100
環境構築
このプロジェクトには開発コンテナ、Dockerfile、コントリビューションガイドがありません。まず README を読み、一般的な手順ははじめてのコントリビューションガイドを参照してください。
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
FlashML-org/FreeToken のほかの issue
-
Desktop feature
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
FlashML-org/FreeToken#800 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
-
test_gemv_path_matches_the_transcription fails on sm_120 (bf16 rounding tie exceeds the tolerance)対応中かも @void-mckenzie が 17 日前に担当しました。 オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 77/100
FlashML-org/FreeToken#649 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
FlashML-org/FreeToken#648 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 76/100
FlashML-org/FreeToken#646 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 88/100
FlashML-org/FreeToken#645 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
FlashML-org/FreeToken の issue をすべて見る
似ている issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 82/100
メンテナーはふだん 2 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 86/100
-
Validate finite output clamping limits in StepPredictor.prepare_clamping_params to prevent silent NaN/Inf propagation対応中かも @muskan-creates352 が今日担当しました。 オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 84/100
mllam/neural-lam#848 ·
メンテナーはふだん 2 日以内に返信
-
infra
難易度 1/5 1時間未満 初心者へのやさしさ 88/100
fab-geocommuns/RNB-coeur#1166 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
deepspeedai/DeepSpeed#8951 ·
メンテナーはふだん 2 日以内に返信