AI runs on our own model server (Ollama): address and token in Server settings
With a model server address set, runs send every model slot (opus/sonnet/haiku/subagent) to the chosen model and never send the Anthropic key there. Tested locally with qwen3.6:35b-a3b through the Agent SDK. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
dff014e69a
commit
32563e87f9
@ -51,8 +51,9 @@ export function checks() {
|
|||||||
// AI runs
|
// AI runs
|
||||||
const recent = jobs.listJobs({}).filter((j) => Date.now() - new Date(j.createdAt) < 7 * 24 * HOUR);
|
const recent = jobs.listJobs({}).filter((j) => Date.now() - new Date(j.createdAt) < 7 * 24 * HOUR);
|
||||||
const jobFails = recent.filter((j) => ["failed", "interrupted"].includes(j.status));
|
const jobFails = recent.filter((j) => ["failed", "interrupted"].includes(j.status));
|
||||||
add("AI runs", process.env.ANTHROPIC_API_KEY ? (jobFails.length ? "warn" : "ok") : "warn",
|
const aiReady = Boolean(process.env.ANTHROPIC_API_KEY || process.env.ANTHROPIC_BASE_URL);
|
||||||
!process.env.ANTHROPIC_API_KEY ? "No Anthropic API key: AI runs (audits, weekly plans) can't start" : jobFails.length ? `${jobFails.length} AI run${jobFails.length === 1 ? "" : "s"} failed this week` : `AI runs working (${recent.length} this week)`,
|
add("AI runs", aiReady ? (jobFails.length ? "warn" : "ok") : "warn",
|
||||||
|
!aiReady ? "No Anthropic API key: AI runs (audits, weekly plans) can't start" : jobFails.length ? `${jobFails.length} AI run${jobFails.length === 1 ? "" : "s"} failed this week` : `AI runs working (${recent.length} this week)`,
|
||||||
jobFails.slice(0, 3).map((j) => `${j.type} ${j.slug || ""}: ${j.error || j.status}`).join(" · "));
|
jobFails.slice(0, 3).map((j) => `${j.type} ${j.slug || ""}: ${j.error || j.status}`).join(" · "));
|
||||||
|
|
||||||
// Backups and disk
|
// Backups and disk
|
||||||
|
|||||||
@ -231,7 +231,7 @@ app.get("/api/status", h(async () => {
|
|||||||
return {
|
return {
|
||||||
meta: metaStatus,
|
meta: metaStatus,
|
||||||
odoo: await odoo.status(),
|
odoo: await odoo.status(),
|
||||||
ai: { model: process.env.DASHBOARD_AI_MODEL || "claude-opus-5-5", apiKey: Boolean(process.env.ANTHROPIC_API_KEY) },
|
ai: { model: process.env.DASHBOARD_AI_MODEL || "claude-opus-5-5", apiKey: Boolean(process.env.ANTHROPIC_API_KEY || process.env.ANTHROPIC_BASE_URL), server: process.env.ANTHROPIC_BASE_URL || null },
|
||||||
publishing: process.env.ALLOW_PUBLISH === "true",
|
publishing: process.env.ALLOW_PUBLISH === "true",
|
||||||
};
|
};
|
||||||
}));
|
}));
|
||||||
|
|||||||
@ -131,6 +131,15 @@ function docStamp(slug) {
|
|||||||
return new Map(listDocs(slug).map((d) => [d.path, d.updatedAt]));
|
return new Map(listDocs(slug).map((d) => [d.path, d.updatedAt]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Runs on our own model server (Ollama etc.): every model slot points at the chosen model, and the Anthropic key stays home. */
|
||||||
|
function runEnv() {
|
||||||
|
if (!process.env.ANTHROPIC_BASE_URL) return undefined;
|
||||||
|
const env = { ...process.env, ANTHROPIC_AUTH_TOKEN: process.env.ANTHROPIC_AUTH_TOKEN || "ollama", CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: "1" };
|
||||||
|
delete env.ANTHROPIC_API_KEY;
|
||||||
|
for (const k of ["ANTHROPIC_DEFAULT_OPUS_MODEL", "ANTHROPIC_DEFAULT_SONNET_MODEL", "ANTHROPIC_DEFAULT_HAIKU_MODEL", "CLAUDE_CODE_SUBAGENT_MODEL"]) env[k] = AI.model;
|
||||||
|
return env;
|
||||||
|
}
|
||||||
|
|
||||||
async function run(job) {
|
async function run(job) {
|
||||||
Object.assign(job, { status: "running", startedAt: new Date().toISOString() });
|
Object.assign(job, { status: "running", startedAt: new Date().toISOString() });
|
||||||
job.abort = new AbortController();
|
job.abort = new AbortController();
|
||||||
@ -146,6 +155,7 @@ async function run(job) {
|
|||||||
skills: "all",
|
skills: "all",
|
||||||
model: AI.model,
|
model: AI.model,
|
||||||
maxTurns: AI.maxTurns,
|
maxTurns: AI.maxTurns,
|
||||||
|
env: runEnv(),
|
||||||
permissionMode: "dontAsk",
|
permissionMode: "dontAsk",
|
||||||
allowedTools: READ_TOOLS,
|
allowedTools: READ_TOOLS,
|
||||||
disallowedTools: BLOCKED_TOOLS,
|
disallowedTools: BLOCKED_TOOLS,
|
||||||
|
|||||||
@ -28,6 +28,8 @@ const GROUPS = [
|
|||||||
{ key: "ANTHROPIC_API_KEY", label: "Anthropic API key", hint: "For audits, weekly plans and reports.", secret: true },
|
{ key: "ANTHROPIC_API_KEY", label: "Anthropic API key", hint: "For audits, weekly plans and reports.", secret: true },
|
||||||
{ key: "DASHBOARD_AI_MODEL", label: "Model", placeholder: "claude-opus-5-5" },
|
{ key: "DASHBOARD_AI_MODEL", label: "Model", placeholder: "claude-opus-5-5" },
|
||||||
{ key: "DASHBOARD_AI_MAX_TURNS", label: "Max steps per run", type: "number", placeholder: "80" },
|
{ key: "DASHBOARD_AI_MAX_TURNS", label: "Max steps per run", type: "number", placeholder: "80" },
|
||||||
|
{ key: "ANTHROPIC_BASE_URL", label: "Model server address", type: "url", placeholder: "http://172.17.0.1:11434", hint: "Leave empty to use Anthropic. For your own Ollama, enter its address and set Model to the Ollama model name (e.g. qwen3.6:35b-a3b)." },
|
||||||
|
{ key: "ANTHROPIC_AUTH_TOKEN", label: "Model server token", secret: true, hint: "Only with a model server address. Ollama accepts any text, e.g. ollama." },
|
||||||
] },
|
] },
|
||||||
{ name: "Email (SMTP)", scope: "self", fields: [
|
{ name: "Email (SMTP)", scope: "self", fields: [
|
||||||
{ key: "SMTP_HOST", label: "Host", placeholder: "mail.example.com" },
|
{ key: "SMTP_HOST", label: "Host", placeholder: "mail.example.com" },
|
||||||
|
|||||||
@ -418,7 +418,7 @@ export default function Settings() {
|
|||||||
: s.odoo.configured ? `Can't connect: ${s.odoo.error}` : "Not connected. Connect it in Connections → Odoo."}
|
: s.odoo.configured ? `Can't connect: ${s.odoo.error}` : "Not connected. Connect it in Connections → Odoo."}
|
||||||
</Row>
|
</Row>
|
||||||
<Row ok warn={!s.ai.apiKey} title="Claude · AI runs">
|
<Row ok warn={!s.ai.apiKey} title="Claude · AI runs">
|
||||||
Model {s.ai.model}. {s.ai.apiKey ? "Using the ANTHROPIC_API_KEY on this server." : "No ANTHROPIC_API_KEY set: runs use this machine's Claude Code login. A hosted server needs an API key."}
|
Model {s.ai.model}. {s.ai.server ? `Using the model server at ${s.ai.server}.` : s.ai.apiKey ? "Using the ANTHROPIC_API_KEY on this server." : "No ANTHROPIC_API_KEY set: runs use this machine's Claude Code login. A hosted server needs an API key."}
|
||||||
</Row>
|
</Row>
|
||||||
<Row ok={s.publishing} warn={!s.publishing} title="Publishing">
|
<Row ok={s.publishing} warn={!s.publishing} title="Publishing">
|
||||||
{s.publishing
|
{s.publishing
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user