Repository: kubernetes-sigs/gateway-api-inference-extension

Issue:
Support vLLM cache salting
<!-- Please only use this template for submitting enhancement requests -->

**What would you like to be added**:

vLLM now support adding an optional `cache_salt` which is used as a key to differentiate prefix caches if they have different salt but same prefix. This is a security feature.

```
{
  "messages": [
    {"role": "system", "content": "You are a helpful assistant."},
    {"role": "user", "content": "Here is a document with details about the world series: ..."},
    {"role": "user", "content": "Who won the world series in 2020?"}
  ],
  "cache_salt": "Z3V2bmV3aGxza3ZubGFoZ3Zud3V3ZWZ2bmd0b3V2bnZmc2xpZ3RoZ2x2aQ=="
}
```

https://github.com/vllm-project/vllm/pull/17045

The change would be to update the prefix aware scorers  to be aware of the salt. To implement this, we can add the `cache_salt` to the LLMRequest and make sure it's used to calculate prefix matches.

**Why is this needed**:


Discussion:
@Frapschen: /assign

Solution patch:
diff --git a/Makefile b/Makefile
index a2a2956..66b9a16 100644
--- a/Makefile
+++ b/Makefile
@@ -133,7 +133,7 @@ vet: ## Run go vet against code.
 	go vet ./...
 
 .PHONY: test
-test: generate fmt vet envtest image-build verify-crds ## Run tests.
+test: generate fmt vet envtest image-build verify-crds verify-helm-charts ## Run tests.
 	CGO_ENABLED=1 KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test $$(go list ./... | grep -v /e2e | grep -v /conformance) -race -coverprofile cover.out
 
 .PHONY: test-unit
@@ -174,6 +174,10 @@ verify: vet fmt-verify generate ci-lint api-lint verify-all
 verify-crds: kubectl-validate
 	hack/verify-manifests.sh
 
+.PHONY: verify-helm-charts
+verify-helm-charts: helm-install
+	hack/verify-helm.sh
+
 # Run static analysis.
 .PHONY: verify-all
 verify-all:
@@ -423,8 +427,9 @@ $(GOLANGCI_API_LINT):
 yq: ## Download yq locally if necessary.
 	GOBIN=$(PROJECT_DIR)/bin GO111MODULE=on go install github.com/mikefarah/yq/v4@$(YQ_VERSION)
 
-.PHONY: helm
-helm: ## Download helm locally if necessary.
+.PHONY: helm-install
+helm-install: $(HELM) ## Download helm locally if necessary.
+$(HELM): $(LOCALBIN)
 	GOBIN=$(PROJECT_DIR)/bin GO111MODULE=on go install helm.sh/helm/v3/cmd/helm@$(HELM_VERSION)
 
 .PHONY: kubectl-validate
diff --git a/api/v1/inferencepool_types.go b/api/v1/inferencepool_types.go
index 2c7b527..76026df 100644
--- a/api/v1/inferencepool_types.go
+++ b/api/v1/inferencepool_types.go
@@ -70,10 +70,12 @@ type InferencePoolSpec struct {
 	Selector LabelSelector `json:"selector,omitzero"`
 
 	// TargetPorts defines a list of ports that are exposed by this InferencePool.
-	// Currently, the list may only include a single port definition.
+	// Every port will be treated as a distinctive endpoint by EPP,
+	// addressable as a 'podIP:portNumber' combination.
 	//
 	// +kubebuilder:validation:MinItems=1
-	// +kubebuilder:validation:MaxItems=1
+	// +kubebuilder:validation:MaxItems=8
+	// +kubebuilder:validation:XValidation:message="port number must be unique",rule="self.all(p1, self.exists_one(p2, p1.number==p2.number))"
 	// +listType=atomic
 	// +required
 	TargetPorts []Port `json:"targetPorts,omitempty"`
diff --git a/config/crd/bases/inference.networking.k8s.io_inferencepools.yaml b/config/crd/bases/inference.networking.k8s.io_inferencepools.yaml
index ed325be..2ccaf23 100644
--- a/config/crd/bases/inference.networking.k8s.io_inferencepools.yaml
+++ b/config/crd/bases/inference.networking.k8s.io_inferencepools.yaml
@@ -154,7 +154,8 @@ spec:
               targetPorts:
                 description: |-
                   TargetPorts defines a list of ports that are exposed by this InferencePool.
-                  Currently, the list may only include a single port definition.
+                  Every port will be treated as a distinctive endpoint by EPP,
+                  addressable as a 'podIP:portNumber' combination.
                 items:
                   description: Port defines the network port that will be exposed
                     by this InferencePool.
@@ -170,10 +171,13 @@ spec:
                   required:
                   - number
                   type: object
-                maxItems: 1
+                maxItems: 8
                 minItems: 1
                 type: array
                 x-kubernetes-list-type: atomic
+                x-kubernetes-validations:
+                - message: port number must be unique
+                  rule: self.all(p1, self.exists_one(p2, p1.number==p2.number))
             required:
             - endpointPickerRef
             - selector
diff --git a/go.mod b/go.mod
index 1a60c36..2062225 100644
--- a/go.mod
+++ b/go.mod
@@ -15,7 +15,7 @@ require (
 	github.com/prometheus/client_golang v1.23.2
 	github.com/prometheus/client_model v0.6.2
 	github.com/prometheus/common v0.66.1
-	github.com/prometheus/prometheus v0.305.0
+	github.com/prometheus/prometheus v0.306.0
 	github.com/stretchr/testify v1.11.1
 	go.uber.org/multierr v1.11.0
 	go.uber.org/zap v1.27.0
@@ -106,16 +106,16 @@ require (
 	go.uber.org/automaxprocs v1.6.0 // indirect
 	go.yaml.in/yaml/v2 v2.4.2 // indirect
 	go.yaml.in/yaml/v3 v3.0.4 // indirect
-	golang.org/x/crypto v0.41.0 // indirect
+	golang.org/x/crypto v0.42.0 // indirect
 	golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 // indirect
-	golang.org/x/mod v0.27.0 // indirect
-	golang.org/x/net v0.43.0 // indirect
+	golang.org/x/mod v0.28.0 // indirect
+	golang.org/x/net v0.44.0 // indirect
 	golang.org/x/oauth2 v0.30.0 // indirect
-	golang.org/x/sys v0.35.0 // indirect
-	golang.org/x/term v0.34.0 // indirect
-	golang.org/x/text v0.28.0 // indirect
+	golang.org/x/sys v0.36.0 // indirect
+	golang.org/x/term v0.35.0 // indirect
+	golang.org/x/text v0.29.0 // indirect
 	golang.org/x/time v0.12.0 // indirect
-	golang.org/x/tools v0.36.0 // indirect
+	golang.org/x/tools v0.37.0 // indirect
 	golang.org/x/tools/go/expect v0.1.1-deprecated // indirect
 	gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
 	google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7 // indirect
diff --git a/go.sum b/go.sum
index 817b54b..6586a87 100644
--- a/go.sum
+++ b/go.sum
@@ -219,8 +219,8 @@ github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9Z
 github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA=
 github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
 github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
-github.com/prometheus/prometheus v0.305.0 h1:UO/LsM32/E9yBDtvQj8tN+WwhbyWKR10lO35vmFLx0U=
-github.com/prometheus/prometheus v0.305.0/go.mod h1:JG+jKIDUJ9Bn97anZiCjwCxRyAx+lpcEQ0QnZlUlbwY=
+github.com/prometheus/prometheus v0.306.0 h1:Q0Pvz/ZKS6vVWCa1VSgNyNJlEe8hxdRlKklFg7SRhNw=
+github.com/prometheus/prometheus v0.306.0/go.mod h1:7hMSGyZHt0dcmZ5r4kFPJ/vxPQU99N5/BGwSPDxeZrQ=
 github.com/prometheus/sigv4 v0.2.0 h1:qDFKnHYFswJxdzGeRP63c4HlH3Vbn1Yf/Ao2zabtVXk=
 github.com/prometheus/sigv4 v0.2.0/go.mod h1:D04rqmAaPPEUkjRQxGqjoxdyJuyCh6E0M18fZr0zBiE=
 github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
@@ -285,20 +285,20 @@ go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
-golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
+golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI=
+golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8=
 golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 h1:yqrTHse8TCMW1M1ZCP+VAR/l0kKxwaAIqN/il7x4voA=
 golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU=
 golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ=
-golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc=
+golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U=
+golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI=
 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
-golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
+golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I=
+golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
 golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
 golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -309,22 +309,22 @@ golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
-golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
-golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4=
-golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw=
+golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
+golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
+golang.org/x/term v0.35.0 h1:bZBVKBudEyhRcajGcNc3jIfWPqV4y/Kt2XcoigOWtDQ=
+golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
-golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
+golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
+golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
 golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
 golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
 golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
-golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
+golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE=
+golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w=
 golang.org/x/tools/go/expect v0.1.1-deprecated h1:jpBZDwmgPhXsKZC6WhL20P4b/wmnpsEAGHaNy0n/rJM=
 golang.org/x/tools/go/expect v0.1.1-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY=
 golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated h1:1h2MnaIAIXISqTFKdENegdpAgUXz6NrPEsbIeWaBRvM=
@@ -337,8 +337,8 @@ gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw
 gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY=
 gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
 gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
-google.golang.org/api v0.238.0 h1:+EldkglWIg/pWjkq97sd+XxH7PxakNYoe/rkSTbnvOs=
-google.golang.org/api v0.238.0/go.mod h1:cOVEm2TpdAGHL2z+UwyS+kmlGr3bVWQQ6sYEqkKje50=
+google.golang.org/api v0.239.0 h1:2hZKUnFZEy81eugPs4e2XzIJ5SOwQg0G82bpXD65Puo=
+google.golang.org/api v0.239.0/go.mod h1:cOVEm2TpdAGHL2z+UwyS+kmlGr3bVWQQ6sYEqkKje50=
 google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7 h1:FiusG7LWj+4byqhbvmB+Q93B/mOxJLN2DTozDuZm4EU=
 google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7/go.mod h1:kXqgZtrWaf6qS3jZOCnCH7WYfrvFjkC51bM8fz3RsCA=
 google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7 h1:pFyd6EwwL2TqFf8emdthzeX+gZE1ElRq3iM8pui4KBY=
diff --git a/hack/release-quickstart.sh b/hack/release-quickstart.sh
index 04b79a3..813146d 100755
--- a/hack/release-quickstart.sh
+++ b/hack/release-quickstart.sh
@@ -132,11 +132,15 @@ sed -i.bak '/us-central1-docker.pkg.dev\/k8s-staging-images\/gateway-api-inferen
 # Update the container registry for lora-syncer in vLLM CPU and GPU deployment manifests.
 sed -i.bak -E "s|us-central1-docker\.pkg\.dev/k8s-staging-images|registry.k8s.io|g" "$VLLM_GPU_DEPLOY" "$VLLM_CPU_DEPLOY"
 
+# Update IGW_CHART_VERSION in quickstart guide to match the current release tag
+GUIDES_INDEX="site-src/guides/index.md"
+sed -i.bak -E "s/export IGW_CHART_VERSION=v[0-9]+\.[0-9]+\.[0-9]+(-rc\.[0-9]+)?/export IGW_CHART_VERSION=${RELEASE_TAG}/g" "$GUIDES_INDEX"
+
 # -----------------------------------------------------------------------------
 # Stage the changes
 # -----------------------------------------------------------------------------
-echo "Staging $VERSION_FILE $UPDATED_CRD $README $EPP_HELM $BBR_HELM $CONFORMANCE_MANIFESTS $VLLM_GPU_DEPLOY $VLLM_CPU_DEPLOY $VLLM_SIM_DEPLOY files..."
-git add $VERSION_FILE $UPDATED_CRD $README $EPP_HELM $BBR_HELM $CONFORMANCE_MANIFESTS $VLLM_GPU_DEPLOY $VLLM_CPU_DEPLOY $VLLM_SIM_DEPLOY
+echo "Staging $VERSION_FILE $UPDATED_CRD $README $EPP_HELM $BBR_HELM $CONFORMANCE_MANIFESTS $VLLM_GPU_DEPLOY $VLLM_CPU_DEPLOY $VLLM_SIM_DEPLOY $GUIDES_INDEX files..."
+git add $VERSION_FILE $UPDATED_CRD $README $EPP_HELM $BBR_HELM $CONFORMANCE_MANIFESTS $VLLM_GPU_DEPLOY $VLLM_CPU_DEPLOY $VLLM_SIM_DEPLOY $GUIDES_INDEX
 
 # -----------------------------------------------------------------------------
 # Cleanup backup files and finish
diff --git a/hack/verify-helm.sh b/hack/verify-helm.sh
new file mode 100755
index 0000000..0388b6e
--- /dev/null
+++ b/hack/verify-helm.sh
@@ -0,0 +1,43 @@
+#!/bin/bash
+
+# Copyright 2025 The Kubernetes Authors.
+#
+# 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
+#
+#     http://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.
+
+SCRIPT_ROOT=$(dirname "${BASH_SOURCE}")/..
+
+declare -A test_cases_inference_pool
+
+# InferencePool Helm Chart test cases
+test_cases_inference_pool["basic"]="--set inferencePool.modelServers.matchLabels.app=llm-instance-gateway"
+test_cases_inference_pool["gke-provider"]="--set provider.name=gke --set inferencePool.modelServers.matchLabels.app=llm-instance-gateway"
+test_cases_inference_pool["multiple-replicas"]="--set inferencePool.replicas=3 --set inferencePool.modelServers.matchLabels.app=llm-instance-gateway"
+
+# Run the install command in case this script runs from a different bash
+# source (such as in the verify-all script)
+make helm-install
+
+# Running tests cases
+echo "Running helm template command for inferencePool chart..."
+# Loop through the keys of the associative array
+for key in "${!test_cases_inference_pool[@]}"; do
+  echo "Running test: $key"
+  ${SCRIPT_ROOT}/bin/helm template ${SCRIPT_ROOT}/config/charts/inferencepool ${test_cases_inference_pool[$key]} --output-dir="${SCRIPT_ROOT}/bin"
+  if [ $? -ne 0 ]; then
+    echo "Helm template command failed for test: $key"
+    exit 1
+  fi
+done
+
+
+
diff --git a/hack/verify-manifests.sh b/hack/verify-manifests.sh
index dff4170..d74c277 100755
--- a/hack/verify-manifests.sh
+++ b/hack/verify-manifests.sh
@@ -45,6 +45,8 @@ main() {
   fetch_crds "https://raw.githubusercontent.com/GoogleCloudPlatform/gke-gateway-api/refs/tags/${GKE_GATEWAY_API_VERSION}/config/crd/networking.gke.io_healthcheckpolicies.yaml"
   fetch_crds "https://raw.githubusercontent.com/istio/istio/refs/tags/${ISTIO_VERSION}/manifests/charts/base/files/crd-all.gen.yaml"
 
+  # Run the install command in case this script runs from a different bash
+  # source (such as in the verify-all script)
   make kubectl-validate
 
   ${SCRIPT_ROOT}/bin/kubectl-validate "${TEMP_DIR}"
diff --git a/mkdocs.yml b/mkdocs.yml
index c90b1bb..b746f74 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -88,6 +88,6 @@ nav:
       - InferencePool: api-types/inferencepool.md
       - InferenceObjective: api-types/inferenceobjective.md
   - Enhancements:
-    - Overview: gieps/overview.md
+    - Overview: enhancements/overview.md
   - Contributing:
     - How to Get Involved: contributing/index.md
diff --git a/pkg/epp/scheduling/framework/plugins/multi/prefix/plugin.go b/pkg/epp/scheduling/framework/plugins/multi/prefix/plugin.go
index 6bc81a4..2870288 100644
--- a/pkg/epp/scheduling/framework/plugins/multi/prefix/plugin.go
+++ b/pkg/epp/scheduling/framework/plugins/multi/prefix/plugin.go
@@ -257,7 +257,7 @@ func (p *Plugin) matchLongestPrefix(ctx context.Context, hashes []BlockHash) map
 }
 
 // hashPrompt divides the prompt into blocks and calculate the prefix cache for each block.
-// hash(0) is the hash of the model name, since different models generally don't share prefix cache.
+// hash[0] is calculated including the model name and cache_salt(if provided), since different models generally don't share prefix cache.
 // For block i, hash(i) = hash(block i content, hash(i-1)).
 func hashPrompt(ctx context.Context, request *types.LLMRequest, cacheBlockSize int, maxPrefixBlocks int) []BlockHash {
 	loggerDebug := log.FromContext(ctx).V(logutil.DEBUG)
@@ -286,6 +286,10 @@ func hashPrompt(ctx context.Context, request *types.LLMRequest, cacheBlockSize i
 	// Add the model to the first block hash so that different models have different hashes even with the same body.
 	h := xxhash.New()
 	_, _ = h.Write([]byte(request.TargetModel))
+	if cacheSalt := request.Body.CacheSalt(); cacheSalt != "" {
+		_, _ = h.Write([]byte(cacheSalt))
+	}
+
 	prevBlockHash := BlockHash(h.Sum64())
 	for i := 0; i+cacheBlockSize <= len(userInput); i += cacheBlockSize {
 		h.Reset()
diff --git a/pkg/epp/scheduling/types/types.go b/pkg/epp/scheduling/types/types.go
index 2685a22..c93f0c5 100644
--- a/pkg/epp/scheduling/types/types.go
+++ b/pkg/epp/scheduling/types/types.go
@@ -56,6 +56,18 @@ type LLMRequestBody struct {
 	ChatCompletions *ChatCompletionsRequest `json:"chat_completions,omitempty"`
 }
 
+func (r *LLMRequestBody) CacheSalt() string {
+	if r.ChatCompletions == nil && r.Completions == nil {
+		return ""
+	}
+
+	if r.ChatCompletions != nil {
+		return r.ChatCompletions.CacheSalt
+	}
+
+	return r.Completions.CacheSalt
+}
+
 // CompletionsRequest is a structured representation of the fields we parse out of the
 // /v1/completions request body.
 // This struct includes fields usable for plugins and scheduling decisions - and not the entire
@@ -63,6 +75,8 @@ type LLMRequestBody struct {
 type CompletionsRequest struct {
 	// Prompt is the prompt that was sent in the request body.
 	Prompt string `json:"prompt,omitempty"`
+	// CacheSalt is an optional request parameter to isolate prefix caches for security reasons.
+	CacheSalt string `json:"cache_salt,omitempty"`
 }
 
 func (r *CompletionsRequest) String() string {
@@ -88,6 +102,8 @@ type ChatCompletionsRequest struct {
 	ContinueFinalMessage      bool                   `json:"continue_final_message,omitempty"`
 	AddGenerationPrompt       bool                   `json:"add_generation_prompt,omitempty"`
 	ChatTemplateKWArgs        map[string]interface{} `json:"chat_template_kwargs,omitempty"`
+	// CacheSalt is an optional request parameter to isolate prefix caches for security reasons.
+	CacheSalt string `json:"cache_salt,omitempty"`
 }
 
 func (r *ChatCompletionsRequest) String() string {
diff --git a/site-src/enhancements/overview.md b/site-src/enhancements/overview.md
new file mode 100644
index 0000000..ad2355e
--- /dev/null
+++ b/site-src/enhancements/overview.md
@@ -0,0 +1,6 @@
+# Inference Gateway Proposal process
+
+Our current proposal process is intentionally light-weight. If you have a proposal you are interested in sharing, please follow these steps:
+
+1. Cut an issue or bring a topic to the weekly meeting!
+2. Assuming positive signal, or if more context is needed please add a proposal, following the style and naming conventions shown here: https://github.com/kubernetes-sigs/gateway-api-inference-extension/tree/main/docs/proposals
\ No newline at end of file
diff --git a/site-src/gieps/giep-116/index.md b/site-src/gieps/giep-116/index.md
deleted file mode 100644
index 4d678da..0000000
--- a/site-src/gieps/giep-116/index.md
+++ /dev/null
@@ -1,47 +0,0 @@
-# GIEP-116: GIEP template
-
-* Issue: [#0](https://github.com/kubernetes-sigs/gateway-api-inference-extension/issues/116)
-* Status: Provisional|Implementable|Experimental|Standard|Deferred|Rejected|Withdrawn|Replaced
-
-(See status definitions [here](overview.md#status).)
-
-## TLDR
-
-(1-2 sentence summary of the proposal)
-
-## Goals
-
-(Primary goals of this proposal.)
-
-## Non-Goals
-
-(What is out of scope for this proposal.)
-
-## Introduction
-
-(Can link to external doc -- but we should bias towards copying
-the content into the GEP as online documents are easier to lose
--- e.g. owner messes up the permissions, accidental deletion)
-
-## API
-
-(... details, can point to PR with changes)
-
-## Conformance Details
-
-(This section describes the names to be used for the feature or
-features in conformance tests and profiles.
-
-These should be `CamelCase` names that specify the feature as
-precisely as possible, and are particularly important for
-Extended features, since they may be surfaced to users.)
-
-## Alternatives
-
-(List other design alternatives and why we did not go in that
-direction)
-
-## References
-
-(Add any additional document links. Again, we should try to avoid
-too much content not in version control to avoid broken links)
diff --git a/site-src/gieps/giep-116/metadata.yaml b/site-src/gieps/giep-116/metadata.yaml
deleted file mode 100644
index 56d1018..0000000
--- a/site-src/gieps/giep-116/metadata.yaml
+++ /dev/null
@@ -1,32 +0,0 @@
-apiVersion: internal.gateway.networking.k8s.io/v1alpha1
-kind: GIEPDetails
-number: 116
-name: GIEP template
-status: Completed
-# Any authors who contribute to the GEP in any way should be listed here using
-# their Github handle.
-authors:
-  - robscott
-relationships:
-  # obsoletes indicates that a GEP makes the linked GEP obsolete, and completely
-  # replaces that GEP. The obsoleted GEP MUST have its obsoletedBy field
-  # set back to this GEP, and MUST be moved to Declined.
-  obsoletes: {}
-  obsoletedBy: {}
-  # extends indicates that a GEP extends the linkned GEP, adding more detail
-  # or additional implementation. The extended GEP MUST have its extendedBy
-  # field set back to this GEP.
-  extends: {}
-  extendedBy: {}
-  # seeAlso indicates other GEPs that are relevant in some way without being
-  # covered by an existing relationship.
-  seeAlso: {}
-# references is a list of hyperlinks to relevant external references.
-# It's intended to be used for storing Github discussions, Google docs, etc.
-references: {}
-# featureNames is a list of the feature names introduced by the GEP, if there
-# are any. This will allow us to track which feature was introduced by which GEP.
-featureNames: {}
-# changelog is a list of hyperlinks to PRs that make changes to the GEP, in
-# ascending date order.
-changelog: {}
diff --git a/site-src/gieps/overview.md b/site-src/gieps/overview.md
deleted file mode 100644
index df28fae..0000000
--- a/site-src/gieps/overview.md
+++ /dev/null
@@ -1,272 +0,0 @@
-# Gateway Inference Enhancement Proposal (GIEP)
-
-Gateway Inference Enhancement Proposals (GIEPs) serve a similar purpose to the
-[GEP](https://gateway-api.sigs.k8s.io/GEPs/overview/) process for the main
-Gateway API project:
-
-1. Ensure that changes to the API follow a known process and discussion in the
-  OSS community.
-1. Make changes and proposals discoverable (current and future).
-1. Document design ideas, tradeoffs, decisions that were made for historical
-  reference.
-1. Record the results of larger community discussions.
-1. Record changes to the GIEP process itself.
-
-## Process
-
-This diagram shows the state diagram of the GIEP process at a high level, but
-the details are below.
-
-<div align="center">
-
-```mermaid
-flowchart TD
-    D([Discuss with<br />the community]) --> C
-    C([Issue Created]) -------> Memorandum
-    C([Issue Created]) --> Provisional
-    Provisional -->|If practical <br /> work needed| Prototyping
-    Provisional -->|GIEP Doc PR<br />done| Implementable
-    Prototyping -->|GIEP Doc PR<br />done| Implementable
-    Implementable -->|Gateway API<br />work completed| Experimental
-    Experimental -->|Supported in<br />multiple implementations<br />+ Conformance tests| Standard
-    Standard -->|Entire change is GA or implemented| Completed
-```
-
-</div>
-
-## GIEP Definitions
-
-### GIEP States
-
-Each GIEP has a state, which tracks where it is in the GIEP process.
-
-GIEPs can move to some states from any other state:
-
-  * **Declined**: The GIEP has been declined and further work will not occur.
-  * **Deferred:** We do not currently have bandwidth to handle this GIEP, it may
-    be revisited in the future.
-  * **Declined:** This proposal was considered by the community but ultimately
-  rejected.
-  * **Withdrawn:** This proposal was considered by the community but ultimately
-  withdrawn by the author.
-
-There is a special state to cover Memorandum GIEPs:
-
-  * **Memorandum**: These GIEPs either:
-    * Document an agreement for further work, creating no spec changes
-      themselves, or
-    * Update the GIEP process.
-
-API GIEPs flow through a number of states, which generally correspond to the
-level of stability of the change described in the GIEP:
-
-  * **Provisional:** The goals described by this GIEP have consensus but
-    implementation details have not been agreed to yet.
-  * **Prototyping:** An extension of `Provisional` which can be opted in to in
-    order to indicate to the community that there are some active practical
-    tests and experiments going on which are intended to be a part of the
-    development of this GIEP. This may include APIs or code, but that content
-    _must_ not be distributed with releases.
-  * **Implementable:** The goals and implementation details described by this
-    GIEP have consensus but have not been fully implemented yet.
-  * **Experimental:** This GIEP has been implemented and is part of the
-    "Experimental" release channel. Breaking changes are still possible, up to
-    and including complete removal and moving to `Rejected`.
-  * **Standard:** This GIEP has been implemented and is part of the "Standard"
-    release channel. It should be quite stable.
-  * **Completed**: All implementation work on this API GIEP has been completed.
-
-### Relationships between GIEPs
-
-GIEPs can have relationships between them. At this time, there are three
-possible relationships:
-
-* **Obsoletes** and its backreference **ObsoletedBy**: when a GIEP is made
-  obsolete by another GIEP, and has its functionality completely replaced. The
-  Obsoleted GIEP is moved to the **Declined** state.
-* **Extends** and its backreference **ExtendedBy**: when a GIEP has additional
-  details or implementation added in another GIEP.
-* **SeeAlso**: when a GIEP is relevant to another GIEP, but is not affected in
-  any other defined way.
-
-Relationships are tracked in the YAML metadata files accompanying each GIEP.
-
-### GIEP metadata file
-
-Each GIEP has a YAML file containing metadata alongside it, please keep it up to
-date as changes to the GIEP occur.
-
-In particular, note the `authors`, and `changelog` fields, please keep those up
-to date.
-
-## Process
-
-### 1. Discuss with the community
-
-Before creating a GIEP, share your high level idea with the community. There are
-several places this may be done:
-
-- A [new GitHub
-  Discussion](https://github.com/kubernetes-sigs/gateway-api-inference-extension/discussions/new)
-- On our [Slack Channel](https://kubernetes.slack.com/archives/C08E3RZMT2P)
-- On one of our [community
-  meetings](https://gateway-api-inference-extension.sigs.k8s.io/contributing/?h=meetings#meetings)
-
-Please default to GitHub discussions: they work a lot like GitHub issues which
-makes them easy to search.
-
-### 2. Create an Issue
-[Create a GIEP
-issue](https://github.com/kubernetes-sigs/gateway-api-inference-extension/issues/new)
-in the repo describing your change. At this point, you should copy the outcome
-of any other conversations or documents into this document.
-
-### 3. Agree on the Goals
-Although it can be tempting to start writing out all the details of your
-proposal, it's important to first ensure we all agree on the goals.
-
-For API GIEPs, the first version of your GIEP should aim for a "Provisional"
-status and leave out any implementation details, focusing primarily on "Goals"
-and "Non-Goals".
-
-For Memorandum GIEPs, the first version of your GIEP will be the only one, as
-Memorandums have only a single stage - `Accepted`.
-
-### 3. Document Implementation Details
-Now that everyone agrees on the goals, it is time to start writing out your
-proposed implementation details. These implementation details should be very
-thorough, including the proposed API spec, and covering any relevant edge cases.
-Note that it may be helpful to use a shared doc for part of this phase to enable
-faster iteration on potential designs.
-
-It is likely that throughout this process, you will discuss a variety of
-alternatives. Be sure to document all of these in the GIEP, and why we decided
-against them. At this stage, the GIEP should be targeting the "Implementable"
-stage.
-
-### 4. Implement the GIEP as "Experimental"
-
-With the GIEP marked as "Implementable", it is time to actually make those
-proposed changes in our API. In some cases, these changes will be documentation
-only, but in most cases, some API changes will also be required. It is important
-that every new feature of the API is marked as "Experimental" when it is
-introduced. Within the API, we use `<gateway:experimental>` tags to denote
-experimental fields. Within Golang packages (conformance tests, CLIs, e.t.c.) we
-use the `experimental` Golang build tag to denote experimental functionality.
-
-Some other requirements must be met before marking a GIEP `Experimental`:
-
-- the graduation criteria to reach `Standard` MUST be filled out
-- a proposed probationary period (see next section) must be included in the GIEP
-  and approved by maintainers.
-
-Before changes are released they MUST be documented. GIEPs that have not been
-both implemented and documented before a release cut off will be excluded from
-the release.
-
-#### Probationary Period
-
-Any GIEP in the `Experimental` phase is automatically under a "probationary
-period" where it will come up for re-assessment if its graduation criteria are
-not met within a given time period. GIEPs that wish to move into `Experimental`
-status MUST document a proposed period (6 months is the suggested default) that
-MUST be approved by maintainers. Maintainers MAY select an alternative time
-duration for a probationary period if deemed appropriate, and will document
-their reasoning.
-
-> **Rationale**: This probationary period exists to avoid GIEPs getting "stale"
-> and to provide guidance to implementations about how relevant features should
-> be used, given that they are not guaranteed to become supported.
-
-At the end of a probationary period if the GIEP has not been able to resolve its
-graduation criteria it will move to "Rejected" status. In extenuating
-circumstances an extension of that period may be accepted by approval from
-maintainers. GIEPs which are `Rejected` in this way are removed from the
-experimental CRDs and more or less put on hold. GIEPs may be allowed to move
-back into `Experimental` status from `Rejected` for another probationary period
-if a new strategy for achieving their graduation criteria can be established.
-Any such plan to take a GIEP "off the shelf" must be reviewed and accepted by
-the maintainers.
-
-> **Warning**: It is extremely important** that projects which implement
-> `Experimental` features clearly document that these features may be removed in
-> future releases.
-
-### 5. Graduate the GIEP to "Standard"
-
-Once this feature has met the [graduation
-criteria](/concepts/versioning/#graduation-criteria), it is time to graduate it
-to the "Standard" channel of the API. Depending on the feature, this may include
-any of the following:
-
-1. Graduating the resource to beta
-2. Graduating fields to "standard" by removing `<gateway:experimental>` tags
-3. Graduating a concept to "standard" by updating documentation
-
-### 6. Close out the GIEP issue
-
-The GIEP issue should only be closed once the feature has:
-- Moved to the standard channel for distribution (if necessary)
-- Moved to a "v1" `apiVersion` for CRDs
-- been completely implemented and has wide acceptance (for process changes).
-
-In short, the GIEP issue should only be closed when the work is "done" (whatever
-that means for that GIEP).
-
-## Format
-
-GIEPs should match the format of the template found in
-[GIEP-696](/GIEPs/GIEP-696).
-
-## Out of scope
-
-What is out of scope: see [text from KEP][kep-when-to-use]. Examples:
-
-* Bug fixes
-* Small changes (API validation, documentation, fixups). It is always possible
-  that the reviewers will determine a "small" change ends up requiring a GIEP.
-
-## FAQ
-
-#### Why is it named GIEP?
-To avoid potential confusion if people start following the cross references to
-the full GEP or KEP process.
-
-#### Why have a different process than mainline?
-Gateway API has some differences with most upstream KEPs. Notably Gateway API
-intentionally avoids including any implementation with the project, so this
-process is focused entirely on the substance of the API. As this project is
-based on CRDs it also has an entirely separately release process, and has
-developed concepts like "release channels" that do not exist in upstream.
-
-#### Is it ok to discuss using shared docs, scratch docs etc?
-Yes, this can be a helpful intermediate step when iterating on design details.
-It is important that all major feedback, discussions, and alternatives
-considered in that step are represented in the GIEP though. A key goal of GIEPs
-is to show why we made a decision and which alternatives were considered. If
-separate docs are used, it's important that we can still see all relevant
-context and decisions in the final GIEP.
-
-#### When should I mark a GIEP as `Prototyping` as opposed to `Provisional`?
-The `Prototyping` status carries the same base meaning as `Provisional` in that
-consensus is not complete between stakeholders and we're not ready to move
-toward releasing content yet. You should use `Prototyping` to indicate to your
-fellow community members that we're in a state of active practical tests and
-experiments which are intended to help us learn and iterate on the GIEP. These
-can include distributing content, but not under any release channel.
-
-#### Should I implement support for `Experimental` channel features?
-Ultimately one of the main ways to get something into `Standard` is for it to
-mature through the `Experimental` phase, so we really _need_ people to implement
-these features and provide feedback in order to have progress. That said, the
-graduation of a feature past `Experimental` is not a forgone conclusion. Before
-implementing an experimental feature, you should:
-
-* Clearly document that support for the feature is experimental and may
-  disappear in the future.
-* Have a plan in place for how you would handle the removal of this feature from
-  the API.
-
-[kep]: https://github.com/kubernetes/enhancements
-[kep-when-to-use]:
-    https://github.com/kubernetes/enhancements/tree/master/keps#do-i-have-to-use-the-kep-process
diff --git a/site-src/guides/epp-configuration/config-text.md b/site-src/guides/epp-configuration/config-text.md
index 382edba..43a0e6c 100644
--- a/site-src/guides/epp-configuration/config-text.md
+++ b/site-src/guides/epp-configuration/config-text.md
@@ -235,6 +235,15 @@ Picks a random pod from the list of candidates.
   - `maxNumOfEndpoints`: Maximum number of endpoints to pick from the list of candidates. If not
     specified defaults to `1`.
 
+#### **WeightedRandomPicker**
+
+Picks pod(s) from the list of candidates based on weighted random sampling using A-Res algorithm.
+
+- *Type*: weighted-random-picker
+- *Parameters*:
+  - `maxNumOfEndpoints`: Maximum number of endpoints to pick from the list of candidates. If not
+    specified defaults to `1`.
+
 #### **KvCacheScorer**
 
 Scores the candidate pods based on their KV cache utilization.
diff --git a/site-src/guides/index.md b/site-src/guides/index.md
index 15339aa..b0faa49 100644
--- a/site-src/guides/index.md
+++ b/site-src/guides/index.md
@@ -91,10 +91,11 @@ Tooling:
 
       ```bash
       export GATEWAY_PROVIDER=gke
+      export IGW_CHART_VERSION=v1.0.1
       helm install vllm-llama3-8b-instruct \
       --set inferencePool.modelServers.matchLabels.app=vllm-llama3-8b-instruct \
       --set provider.name=$GATEWAY_PROVIDER \
-      --version v1.0.1-rc.1 \
+      --version $IGW_CHART_VERSION \
       oci://registry.k8s.io/gateway-api-inference-extension/charts/inferencepool
       ```
 
@@ -102,10 +103,11 @@ Tooling:
 
       ```bash
       export GATEWAY_PROVIDER=istio
+      export IGW_CHART_VERSION=v1.0.1
       helm install vllm-llama3-8b-instruct \
       --set inferencePool.modelServers.matchLabels.app=vllm-llama3-8b-instruct \
       --set provider.name=$GATEWAY_PROVIDER \
-      --version v1.0.1-rc.1 \
+      --version $IGW_CHART_VERSION \
       oci://registry.k8s.io/gateway-api-inference-extension/charts/inferencepool
       ```
 
@@ -113,10 +115,11 @@ Tooling:
 
       ```bash
       export GATEWAY_PROVIDER=none
+      export IGW_CHART_VERSION=v1.0.1
       helm install vllm-llama3-8b-instruct \
       --set inferencePool.modelServers.matchLabels.app=vllm-llama3-8b-instruct \
       --set provider.name=$GATEWAY_PROVIDER \
-      --version v1.0.1-rc.1 \
+      --version $IGW_CHART_VERSION \
       oci://registry.k8s.io/gateway-api-inference-extension/charts/inferencepool
       ```
 
@@ -124,10 +127,11 @@ Tooling:
 
       ```bash
       export GATEWAY_PROVIDER=none
+      export IGW_CHART_VERSION=v1.0.1
       helm install vllm-llama3-8b-instruct \
       --set inferencePool.modelServers.matchLabels.app=vllm-llama3-8b-instruct \
       --set provider.name=$GATEWAY_PROVIDER \
-      --version v1.0.1-rc.1 \
+      --version $IGW_CHART_VERSION \
       oci://registry.k8s.io/gateway-api-inference-extension/charts/inferencepool
       ```
 
@@ -192,13 +196,7 @@ Tooling:
          ./istioctl install --set tag=$TAG --set hub=gcr.io/istio-testing --set values.pilot.env.ENABLE_GATEWAY_API_INFERENCE_EXTENSION=true
          ```
 
-      3. If you run the Endpoint Picker (EPP) with the `--secure-serving` flag set to `true` (the default mode), it is currently using a self-signed certificate. As a security measure, Istio does not trust self-signed certificates by default. As a temporary workaround, you can apply the destination rule to bypass TLS verification for EPP. A more secure TLS implementation in EPP is being discussed in [Issue 582](https://github.com/kubernetes-sigs/gateway-api-inference-extension/issues/582).
-
-         ```bash
-         kubectl apply -f https://github.com/kubernetes-sigs/gateway-api-inference-extension/raw/main/config/manifests/gateway/istio/destination-rule.yaml
-         ```
-
-      4. Deploy Gateway
+      3. Deploy Gateway
 
          ```bash
          kubectl apply -f https://github.com/kubernetes-sigs/gateway-api-inference-extension/raw/main/config/manifests/gateway/istio/gateway.yaml
@@ -211,13 +209,13 @@ Tooling:
          inference-gateway   inference-gateway   <MY_ADDRESS>    True         22s
          ```
 
-      6. Deploy the HTTPRoute
+      4. Deploy the HTTPRoute
 
          ```bash
          kubectl apply -f https://github.com/kubernetes-sigs/gateway-api-inference-extension/raw/main/config/manifests/gateway/istio/httproute.yaml
          ```
 
-      7. Confirm that the HTTPRoute status conditions include `Accepted=True` and `ResolvedRefs=True`:
+      5. Confirm that the HTTPRoute status conditions include `Accepted=True` and `ResolvedRefs=True`:
 
          ```bash
          kubectl get httproute llm-route -o yaml
diff --git a/site-src/guides/serve-multiple-genai-models.md b/site-src/guides/serve-multiple-genai-models.md
index 2064f99..25198e8 100644
--- a/site-src/guides/serve-multiple-genai-models.md
+++ b/site-src/guides/serve-multiple-genai-models.md
@@ -180,9 +180,18 @@ kubectl get httproute llm-phi4-route -o yaml
           -H "Content-Type: application/json" \
           -d '{
                 "model": "meta-llama/Llama-3.1-8B-Instruct",
-                "prompt": "Linux is said to be an open source kernel because ",
                 "max_tokens": 100,
-                "temperature": 0
+                "temperature": 0,
+                "messages": [
+                  {
+                    "role": "developer",
+                    "content": "You are a helpful assistant."
+                  },
+                  {
+                    "role": "user",
+                    "content": "Linux is said to be an open source kernel because "
+                  }
+                ]
           }'
         ```
 
@@ -192,9 +201,18 @@ kubectl get httproute llm-phi4-route -o yaml
           -H "Content-Type: application/json" \
           -d '{
                 "model": "microsoft/Phi-4-mini-instruct",
-                "prompt": "2+2 is ",
-                "max_tokens": 20,
-                "temperature": 0
+                "max_tokens": 100,
+                "temperature": 0,
+                "messages": [
+                  {
+                    "role": "developer",
+                    "content": "You are a helpful assistant."
+                  },
+                  {
+                    "role": "user",
+                    "content": "2+2 is "
+                  }
+                ]
           }'
         ```
 



Test patch:
diff --git a/pkg/epp/util/request/body_test.go b/pkg/epp/util/request/body_test.go
index 64ab6de..51389e5 100644
--- a/pkg/epp/util/request/body_test.go
+++ b/pkg/epp/util/request/body_test.go
@@ -225,6 +225,44 @@ func TestExtractRequestData(t *testing.T) {
 			},
 			wantErr: true,
 		},
+		{
+			name: "completions request with cache_salt",
+			body: map[string]any{
+				"model":      "test",
+				"prompt":     "test prompt",
+				"cache_salt": "Z3V2bmV3aGxza3ZubGFoZ3Zud3V3ZWZ2bmd0b3V2bnZmc2xpZ3RoZ2x2aQ==",
+			},
+			want: &types.LLMRequestBody{
+				Completions: &types.CompletionsRequest{
+					Prompt:    "test prompt",
+					CacheSalt: "Z3V2bmV3aGxza3ZubGFoZ3Zud3V3ZWZ2bmd0b3V2bnZmc2xpZ3RoZ2x2aQ==",
+				},
+			},
+		},
+		{
+			name: "chat completions request with cache_salt",
+			body: map[string]any{
+				"model": "test",
+				"messages": []any{
+					map[string]any{
+						"role": "system", "content": "this is a system message",
+					},
+					map[string]any{
+						"role": "user", "content": "hello",
+					},
+				},
+				"cache_salt": "Z3V2bmV3aGxza3ZubGFoZ3Zud3V3ZWZ2bmd0b3V2bnZmc2xpZ3RoZ2x2aQ==",
+			},
+			want: &types.LLMRequestBody{
+				ChatCompletions: &types.ChatCompletionsRequest{
+					Messages: []types.Message{
+						{Role: "system", Content: "this is a system message"},
+						{Role: "user", Content: "hello"},
+					},
+					CacheSalt: "Z3V2bmV3aGxza3ZubGFoZ3Zud3V3ZWZ2bmd0b3V2bnZmc2xpZ3RoZ2x2aQ==",
+				},
+			},
+		},
 	}
 
 	for _, tt := range tests {
diff --git a/test/cel/inferencepool_test.go b/test/cel/inferencepool_test.go
index 8b3ba3e..deccb6e 100644
--- a/test/cel/inferencepool_test.go
+++ b/test/cel/inferencepool_test.go
@@ -80,6 +80,20 @@ func TestValidateInferencePool(t *testing.T) {
 			},
 			wantErrors: []string{"port is required when kind is 'Service' or unspecified (defaults to 'Service')"},
 		},
+		{
+			desc: "passes validation with multiple unique port numbers",
+			mutate: func(ip *v1.InferencePool) {
+				ip.Spec.TargetPorts = []v1.Port{{Number: 8000}, {Number: 80}, {Number: 8081}, {Number: 443}}
+			},
+			wantErrors: nil,
+		},
+		{
+			desc: "fails validation with port numbers containing duplicates",
+			mutate: func(ip *v1.InferencePool) {
+				ip.Spec.TargetPorts = []v1.Port{{Number: 8000}, {Number: 80}, {Number: 8000}, {Number: 443}}
+			},
+			wantErrors: []string{"port number must be unique"},
+		},
 	}
 
 	for _, tc := range testCases {