1"use strict";(self.webpackChunksemantic_router_docs=self.webpackChunksemantic_router_docs||[]).push([[22414],{60832(e,n,i){i.r(n),i.d(n,{assets:()=>o,contentTitle:()=>l,default:()=>h,frontMatter:()=>a,metadata:()=>r,toc:()=>c});const r=JSON.parse('{"id":"installation/k8s/operator","title":"Install with Operator","description":"The Semantic Router Operator provides a Kubernetes-native way to deploy and manage vLLM Semantic Router instances using Custom Resource Definitions (CRDs). It simplifies deployment, configuration, and lifecycle management across Kubernetes and OpenShift platforms.","source":"@site/versioned_docs/version-v0.3/installation/k8s/operator.md","sourceDirName":"installation/k8s","slug":"/installation/k8s/operator","permalink":"/docs/v0.3/installation/k8s/operator","draft":false,"unlisted":false,"editUrl":"https://github.com/vllm-project/semantic-router/edit/main/website/docs/installation/k8s/operator.md","tags":[],"version":"v0.3","sidebarPosition":3,"frontMatter":{"sidebar_position":3,"sidebar_label":"Install with Operator"},"sidebar":"tutorialSidebar","previous":{"title":"Quickstart","permalink":"/docs/v0.3/installation/"},"next":{"title":"Configuration","permalink":"/docs/v0.3/installation/configuration"}}');var s=i(74848),t=i(28453);const a={sidebar_position:3,sidebar_label:"Install with Operator"},l="Install with Operator",o={},c=[{value:"Features",id:"features",level:2},{value:"Prerequisites",id:"prerequisites",level:2},{value:"Installation",id:"installation",level:2},{value:"Option 1: Using Kustomize (Standard Kubernetes)",id:"option-1-using-kustomize-standard-kubernetes",level:3},{value:"Option 2: Using OLM (OpenShift)",id:"option-2-using-olm-openshift",level:3},{value:"Deploy Your First Router",id:"deploy-your-first-router",level:2},{value:"Quick Start with Sample Configurations",id:"quick-start-with-sample-configurations",level:3},{value:"Custom Configuration",id:"custom-configuration",level:3},{value:"Advanced Features",id:"advanced-features",level:2},{value:"Embedding Models Configuration",id:"embedding-models-configuration",level:3},{value:"Available Embedding Models",id:"available-embedding-models",level:4},{value:"Example: mmBERT with Layer Early Exit",id:"example-mmbert-with-layer-early-exit",level:4},{value:"Example: Qwen3 with Redis Cache",id:"example-qwen3-with-redis-cache",level:4},{value:"Complexity-Aware Routing",id:"complexity-aware-routing",level:3},{value:"Example Configuration",id:"example-configuration",level:4},{value:"Verify Deployment",id:"verify-deployment",level:2},{value:"Backend Discovery Types",id:"backend-discovery-types",level:2},{value:"KServe InferenceService Discovery",id:"kserve-inferenceservice-discovery",level:3},{value:"Llama Stack Service Discovery",id:"llama-stack-service-discovery",level:3},{value:"Direct Kubernetes Service",id:"direct-kubernetes-service",level:3},{value:"Multiple Backends",id:"multiple-backends",level:3},{value:"Deployment Modes",id:"deployment-modes",level:2},{value:"Standalone Mode (Default)",id:"standalone-mode-default",level:3},{value:"Gateway Integration Mode",id:"gateway-integration-mode",level:3},{value:"OpenShift Routes",id:"openshift-routes",level:2},{value:"Basic OpenShift Route",id:"basic-openshift-route",level:3},{value:"TLS Termination Options",id:"tls-termination-options",level:3},{value:"When to Use OpenShift Routes",id:"when-to-use-openshift-routes",level:3},{value:"Status Information",id:"status-information",level:3},{value:"Choosing Your Configuration",id:"choosing-your-configuration",level:2},{value:"Architecture",id:"architecture",level:2},{value:"Platform Detection and Security",id:"platform-detection-and-security",level:2},{value:"OpenShift Platform",id:"openshift-platform",level:3},{value:"Standard Kubernetes",id:"standard-kubernetes",level:3},{value:"Both Platforms",id:"both-platforms",level:3},{value:"Override Security Context",id:"override-security-context",level:3},{value:"Configuration Reference",id:"configuration-reference",level:2},{value:"Image Configuration",id:"image-configuration",level:3},{value:"Service Configuration",id:"service-configuration",level:3},{value:"Persistence Configuration",id:"persistence-configuration",level:3},{value:"Semantic Cache Backends",id:"semantic-cache-backends",level:3},{value:"Supported Backends",id:"supported-backends",level:4},{value:"1. Memory Cache (Default)",id:"1-memory-cache-default",level:5},{value:"2. Redis Cache",id:"2-redis-cache",level:5},{value:"3. Milvus Cache",id:"3-milvus-cache",level:5},{value:"4. Hybrid Cache",id:"4-hybrid-cache",level:5},{value:"Deploying Redis",id:"deploying-redis",level:4},{value:"Deploying Milvus",id:"deploying-milvus",level:4},{value:"Embedding Models",id:"embedding-models",level:4},{value:"Migration Between Backends",id:"migration-between-backends",level:4},{value:"Cache Configuration Reference",id:"cache-configuration-reference",level:4},{value:"Semantic Router Configuration",id:"semantic-router-configuration",level:3},{value:"Tools Database",id:"tools-database",level:3},{value:"Autoscaling (HPA)",id:"autoscaling-hpa",level:3},{value:"Ingress Configuration",id:"ingress-configuration",level:3},{value:"Production Deployment",id:"production-deployment",level:2},{value:"High Availability Setup",id:"high-availability-setup",level:3},{value:"Pod Disruption Budget",id:"pod-disruption-budget",level:3},{value:"Resource Allocation Guidelines",id:"resource-allocation-guidelines",level:3},{value:"Monitoring and Observability",id:"monitoring-and-observability",level:2},{value:"Metrics",id:"metrics",level:3},{value:"ServiceMonitor (Prometheus Operator)",id:"servicemonitor-prometheus-operator",level:3},{value:"Distributed Tracing",id:"distributed-tracing",level:3},{value:"Troubleshooting",id:"troubleshooting",level:2},{value:"Common Issues",id:"common-issues",level:3},{value:"Backend Discovery Failures",id:"backend-discovery-failures",level:4},{value:"Gateway Integration Issues",id:"gateway-integration-issues",level:4},{value:"OpenShift Route Issues",id:"openshift-route-issues",level:4},{value:"Pod stuck in <code>ImagePullBackOff</code>
1",id:"pod-stuck-in-imagepullbackoff",level:4},{value:"PVC stuck in <code>Pending</code>",id:"pvc-stuck-in-pending",level:4},{value:"Models not downloading",id:"models-not-downloading",level:4},{value:"Operator not detecting platform correctly",id:"operator-not-detecting-platform-correctly",level:4},{value:"Development",id:"development",level:2},{value:"Local Development",id:"local-development",level:3},{value:"Testing with kind",id:"testing-with-kind",level:3},{value:"Next Steps",id:"next-steps",level:2}];function d(e){const n={a:"a",admonition:"admonition",code:"code",h1:"h1",h2:"h2",h3:"h3",h4:"h4",h5:"h5",header:"header",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",table:"table",tbody:"tbody",td:"td",th:"th",thead:"thead",tr:"tr",ul:"ul",...(0,t.R)(),...e.components};return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(n.header,{children:(0,s.jsx)(n.h1,{id:"install-with-operator",children:"Install with Operator"})}),"\n",(0,s.jsx)(n.p,{children:"The Semantic Router Operator provides a Kubernetes-native way to deploy and manage vLLM Semantic Router instances using Custom Resource Definitions (CRDs). It simplifies deployment, configuration, and lifecycle management across Kubernetes and OpenShift platforms."}),"\n",(0,s.jsx)(n.h2,{id:"features",children:"Features"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"Declarative deployment"}),": Define semantic router instances using Kubernetes CRDs"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"Automatic configuration"}),": Generates and manages ConfigMaps for semantic router configuration"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"Persistent storage"}),": Manages PVCs for ML model storage with automatic lifecycle"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"Platform detection"}),": Automatically detects and configures for OpenShift or standard Kubernetes"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"Built-in observability"}),": Metrics, tracing, and monitoring support out of the box"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"Production features"}),": HPA, ingress, service mesh integration, and pod disruption budgets"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"Secure by default"}),": Drops all capabilities, prevents privilege escalation"]}),"\n"]}),"\n",(0,s.jsx)(n.h2,{id:"prerequisites",children:"Prerequisites"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:"Kubernetes 1.24+ or OpenShift 4.12+"}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.code,{children:"kubectl"})," or ",(0,s.jsx)(n.code,{children:"oc"})," CLI configured"]}),"\n",(0,s.jsx)(n.li,{children:"Cluster admin access (for CRD installation)"}),"\n"]}),"\n",(0,s.jsx)(n.h2,{id:"installation",children:"Installation"}),"\n",(0,s.jsx)(n.h3,{id:"option-1-using-kustomize-standard-kubernetes",children:"Option 1: Using Kustomize (Standard Kubernetes)"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"# Clone the repository\ngit clone https://github.com/vllm-project/semantic-router\ncd semantic-router/deploy/operator\n\n# Install CRDs\nmake install\n\n# Deploy the operator\nmake deploy IMG=ghcr.io/vllm-project/semantic-router/operator:latest\n"})}),"\n",(0,s.jsx)(n.p,{children:"Verify the operator is running:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"kubectl get pods -n semantic-router-operator-system\n"})}),"\n",(0,s.jsx)(n.h3,{id:"option-2-using-olm-openshift",children:"Option 2: Using OLM (OpenShift)"}),"\n",(0,s.jsx)(n.p,{children:"For OpenShift deployments using Operator Lifecycle Manager:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"cd semantic-router/deploy/operator\n\n# Build and push to your registry (Quay, internal registry, etc.)\npodman login quay.io\nmake podman-build IMG=quay.io/<your-org>/semantic-router-operator:latest\nmake podman-push IMG=quay.io/<your-org>/semantic-router-operator:latest\n\n# Deploy using OLM\nmake openshift-deploy\n"})}),"\n",(0,s.jsx)(n.h2,{id:"deploy-your-first-router",children:"Deploy Your First Router"}),"\n",(0,s.jsx)(n.h3,{id:"quick-start-with-sample-configurations",children:"Qu
1ick Start with Sample Configurations"}),"\n",(0,s.jsx)(n.p,{children:"Choose a pre-configured sample based on your infrastructure:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"# Simple standalone deployment with KServe backend\nkubectl apply -f https://raw.githubusercontent.com/vllm-project/semantic-router/main/deploy/operator/config/samples/vllm.ai_v1alpha1_semanticrouter_simple.yaml\n\n# Full-featured OpenShift deployment with Routes\nkubectl apply -f https://raw.githubusercontent.com/vllm-project/semantic-router/main/deploy/operator/config/samples/vllm.ai_v1alpha1_semanticrouter_openshift.yaml\n\n# Gateway integration mode (Istio/Envoy Gateway)\nkubectl apply -f https://raw.githubusercontent.com/vllm-project/semantic-router/main/deploy/operator/config/samples/vllm.ai_v1alpha1_semanticrouter_gateway.yaml\n\n# Llama Stack backend discovery\nkubectl apply -f https://raw.githubusercontent.com/vllm-project/semantic-router/main/deploy/operator/config/samples/vllm.ai_v1alpha1_semanticrouter_llamastack.yaml\n\n# Redis cache backend for production caching\nkubectl apply -f https://raw.githubusercontent.com/vllm-project/semantic-router/main/deploy/operator/config/samples/vllm.ai_v1alpha1_semanticrouter_redis_cache.yaml\n\n# Milvus cache backend for large-scale deployments\nkubectl apply -f https://raw.githubusercontent.com/vllm-project/semantic-router/main/deploy/operator/config/samples/vllm.ai_v1alpha1_semanticrouter_milvus_cache.yaml\n\n# Hybrid cache backend for optimal performance\nkubectl apply -f https://raw.githubusercontent.com/vllm-project/semantic-router/main/deploy/operator/config/samples/vllm.ai_v1alpha1_semanticrouter_hybrid_cache.yaml\n\n# mmBERT 2D Matryoshka embeddings with layer early exit\nkubectl apply -f https://raw.githubusercontent.com/vllm-project/semantic-router/main/deploy/operator/config/samples/vllm.ai_v1alpha1_semanticrouter_mmbert.yaml\n\n# Complexity-aware routing for intelligent model selection\nkubectl apply -f https://raw.githubusercontent.com/vllm-project/semantic-router/main/deploy/operator/config/samples/vllm.ai_v1alpha1_semanticrouter_complexity.yaml\n"})}),"\n",(0,s.jsx)(n.h3,{id:"custom-configuration",children:"Custom Configuration"}),"\n",(0,s.jsxs)(n.p,{children:["Create a ",(0,s.jsx)(n.code,{children:"my-router.yaml"})," file:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-yaml",children:'apiVersion: vllm.ai/v1alpha1\nkind: SemanticRouter\nmetadata:\n name: my-router\n namespace: default\nspec:\n replicas: 2\n\n image:\n repository: ghcr.io/vllm-project/semantic-router/extproc\n tag: latest\n\n # Configure vLLM backend endpoints\n vllmEndpoints:\n # KServe InferenceService (RHOAI 3.x)\n - name: llama3-8b-endpoint\n model: llama3-8b\n reasoningFamily: qwen3\n loras:\n - name: computer-science-expert\n description: Adapter for advanced computer science prompts\n backend:\n type: kserve\n inferenceServiceName: llama-3-8b\n weight: 1\n\n resources:\n limits:\n memory: "7Gi"\n cpu: "2"\n requests:\n memory: "3Gi"\n cpu: "1"\n\n persistence:\n enabled: true\n size: 10Gi\n storageClassName: "standard"\n\n config:\n providers:\n defaults:\n default_model: llama3-8b\n default_reasoning_effort: medium\n reasoning_families:\n qwen3:\n type: chat_template_kwargs\n parameter: enable_thinking\n models:\n - name: llama3-8b\n provider_model_id: llama3-8b\n backend_refs:\n - name: llama3-8b-endpoint\n endpoint: llama-3-8b-predictor.default.svc.cluster.local:80\n protocol: http\n\n routing:\n modelCards:\n - name: llama3-8b\n modality: text\n capabilities: ["chat", "reasoning"]\n decisions:\n - name: default-route\n description: Catch-all route\n priority: 100\n rules:\n operator: AND\n conditions: []\n modelRefs:\n - model: llama3-8b\n use_reasoning: false\n\n global:\n stores:\n semantic_cache:\n enabled: true\n backend_type: memory\n max_entries: 1000\n ttl_seconds: 3600\n integrations:\n tools:\n enabled: true\n top_k: 3\n similarity_threshold: 0.2\n model_catalog:\n system:\n prompt_guard: models/mmbert32k-jailbreak-detector-merged\n modules:\n prompt_guard:\n enabled: true\n model_ref: prompt_guard\n threshold: 0.7\n\n toolsDb:\n - tool:\n type: "function"\n function:\n name: "get_weather"\n description: "Get weather information for a location"\n parameters:\n type: "object"\n properties:\n location:\n type: "string"\n description: "City and state, e.g. San Francisco, CA"\n required: ["location"]\n description: "Weather information tool"\n category: "weather"\n tags: ["weather", "temperature"]\n'})}),"\n",(0,s.jsx)(n.p,{children:"Apply the configuration:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"kubectl apply -f my-router.yaml\n"})}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.code,{children:"spec.config"})," should use the same canonical ",(0,s.jsx)(n.code,{children:"providers/routing/global"})," layout as local ",(0,s.jsx)(n.code,{children:"config.yaml"}),". ",(0,s.jsx)(n.code,{children:"spec.vllmEndpoints"})," remains the Kubernetes adapter for discovering backends and served-model aliases; the operator translates it into canonical ",(0,s.jsx)(n.code,{children:"providers.models[].backend_refs[]"})," and ",(0,s.jsx)(n.code,{children:"routing.modelCards"})," entries, including optional ",(0,s.jsx)(n.code,{children:"loras"}),", when rendering the runtime config."]}),"\n",(0,s.jsx)(n.h2,{id:"advanced-features",children:"Advanced Features"}),"\n",(0,s.jsx)(n.h3,{id:"embedding-models-configuration",children:"Embedding Models Configuration"}),"\n",(0,s.jsx)(n.p,{children:"The operator supports three high-performance embedding models for semantic understanding and caching. You can configure these models to optimize for your specific use case."}),"\n",(0,s.jsx)(n.h4,{id:"available-embedding-models",children:"Available Embedding Models"}),"\n",(0,s.jsxs)(n.ol,{children:["\n",(0,s.jsxs)(n.li,{children:["\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Qwen3-Embedding"})," (1024 dimensions, 32K context)"]}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:"Best for: High-quality semantic understanding with long context"}),"\n",(0,s.jsx)(n.li,{children:"Use case: Complex queries, research documents, detailed analysis"}),"\n"]}),"\n"]}),"\n",(0,s.jsxs)(n.li,{children:["\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"EmbeddingGemma"})," (768 dimensions, 8K context)"]}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:"Best for: Fast performance with good accuracy"}),"\n",(0,s.jsx)(n.li,{children:"Use case: Real-time applications, high-throughput scenarios"}),"\n"]}),"\n"]}),"\n",(0,s.jsxs)(n.li,{children:["\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"mmBERT 2D Matryoshka"})," (64-768 dimensions, multilingual)"]}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:"Best for: Adaptive performance with layer early exit"}),"\n",(0,s.jsx)(n.li,{children:"Use case: Multilingual deployments, flexible quality/speed trade-offs"}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,s.jsx)(n.h4,{id:"example-mmbert-with-layer-early-exit",children:"Example: mmBERT with Layer Early Exit"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-yaml",children:'spec:\n config:\n global:\n model_catalog:\n embeddings:\n semantic:\n mmbert_model_path: "models/mom-embedding-ultra"\n use_cpu: true\n embedding_config:\n model_type: "mmbert"\n # Layer early exit: balance speed vs accuracy\n # Layer 3: ~7x speedup (fast, good for high-volume queries)\n # Layer 6: ~3.6x speedup (balanced - recommended)\n # Layer 11: ~2x speedup (higher accuracy)\n # Layer 22: full model (maximum accuracy)\n target_layer: 6\n # Dimension reduction for faster similarity search\n # Options: 64, 128, 256, 512, 768\n target_dimension: 256\n preload_embeddings: true\n enable_soft_matching: true\n top_k: 1\n min_score_threshold: "0.5"\n stores:\n semantic_cache:\n enabled: true\n backend_type: "memory"\n embedding_model: "mmbert"\n similarity_threshold: "0.85"\n max_entries: 5000\n ttl_seconds: 7200\n'})}),"\n",(0,s.jsxs)(n.p,{children:["See ",(0,s.jsx)(n.a,{href:"https://raw.githubusercontent.com/vllm-project/semantic-router/main/deploy/operator/config/samples/vllm.ai_v1alpha1_semanticrouter_mmbert.yaml",children:"mmbert sample configuration"})," for a complete example."]}
1),"\n",(0,s.jsx)(n.h4,{id:"example-qwen3-with-redis-cache",children:"Example: Qwen3 with Redis Cache"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-yaml",children:'spec:\n config:\n global:\n model_catalog:\n embeddings:\n semantic:\n qwen3_model_path: "models/qwen3-embedding"\n use_cpu: true\n stores:\n semantic_cache:\n enabled: true\n backend_type: "redis"\n embedding_model: "qwen3"\n redis:\n connection:\n host: redis.cache-backends.svc.cluster.local\n port: 6379\n index:\n vector_field:\n dimension: 1024 # Qwen3 dimension\n'})}),"\n",(0,s.jsxs)(n.p,{children:["See ",(0,s.jsx)(n.a,{href:"https://raw.githubusercontent.com/vllm-project/semantic-router/main/deploy/operator/config/samples/vllm.ai_v1alpha1_semanticrouter_redis_cache.yaml",children:"redis cache sample configuration"})," for a complete example."]}),"\n",(0,s.jsx)(n.h3,{id:"complexity-aware-routing",children:"Complexity-Aware Routing"}),"\n",(0,s.jsx)(n.p,{children:"Route queries to different models based on complexity classification. Simple queries go to fast models, complex queries go to powerful models."}),"\n",(0,s.jsx)(n.h4,{id:"example-configuration",children:"Example Configuration"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-yaml",children:'spec:\n # Configure multiple backends with different capabilities\n vllmEndpoints:\n - name: llama-8b-fast\n model: llama3-8b\n reasoningFamily: qwen3\n backend:\n type: kserve\n inferenceServiceName: llama-3-8b\n weight: 2 # Prefer for simple queries\n\n - name: llama-70b-reasoning\n model: llama3-70b\n reasoningFamily: deepseek\n backend:\n type: kserve\n inferenceServiceName: llama-3-70b\n weight: 1 # Use for complex queries\n\n config:\n # Define complexity rules\n complexity_rules:\n # Rule 1: Code complexity\n - name: "code-complexity"\n description: "Classify coding tasks by complexity"\n threshold: "0.3" # Lower threshold works better for embedding-based similarity\n\n # Examples of complex coding tasks\n hard:\n candidates:\n - "Implement a distributed lock manager with leader election"\n - "Design a database migration system with rollback support"\n - "Create a compiler optimization pass for loop unrolling"\n\n # Examples of simple coding tasks\n easy:\n candidates:\n - "Write a function to reverse a string"\n - "Create a class to represent a rectangle"\n - "Implement a simple counter with increment/decrement"\n\n # Rule 2: Reasoning complexity\n - name: "reasoning-complexity"\n description: "Classify reasoning and problem-solving tasks"\n threshold: "0.3" # Lower threshold works better for embedding-based similarity\n\n hard:\n candidates:\n - "Analyze the geopolitical impl
1ications of renewable energy adoption"\n - "Evaluate the ethical considerations of AI in healthcare"\n - "Design a multi-stage marketing strategy for a new product launch"\n\n easy:\n candidates:\n - "What is the capital of France?"\n - "How many days are in a week?"\n - "Name three common pets"\n\n # Rule 3: Domain-specific complexity with conditional application\n - name: "medical-complexity"\n description: "Classify medical queries (only for medical domain)"\n threshold: "0.3" # Lower threshold works better for embedding-based similarity\n\n hard:\n candidates:\n - "Differential diagnosis for chest pain with dyspnea"\n - "Treatment protocol for multi-drug resistant tuberculosis"\n\n easy:\n candidates:\n - "What is the normal body temperature?"\n - "What are common symptoms of a cold?"\n\n # Only apply this rule if domain signal indicates medical domain\n composer:\n operator: "AND"\n conditions:\n - type: "domain"\n name: "medical"\n'})}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.strong,{children:"How it works:"})}),"\n",(0,s.jsxs)(n.ol,{children:["\n",(0,s.jsxs)(n.li,{children:["Incoming query is compared against ",(0,s.jsx)(n.code,{children:"hard"})," and ",(0,s.jsx)(n.code,{children:"easy"})," candidate examples"]}),"\n",(0,s.jsx)(n.li,{children:"Similarity scores determine complexity classification"}),"\n",(0,s.jsxs)(n.li,{children:["Output signals: ",(0,s.jsx)(n.code,{children:"{rule-name}:hard"}),", ",(0,s.jsx)(n.code,{children:"{rule-name}:easy"}),", or ",(0,s.jsx)(n.code,{children:"{rule-name}:medium"})]}),"\n",(0,s.jsx)(n.li,{children:"Router uses signals to select appropriate backend model"}),"\n",(0,s.jsx)(n.li,{children:"Composer allows conditional rule application based on other signals"}),"\n"]}),"\n",(0,s.jsxs)(n.p,{children:["See ",(0,s.jsx)(n.a,{href:"https://raw.githubusercontent.com/vllm-project/semantic-router/main/deploy/operator/config/samples/vllm.ai_v1alpha1_semanticrouter_complexity.yaml",children:"complexity routing sample configuration"})," for a complete example."]}),"\n",(0,s.jsx)(n.h2,{id:"verify-deployment",children:"Verify Deployment"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"# Check the SemanticRouter resource\nkubectl get semanticrouter my-router\n\n# Check created resources\nkubectl get deployment,service,configmap -l app.kubernetes.io/instance=my-router\n\n# View status\nkubectl describe semanticrouter my-router\n\n# View logs\nkubectl logs -f deployment/my-router\n"})}),"\n",(0,s.jsx)(n.p,{children:"Expected output:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{children:"NAME PHASE REPLICAS READY AGE\nsemanticrouter.vllm.ai/my-router Running 2 2 5m\n"})}),"\n",(0,s.jsx)(n.h2,{id:"backend-discovery-types",children:"Backend Discovery Types"}),"\n",(0,s.jsx)(n.p,{children:"The operator supports three types of backend discovery for connecting semantic router to vLLM model servers. Choose the type that matches your infrastructure."}),"\n",(0,s.jsx)(n.h3,{id:"kserve-inferenceservice-discovery",children:"KServe InferenceService Discovery"}),"\n",(0,s.jsx)(n.p,{children:"For RHOAI 3.x or standalone KServe deployments. The operator automatically discovers the predictor service created by KServe."}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-yaml",children:"spec:\n vllmEndpoints:\n - name: llama3-8b-endpoint\n model: llama3-8b\n reasoningFamily: qwen3\n backend:\n type: kserve\n inferenceServiceName: llama-3-8b # InferenceService in same namespace\n weight: 1\n"})}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.strong,{children:"When to use:"})}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:"Running on Red Hat OpenShift AI (RHOAI) 3.x"}),"\n",(0,s.jsx)(n.li,{children:"Using KServe for model serving"}),"\n",(0,s.jsx)(n.li,{children:"Want automatic service discovery"}),"\n"]}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.strong,{children:"How it works:"})}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:["Discovers the predictor service: ",(0,s.jsx)(n.code,{children:"{inferenceServiceName}-predictor"})]}),"\n",(0,s.jsx)(n.li,{children:"Uses port 8443 (KServe default HTTPS port)"}),"\n",(0,s.jsx)(n.li,{children:"Works in the same namespace as SemanticRouter"}),"\n"]}),"\n",(0,s.jsx)(n.h3,{id:"llama-stack-service-discovery",children:"Llama Stack Service Discovery"}),"\n",(0,s.jsx)(n.p,{children:"Discovers Llama Stack deployments using Kubernetes label selectors."}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-yaml",children:"spec:\n vllmEndpoints:\n - name: llama-405b-endpoint\n model: llama-3.3-70b-instruct\n reasoningFamily: gpt\n backend:\n type: llamastack\n discoveryLabels:\n app: llama-stack\n model: llama-3.3-70b\n weight: 1\n"})}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.strong,{children:"When to use:"})}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:"Using Meta's Llama Stack for model serving"}),"\n",(0,s.jsx)(n.li,{children:"Multiple Llama Stack services with different models"}),"\n",(0,s.jsx)(n.li,{children:"Want label-based service discovery"}),"\n"]}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.strong,{children:"How it works:"})}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:"Lists services matching the label selector"}),"\n",(0,s.jsx)(n.li,{children:"Uses first matching service if multiple found"}),"\n",(0,s.jsx)(n.li,{children:"Extracts port from service definition"}),"\n"]}),"\n",(0,s.jsx)(n.h3,{id:"direct-kubernetes-service",children:"Direct Kubernetes Service"}),"\n",(0,s.jsx)(n.p,{children:"Direct connection to any Kubernetes service (vLLM, TGI, etc.)."}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-yaml",children:"spec:\n vllmEndpoints:\n - name: custom-vllm-endpoint\n model: deepseek-r1-distill-qwen-7b\n reasoningFamily: deepseek\n backend:\n type: service\n service:\n name: vllm-deepseek\n namespace: vllm-serving # Can reference service in another namespace\n port: 8000\n weight: 1\n"})}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.strong,{children:"When to use:"})}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:"Direct vLLM deployments"}),"\n",(0,s.jsx)(n.li,{children:"Custom model servers with OpenAI-compatible API"}),"\n",(0,s.jsx)(n.li,{children:"Cross-namespace service references"}),"\n",(0,s.jsx)(n.li,{children:"Maximum control over service endpoints"}),"\n"]}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.strong,{children:"How it works:"})}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:"Connects to specified service directly"}),"\n",(0,s.jsx)(n.li,{children:"No discovery - uses explicit configuration"}),"\n",(0,s.jsx)(n.li,{children:"Supports cross-namespace references"}),"\n"]}),"\n",(0,s.jsx)(n.h3,{id:"multiple-backends",children:"Multiple Backends"}),"\n",(0,s.jsx)(n.p,{children:"You can configure multiple backends with load balancing weights:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-yaml",children:"spec:\n vllmEndpoints:\n # KServe backend\n - name: llama3-8b\n model: llama3-8b\n reasoningFamily: qwen3\n backend:\n type: kserve\n inferenceServiceName: llama-3-8b\n weight: 2 # Higher weight = more traffic\n\n # Direct service backend\n - name: qwen-7b\n model: qwen2.5-7b\n reasoningFamily: qwen3\n backend:\n type: service\n service:\n name: vllm-qwen\n port: 8000\n weight: 1\n"})}),"\n",(0,s.jsx)(n.h2,{id:"deployment-modes",children:"Deployment Modes"}),"\n",(0,s.jsx)(n.p,{children:"The operator supports two deployment modes with different architectures."}),"\n",(0,s.jsx)(n.h3,{id:"standalone-mode-default",children:"Standalone Mode (Default)"}),"\n",(0,s.jsxs)(n.p,{children:["Deploys semantic router with an ",(0,s.jsx)(n.strong,{children:"Envoy sidecar container"})," that acts as an ingress gateway."]}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.strong,{children:"Architecture:"})}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-text",children:"Client \u2192 Service (8080) \u2192 Envoy Sidecar \u2192 ExtProc gRP
1C \u2192 Semantic Router \u2192 vLLM\n"})}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.strong,{children:"When to use:"})}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:"Simple deployments without existing service mesh"}),"\n",(0,s.jsx)(n.li,{children:"Testing and development"}),"\n",(0,s.jsx)(n.li,{children:"Self-contained deployment with minimal dependencies"}),"\n"]}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.strong,{children:"Configuration:"})}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-yaml",children:"spec:\n # No gateway configuration - defaults to standalone mode\n service:\n type: ClusterIP\n api:\n port: 8080 # Client traffic enters here\n targetPort: 8080 # Envoy ingress port\n grpc:\n port: 50051 # ExtProc communication\n targetPort: 50051\n"})}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.strong,{children:"Operator behavior:"})}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:"Deploys pod with two containers: semantic router + Envoy sidecar"}),"\n",(0,s.jsx)(n.li,{children:"Envoy handles ingress and forwards to semantic router via ExtProc gRPC"}),"\n",(0,s.jsxs)(n.li,{children:["Status shows ",(0,s.jsx)(n.code,{children:'gatewayMode: "standalone"'})]}),"\n"]}),"\n",(0,s.jsx)(n.h3,{id:"gateway-integration-mode",children:"Gateway Integration Mode"}),"\n",(0,s.jsxs)(n.p,{children:["Reuses an ",(0,s.jsx)(n.strong,{children:"existing Gateway"})," (Istio, Envoy Gateway, etc.) and expects you to manage the matching HTTPRoute separately."]}),"\n",(0,s.jsx)(n.p,{children:"Current status: the controller resolves the referenced Gateway and switches the deployment into gateway mode, but automatic HTTPRoute creation is still a placeholder."}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.strong,{children:"Architecture:"})}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-text",children:"Client \u2192 Gateway (Istio/Envoy) \u2192 user-managed HTTPRoute \u2192 Service (8080) \u2192 Semantic Router API \u2192 vLLM\n"})}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.strong,{children:"When to use:"})}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:"Existing Istio or Envoy Gateway deployment"}),"\n",(0,s.jsx)(n.li,{children:"Centralized ingress management"}),"\n",(0,s.jsx)(n.li,{children:"Multi-tenancy with shared gateway"}),"\n",(0,s.jsx)(n.li,{children:"Advanced traffic management (circuit breaking, retries, rate limiting)"}),"\n"]}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.strong,{children:"Configuration:"})}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-yaml",children:"spec:\n gateway:\n existingRef:\n name: istio-ingressgateway # Or your Envoy Gateway name\n namespace: istio-system\n\n # Service only needs API port in gateway mode\n service:\n type: ClusterIP\n api:\n port: 8080\n targetPort: 8080\n"})}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.strong,{children:"Operator behavior:"})}),"\n",(0,s.jsxs)(n.ol,{children:["\n",(0,s.jsx)(n.li,{children:"Resolves the referenced Gateway and enters gateway integration mode"}),"\n",(0,s.jsx)(n.li,{children:"Does not create an HTTPRoute yet; you must apply and manage the route separately"}),"\n",(0,s.jsx)(n.li,{children:"Skips Envoy sidecar container in pod spec"}),"\n",(0,s.jsxs)(n.li,{children:["Sets ",(0,s.jsx)(n.code,{children:'status.gatewayMode: "gateway-integration"'})]}),"\n",(0,s.jsx)(n.li,{children:"Semantic router operates in pure API mode (no ExtProc)"}),"\n"]}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Example:"})," See ",(0,s.jsx)(n.a,{href:"https://github.com/vllm-project/semantic-router/blob/main/deploy/operator/config/samples/vllm.ai_v1alpha1_semanticrouter_gateway.yaml",children:(0,s.jsx)(n.code,{children:"vllm.ai_v1alpha1_semanticrouter_gateway.yaml"})})]}),"\n",(0,s.jsxs)(n.p,{children:["That sample configures the ",(0,s.jsx)(n.code,{children:"SemanticRouter"})," resource for gateway mode only. It does not install the Gateway or HTTPRoute resources for you."]}),"\n",(0,s.jsx)(n.h2,{id:"openshift-routes",children:"OpenShift Routes"}),"\n",(0,s.jsx)(n.p,{children:"For OpenShift deployments, the operator can create Routes for external access with TLS termination."}),"\n",(0,s.jsx)(n.h3,{id:"basic-openshift-route",children:"Basic OpenShift Route"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-yaml",children:"spec:\n openshift:\n routes:\n enabled: true\n hostname: semantic-router.apps.openshift.example.com # Optional - auto-generated if omitted\n tls:\n termination: edge # TLS terminates at Route, plain HTTP to backend\n insecureEdgeTerminationPolicy: Redirect # Redirect HTTP to HTTPS\n"})}),"\n",(0,s.jsx)(n.h3,{id:"tls-termination-options",children:"TLS Termination Options"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"edge"})," (recommended): TLS terminates at Route, plain HTTP to backend"]}
1),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"passthrough"}),": TLS passthrough to backend (requires backend TLS)"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"reencrypt"}),": TLS terminates at Route, re-encrypts to backend"]}),"\n"]}),"\n",(0,s.jsx)(n.h3,{id:"when-to-use-openshift-routes",children:"When to Use OpenShift Routes"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:"Running on OpenShift 4.x"}),"\n",(0,s.jsx)(n.li,{children:"Need external access without configuring Ingress"}),"\n",(0,s.jsx)(n.li,{children:"Want auto-generated hostnames"}),"\n",(0,s.jsx)(n.li,{children:"Require OpenShift-native TLS management"}),"\n"]}),"\n",(0,s.jsx)(n.h3,{id:"status-information",children:"Status Information"}),"\n",(0,s.jsx)(n.p,{children:"After creating a Route, check the status:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"kubectl get semanticrouter my-router -o jsonpath='{.status.openshiftFeatures}'\n"})}),"\n",(0,s.jsx)(n.p,{children:"Output:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-json",children:'{\n "routesEnabled": true,\n "routeHostname": "semantic-router-default.apps.openshift.example.com"\n}\n'})}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Example:"})," See ",(0,s.jsx)(n.a,{href:"https://github.com/vllm-project/semantic-router/blob/main/deploy/operator/config/samples/vllm.ai_v1alpha1_semanticrouter_route.yaml",children:(0,s.jsx)(n.code,{children:"vllm.ai_v1alpha1_semanticrouter_route.yaml"})})]}),"\n",(0,s.jsx)(n.h2,{id:"choosing-your-configuration",children:"Choosing Your Configuration"}),"\n",(0,s.jsx)(n.p,{children:"Use this decision tree to select the right configuration:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{children:"\u250c\u2500 Need to run on OpenShift?\n\u2502 \u251c\u2500 YES \u2192 Use openshift sample (Routes + KServe/service backends)\n\u2502 \u2514\u2500 NO \u2193\n\u2502\n\u251c\u2500 Have existing Gateway (Istio/Envoy)?\n\u2502 \u251c\u2500 YES \u2192 Use gateway sample (Gateway integration mode)\n\u2502 \u2514\u2500 NO \u2193\n\u2502\n\u251c\u2500 Using Meta Llama Stack?\n\u2502 \u251c\u2500 YES \u2192 Use llamastack sample\n\u2502 \u2514\u2500 NO \u2193\n\u2502\n\u2514\u2500 Simple deployment \u2192 Use simple sample (standalone mode)\n"})}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.strong,{children:"Backend choice:"})}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{children:"\u250c\u2500 Running RHOAI 3.x or KServe?\n\u2502 \u251c\u2500 YES \u2192 Use KServe backend type\n\u2502 \u2514\u2500 NO \u2193\n\u2502\n\u251c\u2500 Using Meta Llama Stack?\n\u2502 \u251c\u2500 YES \u2192 Use llamastack backend type\n\u2502 \u2514\u2500 NO \u2193\n\u2502\n\u2514\u2500 Have direct vLLM service? \u2192 Use service backend type\n"})}),"\n",(0,s.jsx)(n.h2,{id:"architecture",children:"Architecture"}),"\n",(0,s.jsx)(n.p,{children:"The operator manages a complete stack of resources for each SemanticRouter:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{children:"\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502 SemanticRouter CR \u2502\n\u2502 apiVersion: vllm.ai/v1alpha1 \u2502\n\u2502 kind: SemanticRouter \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n \u2502\n \u25bc\n \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n \u2502 Operator Controller \u2502\n \u2502 - Watches CR \u2502\n \u2502 - Reconciles state \u2502\n \u2502 - Platform detection\u2502\n \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n \u2502\n \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n \u25bc \u25bc \u25bc \u25bc\n\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502Deployment\u2502 \u2502 Service \u2502 \u2502ConfigMap\u2502 \u2502 PVC \u2502\n\u2502 \u2502 \u2502 - gRPC \u2502 \u2502 - config\u2502 \u2502 - models\u2502\n\u2502 \u2502 \u2502 - API \u2502 \u2502 - tools \u2502 \u2502 \u2502\n\u2502 \u2502 \u2502 - metrics\u2502 \u2502 \u2502 \u2502 \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n"})}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.strong,{children:"Managed Resources:"})}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"Deployment"}),": Runs semantic router pods with configurable replicas"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"Service"}),": Exposes gRPC (50051), HTTP API (8080), and metrics (9190)"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"ConfigMap"}),": Contains semantic router configuration and tools database"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"ServiceAccount"}),": For RBAC (optional, created when specified)"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"PersistentVolumeClaim"}),": For ML model storage (optional, when persistence enabled)"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"HorizontalPodAutoscaler"}),": For auto-scaling (optional, when autoscaling enabled)"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"Ingress"}),": For external access (optional, when ingress enabled)"]}),"\n"]}),"\n",(0,s.jsx)(n.h2,{id:"platform-detection-and-security",children:"Platform Detection and Security"}),"\n",(0,s.jsx)(n.p,{children:"The operator automatically detects the platform and configures security contexts appropriately."}),"\n",(0,s.jsx)(n.h3,{id:"openshift-platform",children:"OpenShift Platform"}),"\n",(0,s.jsx)(n.p,{children:"When running on OpenShift, the operator:"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"Detects"}),": Checks for ",(0,s.jsx)(n.code,{children:"route.openshift.io"})," API resources"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"Security Context"}),": Does NOT set ",(0,s.jsx)(n.code,{children:"runAsUser"}),", ",(0,s.jsx)(n.code,{children:"runAsGroup"}),", or ",(0,s.jsx)(n.code,{children:"fsGroup"})]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"Rationale"}),": Lets OpenShift SCCs assign UIDs/GIDs from the namespace's allowed range"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"Compatible with"}),": ",(0,s.jsx)(n.code,{children:"restricted"})," SCC (default) and custom SCCs"]}),"\n"]}),"\n",(0,s.jsx)(n.h3,{id:"standard-kubernetes",children:"Standard Kubernetes"}),"\n",(0,s.jsx)(n.p,{children:"When running on standard Kubernetes, the operator:"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"Security Context"}),": Sets ",(0,s.jsx)(n.code,{children:"runAsUser: 1000"}),", ",(0,s.jsx)(n.code,{children:"fsGroup: 1000"}),", ",(0,s.jsx)(n.code,{children:"runAsNonRoot: true"})]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"Rationale"}),": Provides secure defaults for pod security policies/standards"]}),"\n"]}),"\n",(0,s.jsx)(n.h3,{id:"both-platforms",children:"Both Platforms"}),"\n",(0,s.jsx)(n.p,{children:"Regardless of platform:"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:["Drops ALL capabilities (",(0,s.jsx)(n.code,{children:"drop: [ALL]"}),")"]}),"\n",(0,s.jsxs)(n.li,{children:["Prevents privilege escalation (",(0,s.jsx)(n.code,{children:"allowPrivilegeEscalation: false"}),")"]}),"\n",(0,s.jsx)(n.li,{children:"No special permissions or SCCs required beyond defaults"}),"\n"]}),"\n",(0,s.jsx)(n.h3,{id:"override-security-context",children:"Override Security Context"}),"\n",(0,s.jsx)(n.p,{children:"You can override automatic security contexts in your CR:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-yaml",children:"spec:\n # Container security context\n securityContext:\n runAsNonRoot: true\n runAsUser: 2000\n allowPrivilegeEscalation: false\n capabilities:\n drop:\n - ALL\n\n # Pod security context\n podSecurityContext:\n runAsNonRoot: true\n runAsUser: 2000\n fsGroup: 2000\n"})}),"\n",(0,s.jsx)(n.admonition,{title:"OpenShift Note",type:"caution",children:(0,s.jsxs)(n.p,{children:["When running on OpenShift, it's recommended to omit ",(0,s.jsx)(n.code,{children:"runAsUser"})," and ",(0,s.jsx)(n.code,{children:"fsGroup"})," and let SCCs handle UID/GID assignment automatically."]})}),"\n",(0,s.jsx)(n.h2,{id:"configuration-reference",children:"Configuration Reference"}),"\n",(0,s.jsx)(n.h3,{id:"image-configuration",children:"Image Configuration"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-yaml",children:'spec:\n image:\n repository: ghcr.io/vllm-project/semantic-router/extproc\n tag: latest\n pullPolicy: IfNotPresent\n imageRegistry: "" # Optional: custom registry prefix\n\n # Optional: Image pull secrets\n imagePullSecrets:\n - name: ghcr-secret\n'})}),"\n",(0,s.jsx)(n.h3,{id:"service-configuration",children:"Service Configuration"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-yaml",children:"spec:\n service:\n type: ClusterIP # or NodePort, LoadBalancer\n\n grpc:\n port: 50051\n targetPort: 50051\n\n api:\n port: 8080\n targetPort: 8080\n\n metrics:\n enabled: true\n port: 9190\n targetPort: 9190\n"})}),"\n",(0,s.jsx)(n.h3,{id:"persistence-configuration",children:"Persistence Configuration"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-yaml",children:'spec:\n persistence:\n enabled: true\n storageClassName: "standard" # Adjust for your cluster\n accessMode: ReadWriteOnce\n size: 10Gi\n\n # Optional: Use existing PVC\n existingClaim: "my-existing-pvc"\n\n # Optional: PVC annotations\n annotations:\n backup.velero.io/backup-volumes: "models"\n'})}),"\n",(0,s.jsx)(n.admonition,{title:"Storage Validation",type:"info",children:(0,s.jsxs)(n.p,{children:["The operator val
1idates that the specified StorageClass exists before creating the PVC. If ",(0,s.jsx)(n.code,{children:"storageClassName"})," is omitted, the cluster's default StorageClass is used."]})}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.strong,{children:"Storage Class Examples:"})}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"AWS EKS"}),": ",(0,s.jsx)(n.code,{children:"gp3-csi"}),", ",(0,s.jsx)(n.code,{children:"gp2"})]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"GKE"}),": ",(0,s.jsx)(n.code,{children:"standard"}),", ",(0,s.jsx)(n.code,{children:"premium-rwo"})]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"Azure AKS"}),": ",(0,s.jsx)(n.code,{children:"managed"}),", ",(0,s.jsx)(n.code,{children:"managed-premium"})]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"OpenShift"}),": ",(0,s.jsx)(n.code,{children:"gp3-csi"}),", ",(0,s.jsx)(n.code,{children:"thin"}),", ",(0,s.jsx)(n.code,{children:"ocs-storagecluster-ceph-rbd"})]}),"\n"]}),"\n",(0,s.jsx)(n.h3,{id:"semantic-cache-backends",children:"Semantic Cache Backends"}),"\n",(0,s.jsx)(n.p,{children:"The operator supports multiple cache backends for semantic caching, which significantly improves latency and reduces token usage by caching similar queries and their responses."}),"\n",(0,s.jsxs)(n.admonition,{title:"Prerequisites",type:"warning",children:[(0,s.jsxs)(n.p,{children:["The operator does ",(0,s.jsx)(n.strong,{children:"not"})," deploy Redis or Milvus. You must deploy these services separately in your cluster before using them as cache backends. The operator only configures the SemanticRouter to connect to your existing Redis/Milvus deployment."]}),(0,s.jsxs)(n.p,{children:["For deployment examples, see the ",(0,s.jsx)(n.a,{href:"#deploying-redis",children:"Redis"})," and ",(0,s.jsx)(n.a,{href:"#deploying-milvus",children:"Milvus"})," sections below."]}),(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Alternative:"})," If you prefer automatic deployment of Redis/Milvus, consider using the ",(0,s.jsx)(n.a,{href:"https://github.com/vllm-project/semantic-router/tree/main/deploy/helm",children:"Helm chart"}),", which can deploy cache backends as Helm chart dependencies."]})]}),"\n",(0,s.jsx)(n.h4,{id:"supported-backends",children:"Supported Backends"}),"\n",(0,s.jsx)(n.h5,{id:"1-memory-cache-default",children:"1. Memory Cache (Default)"}),"\n",(0,s.jsx)(n.p,{children:"Simple in-memory cache suitable for development and small deployments."}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.strong,{children:"Characteristics:"})}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:"No external dependencies"}),"\n",(0,s.jsx)(n.li,{children:"Fast access"}),"\n",(0,s.jsx)(n.li,{children:"Not persistent (cleared on restart)"}),"\n",(0,s.jsx)(n.li,{children:"Limited by pod memory"}),"\n"]}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.strong,{children:"Configuration:"})}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-yaml",children:'spec:\n config:\n global:\n stores:\n semantic_cache:\n enabled: true\n backend_type: memory\n similarity_threshold: "0.8"\n max_entries: 1000\n ttl_seconds: 3600\n eviction_policy: fifo # fifo, lru, or lfu\n'})}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.strong,{children:"When to use:"})}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:"Development and testing"}),"\n",(0,s.jsx)(n.li,{children:"Small deployments (<1000 cached queries)"}),"\n",(0,s.jsx)(n.li,{children:"No persistence requirements"}),"\n"]}),"\n",(0,s.jsx)(n.h5,{id:"2-redis-cache",children:"2. Redis Cache"}),"\n",(0,s.jsx)(n.p,{children:"High-performance distributed cache using Redis with vector search capabilities."}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.strong,{children:"Characteristics:"})}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:"Distributed and scalable"}),"\n",(0,s.jsx)(n.li,{children:"Persistent storage (with AOF/RDB)"}),"\n",(0,s.jsx)(n.li,{children:"HNSW or FLAT indexing"}),"\n",(0,s.jsx)(n.li,{children:"Wide ecosystem support"}),"\n"]}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.strong,{children:"Prerequisites:"})}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:"Redis 7.0+ with RediSearch module"}),"\n",(0,s.jsx)(n.li,{children:"Create Kubernetes Secret for password:"}),"\n"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"kubectl create secret generic redis-credentials \\\n --from-literal=password='your-redis-password'\n"})}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.strong,{children:"Configuration:"})}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-yaml",children:'spec:\n config:\n global:\n stores:\n semantic_cache:\n enabled: true\n backend_type: redis\n similarity_threshold: "0.85"\n ttl_seconds: 3600\n embedding_model: mmbert\n redis:\n connection:\n host: redis.default.svc.cluster.local\n port: 6379\n database: 0\n password_secret_ref:\n name: redis-credentials\n key: password\n timeout: 30\n tls:\n enabled: false\n index:\n name: semantic_cache_idx\n prefix: "cache:"\n vector_field:\n name: embedding\n dimension: 768 # Match the default mmBERT ultra embedding model\n metric_type: COSINE\n index_type: HNSW\n params:\n M: 16\n efConstruction: 64\n search:\n topk: 1\n development:\n auto_create_index: true\n verbose_errors: true\n'})}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.strong,{children:"When to use:"})}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:"Production deployments with moderate scale"}),"\n",(0,s.jsx)(n.li,{children:"Need persistence and high availability"}
1),"\n",(0,s.jsx)(n.li,{children:"Existing Redis infrastructure"}),"\n",(0,s.jsx)(n.li,{children:"Fast in-memory performance required"}),"\n"]}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Example:"})," ",(0,s.jsx)(n.a,{href:"https://github.com/vllm-project/semantic-router/blob/main/deploy/operator/config/samples/vllm.ai_v1alpha1_semanticrouter_redis_cache.yaml",children:(0,s.jsx)(n.code,{children:"vllm.ai_v1alpha1_semanticrouter_redis_cache.yaml"})})]}),"\n",(0,s.jsx)(n.h5,{id:"3-milvus-cache",children:"3. Milvus Cache"}),"\n",(0,s.jsx)(n.p,{children:"Enterprise-grade vector database for production deployments with large cache volumes."}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.strong,{children:"Characteristics:"})}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:"Highly scalable and distributed"}),"\n",(0,s.jsx)(n.li,{children:"Advanced indexing (HNSW, IVF, etc.)"}),"\n",(0,s.jsx)(n.li,{children:"Built-in data lifecycle management"}),"\n",(0,s.jsx)(n.li,{children:"High availability support"}),"\n"]}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.strong,{children:"Prerequisites:"})}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:"Milvus 2.3+ (standalone or cluster)"}),"\n",(0,s.jsx)(n.li,{children:"Create Kubernetes Secret for credentials:"}),"\n"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"kubectl create secret generic milvus-credentials \\\n --from-literal=password='your-milvus-password'\n"})}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.strong,{children:"Configuration:"})}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-yaml",children:'spec:\n config:\n global:\n stores:\n semantic_cache:\n enabled: true\n backend_type: milvus\n similarity_threshold: "0.90"\n ttl_seconds: 7200\n embedding_model: mmbert\n milvus:\n connection:\n host: milvus-standalone.default.svc.cluster.local\n port: 19530\n database: semantic_router_cache\n timeout: 30\n auth:\n enabled: true\n username: root\n password_secret_ref:\n name: milvus-credentials\n key: password\n collection:\n name: semantic_cache\n description: "Semantic cache for LLM responses"\n vector_field:\n name: embedding\n dimension: 768 # Match the default mmBERT ultra embedding model\n metric_type: IP\n index:\n type: HNSW\n params:\n M: 16\n efConstruction: 64\n search:\n params:\n ef: 64\n topk: 10\n consistency_level: Session\n performance:\n connection_pool:\n max_connections: 10\n max_idle_connections: 5\n batch:\n insert_batch_size: 100\n data_management:\n ttl:\n enabled: true\n timestamp_field: created_at\n cleanup_interval: 3600\n development:\n auto_create_collection: true\n'})}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.strong,{children:"When to use:"})}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:"Large-scale production deployments"}),"\n",(0,s.jsx)(n.li,{children:"Need advanced vector search capabilities"}),"\n",(0,s.jsx)(n.li,{children:"Require data lifecycle management (TTL, compaction)"}),"\n",(0,s.jsx)(n.li,{children:"High availability and scalability requirements"}),"\n"]}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Example:"})," ",(0,s.jsx)(n.a,{href:"https://github.com/vllm-project/semantic-router/blob/main/deploy/operator/config/samples/vllm.ai_v1alpha1_semanticrouter_milvus_cache.yaml",children:(0,s.jsx)(n.code,{children:"vllm.ai_v1alpha1_semanticrouter_milvus_cache.yaml"})})]}),"\n",(0,s.jsx)(n.h5,{id:"4-hybrid-cache",children:"4. Hybrid Cache"}),"\n",(0,s.jsx)(n.p,{children:"Combines in-memory HNSW index with persistent Milvus storage for optimal performance and durability."}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.strong,{children:"Characteristics:"})}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:"Fast in-memory search with HNSW"}),"\n",(0,s.jsx)(n.li,{children:"Persistent storage in Milvus"}),"\n",(0,s.jsx)(n.li,{children:"Best of both worlds"}),"\n",(0,s.jsx)(n.li,{children:"Automatic synchronization"}),"\n"]}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.strong,{children:"Configuration:"})}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-yaml",children:'spec:\n config:\n global:\n stores:\n semantic_cache:\n enabled: true\n backend_type: hybrid\n similarity_threshold: "0.85"\n ttl_seconds: 3600\n max_entries: 5000\n eviction_policy: lru\n embedding_model: mmbert\n # HNSW in-memory configuration\n hnsw:\n use_hnsw: true\n hnsw_m: 32\n hnsw_ef_construction: 128\n max_memory_entries: 5000\n\n # Milvus persistent storage (same config as milvus backend)\n milvus:\n connection:\n host: milvus-standalone.default.svc.cluster.local\n port: 19530\n # ... rest of milvus config\n'})}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.strong,{children:"When to use:"})}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:"Need fastest possible cache lookups"}),"\n",(0,s.jsx)(n.li,{children:"Require persistence and durability"}),"\n",(0,s.jsx)(n.li,{children:"Willing to trade memory for performance"}),"\n",(0,s.jsx)(n.li,{children:"High-throughput production deployments"}),"\n"]}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Example:"})," ",(0,s.jsx)(n.a,{href:"https://github.com/vllm-project/semantic-router/blob/main/deploy/operator/config/samples/vllm.ai_v1alpha1_semanticrouter_hybrid_cache.yaml",children:(0,s.jsx)(n.code,{children:"vllm.ai_v1alpha1_semanticrouter_hybrid_cache.yaml"})})]}),"\n",(0,s.jsx)(n.h4,{id:"deploying-redis",children:"Deploying Redis"}),"\n",(0,s.jsx)(n.p,{children:"Before using Redis cache backend, deploy Redis with RediSearch module to your cluster:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-yaml",children:"---\napiVersion: v1\nkind: Namespace\nmetadata:\n name: cache-backends\n---\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n name: redis\n namespace: cac
1he-backends\nspec:\n replicas: 1\n selector:\n matchLabels:\n app: redis\n template:\n metadata:\n labels:\n app: redis\n spec:\n containers:\n - name: redis\n image: redis/redis-stack-server:latest\n ports:\n - containerPort: 6379\n resources:\n requests:\n cpu: 100m\n memory: 256Mi\n limits:\n cpu: 500m\n memory: 512Mi\n---\napiVersion: v1\nkind: Service\nmetadata:\n name: redis\n namespace: cache-backends\nspec:\n type: ClusterIP\n ports:\n - port: 6379\n targetPort: 6379\n selector:\n app: redis\n"})}),"\n",(0,s.jsx)(n.p,{children:"Apply the configuration:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"kubectl apply -f redis-deployment.yaml\n"})}),"\n",(0,s.jsx)(n.p,{children:"Create the credentials Secret:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"kubectl create secret generic redis-credentials \\\n --from-literal=password='' # Empty for no password, or set your password\n"})}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"For production deployments"}),", consider using:"]}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"https://github.com/spotahome/redis-operator",children:"Redis Operator"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"https://docs.redis.com/latest/kubernetes/",children:"Redis Enterprise Operator"})}),"\n",(0,s.jsx)(n.li,{children:"Managed Redis services (AWS ElastiCache, Azure Cache for Redis, GCP Memorystore)"}),"\n"]}),"\n",(0,s.jsx)(n.h4,{id:"deploying-milvus",children:"Deploying Milvus"}),"\n",(0,s.jsx)(n.p,{children:"Before using Milvus cache backend, deploy Milvus to your cluster:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-yaml",children:'---\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n name: milvus-standalone\n namespace: cache-backends\nspec:\n replicas: 1\n selector:\n matchLabels:\n app: milvus\n template:\n metadata:\n labels:\n app: milvus\n spec:\n containers:\n - name: milvus\n image: milvusdb/milvus:v2.4.0\n command: ["milvus", "run", "standalone"]\n ports:\n - containerPort: 19530\n name: grpc\n - containerPort: 9091\n name: metrics\n env:\n - name: ETCD_USE_EMBED\n value: "true"\n - name: COMMON_STORAGETYPE\n value: "local"\n resources:\n requests:\n cpu: 500m\n memory: 1Gi\n limits:\n cpu: 2000m\n memory: 4Gi\n volumeMounts:\n - name: milvus-data\n mountPath: /var/lib/milvus\n volumes:\n - name: milvus-data\n emptyDir: {} # Use PVC for production\n---\napiVersion: v1\nkind: Service\nmetadata:\n name: milvus-standalone\n namespace: cache-backends\nspec:\n type: ClusterIP\n ports:\n - port: 19530\n targetPort: 19530\n name: grpc\n selector:\n app: milvus\n'})}),"\n",(0,s.jsx)(n.p,{children:"Apply the configuration:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"kubectl apply -f milvus-deployment.yaml\n"})}),"\n",(0,s.jsx)(n.p,{children:"Create the credentials Secret:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"kubectl create secret generic milvus-credentials \\\n --from-literal=password='Milvus' # Default Milvus password\n"})}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"For production deployments"}),", consider using:"]}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"https://milvus.io/docs/install_cluster-milvusoperator.md",children:"Milvus Operator"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"https://milvus.io/docs/install_cluster-helm.md",children:"Milvus Helm Chart"})}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.a,{href:"https://cloud.zilliz.com/",children:"Zilliz Cloud"})," (managed Milvus service)"]}),"\n"]}),"\n",(0,s.jsxs)(n.admonition,{title:"Production Best Practices",type:"tip",children:[(0,s.jsx)(n.p,{children:"For production cache backends:"}),(0,s.jsxs)(n.ol,{children:["\n",(0,s.jsx)(n.li,{children:"Use persistent volumes (not emptyDir)"}),"\n",(0,s.jsx)(n.li,{children:"Enable authentication and TLS"}),"\n",(0,s.jsx)(n.li,{children:"Configure resource limits appropriately"}),"\n",(0,s.jsx)(n.li,{children:"Set up monitoring and alerting"}),"\n",(0,s.jsx)(n.li,{children:"Use operators or Helm charts for easier management"}),"\n",(0,s.jsx)(n.li,{children:"Consider managed services for reduced operational overhead"}),"\n"]})]}),"\n",(0,s.jsx)(n.h4,{id:"embedding-models",children:"Embedding Models"}),"\n",(0,s.jsx)(n.p,{children:"The semantic cache supports different embedding models for similarity calculation:"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"bert"})," (default): Lightweight, 384 dimensions, good for general use"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"qwen3"}),": Higher quality, 1024 dimensions, better accuracy"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"gemma"}),": Balanced, 768 dimensions, moderate performance"]}),"\n"]}),"\n",(0,s.jsx)(n.p,{children:"Configure via:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-yaml",children:"spec:\n config:\n global:\n stores:\n semantic_cache:\n embedding_model: mmbert # or qwen3, gemma with explicit model_catalog override\n"})}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Note:"})," Ensure the ",(0,s.jsx)(n.code,{children:"dimension"})," in cache config matches your chosen embedding model."]}),"\n",(0,s.jsx)(n.h4,{id:"migration-between-backends",children:"Migration Between Backends"}),"\n",(0,s.jsx)(n.p,{children:"Migrating from memory cache to Redis or Milvus is straightforward:"}),"\n",(0,s.jsxs)(n.ol,{children:["\n",(0,s.jsx)(n.li,{children:"Deploy Redis or Milvus in your cluster"}),"\n",(0,s.jsx)(n.li,{children:"Create the credentials Secret"}),"\n",(0,s.jsx)(n.li,{children:"Update SemanticRouter CR with new backend configuration"}),"\n",(0,s.jsx)(n.li,{children:"Apply the changes - operator will perform rolling update"}),"\n"]}),"\n",(0,s.jsx)(n.p,{children:"The cache will be empty after migration but will populate naturally as queries are processed."}),"\n",(0,s.jsx)(n.h4,{id:"cache-configuration-reference",children:"Cache Configuration Reference"}),"\n",(0,s.jsxs)(n.p,{children:["For detailed configuration options, use ",(0,s.jsx)(n.code,{children:"kubectl explain"}),":"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"# Redis cache configuration\nkubectl explain semanticrouter.spec.config.global.stores.semantic_cache.redis\n\n# Milvus cache
1configuration\nkubectl explain semanticrouter.spec.config.global.stores.semantic_cache.milvus\n\n# HNSW configuration\nkubectl explain semanticrouter.spec.config.global.stores.semantic_cache.hnsw\n"})}),"\n",(0,s.jsx)(n.h3,{id:"semantic-router-configuration",children:"Semantic Router Configuration"}),"\n",(0,s.jsxs)(n.p,{children:["Full semantic router configuration is embedded in the CR. See the complete examples above and in ",(0,s.jsx)(n.a,{href:"https://github.com/vllm-project/semantic-router/tree/main/deploy/operator/config/samples",children:(0,s.jsx)(n.code,{children:"deploy/operator/config/samples/"})}),"."]}),"\n",(0,s.jsx)(n.p,{children:"Key configuration sections:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-yaml",children:'spec:\n config:\n providers:\n defaults:\n reasoning_families:\n deepseek:\n type: "chat_template_kwargs"\n parameter: "thinking"\n qwen3:\n type: "chat_template_kwargs"\n parameter: "enable_thinking"\n gpt:\n type: "reasoning_effort"\n parameter: "reasoning_effort"\n\n global:\n model_catalog:\n embeddings:\n semantic:\n mmbert_model_path: "models/mom-embedding-ultra"\n use_cpu: true\n embedding_config:\n model_type: "mmbert"\n preload_embeddings: true\n target_dimension: 768\n target_layer: 22\n top_k: 1\n min_score_threshold: 0.5\n system:\n prompt_guard: "models/mmbert32k-jailbreak-detector-merged"\n domain_classifier: "models/mmbert32k-intent-classifier-merged"\n pii_classifier: "models/mmbert32k-pii-detector-merged"\n modules:\n prompt_guard:\n enabled: true\n model_id: "models/mmbert32k-jailbreak-detector-merged"\n threshold: 0.7\n use_cpu: true\n use_mmbert_32k: true\n classifier:\n domain:\n model_id: "models/mmbert32k-intent-classifier-merged"\n threshold: 0.5\n use_cpu: true\n use_mmbert_32k: true\n pii:\n model_id: "models/mmbert32k-pii-detector-merged"\n threshold: 0.9\n use_cpu: true\n use_mmbert_32k: true\n\n stores:\n semantic_cache:\n enabled: true\n backend_type: "memory" # or redis, milvus, hybrid\n similarity_threshold: 0.8\n max_entries: 1000\n ttl_seconds: 3600\n eviction_policy: "fifo"\n\n integrations:\n tools:\n enabled: true\n top_k: 3\n similarity_threshold: 0.2\n tools_db_path: "config/tools_db.json"\n fallback_to_empty: true\n\n services:\n api:\n batch_classification:\n max_batch_size: 100\n concurrency_threshold: 5\n max_concurrency: 8\n metrics:\n enabled: true\n detailed_goroutine_tracking: true\n sample_rate: 1.0\n observability:\n tracing:\n enabled: false\n provider: "opentelemetry"\n exporter:\n type: "otlp"\n endpoint: "jaeger:4317"\n'})}),"\n",(0,s.jsx)(n.h3,{id:"tools-database",children:"Tools Database"}),"\n",(0,s.jsx)(n.p,{children:"Define available tools for auto-selection:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-yaml",children:'spec:\n toolsDb:\n - tool:\n type: "function"\n function:\n name: "search_web"\n description: "Search the web for information"\n parameters:\n type: "object"\n properties:\n query:\n type: "string"\n description: "Search query"\n required: ["query"]\n description: "Search the internet, web search, find information online"\n category: "search"\n tags: ["search", "web", "internet"]\n\n - tool:\n type: "function"\n function:\n name: "calculate"\n description: "Perform mathematical calculations"\n parameters:\n type: "object"\n properties:\n expression:\n type: "string"\n required: ["expression"]\n description: "Calculate mathematical expressions"\n category: "math"\n tags: ["math", "calculation"]\n'})}),"\n",(0,s.jsx)(n.h3,{id:"autoscaling-hpa",children:"Autoscaling (HPA)"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-yaml",children:"spec:\n autoscaling:\n enabled: true\n minReplicas: 2\n maxReplicas: 10\n targetCPUUtilizationPercentage: 70\n targetMemoryUtilizationPercentage: 80\n"})}),"\n",(0,s.jsx)(n.h3,{id:"ingress-configuration",children:"Ingress Configuration"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-yaml",children:'spec:\n ingress:\n enabled: true\n className: "nginx" # or "haproxy", "traefik", etc.\n annotations:\n cert-manager.io/cluster-issuer: "letsencrypt-prod"\n hosts:\n - host: router.example.com\n paths:\n - path: /\n pathType: Prefix\n servicePort: 8080\n tls:\n - secretName: router-tls\n hosts:\n - router.example.com\n'})}),"\n",(0,s.jsx)(n.h2,{id:"production-deployment",children:"Production Deployment"}),"\n",(0,s.jsx)(n.h3,{id:"high-availability-setup",children:"High Availability Setup"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-yaml",children:'apiVersion: vllm.ai/v1alpha1\nkind: SemanticRouter\nmetadata:\n name: prod-router\nspec:\n replicas: 3\n\n # Anti-affinity for spreading across nodes\n affinity:\n podAntiAffinity:\n requiredDuringSchedulingIgnoredDuringExecution:\n - labelSelector:\n matchLabels:\n app.kubernetes.io/instance: prod-router\n topologyKey: kubernetes.io/hostname\n\n # Autoscaling\n autoscaling:\n enabled: true\n minReplicas: 3\n maxReplicas: 20\n targetCPUUtilizationPercentage: 70\n\n # Production resources\n resources:\n limits:\n memory: "10Gi"\n cpu: "4"\n requests:\n memory: "5Gi"\n cpu: "2"\n\n # Strict probes\n livenessProbe:\n enabled: true\n initialDelaySeconds: 60\n periodSeconds: 30\n failureThreshold: 3\n\n readinessProbe:\n enabled: true\n initialDelaySeconds: 30\n periodSeconds: 10\n failureThreshold: 3\n'})}),"\n",(0,s.jsx)(n.h3,{id:"pod-disruption-budget",children:"Pod Disruption Budget"}),"\n",(0,s.jsx)(n.p,{children:"Create a PDB to ensure availability during updates:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-yaml",children:"apiVersion: policy/v1\nkind: PodDisruptionBudget\nmetadata:\n name: prod-router-pdb\nspec:\n maxUnavailable: 1\n selector:\n matchLabels:\n app.kubernetes.io/instance: prod-router\n"})}),"\n",(0,s.jsx)(n.h3,{id:"resource-allocation-guidelines",children:"Resource Allocation Guidelines"}),"\n",(0,s.jsxs)(n.table,{children:[(0,s.jsx)(n.thead,{children:(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.th,{children:"Workload Type"}),(0,s.jsx)(n.th,{children:"Memory Request"}),(0,s.jsx)(n.th,{children:"CPU Request"}),(0,s.jsx)(n.th,{children:"Memory Limit"}),(0,s.jsx)(n.th,{children:"CPU Limit"})]})}),(0,s.jsxs)(n.tbody,{children:[(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:"Development"}),(0,s.jsx)(n.td,{children:"1Gi"}),(0,s.jsx)(n.td,{children:"500m"}),(0,s.jsx)(n.td,{children:"2Gi"}),(0,s.jsx)(n.td,{children:"1"})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:"Staging"}),(0,s.jsx)(n.td,{children:"3Gi"}),(0,s.jsx)(n.td,{children:"1"}),(0,s.jsx)(n.td,{children:"7Gi"}),(0,s.jsx)(n.td,{children:"2"})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:"Production"}),(0,s.jsx)(n.td,{children:"5Gi"}),(0,s.jsx)(n.td,{children:"2"}),(0,s.jsx)(n.td,{children:"10Gi"}),(0,s.jsx)(n.td,{children:"4"})]})]})]}),"\n",(0,s.jsx)(n.h2,{id:"monitoring-and-observability",children:"Monitoring and Observability"}),"\n",(0,s.jsx)(n.h3,{id:"metrics",children:"Metrics"}),"\n",(0,s.jsx)(n.p,{children:"Prometheus metrics are exposed on port 9190:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"# Port-forward to access metrics locally\nkubectl port-forward svc/my-router 9190:9190\n\n# View metrics\ncurl http://localhost:9190/metrics\n"})}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.strong,{children:"Key Metrics:"})}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.code,{children:"semantic_router_request_duration_seconds"})," - Request latency"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.code,{children:"semantic_router_cache_hit_total"})," - Cache hit rate"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.code,{children:"semantic_router_classification_duration_seconds"})," - Classification latency"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.code,{children:"semantic_router_tokens_total"})," - Token usage"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.code,{children:"semantic_router_reasoning_requests_total"})," - Reasoning mode usage"]}),"\n"]}),"\n",(0,s.jsx)(n.h3,{id:"servicemonitor-prometheus-operator",children:"ServiceMonitor (Prometheus Operator)"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-yaml",children:"apiVersion: monitoring.coreos.com/v1\nkind: ServiceMonitor\nmetadata:\n name: semantic-router-metrics\nspec:\n selector:\n matchLabels:\n app.kubernetes.io/instance: my-router\n endpoints:\n - port: metrics\n interval: 30s\n path: /metrics\n"})}),"\n",(0,s.jsx)(n.h3,{id:"distributed-tracing",children:"Distributed Tracing"}),"\n",(0,s.jsx)(n.p,{children:"Enable OpenTelemetry tracing:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-yaml",children:'spec:\n config:\n observability:\n tracing:\n enabled: true\n provider: "opentelemetry"\n exporter:\n type: "otlp"\n endpoint: "jaeger-collector:4317"\n insecure: true\n sampling:\n type: "always_on"\n rate: 1.0\n'})}),"\n",(0,s.jsx)(n.h2,{id:"troubleshooting",children:"Troubleshooting"}),"\n",(0,s.jsx)(n.h3,{id:"common-issues",children:"Common Issues"}),"\n",(0,s.jsx)(n.h4,{id:"backend-discovery-failures",children:"Backend Discovery Failures"}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Symptom:"}),' "No backends found" or "Failed to discover backend" in logs']}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.strong,{children:"For KServe backends:"})}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"# Check InferenceService exists and is ready\nkubectl get inferenceservice llama-3-8b\n\n# Check predictor service was created by KServe\nkubectl get service llama-3-8b-predictor\n\n# Verify InferenceService status\nkubectl describe inferenceservice llama-3-8b\n"})}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.strong,{children:"For Llama Stack backends:"})}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"# Verify services exist with correct labels\nkubectl get services -l app=llama-stack,model=llama-3.3-70b\n\n# Check service labels match discoveryLabels in CR\nkubectl get service <service-name> -o jsonpath='{.metadata.labels}'\n"})}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.strong,{children:"For direct service backends:"})}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"# Verify service exists in specified namespace\nkubectl get service vllm-deepseek -n vllm-serving\n\n# Check service has ports defined\nkubectl get service vllm-deepseek -n vllm-serving -o jsonpath='{.spec.ports[0]}'\n"})}),"\n",(0,s.jsx)(n.h4,{id:"gateway-integration-issues",children:"Gateway Integration Issues"}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Symptom:"})," No HTTPRoute exists yet, or traffic is not reaching semantic router"]}),"\n",(0,s.jsx)(n.p,{children:"The current operator implementation does not create HTTPRoute resources automatically. If you have not applied an HTTPRoute manifest yourself, that is expected behavior rather than a failed reconcile."}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"# Verify Gateway exists\nkubectl get gateway istio-ingressgateway -n istio-system\n\n# Check whether you created an HTTPRoute yourself\nkubectl get httproute -l app.kubernetes.io/instance=my-router\n\n# Verify Gateway supports HTTPRoute (Gateway API v1)\nkubectl get gateway istio-ingressgateway -n istio-system -o yaml | grep -A5 listeners\n\n# Check operator status\nkubectl get semanticrouter my-router -o jsonpath='{.status.gatewayMode}'\n# Should show: \"gateway-integration\"\n\n# Check operator logs for the current placeholder message\nkubectl logs deployment/semantic-router-operator-controller-manager -n semantic-router-system | grep 'HTTPRoute creation placeholder'\n"})}),"\n",(0,s.jsxs)(n.p,{children:["If the operator logs show ",(0,s.jsx)(n.code,{children:"HTTPRoute creation placeholder - requires Gateway API version-specific implementation"}),", the controller recognized gateway mode but did not create a route. Apply an HTTPRoute manifest manually and verify that it targets the semantic router service on port 8080."]}),"\n",(0,s.jsx)(n.h4,{id:"openshift-route-issues",children:"OpenShift Route Issues"}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Symptom:"})," Route not created on OpenShift"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"# Verify running on OpenShift cluster\nkubectl api-resources | grep route.openshift.io\n\n# Check if Route was created\nkubectl get route -l app.kubernetes.io/instance=my-router\n\n# Check operator detected OpenShift\nkubectl logs -n semantic-router-operator-system \\\n deployment/semantic-router-operator-controller-manager \\\n | grep -i \"openshift\\|route\"\n\n# Verify Route status\nkubectl get semanticrouter my-router -o jsonpath='{.status.openshiftFeatures}'\n"})}),"\n",(0,s.jsxs)(n.h4,{id:"pod-stuck-in-imagepullbackoff",children:["Pod stuck in ",(0,s.jsx)(n.code,{children:"ImagePullBackOff"})]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"# Check image pull secrets\nkubectl describe pod <pod-name>\n\n# Create image pull secret\nkubectl create secret docker-registry ghcr-secret \\\n --docker-server=ghcr.io \\\n --docker-username=<username>
1 \\\n --docker-password=<personal-access-token>\n\n# Add to SemanticRouter\nspec:\n imagePullSecrets:\n - name: ghcr-secret\n"})}),"\n",(0,s.jsxs)(n.h4,{id:"pvc-stuck-in-pending",children:["PVC stuck in ",(0,s.jsx)(n.code,{children:"Pending"})]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:'# Check storage class exists\nkubectl get storageclass\n\n# Check PVC events\nkubectl describe pvc my-router-models\n\n# Update storage class in CR\nspec:\n persistence:\n storageClassName: "your-available-storage-class"\n'})}),"\n",(0,s.jsx)(n.h4,{id:"models-not-downloading",children:"Models not downloading"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"# Check if HF token secret exists\nkubectl get secret hf-token-secret\n\n# Create HF token secret\nkubectl create secret generic hf-token-secret \\\n --from-literal=token=hf_xxxxxxxxxxxxx\n\n# Add to SemanticRouter CR\nspec:\n env:\n - name: HF_TOKEN\n valueFrom:\n secretKeyRef:\n name: hf-token-secret\n key: token\n"})}),"\n",(0,s.jsx)(n.h4,{id:"operator-not-detecting-platform-correctly",children:"Operator not detecting platform correctly"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:'# Check operator logs for platform detection\nkubectl logs -n semantic-router-operator-system \\\n deployment/semantic-router-operator-controller-manager \\\n | grep -i "platform\\|openshift"\n\n# Should see one of:\n# "Detected OpenShift platform - will use OpenShift-compatible security contexts"\n# "Detected standard Kubernetes platform - will use standard security contexts"\n'})}),"\n",(0,s.jsx)(n.h2,{id:"development",children:"Development"}),"\n",(0,s.jsx)(n.h3,{id:"local-development",children:"Local Development"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"cd deploy/operator\n\n# Run tests\nmake test\n\n# Generate CRDs and code\nmake generate\nmake manifests\n\n# Build operator binary\nmake build\n\n# Run locally against your kubeconfig\nmake run\n"})}),"\n",(0,s.jsx)(n.h3,{id:"testing-with-kind",children:"Testing with kind"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"# Create kind cluster\nkind create cluster --name operator-test\n\n# Build and load image\nmake docker-build IMG=semantic-router-operator:dev\nkind load docker-image semantic-router-operator:dev --name operator-test\n\n# Deploy\nmake install\nmake deploy IMG=semantic-router-operator:dev\n\n# Create test instance\nkubectl apply -f config/samples/vllm_v1alpha1_semanticrouter.yaml\n"})}),"\n",(0,s.jsx)(n.h2,{id:"next-steps",children:"Next Steps"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"../configuration",children:"Configure semantic router features"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"../../tutorials/global/api-and-observability",children:"Set up monitoring and observability"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"/docs/installation",children:"Explore other deployment options"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"../../community/overview",children:"Join the community"})}),"\n"]})]})}function h(e={}){const{wrapper:n}={...(0,t.R)(),...e.components};return n?(0,s.jsx)(n,{...e,children:(0,s.jsx)(d,{...e})}):d(e)}},28453(e,n,i){i.d(n,{R:()=>a,x:()=>l});var r=i(96540);const s={},t=r.createContext(s);function a(e){const n=r.useContext(t);return r.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function l(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:a(e.components),r.createElement(t.Provider,{value:n},e.children)}}}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.