Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit3bb7975

Browse files
authored
feat: add page for ai-bridge interception logs (#20331)
Relates#20287 This pull-request introduces a basic routing for `AI Governance`'sRequest Logs feature. Currently we're just pulling back the basics fromthe database and rendering it into the table. Nothing exciting.The idea is to extend further upon the `/aigovernance` route so it hasbeen appropriately wrapped with a `<AIGovernanceLayout />` to introducea navigation later.
1 parentdc21699 commit3bb7975

File tree

17 files changed

+718
-0
lines changed

17 files changed

+718
-0
lines changed

‎site/src/api/api.ts‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2716,6 +2716,16 @@ class ExperimentalApiMethods {
27162716
setTimeout(()=>res(),500);
27172717
});
27182718
};
2719+
2720+
getAIBridgeInterceptions=async(options:SearchParamOptions)=>{
2721+
consturl=getURLWithSearchParams(
2722+
"/api/experimental/aibridge/interceptions",
2723+
options,
2724+
);
2725+
constresponse=
2726+
awaitthis.axios.get<TypesGen.AIBridgeListInterceptionsResponse>(url);
2727+
returnresponse.data;
2728+
};
27192729
}
27202730

27212731
// This is a hard coded CSRF token/cookie pair for local development. In prod,

‎site/src/api/queries/aiBridge.ts‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import{API}from"api/api";
2+
importtype{AIBridgeListInterceptionsResponse}from"api/typesGenerated";
3+
import{useFilterParamsKey}from"components/Filter/Filter";
4+
importtype{UsePaginatedQueryOptions}from"hooks/usePaginatedQuery";
5+
6+
exportconstpaginatedInterceptions=(
7+
searchParams:URLSearchParams,
8+
):UsePaginatedQueryOptions<AIBridgeListInterceptionsResponse,string>=>{
9+
return{
10+
queryPayload:()=>searchParams.get(useFilterParamsKey)??"",
11+
queryKey:({ payload, pageNumber})=>{
12+
return["aiBridgeInterceptions",payload,pageNumber]asconst;
13+
},
14+
queryFn:({ limit, offset, payload})=>
15+
API.experimental.getAIBridgeInterceptions({
16+
offset,
17+
limit,
18+
q:payload,
19+
}),
20+
};
21+
};

‎site/src/modules/dashboard/Navbar/DeploymentDropdown.tsx‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ interface DeploymentDropdownProps {
1818
canViewAuditLog:boolean;
1919
canViewConnectionLog:boolean;
2020
canViewHealth:boolean;
21+
canViewAIGovernance:boolean;
2122
}
2223

2324
exportconstDeploymentDropdown:FC<DeploymentDropdownProps>=({
@@ -26,6 +27,7 @@ export const DeploymentDropdown: FC<DeploymentDropdownProps> = ({
2627
canViewAuditLog,
2728
canViewConnectionLog,
2829
canViewHealth,
30+
canViewAIGovernance,
2931
})=>{
3032
if(
3133
!canViewAuditLog&&
@@ -56,6 +58,7 @@ export const DeploymentDropdown: FC<DeploymentDropdownProps> = ({
5658
canViewAuditLog={canViewAuditLog}
5759
canViewConnectionLog={canViewConnectionLog}
5860
canViewHealth={canViewHealth}
61+
canViewAIGovernance={canViewAIGovernance}
5962
/>
6063
</PopoverContent>
6164
</Popover>
@@ -68,6 +71,7 @@ const DeploymentDropdownContent: FC<DeploymentDropdownProps> = ({
6871
canViewAuditLog,
6972
canViewHealth,
7073
canViewConnectionLog,
74+
canViewAIGovernance,
7175
})=>{
7276
return(
7377
<nav>
@@ -111,6 +115,17 @@ const DeploymentDropdownContent: FC<DeploymentDropdownProps> = ({
111115
</MenuItem>
112116
</PopoverClose>
113117
)}
118+
{canViewAIGovernance&&(
119+
<PopoverCloseasChild>
120+
<MenuItem
121+
component={NavLink}
122+
to="/aigovernance"
123+
css={styles.menuItem}
124+
>
125+
AI Governance
126+
</MenuItem>
127+
</PopoverClose>
128+
)}
114129
{canViewHealth&&(
115130
<PopoverCloseasChild>
116131
<MenuItemcomponent={NavLink}to="/health"css={styles.menuItem}>

‎site/src/modules/dashboard/Navbar/Navbar.tsx‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ export const Navbar: FC = () => {
2525
featureVisibility.audit_log&&permissions.viewAnyAuditLog;
2626
constcanViewConnectionLog=
2727
featureVisibility.connection_log&&permissions.viewAnyConnectionLog;
28+
constcanViewAIGovernance=
29+
featureVisibility.aibridge&&permissions.viewAnyAIBridgeInterception;
2830

2931
constuniqueLinks=newMap<string,LinkConfig>();
3032
for(constlinkofappearance.support_links??[]){
@@ -44,6 +46,7 @@ export const Navbar: FC = () => {
4446
canViewHealth={canViewHealth}
4547
canViewAuditLog={canViewAuditLog}
4648
canViewConnectionLog={canViewConnectionLog}
49+
canViewAIGovernance={canViewAIGovernance}
4750
proxyContextValue={proxyContextValue}
4851
/>
4952
);

‎site/src/modules/dashboard/Navbar/NavbarView.tsx‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ interface NavbarViewProps {
3535
canViewAuditLog:boolean;
3636
canViewConnectionLog:boolean;
3737
canViewHealth:boolean;
38+
canViewAIGovernance:boolean;
3839
proxyContextValue?:ProxyContextValue;
3940
}
4041

@@ -55,6 +56,7 @@ export const NavbarView: FC<NavbarViewProps> = ({
5556
canViewHealth,
5657
canViewAuditLog,
5758
canViewConnectionLog,
59+
canViewAIGovernance,
5860
proxyContextValue,
5961
})=>{
6062
constwebPush=useWebpushNotifications();
@@ -95,6 +97,7 @@ export const NavbarView: FC<NavbarViewProps> = ({
9597
canViewDeployment={canViewDeployment}
9698
canViewHealth={canViewHealth}
9799
canViewConnectionLog={canViewConnectionLog}
100+
canViewAIGovernance={canViewAIGovernance}
98101
/>
99102
</div>
100103

‎site/src/modules/permissions/index.ts‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,13 @@ export const permissionChecks = {
169169
},
170170
action:"read",
171171
},
172+
viewAnyAIBridgeInterception:{
173+
object:{
174+
resource_type:"aibridge_interception",
175+
any_org:true,
176+
},
177+
action:"read",
178+
},
172179
}asconstsatisfiesRecord<string,AuthorizationCheck>;
173180

174181
exportconstcanViewDeploymentSettings=(
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import{
2+
HelpTooltip,
3+
HelpTooltipContent,
4+
HelpTooltipIconTrigger,
5+
HelpTooltipLink,
6+
HelpTooltipLinksGroup,
7+
HelpTooltipText,
8+
HelpTooltipTitle,
9+
}from"components/HelpTooltip/HelpTooltip";
10+
importtype{FC}from"react";
11+
import{docs}from"utils/docs";
12+
13+
exportconstAIGovernanceHelpTooltip:FC=()=>{
14+
return(
15+
<HelpTooltip>
16+
<HelpTooltipIconTrigger/>
17+
18+
<HelpTooltipContent>
19+
<HelpTooltipTitle>What is AI Governance?</HelpTooltipTitle>
20+
<HelpTooltipText>
21+
AI Governance is a proxy that unifies and audits LLM usage across your
22+
organization.
23+
</HelpTooltipText>
24+
<HelpTooltipLinksGroup>
25+
<HelpTooltipLinkhref={docs("/ai-coder/ai-bridge")}>
26+
What we track
27+
</HelpTooltipLink>
28+
</HelpTooltipLinksGroup>
29+
</HelpTooltipContent>
30+
</HelpTooltip>
31+
);
32+
};
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import{Margins}from"components/Margins/Margins";
2+
import{
3+
PageHeader,
4+
PageHeaderSubtitle,
5+
PageHeaderTitle,
6+
}from"components/PageHeader/PageHeader";
7+
importtype{FC,PropsWithChildren}from"react";
8+
import{Outlet}from"react-router";
9+
import{AIGovernanceHelpTooltip}from"./AIGovernanceHelpTooltip";
10+
11+
constAIGovernanceLayout:FC<PropsWithChildren>=()=>{
12+
return(
13+
<MarginsclassName="pb-12">
14+
<PageHeader>
15+
<PageHeaderTitle>
16+
<divclassName="flex items-center gap-2">
17+
<span>AI Governance</span>
18+
<AIGovernanceHelpTooltip/>
19+
</div>
20+
</PageHeaderTitle>
21+
<PageHeaderSubtitle>
22+
Manage usage for your organization.
23+
</PageHeaderSubtitle>
24+
</PageHeader>
25+
<Outlet/>
26+
</Margins>
27+
);
28+
};
29+
30+
exportdefaultAIGovernanceLayout;
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import{paginatedInterceptions}from"api/queries/aiBridge";
2+
import{useFilter}from"components/Filter/Filter";
3+
import{useUserFilterMenu}from"components/Filter/UserFilter";
4+
import{usePaginatedQuery}from"hooks/usePaginatedQuery";
5+
import{useFeatureVisibility}from"modules/dashboard/useFeatureVisibility";
6+
importtype{FC}from"react";
7+
import{useSearchParams}from"react-router";
8+
import{pageTitle}from"utils/page";
9+
import{useProviderFilterMenu}from"./filter/filter";
10+
import{RequestLogsPageView}from"./RequestLogsPageView";
11+
12+
constRequestLogsPage:FC=()=>{
13+
constfeats=useFeatureVisibility();
14+
constisRequestLogsVisible=Boolean(feats.aibridge);
15+
16+
const[searchParams,setSearchParams]=useSearchParams();
17+
constinterceptionsQuery=usePaginatedQuery(
18+
paginatedInterceptions(searchParams),
19+
);
20+
constfilter=useFilter({
21+
searchParams,
22+
onSearchParamsChange:setSearchParams,
23+
onUpdate:interceptionsQuery.goToFirstPage,
24+
});
25+
26+
constuserMenu=useUserFilterMenu({
27+
value:filter.values.initiator,
28+
onChange:(option)=>
29+
filter.update({
30+
...filter.values,
31+
initiator:option?.value,
32+
}),
33+
});
34+
35+
constproviderMenu=useProviderFilterMenu({
36+
value:filter.values.provider,
37+
onChange:(option)=>
38+
filter.update({
39+
...filter.values,
40+
provider:option?.value,
41+
}),
42+
});
43+
44+
return(
45+
<>
46+
<title>{pageTitle("Request Logs","AI Governance")}</title>
47+
48+
<RequestLogsPageView
49+
isLoading={interceptionsQuery.isLoading}
50+
isRequestLogsVisible={isRequestLogsVisible}
51+
interceptions={interceptionsQuery.data?.results}
52+
interceptionsQuery={interceptionsQuery}
53+
filterProps={{
54+
filter,
55+
error:interceptionsQuery.error,
56+
menus:{
57+
user:userMenu,
58+
provider:providerMenu,
59+
},
60+
}}
61+
/>
62+
</>
63+
);
64+
};
65+
66+
exportdefaultRequestLogsPage;
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import{MockInterception}from"testHelpers/entities";
2+
importtype{Meta,StoryObj}from"@storybook/react-vite";
3+
import{
4+
getDefaultFilterProps,
5+
MockMenu,
6+
}from"components/Filter/storyHelpers";
7+
import{
8+
mockInitialRenderResult,
9+
mockSuccessResult,
10+
}from"components/PaginationWidget/PaginationContainer.mocks";
11+
importtype{ComponentProps}from"react";
12+
import{RequestLogsPageView}from"./RequestLogsPageView";
13+
14+
typeFilterProps=ComponentProps<typeofRequestLogsPageView>["filterProps"];
15+
16+
constdefaultFilterProps=getDefaultFilterProps<FilterProps>({
17+
query:"owner:me",
18+
values:{
19+
username:undefined,
20+
provider:undefined,
21+
},
22+
menus:{
23+
user:MockMenu,
24+
provider:MockMenu,
25+
},
26+
});
27+
28+
constinterceptions=[MockInterception,MockInterception,MockInterception];
29+
30+
constmeta:Meta<typeofRequestLogsPageView>={
31+
title:"pages/AIGovernancePage/RequestLogsPageView",
32+
component:RequestLogsPageView,
33+
args:{},
34+
};
35+
36+
exportdefaultmeta;
37+
typeStory=StoryObj<typeofRequestLogsPageView>;
38+
39+
exportconstPaywall:Story={
40+
args:{
41+
isRequestLogsVisible:false,
42+
},
43+
};
44+
45+
exportconstLoaded:Story={
46+
args:{
47+
isRequestLogsVisible:true,
48+
interceptions,
49+
filterProps:{
50+
...defaultFilterProps,
51+
},
52+
interceptionsQuery:mockSuccessResult,
53+
},
54+
};
55+
56+
exportconstEmpty:Story={
57+
args:{
58+
isRequestLogsVisible:true,
59+
interceptions:[],
60+
filterProps:{
61+
...defaultFilterProps,
62+
},
63+
interceptionsQuery:mockSuccessResult,
64+
},
65+
};
66+
67+
exportconstLoading:Story={
68+
args:{
69+
isLoading:true,
70+
isRequestLogsVisible:true,
71+
interceptions:[],
72+
filterProps:{
73+
...defaultFilterProps,
74+
},
75+
interceptionsQuery:mockInitialRenderResult,
76+
},
77+
};

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp