|
|
@@ -1,5 +1,6 @@
|
|
|
import type { TopicDiscussionConfig } from '@/types/englishSpeaking'
|
|
|
import { SPEAKING_CONFIG_API_BASE_URL, SPEAKING_DIALOGUE_API_BASE_URL } from '@/views/Editor/EnglishSpeaking/services/speakingApiConfig'
|
|
|
+import { parseSSEStream } from '@/views/Editor/EnglishSpeaking/services/sseStream'
|
|
|
|
|
|
const API_BASE = SPEAKING_CONFIG_API_BASE_URL
|
|
|
|
|
|
@@ -83,25 +84,39 @@ export async function listSpeakingSessionsByConfig(
|
|
|
return parse<ListSessionsByConfigResponse>(res)
|
|
|
}
|
|
|
|
|
|
-export interface ClassSummaryResponse {
|
|
|
- bullets: [string, string, string]
|
|
|
- generatedAt: string
|
|
|
- fromCache: boolean
|
|
|
- llmStatus: 'ok' | 'fallback'
|
|
|
+export interface ClassReportStudentRef {
|
|
|
+ userId: string
|
|
|
+ name: string
|
|
|
}
|
|
|
|
|
|
-export async function generateClassSummary(
|
|
|
+/**
|
|
|
+ * Stream a tiered Markdown class-analysis report.
|
|
|
+ * Yields Markdown text chunks. Throws Error on a stream `error` event or HTTP failure.
|
|
|
+ */
|
|
|
+export async function* streamClassReport(
|
|
|
configId: string,
|
|
|
- userIds: string[],
|
|
|
+ students: ClassReportStudentRef[],
|
|
|
locale: 'zh' | 'en' | 'hk',
|
|
|
-): Promise<ClassSummaryResponse> {
|
|
|
- const res = await fetch(`${DIALOGUE_BASE}/sessions/by-config/summary`, {
|
|
|
+): AsyncGenerator<string> {
|
|
|
+ const res = await fetch(`${DIALOGUE_BASE}/sessions/by-config/summary/stream`, {
|
|
|
method: 'POST',
|
|
|
headers: { 'Content-Type': 'application/json' },
|
|
|
credentials: 'include',
|
|
|
- body: JSON.stringify({ configId, userIds, locale }),
|
|
|
+ body: JSON.stringify({ configId, students, locale }),
|
|
|
})
|
|
|
- return parse<ClassSummaryResponse>(res)
|
|
|
+ if (!res.ok || !res.body) {
|
|
|
+ const detail = await res.text().catch(() => '')
|
|
|
+ throw new Error(`[${res.status}] ${detail || res.statusText}`)
|
|
|
+ }
|
|
|
+ for await (const event of parseSSEStream(res.body.getReader())) {
|
|
|
+ if (event.type === 'token') {
|
|
|
+ yield event.text
|
|
|
+ } else if (event.type === 'error') {
|
|
|
+ throw new Error(event.message || 'report stream error')
|
|
|
+ } else if (event.type === 'done') {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
export interface DialogueModeDefaults {
|