feat: sync chatnio blob service ocr feature

This commit is contained in:
Zhang Minghan 2024-04-25 20:36:35 +08:00
parent 72af27b26a
commit 1150ff2e29
14 changed files with 48 additions and 27 deletions

View File

@ -8,7 +8,7 @@
}, },
"package": { "package": {
"productName": "chatnio", "productName": "chatnio",
"version": "3.10.8" "version": "3.10.9"
}, },
"tauri": { "tauri": {
"allowlist": { "allowlist": {

View File

@ -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;
} }

View File

@ -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,

View File

@ -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;
} }

View File

@ -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" },
}, },

View File

@ -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) {

View File

@ -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 {

View File

@ -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"}>

View File

@ -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]);

View File

@ -142,7 +142,6 @@ const DialogAction = React.forwardRef<HTMLButtonElement, ButtonProps>(
); );
DialogAction.displayName = "DialogAction"; DialogAction.displayName = "DialogAction";
export { export {
Dialog, Dialog,
DialogTrigger, DialogTrigger,

View File

@ -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

View File

@ -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]);

View File

@ -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

View File

@ -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;
}