feat: Connect chat interface to conversation layer router

Integrates the chat UI with the conversation layer router.

Key changes:

- Added new environment variables (`CONVERSATION_LAYER_ROUTER_URL` for the API and `VITE_CONVERSATION_LAYER_ROUTER_URL` for the UI) to specify the router's URL. These have been configured in `docker-compose.yml`, `api/.env.example`, and `ui/runtime-env.sh`.

- Modified `ui/src/stores/useChat.js`:
    - Introduced a `sendMessage` action that constructs a message object (as per the specified format with `id`, `from`, `to`, `ts`, `type`, `text`, `meta`) and sends it to the conversation router.
    - Implemented optimistic local display of your message.
    - Handles the `Conversation` object returned by the router, iterating through its `messages` array and adding them to the chat display.
    - Determines message ownership ('user' for UI messages, 'bot' for Nucleo messages) for appropriate rendering.
    - Includes basic error handling for API requests, logging errors and showing system messages in the chat.

- Updated `ui/src/components/chat/CanvasChat.vue`:
    - Modified the message sending mechanism to use the new `chat.sendMessage()` action.
    - Adjusted message rendering to correctly differentiate between messages from 'user' and 'bot' based on the `owner` field from the chat store.

This allows the UI to send messages to the conversation router and display the resulting conversation, focusing on rendering messages from both the UI ('planilla-UI') and the bot ('Nucleo').
This commit is contained in:
google-labs-jules[bot]
2025-06-12 01:23:09 +00:00
parent 33fc3abdaa
commit 2dc118dc19
5 changed files with 92 additions and 7 deletions

View File

@@ -5,4 +5,5 @@
# See the documentation for all the connection string options: https://pris.ly/d/connection-strings
DATABASE_URL=postgresql://planilla:planilla@localhost:5434/planilla_db?schema=public
CONVERSATION_LAYER_ROUTER_URL='http://your-router-url:port'

View File

@@ -21,6 +21,7 @@ services:
restart: unless-stopped
environment:
DATABASE_URL: "postgresql://planilla:secret@db:5432/planilla_db?schema=public"
CONVERSATION_LAYER_ROUTER_URL: 'http://your-router-url:port'
depends_on:
- db
ports:
@@ -34,6 +35,7 @@ services:
environment:
VITE_API_EVENTS_URL: https://planilla.interno.com/events
VITE_API_DB_URL: https://planilla.interno.com
VITE_CONVERSATION_LAYER_ROUTER_URL: 'http://your-router-url:port'
ports:
- "3008:80"
networks: [planilla, principal]

View File

@@ -1,5 +1,6 @@
#!/bin/sh
echo "window.RUNTIME_CONFIG = {" > /usr/share/nginx/html/config.js
echo " VITE_API_EVENTS_URL: '${VITE_API_EVENTS_URL}'," >> /usr/share/nginx/html/config.js
echo " VITE_API_DB_URL: '${VITE_API_DB_URL}'" >> /usr/share/nginx/html/config.js
echo " VITE_API_DB_URL: '${VITE_API_DB_URL}'," >> /usr/share/nginx/html/config.js
echo " VITE_CONVERSATION_LAYER_ROUTER_URL: '${VITE_CONVERSATION_LAYER_ROUTER_URL}'," >> /usr/share/nginx/html/config.js
echo "};" >> /usr/share/nginx/html/config.js

View File

@@ -28,7 +28,7 @@ function send () {
if (!t) return
if (t.startsWith('/')) chat.run(t.slice(1))
else chat.add({ type: 'text', owner: 'yo', text: t })
else chat.sendMessage(t)
msg.value = ''
scrollBottom()
@@ -58,12 +58,12 @@ watch(() => chat.items.length, scrollBottom)
<div ref="list" class="flex-1 min-h-0 overflow-auto p-6 space-y-4 custom-scroll">
<template v-for="(m,i) in chat.items" :key="i">
<!-- mensaje de texto -->
<div :class="m.owner==='yo' ? 'flex justify-end' : 'flex justify-start'" v-if="m.type==='text'">
<div :class="m.owner==='user' ? 'flex justify-end' : 'flex justify-start'" v-if="m.type==='text'">
<div
class="max-w-lg rounded-lg px-4 py-2 shadow break-words"
:class="m.owner === 'yo' ? '' : ''"
:class="m.owner === 'user' ? '' : ''"
:style="{
backgroundColor: m.owner === 'yo' ? 'var(--chat-own-message-color)' : 'var(--chat-agent-message-color)',
backgroundColor: m.owner === 'user' ? 'var(--chat-own-message-color)' : 'var(--chat-agent-message-color)',
color: 'var(--chat-font-color)',
fontFamily: 'var(--chat-font-family)',
fontSize: 'var(--chat-font-size)'

View File

@@ -1,6 +1,5 @@
import axios from 'axios';
import { defineStore } from 'pinia'
/**
@@ -38,5 +37,87 @@ export const useChat = defineStore('chat', {
this.add({ type: 'text', owner: 'bot', text: `❓ No reconozco /${cmd}` })
}
},
async sendMessage (text) {
const routerUrl = window.RUNTIME_CONFIG?.VITE_CONVERSATION_LAYER_ROUTER_URL || 'http://localhost:8080/fallback-router'; // Fallback or error
if (!window.RUNTIME_CONFIG?.VITE_CONVERSATION_LAYER_ROUTER_URL) {
console.error('VITE_CONVERSATION_LAYER_ROUTER_URL is not defined in window.RUNTIME_CONFIG. Using fallback or please check your configuration.');
}
const messagePayload = {
id: `planilla-UI-${Date.now()}`, // Simple unique ID for now
from: 'planilla-UI',
to: 'Nucleo',
ts: Math.floor(Date.now() / 1000), // Timestamp in seconds
type: 'chat',
text: text, // User's message text
mediaUrl: null, // Assuming no media for now
mentions: null, // Assuming no mentions for now
meta: {
ack: 1, // Or 0 if acknowledgement is handled by the router
hasReaction: false,
isQuoted: false,
},
};
// Optimistically add message to chat
this.add({
id: messagePayload.id, // Use the same ID
type: 'chat',
owner: 'user', // Or a more specific identifier if available
text: text,
ts: messagePayload.ts
});
try {
const response = await axios.post(routerUrl, messagePayload);
console.log('Message sent, raw response:', response.data); // Log raw response
const conversation = response.data;
if (conversation && Array.isArray(conversation.messages)) {
conversation.messages.forEach(msg => {
let owner = 'bot'; // Default to bot
if (msg.from === 'planilla-UI') {
owner = 'user';
// NOTE: This might add a duplicate if the optimistic message's ID
// is different from the ID assigned by the router for the echoed message.
// The current instructions are to render all messages from the router.
// Refinement for deduplication can be done later if needed.
} else if (msg.from === 'Nucleo') {
owner = 'bot';
}
// else, it could be from another participant
this.add({
id: msg.id,
type: msg.type || 'chat', // Default to 'chat' if type is not present
owner: owner,
text: msg.text,
ts: msg.ts,
// mediaUrl: msg.mediaUrl, // Uncomment if mediaUrl is expected
});
});
} else {
console.error('Invalid conversation response format:', conversation);
this.add({
id: `error-${Date.now()}`,
type: 'error',
owner: 'system',
text: 'Error processing server response. Invalid format.',
ts: Math.floor(Date.now() / 1000)
});
}
} catch (error) {
console.error('Error sending message:', error);
this.add({
id: `error-send-${Date.now()}`,
type: 'error',
owner: 'system',
text: `Error sending message: ${error.message}`,
ts: Math.floor(Date.now() / 1000)
});
}
}
},
})