2026-10-29 23:23:03 -07:00
|
|
|
# clef-flash: a decision model, not a chat model.
|
|
|
|
|
#
|
|
|
|
|
# It reads a `state` plus a schema of typed `questions` and returns one probability
|
|
|
|
|
# per allowed option in a single prefill pass. There is no decoding, no sampling and
|
|
|
|
|
# no free-form text. The public route is /v1/systemone, and its request and response
|
|
|
|
|
# shapes are Clef's own: no `messages`, no `choices`.
|
|
|
|
|
#
|
|
|
|
|
# Why not SGLang: Clef support landed in SGLang v0.5.22, which upstream publishes only
|
|
|
|
|
# on CUDA 13. CUDA 13 dropped Volta kernels and every card here is a Tesla V100
|
|
|
|
|
# (compute capability 7.0), so SGLang aborts with "no kernel image is available for
|
|
|
|
|
# execution on the device". SGLang also states sm75 as its floor regardless.
|
|
|
|
|
#
|
|
|
|
|
# So this runs the implementation the model ships: joint_schema_model.py, which has
|
|
|
|
|
# load_release_model, encode_record, collate_records and systemone. The weights are
|
|
|
|
|
# 17.75 GiB of BF16, and fp16 costs the same bytes per parameter. On one 32 GiB card
|
|
|
|
|
# that leaves room for a 32k state, which is why this is one GPU and not two: with
|
|
|
|
|
# device_map over two cards the embedding table and the tensors indexing into it land
|
|
|
|
|
# on different devices, and the forward pass dies with
|
|
|
|
|
# "indices should be either on cpu or on the same device as the indexed tensor".
|
|
|
|
|
#
|
|
|
|
|
# Known gap: the image cannot be pushed to the registry. Cloudflare fronts
|
|
|
|
|
# forgejo.riotpiao.com and answers 413 Payload Too Large for any layer above 100 MB,
|
|
|
|
|
# and the torch base alone is 7.58 GB. So this mounts app.py from a ConfigMap and pip
|
|
|
|
|
# installs on every start. A cold start therefore needs PyPI reachable, and takes
|
|
|
|
|
# about four minutes. Build the image in-cluster, or unproxy the registry host, before
|
|
|
|
|
# treating this as production.
|
|
|
|
|
apiVersion: apps/v1
|
|
|
|
|
kind: Deployment
|
|
|
|
|
metadata:
|
|
|
|
|
name: clef-serve
|
|
|
|
|
namespace: llm-serving
|
|
|
|
|
labels:
|
|
|
|
|
app: clef-serve
|
|
|
|
|
app.kubernetes.io/part-of: llm-serving
|
|
|
|
|
spec:
|
2026-10-31 19:40:27 -07:00
|
|
|
# Parked at 0 to free the only V100 that was not serving anything, so candle-vllm can
|
|
|
|
|
# run its Phase 0 on one card. All four cards were allocated (coder 2, reasoning 1,
|
|
|
|
|
# this 1) and a new GPU workload had nowhere to land.
|
|
|
|
|
#
|
|
|
|
|
# This is the only GPU consumer in the namespace that is not vLLM, which is why it is
|
|
|
|
|
# the first card released: the standing instruction is to keep the vLLM pods healthy
|
|
|
|
|
# until candle-vllm answers, and this pod is not one of them.
|
|
|
|
|
#
|
|
|
|
|
# It was serving nothing. The last 30 log lines at park time were 30/30 kubelet
|
|
|
|
|
# /health probes and no inference request since start (2026-10-30T06:23:40Z). Nothing
|
|
|
|
|
# in git selects this service. Restore it once the rollout leaves a card free:
|
|
|
|
|
# 30B-A3B takes 1, Coder-Next TP=2 takes 2, so card 4 is this one.
|
|
|
|
|
replicas: 0
|
2026-10-29 23:23:03 -07:00
|
|
|
# Recreate, not RollingUpdate. Every GPU card is allocated, so a rolling update leaves
|
|
|
|
|
# the new pod Pending forever waiting for a card the old pod still holds.
|
|
|
|
|
strategy:
|
|
|
|
|
type: Recreate
|
|
|
|
|
selector:
|
|
|
|
|
matchLabels:
|
|
|
|
|
app: clef-serve
|
|
|
|
|
template:
|
|
|
|
|
metadata:
|
|
|
|
|
labels:
|
|
|
|
|
app: clef-serve
|
|
|
|
|
app.kubernetes.io/part-of: llm-serving
|
|
|
|
|
spec:
|
|
|
|
|
runtimeClassName: nvidia
|
|
|
|
|
nodeSelector:
|
|
|
|
|
kubernetes.io/hostname: worker-1
|
|
|
|
|
containers:
|
|
|
|
|
- name: clef
|
|
|
|
|
image: pytorch/pytorch:2.8.0-cuda12.8-cudnn9-runtime
|
|
|
|
|
command: ["/bin/bash","-lc"]
|
|
|
|
|
args:
|
|
|
|
|
- |
|
|
|
|
|
set -e
|
|
|
|
|
pip install --no-cache-dir transformers==5.10.2 accelerate fastapi 'uvicorn[standard]' pillow safetensors sentencepiece protobuf
|
|
|
|
|
exec uvicorn app:create_app --factory --host 0.0.0.0 --port 8080
|
|
|
|
|
workingDir: /app
|
|
|
|
|
env:
|
|
|
|
|
- name: PYTHONUNBUFFERED
|
|
|
|
|
value: "1"
|
|
|
|
|
- name: MODEL_PATH
|
|
|
|
|
value: /mnt/models/clef-flash
|
|
|
|
|
# V100 has no bf16, and the release weights are bf16. fp16 is the same width.
|
|
|
|
|
- name: DTYPE
|
|
|
|
|
value: float16
|
|
|
|
|
# The state is truncated at this length without warning, so raise it only with
|
|
|
|
|
# spare VRAM: 17.75 GiB of weights plus 128 KiB of KV per token.
|
|
|
|
|
- name: MAX_LEN
|
|
|
|
|
value: "32768"
|
|
|
|
|
ports:
|
|
|
|
|
- containerPort: 8080
|
|
|
|
|
# A cold start pip-installs and loads 17.75 GiB, measured at about four minutes.
|
|
|
|
|
startupProbe:
|
|
|
|
|
httpGet:
|
|
|
|
|
path: /health
|
|
|
|
|
port: 8080
|
|
|
|
|
failureThreshold: 120
|
|
|
|
|
periodSeconds: 10
|
|
|
|
|
readinessProbe:
|
|
|
|
|
httpGet:
|
|
|
|
|
path: /health
|
|
|
|
|
port: 8080
|
|
|
|
|
periodSeconds: 10
|
|
|
|
|
resources:
|
|
|
|
|
requests:
|
|
|
|
|
cpu: "4"
|
|
|
|
|
memory: 12Gi
|
|
|
|
|
nvidia.com/gpu: "1"
|
|
|
|
|
limits:
|
|
|
|
|
cpu: "8"
|
|
|
|
|
memory: 40Gi
|
|
|
|
|
nvidia.com/gpu: "1"
|
|
|
|
|
volumeMounts:
|
|
|
|
|
- name: app
|
|
|
|
|
mountPath: /app
|
|
|
|
|
- name: models
|
|
|
|
|
mountPath: /mnt/models
|
|
|
|
|
readOnly: true
|
|
|
|
|
volumes:
|
|
|
|
|
- name: app
|
|
|
|
|
configMap:
|
|
|
|
|
name: clef-serve-app
|
|
|
|
|
- name: models
|
|
|
|
|
persistentVolumeClaim:
|
|
|
|
|
claimName: llm-models
|
|
|
|
|
---
|
|
|
|
|
apiVersion: v1
|
|
|
|
|
kind: Service
|
|
|
|
|
metadata:
|
|
|
|
|
name: clef-serve
|
|
|
|
|
namespace: llm-serving
|
|
|
|
|
labels:
|
|
|
|
|
app.kubernetes.io/part-of: llm-serving
|
|
|
|
|
spec:
|
|
|
|
|
selector:
|
|
|
|
|
app: clef-serve
|
|
|
|
|
ports:
|
|
|
|
|
- name: http
|
|
|
|
|
port: 80
|
|
|
|
|
targetPort: 8080
|