Repository navigation
fix: grpc_servicer add check empty input and centralize tokenized validation #979
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
c66bdf7
5a5eb77
a6fd2ac
c6eae0e
3df023b
a548659
9df0bf7
0d0b9bd
723cb08
732998b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,5 +1,12 @@ | ||||||||||||||||||||||||||||||||
| """SGLang gRPC servicer — implements SglangScheduler proto service.""" | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| from smg_grpc_servicer.sglang.servicer import SGLangSchedulerServicer | ||||||||||||||||||||||||||||||||
| """SGLang gRPC servicer package.""" | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| __all__ = ["SGLangSchedulerServicer"] | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| def __getattr__(name: str): | ||||||||||||||||||||||||||||||||
| """Lazily expose `SGLangSchedulerServicer` to avoid eager heavy imports.""" | ||||||||||||||||||||||||||||||||
| if name == "SGLangSchedulerServicer": | ||||||||||||||||||||||||||||||||
| from smg_grpc_servicer.sglang.servicer import SGLangSchedulerServicer | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| return SGLangSchedulerServicer | ||||||||||||||||||||||||||||||||
| raise AttributeError(f"module {__name__!r} has no attribute {name!r}") | ||||||||||||||||||||||||||||||||
|
Comment on lines
+6
to
+12
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 Nit:
Suggested change
This is the pattern CPython's stdlib uses (e.g. |
||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| """Validation helpers for SGLang gRPC request payloads.""" | ||
|
|
||
| from typing import Any | ||
|
|
||
|
|
||
| def validate_tokenized_input(grpc_req: Any, request_kind: str) -> tuple[str, list[int]]: | ||
| """Validate tokenized field and return (original_text, input_ids).""" | ||
| if not grpc_req.HasField("tokenized"): | ||
| raise ValueError("Tokenized input must be provided") | ||
|
|
||
| input_text = grpc_req.tokenized.original_text | ||
| input_ids = list(grpc_req.tokenized.input_ids) | ||
| if not input_ids: | ||
| raise ValueError(f"input_ids cannot be empty for {request_kind} requests") | ||
|
|
||
| return input_text, input_ids |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| """ | ||
| Unit tests for SGLang request validation helpers. | ||
|
|
||
| These tests focus on validating tokenized request input in isolation. | ||
| """ | ||
|
|
||
| import pytest | ||
| from smg_grpc_servicer.sglang.validation import validate_tokenized_input | ||
|
gongwei-130 marked this conversation as resolved.
|
||
|
|
||
|
|
||
| class _Tokenized: | ||
| def __init__(self, original_text: str, input_ids: list[int]): | ||
| """Store tokenized payload fields used by validation tests.""" | ||
| self.original_text = original_text | ||
| self.input_ids = input_ids | ||
|
|
||
|
|
||
| class _GrpcReqStub: | ||
| def __init__(self, tokenized: _Tokenized | None): | ||
| """Mimic a protobuf request object with an optional `tokenized` field.""" | ||
| self.tokenized = tokenized | ||
|
|
||
| def HasField(self, field_name: str) -> bool: # noqa: N802 | ||
| """Match protobuf HasField behavior for the `tokenized` presence check.""" | ||
| return field_name == "tokenized" and self.tokenized is not None | ||
|
|
||
|
|
||
| class TestSGLangValidation: | ||
| """Test tokenized input validation behavior.""" | ||
|
|
||
| def test_validate_tokenized_input_rejects_missing_tokenized(self): | ||
| """Reject requests without tokenized payload.""" | ||
| req = _GrpcReqStub(tokenized=None) | ||
| with pytest.raises(ValueError, match="Tokenized input must be provided"): | ||
| validate_tokenized_input(req, "generate") | ||
|
|
||
| def test_validate_tokenized_input_rejects_empty_generate(self): | ||
| """Reject generate requests with empty input_ids.""" | ||
| req = _GrpcReqStub(tokenized=_Tokenized("hello", [])) | ||
| with pytest.raises(ValueError, match="input_ids cannot be empty for generate requests"): | ||
| validate_tokenized_input(req, "generate") | ||
|
|
||
| def test_validate_tokenized_input_rejects_empty_embed(self): | ||
| """Reject embed requests with empty input_ids.""" | ||
| req = _GrpcReqStub(tokenized=_Tokenized("hello", [])) | ||
| with pytest.raises(ValueError, match="input_ids cannot be empty for embed requests"): | ||
| validate_tokenized_input(req, "embed") | ||
|
Comment on lines
+37
to
+47
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick | 🔵 Trivial Parametrize duplicated empty- Line 34-44 repeats the same assertion shape with only ♻️ Suggested refactor class TestSGLangValidation:
@@
- def test_validate_tokenized_input_rejects_empty_generate(self):
- """Reject generate requests with empty input_ids."""
- req = _GrpcReqStub(tokenized=_Tokenized("hello", []))
- with pytest.raises(ValueError, match="input_ids cannot be empty for generate requests"):
- validate_tokenized_input(req, "generate")
-
- def test_validate_tokenized_input_rejects_empty_embed(self):
- """Reject embed requests with empty input_ids."""
- req = _GrpcReqStub(tokenized=_Tokenized("hello", []))
- with pytest.raises(ValueError, match="input_ids cannot be empty for embed requests"):
- validate_tokenized_input(req, "embed")
+ `@pytest.mark.parametrize`("request_kind", ["generate", "embed"])
+ def test_validate_tokenized_input_rejects_empty_input_ids(self, request_kind: str):
+ """Reject requests with empty input_ids."""
+ req = _GrpcReqStub(tokenized=_Tokenized("hello", []))
+ with pytest.raises(
+ ValueError,
+ match=rf"input_ids cannot be empty for {request_kind} requests",
+ ):
+ validate_tokenized_input(req, request_kind)🤖 Prompt for AI Agents |
||
|
|
||
| def test_validate_tokenized_input_returns_text_and_ids(self): | ||
| """Return original text and ids for valid tokenized payload.""" | ||
| req = _GrpcReqStub(tokenized=_Tokenized("hello", [1, 2])) | ||
| input_text, input_ids = validate_tokenized_input(req, "embed") | ||
| assert input_text == "hello" | ||
| assert input_ids == [1, 2] | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
|
|
||
| def test_validate_tokenized_input_returns_text_and_ids_generate(self): | ||
| """Return original text and ids for valid generate tokenized payload.""" | ||
| req = _GrpcReqStub(tokenized=_Tokenized("world", [3, 4, 5])) | ||
| input_text, input_ids = validate_tokenized_input(req, "generate") | ||
| assert input_text == "world" | ||
| assert input_ids == [3, 4, 5] | ||
Uh oh!
There was an error while loading. Please reload this page.