mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-04 19:21:51 +00:00
fix(ui): show Lighthouse provider and model names
This commit is contained in:
@@ -49,14 +49,15 @@ describe("lighthouse-v2.adapter", () => {
|
||||
it("should map supported provider and model payloads", () => {
|
||||
// Given
|
||||
const provider = {
|
||||
id: "openai",
|
||||
id: "openai_compatible",
|
||||
type: "lighthouse-supported-providers",
|
||||
attributes: { name: "OpenAI" },
|
||||
attributes: { name: "OpenAI Compatible" },
|
||||
};
|
||||
const model = {
|
||||
id: "gpt-5.5",
|
||||
type: "lighthouse-supported-models",
|
||||
attributes: {
|
||||
model_name: "GPT 5.5",
|
||||
max_input_tokens: 100000,
|
||||
max_output_tokens: 8192,
|
||||
supports_function_calling: true,
|
||||
@@ -67,11 +68,12 @@ describe("lighthouse-v2.adapter", () => {
|
||||
|
||||
// When / Then
|
||||
expect(mapLighthouseV2Provider(provider)).toEqual({
|
||||
id: "openai",
|
||||
name: "OpenAI",
|
||||
id: "openai-compatible",
|
||||
name: "OpenAI Compatible",
|
||||
});
|
||||
expect(mapLighthouseV2Model(model)).toEqual({
|
||||
id: "gpt-5.5",
|
||||
name: "GPT 5.5",
|
||||
maxInputTokens: 100000,
|
||||
maxOutputTokens: 8192,
|
||||
supportsFunctionCalling: true,
|
||||
|
||||
@@ -30,7 +30,7 @@ export interface JsonApiDocument<TData> {
|
||||
}
|
||||
|
||||
interface ConfigurationAttributes {
|
||||
provider_type: LighthouseV2ProviderType;
|
||||
provider_type: string;
|
||||
base_url: string | null;
|
||||
default_model?: string | null;
|
||||
business_context?: string | null;
|
||||
@@ -45,6 +45,8 @@ interface SupportedProviderAttributes {
|
||||
}
|
||||
|
||||
interface SupportedModelAttributes {
|
||||
model_name?: string | null;
|
||||
name?: string | null;
|
||||
max_input_tokens: number | null;
|
||||
max_output_tokens: number | null;
|
||||
supports_function_calling: boolean | null;
|
||||
@@ -112,7 +114,9 @@ export function mapLighthouseV2Configuration(
|
||||
): LighthouseV2Configuration {
|
||||
return {
|
||||
id: resource.id,
|
||||
providerType: resource.attributes.provider_type,
|
||||
providerType: normalizeLighthouseV2ProviderType(
|
||||
resource.attributes.provider_type,
|
||||
),
|
||||
baseUrl: resource.attributes.base_url,
|
||||
defaultModel: resource.attributes.default_model ?? null,
|
||||
businessContext: resource.attributes.business_context ?? "",
|
||||
@@ -127,7 +131,7 @@ export function mapLighthouseV2Provider(
|
||||
resource: JsonApiResource<SupportedProviderAttributes>,
|
||||
): LighthouseV2SupportedProvider {
|
||||
return {
|
||||
id: resource.id as LighthouseV2ProviderType,
|
||||
id: normalizeLighthouseV2ProviderType(resource.id),
|
||||
name: resource.attributes.name,
|
||||
};
|
||||
}
|
||||
@@ -137,6 +141,8 @@ export function mapLighthouseV2Model(
|
||||
): LighthouseV2SupportedModel {
|
||||
return {
|
||||
id: resource.id,
|
||||
name:
|
||||
resource.attributes.model_name ?? resource.attributes.name ?? resource.id,
|
||||
maxInputTokens: resource.attributes.max_input_tokens,
|
||||
maxOutputTokens: resource.attributes.max_output_tokens,
|
||||
supportsFunctionCalling: resource.attributes.supports_function_calling,
|
||||
@@ -344,3 +350,13 @@ function hasBedrockRegion(credentials: LighthouseV2Credentials): boolean {
|
||||
"aws_region_name" in credentials && Boolean(credentials.aws_region_name)
|
||||
);
|
||||
}
|
||||
|
||||
function normalizeLighthouseV2ProviderType(
|
||||
providerType: string,
|
||||
): LighthouseV2ProviderType {
|
||||
if (providerType === "openai_compatible") {
|
||||
return LIGHTHOUSE_V2_PROVIDER_TYPE.OPENAI_COMPATIBLE;
|
||||
}
|
||||
|
||||
return providerType as LighthouseV2ProviderType;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import type {
|
||||
LighthouseV2Configuration,
|
||||
LighthouseV2Message,
|
||||
LighthouseV2SupportedModel,
|
||||
LighthouseV2SupportedProvider,
|
||||
} from "@/app/(prowler)/lighthouse/_types";
|
||||
|
||||
import { LighthouseV2ChatPage } from "./lighthouse-v2-chat-page";
|
||||
@@ -117,6 +118,12 @@ const modelsByProvider = {
|
||||
"openai-compatible": [model("llama-3.3")],
|
||||
};
|
||||
|
||||
const supportedProviders: LighthouseV2SupportedProvider[] = [
|
||||
{ id: "openai", name: "OpenAI" },
|
||||
{ id: "bedrock", name: "AWS Bedrock" },
|
||||
{ id: "openai-compatible", name: "OpenAI Compatible" },
|
||||
];
|
||||
|
||||
describe("LighthouseV2ChatPage", () => {
|
||||
beforeEach(() => {
|
||||
vi.stubGlobal(
|
||||
@@ -180,6 +187,84 @@ describe("LighthouseV2ChatPage", () => {
|
||||
).toHaveAttribute("href", "/lighthouse/settings");
|
||||
});
|
||||
|
||||
it("shows model names in the selector while keeping model ids for persistence", async () => {
|
||||
// Given
|
||||
const user = userEvent.setup();
|
||||
renderPage({
|
||||
configurations: [
|
||||
{ ...configurations[0], defaultModel: "gpt-5.1" },
|
||||
{
|
||||
...configurations[1],
|
||||
defaultModel: "us.anthropic.claude-sonnet-4-20250514-v1:0",
|
||||
},
|
||||
],
|
||||
modelsByProvider: {
|
||||
openai: [model("gpt-5.1", "GPT-5.1")],
|
||||
bedrock: [
|
||||
model(
|
||||
"us.anthropic.claude-sonnet-4-20250514-v1:0",
|
||||
"Claude Sonnet 4",
|
||||
),
|
||||
],
|
||||
"openai-compatible": [],
|
||||
},
|
||||
});
|
||||
|
||||
// When
|
||||
const modelSelector = screen.getByRole("combobox", { name: "Model" });
|
||||
await user.click(modelSelector);
|
||||
|
||||
// Then
|
||||
expect(modelSelector).toHaveTextContent("GPT-5.1");
|
||||
expect(
|
||||
await screen.findByRole("option", { name: "Claude Sonnet 4" }),
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByText("us.anthropic.claude-sonnet-4-20250514-v1:0"),
|
||||
).not.toBeInTheDocument();
|
||||
|
||||
// When
|
||||
await user.click(screen.getByRole("option", { name: "Claude Sonnet 4" }));
|
||||
|
||||
// Then
|
||||
await waitFor(() =>
|
||||
expect(updateConfigurationMock).toHaveBeenCalledWith("config-bedrock", {
|
||||
defaultModel: "us.anthropic.claude-sonnet-4-20250514-v1:0",
|
||||
}),
|
||||
);
|
||||
expect(modelSelector).toHaveTextContent("Claude Sonnet 4");
|
||||
});
|
||||
|
||||
it("uses supported provider names as model selector section headings", async () => {
|
||||
// Given
|
||||
const user = userEvent.setup();
|
||||
renderPage({
|
||||
configurations: [
|
||||
...configurations,
|
||||
{
|
||||
id: "config-openai-compatible",
|
||||
providerType: "openai-compatible",
|
||||
baseUrl: "https://example.com/v1",
|
||||
defaultModel: "llama-3.3",
|
||||
businessContext: "Production account",
|
||||
connected: true,
|
||||
connectionLastCheckedAt: "2026-06-22T10:00:00Z",
|
||||
insertedAt: "2026-06-22T09:00:00Z",
|
||||
updatedAt: "2026-06-22T10:00:00Z",
|
||||
},
|
||||
],
|
||||
supportedProviders,
|
||||
});
|
||||
|
||||
// When
|
||||
await user.click(screen.getByRole("combobox", { name: "Model" }));
|
||||
|
||||
// Then
|
||||
expect(await screen.findByText("AWS Bedrock")).toBeInTheDocument();
|
||||
expect(screen.getByText("OpenAI Compatible")).toBeInTheDocument();
|
||||
expect(screen.queryByText("Amazon Bedrock")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("uses the tuned scrollbar and bottom fade without a composer separator", () => {
|
||||
// Given / When
|
||||
const { container } = renderPage({
|
||||
@@ -476,6 +561,7 @@ function renderPage(props?: RenderPageProps) {
|
||||
const componentProps = {
|
||||
configurations: props?.configurations ?? configurations,
|
||||
modelsByProvider: props?.modelsByProvider ?? modelsByProvider,
|
||||
supportedProviders: props?.supportedProviders ?? supportedProviders,
|
||||
initialSessionId: props?.initialSessionId,
|
||||
initialMessages: props?.initialMessages ?? [],
|
||||
initialPrompt: props?.initialPrompt,
|
||||
@@ -486,9 +572,10 @@ function renderPage(props?: RenderPageProps) {
|
||||
return render(<LighthouseV2ChatPage {...componentProps} />);
|
||||
}
|
||||
|
||||
function model(id: string): LighthouseV2SupportedModel {
|
||||
function model(id: string, name = id): LighthouseV2SupportedModel {
|
||||
return {
|
||||
id,
|
||||
name,
|
||||
maxInputTokens: null,
|
||||
maxOutputTokens: null,
|
||||
supportsFunctionCalling: null,
|
||||
|
||||
@@ -42,6 +42,7 @@ import {
|
||||
type LighthouseV2ProviderType,
|
||||
type LighthouseV2SSEEvent,
|
||||
type LighthouseV2SupportedModel,
|
||||
type LighthouseV2SupportedProvider,
|
||||
} from "@/app/(prowler)/lighthouse/_types";
|
||||
import { Card } from "@/components/shadcn";
|
||||
import {
|
||||
@@ -61,6 +62,7 @@ interface LighthouseV2ChatPageProps {
|
||||
LighthouseV2ProviderType,
|
||||
LighthouseV2SupportedModel[]
|
||||
>;
|
||||
supportedProviders: LighthouseV2SupportedProvider[];
|
||||
initialSessionId?: string;
|
||||
initialMessages: LighthouseV2Message[];
|
||||
initialActiveTaskId?: string | null;
|
||||
@@ -71,6 +73,7 @@ interface LighthouseV2ChatPageProps {
|
||||
export function LighthouseV2ChatPage({
|
||||
configurations,
|
||||
modelsByProvider,
|
||||
supportedProviders,
|
||||
initialSessionId,
|
||||
initialMessages,
|
||||
initialActiveTaskId,
|
||||
@@ -112,6 +115,7 @@ export function LighthouseV2ChatPage({
|
||||
const modelSelectorGroups = buildModelSelectorGroups(
|
||||
connectedConfigurations,
|
||||
modelsByProvider,
|
||||
supportedProviders,
|
||||
);
|
||||
const selectedModelValue = selectedModelSelection
|
||||
? buildLighthouseV2ModelSelectionValue(
|
||||
@@ -526,29 +530,33 @@ function buildModelSelectorGroups(
|
||||
LighthouseV2ProviderType,
|
||||
LighthouseV2SupportedModel[]
|
||||
>,
|
||||
supportedProviders: LighthouseV2SupportedProvider[],
|
||||
): ComboboxGroup[] {
|
||||
return connectedConfigurations
|
||||
.map((configuration) => ({
|
||||
heading: getLighthouseV2ProviderLabel(configuration.providerType),
|
||||
options: (modelsByProvider[configuration.providerType] ?? []).map(
|
||||
(model) => ({
|
||||
value: buildLighthouseV2ModelSelectionValue(
|
||||
configuration.providerType,
|
||||
model.id,
|
||||
),
|
||||
label: model.id,
|
||||
}),
|
||||
),
|
||||
}))
|
||||
.filter((group) => group.options.length > 0);
|
||||
}
|
||||
const groups: ComboboxGroup[] = [];
|
||||
|
||||
function getLighthouseV2ProviderLabel(providerType: LighthouseV2ProviderType) {
|
||||
return LIGHTHOUSE_V2_PROVIDER_LABELS[providerType] ?? providerType;
|
||||
}
|
||||
for (const provider of supportedProviders) {
|
||||
const configuration = connectedConfigurations.find(
|
||||
(item) => item.providerType === provider.id,
|
||||
);
|
||||
if (!configuration) continue;
|
||||
|
||||
const LIGHTHOUSE_V2_PROVIDER_LABELS = {
|
||||
openai: "OpenAI",
|
||||
bedrock: "Amazon Bedrock",
|
||||
"openai-compatible": "OpenAI Compatible",
|
||||
} as const satisfies Record<LighthouseV2ProviderType, string>;
|
||||
const options = (modelsByProvider[configuration.providerType] ?? []).map(
|
||||
(model) => ({
|
||||
value: buildLighthouseV2ModelSelectionValue(
|
||||
configuration.providerType,
|
||||
model.id,
|
||||
),
|
||||
label: model.name,
|
||||
}),
|
||||
);
|
||||
|
||||
if (options.length === 0) continue;
|
||||
|
||||
groups.push({
|
||||
heading: provider.name,
|
||||
options,
|
||||
});
|
||||
}
|
||||
|
||||
return groups;
|
||||
}
|
||||
|
||||
@@ -64,6 +64,7 @@ export interface LighthouseV2SupportedProvider {
|
||||
|
||||
export interface LighthouseV2SupportedModel {
|
||||
id: string;
|
||||
name: string;
|
||||
maxInputTokens: number | null;
|
||||
maxOutputTokens: number | null;
|
||||
supportsFunctionCalling: boolean | null;
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
getLighthouseV2Messages,
|
||||
getLighthouseV2Session,
|
||||
getLighthouseV2SupportedModels,
|
||||
getLighthouseV2SupportedProviders,
|
||||
} from "@/app/(prowler)/lighthouse/_actions";
|
||||
import { LighthouseV2ChatPage } from "@/app/(prowler)/lighthouse/_components/chat";
|
||||
import { LighthouseV2NavigationModeSync } from "@/app/(prowler)/lighthouse/_components/navigation";
|
||||
@@ -36,9 +37,14 @@ export default async function AIChatbot({
|
||||
typeof params.session === "string" ? params.session : undefined;
|
||||
|
||||
if (isCloud()) {
|
||||
const configurationsResult = await getLighthouseV2Configurations();
|
||||
const [configurationsResult, supportedProvidersResult] = await Promise.all([
|
||||
getLighthouseV2Configurations(),
|
||||
getLighthouseV2SupportedProviders(),
|
||||
]);
|
||||
const configurations =
|
||||
"data" in configurationsResult ? configurationsResult.data : [];
|
||||
const supportedProviders =
|
||||
"data" in supportedProvidersResult ? supportedProvidersResult.data : [];
|
||||
const connectedConfigurations = configurations.filter(
|
||||
(configuration) => configuration.connected === true,
|
||||
);
|
||||
@@ -89,6 +95,7 @@ export default async function AIChatbot({
|
||||
key={chatRouteKey}
|
||||
configurations={configurations}
|
||||
modelsByProvider={modelsByProvider}
|
||||
supportedProviders={supportedProviders}
|
||||
initialSessionId={activeSessionId}
|
||||
initialMessages={
|
||||
"data" in initialMessages ? initialMessages.data : []
|
||||
|
||||
Reference in New Issue
Block a user