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:
yassin 2026-09-13 09:21:09 +00:00
parent c2c2a623c0
commit 330ba7cbf9
3 changed files with 96 additions and 3 deletions

View file

@ -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>

View file

@ -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 });

View file

@ -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>