mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-06 08:16:43 +00:00
differentiate solely by model_Id
This commit is contained in:
parent
82f6d0fe43
commit
c9e15005b1
2 changed files with 134 additions and 66 deletions
|
|
@ -92,56 +92,140 @@ describe("HealthCheckComponent", () => {
|
|||
expect(mockIndividualModelHealthCheckCall).not.toHaveBeenCalledWith("token-123", "gpt-4");
|
||||
});
|
||||
|
||||
it("should key health status by model id and show status from latest_health_checks by model_id", async () => {
|
||||
const modelData = {
|
||||
data: [
|
||||
{
|
||||
model_name: "gpt-4",
|
||||
model_info: { id: "id-alpha" },
|
||||
litellm_model_name: "gpt-4",
|
||||
},
|
||||
{
|
||||
model_name: "gpt-4",
|
||||
model_info: { id: "id-beta" },
|
||||
litellm_model_name: "gpt-4",
|
||||
},
|
||||
],
|
||||
};
|
||||
describe("latest_health_checks keyed by model id", () => {
|
||||
it("should show status from latest_health_checks when keys match model ids", async () => {
|
||||
const modelData = {
|
||||
data: [
|
||||
{
|
||||
model_name: "gpt-4",
|
||||
model_info: { id: "id-alpha" },
|
||||
litellm_model_name: "gpt-4",
|
||||
},
|
||||
{
|
||||
model_name: "gpt-4",
|
||||
model_info: { id: "id-beta" },
|
||||
litellm_model_name: "gpt-4",
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
mockLatestHealthChecksCall.mockResolvedValue({
|
||||
latest_health_checks: {
|
||||
"id-alpha": {
|
||||
status: "healthy",
|
||||
checked_at: "2024-01-15T10:00:00Z",
|
||||
error_message: null,
|
||||
mockLatestHealthChecksCall.mockResolvedValue({
|
||||
latest_health_checks: {
|
||||
"id-alpha": {
|
||||
status: "healthy",
|
||||
checked_at: "2024-01-15T10:00:00Z",
|
||||
error_message: null,
|
||||
},
|
||||
"id-beta": {
|
||||
status: "unhealthy",
|
||||
checked_at: "2024-01-15T10:05:00Z",
|
||||
error_message: "Connection failed",
|
||||
},
|
||||
},
|
||||
"id-beta": {
|
||||
status: "unhealthy",
|
||||
checked_at: "2024-01-15T10:05:00Z",
|
||||
error_message: "Connection failed",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
render(
|
||||
<HealthCheckComponent
|
||||
accessToken="token"
|
||||
modelData={modelData}
|
||||
all_models_on_proxy={["id-alpha", "id-beta"]}
|
||||
getDisplayModelName={getDisplayModelName}
|
||||
/>,
|
||||
);
|
||||
});
|
||||
await act(async () => {
|
||||
await new Promise((r) => setTimeout(r, 0));
|
||||
});
|
||||
|
||||
expect(mockLatestHealthChecksCall).toHaveBeenCalledWith("token");
|
||||
const healthyBadges = screen.getAllByText("healthy");
|
||||
const unhealthyBadges = screen.getAllByText("unhealthy");
|
||||
expect(healthyBadges.length).toBeGreaterThanOrEqual(1);
|
||||
expect(unhealthyBadges.length).toBeGreaterThanOrEqual(1);
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
render(
|
||||
<HealthCheckComponent
|
||||
accessToken="token"
|
||||
modelData={modelData}
|
||||
all_models_on_proxy={["id-alpha", "id-beta"]}
|
||||
getDisplayModelName={getDisplayModelName}
|
||||
/>,
|
||||
);
|
||||
it("should skip latest_health_checks entries whose key is not a known model id", async () => {
|
||||
const modelData = {
|
||||
data: [
|
||||
{
|
||||
model_name: "gpt-4",
|
||||
model_info: { id: "current-model-id" },
|
||||
litellm_model_name: "gpt-4",
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
mockLatestHealthChecksCall.mockResolvedValue({
|
||||
latest_health_checks: {
|
||||
"current-model-id": {
|
||||
status: "healthy",
|
||||
checked_at: "2024-01-15T10:00:00Z",
|
||||
error_message: null,
|
||||
},
|
||||
"deleted-or-unknown-id": {
|
||||
status: "unhealthy",
|
||||
checked_at: "2024-01-15T10:05:00Z",
|
||||
error_message: "Stale entry",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
render(
|
||||
<HealthCheckComponent
|
||||
accessToken="token"
|
||||
modelData={modelData}
|
||||
all_models_on_proxy={["current-model-id"]}
|
||||
getDisplayModelName={getDisplayModelName}
|
||||
/>,
|
||||
);
|
||||
});
|
||||
await act(async () => {
|
||||
await new Promise((r) => setTimeout(r, 0));
|
||||
});
|
||||
|
||||
expect(screen.getByText("healthy")).toBeInTheDocument();
|
||||
expect(screen.queryByText("unhealthy")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
await new Promise((r) => setTimeout(r, 0));
|
||||
});
|
||||
it("should not apply status when latest_health_checks key is model name not model id", async () => {
|
||||
const modelData = {
|
||||
data: [
|
||||
{
|
||||
model_name: "gpt-4",
|
||||
model_info: { id: "model-id-123" },
|
||||
litellm_model_name: "gpt-4",
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
expect(mockLatestHealthChecksCall).toHaveBeenCalledWith("token");
|
||||
const healthyBadges = screen.getAllByText("healthy");
|
||||
const unhealthyBadges = screen.getAllByText("unhealthy");
|
||||
expect(healthyBadges.length).toBeGreaterThanOrEqual(1);
|
||||
expect(unhealthyBadges.length).toBeGreaterThanOrEqual(1);
|
||||
mockLatestHealthChecksCall.mockResolvedValue({
|
||||
latest_health_checks: {
|
||||
"gpt-4": {
|
||||
status: "healthy",
|
||||
checked_at: "2024-01-15T10:00:00Z",
|
||||
error_message: null,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
render(
|
||||
<HealthCheckComponent
|
||||
accessToken="token"
|
||||
modelData={modelData}
|
||||
all_models_on_proxy={["model-id-123"]}
|
||||
getDisplayModelName={getDisplayModelName}
|
||||
/>,
|
||||
);
|
||||
});
|
||||
await act(async () => {
|
||||
await new Promise((r) => setTimeout(r, 0));
|
||||
});
|
||||
|
||||
expect(screen.queryByText("healthy")).not.toBeInTheDocument();
|
||||
expect(screen.getByText("none")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -85,31 +85,16 @@ const HealthCheckComponent: React.FC<HealthCheckComponentProps> = ({
|
|||
latestHealthChecks.latest_health_checks &&
|
||||
typeof latestHealthChecks.latest_health_checks === "object"
|
||||
) {
|
||||
Object.entries(latestHealthChecks.latest_health_checks).forEach(([key, checkData]: [string, any]) => {
|
||||
Object.entries(latestHealthChecks.latest_health_checks).forEach(([modelId, checkData]: [string, any]) => {
|
||||
if (!checkData) return;
|
||||
|
||||
let targetModelId: string | null = null;
|
||||
// Key is model_id from the backend (guaranteed by DB schema)
|
||||
const modelExists = modelData.data.some((m: any) => m.model_info?.id === modelId);
|
||||
if (!modelExists) return;
|
||||
|
||||
// The key is model_id from the backend; fallback to matching by model_name for legacy data
|
||||
const modelByIdMatch = modelData.data.find((m: any) => m.model_info && m.model_info.id === key);
|
||||
if (modelByIdMatch) {
|
||||
targetModelId = modelByIdMatch.model_info.id;
|
||||
} else {
|
||||
const directModelMatch = modelData.data.find((m: any) => m.model_name === key);
|
||||
if (directModelMatch?.model_info?.id) {
|
||||
targetModelId = directModelMatch.model_info.id;
|
||||
} else if (checkData.model_name) {
|
||||
const modelByNameInData = modelData.data.find((m: any) => m.model_name === checkData.model_name);
|
||||
if (modelByNameInData?.model_info?.id) {
|
||||
targetModelId = modelByNameInData.model_info.id;
|
||||
}
|
||||
}
|
||||
}
|
||||
const fullError = checkData.error_message || undefined;
|
||||
|
||||
if (targetModelId) {
|
||||
const fullError = checkData.error_message || undefined;
|
||||
|
||||
healthStatusMap[targetModelId] = {
|
||||
healthStatusMap[modelId] = {
|
||||
status: checkData.status || "unknown",
|
||||
lastCheck: checkData.checked_at ? new Date(checkData.checked_at).toLocaleString() : "None",
|
||||
lastSuccess:
|
||||
|
|
@ -123,7 +108,6 @@ const HealthCheckComponent: React.FC<HealthCheckComponentProps> = ({
|
|||
fullError: fullError,
|
||||
successResponse: checkData.status === "healthy" ? checkData : undefined,
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (healthError) {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue