mirror of
https://github.com/coaidev/coai.git
synced 2025-05-29 09:50:16 +09:00
feat: sync chatnio blob service ocr feature
This commit is contained in:
parent
72af27b26a
commit
1150ff2e29
@ -8,7 +8,7 @@
|
|||||||
},
|
},
|
||||||
"package": {
|
"package": {
|
||||||
"productName": "chatnio",
|
"productName": "chatnio",
|
||||||
"version": "3.10.8"
|
"version": "3.10.9"
|
||||||
},
|
},
|
||||||
"tauri": {
|
"tauri": {
|
||||||
"allowlist": {
|
"allowlist": {
|
||||||
|
@ -187,13 +187,9 @@ export const ChannelInfos: Record<string, ChannelInfo> = {
|
|||||||
chatglm: {
|
chatglm: {
|
||||||
endpoint: "https://open.bigmodel.cn",
|
endpoint: "https://open.bigmodel.cn",
|
||||||
format: "<api-key>",
|
format: "<api-key>",
|
||||||
models: [
|
models: ["glm-4", "glm-4v", "glm-3-turbo"],
|
||||||
"glm-4",
|
|
||||||
"glm-4v",
|
|
||||||
"glm-3-turbo"
|
|
||||||
],
|
|
||||||
description:
|
description:
|
||||||
"> 智谱 ChatGLM 密钥格式为 **api-key**,接入点填写 *https://open.bigmodel.cn* \n"
|
"> 智谱 ChatGLM 密钥格式为 **api-key**,接入点填写 *https://open.bigmodel.cn* \n",
|
||||||
},
|
},
|
||||||
qwen: {
|
qwen: {
|
||||||
endpoint: "https://dashscope.aliyuncs.com",
|
endpoint: "https://dashscope.aliyuncs.com",
|
||||||
@ -297,4 +293,4 @@ export function getChannelType(type?: string): string {
|
|||||||
export function getShortChannelType(type?: string): string {
|
export function getShortChannelType(type?: string): string {
|
||||||
if (type && type in ShortChannelTypes) return ShortChannelTypes[type];
|
if (type && type in ShortChannelTypes) return ShortChannelTypes[type];
|
||||||
return ShortChannelTypes.openai;
|
return ShortChannelTypes.openai;
|
||||||
}
|
}
|
||||||
|
@ -173,7 +173,12 @@ export const pricing: PricingDataset = [
|
|||||||
currency: Currency.CNY,
|
currency: Currency.CNY,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
models: ["zhipu-chatglm-lite", "zhipu-chatglm-std", "zhipu-chatglm-turbo", "glm-3-turbo"],
|
models: [
|
||||||
|
"zhipu-chatglm-lite",
|
||||||
|
"zhipu-chatglm-std",
|
||||||
|
"zhipu-chatglm-turbo",
|
||||||
|
"glm-3-turbo",
|
||||||
|
],
|
||||||
input: 0.005,
|
input: 0.005,
|
||||||
output: 0.005,
|
output: 0.005,
|
||||||
currency: Currency.CNY,
|
currency: Currency.CNY,
|
||||||
|
@ -85,10 +85,14 @@ export async function doRegister(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function doVerify(email: string, checkout?: boolean): Promise<VerifyResponse> {
|
export async function doVerify(
|
||||||
|
email: string,
|
||||||
|
checkout?: boolean,
|
||||||
|
): Promise<VerifyResponse> {
|
||||||
try {
|
try {
|
||||||
const response = await axios.post("/verify", {
|
const response = await axios.post("/verify", {
|
||||||
email, checkout,
|
email,
|
||||||
|
checkout,
|
||||||
} as VerifyForm);
|
} as VerifyForm);
|
||||||
return response.data as VerifyResponse;
|
return response.data as VerifyResponse;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -132,4 +136,4 @@ export async function sendCode(
|
|||||||
});
|
});
|
||||||
|
|
||||||
return res.status;
|
return res.status;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { blobEndpoint } from "@/conf/env.ts";
|
import { blobEndpoint } from "@/conf/env.ts";
|
||||||
|
import { trimSuffixes } from "@/utils/base.ts";
|
||||||
|
|
||||||
export type BlobParserResponse = {
|
export type BlobParserResponse = {
|
||||||
status: boolean;
|
status: boolean;
|
||||||
@ -15,11 +16,16 @@ export type FileObject = {
|
|||||||
|
|
||||||
export type FileArray = FileObject[];
|
export type FileArray = FileObject[];
|
||||||
|
|
||||||
export async function blobParser(file: File): Promise<BlobParserResponse> {
|
export async function blobParser(
|
||||||
|
file: File,
|
||||||
|
model: string,
|
||||||
|
): Promise<BlobParserResponse> {
|
||||||
|
const endpoint = trimSuffixes(blobEndpoint, ["/upload", "/"]);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const resp = await axios.post(
|
const resp = await axios.post(
|
||||||
`${blobEndpoint}/upload`,
|
`${endpoint}/upload`,
|
||||||
{ file },
|
{ file, model },
|
||||||
{
|
{
|
||||||
headers: { "Content-Type": "multipart/form-data" },
|
headers: { "Content-Type": "multipart/form-data" },
|
||||||
},
|
},
|
||||||
|
@ -77,7 +77,7 @@ function FileProvider({ files, dispatch }: FileProviderProps) {
|
|||||||
});
|
});
|
||||||
}, 2000);
|
}, 2000);
|
||||||
|
|
||||||
const resp = await blobParser(file);
|
const resp = await blobParser(file, model);
|
||||||
clearTimeout(timeout);
|
clearTimeout(timeout);
|
||||||
|
|
||||||
if (!resp.status) {
|
if (!resp.status) {
|
||||||
|
@ -102,9 +102,9 @@ function reducer(state: ChargeProps, action: any): ChargeProps {
|
|||||||
if (action.payload.trim().length === 0) return state;
|
if (action.payload.trim().length === 0) return state;
|
||||||
return state.models.includes(action.payload)
|
return state.models.includes(action.payload)
|
||||||
? {
|
? {
|
||||||
...state,
|
...state,
|
||||||
models: state.models.filter((model) => model !== action.payload),
|
models: state.models.filter((model) => model !== action.payload),
|
||||||
}
|
}
|
||||||
: { ...state, models: [...state.models, action.payload] };
|
: { ...state, models: [...state.models, action.payload] };
|
||||||
case "remove-model":
|
case "remove-model":
|
||||||
return {
|
return {
|
||||||
|
@ -41,7 +41,7 @@ function Announcement() {
|
|||||||
>
|
>
|
||||||
<Bell className={`h-4 w-4`} />
|
<Bell className={`h-4 w-4`} />
|
||||||
</Button>
|
</Button>
|
||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
<DialogContent className={`announcement-dialog flex-dialog`}>
|
<DialogContent className={`announcement-dialog flex-dialog`}>
|
||||||
<DialogHeader notTextCentered>
|
<DialogHeader notTextCentered>
|
||||||
<DialogTitle className={"flex flex-row items-center select-none"}>
|
<DialogTitle className={"flex flex-row items-center select-none"}>
|
||||||
|
@ -40,7 +40,7 @@ export function Combobox({
|
|||||||
const [open, setOpen] = React.useState(defaultOpen ?? false);
|
const [open, setOpen] = React.useState(defaultOpen ?? false);
|
||||||
const valueList = React.useMemo((): string[] => {
|
const valueList = React.useMemo((): string[] => {
|
||||||
// list set (if some element in current value is not in list, it will be added)
|
// list set (if some element in current value is not in list, it will be added)
|
||||||
const seq = [...list, (value ?? "")].filter((v) => v);
|
const seq = [...list, value ?? ""].filter((v) => v);
|
||||||
const set = new Set(seq);
|
const set = new Set(seq);
|
||||||
return [...set];
|
return [...set];
|
||||||
}, [list]);
|
}, [list]);
|
||||||
|
@ -142,7 +142,6 @@ const DialogAction = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|||||||
);
|
);
|
||||||
DialogAction.displayName = "DialogAction";
|
DialogAction.displayName = "DialogAction";
|
||||||
|
|
||||||
|
|
||||||
export {
|
export {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogTrigger,
|
DialogTrigger,
|
||||||
|
@ -7,7 +7,7 @@ import {
|
|||||||
import { syncSiteInfo } from "@/admin/api/info.ts";
|
import { syncSiteInfo } from "@/admin/api/info.ts";
|
||||||
import { setAxiosConfig } from "@/conf/api.ts";
|
import { setAxiosConfig } from "@/conf/api.ts";
|
||||||
|
|
||||||
export const version = "3.10.8"; // version of the current build
|
export const version = "3.10.9"; // version of the current build
|
||||||
export const dev: boolean = getDev(); // is in development mode (for debugging, in localhost origin)
|
export const dev: boolean = getDev(); // is in development mode (for debugging, in localhost origin)
|
||||||
export const deploy: boolean = true; // is production environment (for api endpoint)
|
export const deploy: boolean = true; // is production environment (for api endpoint)
|
||||||
export const tokenField = getTokenField(deploy); // token field name for storing token
|
export const tokenField = getTokenField(deploy); // token field name for storing token
|
||||||
|
@ -74,7 +74,7 @@ function Wrapper({ onSend }: WrapperProps) {
|
|||||||
const target = ref.current as HTMLInputElement | null;
|
const target = ref.current as HTMLInputElement | null;
|
||||||
if (!target) return;
|
if (!target) return;
|
||||||
target.focus();
|
target.focus();
|
||||||
target.removeEventListener("keydown", () => { });
|
target.removeEventListener("keydown", () => {});
|
||||||
target.addEventListener("keydown", (e) => {
|
target.addEventListener("keydown", (e) => {
|
||||||
if (isEnter(e)) {
|
if (isEnter(e)) {
|
||||||
// cannot use model here, because model is not updated
|
// cannot use model here, because model is not updated
|
||||||
@ -86,7 +86,7 @@ function Wrapper({ onSend }: WrapperProps) {
|
|||||||
ref.current &&
|
ref.current &&
|
||||||
(ref.current as HTMLInputElement).removeEventListener(
|
(ref.current as HTMLInputElement).removeEventListener(
|
||||||
"keydown",
|
"keydown",
|
||||||
() => { },
|
() => {},
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
}, [ref]);
|
}, [ref]);
|
||||||
|
@ -845,7 +845,9 @@ function Search({ data, dispatch, onChange }: CompProps<SearchState>) {
|
|||||||
max={50}
|
max={50}
|
||||||
/>
|
/>
|
||||||
</ParagraphItem>
|
</ParagraphItem>
|
||||||
<ParagraphDescription border>{t("admin.system.searchTip")}</ParagraphDescription>
|
<ParagraphDescription border>
|
||||||
|
{t("admin.system.searchTip")}
|
||||||
|
</ParagraphDescription>
|
||||||
<ParagraphFooter>
|
<ParagraphFooter>
|
||||||
<div className={`grow`} />
|
<div className={`grow`} />
|
||||||
<Button
|
<Button
|
||||||
|
@ -94,7 +94,9 @@ export function isUrl(value: string): boolean {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isEnter<T extends HTMLElement>(e: React.KeyboardEvent<T> | KeyboardEvent): boolean {
|
export function isEnter<T extends HTMLElement>(
|
||||||
|
e: React.KeyboardEvent<T> | KeyboardEvent,
|
||||||
|
): boolean {
|
||||||
return e.key === "Enter" && e.keyCode != 229;
|
return e.key === "Enter" && e.keyCode != 229;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,3 +127,10 @@ export function getHostName(url: string): string {
|
|||||||
export function isB64Image(value: string): boolean {
|
export function isB64Image(value: string): boolean {
|
||||||
return /data:image\/([^;]+);base64,([a-zA-Z0-9+/=]+)/g.test(value);
|
return /data:image\/([^;]+);base64,([a-zA-Z0-9+/=]+)/g.test(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function trimSuffixes(value: string, suffixes: string[]): string {
|
||||||
|
for (const suffix of suffixes) {
|
||||||
|
if (value.endsWith(suffix)) return value.slice(0, -suffix.length);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user