Skip to content

Commit

Permalink
Merge pull request #1209 from samchon/feat/chat-bbs
Browse files Browse the repository at this point in the history
Add a new A.I. chabot example composed by TypeScript controller class instead of HTTP server.
  • Loading branch information
samchon authored Jan 28, 2025
2 parents faad136 + b481adf commit 85ed1a2
Show file tree
Hide file tree
Showing 18 changed files with 362 additions and 36 deletions.
21 changes: 21 additions & 0 deletions packages/chat/bbs/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta
name="viewport"
content="width=device-width, height=device-height, initial-scale=1, minimum-scale=1.0, maximum-scale=3.0s"
/>
<script type="text/javascript">
(function(c,l,a,r,i,t,y){
c[a]=c[a]||function(){(c[a].q=c[a].q||[]).push(arguments)};
t=l.createElement(r);t.async=1;t.src="https://www.clarity.ms/tag/"+i;
y=l.getElementsByTagName(r)[0];y.parentNode.insertBefore(t,y);
})(window, document, "clarity", "script", "gu5qz1srj3");
</script>
<title>Nestia A.I. Chatbot (TypeScript Class Demonstration)</title>
</head>
<body style="width: 100%; height: 100%; margin: 0px; overflow: hidden;">
<div id="root" style="width: 100%; height: 100%"></div>
<script type="module" src="../src/html/bbs.tsx"></script>
</body>
</html>
20 changes: 12 additions & 8 deletions packages/chat/build/deploy.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,18 @@ const main = async () => {
cwd: `${__dirname}/..`,
stdio: "inherit",
});
execute("npm run build");
execute(`npm publish --tag ${tag}`);

await fs.promises.writeFile(
`${__dirname}/../package.json`,
JSON.stringify(packageJson, null, 2),
"utf8",
);
try {
execute("npm run build");
execute(`npm publish --tag ${tag}`);
} catch (error) {
throw error;
} finally {
await fs.promises.writeFile(
`${__dirname}/../package.json`,
JSON.stringify(packageJson, null, 2),
"utf8",
);
}
};
main().catch((error) => {
console.log(error);
Expand Down
2 changes: 1 addition & 1 deletion packages/chat/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@
</head>
<body style="width: 100%; height: 100%; margin: 0px; overflow: hidden;">
<div id="root" style="width: 100%; height: 100%"></div>
<script type="module" src="./src/main.tsx"></script>
<script type="module" src="./src/html/uploader.tsx"></script>
</body>
</html>
10 changes: 7 additions & 3 deletions packages/chat/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@nestia/chat",
"version": "0.3.4",
"version": "0.3.5",
"type": "module",
"main": "./lib/index.mjs",
"typings": "./lib/index.d.ts",
Expand All @@ -11,6 +11,7 @@
"build:lib": "rimraf lib && tsc --project tsconfig.lib.json --emitDeclarationOnly && rollup -c",
"dev": "vite",
"lint": "eslint .",
"prepare": "ts-patch install",
"preview": "vite preview",
"deploy": "node build/deploy.mjs"
},
Expand Down Expand Up @@ -45,7 +46,8 @@
"rehype-raw": "^7.0.0",
"rehype-stringify": "^10.0.1",
"remark-mermaid-plugin": "^1.0.2",
"typia": "^7.6.0"
"typia": "^7.6.0",
"uuid": "^11.0.5"
},
"devDependencies": {
"@eslint/js": "^9.13.0",
Expand All @@ -55,12 +57,14 @@
"@nestjs/platform-fastify": "^11.0.2",
"@rollup/plugin-terser": "^0.4.4",
"@rollup/plugin-typescript": "^12.1.1",
"@ryoppippi/unplugin-typia": "^1.2.0",
"@samchon/shopping-api": "^0.14.1",
"@types/js-yaml": "^4.0.9",
"@types/node": "^22.10.5",
"@types/react": "^18.3.11",
"@types/react-dom": "^18.3.1",
"@types/react-json-editor-ajrm": "^2.5.6",
"@types/uuid": "^10.0.0",
"@vitejs/plugin-react": "^4.3.3",
"eslint": "^9.13.0",
"eslint-plugin-react-hooks": "^5.0.0",
Expand All @@ -74,7 +78,7 @@
"react-mui-fileuploader": "^0.5.2",
"rimraf": "^6.0.1",
"rollup": "^4.24.2",
"ts-node": "^10.9.2",
"ts-patch": "^3.3.0",
"typescript": "~5.7.2",
"typescript-eslint": "^8.10.0",
"vite": "^5.4.9"
Expand Down
2 changes: 1 addition & 1 deletion packages/chat/shopping/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@
</head>
<body style="width: 100%; height: 100%; margin: 0px; overflow: hidden;">
<div id="root" style="width: 100%; height: 100%"></div>
<script type="module" src="../src/shopping.tsx"></script>
<script type="module" src="../src/html/shopping.tsx"></script>
</body>
</html>
143 changes: 143 additions & 0 deletions packages/chat/src/html/bbs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
import {
Button,
Divider,
FormControl,
FormControlLabel,
Radio,
RadioGroup,
TextField,
Typography,
} from "@mui/material";
import { NestiaAgent } from "@nestia/agent";
import OpenAI from "openai";
import { useState } from "react";
import { createRoot } from "react-dom/client";
import typia from "typia";

import { NestiaChatApplication } from "../applications/NestiaChatApplication";
import { BbsArticleService } from "../services/BbsArticleService";

function BbsChatApplication() {
const [apiKey, setApiKey] = useState("");
const [model, setModel] = useState("gpt-4o-mini");
const [locale, setLocale] = useState(window.navigator.language);

const [agent, setAgent] = useState<NestiaAgent | null>(null);

const startChatApplication = (): void => {
const service: BbsArticleService = new BbsArticleService();
const agent: NestiaAgent = new NestiaAgent({
provider: {
type: "chatgpt",
api: new OpenAI({
apiKey,
dangerouslyAllowBrowser: true,
}),
model: "gpt-4o-mini",
},
controllers: [
{
protocol: "class",
name: "bbs",
application: typia.llm.applicationOfValidate<
BbsArticleService,
"chatgpt"
>(),
execute: async (props) => {
return (service as any)[props.function.name](props.arguments);
},
},
],
config: {
locale,
},
});
setAgent(agent);
};

return (
<div
style={{
width: "100%",
height: "100%",
overflow: agent ? undefined : "auto",
}}
>
{agent ? (
<NestiaChatApplication agent={agent} />
) : (
<FormControl
style={{
width: "calc(100% - 60px)",
padding: 15,
margin: 15,
}}
>
<Typography variant="h6">BBS A.I. Chatbot</Typography>
<br />
<Divider />
<br />
Demonstration of Nestia A.I. Chatbot with TypeScript Controller Class.
<br />
<br />
<Typography variant="h6"> OpenAI Configuration </Typography>
<TextField
onChange={(e) => setApiKey(e.target.value)}
defaultValue={apiKey}
label="OpenAI API Key"
variant="outlined"
placeholder="Your OpenAI API Key"
error={apiKey.length === 0}
/>
<br />
<RadioGroup
defaultValue={model}
onChange={(_e, value) => setModel(value)}
style={{ paddingLeft: 15 }}
>
<FormControlLabel
control={<Radio />}
label="GPT-4o Mini"
value="gpt-4o-mini"
/>
<FormControlLabel
control={<Radio />}
label="GPT-4o"
value="gpt-4o"
/>
</RadioGroup>
<br />
<Typography variant="h6"> Membership Information </Typography>
<br />
<TextField
onChange={(e) => setLocale(e.target.value)}
defaultValue={locale}
label="Locale"
variant="outlined"
error={locale.length === 0}
/>
<br />
<br />
<Button
component="a"
fullWidth
variant="contained"
color={"info"}
size="large"
disabled={apiKey.length === 0 || locale.length === 0}
onClick={() => startChatApplication()}
>
Start A.I. Chatbot
</Button>
</FormControl>
)}
</div>
);
}

const main = async (): Promise<void> => {
createRoot(window.document.getElementById("root")!).render(
<BbsChatApplication />,
);
};
main().catch(console.error);
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import OpenAI from "openai";
import { useState } from "react";
import { createRoot } from "react-dom/client";

import { NestiaChatApplication } from "./applications/NestiaChatApplication";
import { NestiaChatApplication } from "../applications/NestiaChatApplication";

function ShoppingChatApplication() {
const [apiKey, setApiKey] = useState("");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { createRoot } from "react-dom/client";

import { NestiaChatUploader } from "./applications/NestiaChatUploader";
import { NestiaChatUploader } from "../applications/NestiaChatUploader";

createRoot(window.document.getElementById("root")!).render(
<NestiaChatUploader
Expand Down
83 changes: 83 additions & 0 deletions packages/chat/src/services/BbsArticleService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { tags } from "typia";
import { v4 } from "uuid";

import { IBbsArticle } from "./IBbsArticle";

export class BbsArticleService {
private readonly articles: IBbsArticle[] = [];

/**
* Create a new article.
*
* Writes a new article and archives it into the DB.
*
* @param props Properties of create function
* @returns Newly created article
*/
public create(props: {
/**
* Information of the article to create
*/
input: IBbsArticle.ICreate;
}): IBbsArticle {
const article: IBbsArticle = {
id: v4(),
title: props.input.title,
body: props.input.body,
thumbnail: props.input.thumbnail,
created_at: new Date().toISOString(),
updated_at: new Date().toISOString(),
};
this.articles.push(article);
return article;
}

/**
* Update an article.
*
* Updates an article with new content.
*
* @param props Properties of update function
* @param input New content to update
*/
public update(props: {
/**
* Target article's {@link IBbsArticle.id}.
*/
id: string & tags.Format<"uuid">;

/**
* New content to update.
*/
input: IBbsArticle.IUpdate;
}): void {
const article: IBbsArticle | undefined = this.articles.find(
(a) => a.id === props.id,
);
if (article === undefined)
throw new Error("Unable to find the matched article.");
if (props.input.title !== undefined) article.title = props.input.title;
if (props.input.body !== undefined) article.body = props.input.body;
if (props.input.thumbnail !== undefined)
article.thumbnail = props.input.thumbnail;
article.updated_at = new Date().toISOString();
}

/**
* Erase an article.
*
* Erases an article from the DB.
*
* @param props Properties of erase function
*/
public erase(props: {
/**
* Target article's {@link IBbsArticle.id}.
*/
id: string & tags.Format<"uuid">;
}): void {
const index: number = this.articles.findIndex((a) => a.id === props.id);
if (index === -1) throw new Error("Unable to find the matched article.");
this.articles.splice(index, 1);
}
}
Loading

0 comments on commit 85ed1a2

Please # to comment.