From ddbd8ad7376adb0290256ca8f28934d368919a69 Mon Sep 17 00:00:00 2001 From: jocelyneholdbrook Date: Thu, 16 Apr 2026 14:52:47 +0100 Subject: [PATCH 1/8] feat(cogstack-cohorter): Add helm chart for cogstack-cohorter --- .../workflows/kubernetes-charts-build.yaml | 5 +- .../cogstack-cohorter-helm/.helmignore | 23 +++ helm-charts/cogstack-cohorter-helm/Chart.lock | 9 + helm-charts/cogstack-cohorter-helm/Chart.yaml | 27 +++ helm-charts/cogstack-cohorter-helm/README.md | 160 +++++++++++++++ .../cogstack-cohorter-helm/README.md.gotmpl | 136 +++++++++++++ .../templates/NOTES.txt | 46 +++++ .../templates/_helpers.tpl | 103 ++++++++++ .../cogstack-cohorter-helm/templates/hpa.yaml | 32 +++ .../templates/ingress.yaml | 61 ++++++ .../templates/networkpolicy.yaml | 71 +++++++ .../templates/nl2dsl-deployment.yaml | 79 ++++++++ .../templates/nl2dsl-service.yaml | 17 ++ .../templates/serviceaccount.yaml | 13 ++ .../templates/webapp-deployment.yaml | 91 +++++++++ .../templates/webapp-service.yaml | 17 ++ .../cogstack-cohorter-helm/values.yaml | 183 ++++++++++++++++++ 17 files changed, 1072 insertions(+), 1 deletion(-) create mode 100644 helm-charts/cogstack-cohorter-helm/.helmignore create mode 100644 helm-charts/cogstack-cohorter-helm/Chart.lock create mode 100644 helm-charts/cogstack-cohorter-helm/Chart.yaml create mode 100644 helm-charts/cogstack-cohorter-helm/README.md create mode 100644 helm-charts/cogstack-cohorter-helm/README.md.gotmpl create mode 100644 helm-charts/cogstack-cohorter-helm/templates/NOTES.txt create mode 100644 helm-charts/cogstack-cohorter-helm/templates/_helpers.tpl create mode 100644 helm-charts/cogstack-cohorter-helm/templates/hpa.yaml create mode 100644 helm-charts/cogstack-cohorter-helm/templates/ingress.yaml create mode 100644 helm-charts/cogstack-cohorter-helm/templates/networkpolicy.yaml create mode 100644 helm-charts/cogstack-cohorter-helm/templates/nl2dsl-deployment.yaml create mode 100644 helm-charts/cogstack-cohorter-helm/templates/nl2dsl-service.yaml create mode 100644 helm-charts/cogstack-cohorter-helm/templates/serviceaccount.yaml create mode 100644 helm-charts/cogstack-cohorter-helm/templates/webapp-deployment.yaml create mode 100644 helm-charts/cogstack-cohorter-helm/templates/webapp-service.yaml create mode 100644 helm-charts/cogstack-cohorter-helm/values.yaml diff --git a/.github/workflows/kubernetes-charts-build.yaml b/.github/workflows/kubernetes-charts-build.yaml index 7fe36ca..5519c12 100644 --- a/.github/workflows/kubernetes-charts-build.yaml +++ b/.github/workflows/kubernetes-charts-build.yaml @@ -30,7 +30,8 @@ jobs: - name: Set up Helm uses: azure/setup-helm@v4.3.1 - - uses: actions/setup-python@v6.0.0 + - name: Set up Python + uses: actions/setup-python@v6.0.0 with: python-version: "3.x" check-latest: true @@ -131,6 +132,7 @@ jobs: helm package ./medcat-service-helm --version ${{ steps.version.outputs.chart_version }} helm package ./medcat-trainer-helm --version ${{ steps.version.outputs.chart_version }} --dependency-update helm package ./cogstack-ce-helm --version ${{ steps.version.outputs.chart_version }} --dependency-update + helm package ./cogstack-cohorter-helm --version ${{ steps.version.outputs.chart_version }} --dependency-update helm package ./cogstack-observability-helm --version ${{ steps.version.outputs.chart_version }} --dependency-update - name: Helm OCI login to Docker Hub @@ -141,6 +143,7 @@ jobs: helm push ./medcat-service-helm-${{ steps.version.outputs.chart_version }}.tgz oci://registry-1.docker.io/cogstacksystems helm push ./medcat-trainer-helm-${{ steps.version.outputs.chart_version }}.tgz oci://registry-1.docker.io/cogstacksystems helm push ./cogstack-ce-helm-${{ steps.version.outputs.chart_version }}.tgz oci://registry-1.docker.io/cogstacksystems + helm push ./cogstack-cohorter-helm-${{ steps.version.outputs.chart_version }}.tgz oci://registry-1.docker.io/cogstacksystems helm push ./cogstack-observability-helm-${{ steps.version.outputs.chart_version }}.tgz oci://registry-1.docker.io/cogstacksystems - name: Release diff --git a/helm-charts/cogstack-cohorter-helm/.helmignore b/helm-charts/cogstack-cohorter-helm/.helmignore new file mode 100644 index 0000000..0e8a0eb --- /dev/null +++ b/helm-charts/cogstack-cohorter-helm/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/helm-charts/cogstack-cohorter-helm/Chart.lock b/helm-charts/cogstack-cohorter-helm/Chart.lock new file mode 100644 index 0000000..1f36690 --- /dev/null +++ b/helm-charts/cogstack-cohorter-helm/Chart.lock @@ -0,0 +1,9 @@ +dependencies: +- name: medcat-service-helm + repository: oci://registry-1.docker.io/cogstacksystems + version: 0.0.1 +- name: ollama + repository: https://otwld.github.io/ollama-helm/ + version: 1.54.0 +digest: sha256:c19deffe7da9495af6f74b6a6bb26157bf7faf6abac6be8fc94638deff3bc6d3 +generated: "2026-04-16T14:51:41.6563526+01:00" diff --git a/helm-charts/cogstack-cohorter-helm/Chart.yaml b/helm-charts/cogstack-cohorter-helm/Chart.yaml new file mode 100644 index 0000000..d11cf5d --- /dev/null +++ b/helm-charts/cogstack-cohorter-helm/Chart.yaml @@ -0,0 +1,27 @@ +apiVersion: v2 +name: cogstack-cohorter-helm +description: CogStack Cohorter — cohort identification powered by Ollama and MedCAT +type: application +version: 0.0.1 +appVersion: "latest" + +maintainers: + - name: jocelyneholdbrook + email: jocelyne@cogstack.org + +icon: "https://avatars.githubusercontent.com/u/28688163" + +dependencies: + # MedCAT annotation service + - name: medcat-service-helm + version: "0.0.1" + repository: "oci://registry-1.docker.io/cogstacksystems" + alias: medcat + condition: medcat.enabled + + # Ollama LLM serving — https://github.com/otwld/ollama-helm + - name: ollama + version: ">=0.1.0" + repository: "https://otwld.github.io/ollama-helm/" + alias: ollama + condition: ollama.enabled diff --git a/helm-charts/cogstack-cohorter-helm/README.md b/helm-charts/cogstack-cohorter-helm/README.md new file mode 100644 index 0000000..92231e9 --- /dev/null +++ b/helm-charts/cogstack-cohorter-helm/README.md @@ -0,0 +1,160 @@ +# CogStack Cohorter Helm Chart + +CogStack Cohorter — cohort identification powered by Ollama and MedCAT + +## Architecture + +| Component | Image | Description | +|-----------|-------|-------------| +| **WebApp** | `cogstacksystems/cogstack-cohorter-webapp` | React + Node.js frontend and API | +| **NL2DSL** | `cogstacksystems/cogstack-cohorter-nl2dsl` | Natural language → cohort DSL compiler | +| **MedCAT** | `cogstacksystems/medcat-service` | Clinical NER and concept normalisation (subchart) | +| **Ollama** | `ollama/ollama` | LLM serving backend (subchart) | + +MedCAT and Ollama are deployed as **subcharts**: +- MedCAT: [`cogstacksystems/medcat-service-helm`](https://hub.docker.com/r/cogstacksystems/medcat-service-helm) (OCI) +- Ollama: [`otwld/ollama`](https://github.com/otwld/ollama-helm) + +## Prerequisites + +- Kubernetes 1.21+ +- Helm 3.10+ +- A storage class that supports `ReadWriteOnce` PVCs +- Sufficient node resources for the Ollama model (the default `gpt-oss:20b` requires ~14 GB of memory/VRAM) + +## Installation + +From Docker Hub OCI (published chart): + +```bash +helm install cogstack-cohorter oci://registry-1.docker.io/cogstacksystems/cogstack-cohorter-helm +``` + +## Configuration + +All configurable values are in [`values.yaml`](./values.yaml). Key sections: + +### Ollama + +```yaml +ollama: + enabled: true + ollama: + models: + - gpt-oss:20b # pulled automatically on first startup + persistentVolume: + enabled: true + size: 10Gi +``` + +Models are pulled automatically by the otwld subchart's built-in init container. Change `ollama.ollama.models` to use a different model — make sure `nl2dsl.env.OLLAMA_MODEL` matches. + +### MedCAT + +```yaml +medcat: + enabled: true + env: + APP_MEDCAT_MODEL_PACK: "/cat/models/examples/example-medcat-v2-model-pack.zip" +``` + +To use a custom model pack, provide a download URL: + +```yaml +medcat: + model: + downloadUrl: "https://your-host/medcat_model_pack.zip" + name: "medcat_model_pack.zip" +``` + +### WebApp data volume + +The WebApp requires a SNOMED data directory mounted at `/usr/src/app/server/data`. A PVC is provisioned automatically: + +```yaml +webapp: + persistence: + enabled: true + size: 5Gi +``` + +Populate the PVC with either: +- `snomed_terms_data.tar.gz` — auto-extracted by the entrypoint on first startup, or +- Pre-extracted files: `snomed_terms.json`, `cui_pt2ch.json`, and patient data files + +To generate synthetic patient data on first startup (demo mode): + +```yaml +webapp: + env: + RANDOM_DATA: "true" +``` + +### Ingress + +```yaml +ingress: + enabled: true + className: nginx + hosts: + - host: cohorter.example.com + paths: + - path: / + pathType: ImplementationSpecific + tls: + - secretName: cohorter-tls + hosts: + - cohorter.example.com +``` + +### Autoscaling (webapp only) + +```yaml +autoscaling: + enabled: true + minReplicas: 1 + maxReplicas: 3 + targetCPUUtilizationPercentage: 80 +``` + +## Uninstallation + +```bash +helm uninstall cogstack-cohorter +``` + +## Support + +For issues and questions, please visit the [CogStack GitHub repository](https://github.com/CogStack/cogstack-platform). + +## Requirements + +| Repository | Name | Version | +|------------|------|---------| +| https://otwld.github.io/ollama-helm/ | ollama(ollama) | >=0.1.0 | +| oci://registry-1.docker.io/cogstacksystems | medcat(medcat-service-helm) | 0.0.1 | + +## Values + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| autoscaling | object | `{"enabled":false,"maxReplicas":3,"minReplicas":1,"targetCPUUtilizationPercentage":80}` | ------------------------------------------------------------------------- | +| fullnameOverride | string | `""` | | +| global.imagePullSecrets | list | `[]` | | +| ingress | object | `{"annotations":{},"className":"","enabled":false,"hosts":[{"host":"cogstack-cohort.local","paths":[{"path":"/","pathType":"ImplementationSpecific"}]}],"tls":[]}` | ------------------------------------------------------------------------- | +| medcat | object | `{"enabled":true,"env":{"APP_ENABLE_METRICS":"true","APP_MEDCAT_MODEL_PACK":"/cat/models/examples/example-medcat-v2-model-pack.zip"},"image":{"tag":"latest"},"resources":{},"service":{"port":5000}}` | ------------------------------------------------------------------------- | +| nameOverride | string | `""` | | +| nl2dsl | object | `{"affinity":{},"enabled":true,"env":{"ALLOW_ORIGINS":"*","OLLAMA_MODEL":"gpt-oss:20b"},"image":{"pullPolicy":"IfNotPresent","repository":"cogstacksystems/cogstack-cohorter-nl2dsl","tag":"latest"},"livenessProbe":{"httpGet":{"path":"/","port":"http"},"initialDelaySeconds":30,"periodSeconds":10},"nodeSelector":{},"readinessProbe":{"httpGet":{"path":"/","port":"http"},"initialDelaySeconds":10,"periodSeconds":5},"replicaCount":1,"resources":{},"service":{"port":3002,"type":"ClusterIP"},"tolerations":[]}` | ------------------------------------------------------------------------- | +| ollama | object | `{"enabled":true,"ollama":{"models":["gpt-oss:20b"]},"persistentVolume":{"enabled":true,"size":"10Gi","storageClass":""},"resources":{},"service":{"port":11434,"type":"ClusterIP"}}` | ------------------------------------------------------------------------- | +| podAnnotations | object | `{}` | | +| podLabels | object | `{}` | | +| podSecurityContext | object | `{}` | | +| securityContext | object | `{}` | | +| serviceAccount.annotations | object | `{}` | | +| serviceAccount.automount | bool | `true` | | +| serviceAccount.create | bool | `true` | | +| serviceAccount.name | string | `""` | | +| webapp | object | `{"affinity":{},"enabled":true,"env":{"RANDOM_DATA":"false"},"image":{"pullPolicy":"IfNotPresent","repository":"cogstacksystems/cogstack-cohorter-webapp","tag":"latest"},"livenessProbe":{"httpGet":{"path":"/","port":"http"},"initialDelaySeconds":60,"periodSeconds":15},"nodeSelector":{},"persistence":{"accessMode":"ReadWriteOnce","enabled":true,"existingClaim":"","size":"5Gi","storageClass":""},"readinessProbe":{"httpGet":{"path":"/","port":"http"},"initialDelaySeconds":30,"periodSeconds":10},"replicaCount":1,"resources":{},"service":{"port":3000,"type":"ClusterIP"},"tolerations":[]}` | ------------------------------------------------------------------------- | + +---------------------------------------------- +Autogenerated from chart metadata using [helm-docs v1.14.2](https://github.com/norwoodj/helm-docs/releases/v1.14.2) diff --git a/helm-charts/cogstack-cohorter-helm/README.md.gotmpl b/helm-charts/cogstack-cohorter-helm/README.md.gotmpl new file mode 100644 index 0000000..8bfc0c3 --- /dev/null +++ b/helm-charts/cogstack-cohorter-helm/README.md.gotmpl @@ -0,0 +1,136 @@ +# CogStack Cohorter Helm Chart + +{{ template "chart.description" . }} + +{{ template "chart.homepageLine" . }} + +## Architecture + +| Component | Image | Description | +|-----------|-------|-------------| +| **WebApp** | `cogstacksystems/cogstack-cohorter-webapp` | React + Node.js frontend and API | +| **NL2DSL** | `cogstacksystems/cogstack-cohorter-nl2dsl` | Natural language → cohort DSL compiler | +| **MedCAT** | `cogstacksystems/medcat-service` | Clinical NER and concept normalisation (subchart) | +| **Ollama** | `ollama/ollama` | LLM serving backend (subchart) | + +MedCAT and Ollama are deployed as **subcharts**: +- MedCAT: [`cogstacksystems/medcat-service-helm`](https://hub.docker.com/r/cogstacksystems/medcat-service-helm) (OCI) +- Ollama: [`otwld/ollama`](https://github.com/otwld/ollama-helm) + +## Prerequisites + +- Kubernetes 1.21+ +- Helm 3.10+ +- A storage class that supports `ReadWriteOnce` PVCs +- Sufficient node resources for the Ollama model (the default `gpt-oss:20b` requires ~14 GB of memory/VRAM) + +## Installation + +From Docker Hub OCI (published chart): + +```bash +helm install cogstack-cohorter oci://registry-1.docker.io/cogstacksystems/cogstack-cohorter-helm +``` + +## Configuration + +All configurable values are in [`values.yaml`](./values.yaml). Key sections: + +### Ollama + +```yaml +ollama: + enabled: true + ollama: + models: + - gpt-oss:20b # pulled automatically on first startup + persistentVolume: + enabled: true + size: 10Gi +``` + +Models are pulled automatically by the otwld subchart's built-in init container. Change `ollama.ollama.models` to use a different model — make sure `nl2dsl.env.OLLAMA_MODEL` matches. + +### MedCAT + +```yaml +medcat: + enabled: true + env: + APP_MEDCAT_MODEL_PACK: "/cat/models/examples/example-medcat-v2-model-pack.zip" +``` + +To use a custom model pack, provide a download URL: + +```yaml +medcat: + model: + downloadUrl: "https://your-host/medcat_model_pack.zip" + name: "medcat_model_pack.zip" +``` + +### WebApp data volume + +The WebApp requires a SNOMED data directory mounted at `/usr/src/app/server/data`. A PVC is provisioned automatically: + +```yaml +webapp: + persistence: + enabled: true + size: 5Gi +``` + +Populate the PVC with either: +- `snomed_terms_data.tar.gz` — auto-extracted by the entrypoint on first startup, or +- Pre-extracted files: `snomed_terms.json`, `cui_pt2ch.json`, and patient data files + +To generate synthetic patient data on first startup (demo mode): + +```yaml +webapp: + env: + RANDOM_DATA: "true" +``` + +### Ingress + +```yaml +ingress: + enabled: true + className: nginx + hosts: + - host: cohorter.example.com + paths: + - path: / + pathType: ImplementationSpecific + tls: + - secretName: cohorter-tls + hosts: + - cohorter.example.com +``` + +### Autoscaling (webapp only) + +```yaml +autoscaling: + enabled: true + minReplicas: 1 + maxReplicas: 3 + targetCPUUtilizationPercentage: 80 +``` + +## Uninstallation + +```bash +helm uninstall cogstack-cohorter +``` + +## Support + +For issues and questions, please visit the [CogStack GitHub repository](https://github.com/CogStack/cogstack-platform). + +{{ template "chart.requirementsSection" . }} + +{{ template "chart.valuesSection" . }} + +{{ template "helm-docs.versionFooter" . }} diff --git a/helm-charts/cogstack-cohorter-helm/templates/NOTES.txt b/helm-charts/cogstack-cohorter-helm/templates/NOTES.txt new file mode 100644 index 0000000..d083b44 --- /dev/null +++ b/helm-charts/cogstack-cohorter-helm/templates/NOTES.txt @@ -0,0 +1,46 @@ +CogStack Cohort has been deployed. + +Components: +{{- if .Values.webapp.enabled }} + - WebApp: {{ include "cogstack-cohort.fullname" . }}-webapp (port {{ .Values.webapp.service.port }}) +{{- end }} +{{- if .Values.nl2dsl.enabled }} + - NL2DSL: {{ include "cogstack-cohort.fullname" . }}-nl2dsl (port {{ .Values.nl2dsl.service.port }}) +{{- end }} +{{- if .Values.medcat.enabled }} + - MedCAT: {{ include "cogstack-cohort.medcatServiceName" . }} (port {{ .Values.medcat.service.port }}) [subchart: medcat-service-helm] +{{- end }} +{{- if .Values.ollama.enabled }} + - Ollama: {{ include "cogstack-cohort.ollamaServiceName" . }} (port {{ .Values.ollama.service.port }}) [subchart: otwld/ollama] + Models pulled on startup: + {{- range .Values.ollama.ollama.models }} + - {{ . }} + {{- end }} +{{- end }} + +WebApp data volume: +{{- if .Values.webapp.persistence.enabled }} + A PersistentVolumeClaim is provisioned for the SNOMED data directory. + Populate it with snomed_terms_data.tar.gz (auto-extracted on startup) + or pre-extracted files (snomed_terms.json, cui_pt2ch.json, etc.). +{{- else }} + WARNING: persistence is disabled — data will be lost on pod restart. + Enable webapp.persistence for production use. +{{- end }} + +Access the WebApp: +{{- if .Values.ingress.enabled }} +{{- range .Values.ingress.hosts }} + http{{ if $.Values.ingress.tls }}s{{ end }}://{{ .host }} +{{- end }} +{{- else if contains "NodePort" .Values.webapp.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "cogstack-cohort.fullname" . }}-webapp) + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT +{{- else if contains "LoadBalancer" .Values.webapp.service.type }} + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "cogstack-cohort.fullname" . }}-webapp --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") + echo http://$SERVICE_IP:{{ .Values.webapp.service.port }} +{{- else }} + kubectl port-forward --namespace {{ .Release.Namespace }} svc/{{ include "cogstack-cohort.fullname" . }}-webapp {{ .Values.webapp.service.port }}:{{ .Values.webapp.service.port }} + Then open: http://localhost:{{ .Values.webapp.service.port }} +{{- end }} diff --git a/helm-charts/cogstack-cohorter-helm/templates/_helpers.tpl b/helm-charts/cogstack-cohorter-helm/templates/_helpers.tpl new file mode 100644 index 0000000..e74a623 --- /dev/null +++ b/helm-charts/cogstack-cohorter-helm/templates/_helpers.tpl @@ -0,0 +1,103 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "cogstack-cohort.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +*/}} +{{- define "cogstack-cohort.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 }} + +{{/* +Chart name and version label. +*/}} +{{- define "cogstack-cohort.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels. +*/}} +{{- define "cogstack-cohort.labels" -}} +helm.sh/chart: {{ include "cogstack-cohort.chart" . }} +{{ include "cogstack-cohort.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +app.kubernetes.io/part-of: cogstack +{{- end }} + +{{/* +Selector labels. +*/}} +{{- define "cogstack-cohort.selectorLabels" -}} +app.kubernetes.io/name: {{ include "cogstack-cohort.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Service account name. +*/}} +{{- define "cogstack-cohort.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "cogstack-cohort.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} + +{{/* +NL2DSL component labels / selector labels. +*/}} +{{- define "cogstack-cohort.nl2dsl.labels" -}} +{{ include "cogstack-cohort.labels" . }} +app.kubernetes.io/component: nl2dsl +{{- end }} + +{{- define "cogstack-cohort.nl2dsl.selectorLabels" -}} +{{ include "cogstack-cohort.selectorLabels" . }} +app.kubernetes.io/component: nl2dsl +{{- end }} + +{{/* +WebApp component labels / selector labels. +*/}} +{{- define "cogstack-cohort.webapp.labels" -}} +{{ include "cogstack-cohort.labels" . }} +app.kubernetes.io/component: webapp +{{- end }} + +{{- define "cogstack-cohort.webapp.selectorLabels" -}} +{{ include "cogstack-cohort.selectorLabels" . }} +app.kubernetes.io/component: webapp +{{- end }} + +{{/* +Fully-qualified service name for the ollama subchart. +The otwld/ollama chart names its service -ollama. +*/}} +{{- define "cogstack-cohort.ollamaServiceName" -}} +{{- printf "%s-ollama" .Release.Name }} +{{- end }} + +{{/* +Fully-qualified service name for the medcat subchart. +The medcat-service-helm chart names its service -medcat. +*/}} +{{- define "cogstack-cohort.medcatServiceName" -}} +{{- printf "%s-medcat" .Release.Name }} +{{- end }} diff --git a/helm-charts/cogstack-cohorter-helm/templates/hpa.yaml b/helm-charts/cogstack-cohorter-helm/templates/hpa.yaml new file mode 100644 index 0000000..6e5192b --- /dev/null +++ b/helm-charts/cogstack-cohorter-helm/templates/hpa.yaml @@ -0,0 +1,32 @@ +{{- if .Values.autoscaling.enabled }} +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "cogstack-cohort.fullname" . }}-webapp + labels: + {{- include "cogstack-cohort.webapp.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ include "cogstack-cohort.fullname" . }}-webapp + minReplicas: {{ .Values.autoscaling.minReplicas }} + maxReplicas: {{ .Values.autoscaling.maxReplicas }} + metrics: + {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} + {{- end }} + {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} + {{- end }} +{{- end }} diff --git a/helm-charts/cogstack-cohorter-helm/templates/ingress.yaml b/helm-charts/cogstack-cohorter-helm/templates/ingress.yaml new file mode 100644 index 0000000..88f77e1 --- /dev/null +++ b/helm-charts/cogstack-cohorter-helm/templates/ingress.yaml @@ -0,0 +1,61 @@ +{{- if .Values.ingress.enabled -}} +{{- $fullName := include "cogstack-cohort.fullname" . -}} +{{- $svcPort := .Values.webapp.service.port -}} +{{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} + {{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }} + {{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}} + {{- end }} +{{- end }} +{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: networking.k8s.io/v1 +{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: networking.k8s.io/v1beta1 +{{- else -}} +apiVersion: extensions/v1beta1 +{{- end }} +kind: Ingress +metadata: + name: {{ $fullName }} + labels: + {{- include "cogstack-cohort.labels" . | nindent 4 }} + {{- with .Values.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} + ingressClassName: {{ .Values.ingress.className }} + {{- end }} + {{- if .Values.ingress.tls }} + tls: + {{- range .Values.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . | quote }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} + {{- end }} + rules: + {{- range .Values.ingress.hosts }} + - host: {{ .host | quote }} + http: + paths: + {{- range .paths }} + - path: {{ .path }} + {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }} + pathType: {{ .pathType }} + {{- end }} + backend: + {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} + service: + name: {{ $fullName }}-webapp + port: + number: {{ $svcPort }} + {{- else }} + serviceName: {{ $fullName }}-webapp + servicePort: {{ $svcPort }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} diff --git a/helm-charts/cogstack-cohorter-helm/templates/networkpolicy.yaml b/helm-charts/cogstack-cohorter-helm/templates/networkpolicy.yaml new file mode 100644 index 0000000..424005f --- /dev/null +++ b/helm-charts/cogstack-cohorter-helm/templates/networkpolicy.yaml @@ -0,0 +1,71 @@ +{{- if .Values.networkPolicy.enabled }} +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: {{ include "cogstack-cohort.fullname" . }} + labels: + {{- include "cogstack-cohort.labels" . | nindent 4 }} +spec: + podSelector: + matchLabels: + {{- include "cogstack-cohort.selectorLabels" . | nindent 6 }} + policyTypes: + - Ingress + {{- if .Values.networkPolicy.egress.enabled }} + - Egress + {{- end }} + ingress: + # Allow traffic to webapp from anywhere + - to: + - podSelector: + matchLabels: + {{- include "cogstack-cohort.webapp.selectorLabels" . | nindent 12 }} + ports: + - protocol: TCP + port: {{ .Values.webapp.service.port }} + # Allow traffic to medcat from nl2dsl and webapp + - to: + - podSelector: + matchLabels: + {{- include "cogstack-cohort.medcat.selectorLabels" . | nindent 12 }} + from: + - podSelector: + matchLabels: + {{- include "cogstack-cohort.nl2dsl.selectorLabels" . | nindent 12 }} + - podSelector: + matchLabels: + {{- include "cogstack-cohort.webapp.selectorLabels" . | nindent 12 }} + ports: + - protocol: TCP + port: {{ .Values.medcat.service.port }} + # Allow traffic to ollama from nl2dsl + - to: + - podSelector: + matchLabels: + {{- include "cogstack-cohort.ollama.selectorLabels" . | nindent 12 }} + from: + - podSelector: + matchLabels: + {{- include "cogstack-cohort.nl2dsl.selectorLabels" . | nindent 12 }} + ports: + - protocol: TCP + port: {{ .Values.ollama.service.port }} + # Allow traffic to nl2dsl from webapp + - to: + - podSelector: + matchLabels: + {{- include "cogstack-cohort.nl2dsl.selectorLabels" . | nindent 12 }} + from: + - podSelector: + matchLabels: + {{- include "cogstack-cohort.webapp.selectorLabels" . | nindent 12 }} + ports: + - protocol: TCP + port: {{ .Values.nl2dsl.service.port }} + {{- if .Values.networkPolicy.egress.enabled }} + egress: + {{- with .Values.networkPolicy.egress.egressRules }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- end }} +{{- end }} diff --git a/helm-charts/cogstack-cohorter-helm/templates/nl2dsl-deployment.yaml b/helm-charts/cogstack-cohorter-helm/templates/nl2dsl-deployment.yaml new file mode 100644 index 0000000..5ffcf03 --- /dev/null +++ b/helm-charts/cogstack-cohorter-helm/templates/nl2dsl-deployment.yaml @@ -0,0 +1,79 @@ +{{- if .Values.nl2dsl.enabled }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "cogstack-cohort.fullname" . }}-nl2dsl + labels: + {{- include "cogstack-cohort.nl2dsl.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.nl2dsl.replicaCount }} + selector: + matchLabels: + {{- include "cogstack-cohort.nl2dsl.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "cogstack-cohort.nl2dsl.labels" . | nindent 8 }} + {{- with .Values.podLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- with .Values.global.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "cogstack-cohort.serviceAccountName" . }} + {{- with .Values.podSecurityContext }} + securityContext: + {{- toYaml . | nindent 8 }} + {{- end }} + containers: + - name: nl2dsl + {{- with .Values.securityContext }} + securityContext: + {{- toYaml . | nindent 12 }} + {{- end }} + image: "{{ .Values.nl2dsl.image.repository }}:{{ .Values.nl2dsl.image.tag }}" + imagePullPolicy: {{ .Values.nl2dsl.image.pullPolicy }} + ports: + - name: http + containerPort: {{ .Values.nl2dsl.service.port }} + protocol: TCP + env: + - name: OLLAMA_URL + value: "http://{{ include "cogstack-cohort.ollamaServiceName" . }}:{{ .Values.ollama.service.port }}/api/generate" + - name: MEDCAT_URL + value: "http://{{ include "cogstack-cohort.medcatServiceName" . }}:{{ .Values.medcat.service.port }}" + {{- range $key, $value := .Values.nl2dsl.env }} + - name: {{ $key | quote }} + value: {{ $value | quote }} + {{- end }} + {{- with .Values.nl2dsl.livenessProbe }} + livenessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.nl2dsl.readinessProbe }} + readinessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.nl2dsl.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.nl2dsl.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.nl2dsl.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.nl2dsl.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} +{{- end }} diff --git a/helm-charts/cogstack-cohorter-helm/templates/nl2dsl-service.yaml b/helm-charts/cogstack-cohorter-helm/templates/nl2dsl-service.yaml new file mode 100644 index 0000000..9cb54ef --- /dev/null +++ b/helm-charts/cogstack-cohorter-helm/templates/nl2dsl-service.yaml @@ -0,0 +1,17 @@ +{{- if .Values.nl2dsl.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "cogstack-cohort.fullname" . }}-nl2dsl + labels: + {{- include "cogstack-cohort.nl2dsl.labels" . | nindent 4 }} +spec: + type: {{ .Values.nl2dsl.service.type }} + ports: + - port: {{ .Values.nl2dsl.service.port }} + targetPort: http + protocol: TCP + name: http + selector: + {{- include "cogstack-cohort.nl2dsl.selectorLabels" . | nindent 4 }} +{{- end }} diff --git a/helm-charts/cogstack-cohorter-helm/templates/serviceaccount.yaml b/helm-charts/cogstack-cohorter-helm/templates/serviceaccount.yaml new file mode 100644 index 0000000..757d2fd --- /dev/null +++ b/helm-charts/cogstack-cohorter-helm/templates/serviceaccount.yaml @@ -0,0 +1,13 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "cogstack-cohort.serviceAccountName" . }} + labels: + {{- include "cogstack-cohort.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +automountServiceAccountToken: {{ .Values.serviceAccount.automount }} +{{- end }} diff --git a/helm-charts/cogstack-cohorter-helm/templates/webapp-deployment.yaml b/helm-charts/cogstack-cohorter-helm/templates/webapp-deployment.yaml new file mode 100644 index 0000000..b608c85 --- /dev/null +++ b/helm-charts/cogstack-cohorter-helm/templates/webapp-deployment.yaml @@ -0,0 +1,91 @@ +{{- if .Values.webapp.enabled }} +{{- $fullName := include "cogstack-cohort.fullname" . }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ $fullName }}-webapp + labels: + {{- include "cogstack-cohort.webapp.labels" . | nindent 4 }} +spec: + {{- if not .Values.autoscaling.enabled }} + replicas: {{ .Values.webapp.replicaCount }} + {{- end }} + selector: + matchLabels: + {{- include "cogstack-cohort.webapp.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "cogstack-cohort.webapp.labels" . | nindent 8 }} + {{- with .Values.podLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- with .Values.global.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "cogstack-cohort.serviceAccountName" . }} + {{- with .Values.podSecurityContext }} + securityContext: + {{- toYaml . | nindent 8 }} + {{- end }} + containers: + - name: webapp + {{- with .Values.securityContext }} + securityContext: + {{- toYaml . | nindent 12 }} + {{- end }} + image: "{{ .Values.webapp.image.repository }}:{{ .Values.webapp.image.tag }}" + imagePullPolicy: {{ .Values.webapp.image.pullPolicy }} + ports: + - name: http + containerPort: {{ .Values.webapp.service.port }} + protocol: TCP + env: + - name: NL2DSL_SERVER + value: "http://{{ $fullName }}-nl2dsl:{{ .Values.nl2dsl.service.port }}/api/compile" + {{- range $key, $value := .Values.webapp.env }} + - name: {{ $key | quote }} + value: {{ $value | quote }} + {{- end }} + volumeMounts: + - name: data + mountPath: /usr/src/app/server/data + {{- with .Values.webapp.livenessProbe }} + livenessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.webapp.readinessProbe }} + readinessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.webapp.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + volumes: + - name: data + {{- if .Values.webapp.persistence.enabled }} + persistentVolumeClaim: + claimName: {{ .Values.webapp.persistence.existingClaim | default (printf "%s-webapp-data" $fullName) }} + {{- else }} + emptyDir: {} + {{- end }} + {{- with .Values.webapp.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.webapp.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.webapp.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} +{{- end }} diff --git a/helm-charts/cogstack-cohorter-helm/templates/webapp-service.yaml b/helm-charts/cogstack-cohorter-helm/templates/webapp-service.yaml new file mode 100644 index 0000000..27720cc --- /dev/null +++ b/helm-charts/cogstack-cohorter-helm/templates/webapp-service.yaml @@ -0,0 +1,17 @@ +{{- if .Values.webapp.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "cogstack-cohort.fullname" . }}-webapp + labels: + {{- include "cogstack-cohort.webapp.labels" . | nindent 4 }} +spec: + type: {{ .Values.webapp.service.type }} + ports: + - port: {{ .Values.webapp.service.port }} + targetPort: http + protocol: TCP + name: http + selector: + {{- include "cogstack-cohort.webapp.selectorLabels" . | nindent 4 }} +{{- end }} diff --git a/helm-charts/cogstack-cohorter-helm/values.yaml b/helm-charts/cogstack-cohorter-helm/values.yaml new file mode 100644 index 0000000..bbe52b9 --- /dev/null +++ b/helm-charts/cogstack-cohorter-helm/values.yaml @@ -0,0 +1,183 @@ +# Default values for cogstack-cohort. + +global: + imagePullSecrets: [] + +nameOverride: "" +fullnameOverride: "" + +serviceAccount: + create: true + automount: true + annotations: {} + name: "" + +podAnnotations: {} +podLabels: {} +podSecurityContext: {} +securityContext: {} + +# --------------------------------------------------------------------------- +# Ollama — subchart: otwld/ollama +# All keys are passed through to the subchart. The model is pulled +# automatically by the subchart's built-in init container. +# Ref: https://github.com/otwld/ollama-helm +# --------------------------------------------------------------------------- +ollama: + enabled: true + + ollama: + # Model to pull on startup. Change to any model available on ollama.com. + models: + - gpt-oss:20b + + service: + type: ClusterIP + port: 11434 + + persistentVolume: + enabled: true + size: 10Gi + storageClass: "" + + resources: {} + +# --------------------------------------------------------------------------- +# MedCAT — subchart: cogstacksystems/medcat-service-helm +# All keys are passed through to the subchart. +# --------------------------------------------------------------------------- +medcat: + enabled: true + + image: + tag: "latest" + + env: + APP_MEDCAT_MODEL_PACK: "/cat/models/examples/example-medcat-v2-model-pack.zip" + APP_ENABLE_METRICS: "true" + + # To download a custom model pack at startup, set: + # model: + # downloadUrl: "https://your-host/medcat_model_pack.zip" + # name: "medcat_model_pack.zip" + + service: + port: 5000 + + resources: {} + +# --------------------------------------------------------------------------- +# NL2DSL (CohortCompiler) — custom templates +# --------------------------------------------------------------------------- +nl2dsl: + enabled: true + + image: + repository: cogstacksystems/cogstack-cohorter-nl2dsl + pullPolicy: IfNotPresent + tag: "latest" + + replicaCount: 1 + + service: + type: ClusterIP + port: 3002 + + env: + OLLAMA_MODEL: "gpt-oss:20b" + ALLOW_ORIGINS: "*" + # OLLAMA_URL and MEDCAT_URL are derived from subchart service names in the template + + resources: {} + + livenessProbe: + httpGet: + path: / + port: http + initialDelaySeconds: 30 + periodSeconds: 10 + + readinessProbe: + httpGet: + path: / + port: http + initialDelaySeconds: 10 + periodSeconds: 5 + + nodeSelector: {} + tolerations: [] + affinity: {} + +# --------------------------------------------------------------------------- +# WebApp — custom templates +# --------------------------------------------------------------------------- +webapp: + enabled: true + + image: + repository: cogstacksystems/cogstack-cohorter-webapp + pullPolicy: IfNotPresent + tag: "latest" + + replicaCount: 1 + + service: + type: ClusterIP + port: 3000 + + env: + RANDOM_DATA: "false" + # NL2DSL_SERVER is derived from subchart service names in the template + + # PVC for the data directory (snomed_terms.json, cui_pt2ch.json, etc.) + # Supply snomed_terms_data.tar.gz or pre-extracted files — the entrypoint + # auto-extracts the archive if the JSON files are not yet present. + persistence: + enabled: true + storageClass: "" + accessMode: ReadWriteOnce + size: 5Gi + existingClaim: "" + + resources: {} + + livenessProbe: + httpGet: + path: / + port: http + initialDelaySeconds: 60 + periodSeconds: 15 + + readinessProbe: + httpGet: + path: / + port: http + initialDelaySeconds: 30 + periodSeconds: 10 + + nodeSelector: {} + tolerations: [] + affinity: {} + +# --------------------------------------------------------------------------- +# Ingress (routes to webapp) +# --------------------------------------------------------------------------- +ingress: + enabled: false + className: "" + annotations: {} + hosts: + - host: cogstack-cohort.local + paths: + - path: / + pathType: ImplementationSpecific + tls: [] + +# --------------------------------------------------------------------------- +# Autoscaling (webapp only) +# --------------------------------------------------------------------------- +autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 3 + targetCPUUtilizationPercentage: 80 From 782b53a8b97d50ef629eef3095f365f890e1451d Mon Sep 17 00:00:00 2001 From: jocelyneholdbrook Date: Thu, 16 Apr 2026 15:26:19 +0100 Subject: [PATCH 2/8] feat(cogstack-cohorter): Update helm chart files --- helm-charts/cogstack-cohorter-helm/Chart.yaml | 2 +- helm-charts/cogstack-cohorter-helm/README.md | 82 +++++++++++++++++-- .../cogstack-cohorter-helm/README.md.gotmpl | 5 +- .../templates/networkpolicy.yaml | 71 ---------------- .../templates/webapp-pvc.yaml | 17 ++++ .../cogstack-cohorter-helm/values.yaml | 29 +------ 6 files changed, 99 insertions(+), 107 deletions(-) delete mode 100644 helm-charts/cogstack-cohorter-helm/templates/networkpolicy.yaml create mode 100644 helm-charts/cogstack-cohorter-helm/templates/webapp-pvc.yaml diff --git a/helm-charts/cogstack-cohorter-helm/Chart.yaml b/helm-charts/cogstack-cohorter-helm/Chart.yaml index d11cf5d..5b52a5e 100644 --- a/helm-charts/cogstack-cohorter-helm/Chart.yaml +++ b/helm-charts/cogstack-cohorter-helm/Chart.yaml @@ -1,5 +1,5 @@ apiVersion: v2 -name: cogstack-cohorter-helm +name: cogstack-cohorter description: CogStack Cohorter — cohort identification powered by Ollama and MedCAT type: application version: 0.0.1 diff --git a/helm-charts/cogstack-cohorter-helm/README.md b/helm-charts/cogstack-cohorter-helm/README.md index 92231e9..3c1280f 100644 --- a/helm-charts/cogstack-cohorter-helm/README.md +++ b/helm-charts/cogstack-cohorter-helm/README.md @@ -41,13 +41,14 @@ ollama: enabled: true ollama: models: - - gpt-oss:20b # pulled automatically on first startup + pull: + - gpt-oss:20b # pulled automatically on first startup persistentVolume: enabled: true size: 10Gi ``` -Models are pulled automatically by the otwld subchart's built-in init container. Change `ollama.ollama.models` to use a different model — make sure `nl2dsl.env.OLLAMA_MODEL` matches. +Models are pulled automatically by the otwld subchart's built-in init container. Change `ollama.ollama.models.pull` to use a different model — make sure `nl2dsl.env.OLLAMA_MODEL` matches. ### MedCAT @@ -138,14 +139,55 @@ For issues and questions, please visit the [CogStack GitHub repository](https:// | Key | Type | Default | Description | |-----|------|---------|-------------| -| autoscaling | object | `{"enabled":false,"maxReplicas":3,"minReplicas":1,"targetCPUUtilizationPercentage":80}` | ------------------------------------------------------------------------- | +| autoscaling.enabled | bool | `false` | | +| autoscaling.maxReplicas | int | `3` | | +| autoscaling.minReplicas | int | `1` | | +| autoscaling.targetCPUUtilizationPercentage | int | `80` | | | fullnameOverride | string | `""` | | | global.imagePullSecrets | list | `[]` | | -| ingress | object | `{"annotations":{},"className":"","enabled":false,"hosts":[{"host":"cogstack-cohort.local","paths":[{"path":"/","pathType":"ImplementationSpecific"}]}],"tls":[]}` | ------------------------------------------------------------------------- | -| medcat | object | `{"enabled":true,"env":{"APP_ENABLE_METRICS":"true","APP_MEDCAT_MODEL_PACK":"/cat/models/examples/example-medcat-v2-model-pack.zip"},"image":{"tag":"latest"},"resources":{},"service":{"port":5000}}` | ------------------------------------------------------------------------- | +| ingress.annotations | object | `{}` | | +| ingress.className | string | `""` | | +| ingress.enabled | bool | `false` | | +| ingress.hosts[0].host | string | `"cogstack-cohort.local"` | | +| ingress.hosts[0].paths[0].path | string | `"/"` | | +| ingress.hosts[0].paths[0].pathType | string | `"ImplementationSpecific"` | | +| ingress.tls | list | `[]` | | +| medcat.enabled | bool | `true` | | +| medcat.env.APP_ENABLE_METRICS | string | `"true"` | | +| medcat.env.APP_MEDCAT_MODEL_PACK | string | `"/cat/models/examples/example-medcat-v2-model-pack.zip"` | | +| medcat.image.tag | string | `"latest"` | | +| medcat.resources | object | `{}` | | +| medcat.service.port | int | `5000` | | | nameOverride | string | `""` | | -| nl2dsl | object | `{"affinity":{},"enabled":true,"env":{"ALLOW_ORIGINS":"*","OLLAMA_MODEL":"gpt-oss:20b"},"image":{"pullPolicy":"IfNotPresent","repository":"cogstacksystems/cogstack-cohorter-nl2dsl","tag":"latest"},"livenessProbe":{"httpGet":{"path":"/","port":"http"},"initialDelaySeconds":30,"periodSeconds":10},"nodeSelector":{},"readinessProbe":{"httpGet":{"path":"/","port":"http"},"initialDelaySeconds":10,"periodSeconds":5},"replicaCount":1,"resources":{},"service":{"port":3002,"type":"ClusterIP"},"tolerations":[]}` | ------------------------------------------------------------------------- | -| ollama | object | `{"enabled":true,"ollama":{"models":["gpt-oss:20b"]},"persistentVolume":{"enabled":true,"size":"10Gi","storageClass":""},"resources":{},"service":{"port":11434,"type":"ClusterIP"}}` | ------------------------------------------------------------------------- | +| nl2dsl.affinity | object | `{}` | | +| nl2dsl.enabled | bool | `true` | | +| nl2dsl.env.ALLOW_ORIGINS | string | `"*"` | | +| nl2dsl.env.OLLAMA_MODEL | string | `"gpt-oss:20b"` | | +| nl2dsl.image.pullPolicy | string | `"IfNotPresent"` | | +| nl2dsl.image.repository | string | `"cogstacksystems/cogstack-cohorter-nl2dsl"` | | +| nl2dsl.image.tag | string | `"latest"` | | +| nl2dsl.livenessProbe.httpGet.path | string | `"/"` | | +| nl2dsl.livenessProbe.httpGet.port | string | `"http"` | | +| nl2dsl.livenessProbe.initialDelaySeconds | int | `30` | | +| nl2dsl.livenessProbe.periodSeconds | int | `10` | | +| nl2dsl.nodeSelector | object | `{}` | | +| nl2dsl.readinessProbe.httpGet.path | string | `"/"` | | +| nl2dsl.readinessProbe.httpGet.port | string | `"http"` | | +| nl2dsl.readinessProbe.initialDelaySeconds | int | `10` | | +| nl2dsl.readinessProbe.periodSeconds | int | `5` | | +| nl2dsl.replicaCount | int | `1` | | +| nl2dsl.resources | object | `{}` | | +| nl2dsl.service.port | int | `3002` | | +| nl2dsl.service.type | string | `"ClusterIP"` | | +| nl2dsl.tolerations | list | `[]` | | +| ollama.enabled | bool | `true` | | +| ollama.ollama.models.pull[0] | string | `"gpt-oss:20b"` | | +| ollama.persistentVolume.enabled | bool | `true` | | +| ollama.persistentVolume.size | string | `"10Gi"` | | +| ollama.persistentVolume.storageClass | string | `""` | | +| ollama.resources | object | `{}` | | +| ollama.service.port | int | `11434` | | +| ollama.service.type | string | `"ClusterIP"` | | | podAnnotations | object | `{}` | | | podLabels | object | `{}` | | | podSecurityContext | object | `{}` | | @@ -154,7 +196,31 @@ For issues and questions, please visit the [CogStack GitHub repository](https:// | serviceAccount.automount | bool | `true` | | | serviceAccount.create | bool | `true` | | | serviceAccount.name | string | `""` | | -| webapp | object | `{"affinity":{},"enabled":true,"env":{"RANDOM_DATA":"false"},"image":{"pullPolicy":"IfNotPresent","repository":"cogstacksystems/cogstack-cohorter-webapp","tag":"latest"},"livenessProbe":{"httpGet":{"path":"/","port":"http"},"initialDelaySeconds":60,"periodSeconds":15},"nodeSelector":{},"persistence":{"accessMode":"ReadWriteOnce","enabled":true,"existingClaim":"","size":"5Gi","storageClass":""},"readinessProbe":{"httpGet":{"path":"/","port":"http"},"initialDelaySeconds":30,"periodSeconds":10},"replicaCount":1,"resources":{},"service":{"port":3000,"type":"ClusterIP"},"tolerations":[]}` | ------------------------------------------------------------------------- | +| webapp.affinity | object | `{}` | | +| webapp.enabled | bool | `true` | | +| webapp.env.RANDOM_DATA | string | `"false"` | | +| webapp.image.pullPolicy | string | `"IfNotPresent"` | | +| webapp.image.repository | string | `"cogstacksystems/cogstack-cohorter-webapp"` | | +| webapp.image.tag | string | `"latest"` | | +| webapp.livenessProbe.httpGet.path | string | `"/"` | | +| webapp.livenessProbe.httpGet.port | string | `"http"` | | +| webapp.livenessProbe.initialDelaySeconds | int | `60` | | +| webapp.livenessProbe.periodSeconds | int | `15` | | +| webapp.nodeSelector | object | `{}` | | +| webapp.persistence.accessMode | string | `"ReadWriteOnce"` | | +| webapp.persistence.enabled | bool | `true` | | +| webapp.persistence.existingClaim | string | `""` | | +| webapp.persistence.size | string | `"5Gi"` | | +| webapp.persistence.storageClass | string | `""` | | +| webapp.readinessProbe.httpGet.path | string | `"/"` | | +| webapp.readinessProbe.httpGet.port | string | `"http"` | | +| webapp.readinessProbe.initialDelaySeconds | int | `30` | | +| webapp.readinessProbe.periodSeconds | int | `10` | | +| webapp.replicaCount | int | `1` | | +| webapp.resources | object | `{}` | | +| webapp.service.port | int | `3000` | | +| webapp.service.type | string | `"ClusterIP"` | | +| webapp.tolerations | list | `[]` | | ---------------------------------------------- Autogenerated from chart metadata using [helm-docs v1.14.2](https://github.com/norwoodj/helm-docs/releases/v1.14.2) diff --git a/helm-charts/cogstack-cohorter-helm/README.md.gotmpl b/helm-charts/cogstack-cohorter-helm/README.md.gotmpl index 8bfc0c3..beea0c7 100644 --- a/helm-charts/cogstack-cohorter-helm/README.md.gotmpl +++ b/helm-charts/cogstack-cohorter-helm/README.md.gotmpl @@ -43,13 +43,14 @@ ollama: enabled: true ollama: models: - - gpt-oss:20b # pulled automatically on first startup + pull: + - gpt-oss:20b # pulled automatically on first startup persistentVolume: enabled: true size: 10Gi ``` -Models are pulled automatically by the otwld subchart's built-in init container. Change `ollama.ollama.models` to use a different model — make sure `nl2dsl.env.OLLAMA_MODEL` matches. +Models are pulled automatically by the otwld subchart's built-in init container. Change `ollama.ollama.models.pull` to use a different model — make sure `nl2dsl.env.OLLAMA_MODEL` matches. ### MedCAT diff --git a/helm-charts/cogstack-cohorter-helm/templates/networkpolicy.yaml b/helm-charts/cogstack-cohorter-helm/templates/networkpolicy.yaml deleted file mode 100644 index 424005f..0000000 --- a/helm-charts/cogstack-cohorter-helm/templates/networkpolicy.yaml +++ /dev/null @@ -1,71 +0,0 @@ -{{- if .Values.networkPolicy.enabled }} -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: {{ include "cogstack-cohort.fullname" . }} - labels: - {{- include "cogstack-cohort.labels" . | nindent 4 }} -spec: - podSelector: - matchLabels: - {{- include "cogstack-cohort.selectorLabels" . | nindent 6 }} - policyTypes: - - Ingress - {{- if .Values.networkPolicy.egress.enabled }} - - Egress - {{- end }} - ingress: - # Allow traffic to webapp from anywhere - - to: - - podSelector: - matchLabels: - {{- include "cogstack-cohort.webapp.selectorLabels" . | nindent 12 }} - ports: - - protocol: TCP - port: {{ .Values.webapp.service.port }} - # Allow traffic to medcat from nl2dsl and webapp - - to: - - podSelector: - matchLabels: - {{- include "cogstack-cohort.medcat.selectorLabels" . | nindent 12 }} - from: - - podSelector: - matchLabels: - {{- include "cogstack-cohort.nl2dsl.selectorLabels" . | nindent 12 }} - - podSelector: - matchLabels: - {{- include "cogstack-cohort.webapp.selectorLabels" . | nindent 12 }} - ports: - - protocol: TCP - port: {{ .Values.medcat.service.port }} - # Allow traffic to ollama from nl2dsl - - to: - - podSelector: - matchLabels: - {{- include "cogstack-cohort.ollama.selectorLabels" . | nindent 12 }} - from: - - podSelector: - matchLabels: - {{- include "cogstack-cohort.nl2dsl.selectorLabels" . | nindent 12 }} - ports: - - protocol: TCP - port: {{ .Values.ollama.service.port }} - # Allow traffic to nl2dsl from webapp - - to: - - podSelector: - matchLabels: - {{- include "cogstack-cohort.nl2dsl.selectorLabels" . | nindent 12 }} - from: - - podSelector: - matchLabels: - {{- include "cogstack-cohort.webapp.selectorLabels" . | nindent 12 }} - ports: - - protocol: TCP - port: {{ .Values.nl2dsl.service.port }} - {{- if .Values.networkPolicy.egress.enabled }} - egress: - {{- with .Values.networkPolicy.egress.egressRules }} - {{- toYaml . | nindent 4 }} - {{- end }} - {{- end }} -{{- end }} diff --git a/helm-charts/cogstack-cohorter-helm/templates/webapp-pvc.yaml b/helm-charts/cogstack-cohorter-helm/templates/webapp-pvc.yaml new file mode 100644 index 0000000..dd47709 --- /dev/null +++ b/helm-charts/cogstack-cohorter-helm/templates/webapp-pvc.yaml @@ -0,0 +1,17 @@ +{{- if and .Values.webapp.enabled .Values.webapp.persistence.enabled (not .Values.webapp.persistence.existingClaim) }} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ include "cogstack-cohort.fullname" . }}-webapp-data + labels: + {{- include "cogstack-cohort.webapp.labels" . | nindent 4 }} +spec: + accessModes: + - {{ .Values.webapp.persistence.accessMode }} + {{- if .Values.webapp.persistence.storageClass }} + storageClassName: {{ .Values.webapp.persistence.storageClass }} + {{- end }} + resources: + requests: + storage: {{ .Values.webapp.persistence.size }} +{{- end }} diff --git a/helm-charts/cogstack-cohorter-helm/values.yaml b/helm-charts/cogstack-cohorter-helm/values.yaml index bbe52b9..1ca0cab 100644 --- a/helm-charts/cogstack-cohorter-helm/values.yaml +++ b/helm-charts/cogstack-cohorter-helm/values.yaml @@ -1,4 +1,4 @@ -# Default values for cogstack-cohort. +# Default values for cogstack-cohorter. global: imagePullSecrets: [] @@ -17,19 +17,14 @@ podLabels: {} podSecurityContext: {} securityContext: {} -# --------------------------------------------------------------------------- -# Ollama — subchart: otwld/ollama -# All keys are passed through to the subchart. The model is pulled -# automatically by the subchart's built-in init container. -# Ref: https://github.com/otwld/ollama-helm -# --------------------------------------------------------------------------- ollama: enabled: true ollama: - # Model to pull on startup. Change to any model available on ollama.com. models: - - gpt-oss:20b + # Models to pull on startup. Change to any model available on ollama.com. + pull: + - gpt-oss:20b service: type: ClusterIP @@ -42,10 +37,6 @@ ollama: resources: {} -# --------------------------------------------------------------------------- -# MedCAT — subchart: cogstacksystems/medcat-service-helm -# All keys are passed through to the subchart. -# --------------------------------------------------------------------------- medcat: enabled: true @@ -66,9 +57,6 @@ medcat: resources: {} -# --------------------------------------------------------------------------- -# NL2DSL (CohortCompiler) — custom templates -# --------------------------------------------------------------------------- nl2dsl: enabled: true @@ -108,9 +96,6 @@ nl2dsl: tolerations: [] affinity: {} -# --------------------------------------------------------------------------- -# WebApp — custom templates -# --------------------------------------------------------------------------- webapp: enabled: true @@ -159,9 +144,6 @@ webapp: tolerations: [] affinity: {} -# --------------------------------------------------------------------------- -# Ingress (routes to webapp) -# --------------------------------------------------------------------------- ingress: enabled: false className: "" @@ -173,9 +155,6 @@ ingress: pathType: ImplementationSpecific tls: [] -# --------------------------------------------------------------------------- -# Autoscaling (webapp only) -# --------------------------------------------------------------------------- autoscaling: enabled: false minReplicas: 1 From 26abcb4e65b289df8eec49448deafddaa4ced02a Mon Sep 17 00:00:00 2001 From: jocelyneholdbrook Date: Thu, 16 Apr 2026 15:59:12 +0100 Subject: [PATCH 3/8] feat(cogstack-cohorter): Add step to add Ollama Helm repo charts build workflow since it is not available via OCI --- .github/workflows/kubernetes-charts-build.yaml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/kubernetes-charts-build.yaml b/.github/workflows/kubernetes-charts-build.yaml index 5519c12..dd7556e 100644 --- a/.github/workflows/kubernetes-charts-build.yaml +++ b/.github/workflows/kubernetes-charts-build.yaml @@ -39,6 +39,11 @@ jobs: - name: Set up chart-testing uses: helm/chart-testing-action@v2.8.0 + - name: Add Helm repositories + run: | + helm repo add ollama https://otwld.github.io/ollama-helm/ + helm repo update + - name: Run chart-testing (list-changed) id: list-changed working-directory: . @@ -111,6 +116,11 @@ jobs: fi echo "chart_version=$CHART_VERSION" >> "$GITHUB_OUTPUT" + - name: Add Helm repositories + run: | + helm repo add ollama https://otwld.github.io/ollama-helm/ + helm repo update + - name: Recursive dependency update (all charts, including nested) # Waiting on helm recursive feature https://github.com/helm/helm/pull/30855 # Could alternatively switch to helm "cascade" plugin From 7b4abe2e35f933dbf11765916ba620a785516196 Mon Sep 17 00:00:00 2001 From: jocelyneholdbrook Date: Mon, 20 Apr 2026 12:11:40 +0100 Subject: [PATCH 4/8] feat(cogstack-cohorter): Address code review feedback --- .github/linters/ct.yaml | 1 + .../workflows/kubernetes-charts-build.yaml | 5 - helm-charts/cogstack-cohorter-helm/Chart.yaml | 2 +- helm-charts/cogstack-cohorter-helm/README.md | 1 - .../cogstack-cohorter-helm/ci/ci-values.yaml | 2 + .../templates/NOTES.txt | 16 +- .../templates/_helpers.tpl | 51 +-- .../cogstack-cohorter-helm/templates/hpa.yaml | 8 +- .../templates/httproute.yaml | 38 +++ .../templates/ingress.yaml | 36 +-- .../templates/nl2dsl-deployment.yaml | 14 +- .../templates/nl2dsl-service.yaml | 6 +- .../templates/serviceaccount.yaml | 4 +- .../templates/tests/test-connection.yaml | 15 + .../templates/webapp-deployment.yaml | 10 +- .../templates/webapp-pvc.yaml | 4 +- .../templates/webapp-service.yaml | 6 +- .../cogstack-cohorter-helm/values.yaml | 290 ++++++++++++------ 18 files changed, 325 insertions(+), 184 deletions(-) create mode 100644 helm-charts/cogstack-cohorter-helm/ci/ci-values.yaml create mode 100644 helm-charts/cogstack-cohorter-helm/templates/httproute.yaml create mode 100644 helm-charts/cogstack-cohorter-helm/templates/tests/test-connection.yaml diff --git a/.github/linters/ct.yaml b/.github/linters/ct.yaml index 0420d84..3b925ef 100644 --- a/.github/linters/ct.yaml +++ b/.github/linters/ct.yaml @@ -6,3 +6,4 @@ chart-dirs: helm-extra-args: --timeout 1000s chart-repos: - opensearch=https://opensearch-project.github.io/helm-charts + - ollama=https://otwld.github.io/ollama-helm diff --git a/.github/workflows/kubernetes-charts-build.yaml b/.github/workflows/kubernetes-charts-build.yaml index dd7556e..b16e072 100644 --- a/.github/workflows/kubernetes-charts-build.yaml +++ b/.github/workflows/kubernetes-charts-build.yaml @@ -39,11 +39,6 @@ jobs: - name: Set up chart-testing uses: helm/chart-testing-action@v2.8.0 - - name: Add Helm repositories - run: | - helm repo add ollama https://otwld.github.io/ollama-helm/ - helm repo update - - name: Run chart-testing (list-changed) id: list-changed working-directory: . diff --git a/helm-charts/cogstack-cohorter-helm/Chart.yaml b/helm-charts/cogstack-cohorter-helm/Chart.yaml index 5b52a5e..d11cf5d 100644 --- a/helm-charts/cogstack-cohorter-helm/Chart.yaml +++ b/helm-charts/cogstack-cohorter-helm/Chart.yaml @@ -1,5 +1,5 @@ apiVersion: v2 -name: cogstack-cohorter +name: cogstack-cohorter-helm description: CogStack Cohorter — cohort identification powered by Ollama and MedCAT type: application version: 0.0.1 diff --git a/helm-charts/cogstack-cohorter-helm/README.md b/helm-charts/cogstack-cohorter-helm/README.md index 3c1280f..dc20e0e 100644 --- a/helm-charts/cogstack-cohorter-helm/README.md +++ b/helm-charts/cogstack-cohorter-helm/README.md @@ -19,7 +19,6 @@ MedCAT and Ollama are deployed as **subcharts**: - Kubernetes 1.21+ - Helm 3.10+ -- A storage class that supports `ReadWriteOnce` PVCs - Sufficient node resources for the Ollama model (the default `gpt-oss:20b` requires ~14 GB of memory/VRAM) ## Installation diff --git a/helm-charts/cogstack-cohorter-helm/ci/ci-values.yaml b/helm-charts/cogstack-cohorter-helm/ci/ci-values.yaml new file mode 100644 index 0000000..1eae0db --- /dev/null +++ b/helm-charts/cogstack-cohorter-helm/ci/ci-values.yaml @@ -0,0 +1,2 @@ +env: + RANDOM_DATA: "false" \ No newline at end of file diff --git a/helm-charts/cogstack-cohorter-helm/templates/NOTES.txt b/helm-charts/cogstack-cohorter-helm/templates/NOTES.txt index d083b44..5b03841 100644 --- a/helm-charts/cogstack-cohorter-helm/templates/NOTES.txt +++ b/helm-charts/cogstack-cohorter-helm/templates/NOTES.txt @@ -2,18 +2,18 @@ CogStack Cohort has been deployed. Components: {{- if .Values.webapp.enabled }} - - WebApp: {{ include "cogstack-cohort.fullname" . }}-webapp (port {{ .Values.webapp.service.port }}) + - WebApp: {{ include "cogstack-cohorter-helm.fullname" . }}-webapp (port {{ .Values.webapp.service.port }}) {{- end }} {{- if .Values.nl2dsl.enabled }} - - NL2DSL: {{ include "cogstack-cohort.fullname" . }}-nl2dsl (port {{ .Values.nl2dsl.service.port }}) + - NL2DSL: {{ include "cogstack-cohorter-helm.fullname" . }}-nl2dsl (port {{ .Values.nl2dsl.service.port }}) {{- end }} {{- if .Values.medcat.enabled }} - - MedCAT: {{ include "cogstack-cohort.medcatServiceName" . }} (port {{ .Values.medcat.service.port }}) [subchart: medcat-service-helm] + - MedCAT: {{ include "cogstack-cohorter-helm.medcatServiceName" . }} (port {{ .Values.medcat.service.port }}) [subchart: medcat-service-helm] {{- end }} {{- if .Values.ollama.enabled }} - - Ollama: {{ include "cogstack-cohort.ollamaServiceName" . }} (port {{ .Values.ollama.service.port }}) [subchart: otwld/ollama] + - Ollama: {{ include "cogstack-cohorter-helm.ollamaServiceName" . }} (port {{ .Values.ollama.service.port }}) [subchart: otwld/ollama] Models pulled on startup: - {{- range .Values.ollama.ollama.models }} + {{- range .Values.ollama.ollama.models.pull }} - {{ . }} {{- end }} {{- end }} @@ -34,13 +34,13 @@ Access the WebApp: http{{ if $.Values.ingress.tls }}s{{ end }}://{{ .host }} {{- end }} {{- else if contains "NodePort" .Values.webapp.service.type }} - export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "cogstack-cohort.fullname" . }}-webapp) + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "cogstack-cohorter-helm.fullname" . }}-webapp) export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") echo http://$NODE_IP:$NODE_PORT {{- else if contains "LoadBalancer" .Values.webapp.service.type }} - export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "cogstack-cohort.fullname" . }}-webapp --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "cogstack-cohorter-helm.fullname" . }}-webapp --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") echo http://$SERVICE_IP:{{ .Values.webapp.service.port }} {{- else }} - kubectl port-forward --namespace {{ .Release.Namespace }} svc/{{ include "cogstack-cohort.fullname" . }}-webapp {{ .Values.webapp.service.port }}:{{ .Values.webapp.service.port }} + kubectl port-forward --namespace {{ .Release.Namespace }} svc/{{ include "cogstack-cohorter-helm.fullname" . }}-webapp {{ .Values.webapp.service.port }}:{{ .Values.webapp.service.port }} Then open: http://localhost:{{ .Values.webapp.service.port }} {{- end }} diff --git a/helm-charts/cogstack-cohorter-helm/templates/_helpers.tpl b/helm-charts/cogstack-cohorter-helm/templates/_helpers.tpl index e74a623..0571ab0 100644 --- a/helm-charts/cogstack-cohorter-helm/templates/_helpers.tpl +++ b/helm-charts/cogstack-cohorter-helm/templates/_helpers.tpl @@ -1,14 +1,16 @@ {{/* Expand the name of the chart. */}} -{{- define "cogstack-cohort.name" -}} +{{- define "cogstack-cohorter-helm.name" -}} {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} {{- end }} {{/* Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. */}} -{{- define "cogstack-cohort.fullname" -}} +{{- define "cogstack-cohorter-helm.fullname" -}} {{- if .Values.fullnameOverride }} {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} {{- else }} @@ -22,39 +24,38 @@ Create a default fully qualified app name. {{- end }} {{/* -Chart name and version label. +Create chart name and version as used by the chart label. */}} -{{- define "cogstack-cohort.chart" -}} +{{- define "cogstack-cohorter-helm.chart" -}} {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} {{- end }} {{/* -Common labels. +Common labels */}} -{{- define "cogstack-cohort.labels" -}} -helm.sh/chart: {{ include "cogstack-cohort.chart" . }} -{{ include "cogstack-cohort.selectorLabels" . }} +{{- define "cogstack-cohorter-helm.labels" -}} +helm.sh/chart: {{ include "cogstack-cohorter-helm.chart" . }} +{{ include "cogstack-cohorter-helm.selectorLabels" . }} {{- if .Chart.AppVersion }} app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} {{- end }} app.kubernetes.io/managed-by: {{ .Release.Service }} -app.kubernetes.io/part-of: cogstack {{- end }} {{/* -Selector labels. +Selector labels */}} -{{- define "cogstack-cohort.selectorLabels" -}} -app.kubernetes.io/name: {{ include "cogstack-cohort.name" . }} +{{- define "cogstack-cohorter-helm.selectorLabels" -}} +app.kubernetes.io/name: {{ include "cogstack-cohorter-helm.name" . }} app.kubernetes.io/instance: {{ .Release.Name }} {{- end }} {{/* -Service account name. +Create the name of the service account to use */}} -{{- define "cogstack-cohort.serviceAccountName" -}} +{{- define "cogstack-cohorter-helm.serviceAccountName" -}} {{- if .Values.serviceAccount.create }} -{{- default (include "cogstack-cohort.fullname" .) .Values.serviceAccount.name }} +{{- default (include "cogstack-cohorter-helm.fullname" .) .Values.serviceAccount.name }} {{- else }} {{- default "default" .Values.serviceAccount.name }} {{- end }} @@ -63,26 +64,26 @@ Service account name. {{/* NL2DSL component labels / selector labels. */}} -{{- define "cogstack-cohort.nl2dsl.labels" -}} -{{ include "cogstack-cohort.labels" . }} +{{- define "cogstack-cohorter-helm.nl2dsl.labels" -}} +{{ include "cogstack-cohorter-helm.labels" . }} app.kubernetes.io/component: nl2dsl {{- end }} -{{- define "cogstack-cohort.nl2dsl.selectorLabels" -}} -{{ include "cogstack-cohort.selectorLabels" . }} +{{- define "cogstack-cohorter-helm.nl2dsl.selectorLabels" -}} +{{ include "cogstack-cohorter-helm.selectorLabels" . }} app.kubernetes.io/component: nl2dsl {{- end }} {{/* WebApp component labels / selector labels. */}} -{{- define "cogstack-cohort.webapp.labels" -}} -{{ include "cogstack-cohort.labels" . }} +{{- define "cogstack-cohorter-helm.webapp.labels" -}} +{{ include "cogstack-cohorter-helm.labels" . }} app.kubernetes.io/component: webapp {{- end }} -{{- define "cogstack-cohort.webapp.selectorLabels" -}} -{{ include "cogstack-cohort.selectorLabels" . }} +{{- define "cogstack-cohorter-helm.webapp.selectorLabels" -}} +{{ include "cogstack-cohorter-helm.selectorLabels" . }} app.kubernetes.io/component: webapp {{- end }} @@ -90,7 +91,7 @@ app.kubernetes.io/component: webapp Fully-qualified service name for the ollama subchart. The otwld/ollama chart names its service -ollama. */}} -{{- define "cogstack-cohort.ollamaServiceName" -}} +{{- define "cogstack-cohorter-helm.ollamaServiceName" -}} {{- printf "%s-ollama" .Release.Name }} {{- end }} @@ -98,6 +99,6 @@ The otwld/ollama chart names its service -ollama. Fully-qualified service name for the medcat subchart. The medcat-service-helm chart names its service -medcat. */}} -{{- define "cogstack-cohort.medcatServiceName" -}} +{{- define "cogstack-cohorter-helm.medcatServiceName" -}} {{- printf "%s-medcat" .Release.Name }} {{- end }} diff --git a/helm-charts/cogstack-cohorter-helm/templates/hpa.yaml b/helm-charts/cogstack-cohorter-helm/templates/hpa.yaml index 6e5192b..2d51d37 100644 --- a/helm-charts/cogstack-cohorter-helm/templates/hpa.yaml +++ b/helm-charts/cogstack-cohorter-helm/templates/hpa.yaml @@ -2,14 +2,14 @@ apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: - name: {{ include "cogstack-cohort.fullname" . }}-webapp + name: {{ include "cogstack-cohorter-helm.fullname" . }} labels: - {{- include "cogstack-cohort.webapp.labels" . | nindent 4 }} + {{- include "cogstack-cohorter-helm.labels" . | nindent 4 }} spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment - name: {{ include "cogstack-cohort.fullname" . }}-webapp + name: {{ include "cogstack-cohorter-helm.fullname" . }} minReplicas: {{ .Values.autoscaling.minReplicas }} maxReplicas: {{ .Values.autoscaling.maxReplicas }} metrics: @@ -29,4 +29,4 @@ spec: type: Utilization averageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} {{- end }} -{{- end }} + {{- end }} diff --git a/helm-charts/cogstack-cohorter-helm/templates/httproute.yaml b/helm-charts/cogstack-cohorter-helm/templates/httproute.yaml new file mode 100644 index 0000000..9299a03 --- /dev/null +++ b/helm-charts/cogstack-cohorter-helm/templates/httproute.yaml @@ -0,0 +1,38 @@ +{{- if .Values.httpRoute.enabled -}} +{{- $fullName := include "cogstack-cohorter-helm.fullname" . -}} +{{- $svcPort := .Values.service.port -}} +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: {{ $fullName }} + labels: + {{- include "cogstack-cohorter-helm.labels" . | nindent 4 }} + {{- with .Values.httpRoute.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + parentRefs: + {{- with .Values.httpRoute.parentRefs }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.httpRoute.hostnames }} + hostnames: + {{- toYaml . | nindent 4 }} + {{- end }} + rules: + {{- range .Values.httpRoute.rules }} + {{- with .matches }} + - matches: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .filters }} + filters: + {{- toYaml . | nindent 8 }} + {{- end }} + backendRefs: + - name: {{ $fullName }} + port: {{ $svcPort }} + weight: 1 + {{- end }} +{{- end }} diff --git a/helm-charts/cogstack-cohorter-helm/templates/ingress.yaml b/helm-charts/cogstack-cohorter-helm/templates/ingress.yaml index 88f77e1..3fb07ef 100644 --- a/helm-charts/cogstack-cohorter-helm/templates/ingress.yaml +++ b/helm-charts/cogstack-cohorter-helm/templates/ingress.yaml @@ -1,30 +1,17 @@ {{- if .Values.ingress.enabled -}} -{{- $fullName := include "cogstack-cohort.fullname" . -}} -{{- $svcPort := .Values.webapp.service.port -}} -{{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} - {{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }} - {{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}} - {{- end }} -{{- end }} -{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}} apiVersion: networking.k8s.io/v1 -{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} -apiVersion: networking.k8s.io/v1beta1 -{{- else -}} -apiVersion: extensions/v1beta1 -{{- end }} kind: Ingress metadata: - name: {{ $fullName }} + name: {{ include "cogstack-cohorter-helm.fullname" . }} labels: - {{- include "cogstack-cohort.labels" . | nindent 4 }} + {{- include "cogstack-cohorter-helm.labels" . | nindent 4 }} {{- with .Values.ingress.annotations }} annotations: {{- toYaml . | nindent 4 }} {{- end }} spec: - {{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} - ingressClassName: {{ .Values.ingress.className }} + {{- with .Values.ingress.className }} + ingressClassName: {{ . }} {{- end }} {{- if .Values.ingress.tls }} tls: @@ -43,19 +30,14 @@ spec: paths: {{- range .paths }} - path: {{ .path }} - {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }} - pathType: {{ .pathType }} + {{- with .pathType }} + pathType: {{ . }} {{- end }} backend: - {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} service: - name: {{ $fullName }}-webapp + name: {{ include "cogstack-cohorter-helm.fullname" $ }} port: - number: {{ $svcPort }} - {{- else }} - serviceName: {{ $fullName }}-webapp - servicePort: {{ $svcPort }} - {{- end }} + number: {{ $.Values.service.port }} {{- end }} {{- end }} -{{- end }} + {{- end }} diff --git a/helm-charts/cogstack-cohorter-helm/templates/nl2dsl-deployment.yaml b/helm-charts/cogstack-cohorter-helm/templates/nl2dsl-deployment.yaml index 5ffcf03..cf16b14 100644 --- a/helm-charts/cogstack-cohorter-helm/templates/nl2dsl-deployment.yaml +++ b/helm-charts/cogstack-cohorter-helm/templates/nl2dsl-deployment.yaml @@ -2,14 +2,14 @@ apiVersion: apps/v1 kind: Deployment metadata: - name: {{ include "cogstack-cohort.fullname" . }}-nl2dsl + name: {{ include "cogstack-cohorter-helm.fullname" . }}-nl2dsl labels: - {{- include "cogstack-cohort.nl2dsl.labels" . | nindent 4 }} + {{- include "cogstack-cohorter-helm.nl2dsl.labels" . | nindent 4 }} spec: replicas: {{ .Values.nl2dsl.replicaCount }} selector: matchLabels: - {{- include "cogstack-cohort.nl2dsl.selectorLabels" . | nindent 6 }} + {{- include "cogstack-cohorter-helm.nl2dsl.selectorLabels" . | nindent 6 }} template: metadata: {{- with .Values.podAnnotations }} @@ -17,7 +17,7 @@ spec: {{- toYaml . | nindent 8 }} {{- end }} labels: - {{- include "cogstack-cohort.nl2dsl.labels" . | nindent 8 }} + {{- include "cogstack-cohorter-helm.nl2dsl.labels" . | nindent 8 }} {{- with .Values.podLabels }} {{- toYaml . | nindent 8 }} {{- end }} @@ -26,7 +26,7 @@ spec: imagePullSecrets: {{- toYaml . | nindent 8 }} {{- end }} - serviceAccountName: {{ include "cogstack-cohort.serviceAccountName" . }} + serviceAccountName: {{ include "cogstack-cohorter-helm.serviceAccountName" . }} {{- with .Values.podSecurityContext }} securityContext: {{- toYaml . | nindent 8 }} @@ -45,9 +45,9 @@ spec: protocol: TCP env: - name: OLLAMA_URL - value: "http://{{ include "cogstack-cohort.ollamaServiceName" . }}:{{ .Values.ollama.service.port }}/api/generate" + value: "http://{{ include "cogstack-cohorter-helm.ollamaServiceName" . }}:{{ .Values.ollama.service.port }}/api/generate" - name: MEDCAT_URL - value: "http://{{ include "cogstack-cohort.medcatServiceName" . }}:{{ .Values.medcat.service.port }}" + value: "http://{{ include "cogstack-cohorter-helm.medcatServiceName" . }}:{{ .Values.medcat.service.port }}" {{- range $key, $value := .Values.nl2dsl.env }} - name: {{ $key | quote }} value: {{ $value | quote }} diff --git a/helm-charts/cogstack-cohorter-helm/templates/nl2dsl-service.yaml b/helm-charts/cogstack-cohorter-helm/templates/nl2dsl-service.yaml index 9cb54ef..40383af 100644 --- a/helm-charts/cogstack-cohorter-helm/templates/nl2dsl-service.yaml +++ b/helm-charts/cogstack-cohorter-helm/templates/nl2dsl-service.yaml @@ -2,9 +2,9 @@ apiVersion: v1 kind: Service metadata: - name: {{ include "cogstack-cohort.fullname" . }}-nl2dsl + name: {{ include "cogstack-cohorter-helm.fullname" . }}-nl2dsl labels: - {{- include "cogstack-cohort.nl2dsl.labels" . | nindent 4 }} + {{- include "cogstack-cohorter-helm.nl2dsl.labels" . | nindent 4 }} spec: type: {{ .Values.nl2dsl.service.type }} ports: @@ -13,5 +13,5 @@ spec: protocol: TCP name: http selector: - {{- include "cogstack-cohort.nl2dsl.selectorLabels" . | nindent 4 }} + {{- include "cogstack-cohorter-helm.nl2dsl.selectorLabels" . | nindent 4 }} {{- end }} diff --git a/helm-charts/cogstack-cohorter-helm/templates/serviceaccount.yaml b/helm-charts/cogstack-cohorter-helm/templates/serviceaccount.yaml index 757d2fd..5bf39c1 100644 --- a/helm-charts/cogstack-cohorter-helm/templates/serviceaccount.yaml +++ b/helm-charts/cogstack-cohorter-helm/templates/serviceaccount.yaml @@ -2,9 +2,9 @@ apiVersion: v1 kind: ServiceAccount metadata: - name: {{ include "cogstack-cohort.serviceAccountName" . }} + name: {{ include "cogstack-cohorter-helm.serviceAccountName" . }} labels: - {{- include "cogstack-cohort.labels" . | nindent 4 }} + {{- include "cogstack-cohorter-helm.labels" . | nindent 4 }} {{- with .Values.serviceAccount.annotations }} annotations: {{- toYaml . | nindent 4 }} diff --git a/helm-charts/cogstack-cohorter-helm/templates/tests/test-connection.yaml b/helm-charts/cogstack-cohorter-helm/templates/tests/test-connection.yaml new file mode 100644 index 0000000..afcbb0c --- /dev/null +++ b/helm-charts/cogstack-cohorter-helm/templates/tests/test-connection.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Pod +metadata: + name: "{{ include "cogstack-cohorter-helm.fullname" . }}-test-connection" + labels: + {{- include "cogstack-cohorter-helm.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": test +spec: + containers: + - name: wget + image: busybox + command: ['wget'] + args: ['{{ include "cogstack-cohorter-helm.fullname" . }}-webapp:{{ .Values.webapp.service.port }}'] + restartPolicy: Never \ No newline at end of file diff --git a/helm-charts/cogstack-cohorter-helm/templates/webapp-deployment.yaml b/helm-charts/cogstack-cohorter-helm/templates/webapp-deployment.yaml index b608c85..326070d 100644 --- a/helm-charts/cogstack-cohorter-helm/templates/webapp-deployment.yaml +++ b/helm-charts/cogstack-cohorter-helm/templates/webapp-deployment.yaml @@ -1,18 +1,18 @@ {{- if .Values.webapp.enabled }} -{{- $fullName := include "cogstack-cohort.fullname" . }} +{{- $fullName := include "cogstack-cohorter-helm.fullname" . }} apiVersion: apps/v1 kind: Deployment metadata: name: {{ $fullName }}-webapp labels: - {{- include "cogstack-cohort.webapp.labels" . | nindent 4 }} + {{- include "cogstack-cohorter-helm.webapp.labels" . | nindent 4 }} spec: {{- if not .Values.autoscaling.enabled }} replicas: {{ .Values.webapp.replicaCount }} {{- end }} selector: matchLabels: - {{- include "cogstack-cohort.webapp.selectorLabels" . | nindent 6 }} + {{- include "cogstack-cohorter-helm.webapp.selectorLabels" . | nindent 6 }} template: metadata: {{- with .Values.podAnnotations }} @@ -20,7 +20,7 @@ spec: {{- toYaml . | nindent 8 }} {{- end }} labels: - {{- include "cogstack-cohort.webapp.labels" . | nindent 8 }} + {{- include "cogstack-cohorter-helm.webapp.labels" . | nindent 8 }} {{- with .Values.podLabels }} {{- toYaml . | nindent 8 }} {{- end }} @@ -29,7 +29,7 @@ spec: imagePullSecrets: {{- toYaml . | nindent 8 }} {{- end }} - serviceAccountName: {{ include "cogstack-cohort.serviceAccountName" . }} + serviceAccountName: {{ include "cogstack-cohorter-helm.serviceAccountName" . }} {{- with .Values.podSecurityContext }} securityContext: {{- toYaml . | nindent 8 }} diff --git a/helm-charts/cogstack-cohorter-helm/templates/webapp-pvc.yaml b/helm-charts/cogstack-cohorter-helm/templates/webapp-pvc.yaml index dd47709..7ae1a86 100644 --- a/helm-charts/cogstack-cohorter-helm/templates/webapp-pvc.yaml +++ b/helm-charts/cogstack-cohorter-helm/templates/webapp-pvc.yaml @@ -2,9 +2,9 @@ apiVersion: v1 kind: PersistentVolumeClaim metadata: - name: {{ include "cogstack-cohort.fullname" . }}-webapp-data + name: {{ include "cogstack-cohorter-helm.fullname" . }}-webapp-data labels: - {{- include "cogstack-cohort.webapp.labels" . | nindent 4 }} + {{- include "cogstack-cohorter-helm.webapp.labels" . | nindent 4 }} spec: accessModes: - {{ .Values.webapp.persistence.accessMode }} diff --git a/helm-charts/cogstack-cohorter-helm/templates/webapp-service.yaml b/helm-charts/cogstack-cohorter-helm/templates/webapp-service.yaml index 27720cc..8609bab 100644 --- a/helm-charts/cogstack-cohorter-helm/templates/webapp-service.yaml +++ b/helm-charts/cogstack-cohorter-helm/templates/webapp-service.yaml @@ -2,9 +2,9 @@ apiVersion: v1 kind: Service metadata: - name: {{ include "cogstack-cohort.fullname" . }}-webapp + name: {{ include "cogstack-cohorter-helm.fullname" . }}-webapp labels: - {{- include "cogstack-cohort.webapp.labels" . | nindent 4 }} + {{- include "cogstack-cohorter-helm.webapp.labels" . | nindent 4 }} spec: type: {{ .Values.webapp.service.type }} ports: @@ -13,5 +13,5 @@ spec: protocol: TCP name: http selector: - {{- include "cogstack-cohort.webapp.selectorLabels" . | nindent 4 }} + {{- include "cogstack-cohorter-helm.webapp.selectorLabels" . | nindent 4 }} {{- end }} diff --git a/helm-charts/cogstack-cohorter-helm/values.yaml b/helm-charts/cogstack-cohorter-helm/values.yaml index 1ca0cab..715c5e7 100644 --- a/helm-charts/cogstack-cohorter-helm/values.yaml +++ b/helm-charts/cogstack-cohorter-helm/values.yaml @@ -1,82 +1,188 @@ -# Default values for cogstack-cohorter. +# Default values for cogstack-cohorter-helm. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. +# --------------------------------------------------------------------------- +# Global +# --------------------------------------------------------------------------- global: + # Image pull secrets shared across all components (including subcharts). imagePullSecrets: [] -nameOverride: "" -fullnameOverride: "" - +# --------------------------------------------------------------------------- +# Service account +# More information: https://kubernetes.io/docs/concepts/security/service-accounts/ +# --------------------------------------------------------------------------- serviceAccount: + # Specifies whether a service account should be created. create: true + # Automatically mount a ServiceAccount's API credentials? automount: true + # Annotations to add to the service account. annotations: {} + # The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template. name: "" +# --------------------------------------------------------------------------- +# Chart name overrides +# --------------------------------------------------------------------------- +nameOverride: "" +fullnameOverride: "" + +# --------------------------------------------------------------------------- +# Pod-level settings (shared by webapp and nl2dsl pods) +# --------------------------------------------------------------------------- podAnnotations: {} podLabels: {} podSecurityContext: {} + # fsGroup: 2000 securityContext: {} + # capabilities: + # drop: + # - ALL + # readOnlyRootFilesystem: true + # runAsNonRoot: true + # runAsUser: 1000 + +# --------------------------------------------------------------------------- +# Autoscaling (webapp only) +# More information: https://kubernetes.io/docs/concepts/workloads/autoscaling/ +# --------------------------------------------------------------------------- +autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 3 + targetCPUUtilizationPercentage: 80 + # targetMemoryUtilizationPercentage: 80 -ollama: +# --------------------------------------------------------------------------- +# Ingress +# More information: https://kubernetes.io/docs/concepts/services-networking/ingress/ +# --------------------------------------------------------------------------- +ingress: + enabled: false + className: "" + annotations: {} + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + hosts: + - host: cogstack-cohort.local + paths: + - path: / + pathType: ImplementationSpecific + tls: [] + # - secretName: cogstack-cohort-tls + # hosts: + # - cogstack-cohort.local + +# --------------------------------------------------------------------------- +# Gateway API HTTPRoute (alternative to Ingress) +# Requires Gateway API resources and a suitable controller installed in the cluster. +# See: https://gateway-api.sigs.k8s.io/guides/ +# --------------------------------------------------------------------------- +httpRoute: + enabled: false + annotations: {} + # Which Gateways this Route is attached to. + parentRefs: + - name: gateway + sectionName: http + # namespace: default + hostnames: + - chart-example.local + rules: + - matches: + - path: + type: PathPrefix + value: / + +# --------------------------------------------------------------------------- +# WebApp — React + Node.js frontend and REST API +# --------------------------------------------------------------------------- +webapp: + # -- Enable the WebApp deployment and service. enabled: true - ollama: - models: - # Models to pull on startup. Change to any model available on ollama.com. - pull: - - gpt-oss:20b + replicaCount: 1 + + image: + repository: cogstacksystems/cogstack-cohorter-webapp + tag: latest + pullPolicy: IfNotPresent + + # Environment variables injected into the webapp container. + # NL2DSL_SERVER is set automatically from nl2dsl.service.port. + env: + # Set to "true" to generate synthetic patient data on first startup (demo mode). + RANDOM_DATA: "false" service: type: ClusterIP - port: 11434 + port: 3000 - persistentVolume: + # Persistent volume for SNOMED data directory (/usr/src/app/server/data). + # Populate the PVC with snomed_terms_data.tar.gz (auto-extracted on startup) + # or the pre-extracted files (snomed_terms.json, cui_pt2ch.json, patient data). + persistence: enabled: true - size: 10Gi + # Use an existing PVC instead of creating a new one. + existingClaim: "" storageClass: "" + accessMode: ReadWriteOnce + size: 5Gi - resources: {} - -medcat: - enabled: true - - image: - tag: "latest" - - env: - APP_MEDCAT_MODEL_PACK: "/cat/models/examples/example-medcat-v2-model-pack.zip" - APP_ENABLE_METRICS: "true" - - # To download a custom model pack at startup, set: - # model: - # downloadUrl: "https://your-host/medcat_model_pack.zip" - # name: "medcat_model_pack.zip" + livenessProbe: + httpGet: + path: / + port: http + initialDelaySeconds: 60 + periodSeconds: 15 - service: - port: 5000 + readinessProbe: + httpGet: + path: / + port: http + initialDelaySeconds: 30 + periodSeconds: 10 resources: {} + # limits: + # cpu: 500m + # memory: 512Mi + # requests: + # cpu: 250m + # memory: 256Mi + + nodeSelector: {} + tolerations: [] + affinity: {} +# --------------------------------------------------------------------------- +# NL2DSL — natural language → cohort DSL compiler (backed by Ollama + MedCAT) +# --------------------------------------------------------------------------- nl2dsl: + # -- Enable the NL2DSL deployment and service. enabled: true + replicaCount: 1 + image: repository: cogstacksystems/cogstack-cohorter-nl2dsl + tag: latest pullPolicy: IfNotPresent - tag: "latest" - - replicaCount: 1 - - service: - type: ClusterIP - port: 3002 + # Environment variables injected into the nl2dsl container. + # OLLAMA_URL and MEDCAT_URL are set automatically from subchart service names. env: + # Ollama model to use for NL → DSL compilation. OLLAMA_MODEL: "gpt-oss:20b" + # CORS origins allowed to call the NL2DSL API. ALLOW_ORIGINS: "*" - # OLLAMA_URL and MEDCAT_URL are derived from subchart service names in the template - resources: {} + service: + type: ClusterIP + port: 3002 livenessProbe: httpGet: @@ -92,71 +198,73 @@ nl2dsl: initialDelaySeconds: 10 periodSeconds: 5 + resources: {} + # limits: + # cpu: 500m + # memory: 512Mi + # requests: + # cpu: 250m + # memory: 256Mi + nodeSelector: {} tolerations: [] affinity: {} -webapp: +# --------------------------------------------------------------------------- +# MedCAT subchart — clinical NER and concept normalisation +# Source: oci://registry-1.docker.io/cogstacksystems/medcat-service-helm +# Full list of medcat-service-helm values: +# https://hub.docker.com/r/cogstacksystems/medcat-service-helm +# --------------------------------------------------------------------------- +medcat: + # -- Enable the MedCAT subchart. enabled: true image: - repository: cogstacksystems/cogstack-cohorter-webapp - pullPolicy: IfNotPresent - tag: "latest" - - replicaCount: 1 - - service: - type: ClusterIP - port: 3000 + tag: latest env: - RANDOM_DATA: "false" - # NL2DSL_SERVER is derived from subchart service names in the template + # Path to the MedCAT model pack inside the container. + APP_MEDCAT_MODEL_PACK: "/cat/models/examples/example-medcat-v2-model-pack.zip" + APP_ENABLE_METRICS: "true" - # PVC for the data directory (snomed_terms.json, cui_pt2ch.json, etc.) - # Supply snomed_terms_data.tar.gz or pre-extracted files — the entrypoint - # auto-extracts the archive if the JSON files are not yet present. - persistence: - enabled: true - storageClass: "" - accessMode: ReadWriteOnce - size: 5Gi - existingClaim: "" + # To download a custom model pack at startup: + # model: + # downloadUrl: "https://your-host/medcat_model_pack.zip" + # name: "medcat_model_pack.zip" + + service: + port: 5000 resources: {} - livenessProbe: - httpGet: - path: / - port: http - initialDelaySeconds: 60 - periodSeconds: 15 +# --------------------------------------------------------------------------- +# Ollama subchart — LLM serving backend +# Source: https://github.com/otwld/ollama-helm +# Full list of otwld/ollama values: +# https://github.com/otwld/ollama-helm/blob/main/charts/ollama/values.yaml +# --------------------------------------------------------------------------- +ollama: + # -- Enable the Ollama subchart. + enabled: true - readinessProbe: - httpGet: - path: / - port: http - initialDelaySeconds: 30 - periodSeconds: 10 + ollama: + # Models pulled automatically by the built-in init container on first startup. + # Ensure nl2dsl.env.OLLAMA_MODEL matches the model listed here. + models: + pull: + - gpt-oss:20b - nodeSelector: {} - tolerations: [] - affinity: {} + persistentVolume: + enabled: true + storageClass: "" + size: 10Gi -ingress: - enabled: false - className: "" - annotations: {} - hosts: - - host: cogstack-cohort.local - paths: - - path: / - pathType: ImplementationSpecific - tls: [] + service: + type: ClusterIP + port: 11434 -autoscaling: - enabled: false - minReplicas: 1 - maxReplicas: 3 - targetCPUUtilizationPercentage: 80 + resources: {} + # GPU example: + # limits: + # nvidia.com/gpu: 1 From 002dc2969c6d724b66253adba910d52803175d62 Mon Sep 17 00:00:00 2001 From: jocelyneholdbrook Date: Mon, 20 Apr 2026 12:19:21 +0100 Subject: [PATCH 5/8] feat(cogstack-cohorter): New line required at the end of ci-values.yaml file --- helm-charts/cogstack-cohorter-helm/ci/ci-values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helm-charts/cogstack-cohorter-helm/ci/ci-values.yaml b/helm-charts/cogstack-cohorter-helm/ci/ci-values.yaml index 1eae0db..bde8664 100644 --- a/helm-charts/cogstack-cohorter-helm/ci/ci-values.yaml +++ b/helm-charts/cogstack-cohorter-helm/ci/ci-values.yaml @@ -1,2 +1,2 @@ env: - RANDOM_DATA: "false" \ No newline at end of file + RANDOM_DATA: "false" From 99e596ef7c5b4a2c1c572cbc3270ba324c47b217 Mon Sep 17 00:00:00 2001 From: jocelyneholdbrook Date: Mon, 20 Apr 2026 14:22:40 +0100 Subject: [PATCH 6/8] feat(cogstack-cohorter): Stub data for CI build --- .../cogstack-cohorter-helm/ci/ci-values.yaml | 38 ++++++++++++++++++- .../templates/_helpers.tpl | 25 ------------ .../templates/webapp-deployment.yaml | 4 ++ .../cogstack-cohorter-helm/values.yaml | 6 ++- 4 files changed, 45 insertions(+), 28 deletions(-) diff --git a/helm-charts/cogstack-cohorter-helm/ci/ci-values.yaml b/helm-charts/cogstack-cohorter-helm/ci/ci-values.yaml index bde8664..786fd3f 100644 --- a/helm-charts/cogstack-cohorter-helm/ci/ci-values.yaml +++ b/helm-charts/cogstack-cohorter-helm/ci/ci-values.yaml @@ -1,2 +1,36 @@ -env: - RANDOM_DATA: "false" +# CI smoke-test overrides. +# An init container seeds the empty data volume with minimal stub SNOMED data +# so the webapp entrypoint can proceed and RANDOM_DATA=true can generate +# synthetic patient records without requiring a real data mount. +webapp: + env: + RANDOM_DATA: "true" + persistence: + enabled: false + initContainers: + - name: init-snomed-stub + image: busybox + command: + - sh + - -c + - | + mkdir -p /data + # Minimal snomed_terms.json — a few entries covering each clinical + # category that gen_random_data.js filters on. + cat > /data/snomed_terms.json << 'EOF' + [ + {"cui":"73211009","str":"Diabetes mellitus (disorder)"}, + {"cui":"44054006","str":"Diabetes mellitus type 2 (disorder)"}, + {"cui":"38341003","str":"Hypertensive disorder (disorder)"}, + {"cui":"195967001","str":"Asthma (disorder)"}, + {"cui":"271807003","str":"Eruption of skin (finding)"}, + {"cui":"386661006","str":"Fever (finding)"}, + {"cui":"80146002","str":"Appendectomy (procedure)"}, + {"cui":"387517004","str":"Paracetamol (substance)"} + ] + EOF + # cui_pt2ch.json — empty hierarchy is valid; server handles missing keys + echo '{}' > /data/cui_pt2ch.json + volumeMounts: + - name: data + mountPath: /data diff --git a/helm-charts/cogstack-cohorter-helm/templates/_helpers.tpl b/helm-charts/cogstack-cohorter-helm/templates/_helpers.tpl index 0571ab0..2967786 100644 --- a/helm-charts/cogstack-cohorter-helm/templates/_helpers.tpl +++ b/helm-charts/cogstack-cohorter-helm/templates/_helpers.tpl @@ -61,31 +61,6 @@ Create the name of the service account to use {{- end }} {{- end }} -{{/* -NL2DSL component labels / selector labels. -*/}} -{{- define "cogstack-cohorter-helm.nl2dsl.labels" -}} -{{ include "cogstack-cohorter-helm.labels" . }} -app.kubernetes.io/component: nl2dsl -{{- end }} - -{{- define "cogstack-cohorter-helm.nl2dsl.selectorLabels" -}} -{{ include "cogstack-cohorter-helm.selectorLabels" . }} -app.kubernetes.io/component: nl2dsl -{{- end }} - -{{/* -WebApp component labels / selector labels. -*/}} -{{- define "cogstack-cohorter-helm.webapp.labels" -}} -{{ include "cogstack-cohorter-helm.labels" . }} -app.kubernetes.io/component: webapp -{{- end }} - -{{- define "cogstack-cohorter-helm.webapp.selectorLabels" -}} -{{ include "cogstack-cohorter-helm.selectorLabels" . }} -app.kubernetes.io/component: webapp -{{- end }} {{/* Fully-qualified service name for the ollama subchart. diff --git a/helm-charts/cogstack-cohorter-helm/templates/webapp-deployment.yaml b/helm-charts/cogstack-cohorter-helm/templates/webapp-deployment.yaml index 326070d..54ed866 100644 --- a/helm-charts/cogstack-cohorter-helm/templates/webapp-deployment.yaml +++ b/helm-charts/cogstack-cohorter-helm/templates/webapp-deployment.yaml @@ -34,6 +34,10 @@ spec: securityContext: {{- toYaml . | nindent 8 }} {{- end }} + {{- with .Values.webapp.initContainers }} + initContainers: + {{- toYaml . | nindent 8 }} + {{- end }} containers: - name: webapp {{- with .Values.securityContext }} diff --git a/helm-charts/cogstack-cohorter-helm/values.yaml b/helm-charts/cogstack-cohorter-helm/values.yaml index 715c5e7..dabbc86 100644 --- a/helm-charts/cogstack-cohorter-helm/values.yaml +++ b/helm-charts/cogstack-cohorter-helm/values.yaml @@ -27,7 +27,7 @@ serviceAccount: # --------------------------------------------------------------------------- # Chart name overrides # --------------------------------------------------------------------------- -nameOverride: "" +nameOverride: "cogstack-cohorter" fullnameOverride: "" # --------------------------------------------------------------------------- @@ -154,6 +154,10 @@ webapp: # cpu: 250m # memory: 256Mi + # Optional init containers run before the webapp container starts. + # Useful for seeding the data volume (see ci/ci-values.yaml for an example). + initContainers: [] + nodeSelector: {} tolerations: [] affinity: {} From 86fa83ab61a6b82227ae59b4232e6c06fd5877a9 Mon Sep 17 00:00:00 2001 From: jocelyneholdbrook Date: Mon, 20 Apr 2026 14:34:37 +0100 Subject: [PATCH 7/8] feat(cogstack-cohorter): Fix lint errors --- .../cogstack-cohorter-helm/templates/nl2dsl-deployment.yaml | 6 +++--- .../cogstack-cohorter-helm/templates/nl2dsl-service.yaml | 4 ++-- .../cogstack-cohorter-helm/templates/webapp-deployment.yaml | 6 +++--- .../cogstack-cohorter-helm/templates/webapp-pvc.yaml | 2 +- .../cogstack-cohorter-helm/templates/webapp-service.yaml | 4 ++-- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/helm-charts/cogstack-cohorter-helm/templates/nl2dsl-deployment.yaml b/helm-charts/cogstack-cohorter-helm/templates/nl2dsl-deployment.yaml index cf16b14..421e281 100644 --- a/helm-charts/cogstack-cohorter-helm/templates/nl2dsl-deployment.yaml +++ b/helm-charts/cogstack-cohorter-helm/templates/nl2dsl-deployment.yaml @@ -4,12 +4,12 @@ kind: Deployment metadata: name: {{ include "cogstack-cohorter-helm.fullname" . }}-nl2dsl labels: - {{- include "cogstack-cohorter-helm.nl2dsl.labels" . | nindent 4 }} + {{- include "cogstack-cohorter-helm.labels" . | nindent 4 }} spec: replicas: {{ .Values.nl2dsl.replicaCount }} selector: matchLabels: - {{- include "cogstack-cohorter-helm.nl2dsl.selectorLabels" . | nindent 6 }} + {{- include "cogstack-cohorter-helm.selectorLabels" . | nindent 6 }} template: metadata: {{- with .Values.podAnnotations }} @@ -17,7 +17,7 @@ spec: {{- toYaml . | nindent 8 }} {{- end }} labels: - {{- include "cogstack-cohorter-helm.nl2dsl.labels" . | nindent 8 }} + {{- include "cogstack-cohorter-helm.labels" . | nindent 8 }} {{- with .Values.podLabels }} {{- toYaml . | nindent 8 }} {{- end }} diff --git a/helm-charts/cogstack-cohorter-helm/templates/nl2dsl-service.yaml b/helm-charts/cogstack-cohorter-helm/templates/nl2dsl-service.yaml index 40383af..376c5bb 100644 --- a/helm-charts/cogstack-cohorter-helm/templates/nl2dsl-service.yaml +++ b/helm-charts/cogstack-cohorter-helm/templates/nl2dsl-service.yaml @@ -4,7 +4,7 @@ kind: Service metadata: name: {{ include "cogstack-cohorter-helm.fullname" . }}-nl2dsl labels: - {{- include "cogstack-cohorter-helm.nl2dsl.labels" . | nindent 4 }} + {{- include "cogstack-cohorter-helm.labels" . | nindent 4 }} spec: type: {{ .Values.nl2dsl.service.type }} ports: @@ -13,5 +13,5 @@ spec: protocol: TCP name: http selector: - {{- include "cogstack-cohorter-helm.nl2dsl.selectorLabels" . | nindent 4 }} + {{- include "cogstack-cohorter-helm.selectorLabels" . | nindent 4 }} {{- end }} diff --git a/helm-charts/cogstack-cohorter-helm/templates/webapp-deployment.yaml b/helm-charts/cogstack-cohorter-helm/templates/webapp-deployment.yaml index 54ed866..7c72f46 100644 --- a/helm-charts/cogstack-cohorter-helm/templates/webapp-deployment.yaml +++ b/helm-charts/cogstack-cohorter-helm/templates/webapp-deployment.yaml @@ -5,14 +5,14 @@ kind: Deployment metadata: name: {{ $fullName }}-webapp labels: - {{- include "cogstack-cohorter-helm.webapp.labels" . | nindent 4 }} + {{- include "cogstack-cohorter-helm.labels" . | nindent 4 }} spec: {{- if not .Values.autoscaling.enabled }} replicas: {{ .Values.webapp.replicaCount }} {{- end }} selector: matchLabels: - {{- include "cogstack-cohorter-helm.webapp.selectorLabels" . | nindent 6 }} + {{- include "cogstack-cohorter-helm.selectorLabels" . | nindent 6 }} template: metadata: {{- with .Values.podAnnotations }} @@ -20,7 +20,7 @@ spec: {{- toYaml . | nindent 8 }} {{- end }} labels: - {{- include "cogstack-cohorter-helm.webapp.labels" . | nindent 8 }} + {{- include "cogstack-cohorter-helm.labels" . | nindent 8 }} {{- with .Values.podLabels }} {{- toYaml . | nindent 8 }} {{- end }} diff --git a/helm-charts/cogstack-cohorter-helm/templates/webapp-pvc.yaml b/helm-charts/cogstack-cohorter-helm/templates/webapp-pvc.yaml index 7ae1a86..2dde988 100644 --- a/helm-charts/cogstack-cohorter-helm/templates/webapp-pvc.yaml +++ b/helm-charts/cogstack-cohorter-helm/templates/webapp-pvc.yaml @@ -4,7 +4,7 @@ kind: PersistentVolumeClaim metadata: name: {{ include "cogstack-cohorter-helm.fullname" . }}-webapp-data labels: - {{- include "cogstack-cohorter-helm.webapp.labels" . | nindent 4 }} + {{- include "cogstack-cohorter-helm.labels" . | nindent 4 }} spec: accessModes: - {{ .Values.webapp.persistence.accessMode }} diff --git a/helm-charts/cogstack-cohorter-helm/templates/webapp-service.yaml b/helm-charts/cogstack-cohorter-helm/templates/webapp-service.yaml index 8609bab..403a915 100644 --- a/helm-charts/cogstack-cohorter-helm/templates/webapp-service.yaml +++ b/helm-charts/cogstack-cohorter-helm/templates/webapp-service.yaml @@ -4,7 +4,7 @@ kind: Service metadata: name: {{ include "cogstack-cohorter-helm.fullname" . }}-webapp labels: - {{- include "cogstack-cohorter-helm.webapp.labels" . | nindent 4 }} + {{- include "cogstack-cohorter-helm.labels" . | nindent 4 }} spec: type: {{ .Values.webapp.service.type }} ports: @@ -13,5 +13,5 @@ spec: protocol: TCP name: http selector: - {{- include "cogstack-cohorter-helm.webapp.selectorLabels" . | nindent 4 }} + {{- include "cogstack-cohorter-helm.selectorLabels" . | nindent 4 }} {{- end }} From dabd83315657d152a14aedc5a29ae1bd36c23278 Mon Sep 17 00:00:00 2001 From: jocelyneholdbrook Date: Mon, 20 Apr 2026 16:30:04 +0100 Subject: [PATCH 8/8] feat(cogstack-cohorter): Remove helm repo step from k8s chart build pipeline --- .github/workflows/kubernetes-charts-build.yaml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/workflows/kubernetes-charts-build.yaml b/.github/workflows/kubernetes-charts-build.yaml index b16e072..5519c12 100644 --- a/.github/workflows/kubernetes-charts-build.yaml +++ b/.github/workflows/kubernetes-charts-build.yaml @@ -111,11 +111,6 @@ jobs: fi echo "chart_version=$CHART_VERSION" >> "$GITHUB_OUTPUT" - - name: Add Helm repositories - run: | - helm repo add ollama https://otwld.github.io/ollama-helm/ - helm repo update - - name: Recursive dependency update (all charts, including nested) # Waiting on helm recursive feature https://github.com/helm/helm/pull/30855 # Could alternatively switch to helm "cascade" plugin