Skip to content

Commit

Permalink
feat: adding create bot functionality
Browse files Browse the repository at this point in the history
Signed-off-by: James <[email protected]>
  • Loading branch information
James committed Oct 19, 2023
1 parent 1f40c26 commit 2de25cb
Show file tree
Hide file tree
Showing 80 changed files with 1,480 additions and 528 deletions.
32 changes: 32 additions & 0 deletions plugins/data-plugin/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ function findMany({
function onStart() {
createCollection({ name: "conversations", schema: {} });
createCollection({ name: "messages", schema: {} });
createCollection({ name: "bots", schema: {} });
}

// Register all the above functions and objects with the relevant extension points
Expand All @@ -151,12 +152,23 @@ export function init({ register }: { register: RegisterExtensionPoint }) {
register(DataService.DeleteConversation, deleteConversation.name, deleteConversation);
register(DataService.CreateMessage, createMessage.name, createMessage);
register(DataService.GetConversationMessages, getConversationMessages.name, getConversationMessages);

register("getConversationById", getConversationById.name, getConversationById);
register("createBot", createBot.name, createBot);
register("getBots", getBots.name, getBots);
register("getBotById", getBotById.name, getBotById);
register("deleteBot", deleteBot.name, deleteBot);
register("updateBot", updateBot.name, updateBot);
}

function getConversations(): Promise<any> {
return store.findMany("conversations", {}, [{ updatedAt: "desc" }]);
}

function getConversationById(id: string): Promise<any> {
return store.findOne("conversations", id);
}

function createConversation(conversation: any): Promise<number | undefined> {
return store.insertOne("conversations", conversation);
}
Expand All @@ -180,3 +192,23 @@ function deleteConversation(id: any) {
function getConversationMessages(conversationId: any) {
return store.findMany("messages", { conversationId }, [{ createdAt: "desc" }]);
}

function createBot(bot: any): Promise<void> {
return store.insertOne("bots", bot);
}

function getBots(): Promise<any> {
return store.findMany("bots", {});
}

function deleteBot(id: any): Promise<any> {
return store.deleteOne("bots", id);
}

function updateBot(bot: any): Promise<void> {
return store.updateOne("bots", bot._id, bot);
}

function getBotById(botId: string): Promise<any> {
return store.findOne("bots", botId);
}
8 changes: 6 additions & 2 deletions plugins/data-plugin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,12 @@
"node_modules"
],
"dependencies": {
"@janhq/core": "^0.1.3",
"@janhq/core": "file:../../core", // revert back this line to the original one
"pouchdb-find": "^8.0.1",
"pouchdb-node": "^8.0.1"
}
},
"bundleDependencies": [
"pouchdb-node",
"pouchdb-find"
]
}
33 changes: 25 additions & 8 deletions plugins/inference-plugin/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,16 +58,33 @@ function requestInference(recentMessages: any[]): Observable<string> {

async function retrieveLastTenMessages(conversationId: string) {
// TODO: Common collections should be able to access via core functions instead of store
const conversation = await store.findOne("conversations", conversationId);
let bot = undefined;
if (conversation.botId != null) {
bot = await store.findOne("bots", conversation.botId);
}

const messageHistory = (await store.findMany("messages", { conversationId }, [{ createdAt: "asc" }])) ?? [];
return messageHistory

let recentMessages = messageHistory
.filter((e) => e.message !== "" && (e.user === "user" || e.user === "assistant"))
.slice(-10)
.map((message) => {
return {
content: message.message.trim(),
role: message.user === "user" ? "user" : "assistant",
};
});
.slice(-9)
.map((message) => ({
content: message.message.trim(),
role: message.user === "user" ? "user" : "assistant",
}));

if (bot && bot.systemPrompt) {
// append bot's system prompt
recentMessages = [{
content: `[INST] ${bot.systemPrompt}`,
role: 'system'
},...recentMessages];
}

console.debug(`Sending: ${JSON.stringify(recentMessages)}`);

return recentMessages;
}

async function handleMessageRequest(data: NewMessageRequest) {
Expand Down
19 changes: 19 additions & 0 deletions web/app/_components/Avatar/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React, { Fragment } from "react";
import SecondaryButton from "../SecondaryButton";

const Avatar: React.FC = () => (
<div className="mx-auto flex flex-col gap-5">
<span className="inline-block h-14 w-14 overflow-hidden rounded-full bg-gray-100 mx-auto">
<svg
className="h-full w-full text-gray-300 mx-auto"
fill="currentColor"
viewBox="0 0 24 24"
>
<path d="M24 20.993V24H0v-2.996A14.977 14.977 0 0112.004 15c4.904 0 9.26 2.354 11.996 5.993zM16.002 8.999a4 4 0 11-8 0 4 4 0 018 0z" />
</svg>
</span>
<SecondaryButton title={"Edit picture"} />
</div>
);

export default Avatar;
54 changes: 54 additions & 0 deletions web/app/_components/BotInfo/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { activeBotAtom } from "@/_helpers/atoms/Bot.atom";
import {
MainViewState,
setMainViewStateAtom,
} from "@/_helpers/atoms/MainView.atom";
import useCreateConversation from "@/_hooks/useCreateConversation";
import useDeleteBot from "@/_hooks/useDeleteBot";
import { useAtomValue, useSetAtom } from "jotai";
import React from "react";

const BotInfo: React.FC = () => {
const { deleteBot } = useDeleteBot();
const { createConvoByBot } = useCreateConversation();
const setMainView = useSetAtom(setMainViewStateAtom);
const botInfo = useAtomValue(activeBotAtom);
if (!botInfo) return null;

const onNewChatClicked = () => {
if (!botInfo) {
alert("No bot selected");
return;
}

createConvoByBot(botInfo);
};

const onDeleteBotClick = async () => {
// TODO: display confirmation diaglog
const result = await deleteBot(botInfo._id);
if (result === "success") {
setMainView(MainViewState.Welcome);
}
};

return (
<div className="flex flex-col">
{/* Header */}
<div>Bot Info</div>

{/* Body */}
<div className="flex flex-col">
<label>{botInfo.name}</label>
<button onClick={onNewChatClicked}>New chat</button>
<span>{botInfo.description}</span>
</div>

<div role="button" onClick={onDeleteBotClick}>
<span>Delete bot</span>
</div>
</div>
);
};

export default BotInfo;
78 changes: 78 additions & 0 deletions web/app/_components/BotListModal/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { activeBotAtom } from "@/_helpers/atoms/Bot.atom";
import { showingBotListModalAtom } from "@/_helpers/atoms/Modal.atom";
import useGetBots from "@/_hooks/useGetBots";
import { Bot } from "@/_models/Bot";
import { Dialog, Transition } from "@headlessui/react";
import { useAtom } from "jotai";
import React, { Fragment, useEffect, useState } from "react";

const BotListModal: React.FC = () => {
const [open, setOpen] = useAtom(showingBotListModalAtom);
const [bots, setBots] = useState<Bot[]>([]);
const [activeBot, setActiveBot] = useAtom(activeBotAtom);
const { getAllBots } = useGetBots();

useEffect(() => {
getAllBots().then((res) => {
setBots(res);
});
}, [open]);

const onBotSelected = (bot: Bot) => {
if (bot._id !== activeBot?._id) {
setActiveBot(bot);
}
setOpen(false);
};

return (
<Transition.Root show={open} as={Fragment}>
<Dialog as="div" className="relative z-10" onClose={setOpen}>
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="ease-in duration-200"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" />
</Transition.Child>

<div className="fixed inset-0 z-10 w-screen overflow-y-auto">
<div className="flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
enterTo="opacity-100 translate-y-0 sm:scale-100"
leave="ease-in duration-200"
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
>
<Dialog.Panel className="relative transform overflow-hidden rounded-lg bg-white px-4 pb-4 pt-5 text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-sm sm:p-6">
<div className="overflow-hidden bg-white shadow sm:rounded-md">
<ul role="list" className="divide-y divide-gray-200">
{bots.map((bot) => (
<li
role="button"
key={bot._id}
className="px-4 py-4 sm:px-6"
onClick={() => onBotSelected(bot)}
>
<p>{bot.name}</p>
</li>
))}
</ul>
</div>
</Dialog.Panel>
</Transition.Child>
</div>
</div>
</Dialog>
</Transition.Root>
);
};

export default BotListModal;
26 changes: 26 additions & 0 deletions web/app/_components/BotPreview/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import Image from "next/image";

const BotPreview: React.FC = () => {
return (
<div className="flex pb-2 flex-col border border-gray-400 min-h-[235px] gap-2 overflow-hidden rounded-lg">
<div className="flex items-center justify-center p-2 bg-gray-400">
<Image
className="rounded-md"
src={
"https://i.pinimg.com/564x/52/b1/6f/52b16f96f52221d48bea716795ccc89a.jpg"
}
width={32}
height={32}
alt=""
/>
</div>
<div className="flex items-center text-xs text-gray-400 gap-1 px-1">
<div className="flex-grow mx-1 border-b border-gray-400"></div>
Context cleared
<div className="flex-grow mx-1 border-b border-gray-400"></div>
</div>
</div>
);
};

export default BotPreview;
79 changes: 79 additions & 0 deletions web/app/_components/BotSetting/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { activeBotAtom } from "@/_helpers/atoms/Bot.atom";
import { useAtomValue } from "jotai";
import React from "react";
import ExpandableHeader from "../ExpandableHeader";
import { useDebouncedCallback } from "use-debounce";
import useUpdateBot from "@/_hooks/useUpdateBot";
import { formatTwoDigits } from "@/_utils/converter";

const delayBeforeUpdateInMs = 1000;

const BotSetting: React.FC = () => {
const activeBot = useAtomValue(activeBotAtom);
const { updateBot } = useUpdateBot();

const debouncedTemperature = useDebouncedCallback((value) => {
if (!activeBot) return;
if (activeBot.customTemperature === value) return;
updateBot(activeBot, { customTemperature: value });
}, delayBeforeUpdateInMs);

const debouncedSystemPrompt = useDebouncedCallback((value) => {
if (!activeBot) return;
if (activeBot.systemPrompt === value) return;
updateBot(activeBot, { systemPrompt: value });
}, delayBeforeUpdateInMs);

if (!activeBot) return null;

return (
<div className="flex flex-col my-3">
<ExpandableHeader
title="BOT SETTINGS"
expanded={true}
onClick={() => {}}
/>

<div className="flex flex-col mx-2 flex-shrink-0 gap-4 mt-3">
{/* System prompt */}
<div>
<label
htmlFor="comment"
className="block text-sm font-medium leading-6 text-gray-900"
>
System prompt
</label>
<div className="mt-2">
<textarea
rows={4}
name="comment"
id="comment"
className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
defaultValue={activeBot.systemPrompt}
onChange={(e) => debouncedSystemPrompt(e.target.value)}
/>
</div>
</div>

{/* Custom temp */}
<div className="flex items-center gap-2 mt-2">
<input
className="flex-1"
type="range"
id="volume"
name="volume"
min="0"
max="1"
step="0.01"
onChange={(e) => debouncedTemperature(e.target.value)}
/>
{/* <span className="border border-[#737d7d] rounded-md py-1 px-2 text-gray-900">
{formatTwoDigits(value)}
</span> */}
</div>
</div>
</div>
);
};

export default BotSetting;
12 changes: 12 additions & 0 deletions web/app/_components/CenterContainer/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import React from "react";
import MainHeader from "../MainHeader";
import MainView from "../MainView";

const CenterContainer: React.FC = () => (
<div className="flex-1 flex flex-col">
<MainHeader />
<MainView />
</div>
);

export default React.memo(CenterContainer);
Loading

0 comments on commit 2de25cb

Please sign in to comment.