feat(supplier): HuggingFace Inference Providers router backend preset
Some checks failed
CI / typecheck + vitest (Node 20) (push) Has been cancelled
Some checks failed
CI / typecheck + vitest (Node 20) (push) Has been cancelled
OpenAI-compatible compute-backend preset (config only, reuses LLM_BACKEND=openai) for pointing a supplier node at the HuggingFace Inference Providers router. Adds a boot guard that rejects the OpenRouter-only reasoning param against the HF router, unit tests, and an operator runbook. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
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;
|
||||
@@ -269,6 +281,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
|
||||
|
||||
Reference in New Issue
Block a user