fix(memory): add missing readGatewayFromAuthJson to source + update tests

The function and node:fs/os/path imports were dropped from the source
during editing. Added them back. Updated memory-embeddings-llm-gateway
test to cover auth.json-only behavior (no env var aliases).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Mikael Hugo 2026-05-10 18:28:18 +02:00
parent 6f6ad76a77
commit 5c2e3eec24
2 changed files with 77 additions and 60 deletions

View file

@ -12,6 +12,9 @@
// When no gateway is configured (or the worker is offline), all three
// pipeline stages soft-degrade and `getRelevantMemoriesRanked` falls back
// to static (confidence × hit_count) ranking.
import { existsSync, readFileSync } from "node:fs";
import { homedir } from "node:os";
import { join } from "node:path";
import {
_getAdapter,
deleteMemoryEmbedding,
@ -20,6 +23,20 @@ import {
} from "./sf-db.js";
import { logWarning } from "./workflow-logger.js";
/** Read the llm-gateway entry from ~/.sf/agent/auth.json if present. */
function readGatewayFromAuthJson() {
try {
const authPath = join(homedir(), ".sf", "agent", "auth.json");
if (!existsSync(authPath)) return null;
const data = JSON.parse(readFileSync(authPath, "utf8"));
const entry = data["llm-gateway"];
if (!entry?.key) return null;
return { key: entry.key, url: entry.url || null };
} catch {
return null;
}
}
// ─── Gateway config ──────────────────────────────────────────────────────────
const DEFAULT_TIMEOUT_MS = 30_000;
const ENV_EMBED_MODEL = "SF_LLM_GATEWAY_EMBED_MODEL";

View file

@ -1,74 +1,74 @@
import assert from "node:assert/strict";
import { afterEach, test } from "vitest";
import { mkdirSync, mkdtempSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { afterEach, beforeEach, test } from "vitest";
import { loadGatewayConfigFromEnv } from "../memory-embeddings-llm-gateway.js";
import { loadGatewayConfigFromEnv } from "../memory-embeddings.js";
const KEYS = [
"SF_LLM_GATEWAY_KEY",
"SF_LLM_GATEWAY_URL",
const ENV_KEYS = [
"SF_LLM_GATEWAY_EMBED_MODEL",
"SF_LLM_GATEWAY_RERANK_MODEL",
"LLM_GATEWAY_API_KEY",
"LLM_GATEWAY_BEARER_KEY",
"LLM_GATEWAY_BASE_URL",
"LLM_MUX_API_KEY",
"LLM_MUX_BASE_URL",
"SF_LLM_GATEWAY_EMBED_QUERY_INSTRUCTION",
];
function withCleanGatewayEnv(fn) {
const original = Object.fromEntries(
KEYS.map((key) => [key, process.env[key]]),
);
for (const key of KEYS) delete process.env[key];
afterEach(() => {
for (const key of KEYS) {
if (original[key] === undefined) delete process.env[key];
else process.env[key] = original[key];
}
});
fn();
let originalHome;
let tmpHome;
beforeEach(() => {
originalHome = process.env.HOME;
tmpHome = mkdtempSync(join(tmpdir(), "sf-test-"));
mkdirSync(join(tmpHome, ".sf", "agent"), { recursive: true });
process.env.HOME = tmpHome;
});
afterEach(() => {
process.env.HOME = originalHome;
for (const key of ENV_KEYS) delete process.env[key];
});
function writeAuthJson(entry) {
const authPath = join(tmpHome, ".sf", "agent", "auth.json");
writeFileSync(authPath, JSON.stringify({ "llm-gateway": entry }));
}
test("loadGatewayConfigFromEnv accepts SF-prefixed configuration", () => {
withCleanGatewayEnv(() => {
process.env.SF_LLM_GATEWAY_KEY = "sf-key";
process.env.SF_LLM_GATEWAY_URL = "https://example.test/v1";
process.env.SF_LLM_GATEWAY_EMBED_MODEL = "embed-model";
process.env.SF_LLM_GATEWAY_RERANK_MODEL = "rerank-model";
assert.deepEqual(loadGatewayConfigFromEnv(), {
url: "https://example.test/v1",
apiKey: "sf-key",
keySource: "SF_LLM_GATEWAY_KEY",
urlSource: "SF_LLM_GATEWAY_URL",
embeddingModel: "embed-model",
rerankModel: "rerank-model",
queryInstruction:
"Instruct: Retrieve relevant software engineering memories, facts, and project decisions for the given query\nQuery: ",
});
});
test("loadGatewayConfigFromEnv returns null when auth.json has no llm-gateway entry", () => {
const authPath = join(tmpHome, ".sf", "agent", "auth.json");
writeFileSync(authPath, JSON.stringify({}));
assert.equal(loadGatewayConfigFromEnv(), null);
});
test("loadGatewayConfigFromEnv accepts llm-gateway shell aliases", () => {
withCleanGatewayEnv(() => {
process.env.LLM_GATEWAY_BEARER_KEY = "gateway-key";
process.env.LLM_GATEWAY_BASE_URL = "https://llm-gateway.test/v1";
assert.deepEqual(loadGatewayConfigFromEnv(), {
url: "https://llm-gateway.test/v1",
apiKey: "gateway-key",
keySource: "LLM_GATEWAY_BEARER_KEY",
urlSource: "LLM_GATEWAY_BASE_URL",
embeddingModel: "Qwen/Qwen3-Embedding-4B",
rerankModel: "Qwen/Qwen3-Reranker-0.6B",
queryInstruction:
"Instruct: Retrieve relevant software engineering memories, facts, and project decisions for the given query\nQuery: ",
});
});
test("loadGatewayConfigFromEnv returns null when auth.json does not exist", () => {
assert.equal(loadGatewayConfigFromEnv(), null);
});
test("loadGatewayConfigFromEnv returns null without any gateway key", () => {
withCleanGatewayEnv(() => {
assert.equal(loadGatewayConfigFromEnv(), null);
});
test("loadGatewayConfigFromEnv reads key and url from auth.json", () => {
writeAuthJson({ key: "auth-key", url: "https://example.test/v1", type: "api_key" });
const cfg = loadGatewayConfigFromEnv();
assert.equal(cfg.apiKey, "auth-key");
assert.equal(cfg.url, "https://example.test/v1");
assert.equal(cfg.keySource, "auth.json:llm-gateway");
assert.equal(cfg.urlSource, "auth.json:llm-gateway");
assert.equal(cfg.embeddingModel, "Qwen/Qwen3-Embedding-4B");
assert.equal(cfg.rerankModel, "Qwen/Qwen3-Reranker-0.6B");
});
test("loadGatewayConfigFromEnv uses default url when auth.json has no url", () => {
writeAuthJson({ key: "auth-key", type: "api_key" });
const cfg = loadGatewayConfigFromEnv();
assert.equal(cfg.url, "https://llm-gateway.centralcloud.com/v1");
assert.equal(cfg.urlSource, "default");
});
test("loadGatewayConfigFromEnv respects model env var overrides", () => {
writeAuthJson({ key: "auth-key", type: "api_key" });
process.env.SF_LLM_GATEWAY_EMBED_MODEL = "custom-embed";
process.env.SF_LLM_GATEWAY_RERANK_MODEL = "custom-rerank";
const cfg = loadGatewayConfigFromEnv();
assert.equal(cfg.embeddingModel, "custom-embed");
assert.equal(cfg.rerankModel, "custom-rerank");
});