Skip to content

Commit 877f04f

Browse files
committed
feat: ai summary widget
1 parent 082cf02 commit 877f04f

File tree

5 files changed

+124
-8
lines changed

5 files changed

+124
-8
lines changed

src/app/api/ai/summary/route.ts

+6-8
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import OpenAI from 'openai'
2+
import type { ArticleDataType } from '~/types/api'
23
import type { NextRequest } from 'next/server'
34

45
import { sql } from '@vercel/postgres'
@@ -16,13 +17,7 @@ export const GET = async (req: NextRequest) => {
1617
const dataString = searchParams.get('data') as string
1718
const lang = searchParams.get('lang') || ('zh' as string)
1819

19-
let data:
20-
| { type: 'post'; category: string; slug: string }
21-
| { type: 'note'; nid: number }
22-
| {
23-
type: 'page'
24-
slug: string
25-
}
20+
let data: ArticleDataType
2621

2722
try {
2823
data = JSON.parse(decodeURIComponent(dataString))
@@ -87,7 +82,10 @@ export const GET = async (req: NextRequest) => {
8782
messages: [
8883
{
8984
role: 'user',
90-
content: `Summarize the following and use a third person description in ${lang} : ${text}`,
85+
content: `Summarize this in "${lang}" language:
86+
"${text}"
87+
88+
CONCISE SUMMARY:`,
9189
},
9290
],
9391
model: 'gpt-3.5-turbo',

src/app/notes/[id]/pageImpl.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ const NotePage = function (props: NoteModel) {
5555

5656
<NoteHideIfSecret>
5757
<XLogSummary cid={getCidForBaseModel(props)} />
58+
{/* <AISummary data={props} /> */}
5859
<WrappedElementProvider>
5960
<ReadIndicatorForMobile />
6061
<NoteMarkdownImageRecordProvider>

src/components/widgets/ai/Summary.tsx

+102
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
'use client'
2+
3+
import { useQuery } from '@tanstack/react-query'
4+
import { useMemo } from 'react'
5+
import type { NoteModel, PageModel, PostModel } from '@mx-space/api-client'
6+
import type { ArticleDataType } from '~/types/api'
7+
import type { FC, ReactNode } from 'react'
8+
9+
import { AutoResizeHeight } from '~/components/widgets/shared/AutoResizeHeight'
10+
import { API_URL } from '~/constants/env'
11+
import { clsxm } from '~/lib/helper'
12+
import { isNoteModel, isPageModel, isPostModel } from '~/lib/url-builder'
13+
14+
export const AISummary: FC<{
15+
data: PostModel | NoteModel | PageModel
16+
className?: string
17+
}> = (props) => {
18+
const { data } = props
19+
20+
const payload = useMemo(() => {
21+
let payload: ArticleDataType
22+
23+
if (isPostModel(data)) {
24+
payload = {
25+
category: data.category.slug,
26+
slug: data.slug,
27+
type: 'post',
28+
}
29+
} else if (isNoteModel(data)) {
30+
payload = {
31+
nid: data.nid,
32+
type: 'note',
33+
}
34+
} else if (isPageModel(data)) {
35+
payload = {
36+
slug: data.slug,
37+
type: 'page',
38+
}
39+
} else {
40+
throw new Error('未知类型')
41+
}
42+
43+
return payload
44+
}, [data])
45+
const { data: response, isLoading } = useQuery<{
46+
summary: string
47+
source: string
48+
}>(
49+
[`ai-summary`, data.id, API_URL, data.modified],
50+
async () => {
51+
const data = await fetch(
52+
`/api/ai/summary?data=${encodeURIComponent(JSON.stringify(payload))}`,
53+
{
54+
next: {
55+
revalidate: 60 * 10,
56+
},
57+
},
58+
).then((res) => res.json())
59+
if (!data) throw new Error('请求错误')
60+
return data
61+
},
62+
{
63+
staleTime: 1000 * 60 * 60 * 24 * 7,
64+
retryDelay: 5000,
65+
},
66+
)
67+
68+
const Inner: ReactNode = (
69+
<div
70+
data-hide-print
71+
className={clsxm(
72+
`space-y-2 rounded-xl border border-slate-200 p-4 dark:border-neutral-800`,
73+
props.className,
74+
)}
75+
>
76+
<div className="flex items-center">
77+
<i className="icon-[mingcute--sparkles-line] mr-2 text-lg" />
78+
AI 生成的摘要
79+
</div>
80+
81+
<AutoResizeHeight duration={0.3}>
82+
<p className="text-base-content/85 !m-0 text-sm leading-loose">
83+
{isLoading ? (
84+
<div className="space-y-2">
85+
<span className="block h-5 w-full animate-pulse rounded-xl bg-zinc-200 dark:bg-neutral-800" />
86+
<span className="block h-5 w-full animate-pulse rounded-xl bg-zinc-200 dark:bg-neutral-800" />
87+
<span className="block h-5 w-full animate-pulse rounded-xl bg-zinc-200 dark:bg-neutral-800" />
88+
</div>
89+
) : (
90+
response?.summary
91+
)}
92+
</p>
93+
</AutoResizeHeight>
94+
</div>
95+
)
96+
97+
return (
98+
<AutoResizeHeight duration={0.2} className="mt-4">
99+
{Inner}
100+
</AutoResizeHeight>
101+
)
102+
}

src/components/widgets/xlog/XLogSummaryRSC.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ const fetchData = async (cid: string, lang = 'zh') => {
2828
.then((res) => res.json())
2929
.catch(() => null)
3030
}
31+
3132
export const XLogSummary = async (
3233
props: ComponentType<{
3334
cid: string

src/types/api.ts

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
export type ArticleDataType =
2+
| {
3+
type: 'post'
4+
category: string
5+
slug: string
6+
}
7+
| {
8+
type: 'note'
9+
nid: number
10+
}
11+
| {
12+
type: 'page'
13+
slug: string
14+
}

0 commit comments

Comments
 (0)