feat: change the AI summary to pure server-side rendering

This commit is contained in:
DIYgod 2023-05-09 22:32:10 +01:00
parent 7490395fef
commit 24b4b284b3
No known key found for this signature in database
6 changed files with 151 additions and 163 deletions

View File

@ -61,6 +61,7 @@
"@urql/core": "4.0.7",
"ahooks": "^3.7.7",
"aplayer-react": "^1.1.0",
"async-lock": "^1.4.0",
"canvas": "^2.11.2",
"canvas-confetti": "^1.6.0",
"chroma-js": "^2.4.2",
@ -148,6 +149,7 @@
"@next/bundle-analyzer": "13.4.1",
"@playwright/test": "1.33.0",
"@trivago/prettier-plugin-sort-imports": "4.1.1",
"@types/async-lock": "^1.4.0",
"@types/canvas-confetti": "^1.6.0",
"@types/chroma-js": "^2.4.0",
"@types/cookie": "0.5.1",

View File

@ -100,6 +100,9 @@ dependencies:
aplayer-react:
specifier: ^1.1.0
version: 1.1.0(react@18.2.0)
async-lock:
specifier: ^1.4.0
version: 1.4.0
canvas:
specifier: ^2.11.2
version: 2.11.2
@ -357,6 +360,9 @@ devDependencies:
'@trivago/prettier-plugin-sort-imports':
specifier: 4.1.1
version: 4.1.1(prettier@2.8.8)
'@types/async-lock':
specifier: ^1.4.0
version: 1.4.0
'@types/canvas-confetti':
specifier: ^1.6.0
version: 1.6.0
@ -4430,6 +4436,10 @@ packages:
- supports-color
dev: true
/@types/async-lock@1.4.0:
resolution: {integrity: sha512-2+rYSaWrpdbQG3SA0LmMT6YxWLrI81AqpMlSkw3QtFc2HGDufkweQSn30Eiev7x9LL0oyFrBqk1PXOnB9IEgKg==}
dev: true
/@types/bn.js@5.1.1:
resolution: {integrity: sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g==}
dependencies:
@ -5641,6 +5651,10 @@ packages:
engines: {node: '>=8'}
dev: true
/async-lock@1.4.0:
resolution: {integrity: sha512-coglx5yIWuetakm3/1dsX9hxCNox22h7+V80RQOu2XUUMidtArxKoZoOtHUPuR84SycKTXzgGzAUR5hJxujyJQ==}
dev: false
/async-mutex@0.2.6:
resolution: {integrity: sha512-Hs4R+4SPgamu6rSGW8C7cV9gaWUKEHykfzCCvIRuaVv636Ju10ZdeUbvb4TBEW0INuq2DHZqXbK4Nd3yG4RaRw==}
dependencies:

View File

@ -1,139 +0,0 @@
import { AnalyzeDocumentChain, loadSummarizationChain } from "langchain/chains"
import { OpenAI } from "langchain/llms/openai"
import { PromptTemplate } from "langchain/prompts"
import removeMarkdown from "remove-markdown"
import { Metadata } from "@prisma/client"
import { toGateway } from "~/lib/ipfs-parser"
import prisma from "~/lib/prisma.server"
import { cacheGet } from "~/lib/redis.server"
import { NextServerResponse, getQuery } from "~/lib/server-helper"
let model: OpenAI | undefined
if (process.env.OPENAI_API_KEY) {
model = new OpenAI({
openAIApiKey: process.env.OPENAI_API_KEY,
modelName: "gpt-3.5-turbo",
temperature: 0.3,
maxTokens: 400,
})
}
const chains = new Map<string, AnalyzeDocumentChain>()
const getOriginalSummary = async (cid: string, lang: string) => {
if (!model) return
try {
let { content } = await (await fetch(toGateway(`ipfs://${cid}`))).json()
if (content?.length > 5000) {
content = content.slice(0, 5000)
}
if (content?.length < 200) {
return ""
} else if (content) {
console.time(`fetching summary ${cid}, ${lang}`)
let chain = chains.get(lang)
if (!chain) {
const prompt = new PromptTemplate({
template: `Summarize this in "${lang}" language:
"{text}"
CONCISE SUMMARY:`,
inputVariables: ["text"],
})
const combineDocsChain = loadSummarizationChain(model, {
type: "map_reduce",
combineMapPrompt: prompt,
combinePrompt: prompt,
})
chain = new AnalyzeDocumentChain({
combineDocumentsChain: combineDocsChain,
})
chains.set(lang, chain)
}
const res = await chain.call({
input_document: removeMarkdown(content, {
useImgAltText: true,
gfm: true,
}),
})
console.timeEnd(`fetching summary ${cid}, ${lang}`)
return res?.text
}
} catch (error) {
console.error(error)
console.timeEnd(`fetching summary ${cid}, ${lang}`)
}
}
async function getSummary(cid: string, lang: string = "en") {
const summary = await cacheGet({
key: ["summary", cid, lang],
getValueFun: async () => {
if (["en", "zh", "zh-TW", "ja"].includes(lang)) {
const key = `ai_summary_${lang.replace("-", "").toLowerCase()}`
const meta = await prisma.metadata.findFirst({
where: {
uri: `ipfs://${cid}`,
},
})
if (meta) {
if (meta?.[key as keyof Metadata]) {
return meta?.[key as keyof Metadata]
} else {
const summary = await getOriginalSummary(cid, lang)
if (summary) {
await prisma.metadata.update({
where: {
uri: `ipfs://${cid}`,
},
data: {
[key as keyof Metadata]: summary,
},
})
return summary
}
}
} else {
const summary = await getOriginalSummary(cid, lang)
if (summary) {
await prisma.metadata.create({
data: {
uri: `ipfs://${cid}`,
[key as keyof Metadata]: summary,
},
})
return summary
}
}
}
},
noUpdate: true,
})
return summary
}
export async function GET(req: Request): Promise<Response> {
let { cid, lang } = getQuery(req)
const res = new NextServerResponse()
if (!cid) {
return res.status(400).send("Bad Request")
}
return res.status(200).json({
data: await getSummary(cid as string, lang as string),
})
}

View File

@ -598,18 +598,6 @@ export async function updateComment(
})
}
export async function getSummary({
cid,
lang,
}: {
cid: string
lang?: string
}) {
return (
await (await fetch(`/api/summary?cid=${cid}&lang=${lang || "en"}`)).json()
).data
}
export async function checkMirror(characterId: number) {
const notes = await indexer.getNotes({
characterId,

View File

@ -1,6 +1,15 @@
import AsyncLock from "async-lock"
import { AnalyzeDocumentChain, loadSummarizationChain } from "langchain/chains"
import { OpenAI } from "langchain/llms/openai"
import { PromptTemplate } from "langchain/prompts"
import removeMarkdown from "remove-markdown"
import { Metadata } from "@prisma/client"
import { QueryClient } from "@tanstack/react-query"
import { getNoteSlug } from "~/lib/helpers"
import { toGateway } from "~/lib/ipfs-parser"
import prisma from "~/lib/prisma.server"
import { cacheDelete, cacheGet } from "~/lib/redis.server"
import * as pageModel from "~/models/page.model"
@ -118,3 +127,129 @@ export const fetchGetPagesBySite = async (
}) as Promise<ReturnType<typeof pageModel.getPagesBySite>>
})
}
// Post summary
let model: OpenAI | undefined
if (process.env.OPENAI_API_KEY) {
model = new OpenAI({
openAIApiKey: process.env.OPENAI_API_KEY,
modelName: "gpt-3.5-turbo",
temperature: 0.3,
maxTokens: 400,
})
}
const chains = new Map<string, AnalyzeDocumentChain>()
const getOriginalSummary = async (cid: string, lang: string) => {
if (!model) return
try {
let { content } = await (await fetch(toGateway(`ipfs://${cid}`))).json()
if (content?.length > 5000) {
content = content.slice(0, 5000)
}
if (content?.length < 200) {
return
} else if (content) {
console.time(`fetching summary ${cid}, ${lang}`)
let chain = chains.get(lang)
if (!chain) {
const prompt = new PromptTemplate({
template: `Summarize this in "${lang}" language:
"{text}"
CONCISE SUMMARY:`,
inputVariables: ["text"],
})
const combineDocsChain = loadSummarizationChain(model, {
type: "map_reduce",
combineMapPrompt: prompt,
combinePrompt: prompt,
})
chain = new AnalyzeDocumentChain({
combineDocumentsChain: combineDocsChain,
})
chains.set(lang, chain)
}
const res = await chain.call({
input_document: removeMarkdown(content, {
useImgAltText: true,
gfm: true,
}),
})
console.timeEnd(`fetching summary ${cid}, ${lang}`)
return res?.text as string
}
} catch (error) {
console.error(error)
console.timeEnd(`fetching summary ${cid}, ${lang}`)
}
}
const lock = new AsyncLock()
export async function getSummary({
cid,
lang = "en",
}: {
cid: string
lang?: string
}) {
const key = `ai_summary_${lang.replace("-", "").toLowerCase()}`
const summary = (await cacheGet({
key: ["summary", cid, lang],
getValueFun: async () => {
if (["en", "zh", "zh-TW", "ja"].includes(lang)) {
const meta = await prisma.metadata.findFirst({
where: {
uri: `ipfs://${cid}`,
},
})
if (meta) {
if (meta?.[key as keyof Metadata]) {
return meta?.[key as keyof Metadata]
} else {
lock.acquire(cid, async () => {
const summary = await getOriginalSummary(cid, lang)
if (summary) {
await prisma.metadata.update({
where: {
uri: `ipfs://${cid}`,
},
data: {
[key as keyof Metadata]: summary,
},
})
}
})
return
}
} else {
lock.acquire(cid, async () => {
const summary = await getOriginalSummary(cid, lang)
if (summary) {
await prisma.metadata.create({
data: {
uri: `ipfs://${cid}`,
[key as keyof Metadata]: summary,
},
})
}
})
return
}
}
},
noUpdate: true,
})) as string | undefined
return summary
}

View File

@ -359,18 +359,6 @@ export function useGetComments(
})
}
export function useGetSummary(input: { cid?: string; lang?: string }) {
return useQuery(["getSummary", input.cid, input.lang], async () => {
if (!input.cid || !input.lang) {
return
}
return pageModel.getSummary({
cid: input.cid,
lang: input.lang,
})
})
}
export function useGetMirrorXyz(input: { address?: string }) {
return useQuery(["getMirror", input.address], async () => {
if (!input.address) {