Remove plan from chat payload and UI
This commit is contained in:
117
Frontend/app.vue
117
Frontend/app.vue
@@ -5,7 +5,6 @@ import chatMessagesQuery from "./graphql/operations/chat-messages.graphql?raw";
|
||||
import dashboardQuery from "./graphql/operations/dashboard.graphql?raw";
|
||||
import loginMutation from "./graphql/operations/login.graphql?raw";
|
||||
import logoutMutation from "./graphql/operations/logout.graphql?raw";
|
||||
import sendPilotMessageMutation from "./graphql/operations/send-pilot-message.graphql?raw";
|
||||
import logPilotNoteMutation from "./graphql/operations/log-pilot-note.graphql?raw";
|
||||
import createCalendarEventMutation from "./graphql/operations/create-calendar-event.graphql?raw";
|
||||
import createCommunicationMutation from "./graphql/operations/create-communication.graphql?raw";
|
||||
@@ -15,6 +14,8 @@ import createChatConversationMutation from "./graphql/operations/create-chat-con
|
||||
import selectChatConversationMutation from "./graphql/operations/select-chat-conversation.graphql?raw";
|
||||
import confirmLatestChangeSetMutation from "./graphql/operations/confirm-latest-change-set.graphql?raw";
|
||||
import rollbackLatestChangeSetMutation from "./graphql/operations/rollback-latest-change-set.graphql?raw";
|
||||
import { Chat as AiChat } from "@ai-sdk/vue";
|
||||
import { DefaultChatTransport, isTextUIPart, type UIMessage } from "ai";
|
||||
type TabId = "communications" | "documents";
|
||||
type CalendarView = "day" | "week" | "month" | "year" | "agenda";
|
||||
type SortMode = "name" | "lastContact";
|
||||
@@ -182,7 +183,6 @@ type PilotMessage = {
|
||||
id: string;
|
||||
role: "user" | "assistant" | "system";
|
||||
text: string;
|
||||
plan?: string[] | null;
|
||||
thinking?: string[] | null;
|
||||
tools?: string[] | null;
|
||||
toolRuns?: Array<{
|
||||
@@ -203,6 +203,7 @@ type PilotMessage = {
|
||||
after: string;
|
||||
}> | null;
|
||||
createdAt?: string;
|
||||
_live?: boolean;
|
||||
};
|
||||
|
||||
type ChatConversation = {
|
||||
@@ -217,6 +218,25 @@ type ChatConversation = {
|
||||
const pilotMessages = ref<PilotMessage[]>([]);
|
||||
const pilotInput = ref("");
|
||||
const pilotSending = ref(false);
|
||||
const livePilotUserText = ref("");
|
||||
const livePilotAssistantText = ref("");
|
||||
const pilotChat = new AiChat<UIMessage>({
|
||||
transport: new DefaultChatTransport({
|
||||
api: "/api/pilot-chat",
|
||||
}),
|
||||
onFinish: async () => {
|
||||
livePilotUserText.value = "";
|
||||
livePilotAssistantText.value = "";
|
||||
await Promise.all([loadPilotMessages(), loadChatConversations(), refreshCrmData()]);
|
||||
},
|
||||
onError: () => {
|
||||
if (livePilotUserText.value) {
|
||||
pilotInput.value = livePilotUserText.value;
|
||||
}
|
||||
livePilotUserText.value = "";
|
||||
livePilotAssistantText.value = "";
|
||||
},
|
||||
});
|
||||
const authMe = ref<{
|
||||
user: { id: string; phone: string; name: string };
|
||||
team: { id: string; name: string };
|
||||
@@ -264,6 +284,47 @@ function formatPilotStamp(iso?: string) {
|
||||
}).format(new Date(iso));
|
||||
}
|
||||
|
||||
function pilotToUiMessage(message: PilotMessage): UIMessage {
|
||||
return {
|
||||
id: message.id,
|
||||
role: message.role,
|
||||
parts: [{ type: "text", text: message.text }],
|
||||
metadata: {
|
||||
createdAt: message.createdAt ?? null,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function syncPilotChatFromHistory(messages: PilotMessage[]) {
|
||||
pilotChat.messages = messages.map(pilotToUiMessage);
|
||||
}
|
||||
|
||||
const renderedPilotMessages = computed<PilotMessage[]>(() => {
|
||||
const items = [...pilotMessages.value];
|
||||
|
||||
if (livePilotUserText.value) {
|
||||
items.push({
|
||||
id: "pilot-live-user",
|
||||
role: "user",
|
||||
text: livePilotUserText.value,
|
||||
createdAt: new Date().toISOString(),
|
||||
_live: true,
|
||||
});
|
||||
}
|
||||
|
||||
if (livePilotAssistantText.value) {
|
||||
items.push({
|
||||
id: "pilot-live-assistant",
|
||||
role: "assistant",
|
||||
text: livePilotAssistantText.value,
|
||||
createdAt: new Date().toISOString(),
|
||||
_live: true,
|
||||
});
|
||||
}
|
||||
|
||||
return items;
|
||||
});
|
||||
|
||||
async function gqlFetch<TData>(query: string, variables?: Record<string, unknown>) {
|
||||
const result = await $fetch<{ data?: TData; errors?: Array<{ message: string }> }>("/api/graphql", {
|
||||
method: "POST",
|
||||
@@ -284,6 +345,7 @@ async function gqlFetch<TData>(query: string, variables?: Record<string, unknown
|
||||
async function loadPilotMessages() {
|
||||
const data = await gqlFetch<{ chatMessages: PilotMessage[] }>(chatMessagesQuery);
|
||||
pilotMessages.value = data.chatMessages ?? [];
|
||||
syncPilotChatFromHistory(pilotMessages.value);
|
||||
}
|
||||
|
||||
async function loadChatConversations() {
|
||||
@@ -352,6 +414,9 @@ async function logout() {
|
||||
await gqlFetch<{ logout: { ok: boolean } }>(logoutMutation);
|
||||
authMe.value = null;
|
||||
pilotMessages.value = [];
|
||||
livePilotUserText.value = "";
|
||||
livePilotAssistantText.value = "";
|
||||
pilotChat.messages = [];
|
||||
chatConversations.value = [];
|
||||
}
|
||||
|
||||
@@ -394,21 +459,39 @@ async function sendPilotMessage() {
|
||||
|
||||
pilotSending.value = true;
|
||||
pilotInput.value = "";
|
||||
await loadPilotMessages().catch(() => {});
|
||||
const pollId = setInterval(() => {
|
||||
loadPilotMessages().catch(() => {});
|
||||
}, 450);
|
||||
livePilotUserText.value = text;
|
||||
livePilotAssistantText.value = "";
|
||||
try {
|
||||
await gqlFetch<{ sendPilotMessage: { ok: boolean } }>(sendPilotMessageMutation, { text });
|
||||
await Promise.all([loadPilotMessages(), loadChatConversations(), refreshCrmData()]);
|
||||
await pilotChat.sendMessage({ text });
|
||||
} catch {
|
||||
pilotInput.value = text;
|
||||
} finally {
|
||||
clearInterval(pollId);
|
||||
const latestAssistant = [...pilotChat.messages]
|
||||
.reverse()
|
||||
.find((message) => message.role === "assistant");
|
||||
|
||||
if (latestAssistant) {
|
||||
const textPart = latestAssistant.parts.find(isTextUIPart);
|
||||
livePilotAssistantText.value = textPart?.text ?? "";
|
||||
}
|
||||
|
||||
livePilotUserText.value = "";
|
||||
livePilotAssistantText.value = "";
|
||||
pilotSending.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
watchEffect(() => {
|
||||
if (!pilotSending.value) return;
|
||||
const latestAssistant = [...pilotChat.messages]
|
||||
.reverse()
|
||||
.find((message) => message.role === "assistant");
|
||||
if (!latestAssistant) return;
|
||||
|
||||
const textPart = latestAssistant.parts.find(isTextUIPart);
|
||||
livePilotAssistantText.value = textPart?.text ?? "";
|
||||
});
|
||||
|
||||
const changePanelOpen = ref(true);
|
||||
const changeActionBusy = ref(false);
|
||||
|
||||
@@ -1343,7 +1426,7 @@ async function decideFeedCard(card: FeedCard, decision: "accepted" | "rejected")
|
||||
|
||||
<div class="pilot-timeline min-h-0 flex-1 overflow-y-auto">
|
||||
<div
|
||||
v-for="message in pilotMessages"
|
||||
v-for="message in renderedPilotMessages"
|
||||
:key="message.id"
|
||||
class="pilot-row"
|
||||
>
|
||||
@@ -1362,16 +1445,9 @@ async function decideFeedCard(card: FeedCard, decision: "accepted" | "rejected")
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="message.role !== 'user' && ((message.plan && message.plan.length) || (message.thinking && message.thinking.length) || (message.toolRuns && message.toolRuns.length) || (message.tools && message.tools.length))"
|
||||
v-if="!message._live && message.role !== 'user' && ((message.thinking && message.thinking.length) || (message.toolRuns && message.toolRuns.length) || (message.tools && message.tools.length))"
|
||||
class="pilot-debug mt-2"
|
||||
>
|
||||
<div v-if="message.plan && message.plan.length" class="pilot-debug-block">
|
||||
<p class="pilot-debug-title">Plan</p>
|
||||
<ol class="pilot-debug-list">
|
||||
<li v-for="(step, idx) in message.plan" :key="`plan-${message.id}-${idx}`">{{ step }}</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
<div v-if="message.thinking && message.thinking.length" class="pilot-debug-block">
|
||||
<p class="pilot-debug-title">Trace</p>
|
||||
<ol class="pilot-debug-list">
|
||||
@@ -1469,13 +1545,13 @@ async function decideFeedCard(card: FeedCard, decision: "accepted" | "rejected")
|
||||
</aside>
|
||||
|
||||
<main class="min-h-0 bg-base-100">
|
||||
<div class="flex h-full min-h-0 flex-col pb-20 md:pb-24">
|
||||
<div class="flex h-full min-h-0 flex-col pb-16 md:pb-0">
|
||||
<div class="workspace-topbar border-b border-base-300 px-3 py-2 md:px-4">
|
||||
<div class="ml-auto flex items-center gap-2">
|
||||
<button class="btn btn-sm btn-outline" @click="logout">Logout</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="min-h-0 flex-1 p-3 md:p-4">
|
||||
<div class="min-h-0 flex-1 px-3 pt-3 pb-0 md:px-4 md:pt-4 md:pb-0">
|
||||
<section v-if="selectedTab === 'communications' && peopleLeftMode === 'calendar'" class="flex h-full min-h-0 flex-col gap-3">
|
||||
<div class="mb-1 flex justify-end">
|
||||
<div class="join">
|
||||
@@ -2286,6 +2362,7 @@ async function decideFeedCard(card: FeedCard, decision: "accepted" | "rejected")
|
||||
v-model="selectedWorkspaceContact.description"
|
||||
:room="`crm-contact-${selectedWorkspaceContact.id}`"
|
||||
placeholder="Contact summary..."
|
||||
:plain="true"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -3,7 +3,6 @@ query ChatMessagesQuery {
|
||||
id
|
||||
role
|
||||
text
|
||||
plan
|
||||
thinking
|
||||
tools
|
||||
toolRuns {
|
||||
|
||||
@@ -859,7 +859,6 @@ export async function runLangGraphCrmAgentFor(input: {
|
||||
tools: [crmTool],
|
||||
responseFormat: z.object({
|
||||
answer: z.string().describe("Final assistant answer for the user."),
|
||||
plan: z.array(z.string()).min(1).max(10).describe("Short plan (3-8 steps)."),
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -924,7 +923,7 @@ export async function runLangGraphCrmAgentFor(input: {
|
||||
return String(msg?.type ?? msg?.role ?? msg?.constructor?.name ?? "");
|
||||
};
|
||||
|
||||
const structured = res?.structuredResponse as { answer?: string; plan?: string[] } | undefined;
|
||||
const structured = res?.structuredResponse as { answer?: string } | undefined;
|
||||
const fallbackText = (() => {
|
||||
const messages = Array.isArray(res?.messages) ? res.messages : [];
|
||||
for (let i = messages.length - 1; i >= 0; i -= 1) {
|
||||
@@ -945,7 +944,7 @@ export async function runLangGraphCrmAgentFor(input: {
|
||||
if (!text) {
|
||||
throw new Error("Model returned empty response");
|
||||
}
|
||||
const plan = Array.isArray(structured?.plan) ? structured.plan : [];
|
||||
const plan: string[] = [];
|
||||
|
||||
return {
|
||||
text,
|
||||
|
||||
@@ -225,7 +225,6 @@ async function getChatMessages(auth: AuthContext | null) {
|
||||
id: m.id,
|
||||
role: m.role === "USER" ? "user" : m.role === "ASSISTANT" ? "assistant" : "system",
|
||||
text: m.text,
|
||||
plan: Array.isArray(debug.steps) ? (debug.steps as string[]) : [],
|
||||
thinking: Array.isArray(debug.thinking) ? (debug.thinking as string[]) : [],
|
||||
tools: Array.isArray(debug.tools) ? (debug.tools as string[]) : [],
|
||||
toolRuns: Array.isArray(debug.toolRuns)
|
||||
@@ -619,7 +618,6 @@ async function sendPilotMessage(auth: AuthContext | null, textInput: string) {
|
||||
authorUserId: null,
|
||||
role: "SYSTEM",
|
||||
text: event.text,
|
||||
plan: [],
|
||||
thinking: [],
|
||||
tools: event.toolRun ? [event.toolRun.name] : [],
|
||||
toolRuns: event.toolRun ? [event.toolRun] : [],
|
||||
@@ -636,7 +634,6 @@ async function sendPilotMessage(auth: AuthContext | null, textInput: string) {
|
||||
authorUserId: null,
|
||||
role: "ASSISTANT",
|
||||
text: reply.text,
|
||||
plan: reply.plan,
|
||||
thinking: reply.thinking ?? [],
|
||||
tools: reply.tools,
|
||||
toolRuns: reply.toolRuns ?? [],
|
||||
@@ -657,7 +654,6 @@ async function logPilotNote(auth: AuthContext | null, textInput: string) {
|
||||
authorUserId: null,
|
||||
role: "ASSISTANT",
|
||||
text,
|
||||
plan: [],
|
||||
thinking: [],
|
||||
tools: [],
|
||||
toolRuns: [],
|
||||
@@ -747,7 +743,6 @@ export const crmGraphqlSchema = buildSchema(`
|
||||
id: ID!
|
||||
role: String!
|
||||
text: String!
|
||||
plan: [String!]!
|
||||
thinking: [String!]!
|
||||
tools: [String!]!
|
||||
toolRuns: [PilotToolRun!]!
|
||||
|
||||
Reference in New Issue
Block a user