Fix always_include_stream_usage missing in UI

This commit is contained in:
joshhatfield 2026-05-07 17:36:24 +10:00
parent a67b7a7e87
commit dfc386aac2
3 changed files with 86 additions and 15 deletions

View file

@ -2452,6 +2452,10 @@ class ConfigGeneralSettings(LiteLLMPydanticObjectBase):
None,
description="If True, forwards client headers (e.g. Authorization) to the LLM API. Required for Claude Code with Max subscription.",
)
always_include_stream_usage: Optional[bool] = Field(
None,
description="If True, automatically includes usage information in all streaming responses.",
)
mcp_required_fields: Optional[List[str]] = Field(
None,
description="List of MCP server fields that must be filled in for a submission to pass standards checks (e.g. ['description', 'source_url', 'alias']).",

View file

@ -41,12 +41,16 @@ const buildSettingsResponse = (overrides?: Partial<Record<string, unknown>>) =>
require_auth_for_public_ai_hub: {
description: "Require authentication for public AI Hub",
},
always_include_stream_usage: {
description: "Always include stream usage",
},
},
},
values: {
disable_model_add_for_internal_users: false,
disable_team_admin_delete_team_user: false,
require_auth_for_public_ai_hub: false,
always_include_stream_usage: false,
},
},
isLoading: false,
@ -74,6 +78,7 @@ describe("UISettings", () => {
expect(screen.getByRole("switch", { name: "Disable model add for internal users" })).toBeInTheDocument();
expect(screen.getByRole("switch", { name: "Disable team admin delete team user" })).toBeInTheDocument();
expect(screen.getByRole("switch", { name: "Require authentication for public AI Hub" })).toBeInTheDocument();
expect(screen.getByRole("switch", { name: "Always include stream usage" })).toBeInTheDocument();
});
it("should toggle setting and call update", () => {
@ -162,4 +167,33 @@ describe("UISettings", () => {
);
expect(NotificationManager.success).toHaveBeenCalledWith("UI settings updated successfully");
});
it("should toggle always include stream usage setting and call update", () => {
const mutateMock = vi.fn((_settings, options) => {
options?.onSuccess?.();
});
mockUseUpdateUISettings.mockReturnValue({
mutate: mutateMock,
isPending: false,
error: null,
});
render(<UISettings />);
const toggle = screen.getByRole("switch", { name: "Always include stream usage" });
act(() => {
fireEvent.click(toggle);
});
expect(mutateMock).toHaveBeenCalledWith(
{ always_include_stream_usage: true },
expect.objectContaining({
onSuccess: expect.any(Function),
onError: expect.any(Function),
}),
);
expect(NotificationManager.success).toHaveBeenCalledWith("UI settings updated successfully");
});
});

View file

@ -17,6 +17,7 @@ export default function UISettings() {
const disableTeamAdminDeleteProperty = schema?.properties?.disable_team_admin_delete_team_user;
const requireAuthForPublicAIHubProperty = schema?.properties?.require_auth_for_public_ai_hub;
const forwardClientHeadersProperty = schema?.properties?.forward_client_headers_to_llm_api;
const alwaysIncludeStreamUsageProperty = schema?.properties?.always_include_stream_usage;
const forwardLLMProviderAuthHeadersProperty =
schema?.properties?.forward_llm_provider_auth_headers;
const enableProjectsUIProperty = schema?.properties?.enable_projects_ui;
@ -28,6 +29,7 @@ export default function UISettings() {
const scopeUserSearchProperty = schema?.properties?.scope_user_search_to_org;
const disableCustomApiKeysProperty = schema?.properties?.disable_custom_api_keys;
const values = data?.values ?? {};
const isAlwaysIncludeStreamUsage = Boolean(values.always_include_stream_usage);
const isDisabledForInternalUsers = Boolean(values.disable_model_add_for_internal_users);
const isDisabledTeamAdminDeleteTeamUser = Boolean(values.disable_team_admin_delete_team_user);
const isAgentsDisabled = Boolean(values.disable_agents_for_internal_users);
@ -86,6 +88,20 @@ export default function UISettings() {
);
};
const handleToggleAlwaysIncludeStreamUsage = (checked: boolean) => {
updateSettings(
{ always_include_stream_usage: checked },
{
onSuccess: () => {
NotificationManager.success("UI settings updated successfully");
},
onError: (error) => {
NotificationManager.fromBackend(error);
},
},
);
};
const handleToggleForwardLLMProviderAuthHeaders = (checked: boolean) => {
updateSettings(
{ forward_llm_provider_auth_headers: checked },
@ -283,22 +299,39 @@ export default function UISettings() {
</Space>
</Space>
<Space align="start" size="middle">
<Switch
checked={Boolean(values.forward_client_headers_to_llm_api)}
disabled={isUpdating}
loading={isUpdating}
onChange={handleToggleForwardClientHeaders}
aria-label={forwardClientHeadersProperty?.description ?? "Forward client headers to LLM API"}
/>
<Space direction="vertical" size={4}>
<Typography.Text strong>Forward client headers to LLM API</Typography.Text>
<Typography.Text type="secondary">
{forwardClientHeadersProperty?.description ??
"Forwards client headers (Authorization, anthropic-beta, and x-* custom headers) to the upstream LLM. Enable for Claude Code with a Max subscription (forwards the OAuth token) or to pass custom/tracing headers through to the provider. Independent of the BYOK toggle — enable only the one(s) you need."}
</Typography.Text>
<Space align="start" size="middle">
<Switch
checked={Boolean(values.forward_client_headers_to_llm_api)}
disabled={isUpdating}
loading={isUpdating}
onChange={handleToggleForwardClientHeaders}
aria-label={forwardClientHeadersProperty?.description ?? "Forward client headers to LLM API"}
/>
<Space direction="vertical" size={4}>
<Typography.Text strong>Forward client headers to LLM API</Typography.Text>
<Typography.Text type="secondary">
{forwardClientHeadersProperty?.description ??
"Forwards client headers (Authorization, anthropic-beta, and x-* custom headers) to the upstream LLM. Enable for Claude Code with a Max subscription (forwards the OAuth token) or to pass custom/tracing headers through to the provider. Independent of the BYOK toggle — enable only the one(s) you need."}
</Typography.Text>
</Space>
</Space>
<Space align="start" size="middle">
<Switch
checked={isAlwaysIncludeStreamUsage}
disabled={isUpdating}
loading={isUpdating}
onChange={handleToggleAlwaysIncludeStreamUsage}
aria-label={alwaysIncludeStreamUsageProperty?.description ?? "Always include stream usage"}
/>
<Space direction="vertical" size={4}>
<Typography.Text strong>Always include stream usage</Typography.Text>
<Typography.Text type="secondary">
{alwaysIncludeStreamUsageProperty?.description ??
"If enabled, usage is included in stream responses."}
</Typography.Text>
</Space>
</Space>
</Space>
<Space align="start" size="middle">
<Switch