diff --git a/frontend/src/lib/components/chat/chat-message/ChatMessage.svelte b/frontend/src/lib/components/chat/chat-message/ChatMessage.svelte
index 9e45b38..a61aefd 100644
--- a/frontend/src/lib/components/chat/chat-message/ChatMessage.svelte
+++ b/frontend/src/lib/components/chat/chat-message/ChatMessage.svelte
@@ -4,10 +4,12 @@
import MarkdownRenderer from "$lib/components/markdown/markdown-renderer.svelte";
import AgentIcon from "$lib/assets/img/agent-icon.svg";
import { authStore } from "$lib/stores/auth";
+ import type { ChatMessageFormat } from "$lib/stores/chat";
export let time: string;
export let body: string;
export let from: "USER" | "AGENT" | "SYSTEM";
+ export let format: ChatMessageFormat;
let bubbleClass: string;
@@ -37,7 +39,7 @@
{time}
- {#if from !== "USER"}
+ {#if format === "MARKDOWN"}
{:else}
{body}
diff --git a/frontend/src/lib/components/chat/chat.svelte b/frontend/src/lib/components/chat/chat.svelte
index 5b275eb..d2ba74a 100644
--- a/frontend/src/lib/components/chat/chat.svelte
+++ b/frontend/src/lib/components/chat/chat.svelte
@@ -59,7 +59,8 @@
addMessage({
id: uuidv4(), // this is a fake id, the real id will be set by the server
...payload.data,
- createdAt: timestamp
+ createdAt: timestamp,
+ format: 'PLAIN_TEXT'
});
isWaitingForAnswer = true;
@@ -108,12 +109,15 @@
shouldRedirectToConversation = false;
}
+ console.log(payload);
+
isWaitingForAnswer = false;
addMessage({
id: payload.data.messageId,
text: payload.data.text,
source: payload.data.source,
- createdAt: payload.timestamp
+ createdAt: payload.timestamp,
+ format: payload.data.format
});
}
@@ -128,7 +132,8 @@
id: payload.data.messageId,
text: payload.data.text,
source: 'AGENT',
- createdAt: payload.timestamp
+ createdAt: payload.timestamp,
+ format: payload.data.format
});
}
@@ -186,7 +191,9 @@
+ body={message.text}
+ format={message.format}
+ />
{/each}
diff --git a/frontend/src/lib/stores/chat.ts b/frontend/src/lib/stores/chat.ts
index aea165b..ee210ac 100644
--- a/frontend/src/lib/stores/chat.ts
+++ b/frontend/src/lib/stores/chat.ts
@@ -1,10 +1,18 @@
import { writable } from "svelte/store";
+export const ChatMessageFormats = [
+ 'PLAIN_TEXT',
+ 'MARKDOWN',
+] as const;
+
+export type ChatMessageFormat = typeof ChatMessageFormats[number];
+
export interface ChatMessage {
id: string;
text: string;
source: 'USER' | 'AGENT' | 'SYSTEM';
createdAt: string;
+ format: ChatMessageFormat;
}
export interface ChatStore {
diff --git a/sdks/node-sdk/src/agent.ts b/sdks/node-sdk/src/agent.ts
index bf0a249..1c2efdf 100644
--- a/sdks/node-sdk/src/agent.ts
+++ b/sdks/node-sdk/src/agent.ts
@@ -12,13 +12,26 @@ export interface RawChatMessage {
memberId: string;
}
+export interface ReplyMessageOptions {
+ format?: MessageFormat;
+}
+
+const MessageFormats = [
+ 'PLAIN_TEXT',
+ 'MARKDOWN',
+] as const;
+
+export type MessageFormat = typeof MessageFormats[number];
+
export type OnChatMessageHandler = (message: IncomingChatMessage) => void;
class StreamedMessage {
private readonly messageId = randomUUID()
private isEnded = false;
+ private format: MessageFormat;
- constructor(private readonly io: Socket, private readonly conversationId: string) {
+ constructor(private readonly io: Socket, private readonly conversationId: string, options: ReplyMessageOptions) {
+ this.format = options.format ?? 'PLAIN_TEXT';
}
/**
@@ -37,6 +50,7 @@ class StreamedMessage {
attachments: [],
conversationId: this.conversationId,
messageId: this.messageId,
+ format: this.format
}
})
}
@@ -77,21 +91,24 @@ class IncomingChatMessage {
* Such reply is intended to be read by the user in realtime.
* If you are looking to send the entire message at once, use `reply` instead.
*/
- streamedReply() {
- return new StreamedMessage(this.io, this.conversationId);
+ streamedReply(options: ReplyMessageOptions = {}) {
+ return new StreamedMessage(this.io, this.conversationId, options);
}
/**
* Reply to the message, sending the entire message at once.
* If you are looking to stream the reply as multiple parts, use `streamedReply` instead.
*/
- reply(message: string) {
+ reply(message: string, options: ReplyMessageOptions = {}) {
+ const format = options.format ?? 'PLAIN_TEXT';
+
this.io.emit('chat-message', {
timestamp: new Date().toISOString(),
data: {
text: message,
attachments: [],
conversationId: this.conversationId,
+ format
}
});
}
diff --git a/sdks/python-sdk/agentlabs/agent.py b/sdks/python-sdk/agentlabs/agent.py
index 0e38311..9f0e294 100644
--- a/sdks/python-sdk/agentlabs/agent.py
+++ b/sdks/python-sdk/agentlabs/agent.py
@@ -1,3 +1,4 @@
+from enum import Enum
import os
from typing import Any, Callable, TypedDict
from socketio.pubsub_manager import uuid
@@ -6,14 +7,11 @@
from agentlabs.logger import AgentLogger
from .server import emit, agent_namespace
-from .attachment import Attachment
import socketio
-class AgentConfig(TypedDict):
- agentlabs_url: str;
- project_id: str;
- agent_id: str;
- secret: str;
+class MessageFormat(Enum):
+ PLAIN_TEXT = "PLAIN_TEXT"
+ MARKDOWN = "MARKDOWN"
class _DecodedUser(TypedDict):
id: str
@@ -38,10 +36,11 @@ def __init__(self, decoded_user: _DecodedUser):
class StreamedChatMessage:
is_ended: bool = False
- def __init__(self, io: socketio.Client, conversation_id: str):
+ def __init__(self, io: socketio.Client, conversation_id: str, format: MessageFormat):
self.io = io
self.conversation_id = conversation_id
self.message_id = str(uuid.uuid4())
+ self.format = format
"""
Writes a token to the stream. This can be used to send a message in multiple parts.
@@ -55,7 +54,8 @@ def write(self, token: str):
"conversationId": self.conversation_id,
"messageId": self.message_id,
"text": token,
- "attachments": []
+ "attachments": [],
+ "format": self.format.value
})
"""
@@ -85,18 +85,19 @@ def __init__(self, http: HttpApi,io: socketio.Client, message: _ChatMessage):
in multiple parts.
Well suited to stream LLM outputs.
"""
- def streamed_reply(self):
- return StreamedChatMessage(self.io, self.conversation_id)
+ def streamed_reply(self, format: MessageFormat = MessageFormat.PLAIN_TEXT):
+ return StreamedChatMessage(self.io, self.conversation_id, format)
"""
Replies to the message instantly. If you are looking to stream a reply in multiple parts,
use streamed_reply() instead.
"""
- def reply(self, message: str):
+ def reply(self, message: str, format: MessageFormat = MessageFormat.PLAIN_TEXT):
emit(self.io, 'chat-message', {
"conversationId": self.conversation_id,
"text": message,
- "attachments": []
+ "attachments": [],
+ "format": format.value
})
# TODO: we'll come back to this when we officially support attachments
diff --git a/server/prisma/migrations/20231010132616_init/migration.sql b/server/prisma/migrations/20231010132616_init/migration.sql
new file mode 100644
index 0000000..045eff6
--- /dev/null
+++ b/server/prisma/migrations/20231010132616_init/migration.sql
@@ -0,0 +1,378 @@
+-- CreateEnum
+CREATE TYPE "OrganizationUserRole" AS ENUM ('USER', 'ADMIN');
+
+-- CreateEnum
+CREATE TYPE "AuthMethodType" AS ENUM ('OAUTH2', 'EMAIL', 'PHONE_NUMBER', 'ANONYMOUS');
+
+-- CreateEnum
+CREATE TYPE "AuthProvider" AS ENUM ('PASSWORDLESS_EMAIL', 'EMAIL_AND_PASSWORD', 'SMS', 'ANONYMOUS', 'GOOGLE', 'GITHUB', 'GITLAB', 'MICROSOFT');
+
+-- CreateEnum
+CREATE TYPE "AgentMessageSource" AS ENUM ('USER', 'AGENT', 'SYSTEM');
+
+-- CreateEnum
+CREATE TYPE "MessageFormat" AS ENUM ('PLAIN_TEXT', 'MARKDOWN', 'HTML');
+
+-- CreateEnum
+CREATE TYPE "AttachmentStorageDriver" AS ENUM ('AWS_S3', 'AZURE_BLOB_STORAGE', 'GOOGLE_CLOUD_STORAGE', 'LOCAL_FILE_SYSTEM');
+
+-- CreateTable
+CREATE TABLE "PasswordHashConfig" (
+ "id" TEXT NOT NULL,
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "userId" TEXT NOT NULL,
+ "algorithm" TEXT NOT NULL,
+ "memCost" INTEGER NOT NULL,
+ "keyLength" INTEGER NOT NULL,
+ "salt" TEXT NOT NULL,
+
+ CONSTRAINT "PasswordHashConfig_pkey" PRIMARY KEY ("id")
+);
+
+-- CreateTable
+CREATE TABLE "Onboarding" (
+ "id" TEXT NOT NULL,
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "hasAddedAuthMethod" BOOLEAN NOT NULL DEFAULT false,
+ "hasUsedTheApplication" BOOLEAN NOT NULL DEFAULT false,
+ "organizationId" TEXT NOT NULL,
+ "projectId" TEXT,
+ "userId" TEXT NOT NULL,
+
+ CONSTRAINT "Onboarding_pkey" PRIMARY KEY ("id")
+);
+
+-- CreateTable
+CREATE TABLE "User" (
+ "id" TEXT NOT NULL,
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "email" TEXT NOT NULL,
+ "fullName" TEXT NOT NULL,
+ "profilePictureUrl" TEXT,
+ "verifiedAt" TIMESTAMP(3),
+ "bannedAt" TIMESTAMP(3),
+ "passwordHash" TEXT,
+
+ CONSTRAINT "User_pkey" PRIMARY KEY ("id")
+);
+
+-- CreateTable
+CREATE TABLE "Organization" (
+ "id" TEXT NOT NULL,
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "name" TEXT NOT NULL,
+
+ CONSTRAINT "Organization_pkey" PRIMARY KEY ("id")
+);
+
+-- CreateTable
+CREATE TABLE "OrganizationUser" (
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "organizationId" TEXT NOT NULL,
+ "userId" TEXT NOT NULL,
+ "role" "OrganizationUserRole" NOT NULL DEFAULT 'ADMIN',
+
+ CONSTRAINT "OrganizationUser_pkey" PRIMARY KEY ("organizationId","userId")
+);
+
+-- CreateTable
+CREATE TABLE "Project" (
+ "id" TEXT NOT NULL,
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "name" TEXT NOT NULL,
+ "slug" TEXT NOT NULL,
+ "creatorId" TEXT NOT NULL,
+ "organizationId" TEXT NOT NULL,
+
+ CONSTRAINT "Project_pkey" PRIMARY KEY ("id")
+);
+
+-- CreateTable
+CREATE TABLE "SmtpConfiguration" (
+ "id" TEXT NOT NULL,
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "host" TEXT NOT NULL,
+ "port" INTEGER NOT NULL,
+ "secure" BOOLEAN NOT NULL,
+ "username" TEXT NOT NULL,
+ "password" TEXT NOT NULL,
+ "senderName" TEXT NOT NULL,
+ "senderEmail" TEXT NOT NULL,
+ "replyTo" TEXT NOT NULL,
+ "projectId" TEXT NOT NULL,
+
+ CONSTRAINT "SmtpConfiguration_pkey" PRIMARY KEY ("id")
+);
+
+-- CreateTable
+CREATE TABLE "AuthMethod" (
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "provider" "AuthProvider" NOT NULL,
+ "type" "AuthMethodType" NOT NULL,
+ "isEnabled" BOOLEAN NOT NULL DEFAULT false,
+ "clientId" TEXT,
+ "clientSecret" TEXT,
+ "clientSecretIv" TEXT,
+ "scopes" TEXT[],
+ "projectId" TEXT NOT NULL,
+
+ CONSTRAINT "AuthMethod_pkey" PRIMARY KEY ("projectId","provider")
+);
+
+-- CreateTable
+CREATE TABLE "SdkSecret" (
+ "id" TEXT NOT NULL,
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "revokedAt" TIMESTAMP(3),
+ "hash" TEXT NOT NULL,
+ "preview" TEXT NOT NULL,
+ "description" TEXT,
+ "projectId" TEXT NOT NULL,
+ "creatorId" TEXT NOT NULL,
+
+ CONSTRAINT "SdkSecret_pkey" PRIMARY KEY ("id")
+);
+
+-- CreateTable
+CREATE TABLE "Agent" (
+ "id" TEXT NOT NULL,
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "deletedAt" TIMESTAMP(3),
+ "name" TEXT NOT NULL,
+ "creatorId" TEXT NOT NULL,
+ "projectId" TEXT NOT NULL,
+ "logoUrl" TEXT,
+
+ CONSTRAINT "Agent_pkey" PRIMARY KEY ("id")
+);
+
+-- CreateTable
+CREATE TABLE "MemberAuthVerificationCode" (
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "code" TEXT NOT NULL,
+ "expiresAt" TIMESTAMP(3) NOT NULL,
+ "projectId" TEXT NOT NULL,
+ "memberId" TEXT NOT NULL,
+
+ CONSTRAINT "MemberAuthVerificationCode_pkey" PRIMARY KEY ("projectId","memberId")
+);
+
+-- CreateTable
+CREATE TABLE "MemberAuth" (
+ "id" TEXT NOT NULL,
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "passwordHash" TEXT,
+ "memberId" TEXT NOT NULL,
+ "algorithm" TEXT NOT NULL,
+ "memCost" INTEGER NOT NULL,
+ "keyLength" INTEGER NOT NULL,
+ "salt" TEXT NOT NULL,
+
+ CONSTRAINT "MemberAuth_pkey" PRIMARY KEY ("id")
+);
+
+-- CreateTable
+CREATE TABLE "Member" (
+ "id" TEXT NOT NULL,
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "verifiedAt" TIMESTAMP(3),
+ "bannedAt" TIMESTAMP(3),
+ "firstName" TEXT,
+ "lastName" TEXT,
+ "fullName" TEXT,
+ "email" TEXT NOT NULL,
+ "profilePictureUrl" TEXT,
+ "projectId" TEXT NOT NULL,
+
+ CONSTRAINT "Member_pkey" PRIMARY KEY ("id")
+);
+
+-- CreateTable
+CREATE TABLE "MemberIdentity" (
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "provider" "AuthProvider" NOT NULL,
+ "providerUserId" TEXT NOT NULL,
+ "memberId" TEXT NOT NULL,
+ "lastSignedInAt" TIMESTAMP(3),
+ "accessToken" TEXT,
+ "refreshToken" TEXT,
+ "accessTokenExpiresAt" TIMESTAMP(3),
+ "refreshTokenExpiresAt" TIMESTAMP(3),
+
+ CONSTRAINT "MemberIdentity_pkey" PRIMARY KEY ("memberId","provider")
+);
+
+-- CreateTable
+CREATE TABLE "AgentConversation" (
+ "id" TEXT NOT NULL,
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "agentId" TEXT NOT NULL,
+ "memberId" TEXT NOT NULL,
+
+ CONSTRAINT "AgentConversation_pkey" PRIMARY KEY ("id")
+);
+
+-- CreateTable
+CREATE TABLE "AgentMessage" (
+ "id" TEXT NOT NULL,
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "updateAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "source" "AgentMessageSource" NOT NULL,
+ "text" TEXT NOT NULL,
+ "conversationId" TEXT NOT NULL,
+ "format" "MessageFormat" NOT NULL DEFAULT 'PLAIN_TEXT',
+
+ CONSTRAINT "AgentMessage_pkey" PRIMARY KEY ("id")
+);
+
+-- CreateTable
+CREATE TABLE "AgentMessageAttachment" (
+ "id" TEXT NOT NULL,
+ "attachmentId" TEXT NOT NULL,
+ "messageId" TEXT NOT NULL,
+
+ CONSTRAINT "AgentMessageAttachment_pkey" PRIMARY KEY ("id")
+);
+
+-- CreateTable
+CREATE TABLE "Attachment" (
+ "id" TEXT NOT NULL,
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "updateAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "name" TEXT NOT NULL,
+ "mimeType" TEXT NOT NULL,
+ "checksumSha256" TEXT NOT NULL,
+ "driver" "AttachmentStorageDriver" NOT NULL,
+ "isPublic" BOOLEAN NOT NULL DEFAULT false,
+
+ CONSTRAINT "Attachment_pkey" PRIMARY KEY ("id")
+);
+
+-- CreateTable
+CREATE TABLE "AgentConnectionEvent" (
+ "id" TEXT NOT NULL,
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "agentId" TEXT NOT NULL,
+
+ CONSTRAINT "AgentConnectionEvent_pkey" PRIMARY KEY ("id")
+);
+
+-- CreateIndex
+CREATE UNIQUE INDEX "PasswordHashConfig_userId_key" ON "PasswordHashConfig"("userId");
+
+-- CreateIndex
+CREATE UNIQUE INDEX "Onboarding_organizationId_key" ON "Onboarding"("organizationId");
+
+-- CreateIndex
+CREATE UNIQUE INDEX "Onboarding_userId_key" ON "Onboarding"("userId");
+
+-- CreateIndex
+CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
+
+-- CreateIndex
+CREATE UNIQUE INDEX "Project_slug_key" ON "Project"("slug");
+
+-- CreateIndex
+CREATE UNIQUE INDEX "SmtpConfiguration_projectId_key" ON "SmtpConfiguration"("projectId");
+
+-- CreateIndex
+CREATE UNIQUE INDEX "SdkSecret_hash_key" ON "SdkSecret"("hash");
+
+-- CreateIndex
+CREATE UNIQUE INDEX "MemberAuthVerificationCode_memberId_key" ON "MemberAuthVerificationCode"("memberId");
+
+-- CreateIndex
+CREATE UNIQUE INDEX "MemberAuth_memberId_key" ON "MemberAuth"("memberId");
+
+-- CreateIndex
+CREATE UNIQUE INDEX "AgentMessageAttachment_attachmentId_key" ON "AgentMessageAttachment"("attachmentId");
+
+-- AddForeignKey
+ALTER TABLE "PasswordHashConfig" ADD CONSTRAINT "PasswordHashConfig_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "Onboarding" ADD CONSTRAINT "Onboarding_organizationId_fkey" FOREIGN KEY ("organizationId") REFERENCES "Organization"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "Onboarding" ADD CONSTRAINT "Onboarding_projectId_fkey" FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE SET NULL ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "Onboarding" ADD CONSTRAINT "Onboarding_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "OrganizationUser" ADD CONSTRAINT "OrganizationUser_organizationId_fkey" FOREIGN KEY ("organizationId") REFERENCES "Organization"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "OrganizationUser" ADD CONSTRAINT "OrganizationUser_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "Project" ADD CONSTRAINT "Project_creatorId_fkey" FOREIGN KEY ("creatorId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "Project" ADD CONSTRAINT "Project_organizationId_fkey" FOREIGN KEY ("organizationId") REFERENCES "Organization"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "SmtpConfiguration" ADD CONSTRAINT "SmtpConfiguration_projectId_fkey" FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "AuthMethod" ADD CONSTRAINT "AuthMethod_projectId_fkey" FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "SdkSecret" ADD CONSTRAINT "SdkSecret_projectId_fkey" FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "SdkSecret" ADD CONSTRAINT "SdkSecret_creatorId_fkey" FOREIGN KEY ("creatorId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "Agent" ADD CONSTRAINT "Agent_creatorId_fkey" FOREIGN KEY ("creatorId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "Agent" ADD CONSTRAINT "Agent_projectId_fkey" FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "MemberAuthVerificationCode" ADD CONSTRAINT "MemberAuthVerificationCode_projectId_fkey" FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "MemberAuthVerificationCode" ADD CONSTRAINT "MemberAuthVerificationCode_memberId_fkey" FOREIGN KEY ("memberId") REFERENCES "Member"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "MemberAuth" ADD CONSTRAINT "MemberAuth_memberId_fkey" FOREIGN KEY ("memberId") REFERENCES "Member"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "Member" ADD CONSTRAINT "Member_projectId_fkey" FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "MemberIdentity" ADD CONSTRAINT "MemberIdentity_memberId_fkey" FOREIGN KEY ("memberId") REFERENCES "Member"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "AgentConversation" ADD CONSTRAINT "AgentConversation_agentId_fkey" FOREIGN KEY ("agentId") REFERENCES "Agent"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "AgentConversation" ADD CONSTRAINT "AgentConversation_memberId_fkey" FOREIGN KEY ("memberId") REFERENCES "Member"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "AgentMessage" ADD CONSTRAINT "AgentMessage_conversationId_fkey" FOREIGN KEY ("conversationId") REFERENCES "AgentConversation"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "AgentMessageAttachment" ADD CONSTRAINT "AgentMessageAttachment_attachmentId_fkey" FOREIGN KEY ("attachmentId") REFERENCES "Attachment"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "AgentMessageAttachment" ADD CONSTRAINT "AgentMessageAttachment_messageId_fkey" FOREIGN KEY ("messageId") REFERENCES "AgentMessage"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "AgentConnectionEvent" ADD CONSTRAINT "AgentConnectionEvent_agentId_fkey" FOREIGN KEY ("agentId") REFERENCES "Agent"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
diff --git a/server/prisma/schema.prisma b/server/prisma/schema.prisma
index 5b3c651..9bbb61d 100644
--- a/server/prisma/schema.prisma
+++ b/server/prisma/schema.prisma
@@ -325,6 +325,12 @@ enum AgentMessageSource {
SYSTEM // reserved for future use
}
+enum MessageFormat {
+ PLAIN_TEXT
+ MARKDOWN
+ HTML // reserved for future use
+}
+
model AgentMessage {
id String @id @default(uuid())
createdAt DateTime @default(now())
@@ -337,6 +343,7 @@ model AgentMessage {
conversation AgentConversation @relation(fields: [conversationId], references: [id])
attachments AgentMessageAttachment[]
+ format MessageFormat @default(PLAIN_TEXT)
}
model AgentMessageAttachment {
diff --git a/server/src/agent-chat/agent-chat-messages/agent-chat-messages.service.ts b/server/src/agent-chat/agent-chat-messages/agent-chat-messages.service.ts
index 59e5ba9..b6d6684 100644
--- a/server/src/agent-chat/agent-chat-messages/agent-chat-messages.service.ts
+++ b/server/src/agent-chat/agent-chat-messages/agent-chat-messages.service.ts
@@ -28,6 +28,7 @@ export class AgentChatMessagesService {
text: payload.text,
source: payload.source,
conversationId: payload.conversationId,
+ format: payload.format,
},
});
diff --git a/server/src/agent-chat/agent-chat-messages/agent-chat-messages.types.ts b/server/src/agent-chat/agent-chat-messages/agent-chat-messages.types.ts
index 09303b5..9d3fdc2 100644
--- a/server/src/agent-chat/agent-chat-messages/agent-chat-messages.types.ts
+++ b/server/src/agent-chat/agent-chat-messages/agent-chat-messages.types.ts
@@ -1,7 +1,8 @@
-import { AgentMessageSource } from 'prisma/prisma-client';
+import { AgentMessageSource, MessageFormat } from 'prisma/prisma-client';
export interface CreateAgentChatMessagePayload {
text: string;
source: AgentMessageSource;
conversationId: string;
+ format: MessageFormat;
}
diff --git a/server/src/agent-connection/agent-connection.gateway.ts b/server/src/agent-connection/agent-connection.gateway.ts
index 7b7070d..66cee8b 100644
--- a/server/src/agent-connection/agent-connection.gateway.ts
+++ b/server/src/agent-connection/agent-connection.gateway.ts
@@ -172,10 +172,11 @@ export class AgentConnectionGateway
try {
await this.conversationMutexManager.acquire(conversation.id);
- await this.messagesService.createMessage({
+ const message = await this.messagesService.createMessage({
conversationId: conversation.id,
text: payload.data.text,
source: 'AGENT',
+ format: payload.data.format,
});
const frontendConnection =
@@ -201,7 +202,16 @@ export class AgentConnectionGateway
};
}
- frontendConnection.socket.emit('chat-message', payload);
+ frontendConnection.socket.emit('chat-message', {
+ timestamp: new Date().toISOString(),
+ data: {
+ conversationId: conversation.id,
+ text: payload.data.text,
+ format: payload.data.format,
+ source: 'AGENT',
+ messageId: message.id,
+ },
+ });
return {
message: 'Message sent successfully',
@@ -221,6 +231,7 @@ export class AgentConnectionGateway
messageId: payload.data.messageId,
conversationId: payload.data.conversationId,
token: payload.data.text,
+ format: payload.data.format,
});
}
diff --git a/server/src/agent-connection/agent-stream-manager/agent-stream-manager.service.ts b/server/src/agent-connection/agent-stream-manager/agent-stream-manager.service.ts
index b7fb591..679703d 100644
--- a/server/src/agent-connection/agent-stream-manager/agent-stream-manager.service.ts
+++ b/server/src/agent-connection/agent-stream-manager/agent-stream-manager.service.ts
@@ -1,4 +1,5 @@
import { Injectable } from '@nestjs/common';
+import { MessageFormat } from '@prisma/client';
import { Socket } from 'socket.io';
import { seconds } from 'src/common/ms-time';
import { FrontendConnectionManagerService } from 'src/frontend-connection-manager/frontend-connection-manager.service';
@@ -11,12 +12,14 @@ export interface StreamData {
conversationId: string;
messageId: string;
createdAtTs: number;
+ format: MessageFormat;
}
interface HandlePayload {
messageId: string;
conversationId: string;
token: string;
+ format: MessageFormat;
}
@Injectable()
@@ -86,6 +89,7 @@ export class AgentStreamManagerService {
text: stream.buffer,
source: 'AGENT',
conversationId: stream.conversationId,
+ format: stream.format,
},
});
} else {
@@ -143,6 +147,7 @@ export class AgentStreamManagerService {
buffer: '',
messageId: data.messageId,
createdAtTs: Date.now(),
+ format: data.format,
};
this.activeStreams.set(data.messageId, stream);
@@ -154,6 +159,7 @@ export class AgentStreamManagerService {
text: data.token,
conversationId: stream.conversationId,
messageId: stream.messageId,
+ format: stream.format,
},
});
} finally {
diff --git a/server/src/agent-connection/dto/agent-message.dto.ts b/server/src/agent-connection/dto/agent-message.dto.ts
index 82e4037..ea67b51 100644
--- a/server/src/agent-connection/dto/agent-message.dto.ts
+++ b/server/src/agent-connection/dto/agent-message.dto.ts
@@ -1,5 +1,6 @@
+import { MessageFormat } from '@prisma/client';
import { Type } from 'class-transformer';
-import { IsString, ValidateNested } from 'class-validator';
+import { IsEnum, IsString, ValidateNested } from 'class-validator';
import { BaseRealtimeMessageDto } from 'src/common/base-realtime-message.dto';
class AgentMessageDataDto {
@@ -11,6 +12,9 @@ class AgentMessageDataDto {
@IsString()
messageId: string;
+
+ @IsEnum(MessageFormat)
+ format: MessageFormat;
}
export class AgentMessageDto extends BaseRealtimeMessageDto {
diff --git a/server/src/agent-connection/dto/stream-chat-message-token.dto.ts b/server/src/agent-connection/dto/stream-chat-message-token.dto.ts
index 3f20be8..3e82dad 100644
--- a/server/src/agent-connection/dto/stream-chat-message-token.dto.ts
+++ b/server/src/agent-connection/dto/stream-chat-message-token.dto.ts
@@ -1,5 +1,6 @@
+import { MessageFormat } from '@prisma/client';
import { Type } from 'class-transformer';
-import { IsString, ValidateNested } from 'class-validator';
+import { IsEnum, IsString, ValidateNested } from 'class-validator';
import { BaseRealtimeMessageDto } from 'src/common/base-realtime-message.dto';
class StreamChatMessageTokenDtoData {
@@ -11,6 +12,9 @@ class StreamChatMessageTokenDtoData {
@IsString()
messageId: string;
+
+ @IsEnum(MessageFormat)
+ format: MessageFormat;
}
export class StreamChatMessageTokenDto extends BaseRealtimeMessageDto {
diff --git a/server/src/frontend-connection/frontend-connection.gateway.ts b/server/src/frontend-connection/frontend-connection.gateway.ts
index 91cceff..027e7d1 100644
--- a/server/src/frontend-connection/frontend-connection.gateway.ts
+++ b/server/src/frontend-connection/frontend-connection.gateway.ts
@@ -152,6 +152,7 @@ export class FrontendConnectionGateway
source: 'USER',
text: payload.data.text,
conversationId: conversation.id,
+ format: 'PLAIN_TEXT',
});
const clientPayload: BaseRealtimeMessageDto = {
@@ -189,6 +190,7 @@ export class FrontendConnectionGateway
conversationId: conversation.id,
source: 'SYSTEM',
text,
+ format: 'MARKDOWN',
});
const payload: BaseRealtimeMessageDto = {
@@ -198,6 +200,7 @@ export class FrontendConnectionGateway
conversationId: conversation.id,
source: 'SYSTEM',
messageId: message.id,
+ format: 'MARKDOWN',
},
message: 'Agent is offline',
};