Skip to content

Commit

Permalink
Allow editing of prior messages in the chat playground (#516)
Browse files Browse the repository at this point in the history
  • Loading branch information
jacoblee93 authored Mar 11, 2024
1 parent a9b5b59 commit 51969be
Show file tree
Hide file tree
Showing 8 changed files with 121 additions and 46 deletions.

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion langserve/chat_playground/dist/assets/index-a69b1f28.css

This file was deleted.

1 change: 1 addition & 0 deletions langserve/chat_playground/dist/assets/index-b47ed17e.css

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions langserve/chat_playground/dist/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
<link rel="icon" href="/____LANGSERVE_BASE_URL/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Chat Playground</title>
<script type="module" crossorigin src="/____LANGSERVE_BASE_URL/assets/index-5a8f5717.js"></script>
<link rel="stylesheet" href="/____LANGSERVE_BASE_URL/assets/index-a69b1f28.css">
<script type="module" crossorigin src="/____LANGSERVE_BASE_URL/assets/index-34f4a840.js"></script>
<link rel="stylesheet" href="/____LANGSERVE_BASE_URL/assets/index-b47ed17e.css">
</head>
<body>
<div id="root"></div>
Expand Down
1 change: 1 addition & 0 deletions langserve/chat_playground/src/assets/RefreshCW.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@ export function AutosizeTextarea(props: {
readOnly?: boolean;
cursorPointer?: boolean;
disabled?: boolean;
fullHeight?: boolean;
}) {
return (
<div className={cn("grid w-full", props.className) + " max-h-80 overflow-auto"}>
<div className={cn("grid w-full", props.className) + (props.fullHeight ? "" : " max-h-80 overflow-auto")}>
<textarea
ref={props.inputRef}
id={props.id}
Expand Down
55 changes: 47 additions & 8 deletions langserve/chat_playground/src/components/ChatMessage.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,32 @@
import { useState } from "react";
import { CorrectnessFeedback } from "./feedback/CorrectnessFeedback";
import { resolveApiUrl } from "../utils/url";
import { AutosizeTextarea } from "./AutosizeTextarea";
import TrashIcon from "../assets/TrashIcon.svg?react";
import RefreshCW from "../assets/RefreshCW.svg?react";

export type ChatMessageType = {
role: "human" | "ai" | "function" | "tool" | "system";
export type ChatMessageType = "human" | "ai" | "function" | "tool" | "system";

export type ChatMessageBody = {
type: ChatMessageType;
content: string;
runId?: string;
}

export function ChatMessage(props: {
message: ChatMessageType;
message: ChatMessageBody;
isLoading?: boolean;
onError?: (e: any) => void;
onTypeChange?: (newValue: string) => void;
onChange?: (newValue: string) => void;
onRemove?: (e: any) => void;
onRegenerate?: (e?: any) => void;
isFinalMessage?: boolean;
feedbackEnabled?: boolean;
publicTraceLinksEnabled?: boolean;
}) {
const { message, feedbackEnabled, publicTraceLinksEnabled, onError, isLoading } = props;
const { content, role, runId } = message;
const { content, type, runId } = message;

const [publicTraceLink, setPublicTraceLink] = useState<string | null>(null);
const [messageActionIsLoading, setMessageActionIsLoading] = useState(false);
Expand Down Expand Up @@ -57,10 +67,39 @@ export function ChatMessage(props: {
};

return (
<div className="mb-8">
<p className="font-medium text-transform uppercase mb-2">{role}</p>
<p className="whitespace-pre-wrap">{content}</p>
{role === "ai" && !isLoading && runId != null && (
<div className="mb-8 group">
<div className="flex justify-between">
<select
className="font-medium text-transform uppercase mb-2 appearance-none"
defaultValue={type}
onChange={(e) => props.onTypeChange?.(e.target.value)}
>
<option value="human">HUMAN</option>
<option value="ai">AI</option>
<option value="system">SYSTEM</option>
</select>
<span className="flex">
{props.isFinalMessage &&
type === "human" &&
<RefreshCW className="opacity-0 group-hover:opacity-50 transition-opacity duration-200 cursor-pointer h-4 w-4 mr-2" onMouseUp={props.onRegenerate}></RefreshCW>}
<TrashIcon
className="opacity-0 group-hover:opacity-50 transition-opacity duration-200 cursor-pointer h-4 w-4"
onMouseUp={props.onRemove}
></TrashIcon>
</span>
</div>
<AutosizeTextarea value={content} fullHeight={true} onChange={props.onChange} onKeyDown={(e) => {
if (
e.key === 'Enter' &&
!e.shiftKey &&
props.isFinalMessage &&
type === "human"
) {
e.preventDefault();
props.onRegenerate?.();
}
}}></AutosizeTextarea>
{type === "ai" && !isLoading && runId != null && (
<div className="mt-2 flex items-center">
{feedbackEnabled && <span className="mr-2"><CorrectnessFeedback runId={runId} onError={props.onError}></CorrectnessFeedback></span>}
{publicTraceLinksEnabled && <>
Expand Down
45 changes: 39 additions & 6 deletions langserve/chat_playground/src/components/ChatWindow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { AutosizeTextarea } from "./AutosizeTextarea";
import {
ChatMessage,
type ChatMessageType,
type ChatMessageBody,
} from "./ChatMessage";
import { ShareDialog } from "./ShareDialog";
import { useStreamCallback } from "../useStreamCallback";
Expand Down Expand Up @@ -38,7 +39,7 @@ export function ChatWindow(props: {

const [currentInputValue, setCurrentInputValue] = useState("");
const [isLoading, setIsLoading] = useState(false);
const [messages, setMessages] = useState<ChatMessageType[]>([]);
const [messages, setMessages] = useState<ChatMessageBody[]>([]);
const messageInputRef = useRef<HTMLTextAreaElement>(null);

const feedbackEnabled = useFeedback()
Expand All @@ -52,31 +53,40 @@ export function ChatWindow(props: {
setIsLoading(true);
const newMessages = [
...messages,
{ role: "human", content: submittedValue } as const
{ type: "human", content: submittedValue } as const
];
setMessages(newMessages);
setCurrentInputValue("");
// TODO: Add config schema support
startStream({ [inputKey]: newMessages }, {});
};

const regenerateMessages = () => {
if (isLoading) {
return;
}
setIsLoading(true);
// TODO: Add config schema support
startStream({ [inputKey]: messages }, {});
};

useStreamCallback("onStart", () => {
setMessages((prevMessages) => [
...prevMessages,
{ role: "ai", content: "" },
{ type: "ai", content: "" },
]);
});
useStreamCallback("onChunk", (_chunk, aggregatedState) => {
const finalOutput = aggregatedState?.final_output;
if (typeof finalOutput === "string") {
setMessages((prevMessages) => [
...prevMessages.slice(0, -1),
{ role: "ai", content: finalOutput, runId: aggregatedState?.id }
{ type: "ai", content: finalOutput, runId: aggregatedState?.id }
]);
} else if (isAIMessage(finalOutput)) {
setMessages((prevMessages) => [
...prevMessages.slice(0, -1),
{ role: "ai", content: finalOutput.content, runId: aggregatedState?.id }
{ type: "ai", content: finalOutput.content, runId: aggregatedState?.id }
]);
}
});
Expand All @@ -85,7 +95,7 @@ export function ChatWindow(props: {
});
useStreamCallback("onError", (e) => {
setIsLoading(false);
toast(e.message, { hideProgressBar: true });
toast(e.message + "\nCheck your backend logs for errors.", { hideProgressBar: true });
setCurrentInputValue(messages[messages.length - 2]?.content);
setMessages((prevMessages) => [
...prevMessages.slice(0, -2),
Expand Down Expand Up @@ -122,6 +132,29 @@ export function ChatWindow(props: {
onError={(e: any) => toast(e.message, { hideProgressBar: true })}
feedbackEnabled={feedbackEnabled.data}
publicTraceLinksEnabled={publicTraceLinksEnabled.data}
isFinalMessage={i === messages.length - 1}
onRemove={() => setMessages(
(previousMessages) => [...previousMessages.slice(0, i), ...previousMessages.slice(i + 1)]
)}
onTypeChange={(newValue) => {
setMessages(
(previousMessages) => [
...previousMessages.slice(0, i),
{...message, type: newValue as ChatMessageType},
...previousMessages.slice(i + 1)
]
)
}}
onChange={(newValue) => {
setMessages(
(previousMessages) => [
...previousMessages.slice(0, i),
{...message, content: newValue},
...previousMessages.slice(i + 1)
]
);
}}
onRegenerate={() => regenerateMessages()}
></ChatMessage>
);
}).reverse()}
Expand Down

0 comments on commit 51969be

Please sign in to comment.