From 4d284b58d7755a688842078f11bf90e2e2359fe8 Mon Sep 17 00:00:00 2001 From: NinjasPyajamas Date: Sat, 30 May 2026 02:22:55 +0530 Subject: [PATCH] implemented chat and workflows --- src/routes/chat.ts | 43 ++++++++++++++++++++++++++++++++++++++++- src/routes/workflows.ts | 14 +++++++++++--- 2 files changed, 53 insertions(+), 4 deletions(-) diff --git a/src/routes/chat.ts b/src/routes/chat.ts index 0a3ccb9..3aa8814 100644 --- a/src/routes/chat.ts +++ b/src/routes/chat.ts @@ -96,6 +96,44 @@ export function chatRoutes() { const app = new Hono(); app.use("*", requireUser); + // Infer workflow step from which agents have been run + function inferWorkflowStep(sessions: Array<{ moduleId: string; status: string }>, messages: Array<{ role: string; content: string }>): { workflowActive: boolean; workflowStep: number; goal: string } { + const doneModules = new Set(sessions.filter(s => s.status === "done").map(s => s.moduleId)); + let step = 0; + let goal = ""; + + // Extract goal from conversation (look for "I have an interview at..." or "prepare for...") + for (const m of messages) { + if (m.role === "user") { + const lower = m.content.toLowerCase(); + if (lower.includes("interview at") || lower.includes("prepare for") || lower.includes("role at") || lower.includes("apply to")) { + goal = m.content; + break; + } + } + } + + // Infer step from completed modules + // Step 1: Workflow started (user described goal) + if (goal) step = 1; + // Step 2: User shared JD/role info + if (messages.filter(m => m.role === "user" && m.content.length > 30).length >= 2) step = 2; + // Step 3: Resume agent done + if (doneModules.has("resume")) step = 3; + // Step 4: Interview session created + if (doneModules.has("sara")) step = 4; + // Step 5: Roleplay session created + if (doneModules.has("emily")) step = 5; + // Step 6: QScore computed + if (doneModules.has("qscore")) step = 6; + + return { + workflowActive: step > 0, + workflowStep: step, + goal: goal || "Career preparation", + }; + } + app.post("/", async (c) => { const userId = c.get("userId"); const body = chatSchema.parse(await c.req.json()); @@ -110,7 +148,9 @@ export function chatRoutes() { if (result?.reply) { const reply = cleanWorkflowTag(String(result.reply)); const workflow = extractWorkflowTag(String(result.reply)); - return c.json({ reply, workflow, sessions: (result as any).sessions ?? [] }); + const sessions = (result as any).sessions ?? []; + const stepInfo = inferWorkflowStep(sessions, body.messages); + return c.json({ reply, workflow, sessions, ...stepInfo }); } } catch (err) { console.warn("Rivet chat unavailable, using direct LLM:", err instanceof Error ? err.message : String(err)); @@ -239,6 +279,7 @@ export function chatRoutes() { reply: cleanWorkflowTag(reply), workflow: extractWorkflowTag(reply), sessions, + ...inferWorkflowStep(sessions, body.messages), }); } catch (llmErr) { console.error("Direct LLM chat error:", llmErr); diff --git a/src/routes/workflows.ts b/src/routes/workflows.ts index 459b1ba..bccb0d4 100644 --- a/src/routes/workflows.ts +++ b/src/routes/workflows.ts @@ -1,15 +1,23 @@ import { Hono } from "hono"; import { z } from "zod"; -import { createClient } from "rivetkit/client"; +import { createClient, type Client } from "rivetkit/client"; import { config } from "../config.js"; import { requireUser, type AuthContext } from "../auth/clerk.js"; import type { Registry } from "../actors/registry.js"; -const client = createClient(config.rivetEndpoint); +// Lazy-load the Rivet client to avoid connecting at import time when the engine +// isn't running (avoids "failed to fetch metadata" spam on startup). +let _client: Client | null = null; +function getClient(): Client { + if (!_client) { + _client = createClient(config.rivetEndpoint); + } + return _client; +} // Per changes.md ยง5: one unified userActor per user. function userActorFor(userId: string) { - return client.userActor.getOrCreate([userId]); + return getClient().userActor.getOrCreate([userId]); } export function workflowRoutes() {