@@ -165,20 +165,23 @@ no_dp = decode.get('no_dp', {})
165165# Decode DP config
166166print(f'DECODE_MAX_RUNNING_REQUESTS_DP=\" {dp.get(\" max_running_requests\" , 4096)}\" ')
167167print(f'DECODE_CHUNKED_PREFILL_SIZE_DP=\" {eval_formula(dp.get(\" chunked_prefill_size\" , 262144))}\" ')
168+ print(f'DECODE_CONTEXT_LENGTH_DP=\" {dp.get(\" context_length\" , \"\" )}\" ')
168169s, e = parse_range(dp.get('cuda_graph_bs_range', '1-160'), 1, 160)
169170print(f'DECODE_CUDA_GRAPH_BS_DP_START=\" {s}\" ')
170171print(f'DECODE_CUDA_GRAPH_BS_DP_END=\" {e}\" ')
171172
172173# Decode EP-only config (EP enabled but DP disabled)
173174print(f'DECODE_MAX_RUNNING_REQUESTS_EP_ONLY=\" {ep_only.get(\" max_running_requests\" , 256)}\" ')
174175print(f'DECODE_CHUNKED_PREFILL_SIZE_EP_ONLY=\" {eval_formula(ep_only.get(\" chunked_prefill_size\" , 262144))}\" ')
176+ print(f'DECODE_CONTEXT_LENGTH_EP_ONLY=\" {ep_only.get(\" context_length\" , \"\" )}\" ')
175177s, e = parse_range(ep_only.get('cuda_graph_bs_range', '1-256'), 1, 256)
176178print(f'DECODE_CUDA_GRAPH_BS_EP_ONLY_START=\" {s}\" ')
177179print(f'DECODE_CUDA_GRAPH_BS_EP_ONLY_END=\" {e}\" ')
178180
179181# Decode no-DP config
180182print(f'DECODE_MAX_RUNNING_REQUESTS_NO_DP=\" {no_dp.get(\" max_running_requests\" , 128)}\" ')
181183print(f'DECODE_CHUNKED_PREFILL_SIZE_NO_DP=\" {eval_formula(no_dp.get(\" chunked_prefill_size\" , 262144))}\" ')
184+ print(f'DECODE_CONTEXT_LENGTH_NO_DP=\" {no_dp.get(\" context_length\" , \"\" )}\" ')
182185s, e = parse_range(no_dp.get('cuda_graph_bs_range', '1-128'), 1, 128)
183186print(f'DECODE_CUDA_GRAPH_BS_NO_DP_START=\" {s}\" ')
184187print(f'DECODE_CUDA_GRAPH_BS_NO_DP_END=\" {e}\" ')
216219if [[ " $DECODE_ENABLE_DP " == " true" ]]; then
217220 decode_cuda_graph_bs=($( seq $DECODE_CUDA_GRAPH_BS_DP_START $DECODE_CUDA_GRAPH_BS_DP_END ) )
218221 decode_max_running_requests=$(( DECODE_CUDA_GRAPH_BS_DP_END * DECODE_TP_SIZE))
222+ decode_context_length=$DECODE_CONTEXT_LENGTH_DP
219223elif [[ " $DECODE_ENABLE_EP " == " true" ]]; then
220224 decode_cuda_graph_bs=($( seq $DECODE_CUDA_GRAPH_BS_EP_ONLY_START $DECODE_CUDA_GRAPH_BS_EP_ONLY_END ) )
221225 decode_max_running_requests=$DECODE_MAX_RUNNING_REQUESTS_EP_ONLY
226+ decode_context_length=$DECODE_CONTEXT_LENGTH_EP_ONLY
222227else
223228 decode_cuda_graph_bs=($( seq $DECODE_CUDA_GRAPH_BS_NO_DP_START $DECODE_CUDA_GRAPH_BS_NO_DP_END ) )
224229 decode_max_running_requests=$DECODE_MAX_RUNNING_REQUESTS_NO_DP
230+ decode_context_length=$DECODE_CONTEXT_LENGTH_NO_DP
231+ fi
232+ # In PD-disaggregation the decode must admit requests against the SAME context
233+ # length as prefill; otherwise decode accepts over-length requests that prefill
234+ # rejects, and those requests hang forever waiting for a KV transfer that never
235+ # comes (the 8k1k conc-500 straggler). Fall back to the prefill value if the
236+ # decode context_length is not set in the model config, so the two always agree.
237+ if [[ -z " $decode_context_length " ]]; then
238+ decode_context_length=$prefill_context_length
225239fi
226240
227241# When both DP and EP are enabled, override max-running-requests and dispatch tokens
@@ -267,6 +281,10 @@ DECODE_MODE_FLAGS="--mem-fraction-static ${DECODE_MEM_FRACTION_STATIC} --max-run
267281if [[ " $DECODE_PREFILL_ROUND_ROBIN_BALANCE " == " True" ]] || [[ " $DECODE_PREFILL_ROUND_ROBIN_BALANCE " == " true" ]]; then
268282 DECODE_MODE_FLAGS=" $DECODE_MODE_FLAGS --prefill-round-robin-balance"
269283fi
284+ if [[ -n " $decode_context_length " ]]; then
285+ DECODE_MODE_FLAGS=" $DECODE_MODE_FLAGS --context-length ${decode_context_length} "
286+ fi
287+
270288if [[ " $DECODE_DISAGG_ENABLE_RADIX_CACHE " == " True" ]] || [[ " $DECODE_DISAGG_ENABLE_RADIX_CACHE " == " true" ]]; then
271289 DECODE_MODE_FLAGS=" $DECODE_MODE_FLAGS --disaggregation-decode-enable-radix-cache"
272290fi
@@ -750,26 +768,37 @@ if [ "$NODE_RANK" -eq 0 ]; then
750768 fi
751769 echo " Congratulations!!! All prefill and decode servers are up . . ."
752770
753- # Router resilience: a single prefill worker doing huge long-context prefills
754- # (256K+ token agentic prompts in 65280-token chunks) can be slow to drain a
755- # concurrent burst. With defaults the circuit breaker opens after 10 failures
756- # and short-circuits the whole worker, so the aiperf profiling burst sees
757- # "No available prefill workers (all circuits open or unhealthy)" and aborts
758- # with 100% errors. Disable the breaker and relax health-check sensitivity so
759- # a busy-but-alive worker is not ejected. Override via ROUTER_RESILIENCE_FLAGS.
760- ROUTER_RESILIENCE_FLAGS=" ${ROUTER_RESILIENCE_FLAGS:- --disable-circuit-breaker --health-failure-threshold 100 --health-check-timeout-secs 600 --health-check-interval-secs 30} "
761-
762- # Router scheduling policy. cache_aware prefill routing exploits HiCache/radix
763- # prefix reuse across the agentic trace; round_robin decode keeps the single
764- # decode worker fed evenly. cache_threshold / balance_*_threshold tune the
765- # cache_aware load-balancing (router defaults are 0.5 / 64 / 1.5). Override any
766- # of these via env.
767- ROUTER_PREFILL_POLICY=" ${ROUTER_PREFILL_POLICY:- cache_aware} "
768- ROUTER_DECODE_POLICY=" ${ROUTER_DECODE_POLICY:- round_robin} "
769- ROUTER_CACHE_THRESHOLD=" ${ROUTER_CACHE_THRESHOLD:- 0.3} "
770- ROUTER_BALANCE_ABS_THRESHOLD=" ${ROUTER_BALANCE_ABS_THRESHOLD:- 2} "
771- ROUTER_BALANCE_REL_THRESHOLD=" ${ROUTER_BALANCE_REL_THRESHOLD:- 1.1} "
772- ROUTER_POLICY_FLAGS=" ${ROUTER_POLICY_FLAGS:- --policy ${ROUTER_PREFILL_POLICY} --prefill-policy ${ROUTER_PREFILL_POLICY} --decode-policy ${ROUTER_DECODE_POLICY} --cache-threshold ${ROUTER_CACHE_THRESHOLD} --balance-abs-threshold ${ROUTER_BALANCE_ABS_THRESHOLD} --balance-rel-threshold ${ROUTER_BALANCE_REL_THRESHOLD} } "
771+ if [[ " ${IS_AGENTIC:- 0} " == " 1" || " ${IS_AGENTIC:- } " == " true" ]]; then
772+ # Agentic router config (main): long-context prefills can look unhealthy to
773+ # the default circuit breaker during a concurrent burst. Disable the breaker
774+ # and relax health-check sensitivity so a busy-but-alive worker is not
775+ # ejected. cache_aware prefill routing exploits HiCache/radix prefix reuse
776+ # across the agentic trace; round_robin decode keeps the single decode worker
777+ # fed evenly. Override via ROUTER_RESILIENCE_FLAGS / ROUTER_POLICY_FLAGS.
778+ ROUTER_RESILIENCE_FLAGS=" ${ROUTER_RESILIENCE_FLAGS:- --disable-circuit-breaker --health-failure-threshold 100 --health-check-timeout-secs 600 --health-check-interval-secs 30} "
779+ ROUTER_PREFILL_POLICY=" ${ROUTER_PREFILL_POLICY:- cache_aware} "
780+ ROUTER_DECODE_POLICY=" ${ROUTER_DECODE_POLICY:- round_robin} "
781+ ROUTER_CACHE_THRESHOLD=" ${ROUTER_CACHE_THRESHOLD:- 0.3} "
782+ ROUTER_BALANCE_ABS_THRESHOLD=" ${ROUTER_BALANCE_ABS_THRESHOLD:- 2} "
783+ ROUTER_BALANCE_REL_THRESHOLD=" ${ROUTER_BALANCE_REL_THRESHOLD:- 1.1} "
784+ ROUTER_POLICY_FLAGS=" ${ROUTER_POLICY_FLAGS:- --policy ${ROUTER_PREFILL_POLICY} --prefill-policy ${ROUTER_PREFILL_POLICY} --decode-policy ${ROUTER_DECODE_POLICY} --cache-threshold ${ROUTER_CACHE_THRESHOLD} --balance-abs-threshold ${ROUTER_BALANCE_ABS_THRESHOLD} --balance-rel-threshold ${ROUTER_BALANCE_REL_THRESHOLD} } "
785+ else
786+ # DI router config (8k1k branch, run 28696443568): with defaults the per-worker
787+ # circuit stays OPEN for cb-timeout-duration-secs=60 before a half-open retrial.
788+ # In that run every request 503'd from request #1 ("all circuits open or
789+ # unhealthy") for ~31s and lm_eval (max_retries=5) then gave up -- i.e. the
790+ # circuit was still open when the client budget ran out, so 0 result files were
791+ # produced. Shortening the open->half-open window (and letting the router itself
792+ # retry a failed worker selection) lets a transient trip re-close INSIDE the
793+ # client retry budget instead of nuking the whole eval. The breaker stays fully
794+ # ENABLED (thresholds unchanged); this only speeds recovery. Override via
795+ # ROUTER_CB_ARGS / ROUTER_POLICY_FLAGS.
796+ ROUTER_CB_ARGS=" ${ROUTER_CB_ARGS:- --cb-timeout-duration-secs 15 --retry-max-retries 3} "
797+ ROUTER_POLICY_FLAGS=" ${ROUTER_POLICY_FLAGS:- --policy random --prefill-policy random --decode-policy random} "
798+ ROUTER_RESILIENCE_FLAGS=" ${ROUTER_RESILIENCE_FLAGS:- ${ROUTER_CB_ARGS} } "
799+ fi
800+
801+ echo " Router config: IS_AGENTIC=${IS_AGENTIC:- 0} policy/resilience=${ROUTER_POLICY_FLAGS} ${ROUTER_RESILIENCE_FLAGS} "
773802
774803 ROUTER_CMD=" python -m sglang_router.launch_router \
775804 --pd-disaggregation \
@@ -822,6 +851,47 @@ if [ "$NODE_RANK" -eq 0 ]; then
822851 wait_or_die " $prefill0_pid " bash -c " $HEALTH_BARRIER_CMD " || exit 1
823852 fi
824853
854+ # ---- End-to-end router readiness canary (run 28696443568) ----
855+ # The /readiness barrier above only proves the router PROCESS is up; it does
856+ # NOT prove the router can reach a prefill worker and complete a generation.
857+ # In that run the eval fired the instant /readiness passed and EVERY request
858+ # 503'd ("No available prefill workers (all circuits open or unhealthy)") from
859+ # request #1 -> lm_eval gave up -> 0 result files -> "Verify eval scores" failed.
860+ # Gate the benchmark on ONE successful generation THROUGH the router so the eval
861+ # never starts against a router whose prefill path is not yet actually serving.
862+ #
863+ # Run the poll loop under wait_or_die (like the barriers above) rather than
864+ # inline: the canary is the FIRST real generation through prefill, so a
865+ # prefill crash right after /readiness passes is plausible. Without
866+ # wait_or_die watching prefill0_pid, that just looks like repeated 503s and
867+ # the loop burns the full ROUTER_CANARY_TIMEOUT (default 600s) instead of
868+ # detecting the dead pid and aborting in ~5s.
869+ run_router_canary () {
870+ local canary_url=" http://${NODE0_ADDR} :30000/v1/chat/completions"
871+ local canary_model=" ${MODEL_DIR} /${MODEL_NAME} "
872+ local canary_deadline=$(( $(date +% s) + ${ROUTER_CANARY_TIMEOUT:- 600} ))
873+ local canary_code
874+ while [ " $( date +%s) " -lt " $canary_deadline " ]; do
875+ canary_code=$( curl -s -o /tmp/router_canary.out -w ' %{http_code}' \
876+ -m " ${ROUTER_CANARY_REQ_TIMEOUT:- 120} " \
877+ -X POST " $canary_url " -H ' Content-Type: application/json' \
878+ -d " {\" model\" :\" ${canary_model} \" ,\" messages\" :[{\" role\" :\" user\" ,\" content\" :\" ping\" }],\" max_tokens\" :1,\" temperature\" :0}" 2> /dev/null)
879+ if [ " $canary_code " = " 200" ] && \
880+ ! grep -qE " circuits open|server_selection_failed|No available" /tmp/router_canary.out 2> /dev/null; then
881+ echo " Router readiness canary passed (end-to-end generation OK)"
882+ return 0
883+ fi
884+ echo " Router readiness canary not ready yet (http=${canary_code} ); retrying in 5s . . ."
885+ sleep 5
886+ done
887+ echo " ERROR: router readiness canary failed after ${ROUTER_CANARY_TIMEOUT:- 600} s -- the router cannot complete a generation through a prefill worker (all circuits open/unhealthy). Refusing to start the eval against a non-serving router."
888+ head -c 800 /tmp/router_canary.out 2> /dev/null
889+ return 1
890+ }
891+ if [[ " ${ROUTER_READINESS_CANARY:- 1} " == " 1" ]]; then
892+ wait_or_die " $prefill0_pid " run_router_canary || exit 1
893+ fi
894+
825895 echo " Router is ready for benchmarking"
826896 fi
827897
0 commit comments