perf(ai): improve ai message streaming performance
- Updated ai package version from 5.0.0-beta.34 to 5.0.0. - Added react-fast-compare package for improved memoization in chat components. - Refactored AIDisplayAnalyticsPart and AIDisplaySubscriptionsPart to utilize toolMemo for performance optimization. - Introduced a new utility function for throttled markdown parsing in AIMarkdownMessage component. Signed-off-by: Innei <tukon479@gmail.com>
This commit is contained in:
parent
29b96118dd
commit
6ca2ab1e2d
|
|
@ -51,7 +51,7 @@
|
|||
"@use-gesture/react": "10.3.1",
|
||||
"@welldone-software/why-did-you-render": "10.0.1",
|
||||
"@yornaath/batshit": "0.10.1",
|
||||
"ai": "5.0.0-beta.34",
|
||||
"ai": "5.0.0",
|
||||
"camelcase-keys": "9.1.3",
|
||||
"class-variance-authority": "0.7.1",
|
||||
"clsx": "2.1.1",
|
||||
|
|
@ -85,6 +85,7 @@
|
|||
"plain-shiki": "0.3.0",
|
||||
"re-resizable": "6.11.2",
|
||||
"react-blurhash": "0.3.0",
|
||||
"react-fast-compare": "3.2.2",
|
||||
"react-fast-marquee": "1.6.5",
|
||||
"react-hook-form": "7.60.0",
|
||||
"react-hotkeys-hook": "5.1.0",
|
||||
|
|
|
|||
|
|
@ -14,10 +14,10 @@ import {
|
|||
TableRow,
|
||||
} from "@follow/components/ui/table/index.js"
|
||||
import dayjs from "dayjs"
|
||||
import { memo } from "react"
|
||||
|
||||
import type { AIDisplayAnalyticsTool } from "../../__internal__/types"
|
||||
import { ErrorState, LoadingState } from "../common-states"
|
||||
import { toolMemo } from "./share"
|
||||
import { ChartPlaceholder, StatCard } from "./shared"
|
||||
|
||||
type AnalyticsData = AIDisplayAnalyticsTool["output"]["analyticsData"]
|
||||
|
|
@ -230,7 +230,7 @@ const OverviewAnalytics = ({ data }: { data: AnalyticsData["overviewStats"] }) =
|
|||
)
|
||||
}
|
||||
|
||||
export const AIDisplayAnalyticsPart = memo(({ part }: { part: AIDisplayAnalyticsTool }) => {
|
||||
export const AIDisplayAnalyticsPart = toolMemo(({ part }: { part: AIDisplayAnalyticsTool }) => {
|
||||
// Handle error state
|
||||
if (part.state === "output-error") {
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -6,12 +6,12 @@ import {
|
|||
CardTitle,
|
||||
} from "@follow/components/ui/card/index.js"
|
||||
import dayjs from "dayjs"
|
||||
import { memo } from "react"
|
||||
|
||||
import { FeedIcon } from "~/modules/feed/feed-icon"
|
||||
|
||||
import type { AIDisplaySubscriptionsTool } from "../../__internal__/types"
|
||||
import { ErrorState, LoadingState } from "../common-states"
|
||||
import { toolMemo } from "./share"
|
||||
import { AnalyticsMetrics, CategoryTag, EmptyState, StatCard } from "./shared"
|
||||
|
||||
type SubscriptionData = AIDisplaySubscriptionsTool["output"]["subscriptions"]
|
||||
|
|
@ -172,105 +172,107 @@ const GroupedSubscriptions = ({
|
|||
)
|
||||
}
|
||||
|
||||
export const AIDisplaySubscriptionsPart = memo(({ part }: { part: AIDisplaySubscriptionsTool }) => {
|
||||
// Handle error state
|
||||
if (part.state === "output-error") {
|
||||
return (
|
||||
<ErrorState
|
||||
title="Subscriptions Error"
|
||||
error="An error occurred while loading subscriptions"
|
||||
maxWidth="max-w-6xl"
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
// Handle loading state
|
||||
if (part.state !== "output-available" || !part.output) {
|
||||
return (
|
||||
<LoadingState
|
||||
title="Loading Subscriptions..."
|
||||
description="Fetching subscription data..."
|
||||
maxWidth="max-w-6xl"
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
// Extract output with proper typing
|
||||
const output = part.output as NonNullable<AIDisplaySubscriptionsTool["output"]>
|
||||
|
||||
const {
|
||||
subscriptions,
|
||||
displayType = "list",
|
||||
showAnalytics = true,
|
||||
showCategories = true,
|
||||
title,
|
||||
groupBy = "none",
|
||||
filterBy = "all",
|
||||
} = output
|
||||
|
||||
// Calculate statistics
|
||||
const totalSubscriptions = subscriptions.length
|
||||
const categoriesCount = new Set(
|
||||
subscriptions.map((s) => s.subscription?.category).filter(Boolean),
|
||||
).size
|
||||
const activeSubscriptions = subscriptions.filter((s) => !s.feed?.errorMessage).length
|
||||
const totalViews = subscriptions.reduce((acc, s) => acc + (s.subscription?.view || 0), 0)
|
||||
|
||||
const renderSubscriptions = () => {
|
||||
if (groupBy !== "none") {
|
||||
export const AIDisplaySubscriptionsPart = toolMemo(
|
||||
({ part }: { part: AIDisplaySubscriptionsTool }) => {
|
||||
// Handle error state
|
||||
if (part.state === "output-error") {
|
||||
return (
|
||||
<GroupedSubscriptions
|
||||
data={subscriptions}
|
||||
groupBy={groupBy}
|
||||
displayType={displayType}
|
||||
showAnalytics={showAnalytics}
|
||||
showCategories={showCategories}
|
||||
<ErrorState
|
||||
title="Subscriptions Error"
|
||||
error="An error occurred while loading subscriptions"
|
||||
maxWidth="max-w-6xl"
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
switch (displayType) {
|
||||
default: {
|
||||
// Handle loading state
|
||||
if (part.state !== "output-available" || !part.output) {
|
||||
return (
|
||||
<LoadingState
|
||||
title="Loading Subscriptions..."
|
||||
description="Fetching subscription data..."
|
||||
maxWidth="max-w-6xl"
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
// Extract output with proper typing
|
||||
const output = part.output as NonNullable<AIDisplaySubscriptionsTool["output"]>
|
||||
|
||||
const {
|
||||
subscriptions,
|
||||
displayType = "list",
|
||||
showAnalytics = true,
|
||||
showCategories = true,
|
||||
title,
|
||||
groupBy = "none",
|
||||
filterBy = "all",
|
||||
} = output
|
||||
|
||||
// Calculate statistics
|
||||
const totalSubscriptions = subscriptions.length
|
||||
const categoriesCount = new Set(
|
||||
subscriptions.map((s) => s.subscription?.category).filter(Boolean),
|
||||
).size
|
||||
const activeSubscriptions = subscriptions.filter((s) => !s.feed?.errorMessage).length
|
||||
const totalViews = subscriptions.reduce((acc, s) => acc + (s.subscription?.view || 0), 0)
|
||||
|
||||
const renderSubscriptions = () => {
|
||||
if (groupBy !== "none") {
|
||||
return (
|
||||
<SubscriptionsGrid
|
||||
<GroupedSubscriptions
|
||||
data={subscriptions}
|
||||
groupBy={groupBy}
|
||||
displayType={displayType}
|
||||
showAnalytics={showAnalytics}
|
||||
showCategories={showCategories}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
switch (displayType) {
|
||||
default: {
|
||||
return (
|
||||
<SubscriptionsGrid
|
||||
data={subscriptions}
|
||||
showAnalytics={showAnalytics}
|
||||
showCategories={showCategories}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Card className="mb-2 w-full min-w-0">
|
||||
<div className="w-[9999px] max-w-[calc(var(--ai-chat-layout-width,65ch)_-120px)]" />
|
||||
<CardHeader>
|
||||
<CardTitle className="text-text flex items-center gap-2 text-xl font-semibold">
|
||||
<span className="text-lg">📋</span>
|
||||
<span>{title || "My Subscriptions"}</span>
|
||||
</CardTitle>
|
||||
<CardDescription>
|
||||
{formatDisplayType(displayType)} • {formatFilterBy(filterBy)} • {formatGroupBy(groupBy)}
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="@container space-y-6">
|
||||
{/* Statistics Overview */}
|
||||
<div className="@[700px]:grid-cols-4 grid grid-cols-2 gap-4">
|
||||
<StatCard title="Total Subscriptions" value={totalSubscriptions} emoji="📊" />
|
||||
<StatCard
|
||||
title="Active Feeds"
|
||||
value={activeSubscriptions}
|
||||
description={`${totalSubscriptions - activeSubscriptions} inactive`}
|
||||
emoji="🟢"
|
||||
/>
|
||||
{showCategories && <StatCard title="Categories" value={categoriesCount} emoji="🏷️" />}
|
||||
<StatCard title="Total Views" value={totalViews.toLocaleString()} emoji="👀" />
|
||||
</div>
|
||||
return (
|
||||
<Card className="mb-2 w-full min-w-0">
|
||||
<div className="w-[9999px] max-w-[calc(var(--ai-chat-layout-width,65ch)_-120px)]" />
|
||||
<CardHeader>
|
||||
<CardTitle className="text-text flex items-center gap-2 text-xl font-semibold">
|
||||
<span className="text-lg">📋</span>
|
||||
<span>{title || "My Subscriptions"}</span>
|
||||
</CardTitle>
|
||||
<CardDescription>
|
||||
{formatDisplayType(displayType)} • {formatFilterBy(filterBy)} • {formatGroupBy(groupBy)}
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="@container space-y-6">
|
||||
{/* Statistics Overview */}
|
||||
<div className="@[700px]:grid-cols-4 grid grid-cols-2 gap-4">
|
||||
<StatCard title="Total Subscriptions" value={totalSubscriptions} emoji="📊" />
|
||||
<StatCard
|
||||
title="Active Feeds"
|
||||
value={activeSubscriptions}
|
||||
description={`${totalSubscriptions - activeSubscriptions} inactive`}
|
||||
emoji="🟢"
|
||||
/>
|
||||
{showCategories && <StatCard title="Categories" value={categoriesCount} emoji="🏷️" />}
|
||||
<StatCard title="Total Views" value={totalViews.toLocaleString()} emoji="👀" />
|
||||
</div>
|
||||
|
||||
{/* Subscriptions Display */}
|
||||
{renderSubscriptions()}
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
})
|
||||
{/* Subscriptions Display */}
|
||||
{renderSubscriptions()}
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
},
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
import type { ComponentType } from "react"
|
||||
import { memo } from "react"
|
||||
import isEqual from "react-fast-compare"
|
||||
|
||||
interface PartWithState {
|
||||
part: {
|
||||
state: string
|
||||
}
|
||||
}
|
||||
|
||||
export const toolMemo = <P extends PartWithState>(FC: ComponentType<P>): ComponentType<P> =>
|
||||
memo(FC, (prev, next) => {
|
||||
if (prev.part.state === "output-available") return true
|
||||
return isEqual(prev, next)
|
||||
}) as ComponentType<P>
|
||||
|
|
@ -1,13 +1,138 @@
|
|||
import type { LinkProps } from "@follow/components/ui/link/LinkWithTooltip.js"
|
||||
import { parseMarkdown } from "@follow/components/utils/parse-markdown.js"
|
||||
import { cn, isBizId } from "@follow/utils"
|
||||
import { createElement, isValidElement, memo, useMemo } from "react"
|
||||
import {
|
||||
createElement,
|
||||
isValidElement,
|
||||
memo,
|
||||
useCallback,
|
||||
useDeferredValue,
|
||||
useMemo,
|
||||
useRef,
|
||||
} from "react"
|
||||
|
||||
import { ShikiHighLighter } from "~/components/ui/code-highlighter"
|
||||
import { MermaidDiagram } from "~/components/ui/diagrams"
|
||||
import { MarkdownLink } from "~/components/ui/markdown/renderers/MarkdownLink"
|
||||
import { usePeekModal } from "~/hooks/biz/usePeekModal"
|
||||
|
||||
// Custom hook for throttled markdown parsing during streaming
|
||||
const useThrottledMarkdownParsing = (text: string, isProcessing: boolean) => {
|
||||
const lastParsedTextRef = useRef<string>("")
|
||||
const cachedResultRef = useRef<any>(null)
|
||||
const lastParseTimeRef = useRef<number>(0)
|
||||
|
||||
const parseWithCache = useCallback((content: string, shouldProcess: boolean) => {
|
||||
const now = Date.now()
|
||||
|
||||
// During streaming, throttle parsing
|
||||
if (shouldProcess && cachedResultRef.current) {
|
||||
const timeSinceLastParse = now - lastParseTimeRef.current
|
||||
if (timeSinceLastParse < 16) {
|
||||
// Return cached result if within throttle window
|
||||
return cachedResultRef.current
|
||||
}
|
||||
}
|
||||
|
||||
// If content hasn't changed, return cached result
|
||||
if (content === lastParsedTextRef.current && cachedResultRef.current) {
|
||||
return cachedResultRef.current
|
||||
}
|
||||
|
||||
// Parse and cache the result
|
||||
const result = parseMarkdown(content, {
|
||||
components: {
|
||||
pre: ({ children }) => {
|
||||
const props = isValidElement(children) && "props" in children && children.props
|
||||
|
||||
if (props) {
|
||||
const { className, children } = props as any
|
||||
|
||||
if (className && className.includes("language-") && typeof children === "string") {
|
||||
const language = className.replace("language-", "")
|
||||
const code = children
|
||||
|
||||
// Render Mermaid diagrams - skip during processing for performance
|
||||
if (language === "mermaid") {
|
||||
return <MermaidDiagram code={code} shouldRender={!shouldProcess} />
|
||||
}
|
||||
|
||||
return <ShikiHighLighter code={code} language={language} showCopy />
|
||||
}
|
||||
}
|
||||
|
||||
return <pre className="text-text-secondary">{children}</pre>
|
||||
},
|
||||
a: ({ node, ...props }) => {
|
||||
return createElement(RelatedEntryLink, { ...props } as any)
|
||||
},
|
||||
table: ({ children, ref, node, ...props }) => {
|
||||
return (
|
||||
<div className="border-border bg-material-thin overflow-x-auto rounded-lg border">
|
||||
<table {...props} className="divide-border my-0 min-w-full divide-y text-sm">
|
||||
{children}
|
||||
</table>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
thead: ({ children, ref, node, ...props }) => {
|
||||
return (
|
||||
<thead {...props} className="bg-fill-tertiary">
|
||||
{children}
|
||||
</thead>
|
||||
)
|
||||
},
|
||||
th: ({ children, ref, node, ...props }) => {
|
||||
return (
|
||||
<th
|
||||
{...props}
|
||||
className="text-text-secondary whitespace-nowrap px-4 py-3 text-left text-xs font-medium uppercase tracking-wider"
|
||||
>
|
||||
{children}
|
||||
</th>
|
||||
)
|
||||
},
|
||||
tbody: ({ children, ref, node, ...props }) => {
|
||||
return (
|
||||
<tbody {...props} className="bg-material-ultra-thin divide-border divide-y">
|
||||
{children}
|
||||
</tbody>
|
||||
)
|
||||
},
|
||||
tr: ({ children, ref, node, ...props }) => {
|
||||
return (
|
||||
<tr {...props} className="hover:bg-material-thin transition-colors duration-150">
|
||||
{children}
|
||||
</tr>
|
||||
)
|
||||
},
|
||||
td: ({ children, ref, node, ...props }) => {
|
||||
return (
|
||||
<td {...props} className="text-text whitespace-nowrap px-4 py-3 text-sm">
|
||||
{children}
|
||||
</td>
|
||||
)
|
||||
},
|
||||
},
|
||||
}).content
|
||||
|
||||
lastParsedTextRef.current = content
|
||||
cachedResultRef.current = result
|
||||
lastParseTimeRef.current = now
|
||||
return result
|
||||
}, [])
|
||||
|
||||
return useMemo(() => {
|
||||
// For non-processing state, always parse immediately
|
||||
if (!isProcessing) {
|
||||
return parseWithCache(text, false)
|
||||
}
|
||||
|
||||
// During processing, apply throttling logic
|
||||
return parseWithCache(text, true)
|
||||
}, [text, isProcessing, parseWithCache])
|
||||
}
|
||||
|
||||
export const AIMarkdownMessage = memo(
|
||||
({
|
||||
text,
|
||||
|
|
@ -18,102 +143,45 @@ export const AIMarkdownMessage = memo(
|
|||
className?: string
|
||||
isProcessing?: boolean
|
||||
}) => {
|
||||
const className = tw`prose dark:prose-invert text-sm
|
||||
const className = `prose dark:prose-invert text-sm
|
||||
prose-h1:text-2xl prose-h2:text-xl prose-h3:text-lg prose-h4:text-base prose-h5:text-base prose-h6:text-sm
|
||||
prose-li:list-disc prose-li:marker:text-accent prose-hr:border-border prose-hr:mx-8
|
||||
`
|
||||
prose-li:list-disc prose-li:marker:text-accent prose-hr:border-border prose-hr:mx-8`
|
||||
|
||||
return (
|
||||
<div className={cn(className, classNameProp)}>
|
||||
{useMemo(
|
||||
() =>
|
||||
parseMarkdown(text, {
|
||||
components: {
|
||||
pre: ({ children }) => {
|
||||
// props
|
||||
const props = isValidElement(children) && "props" in children && children.props
|
||||
// Use deferred value for lower priority rendering during streaming
|
||||
const deferredText = useDeferredValue(text)
|
||||
|
||||
if (props) {
|
||||
const { className, children } = props as any
|
||||
|
||||
if (
|
||||
className &&
|
||||
className.includes("language-") &&
|
||||
typeof children === "string"
|
||||
) {
|
||||
const language = className.replace("language-", "")
|
||||
const code = children
|
||||
|
||||
// Render Mermaid diagrams
|
||||
if (language === "mermaid") {
|
||||
return <MermaidDiagram code={code} shouldRender={!isProcessing} />
|
||||
}
|
||||
|
||||
return <ShikiHighLighter code={code} language={language} showCopy />
|
||||
}
|
||||
}
|
||||
|
||||
return <pre className="text-text-secondary">{children}</pre>
|
||||
},
|
||||
a: ({ node, ...props }) => {
|
||||
return createElement(RelatedEntryLink, { ...props } as any)
|
||||
},
|
||||
table: ({ children, ref, node, ...props }) => {
|
||||
return (
|
||||
<div className="border-border bg-material-thin overflow-x-auto rounded-lg border">
|
||||
<table {...props} className="divide-border my-0 min-w-full divide-y text-sm">
|
||||
{children}
|
||||
</table>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
thead: ({ children, ref, node, ...props }) => {
|
||||
return (
|
||||
<thead {...props} className="bg-fill-tertiary">
|
||||
{children}
|
||||
</thead>
|
||||
)
|
||||
},
|
||||
th: ({ children, ref, node, ...props }) => {
|
||||
return (
|
||||
<th
|
||||
{...props}
|
||||
className="text-text-secondary whitespace-nowrap px-4 py-3 text-left text-xs font-medium uppercase tracking-wider"
|
||||
>
|
||||
{children}
|
||||
</th>
|
||||
)
|
||||
},
|
||||
tbody: ({ children, ref, node, ...props }) => {
|
||||
return (
|
||||
<tbody {...props} className="bg-material-ultra-thin divide-border divide-y">
|
||||
{children}
|
||||
</tbody>
|
||||
)
|
||||
},
|
||||
tr: ({ children, ref, node, ...props }) => {
|
||||
return (
|
||||
<tr
|
||||
{...props}
|
||||
className="hover:bg-material-thin transition-colors duration-150"
|
||||
>
|
||||
{children}
|
||||
</tr>
|
||||
)
|
||||
},
|
||||
td: ({ children, ref, node, ...props }) => {
|
||||
return (
|
||||
<td {...props} className="text-text whitespace-nowrap px-4 py-3 text-sm">
|
||||
{children}
|
||||
</td>
|
||||
)
|
||||
},
|
||||
},
|
||||
}).content,
|
||||
[isProcessing, text],
|
||||
)}
|
||||
</div>
|
||||
// Use our optimized parsing hook
|
||||
const parsedContent = useThrottledMarkdownParsing(
|
||||
// During streaming, use deferred text for non-urgent updates
|
||||
isProcessing ? deferredText : text,
|
||||
isProcessing ?? false,
|
||||
)
|
||||
|
||||
return <div className={cn(className, classNameProp)}>{parsedContent}</div>
|
||||
},
|
||||
// Enhanced memo comparison for better performance
|
||||
(prevProps, nextProps) => {
|
||||
// If not processing, do normal comparison
|
||||
if (!nextProps.isProcessing && !prevProps.isProcessing) {
|
||||
return (
|
||||
prevProps.text === nextProps.text &&
|
||||
prevProps.className === nextProps.className &&
|
||||
prevProps.isProcessing === nextProps.isProcessing
|
||||
)
|
||||
}
|
||||
|
||||
// During processing, be more lenient with text changes to reduce re-renders
|
||||
if (nextProps.isProcessing) {
|
||||
// Only re-render if there's a significant change or processing state changes
|
||||
return (
|
||||
prevProps.text === nextProps.text &&
|
||||
prevProps.className === nextProps.className &&
|
||||
prevProps.isProcessing === nextProps.isProcessing
|
||||
)
|
||||
}
|
||||
|
||||
// Default comparison
|
||||
return false
|
||||
},
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -29,28 +29,6 @@ export function convertLexicalToMarkdown(editor: LexicalEditor): string {
|
|||
return markdown
|
||||
}
|
||||
|
||||
/**
|
||||
* Create MessageContent for user's rich text input
|
||||
*/
|
||||
export function createRichTextMessage(editor: LexicalEditor): MessageContent {
|
||||
const schema = editor.getEditorState().toJSON()
|
||||
|
||||
return {
|
||||
format: "richtext",
|
||||
content: schema,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create MessageContent for plaintext (AI responses, existing messages)
|
||||
*/
|
||||
export function createPlaintextMessage(text: string): MessageContent {
|
||||
return {
|
||||
format: "plaintext",
|
||||
content: text,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get markdown string for AI communication from any MessageContent
|
||||
*/
|
||||
|
|
@ -70,17 +48,3 @@ export function getMarkdownForAI(message: MessageContent, editor?: LexicalEditor
|
|||
// Fallback
|
||||
return ""
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if message is rich text format
|
||||
*/
|
||||
export function isRichTextMessage(message: MessageContent): boolean {
|
||||
return message.format === "richtext"
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if message is plaintext format
|
||||
*/
|
||||
export function isPlaintextMessage(message: MessageContent): boolean {
|
||||
return message.format === "plaintext"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -562,8 +562,8 @@ importers:
|
|||
specifier: 0.10.1
|
||||
version: 0.10.1
|
||||
ai:
|
||||
specifier: 5.0.0-beta.34
|
||||
version: 5.0.0-beta.34(zod@3.25.75)
|
||||
specifier: 5.0.0
|
||||
version: 5.0.0(zod@3.25.75)
|
||||
camelcase-keys:
|
||||
specifier: 9.1.3
|
||||
version: 9.1.3
|
||||
|
|
@ -663,6 +663,9 @@ importers:
|
|||
react-blurhash:
|
||||
specifier: 0.3.0
|
||||
version: 0.3.0(blurhash@2.0.5)(react@19.0.0)
|
||||
react-fast-compare:
|
||||
specifier: 3.2.2
|
||||
version: 3.2.2
|
||||
react-fast-marquee:
|
||||
specifier: 1.6.5
|
||||
version: 1.6.5(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
|
|
@ -1957,8 +1960,8 @@ packages:
|
|||
graphql:
|
||||
optional: true
|
||||
|
||||
'@ai-sdk/gateway@1.0.0-beta.19':
|
||||
resolution: {integrity: sha512-felWPMuECZRGx8xnmvH5dW3jywKTkGnw/tXN8szphGzEDr/BfxywuXijfPBG2WBUS6frPXsvSLDRdCm5W38PXA==}
|
||||
'@ai-sdk/gateway@1.0.0':
|
||||
resolution: {integrity: sha512-VEm87DyRx1yIPywbTy8ntoyh4jEDv1rJ88m+2I7zOm08jJI5BhFtAWh0OF6YzZu1Vu4NxhOWO4ssGdsqydDQ3A==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
zod: ^3.25.76 || ^4
|
||||
|
|
@ -1975,8 +1978,8 @@ packages:
|
|||
peerDependencies:
|
||||
zod: ^3.25.76 || ^4
|
||||
|
||||
'@ai-sdk/provider-utils@3.0.0-beta.10':
|
||||
resolution: {integrity: sha512-e6WSsgM01au04/1L/v5daXHn00eKjPBQXl3jq3BfvQbQ1jo8Rls2pvrdkyVc25jBW4TV4Zm+tw+v6NAh5NPXMA==}
|
||||
'@ai-sdk/provider-utils@3.0.0':
|
||||
resolution: {integrity: sha512-BoQZtGcBxkeSH1zK+SRYNDtJPIPpacTeiMZqnG4Rv6xXjEwM0FH4MGs9c+PlhyEWmQCzjRM2HAotEydFhD4dYw==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
zod: ^3.25.76 || ^4
|
||||
|
|
@ -1993,12 +1996,12 @@ packages:
|
|||
peerDependencies:
|
||||
zod: ^3.25.76 || ^4
|
||||
|
||||
'@ai-sdk/provider@2.0.0-beta.1':
|
||||
resolution: {integrity: sha512-Z8SPncMtS3RsoXITmT7NVwrAq6M44dmw0DoUOYJqNNtCu8iMWuxB8Nxsoqpa0uEEy9R1V1ZThJAXTYgjTUxl3w==}
|
||||
'@ai-sdk/provider@2.0.0':
|
||||
resolution: {integrity: sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
'@ai-sdk/provider@2.0.0-beta.2':
|
||||
resolution: {integrity: sha512-vqhtZA7R24q1XnmfmIb1fZSmHMIaJH1BVQ+0kFnNJgqWsc+V8i+yfetZ37gUc4fXATFmBuS/6O7+RPoHsZ2Fqg==}
|
||||
'@ai-sdk/provider@2.0.0-beta.1':
|
||||
resolution: {integrity: sha512-Z8SPncMtS3RsoXITmT7NVwrAq6M44dmw0DoUOYJqNNtCu8iMWuxB8Nxsoqpa0uEEy9R1V1ZThJAXTYgjTUxl3w==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
'@alloc/quick-lru@5.2.0':
|
||||
|
|
@ -7116,8 +7119,8 @@ packages:
|
|||
resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
ai@5.0.0-beta.34:
|
||||
resolution: {integrity: sha512-AFJ4p35AxA+1KFtnoouePLaAUpoj0IxIAoq/xgIv88qzYajTg4Sac5KaV4CDHFRLoF0L2cwhlFXt/Ss/zyBKkA==}
|
||||
ai@5.0.0:
|
||||
resolution: {integrity: sha512-F4jOhOSeiZD8lXpF4l1hRqyM1jbqoLKGVZNxAP467wmQCsWUtElMa3Ki5PrDMq6qvUNC3deUKfERDAsfj7IDlg==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
zod: ^3.25.76 || ^4
|
||||
|
|
@ -13548,6 +13551,9 @@ packages:
|
|||
peerDependencies:
|
||||
react: 19.0.0
|
||||
|
||||
react-fast-compare@3.2.2:
|
||||
resolution: {integrity: sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==}
|
||||
|
||||
react-fast-marquee@1.6.5:
|
||||
resolution: {integrity: sha512-swDnPqrT2XISAih0o74zQVE2wQJFMvkx+9VZXYYNSLb/CUcAzU9pNj637Ar2+hyRw6b4tP6xh4GQZip2ZCpQpg==}
|
||||
peerDependencies:
|
||||
|
|
@ -16173,16 +16179,16 @@ snapshots:
|
|||
optionalDependencies:
|
||||
graphql: 16.8.1
|
||||
|
||||
'@ai-sdk/gateway@1.0.0-beta.19(zod@3.25.75)':
|
||||
'@ai-sdk/gateway@1.0.0(zod@3.25.75)':
|
||||
dependencies:
|
||||
'@ai-sdk/provider': 2.0.0-beta.2
|
||||
'@ai-sdk/provider-utils': 3.0.0-beta.10(zod@3.25.75)
|
||||
'@ai-sdk/provider': 2.0.0
|
||||
'@ai-sdk/provider-utils': 3.0.0(zod@3.25.75)
|
||||
zod: 3.25.75
|
||||
|
||||
'@ai-sdk/gateway@1.0.0-beta.19(zod@3.25.76)':
|
||||
'@ai-sdk/gateway@1.0.0(zod@3.25.76)':
|
||||
dependencies:
|
||||
'@ai-sdk/provider': 2.0.0-beta.2
|
||||
'@ai-sdk/provider-utils': 3.0.0-beta.10(zod@3.25.76)
|
||||
'@ai-sdk/provider': 2.0.0
|
||||
'@ai-sdk/provider-utils': 3.0.0(zod@3.25.76)
|
||||
zod: 3.25.76
|
||||
|
||||
'@ai-sdk/gateway@1.0.0-beta.3(zod@3.25.75)':
|
||||
|
|
@ -16203,17 +16209,17 @@ snapshots:
|
|||
'@ai-sdk/provider-utils': 3.0.0-beta.5(zod@3.25.76)
|
||||
zod: 3.25.76
|
||||
|
||||
'@ai-sdk/provider-utils@3.0.0-beta.10(zod@3.25.75)':
|
||||
'@ai-sdk/provider-utils@3.0.0(zod@3.25.75)':
|
||||
dependencies:
|
||||
'@ai-sdk/provider': 2.0.0-beta.2
|
||||
'@ai-sdk/provider': 2.0.0
|
||||
'@standard-schema/spec': 1.0.0
|
||||
eventsource-parser: 3.0.3
|
||||
zod: 3.25.75
|
||||
zod-to-json-schema: 3.24.5(zod@3.25.75)
|
||||
|
||||
'@ai-sdk/provider-utils@3.0.0-beta.10(zod@3.25.76)':
|
||||
'@ai-sdk/provider-utils@3.0.0(zod@3.25.76)':
|
||||
dependencies:
|
||||
'@ai-sdk/provider': 2.0.0-beta.2
|
||||
'@ai-sdk/provider': 2.0.0
|
||||
'@standard-schema/spec': 1.0.0
|
||||
eventsource-parser: 3.0.3
|
||||
zod: 3.25.76
|
||||
|
|
@ -16243,11 +16249,11 @@ snapshots:
|
|||
zod: 3.25.76
|
||||
zod-to-json-schema: 3.24.5(zod@3.25.76)
|
||||
|
||||
'@ai-sdk/provider@2.0.0-beta.1':
|
||||
'@ai-sdk/provider@2.0.0':
|
||||
dependencies:
|
||||
json-schema: 0.4.0
|
||||
|
||||
'@ai-sdk/provider@2.0.0-beta.2':
|
||||
'@ai-sdk/provider@2.0.0-beta.1':
|
||||
dependencies:
|
||||
json-schema: 0.4.0
|
||||
|
||||
|
|
@ -19381,7 +19387,7 @@ snapshots:
|
|||
dependencies:
|
||||
'@ai-sdk/openai': 2.0.0-beta.11(zod@3.25.76)
|
||||
'@folo-services/drizzle': 0.1.9(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.0)(@expo/metro-runtime@5.0.4(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.15.0(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(kysely@0.28.2)
|
||||
ai: 5.0.0-beta.34(zod@3.25.76)
|
||||
ai: 5.0.0(zod@3.25.76)
|
||||
drizzle-orm: 0.44.3(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.0)(@expo/metro-runtime@5.0.4(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.15.0(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.1(@babel/core@7.28.0)(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(kysely@0.28.2)(pg@8.16.3)
|
||||
zod: 3.25.76
|
||||
transitivePeerDependencies:
|
||||
|
|
@ -23138,19 +23144,19 @@ snapshots:
|
|||
clean-stack: 2.2.0
|
||||
indent-string: 4.0.0
|
||||
|
||||
ai@5.0.0-beta.34(zod@3.25.75):
|
||||
ai@5.0.0(zod@3.25.75):
|
||||
dependencies:
|
||||
'@ai-sdk/gateway': 1.0.0-beta.19(zod@3.25.75)
|
||||
'@ai-sdk/provider': 2.0.0-beta.2
|
||||
'@ai-sdk/provider-utils': 3.0.0-beta.10(zod@3.25.75)
|
||||
'@ai-sdk/gateway': 1.0.0(zod@3.25.75)
|
||||
'@ai-sdk/provider': 2.0.0
|
||||
'@ai-sdk/provider-utils': 3.0.0(zod@3.25.75)
|
||||
'@opentelemetry/api': 1.9.0
|
||||
zod: 3.25.75
|
||||
|
||||
ai@5.0.0-beta.34(zod@3.25.76):
|
||||
ai@5.0.0(zod@3.25.76):
|
||||
dependencies:
|
||||
'@ai-sdk/gateway': 1.0.0-beta.19(zod@3.25.76)
|
||||
'@ai-sdk/provider': 2.0.0-beta.2
|
||||
'@ai-sdk/provider-utils': 3.0.0-beta.10(zod@3.25.76)
|
||||
'@ai-sdk/gateway': 1.0.0(zod@3.25.76)
|
||||
'@ai-sdk/provider': 2.0.0
|
||||
'@ai-sdk/provider-utils': 3.0.0(zod@3.25.76)
|
||||
'@opentelemetry/api': 1.9.0
|
||||
zod: 3.25.76
|
||||
|
||||
|
|
@ -30761,6 +30767,8 @@ snapshots:
|
|||
'@babel/runtime': 7.27.6
|
||||
react: 19.0.0
|
||||
|
||||
react-fast-compare@3.2.2: {}
|
||||
|
||||
react-fast-marquee@1.6.5(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
|
||||
dependencies:
|
||||
react: 19.0.0
|
||||
|
|
|
|||
Loading…
Reference in New Issue