Skip to content

Commit

Permalink
feat(prompts): Render prompt messages on prompt detail view (#5786)
Browse files Browse the repository at this point in the history
* feat(prompts): Render prompt messages on prompt detail view

* Remove unused import

* Implement copy and paste button on prompt chat messages

* Render chat messages on prompt version details page

* Redirect to latest version when clicking versions tab

Additionally highlight active version in versions list

* Update example data to match template language

* Fix prompt details page width, prompt versions borders / scroll

* Distinguish readOnly TemplateEditor styling from normal TemplateEditor

* Refactor active prompt id into prop

* Replace custom button with styled anchor

* Rename node in promptVersion query
  • Loading branch information
cephalization authored and mikeldking committed Dec 28, 2024
1 parent 8078906 commit 0f9a33e
Show file tree
Hide file tree
Showing 20 changed files with 592 additions and 79 deletions.
7 changes: 6 additions & 1 deletion app/src/Routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { PromptLayout } from "./pages/prompt/PromptLayout";
import { PromptPlaygroundPage } from "./pages/prompt/PromptPlaygroundPage";
import { PromptVersionDetailsPage } from "./pages/prompt/PromptVersionDetailsPage";
import { promptVersionLoader } from "./pages/prompt/promptVersionLoader";
import { promptVersionsLoader } from "./pages/prompt/promptVersionsLoader";
import { PromptVersionsPage } from "./pages/prompt/PromptVersionsPage";
import { sessionLoader } from "./pages/trace/sessionLoader";
import { SessionPage } from "./pages/trace/SessionPage";
Expand Down Expand Up @@ -225,7 +226,11 @@ const router = createBrowserRouter(
>
<Route element={<PromptLayout />}>
<Route index element={<PromptIndexPage />} />
<Route path="versions" element={<PromptVersionsPage />}>
<Route
path="versions"
loader={promptVersionsLoader}
element={<PromptVersionsPage />}
>
<Route
path=":versionId"
loader={promptVersionLoader}
Expand Down
40 changes: 40 additions & 0 deletions app/src/components/templateEditor/TemplateEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import CodeMirror, {
EditorView,
ReactCodeMirrorProps,
} from "@uiw/react-codemirror";
import { css } from "@emotion/react";

import { useTheme } from "@phoenix/contexts";
import { assertUnreachable } from "@phoenix/typeUtils";
Expand Down Expand Up @@ -59,3 +60,42 @@ export const TemplateEditor = ({
/>
);
};

export const TemplateEditorWrap = ({
readOnly,
children,
}: {
readOnly?: boolean;
children: React.ReactNode;
}) => {
return (
<div
css={css`
& .cm-editor,
& .cm-gutters {
background-color: ${!readOnly ? "auto" : "transparent !important"};
}
& .cm-gutters {
border-right: none !important;
}
& .cm-content {
padding: var(--ac-global-dimension-size-100)
var(--ac-global-dimension-size-250);
}
& .cm-gutter,
& .cm-content {
min-height: ${!readOnly ? "75px" : "100%"};
}
& .cm-line {
padding-left: 0;
padding-right: 0;
}
& .cm-cursor {
display: ${!readOnly ? "auto" : "none !important"};
}
`}
>
{children}
</div>
);
};
24 changes: 6 additions & 18 deletions app/src/pages/playground/PlaygroundChatTemplate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ import {
} from "@phoenix/components";
import { CodeWrap, JSONEditor } from "@phoenix/components/code";
import { DragHandle } from "@phoenix/components/dnd/DragHandle";
import { TemplateEditor } from "@phoenix/components/templateEditor";
import {
TemplateEditor,
TemplateEditorWrap,
} from "@phoenix/components/templateEditor";
import { TemplateLanguage } from "@phoenix/components/templateEditor/types";
import { usePlaygroundContext } from "@phoenix/contexts/PlaygroundContext";
import { useChatMessageStyles } from "@phoenix/hooks/useChatMessageStyles";
Expand Down Expand Up @@ -259,22 +262,7 @@ function MessageEditor({
);
}
return (
<div
css={css`
& .cm-content {
padding: var(--ac-global-dimension-size-100)
var(--ac-global-dimension-size-250);
}
& .cm-gutter,
& .cm-content {
min-height: 75px;
}
& .cm-line {
padding-left: 0;
padding-right: 0;
}
`}
>
<TemplateEditorWrap>
<TemplateEditor
height="100%"
value={
Expand All @@ -293,7 +281,7 @@ function MessageEditor({
: "What is the weather in San Francisco?"
}
/>
</div>
</TemplateEditorWrap>
);
}

Expand Down
29 changes: 19 additions & 10 deletions app/src/pages/prompt/ChatTemplateMessage.tsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,44 @@
import React from "react";

import { Button, Card, Icon, Icons } from "@arizeai/components";
import { Card } from "@arizeai/components";

import { CopyToClipboardButton } from "@phoenix/components";
import {
TemplateEditor,
TemplateEditorWrap,
} from "@phoenix/components/templateEditor";
import { TemplateLanguage } from "@phoenix/components/templateEditor/types";
import { useChatMessageStyles } from "@phoenix/hooks/useChatMessageStyles";

export type ChatTemplateMessageProps = {
role: string;
content: string;
templateFormat: TemplateLanguage;
};

/**
* A Read-Only CodeMirror component for the chat template message
* E.x. a system or user message template part
*/
export function ChatTemplateMessage(props: ChatTemplateMessageProps) {
const { role, content } = props;
const { role, content, templateFormat } = props;
const styles = useChatMessageStyles(role);
return (
<Card
title={role}
variant="compact"
{...styles}
extra={
<Button
variant="default"
size="compact"
icon={<Icon svg={<Icons.ClipboardCopy />} />}
/>
}
bodyStyle={{ padding: 0 }}
extra={<CopyToClipboardButton text={content} />}
>
{content}
<TemplateEditorWrap readOnly>
<TemplateEditor
readOnly
height="100%"
value={content}
templateLanguage={templateFormat}
/>
</TemplateEditorWrap>
</Card>
);
}
81 changes: 81 additions & 0 deletions app/src/pages/prompt/PromptChatMessages.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import React, { useMemo } from "react";
import { useFragment } from "react-relay";
import { graphql } from "relay-runtime";

import { Flex, Text } from "@arizeai/components";

import { TemplateLanguages } from "@phoenix/components/templateEditor/constants";
import { TemplateLanguage } from "@phoenix/components/templateEditor/types";

import {
PromptChatMessages__main$key,
PromptTemplateFormat,
} from "./__generated__/PromptChatMessages__main.graphql";
import { ChatTemplateMessage } from "./ChatTemplateMessage";
import { PromptChatTemplate, PromptChatTemplateSchema } from "./schemas";

const convertTemplateFormat = (
templateFormat: PromptTemplateFormat
): TemplateLanguage => {
if (templateFormat === "FSTRING") {
return TemplateLanguages.FString;
} else if (templateFormat === "MUSTACHE") {
return TemplateLanguages.Mustache;
}
return TemplateLanguages.NONE;
};

export function PromptChatMessages({
promptVersion,
}: {
promptVersion: PromptChatMessages__main$key;
}) {
const { template, templateType, templateFormat } = useFragment(
graphql`
fragment PromptChatMessages__main on PromptVersion {
template
templateType
templateFormat
}
`,
promptVersion
);

if (templateType === "STRING") {
return <Text>{template}</Text>;
}

return (
<ChatMessages
template={template}
templateFormat={convertTemplateFormat(templateFormat)}
/>
);
}

function ChatMessages({
template,
templateFormat,
}: {
template: PromptChatTemplate | unknown;
templateFormat: TemplateLanguage;
}) {
const messages = useMemo(() => {
const parsedTemplate = PromptChatTemplateSchema.safeParse(template);
if (!parsedTemplate.success) {
return [];
}
return parsedTemplate.data.messages;
}, [template]);
return (
<Flex direction="column" gap="size-200">
{messages.map((message, i) => (
<ChatTemplateMessage
key={i}
{...message}
templateFormat={templateFormat}
/>
))}
</Flex>
);
}
19 changes: 11 additions & 8 deletions app/src/pages/prompt/PromptIndexPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {

import { PromptIndexPage__aside$key } from "./__generated__/PromptIndexPage__aside.graphql";
import { PromptIndexPage__main$key } from "./__generated__/PromptIndexPage__main.graphql";
import { ChatTemplateMessage } from "./ChatTemplateMessage";
import { PromptChatMessages } from "./PromptChatMessages";
import { PromptInvocationParameters } from "./PromptInvocationParameters";
import { usePromptIdLoader } from "./usePromptIdLoader";

Expand All @@ -40,6 +40,7 @@ export function PromptIndexPageContent({
edges {
node {
...PromptInvocationParameters__main
...PromptChatMessages__main
}
}
}
Expand All @@ -53,20 +54,22 @@ export function PromptIndexPageContent({

return (
<Flex direction="row" height="100%">
<View height="100%" overflow="auto" data-testid="scroll-container">
<View
height="100%"
overflow="auto"
width="100%"
data-testid="scroll-container"
>
<View padding="size-200">
<Flex
direction="column"
gap="size-200"
maxWidth={900}
marginStart="auto"
marginEnd="auto"
maxWidth={900}
>
<Card title="Prompt Template" variant="compact">
<Flex direction="column" gap="size-100">
<ChatTemplateMessage role="system" content="System message" />
<ChatTemplateMessage role="user" content="User message" />
</Flex>
<PromptChatMessages promptVersion={latestVersion} />
</Card>
<Card
title="Model Configuration"
Expand Down Expand Up @@ -125,7 +128,7 @@ function PromptIndexPageAside({
return (
<View
flex="none"
width="400px"
width={400}
borderStartColor="dark"
borderStartWidth="thin"
>
Expand Down
4 changes: 2 additions & 2 deletions app/src/pages/prompt/PromptInvocationParameters.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ export function PromptInvocationParameters({

return (
<List listSize="small">
{parameters.map(({ key, value }) => (
<ListItem key="key">
{parameters.map(({ key, value }, i) => (
<ListItem key={`${key}-${i}`}>
<PromptInvocationParameterItem keyName={key} value={value} />
</ListItem>
))}
Expand Down
4 changes: 4 additions & 0 deletions app/src/pages/prompt/PromptVersionDetailsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
} from "@arizeai/components";

import { promptVersionLoaderQuery$data } from "./__generated__/promptVersionLoaderQuery.graphql";
import { PromptChatMessages } from "./PromptChatMessages";
import { PromptInvocationParameters } from "./PromptInvocationParameters";

export function PromptVersionDetailsPage() {
Expand All @@ -31,6 +32,9 @@ function PromptVersionDetailsPageContent({
marginStart="auto"
marginEnd="auto"
>
<Card title="Prompt">
<PromptChatMessages promptVersion={promptVersion} />
</Card>
<Card
title="Model Configuration"
variant="compact"
Expand Down
Loading

0 comments on commit 0f9a33e

Please sign in to comment.