Skip to content

Commit

Permalink
Merge pull request #77 from addyosmani/feature/reasoning
Browse files Browse the repository at this point in the history
feat: DeepSeek R1 models with thought process
  • Loading branch information
jakobhoeg authored Jan 22, 2025
2 parents 1eef8a3 + 1b200e1 commit ff398b3
Show file tree
Hide file tree
Showing 15 changed files with 308 additions and 49 deletions.
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
},
"dependencies": {
"@hookform/resolvers": "^3.3.4",
"@mlc-ai/web-llm": "^0.2.77",
"@mlc-ai/web-llm": "^0.2.78",
"@next/third-parties": "^14.2.3",
"@radix-ui/react-avatar": "^1.0.4",
"@radix-ui/react-dialog": "^1.0.5",
Expand Down
11 changes: 11 additions & 0 deletions public/model-icons/deepseek.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 21 additions & 0 deletions public/model-icons/google.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
44 changes: 44 additions & 0 deletions public/model-icons/meta.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 10 additions & 0 deletions public/model-icons/microsoft.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
37 changes: 37 additions & 0 deletions public/model-icons/mistral.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/model-icons/qwen.webp
Binary file not shown.
1 change: 1 addition & 0 deletions public/model-icons/together.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 0 additions & 1 deletion src/app/(chat)/c/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ export default function Page({ params }: { params: { id: string } }) {

const getChatById = useChatStore((state) => state.getChatById);
const chat = getChatById(id);
console.log(chat);

if (!chat) {
return notFound();
Expand Down
49 changes: 23 additions & 26 deletions src/components/chat/chat-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -112,38 +112,24 @@ export default function ChatList({
}
};

// if (messages.length === 0) {
// return (
// <div className="w-full h-full flex justify-center items-center p-5 md:p-0">
// <div className="relative flex flex-col gap-4 items-center justify-center w-full h-full">
// <div></div>
// <div className="flex flex-col gap-1 items-center">
// <Image
// src="/logo.svg"
// alt="AI"
// width={70}
// height={70}
// className="dark:invert"
// />
// <p className="text-center text-2xl md:text-5xl font-semibold text-muted-foreground/75">
// How can I help you today?
// </p>
// <p className="text-center text-sm text-muted-foreground/60 max-w-lg">
// Models with <strong>(1k)</strong> suffix lowers VRAM requirements
// by ~2-3GB.
// </p>
// </div>
// </div>
// </div>
// );
// }
const getThinkContent = (content: string) => {
const match = content.match(/<think>([\s\S]*?)(?:<\/think>|$)/);
return match ? match[1].trim() : null;
};

return (
<div className="flex-1 w-full overflow-y-auto">
<ChatMessageList>
{messages.map((message, index) => {

const variant = message.role === "user" ? "sent" : "received";

const thinkContent = message.role === "assistant" && message.content ?
getThinkContent(message.content.toString()) : null;

const cleanContent = message.content ?
message.content.toString().replace(/<think>[\s\S]*?(?:<\/think>|$)/g, '').trim() : '';

return (
<motion.div
key={index}
Expand Down Expand Up @@ -171,6 +157,17 @@ export default function ChatList({
/>
<ChatBubbleMessage isLoading={loadingSubmit && messages.indexOf(message) === messages.length - 1}>
<div className="flex flex-col gap-1">
{thinkContent && message.role === "assistant" && (
<details className="mb-1 text-sm" open>
<summary className="cursor-pointer text-muted-foreground hover:text-foreground">
Thought process
</summary>
<div className="mt-1 text-muted-foreground pl-3 border-l-2 border-foreground/10">
<Markdown remarkPlugins={[remarkGfm]}>{thinkContent}</Markdown>
</div>
</details>
)}

{message.fileName && (
<div className="flex items-center gap-2 border border-green-500 border-opacity-10 rounded-sm bg-green-500/20 p-2 text-sm">
<FileTextIcon className="w-4 h-4" />
Expand All @@ -193,7 +190,7 @@ export default function ChatList({
)}
</div>

{message.content && typeof message.content === 'string' && message.content.toString()
{cleanContent && typeof cleanContent === 'string' && cleanContent
.split("```")
.map((part: string, index: number) => {
if (index % 2 === 0) {
Expand Down
63 changes: 47 additions & 16 deletions src/components/chat/chat-topbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ import { CaretSortIcon, HamburgerMenuIcon } from "@radix-ui/react-icons";
import { Sidebar } from "../sidebar";
import { Message } from "ai/react";
import useChatStore from "@/hooks/useChatStore";
import { Models, Model } from "@/lib/models";
import { Models, Model, ModelGroup, modelDetailsList } from "@/lib/models";
import { Badge } from "../ui/badge";
import { Image } from "lucide-react";
import Image from "next/image";

interface ChatTopbarProps {
chatId?: string;
Expand All @@ -30,6 +30,21 @@ export default function ChatTopbar({ chatId, stop }: ChatTopbarProps) {
const setSelectedModel = useChatStore((state) => state.setSelectedModel);
const isLoading = useChatStore((state) => state.isLoading);

const groupedModels = React.useMemo(() => {
return Models.reduce((acc, model) => {
if (!acc[model.group]) {
acc[model.group] = [];
}
acc[model.group].push(model);
return acc;
}, {} as Record<string, Model[]>);
}, []);

const getGroupIcon = (group: string) => {
const details = modelDetailsList.find(m => m.group === group);
return details?.icon;
};

return (
<div className="w-full flex px-4 py-6 items-center justify-between lg:justify-center ">
<Sheet>
Expand Down Expand Up @@ -59,20 +74,36 @@ export default function ChatTopbar({ chatId, stop }: ChatTopbarProps) {
</Button>
</PopoverTrigger>
<PopoverContent className="w-[200px] md:w-[300px] max-h-96 overflow-y-scroll p-1">
{Models.map((model) => (
<Button
key={model.name}
variant="ghost"
className="w-full justify-start flex gap-2 items-center truncate"
onClick={() => {
setSelectedModel(model);
setOpen(false);
}}
>
{model.displayName}
{model.badge && <Badge>{model.badge}</Badge>}
{model.vision && <Badge>Vision</Badge>}
</Button>
{Object.entries(groupedModels).map(([group, models]) => (
<div key={group}>
<div className="px-2 py-1.5 text-sm font-semibold text-muted-foreground flex items-center gap-2">
{getGroupIcon(group) && (
<Image
src={getGroupIcon(group)!}
alt={`${group} Logo`}
width={16}
height={16}
className="object-contain shrink-0"
/>
)}
{group}
</div>
{models.map((model) => (
<Button
key={model.name}
variant="ghost"
className="w-full justify-start flex gap-2 items-center truncate"
onClick={() => {
setSelectedModel(model);
setOpen(false);
}}
>
{model.displayName}
{model.badge && <Badge>{model.badge}</Badge>}
{model.vision && <Badge>Vision</Badge>}
</Button>
))}
</div>
))}
</PopoverContent>
</Popover>
Expand Down
12 changes: 12 additions & 0 deletions src/hooks/useChat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,18 @@ export const useChat = ({ id, initialMessages }: UseChatOptions) => {
content: report.text,
} as AssistantMessage
]);

// Clear assistant msg when the model loading is done
if (report.text.includes('Finish loading')) {
setStoredMessages((messages) => [
...messages.slice(0, -1),
{
id: generateMessageId(),
role: 'assistant',
content: '',
} as AssistantMessage
]);
}
},
};

Expand Down
2 changes: 1 addition & 1 deletion src/hooks/useChatStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ const useChatStore = create<State & Actions>()(
userName: 'User',
setUserName: (userName) => set({ userName }),

selectedModel: Models[5],
selectedModel: Models[7],
setSelectedModel: (model: Model) =>
set((state: State) => ({
selectedModel:
Expand Down
Loading

0 comments on commit ff398b3

Please sign in to comment.