Skip to content

Commit

Permalink
🐛 fix: fix can send message on uploading files (lobehub#3618)
Browse files Browse the repository at this point in the history
* 🔧 chore: add deps

* ♻️ refactor: refactor code

* Update package.json

* fix send issue on uploading file

* add tests
  • Loading branch information
arvinxx authored Aug 27, 2024
1 parent 47da20d commit fe4329a
Show file tree
Hide file tree
Showing 11 changed files with 129 additions and 36 deletions.
8 changes: 8 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@
},
"dependencies": {
"@ant-design/icons": "^5.4.0",
"@ant-design/pro-components": "^2.7.10",
"@anthropic-ai/sdk": "^0.24.3",
"@auth/core": "0.28.0",
"@aws-sdk/client-bedrock-runtime": "^3.637.0",
Expand All @@ -123,6 +124,7 @@
"@lobehub/ui": "^1.149.2",
"@neondatabase/serverless": "^0.9.4",
"@next/third-parties": "^14.2.6",
"@react-spring/web": "^9.7.3",
"@sentry/nextjs": "^7.119.0",
"@t3-oss/env-nextjs": "^0.11.0",
"@tanstack/react-query": "^5.52.1",
Expand All @@ -147,6 +149,7 @@
"drizzle-zod": "^0.5.1",
"fast-deep-equal": "^3.1.3",
"file-type": "^19.4.1",
"framer-motion": "^11.2.6",
"gpt-tokenizer": "^2.2.1",
"i18next": "^23.14.0",
"i18next-browser-languagedetector": "^7.2.1",
Expand All @@ -172,6 +175,7 @@
"officeparser": "^4.1.1",
"ollama": "^0.5.8",
"openai": "^4.56.0",
"openapi-fetch": "^0.9.7",
"partial-json": "^0.1.7",
"pdf-parse": "^1.1.1",
"pdfjs-dist": "4.4.168",
Expand All @@ -183,6 +187,7 @@
"query-string": "^9.1.0",
"random-words": "^2.0.1",
"react": "^18.3.1",
"react-confetti": "^6.1.0",
"react-dom": "^18.3.1",
"react-fast-marquee": "^1.6.5",
"react-hotkeys-hook": "^4.5.0",
Expand All @@ -199,6 +204,7 @@
"rtl-detect": "^1.1.2",
"semver": "^7.6.3",
"sharp": "^0.33.5",
"stripe": "^15.8.0",
"superjson": "^2.2.1",
"svix": "^1.30.0",
"swr": "^2.2.5",
Expand Down Expand Up @@ -270,12 +276,14 @@
"markdown-table": "^3.0.3",
"node-fetch": "^3.3.2",
"node-gyp": "^10.2.0",
"openapi-typescript": "^6.7.6",
"p-map": "^7.0.2",
"prettier": "^3.3.3",
"remark-cli": "^11.0.0",
"remark-parse": "^10.0.2",
"semantic-release": "^21.1.2",
"stylelint": "^15.11.0",
"supports-color": "8",
"tsx": "^4.17.0",
"typescript": "^5.5.4",
"unified": "^11.0.5",
Expand Down
2 changes: 1 addition & 1 deletion src/app/(main)/settings/_layout/Desktop/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import { memo, useRef } from 'react';
import { useTranslation } from 'react-i18next';
import { Flexbox } from 'react-layout-kit';

import SettingContainer from '@/features/Setting//SettingContainer';
import Footer from '@/features/Setting/Footer';
import SettingContainer from '@/features/Setting/SettingContainer';
import { useActiveSettingsKey } from '@/hooks/useActiveSettingsKey';
import { SettingsTabs } from '@/store/global/initialState';

Expand Down
14 changes: 11 additions & 3 deletions src/components/ModelIcon/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {
AiMass,
Adobe,
Ai21,
Ai360,
AiMass,
Aws,
Aya,
Azure,
Expand Down Expand Up @@ -49,6 +49,7 @@ const ModelIcon = memo<ModelProviderIconProps>(({ model: originModel, size = 12
const model = originModel.toLowerCase();

// currently supported models, maybe not in its own provider
if (model.includes('text-embedding-')) return <OpenAI.Avatar size={size} />;
if (model.includes('gpt-3')) return <OpenAI.Avatar size={size} type={'gpt3'} />;
if (model.includes('gpt-4')) return <OpenAI.Avatar size={size} type={'gpt4'} />;
if (model.includes('glm-') || model.includes('chatglm')) return <ChatGLM.Avatar size={size} />;
Expand All @@ -63,7 +64,13 @@ const ModelIcon = memo<ModelProviderIconProps>(({ model: originModel, size = 12
if (model.includes('moonshot')) return <Moonshot.Avatar size={size} />;
if (model.includes('qwen')) return <Tongyi.Avatar background={Tongyi.colorPrimary} size={size} />;
if (model.includes('minmax') || model.includes('abab')) return <Minimax.Avatar size={size} />;
if (model.includes('mistral') || model.includes('mixtral') || model.includes('codestral') || model.includes('mathstral')) return <Mistral.Avatar size={size} />;
if (
model.includes('mistral') ||
model.includes('mixtral') ||
model.includes('codestral') ||
model.includes('mathstral')
)
return <Mistral.Avatar size={size} />;
if (model.includes('pplx') || model.includes('sonar')) return <Perplexity.Avatar size={size} />;
if (model.includes('yi-')) return <Yi.Avatar size={size} />;
if (model.startsWith('openrouter')) return <OpenRouter.Avatar size={size} />; // only for Cinematika and Auto
Expand Down Expand Up @@ -98,7 +105,8 @@ const ModelIcon = memo<ModelProviderIconProps>(({ model: originModel, size = 12
)
return <Stability.Avatar size={size} />;

if (model.includes('phi3') || model.includes('phi-3') || model.includes('wizardlm')) return <Azure.Avatar size={size} />;
if (model.includes('phi3') || model.includes('phi-3') || model.includes('wizardlm'))
return <Azure.Avatar size={size} />;
if (model.includes('firefly')) return <Adobe.Avatar size={size} />;
if (model.includes('jamba') || model.includes('j2-')) return <Ai21.Avatar size={size} />;
});
Expand Down
33 changes: 33 additions & 0 deletions src/database/server/models/__tests__/file.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -570,4 +570,37 @@ describe('FileModel', () => {
const data = await fileModel.countFilesByHash('hash1');
expect(data).toEqual(2);
});

describe('countUsage', () => {
const sharedFileList = [
{
name: 'document.pdf',
url: 'https://example.com/document.pdf',
size: 1000,
fileType: 'application/pdf',
userId,
},
{
name: 'image.jpg',
url: 'https://example.com/image.jpg',
size: 500,
fileType: 'image/jpeg',
userId,
},
{
name: 'audio.mp3',
url: 'https://example.com/audio.mp3',
size: 2000,
fileType: 'audio/mpeg',
userId,
},
];

it('should get total size of files for the user', async () => {
await serverDB.insert(files).values(sharedFileList);
const size = await fileModel.countUsage();

expect(size).toBe(3500);
});
});
});
13 changes: 12 additions & 1 deletion src/database/server/models/embedding.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { eq } from 'drizzle-orm';
import { count, eq } from 'drizzle-orm';
import { and } from 'drizzle-orm/expressions';

import { serverDB } from '@/database/server';
Expand Down Expand Up @@ -47,4 +47,15 @@ export class EmbeddingModel {
where: and(eq(embeddings.id, id), eq(embeddings.userId, this.userId)),
});
};

countUsage = async () => {
const result = await serverDB
.select({
count: count(),
})
.from(embeddings)
.where(eq(embeddings.userId, this.userId));

return result[0].count;
};
}
13 changes: 12 additions & 1 deletion src/database/server/models/file.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { asc, count, eq, ilike, inArray, notExists } from 'drizzle-orm';
import { asc, count, eq, ilike, inArray, notExists, sum } from 'drizzle-orm';
import { and, desc } from 'drizzle-orm/expressions';

import { serverDBEnv } from '@/config/db';
Expand Down Expand Up @@ -91,6 +91,17 @@ export class FileModel {
return serverDB.delete(globalFiles).where(eq(globalFiles.hashId, hashId));
};

countUsage = async () => {
const result = await serverDB
.select({
totalSize: sum(files.size),
})
.from(files)
.where(eq(files.userId, this.userId));

return parseInt(result[0].totalSize!) || 0;
};

deleteMany = async (ids: string[]) => {
const fileList = await this.findByIds(ids);
const hashList = fileList.map((file) => file.fileHash!);
Expand Down
19 changes: 14 additions & 5 deletions src/features/ChatInput/useSend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,24 @@ export const useSendMessage = () => {

const clearChatUploadFileList = useFileStore((s) => s.clearChatUploadFileList);

const isUploadingFiles = useFileStore(fileChatSelectors.isUploadingFiles);
const isSendButtonDisabledByMessage = useChatStore(chatSelectors.isSendButtonDisabledByMessage);

const canSend = !isUploadingFiles && !isSendButtonDisabledByMessage;

const send = useCallback((params: UseSendMessageParams = {}) => {
const store = useChatStore.getState();
if (chatSelectors.isAIGenerating(store)) return;

// if uploading file or send button is disabled by message, then we should not send the message
const isUploadingFiles = fileChatSelectors.isUploadingFiles(useFileStore.getState());
const isSendButtonDisabledByMessage = chatSelectors.isSendButtonDisabledByMessage(
useChatStore.getState(),
);

const canSend = !isUploadingFiles && !isSendButtonDisabledByMessage;
if (!canSend) return;

const fileList = fileChatSelectors.chatUploadFileList(useFileStore.getState());
// if there is no message and no image, then we should not send the message
if (!store.inputMessage && fileList.length === 0) return;
Expand All @@ -44,10 +58,5 @@ export const useSendMessage = () => {
// }
}, []);

const isUploadingFiles = useFileStore(fileChatSelectors.isUploadingFiles);
const isSendButtonDisabledByMessage = useChatStore(chatSelectors.isSendButtonDisabledByMessage);

const canSend = !isUploadingFiles && !isSendButtonDisabledByMessage;

return useMemo(() => ({ canSend, send }), [canSend]);
};
51 changes: 28 additions & 23 deletions src/features/Setting/SettingContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,37 @@ import { useResponsive } from 'antd-style';
import { PropsWithChildren, ReactNode, memo } from 'react';
import { Flexbox } from 'react-layout-kit';

const SettingContainer = memo<
PropsWithChildren<{ addonAfter?: ReactNode; addonBefore?: ReactNode }>
>(({ children, addonAfter, addonBefore }) => {
const { mobile = false } = useResponsive();
return (
<Flexbox
align={'center'}
height={'100%'}
paddingBlock={mobile ? undefined : 32}
style={{ overflowX: 'hidden', overflowY: 'auto' }}
width={'100%'}
>
{addonBefore}
interface SettingContainerProps {
addonAfter?: ReactNode;
addonBefore?: ReactNode;
fullWidth?: boolean;
}
const SettingContainer = memo<PropsWithChildren<SettingContainerProps>>(
({ children, addonAfter, addonBefore, fullWidth }) => {
const { mobile = false } = useResponsive();
return (
<Flexbox
gap={64}
paddingInline={mobile ? undefined : 24}
style={{
maxWidth: 1024,
}}
align={'center'}
height={'100%'}
paddingBlock={mobile ? undefined : 32}
style={{ overflowX: 'hidden', overflowY: 'auto' }}
width={'100%'}
>
{children}
{addonBefore}
<Flexbox
gap={64}
paddingInline={mobile ? undefined : 24}
style={{
maxWidth: fullWidth ? undefined : 1024,
}}
width={'100%'}
>
{children}
</Flexbox>
{addonAfter}
</Flexbox>
{addonAfter}
</Flexbox>
);
});
);
},
);

export default SettingContainer;
3 changes: 2 additions & 1 deletion src/server/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { User } from 'next-auth';
import { NextRequest } from 'next/server';

import { JWTPayload, LOBE_CHAT_AUTH_HEADER, enableClerk, enableNextAuth } from '@/const/auth';
import NextAuthEdge from '@/libs/next-auth/edge';

type ClerkAuth = ReturnType<typeof getAuth>;

Expand Down Expand Up @@ -55,6 +54,8 @@ export const createContext = async (request: NextRequest): Promise<Context> => {

if (enableNextAuth) {
try {
const { default: NextAuthEdge } = await import('@/libs/next-auth/edge');

const session = await NextAuthEdge.auth();
if (session && session?.user?.id) {
auth = session.user;
Expand Down
1 change: 0 additions & 1 deletion src/store/chat/slices/message/selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,6 @@ const isHasMessageLoading = (s: ChatStore) => s.messageLoadingIds.length > 0;

/**
* this function is used to determine whether the send button should be disabled
* @param s
*/
const isSendButtonDisabledByMessage = (s: ChatStore) =>
// 1. when there is message loading
Expand Down
8 changes: 8 additions & 0 deletions src/utils/format.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,11 @@ export const formatTime = (timeInSeconds: number): string => {
return `${(timeInSeconds / 3600).toFixed(2)} h`;
}
};

/**
* format number with comma
* @param num
*/
export const formatNumber = (num: any) => {
return new Intl.NumberFormat('en-US').format(num);
};

0 comments on commit fe4329a

Please sign in to comment.