fix(sse): disambiguate compatible provider credential lookup

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
This commit is contained in:
adevwithpurpose
2026-08-17 11:09:56 -03:00
parent d3e561eb91
commit be43693376
2 changed files with 53 additions and 2 deletions

View File

@@ -964,7 +964,17 @@ async function getProviderSearchPool(provider: string): Promise<string[]> {
// internal provider ids like openai-compatible-responses-<uuid>.
try {
const providerNodes = await getCachedProviderNodes();
for (const node of Array.isArray(providerNodes) ? providerNodes : []) {
const compatibleNodes = Array.isArray(providerNodes) ? providerNodes : [];
const nodeTypes = new Map<string, number>();
for (const node of compatibleNodes) {
const nodeRecord = asRecord(node);
const nodeId = typeof nodeRecord.id === "string" ? nodeRecord.id.trim() : "";
if (!nodeId) continue;
const derivedType = nodeTypeFromId(nodeId);
nodeTypes.set(derivedType, (nodeTypes.get(derivedType) || 0) + 1);
}
for (const node of compatibleNodes) {
const nodeRecord = asRecord(node);
const nodePrefix = typeof nodeRecord.prefix === "string" ? nodeRecord.prefix.trim() : "";
const nodeId = typeof nodeRecord.id === "string" ? nodeRecord.id.trim() : "";
@@ -984,8 +994,9 @@ async function getProviderSearchPool(provider: string): Promise<string[]> {
// looks up the concrete node id, and vice versa.
const derivedType = nodeTypeFromId(nodeId);
if (derivedType && derivedType !== nodeId) {
const typeIsUnambiguous = nodeTypes.get(derivedType) === 1;
if (nodeId === provider || nodeId === canonicalProvider || nodeId === canonicalAlias) {
searchPool.add(derivedType);
if (typeIsUnambiguous) searchPool.add(derivedType);
}
if (
derivedType === provider ||

View File

@@ -29,6 +29,7 @@ const auth = await import("../../src/sse/services/auth.ts");
const NODE_PREFIX = "my-compat-10085";
const NODE_ID = `openai-compatible-chat-458d982b-0000-4000-8000-000000000000`;
const NODE_B_ID = `openai-compatible-chat-558d982b-0000-4000-8000-000000000000`;
async function resetStorage() {
core.resetDbInstance();
@@ -52,6 +53,17 @@ async function seedNode() {
});
}
async function seedSecondNode() {
await nodesDb.createProviderNode({
id: NODE_B_ID,
type: "openai-compatible",
name: "My Compat B",
prefix: "my-compat-10085-b",
apiType: "chat",
baseUrl: "https://example-b.test/v1",
});
}
test("a connection stored under the GENERIC type id is reachable when chat resolves the uuid node id (#10085)", async () => {
await resetStorage();
await seedNode();
@@ -99,6 +111,34 @@ test("the bridge works in the other direction too: a uuid-stored connection is r
);
});
test("a concrete second node does not inherit the first node's generic credentials", async () => {
await resetStorage();
await seedNode();
await seedSecondNode();
await providersDb.createProviderConnection({
provider: "openai-compatible-chat",
authType: "apikey",
apiKey: "sk-test-10085-node-a",
name: "test-compat-node-a",
isActive: true,
testStatus: "active",
priority: 1,
providerSpecificData: {
nodeId: NODE_ID,
prefix: NODE_PREFIX,
baseUrl: "https://example-a.test/v1",
},
});
const creds = await auth.getProviderCredentials(NODE_B_ID);
assert.equal(
creds,
null,
"node B must not receive node A's generic connection when both nodes share a type"
);
});
test("control: a connection stored under the uuid node id is found by a uuid node id lookup", async () => {
await resetStorage();
await seedNode();