diff --git a/src/routes/services.ts b/src/routes/services.ts index 7576795..af302e0 100644 --- a/src/routes/services.ts +++ b/src/routes/services.ts @@ -298,6 +298,39 @@ async function buildPersonalizedConfigurePayload(req: Request, body: JsonObject, }; } +async function buildPersonalizedRoleplayConfigurePayload(req: Request, body: JsonObject, userId: string): Promise { + const userContext = await resolveGrowUserContext(req, userId).catch((err) => { + log.warn({ err, userId }, "failed to resolve Grow user context for roleplay configure"); + return {} as Record; + }); + const incomingMetadata = isRecord(body.metadata) ? body.metadata : {}; + const metadata: Record = { + ...incomingMetadata, + candidate_name: getString(incomingMetadata.candidate_name) ?? getString(userContext.first_name) ?? "", + target_role: getString(incomingMetadata.target_role) ?? getString(userContext.current_role) ?? "General", + candidate_role: getString(incomingMetadata.candidate_role) ?? getString(incomingMetadata.target_role) ?? getString(userContext.current_role) ?? "General", + difficulty: getString(incomingMetadata.difficulty) ?? "medium", + }; + + // Match the production orchestrator behavior: roleplay-service receives the + // same enriched user context the websocket orchestrator attached to A2A tasks, + // plus prompt-safe metadata that can personalize scenario planning/review. + const candidateProfile = composeCandidateProfile(userContext); + if (candidateProfile && (incomingMetadata.personalize ?? true)) { + metadata.candidate_profile = candidateProfile; + metadata.context_notes = getString(incomingMetadata.context_notes) ?? candidateProfile; + } + + return { + ...body, + user_id: String(body.user_id ?? userId), + org_id: String(body.org_id ?? "growqr"), + metadata, + qscore: (body.qscore as JsonObject | undefined) ?? (isRecord(userContext.qscore) ? userContext.qscore : DEFAULT_QSCORE), + user_context: userContext, + }; +} + async function proxySocialRequest(req: Request, rest: string, userId: string) { const incoming = new URL(req.url); const normalizedRest = rest.replace(/^\/+/, ""); @@ -498,16 +531,25 @@ export function serviceRoutes() { app.post("/interview/sessions/:sessionId/video/upload-url", async (c) => c.json(await interviewService.createVideoUploadUrl(c.req.param("sessionId")))); app.post("/interview/sessions/:sessionId/video/uploaded", async (c) => c.json(await interviewService.markVideoUploaded(c.req.param("sessionId")))); - app.get("/roleplay/page-state", async (c) => c.json(await roleplayService.pageState(c.get("userId")))); + app.get("/roleplay/page-state", async (c) => { + const userId = c.get("userId"); + const [state, userContext] = await Promise.all([ + roleplayService.pageState(userId), + resolveGrowUserContext(c.req.raw, userId).catch((err) => { + log.warn({ err, userId }, "failed to resolve Grow user context for roleplay page-state"); + return {} as Record; + }), + ]); + return c.json({ + ...state, + resume_available: hasResumeContext(userContext), + linkedin_available: hasLinkedInContext(userContext), + }); + }); app.post("/roleplay/configure", async (c) => { const userId = c.get("userId"); const body = await c.req.json(); - const payload = { - ...body, - user_id: String(body.user_id ?? userId), - org_id: String(body.org_id ?? "growqr"), - qscore: (body.qscore as JsonObject | undefined) ?? DEFAULT_QSCORE, - } satisfies JsonObject; + const payload = await buildPersonalizedRoleplayConfigurePayload(c.req.raw, body, userId); const result = await roleplayService.configure(payload); const resultObj = result as Record; await recordGatewayEvent({