+26









Liangsheng Yin
DarkSharpness
Xiaoyu Zhang
Mick
Yuhao Yang
Cheng Wan
Ke Bao
Baizhou Zhang
Chunan Zeng
Khoa Pham
Ziyi Xu
Zijie Xia
Yuwei An
zhangxiaohao
Yangmin Li
Julien Lin
Hao Phan
Thomas Wang
RolaoDenthu
pigeonsoup
HaiShaw
Xinyuan Tong
Pranjal Shankhdhar
Lee Nau
HMING
elvischenv
Byron Hsu
Byron Hsu
Claude Opus 5
Thomas Wang
Xinyi Song
Mohammad Miadh Angkad
Cheng Wan
BBuf
Hanming Lu
Xinyi Song
abddb1c7e9
Co-authored-by: DarkSharpness <76582120+DarkSharpness@users.noreply.github.com> Co-authored-by: Xiaoyu Zhang <1182563586@qq.com> Co-authored-by: Mick <mickjagger19@icloud.com> Co-authored-by: Yuhao Yang <47235274+yhyang201@users.noreply.github.com> Co-authored-by: Cheng Wan <54331508+ch-wan@users.noreply.github.com> Co-authored-by: Ke Bao <ispobaoke@gmail.com> Co-authored-by: Baizhou Zhang <sobereddiezhang@gmail.com> Co-authored-by: Chunan Zeng <zcnrex@gmail.com> Co-authored-by: Khoa Pham <khoa.pham@radixark.ai> Co-authored-by: Ziyi Xu <ziyi.xu@radixark.ai> Co-authored-by: Zijie Xia <37504505+zijiexia@users.noreply.github.com> Co-authored-by: Yuwei An <ayw.sirius19@gmail.com> Co-authored-by: zhangxiaohao <1024393531@qq.com> Co-authored-by: Yangmin Li <yangminl@nvidia.com> Co-authored-by: Julien Lin <jullin@nvidia.com> Co-authored-by: Hao Phan <htphan@nvidia.com> Co-authored-by: Thomas Wang <1am9trash@gmail.com> Co-authored-by: RolaoDenthu <xinyisong0111@gmail.com> Co-authored-by: pigeonsoup <32922982+pigeonsoup@users.noreply.github.com> Co-authored-by: HaiShaw <hixiao@gmail.com> Co-authored-by: Xinyuan Tong <115166877+JustinTong0323@users.noreply.github.com> Co-authored-by: Pranjal Shankhdhar <pranjal.ssh@gmail.com> Co-authored-by: Lee Nau <lee.nau@gmail.com> Co-authored-by: HMING <126185151+Hearum@users.noreply.github.com> Co-authored-by: elvischenv <219235043+elvischenv@users.noreply.github.com> Co-authored-by: Byron Hsu <byronhsu1230@gmail.com> Co-authored-by: Byron Hsu <byron+per@periodiclabs.ai> Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: Thomas Wang <thomawan@amd.com> Co-authored-by: Xinyi Song <86638975+RolaoDenthu@users.noreply.github.com> Co-authored-by: Mohammad Miadh Angkad <176301910+mmangkad@users.noreply.github.com> Co-authored-by: Cheng Wan <cheng.wan@radixark.ai> Co-authored-by: BBuf <xiaoyu.zhang@radixark.ai> Co-authored-by: Hanming Lu <hanminglu@meta.com> Co-authored-by: Xinyi Song <xinyis10@illinois.edu>
126 lines
3.7 KiB
Python
126 lines
3.7 KiB
Python
import asyncio
|
|
import sys
|
|
|
|
import pytest
|
|
|
|
from sglang.srt.disaggregation.encode_server import (
|
|
EncoderScheduler,
|
|
PendingRequest,
|
|
_resolve_encoder_batch_policy,
|
|
)
|
|
from sglang.test.ci.ci_register import register_cpu_ci
|
|
|
|
register_cpu_ci(est_time=1, suite="base-a-test-cpu")
|
|
|
|
|
|
def _pending(modality: str = "image") -> PendingRequest:
|
|
return PendingRequest(
|
|
{"req_id": f"{modality}-request", "modality": modality},
|
|
asyncio.get_running_loop(),
|
|
)
|
|
|
|
|
|
def test_collect_batch_yields_for_concurrent_image_request_without_fixed_wait():
|
|
# The end-to-end coalescing test cannot replace this case: asyncio.gather
|
|
# enqueues both requests within one event-loop turn, so it passes even with
|
|
# the yield removed. Only a second request enqueued from a separate task
|
|
# observes whether _collect_batch yields at all.
|
|
async def run_test():
|
|
scheduler = EncoderScheduler(
|
|
encoder=None,
|
|
send_sockets=[],
|
|
max_batch_size=8,
|
|
coalesce_same_turn=True,
|
|
)
|
|
first = _pending()
|
|
second = _pending()
|
|
await scheduler.pending_queue.put(first)
|
|
|
|
async def enqueue_after_worker_yields():
|
|
await scheduler.pending_queue.put(second)
|
|
|
|
producer = asyncio.create_task(enqueue_after_worker_yields())
|
|
batch = await scheduler._collect_batch()
|
|
await producer
|
|
|
|
assert batch == [first, second]
|
|
|
|
asyncio.run(run_test())
|
|
|
|
|
|
def test_collect_batch_respects_max_batch_size():
|
|
async def run_test():
|
|
scheduler = EncoderScheduler(
|
|
encoder=None,
|
|
send_sockets=[],
|
|
max_batch_size=2,
|
|
coalesce_same_turn=True,
|
|
)
|
|
requests = [_pending() for _ in range(3)]
|
|
for request in requests:
|
|
await scheduler.pending_queue.put(request)
|
|
|
|
assert await scheduler._collect_batch() == requests[:2]
|
|
assert scheduler.pending_queue.get_nowait() is requests[2]
|
|
|
|
asyncio.run(run_test())
|
|
|
|
|
|
def test_scheduler_coalesces_concurrent_submissions():
|
|
class FakeEncoder:
|
|
def __init__(self):
|
|
self.encode_dispatch_lock = asyncio.Lock()
|
|
self.batches = []
|
|
|
|
async def batch_encode(self, requests, _modality):
|
|
self.batches.append([request["req_id"] for request in requests])
|
|
return [(1, 2, 3, None, None) for _ in requests]
|
|
|
|
async def run_test():
|
|
encoder = FakeEncoder()
|
|
scheduler = EncoderScheduler(
|
|
encoder=encoder,
|
|
send_sockets=[],
|
|
max_batch_size=8,
|
|
coalesce_same_turn=True,
|
|
)
|
|
scheduler.start()
|
|
try:
|
|
requests = [
|
|
{
|
|
"req_id": f"image-{index}",
|
|
"modality": "image",
|
|
"mm_items": [object()],
|
|
"num_parts": 1,
|
|
"part_idx": 0,
|
|
}
|
|
for index in range(2)
|
|
]
|
|
results = await asyncio.gather(
|
|
*(scheduler.submit(request) for request in requests)
|
|
)
|
|
finally:
|
|
await scheduler.stop()
|
|
|
|
assert encoder.batches == [["image-0", "image-1"]]
|
|
assert results == [(1, 2, 3, None, None)] * 2
|
|
|
|
asyncio.run(run_test())
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("model_type", "configured", "explicit", "expected"),
|
|
[
|
|
("kimi_k3", 8, False, (2, True)),
|
|
("kimi_k3", 8, True, (8, True)),
|
|
("kimi_k3", 1, False, (1, True)),
|
|
("qwen3_vl", 8, False, (8, False)),
|
|
],
|
|
)
|
|
def test_resolve_encoder_batch_policy(model_type, configured, explicit, expected):
|
|
assert _resolve_encoder_batch_policy(model_type, configured, explicit) == expected
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(pytest.main([__file__, "-v"]))
|