Skip to content

Commit c21eec3

Browse files
Inokinokikevinmessiaenandreybavt
authored
[GSK-1593] Display Giskard Client info including URL, token and Spaces token (#1324)
* Add Giskard instance URL in general setting page * Add HF Spaces token gen in API token card * Force to render CodeSnippets to show new API token * Improve state management for HF Spaces token * Polish layout to remove space in Space token card * Add a card for Giskard Client create instructions * Add Giskard Client create instructions card in Test Suite Uploading and Settings pages. * Display generate button if MLWorker not connected Allow to regenerate token when HF access token is entered somewhere else * Allow to show URL and tokens inside the card * Remove HF Spaces token from `ApiTokenCard` To avoid too many synchronizations of the state between components. * Do not display `hf_token` for public space * Use new API key store and improve behaviors * Remove unused import in ClientInstructionCard.vue * Add HFSpaces token store * Refactor HFSpaces token fetching using store * Get ApiKey before showing instructions for client and worker * Fix state for public HF Spaces * Add a tip to indicate Client creation card * Remove unused import in `ClientInstructionCard` * Put API and HFSpaces token cards together * Fix typo in `frontend/src/stores/hfspaces.ts` Co-authored-by: Kevin Messiaen <[email protected]> --------- Co-authored-by: Kevin Messiaen <[email protected]> Co-authored-by: Andrey Avtomonov <[email protected]>
1 parent 84f15eb commit c21eec3

File tree

8 files changed

+599
-412
lines changed

8 files changed

+599
-412
lines changed
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
<template>
2+
<v-card height="100%">
3+
<v-card-title class="font-weight-light secondary--text">Create a Giskard Client</v-card-title>
4+
<v-card-text v-if="giskardClientTemplate != null">
5+
<CodeSnippet :code-content="giskardClientTemplate"/>
6+
</v-card-text>
7+
<v-card-text v-else-if="needFetchWithHFAccessToken == null">
8+
<LoadingFullscreen :name="'Giskard Client creating instructions'"/>
9+
</v-card-text>
10+
<v-card-text v-else-if="!state.workerStatus.connected && !props.internalHFAccessToken">
11+
<div class="mb-2">
12+
<v-btn small tile color="primaryLight" class="primaryLightBtn" @click="generateHFSpacesToken">Generate</v-btn>
13+
</div>
14+
<v-row>
15+
<v-col>
16+
Please enter your Hugging Face access token in Giskard Settings page to generate the instructions.
17+
</v-col>
18+
</v-row>
19+
</v-card-text>
20+
<v-card-text v-else>
21+
<HuggingFaceTokenCard @submit="fetchAndSaveHFSpacesTokenWithAccessToken"/>
22+
</v-card-text>
23+
</v-card>
24+
</template>
25+
26+
<script setup lang="ts">
27+
import { ref, onMounted } from "vue";
28+
import { useMainStore } from "@/stores/main";
29+
import { TYPE } from "vue-toastification";
30+
31+
import CodeSnippet from "@/components/CodeSnippet.vue";
32+
import LoadingFullscreen from "./LoadingFullscreen.vue";
33+
import HuggingFaceTokenCard from "./HuggingFaceTokenCard.vue";
34+
35+
import { saveLocalHFToken } from "@/utils";
36+
import { useHFSpacesTokenStore } from "@/stores/hfspaces";
37+
38+
import { apiURL } from "@/env";
39+
import { state } from "@/socket";
40+
41+
import { useApiKeyStore } from "@/stores/api-key-store";
42+
43+
const apiKeyStore = useApiKeyStore();
44+
const mainStore = useMainStore();
45+
46+
interface Props {
47+
internalHFAccessToken: boolean,
48+
}
49+
50+
const props = withDefaults(defineProps<Props>(), {
51+
internalHFAccessToken: false,
52+
});
53+
54+
function generateGiskardClientInstruction(hf_token: string | null = null) {
55+
let snippet = `from giskard import GiskardClient
56+
57+
url = "${apiURL}"
58+
api_token = "${apiKeyStore.getFirstApiKey ? apiKeyStore.getFirstApiKey : '<Generate your API Key first>'}"
59+
`;
60+
if (hf_token) {
61+
snippet += `hf_token = "${hf_token}"
62+
`;
63+
}
64+
snippet += `
65+
# Create a giskard client to communicate with Giskard
66+
client = GiskardClient(url, api_token`;
67+
if (hf_token) {
68+
snippet += `, hf_token`;
69+
}
70+
snippet += `)`;
71+
return snippet;
72+
};
73+
74+
// Hugging Face Spaces token (Giskard Space token)
75+
const giskardClientTemplate = ref<string | null>(null);
76+
const needFetchWithHFAccessToken = ref<boolean | null>(null);
77+
78+
async function fetchAndSaveHFSpacesTokenWithAccessToken(accessToken: string) {
79+
if (mainStore.appSettings!.isRunningOnHfSpaces) {
80+
saveLocalHFToken(accessToken);
81+
const hfSpacesTokenStore = useHFSpacesTokenStore();
82+
const token = await hfSpacesTokenStore.getHFSpacesToken();
83+
if (token === null) {
84+
needFetchWithHFAccessToken.value = true;
85+
mainStore.addNotification({content: 'Invalid Hugging Face access token', color: TYPE.ERROR});
86+
} else {
87+
giskardClientTemplate.value = generateGiskardClientInstruction(token);
88+
needFetchWithHFAccessToken.value = false;
89+
}
90+
}
91+
}
92+
93+
async function generateHFSpacesToken() {
94+
if (mainStore.appSettings?.isRunningOnHfSpaces) {
95+
const hfSpacesTokenStore = useHFSpacesTokenStore();
96+
const token = await hfSpacesTokenStore.getHFSpacesToken();
97+
if (token === null) {
98+
// Private HFSpaces or no valid HF access token
99+
needFetchWithHFAccessToken.value = true;
100+
return;
101+
} else if (!hfSpacesTokenStore.publicSpace) {
102+
giskardClientTemplate.value = generateGiskardClientInstruction(token);
103+
} else {
104+
giskardClientTemplate.value = generateGiskardClientInstruction();
105+
}
106+
needFetchWithHFAccessToken.value = false;
107+
}
108+
}
109+
110+
onMounted(async () => {
111+
await apiKeyStore.getAll();
112+
if (mainStore.appSettings?.isRunningOnHfSpaces) {
113+
await generateHFSpacesToken();
114+
} else {
115+
needFetchWithHFAccessToken.value = false;
116+
giskardClientTemplate.value = generateGiskardClientInstruction();
117+
}
118+
});
119+
120+
</script>
121+

frontend/src/components/StartWorkerInstructions.vue

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,12 @@ import {useMainStore} from "@/stores/main";
4646
import {useRoute} from "vue-router/composables";
4747
import {apiURL} from "@/env";
4848
import CodeSnippet from "./CodeSnippet.vue";
49-
import {getLocalHFToken, saveLocalHFToken} from "@/utils";
49+
import {saveLocalHFToken} from "@/utils";
5050
import HuggingFaceTokenCard from "./HuggingFaceTokenCard.vue";
51-
import {attemptFetchHFSpacesToken} from "@/hf-utils";
5251
import LoadingFullscreen from "./LoadingFullscreen.vue";
5352
import {TYPE} from 'vue-toastification';
5453
import {useApiKeyStore} from "@/stores/api-key-store";
54+
import { useHFSpacesTokenStore } from "@/stores/hfspaces";
5555
5656
const appSettings = computed(() => mainStore.appSettings);
5757
@@ -79,29 +79,40 @@ const codeContent = computed(() => {
7979
async function fetchAndSaveHFSpacesTokenWithAccessToken(accessToken: string) {
8080
if (mainStore.appSettings!.isRunningOnHfSpaces) {
8181
saveLocalHFToken(accessToken);
82-
await attemptFetchHFSpacesToken((token) => {
82+
83+
const hfSpacesTokenStore = useHFSpacesTokenStore();
84+
const token = await hfSpacesTokenStore.getHFSpacesToken();
85+
if (token === null) {
86+
// Private HFSpaces or no valid HF access token
87+
needFetchWithHFAccessToken.value = true;
88+
mainStore.addNotification({content: 'Invalid Hugging Face access token', color: TYPE.ERROR});
89+
} else if (!hfSpacesTokenStore.publicSpace) {
8390
needFetchWithHFAccessToken.value = false;
8491
hfToken.value = token;
85-
}, () => {
92+
}
93+
}
94+
}
95+
96+
async function generateHFSpacesToken() {
97+
if (mainStore.appSettings?.isRunningOnHfSpaces) {
98+
const hfSpacesTokenStore = useHFSpacesTokenStore();
99+
const token = await hfSpacesTokenStore.getHFSpacesToken();
100+
if (token === null) {
101+
// Private HFSpaces or no valid HF access token
86102
needFetchWithHFAccessToken.value = true;
87-
mainStore.addNotification({content: 'Invalid Hugging Face access token', color: TYPE.ERROR});
88-
});
103+
return;
104+
} else if (!hfSpacesTokenStore.publicSpace) {
105+
hfToken.value = token;
106+
}
107+
needFetchWithHFAccessToken.value = false;
89108
}
90109
}
91110
92111
onMounted(async () => {
93112
94113
await apiKeyStore.getAll();
95114
if (mainStore.appSettings!.isRunningOnHfSpaces) {
96-
await attemptFetchHFSpacesToken((token) => {
97-
if (getLocalHFToken()) {
98-
hfToken.value = token;
99-
}
100-
needFetchWithHFAccessToken.value = false;
101-
}, () => {
102-
// Access Token seems invalidated or private
103-
needFetchWithHFAccessToken.value = true;
104-
});
115+
await generateHFSpacesToken();
105116
} else {
106117
needFetchWithHFAccessToken.value = false;
107118
}

frontend/src/hf-utils.ts

Lines changed: 0 additions & 31 deletions
This file was deleted.

frontend/src/snippets.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {useApiKeyStore} from "@/stores/api-key-store";
44

55
const mainStore = useMainStore();
66
const apiKeyStore = useApiKeyStore();
7-
export async function generateGiskardClientSnippet(hfToken=null) {
7+
export async function generateGiskardClientSnippet(hfToken: string | null = null) {
88
let apiKey: string;
99
if (apiKeyStore.getFirstApiKey) {
1010
apiKey = apiKeyStore.getFirstApiKey;

frontend/src/stores/hfspaces.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { defineStore } from "pinia";
2+
import { api } from "@/api";
3+
import { useMainStore } from "./main";
4+
import { getLocalHFToken } from "@/utils";
5+
6+
7+
interface HFSpacesTokenState {
8+
token: string | null,
9+
expire: Date,
10+
publicSpace: boolean,
11+
}
12+
13+
export const useHFSpacesTokenStore = defineStore('hfSpacesToken', {
14+
state: (): HFSpacesTokenState => ({
15+
token: null,
16+
expire: new Date(),
17+
publicSpace: true,
18+
}),
19+
actions: {
20+
async getHFSpacesToken() {
21+
const now = new Date();
22+
if (now < this.expire) {
23+
// Not yet expired
24+
return this.token;
25+
}
26+
27+
// Fetch new HFSpaces token
28+
try {
29+
const res = await api.getHuggingFaceSpacesToken(useMainStore().appSettings?.hfSpaceId!);
30+
this.token = res.data.token;
31+
this.expire = new Date();
32+
this.expire.setDate(this.expire.getDate() + 1); // Expire in 24 hours
33+
34+
if (getLocalHFToken() !== null) {
35+
// Fetched with HF Access token
36+
this.publicSpace = false;
37+
}
38+
} catch(error) {
39+
if (error.response.status == 401) {
40+
console.warn("Running in a private Hugging Face space, may need an access token.")
41+
this.publicSpace = false;
42+
this.token = null;
43+
}
44+
}
45+
return this.token;
46+
},
47+
}
48+
});

frontend/src/views/main/admin/settings/SettingsGeneral.vue

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@
1010
<v-card-text>
1111
<v-simple-table>
1212
<table class="w100">
13+
<tr>
14+
<td>Giskard Instance URL</td>
15+
<td>{{ apiURL }}</td>
16+
</tr>
1317
<tr>
1418
<td>Instance</td>
1519
<td>{{ appSettings.generalSettings?.instanceId }}</td>
@@ -78,6 +82,9 @@
7882
<v-col>
7983
<ApiTokenCard />
8084
</v-col>
85+
<v-col>
86+
<ClientInstructionCard :internalHFAccessToken="false" />
87+
</v-col>
8188
</v-row>
8289
<v-row>
8390
<v-col>
@@ -192,6 +199,7 @@ import ApiTokenCard from "@/components/ApiTokenCard.vue";
192199
import PlanUpgradeCard from "@/components/ee/PlanUpgradeCard.vue";
193200
import StartWorkerInstructions from "@/components/StartWorkerInstructions.vue";
194201
import CodeSnippet from "@/components/CodeSnippet.vue";
202+
import ClientInstructionCard from "@/components/ClientInstructionCard.vue";
195203
import {openapi} from "@/api-v2";
196204
import {GeneralSettings, MLWorkerInfoDTO} from "@/generated/client";
197205

frontend/src/views/main/profile/UserProfile.vue

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,11 @@
7373
<ApiTokenCard/>
7474
</v-col>
7575
</v-row>
76+
<v-row>
77+
<v-col>
78+
<ClientInstructionCard :internalHFAccessToken="true" />
79+
</v-col>
80+
</v-row>
7681
<v-row v-if="isAdmin">
7782
<v-col>
7883
<RunningWorkerJobs/>
@@ -87,6 +92,7 @@ import {computed, ref} from "vue";
8792
import {UpdateMeDTO} from "@/generated-sources";
8893
import {Role} from "@/enums";
8994
import ApiTokenCard from "@/components/ApiTokenCard.vue";
95+
import ClientInstructionCard from "@/components/ClientInstructionCard.vue";
9096
import RunningWorkerJobs from '@/views/main/profile/RunningWorkerJobs.vue';
9197
9298
import {useUserStore} from "@/stores/user";

0 commit comments

Comments
 (0)