Download frontend/src/components/ai-elements/context.tsx from yonagush/deerflow: direct link, hf CLI and curl.
- Browser
- Download file 9.76 kB
-
https://huggingface.co/spaces/yonagush/deerflow/resolve/main/frontend/src/components/ai-elements/context.tsx
- Command line
-
hf download hf://spaces/yonagush/deerflow/frontend/src/components/ai-elements/context.tsx
-
curl -L -o context.tsx https://huggingface.co/spaces/yonagush/deerflow/resolve/main/frontend/src/components/ai-elements/context.tsx
9.76 kB
| "use client"; | |
| import { Button } from "@/components/ui/button"; | |
| import { | |
| HoverCard, | |
| HoverCardContent, | |
| HoverCardTrigger, | |
| } from "@/components/ui/hover-card"; | |
| import { Progress } from "@/components/ui/progress"; | |
| import { cn } from "@/lib/utils"; | |
| import type { LanguageModelUsage } from "ai"; | |
| import { type ComponentProps, createContext, useContext } from "react"; | |
| import { getUsage } from "tokenlens"; | |
| const PERCENT_MAX = 100; | |
| const ICON_RADIUS = 10; | |
| const ICON_VIEWBOX = 24; | |
| const ICON_CENTER = 12; | |
| const ICON_STROKE_WIDTH = 2; | |
| type ModelId = string; | |
| type ContextSchema = { | |
| usedTokens: number; | |
| maxTokens: number; | |
| usage?: LanguageModelUsage; | |
| modelId?: ModelId; | |
| }; | |
| const ContextContext = createContext<ContextSchema | null>(null); | |
| const useContextValue = () => { | |
| const context = useContext(ContextContext); | |
| if (!context) { | |
| throw new Error("Context components must be used within Context"); | |
| } | |
| return context; | |
| }; | |
| export type ContextProps = ComponentProps<typeof HoverCard> & ContextSchema; | |
| export const Context = ({ | |
| usedTokens, | |
| maxTokens, | |
| usage, | |
| modelId, | |
| ...props | |
| }: ContextProps) => ( | |
| <ContextContext.Provider | |
| value={{ | |
| usedTokens, | |
| maxTokens, | |
| usage, | |
| modelId, | |
| }} | |
| > | |
| <HoverCard closeDelay={0} openDelay={0} {...props} /> | |
| </ContextContext.Provider> | |
| ); | |
| const ContextIcon = () => { | |
| const { usedTokens, maxTokens } = useContextValue(); | |
| const circumference = 2 * Math.PI * ICON_RADIUS; | |
| const usedPercent = usedTokens / maxTokens; | |
| const dashOffset = circumference * (1 - usedPercent); | |
| return ( | |
| <svg | |
| aria-label="Model context usage" | |
| height="20" | |
| role="img" | |
| style={{ color: "currentcolor" }} | |
| viewBox={`0 0 ${ICON_VIEWBOX} ${ICON_VIEWBOX}`} | |
| width="20" | |
| > | |
| <circle | |
| cx={ICON_CENTER} | |
| cy={ICON_CENTER} | |
| fill="none" | |
| opacity="0.25" | |
| r={ICON_RADIUS} | |
| stroke="currentColor" | |
| strokeWidth={ICON_STROKE_WIDTH} | |
| /> | |
| <circle | |
| cx={ICON_CENTER} | |
| cy={ICON_CENTER} | |
| fill="none" | |
| opacity="0.7" | |
| r={ICON_RADIUS} | |
| stroke="currentColor" | |
| strokeDasharray={`${circumference} ${circumference}`} | |
| strokeDashoffset={dashOffset} | |
| strokeLinecap="round" | |
| strokeWidth={ICON_STROKE_WIDTH} | |
| style={{ transformOrigin: "center", transform: "rotate(-90deg)" }} | |
| /> | |
| </svg> | |
| ); | |
| }; | |
| export type ContextTriggerProps = ComponentProps<typeof Button>; | |
| export const ContextTrigger = ({ children, ...props }: ContextTriggerProps) => { | |
| const { usedTokens, maxTokens } = useContextValue(); | |
| const usedPercent = usedTokens / maxTokens; | |
| const renderedPercent = new Intl.NumberFormat("en-US", { | |
| style: "percent", | |
| maximumFractionDigits: 1, | |
| }).format(usedPercent); | |
| return ( | |
| <HoverCardTrigger asChild> | |
| {children ?? ( | |
| <Button type="button" variant="ghost" {...props}> | |
| <span className="font-medium text-muted-foreground"> | |
| {renderedPercent} | |
| </span> | |
| <ContextIcon /> | |
| </Button> | |
| )} | |
| </HoverCardTrigger> | |
| ); | |
| }; | |
| export type ContextContentProps = ComponentProps<typeof HoverCardContent>; | |
| export const ContextContent = ({ | |
| className, | |
| ...props | |
| }: ContextContentProps) => ( | |
| <HoverCardContent | |
| className={cn("min-w-60 divide-y overflow-hidden p-0", className)} | |
| {...props} | |
| /> | |
| ); | |
| export type ContextContentHeaderProps = ComponentProps<"div">; | |
| export const ContextContentHeader = ({ | |
| children, | |
| className, | |
| ...props | |
| }: ContextContentHeaderProps) => { | |
| const { usedTokens, maxTokens } = useContextValue(); | |
| const usedPercent = usedTokens / maxTokens; | |
| const displayPct = new Intl.NumberFormat("en-US", { | |
| style: "percent", | |
| maximumFractionDigits: 1, | |
| }).format(usedPercent); | |
| const used = new Intl.NumberFormat("en-US", { | |
| notation: "compact", | |
| }).format(usedTokens); | |
| const total = new Intl.NumberFormat("en-US", { | |
| notation: "compact", | |
| }).format(maxTokens); | |
| return ( | |
| <div className={cn("w-full space-y-2 p-3", className)} {...props}> | |
| {children ?? ( | |
| <> | |
| <div className="flex items-center justify-between gap-3 text-xs"> | |
| <p>{displayPct}</p> | |
| <p className="font-mono text-muted-foreground"> | |
| {used} / {total} | |
| </p> | |
| </div> | |
| <div className="space-y-2"> | |
| <Progress className="bg-muted" value={usedPercent * PERCENT_MAX} /> | |
| </div> | |
| </> | |
| )} | |
| </div> | |
| ); | |
| }; | |
| export type ContextContentBodyProps = ComponentProps<"div">; | |
| export const ContextContentBody = ({ | |
| children, | |
| className, | |
| ...props | |
| }: ContextContentBodyProps) => ( | |
| <div className={cn("w-full p-3", className)} {...props}> | |
| {children} | |
| </div> | |
| ); | |
| export type ContextContentFooterProps = ComponentProps<"div">; | |
| export const ContextContentFooter = ({ | |
| children, | |
| className, | |
| ...props | |
| }: ContextContentFooterProps) => { | |
| const { modelId, usage } = useContextValue(); | |
| const costUSD = modelId | |
| ? getUsage({ | |
| modelId, | |
| usage: { | |
| input: usage?.inputTokens ?? 0, | |
| output: usage?.outputTokens ?? 0, | |
| }, | |
| }).costUSD?.totalUSD | |
| : undefined; | |
| const totalCost = new Intl.NumberFormat("en-US", { | |
| style: "currency", | |
| currency: "USD", | |
| }).format(costUSD ?? 0); | |
| return ( | |
| <div | |
| className={cn( | |
| "flex w-full items-center justify-between gap-3 bg-secondary p-3 text-xs", | |
| className | |
| )} | |
| {...props} | |
| > | |
| {children ?? ( | |
| <> | |
| <span className="text-muted-foreground">Total cost</span> | |
| <span>{totalCost}</span> | |
| </> | |
| )} | |
| </div> | |
| ); | |
| }; | |
| export type ContextInputUsageProps = ComponentProps<"div">; | |
| export const ContextInputUsage = ({ | |
| className, | |
| children, | |
| ...props | |
| }: ContextInputUsageProps) => { | |
| const { usage, modelId } = useContextValue(); | |
| const inputTokens = usage?.inputTokens ?? 0; | |
| if (children) { | |
| return children; | |
| } | |
| if (!inputTokens) { | |
| return null; | |
| } | |
| const inputCost = modelId | |
| ? getUsage({ | |
| modelId, | |
| usage: { input: inputTokens, output: 0 }, | |
| }).costUSD?.totalUSD | |
| : undefined; | |
| const inputCostText = new Intl.NumberFormat("en-US", { | |
| style: "currency", | |
| currency: "USD", | |
| }).format(inputCost ?? 0); | |
| return ( | |
| <div | |
| className={cn("flex items-center justify-between text-xs", className)} | |
| {...props} | |
| > | |
| <span className="text-muted-foreground">Input</span> | |
| <TokensWithCost costText={inputCostText} tokens={inputTokens} /> | |
| </div> | |
| ); | |
| }; | |
| export type ContextOutputUsageProps = ComponentProps<"div">; | |
| export const ContextOutputUsage = ({ | |
| className, | |
| children, | |
| ...props | |
| }: ContextOutputUsageProps) => { | |
| const { usage, modelId } = useContextValue(); | |
| const outputTokens = usage?.outputTokens ?? 0; | |
| if (children) { | |
| return children; | |
| } | |
| if (!outputTokens) { | |
| return null; | |
| } | |
| const outputCost = modelId | |
| ? getUsage({ | |
| modelId, | |
| usage: { input: 0, output: outputTokens }, | |
| }).costUSD?.totalUSD | |
| : undefined; | |
| const outputCostText = new Intl.NumberFormat("en-US", { | |
| style: "currency", | |
| currency: "USD", | |
| }).format(outputCost ?? 0); | |
| return ( | |
| <div | |
| className={cn("flex items-center justify-between text-xs", className)} | |
| {...props} | |
| > | |
| <span className="text-muted-foreground">Output</span> | |
| <TokensWithCost costText={outputCostText} tokens={outputTokens} /> | |
| </div> | |
| ); | |
| }; | |
| export type ContextReasoningUsageProps = ComponentProps<"div">; | |
| export const ContextReasoningUsage = ({ | |
| className, | |
| children, | |
| ...props | |
| }: ContextReasoningUsageProps) => { | |
| const { usage, modelId } = useContextValue(); | |
| const reasoningTokens = usage?.reasoningTokens ?? 0; | |
| if (children) { | |
| return children; | |
| } | |
| if (!reasoningTokens) { | |
| return null; | |
| } | |
| const reasoningCost = modelId | |
| ? getUsage({ | |
| modelId, | |
| usage: { reasoningTokens }, | |
| }).costUSD?.totalUSD | |
| : undefined; | |
| const reasoningCostText = new Intl.NumberFormat("en-US", { | |
| style: "currency", | |
| currency: "USD", | |
| }).format(reasoningCost ?? 0); | |
| return ( | |
| <div | |
| className={cn("flex items-center justify-between text-xs", className)} | |
| {...props} | |
| > | |
| <span className="text-muted-foreground">Reasoning</span> | |
| <TokensWithCost costText={reasoningCostText} tokens={reasoningTokens} /> | |
| </div> | |
| ); | |
| }; | |
| export type ContextCacheUsageProps = ComponentProps<"div">; | |
| export const ContextCacheUsage = ({ | |
| className, | |
| children, | |
| ...props | |
| }: ContextCacheUsageProps) => { | |
| const { usage, modelId } = useContextValue(); | |
| const cacheTokens = usage?.cachedInputTokens ?? 0; | |
| if (children) { | |
| return children; | |
| } | |
| if (!cacheTokens) { | |
| return null; | |
| } | |
| const cacheCost = modelId | |
| ? getUsage({ | |
| modelId, | |
| usage: { cacheReads: cacheTokens, input: 0, output: 0 }, | |
| }).costUSD?.totalUSD | |
| : undefined; | |
| const cacheCostText = new Intl.NumberFormat("en-US", { | |
| style: "currency", | |
| currency: "USD", | |
| }).format(cacheCost ?? 0); | |
| return ( | |
| <div | |
| className={cn("flex items-center justify-between text-xs", className)} | |
| {...props} | |
| > | |
| <span className="text-muted-foreground">Cache</span> | |
| <TokensWithCost costText={cacheCostText} tokens={cacheTokens} /> | |
| </div> | |
| ); | |
| }; | |
| const TokensWithCost = ({ | |
| tokens, | |
| costText, | |
| }: { | |
| tokens?: number; | |
| costText?: string; | |
| }) => ( | |
| <span> | |
| {tokens === undefined | |
| ? "—" | |
| : new Intl.NumberFormat("en-US", { | |
| notation: "compact", | |
| }).format(tokens)} | |
| {costText ? ( | |
| <span className="ml-2 text-muted-foreground">• {costText}</span> | |
| ) : null} | |
| </span> | |
| ); | |