feat: productionize learning engine and classroom
This commit is contained in:
1 parent
2d04197f3f
commit
ba35adfbfa
124 files changed
+9071
-796
No files matched your search
@@ -0,0 +1,76 @@
|
||||
import { after, type NextRequest } from 'next/server';
|
||||
import { authorizeLearningRuntime, runtimeUnauthorized } from '@/lib/makelore-runtime/auth';
|
||||
import { LEARNING_GENERATION_CONTRACT_VERSION } from '@/lib/makelore-runtime/generation-request';
|
||||
import {
|
||||
controlLargeCourse,
|
||||
LARGE_COURSE_CONTROL_ACTIONS,
|
||||
largeCourseControlErrorResponse,
|
||||
type LargeCourseControlAction,
|
||||
} from '@/lib/makelore-runtime/large-course-control';
|
||||
import { buildRequestOrigin } from '@/lib/server/classroom-storage';
|
||||
|
||||
const MAX_CONTROL_BODY_BYTES = 8 * 1024;
|
||||
|
||||
function parseControlBody(raw: string): {
|
||||
action: LargeCourseControlAction;
|
||||
moduleIndex?: number;
|
||||
} | null {
|
||||
if (new TextEncoder().encode(raw).byteLength > MAX_CONTROL_BODY_BYTES) return null;
|
||||
try {
|
||||
const value = JSON.parse(raw) as unknown;
|
||||
if (!value || typeof value !== 'object' || Array.isArray(value)) return null;
|
||||
const record = value as Record<string, unknown>;
|
||||
if (Object.keys(record).some((key) => key !== 'action' && key !== 'moduleIndex')) return null;
|
||||
const action = record.action as LargeCourseControlAction;
|
||||
if (typeof record.action !== 'string' || !LARGE_COURSE_CONTROL_ACTIONS.includes(action)) {
|
||||
return null;
|
||||
}
|
||||
if (action === 'module-regenerate') {
|
||||
if (
|
||||
typeof record.moduleIndex !== 'number'
|
||||
|| !Number.isInteger(record.moduleIndex)
|
||||
|| record.moduleIndex < 1
|
||||
) return null;
|
||||
return { action, moduleIndex: record.moduleIndex };
|
||||
}
|
||||
if (record.moduleIndex !== undefined) return null;
|
||||
return { action };
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export async function POST(
|
||||
request: NextRequest,
|
||||
context: { params: Promise<{ jobId: string }> },
|
||||
) {
|
||||
if (!authorizeLearningRuntime(request)) return runtimeUnauthorized();
|
||||
const ownerPrincipalId = request.headers.get('x-owner-user-id')?.trim() ?? '';
|
||||
const { jobId } = await context.params;
|
||||
const body = parseControlBody(await request.text());
|
||||
if (!ownerPrincipalId || !body) {
|
||||
return Response.json({ error: 'invalid_request' }, { status: 400 });
|
||||
}
|
||||
try {
|
||||
const result = await controlLargeCourse({
|
||||
courseId: jobId,
|
||||
ownerPrincipalId,
|
||||
action: body.action,
|
||||
moduleIndex: body.moduleIndex,
|
||||
baseUrl: buildRequestOrigin(request),
|
||||
});
|
||||
if (result.run) after(() => result.run!);
|
||||
return Response.json({
|
||||
contractVersion: LEARNING_GENERATION_CONTRACT_VERSION,
|
||||
jobId,
|
||||
mode: 'large',
|
||||
action: result.action,
|
||||
status: result.status,
|
||||
done: false,
|
||||
message: result.message,
|
||||
...(result.moduleIndex === undefined ? {} : { moduleIndex: result.moduleIndex }),
|
||||
});
|
||||
} catch (error) {
|
||||
return largeCourseControlErrorResponse(error);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user