From 877f04fa50048e97038c55df305e5f97b27dc6f4 Mon Sep 17 00:00:00 2001 From: Innei Date: Fri, 18 Aug 2023 12:13:14 +0800 Subject: [PATCH] feat: ai summary widget --- src/app/api/ai/summary/route.ts | 14 ++- src/app/notes/[id]/pageImpl.tsx | 1 + src/components/widgets/ai/Summary.tsx | 102 ++++++++++++++++++ .../widgets/xlog/XLogSummaryRSC.tsx | 1 + src/types/api.ts | 14 +++ 5 files changed, 124 insertions(+), 8 deletions(-) create mode 100644 src/components/widgets/ai/Summary.tsx create mode 100644 src/types/api.ts diff --git a/src/app/api/ai/summary/route.ts b/src/app/api/ai/summary/route.ts index 306d7e99cf..57626f0a60 100644 --- a/src/app/api/ai/summary/route.ts +++ b/src/app/api/ai/summary/route.ts @@ -1,4 +1,5 @@ import OpenAI from 'openai' +import type { ArticleDataType } from '~/types/api' import type { NextRequest } from 'next/server' import { sql } from '@vercel/postgres' @@ -16,13 +17,7 @@ export const GET = async (req: NextRequest) => { const dataString = searchParams.get('data') as string const lang = searchParams.get('lang') || ('zh' as string) - let data: - | { type: 'post'; category: string; slug: string } - | { type: 'note'; nid: number } - | { - type: 'page' - slug: string - } + let data: ArticleDataType try { data = JSON.parse(decodeURIComponent(dataString)) @@ -87,7 +82,10 @@ export const GET = async (req: NextRequest) => { messages: [ { role: 'user', - content: `Summarize the following and use a third person description in ${lang} : ${text}`, + content: `Summarize this in "${lang}" language: +"${text}" + +CONCISE SUMMARY:`, }, ], model: 'gpt-3.5-turbo', diff --git a/src/app/notes/[id]/pageImpl.tsx b/src/app/notes/[id]/pageImpl.tsx index 64ed66851a..8747708272 100644 --- a/src/app/notes/[id]/pageImpl.tsx +++ b/src/app/notes/[id]/pageImpl.tsx @@ -55,6 +55,7 @@ const NotePage = function (props: NoteModel) { + {/* */} diff --git a/src/components/widgets/ai/Summary.tsx b/src/components/widgets/ai/Summary.tsx new file mode 100644 index 0000000000..3768df6c34 --- /dev/null +++ b/src/components/widgets/ai/Summary.tsx @@ -0,0 +1,102 @@ +'use client' + +import { useQuery } from '@tanstack/react-query' +import { useMemo } from 'react' +import type { NoteModel, PageModel, PostModel } from '@mx-space/api-client' +import type { ArticleDataType } from '~/types/api' +import type { FC, ReactNode } from 'react' + +import { AutoResizeHeight } from '~/components/widgets/shared/AutoResizeHeight' +import { API_URL } from '~/constants/env' +import { clsxm } from '~/lib/helper' +import { isNoteModel, isPageModel, isPostModel } from '~/lib/url-builder' + +export const AISummary: FC<{ + data: PostModel | NoteModel | PageModel + className?: string +}> = (props) => { + const { data } = props + + const payload = useMemo(() => { + let payload: ArticleDataType + + if (isPostModel(data)) { + payload = { + category: data.category.slug, + slug: data.slug, + type: 'post', + } + } else if (isNoteModel(data)) { + payload = { + nid: data.nid, + type: 'note', + } + } else if (isPageModel(data)) { + payload = { + slug: data.slug, + type: 'page', + } + } else { + throw new Error('未知类型') + } + + return payload + }, [data]) + const { data: response, isLoading } = useQuery<{ + summary: string + source: string + }>( + [`ai-summary`, data.id, API_URL, data.modified], + async () => { + const data = await fetch( + `/api/ai/summary?data=${encodeURIComponent(JSON.stringify(payload))}`, + { + next: { + revalidate: 60 * 10, + }, + }, + ).then((res) => res.json()) + if (!data) throw new Error('请求错误') + return data + }, + { + staleTime: 1000 * 60 * 60 * 24 * 7, + retryDelay: 5000, + }, + ) + + const Inner: ReactNode = ( +
+
+ + AI 生成的摘要 +
+ + +

+ {isLoading ? ( +

+ + + +
+ ) : ( + response?.summary + )} +

+
+
+ ) + + return ( + + {Inner} + + ) +} diff --git a/src/components/widgets/xlog/XLogSummaryRSC.tsx b/src/components/widgets/xlog/XLogSummaryRSC.tsx index 988704cfee..d4f55a0b45 100644 --- a/src/components/widgets/xlog/XLogSummaryRSC.tsx +++ b/src/components/widgets/xlog/XLogSummaryRSC.tsx @@ -28,6 +28,7 @@ const fetchData = async (cid: string, lang = 'zh') => { .then((res) => res.json()) .catch(() => null) } + export const XLogSummary = async ( props: ComponentType<{ cid: string diff --git a/src/types/api.ts b/src/types/api.ts new file mode 100644 index 0000000000..ce1c1c05d1 --- /dev/null +++ b/src/types/api.ts @@ -0,0 +1,14 @@ +export type ArticleDataType = + | { + type: 'post' + category: string + slug: string + } + | { + type: 'note' + nid: number + } + | { + type: 'page' + slug: string + }