Skip to content

Commit

Permalink
feat(weave): Add mods page and menu item only for wandb admins (#3279)
Browse files Browse the repository at this point in the history
* Basic UI for mods

* Pass along purl for wild demo purposes

* Mod demo updates

* Wire up secret setting, add mod visual indicator

* gradient similar to streamlit

* Added secret types

* Fix mutation types

* Use the wandb api host for our iframe

* Wire up backend host

* Make mods page scroll properly

* Only show mods to admins

* Fix TSC errors

* Fix project sidebar deps

* Fix bungled merge

* Add grace period for startup

* Try some debugging, increase retries

* Fix formatting

* Fix trailing newline

* Enabling container logging

* Use wget instead of curl as we dont have it in the container anymore

* Fix shadow variable lint error
  • Loading branch information
vanpelt authored Dec 20, 2024
1 parent 7d1d212 commit fcb51c3
Show file tree
Hide file tree
Showing 7 changed files with 550 additions and 9 deletions.
16 changes: 14 additions & 2 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,16 @@ jobs:
env:
CI: 1
WANDB_ENABLE_TEST_CONTAINER: true
LOGGING_ENABLED: true
ports:
- '8080:8080'
- '8083:8083'
- '9015:9015'
options: --health-cmd "curl --fail http://localhost:8080/healthz || exit 1" --health-interval=5s --health-timeout=3s
options: >-
--health-cmd "wget -q -O /dev/null http://localhost:8080/healthz || exit 1"
--health-interval=5s
--health-timeout=3s
--health-start-period=10s
outputs:
tests_should_run: ${{ steps.test_check.outputs.tests_should_run }}
steps:
Expand Down Expand Up @@ -254,11 +259,16 @@ jobs:
env:
CI: 1
WANDB_ENABLE_TEST_CONTAINER: true
LOGGING_ENABLED: true
ports:
- '8080:8080'
- '8083:8083'
- '9015:9015'
options: --health-cmd "curl --fail http://localhost:8080/healthz || exit 1" --health-interval=5s --health-timeout=3s
options: >-
--health-cmd "wget -q -O /dev/null http://localhost:8080/healthz || exit 1"
--health-interval=5s
--health-timeout=3s
--health-start-period=10s
weave_clickhouse:
image: clickhouse/clickhouse-server
ports:
Expand All @@ -267,6 +277,8 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Enable debug logging
run: echo "ACTIONS_STEP_DEBUG=true" >> $GITHUB_ENV
- name: Set up Python ${{ matrix.python-version-major }}.${{ matrix.python-version-minor }}
uses: actions/setup-python@v5
with:
Expand Down
21 changes: 21 additions & 0 deletions wb_schema.gql
Original file line number Diff line number Diff line change
Expand Up @@ -104,10 +104,24 @@ type UpdateUserPayload {
clientMutationId: String
}

input InsertSecretInput {
entityName: String!
secretName: String!
@constraints(max: 255, pattern: "^[A-Za-z_][A-Za-z0-9_]*$")
secretValue: String!
clientMutationId: String
}

type InsertSecretPayload {
success: Boolean!
clientMutationId: String
}

type Mutation {
updateUser(input: UpdateUserInput!): UpdateUserPayload @audit
deleteView(input: DeleteViewInput!): DeleteViewPayload
upsertView(input: UpsertViewInput!): UpsertViewPayload @audit
insertSecret(input: InsertSecretInput!): InsertSecretPayload
updateArtifactSequence(
input: UpdateArtifactSequenceInput!
): UpdateArtifactCollectionPayload
Expand Down Expand Up @@ -275,6 +289,12 @@ type RowType {
row: JSON!
}

type Secret {
entityId: Int!
name: String!
createdAt: DateTime!
}

type Entity implements Node {
id: ID!
name: String!
Expand All @@ -296,6 +316,7 @@ type Entity implements Node {
filters: JSONString
collectionTypes: [ArtifactCollectionType!]
): ArtifactCollectionConnection
secrets: [Secret!]!
}

type EntityConnection {
Expand Down
121 changes: 121 additions & 0 deletions weave-js/src/common/hooks/useSecrets.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/**
* This is a GraphQL approach to querying viewer information.
* There is a query engine based approach in useViewerUserInfo.ts.
*/

import {
gql,
TypedDocumentNode,
useApolloClient,
useMutation,
} from '@apollo/client';
import {useEffect, useState} from 'react';

const SECRETS_QUERY = gql`
query secrets($entityName: String!) {
entity(name: $entityName) {
id
secrets {
entityId
name
createdAt
}
}
}
`;

const SECRETS_MUTATION = gql`
mutation insertSecret(
$entityName: String!
$secretName: String!
$secretValue: String!
) {
insertSecret(
input: {
entityName: $entityName
secretName: $secretName
secretValue: $secretValue
}
) {
success
}
}
` as TypedDocumentNode<InsertSecretResponse, InsertSecretVariables>;

type SecretResponseLoading = {
loading: true;
entityId: string;
secrets: string[];
};
type SecretResponseSuccess = {
loading: false;
entityId: string;
secrets: string[];
};
type SecretResponse = SecretResponseLoading | SecretResponseSuccess;

export const useSecrets = ({
entityName,
}: {
entityName: string;
}): SecretResponse => {
const [response, setResponse] = useState<SecretResponse>({
loading: true,
entityId: '',
secrets: [],
});

const apolloClient = useApolloClient();

useEffect(() => {
let mounted = true;
apolloClient
.query({query: SECRETS_QUERY as any, variables: {entityName}})
.then(result => {
if (!mounted) {
return;
}
const secretPayloads = result.data.entity?.secrets ?? [];
if (!secretPayloads) {
setResponse({
loading: false,
entityId: '',
secrets: [],
});
return;
}
const secrets = secretPayloads.map((secret: any) => secret.name).sort();
setResponse({
loading: false,
entityId: result.data.entity?.id ?? '',
secrets,
});
});
return () => {
mounted = false;
};
}, [apolloClient, entityName]);

return response;
};

interface InsertSecretResponse {
insertSecret: {
success: boolean;
};
}

type InsertSecretVariables = {
entityName: string;
secretName: string;
secretValue: string;
};

export const useInsertSecret = () => {
const [insertSecret] = useMutation<
InsertSecretResponse,
InsertSecretVariables
>(SECRETS_MUTATION);

return insertSecret;
};
27 changes: 21 additions & 6 deletions weave-js/src/components/FancyPage/useProjectSidebar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ export const useProjectSidebar = (
hasWeaveData: boolean,
hasTraceBackend: boolean = true,
hasModelsAccess: boolean = true,
isLaunchActive: boolean = false
isLaunchActive: boolean = false,
isWandbAdmin: boolean = false
): FancyPageSidebarItem[] => {
// Should show models sidebar items if we have models data or if we don't have a trace backend
let showModelsSidebarItems = hasModelsData || !hasTraceBackend;
Expand All @@ -34,6 +35,14 @@ export const useProjectSidebar = (
const isShowAll = isNoSidebarItems || isBothSidebarItems;

return useMemo(() => {
const weaveOnlyMenu = [
'weave/leaderboards',
'weave/operations',
'weave/objects',
];
if (isWandbAdmin) {
weaveOnlyMenu.push('weave/mods');
}
const allItems = isLoading
? []
: [
Expand Down Expand Up @@ -178,6 +187,14 @@ export const useProjectSidebar = (
isShown: isWeaveOnly,
iconName: IconNames.TypeNumberAlt,
},
{
type: 'button' as const,
name: 'Mods',
slug: 'weave/mods',
isShown: false, // Only shown in overflow menu
isDisabled: !isWandbAdmin,
iconName: IconNames.LayoutGrid,
},
{
type: 'button' as const,
name: 'Leaders',
Expand Down Expand Up @@ -205,7 +222,7 @@ export const useProjectSidebar = (
type: 'menuPlaceholder' as const,
key: 'moreWeaveOnly',
isShown: isWeaveOnly,
menu: ['weave/leaderboards', 'weave/operations', 'weave/objects'],
menu: weaveOnlyMenu,
},
{
type: 'menuPlaceholder' as const,
Expand All @@ -216,10 +233,7 @@ export const useProjectSidebar = (
'weave/models',
'weave/datasets',
'weave/scorers',
'weave/leaderboards',
'weave/operations',
'weave/objects',
],
].concat(weaveOnlyMenu),
},
];

Expand Down Expand Up @@ -252,5 +266,6 @@ export const useProjectSidebar = (
isModelsOnly,
showWeaveSidebarItems,
isLaunchActive,
isWandbAdmin,
]);
};
18 changes: 18 additions & 0 deletions weave-js/src/components/PagePanelComponents/Home/Browse3.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ import {SimplePageLayoutContext} from './Browse3/pages/common/SimplePageLayout';
import {CompareEvaluationsPage} from './Browse3/pages/CompareEvaluationsPage/CompareEvaluationsPage';
import {LeaderboardListingPage} from './Browse3/pages/LeaderboardPage/LeaderboardListingPage';
import {LeaderboardPage} from './Browse3/pages/LeaderboardPage/LeaderboardPage';
import {ModsPage} from './Browse3/pages/ModsPage';
import {ObjectPage} from './Browse3/pages/ObjectPage';
import {ObjectVersionPage} from './Browse3/pages/ObjectVersionPage';
import {
Expand Down Expand Up @@ -146,6 +147,7 @@ const tabOptions = [
'leaderboards',
'boards',
'tables',
'mods',
'scorers',
];
const tabs = tabOptions.join('|');
Expand Down Expand Up @@ -484,6 +486,11 @@ const Browse3ProjectRoot: FC<{
<Route path={`${projectRoot}/tables`}>
<TablesPageBinding />
</Route>
{/* MODS */}
<Route
path={[`${projectRoot}/mods/:itemName`, `${projectRoot}/:tab(mods)`]}>
<ModsPageBinding />
</Route>
{/* PLAYGROUND */}
<Route
path={[
Expand Down Expand Up @@ -992,6 +999,17 @@ const BoardsPageBinding = () => {
return <BoardsPage entity={params.entity} project={params.project} />;
};

const ModsPageBinding = () => {
const params = useParamsDecoded<Browse3TabItemVersionParams>();
return (
<ModsPage
entity={params.entity}
project={params.project}
itemName={params.itemName}
/>
);
};

const TablesPageBinding = () => {
const params = useParamsDecoded<Browse3TabItemParams>();

Expand Down
Loading

0 comments on commit fcb51c3

Please sign in to comment.