Merge pull request #13 from josedario87/codex/create-conversation-object
Add conversation snapshot structure
This commit is contained in:
@@ -6,12 +6,37 @@ import dotenv from 'dotenv';
|
|||||||
|
|
||||||
dotenv.config();
|
dotenv.config();
|
||||||
|
|
||||||
|
interface Participant {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
isMe: boolean;
|
||||||
|
isAdmin?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Msg {
|
||||||
|
id: string;
|
||||||
|
from: string;
|
||||||
|
to: string;
|
||||||
|
ts: number;
|
||||||
|
type: 'chat' | 'image' | 'audio' | 'sticker' | 'doc';
|
||||||
|
text?: string;
|
||||||
|
mediaUrl?: string;
|
||||||
|
mentions?: string[];
|
||||||
|
meta: {
|
||||||
|
ack: number;
|
||||||
|
hasReaction: boolean;
|
||||||
|
isQuoted: boolean;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
interface Conversation {
|
interface Conversation {
|
||||||
chatId: string;
|
chatId: string;
|
||||||
messages: { text: string }[];
|
title: string;
|
||||||
|
isGroup: boolean;
|
||||||
|
unreadCount: number;
|
||||||
|
participants: Participant[];
|
||||||
|
messages: Msg[];
|
||||||
createdAt: number;
|
createdAt: number;
|
||||||
updatedAt: number;
|
|
||||||
messageCount: number;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const PORT = Number(process.env.PORT) || 8001;
|
const PORT = Number(process.env.PORT) || 8001;
|
||||||
@@ -44,16 +69,27 @@ app.use(express.json());
|
|||||||
app.post('/', async (req, res) => {
|
app.post('/', async (req, res) => {
|
||||||
const conversation = req.body?.conversation as Conversation | undefined;
|
const conversation = req.body?.conversation as Conversation | undefined;
|
||||||
if (!conversation) return res.status(400).json({ error: 'Missing conversation' });
|
if (!conversation) return res.status(400).json({ error: 'Missing conversation' });
|
||||||
const message = conversation.messages[conversation.messages.length - 1]?.text || '';
|
const lastMsg = conversation.messages[conversation.messages.length - 1];
|
||||||
|
const message = lastMsg?.text || '';
|
||||||
|
|
||||||
|
const context = conversation.messages
|
||||||
|
.slice(-10)
|
||||||
|
.map((m) => {
|
||||||
|
const sender =
|
||||||
|
conversation.participants.find((p) => p.id === m.from)?.name || m.from;
|
||||||
|
const content = m.text || `[${m.type}]`;
|
||||||
|
return `${sender}: ${content}`;
|
||||||
|
})
|
||||||
|
.join('\n');
|
||||||
|
|
||||||
if (!genAI) {
|
if (!genAI) {
|
||||||
return res.json({ reply: repoInfo });
|
return res.json({ reply: repoInfo });
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const contents = `Repo information: ${repoInfo}\nUser message: ${message}`;
|
const contents = `Repo information: ${repoInfo}\nConversation:\n${context}\n`;
|
||||||
const config: any = {};
|
const config: any = {};
|
||||||
if (message.toLowerCase().includes('/planilla')) {
|
if (true) {
|
||||||
console.log('Using Model Context Protocol tools ', MCP_URL);
|
console.log('Using Model Context Protocol tools ', MCP_URL);
|
||||||
const client = await getMcpClient();
|
const client = await getMcpClient();
|
||||||
config.tools = [mcpToTool(client)];
|
config.tools = [mcpToTool(client)];
|
||||||
@@ -76,7 +112,7 @@ app.get('/', (req, res) => {
|
|||||||
<h1>Conversation Layer Agent</h1>
|
<h1>Conversation Layer Agent</h1>
|
||||||
<p>This service answers questions about the repository.</p>
|
<p>This service answers questions about the repository.</p>
|
||||||
<p>Send a POST request to / with a JSON body containing {"conversation": {...}}</p>
|
<p>Send a POST request to / with a JSON body containing {"conversation": {...}}</p>
|
||||||
<p>Example: {"conversation": {"chatId": "123@c.us", "messages": [{"text": "hello"}]}}</p>
|
<p>Example: {"conversation": {"chatId": "123@c.us", "title": "Chat", "isGroup": false, "unreadCount": 0, "participants": [{"id": "123@c.us", "name": "Alice", "isMe": false}], "messages": [{"id": "m1", "from": "123@c.us", "to": "me@c.us", "ts": 0, "type": "chat", "text": "hello", "meta": {"ack":0,"hasReaction":false,"isQuoted":false}}]}}</p>
|
||||||
<p>It will respond with a JSON object containing {"reply": "the answer"}</p>
|
<p>It will respond with a JSON object containing {"reply": "the answer"}</p>
|
||||||
|
|
||||||
<p>Repository info: ${repoInfo}</p>
|
<p>Repository info: ${repoInfo}</p>
|
||||||
@@ -86,4 +122,4 @@ app.get('/', (req, res) => {
|
|||||||
|
|
||||||
app.listen(PORT, () => {
|
app.listen(PORT, () => {
|
||||||
console.log(`conversation-layer-agent listening on ${PORT}`);
|
console.log(`conversation-layer-agent listening on ${PORT}`);
|
||||||
});
|
});
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { WhatsAppMessage, Conversation } from '../types';
|
import { WhatsAppMessage, Conversation, Msg, Participant } from '../types';
|
||||||
|
|
||||||
const conversations = new Map<string, Conversation>();
|
const conversations = new Map<string, Conversation>();
|
||||||
|
|
||||||
@@ -19,6 +19,24 @@ async function loadMessages(
|
|||||||
return msgs;
|
return msgs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function mapMessage(m: WhatsAppMessage): Msg {
|
||||||
|
return {
|
||||||
|
id: m.id,
|
||||||
|
from: m.from,
|
||||||
|
to: m.to,
|
||||||
|
ts: (m as any).timestamp || (m as any).t,
|
||||||
|
type: ((m as any).type as any) || 'chat',
|
||||||
|
text: (m as any).text || (m as any).caption || (m as any).body,
|
||||||
|
mediaUrl: (m as any).cloudUrl || (m as any).clientUrl,
|
||||||
|
mentions: ((m as any).mentionedJidList as any) || [],
|
||||||
|
meta: {
|
||||||
|
ack: (m as any).ack || 0,
|
||||||
|
hasReaction: (m as any).hasReaction || false,
|
||||||
|
isQuoted: !!(m as any).quotedMsg,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export async function getConversation(
|
export async function getConversation(
|
||||||
chatId: string,
|
chatId: string,
|
||||||
openWaUrl: string
|
openWaUrl: string
|
||||||
@@ -41,15 +59,61 @@ export async function buildConversation(
|
|||||||
openWaUrl: string
|
openWaUrl: string
|
||||||
): Promise<Conversation> {
|
): Promise<Conversation> {
|
||||||
console.log(`[conversationStore] Building conversation for ${chatId}`);
|
console.log(`[conversationStore] Building conversation for ${chatId}`);
|
||||||
const messages = await loadMessages(chatId, openWaUrl);
|
const rawMessages = await loadMessages(chatId, openWaUrl);
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
|
|
||||||
|
const first = rawMessages[0];
|
||||||
|
const chat = first?.chat;
|
||||||
|
const title = chat?.formattedTitle || chat?.name || chatId;
|
||||||
|
const isGroup = chat?.isGroup || false;
|
||||||
|
const unreadCount = chat?.unreadCount || 0;
|
||||||
|
|
||||||
|
const participantsMap = new Map<string, Participant>();
|
||||||
|
if (chat?.contact) {
|
||||||
|
const c = chat.contact;
|
||||||
|
participantsMap.set(c.id, {
|
||||||
|
id: c.id,
|
||||||
|
name: c.pushname || c.name || '',
|
||||||
|
isMe: c.isMe,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (isGroup && chat?.groupMetadata?.participants) {
|
||||||
|
for (const p of chat.groupMetadata.participants as any[]) {
|
||||||
|
const c = p.contact || {};
|
||||||
|
const id = c.id || p.id;
|
||||||
|
participantsMap.set(id, {
|
||||||
|
id,
|
||||||
|
name: c.pushname || c.name || '',
|
||||||
|
isMe: c.isMe || false,
|
||||||
|
isAdmin: p.isAdmin || p.isSuperAdmin,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const m of rawMessages) {
|
||||||
|
const s = m.sender;
|
||||||
|
if (s && !participantsMap.has(s.id)) {
|
||||||
|
participantsMap.set(s.id, {
|
||||||
|
id: s.id,
|
||||||
|
name: s.pushname || s.name || '',
|
||||||
|
isMe: s.isMe,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const messages: Msg[] = rawMessages.slice(-20).map(mapMessage);
|
||||||
|
messages.sort((a, b) => a.ts - b.ts);
|
||||||
|
|
||||||
const conv: Conversation = {
|
const conv: Conversation = {
|
||||||
chatId,
|
chatId,
|
||||||
|
title,
|
||||||
|
isGroup,
|
||||||
|
unreadCount,
|
||||||
|
participants: Array.from(participantsMap.values()),
|
||||||
messages,
|
messages,
|
||||||
createdAt: conversations.get(chatId)?.createdAt || now,
|
createdAt: conversations.get(chatId)?.createdAt || now,
|
||||||
updatedAt: now,
|
|
||||||
messageCount: messages.length,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
conversations.set(chatId, conv);
|
conversations.set(chatId, conv);
|
||||||
return conv;
|
return conv;
|
||||||
}
|
}
|
||||||
@@ -66,8 +130,16 @@ export async function addMessageToConversation(
|
|||||||
): Promise<Conversation> {
|
): Promise<Conversation> {
|
||||||
console.log(`[conversationStore] Adding message to ${chatId}`);
|
console.log(`[conversationStore] Adding message to ${chatId}`);
|
||||||
const conv = await getConversation(chatId, openWaUrl);
|
const conv = await getConversation(chatId, openWaUrl);
|
||||||
conv.messages.push(msg);
|
const mapped = mapMessage(msg);
|
||||||
conv.messageCount = conv.messages.length;
|
conv.messages.push(mapped);
|
||||||
conv.updatedAt = Date.now();
|
if (conv.messages.length > 20) conv.messages.shift();
|
||||||
|
const s = msg.sender;
|
||||||
|
if (s && !conv.participants.some((p) => p.id === s.id)) {
|
||||||
|
conv.participants.push({
|
||||||
|
id: s.id,
|
||||||
|
name: s.pushname || s.name || '',
|
||||||
|
isMe: s.isMe,
|
||||||
|
});
|
||||||
|
}
|
||||||
return conv;
|
return conv;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -193,10 +193,35 @@ export interface WhatsAppMessage {
|
|||||||
text: string;
|
text: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface Participant {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
isMe: boolean;
|
||||||
|
isAdmin?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Msg {
|
||||||
|
id: string;
|
||||||
|
from: string;
|
||||||
|
to: string;
|
||||||
|
ts: number;
|
||||||
|
type: 'chat' | 'image' | 'audio' | 'sticker' | 'doc';
|
||||||
|
text?: string;
|
||||||
|
mediaUrl?: string;
|
||||||
|
mentions?: string[];
|
||||||
|
meta: {
|
||||||
|
ack: number;
|
||||||
|
hasReaction: boolean;
|
||||||
|
isQuoted: boolean;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export interface Conversation {
|
export interface Conversation {
|
||||||
chatId: string;
|
chatId: string;
|
||||||
messages: WhatsAppMessage[];
|
title: string;
|
||||||
|
isGroup: boolean;
|
||||||
|
unreadCount: number;
|
||||||
|
participants: Participant[];
|
||||||
|
messages: Msg[];
|
||||||
createdAt: number;
|
createdAt: number;
|
||||||
updatedAt: number;
|
|
||||||
messageCount: number;
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user