feat(deploy): Hetzner Inference supplier fleet — 4 models x {text, chat}

Eight new mainnet suppliers backed by Hetzner's OpenAI-compatible
inference API (https://inference.hetzner.com/api). Pure configuration —
the LLM_BACKEND=openai path is reused unchanged, following the
HuggingFace-router precedent:

  kimi-code[-chat]     Kimi-K2.7-Code            (ctx 262144)
  glm[-chat]           GLM-5.2-NVFP4             (ctx 512000)
  ds-flash-htz[-chat]  DeepSeek-V4-Flash-0731    (ctx 512000)
  qwen35b[-chat]       Qwen/Qwen3.6-35B-A3B-FP8  (ctx 262144)

Uniform 0.2 AP3X per job, default 1 AP3X bonds, no upstream max_tokens
(advert cap = model context length), 300s SLA (1800s for chat sessions).
All four models smoke-tested for non-empty content and tools round-trip.

Runbook: docs/HETZNER_SETUP.md. Adverts, wallets, and host env files are
operator steps after merge; first `up -d` is manual (CD skips projects
with no running containers).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
david
2026-08-11 14:24:51 +02:00
parent 83f81ab5fc
commit 6c5a068fd9
10 changed files with 985 additions and 0 deletions

View File

@@ -0,0 +1,105 @@
# supplier/.env.hetzner.example
#
# Preset for a supplier that uses Hetzner Inference
# (https://inference.hetzner.com) as its COMPUTE BACKEND to fulfil bonded
# inference work on Vector. Hetzner exposes an OpenAI-compatible API, so this
# reuses the existing LLM_BACKEND=openai path with NO code changes — config only.
#
# One template for all 8 Hetzner-backed suppliers (4 models x {one-off, chat}):
# kimi-code[-chat] → Kimi-K2.7-Code (ctx 262144)
# glm[-chat] → GLM-5.2-NVFP4 (ctx 512000)
# ds-flash-htz[-chat] → DeepSeek-V4-Flash-0731 (ctx 512000)
# qwen35b[-chat] → Qwen/Qwen3.6-35B-A3B-FP8 (ctx 262144)
#
# Copy to supplier/.env.<name> on the host (chmod 600) and fill in real values.
# .env.* is gitignored — NEVER commit a populated copy.
#
# Runbook: docs/HETZNER_SETUP.md
#
# Lines marked SECRET must never be shared, logged, or committed.
# ─── Wallet identity ──────────────────────────────────────────────────────
# Generate all four values in one shot (from repo root):
# pnpm --filter @marketplace/supplier tx:gen-keypair --network 1
#
# ALL FOUR are required. The supplier does NOT re-derive address/pkh/pubkey
# from the private key at boot — leaving them unset silently yields an empty
# wallet identity and every escrow is rejected 403 wrong_supplier (this took
# down the TTS supplier once; don't repeat it).
# 64-char hex Ed25519 private key seed for this supplier's wallet. SECRET.
SUPPLIER_PRIV_KEY_HEX=PASTE_64_HEX_CHARS_HERE
SUPPLIER_ADDRESS=PASTE_ADDR1_HERE
SUPPLIER_PKH=PASTE_28_BYTE_HEX_HERE
SUPPLIER_PUB_KEY_HEX=PASTE_32_BYTE_HEX_HERE
# ─── Chain plumbing (mainnet) ─────────────────────────────────────────────
OGMIOS_URL=wss://ogmios.vector.mainnet.apexfusion.org
NETWORK_ID=1
VECTOR_ZERO_TIME_MS=1756485600000
# CIP-33 reference scripts (shared mainnet deployment).
ESCROW_REF_UTXO=c8d84c6d67ec67a1efe5e9c6c06d53020e05d1bb96d1c55ecb1eb7d5010c4d54#0
ADVERT_REF_UTXO=c8d84c6d67ec67a1efe5e9c6c06d53020e05d1bb96d1c55ecb1eb7d5010c4d54#1
# OutputReference of this supplier's on-chain advertisement UTxO.
# Post it first (see tx:post-advert below), then paste the printed "<txHash>#0".
ADVERT_REF=PASTE_TXHASH_HERE#0
# LIVE_CHAIN: only the literal "1" opts in to real Claim/Submit txs.
LIVE_CHAIN=1
# ─── Backend: Hetzner Inference (OpenAI-compatible) ───────────────────────
LLM_BACKEND=openai
# Hetzner base URL. The supplier appends /v1/chat/completions itself, so do
# NOT include the /api/v1 → only up to /api.
OPENAI_BASE_URL=https://inference.hetzner.com/api
# Hetzner Inference API key. Shared across all 8 Hetzner-backed suppliers —
# rate limits apply to the aggregate. SECRET.
OPENAI_API_KEY=PASTE_HETZNER_KEY_HERE
# All four models are reasoning-style; generations can be slow.
OPENAI_TIMEOUT_MS=300000
# Do NOT set OPENAI_REASONING — `reasoning:{enabled:false}` is an
# OpenRouter-only param.
# OPENAI_REASONING=
# OPENAI_MAX_TOKENS intentionally unset: no max_tokens is forwarded upstream,
# Hetzner grants context − prompt. The advert's max_output_tokens (= model
# context length) only caps what buyers may request.
# OPENAI_MAX_TOKENS=
PORT=8080
# ─── tx:post-advert — run once before first deploy ────────────────────────
# One-off (llm.text.generate.v1) suppliers, from repo root:
# pnpm --filter @marketplace/supplier tx:post-advert \
# --capability-id llm.text.generate.v1 \
# --model <EXACT Hetzner model id, see table above> \
# --max-output-tokens <262144 or 512000 per table> \
# --max-processing-ms 300000 \
# --price-lovelace 200000 \
# --endpoint-url https://mp-suppliers-<name>.vector.apexfusion.org
#
# Chat (llm.chat.v1) suppliers: same, but
# --capability-id llm.chat.v1 --max-processing-ms 1800000
# (the session spans the whole conversation; Claim+Submit must land before
# deliver_by — mirrors the kimi-chat fleet).
#
# CRITICAL: --model goes on-chain AND is sent verbatim to Hetzner as the API
# `model` param (jobRunner uses advert.model). It must be the EXACT id from
# GET https://inference.hetzner.com/api/v1/models — including the "Qwen/"
# prefix on Qwen/Qwen3.6-35B-A3B-FP8.
ADVERT_CAPABILITY_ID=llm.text.generate.v1
ADVERT_MODEL=GLM-5.2-NVFP4
ADVERT_MAX_OUTPUT_TOKENS=512000
ADVERT_MAX_PROCESSING_MS=300000
ADVERT_PRICE_LOVELACE=200000
ADVERT_SUPPLIER_BOND_LOVELACE=1000000
ADVERT_BUYER_BOND_LOVELACE=1000000
ADVERT_ENDPOINT_URL=https://mp-suppliers-glm.vector.apexfusion.org
ADVERT_DETAIL_URI=
ADVERT_DETAIL_HASH=