feat: wire real service agents into chat with LLM tool dispatch + Rivet proxy fix #3
@@ -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);
|
||||
|
||||
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user