mirror of
https://github.com/coaidev/coai.git
synced 2025-05-21 05:50:14 +09:00
feat: support rename conversation, support vision capability for claude-3 models and fix high context bug (#83)
This commit is contained in:
parent
f2bf2bdf76
commit
7cf6bd1ed8
@ -9,7 +9,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func formatMessages(props *ChatProps) interface{} {
|
func formatMessages(props *ChatProps) interface{} {
|
||||||
if globals.IsOpenAIVisionModels(props.Model) {
|
if globals.IsVisionModel(props.Model) {
|
||||||
return utils.Each[globals.Message, Message](props.Message, func(message globals.Message) Message {
|
return utils.Each[globals.Message, Message](props.Message, func(message globals.Message) Message {
|
||||||
if message.Role == globals.User {
|
if message.Role == globals.User {
|
||||||
raw, urls := utils.ExtractImages(message.Content, true)
|
raw, urls := utils.ExtractImages(message.Content, true)
|
||||||
|
@ -9,7 +9,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func formatMessages(props *ChatProps) interface{} {
|
func formatMessages(props *ChatProps) interface{} {
|
||||||
if globals.IsOpenAIVisionModels(props.Model) {
|
if globals.IsVisionModel(props.Model) {
|
||||||
return utils.Each[globals.Message, Message](props.Message, func(message globals.Message) Message {
|
return utils.Each[globals.Message, Message](props.Message, func(message globals.Message) Message {
|
||||||
if message.Role == globals.User {
|
if message.Role == globals.User {
|
||||||
content, urls := utils.ExtractImages(message.Content, true)
|
content, urls := utils.ExtractImages(message.Content, true)
|
||||||
|
@ -2,6 +2,7 @@ export type CommonResponse = {
|
|||||||
status: boolean;
|
status: boolean;
|
||||||
error?: string;
|
error?: string;
|
||||||
reason?: string;
|
reason?: string;
|
||||||
|
message?: string;
|
||||||
data?: any;
|
data?: any;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -17,6 +18,7 @@ export function toastState(
|
|||||||
else
|
else
|
||||||
toast({
|
toast({
|
||||||
title: t("error"),
|
title: t("error"),
|
||||||
description: state.error ?? state.reason ?? "error occurred",
|
description:
|
||||||
|
state.error ?? state.reason ?? state.message ?? "error occurred",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,8 @@ import axios from "axios";
|
|||||||
import type { ConversationInstance } from "./types.tsx";
|
import type { ConversationInstance } from "./types.tsx";
|
||||||
import { setHistory } from "@/store/chat.ts";
|
import { setHistory } from "@/store/chat.ts";
|
||||||
import { AppDispatch } from "@/store";
|
import { AppDispatch } from "@/store";
|
||||||
|
import { CommonResponse } from "@/api/common.ts";
|
||||||
|
import { getErrorMessage } from "@/utils/base.ts";
|
||||||
|
|
||||||
export async function getConversationList(): Promise<ConversationInstance[]> {
|
export async function getConversationList(): Promise<ConversationInstance[]> {
|
||||||
try {
|
try {
|
||||||
@ -46,6 +48,19 @@ export async function deleteConversation(id: number): Promise<boolean> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function renameConversation(
|
||||||
|
id: number,
|
||||||
|
name: string,
|
||||||
|
): Promise<CommonResponse> {
|
||||||
|
try {
|
||||||
|
const resp = await axios.post("/conversation/rename", { id, name });
|
||||||
|
return resp.data as CommonResponse;
|
||||||
|
} catch (e) {
|
||||||
|
console.warn(e);
|
||||||
|
return { status: false, error: getErrorMessage(e) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function deleteAllConversations(): Promise<boolean> {
|
export async function deleteAllConversations(): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
const resp = await axios.get("/conversation/clean");
|
const resp = await axios.get("/conversation/clean");
|
||||||
|
@ -149,6 +149,7 @@
|
|||||||
border-radius: var(--radius);
|
border-radius: var(--radius);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 0.785rem;
|
font-size: 0.785rem;
|
||||||
|
padding: 0.125rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
@ -445,11 +445,14 @@
|
|||||||
|
|
||||||
.more {
|
.more {
|
||||||
color: hsl(var(--text-secondary));
|
color: hsl(var(--text-secondary));
|
||||||
display: none;
|
scale: 0;
|
||||||
|
opacity: 1;
|
||||||
transition: 0.2s;
|
transition: 0.2s;
|
||||||
opacity: 0;
|
transition-property: color, opacity;
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
outline: none;
|
outline: none;
|
||||||
|
height: 0;
|
||||||
|
width: 0;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: hsl(var(--text));
|
color: hsl(var(--text));
|
||||||
@ -461,12 +464,14 @@
|
|||||||
border-color: hsl(var(--border-hover));
|
border-color: hsl(var(--border-hover));
|
||||||
|
|
||||||
.id {
|
.id {
|
||||||
display: none;
|
scale: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.more {
|
.more {
|
||||||
display: block;
|
scale: 1;
|
||||||
opacity: 1;
|
height: 1.25rem;
|
||||||
|
width: 1.25rem;
|
||||||
|
padding: 0.125rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,7 +117,7 @@ function FileProvider({ value, onChange }: FileProviderProps) {
|
|||||||
);
|
);
|
||||||
if (
|
if (
|
||||||
file.content.length > MaxPromptSize &&
|
file.content.length > MaxPromptSize &&
|
||||||
isHighContextModel(supportModels, model)
|
!isHighContextModel(supportModels, model)
|
||||||
) {
|
) {
|
||||||
file.content = file.content.slice(0, MaxPromptSize);
|
file.content = file.content.slice(0, MaxPromptSize);
|
||||||
toast({
|
toast({
|
||||||
|
@ -126,7 +126,7 @@ function MessageContent({ message, end, index, onEvent }: MessageProps) {
|
|||||||
isUser ? (
|
isUser ? (
|
||||||
<Avatar className={`message-avatar`} username={username} />
|
<Avatar className={`message-avatar`} username={username} />
|
||||||
) : (
|
) : (
|
||||||
<img src={appLogo} alt={``} className={`message-avatar p-1`} />
|
<img src={appLogo} alt={``} className={`message-avatar`} />
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
@ -5,6 +5,7 @@ import {
|
|||||||
Loader2,
|
Loader2,
|
||||||
MessageSquare,
|
MessageSquare,
|
||||||
MoreHorizontal,
|
MoreHorizontal,
|
||||||
|
PencilLine,
|
||||||
Share2,
|
Share2,
|
||||||
Trash2,
|
Trash2,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
@ -20,6 +21,9 @@ import { ConversationInstance } from "@/api/types.tsx";
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { closeMarket, useConversationActions } from "@/store/chat.ts";
|
import { closeMarket, useConversationActions } from "@/store/chat.ts";
|
||||||
import { cn } from "@/components/ui/lib/utils.ts";
|
import { cn } from "@/components/ui/lib/utils.ts";
|
||||||
|
import PopupDialog, { popupTypes } from "@/components/PopupDialog.tsx";
|
||||||
|
import { toastState } from "@/api/common.ts";
|
||||||
|
import { useToast } from "@/components/ui/use-toast.ts";
|
||||||
|
|
||||||
type ConversationSegmentProps = {
|
type ConversationSegmentProps = {
|
||||||
conversation: ConversationInstance;
|
conversation: ConversationInstance;
|
||||||
@ -37,9 +41,13 @@ function ConversationSegment({
|
|||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const { toggle } = useConversationActions();
|
const { toggle } = useConversationActions();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const { toast } = useToast();
|
||||||
|
const { rename } = useConversationActions();
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [offset, setOffset] = useState(0);
|
const [offset, setOffset] = useState(0);
|
||||||
|
|
||||||
|
const [editDialog, setEditDialog] = useState(false);
|
||||||
|
|
||||||
const loading = conversation.id <= 0;
|
const loading = conversation.id <= 0;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -59,13 +67,6 @@ function ConversationSegment({
|
|||||||
>
|
>
|
||||||
<MessageSquare className={`h-4 w-4 mr-1`} />
|
<MessageSquare className={`h-4 w-4 mr-1`} />
|
||||||
<div className={`title`}>{filterMessage(conversation.name)}</div>
|
<div className={`title`}>{filterMessage(conversation.name)}</div>
|
||||||
<div className={cn("id", loading && "loading")}>
|
|
||||||
{loading ? (
|
|
||||||
<Loader2 className={`mr-0.5 h-4 w-4 animate-spin`} />
|
|
||||||
) : (
|
|
||||||
conversation.id
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<DropdownMenu
|
<DropdownMenu
|
||||||
open={open}
|
open={open}
|
||||||
onOpenChange={(state: boolean) => {
|
onOpenChange={(state: boolean) => {
|
||||||
@ -74,20 +75,54 @@ function ConversationSegment({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<DropdownMenuTrigger
|
<DropdownMenuTrigger
|
||||||
className={`outline-none`}
|
className={`flex flex-row outline-none`}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
<div className={cn("id", loading && "loading")}>
|
||||||
|
{loading ? (
|
||||||
|
<Loader2 className={`mr-0.5 h-4 w-4 animate-spin`} />
|
||||||
|
) : (
|
||||||
|
conversation.id
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
<MoreHorizontal className={`more h-5 w-5 p-0.5`} />
|
<MoreHorizontal className={`more h-5 w-5 p-0.5`} />
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent>
|
<DropdownMenuContent>
|
||||||
|
<PopupDialog
|
||||||
|
title={t("conversation.edit-title")}
|
||||||
|
open={editDialog}
|
||||||
|
setOpen={setEditDialog}
|
||||||
|
type={popupTypes.Text}
|
||||||
|
name={t("title")}
|
||||||
|
defaultValue={conversation.name}
|
||||||
|
onSubmit={async (name) => {
|
||||||
|
const resp = await rename(conversation.id, name);
|
||||||
|
toastState(toast, t, resp, true);
|
||||||
|
if (!resp.status) return false;
|
||||||
|
|
||||||
|
setEditDialog(false);
|
||||||
|
return true;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
// prevent click event from opening the dropdown menu
|
// prevent click event from opening the dropdown menu
|
||||||
if (offset + 500 > new Date().getTime()) return;
|
if (offset + 500 > new Date().getTime()) return;
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
setEditDialog(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<PencilLine className={`h-4 w-4 mx-1`} />
|
||||||
|
{t("conversation.edit-title")}
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem
|
||||||
|
onClick={(e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
operate({ target: conversation, type: "delete" });
|
operate({ target: conversation, type: "delete" });
|
||||||
@ -95,7 +130,7 @@ function ConversationSegment({
|
|||||||
setOpen(false);
|
setOpen(false);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Trash2 className={`more h-4 w-4 mx-1`} />
|
<Trash2 className={`h-4 w-4 mx-1`} />
|
||||||
{t("conversation.delete-conversation")}
|
{t("conversation.delete-conversation")}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
@ -107,7 +142,7 @@ function ConversationSegment({
|
|||||||
setOpen(false);
|
setOpen(false);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Share2 className={`more h-4 w-4 mx-1`} />
|
<Share2 className={`h-4 w-4 mx-1`} />
|
||||||
{t("share.share-conversation")}
|
{t("share.share-conversation")}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
|
@ -51,6 +51,7 @@
|
|||||||
"model": "模型",
|
"model": "模型",
|
||||||
"min-quota": "最低余额",
|
"min-quota": "最低余额",
|
||||||
"your-quota": "您的余额",
|
"your-quota": "您的余额",
|
||||||
|
"title": "标题",
|
||||||
"auth": {
|
"auth": {
|
||||||
"username": "用户名",
|
"username": "用户名",
|
||||||
"username-placeholder": "请输入用户名",
|
"username-placeholder": "请输入用户名",
|
||||||
@ -126,6 +127,7 @@
|
|||||||
"remove-all-description": "此操作无法撤消。这将永久删除所有对话,是否继续?",
|
"remove-all-description": "此操作无法撤消。这将永久删除所有对话,是否继续?",
|
||||||
"cancel": "取消",
|
"cancel": "取消",
|
||||||
"delete": "删除",
|
"delete": "删除",
|
||||||
|
"edit-title": "编辑标题",
|
||||||
"delete-conversation": "删除对话",
|
"delete-conversation": "删除对话",
|
||||||
"delete-success": "对话已删除",
|
"delete-success": "对话已删除",
|
||||||
"delete-success-prompt": "对话已删除。",
|
"delete-success-prompt": "对话已删除。",
|
||||||
|
@ -75,7 +75,8 @@
|
|||||||
"delete-success": "Conversation deleted",
|
"delete-success": "Conversation deleted",
|
||||||
"delete-success-prompt": "Conversation has been deleted.",
|
"delete-success-prompt": "Conversation has been deleted.",
|
||||||
"delete-failed": "Delete failed",
|
"delete-failed": "Delete failed",
|
||||||
"delete-failed-prompt": "Failed to delete conversation. Please check your network and try again."
|
"delete-failed-prompt": "Failed to delete conversation. Please check your network and try again.",
|
||||||
|
"edit-title": "Edit Title"
|
||||||
},
|
},
|
||||||
"chat": {
|
"chat": {
|
||||||
"web": "Web Searching",
|
"web": "Web Searching",
|
||||||
@ -746,5 +747,6 @@
|
|||||||
"exit": "Leave",
|
"exit": "Leave",
|
||||||
"model": "Model",
|
"model": "Model",
|
||||||
"min-quota": "Minimum Balance",
|
"min-quota": "Minimum Balance",
|
||||||
"your-quota": "Your balance"
|
"your-quota": "Your balance",
|
||||||
|
"title": "Title"
|
||||||
}
|
}
|
@ -75,7 +75,8 @@
|
|||||||
"delete-success": "会話が削除されました",
|
"delete-success": "会話が削除されました",
|
||||||
"delete-success-prompt": "会話が削除されました。",
|
"delete-success-prompt": "会話が削除されました。",
|
||||||
"delete-failed": "削除失敗",
|
"delete-failed": "削除失敗",
|
||||||
"delete-failed-prompt": "会話を削除できませんでした。ネットワークを確認して、もう一度お試しください。"
|
"delete-failed-prompt": "会話を削除できませんでした。ネットワークを確認して、もう一度お試しください。",
|
||||||
|
"edit-title": "タイトルを編集..."
|
||||||
},
|
},
|
||||||
"chat": {
|
"chat": {
|
||||||
"web": "インターネット検索",
|
"web": "インターネット検索",
|
||||||
@ -746,5 +747,6 @@
|
|||||||
"exit": "離れる",
|
"exit": "離れる",
|
||||||
"model": "モデル",
|
"model": "モデル",
|
||||||
"min-quota": "最低残高",
|
"min-quota": "最低残高",
|
||||||
"your-quota": "残高"
|
"your-quota": "残高",
|
||||||
|
"title": "タイトル"
|
||||||
}
|
}
|
@ -75,7 +75,8 @@
|
|||||||
"delete-success": "Разговор удален",
|
"delete-success": "Разговор удален",
|
||||||
"delete-success-prompt": "Разговор был удален.",
|
"delete-success-prompt": "Разговор был удален.",
|
||||||
"delete-failed": "Ошибка удаления",
|
"delete-failed": "Ошибка удаления",
|
||||||
"delete-failed-prompt": "Не удалось удалить разговор. Пожалуйста, проверьте свою сеть и попробуйте еще раз."
|
"delete-failed-prompt": "Не удалось удалить разговор. Пожалуйста, проверьте свою сеть и попробуйте еще раз.",
|
||||||
|
"edit-title": "Редактировать заголовок"
|
||||||
},
|
},
|
||||||
"chat": {
|
"chat": {
|
||||||
"web": "Веб-поиск",
|
"web": "Веб-поиск",
|
||||||
@ -746,5 +747,6 @@
|
|||||||
"exit": "Закрыть",
|
"exit": "Закрыть",
|
||||||
"model": "модель",
|
"model": "модель",
|
||||||
"min-quota": "Минимальный баланс",
|
"min-quota": "Минимальный баланс",
|
||||||
"your-quota": "Ваш баланс"
|
"your-quota": "Ваш баланс",
|
||||||
|
"title": "заглавие"
|
||||||
}
|
}
|
@ -22,6 +22,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
deleteConversation as doDeleteConversation,
|
deleteConversation as doDeleteConversation,
|
||||||
deleteAllConversations as doDeleteAllConversations,
|
deleteAllConversations as doDeleteAllConversations,
|
||||||
|
renameConversation as doRenameConversation,
|
||||||
loadConversation,
|
loadConversation,
|
||||||
getConversationList,
|
getConversationList,
|
||||||
} from "@/api/history.ts";
|
} from "@/api/history.ts";
|
||||||
@ -257,6 +258,11 @@ const chatSlice = createSlice({
|
|||||||
// add a new history at the beginning
|
// add a new history at the beginning
|
||||||
state.history = [{ id: -1, name, message: [] }, ...state.history];
|
state.history = [{ id: -1, name, message: [] }, ...state.history];
|
||||||
},
|
},
|
||||||
|
renameHistory: (state, action) => {
|
||||||
|
const { id, name } = action.payload as { id: number; name: string };
|
||||||
|
const conversation = state.history.find((item) => item.id === id);
|
||||||
|
if (conversation) conversation.name = name;
|
||||||
|
},
|
||||||
setModel: (state, action) => {
|
setModel: (state, action) => {
|
||||||
setMemory("model", action.payload as string);
|
setMemory("model", action.payload as string);
|
||||||
state.model = action.payload as string;
|
state.model = action.payload as string;
|
||||||
@ -355,6 +361,7 @@ const chatSlice = createSlice({
|
|||||||
|
|
||||||
export const {
|
export const {
|
||||||
setHistory,
|
setHistory,
|
||||||
|
renameHistory,
|
||||||
setCurrent,
|
setCurrent,
|
||||||
setModel,
|
setModel,
|
||||||
setWeb,
|
setWeb,
|
||||||
@ -441,6 +448,12 @@ export function useConversationActions() {
|
|||||||
|
|
||||||
dispatch(setCurrent(id));
|
dispatch(setCurrent(id));
|
||||||
},
|
},
|
||||||
|
rename: async (id: number, name: string) => {
|
||||||
|
const resp = await doRenameConversation(id, name);
|
||||||
|
resp.status && dispatch(renameHistory({ id, name }));
|
||||||
|
|
||||||
|
return resp;
|
||||||
|
},
|
||||||
remove: async (id: number) => {
|
remove: async (id: number) => {
|
||||||
const state = await doDeleteConversation(id);
|
const state = await doDeleteConversation(id);
|
||||||
state && dispatch(deleteConversation(id));
|
state && dispatch(deleteConversation(id));
|
||||||
|
@ -75,6 +75,7 @@ const (
|
|||||||
Claude2 = "claude-1-100k"
|
Claude2 = "claude-1-100k"
|
||||||
Claude2100k = "claude-2"
|
Claude2100k = "claude-2"
|
||||||
Claude2200k = "claude-2.1"
|
Claude2200k = "claude-2.1"
|
||||||
|
Claude3 = "claude-3"
|
||||||
ClaudeSlack = "claude-slack"
|
ClaudeSlack = "claude-slack"
|
||||||
SparkDesk = "spark-desk-v1.5"
|
SparkDesk = "spark-desk-v1.5"
|
||||||
SparkDeskV2 = "spark-desk-v2"
|
SparkDeskV2 = "spark-desk-v2"
|
||||||
@ -109,14 +110,10 @@ var OpenAIDalleModels = []string{
|
|||||||
Dalle, Dalle2, Dalle3,
|
Dalle, Dalle2, Dalle3,
|
||||||
}
|
}
|
||||||
|
|
||||||
var OpenAIVisionModels = []string{
|
|
||||||
//GPT4Vision, GPT4All, GPT4Dalle,
|
|
||||||
GPT4VisionPreview, GPT41106VisionPreview,
|
|
||||||
}
|
|
||||||
|
|
||||||
var VisionModels = []string{
|
var VisionModels = []string{
|
||||||
GPT4VisionPreview, GPT41106VisionPreview,
|
GPT4VisionPreview, GPT41106VisionPreview, // openai
|
||||||
GeminiProVision,
|
GeminiProVision, // gemini
|
||||||
|
Claude3, // anthropic
|
||||||
}
|
}
|
||||||
|
|
||||||
func in(value string, slice []string) bool {
|
func in(value string, slice []string) bool {
|
||||||
@ -133,11 +130,6 @@ func IsOpenAIDalleModel(model string) bool {
|
|||||||
return in(model, OpenAIDalleModels) && !strings.Contains(model, "gpt-4-dalle")
|
return in(model, OpenAIDalleModels) && !strings.Contains(model, "gpt-4-dalle")
|
||||||
}
|
}
|
||||||
|
|
||||||
func IsOpenAIVisionModels(model string) bool {
|
|
||||||
// enable openai image format for gpt-4-vision-preview models
|
|
||||||
return in(model, OpenAIVisionModels)
|
|
||||||
}
|
|
||||||
|
|
||||||
func IsVisionModel(model string) bool {
|
func IsVisionModel(model string) bool {
|
||||||
return in(model, VisionModels)
|
return in(model, VisionModels)
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,11 @@ type ShareForm struct {
|
|||||||
Refs []int `json:"refs"`
|
Refs []int `json:"refs"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type RenameConversationForm struct {
|
||||||
|
Id int64 `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
type DeleteMaskForm struct {
|
type DeleteMaskForm struct {
|
||||||
Id int `json:"id" binding:"required"`
|
Id int `json:"id" binding:"required"`
|
||||||
}
|
}
|
||||||
@ -116,6 +121,41 @@ func DeleteAPI(c *gin.Context) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func RenameAPI(c *gin.Context) {
|
||||||
|
user := auth.GetUser(c)
|
||||||
|
if user == nil {
|
||||||
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
"status": false,
|
||||||
|
"message": "user not found",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
db := utils.GetDBFromContext(c)
|
||||||
|
var form RenameConversationForm
|
||||||
|
if err := c.ShouldBindJSON(&form); err != nil {
|
||||||
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
"status": false,
|
||||||
|
"message": "invalid form",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
conversation := LoadConversation(db, user.GetID(db), form.Id)
|
||||||
|
if conversation == nil {
|
||||||
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
"status": false,
|
||||||
|
"message": "conversation not found",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
conversation.RenameConversation(db, form.Name)
|
||||||
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
"status": true,
|
||||||
|
"message": "",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func CleanAPI(c *gin.Context) {
|
func CleanAPI(c *gin.Context) {
|
||||||
user := auth.GetUser(c)
|
user := auth.GetUser(c)
|
||||||
if user == nil {
|
if user == nil {
|
||||||
|
@ -7,6 +7,7 @@ func Register(app *gin.RouterGroup) {
|
|||||||
{
|
{
|
||||||
router.GET("/list", ListAPI)
|
router.GET("/list", ListAPI)
|
||||||
router.GET("/load", LoadAPI)
|
router.GET("/load", LoadAPI)
|
||||||
|
router.POST("/rename", RenameAPI)
|
||||||
router.GET("/delete", DeleteAPI)
|
router.GET("/delete", DeleteAPI)
|
||||||
router.GET("/clean", CleanAPI)
|
router.GET("/clean", CleanAPI)
|
||||||
|
|
||||||
|
@ -115,6 +115,14 @@ func (c *Conversation) DeleteConversation(db *sql.DB) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Conversation) RenameConversation(db *sql.DB, name string) bool {
|
||||||
|
_, err := globals.ExecDb(db, "UPDATE conversation SET conversation_name = ? WHERE user_id = ? AND conversation_id = ?", name, c.UserID, c.Id)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func DeleteAllConversations(db *sql.DB, user auth.User) error {
|
func DeleteAllConversations(db *sql.DB, user auth.User) error {
|
||||||
_, err := globals.ExecDb(db, "DELETE FROM conversation WHERE user_id = ?", user.GetID(db))
|
_, err := globals.ExecDb(db, "DELETE FROM conversation WHERE user_id = ?", user.GetID(db))
|
||||||
return err
|
return err
|
||||||
|
@ -113,7 +113,7 @@ func (i *Image) GetPixelColor(x int, y int) (int, int, int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (i *Image) CountTokens(model string) int {
|
func (i *Image) CountTokens(model string) int {
|
||||||
if globals.IsOpenAIVisionModels(model) {
|
if globals.IsVisionModel(model) {
|
||||||
// tile size is 512x512
|
// tile size is 512x512
|
||||||
// the max size of image is 2048x2048
|
// the max size of image is 2048x2048
|
||||||
// the image that is larger than 2048x2048 will be resized in 16 tiles
|
// the image that is larger than 2048x2048 will be resized in 16 tiles
|
||||||
|
Loading…
Reference in New Issue
Block a user