From 7620a5274fd1c67f25a3ff75fd3efa011b4993ac Mon Sep 17 00:00:00 2001 From: Bob Bai Date: Thu, 28 May 2026 11:11:38 -0700 Subject: [PATCH] feat(k8s): add agent service and LiteLLM to the Helm chart The agent-service image is built and runs under single-node compose but had no Helm deployment, and there was no in-cluster LLM gateway. This adds both, mirroring the proven preview/production chart: Agent service - deployment + service (gated on agentService.enabled), wired to in-cluster service DNS using the env names the service actually reads (TEXERA_DASHBOARD_SERVICE_ENDPOINT, LLM_ENDPOINT, WORKFLOW_COMPILING_SERVICE_ENDPOINT, EXECUTION_ENDPOINT_TEMPLATE). - a dedicated /api/agents HTTPRoute plus a BackendTrafficPolicy that consistent-hashes on X-Agent-Workflow-Id, so a workflow's requests always reach the replica holding its in-memory agent. - readiness/liveness on /api/healthcheck. LiteLLM (in-cluster LLM gateway) - deployment + service + config ConfigMap (gated on litellm.enabled). - Postgres persistence enabled by default: a texera_litellm database created by the postgres init script, with DATABASE_URL + STORE_MODEL_IN_DB so keys, spend, and model config survive restarts. - access-control-service wired to LiteLLM (LITELLM_BASE_URL/MASTER_KEY, copilot enabled). A shared Opaque Secret holds the agent gateway key, the LiteLLM master key, and the provider API keys (supply via --set / override; none committed). Closes #5269 --- .../access-control-service-deployment.yaml | 12 +++ .../agent-service-backend-traffic-policy.yaml | 38 +++++++++ .../templates/agent-service-deployment.yaml | 79 +++++++++++++++++ bin/k8s/templates/agent-service-secret.yaml | 34 ++++++++ bin/k8s/templates/agent-service-service.yaml | 32 +++++++ bin/k8s/templates/gateway-routes.yaml | 27 ++++++ bin/k8s/templates/litellm-config.yaml | 27 ++++++ bin/k8s/templates/litellm-deployment.yaml | 84 +++++++++++++++++++ bin/k8s/templates/litellm-service.yaml | 32 +++++++ .../postgresql-init-script-config.yaml | 7 ++ bin/k8s/values-development.yaml | 53 ++++++++++++ bin/k8s/values.yaml | 53 ++++++++++++ 12 files changed, 478 insertions(+) create mode 100644 bin/k8s/templates/agent-service-backend-traffic-policy.yaml create mode 100644 bin/k8s/templates/agent-service-deployment.yaml create mode 100644 bin/k8s/templates/agent-service-secret.yaml create mode 100644 bin/k8s/templates/agent-service-service.yaml create mode 100644 bin/k8s/templates/litellm-config.yaml create mode 100644 bin/k8s/templates/litellm-deployment.yaml create mode 100644 bin/k8s/templates/litellm-service.yaml diff --git a/bin/k8s/templates/access-control-service-deployment.yaml b/bin/k8s/templates/access-control-service-deployment.yaml index f4d4405d331..7939ab547d9 100644 --- a/bin/k8s/templates/access-control-service-deployment.yaml +++ b/bin/k8s/templates/access-control-service-deployment.yaml @@ -50,6 +50,18 @@ spec: value: {{ .Values.workflowComputingUnitPool.name }} - name: KUBERNETES_COMPUTE_UNIT_POOL_NAMESPACE value: {{ .Values.workflowComputingUnitPool.namespace }} + {{- if .Values.litellm.enabled }} + # LLM gateway used to serve /api/chat and /api/models to the agent service. + - name: LITELLM_BASE_URL + value: http://{{ .Release.Name }}-{{ .Values.litellm.name }}-svc:{{ .Values.litellm.service.port }} + - name: LITELLM_MASTER_KEY + valueFrom: + secretKeyRef: + name: {{ .Release.Name }}-agent-service-secret + key: litellm-master-key + - name: GUI_WORKFLOW_WORKSPACE_COPILOT_ENABLED + value: "true" + {{- end }} {{- range .Values.texeraEnvVars }} - name: {{ .name }} value: "{{ .value }}" diff --git a/bin/k8s/templates/agent-service-backend-traffic-policy.yaml b/bin/k8s/templates/agent-service-backend-traffic-policy.yaml new file mode 100644 index 00000000000..dffecbfb60a --- /dev/null +++ b/bin/k8s/templates/agent-service-backend-traffic-policy.yaml @@ -0,0 +1,38 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# Agents are stateful and held in memory per pod, so all requests for a given +# workflow must reach the same replica. The client stamps X-Agent-Workflow-Id on +# every agent request; pin routing to it with a consistent hash. +{{- if .Values.agentService.enabled }} +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: BackendTrafficPolicy +metadata: + name: {{ .Release.Name }}-agent-service-traffic-policy + namespace: {{ .Release.Namespace }} +spec: + targetRefs: + - group: gateway.networking.k8s.io + kind: HTTPRoute + name: {{ .Release.Name }}-agent-service-route + loadBalancer: + type: ConsistentHash + consistentHash: + type: Header + header: + name: X-Agent-Workflow-Id +{{- end }} diff --git a/bin/k8s/templates/agent-service-deployment.yaml b/bin/k8s/templates/agent-service-deployment.yaml new file mode 100644 index 00000000000..c6aeb251753 --- /dev/null +++ b/bin/k8s/templates/agent-service-deployment.yaml @@ -0,0 +1,79 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +{{- if .Values.agentService.enabled }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Release.Name }}-{{ .Values.agentService.name }} + namespace: {{ .Release.Namespace }} + labels: + app: {{ .Release.Name }}-{{ .Values.agentService.name }} +spec: + replicas: {{ .Values.agentService.numOfPods | default 1 }} + selector: + matchLabels: + app: {{ .Release.Name }}-{{ .Values.agentService.name }} + template: + metadata: + labels: + app: {{ .Release.Name }}-{{ .Values.agentService.name }} + spec: + containers: + - name: {{ .Values.agentService.name }} + image: {{ .Values.texera.imageRegistry }}/{{ .Values.agentService.imageName }}:{{ .Values.texera.imageTag }} + imagePullPolicy: {{ .Values.texeraImages.pullPolicy }} + ports: + - containerPort: {{ .Values.agentService.service.port }} + # Env names match the agent service's config schema (agent-service/src/config/env.ts). + env: + - name: PORT + value: "{{ .Values.agentService.service.port }}" + # Dashboard service: workflow CRUD + operator metadata. + - name: TEXERA_DASHBOARD_SERVICE_ENDPOINT + value: http://{{ .Values.webserver.name }}-svc:{{ .Values.webserver.service.port }} + # LLM gateway: access-control-service serves /api/chat and /api/models, + # forwarding to LiteLLM (mirrors the single-node nginx routing). + - name: LLM_ENDPOINT + value: http://{{ .Release.Name }}-{{ .Values.accessControlService.name }}-svc:{{ .Values.accessControlService.service.port }} + - name: WORKFLOW_COMPILING_SERVICE_ENDPOINT + value: http://{{ .Values.workflowCompilingService.name }}-svc:{{ .Values.workflowCompilingService.service.port }} + # Per-computing-unit execution endpoint; "{cuid}" is substituted with the + # computing unit id at request time. + - name: EXECUTION_ENDPOINT_TEMPLATE + value: http://computing-unit-{cuid}.{{ .Values.workflowComputingUnitPool.name }}-svc.{{ .Values.workflowComputingUnitPool.namespace }}.svc.cluster.local:{{ .Values.workflowComputingUnitPool.service.port }} + - name: LLM_API_KEY + valueFrom: + secretKeyRef: + name: {{ .Release.Name }}-agent-service-secret + key: llm-api-key + # The service loads operator metadata from the dashboard service on + # startup, so gate readiness on its health endpoint before the gateway + # routes traffic here. /api/healthcheck needs no auth. + readinessProbe: + httpGet: + path: /api/healthcheck + port: {{ .Values.agentService.service.port }} + initialDelaySeconds: 5 + periodSeconds: 5 + livenessProbe: + httpGet: + path: /api/healthcheck + port: {{ .Values.agentService.service.port }} + initialDelaySeconds: 15 + periodSeconds: 10 +{{- end }} diff --git a/bin/k8s/templates/agent-service-secret.yaml b/bin/k8s/templates/agent-service-secret.yaml new file mode 100644 index 00000000000..61746a0aeb8 --- /dev/null +++ b/bin/k8s/templates/agent-service-secret.yaml @@ -0,0 +1,34 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# Shared secret for the agent service and LiteLLM. Holds the agent's gateway +# key, LiteLLM's master key, and the upstream provider API keys. Provide real +# values via `--set` or a values override file; do not commit them. +{{- if or .Values.agentService.enabled .Values.litellm.enabled }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ .Release.Name }}-agent-service-secret + namespace: {{ .Release.Namespace }} +type: Opaque +stringData: + llm-api-key: "{{ .Values.agentService.env.llmApiKey }}" + litellm-master-key: "{{ .Values.litellm.masterKey }}" + {{- range $key, $value := .Values.litellm.providerApiKeys }} + {{ $key }}: "{{ $value }}" + {{- end }} +{{- end }} diff --git a/bin/k8s/templates/agent-service-service.yaml b/bin/k8s/templates/agent-service-service.yaml new file mode 100644 index 00000000000..ada6a72fa57 --- /dev/null +++ b/bin/k8s/templates/agent-service-service.yaml @@ -0,0 +1,32 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +{{- if .Values.agentService.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ .Release.Name }}-{{ .Values.agentService.name }}-svc + namespace: {{ .Release.Namespace }} +spec: + type: {{ .Values.agentService.service.type }} + selector: + app: {{ .Release.Name }}-{{ .Values.agentService.name }} + ports: + - protocol: TCP + port: {{ .Values.agentService.service.port }} + targetPort: {{ .Values.agentService.service.port }} +{{- end }} diff --git a/bin/k8s/templates/gateway-routes.yaml b/bin/k8s/templates/gateway-routes.yaml index 55dc40f581a..ad3d1dde76a 100644 --- a/bin/k8s/templates/gateway-routes.yaml +++ b/bin/k8s/templates/gateway-routes.yaml @@ -119,6 +119,33 @@ spec: kind: Backend name: texera-dynamic-backend --- +# Agent Service Route (a separate HTTPRoute so the BackendTrafficPolicy can +# target it for consistent-hash routing). Covers REST and the +# /api/agents/:id/react WebSocket. Longest-prefix matching gives /api/agents +# priority over the /api catch-all in the static routes above. +{{- if .Values.agentService.enabled }} +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: {{ .Release.Name }}-agent-service-route + namespace: {{ .Release.Namespace }} +spec: + parentRefs: + - name: {{ .Release.Name }}-gateway + {{- if and .Values.gatewayConfig .Values.gatewayConfig.hostname }} + hostnames: + - {{ .Values.gatewayConfig.hostname }} + {{- end }} + rules: + - matches: + - path: + type: PathPrefix + value: /api/agents + backendRefs: + - name: {{ .Release.Name }}-{{ .Values.agentService.name }}-svc + port: {{ .Values.agentService.service.port }} +{{- end }} +--- # MinIO Route {{- if .Values.minio.gateway.enabled }} apiVersion: gateway.networking.k8s.io/v1 diff --git a/bin/k8s/templates/litellm-config.yaml b/bin/k8s/templates/litellm-config.yaml new file mode 100644 index 00000000000..c4c2aa136d1 --- /dev/null +++ b/bin/k8s/templates/litellm-config.yaml @@ -0,0 +1,27 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +{{- if .Values.litellm.enabled }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ .Release.Name }}-litellm-config + namespace: {{ .Release.Namespace }} +data: + litellm-config.yaml: | +{{ .Values.litellm.config | indent 4 }} +{{- end }} diff --git a/bin/k8s/templates/litellm-deployment.yaml b/bin/k8s/templates/litellm-deployment.yaml new file mode 100644 index 00000000000..6199f4cfc99 --- /dev/null +++ b/bin/k8s/templates/litellm-deployment.yaml @@ -0,0 +1,84 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +{{- if .Values.litellm.enabled }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Release.Name }}-{{ .Values.litellm.name }} + namespace: {{ .Release.Namespace }} + labels: + app: {{ .Release.Name }}-{{ .Values.litellm.name }} +spec: + replicas: {{ .Values.litellm.numOfPods | default 1 }} + selector: + matchLabels: + app: {{ .Release.Name }}-{{ .Values.litellm.name }} + template: + metadata: + labels: + app: {{ .Release.Name }}-{{ .Values.litellm.name }} + spec: + containers: + - name: {{ .Values.litellm.name }} + image: {{ .Values.litellm.image }} + imagePullPolicy: Always + args: + - "--config" + - "/app/config/litellm-config.yaml" + ports: + - containerPort: {{ .Values.litellm.service.port }} + env: + - name: LITELLM_MASTER_KEY + valueFrom: + secretKeyRef: + name: {{ .Release.Name }}-agent-service-secret + key: litellm-master-key + # Postgres persistence: LiteLLM runs its Prisma migrations against this + # database on startup and stores keys, spend, and (with STORE_MODEL_IN_DB) + # model config there, so state survives pod restarts. + - name: DATABASE_URL + value: postgresql://postgres:{{ .Values.postgresql.auth.postgresPassword }}@{{ .Release.Name }}-postgresql:5432/{{ .Values.litellm.databaseName }} + - name: STORE_MODEL_IN_DB + value: "{{ .Values.litellm.storeModelInDb }}" + {{- range $key, $value := .Values.litellm.providerApiKeys }} + - name: {{ $key }} + valueFrom: + secretKeyRef: + name: {{ $.Release.Name }}-agent-service-secret + key: {{ $key }} + {{- end }} + volumeMounts: + - name: litellm-config + mountPath: /app/config + livenessProbe: + httpGet: + path: /health/liveliness + port: {{ .Values.litellm.service.port }} + initialDelaySeconds: 30 + periodSeconds: 10 + readinessProbe: + httpGet: + path: /health/liveliness + port: {{ .Values.litellm.service.port }} + initialDelaySeconds: 15 + periodSeconds: 5 + volumes: + - name: litellm-config + configMap: + name: {{ .Release.Name }}-litellm-config +{{- end }} diff --git a/bin/k8s/templates/litellm-service.yaml b/bin/k8s/templates/litellm-service.yaml new file mode 100644 index 00000000000..00ab096e8bc --- /dev/null +++ b/bin/k8s/templates/litellm-service.yaml @@ -0,0 +1,32 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +{{- if .Values.litellm.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ .Release.Name }}-{{ .Values.litellm.name }}-svc + namespace: {{ .Release.Namespace }} +spec: + type: {{ .Values.litellm.service.type }} + selector: + app: {{ .Release.Name }}-{{ .Values.litellm.name }} + ports: + - protocol: TCP + port: {{ .Values.litellm.service.port }} + targetPort: {{ .Values.litellm.service.port }} +{{- end }} diff --git a/bin/k8s/templates/postgresql-init-script-config.yaml b/bin/k8s/templates/postgresql-init-script-config.yaml index 9b7e5488b5b..f9bb8c4b522 100644 --- a/bin/k8s/templates/postgresql-init-script-config.yaml +++ b/bin/k8s/templates/postgresql-init-script-config.yaml @@ -52,5 +52,12 @@ data: {{ .Files.Get "files/texera_ddl.sql" | indent 6 }} EOF psql -U postgres -f /tmp/texera_ddl.sql +{{- if .Values.litellm.enabled }} + + echo "Initializing LiteLLM database..." + # LiteLLM (Prisma) creates its own tables on startup but the database must exist. + psql -U postgres -tc "SELECT 1 FROM pg_database WHERE datname = '{{ .Values.litellm.databaseName }}'" | grep -q 1 \ + || psql -U postgres -c "CREATE DATABASE {{ .Values.litellm.databaseName }}" +{{- end }} echo "Schema initialization complete." \ No newline at end of file diff --git a/bin/k8s/values-development.yaml b/bin/k8s/values-development.yaml index cbf0183f004..f58ad27c6f3 100644 --- a/bin/k8s/values-development.yaml +++ b/bin/k8s/values-development.yaml @@ -225,6 +225,59 @@ accessControlService: cpu: 1000m memory: 256Mi +agentService: + enabled: true + name: agent-service + numOfPods: 1 + imageName: texera-agent-service + service: + type: ClusterIP + port: 3001 + env: + # Authenticates the agent service to the in-cluster LLM gateway + # (access-control-service / LiteLLM), not to the upstream provider. + llmApiKey: "dummy" + +litellm: + enabled: true + name: litellm + image: ghcr.io/berriai/litellm:main-latest + numOfPods: 1 + service: + type: ClusterIP + port: 4000 + masterKey: "sk-texera-litellm-key" + # Postgres persistence: the database is created by the postgres init script and + # LiteLLM stores keys/spend (and model config when storeModelInDb is true) there. + databaseName: texera_litellm + storeModelInDb: true + # Provider API keys - injected as env vars into the LiteLLM pod and stored in a + # Secret. Set these via --set or a values override file (do NOT commit real keys). + # The config below references them via "os.environ/KEY_NAME". + providerApiKeys: + OPENAI_API_KEY: "" + ANTHROPIC_API_KEY: "" + config: | + litellm_settings: + drop_params: true + model_list: + - model_name: claude-sonnet-4 + litellm_params: + model: claude-sonnet-4-20250514 + api_key: "os.environ/ANTHROPIC_API_KEY" + - model_name: claude-haiku-4.5 + litellm_params: + model: claude-haiku-4-5-20251001 + api_key: "os.environ/ANTHROPIC_API_KEY" + - model_name: gpt-4.1 + litellm_params: + model: gpt-4.1 + api_key: "os.environ/OPENAI_API_KEY" + - model_name: gpt-4.1-mini + litellm_params: + model: gpt-4.1-mini + api_key: "os.environ/OPENAI_API_KEY" + # headless service for the access of computing units workflowComputingUnitPool: createNamespaces: true diff --git a/bin/k8s/values.yaml b/bin/k8s/values.yaml index 2d7c520ff7a..4ebec85cde9 100644 --- a/bin/k8s/values.yaml +++ b/bin/k8s/values.yaml @@ -207,6 +207,59 @@ accessControlService: type: ClusterIP port: 9096 +agentService: + enabled: true + name: agent-service + numOfPods: 1 + imageName: texera-agent-service + service: + type: ClusterIP + port: 3001 + env: + # Authenticates the agent service to the in-cluster LLM gateway + # (access-control-service / LiteLLM), not to the upstream provider. + llmApiKey: "dummy" + +litellm: + enabled: true + name: litellm + image: ghcr.io/berriai/litellm:main-latest + numOfPods: 1 + service: + type: ClusterIP + port: 4000 + masterKey: "sk-texera-litellm-key" + # Postgres persistence: the database is created by the postgres init script and + # LiteLLM stores keys/spend (and model config when storeModelInDb is true) there. + databaseName: texera_litellm + storeModelInDb: true + # Provider API keys - injected as env vars into the LiteLLM pod and stored in a + # Secret. Set these via --set or a values override file (do NOT commit real keys). + # The config below references them via "os.environ/KEY_NAME". + providerApiKeys: + OPENAI_API_KEY: "" + ANTHROPIC_API_KEY: "" + config: | + litellm_settings: + drop_params: true + model_list: + - model_name: claude-sonnet-4 + litellm_params: + model: claude-sonnet-4-20250514 + api_key: "os.environ/ANTHROPIC_API_KEY" + - model_name: claude-haiku-4.5 + litellm_params: + model: claude-haiku-4-5-20251001 + api_key: "os.environ/ANTHROPIC_API_KEY" + - model_name: gpt-4.1 + litellm_params: + model: gpt-4.1 + api_key: "os.environ/OPENAI_API_KEY" + - model_name: gpt-4.1-mini + litellm_params: + model: gpt-4.1-mini + api_key: "os.environ/OPENAI_API_KEY" + # headless service for the access of computing units workflowComputingUnitPool: createNamespaces: true