-
+
-
-
-
-
+
-
+
+
+
+
+
+
+ {
diff --git a/src/panels/RolePanel/RoleEdit/Model/index.tsx b/src/panels/RolePanel/RoleEdit/Model/index.tsx
index c13c0e51..d859f1fe 100644
--- a/src/panels/RolePanel/RoleEdit/Model/index.tsx
+++ b/src/panels/RolePanel/RoleEdit/Model/index.tsx
@@ -43,7 +43,12 @@ const Model = (props: ModelProps) => {
-
+
diff --git a/src/panels/RolePanel/RoleEdit/Role/index.tsx b/src/panels/RolePanel/RoleEdit/Role/index.tsx
index 1e849695..fb1217b7 100644
--- a/src/panels/RolePanel/RoleEdit/Role/index.tsx
+++ b/src/panels/RolePanel/RoleEdit/Role/index.tsx
@@ -36,6 +36,7 @@ const Info = (props: InfoProps) => {
diff --git a/src/panels/RolePanel/RoleEdit/index.tsx b/src/panels/RolePanel/RoleEdit/index.tsx
index c79b1ab1..18ae3563 100644
--- a/src/panels/RolePanel/RoleEdit/index.tsx
+++ b/src/panels/RolePanel/RoleEdit/index.tsx
@@ -4,6 +4,8 @@ import { TabsNav } from '@lobehub/ui';
import classNames from 'classnames';
import React, { useState } from 'react';
+import SubmitAgentButton from '@/features/Actions/SubmitAgentButton';
+
import Info from './Info';
import LangModel from './LangModel';
import Model from './Model';
@@ -48,7 +50,7 @@ const RolePanel = (props: RolePanelProps) => {
label: '语言模型',
},
]}
- // tabBarExtraContent={
}
+ tabBarExtraContent={
}
onChange={(key) => {
setTab(key);
}}
diff --git a/src/server/routers/edge/upload.ts b/src/server/routers/edge/upload.ts
new file mode 100644
index 00000000..b238fd80
--- /dev/null
+++ b/src/server/routers/edge/upload.ts
@@ -0,0 +1,16 @@
+import { z } from 'zod';
+
+import { S3 } from '@/server/s3';
+import { publicProcedure, router } from '@/server/trpc';
+
+export const uploadRouter = router({
+ createS3PreSignedUrl: publicProcedure
+ .input(z.object({ pathname: z.string() }))
+ .mutation(async ({ input }) => {
+ const s3 = new S3();
+
+ return await s3.createPreSignedUrl(input.pathname);
+ }),
+});
+
+export type FileRouter = typeof uploadRouter;
diff --git a/src/server/routers/index.ts b/src/server/routers/index.ts
new file mode 100644
index 00000000..37575c73
--- /dev/null
+++ b/src/server/routers/index.ts
@@ -0,0 +1,13 @@
+/**
+ * This file contains the root router of lobe chat tRPC-backend
+ */
+import { publicProcedure, router } from '@/server/trpc';
+
+import { uploadRouter } from './edge/upload';
+
+export const edgeRouter = router({
+ healthcheck: publicProcedure.query(() => "i'm live!"),
+ upload: uploadRouter,
+});
+
+export type EdgeRouter = typeof edgeRouter;
diff --git a/src/utils/s3.ts b/src/server/s3.ts
similarity index 95%
rename from src/utils/s3.ts
rename to src/server/s3.ts
index f8ecadc4..7d9f8ccf 100644
--- a/src/utils/s3.ts
+++ b/src/server/s3.ts
@@ -21,9 +21,6 @@ export class S3 {
});
}
- public getClient() {
- return this.client;
- }
public async createPreSignedUrl(key: string): Promise
{
const command = new PutObjectCommand({
ACL: 'public-read',
diff --git a/src/server/trpc.ts b/src/server/trpc.ts
new file mode 100644
index 00000000..5310780c
--- /dev/null
+++ b/src/server/trpc.ts
@@ -0,0 +1,26 @@
+import { initTRPC } from '@trpc/server';
+import superjson from 'superjson';
+
+/**
+ * Initialization of tRPC backend
+ * Should be done only once per backend!
+ */
+const t = initTRPC.create({
+ /**
+ * @link https://trpc.io/docs/v11/error-formatting
+ */
+ errorFormatter({ shape }) {
+ return shape;
+ },
+ /**
+ * @link https://trpc.io/docs/v11/data-transformers
+ */
+ transformer: superjson,
+});
+
+/**
+ * Export reusable router and procedure helpers
+ * that can be used throughout the router
+ */
+export const router = t.router;
+export const publicProcedure = t.procedure;
diff --git a/src/services/upload.ts b/src/services/upload.ts
index b2f7b253..1bcdb1f5 100644
--- a/src/services/upload.ts
+++ b/src/services/upload.ts
@@ -1,29 +1,35 @@
+import axios from 'axios';
import dayjs from 'dayjs';
-import { S3 } from '@/utils/s3';
+import { OSS_PREFIX } from '@/constants/common';
+import { edgeClient } from '@/libs/trpc/client';
import { uuid } from '@/utils/uuid';
-export const upload = async (file: File) => {
+export const upload = async (
+ file: File,
+ handlers: {
+ onProgress?: (progress: number) => void;
+ },
+) => {
const dateFolder = dayjs().format('YYYY/MM/DD'); // 使用当前日期作为文件夹名称
- const folderName = `files/${dateFolder}`; // e.g., "uploads/2023-10-10"
+ const folderName = `files/${dateFolder}`; // e.g., "files/2023/10/10"
const fileName = `${uuid()}.${file.name.split('.').at(-1)}`;
- const key = `${folderName}/${fileName}`;
+ const pathname = `${folderName}/${fileName}`;
- const s3 = new S3();
- const url = await s3.createPreSignedUrl(key);
+ const url = await edgeClient.upload.createS3PreSignedUrl.mutate({ pathname });
- const res = await fetch(url, {
- body: file.stream(),
+ await axios({
+ method: 'put',
+ url,
+ data: file,
headers: {
'Content-Type': file.type,
},
- method: 'POST',
+ onUploadProgress: ({ progress }) => {
+ handlers.onProgress?.(progress ? Math.ceil(progress * 100) : 0);
+ },
});
- if (res.ok) {
- return { success: true, message: 'File uploaded successfully', url: key };
- } else {
- throw new Error('Upload Error');
- }
+ return `${OSS_PREFIX}/${pathname}`;
};
diff --git a/src/store/session/index.ts b/src/store/session/index.ts
index e8063224..dde939a1 100644
--- a/src/store/session/index.ts
+++ b/src/store/session/index.ts
@@ -371,7 +371,7 @@ export const createSessionStore: StateCreator s.role === 'user').at(-1);
+ const latestMsg = contextMessages.findLast((s) => s.role === 'user');
if (!latestMsg) return;
diff --git a/src/types/agent.ts b/src/types/agent.ts
index 1e9ce881..097ffb5d 100644
--- a/src/types/agent.ts
+++ b/src/types/agent.ts
@@ -1,4 +1,5 @@
-import { ChatStreamPayload } from './openai/chat';
+import { LLMParams } from '@/types/llm';
+
import { TouchActionConfig } from './touch';
import { TTS } from './tts';
@@ -34,7 +35,7 @@ export interface AgentMeta {
/**
* 封面图片路径
*/
- cover?: string;
+ cover: string;
/**
* 角色描述
*/
@@ -89,7 +90,7 @@ export interface Agent {
/**
* 语言模型配置
*/
- params?: Partial;
+ params?: LLMParams;
/**
* 角色设定
*/
diff --git a/src/types/llm.ts b/src/types/llm.ts
index 041966ca..24c87caf 100644
--- a/src/types/llm.ts
+++ b/src/types/llm.ts
@@ -42,3 +42,31 @@ export interface ChatModelCard {
*/
vision?: boolean;
}
+
+// 语言模型的设置参数
+export interface LLMParams {
+ /**
+ * 控制生成文本中的惩罚系数,用于减少重复性
+ * @default 0
+ */
+ frequency_penalty?: number;
+ /**
+ * 生成文本的最大长度
+ */
+ max_tokens?: number;
+ /**
+ * 控制生成文本中的惩罚系数,用于减少主题的变化
+ * @default 0
+ */
+ presence_penalty?: number;
+ /**
+ * 生成文本的随机度量,用于控制文本的创造性和多样性
+ * @default 0.6
+ */
+ temperature?: number;
+ /**
+ * 控制生成文本中最高概率的单个 token
+ * @default 1
+ */
+ top_p?: number;
+}
diff --git a/src/utils/file.ts b/src/utils/file.ts
index 651e4c2a..338c70e1 100644
--- a/src/utils/file.ts
+++ b/src/utils/file.ts
@@ -6,6 +6,10 @@ export const getModelPathByAgentId = (agentId: string) => {
return `${MODEL_SCHEMA}://${agentId}`;
};
+export const isLocalModelPath = (path: string) => {
+ return path.startsWith(MODEL_SCHEMA);
+};
+
export const getAudioPathByDanceId = (danceId: string) => {
return `${AUDIO_SCHEMA}://${danceId}`;
};
diff --git a/src/utils/imageToBase64.ts b/src/utils/imageToBase64.ts
index 2bffe43f..4ca1ce43 100644
--- a/src/utils/imageToBase64.ts
+++ b/src/utils/imageToBase64.ts
@@ -1,3 +1,5 @@
+import mime from 'mime';
+
export const imageToBase64 = ({
size,
img,
@@ -81,3 +83,14 @@ export const blobToDataURI = (blob: Blob) => {
reader.readAsDataURL(blob);
});
};
+
+export const base64ToFile = (base64: string, fileName: string) => {
+ const arr = base64.split('base64,');
+ const binaryString = atob(arr[1]);
+ // @ts-ignore
+ const mimeType = arr[0].match(/:(.*?);/)[1];
+ const uint8Array = Uint8Array.from(binaryString, (char) => char.charCodeAt(0));
+ return new File([uint8Array], `${fileName}.${mime.getExtension(mimeType)}`, {
+ type: mimeType,
+ });
+};