implemented chat and workflows

This commit is contained in:
2026-05-30 02:22:55 +05:30
parent e48c19b840
commit 4d284b58d7
2 changed files with 53 additions and 4 deletions

View File

@@ -96,6 +96,44 @@ export function chatRoutes() {
const app = new Hono<AuthContext>();
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);

View File

@@ -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<Registry>(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<Registry> | null = null;
function getClient(): Client<Registry> {
if (!_client) {
_client = createClient<Registry>(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() {