-
Notifications
You must be signed in to change notification settings - Fork 13
agentevals docker build and helm deploy #66
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| .venv | ||
| **/__pycache__ | ||
| *.py[cod] | ||
| .git | ||
| .gitignore | ||
| tests | ||
| .pytest_cache | ||
| .ruff_cache | ||
| htmlcov | ||
| .coverage | ||
| ui/node_modules | ||
| dist | ||
| *.egg-info | ||
| **/*.egg-info | ||
| agents.md | ||
| .cursor |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| # syntax=docker/dockerfile:1 | ||
|
|
||
| FROM node:25-bookworm-slim AS ui | ||
| WORKDIR /build/ui | ||
| COPY ui/package.json ui/package-lock.json ./ | ||
| # Skip lifecycle scripts during ci, then rebuild esbuild in its own layer — avoids ETXTBSY when | ||
| # install.js execs the binary while overlayfs still has the file busy (common with BuildKit). | ||
| RUN npm ci --ignore-scripts | ||
| RUN npm rebuild esbuild | ||
| COPY ui/ ./ | ||
| RUN npm run build | ||
|
|
||
| FROM python:3.14-slim-bookworm | ||
|
|
||
| WORKDIR /app | ||
|
|
||
| # Install uv binary only (no pip); same approach as astral-sh/uv's Dockerfile. | ||
| # https://github.com/astral-sh/uv/blob/6d889fd53d5c108d304c5a4085eb3140ec6a9cdb/Dockerfile#L21 | ||
| COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv | ||
|
|
||
| COPY pyproject.toml uv.lock README.md ./ | ||
| COPY packages ./packages | ||
| COPY src ./src | ||
|
|
||
| COPY --from=ui /build/ui/dist ./src/agentevals/_static | ||
|
|
||
| RUN uv sync --frozen --no-dev --extra live \ | ||
| && groupadd --gid 1000 app \ | ||
| && useradd --uid 1000 --gid app --home-dir /app --no-log-init app \ | ||
| && chown -R app:app /app | ||
|
|
||
| USER app | ||
| ENV PATH="/app/.venv/bin:$PATH" | ||
| ENV AGENTEVALS_SERVER_URL=http://127.0.0.1:8001 | ||
|
|
||
| EXPOSE 8001 4318 8080 | ||
|
|
||
| CMD ["agentevals", "serve", "--host", "0.0.0.0", "--port", "8001", "--otlp-port", "4318", "--mcp-port", "8080"] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| apiVersion: v2 | ||
| name: agentevals | ||
| description: agentevals web UI, OTLP HTTP receiver, and MCP (Streamable HTTP) | ||
| type: application | ||
| version: 0.1.0 | ||
| appVersion: "0.5.2" | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we auto-generete this from
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am not sure if modifying the chart from the makefile is a good idea, I think the helm chart should be modified with a new versions not from the build |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| 1. UI and API are available at port {{ .Values.service.http.port }} (Service port name: http). | ||
| 2. OTLP HTTP receiver: port {{ .Values.service.otlpHttp.port }} (OTEL_EXPORTER_OTLP_ENDPOINT=http://<service>:{{ .Values.service.otlpHttp.port }}). | ||
| 3. MCP (Streamable HTTP): port {{ .Values.service.mcp.port }}, path /mcp (e.g. http://<service>:{{ .Values.service.mcp.port }}/mcp). | ||
| {{- if .Values.ephemeralVolume.enabled }} | ||
| 4. An emptyDir is mounted at /tmp with HOME=/tmp/agentevals-home (ephemeral; lost on pod restart). Set ephemeralVolume.enabled=false and readOnlyRootFilesystem=false if you need a writable root without this mount. | ||
| {{- end }} | ||
|
|
||
| Get the Service URL: | ||
| export POD_NAME=$(kubectl get pods --namespace {{ include "agentevals.namespace" . }} -l "app.kubernetes.io/name={{ include "agentevals.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") | ||
| kubectl --namespace {{ include "agentevals.namespace" . }} port-forward $POD_NAME {{ .Values.service.http.port }}:{{ .Values.service.http.port }} | ||
|
|
||
| Health check: GET http://<pod-ip>:{{ .Values.service.http.containerPort }}/api/health |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| {{- define "agentevals.name" -}} | ||
| {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} | ||
| {{- end }} | ||
|
|
||
| {{- define "agentevals.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 }} | ||
|
|
||
| {{- define "agentevals.namespace" -}} | ||
| {{- default .Release.Namespace .Values.namespaceOverride }} | ||
| {{- end }} | ||
|
|
||
| {{- define "agentevals.chart" -}} | ||
| {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} | ||
| {{- end }} | ||
|
|
||
| {{- define "agentevals.image" -}} | ||
| {{- $registry := .Values.image.registry | default .Values.registry -}} | ||
| {{- $tag := .Values.image.tag | default .Values.tag | default .Chart.AppVersion -}} | ||
| {{- if $registry -}} | ||
| {{- printf "%s/%s:%s" $registry .Values.image.repository $tag -}} | ||
| {{- else -}} | ||
| {{- printf "%s:%s" .Values.image.repository $tag -}} | ||
| {{- end -}} | ||
| {{- end }} | ||
|
|
||
| {{- define "agentevals.labels" -}} | ||
| helm.sh/chart: {{ include "agentevals.chart" . }} | ||
| {{ include "agentevals.selectorLabels" . }} | ||
| {{- if .Chart.AppVersion }} | ||
| app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} | ||
| {{- end }} | ||
| app.kubernetes.io/managed-by: {{ .Release.Service }} | ||
| app.kubernetes.io/part-of: agentevals | ||
| {{- end }} | ||
|
|
||
| {{- define "agentevals.selectorLabels" -}} | ||
| app.kubernetes.io/name: {{ include "agentevals.name" . }} | ||
| app.kubernetes.io/instance: {{ .Release.Name }} | ||
| {{- end }} | ||
|
|
||
| {{- define "agentevals.serviceAccountName" -}} | ||
| {{- if .Values.serviceAccount.create }} | ||
| {{- default (include "agentevals.fullname" .) .Values.serviceAccount.name }} | ||
| {{- else }} | ||
| {{- default "default" .Values.serviceAccount.name }} | ||
| {{- end }} | ||
| {{- end }} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,128 @@ | ||
| apiVersion: apps/v1 | ||
| kind: Deployment | ||
| metadata: | ||
| name: {{ include "agentevals.fullname" . }} | ||
| namespace: {{ include "agentevals.namespace" . }} | ||
| labels: | ||
| {{- include "agentevals.labels" . | nindent 4 }} | ||
| spec: | ||
| replicas: {{ .Values.replicaCount }} | ||
| selector: | ||
| matchLabels: | ||
| {{- include "agentevals.selectorLabels" . | nindent 6 }} | ||
| template: | ||
| metadata: | ||
| {{- with .Values.podAnnotations }} | ||
| annotations: | ||
| {{- toYaml . | nindent 8 }} | ||
| {{- end }} | ||
| labels: | ||
| {{- include "agentevals.selectorLabels" . | nindent 8 }} | ||
| {{- with .Values.podLabels }} | ||
| {{- toYaml . | nindent 8 }} | ||
| {{- end }} | ||
| spec: | ||
| {{- with .Values.imagePullSecrets }} | ||
| imagePullSecrets: | ||
| {{- toYaml . | nindent 8 }} | ||
| {{- end }} | ||
| securityContext: | ||
| {{- toYaml .Values.podSecurityContext | nindent 8 }} | ||
| serviceAccountName: {{ include "agentevals.serviceAccountName" . }} | ||
| {{- if .Values.ephemeralVolume.enabled }} | ||
| volumes: | ||
| - name: agentevals-tmp | ||
| {{- if or .Values.ephemeralVolume.sizeLimit (eq .Values.ephemeralVolume.medium "Memory") }} | ||
| emptyDir: | ||
| {{- if eq .Values.ephemeralVolume.medium "Memory" }} | ||
| medium: Memory | ||
| {{- end }} | ||
| {{- with .Values.ephemeralVolume.sizeLimit }} | ||
| sizeLimit: {{ . }} | ||
| {{- end }} | ||
| {{- else }} | ||
| emptyDir: {} | ||
| {{- end }} | ||
| {{- end }} | ||
| containers: | ||
| - name: agentevals | ||
| image: {{ include "agentevals.image" . | quote }} | ||
| imagePullPolicy: {{ .Values.image.pullPolicy | default .Values.imagePullPolicy }} | ||
| {{- if .Values.command }} | ||
| command: | ||
| {{- toYaml .Values.command | nindent 12 }} | ||
| {{- end }} | ||
| {{- if .Values.args }} | ||
| args: | ||
| {{- toYaml .Values.args | nindent 12 }} | ||
| {{- end }} | ||
| env: | ||
| - name: AGENTEVALS_SERVER_URL | ||
| value: "http://127.0.0.1:{{ .Values.service.http.containerPort }}" | ||
| {{- if .Values.ephemeralVolume.enabled }} | ||
| - name: TMPDIR | ||
| value: "/tmp" | ||
| - name: HOME | ||
| value: "/tmp/agentevals-home" | ||
| {{- end }} | ||
| {{- with .Values.env }} | ||
| {{- toYaml . | nindent 12 }} | ||
| {{- end }} | ||
| {{- with .Values.envFrom }} | ||
| envFrom: | ||
| {{- toYaml . | nindent 12 }} | ||
| {{- end }} | ||
| ports: | ||
| - name: http | ||
| containerPort: {{ .Values.service.http.containerPort }} | ||
| protocol: TCP | ||
| - name: otlp-http | ||
| containerPort: {{ .Values.service.otlpHttp.containerPort }} | ||
| protocol: TCP | ||
| - name: mcp | ||
| containerPort: {{ .Values.service.mcp.containerPort }} | ||
| protocol: TCP | ||
| resources: | ||
| {{- toYaml .Values.resources | nindent 12 }} | ||
| securityContext: | ||
| {{- $sc := deepCopy .Values.securityContext }} | ||
| {{- if not .Values.ephemeralVolume.enabled }} | ||
| {{- $_ := set $sc "readOnlyRootFilesystem" false }} | ||
| {{- end }} | ||
| {{- toYaml $sc | nindent 12 }} | ||
| startupProbe: | ||
| httpGet: | ||
| path: /api/health | ||
| port: http | ||
| failureThreshold: 60 | ||
| periodSeconds: 10 | ||
| timeoutSeconds: 5 | ||
| readinessProbe: | ||
| httpGet: | ||
| path: /api/health | ||
| port: http | ||
| initialDelaySeconds: 5 | ||
| periodSeconds: 10 | ||
| livenessProbe: | ||
| httpGet: | ||
| path: /api/health | ||
| port: http | ||
| initialDelaySeconds: 15 | ||
| periodSeconds: 20 | ||
| {{- if .Values.ephemeralVolume.enabled }} | ||
| volumeMounts: | ||
| - name: agentevals-tmp | ||
| mountPath: /tmp | ||
| {{- end }} | ||
| {{- with .Values.nodeSelector }} | ||
| nodeSelector: | ||
| {{- toYaml . | nindent 8 }} | ||
| {{- end }} | ||
| {{- with .Values.affinity }} | ||
| affinity: | ||
| {{- toYaml . | nindent 8 }} | ||
| {{- end }} | ||
| {{- with .Values.tolerations }} | ||
| tolerations: | ||
| {{- toYaml . | nindent 8 }} | ||
| {{- end }} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| apiVersion: v1 | ||
| kind: Service | ||
| metadata: | ||
| name: {{ include "agentevals.fullname" . }} | ||
| namespace: {{ include "agentevals.namespace" . }} | ||
| labels: | ||
| {{- include "agentevals.labels" . | nindent 4 }} | ||
| spec: | ||
| type: {{ .Values.service.type }} | ||
| ports: | ||
| - name: http | ||
| port: {{ .Values.service.http.port }} | ||
| targetPort: http | ||
| protocol: TCP | ||
| - name: otlp-http | ||
| port: {{ .Values.service.otlpHttp.port }} | ||
| targetPort: otlp-http | ||
| protocol: TCP | ||
| - name: mcp | ||
| port: {{ .Values.service.mcp.port }} | ||
| targetPort: mcp | ||
| protocol: TCP | ||
| selector: | ||
| {{- include "agentevals.selectorLabels" . | nindent 4 }} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| {{- if .Values.serviceAccount.create -}} | ||
| apiVersion: v1 | ||
| kind: ServiceAccount | ||
| metadata: | ||
| name: {{ include "agentevals.serviceAccountName" . }} | ||
| namespace: {{ include "agentevals.namespace" . }} | ||
| labels: | ||
| {{- include "agentevals.labels" . | nindent 4 }} | ||
| {{- with .Values.serviceAccount.annotations }} | ||
| annotations: | ||
| {{- toYaml . | nindent 4 }} | ||
| {{- end }} | ||
| automountServiceAccountToken: {{ .Values.serviceAccount.automount }} | ||
| {{- end }} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we'll need to set up the image builds too -- I think this will end up being ghcr.io (instead lof soloio). cc @EItanya as he's the master GH registry setter-upper.