Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions src/config/providers.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { describe, expect, test } from "bun:test";

import type { ProviderCatalogEntry } from "./index.js";
import { buildProviderEntry } from "./providers.js";

const baseCatalog: ProviderCatalogEntry[] = [
{
name: "bf",
baseURL: "http://localhost:8080/v1",
apiKey: "sk-bf-existing",
models: ["m1"],
bifrostVirtualKey: true,
},
{
name: "openai",
baseURL: "https://api.openai.com/v1",
apiKey: "sk-openai",
models: ["gpt-4o"],
},
];

describe("buildProviderEntry bifrostVirtualKey preserve-on-edit", () => {
test("keeps existing bifrostVirtualKey when submission omits the flag", () => {
const result = buildProviderEntry(
{
name: "bf",
originalName: "bf",
baseURL: "http://localhost:8080/v1",
models: ["m1", "m2"],
},
baseCatalog,
);

expect(result.ok).toBe(true);
if (!result.ok) return;
expect(result.entry.bifrostVirtualKey).toBe(true);
});

test("omits bifrostVirtualKey when existing entry has no flag", () => {
const result = buildProviderEntry(
{
name: "openai",
originalName: "openai",
baseURL: "https://api.openai.com/v1",
models: ["gpt-4o-mini"],
},
baseCatalog,
);

expect(result.ok).toBe(true);
if (!result.ok) return;
expect(result.entry.bifrostVirtualKey).toBeUndefined();
});
});
6 changes: 5 additions & 1 deletion src/config/providers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,11 @@ export function buildProviderEntry(
...(apiKey !== undefined && apiKey.length > 0 ? { apiKey } : {}),
models: submission.models,
...(submission.defaultModel !== undefined ? { defaultModel: submission.defaultModel } : {}),
...(submission.bifrostVirtualKey === true ? { bifrostVirtualKey: true } : {}),
// Form no longer exposes Bifrost; keep any previously stored flag on edit so
// re-saving a provider does not silently drop x-bf-vk routing.
...(submission.bifrostVirtualKey === true || existing?.bifrostVirtualKey === true
? { bifrostVirtualKey: true }
: {}),
};
const catalog = currentCatalog
.filter((p) => p.name !== submission.name && p.name !== submission.originalName)
Expand Down
3 changes: 1 addition & 2 deletions src/tui/components/agent-modal.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,8 @@ const form = (overrides: Partial<ProviderFormValues> = {}): ProviderFormValues =
keyless: "no",
models: "fp-large, fp-small",
defaultModel: "fp-large",
bifrostVirtualKey: "no",
...overrides,
} as ProviderFormValues);
});

describe("validateProviderForm", () => {
test("creates a provider submission from comma-separated models", () => {
Expand Down
19 changes: 3 additions & 16 deletions src/tui/components/agent-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export type AgentProvider = {

export type { ProviderSubmission, ProviderSubmission as ProviderFormSubmission };

export type ProviderFormField = "name" | "baseURL" | "keyless" | "apiKey" | "models" | "defaultModel" | "bifrostVirtualKey";
export type ProviderFormField = "name" | "baseURL" | "keyless" | "apiKey" | "models" | "defaultModel";
export type ProviderFormValues = Record<ProviderFormField, string>;
type Step =
| "provider"
Expand All @@ -62,7 +62,7 @@ type Step =
| "profile-form"
| "profile-delete";

const FORM_FIELDS: readonly ProviderFormField[] = ["name", "baseURL", "keyless", "apiKey", "models", "defaultModel", "bifrostVirtualKey"];
const FORM_FIELDS: readonly ProviderFormField[] = ["name", "baseURL", "keyless", "apiKey", "models", "defaultModel"];

const FIELD_LABELS: Record<ProviderFormField, string> = {
name: "Provider name",
Expand All @@ -71,7 +71,6 @@ const FIELD_LABELS: Record<ProviderFormField, string> = {
apiKey: "API key",
models: "Models",
defaultModel: "Default model",
bifrostVirtualKey: "Bifrost virtual key",
};

const FIELD_HINTS: Record<ProviderFormField, string> = {
Expand All @@ -81,7 +80,6 @@ const FIELD_HINTS: Record<ProviderFormField, string> = {
apiKey: "sk-...",
models: "model-a, model-b",
defaultModel: "optional; must be in models",
bifrostVirtualKey: "yes = use x-bf-vk header (Bifrost)",
};

// Project provider catalog entries carry credentials. The modal receives the
Expand Down Expand Up @@ -153,7 +151,6 @@ function initialFormValues(provider: AgentProvider | undefined): ProviderFormVal
apiKey: "",
models: provider?.models.join(", ") ?? "",
defaultModel: provider?.defaultModel ?? provider?.models[0] ?? "",
bifrostVirtualKey: provider?.bifrostVirtualKey === true ? "yes" : "no",
};
}

Expand All @@ -172,7 +169,6 @@ export function validateProviderForm(
const baseURL = values.baseURL.trim();
const apiKey = values.apiKey.trim();
const keyless = values.keyless === "yes";
const bifrostVirtualKey = values.bifrostVirtualKey === "yes";
const models = parseModels(values.models);
const defaultModel = values.defaultModel.trim();

Expand All @@ -196,7 +192,6 @@ export function validateProviderForm(
...(apiKey.length > 0 ? { apiKey } : {}),
models,
...(defaultModel.length > 0 ? { defaultModel } : {}),
...(bifrostVirtualKey ? { bifrostVirtualKey: true } : {}),
},
};
}
Expand Down Expand Up @@ -768,13 +763,6 @@ export function AgentModal({
}
return;
}
if (currentField === "bifrostVirtualKey") {
if (key.leftArrow || key.rightArrow || input === " ") {
setFormValues((v) => ({ ...v, bifrostVirtualKey: v.bifrostVirtualKey === "yes" ? "no" : "yes" }));
setFormError(null);
}
return;
}
if (key.backspace || key.delete) {
setFormValues((values) => ({ ...values, [currentField]: values[currentField].slice(0, -1) }));
setFormError(null);
Expand Down Expand Up @@ -966,7 +954,6 @@ export function AgentModal({
const showCaret =
isCursor &&
field !== "keyless" &&
field !== "bifrostVirtualKey" &&
!(field === "apiKey" && isKeyless);
return (
<Box key={field} flexDirection="row" gap={1}>
Expand All @@ -975,7 +962,7 @@ export function AgentModal({
{FIELD_LABELS[field]}
</Text>
</Box>
{field === "keyless" || field === "bifrostVirtualKey" ? (
{field === "keyless" ? (
<Text color={value === "yes" ? color("accent") : color("muted")}>
{isCursor ? "< " : " "}
{value === "yes" ? "yes" : "no"}
Expand Down
Loading