void;
constructor() {
- this.processing = false;
+ this.processing = false;
+ this.connection = null;
+ this.message = "";
+ }
+
+ public setProcessingChangeHandler(
+ handler: (processing: boolean) => void,
+ ): void {
+ this.onProcessingChange = handler;
+ }
+
+ public setMessageHandler(handler: (message: MessageEvent) => void): void {
+ this.onMessage = handler;
+ }
+
+ public setErrorHandler(handler: (error: string) => void): void {
+ this.onError = handler;
+ }
+
+ public setFinishedHandler(handler: (hash: string) => void): void {
+ this.onFinished = handler;
+ }
+
+ public isProcessing(): boolean {
+ return this.processing;
+ }
+
+ protected setProcessing(processing: boolean): boolean {
+ this.processing = processing;
+ if (!processing) {
this.connection = null;
this.message = "";
}
+ this.onProcessingChange?.(processing);
+ return processing;
+ }
- public setProcessingChangeHandler(handler: (processing: boolean) => void): void {
- this.onProcessingChange = handler;
+ public getConnection(): WebSocket | null {
+ return this.connection;
+ }
+
+ protected handleMessage(message: GenerationSegmentResponse): void {
+ if (message.error && message.end) {
+ this.onError?.(message.error);
+ this.setProcessing(false);
+ return;
}
- public setMessageHandler(handler: (message: MessageEvent) => void): void {
- this.onMessage = handler;
- }
+ this.message += message.message;
+ this.onMessage?.({
+ message: this.message,
+ quota: message.quota,
+ });
- public setErrorHandler(handler: (error: string) => void): void {
- this.onError = handler;
+ if (message.end) {
+ this.onFinished?.(message.hash);
+ this.setProcessing(false);
}
+ }
- public setFinishedHandler(handler: (hash: string) => void): void {
- this.onFinished = handler;
- }
-
- public isProcessing(): boolean {
- return this.processing;
- }
-
- protected setProcessing(processing: boolean): boolean {
- this.processing = processing;
- if (!processing) {
- this.connection = null;
- this.message = "";
- }
- this.onProcessingChange?.(processing);
- return processing;
- }
-
- public getConnection(): WebSocket | null {
- return this.connection;
- }
-
- protected handleMessage(message: GenerationSegmentResponse): void {
- if (message.error && message.end) {
- this.onError?.(message.error);
+ public generate(prompt: string, model: string) {
+ this.setProcessing(true);
+ const token = localStorage.getItem("token") || "";
+ if (token) {
+ this.connection = new WebSocket(endpoint);
+ this.connection.onopen = () => {
+ this.connection?.send(
+ JSON.stringify({ token, prompt, model } as GenerationForm),
+ );
+ };
+ this.connection.onmessage = (event) => {
+ this.handleMessage(JSON.parse(event.data) as GenerationSegmentResponse);
+ };
+ this.connection.onclose = () => {
this.setProcessing(false);
- return;
- }
-
- this.message += message.message;
- this.onMessage?.({
- message: this.message,
- quota: message.quota,
- });
-
- if (message.end) {
- this.onFinished?.(message.hash);
- this.setProcessing(false);
- }
+ };
}
+ }
- public generate(prompt: string, model: string) {
- this.setProcessing(true);
- const token = localStorage.getItem("token") || "";
- if (token) {
- this.connection = new WebSocket(endpoint);
- this.connection.onopen = () => {
- this.connection?.send(JSON.stringify({ token, prompt, model } as GenerationForm));
- };
- this.connection.onmessage = (event) => {
- this.handleMessage(JSON.parse(event.data) as GenerationSegmentResponse);
- };
- this.connection.onclose = () => {
- this.setProcessing(false);
- };
- }
- }
-
- public generateWithBlock(prompt: string, model: string): boolean {
- if (this.isProcessing()) {
- return false;
- }
- this.generate(prompt, model);
- return true;
+ public generateWithBlock(prompt: string, model: string): boolean {
+ if (this.isProcessing()) {
+ return false;
}
+ this.generate(prompt, model);
+ return true;
+ }
}
export const manager = new GenerationManager();
diff --git a/app/src/i18n.ts b/app/src/i18n.ts
index 82cf0bb..b61a2a2 100644
--- a/app/src/i18n.ts
+++ b/app/src/i18n.ts
@@ -143,16 +143,17 @@ const resources = {
"max-length-prompt":
"The content has been truncated due to the context length limit",
},
- "generate": {
- "title": "AI Project Generator",
+ generate: {
+ title: "AI Project Generator",
"input-placeholder": "generate a python game",
- "failed": "Generate failed",
- "reason": "Reason: ",
- "success": "Generate success",
- "success-prompt": "Project generated successfully! Please select the download format.",
- "empty": "generating...",
- "download": "Download {{name}} format",
- }
+ failed: "Generate failed",
+ reason: "Reason: ",
+ success: "Generate success",
+ "success-prompt":
+ "Project generated successfully! Please select the download format.",
+ empty: "generating...",
+ download: "Download {{name}} format",
+ },
},
},
cn: {
@@ -283,16 +284,16 @@ const resources = {
"max-length": "内容过长",
"max-length-prompt": "由于上下文长度限制,内容已被截取",
},
- "generate": {
- "title": "AI 项目生成器",
+ generate: {
+ title: "AI 项目生成器",
"input-placeholder": "生成一个python小游戏",
- "failed": "生成失败",
- "reason": "原因:",
- "success": "生成成功",
+ failed: "生成失败",
+ reason: "原因:",
+ success: "生成成功",
"success-prompt": "成功生成项目!请选择下载格式。",
- "empty": "生成中...",
- "download": "下载 {{name}} 格式",
- }
+ empty: "生成中...",
+ download: "下载 {{name}} 格式",
+ },
},
},
ru: {
@@ -436,13 +437,14 @@ const resources = {
generate: {
title: "Генератор AI проектов",
"input-placeholder": "сгенерировать python игру",
- "failed": "Генерация не удалась",
- "reason": "Причина: ",
- "success": "Генерация успешна",
- "success-prompt": "Проект успешно сгенерирован! Пожалуйста, выберите формат загрузки.",
- "empty": "генерация...",
- "download": "Загрузить {{name}} формат",
- }
+ failed: "Генерация не удалась",
+ reason: "Причина: ",
+ success: "Генерация успешна",
+ "success-prompt":
+ "Проект успешно сгенерирован! Пожалуйста, выберите формат загрузки.",
+ empty: "генерация...",
+ download: "Загрузить {{name}} формат",
+ },
},
},
};
diff --git a/app/src/routes/Generation.tsx b/app/src/routes/Generation.tsx
index 9fbc0b9..8026670 100644
--- a/app/src/routes/Generation.tsx
+++ b/app/src/routes/Generation.tsx
@@ -1,18 +1,18 @@
import "../assets/generation.less";
-import {useDispatch, useSelector} from "react-redux";
+import { useDispatch, useSelector } from "react-redux";
import { selectAuthenticated } from "../store/auth.ts";
import { useTranslation } from "react-i18next";
import { Button } from "../components/ui/button.tsx";
-import {ChevronLeft, Cloud, FileDown, Info, LogIn, Send} from "lucide-react";
-import {login, rest_api} from "../conf.ts";
+import { ChevronLeft, Cloud, FileDown, Info, LogIn, Send } from "lucide-react";
+import { login, rest_api, supportModels } from "../conf.ts";
import router from "../router.ts";
import { Input } from "../components/ui/input.tsx";
import { useEffect, useRef, useState } from "react";
import SelectGroup from "../components/SelectGroup.tsx";
-import {manager} from "../conversation/generation.ts";
-import {useToast} from "../components/ui/use-toast.ts";
-import {handleGenerationData} from "../utils.ts";
-import {selectModel, setModel} from "../store/chat.ts";
+import { manager } from "../conversation/generation.ts";
+import { useToast } from "../components/ui/use-toast.ts";
+import { handleGenerationData } from "../utils.ts";
+import { selectModel, setModel } from "../store/chat.ts";
type WrapperProps = {
onSend?: (value: string, model: string) => boolean;
@@ -22,10 +22,10 @@ function Wrapper({ onSend }: WrapperProps) {
const { t } = useTranslation();
const dispatch = useDispatch();
const ref = useRef(null);
- const [ stayed, setStayed ] = useState
(false);
- const [ hash, setHash ] = useState("");
- const [ data, setData ] = useState("");
- const [ quota, setQuota ] = useState(0);
+ const [stayed, setStayed] = useState(false);
+ const [hash, setHash] = useState("");
+ const [data, setData] = useState("");
+ const [quota, setQuota] = useState(0);
const model = useSelector(selectModel);
const modelRef = useRef(model);
const auth = useSelector(selectAuthenticated);
@@ -45,21 +45,21 @@ function Wrapper({ onSend }: WrapperProps) {
manager.setMessageHandler(({ message, quota }) => {
setData(message);
setQuota(quota);
- })
+ });
manager.setErrorHandler((err: string) => {
toast({
- title: t('generate.failed'),
- description: `${t('generate.reason')} ${err}`,
- })
- })
+ title: t("generate.failed"),
+ description: `${t("generate.reason")} ${err}`,
+ });
+ });
manager.setFinishedHandler((hash: string) => {
toast({
- title: t('generate.success'),
- description: t('generate.success-prompt'),
- })
+ title: t("generate.success"),
+ description: t("generate.success-prompt"),
+ });
setHash(hash);
- })
+ });
function handleSend(model: string = "gpt-3.5-16k") {
const target = ref.current as HTMLInputElement | null;
@@ -81,15 +81,19 @@ function Wrapper({ onSend }: WrapperProps) {
target.focus();
target.removeEventListener("keydown", () => {});
target.addEventListener("keydown", (e) => {
- if (e.key === "Enter") {
- // cannot use model here, because model is not updated
- handleSend(modelRef.current);
- }
- });
+ if (e.key === "Enter") {
+ // cannot use model here, because model is not updated
+ handleSend(modelRef.current);
+ }
+ });
return () => {
- ref.current && (ref.current as HTMLInputElement).removeEventListener("keydown", () => {});
- }
+ ref.current &&
+ (ref.current as HTMLInputElement).removeEventListener(
+ "keydown",
+ () => {},
+ );
+ };
}, [ref]);
useEffect(() => {
@@ -98,35 +102,48 @@ function Wrapper({ onSend }: WrapperProps) {
return (
- {
- stayed ?
-
- { quota > 0 &&
+ {stayed ? (
+
+ {quota > 0 && (
+
{quota}
-
}
-
- { handleGenerationData(data) || t('generate.empty') }
-
- {
- hash.length > 0 &&
-
- }
-
:
-

AI Code Generator
- }
+
+ )}
+
+ {handleGenerationData(data) || t("generate.empty")}
+
+ {hash.length > 0 && (
+
+ )}
+
+ ) : (
+
+

+ AI Code Generator
+
+ )}
-
+
{
- console.debug(`[generation] create generation request (prompt: ${prompt}, model: ${model.toLowerCase()})`);
+ console.debug(
+ `[generation] create generation request (prompt: ${prompt}, model: ${model.toLowerCase()})`,
+ );
return manager.generateWithBlock(prompt, model.toLowerCase());
}}
/>
diff --git a/app/src/routes/Home.tsx b/app/src/routes/Home.tsx
index 51dbafe..50c92a4 100644
--- a/app/src/routes/Home.tsx
+++ b/app/src/routes/Home.tsx
@@ -3,7 +3,9 @@ import "../assets/chat.less";
import { Input } from "../components/ui/input.tsx";
import { Toggle } from "../components/ui/toggle.tsx";
import {
- ChevronDown, ChevronRight, FolderKanban,
+ ChevronDown,
+ ChevronRight,
+ FolderKanban,
Globe,
LogIn,
MessageSquare,
@@ -21,7 +23,7 @@ import {
import { useDispatch, useSelector } from "react-redux";
import type { RootState } from "../store";
import { selectAuthenticated } from "../store/auth.ts";
-import { login } from "../conf.ts";
+import { login, supportModels } from "../conf.ts";
import {
deleteConversation,
toggleConversation,
@@ -35,7 +37,7 @@ import {
useAnimation,
useEffectAsync,
} from "../utils.ts";
-import {toast, useToast} from "../components/ui/use-toast.ts";
+import { toast, useToast } from "../components/ui/use-toast.ts";
import { ConversationInstance, Message } from "../conversation/types.ts";
import {
selectCurrent,
@@ -315,17 +317,20 @@ function ChatWrapper() {
return (
- {
- messages.length > 0 ?
-
:
-
-
-
- }
+ {messages.length > 0 ? (
+
+ ) : (
+
+
+
+ )}
@@ -385,21 +390,19 @@ function ChatWrapper() {
-
- {
- if (!auth && model !== "GPT-3.5") {
- toast({
- title: t("login-require"),
- })
- return;
- }
- dispatch(setModel(model));
- }}
- />
-
+
{
+ if (!auth && model !== "GPT-3.5") {
+ toast({
+ title: t("login-require"),
+ });
+ return;
+ }
+ dispatch(setModel(model));
+ }}
+ />
diff --git a/app/src/utils.ts b/app/src/utils.ts
index 95947f8..78161ba 100644
--- a/app/src/utils.ts
+++ b/app/src/utils.ts
@@ -199,20 +199,35 @@ export function useDraggableInput(
export function escapeRegExp(str: string): string {
// convert \n to [enter], \t to [tab], \r to [return], \s to [space], \" to [quote], \' to [single-quote]
- return str.replace(/\\n/g, "\n").replace(/\\t/g, "\t").replace(/\\r/g, "\r").replace(/\\s/g, " ").replace(/\\"/g, "\"").replace(/\\'/g, "'");
+ return str
+ .replace(/\\n/g, "\n")
+ .replace(/\\t/g, "\t")
+ .replace(/\\r/g, "\r")
+ .replace(/\\s/g, " ")
+ .replace(/\\"/g, '"')
+ .replace(/\\'/g, "'");
}
-export function handleLine(data: string, max_line: number, end?: boolean): string {
+export function handleLine(
+ data: string,
+ max_line: number,
+ end?: boolean,
+): string {
const segment = data.split("\n");
const line = segment.length;
if (line > max_line) {
- return (end ?? true) ? segment.slice(line - max_line).join("\n") : segment.slice(0, max_line).join("\n");
+ return end ?? true
+ ? segment.slice(line - max_line).join("\n")
+ : segment.slice(0, max_line).join("\n");
} else {
return data;
}
}
export function handleGenerationData(data: string): string {
- data = data.replace(/{\s*"result":\s*{/g, "").trim().replace(/}\s*$/g, "");
+ data = data
+ .replace(/{\s*"result":\s*{/g, "")
+ .trim()
+ .replace(/}\s*$/g, "");
return handleLine(escapeRegExp(data), 6);
}