mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-15 23:31:29 +00:00
fix(ui): show the team alias on the model info page and in its raw JSON
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
parent
c2c2a623c0
commit
330ba7cbf9
3 changed files with 96 additions and 3 deletions
|
|
@ -271,6 +271,7 @@ const displayCost = (localModelData: any, field: TouchedPricingField): string =>
|
|||
interface ModelInfoEditFormProps {
|
||||
localModelData: any;
|
||||
modelData: { model_info: { team_id?: string | null } & Record<string, unknown> };
|
||||
teamAlias: string | null;
|
||||
accessToken: string | null;
|
||||
isEditing: boolean;
|
||||
isSaving: boolean;
|
||||
|
|
@ -341,6 +342,7 @@ const ChipList: React.FC<{ values: unknown; emptyLabel: string }> = ({ values, e
|
|||
const ModelInfoEditForm: React.FC<ModelInfoEditFormProps> = ({
|
||||
localModelData,
|
||||
modelData,
|
||||
teamAlias,
|
||||
accessToken,
|
||||
isEditing,
|
||||
isSaving,
|
||||
|
|
@ -799,8 +801,12 @@ const ModelInfoEditForm: React.FC<ModelInfoEditFormProps> = ({
|
|||
</div>
|
||||
|
||||
<div>
|
||||
<FieldLabel>Team ID</FieldLabel>
|
||||
<Display>{modelData.model_info.team_id || "Not Set"}</Display>
|
||||
<FieldLabel>Team</FieldLabel>
|
||||
<Display>
|
||||
{teamAlias
|
||||
? `${teamAlias} (${modelData.model_info.team_id})`
|
||||
: modelData.model_info.team_id || "Not Set"}
|
||||
</Display>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -42,6 +42,11 @@ vi.mock("@/app/(dashboard)/hooks/models/useModelCostMap", () => ({
|
|||
useModelCostMap: (...args: any[]) => mockUseModelCostMap(...args),
|
||||
}));
|
||||
|
||||
const mockUseTeams = vi.fn();
|
||||
vi.mock("@/app/(dashboard)/hooks/teams/useTeams", () => ({
|
||||
useTeams: () => mockUseTeams(),
|
||||
}));
|
||||
|
||||
const mockUsePtuCostAttributionEnabled = vi.fn();
|
||||
vi.mock("@/app/(dashboard)/hooks/uiSettings/usePtuCostAttributionEnabled", () => ({
|
||||
usePtuCostAttributionEnabled: () => mockUsePtuCostAttributionEnabled(),
|
||||
|
|
@ -102,6 +107,7 @@ describe("ModelInfoView", () => {
|
|||
});
|
||||
vi.clearAllMocks();
|
||||
mockUsePtuCostAttributionEnabled.mockReturnValue(false);
|
||||
mockUseTeams.mockReturnValue({ data: undefined, isLoading: false, error: null });
|
||||
|
||||
mockUseModelsInfo.mockReturnValue({
|
||||
data: {
|
||||
|
|
@ -1305,6 +1311,78 @@ describe("ModelInfoView", () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe("team alias", () => {
|
||||
const teamModel = {
|
||||
...defaultModelData,
|
||||
model_info: { ...defaultModelData.model_info, team_id: "team-1" },
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
mockUseModelsInfo.mockReturnValue({ data: { data: [teamModel] }, isLoading: false, error: null });
|
||||
mockModelInfoV1Call.mockResolvedValue({ data: [teamModel] });
|
||||
});
|
||||
|
||||
const readRawJson = async (user: ReturnType<typeof userEvent.setup>) => {
|
||||
await user.click(await screen.findByRole("tab", { name: /raw json/i }));
|
||||
const pre = await screen.findByText(/"model_name": "GPT-4"/, { selector: "pre" });
|
||||
return JSON.parse(pre.textContent ?? "");
|
||||
};
|
||||
|
||||
it("shows the team alias next to the team id and adds team_alias to the raw JSON", async () => {
|
||||
mockUseTeams.mockReturnValue({
|
||||
data: [
|
||||
{ team_id: "team-0", team_alias: "other" },
|
||||
{ team_id: "team-1", team_alias: "alpha" },
|
||||
],
|
||||
isLoading: false,
|
||||
error: null,
|
||||
});
|
||||
const user = userEvent.setup();
|
||||
render(<ModelInfoView {...DEFAULT_ADMIN_PROPS} />, { wrapper });
|
||||
|
||||
expect(await screen.findByText("alpha (team-1)")).toBeInTheDocument();
|
||||
|
||||
const raw = await readRawJson(user);
|
||||
expect(raw.model_info).toMatchObject({ team_id: "team-1", team_alias: "alpha" });
|
||||
const keys = Object.keys(raw.model_info);
|
||||
expect(keys.indexOf("team_alias")).toBe(keys.indexOf("team_id") + 1);
|
||||
});
|
||||
|
||||
it("falls back to the bare team id when the team is not in the caller's team list", async () => {
|
||||
mockUseTeams.mockReturnValue({
|
||||
data: [{ team_id: "team-0", team_alias: "other" }],
|
||||
isLoading: false,
|
||||
error: null,
|
||||
});
|
||||
const user = userEvent.setup();
|
||||
render(<ModelInfoView {...DEFAULT_ADMIN_PROPS} />, { wrapper });
|
||||
|
||||
expect(await screen.findByText("team-1")).toBeInTheDocument();
|
||||
|
||||
const raw = await readRawJson(user);
|
||||
expect(raw.model_info.team_id).toBe("team-1");
|
||||
expect(raw.model_info).not.toHaveProperty("team_alias");
|
||||
});
|
||||
|
||||
it("shows Not Set and no team_alias for a model without a team", async () => {
|
||||
mockUseModelsInfo.mockReturnValue({ data: { data: [defaultModelData] }, isLoading: false, error: null });
|
||||
mockModelInfoV1Call.mockResolvedValue({ data: [defaultModelData] });
|
||||
mockUseTeams.mockReturnValue({
|
||||
data: [{ team_id: "team-1", team_alias: "alpha" }],
|
||||
isLoading: false,
|
||||
error: null,
|
||||
});
|
||||
const user = userEvent.setup();
|
||||
render(<ModelInfoView {...DEFAULT_ADMIN_PROPS} />, { wrapper });
|
||||
|
||||
expect(await screen.findByText("Team")).toBeInTheDocument();
|
||||
expect(screen.queryByText(/alpha/)).not.toBeInTheDocument();
|
||||
|
||||
const raw = await readRawJson(user);
|
||||
expect(raw.model_info).not.toHaveProperty("team_alias");
|
||||
});
|
||||
});
|
||||
|
||||
it("renders the provider card logo from the bundled provider map", async () => {
|
||||
render(<ModelInfoView {...DEFAULT_ADMIN_PROPS} />, { wrapper });
|
||||
|
||||
|
|
|
|||
|
|
@ -169,6 +169,12 @@ export default function ModelInfoView({
|
|||
// Keep modelData variable name for backwards compatibility
|
||||
const modelData = transformedModelData;
|
||||
|
||||
const teamAlias = teams?.find((team) => team.team_id === modelData?.model_info?.team_id)?.team_alias || null;
|
||||
const rawModelInfoEntries = Object.entries(modelData?.model_info ?? {}).flatMap((entry) =>
|
||||
entry[0] === "team_id" && teamAlias ? [entry, ["team_alias", teamAlias]] : [entry],
|
||||
);
|
||||
const rawModelData = modelData && { ...modelData, model_info: Object.fromEntries(rawModelInfoEntries) };
|
||||
|
||||
const canEditModel = canModifyModel({ userRole, userID, isViewOnly }, teams ?? null, {
|
||||
teamId: modelData?.model_info?.team_id,
|
||||
isDbModel: modelData?.model_info?.db_model === true,
|
||||
|
|
@ -765,6 +771,7 @@ export default function ModelInfoView({
|
|||
<ModelInfoEditForm
|
||||
localModelData={localModelData}
|
||||
modelData={modelData}
|
||||
teamAlias={teamAlias}
|
||||
accessToken={accessToken}
|
||||
isEditing={isEditing}
|
||||
isSaving={isSaving}
|
||||
|
|
@ -788,7 +795,9 @@ export default function ModelInfoView({
|
|||
|
||||
<TabsContent value="raw" keepMounted>
|
||||
<Card className="block p-6">
|
||||
<pre className="bg-muted p-4 rounded-sm text-xs overflow-auto">{JSON.stringify(modelData, null, 2)}</pre>
|
||||
<pre className="bg-muted p-4 rounded-sm text-xs overflow-auto">
|
||||
{JSON.stringify(rawModelData, null, 2)}
|
||||
</pre>
|
||||
</Card>
|
||||
</TabsContent>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue