From d51e797c0e2a63df3552293f54b614c042ddc20a Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 26 Jan 2026 02:23:53 +0000 Subject: [PATCH 1/3] Add Helm chart with production-scale templates Includes: - Full Helm chart from bubble-bullshit branch - qdrant-config.yaml: ConfigMap for Qdrant storage/optimizer config - pdb.yaml: Pod Disruption Budgets for zero-downtime updates - networkpolicy.yaml: Network isolation for services - resourcequota.yaml: Resource quota enforcement - Updated qdrant.yaml with nodeSelector/tolerations/affinity support - Updated values.yaml with config schema for 300 user / 3M LOC scale --- deploy/helm/context-engine/Chart.yaml | 24 + deploy/helm/context-engine/README.md | 384 +++++++++++ .../context-engine/templates/_helpers.tpl | 167 +++++ .../context-engine/templates/configmap.yaml | 165 +++++ deploy/helm/context-engine/templates/hpa.yaml | 123 ++++ .../context-engine/templates/ingress.yaml | 149 ++++ .../templates/learning-reranker-worker.yaml | 68 ++ .../templates/mcp-indexer-http.yaml | 181 +++++ .../templates/mcp-memory-http.yaml | 178 +++++ .../context-engine/templates/namespace.yaml | 8 + .../templates/networkpolicy.yaml | 86 +++ deploy/helm/context-engine/templates/pdb.yaml | 93 +++ deploy/helm/context-engine/templates/pvc.yaml | 71 ++ .../templates/qdrant-config.yaml | 57 ++ .../helm/context-engine/templates/qdrant.yaml | 147 ++++ .../templates/resourcequota.yaml | 32 + .../templates/serviceaccount.yaml | 13 + .../templates/upload-service.yaml | 91 +++ .../context-engine/templates/watcher.yaml | 171 +++++ .../helm/context-engine/values-example.yaml | 250 +++++++ deploy/helm/context-engine/values.yaml | 639 ++++++++++++++++++ 21 files changed, 3097 insertions(+) create mode 100644 deploy/helm/context-engine/Chart.yaml create mode 100644 deploy/helm/context-engine/README.md create mode 100644 deploy/helm/context-engine/templates/_helpers.tpl create mode 100644 deploy/helm/context-engine/templates/configmap.yaml create mode 100644 deploy/helm/context-engine/templates/hpa.yaml create mode 100644 deploy/helm/context-engine/templates/ingress.yaml create mode 100644 deploy/helm/context-engine/templates/learning-reranker-worker.yaml create mode 100644 deploy/helm/context-engine/templates/mcp-indexer-http.yaml create mode 100644 deploy/helm/context-engine/templates/mcp-memory-http.yaml create mode 100644 deploy/helm/context-engine/templates/namespace.yaml create mode 100644 deploy/helm/context-engine/templates/networkpolicy.yaml create mode 100644 deploy/helm/context-engine/templates/pdb.yaml create mode 100644 deploy/helm/context-engine/templates/pvc.yaml create mode 100644 deploy/helm/context-engine/templates/qdrant-config.yaml create mode 100644 deploy/helm/context-engine/templates/qdrant.yaml create mode 100644 deploy/helm/context-engine/templates/resourcequota.yaml create mode 100644 deploy/helm/context-engine/templates/serviceaccount.yaml create mode 100644 deploy/helm/context-engine/templates/upload-service.yaml create mode 100644 deploy/helm/context-engine/templates/watcher.yaml create mode 100644 deploy/helm/context-engine/values-example.yaml create mode 100644 deploy/helm/context-engine/values.yaml diff --git a/deploy/helm/context-engine/Chart.yaml b/deploy/helm/context-engine/Chart.yaml new file mode 100644 index 00000000..7a858bd8 --- /dev/null +++ b/deploy/helm/context-engine/Chart.yaml @@ -0,0 +1,24 @@ +apiVersion: v2 +name: context-engine +description: Self-hosted semantic code search and memory via MCP +type: application +version: 0.1.0 +appVersion: "1.0.0" + +keywords: + - context-engine + - mcp + - code-search + - semantic-search + - qdrant + - ai + +home: https://context-engine.ai +sources: + - https://github.com/Context-Engine-AI/Context-Engine + +maintainers: + - name: Context Engine AI + +annotations: + category: AI/ML diff --git a/deploy/helm/context-engine/README.md b/deploy/helm/context-engine/README.md new file mode 100644 index 00000000..dcdc1ead --- /dev/null +++ b/deploy/helm/context-engine/README.md @@ -0,0 +1,384 @@ +# Context-Engine Helm Chart + +Self-hosted semantic code search and memory via MCP. + +## Prerequisites + +- Kubernetes 1.19+ +- Helm 3.2.0+ +- PV provisioner support (for persistent storage) +- Storage classes: `gp3-sc` (block) and `efs-sc` (shared filesystem) or equivalents + +## Installation + +### Quick Start + +```bash +# Add local chart +helm install ce-dev ./deploy/helm/context-engine \ + --namespace context-engine \ + --create-namespace +``` + +### With Custom Values + +```bash +# Copy the example values and customize +cp deploy/helm/context-engine/values-example.yaml deploy/kubernetes/values-mycompany.yaml +# Edit deploy/kubernetes/values-mycompany.yaml with your settings + +# Install with custom values +helm install ce-mycompany ./deploy/helm/context-engine \ + -f ./deploy/kubernetes/values-mycompany.yaml \ + --namespace context-engine \ + --create-namespace +``` + +**Note**: Customer-specific values files should be stored in `deploy/kubernetes/` (gitignored) to keep sensitive configuration separate from the chart. + +### From OCI Registry (when published) + +```bash +helm install ce-prod oci://ghcr.io/context-engine-ai/charts/context-engine \ + --version 0.1.0 \ + --namespace context-engine \ + --create-namespace \ + -f custom-values.yaml +``` + +## Uninstall + +```bash +helm uninstall ce-dev --namespace context-engine +``` + +## Configuration + +### Global Settings + +| Parameter | Description | Default | +|-----------|-------------|---------| +| `global.environment` | Environment name (dev, staging, prod) | `dev` | +| `global.team` | Team label for resources | `ai` | +| `global.appName` | Application name for labels | `context-engine` | + +### Image + +| Parameter | Description | Default | +|-----------|-------------|---------| +| `image.repository` | Image repository | `context-engine` | +| `image.tag` | Image tag (defaults to Chart appVersion) | `""` | +| `image.pullPolicy` | Pull policy | `IfNotPresent` | +| `image.pullSecrets` | Image pull secrets | `[]` | + +### Namespace + +| Parameter | Description | Default | +|-----------|-------------|---------| +| `namespace.create` | Create namespace | `true` | +| `namespace.name` | Namespace name | `context-engine` | + +### Qdrant + +| Parameter | Description | Default | +|-----------|-------------|---------| +| `qdrant.enabled` | Enable Qdrant | `true` | +| `qdrant.image.repository` | Qdrant image | `qdrant/qdrant` | +| `qdrant.image.tag` | Qdrant version | `latest` | +| `qdrant.replicas` | Number of replicas | `1` | +| `qdrant.service.httpPort` | HTTP port | `6333` | +| `qdrant.service.grpcPort` | gRPC port | `6334` | +| `qdrant.externalService.enabled` | Enable NodePort service | `true` | +| `qdrant.externalService.httpNodePort` | HTTP NodePort | `30333` | +| `qdrant.persistence.enabled` | Enable persistence | `true` | +| `qdrant.persistence.storageClassName` | Storage class | `gp3-sc` | +| `qdrant.persistence.size` | Storage size | `50Gi` | +| `qdrant.resources.requests.cpu` | CPU request | `1` | +| `qdrant.resources.requests.memory` | Memory request | `8Gi` | +| `qdrant.resources.limits.cpu` | CPU limit | `4` | +| `qdrant.resources.limits.memory` | Memory limit | `24Gi` | + +### MCP Indexer HTTP + +| Parameter | Description | Default | +|-----------|-------------|---------| +| `mcpIndexerHttp.enabled` | Enable indexer | `true` | +| `mcpIndexerHttp.replicas` | Number of replicas | `1` | +| `mcpIndexerHttp.service.port` | Service port | `8003` | +| `mcpIndexerHttp.externalService.nodePort` | NodePort | `30806` | +| `mcpIndexerHttp.autoscaling.enabled` | Enable HPA | `true` | +| `mcpIndexerHttp.autoscaling.minReplicas` | Min replicas | `1` | +| `mcpIndexerHttp.autoscaling.maxReplicas` | Max replicas | `4` | +| `mcpIndexerHttp.resources.requests.memory` | Memory request | `8Gi` | +| `mcpIndexerHttp.resources.limits.memory` | Memory limit | `16Gi` | + +### MCP Memory HTTP + +| Parameter | Description | Default | +|-----------|-------------|---------| +| `mcpMemoryHttp.enabled` | Enable memory service | `true` | +| `mcpMemoryHttp.replicas` | Number of replicas | `1` | +| `mcpMemoryHttp.service.port` | Service port | `8002` | +| `mcpMemoryHttp.externalService.nodePort` | NodePort | `30804` | +| `mcpMemoryHttp.autoscaling.enabled` | Enable HPA | `true` | +| `mcpMemoryHttp.autoscaling.minReplicas` | Min replicas | `1` | +| `mcpMemoryHttp.autoscaling.maxReplicas` | Max replicas | `3` | + +### Upload Service + +| Parameter | Description | Default | +|-----------|-------------|---------| +| `uploadService.enabled` | Enable upload service | `true` | +| `uploadService.replicas` | Number of replicas | `1` | +| `uploadService.service.port` | Service port | `8002` | +| `uploadService.service.nodePort` | NodePort | `30810` | +| `uploadService.autoscaling.enabled` | Enable HPA | `true` | + +### Watcher + +| Parameter | Description | Default | +|-----------|-------------|---------| +| `watcher.enabled` | Enable watcher | `true` | +| `watcher.replicas` | Number of replicas | `1` | +| `watcher.initContainers.waitForQdrant.enabled` | Wait for Qdrant | `true` | +| `watcher.initContainers.initCollection.enabled` | Init collection | `true` | + +### Learning Reranker Worker + +| Parameter | Description | Default | +|-----------|-------------|---------| +| `learningRerankerWorker.enabled` | Enable learning worker | `true` | +| `learningRerankerWorker.replicas` | Number of replicas | `1` | +| `learningRerankerWorker.autoscaling.enabled` | Enable HPA | `true` | + +### Persistence (Shared PVCs) + +| Parameter | Description | Default | +|-----------|-------------|---------| +| `persistence.codeRepos.enabled` | Enable code-repos PVC | `true` | +| `persistence.codeRepos.storageClassName` | Storage class | `efs-sc` | +| `persistence.codeRepos.size` | Storage size | `50Gi` | +| `persistence.codeMetadata.enabled` | Enable metadata PVC | `true` | +| `persistence.codeMetadata.size` | Storage size | `10Gi` | +| `persistence.codeModels.enabled` | Enable models PVC | `true` | +| `persistence.codeModels.size` | Storage size | `20Gi` | + +### Ingress + +| Parameter | Description | Default | +|-----------|-------------|---------| +| `ingress.enabled` | Enable ingress | `true` | +| `ingress.className` | Ingress class | `nginx` | +| `ingress.host` | Hostname | `""` | +| `ingress.tls` | TLS configuration | `[]` | +| `ingress.admin.enabled` | Enable admin ingress | `true` | + +### Configuration (ConfigMap) + +| Parameter | Description | Default | +|-----------|-------------|---------| +| `config.collectionName` | Qdrant collection name | `codebase` | +| `config.embeddingModel` | Embedding model | `BAAI/bge-base-en-v1.5` | +| `config.embeddingProvider` | Embedding provider | `fastembed` | +| `config.reranker.enabled` | Enable reranker | `1` | +| `config.reranker.model` | Reranker model | `jinaai/jina-reranker-v2-base-multilingual` | +| `config.refrag.enabled` | Enable ReFRAG | `1` | +| `config.refrag.runtime` | Decoder runtime | `glm` | +| `config.glm.apiBase` | GLM API base URL | `""` | +| `config.glm.apiKey` | GLM API key | `""` | +| `config.glm.model` | GLM model | `glm-4.7` | +| `config.auth.enabled` | Enable auth | `0` | +| `config.extraEnv` | Additional env vars | `{}` | + +## Examples + +### Minimal Installation (Dev/Testing) + +```yaml +# values-minimal.yaml +qdrant: + persistence: + storageClassName: standard + size: 10Gi + +persistence: + codeRepos: + storageClassName: standard + size: 10Gi + codeMetadata: + storageClassName: standard + size: 5Gi + codeModels: + storageClassName: standard + size: 5Gi + +# Disable optional components +learningRerankerWorker: + enabled: false + +ingress: + enabled: false +``` + +### Production with TLS + +```yaml +# values-prod.yaml +image: + repository: 535002867043.dkr.ecr.us-east-1.amazonaws.com/context-engine + tag: v1.0.0 + +config: + collectionName: production-codebase + auth: + enabled: "1" + sharedToken: "your-token-here" + +ingress: + enabled: true + className: alb + host: ce.example.com + annotations: + alb.ingress.kubernetes.io/scheme: internet-facing + alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:... + tls: + - hosts: + - ce.example.com + secretName: ce-tls +``` + +### With GLM Decoder + +```yaml +# values-with-decoder.yaml +config: + refrag: + mode: "1" + decoder: "1" + decoderMode: prompt + runtime: glm + glm: + apiBase: "https://api.z.ai/api/coding/paas/v4/" + apiKey: "your-api-key" + model: glm-4.7 +``` + +### Multi-Repo Mode + +```yaml +# values-multi-repo.yaml +config: + multiRepoMode: "1" + repoAutoFilter: "1" + collectionName: multi-repo-collection + +watcher: + env: + MULTI_REPO_MODE: "1" +``` + +## Architecture + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ Ingress (nginx) │ +│ /indexer → mcp-indexer-http /memory → mcp-memory-http │ +│ /upload → upload-service /qdrant → qdrant │ +└──────────────────────────────┬──────────────────────────────────┘ + │ + ┌─────────────────────────┼─────────────────────────┐ + │ │ │ +┌────┴────┐ ┌─────┴─────┐ ┌──────┴──────┐ +│ Indexer │ │ Memory │ │ Upload │ +│ HTTP │ │ HTTP │ │ Service │ +└────┬────┘ └─────┬─────┘ └──────┬──────┘ + │ │ │ + └─────────────────────────┼─────────────────────────┘ + │ + ┌─────┴─────┐ + │ Qdrant │ + │(StatefulSet) + └───────────┘ + ▲ + ┌─────────────────────────┼─────────────────────────┐ + │ │ │ +┌────┴────┐ ┌─────┴─────┐ ┌──────┴──────┐ +│ Watcher │ │ Learning │ │ Shared │ +│ │ │ Worker │ │ PVCs │ +└─────────┘ └───────────┘ └─────────────┘ +``` + +## Storage Classes + +The chart expects two types of storage: + +1. **Block storage** (`gp3-sc`): For Qdrant StatefulSet (ReadWriteOnce) +2. **Shared filesystem** (`efs-sc`): For code-repos, metadata, models (ReadWriteMany) + +### AWS EKS Example + +```yaml +# gp3-sc.yaml +apiVersion: storage.k8s.io/v1 +kind: StorageClass +metadata: + name: gp3-sc +provisioner: ebs.csi.aws.com +parameters: + type: gp3 +volumeBindingMode: WaitForFirstConsumer +allowVolumeExpansion: true + +--- +# efs-sc.yaml +apiVersion: storage.k8s.io/v1 +kind: StorageClass +metadata: + name: efs-sc +provisioner: efs.csi.aws.com +parameters: + provisioningMode: efs-ap + fileSystemId: fs-xxxxxxxxx + directoryPerms: "700" +``` + +## Upgrading + +```bash +helm upgrade ce-dev ./deploy/helm/context-engine \ + -f values-dev.yaml \ + --namespace context-engine +``` + +## Troubleshooting + +### Check Pod Status + +```bash +kubectl get pods -n context-engine +kubectl describe pod -n context-engine +``` + +### View Logs + +```bash +# Indexer logs +kubectl logs -n context-engine -l app.kubernetes.io/component=mcp-indexer-http + +# Watcher logs +kubectl logs -n context-engine -l app.kubernetes.io/component=watcher + +# Qdrant logs +kubectl logs -n context-engine -l app.kubernetes.io/component=qdrant +``` + +### Common Issues + +1. **Pods pending**: Check PVC status and storage class availability +2. **Watcher init fails**: Verify Qdrant is running and accessible +3. **Memory OOM**: Increase memory limits for indexer/memory services +4. **Ingress not working**: Verify ingress controller and annotations + +## License + +BUSL-1.1 diff --git a/deploy/helm/context-engine/templates/_helpers.tpl b/deploy/helm/context-engine/templates/_helpers.tpl new file mode 100644 index 00000000..7a9b2e1b --- /dev/null +++ b/deploy/helm/context-engine/templates/_helpers.tpl @@ -0,0 +1,167 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "context-engine.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +*/}} +{{- define "context-engine.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "context-engine.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "context-engine.labels" -}} +helm.sh/chart: {{ include "context-engine.chart" . }} +{{ include "context-engine.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +app.kubernetes.io/component: kubernetes-deployment +environment: {{ .Values.global.environment }} +team: {{ .Values.global.team }} +{{- with .Values.commonLabels }} +{{ toYaml . }} +{{- end }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "context-engine.selectorLabels" -}} +app.kubernetes.io/name: {{ include "context-engine.fullname" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +app: {{ .Values.global.appName }} +{{- end }} + +{{/* +Component labels - adds component-specific labels +*/}} +{{- define "context-engine.componentLabels" -}} +{{ include "context-engine.labels" . }} +component: {{ .component }} +{{- end }} + +{{/* +Component selector labels +*/}} +{{- define "context-engine.componentSelectorLabels" -}} +{{ include "context-engine.selectorLabels" . }} +component: {{ .component }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "context-engine.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "context-engine.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} + +{{/* +Create the namespace name +*/}} +{{- define "context-engine.namespace" -}} +{{- default .Release.Namespace .Values.namespace.name }} +{{- end }} + +{{/* +Create Qdrant URL +*/}} +{{- define "context-engine.qdrantUrl" -}} +{{- if .Values.config.qdrantUrl }} +{{- .Values.config.qdrantUrl }} +{{- else }} +{{- printf "http://qdrant:%d" (int .Values.qdrant.service.httpPort) }} +{{- end }} +{{- end }} + +{{/* +Create Memory MCP URL +*/}} +{{- define "context-engine.memoryMcpUrl" -}} +{{- if .Values.config.memory.mcpUrl }} +{{- .Values.config.memory.mcpUrl }} +{{- else }} +{{- printf "http://mcp-memory-http:%d/sse" (int .Values.mcpMemoryHttp.service.port) }} +{{- end }} +{{- end }} + +{{/* +Image name helper +*/}} +{{- define "context-engine.image" -}} +{{- $tag := default .Chart.AppVersion .Values.image.tag }} +{{- printf "%s:%s" .Values.image.repository $tag }} +{{- end }} + +{{/* +Pod security context +*/}} +{{- define "context-engine.podSecurityContext" -}} +{{- with .Values.podSecurityContext }} +{{- toYaml . }} +{{- end }} +{{- end }} + +{{/* +Topology spread constraints helper +*/}} +{{- define "context-engine.topologySpreadConstraints" -}} +{{- if .config.enabled }} +topologySpreadConstraints: + - maxSkew: {{ .config.maxSkew }} + topologyKey: {{ .config.topologyKey }} + whenUnsatisfiable: {{ .config.whenUnsatisfiable }} + labelSelector: + matchLabels: + {{- include "context-engine.componentSelectorLabels" .context | nindent 8 }} +{{- end }} +{{- end }} + +{{/* +HPA behavior configuration +*/}} +{{- define "context-engine.hpaBehavior" -}} +behavior: + scaleDown: + policies: + - type: Percent + value: 100 + periodSeconds: 15 + stabilizationWindowSeconds: 300 + scaleUp: + policies: + - type: Percent + value: 100 + periodSeconds: 30 + - type: Pods + value: 4 + periodSeconds: 30 + selectPolicy: Max + stabilizationWindowSeconds: 0 +{{- end }} diff --git a/deploy/helm/context-engine/templates/configmap.yaml b/deploy/helm/context-engine/templates/configmap.yaml new file mode 100644 index 00000000..2a0f1f85 --- /dev/null +++ b/deploy/helm/context-engine/templates/configmap.yaml @@ -0,0 +1,165 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "context-engine.fullname" . }}-config + namespace: {{ include "context-engine.namespace" . }} + labels: + {{- include "context-engine.labels" . | nindent 4 }} + component: configuration +data: + COLLECTION_NAME: {{ .Values.config.collectionName | quote }} + QDRANT_URL: {{ include "context-engine.qdrantUrl" . | quote }} + EMBEDDING_MODEL: {{ .Values.config.embeddingModel | quote }} + EMBEDDING_PROVIDER: {{ .Values.config.embeddingProvider | quote }} + EMBEDDING_WARMUP: "0" + + FASTMCP_HOST: {{ .Values.config.fastmcp.host | quote }} + FASTMCP_PORT: {{ .Values.config.fastmcp.port | quote }} + FASTMCP_INDEXER_PORT: {{ .Values.config.fastmcp.indexerPort | quote }} + FASTMCP_HTTP_PORT: {{ .Values.config.fastmcp.httpPort | quote }} + FASTMCP_INDEXER_HTTP_PORT: {{ .Values.config.fastmcp.indexerHttpPort | quote }} + FASTMCP_HTTP_TRANSPORT: {{ .Values.config.fastmcp.httpTransport | quote }} + FASTMCP_HTTP_HEALTH_PORT: "18002" + FASTMCP_INDEXER_HTTP_HEALTH_PORT: "18003" + + INDEX_MICRO_CHUNKS: {{ .Values.config.indexing.microChunks | quote }} + MAX_MICRO_CHUNKS_PER_FILE: {{ .Values.config.indexing.maxMicroChunksPerFile | quote }} + INDEX_CHUNK_LINES: {{ .Values.config.indexing.chunkLines | quote }} + INDEX_CHUNK_OVERLAP: {{ .Values.config.indexing.chunkOverlap | quote }} + INDEX_SEMANTIC_CHUNKS: {{ .Values.config.indexing.semanticChunks | quote }} + INDEX_USE_ENHANCED_AST: {{ .Values.config.indexing.useEnhancedAst | quote }} + + HYBRID_EXPAND: {{ .Values.config.hybrid.expand | quote }} + HYBRID_IN_PROCESS: {{ .Values.config.hybrid.inProcess | quote }} + HYBRID_MINI_WEIGHT: {{ .Values.config.hybrid.miniWeight | quote }} + HYBRID_PER_PATH: {{ .Values.config.hybrid.perPath | quote }} + HYBRID_RECENCY_WEIGHT: {{ .Values.config.hybrid.recencyWeight | quote }} + HYBRID_RESULTS_CACHE: {{ .Values.config.hybrid.resultsCache | quote }} + HYBRID_RESULTS_CACHE_ENABLED: {{ .Values.config.hybrid.resultsCacheEnabled | quote }} + HYBRID_SNIPPET_DISK_READ: {{ .Values.config.hybrid.snippetDiskRead | quote }} + HYBRID_SYMBOL_BOOST: {{ .Values.config.hybrid.symbolBoost | quote }} + + RERANKER_ENABLED: {{ .Values.config.reranker.enabled | quote }} + RERANKER_MODEL: {{ .Values.config.reranker.model | quote }} + RERANKER_TIMEOUT_MS: {{ .Values.config.reranker.timeoutMs | quote }} + RERANKER_TOPN: {{ .Values.config.reranker.topN | quote }} + RERANKER_RETURN_M: {{ .Values.config.reranker.returnM | quote }} + RERANKER_ONNX_PATH: "/app/models/reranker.onnx" + RERANKER_TOKENIZER_PATH: "/app/models/tokenizer.json" + + REFRAG_MODE: {{ .Values.config.refrag.mode | quote }} + REFRAG_DECODER: {{ .Values.config.refrag.decoder | quote }} + REFRAG_DECODER_MODE: {{ .Values.config.refrag.decoderMode | quote }} + REFRAG_GATE_FIRST: {{ .Values.config.refrag.gateFirst | quote }} + REFRAG_CANDIDATES: {{ .Values.config.refrag.candidates | quote }} + REFRAG_RUNTIME: {{ .Values.config.refrag.runtime | quote }} + REFRAG_ENCODER_MODEL: {{ .Values.config.embeddingModel | quote }} + REFRAG_SENSE: "heuristic" + REFRAG_SOFT_SCALE: "1.0" + REFRAG_COMMIT_DESCRIBE: "1" + REFRAG_PSEUDO_DESCRIBE: "1" + REFRAG_PHI_PATH: "/work/models/refrag_phi_768_to_dmodel.bin" + + {{- if .Values.config.glm.apiBase }} + GLM_API_BASE: {{ .Values.config.glm.apiBase | quote }} + {{- end }} + {{- if .Values.config.glm.apiKey }} + GLM_API_KEY: {{ .Values.config.glm.apiKey | quote }} + {{- end }} + GLM_MODEL: {{ .Values.config.glm.model | quote }} + GLM_MODEL_FAST: {{ .Values.config.glm.modelFast | quote }} + + GRAPH_RAG_ENABLED: {{ .Values.config.graph.ragEnabled | quote }} + GRAPH_IMPORT_ON_INDEX: {{ .Values.config.graph.importOnIndex | quote }} + GRAPH_CONTEXT_RADIUS: {{ .Values.config.graph.contextRadius | quote }} + + SYMBOL_GRAPH_ENABLED: {{ .Values.config.symbolGraph.enabled | quote }} + + MULTI_REPO_MODE: {{ .Values.config.multiRepoMode | quote }} + REPO_AUTO_FILTER: {{ .Values.config.repoAutoFilter | quote }} + + MEMORY_SSE_ENABLED: {{ .Values.config.memory.sseEnabled | quote }} + MEMORY_MCP_URL: {{ include "context-engine.memoryMcpUrl" . | quote }} + MEMORY_AUTODETECT: {{ .Values.config.memory.autodetect | quote }} + MEMORY_COLLECTION_TTL_SECS: "300" + MEMORY_MCP_TIMEOUT: "6" + MEMORY_UPSERT_WAIT: "1" + + CTXCE_AUTH_ENABLED: {{ .Values.config.auth.enabled | quote }} + {{- if .Values.config.auth.sharedToken }} + CTXCE_AUTH_SHARED_TOKEN: {{ .Values.config.auth.sharedToken | quote }} + {{- end }} + {{- if .Values.config.auth.adminToken }} + CTXCE_AUTH_ADMIN_TOKEN: {{ .Values.config.auth.adminToken | quote }} + {{- end }} + + USE_TREE_SITTER: "1" + TOON_ENABLED: "1" + ADAPTIVE_SPAN_SIZING: "1" + SMART_SYMBOL_REINDEXING: "1" + STRICT_MEMORY_RESTORE: "1" + PATTERN_VECTORS: "1" + PATTERN_ENGRAM_HASH: "1" + MULTI_GRANULAR_VECTORS: "1" + PRF_ENABLED: "1" + PSEUDO_BACKFILL_ENABLED: "1" + PSEUDO_BATCH_BACKFILL: "1" + PSEUDO_BATCH_CONCURRENCY: "3" + + RERANK_EVENTS_ENABLED: "1" + RERANK_LEARNING: "1" + RERANK_LLM_TEACHER: "1" + RERANK_EXPAND: "1" + RERANK_IN_PROCESS: "1" + RERANK_BLEND_WEIGHT: "0.6" + RERANK_EVENT_SAMPLE_RATE: "0.5" + RERANK_LLM_SAMPLE_RATE: "1.0" + RERANK_TIMEOUT_FLOOR_MS: "1000" + RERANK_VICREG_WEIGHT: "0.1" + RERANK_WARMUP: "0" + + LEX_SPARSE_MODE: "1" + LEX_SPARSE_NAME: "lex_sparse" + LEX_VECTOR_DIM: "2048" + LEX_VECTOR_NAME: "lex" + LEX_BIGRAMS: "1" + LEX_BIGRAM_WEIGHT: "0.7" + LEX_MULTI_HASH: "3" + + MINI_VEC_DIM: "64" + MINI_VEC_SEED: "1337" + MINI_VECTOR_NAME: "mini" + + MICRO_BUDGET_TOKENS: "5000" + MICRO_CHUNK_STRIDE: "48" + MICRO_CHUNK_TOKENS: "24" + MICRO_MERGE_LINES: "6" + MICRO_OUT_MAX_SPANS: "10" + MICRO_TOKENS_PER_LINE: "32" + + QDRANT_EF_SEARCH: "128" + QDRANT_TIMEOUT: "20" + + QUERY_OPTIMIZER_ADAPTIVE: "1" + QUERY_OPTIMIZER_COLLECTION_SIZE: "10000" + QUERY_OPTIMIZER_MIN_EF: "64" + QUERY_OPTIMIZER_MAX_EF: "512" + + INDEX_UPSERT_BATCH: "128" + INDEX_UPSERT_RETRIES: "5" + INDEX_UPSERT_BACKOFF: "0.5" + + MAX_EMBED_CACHE: "16384" + MAX_CHANGED_SYMBOLS_RATIO: "0.6" + DECODER_MAX_TOKENS: "4000" + REPO_SEARCH_DEFAULT_LIMIT: "7" + SYMBOL_SUGGESTIONS_LIMIT: "3" + + WATCH_DEBOUNCE_SECS: "4" + + TOOL_STORE_DESCRIPTION: "Store reusable code snippets for later retrieval. The 'information' is a clear NL description; include the actual code in 'metadata.code' and add 'metadata.language' (e.g., python, typescript) and 'metadata.path' when known. Use this whenever you generate or refine a code snippet." + TOOL_FIND_DESCRIPTION: "Search for relevant code snippets using multiple phrasings of the query (multi-query). Prefer results where metadata.language matches the target file and metadata.path is relevant. You may pass optional filters (language, path_prefix, kind) which the server applies server-side. Include 'metadata.code', 'metadata.path', and 'metadata.language' in responses." + + {{- range $key, $value := .Values.config.extraEnv }} + {{ $key }}: {{ $value | quote }} + {{- end }} diff --git a/deploy/helm/context-engine/templates/hpa.yaml b/deploy/helm/context-engine/templates/hpa.yaml new file mode 100644 index 00000000..d13cfc26 --- /dev/null +++ b/deploy/helm/context-engine/templates/hpa.yaml @@ -0,0 +1,123 @@ +{{- if .Values.mcpIndexerHttp.autoscaling.enabled }} +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: mcp-indexer-http-hpa + namespace: {{ include "context-engine.namespace" . }} + labels: + {{- include "context-engine.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: mcp-indexer-http + minReplicas: {{ .Values.mcpIndexerHttp.autoscaling.minReplicas }} + maxReplicas: {{ .Values.mcpIndexerHttp.autoscaling.maxReplicas }} + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: {{ .Values.mcpIndexerHttp.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: {{ .Values.mcpIndexerHttp.autoscaling.targetMemoryUtilizationPercentage }} + {{- include "context-engine.hpaBehavior" . | nindent 2 }} +{{- end }} +--- +{{- if .Values.mcpMemoryHttp.autoscaling.enabled }} +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: mcp-memory-http-hpa + namespace: {{ include "context-engine.namespace" . }} + labels: + {{- include "context-engine.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: mcp-memory-http + minReplicas: {{ .Values.mcpMemoryHttp.autoscaling.minReplicas }} + maxReplicas: {{ .Values.mcpMemoryHttp.autoscaling.maxReplicas }} + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: {{ .Values.mcpMemoryHttp.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: {{ .Values.mcpMemoryHttp.autoscaling.targetMemoryUtilizationPercentage }} + {{- include "context-engine.hpaBehavior" . | nindent 2 }} +{{- end }} +--- +{{- if .Values.uploadService.autoscaling.enabled }} +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: upload-service-hpa + namespace: {{ include "context-engine.namespace" . }} + labels: + {{- include "context-engine.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: upload-service + minReplicas: {{ .Values.uploadService.autoscaling.minReplicas }} + maxReplicas: {{ .Values.uploadService.autoscaling.maxReplicas }} + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: {{ .Values.uploadService.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: {{ .Values.uploadService.autoscaling.targetMemoryUtilizationPercentage }} + {{- include "context-engine.hpaBehavior" . | nindent 2 }} +{{- end }} +--- +{{- if .Values.learningRerankerWorker.autoscaling.enabled }} +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: learning-reranker-worker-hpa + namespace: {{ include "context-engine.namespace" . }} + labels: + {{- include "context-engine.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: learning-reranker-worker + minReplicas: {{ .Values.learningRerankerWorker.autoscaling.minReplicas }} + maxReplicas: {{ .Values.learningRerankerWorker.autoscaling.maxReplicas }} + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: {{ .Values.learningRerankerWorker.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: {{ .Values.learningRerankerWorker.autoscaling.targetMemoryUtilizationPercentage }} + {{- include "context-engine.hpaBehavior" . | nindent 2 }} +{{- end }} diff --git a/deploy/helm/context-engine/templates/ingress.yaml b/deploy/helm/context-engine/templates/ingress.yaml new file mode 100644 index 00000000..709c8141 --- /dev/null +++ b/deploy/helm/context-engine/templates/ingress.yaml @@ -0,0 +1,149 @@ +{{- if .Values.ingress.enabled }} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ include "context-engine.fullname" . }}-ingress + namespace: {{ include "context-engine.namespace" . }} + labels: + {{- include "context-engine.labels" . | nindent 4 }} + {{- with .Values.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if .Values.ingress.className }} + ingressClassName: {{ .Values.ingress.className }} + {{- end }} + {{- if .Values.ingress.tls }} + tls: + {{- range .Values.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . | quote }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} + {{- end }} + rules: + {{- if .Values.ingress.host }} + - host: {{ .Values.ingress.host | quote }} + http: + paths: + {{- if .Values.mcpIndexerHttp.enabled }} + - path: {{ .Values.ingress.paths.indexer.path }} + pathType: {{ .Values.ingress.paths.indexer.pathType }} + backend: + service: + name: mcp-indexer-http + port: + number: {{ .Values.ingress.paths.indexer.servicePort }} + {{- end }} + {{- if .Values.mcpMemoryHttp.enabled }} + - path: {{ .Values.ingress.paths.memory.path }} + pathType: {{ .Values.ingress.paths.memory.pathType }} + backend: + service: + name: mcp-memory-http + port: + number: {{ .Values.ingress.paths.memory.servicePort }} + {{- end }} + {{- if .Values.uploadService.enabled }} + - path: {{ .Values.ingress.paths.upload.path }} + pathType: {{ .Values.ingress.paths.upload.pathType }} + backend: + service: + name: upload-service + port: + number: {{ .Values.ingress.paths.upload.servicePort }} + {{- end }} + {{- if .Values.qdrant.enabled }} + - path: {{ .Values.ingress.paths.qdrant.path }} + pathType: {{ .Values.ingress.paths.qdrant.pathType }} + backend: + service: + name: qdrant + port: + number: {{ .Values.ingress.paths.qdrant.servicePort }} + {{- end }} + {{- else }} + - http: + paths: + {{- if .Values.mcpIndexerHttp.enabled }} + - path: {{ .Values.ingress.paths.indexer.path }} + pathType: {{ .Values.ingress.paths.indexer.pathType }} + backend: + service: + name: mcp-indexer-http + port: + number: {{ .Values.ingress.paths.indexer.servicePort }} + {{- end }} + {{- if .Values.mcpMemoryHttp.enabled }} + - path: {{ .Values.ingress.paths.memory.path }} + pathType: {{ .Values.ingress.paths.memory.pathType }} + backend: + service: + name: mcp-memory-http + port: + number: {{ .Values.ingress.paths.memory.servicePort }} + {{- end }} + {{- if .Values.uploadService.enabled }} + - path: {{ .Values.ingress.paths.upload.path }} + pathType: {{ .Values.ingress.paths.upload.pathType }} + backend: + service: + name: upload-service + port: + number: {{ .Values.ingress.paths.upload.servicePort }} + {{- end }} + {{- if .Values.qdrant.enabled }} + - path: {{ .Values.ingress.paths.qdrant.path }} + pathType: {{ .Values.ingress.paths.qdrant.pathType }} + backend: + service: + name: qdrant + port: + number: {{ .Values.ingress.paths.qdrant.servicePort }} + {{- end }} + {{- end }} +{{- if .Values.ingress.admin.enabled }} +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ include "context-engine.fullname" . }}-admin + namespace: {{ include "context-engine.namespace" . }} + labels: + {{- include "context-engine.labels" . | nindent 4 }} + {{- with .Values.ingress.admin.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if .Values.ingress.className }} + ingressClassName: {{ .Values.ingress.className }} + {{- end }} + rules: + {{- if .Values.ingress.host }} + - host: {{ .Values.ingress.host | quote }} + http: + paths: + - path: {{ .Values.ingress.admin.path }} + pathType: {{ .Values.ingress.admin.pathType }} + backend: + service: + name: upload-service + port: + number: {{ .Values.uploadService.service.port }} + {{- else }} + - http: + paths: + - path: {{ .Values.ingress.admin.path }} + pathType: {{ .Values.ingress.admin.pathType }} + backend: + service: + name: upload-service + port: + number: {{ .Values.uploadService.service.port }} + {{- end }} +{{- end }} +{{- end }} diff --git a/deploy/helm/context-engine/templates/learning-reranker-worker.yaml b/deploy/helm/context-engine/templates/learning-reranker-worker.yaml new file mode 100644 index 00000000..9af2ccbf --- /dev/null +++ b/deploy/helm/context-engine/templates/learning-reranker-worker.yaml @@ -0,0 +1,68 @@ +{{- if .Values.learningRerankerWorker.enabled }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: learning-reranker-worker + namespace: {{ include "context-engine.namespace" . }} + labels: + {{- include "context-engine.labels" . | nindent 4 }} + component: learning-reranker-worker +spec: + replicas: {{ .Values.learningRerankerWorker.replicas }} + selector: + matchLabels: + {{- include "context-engine.selectorLabels" . | nindent 6 }} + component: learning-reranker-worker + template: + metadata: + labels: + {{- include "context-engine.labels" . | nindent 8 }} + component: learning-reranker-worker + spec: + serviceAccountName: {{ include "context-engine.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + {{- if .Values.learningRerankerWorker.topologySpreadConstraints.enabled }} + topologySpreadConstraints: + - maxSkew: {{ .Values.learningRerankerWorker.topologySpreadConstraints.maxSkew }} + topologyKey: {{ .Values.learningRerankerWorker.topologySpreadConstraints.topologyKey }} + whenUnsatisfiable: {{ .Values.learningRerankerWorker.topologySpreadConstraints.whenUnsatisfiable }} + labelSelector: + matchLabels: + {{- include "context-engine.selectorLabels" . | nindent 14 }} + component: learning-reranker-worker + {{- end }} + initContainers: + - name: init-rerank-dirs + image: busybox:1.36 + imagePullPolicy: IfNotPresent + command: + - sh + - -c + - mkdir -p /mnt/rerank_weights /mnt/rerank_events && chmod 777 /mnt/rerank_weights /mnt/rerank_events + volumeMounts: + - name: metadata-volume + mountPath: /mnt + containers: + - name: learning-reranker-worker + image: {{ include "context-engine.image" . }} + imagePullPolicy: {{ .Values.image.pullPolicy }} + command: + {{- toYaml .Values.learningRerankerWorker.command | nindent 12 }} + envFrom: + - configMapRef: + name: {{ include "context-engine.fullname" . }}-config + resources: + {{- toYaml .Values.learningRerankerWorker.resources | nindent 12 }} + volumeMounts: + - name: metadata-volume + mountPath: /tmp/rerank_weights + subPath: rerank_weights + - name: metadata-volume + mountPath: /tmp/rerank_events + subPath: rerank_events + volumes: + - name: metadata-volume + persistentVolumeClaim: + claimName: {{ .Values.persistence.codeMetadata.name }} +{{- end }} diff --git a/deploy/helm/context-engine/templates/mcp-indexer-http.yaml b/deploy/helm/context-engine/templates/mcp-indexer-http.yaml new file mode 100644 index 00000000..8920e2fc --- /dev/null +++ b/deploy/helm/context-engine/templates/mcp-indexer-http.yaml @@ -0,0 +1,181 @@ +{{- if .Values.mcpIndexerHttp.enabled }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: mcp-indexer-http + namespace: {{ include "context-engine.namespace" . }} + labels: + {{- include "context-engine.labels" . | nindent 4 }} + component: mcp-indexer-http +spec: + replicas: {{ .Values.mcpIndexerHttp.replicas }} + selector: + matchLabels: + {{- include "context-engine.selectorLabels" . | nindent 6 }} + component: mcp-indexer-http + template: + metadata: + labels: + {{- include "context-engine.labels" . | nindent 8 }} + component: mcp-indexer-http + spec: + serviceAccountName: {{ include "context-engine.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + {{- if .Values.mcpIndexerHttp.topologySpreadConstraints.enabled }} + topologySpreadConstraints: + - maxSkew: {{ .Values.mcpIndexerHttp.topologySpreadConstraints.maxSkew }} + topologyKey: {{ .Values.mcpIndexerHttp.topologySpreadConstraints.topologyKey }} + whenUnsatisfiable: {{ .Values.mcpIndexerHttp.topologySpreadConstraints.whenUnsatisfiable }} + labelSelector: + matchLabels: + {{- include "context-engine.selectorLabels" . | nindent 14 }} + component: mcp-indexer-http + {{- end }} + initContainers: + - name: init-rerank-dirs + image: busybox:1.36 + imagePullPolicy: IfNotPresent + command: + - sh + - -c + - mkdir -p /work/.codebase/rerank_weights /work/.codebase/rerank_events && chmod 777 /work/.codebase/rerank_weights /work/.codebase/rerank_events + volumeMounts: + - name: codebase-volume + mountPath: /work/.codebase + containers: + - name: mcp-indexer-http + image: {{ include "context-engine.image" . }} + imagePullPolicy: {{ .Values.image.pullPolicy }} + command: + {{- toYaml .Values.mcpIndexerHttp.command | nindent 12 }} + ports: + - name: http + containerPort: {{ .Values.mcpIndexerHttp.ports.http }} + protocol: TCP + - name: health + containerPort: {{ .Values.mcpIndexerHttp.ports.health }} + protocol: TCP + env: + - name: QDRANT_URL + valueFrom: + configMapKeyRef: + name: {{ include "context-engine.fullname" . }}-config + key: QDRANT_URL + - name: COLLECTION_NAME + valueFrom: + configMapKeyRef: + name: {{ include "context-engine.fullname" . }}-config + key: COLLECTION_NAME + - name: EMBEDDING_MODEL + valueFrom: + configMapKeyRef: + name: {{ include "context-engine.fullname" . }}-config + key: EMBEDDING_MODEL + - name: HF_HOME + value: /work/models/hf-cache + - name: XDG_CACHE_HOME + value: /work/models/hf-cache + - name: HF_HUB_CACHE + value: /work/models/hf-cache/huggingface + - name: FASTMCP_HOST + valueFrom: + configMapKeyRef: + name: {{ include "context-engine.fullname" . }}-config + key: FASTMCP_HOST + - name: FASTMCP_INDEXER_PORT + value: {{ .Values.mcpIndexerHttp.ports.http | quote }} + - name: FASTMCP_TRANSPORT + valueFrom: + configMapKeyRef: + name: {{ include "context-engine.fullname" . }}-config + key: FASTMCP_HTTP_TRANSPORT + - name: FASTMCP_HEALTH_PORT + value: {{ .Values.mcpIndexerHttp.ports.health | quote }} + envFrom: + - configMapRef: + name: {{ include "context-engine.fullname" . }}-config + {{- with .Values.mcpIndexerHttp.livenessProbe }} + livenessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.mcpIndexerHttp.readinessProbe }} + readinessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + resources: + {{- toYaml .Values.mcpIndexerHttp.resources | nindent 12 }} + volumeMounts: + - name: work-volume + mountPath: /work + - name: codebase-volume + mountPath: /work/.codebase + - name: models-volume + mountPath: /work/models + - name: codebase-volume + mountPath: /tmp/rerank_weights + subPath: rerank_weights + - name: codebase-volume + mountPath: /tmp/rerank_events + subPath: rerank_events + volumes: + - name: work-volume + persistentVolumeClaim: + claimName: {{ .Values.persistence.codeRepos.name }} + - name: codebase-volume + persistentVolumeClaim: + claimName: {{ .Values.persistence.codeMetadata.name }} + - name: models-volume + persistentVolumeClaim: + claimName: {{ .Values.persistence.codeModels.name }} +--- +apiVersion: v1 +kind: Service +metadata: + name: mcp-indexer-http + namespace: {{ include "context-engine.namespace" . }} + labels: + {{- include "context-engine.labels" . | nindent 4 }} + component: mcp-indexer-http +spec: + type: {{ .Values.mcpIndexerHttp.service.type }} + ports: + - name: http + port: {{ .Values.mcpIndexerHttp.service.port }} + targetPort: http + protocol: TCP + - name: health + port: {{ .Values.mcpIndexerHttp.service.healthPort }} + targetPort: health + protocol: TCP + selector: + {{- include "context-engine.selectorLabels" . | nindent 4 }} + component: mcp-indexer-http +{{- if .Values.mcpIndexerHttp.externalService.enabled }} +--- +apiVersion: v1 +kind: Service +metadata: + name: mcp-indexer-http-external + namespace: {{ include "context-engine.namespace" . }} + labels: + {{- include "context-engine.labels" . | nindent 4 }} + component: mcp-indexer-http +spec: + type: {{ .Values.mcpIndexerHttp.externalService.type }} + ports: + - name: http + port: {{ .Values.mcpIndexerHttp.service.port }} + targetPort: http + nodePort: {{ .Values.mcpIndexerHttp.externalService.nodePort }} + protocol: TCP + - name: health + port: {{ .Values.mcpIndexerHttp.service.healthPort }} + targetPort: health + nodePort: {{ .Values.mcpIndexerHttp.externalService.healthNodePort }} + protocol: TCP + selector: + {{- include "context-engine.selectorLabels" . | nindent 4 }} + component: mcp-indexer-http +{{- end }} +{{- end }} diff --git a/deploy/helm/context-engine/templates/mcp-memory-http.yaml b/deploy/helm/context-engine/templates/mcp-memory-http.yaml new file mode 100644 index 00000000..c05f3827 --- /dev/null +++ b/deploy/helm/context-engine/templates/mcp-memory-http.yaml @@ -0,0 +1,178 @@ +{{- if .Values.mcpMemoryHttp.enabled }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: mcp-memory-http + namespace: {{ include "context-engine.namespace" . }} + labels: + {{- include "context-engine.labels" . | nindent 4 }} + component: mcp-memory-http +spec: + replicas: {{ .Values.mcpMemoryHttp.replicas }} + selector: + matchLabels: + {{- include "context-engine.selectorLabels" . | nindent 6 }} + component: mcp-memory-http + template: + metadata: + labels: + {{- include "context-engine.labels" . | nindent 8 }} + component: mcp-memory-http + spec: + serviceAccountName: {{ include "context-engine.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + {{- if .Values.mcpMemoryHttp.topologySpreadConstraints.enabled }} + topologySpreadConstraints: + - maxSkew: {{ .Values.mcpMemoryHttp.topologySpreadConstraints.maxSkew }} + topologyKey: {{ .Values.mcpMemoryHttp.topologySpreadConstraints.topologyKey }} + whenUnsatisfiable: {{ .Values.mcpMemoryHttp.topologySpreadConstraints.whenUnsatisfiable }} + labelSelector: + matchLabels: + {{- include "context-engine.selectorLabels" . | nindent 14 }} + component: mcp-memory-http + {{- end }} + initContainers: + - name: init-rerank-dirs + image: busybox:1.36 + imagePullPolicy: IfNotPresent + command: + - sh + - -c + - mkdir -p /mnt/rerank_weights /mnt/rerank_events && chmod 777 /mnt/rerank_weights /mnt/rerank_events + volumeMounts: + - name: metadata-volume + mountPath: /mnt + containers: + - name: mcp-memory-http + image: {{ include "context-engine.image" . }} + imagePullPolicy: {{ .Values.image.pullPolicy }} + command: + {{- toYaml .Values.mcpMemoryHttp.command | nindent 12 }} + ports: + - name: http + containerPort: {{ .Values.mcpMemoryHttp.ports.http }} + protocol: TCP + - name: health + containerPort: {{ .Values.mcpMemoryHttp.ports.health }} + protocol: TCP + env: + - name: QDRANT_URL + valueFrom: + configMapKeyRef: + name: {{ include "context-engine.fullname" . }}-config + key: QDRANT_URL + - name: COLLECTION_NAME + valueFrom: + configMapKeyRef: + name: {{ include "context-engine.fullname" . }}-config + key: COLLECTION_NAME + - name: EMBEDDING_MODEL + valueFrom: + configMapKeyRef: + name: {{ include "context-engine.fullname" . }}-config + key: EMBEDDING_MODEL + - name: HF_HOME + value: /work/models/hf-cache + - name: XDG_CACHE_HOME + value: /work/models/hf-cache + - name: FASTMCP_HOST + valueFrom: + configMapKeyRef: + name: {{ include "context-engine.fullname" . }}-config + key: FASTMCP_HOST + - name: FASTMCP_PORT + value: {{ .Values.mcpMemoryHttp.ports.http | quote }} + - name: FASTMCP_TRANSPORT + valueFrom: + configMapKeyRef: + name: {{ include "context-engine.fullname" . }}-config + key: FASTMCP_HTTP_TRANSPORT + - name: FASTMCP_HEALTH_PORT + value: {{ .Values.mcpMemoryHttp.ports.health | quote }} + envFrom: + - configMapRef: + name: {{ include "context-engine.fullname" . }}-config + {{- with .Values.mcpMemoryHttp.livenessProbe }} + livenessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.mcpMemoryHttp.readinessProbe }} + readinessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + resources: + {{- toYaml .Values.mcpMemoryHttp.resources | nindent 12 }} + volumeMounts: + - name: work-volume + mountPath: /work + readOnly: true + - name: codebase-volume + mountPath: /work/.codebase + - name: metadata-volume + mountPath: /tmp/rerank_weights + subPath: rerank_weights + - name: metadata-volume + mountPath: /tmp/rerank_events + subPath: rerank_events + volumes: + - name: work-volume + persistentVolumeClaim: + claimName: {{ .Values.persistence.codeRepos.name }} + - name: codebase-volume + persistentVolumeClaim: + claimName: {{ .Values.persistence.codeMetadata.name }} + - name: metadata-volume + persistentVolumeClaim: + claimName: {{ .Values.persistence.codeMetadata.name }} +--- +apiVersion: v1 +kind: Service +metadata: + name: mcp-memory-http + namespace: {{ include "context-engine.namespace" . }} + labels: + {{- include "context-engine.labels" . | nindent 4 }} + component: mcp-memory-http +spec: + type: {{ .Values.mcpMemoryHttp.service.type }} + ports: + - name: http + port: {{ .Values.mcpMemoryHttp.service.port }} + targetPort: http + protocol: TCP + - name: health + port: {{ .Values.mcpMemoryHttp.service.healthPort }} + targetPort: health + protocol: TCP + selector: + {{- include "context-engine.selectorLabels" . | nindent 4 }} + component: mcp-memory-http +{{- if .Values.mcpMemoryHttp.externalService.enabled }} +--- +apiVersion: v1 +kind: Service +metadata: + name: mcp-memory-http-external + namespace: {{ include "context-engine.namespace" . }} + labels: + {{- include "context-engine.labels" . | nindent 4 }} + component: mcp-memory-http +spec: + type: {{ .Values.mcpMemoryHttp.externalService.type }} + ports: + - name: http + port: {{ .Values.mcpMemoryHttp.service.port }} + targetPort: http + nodePort: {{ .Values.mcpMemoryHttp.externalService.nodePort }} + protocol: TCP + - name: health + port: {{ .Values.mcpMemoryHttp.service.healthPort }} + targetPort: health + nodePort: {{ .Values.mcpMemoryHttp.externalService.healthNodePort }} + protocol: TCP + selector: + {{- include "context-engine.selectorLabels" . | nindent 4 }} + component: mcp-memory-http +{{- end }} +{{- end }} diff --git a/deploy/helm/context-engine/templates/namespace.yaml b/deploy/helm/context-engine/templates/namespace.yaml new file mode 100644 index 00000000..8bb748d5 --- /dev/null +++ b/deploy/helm/context-engine/templates/namespace.yaml @@ -0,0 +1,8 @@ +{{- if .Values.namespace.create }} +apiVersion: v1 +kind: Namespace +metadata: + name: {{ include "context-engine.namespace" . }} + labels: + {{- include "context-engine.labels" . | nindent 4 }} +{{- end }} diff --git a/deploy/helm/context-engine/templates/networkpolicy.yaml b/deploy/helm/context-engine/templates/networkpolicy.yaml new file mode 100644 index 00000000..c2db53d5 --- /dev/null +++ b/deploy/helm/context-engine/templates/networkpolicy.yaml @@ -0,0 +1,86 @@ +{{- if .Values.networkPolicy.enabled }} +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: qdrant-network-policy + namespace: {{ include "context-engine.namespace" . }} + labels: + {{- include "context-engine.labels" . | nindent 4 }} + component: qdrant +spec: + podSelector: + matchLabels: + {{- include "context-engine.selectorLabels" . | nindent 6 }} + component: qdrant + policyTypes: + - Ingress + ingress: + - from: + - podSelector: + matchLabels: + {{- include "context-engine.selectorLabels" . | nindent 14 }} + ports: + - protocol: TCP + port: {{ .Values.qdrant.service.httpPort }} + - protocol: TCP + port: {{ .Values.qdrant.service.grpcPort }} + {{- if .Values.networkPolicy.qdrant.allowExternal }} + - from: [] + ports: + - protocol: TCP + port: {{ .Values.qdrant.service.httpPort }} + - protocol: TCP + port: {{ .Values.qdrant.service.grpcPort }} + {{- end }} +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: mcp-services-network-policy + namespace: {{ include "context-engine.namespace" . }} + labels: + {{- include "context-engine.labels" . | nindent 4 }} +spec: + podSelector: + matchLabels: + {{- include "context-engine.selectorLabels" . | nindent 6 }} + policyTypes: + - Ingress + - Egress + ingress: + - from: + - namespaceSelector: {} + podSelector: {} + ports: + - protocol: TCP + port: 8000 + - protocol: TCP + port: 8001 + - protocol: TCP + port: 8002 + - protocol: TCP + port: 8003 + - protocol: TCP + port: 18000 + - protocol: TCP + port: 18001 + egress: + - to: + - podSelector: + matchLabels: + component: qdrant + ports: + - protocol: TCP + port: {{ .Values.qdrant.service.httpPort }} + - protocol: TCP + port: {{ .Values.qdrant.service.grpcPort }} + - to: + - namespaceSelector: {} + ports: + - protocol: TCP + port: 443 + - protocol: TCP + port: 53 + - protocol: UDP + port: 53 +{{- end }} diff --git a/deploy/helm/context-engine/templates/pdb.yaml b/deploy/helm/context-engine/templates/pdb.yaml new file mode 100644 index 00000000..3162050d --- /dev/null +++ b/deploy/helm/context-engine/templates/pdb.yaml @@ -0,0 +1,93 @@ +{{- if .Values.podDisruptionBudget.enabled }} +{{- if .Values.qdrant.enabled }} +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: qdrant-pdb + namespace: {{ include "context-engine.namespace" . }} + labels: + {{- include "context-engine.labels" . | nindent 4 }} + component: qdrant +spec: + {{- if .Values.podDisruptionBudget.qdrant.minAvailable }} + minAvailable: {{ .Values.podDisruptionBudget.qdrant.minAvailable }} + {{- else if .Values.podDisruptionBudget.qdrant.maxUnavailable }} + maxUnavailable: {{ .Values.podDisruptionBudget.qdrant.maxUnavailable }} + {{- else }} + maxUnavailable: 1 + {{- end }} + selector: + matchLabels: + {{- include "context-engine.selectorLabels" . | nindent 6 }} + component: qdrant +{{- end }} +--- +{{- if .Values.mcpIndexerHttp.enabled }} +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: mcp-indexer-http-pdb + namespace: {{ include "context-engine.namespace" . }} + labels: + {{- include "context-engine.labels" . | nindent 4 }} + component: mcp-indexer-http +spec: + {{- if .Values.podDisruptionBudget.mcpIndexerHttp.minAvailable }} + minAvailable: {{ .Values.podDisruptionBudget.mcpIndexerHttp.minAvailable }} + {{- else if .Values.podDisruptionBudget.mcpIndexerHttp.maxUnavailable }} + maxUnavailable: {{ .Values.podDisruptionBudget.mcpIndexerHttp.maxUnavailable }} + {{- else }} + maxUnavailable: 1 + {{- end }} + selector: + matchLabels: + {{- include "context-engine.selectorLabels" . | nindent 6 }} + component: mcp-indexer-http +{{- end }} +--- +{{- if .Values.mcpMemoryHttp.enabled }} +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: mcp-memory-http-pdb + namespace: {{ include "context-engine.namespace" . }} + labels: + {{- include "context-engine.labels" . | nindent 4 }} + component: mcp-memory-http +spec: + {{- if .Values.podDisruptionBudget.mcpMemoryHttp.minAvailable }} + minAvailable: {{ .Values.podDisruptionBudget.mcpMemoryHttp.minAvailable }} + {{- else if .Values.podDisruptionBudget.mcpMemoryHttp.maxUnavailable }} + maxUnavailable: {{ .Values.podDisruptionBudget.mcpMemoryHttp.maxUnavailable }} + {{- else }} + maxUnavailable: 1 + {{- end }} + selector: + matchLabels: + {{- include "context-engine.selectorLabels" . | nindent 6 }} + component: mcp-memory-http +{{- end }} +--- +{{- if .Values.uploadService.enabled }} +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: upload-service-pdb + namespace: {{ include "context-engine.namespace" . }} + labels: + {{- include "context-engine.labels" . | nindent 4 }} + component: upload-service +spec: + {{- if .Values.podDisruptionBudget.uploadService.minAvailable }} + minAvailable: {{ .Values.podDisruptionBudget.uploadService.minAvailable }} + {{- else if .Values.podDisruptionBudget.uploadService.maxUnavailable }} + maxUnavailable: {{ .Values.podDisruptionBudget.uploadService.maxUnavailable }} + {{- else }} + maxUnavailable: 1 + {{- end }} + selector: + matchLabels: + {{- include "context-engine.selectorLabels" . | nindent 6 }} + component: upload-service +{{- end }} +{{- end }} diff --git a/deploy/helm/context-engine/templates/pvc.yaml b/deploy/helm/context-engine/templates/pvc.yaml new file mode 100644 index 00000000..537fe32a --- /dev/null +++ b/deploy/helm/context-engine/templates/pvc.yaml @@ -0,0 +1,71 @@ +{{- if .Values.persistence.codeRepos.enabled }} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ .Values.persistence.codeRepos.name }} + namespace: {{ include "context-engine.namespace" . }} + labels: + {{- include "context-engine.labels" . | nindent 4 }} + component: workspace + type: storage + annotations: + description: "Code repositories volume" +spec: + accessModes: + {{- toYaml .Values.persistence.codeRepos.accessModes | nindent 4 }} + storageClassName: {{ .Values.persistence.codeRepos.storageClassName }} + resources: + requests: + storage: {{ .Values.persistence.codeRepos.size }} + {{- if .Values.persistence.codeRepos.existingVolumeName }} + volumeName: {{ .Values.persistence.codeRepos.existingVolumeName }} + {{- end }} +{{- end }} +--- +{{- if .Values.persistence.codeMetadata.enabled }} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ .Values.persistence.codeMetadata.name }} + namespace: {{ include "context-engine.namespace" . }} + labels: + {{- include "context-engine.labels" . | nindent 4 }} + component: metadata + type: storage + annotations: + description: "Codebase metadata volume (.codebase directory)" +spec: + accessModes: + {{- toYaml .Values.persistence.codeMetadata.accessModes | nindent 4 }} + storageClassName: {{ .Values.persistence.codeMetadata.storageClassName }} + resources: + requests: + storage: {{ .Values.persistence.codeMetadata.size }} + {{- if .Values.persistence.codeMetadata.existingVolumeName }} + volumeName: {{ .Values.persistence.codeMetadata.existingVolumeName }} + {{- end }} +{{- end }} +--- +{{- if .Values.persistence.codeModels.enabled }} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ .Values.persistence.codeModels.name }} + namespace: {{ include "context-engine.namespace" . }} + labels: + {{- include "context-engine.labels" . | nindent 4 }} + component: models + type: storage + annotations: + description: "Code models volume (HuggingFace cache)" +spec: + accessModes: + {{- toYaml .Values.persistence.codeModels.accessModes | nindent 4 }} + storageClassName: {{ .Values.persistence.codeModels.storageClassName }} + resources: + requests: + storage: {{ .Values.persistence.codeModels.size }} + {{- if .Values.persistence.codeModels.existingVolumeName }} + volumeName: {{ .Values.persistence.codeModels.existingVolumeName }} + {{- end }} +{{- end }} diff --git a/deploy/helm/context-engine/templates/qdrant-config.yaml b/deploy/helm/context-engine/templates/qdrant-config.yaml new file mode 100644 index 00000000..2c97735b --- /dev/null +++ b/deploy/helm/context-engine/templates/qdrant-config.yaml @@ -0,0 +1,57 @@ +{{- if and .Values.qdrant.enabled .Values.qdrant.config }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: qdrant-config + namespace: {{ include "context-engine.namespace" . }} + labels: + {{- include "context-engine.labels" . | nindent 4 }} + component: qdrant +data: + config.yaml: | + {{- if .Values.qdrant.config.storage }} + storage: + {{- if .Values.qdrant.config.storage.on_disk }} + on_disk: {{ .Values.qdrant.config.storage.on_disk }} + {{- end }} + {{- if .Values.qdrant.config.storage.performance }} + performance: + {{- toYaml .Values.qdrant.config.storage.performance | nindent 8 }} + {{- end }} + {{- if .Values.qdrant.config.storage.quantization }} + quantization: + {{- if .Values.qdrant.config.storage.quantization.scalar }} + scalar: + {{- toYaml .Values.qdrant.config.storage.quantization.scalar | nindent 10 }} + {{- end }} + {{- if .Values.qdrant.config.storage.quantization.product }} + product: + {{- toYaml .Values.qdrant.config.storage.quantization.product | nindent 10 }} + {{- end }} + {{- end }} + {{- if .Values.qdrant.config.storage.hnsw_index }} + hnsw_index: + {{- toYaml .Values.qdrant.config.storage.hnsw_index | nindent 8 }} + {{- end }} + {{- if .Values.qdrant.config.storage.wal }} + wal: + {{- toYaml .Values.qdrant.config.storage.wal | nindent 8 }} + {{- end }} + {{- end }} + {{- if .Values.qdrant.config.optimizers }} + optimizers: + {{- toYaml .Values.qdrant.config.optimizers | nindent 6 }} + {{- end }} + {{- if .Values.qdrant.config.service }} + service: + {{- toYaml .Values.qdrant.config.service | nindent 6 }} + {{- end }} + {{- if .Values.qdrant.config.cluster }} + cluster: + {{- toYaml .Values.qdrant.config.cluster | nindent 6 }} + {{- end }} + {{- if .Values.qdrant.config.telemetry }} + telemetry: + {{- toYaml .Values.qdrant.config.telemetry | nindent 6 }} + {{- end }} +{{- end }} diff --git a/deploy/helm/context-engine/templates/qdrant.yaml b/deploy/helm/context-engine/templates/qdrant.yaml new file mode 100644 index 00000000..5f913a93 --- /dev/null +++ b/deploy/helm/context-engine/templates/qdrant.yaml @@ -0,0 +1,147 @@ +{{- if .Values.qdrant.enabled }} +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: qdrant + namespace: {{ include "context-engine.namespace" . }} + labels: + {{- include "context-engine.labels" . | nindent 4 }} + component: qdrant +spec: + serviceName: qdrant + replicas: {{ .Values.qdrant.replicas }} + selector: + matchLabels: + {{- include "context-engine.selectorLabels" . | nindent 6 }} + component: qdrant + template: + metadata: + labels: + {{- include "context-engine.labels" . | nindent 8 }} + component: qdrant + spec: + {{- with .Values.qdrant.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.qdrant.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.qdrant.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + containers: + - name: qdrant + image: {{ .Values.qdrant.image.repository }}:{{ .Values.qdrant.image.tag }} + imagePullPolicy: {{ .Values.qdrant.image.pullPolicy }} + ports: + - name: http + containerPort: {{ .Values.qdrant.service.httpPort }} + protocol: TCP + - name: grpc + containerPort: {{ .Values.qdrant.service.grpcPort }} + protocol: TCP + env: + - name: QDRANT__SERVICE__HTTP_PORT + value: {{ .Values.qdrant.service.httpPort | quote }} + - name: QDRANT__SERVICE__GRPC_PORT + value: {{ .Values.qdrant.service.grpcPort | quote }} + {{- if .Values.qdrant.config }} + - name: QDRANT__STORAGE__ON_DISK + value: {{ .Values.qdrant.config.storage.on_disk | default false | quote }} + {{- end }} + {{- with .Values.qdrant.extraEnv }} + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.qdrant.readinessProbe }} + readinessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.qdrant.livenessProbe }} + livenessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + resources: + {{- toYaml .Values.qdrant.resources | nindent 12 }} + volumeMounts: + - name: qdrant-storage + mountPath: /qdrant/storage + {{- if .Values.qdrant.config }} + - name: qdrant-config + mountPath: /qdrant/config/config.yaml + subPath: config.yaml + {{- end }} + {{- if .Values.qdrant.config }} + volumes: + - name: qdrant-config + configMap: + name: qdrant-config + {{- end }} + {{- if .Values.qdrant.persistence.enabled }} + volumeClaimTemplates: + - metadata: + name: qdrant-storage + labels: + {{- include "context-engine.labels" . | nindent 10 }} + component: qdrant + spec: + accessModes: + {{- toYaml .Values.qdrant.persistence.accessModes | nindent 10 }} + storageClassName: {{ .Values.qdrant.persistence.storageClassName }} + resources: + requests: + storage: {{ .Values.qdrant.persistence.size }} + {{- end }} +--- +apiVersion: v1 +kind: Service +metadata: + name: qdrant + namespace: {{ include "context-engine.namespace" . }} + labels: + {{- include "context-engine.labels" . | nindent 4 }} + component: qdrant +spec: + type: {{ .Values.qdrant.service.type }} + ports: + - name: http + port: {{ .Values.qdrant.service.httpPort }} + targetPort: http + protocol: TCP + - name: grpc + port: {{ .Values.qdrant.service.grpcPort }} + targetPort: grpc + protocol: TCP + selector: + {{- include "context-engine.selectorLabels" . | nindent 4 }} + component: qdrant +{{- if .Values.qdrant.externalService.enabled }} +--- +apiVersion: v1 +kind: Service +metadata: + name: qdrant-external + namespace: {{ include "context-engine.namespace" . }} + labels: + {{- include "context-engine.labels" . | nindent 4 }} + component: qdrant +spec: + type: {{ .Values.qdrant.externalService.type }} + ports: + - name: http + port: {{ .Values.qdrant.service.httpPort }} + targetPort: http + nodePort: {{ .Values.qdrant.externalService.httpNodePort }} + protocol: TCP + - name: grpc + port: {{ .Values.qdrant.service.grpcPort }} + targetPort: grpc + nodePort: {{ .Values.qdrant.externalService.grpcNodePort }} + protocol: TCP + selector: + {{- include "context-engine.selectorLabels" . | nindent 4 }} + component: qdrant +{{- end }} +{{- end }} diff --git a/deploy/helm/context-engine/templates/resourcequota.yaml b/deploy/helm/context-engine/templates/resourcequota.yaml new file mode 100644 index 00000000..915d7d7c --- /dev/null +++ b/deploy/helm/context-engine/templates/resourcequota.yaml @@ -0,0 +1,32 @@ +{{- if .Values.resourceQuota.enabled }} +apiVersion: v1 +kind: ResourceQuota +metadata: + name: {{ include "context-engine.fullname" . }}-quota + namespace: {{ include "context-engine.namespace" . }} + labels: + {{- include "context-engine.labels" . | nindent 4 }} +spec: + hard: + {{- if .Values.resourceQuota.requests.cpu }} + requests.cpu: {{ .Values.resourceQuota.requests.cpu | quote }} + {{- end }} + {{- if .Values.resourceQuota.requests.memory }} + requests.memory: {{ .Values.resourceQuota.requests.memory | quote }} + {{- end }} + {{- if .Values.resourceQuota.limits.cpu }} + limits.cpu: {{ .Values.resourceQuota.limits.cpu | quote }} + {{- end }} + {{- if .Values.resourceQuota.limits.memory }} + limits.memory: {{ .Values.resourceQuota.limits.memory | quote }} + {{- end }} + {{- if .Values.resourceQuota.pods }} + pods: {{ .Values.resourceQuota.pods | quote }} + {{- end }} + {{- if .Values.resourceQuota.persistentvolumeclaims }} + persistentvolumeclaims: {{ .Values.resourceQuota.persistentvolumeclaims | quote }} + {{- end }} + {{- if .Values.resourceQuota.storage }} + requests.storage: {{ .Values.resourceQuota.storage | quote }} + {{- end }} +{{- end }} diff --git a/deploy/helm/context-engine/templates/serviceaccount.yaml b/deploy/helm/context-engine/templates/serviceaccount.yaml new file mode 100644 index 00000000..67e45c16 --- /dev/null +++ b/deploy/helm/context-engine/templates/serviceaccount.yaml @@ -0,0 +1,13 @@ +{{- if .Values.serviceAccount.create }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "context-engine.serviceAccountName" . }} + namespace: {{ include "context-engine.namespace" . }} + labels: + {{- include "context-engine.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} diff --git a/deploy/helm/context-engine/templates/upload-service.yaml b/deploy/helm/context-engine/templates/upload-service.yaml new file mode 100644 index 00000000..0daac81c --- /dev/null +++ b/deploy/helm/context-engine/templates/upload-service.yaml @@ -0,0 +1,91 @@ +{{- if .Values.uploadService.enabled }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: upload-service + namespace: {{ include "context-engine.namespace" . }} + labels: + {{- include "context-engine.labels" . | nindent 4 }} + component: upload-service +spec: + replicas: {{ .Values.uploadService.replicas }} + selector: + matchLabels: + {{- include "context-engine.selectorLabels" . | nindent 6 }} + component: upload-service + template: + metadata: + labels: + {{- include "context-engine.labels" . | nindent 8 }} + component: upload-service + spec: + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + {{- if .Values.uploadService.topologySpreadConstraints.enabled }} + topologySpreadConstraints: + - maxSkew: {{ .Values.uploadService.topologySpreadConstraints.maxSkew }} + topologyKey: {{ .Values.uploadService.topologySpreadConstraints.topologyKey }} + whenUnsatisfiable: {{ .Values.uploadService.topologySpreadConstraints.whenUnsatisfiable }} + labelSelector: + matchLabels: + {{- include "context-engine.selectorLabels" . | nindent 14 }} + component: upload-service + {{- end }} + containers: + - name: upload-service + image: {{ include "context-engine.image" . }} + imagePullPolicy: {{ .Values.image.pullPolicy }} + workingDir: {{ .Values.uploadService.workingDir }} + command: + {{- toYaml .Values.uploadService.command | nindent 12 }} + args: + {{- toYaml .Values.uploadService.args | nindent 12 }} + ports: + - name: http + containerPort: {{ .Values.uploadService.port }} + protocol: TCP + env: + {{- range $key, $value := .Values.uploadService.env }} + - name: {{ $key }} + value: {{ $value | quote }} + {{- end }} + envFrom: + - configMapRef: + name: {{ include "context-engine.fullname" . }}-config + resources: + {{- toYaml .Values.uploadService.resources | nindent 12 }} + volumeMounts: + - name: work-volume + mountPath: /work + - name: codebase-volume + mountPath: /work/.codebase + volumes: + - name: work-volume + persistentVolumeClaim: + claimName: {{ .Values.persistence.codeRepos.name }} + - name: codebase-volume + persistentVolumeClaim: + claimName: {{ .Values.persistence.codeMetadata.name }} +--- +apiVersion: v1 +kind: Service +metadata: + name: upload-service + namespace: {{ include "context-engine.namespace" . }} + labels: + {{- include "context-engine.labels" . | nindent 4 }} + component: upload-service +spec: + type: {{ .Values.uploadService.service.type }} + ports: + - name: http + port: {{ .Values.uploadService.service.port }} + targetPort: http + {{- if eq .Values.uploadService.service.type "NodePort" }} + nodePort: {{ .Values.uploadService.service.nodePort }} + {{- end }} + protocol: TCP + selector: + {{- include "context-engine.selectorLabels" . | nindent 4 }} + component: upload-service +{{- end }} diff --git a/deploy/helm/context-engine/templates/watcher.yaml b/deploy/helm/context-engine/templates/watcher.yaml new file mode 100644 index 00000000..3b675d40 --- /dev/null +++ b/deploy/helm/context-engine/templates/watcher.yaml @@ -0,0 +1,171 @@ +{{- if .Values.watcher.enabled }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: watcher + namespace: {{ include "context-engine.namespace" . }} + labels: + {{- include "context-engine.labels" . | nindent 4 }} + component: watcher +spec: + replicas: {{ .Values.watcher.replicas }} + selector: + matchLabels: + {{- include "context-engine.selectorLabels" . | nindent 6 }} + component: watcher + template: + metadata: + labels: + {{- include "context-engine.labels" . | nindent 8 }} + component: watcher + spec: + serviceAccountName: {{ include "context-engine.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + initContainers: + - name: init-dirs + image: busybox:1.36 + imagePullPolicy: IfNotPresent + command: + - sh + - -c + - | + echo "Creating required directories..." + mkdir -p /work/.codebase/rerank_weights /work/.codebase/rerank_events + chmod 777 /work/.codebase/rerank_weights /work/.codebase/rerank_events + echo "Directories created successfully" + volumeMounts: + - name: codebase-volume + mountPath: /work/.codebase + {{- if .Values.watcher.initContainers.waitForQdrant.enabled }} + - name: wait-for-qdrant + image: {{ include "context-engine.image" . }} + imagePullPolicy: {{ .Values.image.pullPolicy }} + command: + - /bin/sh + - -c + - | + echo "Waiting for Qdrant to be ready..." + max_attempts={{ .Values.watcher.initContainers.waitForQdrant.maxAttempts }} + attempt=0 + until curl -sf http://qdrant:{{ .Values.qdrant.service.httpPort }}/readyz; do + attempt=$((attempt + 1)) + if [ $attempt -ge $max_attempts ]; then + echo "ERROR: Qdrant not ready after $max_attempts attempts" + exit 1 + fi + echo "Qdrant not ready (attempt $attempt/$max_attempts), retrying in {{ .Values.watcher.initContainers.waitForQdrant.sleepSeconds }}s..." + sleep {{ .Values.watcher.initContainers.waitForQdrant.sleepSeconds }} + done + echo "Qdrant is ready!" + env: + - name: QDRANT_URL + valueFrom: + configMapKeyRef: + name: {{ include "context-engine.fullname" . }}-config + key: QDRANT_URL + {{- end }} + {{- if .Values.watcher.initContainers.initCollection.enabled }} + - name: init-collection + image: {{ include "context-engine.image" . }} + imagePullPolicy: {{ .Values.image.pullPolicy }} + workingDir: /app + command: + - /bin/sh + - -c + - | + echo "Initializing Qdrant collection..." + cd /app + PYTHONPATH=/app python /app/scripts/create_indexes.py + echo "Collection initialized, warming caches..." + PYTHONPATH=/app python /app/scripts/warm_all_collections.py || echo "Cache warming skipped (optional)" + echo "Running health check..." + PYTHONPATH=/app python /app/scripts/health_check.py || echo "Health check completed" + echo "Initialization complete!" + env: + - name: QDRANT_URL + valueFrom: + configMapKeyRef: + name: {{ include "context-engine.fullname" . }}-config + key: QDRANT_URL + - name: COLLECTION_NAME + valueFrom: + configMapKeyRef: + name: {{ include "context-engine.fullname" . }}-config + key: COLLECTION_NAME + - name: HF_HOME + value: /work/models/hf-cache + - name: XDG_CACHE_HOME + value: /work/models/hf-cache + - name: HF_HUB_CACHE + value: /work/models/hf-cache/huggingface + envFrom: + - configMapRef: + name: {{ include "context-engine.fullname" . }}-config + volumeMounts: + - name: work-volume + mountPath: /work + - name: codebase-volume + mountPath: /work/.codebase + - name: models-volume + mountPath: /work/models + {{- end }} + containers: + - name: watcher + image: {{ include "context-engine.image" . }} + imagePullPolicy: {{ .Values.image.pullPolicy }} + workingDir: {{ .Values.watcher.workingDir }} + command: + {{- toYaml .Values.watcher.command | nindent 12 }} + env: + - name: QDRANT_URL + valueFrom: + configMapKeyRef: + name: {{ include "context-engine.fullname" . }}-config + key: QDRANT_URL + - name: COLLECTION_NAME + valueFrom: + configMapKeyRef: + name: {{ include "context-engine.fullname" . }}-config + key: COLLECTION_NAME + - name: EMBEDDING_MODEL + valueFrom: + configMapKeyRef: + name: {{ include "context-engine.fullname" . }}-config + key: EMBEDDING_MODEL + - name: EMBEDDING_PROVIDER + valueFrom: + configMapKeyRef: + name: {{ include "context-engine.fullname" . }}-config + key: EMBEDDING_PROVIDER + - name: HF_HOME + value: /work/models/hf-cache + - name: XDG_CACHE_HOME + value: /work/models/hf-cache + {{- range $key, $value := .Values.watcher.env }} + - name: {{ $key }} + value: {{ $value | quote }} + {{- end }} + envFrom: + - configMapRef: + name: {{ include "context-engine.fullname" . }}-config + resources: + {{- toYaml .Values.watcher.resources | nindent 12 }} + volumeMounts: + - name: work-volume + mountPath: /work + - name: codebase-volume + mountPath: /work/.codebase + - name: models-volume + mountPath: /work/models + volumes: + - name: work-volume + persistentVolumeClaim: + claimName: {{ .Values.persistence.codeRepos.name }} + - name: codebase-volume + persistentVolumeClaim: + claimName: {{ .Values.persistence.codeMetadata.name }} + - name: models-volume + persistentVolumeClaim: + claimName: {{ .Values.persistence.codeModels.name }} +{{- end }} diff --git a/deploy/helm/context-engine/values-example.yaml b/deploy/helm/context-engine/values-example.yaml new file mode 100644 index 00000000..4b669359 --- /dev/null +++ b/deploy/helm/context-engine/values-example.yaml @@ -0,0 +1,250 @@ +# Example values for Context-Engine Helm chart +# Copy this file and customize for your environment +# +# Usage: +# cp values-example.yaml ../kubernetes/values-mycompany.yaml +# # Edit values-mycompany.yaml with your settings +# helm install ce-mycompany ./deploy/helm/context-engine \ +# -f ./deploy/kubernetes/values-mycompany.yaml \ +# --namespace context-engine --create-namespace + +global: + environment: dev + team: ai + appName: context-engine + +# Override the release name +# fullnameOverride: ce-mycompany + +namespace: + create: true + name: context-engine + +# Image configuration +# For ECR: 123456789.dkr.ecr.us-east-1.amazonaws.com/context-engine +# For Docker Hub: myorg/context-engine +image: + repository: context-engine + pullPolicy: IfNotPresent + tag: "latest" + # pullSecrets: + # - name: ecr-registry-secret + +# Qdrant configuration +qdrant: + enabled: true + replicas: 1 + resources: + requests: + cpu: "1" + memory: 8Gi + limits: + cpu: "4" + memory: 24Gi + persistence: + enabled: true + storageClassName: gp3-sc # Change to your storage class + size: 50Gi + +# MCP Indexer HTTP +mcpIndexerHttp: + enabled: true + replicas: 1 + resources: + requests: + cpu: 500m + memory: 8Gi + limits: + cpu: "2" + memory: 16Gi + autoscaling: + enabled: true + minReplicas: 1 + maxReplicas: 2 + +# MCP Memory HTTP +mcpMemoryHttp: + enabled: true + replicas: 1 + resources: + requests: + cpu: 500m + memory: 1Gi + limits: + cpu: "1500m" + memory: 3Gi + autoscaling: + enabled: true + minReplicas: 1 + maxReplicas: 1 + +# Upload Service +uploadService: + enabled: true + replicas: 1 + resources: + requests: + cpu: 250m + memory: 1Gi + limits: + cpu: "1500m" + memory: 3Gi + autoscaling: + enabled: true + minReplicas: 1 + maxReplicas: 2 + +# Watcher +watcher: + enabled: true + replicas: 2 + resources: + requests: + cpu: 500m + memory: 3Gi + limits: + cpu: "2500m" + memory: 10Gi + +# Learning Reranker Worker (singleton - only 1 can run due to leader election) +learningRerankerWorker: + enabled: true + replicas: 1 + autoscaling: + enabled: true + minReplicas: 1 + maxReplicas: 1 + +# Persistence - shared PVCs +persistence: + codeRepos: + enabled: true + name: code-repos-pvc + storageClassName: efs-sc # Change to your shared storage class (EFS, NFS, etc.) + accessModes: + - ReadWriteMany + size: 50Gi + # existingVolumeName: my-pre-provisioned-pv # Optional: use existing PV + codeMetadata: + enabled: true + name: code-metadata-pvc + storageClassName: efs-sc + accessModes: + - ReadWriteMany + size: 10Gi + codeModels: + enabled: true + name: code-models-pvc + storageClassName: efs-sc + accessModes: + - ReadWriteMany + size: 20Gi + +# Ingress configuration +ingress: + enabled: true + className: nginx # or: alb, traefik, etc. + host: "" # Set to your domain: ce.example.com + annotations: + nginx.ingress.kubernetes.io/ssl-redirect: "false" + nginx.ingress.kubernetes.io/proxy-body-size: "100m" + nginx.ingress.kubernetes.io/proxy-read-timeout: "300" + nginx.ingress.kubernetes.io/rewrite-target: /$2 + # tls: + # - hosts: + # - ce.example.com + # secretName: ce-tls-secret + admin: + enabled: true + +# ConfigMap values +config: + # Collection name - unique per customer/project + collectionName: codebase + + # Embedding configuration + embeddingModel: BAAI/bge-base-en-v1.5 + embeddingProvider: fastembed + + # Indexing settings + indexing: + microChunks: "1" + maxMicroChunksPerFile: "200" + chunkLines: "60" + chunkOverlap: "10" + semanticChunks: "1" + useEnhancedAst: "1" + + # Hybrid search settings + hybrid: + expand: "0" + inProcess: "1" + miniWeight: "1.0" + perPath: "1" + recencyWeight: "0.1" + resultsCache: "128" + resultsCacheEnabled: "1" + snippetDiskRead: "1" + symbolBoost: "0.35" + + # Reranker settings + reranker: + enabled: "1" + model: jinaai/jina-reranker-v2-base-multilingual + timeoutMs: "3000" + topN: "20" + returnM: "20" + + # ReFRAG / Decoder settings + refrag: + mode: "1" + decoder: "1" + decoderMode: prompt + gateFirst: "1" + candidates: "200" + runtime: glm # Options: glm, llama, disabled + + # GLM API settings (if using GLM decoder) + # IMPORTANT: Set apiKey via Kubernetes Secret, not here + glm: + apiBase: "" # e.g., https://api.z.ai/api/coding/paas/v4/ + apiKey: "" # DO NOT commit API keys - use secrets + model: glm-4.7 + modelFast: glm-4.5 + + # Graph settings + graph: + ragEnabled: "1" + importOnIndex: "1" + contextRadius: "2" + + # Symbol graph + symbolGraph: + enabled: "1" + + # Multi-repo mode + multiRepoMode: "1" + repoAutoFilter: "1" + + # Memory settings + memory: + sseEnabled: "true" + autodetect: "1" + + # Authentication (disabled by default) + auth: + enabled: "0" + sharedToken: "" # Set via secret + adminToken: "" # Set via secret + + # Additional environment variables + extraEnv: {} + # CUSTOM_VAR: "value" + +# Secrets configuration (optional) +# secrets: +# create: true +# name: context-engine-secrets +# data: +# GLM_API_KEY: "your-api-key-here" +# AUTH_SHARED_TOKEN: "your-shared-token" diff --git a/deploy/helm/context-engine/values.yaml b/deploy/helm/context-engine/values.yaml new file mode 100644 index 00000000..eca1c087 --- /dev/null +++ b/deploy/helm/context-engine/values.yaml @@ -0,0 +1,639 @@ +# Default values for context-engine +# This is a YAML-formatted file. + +# -- Global settings +global: + # -- Environment name (dev, staging, prod) + environment: dev + # -- Team label for resources + team: ai + # -- Application name used in labels + appName: context-engine + +# -- Namespace configuration +namespace: + # -- Create namespace + create: true + # -- Namespace name + name: context-engine + +# -- Image configuration +image: + # -- Image repository + repository: context-engine + # -- Image pull policy + pullPolicy: IfNotPresent + # -- Image tag (defaults to Chart appVersion) + tag: "" + # -- Image pull secrets + pullSecrets: [] + +# -- Service account configuration +serviceAccount: + # -- Create service account + create: true + # -- Service account name + name: context-engine + # -- Annotations for service account + annotations: {} + +# -- Common labels for all resources +commonLabels: {} + +# -- Common annotations for all resources +commonAnnotations: {} + +# -- Pod security context (applied to all pods) +podSecurityContext: + runAsUser: 1000 + runAsGroup: 1000 + fsGroup: 1000 + +# -- Container security context +containerSecurityContext: {} + +# ----------------------------------------------------------------------------- +# Qdrant Configuration +# ----------------------------------------------------------------------------- +qdrant: + # -- Enable Qdrant + enabled: true + # -- Image configuration + image: + repository: qdrant/qdrant + tag: latest + pullPolicy: Always + # -- Number of replicas (StatefulSet) + replicas: 1 + # -- Service configuration + service: + # -- Service type for internal access + type: ClusterIP + # -- HTTP port + httpPort: 6333 + # -- gRPC port + grpcPort: 6334 + # -- External service configuration + externalService: + # -- Enable external service + enabled: true + # -- Service type for external access + type: NodePort + # -- NodePort for HTTP + httpNodePort: 30333 + # -- NodePort for gRPC + grpcNodePort: 30334 + # -- Resource requests and limits + resources: + requests: + cpu: "1" + memory: 8Gi + limits: + cpu: "4" + memory: 24Gi + # -- Persistence configuration + persistence: + # -- Enable persistence + enabled: true + # -- Storage class name + storageClassName: gp3-sc + # -- Storage size + size: 50Gi + # -- Access modes + accessModes: + - ReadWriteOnce + # -- Readiness probe configuration + readinessProbe: + httpGet: + path: /readyz + port: http + initialDelaySeconds: 5 + periodSeconds: 5 + # -- Liveness probe configuration + livenessProbe: + httpGet: + path: /readyz + port: http + initialDelaySeconds: 30 + periodSeconds: 30 + failureThreshold: 3 + # -- Qdrant storage and optimizer configuration + config: + storage: + on_disk: false + performance: + max_search_threads: 0 + quantization: + scalar: + type: int8 + always_ram: false + hnsw_index: + on_disk: false + optimizers: + memmap_threshold_kb: 20000 + indexing_threshold_kb: 20000 + flush_interval_sec: 5 + max_optimization_threads: 0 + service: + max_request_size_mb: 32 + enable_tls: false + telemetry: + disabled: false + # -- Extra environment variables + extraEnv: [] + # -- Node selector + nodeSelector: {} + # -- Tolerations + tolerations: [] + # -- Affinity rules + affinity: {} + +# ----------------------------------------------------------------------------- +# Pod Disruption Budget Configuration +# ----------------------------------------------------------------------------- +podDisruptionBudget: + enabled: false + qdrant: + minAvailable: 1 + mcpIndexerHttp: + maxUnavailable: 1 + mcpMemoryHttp: + maxUnavailable: 1 + uploadService: + maxUnavailable: 1 + +# ----------------------------------------------------------------------------- +# Network Policy Configuration +# ----------------------------------------------------------------------------- +networkPolicy: + enabled: false + qdrant: + allowExternal: false + +# ----------------------------------------------------------------------------- +# MCP Indexer HTTP Configuration +# ----------------------------------------------------------------------------- +mcpIndexerHttp: + # -- Enable MCP Indexer HTTP + enabled: true + # -- Number of replicas + replicas: 1 + # -- Command to run + command: + - python + - /app/scripts/mcp_indexer_server.py + # -- Container ports + ports: + http: 8001 + health: 18001 + # -- Service configuration + service: + type: ClusterIP + port: 8003 + healthPort: 18003 + # -- External service configuration + externalService: + enabled: true + type: NodePort + nodePort: 30806 + healthNodePort: 30807 + # -- Resource requests and limits + resources: + requests: + cpu: 500m + memory: 8Gi + limits: + cpu: "2" + memory: 16Gi + # -- Liveness probe + livenessProbe: + httpGet: + path: /readyz + port: health + initialDelaySeconds: 120 + periodSeconds: 30 + timeoutSeconds: 10 + failureThreshold: 6 + # -- Readiness probe + readinessProbe: + httpGet: + path: /readyz + port: health + initialDelaySeconds: 60 + periodSeconds: 15 + timeoutSeconds: 10 + failureThreshold: 6 + # -- HPA configuration + autoscaling: + enabled: true + minReplicas: 1 + maxReplicas: 2 + targetCPUUtilizationPercentage: 70 + targetMemoryUtilizationPercentage: 80 + # -- Topology spread constraints + topologySpreadConstraints: + enabled: true + maxSkew: 1 + topologyKey: topology.kubernetes.io/zone + whenUnsatisfiable: ScheduleAnyway + +# ----------------------------------------------------------------------------- +# MCP Memory HTTP Configuration +# ----------------------------------------------------------------------------- +mcpMemoryHttp: + # -- Enable MCP Memory HTTP + enabled: true + # -- Number of replicas + replicas: 1 + # -- Command to run + command: + - python + - /app/scripts/mcp_memory_server.py + # -- Container ports + ports: + http: 8000 + health: 18000 + # -- Service configuration + service: + type: ClusterIP + port: 8002 + healthPort: 18002 + # -- External service configuration + externalService: + enabled: true + type: NodePort + nodePort: 30804 + healthNodePort: 30805 + # -- Resource requests and limits + resources: + requests: + cpu: 500m + memory: 1Gi + limits: + cpu: "1500m" + memory: 3Gi + # -- Liveness probe + livenessProbe: + httpGet: + path: /readyz + port: health + initialDelaySeconds: 30 + periodSeconds: 10 + # -- Readiness probe + readinessProbe: + httpGet: + path: /readyz + port: health + initialDelaySeconds: 10 + periodSeconds: 5 + # -- HPA configuration + autoscaling: + enabled: true + minReplicas: 1 + maxReplicas: 1 + targetCPUUtilizationPercentage: 70 + targetMemoryUtilizationPercentage: 80 + # -- Topology spread constraints + topologySpreadConstraints: + enabled: true + maxSkew: 1 + topologyKey: topology.kubernetes.io/zone + whenUnsatisfiable: ScheduleAnyway + +# ----------------------------------------------------------------------------- +# Upload Service Configuration +# ----------------------------------------------------------------------------- +uploadService: + # -- Enable Upload Service + enabled: true + # -- Number of replicas + replicas: 1 + # -- Command to run + command: + - uvicorn + # -- Arguments + args: + - scripts.upload_service:app + - --host + - "0.0.0.0" + - --port + - "8002" + - --workers + - "2" + # -- Working directory + workingDir: /app + # -- Container port + port: 8002 + # -- Service configuration + service: + type: NodePort + port: 8002 + nodePort: 30810 + # -- Resource requests and limits + resources: + requests: + cpu: 250m + memory: 1Gi + limits: + cpu: "1500m" + memory: 3Gi + # -- Environment variables + env: + UPLOAD_SERVICE_HOST: "0.0.0.0" + UPLOAD_SERVICE_PORT: "8002" + WORK_DIR: /work + MAX_BUNDLE_SIZE_MB: "100" + UPLOAD_TIMEOUT_SECS: "300" + # -- HPA configuration + autoscaling: + enabled: true + minReplicas: 1 + maxReplicas: 2 + targetCPUUtilizationPercentage: 70 + targetMemoryUtilizationPercentage: 80 + # -- Topology spread constraints + topologySpreadConstraints: + enabled: true + maxSkew: 1 + topologyKey: topology.kubernetes.io/zone + whenUnsatisfiable: ScheduleAnyway + +# ----------------------------------------------------------------------------- +# Watcher Configuration +# ----------------------------------------------------------------------------- +watcher: + # -- Enable Watcher + enabled: true + # -- Number of replicas + replicas: 2 + # -- Command to run + command: + - python + - /app/scripts/watch_index.py + # -- Working directory + workingDir: /work + # -- Resource requests and limits + resources: + requests: + cpu: 500m + memory: 3Gi + limits: + cpu: "2500m" + memory: 10Gi + # -- Environment variables (in addition to configmap) + env: + WATCH_ROOT: /work + WATCH_DEBOUNCE_SECS: "2.0" + WATCH_USE_POLLING: "1" + MULTI_REPO_MODE: "0" + # -- Init containers configuration + initContainers: + # -- Wait for Qdrant + waitForQdrant: + enabled: true + maxAttempts: 60 + sleepSeconds: 5 + # -- Initialize collection + initCollection: + enabled: true + +# ----------------------------------------------------------------------------- +# Learning Reranker Worker Configuration +# ----------------------------------------------------------------------------- +learningRerankerWorker: + # -- Enable Learning Reranker Worker + enabled: true + # -- Number of replicas (singleton worker with leader election - only 1 can run) + replicas: 1 + # -- Command to run + command: + - python + - /app/scripts/learning_reranker_worker.py + - --daemon + # -- Resource requests and limits + resources: + requests: + cpu: 250m + memory: 512Mi + limits: + cpu: "1" + memory: 2Gi + # -- HPA configuration (capped at 1 - singleton worker) + autoscaling: + enabled: true + minReplicas: 1 + maxReplicas: 1 + targetCPUUtilizationPercentage: 70 + targetMemoryUtilizationPercentage: 80 + # -- Topology spread constraints + topologySpreadConstraints: + enabled: true + maxSkew: 1 + topologyKey: topology.kubernetes.io/zone + whenUnsatisfiable: ScheduleAnyway + +# ----------------------------------------------------------------------------- +# Persistence Configuration +# ----------------------------------------------------------------------------- +persistence: + # -- Code repositories PVC + codeRepos: + # -- Enable PVC + enabled: true + # -- PVC name + name: code-repos-pvc + # -- Storage class + storageClassName: efs-sc + # -- Access modes + accessModes: + - ReadWriteMany + # -- Storage size + size: 50Gi + # -- Existing PV name (optional, for pre-provisioned volumes) + existingVolumeName: "" + # -- Code metadata PVC (.codebase directory) + codeMetadata: + enabled: true + name: code-metadata-pvc + storageClassName: efs-sc + accessModes: + - ReadWriteMany + size: 10Gi + existingVolumeName: "" + # -- Code models PVC (HuggingFace cache) + codeModels: + enabled: true + name: code-models-pvc + storageClassName: efs-sc + accessModes: + - ReadWriteMany + size: 20Gi + existingVolumeName: "" + +# ----------------------------------------------------------------------------- +# Ingress Configuration +# ----------------------------------------------------------------------------- +ingress: + # -- Enable ingress + enabled: true + # -- Ingress class name + className: nginx + # -- Annotations + annotations: + nginx.ingress.kubernetes.io/ssl-redirect: "false" + nginx.ingress.kubernetes.io/proxy-body-size: "100m" + nginx.ingress.kubernetes.io/proxy-read-timeout: "300" + nginx.ingress.kubernetes.io/rewrite-target: /$2 + # -- Hostname + host: "" + # -- TLS configuration + tls: [] + # -- Path configurations + paths: + indexer: + path: /indexer(/|$)(.*) + pathType: ImplementationSpecific + servicePort: 8003 + memory: + path: /memory(/|$)(.*) + pathType: ImplementationSpecific + servicePort: 8002 + upload: + path: /upload(/|$)(.*) + pathType: ImplementationSpecific + servicePort: 8002 + qdrant: + path: /qdrant(/|$)(.*) + pathType: ImplementationSpecific + servicePort: 6333 + # -- Additional ingress for admin routes + admin: + enabled: true + annotations: + nginx.ingress.kubernetes.io/ssl-redirect: "false" + path: /admin + pathType: Prefix + +# ----------------------------------------------------------------------------- +# ConfigMap Configuration +# ----------------------------------------------------------------------------- +config: + # -- Collection name + collectionName: codebase + # -- Qdrant URL (auto-generated if not set) + qdrantUrl: "" + # -- Embedding model + embeddingModel: BAAI/bge-base-en-v1.5 + # -- Embedding provider + embeddingProvider: fastembed + + # -- FastMCP settings + fastmcp: + host: "0.0.0.0" + port: "8000" + indexerPort: "8001" + httpPort: "8002" + indexerHttpPort: "8003" + httpTransport: http + + # -- Indexing settings + indexing: + microChunks: "1" + maxMicroChunksPerFile: "200" + chunkLines: "60" + chunkOverlap: "10" + semanticChunks: "1" + useEnhancedAst: "1" + + # -- Hybrid search settings + hybrid: + expand: "0" + inProcess: "1" + miniWeight: "1.0" + perPath: "1" + recencyWeight: "0.1" + resultsCache: "128" + resultsCacheEnabled: "1" + snippetDiskRead: "1" + symbolBoost: "0.35" + + # -- Reranker settings + reranker: + enabled: "1" + model: jinaai/jina-reranker-v2-base-multilingual + timeoutMs: "3000" + topN: "20" + returnM: "20" + + # -- ReFRAG settings + refrag: + mode: "1" + decoder: "1" + decoderMode: prompt + gateFirst: "1" + candidates: "200" + runtime: glm + + # -- GLM API settings (for decoder) + glm: + apiBase: "" + apiKey: "" + model: glm-4.7 + modelFast: glm-4.5 + + # -- Graph settings + graph: + ragEnabled: "1" + importOnIndex: "1" + contextRadius: "2" + + # -- Symbol graph + symbolGraph: + enabled: "1" + + # -- Multi-repo mode + multiRepoMode: "1" + + # -- Repo auto filter + repoAutoFilter: "1" + + # -- Memory settings + memory: + sseEnabled: "true" + mcpUrl: "" + autodetect: "1" + + # -- Authentication settings + auth: + enabled: "0" + sharedToken: "" + adminToken: "" + + # -- Additional environment variables (will be merged into configmap) + extraEnv: {} + +# ----------------------------------------------------------------------------- +# Secrets Configuration +# ----------------------------------------------------------------------------- +secrets: + # -- Create secrets + create: false + # -- Secret name + name: context-engine-secrets + # -- Secret data (base64 encoded in actual secret) + data: {} + +# ----------------------------------------------------------------------------- +# Resource Quota Configuration +# ----------------------------------------------------------------------------- +resourceQuota: + enabled: false + requests: + cpu: "" + memory: "" + limits: + cpu: "" + memory: "" + pods: "" + persistentvolumeclaims: "" + storage: "" From c3375023357ea6b84fdc3c74071453cb3fddb8db Mon Sep 17 00:00:00 2001 From: John Donalson Date: Sun, 25 Jan 2026 22:43:29 -0500 Subject: [PATCH 2/3] Parameterize health ports in Helm values and templates Replaces hardcoded FASTMCP HTTP health ports in the ConfigMap template with values from values.yaml, adding new parameters for httpHealthPort and indexerHttpHealthPort. Updates network policy to clarify external access and egress rules, and adds comments for DNS and HTTPS egress. --- deploy/helm/context-engine/templates/configmap.yaml | 4 ++-- .../helm/context-engine/templates/networkpolicy.yaml | 11 +++++++---- deploy/helm/context-engine/values.yaml | 2 ++ 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/deploy/helm/context-engine/templates/configmap.yaml b/deploy/helm/context-engine/templates/configmap.yaml index 2a0f1f85..7134afcf 100644 --- a/deploy/helm/context-engine/templates/configmap.yaml +++ b/deploy/helm/context-engine/templates/configmap.yaml @@ -19,8 +19,8 @@ data: FASTMCP_HTTP_PORT: {{ .Values.config.fastmcp.httpPort | quote }} FASTMCP_INDEXER_HTTP_PORT: {{ .Values.config.fastmcp.indexerHttpPort | quote }} FASTMCP_HTTP_TRANSPORT: {{ .Values.config.fastmcp.httpTransport | quote }} - FASTMCP_HTTP_HEALTH_PORT: "18002" - FASTMCP_INDEXER_HTTP_HEALTH_PORT: "18003" + FASTMCP_HTTP_HEALTH_PORT: {{ .Values.config.fastmcp.httpHealthPort | quote }} + FASTMCP_INDEXER_HTTP_HEALTH_PORT: {{ .Values.config.fastmcp.indexerHttpHealthPort | quote }} INDEX_MICRO_CHUNKS: {{ .Values.config.indexing.microChunks | quote }} MAX_MICRO_CHUNKS_PER_FILE: {{ .Values.config.indexing.maxMicroChunksPerFile | quote }} diff --git a/deploy/helm/context-engine/templates/networkpolicy.yaml b/deploy/helm/context-engine/templates/networkpolicy.yaml index c2db53d5..7eaf1059 100644 --- a/deploy/helm/context-engine/templates/networkpolicy.yaml +++ b/deploy/helm/context-engine/templates/networkpolicy.yaml @@ -25,8 +25,8 @@ spec: - protocol: TCP port: {{ .Values.qdrant.service.grpcPort }} {{- if .Values.networkPolicy.qdrant.allowExternal }} - - from: [] - ports: + # Allow external access to Qdrant (any source) + - ports: - protocol: TCP port: {{ .Values.qdrant.service.httpPort }} - protocol: TCP @@ -74,13 +74,16 @@ spec: port: {{ .Values.qdrant.service.httpPort }} - protocol: TCP port: {{ .Values.qdrant.service.grpcPort }} + # Allow DNS resolution (kube-dns) - to: - namespaceSelector: {} ports: - - protocol: TCP - port: 443 - protocol: TCP port: 53 - protocol: UDP port: 53 + # Allow external HTTPS egress (for model downloads, external APIs) + - ports: + - protocol: TCP + port: 443 {{- end }} diff --git a/deploy/helm/context-engine/values.yaml b/deploy/helm/context-engine/values.yaml index bdfc11be..4a150cfd 100644 --- a/deploy/helm/context-engine/values.yaml +++ b/deploy/helm/context-engine/values.yaml @@ -474,6 +474,8 @@ config: indexerPort: "8001" httpPort: "8002" indexerHttpPort: "8003" + httpHealthPort: "18002" + indexerHttpHealthPort: "18003" httpTransport: http # -- Indexing settings From 796127dd2c1aedde41a62dd612709b5ddcd7dacb Mon Sep 17 00:00:00 2001 From: John Donalson Date: Sun, 25 Jan 2026 22:57:50 -0500 Subject: [PATCH 3/3] Add Helm template for Kubernetes Secret Introduces a Helm template to create a Kubernetes Secret resource for the context-engine chart. The template conditionally creates the secret based on values and supports dynamic data population. --- deploy/helm/context-engine/templates/secret.yaml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 deploy/helm/context-engine/templates/secret.yaml diff --git a/deploy/helm/context-engine/templates/secret.yaml b/deploy/helm/context-engine/templates/secret.yaml new file mode 100644 index 00000000..a23774d9 --- /dev/null +++ b/deploy/helm/context-engine/templates/secret.yaml @@ -0,0 +1,15 @@ +{{- if .Values.secrets.create }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ .Values.secrets.name }} + namespace: {{ include "context-engine.namespace" . }} + labels: + {{- include "context-engine.labels" . | nindent 4 }} +type: Opaque +data: + {{- range $key, $value := .Values.secrets.data }} + {{ $key }}: {{ $value | b64enc | quote }} + {{- end }} +{{- end }} +