supplier+deploy/mainnet: optional OPENAI_API_KEY for DeepSeek (and other hosted OpenAI-compatible APIs)
Adds an optional OPENAI_API_KEY env var that supplier/src/openai.ts forwards as `Authorization: Bearer <key>`. Empty/unset preserves the ChatMock behaviour (no auth header). Unlocks pointing the openai backend at DeepSeek directly (https://api.deepseek.com) — no local proxy needed. New compose: deploy/mainnet/docker-compose.supplier-deepseek.yml (third mainnet supplier, model deepseek-v4-pro, mp-suppliers-deepseek.vector.apexfusion.org). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -42,6 +42,20 @@ PORT=8080
|
||||
# generous. Increase if you see ollama_timeout reasons in logs.
|
||||
OLLAMA_TIMEOUT_MS=120000
|
||||
|
||||
# ─── Optional — OpenAI-compatible backend (LLM_BACKEND=openai) ────────────
|
||||
# Switch the chat job runner to POST /v1/chat/completions at OPENAI_BASE_URL
|
||||
# instead of Ollama. Used by the mainnet ChatMock-backed supplier and any
|
||||
# hosted OpenAI-compatible API (OpenAI, DeepSeek, etc.).
|
||||
# LLM_BACKEND=openai
|
||||
# OPENAI_BASE_URL=http://marketplace-mainnet-chatmock:8000 # or https://api.deepseek.com
|
||||
# OPENAI_TIMEOUT_MS=180000
|
||||
|
||||
# OPENAI_API_KEY — optional bearer token. When set, the supplier forwards it
|
||||
# as `Authorization: Bearer <key>` on every /v1/chat/completions request.
|
||||
# Leave empty for endpoints that don't require auth (ChatMock, other local
|
||||
# proxies). REQUIRED for hosted APIs (DeepSeek, OpenAI). SECRET.
|
||||
# OPENAI_API_KEY=
|
||||
|
||||
# ─── Optional (not in supplier/src/config.ts; consumed by index.ts boot) ──
|
||||
# Pre-derived public-key + pkh + bech32 address. M1-D will derive these from
|
||||
# SUPPLIER_PRIV_KEY_HEX automatically; until then index.ts reads them from
|
||||
|
||||
@@ -65,6 +65,12 @@ export interface SupplierConfig {
|
||||
ollamaUrl: string;
|
||||
/** Empty string when capabilityKind="tts" or llmBackend="ollama". */
|
||||
openaiBaseUrl: string;
|
||||
/**
|
||||
* Bearer token forwarded as `Authorization: Bearer <key>` when llmBackend="openai".
|
||||
* Empty string means no header is sent (correct for ChatMock and other localhost
|
||||
* proxies). Required for hosted OpenAI-compatible APIs (DeepSeek, OpenAI, etc.).
|
||||
*/
|
||||
openaiApiKey: string;
|
||||
/** Empty string when capabilityKind="chat". */
|
||||
piperUrl: string;
|
||||
advertRef: AdvertRef;
|
||||
@@ -193,6 +199,8 @@ export function loadConfig(env: Record<string, string | undefined>): SupplierCon
|
||||
openaiTimeoutMs = Number(openaiTimeoutStr);
|
||||
}
|
||||
|
||||
const openaiApiKey = env.OPENAI_API_KEY ?? "";
|
||||
|
||||
const piperTimeoutStr = env.PIPER_TIMEOUT_MS;
|
||||
let piperTimeoutMs = 120_000;
|
||||
if (piperTimeoutStr !== undefined && piperTimeoutStr !== "") {
|
||||
@@ -212,6 +220,7 @@ export function loadConfig(env: Record<string, string | undefined>): SupplierCon
|
||||
ogmiosUrl,
|
||||
ollamaUrl,
|
||||
openaiBaseUrl,
|
||||
openaiApiKey,
|
||||
piperUrl,
|
||||
advertRef,
|
||||
networkId,
|
||||
|
||||
@@ -91,6 +91,7 @@ export async function runChatJob(params: RunChatJobParams): Promise<void> {
|
||||
model: advert.model,
|
||||
messages: requestBody.messages,
|
||||
timeoutMs: deps.config.openaiTimeoutMs,
|
||||
apiKey: deps.config.openaiApiKey,
|
||||
});
|
||||
} else {
|
||||
inference = await ollama.callOllama({
|
||||
|
||||
@@ -33,6 +33,12 @@ export interface CallOpenAiParams {
|
||||
model: string;
|
||||
messages: ChatMessage[];
|
||||
timeoutMs: number;
|
||||
/**
|
||||
* Optional bearer token. When non-empty, an `authorization: Bearer <apiKey>`
|
||||
* header is sent. Omit (or pass "") for endpoints that don't require auth,
|
||||
* e.g. the ChatMock localhost proxy.
|
||||
*/
|
||||
apiKey?: string;
|
||||
}
|
||||
|
||||
export interface OpenAiResult {
|
||||
@@ -62,10 +68,15 @@ function isAbortError(err: unknown): boolean {
|
||||
}
|
||||
|
||||
export async function callOpenAi(params: CallOpenAiParams): Promise<OpenAiResult> {
|
||||
const { baseUrl, model, messages, timeoutMs } = params;
|
||||
const { baseUrl, model, messages, timeoutMs, apiKey } = params;
|
||||
const url = `${baseUrl}/v1/chat/completions`;
|
||||
const body = JSON.stringify({ model, messages, stream: false });
|
||||
|
||||
const headers: Record<string, string> = { "content-type": "application/json" };
|
||||
if (apiKey) {
|
||||
headers.authorization = `Bearer ${apiKey}`;
|
||||
}
|
||||
|
||||
const controller = new AbortController();
|
||||
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
||||
|
||||
@@ -74,7 +85,7 @@ export async function callOpenAi(params: CallOpenAiParams): Promise<OpenAiResult
|
||||
try {
|
||||
response = await fetch(url, {
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/json" },
|
||||
headers,
|
||||
body,
|
||||
signal: controller.signal,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user