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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 46 additions & 10 deletions helm/codeapi/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,16 +58,16 @@ verifier through environment variables on the api component, e.g.:

```yaml
api:
extraEnv:
- name: CODEAPI_AUTH_PROVIDER
value: librechat-jwt
- name: CODEAPI_JWT_PUBLIC_KEY # single PEM/base64-DER verifier key
valueFrom:
secretKeyRef:
name: codeapi-jwt-verifier
key: public-key
- name: CODEAPI_JWT_KID
value: my-key-id
extraEnv:
- name: CODEAPI_AUTH_PROVIDER
value: librechat-jwt
- name: CODEAPI_JWT_PUBLIC_KEY # single PEM/base64-DER verifier key
valueFrom:
secretKeyRef:
name: codeapi-jwt-verifier
key: public-key
- name: CODEAPI_JWT_KID
value: my-key-id
```

`CODEAPI_JWT_PUBLIC_KEYS_DIR` (a mounted directory of PEM files) and
Expand All @@ -78,14 +78,35 @@ For development only, `LOCAL_MODE=true` bypasses authentication β€” see
**TLS to Redis.** Set `REDIS_TLS=true` via `extraEnv` on each component when
your Redis (e.g. a managed cache) requires TLS.

**Redis Cluster mode (GCP Memorystore cluster, AWS ElastiCache cluster).** Set
`redis.cluster.enabled=true` and provide the comma-separated startup nodes in
`redis.cluster.nodes`. The chart will set `USE_REDIS_CLUSTER=true` and populate
`REDIS_HOST` with the node list on every component. For TLS with a CA certificate
(recommended for GCP Memorystore), also set:

```yaml
redis:
tls:
enabled: true
caSecretName: my-memorystore-secret # Secret that holds the CA cert
caKey: ca # Key inside the Secret
caMountPath: /etc/redis-tls/ca.crt # Mount path in each pod
useAlternativeDnsLookup: true # Required for GCP Memorystore cluster
```

The CA certificate is mounted from the Secret into every component pod and
passed to ioredis via `REDIS_CA`. No sidecar is needed.

## Quick Start (Local Development)

### 1. Start Minikube

```bash
minikube start --cpus=4 --memory=8192
```

### 2. Build Images Inside Minikube

```bash
# Point docker to minikube's daemon
eval $(minikube docker-env)
Expand All @@ -100,6 +121,7 @@ docker build -t codeapi-package-init:latest -f docker/Dockerfile.package-init .
```

### 3. Install Dependencies & Deploy

```bash
cd helm/codeapi

Expand Down Expand Up @@ -151,6 +173,7 @@ kubectl rollout restart deployment/codeapi-sandbox-runner
```

### 5. Access the API

```bash
# Port forward (in another terminal)
kubectl port-forward svc/codeapi-api 3112:3112
Expand All @@ -164,6 +187,7 @@ curl http://localhost:3112/v1/health
## Commands Reference

### Startup

```bash
# Start minikube
minikube start
Expand All @@ -176,6 +200,7 @@ kubectl port-forward svc/codeapi-api 3112:3112
```

### Check Status

```bash
# View all pods
kubectl get pods
Expand All @@ -190,6 +215,7 @@ kubectl describe pod <pod-name>
```

### Scaling

```bash
# Scale the sandbox execution tier
kubectl scale deployment/codeapi-sandbox-runner --replicas=10
Expand All @@ -200,6 +226,7 @@ helm upgrade codeapi ./helm/codeapi -f ./helm/codeapi/values-local.yaml \
```

### Update After Code Changes

```bash
# Rebuild images (must be in minikube docker env)
eval $(minikube docker-env)
Expand All @@ -212,6 +239,7 @@ kubectl rollout restart deployment/codeapi-sandbox-runner
```

### Teardown

```bash
# Uninstall the Helm release (removes all K8s resources)
helm uninstall codeapi
Expand All @@ -228,12 +256,14 @@ minikube delete
## Testing

### Health Check

```bash
curl http://localhost:3112/v1/health
# Expected: OK
```

### Execute Python Code

```bash
curl -X POST http://localhost:3112/v1/exec \
-H "Content-Type: application/json" \
Expand All @@ -242,6 +272,7 @@ curl -X POST http://localhost:3112/v1/exec \
```

### Verify Horizontal Scaling

```bash
# Check which service-worker processed the job
kubectl logs deployment/codeapi-service-worker --tail=5
Expand Down Expand Up @@ -299,6 +330,7 @@ kubectl logs deployment/codeapi-service-worker --tail=5
## Troubleshooting

### Pod stuck in `ErrImageNeverPull`

```bash
# Images must be built inside minikube's docker
eval $(minikube docker-env)
Expand All @@ -307,13 +339,15 @@ kubectl rollout restart deployment/<deployment-name>
```

### Pod stuck in `CrashLoopBackOff`

```bash
# Check logs
kubectl logs <pod-name> --previous
kubectl describe pod <pod-name>
```

### "runtime is unknown" error

```bash
# Language packages PVC is empty. Check if the init job ran:
kubectl get jobs -l app.kubernetes.io/component=package-init
Expand All @@ -327,12 +361,14 @@ kubectl rollout restart deployment/codeapi-sandbox-runner
```

### Connection refused on port 3112

```bash
# Make sure port-forward is running
kubectl port-forward svc/codeapi-api 3112:3112
```

### MinIO `ImagePullBackOff` (production values)

```bash
# The Bitnami MinIO chart may reference unavailable image tags.
# For local dev, values-local.yaml uses minio.useSimple=true which
Expand Down
70 changes: 69 additions & 1 deletion helm/codeapi/templates/_helpers.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -152,11 +152,15 @@ app.kubernetes.io/component: tool-call-server
{{- end }}

{{/*
Redis host - either from subchart or external
Redis host - either from subchart or external.
In cluster mode this renders the comma-separated node list from redis.cluster.nodes
(falling back to redis.external.host for single-node external deployments).
*/}}
{{- define "codeapi.redis.host" -}}
{{- if .Values.redis.enabled }}
{{- printf "%s-redis-master" .Release.Name }}
{{- else if and .Values.redis.cluster.enabled .Values.redis.cluster.nodes }}
{{- .Values.redis.cluster.nodes }}
{{- else }}
{{- .Values.redis.external.host }}
{{- end }}
Expand All @@ -173,6 +177,70 @@ Redis port
{{- end }}
{{- end }}

{{/*
USE_REDIS_CLUSTER value – "true" when redis.cluster.enabled or when
redis.cluster.nodes contains a comma (auto-detect multiple nodes).
*/}}
{{- define "codeapi.redis.clusterEnabled" -}}
{{- if .Values.redis.cluster.enabled }}
{{- "true" }}
{{- else if and .Values.redis.cluster.nodes (contains "," .Values.redis.cluster.nodes) }}
{{- "true" }}
{{- else }}
{{- "false" }}
{{- end }}
{{- end }}

{{/*
Emit the Redis TLS + CA environment variables and volume mount for each
component that needs it. Renders nothing when redis.tls.enabled is false.
Usage: {{ include "codeapi.redis.tlsEnv" . }}
*/}}
{{- define "codeapi.redis.tlsEnv" -}}
{{- if .Values.redis.tls.enabled }}
- name: REDIS_TLS
value: "true"
{{- if .Values.redis.tls.caSecretName }}
- name: REDIS_CA
value: {{ .Values.redis.tls.caMountPath | quote }}
{{- end }}
{{- end }}
{{- if .Values.redis.useAlternativeDnsLookup }}
- name: REDIS_USE_ALTERNATIVE_DNS_LOOKUP
value: "true"
{{- end }}
{{- end }}

{{/*
Volume definition for the Redis CA certificate secret.
Renders nothing when redis.tls.caSecretName is empty.
Usage: {{ include "codeapi.redis.caVolume" . }}
*/}}
{{- define "codeapi.redis.caVolume" -}}
{{- if and .Values.redis.tls.enabled .Values.redis.tls.caSecretName }}
- name: redis-ca
secret:
secretName: {{ .Values.redis.tls.caSecretName }}
items:
- key: {{ .Values.redis.tls.caKey }}
path: ca.crt
{{- end }}
{{- end }}

{{/*
VolumeMount for the Redis CA certificate inside a container.
Renders nothing when redis.tls.caSecretName is empty.
Usage: {{ include "codeapi.redis.caVolumeMount" . }}
*/}}
{{- define "codeapi.redis.caVolumeMount" -}}
{{- if and .Values.redis.tls.enabled .Values.redis.tls.caSecretName }}
- name: redis-ca
mountPath: {{ .Values.redis.tls.caMountPath | quote }}
subPath: ca.crt
readOnly: true
{{- end }}
{{- end }}

{{/*
Redis NetworkPolicy egress. Kubernetes NetworkPolicy cannot match DNS names,
so external Redis can be scoped with CIDRs when available; otherwise the chart
Expand Down
12 changes: 11 additions & 1 deletion helm/codeapi/templates/api-deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ spec:
secretKeyRef:
name: {{ include "codeapi.fullname" . }}-secrets
key: redis-password
# Service URLs
- name: USE_REDIS_CLUSTER
value: {{ include "codeapi.redis.clusterEnabled" . | quote }}
{{- include "codeapi.redis.tlsEnv" . | nindent 12 }}
- name: FILE_SERVER_URL
value: "http://{{ include "codeapi.fullname" . }}-file-server:{{ .Values.fileServer.service.port }}"
- name: TOOL_CALL_SERVER_URL
Expand Down Expand Up @@ -87,6 +89,14 @@ spec:
periodSeconds: 10
resources:
{{- toYaml .Values.api.resources | nindent 12 }}
{{- with (include "codeapi.redis.caVolumeMount" . | trim) }}
volumeMounts:
{{- . | nindent 12 }}
{{- end }}
{{- with (include "codeapi.redis.caVolume" . | trim) }}
volumes:
{{- . | nindent 8 }}
{{- end }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
Expand Down
11 changes: 11 additions & 0 deletions helm/codeapi/templates/egress-gateway-deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ spec:
secretKeyRef:
name: {{ include "codeapi.fullname" . }}-secrets
key: redis-password
- name: USE_REDIS_CLUSTER
value: {{ include "codeapi.redis.clusterEnabled" . | quote }}
{{- include "codeapi.redis.tlsEnv" . | nindent 12 }}
livenessProbe:
httpGet:
path: /live
Expand All @@ -83,6 +86,14 @@ spec:
periodSeconds: 10
resources:
{{- toYaml .Values.egressGateway.resources | nindent 12 }}
{{- with (include "codeapi.redis.caVolumeMount" . | trim) }}
volumeMounts:
{{- . | nindent 12 }}
{{- end }}
{{- with (include "codeapi.redis.caVolume" . | trim) }}
volumes:
{{- . | nindent 8 }}
{{- end }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
Expand Down
11 changes: 11 additions & 0 deletions helm/codeapi/templates/file-server-deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,9 @@ spec:
secretKeyRef:
name: {{ include "codeapi.fullname" . }}-secrets
key: redis-password
- name: USE_REDIS_CLUSTER
value: {{ include "codeapi.redis.clusterEnabled" . | quote }}
{{- include "codeapi.redis.tlsEnv" . | nindent 12 }}
- name: CODEAPI_INTERNAL_SERVICE_TOKEN
valueFrom:
secretKeyRef:
Expand All @@ -135,6 +138,14 @@ spec:
timeoutSeconds: 5
resources:
{{- toYaml .Values.fileServer.resources | nindent 12 }}
{{- with (include "codeapi.redis.caVolumeMount" . | trim) }}
volumeMounts:
{{- . | nindent 12 }}
{{- end }}
{{- with (include "codeapi.redis.caVolume" . | trim) }}
volumes:
{{- . | nindent 8 }}
{{- end }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
Expand Down
11 changes: 11 additions & 0 deletions helm/codeapi/templates/tool-call-server-deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ spec:
secretKeyRef:
name: {{ include "codeapi.fullname" . }}-secrets
key: redis-password
- name: USE_REDIS_CLUSTER
value: {{ include "codeapi.redis.clusterEnabled" . | quote }}
{{- include "codeapi.redis.tlsEnv" . | nindent 12 }}
- name: CODEAPI_INTERNAL_SERVICE_TOKEN
valueFrom:
secretKeyRef:
Expand All @@ -70,6 +73,14 @@ spec:
periodSeconds: 10
resources:
{{- toYaml .Values.toolCallServer.resources | nindent 12 }}
{{- with (include "codeapi.redis.caVolumeMount" . | trim) }}
volumeMounts:
{{- . | nindent 12 }}
{{- end }}
{{- with (include "codeapi.redis.caVolume" . | trim) }}
volumes:
{{- . | nindent 8 }}
{{- end }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
Expand Down
Loading