feat: preview templates, thumbnail features, 2 new org templates

This commit is contained in:
Rotimi Best
2026-05-23 12:24:57 +01:00
parent 4fdd4115b9
commit 78ab13f54b
106 changed files with 8320 additions and 2040 deletions
+7 -28
View File
@@ -432,12 +432,10 @@ const agentCoreRouter = new Hono()
}
}
if (role === AgentRole.TEACHER && !modelDescriptor.isFree) {
const isPaid = await isOrgOnPaidPlan(orgId);
const isOrgPaid = role === AgentRole.TEACHER ? await isOrgOnPaidPlan(orgId) : false;
if (!isPaid) {
throw new AppError('Upgrade required to use this AI model', 'UPGRADE_REQUIRED', 403);
}
if (role === AgentRole.TEACHER && !modelDescriptor.isFree && !isOrgPaid) {
throw new AppError('Upgrade required to use this AI model', 'UPGRADE_REQUIRED', 403);
}
// Students go through tutor policy (workspace toggle, pool, per-learner cap).
@@ -520,7 +518,8 @@ const agentCoreRouter = new Hono()
// approved plan/active template) is sent as a user-turn message instead so
// it doesn't invalidate the tools+system cache.
const systemPrompt = buildSystemPrompt(agentContext, {
tutorSettings: studentPolicy?.settings
tutorSettings: studentPolicy?.settings,
isOrgOnPaidPlan: isOrgPaid
});
const contextMessageText = buildContextMessage(agentContext, {
@@ -531,7 +530,7 @@ const agentCoreRouter = new Hono()
const agentTools =
role === AgentRole.STUDENT
? buildStudentAgentTools(orgId, user.id, courseId, studentPolicy!.settings)
: buildAgentTools(orgId, user.id, courseId, messages);
: buildAgentTools(orgId, user.id, courseId, messages, { isOrgOnPaidPlan: isOrgPaid });
const contextManaged = await buildModelContextMessages({
conversationId,
@@ -569,27 +568,7 @@ const agentCoreRouter = new Hono()
]
: convertedMessages;
console.log(
'[agent.chat] modelMessages →',
JSON.stringify(
modelMessages.map((m, i) => ({
i,
role: m.role,
parts: Array.isArray(m.content)
? m.content.map((p: any) => ({
type: p.type,
toolCallId: p.toolCallId,
toolName: p.toolName,
textPreview: typeof p.text === 'string' ? p.text.slice(0, 60) : undefined
}))
: typeof m.content === 'string'
? m.content.slice(0, 80)
: m.content
})),
null,
2
)
);
console.log(`[agent.chat] user=${user.id} messages=${modelMessages.length}`);
// Cache the growing conversation prefix: each turn reads the prior
// transcript at ~0.1x cost instead of reprocessing it at full price.
+1
View File
@@ -52,6 +52,7 @@ export const agentRunsRouter = new Hono()
courseId: data.courseId,
conversationId: data.conversationId,
phase: data.phase,
modelId: data.model,
approvedPlan: data.approvedPlan,
executionCursor: data.executionCursor,
sourceIds: data.sourceIds,
+22
View File
@@ -10,6 +10,7 @@ import {
getMediaJobSteps,
listMediaJobEnvelopes,
listMediaJobsForAssetEnvelopes,
startThumbnailRegenJob,
startTranscriptionOnlyMediaJob
} from '@api/services/jobs';
@@ -108,6 +109,27 @@ export const jobsRouter = new Hono()
}
}
)
.post(
'/media/asset/:assetId/regenerate-thumbnail',
authMiddleware,
orgMemberMiddleware,
zValidator('param', ZAssetJobsParam),
async (c) => {
try {
const orgId = c.req.header('cio-org-id')!;
const user = c.get('user')!;
const { assetId } = c.req.valid('param');
const job = await startThumbnailRegenJob({
organizationId: orgId,
assetId,
triggeredByProfileId: user?.id ?? null
});
return c.json({ success: true, data: job }, 202);
} catch (error) {
return handleError(c, error, 'Failed to start thumbnail regeneration');
}
}
)
.post(
'/media/:jobId/cancel',
authMiddleware,