enzostvs HF Staff commited on
Commit
3684768
·
1 Parent(s): 21ec9a7

show error for free limits

Browse files
app/api/ask/route.ts CHANGED
@@ -107,16 +107,26 @@ export async function POST(request: Request) {
107
 
108
  await writer.close();
109
  } catch (error) {
110
- console.error(error);
111
  try {
112
  const errorMessage =
113
  error instanceof Error
114
  ? error.message
115
  : "An error occurred while processing your request";
116
- const errorPayload = JSON.stringify({
117
- messageError: errorMessage,
118
- isError: true,
119
- });
 
 
 
 
 
 
 
 
 
 
 
120
  await writer.write(encoder.encode(`\n\n__ERROR__:${errorPayload}`));
121
  await writer.close();
122
  } catch (closeError) {
 
107
 
108
  await writer.close();
109
  } catch (error) {
 
110
  try {
111
  const errorMessage =
112
  error instanceof Error
113
  ? error.message
114
  : "An error occurred while processing your request";
115
+ let errorPayload = "";
116
+ if (
117
+ errorMessage?.includes("exceeded your monthly included credits")
118
+ ) {
119
+ errorPayload = JSON.stringify({
120
+ messageError: errorMessage,
121
+ showProMessage: true,
122
+ isError: true,
123
+ });
124
+ } else {
125
+ errorPayload = JSON.stringify({
126
+ messageError: errorMessage,
127
+ isError: true,
128
+ });
129
+ }
130
  await writer.write(encoder.encode(`\n\n__ERROR__:${errorPayload}`));
131
  await writer.close();
132
  } catch (closeError) {
components/ask-ai/useGeneration.ts CHANGED
@@ -326,11 +326,6 @@ export const useGeneration = (projectName: string) => {
326
  setIsLoading(false);
327
  return;
328
  }
329
- // if (abortController.current?.signal.aborted) {
330
- // toast.error("Generation aborted");
331
- // setIsLoading(false);
332
- // return;
333
- // }
334
  const chunk = decoder.decode(value, { stream: true });
335
  completeResponse += chunk;
336
 
@@ -339,13 +334,25 @@ export const useGeneration = (projectName: string) => {
339
  if (errorMatch) {
340
  try {
341
  const errorData = JSON.parse(errorMatch[1]);
 
342
  if (errorData.isError) {
343
  const lastMessageId =
344
  currentMessages[currentMessages.length - 1].id;
345
  updateMessage(lastMessageId, {
346
  isThinking: false,
347
  isAborted: true,
348
- content: `Error: ${errorData.messageError}`,
 
 
 
 
 
 
 
 
 
 
 
349
  });
350
  setIsLoading(false);
351
  return;
 
326
  setIsLoading(false);
327
  return;
328
  }
 
 
 
 
 
329
  const chunk = decoder.decode(value, { stream: true });
330
  completeResponse += chunk;
331
 
 
334
  if (errorMatch) {
335
  try {
336
  const errorData = JSON.parse(errorMatch[1]);
337
+ console.log("Parsed error data:", errorData);
338
  if (errorData.isError) {
339
  const lastMessageId =
340
  currentMessages[currentMessages.length - 1].id;
341
  updateMessage(lastMessageId, {
342
  isThinking: false,
343
  isAborted: true,
344
+ content: errorData?.showProMessage
345
+ ? `You have exceeded your monthly included credits with Hugging Face inference provider. Please consider upgrading to a pro plan.`
346
+ : `Error: ${errorData.messageError}`,
347
+ actions: errorData?.showProMessage
348
+ ? [
349
+ {
350
+ label: "Upgrade to Pro",
351
+ variant: "pro",
352
+ type: MessageActionType.UPGRADE_TO_PRO,
353
+ },
354
+ ]
355
+ : [],
356
  });
357
  setIsLoading(false);
358
  return;
components/chat/index.tsx CHANGED
@@ -2,7 +2,8 @@ import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
2
  import { useSession } from "next-auth/react";
3
  import { cn } from "@/lib/utils";
4
  import { ChevronRight, ExternalLink } from "lucide-react";
5
- import { useEffect, useRef } from "react";
 
6
  import Markdown from "react-markdown";
7
  import { useQueryClient } from "@tanstack/react-query";
8
  import { formatDistanceToNow } from "date-fns";
@@ -11,12 +12,14 @@ import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
11
  import { dracula } from "react-syntax-highlighter/dist/esm/styles/prism";
12
 
13
  import { useChat } from "./useChat";
14
- import { AiLoading } from "../ask-ai/loading";
15
- import Loading from "../loading";
16
- import { useGeneration } from "../ask-ai/useGeneration";
17
  import { MessageAction, MessageActionType, File } from "@/lib/type";
18
- import { Button } from "../ui/button";
19
- import { getFileIcon } from "../ask-ai/input-mentions";
 
 
20
 
21
  export function AppEditorChat({
22
  isNew,
@@ -33,6 +36,7 @@ export function AppEditorChat({
33
  const chatProjectName = isNew ? "new" : projectName ?? "new";
34
  const { messages } = useChat(chatProjectName);
35
  const { isLoading, createProject } = useGeneration(chatProjectName);
 
36
 
37
  const project = queryClient.getQueryData<SpaceEntry>(["project"]);
38
  const files = queryClient.getQueryData<File[]>(["files"]) ?? [];
@@ -49,6 +53,7 @@ export function AppEditorChat({
49
  if (!action) return;
50
  switch (action.type) {
51
  case MessageActionType.PUBLISH_PROJECT:
 
52
  return createProject(
53
  files ?? [],
54
  action.projectTitle ?? "",
@@ -60,6 +65,8 @@ export function AppEditorChat({
60
  `https://huggingface.co/spaces/${project?.name}`,
61
  "_blank"
62
  );
 
 
63
  }
64
  };
65
 
@@ -295,6 +302,13 @@ export function AppEditorChat({
295
  overlay={false}
296
  />
297
  )}
 
 
 
 
 
 
 
298
  {action.label}
299
  </Button>
300
  ))}
@@ -304,6 +318,7 @@ export function AppEditorChat({
304
  </div>
305
  ))}
306
  </div>
 
307
  </div>
308
  );
309
  }
 
2
  import { useSession } from "next-auth/react";
3
  import { cn } from "@/lib/utils";
4
  import { ChevronRight, ExternalLink } from "lucide-react";
5
+ import { useEffect, useRef, useState } from "react";
6
+ import Image from "next/image";
7
  import Markdown from "react-markdown";
8
  import { useQueryClient } from "@tanstack/react-query";
9
  import { formatDistanceToNow } from "date-fns";
 
12
  import { dracula } from "react-syntax-highlighter/dist/esm/styles/prism";
13
 
14
  import { useChat } from "./useChat";
15
+ import { AiLoading } from "@/components/ask-ai/loading";
16
+ import Loading from "@/components/loading";
17
+ import { useGeneration } from "@/components/ask-ai/useGeneration";
18
  import { MessageAction, MessageActionType, File } from "@/lib/type";
19
+ import { Button } from "@/components/ui/button";
20
+ import { getFileIcon } from "@/components/ask-ai/input-mentions";
21
+ import ProIcon from "@/assets/pro.svg";
22
+ import ProModal from "../pro-modal";
23
 
24
  export function AppEditorChat({
25
  isNew,
 
36
  const chatProjectName = isNew ? "new" : projectName ?? "new";
37
  const { messages } = useChat(chatProjectName);
38
  const { isLoading, createProject } = useGeneration(chatProjectName);
39
+ const [openProModal, setOpenProModal] = useState(false);
40
 
41
  const project = queryClient.getQueryData<SpaceEntry>(["project"]);
42
  const files = queryClient.getQueryData<File[]>(["files"]) ?? [];
 
53
  if (!action) return;
54
  switch (action.type) {
55
  case MessageActionType.PUBLISH_PROJECT:
56
+ const files = queryClient.getQueryData<File[]>(["files"]) ?? [];
57
  return createProject(
58
  files ?? [],
59
  action.projectTitle ?? "",
 
65
  `https://huggingface.co/spaces/${project?.name}`,
66
  "_blank"
67
  );
68
+ case MessageActionType.UPGRADE_TO_PRO:
69
+ return setOpenProModal(true);
70
  }
71
  };
72
 
 
302
  overlay={false}
303
  />
304
  )}
305
+ {action.type === MessageActionType.UPGRADE_TO_PRO && (
306
+ <Image
307
+ src={ProIcon}
308
+ alt="Pro Icon"
309
+ className="size-3.5"
310
+ />
311
+ )}
312
  {action.label}
313
  </Button>
314
  ))}
 
318
  </div>
319
  ))}
320
  </div>
321
+ <ProModal open={openProModal} onClose={setOpenProModal} />
322
  </div>
323
  );
324
  }
components/pro-modal/index.tsx ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { useLocalStorage } from "react-use";
2
+ import { Button } from "@/components/ui/button";
3
+ import { Dialog, DialogContent, DialogTitle } from "@/components/ui/dialog";
4
+ import { CheckCheck } from "lucide-react";
5
+
6
+ export const ProModal = ({
7
+ open,
8
+ onClose,
9
+ }: {
10
+ open: boolean;
11
+ onClose: React.Dispatch<React.SetStateAction<boolean>>;
12
+ }) => {
13
+ const handleProClick = () => {
14
+ window.open("https://huggingface.co/subscribe/pro?from=DeepSite", "_blank");
15
+ onClose(false);
16
+ };
17
+ return (
18
+ <Dialog open={open} onOpenChange={onClose}>
19
+ <DialogContent
20
+ showCloseButton={false}
21
+ className="sm:max-w-[425px] rounded-3xl! p-0! overflow-hidden"
22
+ >
23
+ <DialogTitle className="hidden" />
24
+ <header className="bg-linear-to-b from-indigo-500/25 dark:from-indigo-500/40 to-background px-6 pt-6">
25
+ <div className="flex items-center justify-start -space-x-4 mb-5">
26
+ <div className="size-14 rounded-full bg-pink-200 shadow-2xs flex items-center justify-center text-3xl opacity-50">
27
+ 🚀
28
+ </div>
29
+ <div className="size-16 rounded-full bg-amber-200 shadow-2xl flex items-center justify-center text-4xl z-2">
30
+ 🤩
31
+ </div>
32
+ <div className="size-14 rounded-full bg-sky-200 shadow-2xs flex items-center justify-center text-3xl opacity-50">
33
+ 🥳
34
+ </div>
35
+ </div>
36
+ <h2 className="text-2xl font-bold text-primary">
37
+ Only $9 to enhance your possibilities
38
+ </h2>
39
+ <p className="text-muted-foreground text-base mt-2 max-w-sm">
40
+ It seems like you have reached the monthly free limit of DeepSite.
41
+ </p>
42
+ </header>
43
+ <main className="flex flex-col items-start text-left relative px-6 pb-6">
44
+ <div className="w-1/2 h-px bg-accent/70 my-4"></div>
45
+ <p className="text-lg mt-3 text-primary font-semibold">
46
+ Upgrade to a <ProTag className="mx-1" /> Account, and unlock your
47
+ DeepSite high quota access ⚡
48
+ </p>
49
+ <ul className="mt-3 space-y-1 text-muted-foreground">
50
+ <li className="text-sm text-muted-foreground space-x-2 flex items-center justify-start gap-2 mb-3">
51
+ You&apos;ll also unlock some Hugging Face PRO features, like:
52
+ </li>
53
+ <li className="text-sm space-x-2 flex items-center justify-start gap-2">
54
+ <CheckCheck className="text-emerald-500 size-4" />
55
+ Get acces to thousands of AI app (ZeroGPU) with high quota
56
+ </li>
57
+ <li className="text-sm space-x-2 flex items-center justify-start gap-2">
58
+ <CheckCheck className="text-emerald-500 size-4" />
59
+ Get exclusive early access to new features and updates
60
+ </li>
61
+ <li className="text-sm space-x-2 flex items-center justify-start gap-2">
62
+ <CheckCheck className="text-emerald-500 size-4" />
63
+ Get free credits across all Inference Providers
64
+ </li>
65
+ <li className="text-sm text-muted-foreground space-x-2 flex items-center justify-start gap-2 mt-3">
66
+ ... and lots more!
67
+ </li>
68
+ </ul>
69
+ <Button
70
+ variant="default"
71
+ size="lg"
72
+ tabIndex={-1}
73
+ className="w-full mt-8"
74
+ onClick={handleProClick}
75
+ >
76
+ Subscribe to PRO ($9/month)
77
+ </Button>
78
+ </main>
79
+ </DialogContent>
80
+ </Dialog>
81
+ );
82
+ };
83
+
84
+ export const ProTag = ({
85
+ className,
86
+ ...props
87
+ }: {
88
+ className?: string;
89
+ onClick?: () => void;
90
+ }) => (
91
+ <span
92
+ className={`${className} ${
93
+ props.onClick ? "cursor-pointer" : ""
94
+ } bg-linear-to-br shadow-green-500/10 dark:shadow-green-500/20 inline-block -skew-x-12 from-pink-500 via-green-400 to-yellow-400 text-xs font-bold text-black shadow-lg rounded-md px-2.5 py-[3px]`}
95
+ {...props}
96
+ >
97
+ PRO
98
+ </span>
99
+ );
100
+ export default ProModal;
components/ui/button.tsx CHANGED
@@ -27,6 +27,7 @@ const buttonVariants = cva(
27
  "border border-indigo-500 bg-indigo-500 text-white hover:bg-indigo-600 dark:border-indigo-500/30 dark:bg-indigo-500/20 dark:text-indigo-400 dark:hover:bg-indigo-500/30",
28
  "ghost-bordered":
29
  "border bg-primary-foreground hover:bg-background hover:text-accent-foreground dark:hover:bg-accent/50",
 
30
  },
31
  size: {
32
  default: "h-9 px-4 py-2 has-[>svg]:px-3",
 
27
  "border border-indigo-500 bg-indigo-500 text-white hover:bg-indigo-600 dark:border-indigo-500/30 dark:bg-indigo-500/20 dark:text-indigo-400 dark:hover:bg-indigo-500/30",
28
  "ghost-bordered":
29
  "border bg-primary-foreground hover:bg-background hover:text-accent-foreground dark:hover:bg-accent/50",
30
+ pro: "bg-linear-to-br from-pink-500 dark:from-pink-500/50 via-green-500 dark:via-green-500/50 to-amber-500 dark:to-amber-500/50 text-white hover:brightness-120 font-semibold! [&_img]:grayscale [&_img]:brightness-1 [&_img]:invert dark:[&_img]:invert-0 dark:[&_img]:grayscale-0 dark:[&_img]:brightness-100",
31
  },
32
  size: {
33
  default: "h-9 px-4 py-2 has-[>svg]:px-3",
lib/type.ts CHANGED
@@ -9,6 +9,7 @@ export type ProviderType = "auto" | "cheapest" | "fastest" | string;
9
  export enum MessageActionType {
10
  PUBLISH_PROJECT = "PUBLISH_PROJECT",
11
  SEE_LIVE_PREVIEW = "SEE_LIVE_PREVIEW",
 
12
  }
13
  export type MessageAction = {
14
  label: string;
@@ -20,7 +21,7 @@ export type MessageAction = {
20
  projectTitle?: string;
21
  prompt?: string;
22
  messageReferrer?: number;
23
- }
24
  export type Message = {
25
  id: string;
26
  role: MessageRole;
@@ -35,11 +36,11 @@ export type Message = {
35
  };
36
  export type File = {
37
  path: string;
38
- content?: string
39
  };
40
  export interface Commit {
41
  title: string;
42
  oid: string;
43
  date: Date;
44
  }
45
- export type MessageRole = "user" | "assistant";
 
9
  export enum MessageActionType {
10
  PUBLISH_PROJECT = "PUBLISH_PROJECT",
11
  SEE_LIVE_PREVIEW = "SEE_LIVE_PREVIEW",
12
+ UPGRADE_TO_PRO = "UPGRADE_TO_PRO",
13
  }
14
  export type MessageAction = {
15
  label: string;
 
21
  projectTitle?: string;
22
  prompt?: string;
23
  messageReferrer?: number;
24
+ };
25
  export type Message = {
26
  id: string;
27
  role: MessageRole;
 
36
  };
37
  export type File = {
38
  path: string;
39
+ content?: string;
40
  };
41
  export interface Commit {
42
  title: string;
43
  oid: string;
44
  date: Date;
45
  }
46
+ export type MessageRole = "user" | "assistant";