Skip to content

Commit

Permalink
Feature/handle custom format (#33)
Browse files Browse the repository at this point in the history
* Add message format handling on the server side

* Add frontend support for dynamic format

* Add dynamic message format support to python sdk

* Fix frontend error
  • Loading branch information
aurelien-brabant authored Oct 10, 2023
1 parent a611a8b commit dceaab5
Show file tree
Hide file tree
Showing 14 changed files with 476 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -37,7 +39,7 @@
{time}
</div>
<div class="text-body-accent dark:text-body-accent-dark text-[11pt] leading-7">
{#if from !== "USER"}
{#if format === "MARKDOWN"}
<MarkdownRenderer source={body} />
{:else}
{body}
Expand Down
15 changes: 11 additions & 4 deletions frontend/src/lib/components/chat/chat.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
});
}
Expand All @@ -128,7 +132,8 @@
id: payload.data.messageId,
text: payload.data.text,
source: 'AGENT',
createdAt: payload.timestamp
createdAt: payload.timestamp,
format: payload.data.format
});
}
Expand Down Expand Up @@ -186,7 +191,9 @@
<ChatMessage
from={message.source}
time={dayjs(message.createdAt).format("M/D/YYYY hh:mm A")}
body={message.text} />
body={message.text}
format={message.format}
/>
</div>
{/each}
</div>
Expand Down
8 changes: 8 additions & 0 deletions frontend/src/lib/stores/chat.ts
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down
25 changes: 21 additions & 4 deletions sdks/node-sdk/src/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
}

/**
Expand All @@ -37,6 +50,7 @@ class StreamedMessage {
attachments: [],
conversationId: this.conversationId,
messageId: this.messageId,
format: this.format
}
})
}
Expand Down Expand Up @@ -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
}
});
}
Expand Down
25 changes: 13 additions & 12 deletions sdks/python-sdk/agentlabs/agent.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from enum import Enum
import os
from typing import Any, Callable, TypedDict
from socketio.pubsub_manager import uuid
Expand All @@ -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
Expand All @@ -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.
Expand All @@ -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
})

"""
Expand Down Expand Up @@ -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
Expand Down
Loading

0 comments on commit dceaab5

Please sign in to comment.