Skip to content

Commit fc49766

Browse files
committed
feat: enhance AI slug generation with page content
Improves AI-generated URL slugs by incorporating page content alongside the URL itself. Fetches page markdown and includes it in the AI prompt context, enabling more meaningful and descriptive slug generation based on actual page content rather than URL alone. Updates the AI prompt to clarify that SLUG derivation should consider both URL and page content when available, removing the strict limitation of using only URL information.
1 parent 98d6d38 commit fc49766

3 files changed

Lines changed: 59 additions & 2 deletions

File tree

nuxt.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export default defineNuxtConfig({
2828
cfApiToken: '',
2929
dataset: 'sink',
3030
aiModel: '@cf/qwen/qwen3-30b-a3b-fp8',
31-
aiPrompt: `You are a URL shortening assistant, please shorten the URL provided by the user into a SLUG. The SLUG information must come from the URL itself, do not make any assumptions. A SLUG is human-readable and should not exceed three words and can be validated using regular expressions {slugRegex} . Only the best one is returned, the format must be JSON reference {"slug": "example-slug"}`,
31+
aiPrompt: `You are a URL shortening assistant, please shorten the URL provided by the user into a SLUG. The SLUG information should be derived from the URL and page content (if provided). Do not make any assumptions beyond the given information. A SLUG is human-readable and should not exceed three words and can be validated using regular expressions {slugRegex} . Only the best one is returned, the format must be JSON reference {"slug": "example-slug"}`,
3232
caseSensitive: false,
3333
listQueryLimit: 500,
3434
disableBotAccessLog: false,

server/api/link/ai.get.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@ export default eventHandler(async (event) => {
3636
const { aiPrompt, aiModel } = useRuntimeConfig(event)
3737
const { slugRegex } = useAppConfig()
3838

39+
const markdown = await fetchPageMarkdown(event, url, AI)
40+
const userContent = markdown
41+
? `URL: ${url}\n\nPage content:\n${markdown}`
42+
: url
43+
3944
const messages = [
4045
{ role: 'system', content: aiPrompt.replace('{slugRegex}', slugRegex.toString()) },
4146

@@ -51,7 +56,7 @@ export default eventHandler(async (event) => {
5156
{ role: 'user', content: 'https://github.com/miantiao-me/sink' },
5257
{ role: 'assistant', content: '{"slug": "sink"}' },
5358

54-
{ role: 'user', content: url },
59+
{ role: 'user', content: userContent },
5560
]
5661

5762
const response = await AI.run(aiModel as keyof AiModels, { messages }) as AiChatResponse

server/utils/markdown.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import type { H3Event } from 'h3'
2+
import { ofetch } from 'ofetch'
3+
4+
export async function fetchPageMarkdown(event: H3Event, url: string, AI: Ai): Promise<string | null> {
5+
const reqHeaders = Object.fromEntries(
6+
Object.entries(getHeaders(event)).filter(([_, v]) => v !== undefined),
7+
) as Record<string, string>
8+
9+
try {
10+
const response = await ofetch.raw(url, {
11+
headers: {
12+
...reqHeaders,
13+
Accept: 'text/markdown, text/html;q=0.9, */*;q=0.8',
14+
},
15+
timeout: 5000,
16+
responseType: 'text',
17+
})
18+
19+
const contentType = response.headers.get('content-type') || ''
20+
const body = response._data as string
21+
22+
if (!body) {
23+
return null
24+
}
25+
26+
// Markdown for Agents: site returns markdown directly
27+
if (contentType.includes('text/markdown')) {
28+
return body.slice(0, 4096)
29+
}
30+
31+
// Fallback: convert HTML to markdown via AI.toMarkdown()
32+
if (contentType.includes('text/html')) {
33+
try {
34+
const result = await AI.toMarkdown({
35+
name: 'page.html',
36+
blob: new Blob([body], { type: 'text/html' }),
37+
})
38+
if (result.format === 'markdown' && result.data) {
39+
return result.data.slice(0, 4096)
40+
}
41+
}
42+
catch (err) {
43+
console.warn(`[markdown] AI.toMarkdown() failed for ${url}:`, err)
44+
}
45+
}
46+
}
47+
catch (err) {
48+
console.warn(`[markdown] Failed to fetch ${url}:`, err)
49+
}
50+
51+
return null
52+
}

0 commit comments

Comments
 (0)