diff --git a/app/src/components/AlphabeticIndexIcon.tsx b/app/src/components/AlphabeticIndexIcon.tsx
new file mode 100644
index 0000000000..a0d944cc41
--- /dev/null
+++ b/app/src/components/AlphabeticIndexIcon.tsx
@@ -0,0 +1,33 @@
+import React, { useMemo } from "react";
+import { schemeSet2 } from "d3-scale-chromatic";
+import { transparentize } from "polished";
+import { css } from "@emotion/react";
+
+function indexToChar(index: number) {
+ // Wrap around using modulo if index exceeds 'C'
+ const charCode = 65 + index; // 'A' has ASCII code 65, 'B' is 66, 'C' is 67
+ return String.fromCharCode(charCode);
+}
+
+export function AlphabeticIndexIcon({ index }: { index: number }) {
+ const char = useMemo(() => indexToChar(index), [index]);
+ const color = useMemo(() => schemeSet2[index % 8], [index]);
+ const backgroundColor = useMemo(() => transparentize(0.8, color), [color]);
+ return (
+
+ {char}
+
+ );
+}
diff --git a/app/src/pages/playground/Playground.tsx b/app/src/pages/playground/Playground.tsx
index bed0f4edd2..14adc0962e 100644
--- a/app/src/pages/playground/Playground.tsx
+++ b/app/src/pages/playground/Playground.tsx
@@ -1,7 +1,7 @@
import React, { Fragment } from "react";
import { Panel, PanelGroup, PanelResizeHandle } from "react-resizable-panels";
-import { Button, Flex, Heading, View } from "@arizeai/components";
+import { Button, Flex, Heading, Icon, Icons, View } from "@arizeai/components";
import { resizeHandleCSS } from "@phoenix/components/resize";
import {
@@ -10,6 +10,7 @@ import {
} from "@phoenix/contexts/PlaygroundContext";
import { InitialPlaygroundState } from "@phoenix/store";
+import { NUM_MAX_PLAYGROUND_INSTANCES } from "./constants";
import { PlaygroundInputTypeTypeRadioGroup } from "./PlaygroundInputModeRadioGroup";
import { PlaygroundInstance } from "./PlaygroundInstance";
import { PlaygroundRunButton } from "./PlaygroundRunButton";
@@ -35,6 +36,7 @@ export function Playground(props: InitialPlaygroundState) {
+
@@ -45,6 +47,25 @@ export function Playground(props: InitialPlaygroundState) {
);
}
+function AddPromptButton() {
+ const addInstance = usePlaygroundContext((state) => state.addInstance);
+ const numInstances = usePlaygroundContext((state) => state.instances.length);
+ return (
+ } />}
+ disabled={numInstances >= NUM_MAX_PLAYGROUND_INSTANCES}
+ onClick={() => {
+ addInstance();
+ }}
+ >
+ Prompt
+
+ );
+}
+
function PlaygroundInstances() {
const instances = usePlaygroundContext((state) => state.instances);
return (
diff --git a/app/src/pages/playground/PlaygroundInstance.tsx b/app/src/pages/playground/PlaygroundInstance.tsx
index 37be6bc84e..8e6bf27de8 100644
--- a/app/src/pages/playground/PlaygroundInstance.tsx
+++ b/app/src/pages/playground/PlaygroundInstance.tsx
@@ -22,7 +22,7 @@ export function PlaygroundInstance(props: PlaygroundInstanceProps) {
-
+
state.instances[instanceId]);
+ const instance = usePlaygroundContext((state) =>
+ state.instances.find((instance) => instance.id === instanceId)
+ );
+ const index = usePlaygroundContext((state) =>
+ state.instances.findIndex((instance) => instance.id === instanceId)
+ );
if (!instance) {
return null;
}
+
const runId = instance.activeRunId;
const hasRunId = runId !== null;
return (
-
+ }
+ collapsible
+ variant="compact"
+ >
{hasRunId ? (
) : (
diff --git a/app/src/pages/playground/PlaygroundTemplate.tsx b/app/src/pages/playground/PlaygroundTemplate.tsx
index 643fca727b..f5448d2ea5 100644
--- a/app/src/pages/playground/PlaygroundTemplate.tsx
+++ b/app/src/pages/playground/PlaygroundTemplate.tsx
@@ -4,6 +4,7 @@ import {
Button,
Card,
Content,
+ Flex,
Icon,
Icons,
Tooltip,
@@ -11,9 +12,9 @@ import {
TriggerWrap,
} from "@arizeai/components";
+import { AlphabeticIndexIcon } from "@phoenix/components/AlphabeticIndexIcon";
import { usePlaygroundContext } from "@phoenix/contexts/PlaygroundContext";
-import { NUM_MAX_PLAYGROUND_INSTANCES } from "./constants";
import { PlaygroundChatTemplate } from "./PlaygroundChatTemplate";
import { PlaygroundInstanceProps } from "./types";
@@ -23,6 +24,7 @@ export function PlaygroundTemplate(props: PlaygroundTemplateProps) {
const instanceId = props.playgroundInstanceId;
const instances = usePlaygroundContext((state) => state.instances);
const instance = instances.find((instance) => instance.id === instanceId);
+ const index = instances.findIndex((instance) => instance.id === instanceId);
if (!instance) {
throw new Error(`Playground instance ${instanceId} not found`);
}
@@ -30,17 +32,16 @@ export function PlaygroundTemplate(props: PlaygroundTemplateProps) {
return (
+
+ Prompt
+
+ }
collapsible
variant="compact"
bodyStyle={{ padding: 0 }}
- extra={
- instances.length >= NUM_MAX_PLAYGROUND_INSTANCES ? (
-
- ) : (
-
- )
- }
+ extra={instances.length > 1 ? : null}
>
{template.__type === "chat" ? (
@@ -51,20 +52,6 @@ export function PlaygroundTemplate(props: PlaygroundTemplateProps) {
);
}
-function CompareButton() {
- const addInstance = usePlaygroundContext((state) => state.addInstance);
- return (
- } />}
- onClick={() => {
- addInstance();
- }}
- />
- );
-}
-
function DeleteButton(props: PlaygroundInstanceProps) {
const deleteInstance = usePlaygroundContext((state) => state.deleteInstance);
return (
diff --git a/app/src/pages/playground/PlaygroundTools.tsx b/app/src/pages/playground/PlaygroundTools.tsx
index b9e5d81bbe..805f4e1823 100644
--- a/app/src/pages/playground/PlaygroundTools.tsx
+++ b/app/src/pages/playground/PlaygroundTools.tsx
@@ -2,9 +2,24 @@ import React from "react";
import { Card } from "@arizeai/components";
-export function PlaygroundTools() {
+import { usePlaygroundContext } from "@phoenix/contexts/PlaygroundContext";
+
+import { TitleWithAlphabeticIndex } from "./TitleWithAlphabeticIndex";
+import { PlaygroundInstanceProps } from "./types";
+
+interface PlaygroundToolsProps extends PlaygroundInstanceProps {}
+export function PlaygroundTools(props: PlaygroundToolsProps) {
+ const index = usePlaygroundContext((state) =>
+ state.instances.findIndex(
+ (instance) => instance.id === props.playgroundInstanceId
+ )
+ );
return (
-
+ }
+ collapsible
+ variant="compact"
+ >
Tools go here
);
diff --git a/app/src/pages/playground/TitleWithAlphabeticIndex.tsx b/app/src/pages/playground/TitleWithAlphabeticIndex.tsx
new file mode 100644
index 0000000000..af6bba26dc
--- /dev/null
+++ b/app/src/pages/playground/TitleWithAlphabeticIndex.tsx
@@ -0,0 +1,23 @@
+import React from "react";
+
+import { Flex } from "@arizeai/components";
+
+import { AlphabeticIndexIcon } from "@phoenix/components/AlphabeticIndexIcon";
+
+/**
+ * Display the alphabetic index and title in a single line
+ */
+export function TitleWithAlphabeticIndex({
+ index,
+ title,
+}: {
+ index: number;
+ title: string;
+}) {
+ return (
+
+
+ {title}
+
+ );
+}
diff --git a/app/src/pages/playground/constants.tsx b/app/src/pages/playground/constants.tsx
index 90ea5993b0..a4b1bb2b59 100644
--- a/app/src/pages/playground/constants.tsx
+++ b/app/src/pages/playground/constants.tsx
@@ -1,6 +1,6 @@
import { ChatMessageRole } from "@phoenix/store";
-export const NUM_MAX_PLAYGROUND_INSTANCES = 2;
+export const NUM_MAX_PLAYGROUND_INSTANCES = 4;
export const DEFAULT_CHAT_ROLE = "user";
diff --git a/app/src/store/playgroundStore.tsx b/app/src/store/playgroundStore.tsx
index 8c9222cd59..0aa0331301 100644
--- a/app/src/store/playgroundStore.tsx
+++ b/app/src/store/playgroundStore.tsx
@@ -265,16 +265,16 @@ export const createPlaygroundStore = (
set({ operationType });
},
addInstance: () => {
- const instance = get().instances[0];
- if (!instance) {
+ const instances = get().instances;
+ const firstInstance = get().instances[0];
+ if (!firstInstance) {
return;
}
- // For now just hard-coded to two instances
set({
instances: [
- instance,
+ ...instances,
{
- ...instance,
+ ...firstInstance,
id: generateInstanceId(),
activeRunId: null,
},