diff --git a/packages/chat/bbs/index.html b/packages/chat/bbs/index.html new file mode 100644 index 000000000..907c972a3 --- /dev/null +++ b/packages/chat/bbs/index.html @@ -0,0 +1,21 @@ + + + + + + Nestia A.I. Chatbot (TypeScript Class Demonstration) + + +
+ + + diff --git a/packages/chat/build/deploy.mjs b/packages/chat/build/deploy.mjs index 3028ac7e6..b2f019fd1 100644 --- a/packages/chat/build/deploy.mjs +++ b/packages/chat/build/deploy.mjs @@ -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); diff --git a/packages/chat/index.html b/packages/chat/index.html index ce5328a31..068abc3ba 100644 --- a/packages/chat/index.html +++ b/packages/chat/index.html @@ -16,6 +16,6 @@
- + diff --git a/packages/chat/package.json b/packages/chat/package.json index 549563895..5967dd638 100644 --- a/packages/chat/package.json +++ b/packages/chat/package.json @@ -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", @@ -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" }, @@ -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", @@ -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", @@ -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" diff --git a/packages/chat/shopping/index.html b/packages/chat/shopping/index.html index 71ca03d5e..ce250f447 100644 --- a/packages/chat/shopping/index.html +++ b/packages/chat/shopping/index.html @@ -16,6 +16,6 @@
- + diff --git a/packages/chat/src/html/bbs.tsx b/packages/chat/src/html/bbs.tsx new file mode 100644 index 000000000..f08af9c03 --- /dev/null +++ b/packages/chat/src/html/bbs.tsx @@ -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(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 ( +
+ {agent ? ( + + ) : ( + + BBS A.I. Chatbot +
+ +
+ Demonstration of Nestia A.I. Chatbot with TypeScript Controller Class. +
+
+ OpenAI Configuration + setApiKey(e.target.value)} + defaultValue={apiKey} + label="OpenAI API Key" + variant="outlined" + placeholder="Your OpenAI API Key" + error={apiKey.length === 0} + /> +
+ setModel(value)} + style={{ paddingLeft: 15 }} + > + } + label="GPT-4o Mini" + value="gpt-4o-mini" + /> + } + label="GPT-4o" + value="gpt-4o" + /> + +
+ Membership Information +
+ setLocale(e.target.value)} + defaultValue={locale} + label="Locale" + variant="outlined" + error={locale.length === 0} + /> +
+
+ +
+ )} +
+ ); +} + +const main = async (): Promise => { + createRoot(window.document.getElementById("root")!).render( + , + ); +}; +main().catch(console.error); diff --git a/packages/chat/src/shopping.tsx b/packages/chat/src/html/shopping.tsx similarity index 98% rename from packages/chat/src/shopping.tsx rename to packages/chat/src/html/shopping.tsx index 0059fe355..04c8b16fc 100644 --- a/packages/chat/src/shopping.tsx +++ b/packages/chat/src/html/shopping.tsx @@ -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(""); diff --git a/packages/chat/src/main.tsx b/packages/chat/src/html/uploader.tsx similarity index 84% rename from packages/chat/src/main.tsx rename to packages/chat/src/html/uploader.tsx index 0da86a0dc..8ecd34988 100644 --- a/packages/chat/src/main.tsx +++ b/packages/chat/src/html/uploader.tsx @@ -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( ; + + /** + * 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); + } +} diff --git a/packages/chat/src/services/IBbsArticle.ts b/packages/chat/src/services/IBbsArticle.ts new file mode 100644 index 000000000..2291b1f0e --- /dev/null +++ b/packages/chat/src/services/IBbsArticle.ts @@ -0,0 +1,61 @@ +import { tags } from "typia"; + +/** + * Article entity. + * + * `IBbsArticle` is an entity representing an article in the BBS (Bulletin Board System). + */ +export interface IBbsArticle extends IBbsArticle.ICreate { + /** + * Primary Key. + */ + id: string & tags.Format<"uuid">; + + /** + * Creation time of the article. + */ + created_at: string & tags.Format<"date-time">; + + /** + * Last updated time of the article. + */ + updated_at: string & tags.Format<"date-time">; +} +export namespace IBbsArticle { + /** + * Information of the article to create. + */ + export interface ICreate { + /** + * Title of the article. + * + * Representative title of the article. + */ + title: string; + + /** + * Content body. + * + * Content body of the article writtn in the markdown format. + */ + body: string; + + /** + * Thumbnail image URI. + * + * Thumbnail image URI which can represent the article. + * + * If configured as `null`, it means that no thumbnail image in the article. + */ + thumbnail: + | null + | (string & tags.Format<"uri"> & tags.ContentMediaType<"image/*">); + } + + /** + * Information of the article to update. + * + * Only the filled properties will be updated. + */ + export type IUpdate = Partial; +} diff --git a/packages/chat/tsconfig.app.json b/packages/chat/tsconfig.app.json index 5a2def4b7..516594c49 100644 --- a/packages/chat/tsconfig.app.json +++ b/packages/chat/tsconfig.app.json @@ -1,7 +1,6 @@ { "compilerOptions": { "target": "ES2020", - "useDefineForClassFields": true, "lib": ["ES2020", "DOM", "DOM.Iterable"], "module": "ESNext", "skipLibCheck": true, diff --git a/packages/chat/tsconfig.app.tsbuildinfo b/packages/chat/tsconfig.app.tsbuildinfo index cadf88b2b..0a97b6682 100644 --- a/packages/chat/tsconfig.app.tsbuildinfo +++ b/packages/chat/tsconfig.app.tsbuildinfo @@ -1 +1 @@ -{"root":["./src/index.ts","./src/main.tsx","./src/shopping.tsx","./src/vite-env.d.ts","./src/applications/nestiachatapplication.tsx","./src/applications/nestiachatuploader.tsx","./src/components/markdownviewer.tsx","./src/movies/nestiachatmovie.tsx","./src/movies/messages/nestiachatdescribemessagemovie.tsx","./src/movies/messages/nestiachatexecutemessagemovie.tsx","./src/movies/messages/nestiachatmessagemovie.tsx","./src/movies/messages/nestiachatselectmessagemovie.tsx","./src/movies/messages/nestiachattextmessagemovie.tsx","./src/movies/sides/nestiachatfunctionstacksidemovie.tsx","./src/movies/sides/nestiachatsidemovie.tsx","./src/movies/sides/nestiachattokenusagesidemovie.tsx","./src/movies/uploader/nestiachatfileuploadmovie.tsx"],"version":"5.7.3"} \ No newline at end of file +{"root":["./src/index.ts","./src/vite-env.d.ts","./src/applications/nestiachatapplication.tsx","./src/applications/nestiachatuploader.tsx","./src/components/markdownviewer.tsx","./src/html/bbs.tsx","./src/html/shopping.tsx","./src/html/uploader.tsx","./src/movies/nestiachatmovie.tsx","./src/movies/messages/nestiachatdescribemessagemovie.tsx","./src/movies/messages/nestiachatexecutemessagemovie.tsx","./src/movies/messages/nestiachatmessagemovie.tsx","./src/movies/messages/nestiachatselectmessagemovie.tsx","./src/movies/messages/nestiachattextmessagemovie.tsx","./src/movies/sides/nestiachatfunctionstacksidemovie.tsx","./src/movies/sides/nestiachatsidemovie.tsx","./src/movies/sides/nestiachattokenusagesidemovie.tsx","./src/movies/uploader/nestiachatfileuploadmovie.tsx","./src/services/bbsarticleservice.ts","./src/services/ibbsarticle.ts"],"version":"5.7.3"} \ No newline at end of file diff --git a/packages/chat/tsconfig.json b/packages/chat/tsconfig.json index 1ffef600d..49dd38172 100644 --- a/packages/chat/tsconfig.json +++ b/packages/chat/tsconfig.json @@ -1,7 +1,20 @@ { + "compilerOptions": { + "strict": true, + "plugins": [ + { + "transform": "typia/lib/transform" + } + ], + "strictNullChecks": true + }, "files": [], "references": [ - { "path": "./tsconfig.app.json" }, - { "path": "./tsconfig.node.json" } + { + "path": "./tsconfig.app.json" + }, + { + "path": "./tsconfig.node.json" + } ] -} +} \ No newline at end of file diff --git a/packages/chat/tsconfig.lib.json b/packages/chat/tsconfig.lib.json index 19a09302e..fa60d5bc8 100644 --- a/packages/chat/tsconfig.lib.json +++ b/packages/chat/tsconfig.lib.json @@ -99,7 +99,10 @@ /* Completeness */ // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ - "skipLibCheck": true /* Skip type checking all .d.ts files. */ + "skipLibCheck": true, /* Skip type checking all .d.ts files. */ + "plugins": [ + { "transform": "typia/lib/transform" }, + ], }, "include": ["./src"], "exclude": ["./src/main.tsx", "./src/shopping.tsx"], diff --git a/packages/chat/tsconfig.node.json b/packages/chat/tsconfig.node.json index 9dad70185..6a3182419 100644 --- a/packages/chat/tsconfig.node.json +++ b/packages/chat/tsconfig.node.json @@ -1,7 +1,8 @@ { "compilerOptions": { - "target": "ES2022", - "lib": ["ES2023"], + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], "module": "ESNext", "skipLibCheck": true, @@ -11,6 +12,7 @@ "isolatedModules": true, "moduleDetection": "force", "noEmit": true, + "jsx": "react-jsx", /* Linting */ "strict": true, @@ -19,5 +21,5 @@ "noFallthroughCasesInSwitch": true, "noUncheckedSideEffectImports": true }, - "include": ["vite.config.ts"] -} + "include": ["src"] +} \ No newline at end of file diff --git a/packages/chat/tsconfig.node.tsbuildinfo b/packages/chat/tsconfig.node.tsbuildinfo index 0440098cb..0a97b6682 100644 --- a/packages/chat/tsconfig.node.tsbuildinfo +++ b/packages/chat/tsconfig.node.tsbuildinfo @@ -1 +1 @@ -{"root":["./vite.config.ts"],"version":"5.7.3"} \ No newline at end of file +{"root":["./src/index.ts","./src/vite-env.d.ts","./src/applications/nestiachatapplication.tsx","./src/applications/nestiachatuploader.tsx","./src/components/markdownviewer.tsx","./src/html/bbs.tsx","./src/html/shopping.tsx","./src/html/uploader.tsx","./src/movies/nestiachatmovie.tsx","./src/movies/messages/nestiachatdescribemessagemovie.tsx","./src/movies/messages/nestiachatexecutemessagemovie.tsx","./src/movies/messages/nestiachatmessagemovie.tsx","./src/movies/messages/nestiachatselectmessagemovie.tsx","./src/movies/messages/nestiachattextmessagemovie.tsx","./src/movies/sides/nestiachatfunctionstacksidemovie.tsx","./src/movies/sides/nestiachatsidemovie.tsx","./src/movies/sides/nestiachattokenusagesidemovie.tsx","./src/movies/uploader/nestiachatfileuploadmovie.tsx","./src/services/bbsarticleservice.ts","./src/services/ibbsarticle.ts"],"version":"5.7.3"} \ No newline at end of file diff --git a/packages/chat/tsconfig.test.json b/packages/chat/tsconfig.test.json deleted file mode 100644 index e6f067419..000000000 --- a/packages/chat/tsconfig.test.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "extends": "./tsconfig.lib.json", - "compilerOptions": { - "target": "ES2015", - "module": "CommonJS", - "outDir": "bin", - "noEmit": true, - }, - "include": ["src", "test"] -} \ No newline at end of file diff --git a/packages/chat/vite.config.ts b/packages/chat/vite.config.ts index bdf6b6ece..6aaf466ef 100644 --- a/packages/chat/vite.config.ts +++ b/packages/chat/vite.config.ts @@ -1,3 +1,4 @@ +import typia from "@ryoppippi/unplugin-typia/vite"; import react from "@vitejs/plugin-react"; import path from "path"; import { fileURLToPath } from "url"; @@ -13,6 +14,7 @@ export default defineConfig({ rollupOptions: { input: { index: path.resolve(__dirname, "index.html"), + "bbs/index": path.resolve(__dirname, "bbs/index.html"), "shopping/index.html": path.resolve(__dirname, "shopping/index.html"), }, }, @@ -25,5 +27,6 @@ export default defineConfig({ return html.replace(`crossorigin`, ""); }, }, + typia(), ], });