Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
400 changes: 395 additions & 5 deletions MODULE.bazel.lock

Large diffs are not rendered by default.

1,061 changes: 1,061 additions & 0 deletions STARGATE_CONFIG_FILE_PLAN.md

Large diffs are not rendered by default.

583 changes: 533 additions & 50 deletions deploy/helm/llm-request-router/bin/manifest.yaml

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

{{- $workloadKind := include "llm-request-router.workloadKind" . -}}
{{- $backendRouterEnabled := eq (include "llm-request-router.backendRouterEnabled" .) "true" -}}
{{- $directDeployment := and (eq $workloadKind "Deployment") (not $backendRouterEnabled) -}}
{{- $replicaCount := .Values.llmRequestRouter.replicaCount | int -}}
{{- $namespace := include "llm-request-router.namespace" . -}}
{{- $discoveryDnsName := dig "discovery" "dnsName" (printf "%s.%s.svc.cluster.local" .Values.llmRequestRouter.service.headlessName $namespace) .Values.llmRequestRouter -}}
{{- $advertisedHostnameTemplate := include "llm-request-router.advertisedHostnameTemplate" . -}}
{{- $useVaultSecrets := not (and .Values.llmRequestRouter.vault .Values.llmRequestRouter.vault.noVaultAnnotations) -}}
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "llm-request-router.fullname" . }}-stargate
namespace: {{ $namespace }}
labels:
{{- include "llm-request-router.labels" . | nindent 4 }}
data:
stargate.toml: |
schema_version = 1

[stargate_identity]
id = { env = "POD_NAME" }
advertised_hostname_template = {{ $advertisedHostnameTemplate | quote }}

[stargate_identity.kubernetes]
pod_name = { env = "POD_NAME" }
namespace = { env = "POD_NAMESPACE" }

[stargate_network]
grpc_listen_addr = {{ printf "0.0.0.0:%v" .Values.llmRequestRouter.service.grpcPort | quote }}
model_discovery_listen_addr = "0.0.0.0:50073"
http_listen_addr = {{ printf "0.0.0.0:%v" .Values.llmRequestRouter.service.httpPort | quote }}
advertise_addr = { env = "STARGATE_ADVERTISE_ADDR" }

[process_lifecycle]
readiness_warmup_ms = {{ .Values.llmRequestRouter.readiness.warmupMs }}
shutdown_drain_timeout_ms = {{ .Values.llmRequestRouter.shutdown.drainTimeoutMs }}

[stargate_discovery]
remote_watch_urls = {{ .Values.llmRequestRouter.discovery.remoteWatchUrls | toJson }}
allow_insecure_remote_watch_http = {{ .Values.llmRequestRouter.discovery.allowInsecureRemoteWatchHttp }}
watch_heartbeat_ms = {{ .Values.llmRequestRouter.discovery.watchHeartbeatMs }}
{{ if not $directDeployment }}
[stargate_discovery.kubernetes_pods]
headless_service_dns_name = {{ $discoveryDnsName | quote }}
poll_interval_ms = {{ dig "discovery" "dnsPollMs" 1000 .Values.llmRequestRouter }}
resolver_ttl_ms = {{ dig "discovery" "dnsResolverTtlMs" 1000 .Values.llmRequestRouter }}
{{ end }}
[pylon_transport]
tunnel_protocol = "raw-quic"
quic_connect_timeout_ms = {{ .Values.llmRequestRouter.transport.quicConnectTimeoutMs }}
quic_request_timeout_ms = {{ .Values.llmRequestRouter.transport.quicRequestTimeoutMs }}
{{ if $backendRouterEnabled }}
pylon_grpc_dial_uri = {{ include "llm-request-router.backendRouterGrpcDialAddress" . | quote }}
{{ end }}
{{ if .Values.llmRequestRouter.transport.reverseTunnelListenAddr }}
[pylon_transport.reverse]
listen_addr = {{ .Values.llmRequestRouter.transport.reverseTunnelListenAddr | quote }}
connect_timeout_ms = {{ .Values.llmRequestRouter.transport.reverseTunnelConnectTimeoutMs }}
{{ if $backendRouterEnabled }}
pylon_dial_addr = {{ include "llm-request-router.backendRouterReverseTunnelDialAddress" . | quote }}
{{ else if gt $replicaCount 1 }}
pylon_dial_addr = { env = "STARGATE_REVERSE_PYLON_DIAL_ADDR" }
{{ end }}
{{ with .Values.llmRequestRouter.tls.certPath }}
certificate_path = {{ . | quote }}
{{ end }}
{{ with .Values.llmRequestRouter.tls.keyPath }}
private_key_path = {{ . | quote }}
{{ end }}
{{ else }}
[pylon_transport.direct]
connections = 1
{{ with .Values.llmRequestRouter.tls.certPath }}
trust_bundle_path = {{ . | quote }}
{{ end }}
{{ end }}
[pylon_transport.tls]
insecure_skip_verify = {{ .Values.llmRequestRouter.tls.quicInsecure }}

{{ if or .Values.llmRequestRouter.loadBalancer.config .Values.llmRequestRouter.loadBalancer.configPath }}
[request_proxy.load_balancer]
{{ if .Values.llmRequestRouter.loadBalancer.config }}
config_path = "/etc/llm-request-router/lb-config.json"
{{ else }}
config_path = {{ .Values.llmRequestRouter.loadBalancer.configPath | quote }}
{{ end }}
{{ end }}
[observability.metrics]
listen_addr = {{ printf "0.0.0.0:%v" .Values.llmRequestRouter.service.metricsPort | quote }}
prefix = "stargate_"

{{ if and .Values.llmRequestRouter.observability.tracing.enabled .Values.llmRequestRouter.observability.tracing.endpoint }}
[observability.tracing]
endpoint = {{ .Values.llmRequestRouter.observability.tracing.endpoint | quote }}
{{ if and .Values.llmRequestRouter.auth.workerAuthEndpoint $useVaultSecrets }}

[observability.tracing.access_token]
secrets_path = "/vault/secrets/secrets.json"
json_path = ["tracingAccessToken"]
{{ end }}
{{ end }}
{{ with .Values.llmRequestRouter.auth.workerAuthEndpoint }}
[worker_authentication]
endpoint = {{ . | quote }}
{{ if $useVaultSecrets }}

[worker_authentication.bearer_token]
secrets_path = "/vault/secrets/secrets.json"
json_path = ["nvcfApiToken"]
{{ end }}
{{ end }}
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,6 @@ router instance that issued the tunnel target.
*/}}
{{- $replicaCount := .Values.llmRequestRouter.replicaCount | int }}
{{- $backendRouterEnabled := eq (include "llm-request-router.backendRouterEnabled" .) "true" }}
{{- $configuredDisableDnsDiscovery := dig "discovery" "disableDnsDiscovery" false .Values.llmRequestRouter }}
{{- $directDeployment := and (eq $workloadKind "Deployment") (not $backendRouterEnabled) }}
{{- $disableDnsDiscovery := or $configuredDisableDnsDiscovery $directDeployment }}
{{- $watchHeartbeatMs := .Values.llmRequestRouter.discovery.watchHeartbeatMs | int }}
{{- if le $watchHeartbeatMs 0 }}
{{- fail "llmRequestRouter.discovery.watchHeartbeatMs must be greater than 0" }}
Expand All @@ -48,15 +45,11 @@ router instance that issued the tunnel target.
{{- if and (eq $workloadKind "Deployment") (gt $replicaCount 1) (not $backendRouterEnabled) }}
{{- fail "llmRequestRouter.backendRouter.enabled must be true when llmRequestRouter.workload.kind is Deployment and replicaCount is greater than 1" }}
{{- end }}
{{- if and $configuredDisableDnsDiscovery (gt $replicaCount 1) (not $backendRouterEnabled) }}
{{- fail "llmRequestRouter.discovery.disableDnsDiscovery cannot be true when llmRequestRouter.replicaCount is greater than 1; multi-replica routers require DNS discovery" }}
{{- end }}
{{- /*
The identity guard lives here, not in certificate.yaml: existingSecret mode
renders no Certificate, so a guard in that template would never run.
*/}}
{{- include "llm-request-router.validateTlsIdentity" . }}
{{- $advertisedHostnameTemplate := include "llm-request-router.advertisedHostnameTemplate" . }}
{{- if and $backendRouterEnabled (not .Values.llmRequestRouter.transport.reverseTunnelListenAddr) }}
{{- fail "llmRequestRouter.backendRouter.enabled requires llmRequestRouter.transport.reverseTunnelListenAddr" }}
{{- end }}
Expand Down Expand Up @@ -94,6 +87,7 @@ spec:
{{- toYaml . | nindent 8 }}
{{- end }}
annotations:
checksum/stargate-config: {{ include (print $.Template.BasePath "/configmap-stargate.yaml") . | sha256sum }}
{{- include "llm-request-router.podAnnotations" . | nindent 8 }}
spec:
serviceAccountName: {{ include "llm-request-router.serviceAccountName" . }}
Expand All @@ -109,74 +103,7 @@ spec:
image: {{ include "llm-request-router.image" . }}
imagePullPolicy: {{ .Values.llmRequestRouter.image.pullPolicy }}
args:
- --stargate-id=$(POD_NAME)
- --listen-addr=0.0.0.0:{{ .Values.llmRequestRouter.service.grpcPort }}
- --http-listen-addr=0.0.0.0:{{ .Values.llmRequestRouter.service.httpPort }}
- --advertise-addr=$(POD_IP):{{ .Values.llmRequestRouter.service.grpcPort }}
- --stargate-discovery-dns-name={{ dig "discovery" "dnsName" (printf "%s.%s.svc.cluster.local" .Values.llmRequestRouter.service.headlessName (include "llm-request-router.namespace" .)) .Values.llmRequestRouter }}
- --pod-name=$(POD_NAME)
- --pod-namespace=$(POD_NAMESPACE)
- --advertised-hostname-template={{ $advertisedHostnameTemplate }}
{{- if $disableDnsDiscovery }}
- --disable-dns-discovery
{{- end }}
{{- with dig "discovery" "dnsPollMs" "" .Values.llmRequestRouter }}
- --dns-poll-ms={{ . }}
{{- end }}
{{- with dig "discovery" "dnsResolverTtlMs" "" .Values.llmRequestRouter }}
- --dns-resolver-ttl-ms={{ . }}
{{- end }}
{{- with dig "discovery" "watchHeartbeatMs" "" .Values.llmRequestRouter }}
- --watch-heartbeat-ms={{ . }}
{{- end }}
{{- range .Values.llmRequestRouter.discovery.remoteWatchUrls }}
- --remote-stargate-url={{ . }}
{{- end }}
{{- if .Values.llmRequestRouter.discovery.allowInsecureRemoteWatchHttp }}
- --allow-insecure-remote-watch-http
{{- end }}
- --shutdown-drain-timeout-ms={{ .Values.llmRequestRouter.shutdown.drainTimeoutMs }}
- --readiness-warmup-ms={{ $readinessWarmupMs }}
- --quic-connect-timeout-ms={{ .Values.llmRequestRouter.transport.quicConnectTimeoutMs }}
- --quic-request-timeout-ms={{ .Values.llmRequestRouter.transport.quicRequestTimeoutMs }}
- --metrics-port={{ .Values.llmRequestRouter.service.metricsPort }}
{{- if .Values.llmRequestRouter.transport.reverseTunnelListenAddr }}
- --backend-connectivity=reverse
- --reverse-tunnel-listen-addr={{ .Values.llmRequestRouter.transport.reverseTunnelListenAddr }}
{{- end }}
{{- if $backendRouterEnabled }}
- --grpc-pylon-dial-addr={{ include "llm-request-router.backendRouterGrpcDialAddress" . }}
- --reverse-tunnel-pylon-dial-addr={{ include "llm-request-router.backendRouterReverseTunnelDialAddress" . }}
{{- else if and .Values.llmRequestRouter.transport.reverseTunnelListenAddr (gt $replicaCount 1) }}
- --reverse-tunnel-pylon-dial-addr=$(POD_IP):{{ .Values.llmRequestRouter.service.reverseTunnelPort }}
{{- end }}
{{- if .Values.llmRequestRouter.transport.reverseTunnelConnectTimeoutMs }}
- --reverse-tunnel-connect-timeout-ms={{ .Values.llmRequestRouter.transport.reverseTunnelConnectTimeoutMs }}
{{- end }}
{{- if .Values.llmRequestRouter.loadBalancer.config }}
- --lb-config-path=/etc/llm-request-router/lb-config.json
{{- else if .Values.llmRequestRouter.loadBalancer.configPath }}
- --lb-config-path={{ .Values.llmRequestRouter.loadBalancer.configPath }}
{{- end }}
{{- if and .Values.llmRequestRouter.observability.tracing.enabled .Values.llmRequestRouter.observability.tracing.endpoint }}
- --otel-endpoint={{ .Values.llmRequestRouter.observability.tracing.endpoint }}
{{- end }}
{{- with .Values.llmRequestRouter.auth.workerAuthEndpoint }}
- --worker-auth-endpoint={{ . }}
{{- if not (and $.Values.llmRequestRouter.vault $.Values.llmRequestRouter.vault.noVaultAnnotations) }}
- --secrets-path=/vault/secrets/secrets.json
- --secrets-json-path=nvcfApiToken
{{- end }}
{{- end }}
{{- with .Values.llmRequestRouter.tls.certPath }}
- --tls-cert-path={{ . }}
{{- end }}
{{- with .Values.llmRequestRouter.tls.keyPath }}
- --tls-key-path={{ . }}
{{- end }}
{{- if .Values.llmRequestRouter.tls.quicInsecure }}
- --quic-insecure
{{- end }}
- --config-file=/etc/stargate/stargate.toml
env:
- name: POD_NAME
valueFrom:
Expand All @@ -190,6 +117,12 @@ spec:
valueFrom:
fieldRef:
fieldPath: status.podIP
- name: STARGATE_ADVERTISE_ADDR
value: "$(POD_IP):{{ .Values.llmRequestRouter.service.grpcPort }}"
{{- if and (not $backendRouterEnabled) .Values.llmRequestRouter.transport.reverseTunnelListenAddr (gt $replicaCount 1) }}
- name: STARGATE_REVERSE_PYLON_DIAL_ADDR
value: "$(POD_IP):{{ .Values.llmRequestRouter.service.reverseTunnelPort }}"
{{- end }}
ports:
- name: http
containerPort: {{ .Values.llmRequestRouter.service.httpPort }}
Expand Down Expand Up @@ -220,6 +153,9 @@ spec:
securityContext:
{{- toYaml .Values.llmRequestRouter.securityContext | nindent 12 }}
volumeMounts:
- name: stargate-config
mountPath: /etc/stargate
readOnly: true
- name: vault-config-templates
mountPath: /vault/config/templates
readOnly: true
Expand All @@ -236,6 +172,9 @@ spec:
readOnly: true
{{- end }}
volumes:
- name: stargate-config
configMap:
name: {{ include "llm-request-router.fullname" . }}-stargate
- name: vault-token
projected:
sources:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,6 @@ llmRequestRouter:
memory: 256Mi

discovery:
disableDnsDiscovery: false
# Maximum interval between unchanged WatchStargates snapshots. This is
# shared by Stargate and the EndpointSlice backend router.
watchHeartbeatMs: 5000
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,9 +166,9 @@ assert_zero_config_contains() {
fi
}

assert_zero_config_contains "--grpc-pylon-dial-addr=http://llm-request-router-backend-router.nvcf.svc.cluster.local:50071" \
assert_zero_config_contains 'pylon_grpc_dial_uri = "http://llm-request-router-backend-router.nvcf.svc.cluster.local:50071"' \
"gRPC dial address must default to the in-cluster backend-router Service"
assert_zero_config_contains "--reverse-tunnel-pylon-dial-addr=llm-request-router-backend-router.nvcf.svc.cluster.local:50072" \
assert_zero_config_contains 'pylon_dial_addr = "llm-request-router-backend-router.nvcf.svc.cluster.local:50072"' \
"reverse-tunnel dial address must default to the in-cluster backend-router Service"

# An explicitly configured address must still win over the default.
Expand Down Expand Up @@ -207,9 +207,9 @@ assert_contains "image: registry.example.invalid/nvcf/stargate:next" \
"backend router must use its explicitly pinned Stargate image"
assert_contains "app.kubernetes.io/version: \"next\"" \
"backend router labels must identify the explicitly pinned image version"
assert_contains "--grpc-pylon-dial-addr=https://llm-router.example.invalid:443" \
assert_contains 'pylon_grpc_dial_uri = "https://llm-router.example.invalid:443"' \
"Stargate must advertise the external gRPC endpoint to pylon"
assert_contains "--reverse-tunnel-pylon-dial-addr=llm-router.example.invalid:8080" \
assert_contains 'pylon_dial_addr = "llm-router.example.invalid:8080"' \
"Stargate must advertise the external reverse-tunnel endpoint to pylon"
assert_contains "--tls-cert-path=/etc/stargate/tls/tls.crt" \
"backend router must use the Stargate TLS certificate"
Expand Down Expand Up @@ -404,7 +404,7 @@ single_replica="$(helm template llm-request-router "$chart_dir" \
--set llmRequestRouter.backendRouter.image.tag=next \
--set llmRequestRouter.backendRouter.pylonGrpcDialAddress=https://llm-router.example.invalid:443 \
--set llmRequestRouter.backendRouter.pylonReverseTunnelDialAddress=llm-router.example.invalid:8080)"
if ! grep -Fq -- "--advertised-hostname-template={pod_name}.llm-request-router-headless.nvcf.svc.cluster.local" <<<"$single_replica"; then
if ! grep -Fq -- 'advertised_hostname_template = "{pod_name}.llm-request-router-headless.nvcf.svc.cluster.local"' <<<"$single_replica"; then
echo "FAIL: backend routing must retain per-pod authority and SNI for one replica" >&2
exit 1
fi
Expand Down
Loading
Loading