음성 재생과 운영 배포 정리

This commit is contained in:
Yun Chan 2026-06-28 12:18:20 +09:00
parent 8ed185ce6c
commit ac7db95542
1020 changed files with 46863 additions and 2175 deletions

View file

@ -173,23 +173,39 @@ export type PersonaSummary = ApiSchema<"PersonaSummary">;
export type PersonaReviewStatus = "draft" | "review" | "approved" | "archived";
export type PersonaReviewAction = ApiSchema<"PersonaReviewDecisionRequest">["action"];
export type PersonaReviewSummary = ApiSchema<"PersonaReviewSummary">;
export type PersonaRevisionRequest = ApiSchema<"PersonaRevisionRequest">;
export type PersonaDraftPayload = ApiSchema<"PersonaDraftPayload">;
export type PersonaDraftDetail = ApiSchema<"PersonaDraftDetail">;
export type PersonaSourceDocumentRequest = ApiSchema<"PersonaSourceDocumentRequest">;
export type PersonaSourceDocumentResponse = ApiSchema<"PersonaSourceDocumentResponse">;
export type PersonaGenerationEvidence = ApiSchema<"PersonaGenerationEvidence">;
export type PersonaDraftGenerateRequest = ApiSchema<"PersonaDraftGenerateRequest">;
export type PersonaDraftGenerateResponse = ApiSchema<"PersonaDraftGenerateResponse">;
/** POST /sessions — sessions.py SessionStartResponse */
export type SessionStartResponse = ApiSchema<"SessionStartResponse">;
/** POST /sessions/{id}/turn — sessions.py TurnResponse */
export type TurnResponse = ApiSchema<"TurnResponse">;
export type LiveCoachRequest = ApiSchema<"LiveCoachRequest">;
export type LiveCoachEvent = ApiSchema<"LiveCoachEvent">;
export type LiveCoachHistoryResponse = ApiSchema<"LiveCoachHistoryResponse">;
export type LiveCoachSuggestion = ApiSchema<"LiveCoachSuggestion">;
export type LiveCoachSource = ApiSchema<"LiveCoachSource">;
/** POST /sessions/{id}/end — sessions.py SessionEndResponse */
export type SessionEndResponse = ApiSchema<"SessionEndResponse">;
export type CrisisResource = ApiSchema<"CrisisResourceResponse">;
export type LearnerSessionSummary = ApiSchema<"LearnerSessionSummary">;
export type SessionArchiveResponse = ApiSchema<"SessionArchiveResponse">;
export type LearnerSessionsResponse = ApiSchema<"LearnerSessionsResponse">;
export type LearnerDashboardResponse = ApiSchema<"LearnerDashboardResponse">;
export type LearnerDashboardPersonaProgress = ApiSchema<"LearnerDashboardPersonaProgress">;
export type LearnerDashboardAchievement = ApiSchema<"LearnerDashboardAchievement">;
export type LearnerDashboardFeedbackItem = ApiSchema<"LearnerDashboardFeedbackItem">;
export type SessionDetailTurn = ApiSchema<"SessionDetailTurn">;
@ -213,6 +229,8 @@ export type ReviewWorksheetSection = ApiSchema<"ReviewWorksheetSection-Output">;
export type ReviewCaseWorksheet = ApiSchema<"ReviewCaseWorksheet">;
export type ReviewCaseWorksheetSaveRequest = ApiSchema<"ReviewCaseWorksheetSaveRequest">;
export type SessionReviewResponse = ApiSchema<"SessionReviewResponse">;
export type SessionShareResponse = ApiSchema<"SessionShareResponse">;
export type SessionShareDeleteResponse = ApiSchema<"SessionShareDeleteResponse">;
/* =====================================================================
SSE POST /sessions/{id}/stream
@ -381,14 +399,23 @@ export const personaReviewApi = {
}),
createDraft: (payload: PersonaDraftPayload) =>
api.post<PersonaReviewSummary>("/personas/drafts", payload),
createSource: (payload: PersonaSourceDocumentRequest) =>
api.post<PersonaSourceDocumentResponse>("/personas/sources", payload),
generateDraft: (payload: PersonaDraftGenerateRequest) =>
api.post<PersonaDraftGenerateResponse>("/personas/drafts/generate", payload),
getDraft: (personaId: string) =>
api.get<PersonaDraftDetail>(`/personas/drafts/${encodeURIComponent(personaId)}`),
updateDraft: (personaId: string, payload: PersonaDraftPayload) =>
api.put<PersonaReviewSummary>(`/personas/drafts/${encodeURIComponent(personaId)}`, payload),
reviseApproved: (personaId: string, payload: PersonaRevisionRequest = { submit_for_review: false }) =>
api.post<PersonaDraftDetail>(`/personas/${encodeURIComponent(personaId)}/revisions`, payload),
archive: (personaId: string) =>
api.del<PersonaReviewSummary>(`/personas/${encodeURIComponent(personaId)}`),
};
export const sessionApi = {
list: () => api.get<LearnerSessionsResponse>("/sessions"),
dashboard: () => api.get<LearnerDashboardResponse>("/sessions/dashboard"),
/**
* (STT/TTS provider ). degraded 503
* available ( ).
@ -408,8 +435,19 @@ export const sessionApi = {
api.post<SessionStartResponse>("/sessions", { persona_code, theory_mode }),
turn: (sessionId: string, text: string) =>
api.post<TurnResponse>(`/sessions/${encodeURIComponent(sessionId)}/turn`, { text }),
liveCoach: (sessionId: string, payload: LiveCoachRequest) =>
api.post<LiveCoachSuggestion>(
`/sessions/${encodeURIComponent(sessionId)}/live-coach`,
payload,
),
liveCoachHistory: (sessionId: string) =>
api.get<LiveCoachHistoryResponse>(`/sessions/${encodeURIComponent(sessionId)}/live-coach`),
end: (sessionId: string) =>
api.post<SessionEndResponse>(`/sessions/${encodeURIComponent(sessionId)}/end`),
archive: (sessionId: string) =>
api.post<SessionArchiveResponse>(`/sessions/${encodeURIComponent(sessionId)}/archive`, {}),
restore: (sessionId: string) =>
api.post<SessionArchiveResponse>(`/sessions/${encodeURIComponent(sessionId)}/restore`, {}),
review: (sessionId: string) =>
api.get<SessionReviewResponse>(`/sessions/${encodeURIComponent(sessionId)}/review`),
saveWorksheet: (sessionId: string, payload: ReviewCaseWorksheetSaveRequest) =>
@ -417,6 +455,10 @@ export const sessionApi = {
`/sessions/${encodeURIComponent(sessionId)}/review/worksheet`,
payload,
),
createShare: (sessionId: string) =>
api.post<SessionShareResponse>(`/sessions/${encodeURIComponent(sessionId)}/share`, {}),
revokeShare: (sessionId: string) =>
api.del<SessionShareDeleteResponse>(`/sessions/${encodeURIComponent(sessionId)}/share`),
stream: openSessionStream,
};
@ -426,10 +468,44 @@ export type AdminHealthResponse = ApiSchema<"AdminHealthResponse">;
export type AdminUsageBreakdown = ApiSchema<"AdminUsageBreakdown">;
export type AdminUsageBudget = ApiSchema<"AdminUsageBudget">;
export type AdminUsageResponse = ApiSchema<"AdminUsageResponse">;
export type AdminHealthEvent = ApiSchema<"AdminHealthEvent">;
export type AdminUptimeServiceSummary = ApiSchema<"AdminUptimeServiceSummary">;
export type AdminUptimeResponse = ApiSchema<"AdminUptimeResponse">;
export type AdminSupportTicket = ApiSchema<"AdminSupportTicketResponse">;
export type AdminTicketsResponse = ApiSchema<"AdminTicketsResponse">;
export type AdminTicketPatchRequest = ApiSchema<"AdminTicketPatch">;
export type AdminTicketFilters = {
status?: AdminSupportTicket["status"] | "";
category?: AdminSupportTicket["category"] | "";
priority?: AdminSupportTicket["priority"] | "";
assignedGroup?: string;
sourcePath?: string;
staleOnly?: boolean;
search?: string;
windowDays?: number;
};
export const adminApi = {
health: () => api.get<AdminHealthResponse>("/admin/health"),
usage: (windowDays = 7) => api.get<AdminUsageResponse>(`/admin/usage?window_days=${windowDays}`),
uptime: (windowHours = 24) =>
api.get<AdminUptimeResponse>(`/admin/uptime?window_hours=${windowHours}`),
tickets: (filters: AdminTicketFilters = {}) => {
const params = new URLSearchParams({ window_days: String(filters.windowDays ?? 30) });
if (filters.status) params.set("status", filters.status);
if (filters.category) params.set("category", filters.category);
if (filters.priority) params.set("priority", filters.priority);
if (filters.assignedGroup?.trim()) params.set("assigned_group", filters.assignedGroup.trim());
if (filters.sourcePath?.trim()) params.set("source_path", filters.sourcePath.trim());
if (filters.staleOnly) params.set("stale_only", "true");
if (filters.search?.trim()) params.set("search", filters.search.trim());
return api.get<AdminTicketsResponse>(`/admin/tickets?${params.toString()}`);
},
updateTicket: (ticketId: string, body: AdminTicketPatchRequest) =>
apiFetch<AdminSupportTicket>(`/admin/tickets/${encodeURIComponent(ticketId)}`, {
method: "PATCH",
body,
}),
};
export type AdminManagedUser = ApiSchema<"AdminUserResponse">;
@ -462,24 +538,52 @@ export type TeacherGrowthPoint = ApiSchema<"TeacherGrowthPoint">;
export type TeacherLearnerGrowth = ApiSchema<"TeacherLearnerGrowth">;
export type TeacherDashboardResponse = ApiSchema<"TeacherDashboardResponse">;
export type TeacherSessionReviewStatusRequest = ApiSchema<"TeacherSessionReviewStatusRequest">;
export type TeacherSessionReviewStatusResponse = ApiSchema<"TeacherSessionReviewStatusResponse">;
export const teacherApi = {
dashboard: () => api.get<TeacherDashboardResponse>("/teacher/dashboard"),
updateSessionReviewStatus: (
sessionId: string,
body: TeacherSessionReviewStatusRequest,
) =>
apiFetch<TeacherSessionReviewStatusResponse>(
`/teacher/sessions/${encodeURIComponent(sessionId)}/review-status`,
{ method: "PUT", body },
),
};
export type UserProfileResponse = ApiSchema<"UserProfileResponse">;
export type LegalDocumentsResponse = ApiSchema<"LegalDocumentsResponse">;
export type NotificationPreferences = ApiSchema<"NotificationPreferences">;
export type UserPreferencesResponse = ApiSchema<"UserPreferencesResponse">;
export type UserPreferencesPatchRequest = ApiSchema<"UserPreferencesPatch">;
export type UserProfilePatchRequest = ApiSchema<"UserProfilePatch">;
export type OnboardingRequest = ApiSchema<"OnboardingRequest">;
export type AvatarUploadResponse = ApiSchema<"AvatarUploadResponse">;
export type VoicePresetResponse = ApiSchema<"VoicePresetResponse">;
export type RoleString = "learner" | "teacher" | "admin" | string;
export const userApi = {
legalDocs: () => api.get<LegalDocumentsResponse>("/users/legal-docs"),
me: () => api.get<UserProfileResponse>("/users/me"),
updateMe: (body: UserProfilePatchRequest) =>
apiFetch<UserProfileResponse>("/users/me", { method: "PATCH", body }),
uploadAvatar: async (file: File) => {
const form = new FormData();
form.append("file", file);
const res = await fetch(joinUrl("/users/me/avatar"), {
method: "POST",
credentials: "include",
headers: { Accept: "application/json" },
body: form,
});
if (!res.ok) throw await parseError(res);
return (await res.json()) as AvatarUploadResponse;
},
completeOnboarding: (body: OnboardingRequest) =>
apiFetch<UserProfileResponse>("/users/me/onboarding", { method: "POST", body }),
preferences: () => api.get<UserPreferencesResponse>("/users/me/preferences"),
updatePreferences: (body: UserPreferencesPatchRequest) =>
apiFetch<UserPreferencesResponse>("/users/me/preferences", { method: "PATCH", body }),