implemented chat and workflows
This commit is contained in:
@@ -96,6 +96,44 @@ export function chatRoutes() {
|
|||||||
const app = new Hono<AuthContext>();
|
const app = new Hono<AuthContext>();
|
||||||
app.use("*", requireUser);
|
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) => {
|
app.post("/", async (c) => {
|
||||||
const userId = c.get("userId");
|
const userId = c.get("userId");
|
||||||
const body = chatSchema.parse(await c.req.json());
|
const body = chatSchema.parse(await c.req.json());
|
||||||
@@ -110,7 +148,9 @@ export function chatRoutes() {
|
|||||||
if (result?.reply) {
|
if (result?.reply) {
|
||||||
const reply = cleanWorkflowTag(String(result.reply));
|
const reply = cleanWorkflowTag(String(result.reply));
|
||||||
const workflow = extractWorkflowTag(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) {
|
} catch (err) {
|
||||||
console.warn("Rivet chat unavailable, using direct LLM:", err instanceof Error ? err.message : String(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),
|
reply: cleanWorkflowTag(reply),
|
||||||
workflow: extractWorkflowTag(reply),
|
workflow: extractWorkflowTag(reply),
|
||||||
sessions,
|
sessions,
|
||||||
|
...inferWorkflowStep(sessions, body.messages),
|
||||||
});
|
});
|
||||||
} catch (llmErr) {
|
} catch (llmErr) {
|
||||||
console.error("Direct LLM chat error:", llmErr);
|
console.error("Direct LLM chat error:", llmErr);
|
||||||
|
|||||||
@@ -1,15 +1,23 @@
|
|||||||
import { Hono } from "hono";
|
import { Hono } from "hono";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { createClient } from "rivetkit/client";
|
import { createClient, type Client } from "rivetkit/client";
|
||||||
import { config } from "../config.js";
|
import { config } from "../config.js";
|
||||||
import { requireUser, type AuthContext } from "../auth/clerk.js";
|
import { requireUser, type AuthContext } from "../auth/clerk.js";
|
||||||
import type { Registry } from "../actors/registry.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.
|
// Per changes.md §5: one unified userActor per user.
|
||||||
function userActorFor(userId: string) {
|
function userActorFor(userId: string) {
|
||||||
return client.userActor.getOrCreate([userId]);
|
return getClient().userActor.getOrCreate([userId]);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function workflowRoutes() {
|
export function workflowRoutes() {
|
||||||
|
|||||||
Reference in New Issue
Block a user