Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 2206799

Browse files
committed
fix: align disabled chat model tests and settings gating with collection contract
1 parent bd9b0bc commit 2206799

7 files changed

Lines changed: 107 additions & 46 deletions

File tree

coderd/exp_chats_test.go

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2772,7 +2772,7 @@ func TestListChatModels(t *testing.T) {
27722772
require.False(t, containsModel(response, other.ID))
27732773
})
27742774

2775-
t.Run("DisabledProvidersAndModelsAreFilteredOut", func(t *testing.T) {
2775+
t.Run("DisabledProvidersAndModelsRemainVisible", func(t *testing.T) {
27762776
t.Parallel()
27772777
ctx := testutil.Context(t, testutil.WaitLong)
27782778
client := newChatClient(t)
@@ -2788,12 +2788,18 @@ func TestListChatModels(t *testing.T) {
27882788
response, err := client.ChatModels(ctx, firstUser.OrganizationID)
27892789
require.NoError(t, err)
27902790
require.True(t, containsModel(response, enabledModel.ID))
2791-
require.False(t, containsModel(response, disabledModel.ID))
2791+
require.True(t, slices.ContainsFunc(response.Models, func(model codersdk.ChatModel) bool {
2792+
return model.ID == disabledModel.ID && !model.Enabled
2793+
}))
27922794
_, err = client.UpdateAIProvider(ctx, provider.ID.String(), codersdk.UpdateAIProviderRequest{Enabled: ptr.Ref(false)})
27932795
require.NoError(t, err)
27942796
response, err = client.ChatModels(ctx, firstUser.OrganizationID)
27952797
require.NoError(t, err)
2796-
require.Empty(t, response.Models)
2798+
require.True(t, containsModel(response, enabledModel.ID))
2799+
require.True(t, containsModel(response, disabledModel.ID))
2800+
disabledProvider := providerByID(t, response, provider.ID)
2801+
require.False(t, disabledProvider.Enabled)
2802+
require.False(t, disabledProvider.Available)
27972803
})
27982804
}
27992805

@@ -4638,7 +4644,7 @@ func TestListChatModelConfigs(t *testing.T) {
46384644
require.NoError(t, client.DeleteChatModel(ctx, created.OrganizationID, created.ID))
46394645
})
46404646

4641-
t.Run("CollectionFiltersDisabledModelConfigs", func(t *testing.T) {
4647+
t.Run("CollectionIncludesDisabledModelConfigs", func(t *testing.T) {
46424648
t.Parallel()
46434649

46444650
ctx := testutil.Context(t, testutil.WaitLong)
@@ -4661,8 +4667,8 @@ func TestListChatModelConfigs(t *testing.T) {
46614667

46624668
configs, err := client.ChatModels(ctx, firstUser.OrganizationID)
46634669
require.NoError(t, err)
4664-
require.False(t, slices.ContainsFunc(configs.Models, func(config codersdk.ChatModel) bool {
4665-
return config.ID == disabledConfig.ID
4670+
require.True(t, slices.ContainsFunc(configs.Models, func(config codersdk.ChatModel) bool {
4671+
return config.ID == disabledConfig.ID && !config.Enabled
46664672
}))
46674673
})
46684674

@@ -5685,17 +5691,15 @@ func TestUpdateChatModel(t *testing.T) {
56855691
adminConfigs, err := adminClient.ChatModels(ctx, firstUser.OrganizationID)
56865692
require.NoError(t, err)
56875693

5688-
require.False(t, slices.ContainsFunc(adminConfigs.Models, func(config codersdk.ChatModel) bool {
5689-
return config.ID == modelConfig.ID
5694+
require.True(t, slices.ContainsFunc(adminConfigs.Models, func(config codersdk.ChatModel) bool {
5695+
return config.ID == modelConfig.ID && !config.Enabled
56905696
}))
56915697

56925698
collection, err := memberClient.ChatModels(ctx, firstUser.OrganizationID)
56935699
require.NoError(t, err)
5694-
runtimeIDs := make([]uuid.UUID, 0, len(collection.Models))
5695-
for _, config := range collection.Models {
5696-
runtimeIDs = append(runtimeIDs, config.ID)
5697-
}
5698-
require.NotContains(t, runtimeIDs, modelConfig.ID)
5700+
require.True(t, slices.ContainsFunc(collection.Models, func(config codersdk.ChatModel) bool {
5701+
return config.ID == modelConfig.ID && !config.Enabled
5702+
}))
56995703

57005704
_, err = memberClient.CreateChat(ctx, codersdk.CreateChatRequest{
57015705
OrganizationID: firstUser.OrganizationID,
@@ -5709,7 +5713,7 @@ func TestUpdateChatModel(t *testing.T) {
57095713
require.Equal(t, "Invalid model_config_id: model config not found or disabled.", sdkErr.Message)
57105714
})
57115715

5712-
t.Run("ReEnableUpdatesRuntimeAvailability", func(t *testing.T) {
5716+
t.Run("ReEnableUpdatesCollectionDescriptor", func(t *testing.T) {
57135717
t.Parallel()
57145718

57155719
ctx := testutil.Context(t, testutil.WaitLong)
@@ -5734,11 +5738,9 @@ func TestUpdateChatModel(t *testing.T) {
57345738

57355739
collection, err := memberClient.ChatModels(ctx, firstUser.OrganizationID)
57365740
require.NoError(t, err)
5737-
runtimeIDs := make([]uuid.UUID, 0, len(collection.Models))
5738-
for _, config := range collection.Models {
5739-
runtimeIDs = append(runtimeIDs, config.ID)
5740-
}
5741-
require.NotContains(t, runtimeIDs, modelConfig.ID)
5741+
require.True(t, slices.ContainsFunc(collection.Models, func(config codersdk.ChatModel) bool {
5742+
return config.ID == modelConfig.ID && !config.Enabled
5743+
}))
57425744

57435745
enabled = true
57445746
updated, err := adminClient.UpdateChatModel(ctx, modelConfig.OrganizationID, modelConfig.ID, codersdk.UpdateChatModelRequest{
@@ -5750,11 +5752,9 @@ func TestUpdateChatModel(t *testing.T) {
57505752

57515753
collection, err = memberClient.ChatModels(ctx, firstUser.OrganizationID)
57525754
require.NoError(t, err)
5753-
runtimeIDs = runtimeIDs[:0]
5754-
for _, config := range collection.Models {
5755-
runtimeIDs = append(runtimeIDs, config.ID)
5756-
}
5757-
require.Contains(t, runtimeIDs, modelConfig.ID)
5755+
require.True(t, slices.ContainsFunc(collection.Models, func(config codersdk.ChatModel) bool {
5756+
return config.ID == modelConfig.ID && config.Enabled
5757+
}))
57585758
})
57595759

57605760
t.Run("UpdateAIProviderID", func(t *testing.T) {

site/src/pages/AgentsPage/AgentSettingsUserAgentsPage.tsx

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,17 @@ const AgentSettingsUserAgentsPage: FC = () => {
3030
);
3131

3232
const defaultOrgModelConfigs = modelsQuery.data?.models ?? [];
33-
const hasDefaultOrgModels = defaultOrgModelConfigs.length > 0;
3433

3534
const { options: modelOptions, isModelCatalogLoading } = resolveModelSelector(
3635
defaultOrganizationId,
3736
modelsQuery,
3837
);
38+
const hasNoAvailableDefaultOrgModels =
39+
defaultOrganizationId !== "" &&
40+
!modelsQuery.isLoading &&
41+
modelsQuery.error === null &&
42+
modelsQuery.data !== undefined &&
43+
modelOptions.length === 0;
3944

4045
const saveModelOverride = (
4146
context: TypesGen.ChatPersonalModelOverrideContext,
@@ -63,13 +68,7 @@ const AgentSettingsUserAgentsPage: FC = () => {
6368
modelsError={modelsQuery.error}
6469
isLoadingModels={isModelCatalogLoading}
6570
isDefaultOrganizationUnresolved={defaultOrganizationId === ""}
66-
hasNoDefaultOrgModels={
67-
defaultOrganizationId !== "" &&
68-
!modelsQuery.isLoading &&
69-
modelsQuery.error === null &&
70-
modelsQuery.data !== undefined &&
71-
!hasDefaultOrgModels
72-
}
71+
hasNoAvailableDefaultOrgModels={hasNoAvailableDefaultOrgModels}
7372
onSaveRootModelOverride={saveModelOverride(
7473
"root",
7574
saveRootModelOverrideMutation,

site/src/pages/AgentsPage/AgentSettingsUserAgentsPageView.stories.tsx

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ const buildArgs = (
161161
modelsError: undefined,
162162
isLoadingModels: false,
163163
isDefaultOrganizationUnresolved: false,
164-
hasNoDefaultOrgModels: false,
164+
hasNoAvailableDefaultOrgModels: false,
165165
onSaveRootModelOverride: fn(),
166166
isSavingRootModelOverride: false,
167167
isSaveRootModelOverrideError: false,
@@ -700,21 +700,34 @@ export const SaveErrorState: Story = {
700700
},
701701
};
702702

703-
export const NoDefaultOrgModels: Story = {
703+
export const NoAvailableDefaultOrgModels: Story = {
704704
args: buildArgs({
705-
hasNoDefaultOrgModels: true,
705+
hasNoAvailableDefaultOrgModels: true,
706706
modelOptions: [],
707-
models: [],
707+
models: [disabledModelConfig],
708708
}),
709709
play: async ({ canvasElement }) => {
710710
const canvas = within(canvasElement);
711711
expect(
712712
canvas.getByText(/default organization has no available chat models/i),
713713
).toBeInTheDocument();
714714
const rootSection = await getSection(canvasElement, "Root agent model");
715-
expect(
716-
within(rootSection).getByRole("button", { name: "Save" }),
717-
).toBeDisabled();
715+
const generalSection = await getSection(
716+
canvasElement,
717+
"General subagent model",
718+
);
719+
const exploreSection = await getSection(
720+
canvasElement,
721+
"Explore subagent model",
722+
);
723+
for (const section of [rootSection, generalSection, exploreSection]) {
724+
expect(
725+
within(section).getByRole("combobox", { name: /behavior/i }),
726+
).toBeDisabled();
727+
expect(
728+
within(section).getByRole("button", { name: "Save" }),
729+
).toBeDisabled();
730+
}
718731
},
719732
};
720733

site/src/pages/AgentsPage/AgentSettingsUserAgentsPageView.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export interface AgentSettingsUserAgentsPageViewProps {
2121
modelsError: unknown;
2222
isLoadingModels: boolean;
2323
isDefaultOrganizationUnresolved: boolean;
24-
hasNoDefaultOrgModels: boolean;
24+
hasNoAvailableDefaultOrgModels: boolean;
2525
onSaveRootModelOverride: SavePersonalOverride;
2626
isSavingRootModelOverride: boolean;
2727
isSaveRootModelOverrideError: boolean;
@@ -46,7 +46,7 @@ export const AgentSettingsUserAgentsPageView: FC<
4646
modelsError,
4747
isLoadingModels,
4848
isDefaultOrganizationUnresolved,
49-
hasNoDefaultOrgModels,
49+
hasNoAvailableDefaultOrgModels,
5050
onSaveRootModelOverride,
5151
isSavingRootModelOverride,
5252
isSaveRootModelOverrideError,
@@ -63,7 +63,7 @@ export const AgentSettingsUserAgentsPageView: FC<
6363
isLoading ||
6464
!personalOverridesEnabled ||
6565
isDefaultOrganizationUnresolved ||
66-
hasNoDefaultOrgModels;
66+
hasNoAvailableDefaultOrgModels;
6767

6868
return (
6969
<div className="flex flex-col gap-8">
@@ -103,7 +103,7 @@ export const AgentSettingsUserAgentsPageView: FC<
103103
</AlertDescription>
104104
</Alert>
105105
)}
106-
{hasNoDefaultOrgModels && (
106+
{hasNoAvailableDefaultOrgModels && (
107107
<Alert severity="info">
108108
<AlertDescription>
109109
Your default organization has no available chat models. Ask an

site/src/pages/AgentsPage/components/AgentCreateForm.stories.tsx

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,22 @@ const unsupportedProviderCatalog: TypesGen.OrganizationChatModelsResponse = {
155155
],
156156
};
157157

158+
const unsupportedProviderWithDisabledSupportedCatalog: TypesGen.OrganizationChatModelsResponse =
159+
{
160+
...unsupportedProviderCatalog,
161+
providers: [
162+
...unsupportedProviderCatalog.providers,
163+
{
164+
...MockChatModelProviderDescriptor,
165+
id: "provider-anthropic",
166+
type: "anthropic",
167+
display_name: "Anthropic",
168+
enabled: false,
169+
available: false,
170+
},
171+
],
172+
};
173+
158174
const defaultUserProviderConfigs: TypesGen.UserChatProviderConfig[] = [
159175
{
160176
provider_id: "provider-1",
@@ -995,6 +1011,26 @@ export const UnsupportedProviderOnly: Story = {
9951011
},
9961012
};
9971013

1014+
export const UnsupportedProviderAndDisabledSupportedProvider: Story = {
1015+
args: { ...defaultArgs, canConfigureAgentSetup: true },
1016+
parameters: {
1017+
queries: [
1018+
{
1019+
key: organizationChatModelsKey(MockDefaultOrganization.id),
1020+
data: unsupportedProviderWithDisabledSupportedCatalog,
1021+
},
1022+
{ key: aiProvidersListKey, data: [] },
1023+
],
1024+
},
1025+
play: async ({ canvasElement }) => {
1026+
const canvas = within(canvasElement);
1027+
expect(canvas.getByText(/GitHub Copilot is configured but/i)).toBeVisible();
1028+
expect(
1029+
canvas.getByRole("link", { name: "not supported by Coder Agents" }),
1030+
).toBeVisible();
1031+
},
1032+
};
1033+
9981034
export const MissingProviderAndModelSetup: Story = {
9991035
parameters: {
10001036
queries: [

site/src/pages/AgentsPage/utils/modelOptions.test.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ type TestProvider = Pick<
5959
ChatModelProviderDescriptor,
6060
"available" | "unavailable_reason"
6161
> & {
62+
enabled?: boolean;
6263
id?: string;
6364
provider: string;
6465
};
@@ -1094,6 +1095,17 @@ describe("getUnsupportedProviderNames", () => {
10941095
expect(getUnsupportedProviderNames(catalog)).toEqual([]);
10951096
});
10961097

1098+
it("returns names when the only supported provider is disabled", () => {
1099+
const catalog = createCatalog(
1100+
[
1101+
{ provider: "copilot", available: false },
1102+
{ provider: "anthropic", available: false, enabled: false },
1103+
],
1104+
unsupportedCopilot,
1105+
);
1106+
expect(getUnsupportedProviderNames(catalog)).toEqual(["GitHub Copilot"]);
1107+
});
1108+
10971109
it("returns empty when there are no unsupported providers", () => {
10981110
expect(getUnsupportedProviderNames(createCatalog([]))).toEqual([]);
10991111
});

site/src/pages/AgentsPage/utils/modelOptions.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ export const getUnsupportedProviderNames = (
5656
const hasSupportedProvider =
5757
catalog?.providers.some(
5858
(provider) =>
59+
provider.enabled &&
5960
!unsupportedProviderTypes.has(normalizeProvider(provider.type)),
6061
) ?? false;
6162
if (unsupported.length === 0 || hasSupportedProvider) {
@@ -196,9 +197,9 @@ export const providerTypeByIDFromUserConfigs = (
196197
);
197198

198199
/**
199-
* Drops models whose provider row is disabled or missing. Both
200-
* provider-info sources include every enabled provider, so a missing row
201-
* means the provider is disabled or deleted.
200+
* Drops models whose provider row is disabled or missing. Provider descriptors
201+
* include disabled providers, while user provider configs may omit inaccessible
202+
* providers.
202203
*/
203204
export const filterModelsWithEnabledProvider = (
204205
models: readonly TypesGen.ChatModel[],

0 commit comments

Comments
 (0)