Compare commits
5 Commits
feat/mainn
...
feat/hetzn
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
88e549bd1d | ||
|
|
6c5a068fd9 | ||
|
|
83f81ab5fc | ||
|
|
6859486334 | ||
|
|
5377d379b7 |
100
deploy/mainnet/docker-compose.supplier-ds-flash-htz-chat.yml
Normal file
100
deploy/mainnet/docker-compose.supplier-ds-flash-htz-chat.yml
Normal file
@@ -0,0 +1,100 @@
|
||||
# deploy/mainnet/docker-compose.supplier-ds-flash-htz-chat.yml — Supplier — MAINNET
|
||||
# (Hetzner Inference / DeepSeek-V4-Flash-0731 — MULTI-TURN CHAT capability `llm.chat.v1`)
|
||||
#
|
||||
# The chat-session sibling of docker-compose.supplier-ds-flash-htz.yml. Same
|
||||
# image, same Hetzner backend (DeepSeek-V4-Flash-0731), but CAPABILITY_KIND=chat-session so
|
||||
# it mounts /v1/chat/{start,message,end} (escrow bookends, off-chain streaming
|
||||
# turns) instead of the one-off /v1/chat/completions route.
|
||||
#
|
||||
# A SEPARATE process/advert from the one-off supplier on purpose: the chat
|
||||
# session holds the supplier's single-slot lock for the whole conversation
|
||||
# (minutes), which would starve one-off traffic if co-hosted.
|
||||
#
|
||||
# One of 8 Hetzner-backed suppliers (4 models x {one-off, chat-session});
|
||||
# fleet runbook: docs/HETZNER_SETUP.md.
|
||||
#
|
||||
# Pre-flight:
|
||||
# 1. DNS: mp-suppliers-ds-flash-htz-chat.vector.apexfusion.org → host
|
||||
# (wildcard *.vector.apexfusion.org already in place via Cloudflare)
|
||||
# 2. supplier/.env.ds-flash-htz-chat written on host (chmod 600) with at minimum:
|
||||
# SUPPLIER_PRIV_KEY_HEX, SUPPLIER_ADDRESS, SUPPLIER_PKH,
|
||||
# SUPPLIER_PUB_KEY_HEX, OPENAI_API_KEY, OGMIOS_URL, NETWORK_ID=1,
|
||||
# VECTOR_ZERO_TIME_MS, ESCROW_REF_UTXO, ADVERT_REF_UTXO, ADVERT_REF,
|
||||
# LIVE_CHAIN=1
|
||||
# The advert must be posted with --capability-id llm.chat.v1 and a large
|
||||
# --max-processing-ms (~1800000) so Claim+Submit both land before deliver_by.
|
||||
# 3. Networks already exist (created by the canonical supplier compose):
|
||||
# docker network create marketplace-mainnet-net # one-shot
|
||||
# # traefik-net is created by the Traefik project
|
||||
|
||||
name: marketplace-mainnet-supplier-ds-flash-htz-chat
|
||||
|
||||
services:
|
||||
supplier:
|
||||
build:
|
||||
context: ../..
|
||||
dockerfile: supplier/Dockerfile
|
||||
container_name: marketplace-mainnet-supplier-ds-flash-htz-chat
|
||||
restart: unless-stopped
|
||||
env_file:
|
||||
# Self-contained env for this Hetzner-backed variant. Holds this
|
||||
# supplier's distinct SUPPLIER_PRIV_KEY_HEX (+ derived wallet vars),
|
||||
# ADVERT_REF, the shared Hetzner OPENAI_API_KEY, and the shared mainnet
|
||||
# plumbing duplicated in full (NETWORK_ID, OGMIOS_URL, VECTOR_ZERO_TIME_MS,
|
||||
# ref-script UTxOs, LIVE_CHAIN). No env_file ordering / override magic.
|
||||
- path: ../../supplier/.env.ds-flash-htz-chat
|
||||
required: false
|
||||
environment:
|
||||
# Multi-turn chat session capability (mounts /v1/chat/{start,message,end}).
|
||||
CAPABILITY_KIND: chat-session
|
||||
LLM_BACKEND: openai
|
||||
# No /v1 suffix — supplier/src/openai.ts appends /v1/chat/completions
|
||||
# (Hetzner serves at https://inference.hetzner.com/api/v1/...).
|
||||
OPENAI_BASE_URL: https://inference.hetzner.com/api
|
||||
# Reasoning-style models can be slow; mirror the one-off compose.
|
||||
OPENAI_TIMEOUT_MS: "300000"
|
||||
# Idle end (supplier Submits the transcript): single-slot suppliers must
|
||||
# free up quickly (the gateway transparently reopens a session on the
|
||||
# next turn). Mirrors the kimi-chat fleet.
|
||||
CHAT_IDLE_TIMEOUT_MS: "900000"
|
||||
CHAT_SETTLE_MODE: ticket
|
||||
MAX_CHAT_SESSIONS: "8"
|
||||
PORT: "8080"
|
||||
# Enable LiveOgmiosProvider so Claim/Submit actually broadcast.
|
||||
LIVE_CHAIN: "1"
|
||||
# NETWORK_ID and OGMIOS_URL intentionally not pinned here — they MUST
|
||||
# come from the operator-provided supplier/.env.ds-flash-htz-chat on a mainnet host.
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "wget -qO- http://localhost:8080/healthz >/dev/null || exit 1"]
|
||||
interval: 20s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
start_period: 15s
|
||||
networks:
|
||||
- marketplace-mainnet-net
|
||||
- traefik-net
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.docker.network=traefik-net"
|
||||
- "traefik.http.routers.mp-suppliers-ds-flash-htz-chat-mainnet.entrypoints=web"
|
||||
- "traefik.http.routers.mp-suppliers-ds-flash-htz-chat-mainnet.rule=Host(`mp-suppliers-ds-flash-htz-chat.vector.apexfusion.org`)"
|
||||
- "traefik.http.routers.mp-suppliers-ds-flash-htz-chat-mainnet.middlewares=mp-suppliers-ds-flash-htz-chat-mainnet-https"
|
||||
- "traefik.http.middlewares.mp-suppliers-ds-flash-htz-chat-mainnet-https.redirectscheme.scheme=https"
|
||||
- "traefik.http.middlewares.mp-suppliers-ds-flash-htz-chat-mainnet-https.redirectscheme.permanent=true"
|
||||
- "traefik.http.routers.mp-suppliers-ds-flash-htz-chat-mainnet-tls.entrypoints=websecure"
|
||||
- "traefik.http.routers.mp-suppliers-ds-flash-htz-chat-mainnet-tls.rule=Host(`mp-suppliers-ds-flash-htz-chat.vector.apexfusion.org`)"
|
||||
- "traefik.http.routers.mp-suppliers-ds-flash-htz-chat-mainnet-tls.tls.certresolver=letsencrypt"
|
||||
- "traefik.http.services.mp-suppliers-ds-flash-htz-chat-mainnet.loadbalancer.server.port=8080"
|
||||
logging:
|
||||
driver: json-file
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "3"
|
||||
|
||||
networks:
|
||||
marketplace-mainnet-net:
|
||||
external: true
|
||||
name: marketplace-mainnet-net
|
||||
traefik-net:
|
||||
external: true
|
||||
name: traefik-net
|
||||
88
deploy/mainnet/docker-compose.supplier-ds-flash-htz.yml
Normal file
88
deploy/mainnet/docker-compose.supplier-ds-flash-htz.yml
Normal file
@@ -0,0 +1,88 @@
|
||||
# deploy/mainnet/docker-compose.supplier-ds-flash-htz.yml — Supplier — MAINNET
|
||||
# (Hetzner Inference / DeepSeek-V4-Flash-0731 — one-off `llm.text.generate.v1`)
|
||||
#
|
||||
# Hetzner-backed supplier. LLM_BACKEND=openai pointed directly at Hetzner's
|
||||
# hosted inference API (OpenAI-compatible at https://inference.hetzner.com/api).
|
||||
# No local proxy container — reachable over public HTTPS.
|
||||
#
|
||||
# One of 8 Hetzner-backed suppliers (4 models x {one-off, chat-session});
|
||||
# fleet runbook: docs/HETZNER_SETUP.md.
|
||||
#
|
||||
# Pre-flight:
|
||||
# 1. DNS: mp-suppliers-ds-flash-htz.vector.apexfusion.org → host
|
||||
# (wildcard *.vector.apexfusion.org already in place via Cloudflare)
|
||||
# 2. supplier/.env.ds-flash-htz written on host (chmod 600) with at minimum:
|
||||
# SUPPLIER_PRIV_KEY_HEX, SUPPLIER_ADDRESS, SUPPLIER_PKH,
|
||||
# SUPPLIER_PUB_KEY_HEX, OPENAI_API_KEY, OGMIOS_URL, NETWORK_ID=1,
|
||||
# VECTOR_ZERO_TIME_MS, ESCROW_REF_UTXO, ADVERT_REF_UTXO, ADVERT_REF,
|
||||
# LIVE_CHAIN=1
|
||||
# 3. Networks already exist (created by the canonical supplier compose):
|
||||
# docker network create marketplace-mainnet-net # one-shot
|
||||
# # traefik-net is created by the Traefik project
|
||||
|
||||
name: marketplace-mainnet-supplier-ds-flash-htz
|
||||
|
||||
services:
|
||||
supplier:
|
||||
build:
|
||||
context: ../..
|
||||
dockerfile: supplier/Dockerfile
|
||||
container_name: marketplace-mainnet-supplier-ds-flash-htz
|
||||
restart: unless-stopped
|
||||
env_file:
|
||||
# Self-contained env for this Hetzner-backed variant. Holds this
|
||||
# supplier's distinct SUPPLIER_PRIV_KEY_HEX (+ derived wallet vars),
|
||||
# ADVERT_REF, the shared Hetzner OPENAI_API_KEY, and the shared mainnet
|
||||
# plumbing duplicated in full (NETWORK_ID, OGMIOS_URL, VECTOR_ZERO_TIME_MS,
|
||||
# ref-script UTxOs, LIVE_CHAIN). No env_file ordering / override magic.
|
||||
- path: ../../supplier/.env.ds-flash-htz
|
||||
required: false
|
||||
environment:
|
||||
LLM_BACKEND: openai
|
||||
# No /v1 suffix — supplier/src/openai.ts appends /v1/chat/completions
|
||||
# (Hetzner serves at https://inference.hetzner.com/api/v1/...).
|
||||
OPENAI_BASE_URL: https://inference.hetzner.com/api
|
||||
# Reasoning-style models can be slow; matches the advert's
|
||||
# max_processing_ms (300000).
|
||||
OPENAI_TIMEOUT_MS: "300000"
|
||||
# OPENAI_MAX_TOKENS intentionally unset: no max_tokens forwarded upstream,
|
||||
# the provider grants context − prompt. Advert cap = model context length.
|
||||
PORT: "8080"
|
||||
# Enable LiveOgmiosProvider so submitTx/awaitTx actually broadcast.
|
||||
LIVE_CHAIN: "1"
|
||||
# NETWORK_ID and OGMIOS_URL intentionally not pinned here — they MUST
|
||||
# come from the operator-provided supplier/.env.ds-flash-htz on a mainnet host.
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "wget -qO- http://localhost:8080/healthz >/dev/null || exit 1"]
|
||||
interval: 20s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
start_period: 15s
|
||||
networks:
|
||||
- marketplace-mainnet-net
|
||||
- traefik-net
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.docker.network=traefik-net"
|
||||
- "traefik.http.routers.mp-suppliers-ds-flash-htz-mainnet.entrypoints=web"
|
||||
- "traefik.http.routers.mp-suppliers-ds-flash-htz-mainnet.rule=Host(`mp-suppliers-ds-flash-htz.vector.apexfusion.org`)"
|
||||
- "traefik.http.routers.mp-suppliers-ds-flash-htz-mainnet.middlewares=mp-suppliers-ds-flash-htz-mainnet-https"
|
||||
- "traefik.http.middlewares.mp-suppliers-ds-flash-htz-mainnet-https.redirectscheme.scheme=https"
|
||||
- "traefik.http.middlewares.mp-suppliers-ds-flash-htz-mainnet-https.redirectscheme.permanent=true"
|
||||
- "traefik.http.routers.mp-suppliers-ds-flash-htz-mainnet-tls.entrypoints=websecure"
|
||||
- "traefik.http.routers.mp-suppliers-ds-flash-htz-mainnet-tls.rule=Host(`mp-suppliers-ds-flash-htz.vector.apexfusion.org`)"
|
||||
- "traefik.http.routers.mp-suppliers-ds-flash-htz-mainnet-tls.tls.certresolver=letsencrypt"
|
||||
- "traefik.http.services.mp-suppliers-ds-flash-htz-mainnet.loadbalancer.server.port=8080"
|
||||
logging:
|
||||
driver: json-file
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "3"
|
||||
|
||||
networks:
|
||||
marketplace-mainnet-net:
|
||||
external: true
|
||||
name: marketplace-mainnet-net
|
||||
traefik-net:
|
||||
external: true
|
||||
name: traefik-net
|
||||
100
deploy/mainnet/docker-compose.supplier-glm-chat.yml
Normal file
100
deploy/mainnet/docker-compose.supplier-glm-chat.yml
Normal file
@@ -0,0 +1,100 @@
|
||||
# deploy/mainnet/docker-compose.supplier-glm-chat.yml — Supplier — MAINNET
|
||||
# (Hetzner Inference / GLM-5.2-NVFP4 — MULTI-TURN CHAT capability `llm.chat.v1`)
|
||||
#
|
||||
# The chat-session sibling of docker-compose.supplier-glm.yml. Same
|
||||
# image, same Hetzner backend (GLM-5.2-NVFP4), but CAPABILITY_KIND=chat-session so
|
||||
# it mounts /v1/chat/{start,message,end} (escrow bookends, off-chain streaming
|
||||
# turns) instead of the one-off /v1/chat/completions route.
|
||||
#
|
||||
# A SEPARATE process/advert from the one-off supplier on purpose: the chat
|
||||
# session holds the supplier's single-slot lock for the whole conversation
|
||||
# (minutes), which would starve one-off traffic if co-hosted.
|
||||
#
|
||||
# One of 8 Hetzner-backed suppliers (4 models x {one-off, chat-session});
|
||||
# fleet runbook: docs/HETZNER_SETUP.md.
|
||||
#
|
||||
# Pre-flight:
|
||||
# 1. DNS: mp-suppliers-glm-chat.vector.apexfusion.org → host
|
||||
# (wildcard *.vector.apexfusion.org already in place via Cloudflare)
|
||||
# 2. supplier/.env.glm-chat written on host (chmod 600) with at minimum:
|
||||
# SUPPLIER_PRIV_KEY_HEX, SUPPLIER_ADDRESS, SUPPLIER_PKH,
|
||||
# SUPPLIER_PUB_KEY_HEX, OPENAI_API_KEY, OGMIOS_URL, NETWORK_ID=1,
|
||||
# VECTOR_ZERO_TIME_MS, ESCROW_REF_UTXO, ADVERT_REF_UTXO, ADVERT_REF,
|
||||
# LIVE_CHAIN=1
|
||||
# The advert must be posted with --capability-id llm.chat.v1 and a large
|
||||
# --max-processing-ms (~1800000) so Claim+Submit both land before deliver_by.
|
||||
# 3. Networks already exist (created by the canonical supplier compose):
|
||||
# docker network create marketplace-mainnet-net # one-shot
|
||||
# # traefik-net is created by the Traefik project
|
||||
|
||||
name: marketplace-mainnet-supplier-glm-chat
|
||||
|
||||
services:
|
||||
supplier:
|
||||
build:
|
||||
context: ../..
|
||||
dockerfile: supplier/Dockerfile
|
||||
container_name: marketplace-mainnet-supplier-glm-chat
|
||||
restart: unless-stopped
|
||||
env_file:
|
||||
# Self-contained env for this Hetzner-backed variant. Holds this
|
||||
# supplier's distinct SUPPLIER_PRIV_KEY_HEX (+ derived wallet vars),
|
||||
# ADVERT_REF, the shared Hetzner OPENAI_API_KEY, and the shared mainnet
|
||||
# plumbing duplicated in full (NETWORK_ID, OGMIOS_URL, VECTOR_ZERO_TIME_MS,
|
||||
# ref-script UTxOs, LIVE_CHAIN). No env_file ordering / override magic.
|
||||
- path: ../../supplier/.env.glm-chat
|
||||
required: false
|
||||
environment:
|
||||
# Multi-turn chat session capability (mounts /v1/chat/{start,message,end}).
|
||||
CAPABILITY_KIND: chat-session
|
||||
LLM_BACKEND: openai
|
||||
# No /v1 suffix — supplier/src/openai.ts appends /v1/chat/completions
|
||||
# (Hetzner serves at https://inference.hetzner.com/api/v1/...).
|
||||
OPENAI_BASE_URL: https://inference.hetzner.com/api
|
||||
# Reasoning-style models can be slow; mirror the one-off compose.
|
||||
OPENAI_TIMEOUT_MS: "300000"
|
||||
# Idle end (supplier Submits the transcript): single-slot suppliers must
|
||||
# free up quickly (the gateway transparently reopens a session on the
|
||||
# next turn). Mirrors the kimi-chat fleet.
|
||||
CHAT_IDLE_TIMEOUT_MS: "900000"
|
||||
CHAT_SETTLE_MODE: ticket
|
||||
MAX_CHAT_SESSIONS: "8"
|
||||
PORT: "8080"
|
||||
# Enable LiveOgmiosProvider so Claim/Submit actually broadcast.
|
||||
LIVE_CHAIN: "1"
|
||||
# NETWORK_ID and OGMIOS_URL intentionally not pinned here — they MUST
|
||||
# come from the operator-provided supplier/.env.glm-chat on a mainnet host.
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "wget -qO- http://localhost:8080/healthz >/dev/null || exit 1"]
|
||||
interval: 20s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
start_period: 15s
|
||||
networks:
|
||||
- marketplace-mainnet-net
|
||||
- traefik-net
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.docker.network=traefik-net"
|
||||
- "traefik.http.routers.mp-suppliers-glm-chat-mainnet.entrypoints=web"
|
||||
- "traefik.http.routers.mp-suppliers-glm-chat-mainnet.rule=Host(`mp-suppliers-glm-chat.vector.apexfusion.org`)"
|
||||
- "traefik.http.routers.mp-suppliers-glm-chat-mainnet.middlewares=mp-suppliers-glm-chat-mainnet-https"
|
||||
- "traefik.http.middlewares.mp-suppliers-glm-chat-mainnet-https.redirectscheme.scheme=https"
|
||||
- "traefik.http.middlewares.mp-suppliers-glm-chat-mainnet-https.redirectscheme.permanent=true"
|
||||
- "traefik.http.routers.mp-suppliers-glm-chat-mainnet-tls.entrypoints=websecure"
|
||||
- "traefik.http.routers.mp-suppliers-glm-chat-mainnet-tls.rule=Host(`mp-suppliers-glm-chat.vector.apexfusion.org`)"
|
||||
- "traefik.http.routers.mp-suppliers-glm-chat-mainnet-tls.tls.certresolver=letsencrypt"
|
||||
- "traefik.http.services.mp-suppliers-glm-chat-mainnet.loadbalancer.server.port=8080"
|
||||
logging:
|
||||
driver: json-file
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "3"
|
||||
|
||||
networks:
|
||||
marketplace-mainnet-net:
|
||||
external: true
|
||||
name: marketplace-mainnet-net
|
||||
traefik-net:
|
||||
external: true
|
||||
name: traefik-net
|
||||
88
deploy/mainnet/docker-compose.supplier-glm.yml
Normal file
88
deploy/mainnet/docker-compose.supplier-glm.yml
Normal file
@@ -0,0 +1,88 @@
|
||||
# deploy/mainnet/docker-compose.supplier-glm.yml — Supplier — MAINNET
|
||||
# (Hetzner Inference / GLM-5.2-NVFP4 — one-off `llm.text.generate.v1`)
|
||||
#
|
||||
# Hetzner-backed supplier. LLM_BACKEND=openai pointed directly at Hetzner's
|
||||
# hosted inference API (OpenAI-compatible at https://inference.hetzner.com/api).
|
||||
# No local proxy container — reachable over public HTTPS.
|
||||
#
|
||||
# One of 8 Hetzner-backed suppliers (4 models x {one-off, chat-session});
|
||||
# fleet runbook: docs/HETZNER_SETUP.md.
|
||||
#
|
||||
# Pre-flight:
|
||||
# 1. DNS: mp-suppliers-glm.vector.apexfusion.org → host
|
||||
# (wildcard *.vector.apexfusion.org already in place via Cloudflare)
|
||||
# 2. supplier/.env.glm written on host (chmod 600) with at minimum:
|
||||
# SUPPLIER_PRIV_KEY_HEX, SUPPLIER_ADDRESS, SUPPLIER_PKH,
|
||||
# SUPPLIER_PUB_KEY_HEX, OPENAI_API_KEY, OGMIOS_URL, NETWORK_ID=1,
|
||||
# VECTOR_ZERO_TIME_MS, ESCROW_REF_UTXO, ADVERT_REF_UTXO, ADVERT_REF,
|
||||
# LIVE_CHAIN=1
|
||||
# 3. Networks already exist (created by the canonical supplier compose):
|
||||
# docker network create marketplace-mainnet-net # one-shot
|
||||
# # traefik-net is created by the Traefik project
|
||||
|
||||
name: marketplace-mainnet-supplier-glm
|
||||
|
||||
services:
|
||||
supplier:
|
||||
build:
|
||||
context: ../..
|
||||
dockerfile: supplier/Dockerfile
|
||||
container_name: marketplace-mainnet-supplier-glm
|
||||
restart: unless-stopped
|
||||
env_file:
|
||||
# Self-contained env for this Hetzner-backed variant. Holds this
|
||||
# supplier's distinct SUPPLIER_PRIV_KEY_HEX (+ derived wallet vars),
|
||||
# ADVERT_REF, the shared Hetzner OPENAI_API_KEY, and the shared mainnet
|
||||
# plumbing duplicated in full (NETWORK_ID, OGMIOS_URL, VECTOR_ZERO_TIME_MS,
|
||||
# ref-script UTxOs, LIVE_CHAIN). No env_file ordering / override magic.
|
||||
- path: ../../supplier/.env.glm
|
||||
required: false
|
||||
environment:
|
||||
LLM_BACKEND: openai
|
||||
# No /v1 suffix — supplier/src/openai.ts appends /v1/chat/completions
|
||||
# (Hetzner serves at https://inference.hetzner.com/api/v1/...).
|
||||
OPENAI_BASE_URL: https://inference.hetzner.com/api
|
||||
# Reasoning-style models can be slow; matches the advert's
|
||||
# max_processing_ms (300000).
|
||||
OPENAI_TIMEOUT_MS: "300000"
|
||||
# OPENAI_MAX_TOKENS intentionally unset: no max_tokens forwarded upstream,
|
||||
# the provider grants context − prompt. Advert cap = model context length.
|
||||
PORT: "8080"
|
||||
# Enable LiveOgmiosProvider so submitTx/awaitTx actually broadcast.
|
||||
LIVE_CHAIN: "1"
|
||||
# NETWORK_ID and OGMIOS_URL intentionally not pinned here — they MUST
|
||||
# come from the operator-provided supplier/.env.glm on a mainnet host.
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "wget -qO- http://localhost:8080/healthz >/dev/null || exit 1"]
|
||||
interval: 20s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
start_period: 15s
|
||||
networks:
|
||||
- marketplace-mainnet-net
|
||||
- traefik-net
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.docker.network=traefik-net"
|
||||
- "traefik.http.routers.mp-suppliers-glm-mainnet.entrypoints=web"
|
||||
- "traefik.http.routers.mp-suppliers-glm-mainnet.rule=Host(`mp-suppliers-glm.vector.apexfusion.org`)"
|
||||
- "traefik.http.routers.mp-suppliers-glm-mainnet.middlewares=mp-suppliers-glm-mainnet-https"
|
||||
- "traefik.http.middlewares.mp-suppliers-glm-mainnet-https.redirectscheme.scheme=https"
|
||||
- "traefik.http.middlewares.mp-suppliers-glm-mainnet-https.redirectscheme.permanent=true"
|
||||
- "traefik.http.routers.mp-suppliers-glm-mainnet-tls.entrypoints=websecure"
|
||||
- "traefik.http.routers.mp-suppliers-glm-mainnet-tls.rule=Host(`mp-suppliers-glm.vector.apexfusion.org`)"
|
||||
- "traefik.http.routers.mp-suppliers-glm-mainnet-tls.tls.certresolver=letsencrypt"
|
||||
- "traefik.http.services.mp-suppliers-glm-mainnet.loadbalancer.server.port=8080"
|
||||
logging:
|
||||
driver: json-file
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "3"
|
||||
|
||||
networks:
|
||||
marketplace-mainnet-net:
|
||||
external: true
|
||||
name: marketplace-mainnet-net
|
||||
traefik-net:
|
||||
external: true
|
||||
name: traefik-net
|
||||
100
deploy/mainnet/docker-compose.supplier-kimi-code-chat.yml
Normal file
100
deploy/mainnet/docker-compose.supplier-kimi-code-chat.yml
Normal file
@@ -0,0 +1,100 @@
|
||||
# deploy/mainnet/docker-compose.supplier-kimi-code-chat.yml — Supplier — MAINNET
|
||||
# (Hetzner Inference / Kimi-K2.7-Code — MULTI-TURN CHAT capability `llm.chat.v1`)
|
||||
#
|
||||
# The chat-session sibling of docker-compose.supplier-kimi-code.yml. Same
|
||||
# image, same Hetzner backend (Kimi-K2.7-Code), but CAPABILITY_KIND=chat-session so
|
||||
# it mounts /v1/chat/{start,message,end} (escrow bookends, off-chain streaming
|
||||
# turns) instead of the one-off /v1/chat/completions route.
|
||||
#
|
||||
# A SEPARATE process/advert from the one-off supplier on purpose: the chat
|
||||
# session holds the supplier's single-slot lock for the whole conversation
|
||||
# (minutes), which would starve one-off traffic if co-hosted.
|
||||
#
|
||||
# One of 8 Hetzner-backed suppliers (4 models x {one-off, chat-session});
|
||||
# fleet runbook: docs/HETZNER_SETUP.md.
|
||||
#
|
||||
# Pre-flight:
|
||||
# 1. DNS: mp-suppliers-kimi-code-chat.vector.apexfusion.org → host
|
||||
# (wildcard *.vector.apexfusion.org already in place via Cloudflare)
|
||||
# 2. supplier/.env.kimi-code-chat written on host (chmod 600) with at minimum:
|
||||
# SUPPLIER_PRIV_KEY_HEX, SUPPLIER_ADDRESS, SUPPLIER_PKH,
|
||||
# SUPPLIER_PUB_KEY_HEX, OPENAI_API_KEY, OGMIOS_URL, NETWORK_ID=1,
|
||||
# VECTOR_ZERO_TIME_MS, ESCROW_REF_UTXO, ADVERT_REF_UTXO, ADVERT_REF,
|
||||
# LIVE_CHAIN=1
|
||||
# The advert must be posted with --capability-id llm.chat.v1 and a large
|
||||
# --max-processing-ms (~1800000) so Claim+Submit both land before deliver_by.
|
||||
# 3. Networks already exist (created by the canonical supplier compose):
|
||||
# docker network create marketplace-mainnet-net # one-shot
|
||||
# # traefik-net is created by the Traefik project
|
||||
|
||||
name: marketplace-mainnet-supplier-kimi-code-chat
|
||||
|
||||
services:
|
||||
supplier:
|
||||
build:
|
||||
context: ../..
|
||||
dockerfile: supplier/Dockerfile
|
||||
container_name: marketplace-mainnet-supplier-kimi-code-chat
|
||||
restart: unless-stopped
|
||||
env_file:
|
||||
# Self-contained env for this Hetzner-backed variant. Holds this
|
||||
# supplier's distinct SUPPLIER_PRIV_KEY_HEX (+ derived wallet vars),
|
||||
# ADVERT_REF, the shared Hetzner OPENAI_API_KEY, and the shared mainnet
|
||||
# plumbing duplicated in full (NETWORK_ID, OGMIOS_URL, VECTOR_ZERO_TIME_MS,
|
||||
# ref-script UTxOs, LIVE_CHAIN). No env_file ordering / override magic.
|
||||
- path: ../../supplier/.env.kimi-code-chat
|
||||
required: false
|
||||
environment:
|
||||
# Multi-turn chat session capability (mounts /v1/chat/{start,message,end}).
|
||||
CAPABILITY_KIND: chat-session
|
||||
LLM_BACKEND: openai
|
||||
# No /v1 suffix — supplier/src/openai.ts appends /v1/chat/completions
|
||||
# (Hetzner serves at https://inference.hetzner.com/api/v1/...).
|
||||
OPENAI_BASE_URL: https://inference.hetzner.com/api
|
||||
# Reasoning-style models can be slow; mirror the one-off compose.
|
||||
OPENAI_TIMEOUT_MS: "300000"
|
||||
# Idle end (supplier Submits the transcript): single-slot suppliers must
|
||||
# free up quickly (the gateway transparently reopens a session on the
|
||||
# next turn). Mirrors the kimi-chat fleet.
|
||||
CHAT_IDLE_TIMEOUT_MS: "900000"
|
||||
CHAT_SETTLE_MODE: ticket
|
||||
MAX_CHAT_SESSIONS: "8"
|
||||
PORT: "8080"
|
||||
# Enable LiveOgmiosProvider so Claim/Submit actually broadcast.
|
||||
LIVE_CHAIN: "1"
|
||||
# NETWORK_ID and OGMIOS_URL intentionally not pinned here — they MUST
|
||||
# come from the operator-provided supplier/.env.kimi-code-chat on a mainnet host.
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "wget -qO- http://localhost:8080/healthz >/dev/null || exit 1"]
|
||||
interval: 20s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
start_period: 15s
|
||||
networks:
|
||||
- marketplace-mainnet-net
|
||||
- traefik-net
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.docker.network=traefik-net"
|
||||
- "traefik.http.routers.mp-suppliers-kimi-code-chat-mainnet.entrypoints=web"
|
||||
- "traefik.http.routers.mp-suppliers-kimi-code-chat-mainnet.rule=Host(`mp-suppliers-kimi-code-chat.vector.apexfusion.org`)"
|
||||
- "traefik.http.routers.mp-suppliers-kimi-code-chat-mainnet.middlewares=mp-suppliers-kimi-code-chat-mainnet-https"
|
||||
- "traefik.http.middlewares.mp-suppliers-kimi-code-chat-mainnet-https.redirectscheme.scheme=https"
|
||||
- "traefik.http.middlewares.mp-suppliers-kimi-code-chat-mainnet-https.redirectscheme.permanent=true"
|
||||
- "traefik.http.routers.mp-suppliers-kimi-code-chat-mainnet-tls.entrypoints=websecure"
|
||||
- "traefik.http.routers.mp-suppliers-kimi-code-chat-mainnet-tls.rule=Host(`mp-suppliers-kimi-code-chat.vector.apexfusion.org`)"
|
||||
- "traefik.http.routers.mp-suppliers-kimi-code-chat-mainnet-tls.tls.certresolver=letsencrypt"
|
||||
- "traefik.http.services.mp-suppliers-kimi-code-chat-mainnet.loadbalancer.server.port=8080"
|
||||
logging:
|
||||
driver: json-file
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "3"
|
||||
|
||||
networks:
|
||||
marketplace-mainnet-net:
|
||||
external: true
|
||||
name: marketplace-mainnet-net
|
||||
traefik-net:
|
||||
external: true
|
||||
name: traefik-net
|
||||
88
deploy/mainnet/docker-compose.supplier-kimi-code.yml
Normal file
88
deploy/mainnet/docker-compose.supplier-kimi-code.yml
Normal file
@@ -0,0 +1,88 @@
|
||||
# deploy/mainnet/docker-compose.supplier-kimi-code.yml — Supplier — MAINNET
|
||||
# (Hetzner Inference / Kimi-K2.7-Code — one-off `llm.text.generate.v1`)
|
||||
#
|
||||
# Hetzner-backed supplier. LLM_BACKEND=openai pointed directly at Hetzner's
|
||||
# hosted inference API (OpenAI-compatible at https://inference.hetzner.com/api).
|
||||
# No local proxy container — reachable over public HTTPS.
|
||||
#
|
||||
# One of 8 Hetzner-backed suppliers (4 models x {one-off, chat-session});
|
||||
# fleet runbook: docs/HETZNER_SETUP.md.
|
||||
#
|
||||
# Pre-flight:
|
||||
# 1. DNS: mp-suppliers-kimi-code.vector.apexfusion.org → host
|
||||
# (wildcard *.vector.apexfusion.org already in place via Cloudflare)
|
||||
# 2. supplier/.env.kimi-code written on host (chmod 600) with at minimum:
|
||||
# SUPPLIER_PRIV_KEY_HEX, SUPPLIER_ADDRESS, SUPPLIER_PKH,
|
||||
# SUPPLIER_PUB_KEY_HEX, OPENAI_API_KEY, OGMIOS_URL, NETWORK_ID=1,
|
||||
# VECTOR_ZERO_TIME_MS, ESCROW_REF_UTXO, ADVERT_REF_UTXO, ADVERT_REF,
|
||||
# LIVE_CHAIN=1
|
||||
# 3. Networks already exist (created by the canonical supplier compose):
|
||||
# docker network create marketplace-mainnet-net # one-shot
|
||||
# # traefik-net is created by the Traefik project
|
||||
|
||||
name: marketplace-mainnet-supplier-kimi-code
|
||||
|
||||
services:
|
||||
supplier:
|
||||
build:
|
||||
context: ../..
|
||||
dockerfile: supplier/Dockerfile
|
||||
container_name: marketplace-mainnet-supplier-kimi-code
|
||||
restart: unless-stopped
|
||||
env_file:
|
||||
# Self-contained env for this Hetzner-backed variant. Holds this
|
||||
# supplier's distinct SUPPLIER_PRIV_KEY_HEX (+ derived wallet vars),
|
||||
# ADVERT_REF, the shared Hetzner OPENAI_API_KEY, and the shared mainnet
|
||||
# plumbing duplicated in full (NETWORK_ID, OGMIOS_URL, VECTOR_ZERO_TIME_MS,
|
||||
# ref-script UTxOs, LIVE_CHAIN). No env_file ordering / override magic.
|
||||
- path: ../../supplier/.env.kimi-code
|
||||
required: false
|
||||
environment:
|
||||
LLM_BACKEND: openai
|
||||
# No /v1 suffix — supplier/src/openai.ts appends /v1/chat/completions
|
||||
# (Hetzner serves at https://inference.hetzner.com/api/v1/...).
|
||||
OPENAI_BASE_URL: https://inference.hetzner.com/api
|
||||
# Reasoning-style models can be slow; matches the advert's
|
||||
# max_processing_ms (300000).
|
||||
OPENAI_TIMEOUT_MS: "300000"
|
||||
# OPENAI_MAX_TOKENS intentionally unset: no max_tokens forwarded upstream,
|
||||
# the provider grants context − prompt. Advert cap = model context length.
|
||||
PORT: "8080"
|
||||
# Enable LiveOgmiosProvider so submitTx/awaitTx actually broadcast.
|
||||
LIVE_CHAIN: "1"
|
||||
# NETWORK_ID and OGMIOS_URL intentionally not pinned here — they MUST
|
||||
# come from the operator-provided supplier/.env.kimi-code on a mainnet host.
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "wget -qO- http://localhost:8080/healthz >/dev/null || exit 1"]
|
||||
interval: 20s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
start_period: 15s
|
||||
networks:
|
||||
- marketplace-mainnet-net
|
||||
- traefik-net
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.docker.network=traefik-net"
|
||||
- "traefik.http.routers.mp-suppliers-kimi-code-mainnet.entrypoints=web"
|
||||
- "traefik.http.routers.mp-suppliers-kimi-code-mainnet.rule=Host(`mp-suppliers-kimi-code.vector.apexfusion.org`)"
|
||||
- "traefik.http.routers.mp-suppliers-kimi-code-mainnet.middlewares=mp-suppliers-kimi-code-mainnet-https"
|
||||
- "traefik.http.middlewares.mp-suppliers-kimi-code-mainnet-https.redirectscheme.scheme=https"
|
||||
- "traefik.http.middlewares.mp-suppliers-kimi-code-mainnet-https.redirectscheme.permanent=true"
|
||||
- "traefik.http.routers.mp-suppliers-kimi-code-mainnet-tls.entrypoints=websecure"
|
||||
- "traefik.http.routers.mp-suppliers-kimi-code-mainnet-tls.rule=Host(`mp-suppliers-kimi-code.vector.apexfusion.org`)"
|
||||
- "traefik.http.routers.mp-suppliers-kimi-code-mainnet-tls.tls.certresolver=letsencrypt"
|
||||
- "traefik.http.services.mp-suppliers-kimi-code-mainnet.loadbalancer.server.port=8080"
|
||||
logging:
|
||||
driver: json-file
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "3"
|
||||
|
||||
networks:
|
||||
marketplace-mainnet-net:
|
||||
external: true
|
||||
name: marketplace-mainnet-net
|
||||
traefik-net:
|
||||
external: true
|
||||
name: traefik-net
|
||||
100
deploy/mainnet/docker-compose.supplier-qwen35b-chat.yml
Normal file
100
deploy/mainnet/docker-compose.supplier-qwen35b-chat.yml
Normal file
@@ -0,0 +1,100 @@
|
||||
# deploy/mainnet/docker-compose.supplier-qwen35b-chat.yml — Supplier — MAINNET
|
||||
# (Hetzner Inference / Qwen/Qwen3.6-35B-A3B-FP8 — MULTI-TURN CHAT capability `llm.chat.v1`)
|
||||
#
|
||||
# The chat-session sibling of docker-compose.supplier-qwen35b.yml. Same
|
||||
# image, same Hetzner backend (Qwen/Qwen3.6-35B-A3B-FP8), but CAPABILITY_KIND=chat-session so
|
||||
# it mounts /v1/chat/{start,message,end} (escrow bookends, off-chain streaming
|
||||
# turns) instead of the one-off /v1/chat/completions route.
|
||||
#
|
||||
# A SEPARATE process/advert from the one-off supplier on purpose: the chat
|
||||
# session holds the supplier's single-slot lock for the whole conversation
|
||||
# (minutes), which would starve one-off traffic if co-hosted.
|
||||
#
|
||||
# One of 8 Hetzner-backed suppliers (4 models x {one-off, chat-session});
|
||||
# fleet runbook: docs/HETZNER_SETUP.md.
|
||||
#
|
||||
# Pre-flight:
|
||||
# 1. DNS: mp-suppliers-qwen35b-chat.vector.apexfusion.org → host
|
||||
# (wildcard *.vector.apexfusion.org already in place via Cloudflare)
|
||||
# 2. supplier/.env.qwen35b-chat written on host (chmod 600) with at minimum:
|
||||
# SUPPLIER_PRIV_KEY_HEX, SUPPLIER_ADDRESS, SUPPLIER_PKH,
|
||||
# SUPPLIER_PUB_KEY_HEX, OPENAI_API_KEY, OGMIOS_URL, NETWORK_ID=1,
|
||||
# VECTOR_ZERO_TIME_MS, ESCROW_REF_UTXO, ADVERT_REF_UTXO, ADVERT_REF,
|
||||
# LIVE_CHAIN=1
|
||||
# The advert must be posted with --capability-id llm.chat.v1 and a large
|
||||
# --max-processing-ms (~1800000) so Claim+Submit both land before deliver_by.
|
||||
# 3. Networks already exist (created by the canonical supplier compose):
|
||||
# docker network create marketplace-mainnet-net # one-shot
|
||||
# # traefik-net is created by the Traefik project
|
||||
|
||||
name: marketplace-mainnet-supplier-qwen35b-chat
|
||||
|
||||
services:
|
||||
supplier:
|
||||
build:
|
||||
context: ../..
|
||||
dockerfile: supplier/Dockerfile
|
||||
container_name: marketplace-mainnet-supplier-qwen35b-chat
|
||||
restart: unless-stopped
|
||||
env_file:
|
||||
# Self-contained env for this Hetzner-backed variant. Holds this
|
||||
# supplier's distinct SUPPLIER_PRIV_KEY_HEX (+ derived wallet vars),
|
||||
# ADVERT_REF, the shared Hetzner OPENAI_API_KEY, and the shared mainnet
|
||||
# plumbing duplicated in full (NETWORK_ID, OGMIOS_URL, VECTOR_ZERO_TIME_MS,
|
||||
# ref-script UTxOs, LIVE_CHAIN). No env_file ordering / override magic.
|
||||
- path: ../../supplier/.env.qwen35b-chat
|
||||
required: false
|
||||
environment:
|
||||
# Multi-turn chat session capability (mounts /v1/chat/{start,message,end}).
|
||||
CAPABILITY_KIND: chat-session
|
||||
LLM_BACKEND: openai
|
||||
# No /v1 suffix — supplier/src/openai.ts appends /v1/chat/completions
|
||||
# (Hetzner serves at https://inference.hetzner.com/api/v1/...).
|
||||
OPENAI_BASE_URL: https://inference.hetzner.com/api
|
||||
# Reasoning-style models can be slow; mirror the one-off compose.
|
||||
OPENAI_TIMEOUT_MS: "300000"
|
||||
# Idle end (supplier Submits the transcript): single-slot suppliers must
|
||||
# free up quickly (the gateway transparently reopens a session on the
|
||||
# next turn). Mirrors the kimi-chat fleet.
|
||||
CHAT_IDLE_TIMEOUT_MS: "900000"
|
||||
CHAT_SETTLE_MODE: ticket
|
||||
MAX_CHAT_SESSIONS: "8"
|
||||
PORT: "8080"
|
||||
# Enable LiveOgmiosProvider so Claim/Submit actually broadcast.
|
||||
LIVE_CHAIN: "1"
|
||||
# NETWORK_ID and OGMIOS_URL intentionally not pinned here — they MUST
|
||||
# come from the operator-provided supplier/.env.qwen35b-chat on a mainnet host.
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "wget -qO- http://localhost:8080/healthz >/dev/null || exit 1"]
|
||||
interval: 20s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
start_period: 15s
|
||||
networks:
|
||||
- marketplace-mainnet-net
|
||||
- traefik-net
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.docker.network=traefik-net"
|
||||
- "traefik.http.routers.mp-suppliers-qwen35b-chat-mainnet.entrypoints=web"
|
||||
- "traefik.http.routers.mp-suppliers-qwen35b-chat-mainnet.rule=Host(`mp-suppliers-qwen35b-chat.vector.apexfusion.org`)"
|
||||
- "traefik.http.routers.mp-suppliers-qwen35b-chat-mainnet.middlewares=mp-suppliers-qwen35b-chat-mainnet-https"
|
||||
- "traefik.http.middlewares.mp-suppliers-qwen35b-chat-mainnet-https.redirectscheme.scheme=https"
|
||||
- "traefik.http.middlewares.mp-suppliers-qwen35b-chat-mainnet-https.redirectscheme.permanent=true"
|
||||
- "traefik.http.routers.mp-suppliers-qwen35b-chat-mainnet-tls.entrypoints=websecure"
|
||||
- "traefik.http.routers.mp-suppliers-qwen35b-chat-mainnet-tls.rule=Host(`mp-suppliers-qwen35b-chat.vector.apexfusion.org`)"
|
||||
- "traefik.http.routers.mp-suppliers-qwen35b-chat-mainnet-tls.tls.certresolver=letsencrypt"
|
||||
- "traefik.http.services.mp-suppliers-qwen35b-chat-mainnet.loadbalancer.server.port=8080"
|
||||
logging:
|
||||
driver: json-file
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "3"
|
||||
|
||||
networks:
|
||||
marketplace-mainnet-net:
|
||||
external: true
|
||||
name: marketplace-mainnet-net
|
||||
traefik-net:
|
||||
external: true
|
||||
name: traefik-net
|
||||
88
deploy/mainnet/docker-compose.supplier-qwen35b.yml
Normal file
88
deploy/mainnet/docker-compose.supplier-qwen35b.yml
Normal file
@@ -0,0 +1,88 @@
|
||||
# deploy/mainnet/docker-compose.supplier-qwen35b.yml — Supplier — MAINNET
|
||||
# (Hetzner Inference / Qwen/Qwen3.6-35B-A3B-FP8 — one-off `llm.text.generate.v1`)
|
||||
#
|
||||
# Hetzner-backed supplier. LLM_BACKEND=openai pointed directly at Hetzner's
|
||||
# hosted inference API (OpenAI-compatible at https://inference.hetzner.com/api).
|
||||
# No local proxy container — reachable over public HTTPS.
|
||||
#
|
||||
# One of 8 Hetzner-backed suppliers (4 models x {one-off, chat-session});
|
||||
# fleet runbook: docs/HETZNER_SETUP.md.
|
||||
#
|
||||
# Pre-flight:
|
||||
# 1. DNS: mp-suppliers-qwen35b.vector.apexfusion.org → host
|
||||
# (wildcard *.vector.apexfusion.org already in place via Cloudflare)
|
||||
# 2. supplier/.env.qwen35b written on host (chmod 600) with at minimum:
|
||||
# SUPPLIER_PRIV_KEY_HEX, SUPPLIER_ADDRESS, SUPPLIER_PKH,
|
||||
# SUPPLIER_PUB_KEY_HEX, OPENAI_API_KEY, OGMIOS_URL, NETWORK_ID=1,
|
||||
# VECTOR_ZERO_TIME_MS, ESCROW_REF_UTXO, ADVERT_REF_UTXO, ADVERT_REF,
|
||||
# LIVE_CHAIN=1
|
||||
# 3. Networks already exist (created by the canonical supplier compose):
|
||||
# docker network create marketplace-mainnet-net # one-shot
|
||||
# # traefik-net is created by the Traefik project
|
||||
|
||||
name: marketplace-mainnet-supplier-qwen35b
|
||||
|
||||
services:
|
||||
supplier:
|
||||
build:
|
||||
context: ../..
|
||||
dockerfile: supplier/Dockerfile
|
||||
container_name: marketplace-mainnet-supplier-qwen35b
|
||||
restart: unless-stopped
|
||||
env_file:
|
||||
# Self-contained env for this Hetzner-backed variant. Holds this
|
||||
# supplier's distinct SUPPLIER_PRIV_KEY_HEX (+ derived wallet vars),
|
||||
# ADVERT_REF, the shared Hetzner OPENAI_API_KEY, and the shared mainnet
|
||||
# plumbing duplicated in full (NETWORK_ID, OGMIOS_URL, VECTOR_ZERO_TIME_MS,
|
||||
# ref-script UTxOs, LIVE_CHAIN). No env_file ordering / override magic.
|
||||
- path: ../../supplier/.env.qwen35b
|
||||
required: false
|
||||
environment:
|
||||
LLM_BACKEND: openai
|
||||
# No /v1 suffix — supplier/src/openai.ts appends /v1/chat/completions
|
||||
# (Hetzner serves at https://inference.hetzner.com/api/v1/...).
|
||||
OPENAI_BASE_URL: https://inference.hetzner.com/api
|
||||
# Reasoning-style models can be slow; matches the advert's
|
||||
# max_processing_ms (300000).
|
||||
OPENAI_TIMEOUT_MS: "300000"
|
||||
# OPENAI_MAX_TOKENS intentionally unset: no max_tokens forwarded upstream,
|
||||
# the provider grants context − prompt. Advert cap = model context length.
|
||||
PORT: "8080"
|
||||
# Enable LiveOgmiosProvider so submitTx/awaitTx actually broadcast.
|
||||
LIVE_CHAIN: "1"
|
||||
# NETWORK_ID and OGMIOS_URL intentionally not pinned here — they MUST
|
||||
# come from the operator-provided supplier/.env.qwen35b on a mainnet host.
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "wget -qO- http://localhost:8080/healthz >/dev/null || exit 1"]
|
||||
interval: 20s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
start_period: 15s
|
||||
networks:
|
||||
- marketplace-mainnet-net
|
||||
- traefik-net
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.docker.network=traefik-net"
|
||||
- "traefik.http.routers.mp-suppliers-qwen35b-mainnet.entrypoints=web"
|
||||
- "traefik.http.routers.mp-suppliers-qwen35b-mainnet.rule=Host(`mp-suppliers-qwen35b.vector.apexfusion.org`)"
|
||||
- "traefik.http.routers.mp-suppliers-qwen35b-mainnet.middlewares=mp-suppliers-qwen35b-mainnet-https"
|
||||
- "traefik.http.middlewares.mp-suppliers-qwen35b-mainnet-https.redirectscheme.scheme=https"
|
||||
- "traefik.http.middlewares.mp-suppliers-qwen35b-mainnet-https.redirectscheme.permanent=true"
|
||||
- "traefik.http.routers.mp-suppliers-qwen35b-mainnet-tls.entrypoints=websecure"
|
||||
- "traefik.http.routers.mp-suppliers-qwen35b-mainnet-tls.rule=Host(`mp-suppliers-qwen35b.vector.apexfusion.org`)"
|
||||
- "traefik.http.routers.mp-suppliers-qwen35b-mainnet-tls.tls.certresolver=letsencrypt"
|
||||
- "traefik.http.services.mp-suppliers-qwen35b-mainnet.loadbalancer.server.port=8080"
|
||||
logging:
|
||||
driver: json-file
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "3"
|
||||
|
||||
networks:
|
||||
marketplace-mainnet-net:
|
||||
external: true
|
||||
name: marketplace-mainnet-net
|
||||
traefik-net:
|
||||
external: true
|
||||
name: traefik-net
|
||||
128
docs/HETZNER_SETUP.md
Normal file
128
docs/HETZNER_SETUP.md
Normal file
@@ -0,0 +1,128 @@
|
||||
# Hetzner Inference Supplier Fleet — Setup Runbook
|
||||
|
||||
> **Status:** shipped. Configures 8 mainnet supplier nodes that use **Hetzner
|
||||
> Inference** (`https://inference.hetzner.com`) as their **compute backend** for
|
||||
> bonded inference work commissioned on Vector. Config only — no code changes.
|
||||
|
||||
---
|
||||
|
||||
## 1. What this is
|
||||
|
||||
Hetzner Inference is an OpenAI-compatible hosted API. Because the supplier's
|
||||
upstream client (`supplier/src/openai.ts`) already speaks plain
|
||||
`POST /v1/chat/completions` with a Bearer token, pointing it at Hetzner is
|
||||
**pure configuration**: `LLM_BACKEND=openai` + `OPENAI_BASE_URL` + the API key.
|
||||
This is the same pattern as the DeepSeek-direct and OpenRouter suppliers, and
|
||||
the direct sibling of `docs/HUGGINGFACE_ROUTER_SETUP.md`.
|
||||
|
||||
One supplier per (model × capability) — 4 models, both the one-off
|
||||
`llm.text.generate.v1` and the multi-turn `llm.chat.v1` capability:
|
||||
|
||||
| Supplier name | Model (on-chain + API param, verbatim) | Capability | Advert max_output_tokens |
|
||||
|---|---|---|---|
|
||||
| `kimi-code` | `Kimi-K2.7-Code` | `llm.text.generate.v1` | 262144 |
|
||||
| `kimi-code-chat` | `Kimi-K2.7-Code` | `llm.chat.v1` | 262144 |
|
||||
| `glm` | `GLM-5.2-NVFP4` | `llm.text.generate.v1` | 512000 |
|
||||
| `glm-chat` | `GLM-5.2-NVFP4` | `llm.chat.v1` | 512000 |
|
||||
| `ds-flash-htz` | `DeepSeek-V4-Flash-0731` | `llm.text.generate.v1` | 512000 |
|
||||
| `ds-flash-htz-chat` | `DeepSeek-V4-Flash-0731` | `llm.chat.v1` | 512000 |
|
||||
| `qwen35b` | `Qwen/Qwen3.6-35B-A3B-FP8` | `llm.text.generate.v1` | 262144 |
|
||||
| `qwen35b-chat` | `Qwen/Qwen3.6-35B-A3B-FP8` | `llm.chat.v1` | 262144 |
|
||||
|
||||
Shared advert parameters: price **200000 lovelace (0.2 AP3X)** flat per job,
|
||||
bonds **1000000 lovelace (1 AP3X)** both sides, `max_processing_ms` **300000**
|
||||
for one-off / **1800000** for chat (the session spans the whole conversation).
|
||||
`max_output_tokens` is set to the model's context length: no `max_tokens` is
|
||||
forwarded upstream (`OPENAI_MAX_TOKENS` unset), so the advert value only gates
|
||||
what buyers may request.
|
||||
|
||||
Names follow the brand-by-model convention; `ds-flash-htz` carries a `-htz`
|
||||
suffix only because `deepseek-flash-*` was already taken by the OpenRouter
|
||||
fleet serving `deepseek/deepseek-v4-flash`.
|
||||
|
||||
## 2. Prerequisites
|
||||
|
||||
1. A Hetzner account with Inference access and an API key (usage-based billing
|
||||
to you as the node operator — the flat 0.2 AP3X job price is a business
|
||||
choice, not cost-derived). One key is shared by all 8 suppliers; Hetzner
|
||||
rate limits apply to the aggregate.
|
||||
2. Eight funded supplier wallets (50 AP3X each recommended; bonds are 1 AP3X
|
||||
per in-flight job and returned on completion).
|
||||
3. The usual mainnet supplier deploy prerequisites (`deploy/README.md`); the
|
||||
wildcard DNS `*.vector.apexfusion.org` already covers the new hosts.
|
||||
|
||||
## 3. Verify the backend before spending on-chain
|
||||
|
||||
The four model ids come from `GET https://inference.hetzner.com/api/v1/models`
|
||||
and are sent **verbatim** as the upstream `model` param (the runtime uses
|
||||
`advert.model`). Before posting adverts, smoke-test each model:
|
||||
|
||||
```bash
|
||||
curl -sS -X POST https://inference.hetzner.com/api/v1/chat/completions \
|
||||
-H "Authorization: Bearer $HETZNER_KEY" -H "Content-Type: application/json" \
|
||||
-d '{"model":"GLM-5.2-NVFP4","messages":[{"role":"user","content":"ping"}]}' \
|
||||
| jq '.choices[0].message.content'
|
||||
```
|
||||
|
||||
`choices[0].message.content` must be a non-empty string with **no `max_tokens`
|
||||
sent** (matches runtime config) — the supplier throws `openai_malformed`
|
||||
otherwise and forfeits its 1 AP3X bond per failed job. For chat suppliers also
|
||||
verify a `tools`/`tool_choice` round-trip. All four models passed both checks
|
||||
on 2026-08-11.
|
||||
|
||||
## 4. Per-supplier bring-up (repeat ×8)
|
||||
|
||||
```bash
|
||||
# 1. wallet — prints privateKeyHex, publicKeyHex, pubKeyHash, address
|
||||
pnpm --filter @marketplace/supplier tx:gen-keypair --network 1
|
||||
# fund the printed address (50 AP3X recommended), then:
|
||||
|
||||
# 2. env file on the mainnet host
|
||||
cp supplier/.env.hetzner.example /root/agents-marketplace/supplier/.env.<name>
|
||||
chmod 600 /root/agents-marketplace/supplier/.env.<name>
|
||||
# fill in: ALL FOUR wallet vars (PRIV_KEY_HEX, ADDRESS, PKH, PUB_KEY_HEX —
|
||||
# they are NOT derived at boot; missing derived vars ⇒ 403 wrong_supplier),
|
||||
# OPENAI_API_KEY, and the ADVERT_* values from the table above.
|
||||
|
||||
# 3. advert (one-off shown; chat: --capability-id llm.chat.v1 --max-processing-ms 1800000)
|
||||
pnpm --filter @marketplace/supplier tx:post-advert \
|
||||
--capability-id llm.text.generate.v1 \
|
||||
--model 'GLM-5.2-NVFP4' \
|
||||
--max-output-tokens 512000 \
|
||||
--max-processing-ms 300000 \
|
||||
--price-lovelace 200000 \
|
||||
--endpoint-url https://mp-suppliers-<name>.vector.apexfusion.org
|
||||
# paste the printed "<txHash>#0" into ADVERT_REF in the env file
|
||||
|
||||
# 4. first start is always manual (CD skips compose projects with no running
|
||||
# containers); subsequent rollouts ride the mainnet CD pipeline
|
||||
docker compose -f deploy/mainnet/docker-compose.supplier-<name>.yml up -d
|
||||
|
||||
# 5. register the wallet for balance alerts
|
||||
# add {name, address} to wallet-monitor/wallets.json on the host
|
||||
```
|
||||
|
||||
## 5. Verification
|
||||
|
||||
- `curl https://mp-suppliers-<name>.vector.apexfusion.org/healthz` → `{"ok":true}`;
|
||||
`/capability` shows the right model + pkh.
|
||||
- Indexer: buyer `GET /v1/indexer/suppliers` lists the new entries with
|
||||
`advert_status: Active`, `status: free`.
|
||||
- Gateway `GET /openai/v1/models` includes the four Hetzner model ids.
|
||||
- Watch `docker logs -f marketplace-mainnet-supplier-<name>` through the first
|
||||
jobs for `openai_malformed` (reasoning-model content-shape risk).
|
||||
|
||||
## 6. Footguns
|
||||
|
||||
- `OPENAI_BASE_URL=https://inference.hetzner.com/api` — **no `/v1`**; the
|
||||
client appends `/v1/chat/completions`.
|
||||
- Do **not** set `OPENAI_REASONING` — `reasoning:{enabled:false}` is an
|
||||
OpenRouter-only param.
|
||||
- The advert `--model` must match the Hetzner id exactly, including the
|
||||
`Qwen/` prefix on `Qwen/Qwen3.6-35B-A3B-FP8` (slash is fine on-chain;
|
||||
precedent `moonshotai/kimi-k2.6`).
|
||||
- `Kimi-K2.7-Code` and `DeepSeek-V4-Flash-0731` auto-join the buyer's PDF pool
|
||||
(case-insensitive substring allowlist `["kimi","deepseek","gpt"]` in
|
||||
`buyer/src/pdf/caps.ts`); GLM and Qwen stay out. Deliberate — adjust
|
||||
`PDF_MODEL_ALLOWLIST`/`PDF_MODEL_DENYLIST` in the buyer env to change it.
|
||||
- `OPENAI_TIMEOUT_MS` must stay ≤ the advert's `max_processing_ms`.
|
||||
168
docs/HUGGINGFACE_ROUTER_SETUP.md
Normal file
168
docs/HUGGINGFACE_ROUTER_SETUP.md
Normal file
@@ -0,0 +1,168 @@
|
||||
# HuggingFace Router Supplier — Setup Runbook (V1 / W1)
|
||||
|
||||
> **Status:** shipped. Configures a Vector supplier node to use the HuggingFace
|
||||
> **Inference Providers router** as its **compute backend** for bonded inference
|
||||
> work commissioned on Vector. Config only — no code changes.
|
||||
|
||||
---
|
||||
|
||||
## 1. What this is
|
||||
|
||||
The HuggingFace **Inference Providers router** (`https://router.huggingface.co`)
|
||||
is an OpenAI-compatible endpoint that fronts hundreds of hosted models across
|
||||
partner providers (Together, Fireworks, DeepInfra, Groq, …). It is the direct
|
||||
analogue of the OpenRouter backend the supplier already supports.
|
||||
|
||||
Because the supplier's upstream client (`supplier/src/openai.ts`) already speaks
|
||||
plain `POST /v1/chat/completions` with a Bearer token, pointing it at HuggingFace
|
||||
is **pure configuration**: set `LLM_BACKEND=openai` and point `OPENAI_BASE_URL` at
|
||||
the router. It is the same as adding any OpenAI-compatible compute backend, with
|
||||
the HuggingFace endpoint and token substituted in.
|
||||
|
||||
```
|
||||
buyer commissions bonded work ──▶ supplier node ──▶ HuggingFace router
|
||||
(AP3X escrow on Vector) │ (compute backend, hf_ token)
|
||||
▼
|
||||
supplier fulfils the work; the escrow settles on-chain
|
||||
```
|
||||
|
||||
The supplier node runs the inference to **fulfil work a buyer has commissioned
|
||||
under stake**; HuggingFace is simply the compute it uses to produce the result.
|
||||
|
||||
**What this does NOT do:** it does not add creator/model-author work-sharing,
|
||||
change protocol economics, or change how an advert's committed amount is sized.
|
||||
Those are separate, larger pieces of work tracked in the team's internal planning
|
||||
notes. This preset is the fast, low-risk on-ramp that proves the path.
|
||||
|
||||
---
|
||||
|
||||
## 2. Prerequisites
|
||||
|
||||
1. A HuggingFace account with **Inference Providers access** (a free monthly
|
||||
credit allowance, then usage-based per token — this is HuggingFace's billing
|
||||
to you as the node operator).
|
||||
2. An access token from <https://huggingface.co/settings/tokens> with the
|
||||
**"Make calls to Inference Providers"** permission. It starts with `hf_`.
|
||||
Treat it like a password.
|
||||
3. A funded supplier wallet (~5 AP3X for tx fees + bonds). Generate a seed with
|
||||
`openssl rand -hex 32`.
|
||||
4. The usual supplier deploy prerequisites (Ogmios URL, a publicly reachable
|
||||
HTTPS endpoint for buyers). See `deploy/README.md`.
|
||||
|
||||
---
|
||||
|
||||
## 3. Steps
|
||||
|
||||
### 3.1 Create the env file
|
||||
|
||||
```bash
|
||||
cp supplier/.env.huggingface-chat.example supplier/.env
|
||||
chmod 600 supplier/.env
|
||||
```
|
||||
|
||||
Fill in `SUPPLIER_PRIV_KEY_HEX`, `OPENAI_API_KEY` (your `hf_…` token),
|
||||
`OGMIOS_URL`, and the `ADVERT_*` values. Key settings for this preset:
|
||||
|
||||
| Var | Value | Why |
|
||||
|-----|-------|-----|
|
||||
| `LLM_BACKEND` | `openai` | routes chat through the OpenAI-compatible client |
|
||||
| `OPENAI_BASE_URL` | `https://router.huggingface.co` | HF router; the `/v1/chat/completions` suffix is appended by the client — do **not** add it |
|
||||
| `OPENAI_API_KEY` | `hf_…` | your HF token (Bearer) |
|
||||
| `OPENAI_REASONING` | **unset** | HF 400s on the OpenRouter-only `reasoning` param (see §5) |
|
||||
| `ADVERT_MODEL` | provider-qualified id, e.g. `deepseek-ai/DeepSeek-V3:fastest` | see §4 |
|
||||
|
||||
### 3.2 Choose a model
|
||||
|
||||
HF uses **provider-qualified** model names. The value goes on-chain in
|
||||
`AdvertDatum.model` and is forwarded verbatim to the router:
|
||||
|
||||
- `deepseek-ai/DeepSeek-V3:fastest` — bare id + `:fastest` picks the fastest provider
|
||||
- `deepseek-ai/DeepSeek-V3:together` — pin a specific provider
|
||||
- `openai/gpt-oss-120b` — open-weight, HF-hosted
|
||||
|
||||
Browse models and their providers at
|
||||
<https://huggingface.co/models?inference_provider=all>.
|
||||
|
||||
Because the model is on-chain data, you can advertise any router-supported chat
|
||||
model without changing the supplier code — just post a new advert.
|
||||
|
||||
### 3.3 Smoke-test the token before going on-chain
|
||||
|
||||
Confirm your token + model + base URL work end to end, independent of the chain:
|
||||
|
||||
```bash
|
||||
curl https://router.huggingface.co/v1/chat/completions \
|
||||
-H "Authorization: Bearer $HF_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"model":"deepseek-ai/DeepSeek-V3:fastest",
|
||||
"messages":[{"role":"user","content":"say hi in 3 words"}],
|
||||
"stream":false}'
|
||||
```
|
||||
|
||||
A `choices[0].message.content` in the response means the compute backend is
|
||||
reachable. (Note: **no** `reasoning` field — see §5.)
|
||||
|
||||
### 3.4 Post the advert
|
||||
|
||||
```bash
|
||||
pnpm --filter @marketplace/supplier tx:post-advert \
|
||||
--capability-id llm.text.generate.v1 \
|
||||
--model deepseek-ai/DeepSeek-V3:fastest \
|
||||
--max-output-tokens 512 \
|
||||
--max-processing-ms 60000 \
|
||||
--price-lovelace 2000000 \
|
||||
--endpoint-url https://your-supplier.example.org
|
||||
```
|
||||
|
||||
Copy the printed `<txHash>#0` into `ADVERT_REF` in `supplier/.env`. Use
|
||||
`--dry-run` first to inspect the tx without submitting.
|
||||
|
||||
### 3.5 Boot
|
||||
|
||||
Start read-only first (`LIVE_CHAIN` unset) to confirm the node boots and can
|
||||
reach both Ogmios and the HF router, then set `LIVE_CHAIN=1` and restart to
|
||||
enable real Claim/Submit. Deploy via the supplier compose file as in
|
||||
`deploy/README.md`.
|
||||
|
||||
---
|
||||
|
||||
## 4. Sizing the advert (read before you post one)
|
||||
|
||||
`ADVERT_PRICE_LOVELACE` is the **flat AP3X amount committed in escrow per job**,
|
||||
but the HuggingFace backend meters **per token**. The committed amount is fixed
|
||||
per job while upstream token usage varies, so:
|
||||
|
||||
- Size it for your **worst-case** prompt length, not the average.
|
||||
- Cap output tightly with `--max-output-tokens` — it is your main lever on
|
||||
upstream token usage.
|
||||
- Prefer **small** models here; large models have wide token-usage variance and
|
||||
are harder to size a fixed per-job amount around.
|
||||
- Buyer **input** tokens are not capped on-chain today. Until an input-token
|
||||
ceiling lands, stick to small models or add your own request-size guard.
|
||||
|
||||
This is a pre-existing protocol design point (fixed per-job advert amount vs.
|
||||
variable upstream token usage), not specific to HuggingFace. It is the main thing
|
||||
to account for when sizing an advert.
|
||||
|
||||
---
|
||||
|
||||
## 5. Footgun: `OPENAI_REASONING` must stay unset
|
||||
|
||||
`reasoning:{enabled:false}` is an **OpenRouter-specific** parameter. The HF
|
||||
router **rejects it with HTTP 400 on every request**. When copying an existing
|
||||
OpenRouter env, it is easy to carry `OPENAI_REASONING=off` over by accident.
|
||||
|
||||
The supplier guards against this: `loadConfig` **refuses to boot** if
|
||||
`OPENAI_REASONING` is disabled while `OPENAI_BASE_URL` resolves to
|
||||
`router.huggingface.co`, with a clear error naming the fix
|
||||
(`supplier/src/config.ts`). Just leave `OPENAI_REASONING` unset for HF.
|
||||
|
||||
---
|
||||
|
||||
## 6. Where this sits
|
||||
|
||||
This preset is the multi-model on-ramp that uses the HuggingFace router as a
|
||||
compute backend. Further work items (additional task adapters, advert-sizing
|
||||
refinements, and protocol-economics changes) are tracked separately in the team's
|
||||
internal planning notes, along with the upstream-terms check every operator must
|
||||
complete before pointing a node at a third-party compute backend.
|
||||
105
supplier/.env.hetzner.example
Normal file
105
supplier/.env.hetzner.example
Normal 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=https://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=
|
||||
114
supplier/.env.huggingface-chat.example
Normal file
114
supplier/.env.huggingface-chat.example
Normal file
@@ -0,0 +1,114 @@
|
||||
# supplier/.env.huggingface-chat.example
|
||||
#
|
||||
# Preset for a chat supplier that uses the HuggingFace Inference Providers router
|
||||
# (https://router.huggingface.co) as its COMPUTE BACKEND to fulfil bonded
|
||||
# inference work commissioned on Vector. It is the OpenAI-compatible on-ramp:
|
||||
# the router is OpenAI-compatible, so this reuses the existing LLM_BACKEND=openai
|
||||
# path with NO code changes — it is config only.
|
||||
#
|
||||
# Copy to supplier/.env on the host (chmod 600) and fill in real values.
|
||||
# .env is gitignored — NEVER commit a populated copy.
|
||||
#
|
||||
# Runbook: docs/HUGGINGFACE_ROUTER_SETUP.md
|
||||
#
|
||||
# To generate a fresh Ed25519 wallet seed: openssl rand -hex 32
|
||||
# Lines marked SECRET must never be shared, logged, or committed.
|
||||
|
||||
# ─── Required ─────────────────────────────────────────────────────────────
|
||||
|
||||
# 64-char hex Ed25519 private key seed for this supplier's wallet. SECRET.
|
||||
SUPPLIER_PRIV_KEY_HEX=PASTE_64_HEX_CHARS_HERE
|
||||
|
||||
# Ogmios WebSocket URL. Testnet shown; use the mainnet URL for NETWORK_ID=1:
|
||||
# testnet: wss://ogmios.vector.testnet.apexfusion.org
|
||||
# mainnet: wss://ogmios.vector.mainnet.apexfusion.org
|
||||
OGMIOS_URL=wss://ogmios.vector.testnet.apexfusion.org
|
||||
|
||||
# OutputReference of this supplier's on-chain advertisement UTxO.
|
||||
# Format "<64-hex-tx-hash>#<int>". Post it first (see tx:post-advert below),
|
||||
# then paste the printed "<txHash>#0" here.
|
||||
ADVERT_REF=PASTE_TXHASH_HERE#0
|
||||
|
||||
# Cardano network ID. "0" = testnet, "1" = mainnet. Keep "0" until you have
|
||||
# validated the setup end-to-end on testnet.
|
||||
NETWORK_ID=0
|
||||
|
||||
# ─── Backend: HuggingFace router (OpenAI-compatible) ──────────────────────
|
||||
# Route the chat job runner at the HF Inference Providers router instead of a
|
||||
# local Ollama. The supplier POSTs to ${OPENAI_BASE_URL}/v1/chat/completions.
|
||||
LLM_BACKEND=openai
|
||||
|
||||
# HF router base URL. The supplier appends /v1/chat/completions itself, so do
|
||||
# NOT include the /v1 suffix here.
|
||||
OPENAI_BASE_URL=https://router.huggingface.co
|
||||
|
||||
# HuggingFace access token (starts with "hf_"). Create one at
|
||||
# https://huggingface.co/settings/tokens with "Make calls to Inference
|
||||
# Providers" permission. Forwarded as `Authorization: Bearer <token>`. SECRET.
|
||||
OPENAI_API_KEY=PASTE_HF_TOKEN_HERE
|
||||
|
||||
# First-token latency across hosted providers can exceed local Ollama — keep a
|
||||
# generous request timeout.
|
||||
OPENAI_TIMEOUT_MS=180000
|
||||
|
||||
# ─── IMPORTANT: do NOT set OPENAI_REASONING here ──────────────────────────
|
||||
# `reasoning:{enabled:false}` is an OpenRouter-only param. The HF router
|
||||
# rejects it with HTTP 400 on every request. The supplier refuses to boot if
|
||||
# OPENAI_REASONING is disabled while OPENAI_BASE_URL points at the HF router.
|
||||
# (This is the #1 footgun when copying an OpenRouter env — leave it unset.)
|
||||
# OPENAI_REASONING=
|
||||
|
||||
# Optional hard ceiling forwarded as `max_tokens`. Unset = provider grants the
|
||||
# max available (context − prompt). Keep below the model's context window.
|
||||
# OPENAI_MAX_TOKENS=
|
||||
|
||||
# ─── Enable real chain submissions ────────────────────────────────────────
|
||||
# LIVE_CHAIN: only the literal "1" opts in to real Claim/Submit txs. Leave
|
||||
# unset for a dry run (read-only, no chain writes) while testing.
|
||||
LIVE_CHAIN=
|
||||
|
||||
# HTTP listen port inside the container. Compose maps this to the host.
|
||||
PORT=8080
|
||||
|
||||
# ─── tx:post-advert — run once before first deploy ────────────────────────
|
||||
# Execute (from repo root):
|
||||
# pnpm --filter @marketplace/supplier tx:post-advert \
|
||||
# --capability-id llm.text.generate.v1 \
|
||||
# --model deepseek-ai/DeepSeek-V3:fastest \
|
||||
# --max-output-tokens 512 \
|
||||
# --max-processing-ms 60000 \
|
||||
# --price-lovelace 2000000 \
|
||||
# --endpoint-url https://mp-supplier.vector.testnet.apexfusion.org
|
||||
# Copy the printed "<txHash>#0" into ADVERT_REF above.
|
||||
#
|
||||
# NOTE on --model: HF uses PROVIDER-QUALIFIED model names. The bare id picks a
|
||||
# default provider; a ":<hint>" suffix pins routing:
|
||||
# deepseek-ai/DeepSeek-V3:fastest # cheapest/fastest provider for the model
|
||||
# deepseek-ai/DeepSeek-V3:together # pin a specific provider
|
||||
# openai/gpt-oss-120b # open-weight, HF-hosted
|
||||
# Browse models + providers at https://huggingface.co/models?inference_provider=all
|
||||
# The value lives on-chain in AdvertDatum.model — it is data, not code, so you
|
||||
# can advertise any router-supported model without touching the supplier.
|
||||
ADVERT_CAPABILITY_ID=llm.text.generate.v1
|
||||
ADVERT_MODEL=deepseek-ai/DeepSeek-V3:fastest
|
||||
ADVERT_MAX_OUTPUT_TOKENS=512
|
||||
ADVERT_MAX_PROCESSING_MS=60000
|
||||
|
||||
# Flat AP3X amount committed in escrow per job (1 AP3X = 1_000_000 lovelace).
|
||||
# The HF backend meters PER TOKEN while this committed amount is fixed per job,
|
||||
# so size it for your worst-case prompt length and cap output tightly via
|
||||
# ADVERT_MAX_OUTPUT_TOKENS. See docs/HUGGINGFACE_ROUTER_SETUP.md §4.
|
||||
ADVERT_PRICE_LOVELACE=2000000
|
||||
ADVERT_SUPPLIER_BOND_LOVELACE=1000000
|
||||
ADVERT_BUYER_BOND_LOVELACE=1000000
|
||||
|
||||
# HTTPS endpoint buyers will call (must be publicly reachable).
|
||||
ADVERT_ENDPOINT_URL=https://mp-supplier.vector.testnet.apexfusion.org
|
||||
|
||||
# Optional off-chain detail pointer + its sha256 (leave empty for v1).
|
||||
ADVERT_DETAIL_URI=
|
||||
ADVERT_DETAIL_HASH=
|
||||
|
||||
# ─── Optional: CIP-33 reference scripts (recommended on mainnet) ──────────
|
||||
# ESCROW_REF_UTXO=<txhash>#0
|
||||
# ADVERT_REF_UTXO=<txhash>#1
|
||||
@@ -15,7 +15,10 @@
|
||||
*
|
||||
* Required for CAPABILITY_KIND="chat" + LLM_BACKEND="openai":
|
||||
* OPENAI_BASE_URL — http(s):// URL for an OpenAI-compatible /v1/chat/completions
|
||||
* host (e.g. ChatMock localhost proxy backed by Codex OAuth).
|
||||
* host. Examples: ChatMock localhost proxy (Codex OAuth),
|
||||
* OpenRouter (https://openrouter.ai/api), or the HuggingFace
|
||||
* Inference Providers router (https://router.huggingface.co).
|
||||
* See docs/HUGGINGFACE_ROUTER_SETUP.md for the HF preset.
|
||||
*
|
||||
* Required for CAPABILITY_KIND="tts":
|
||||
* PIPER_URL — http(s):// URL for the openedai-speech-min PiperTTS host
|
||||
@@ -51,6 +54,15 @@ const TX_HASH_RE = /^[0-9a-fA-F]{64}$/;
|
||||
const NON_NEG_INT_RE = /^(?:0|[1-9]\d*)$/;
|
||||
const POS_INT_RE = /^[1-9]\d*$/;
|
||||
|
||||
/** Lowercased hostname of a URL, or "" if it doesn't parse. */
|
||||
function hostnameOf(url: string): string {
|
||||
try {
|
||||
return new URL(url).hostname.toLowerCase();
|
||||
} catch {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
export interface AdvertRef {
|
||||
txHash: string;
|
||||
index: number;
|
||||
@@ -336,6 +348,24 @@ export function loadConfig(env: Record<string, string | undefined>): SupplierCon
|
||||
reasoningRaw === "0" ||
|
||||
reasoningRaw === "no";
|
||||
|
||||
// HuggingFace router guard. The `reasoning:{enabled:false}` param is
|
||||
// OpenRouter-specific; the HF Inference Providers router
|
||||
// (router.huggingface.co) rejects it with HTTP 400 on EVERY request. This is
|
||||
// the easy footgun when copying an OpenRouter env to the HF preset, so fail
|
||||
// fast at boot with a clear message instead of 400-ing every job.
|
||||
// See docs/HUGGINGFACE_ROUTER_SETUP.md.
|
||||
if (
|
||||
llmBackend === "openai" &&
|
||||
openaiReasoningDisabled &&
|
||||
hostnameOf(openaiBaseUrl) === "router.huggingface.co"
|
||||
) {
|
||||
throw new Error(
|
||||
"loadConfig: OPENAI_REASONING must be unset for the HuggingFace router " +
|
||||
"(OPENAI_BASE_URL=https://router.huggingface.co) — it rejects " +
|
||||
"reasoning:{enabled:false} with HTTP 400. Remove OPENAI_REASONING.",
|
||||
);
|
||||
}
|
||||
|
||||
// OPENAI_MAX_TOKENS — optional hard ceiling forwarded as `max_tokens`. Unset
|
||||
// or non-positive omits it (provider grants max available = context − prompt).
|
||||
// Keep it below the model context (kimi 262144) or the provider 400s when
|
||||
|
||||
@@ -184,6 +184,46 @@ describe("loadConfig() — LIVE_CHAIN parsing", () => {
|
||||
});
|
||||
});
|
||||
|
||||
// ─── loadConfig — HuggingFace router preset (W1) ────────────────────────────
|
||||
// The HF Inference Providers router (router.huggingface.co) is OpenAI-compatible
|
||||
// and reuses LLM_BACKEND=openai, but rejects the OpenRouter-specific
|
||||
// reasoning:{enabled:false} param with HTTP 400 — so OPENAI_REASONING must stay
|
||||
// unset for it. loadConfig fails fast at boot to catch the copy-from-OpenRouter
|
||||
// footgun. See docs/HUGGINGFACE_ROUTER_SETUP.md.
|
||||
|
||||
describe("loadConfig() — HuggingFace router guard", () => {
|
||||
const hfEnv = (over: Record<string, string> = {}) => ({
|
||||
...buildSampleEnv(),
|
||||
LLM_BACKEND: "openai",
|
||||
OPENAI_BASE_URL: "https://router.huggingface.co",
|
||||
OPENAI_API_KEY: "hf_sampletoken",
|
||||
...over,
|
||||
});
|
||||
|
||||
it("accepts the HF router with OPENAI_REASONING unset", () => {
|
||||
const cfg = loadConfig(hfEnv());
|
||||
expect(cfg.openaiBaseUrl).toBe("https://router.huggingface.co");
|
||||
expect(cfg.llmBackend).toBe("openai");
|
||||
expect(cfg.openaiReasoningDisabled).toBe(false);
|
||||
});
|
||||
|
||||
it("throws when OPENAI_REASONING=off is set against the HF router", () => {
|
||||
expect(() => loadConfig(hfEnv({ OPENAI_REASONING: "off" }))).toThrow(
|
||||
/OPENAI_REASONING must be unset for the HuggingFace router/i,
|
||||
);
|
||||
});
|
||||
|
||||
it("still allows OPENAI_REASONING=off for non-HF openai backends (e.g. OpenRouter)", () => {
|
||||
const cfg = loadConfig({
|
||||
...buildSampleEnv(),
|
||||
LLM_BACKEND: "openai",
|
||||
OPENAI_BASE_URL: "https://openrouter.ai/api",
|
||||
OPENAI_REASONING: "off",
|
||||
});
|
||||
expect(cfg.openaiReasoningDisabled).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
// ─── parseAdvertRef ──────────────────────────────────────────────────────────
|
||||
|
||||
describe("parseAdvertRef()", () => {
|
||||
|
||||
Reference in New Issue
Block a user