Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: installer populates vector store #76

Merged
merged 11 commits into from Mar 14, 2024
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

100 changes: 69 additions & 31 deletions src/client/pages/[locale]/installer/02.tsx
Expand Up @@ -6,6 +6,7 @@
import Typography from "@mui/joy/Typography";
import type { Progress } from "electron-dl";
import type { InferGetStaticPropsType } from "next";
import { useRouter } from "next/router";
import { useTranslation } from "next-i18next";
import type { ReactNode } from "react";
import { useCallback, useEffect, useState } from "react";
Expand All @@ -18,6 +19,10 @@
import { QuoteLoop } from "@/organisms/quote-loop";

function useInstallProgress() {
const {
i18n: { language: locale },

Check warning on line 23 in src/client/pages/[locale]/installer/02.tsx

View workflow job for this annotation

GitHub Actions / test-e2e (18.x)

'locale' is assigned a value but never used. Allowed unused vars must match /^_/u
} = useTranslation();
const router = useRouter();

Check warning on line 25 in src/client/pages/[locale]/installer/02.tsx

View workflow job for this annotation

GitHub Actions / test-e2e (18.x)

'router' is assigned a value but never used. Allowed unused vars must match /^_/u
const [status, setStatus] = useState(DownloadState.IDLE);
const [progress, setProgress] = useState<Progress>({
percent: 0,
Expand Down Expand Up @@ -56,7 +61,6 @@
buildKey([ID.INSTALL], { suffix: ":completed" }),
() => {
setStatus(DownloadState.DONE);
window.ipc.send(buildKey([ID.APP], { suffix: ":ready" }), true);
}
);
const unsubscribeUnpacking = window.ipc.on(
Expand Down Expand Up @@ -179,6 +183,28 @@
);
}

case DownloadState.DONE: {
return (
<InstallStep
heading={t("labels:installtionSuccess")}
illustration="/illustrations/minimalistic/discovery.svg"
>
<Box
sx={{
flex: 1,
position: "relative",
display: "flex",
alignItems: "center",
}}
>
<Typography level="body-lg" sx={{ my: 2, textAlign: "center" }}>
{t("texts:unpackingSuccess")}
</Typography>
</Box>
</InstallStep>
);
}

default: {
return null;
}
Expand All @@ -200,36 +226,48 @@
{t("common:previous")}
</Button>
</I18nLink>
<Button
disabled={status !== DownloadState.IDLE}
data-testid="installer-02-start"
onClick={() => {
reset();

window.ipc.send(buildKey([ID.INSTALL], { suffix: ":start" }), [
{
url: "https://blibla-captain-assets.s3.eu-central-1.amazonaws.com/python-embedded-win.7z",
destination: "python-embedded",
size: "2.1 GB",
unzip: true,
},
{
url: "https://blibla-captain-assets.s3.eu-central-1.amazonaws.com/portable-git-win.7z",
destination: "portable-git",
size: "86.9 MB",
unzip: true,
},
{
url: "https://blibla-captain-assets.s3.eu-central-1.amazonaws.com/qdrant-win.7z",
destination: "qdrant",
size: "13.9 MB",
unzip: true,
},
]);
}}
>
{t("installer:install")}
</Button>
{status === DownloadState.DONE ? (
<I18nLink href="/installer/03">
<Button component="a">{t("common:next")}</Button>
</I18nLink>
) : (
<Button
disabled={status !== DownloadState.IDLE}
data-testid="installer-02-start"
onClick={() => {
reset();

window.ipc.send(buildKey([ID.INSTALL], { suffix: ":start" }), [
{
url: "https://blibla-captain-assets.s3.eu-central-1.amazonaws.com/python-embedded-win.7z",
destination: "python-embedded",
size: "2.1 GB",
unzip: true,
},
{
url: "https://blibla-captain-assets.s3.eu-central-1.amazonaws.com/portable-git-win.7z",
destination: "portable-git",
size: "86.9 MB",
unzip: true,
},
{
url: "https://blibla-captain-assets.s3.eu-central-1.amazonaws.com/all-MiniLM-L6-v2.7z",
destination: "downloads/llm/embeddings/Xenova",
size: "15.3 MB",
unzip: true,
},
{
url: "https://blibla-captain-assets.s3.eu-central-1.amazonaws.com/qdrant-win.7z",
destination: "qdrant",
size: "13.9 MB",
unzip: true,
},
]);
}}
>
{t("installer:install")}
</Button>
)}
</Box>
</Box>
</Box>
Expand Down
154 changes: 154 additions & 0 deletions src/client/pages/[locale]/installer/03.tsx
@@ -0,0 +1,154 @@
import { AppFrame } from "@captn/joy/app-frame";
import { TitleBar } from "@captn/joy/title-bar";
import Box from "@mui/joy/Box";
import Button from "@mui/joy/Button";
import CircularProgress from "@mui/joy/CircularProgress";
import Input from "@mui/joy/Input";
import Typography from "@mui/joy/Typography";
import type { InferGetStaticPropsType } from "next";
import { useTranslation } from "next-i18next";
import { useEffect, useState } from "react";

import { buildKey } from "#/build-key";
import { ID } from "#/enums";
import { Captain } from "@/atoms/logo/captain";
import { makeStaticProperties } from "@/ions/i18n/get-static";

function DummyPrompt() {
const { t } = useTranslation(["labels", "texts"]);
return (
<Box
sx={{
position: "relative",
fontSize: 24,
my: "1em",
mx: 1,
overflow: "hidden",
boxShadow: "md",
}}
>
<Input
readOnly
placeholder={t("labels:placeholder.prompt")}
endDecorator={
<Box
sx={{
flex: 1,
display: "flex",
alignItems: "center",
justifyContent: "center",
height: 44,
width: 44,
}}
>
<Captain sx={{ height: "100%" }} />
</Box>
}
sx={{
fontSize: "inherit",
lineHeight: 1.25,
p: 2,
flexDirection: "row",
}}
/>
</Box>
);
}

export default function Page(_properties: InferGetStaticPropsType<typeof getStaticProps>) {
const { t } = useTranslation(["labels", "texts"]);
const [loading, setLoading] = useState(true);
const [starting, setStarting] = useState(false);

useEffect(() => {
const unsubscribeInitialized = window.ipc.on(
buildKey([ID.INSTALL], { suffix: ":initialized" }),
() => {
setLoading(false);
}
);

const unsubscribeError = window.ipc.on(buildKey([ID.INSTALL], { suffix: ":error" }), () => {
setLoading(true);
});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why does an error set loading to true?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was changed to setLoading(false) and I added error handling to show the actual problem.


window.ipc.send(buildKey([ID.INSTALL], { suffix: ":initialize" }));

return () => {
unsubscribeInitialized();
unsubscribeError();
};
}, []);

return (
<AppFrame titleBar={<TitleBar disableMaximize />}>
<Box sx={{ minHeight: "100%", display: "flex", flexDirection: "column" }}>
<Box sx={{ height: 200, alignContent: "center", px: 2 }}>
<DummyPrompt />
</Box>

<Typography level="h1" sx={{ my: 2, textAlign: "center" }}>
{t("labels:finishing")}
</Typography>

<Box
sx={{
flex: 1,
display: "flex",
flexDirection: "column",
justifyContent: "center",
mx: 8,
}}
>
<Box
sx={{
display: "flex",
p: 2,
backgroundColor: "background.default",
borderRadius: "8px",
flexDirection: "column",
}}
>
<Typography level="body-lg" sx={{ my: 2, textAlign: "center" }}>
{t("texts:howToUseCaptain")}
</Typography>

<Typography level="body-lg" sx={{ my: 2, textAlign: "center" }}>
{loading ? t("texts:preparation") : t("texts:preparationDone")}
</Typography>
</Box>
</Box>

<Box sx={{ display: "flex", justifyContent: "flex-end", m: 1 }}>
<Box sx={{ display: "flex", gap: 1 }}>
<Button
endDecorator={
loading || starting ? <CircularProgress size="sm" /> : null
}
onClick={() => {
setStarting(true);
window.ipc.send(buildKey([ID.APP], { suffix: ":ready" }), true);
}}
>
{(() => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no self calling functions. please use a let assignment before the return

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was updated!

if (loading) {
return t("labels:preparation");
}

if (starting) {
return t("labels:starting");
}

return t("labels:start");
})()}
</Button>
</Box>
</Box>
</Box>
</AppFrame>
);
}

export const getStaticProps = makeStaticProperties(["common", "installer", "texts", "labels"]);

export { getStaticPaths } from "@/ions/i18n/get-static";
9 changes: 7 additions & 2 deletions src/client/public/locales/de/labels.json
Expand Up @@ -46,7 +46,8 @@
"overlay": "Überlagerung",
"placeholder": {
"characters": "Der Name des Jungen ist Erin und sein Hund heißt Ruffy",
"customStyle": "Die Geschichte sollte skurril und sehr lustig sein, ein Mysterium ohne Abschluss beinhalten"
"customStyle": "Die Geschichte sollte skurril und sehr lustig sein, ein Mysterium ohne Abschluss beinhalten",
"prompt": "Ich möchte ein Bild malen"
},
"privacy": "Datenschutz",
"prompt": "Prompt",
Expand All @@ -69,5 +70,9 @@
"sciFi": "Science-Fiction"
},
"submitButtonText": "Absenden",
"unpacking": "Entpacken"
"unpacking": "Entpacken",
"finishing": "Abschluss",
"starting": "Captain wird gestartet",
"preparation": "Captain wird vorbereitet",
"installtionSuccess": "Installation fertig"
}
6 changes: 5 additions & 1 deletion src/client/public/locales/de/texts.json
Expand Up @@ -5,5 +5,9 @@
"quote3": "Wenn du denkst, eine Minute vergeht wirklich schnell, warst du noch nie in den 'letzten Sekunden' eines Downloads.",
"quote4": "Mit KI zu arbeiten ist wie einen Freund zu haben, der fast alles weiß, aber immer noch nicht seine Schuhe binden kann.",
"quote5": "Ich bat eine KI um einen Witz. Sie sagte: 'Ich könnte dir einen UDP-Witz erzählen, aber du verstehst ihn vielleicht nicht.'",
"storyFormIntroduction": "Willkommen zu unserem Tool zur Geschichtsanpassung! Nachdem du deine Visionen mit unserem KI-gestützten Malwerkzeug zum Leben erweckt hast, ist es nun an der Zeit, Geschichten um deine Kreationen zu weben. Wähle unten deine Präferenzen, um die Geschichte zu formen, die am besten zu deinem Meisterwerk passt. Ob du eine kurze und süße Erzählung wünschst, eine mittellange Reise ins Fantastische oder einen tiefen Tauchgang in die Reiche des Unbekannten, wir sind hier, um deine Geschichte zum Leben zu erwecken. Wähle deinen Erzählstil, setze den emotionalen Ton und lass uns gemeinsam eine magische Geschichte erschaffen."
"storyFormIntroduction": "Willkommen zu unserem Tool zur Geschichtsanpassung! Nachdem du deine Visionen mit unserem KI-gestützten Malwerkzeug zum Leben erweckt hast, ist es nun an der Zeit, Geschichten um deine Kreationen zu weben. Wähle unten deine Präferenzen, um die Geschichte zu formen, die am besten zu deinem Meisterwerk passt. Ob du eine kurze und süße Erzählung wünschst, eine mittellange Reise ins Fantastische oder einen tiefen Tauchgang in die Reiche des Unbekannten, wir sind hier, um deine Geschichte zum Leben zu erwecken. Wähle deinen Erzählstil, setze den emotionalen Ton und lass uns gemeinsam eine magische Geschichte erschaffen.",
"howToUseCaptain": "Captain kannst du ganz einfach über den Prompt steuern, welchen du mit der Tastenkombination Strg + Alt + Leertaste jederzeit öffnen kannst. Schreibe dann einfach, was du machen möchtest und wir erledigen den Rest.",
"preparation": "Damit der Prompt funktioniert, bereiten wir noch kurz alles vor.",
"preparationDone": "Alle Vorbereitungen sind abgeschlossen! Viel Spaß mit Captain.",
"unpackingSuccess": "Super, alles wurde installiert. Du hast es fast geschafft."
}
18 changes: 17 additions & 1 deletion src/electron/future/ipc/install.ts
Expand Up @@ -6,6 +6,7 @@ import { DownloadState, ID } from "#/enums";
import { appSettingsStore } from "@/stores";
import { getCaptainData, getCaptainDownloads, getDirectory } from "@/utils/path-helpers";
import { unpack } from "@/utils/unpack";
import { initialize, populateFromDocuments, reset } from "@/utils/vector-store";

ipcMain.on(
buildKey([ID.INSTALL], { suffix: ":start" }),
Expand Down Expand Up @@ -51,7 +52,8 @@ ipcMain.on(
unpack(
getDirectory("7zip", "win", "7za.exe"),
file.path,
targetPath
targetPath,
true
)
);
}
Expand All @@ -63,6 +65,7 @@ ipcMain.on(
window_.webContents.send(buildKey([ID.INSTALL], { suffix: ":unpacking" }), true);
appSettingsStore.set("status", DownloadState.UNPACKING);
await Promise.all(items);

// Everything was downloaded & unpacked
window_.webContents.send(buildKey([ID.INSTALL], { suffix: ":completed" }), true);
appSettingsStore.set("status", DownloadState.DONE);
Expand All @@ -77,3 +80,16 @@ ipcMain.on(
}
}
);

ipcMain.on(buildKey([ID.INSTALL], { suffix: ":initialize" }), async event => {
try {
// Start the vector store and fill it with data
await initialize();
await reset();
await populateFromDocuments();

event.sender.send(buildKey([ID.INSTALL], { suffix: ":initialized" }), true);
} catch (error) {
event.sender.send(buildKey([ID.INSTALL], { suffix: ":error" }), error);
}
});