This commit is contained in:
2026-06-24 00:37:19 -06:00
parent 12acaae38b
commit b18bde8d2a
5 changed files with 95 additions and 5 deletions

View File

@@ -86,6 +86,68 @@ describe('generate route', () => {
expect(res.status).toBe(502)
})
it('returns 400 when theme is missing on outline', async () => {
const app = new Hono().route('/api/generate', createGenerateRouter('test-key'))
const res = await app.request('/api/generate/outline', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({}),
})
expect(res.status).toBe(400)
})
it('uses the requested count in the outline prompt', async () => {
let sentBody = ''
globalThis.fetch = mock(async (_url: unknown, init: { body?: string } = {}) => {
sentBody = init.body ?? ''
return new Response(
JSON.stringify({ choices: [{ message: { content: 'A\nB\nC' } }] }),
{ status: 200 },
)
}) as unknown as typeof fetch
const app = new Hono().route('/api/generate', createGenerateRouter('test-key'))
const res = await app.request('/api/generate/outline', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ theme: 'Web 前端', count: 25 }),
})
expect(res.status).toBe(200)
expect(sentBody).toContain('共约25条')
const body = (await res.json()) as { titles: string[] }
expect(body.titles).toEqual(['A', 'B', 'C'])
})
it('defaults the outline count to 18 and clamps out-of-range values', async () => {
const captured: string[] = []
globalThis.fetch = mock(async (_url: unknown, init: { body?: string } = {}) => {
captured.push(init.body ?? '')
return new Response(
JSON.stringify({ choices: [{ message: { content: 'A' } }] }),
{ status: 200 },
)
}) as unknown as typeof fetch
const app = new Hono().route('/api/generate', createGenerateRouter('test-key'))
await app.request('/api/generate/outline', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ theme: 'Web 前端' }),
})
await app.request('/api/generate/outline', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ theme: 'Web 前端', count: 999 }),
})
expect(captured[0]).toContain('共约18条')
expect(captured[1]).toContain('共约50条')
})
it('returns 502 when Deepseek response has no content', async () => {
globalThis.fetch = mock(async () => new Response(JSON.stringify({ choices: [] }), { status: 200 })) as unknown as typeof fetch

View File

@@ -90,13 +90,18 @@ export function createGenerateRouter(apiKey: string | undefined): Hono {
})
app.post('/outline', async (c) => {
const body = (await c.req.json().catch(() => null)) as { theme?: unknown } | null
const body = (await c.req.json().catch(() => null)) as
| { theme?: unknown; count?: unknown }
| null
const theme = body?.theme
if (typeof theme !== 'string' || theme.trim() === '') {
return c.json({ error: '请提供课程主题。' }, 400)
}
const rawCount = typeof body?.count === 'number' ? body.count : 18
const count = Math.min(50, Math.max(1, Math.round(rawCount)))
if (!apiKey) {
return c.json({ error: '未配置 DEEPSEEK_API_KEY。' }, 500)
}
@@ -115,7 +120,7 @@ export function createGenerateRouter(apiKey: string | undefined): Hono {
{
role: 'system',
content:
'你是教学设计专家。根据用户提供的课程主题,生成一份完整的课时大纲标题列表,共约18条。' +
`你是教学设计专家。根据用户提供的课程主题,生成一份完整的课时大纲标题列表,共约${count}条。` +
'每个标题格式为"项目名——课时任务",一行一个,不加序号、不加任何说明,直接输出标题列表。',
},
{ role: 'user', content: `课程主题:${theme.trim()}` },