| import { buildModelContext } from "./agent-core.mjs"; |
| import { SYSTEM_PROMPT, parseModelJson } from "./model-prompt.mjs"; |
|
|
| const DEFAULT_MODEL = "Qwen/Qwen3-Coder-30B-A3B-Instruct"; |
| const DEFAULT_CHAT_URL = "https://router.huggingface.co/v1/chat/completions"; |
|
|
| export async function runHfAgent(localRun, env = process.env) { |
| if (env.AGENT_OFFLINE === "1") { |
| return { |
| status: "skipped", |
| reason: "AGENT_OFFLINE is enabled", |
| model: configuredModel(env), |
| result: null, |
| }; |
| } |
|
|
| const token = env.HF_TOKEN || env.HUGGINGFACEHUB_API_TOKEN || env.HUGGING_FACE_HUB_TOKEN; |
| if (!token) { |
| return { |
| status: "skipped", |
| reason: "HF_TOKEN is not configured", |
| model: configuredModel(env), |
| result: null, |
| }; |
| } |
|
|
| const model = configuredModel(env); |
| const context = buildModelContext(localRun); |
| const basePayload = { |
| model, |
| messages: [ |
| { role: "system", content: SYSTEM_PROMPT }, |
| { |
| role: "user", |
| content: JSON.stringify(context), |
| }, |
| ], |
| temperature: Number(env.HF_TEMPERATURE ?? 0.2), |
| max_tokens: Number(env.HF_MAX_TOKENS ?? 900), |
| }; |
|
|
| const richPayload = { |
| ...basePayload, |
| response_format: { type: "json_object" }, |
| reasoning_effort: env.HF_REASONING_EFFORT || "medium", |
| }; |
|
|
| try { |
| const body = await postChat(richPayload, token, env); |
| return { |
| status: "done", |
| reason: null, |
| model, |
| result: parseModelJson(body, "Hugging Face"), |
| }; |
| } catch (error) { |
| if (error instanceof HfRequestError && (error.status === 400 || error.status === 422)) { |
| try { |
| const body = await postChat(basePayload, token, env); |
| return { |
| status: "done", |
| reason: "Retried without provider-specific JSON/reasoning parameters", |
| model, |
| result: parseModelJson(body, "Hugging Face"), |
| }; |
| } catch (retryError) { |
| return { |
| status: "error", |
| reason: |
| retryError instanceof Error |
| ? retryError.message |
| : "Hugging Face retry failed with an unknown error", |
| model, |
| result: null, |
| }; |
| } |
| } |
|
|
| return { |
| status: "error", |
| reason: error instanceof Error ? error.message : "Unknown Hugging Face error", |
| model, |
| result: null, |
| }; |
| } |
| } |
|
|
| export function configuredModel(env = process.env) { |
| const suffix = env.HF_MODEL_SUFFIX || ""; |
| return `${env.HF_MODEL || DEFAULT_MODEL}${suffix}`; |
| } |
|
|
| export function chatCompletionsUrl(env = process.env) { |
| if (env.HF_CHAT_COMPLETIONS_URL) return env.HF_CHAT_COMPLETIONS_URL; |
| if (env.HF_ENDPOINT_URL) { |
| return `${env.HF_ENDPOINT_URL.replace(/\/$/, "")}/chat/completions`; |
| } |
| return env.HF_ROUTER_URL || DEFAULT_CHAT_URL; |
| } |
|
|
| async function postChat(payload, token, env) { |
| const timeoutMs = Number(env.HF_TIMEOUT_MS ?? 30000); |
| const controller = new AbortController(); |
| const timeout = setTimeout(() => controller.abort(), timeoutMs); |
|
|
| try { |
| const response = await fetch(chatCompletionsUrl(env), { |
| method: "POST", |
| headers: { |
| Authorization: `Bearer ${token}`, |
| "Content-Type": "application/json", |
| }, |
| body: JSON.stringify(payload), |
| signal: controller.signal, |
| }); |
|
|
| const text = await response.text(); |
| if (!response.ok) { |
| throw new HfRequestError(response.status, conciseError(text)); |
| } |
|
|
| try { |
| return JSON.parse(text); |
| } catch { |
| throw new Error("Hugging Face returned a non-JSON chat response"); |
| } |
| } catch (error) { |
| if (error?.name === "AbortError") { |
| throw new Error(`Hugging Face request timed out after ${timeoutMs}ms`); |
| } |
| throw error; |
| } finally { |
| clearTimeout(timeout); |
| } |
| } |
|
|
| function conciseError(text) { |
| if (!text) return "empty error body"; |
| try { |
| const parsed = JSON.parse(text); |
| return parsed.error?.message || parsed.message || JSON.stringify(parsed).slice(0, 500); |
| } catch { |
| return text.slice(0, 500); |
| } |
| } |
|
|
| class HfRequestError extends Error { |
| constructor(status, message) { |
| super(`Hugging Face request failed with HTTP ${status}: ${message}`); |
| this.name = "HfRequestError"; |
| this.status = status; |
| } |
| } |
|
|