Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

temp branch #2827

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions weave-js/src/components/FancyPage/useProjectSidebar.ts
Original file line number Diff line number Diff line change
@@ -186,6 +186,13 @@ export const useProjectSidebar = (
isShown: isWeaveOnly,
iconName: IconNames.CubeContainer,
},
{
type: 'button' as const,
name: 'Playground',
slug: 'weave/playground',
isShown: isWeaveOnly,
iconName: IconNames.CubeContainer,
},
{
type: 'menuPlaceholder' as const,
// name: 'More',
20 changes: 19 additions & 1 deletion weave-js/src/components/PagePanelComponents/Home/Browse3.tsx
Original file line number Diff line number Diff line change
@@ -92,6 +92,7 @@ import {OpPage} from './Browse3/pages/OpPage';
import {OpsPage} from './Browse3/pages/OpsPage';
import {OpVersionPage} from './Browse3/pages/OpVersionPage';
import {OpVersionsPage} from './Browse3/pages/OpVersionsPage';
import {PlaygroundPage} from './Browse3/pages/PlaygroundPage/PlaygroundPage';
import {TablePage} from './Browse3/pages/TablePage';
import {TablesPage} from './Browse3/pages/TablesPage';
import {useURLSearchParamsDict} from './Browse3/pages/util';
@@ -486,6 +487,13 @@ const Browse3ProjectRoot: FC<{
<Route path={`${projectRoot}/calls/:itemName`}>
<CallPageBinding />
</Route>
<Route
path={[
`${projectRoot}/playground/:itemName`,
`${projectRoot}/playground`,
]}>
<PlaygroundPageBinding />
</Route>
<Route path={`${projectRoot}/:tab(evaluations|traces|calls)`}>
<CallsPageBinding />
</Route>
@@ -652,7 +660,17 @@ const useParamsDecoded = <T extends object>() => {
}, [params]);
};

// TODO(tim/weaveflow_improved_nav): Generalize this
const PlaygroundPageBinding = () => {
const params = useParamsDecoded<Browse3TabItemParams>();
return (
<PlaygroundPage
entity={params.entity}
project={params.project}
callId={params.itemName}
/>
);
};

const CallPageBinding = () => {
useCallPeekRedirect();
const params = useParamsDecoded<Browse3TabItemParams>();
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Box from '@mui/material/Box';
import {Loading} from '@wandb/weave/components/Loading';
import {urlPrefixed} from '@wandb/weave/config';
import {useViewTraceEvent} from '@wandb/weave/integrations/analytics/useViewEvents';
import React, {FC, useCallback, useEffect, useState} from 'react';
import {useHistory} from 'react-router-dom';
@@ -11,6 +12,7 @@ import {Browse2OpDefCode} from '../../../Browse2/Browse2OpDefCode';
import {TRACETREE_PARAM, useWeaveflowCurrentRouteContext} from '../../context';
import {FeedbackGrid} from '../../feedback/FeedbackGrid';
import {NotFoundPanel} from '../../NotFoundPanel';
import {CallChat} from '../ChatView/CallChat';
import {isCallChat} from '../ChatView/hooks';
import {isEvaluateOp} from '../common/heuristics';
import {CenteredAnimatedLoader} from '../common/Loader';
@@ -23,7 +25,6 @@ import {TabUseCall} from '../TabUseCall';
import {useURLSearchParamsDict} from '../util';
import {useWFHooks} from '../wfReactInterface/context';
import {CallSchema} from '../wfReactInterface/wfDataModelHooksInterface';
import {CallChat} from './CallChat';
import {CallDetails} from './CallDetails';
import {CallOverview} from './CallOverview';
import {CallSummary} from './CallSummary';
@@ -54,6 +55,14 @@ const useCallTabs = (call: CallSchema) => {
const codeURI = call.opVersionRef;
const {entity, project, callId} = call;
const weaveRef = makeRefCall(entity, project, callId);

const handleOpenInPlayground = () => {
window.open(
urlPrefixed(`/${entity}/${project}/weave/playground/${callId}`),
'_blank'
);
};

return [
// Disabling Evaluation tab until it's better for single evaluation
...(false && isEvaluateOp(call.spanName)
@@ -80,11 +89,20 @@ const useCallTabs = (call: CallSchema) => {
{
label: 'Chat',
content: (
<ScrollableTabContent>
<Tailwind>
<CallChat call={call.traceCall!} />
</Tailwind>
</ScrollableTabContent>
<>
<Button
variant="secondary"
startIcon="sandbox-playground"
className="m-16 mb-8"
onClick={handleOpenInPlayground}>
Open chat in playground
</Button>
<ScrollableTabContent>
<Tailwind>
<CallChat call={call.traceCall!} isPlayground={false} />
</Tailwind>
</ScrollableTabContent>
</>
),
},
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/**
* Get normalized version of call data in chat format and display it.
*/

import React, {useEffect, useState} from 'react';

import {LoadingDots} from '../../../../../LoadingDots';
import {PlaygroundContext} from '../PlaygroundPage/PlaygroundChat/PlaygroundContext';
import {TraceCallSchema} from '../wfReactInterface/traceServerClientTypes';
import {ChatView} from './ChatView';
import {useCallAsChat} from './hooks';

type CallChatProps = {
call: TraceCallSchema;
isPlayground?: boolean;
deleteMessage?: (messageIndex: number) => void;
editMessage?: (messageIndex: number, newMessage: any) => void;
deleteChoice?: (choiceIndex: number) => void;
addMessage?: (newMessage: any) => void;
editChoice?: (choiceIndex: number, newChoice: any) => void;
retry?: (messageIndex: number, isChoice?: boolean) => void;
sendMessage?: (
role: 'assistant' | 'user' | 'tool',
content: string,
toolCallId?: string
) => void;
};

export const CallChat = ({
call,
isPlayground = false,
deleteMessage,
editMessage,
deleteChoice,
addMessage,
editChoice,
retry,
sendMessage,
}: CallChatProps) => {
const chat = useCallAsChat(call);

// This is used because when we first load the chat view in a drawer, the animation cant handle all the rows
// so we delay for the first render
const [animationBuffer, setAnimationBuffer] = useState(true);
useEffect(() => {
setTimeout(() => {
setAnimationBuffer(false);
}, 300);
}, []);

if (chat.loading || animationBuffer) {
return <LoadingDots />;
}
return (
<PlaygroundContext.Provider
value={{
isPlayground,
deleteMessage,
editMessage,
deleteChoice,
addMessage,
editChoice,
retry,
sendMessage,
}}>
<ChatView call={call} chat={chat} />
</PlaygroundContext.Provider>
);
};
Original file line number Diff line number Diff line change
@@ -1,20 +1,26 @@
import React, {useEffect, useRef} from 'react';
import React, {useEffect, useMemo, useRef} from 'react';

import {useDeepMemo} from '../../../../../../hookUtils';
import {TraceCallSchema} from '../wfReactInterface/traceServerClientTypes';
import {ChoicesView} from './ChoicesView';
import {HorizontalRuleWithLabel} from './HorizontalRuleWithLabel';
import {MessageList} from './MessageList';
import {Chat} from './types';

type ChatViewProps = {
call?: TraceCallSchema;
chat: Chat;
};

export const ChatView = ({chat}: ChatViewProps) => {
export const ChatView = ({call, chat}: ChatViewProps) => {
const outputRef = useRef<HTMLDivElement>(null);

const chatResult = useDeepMemo(chat.result);

const scrollLastMessage = useMemo(
() => !(outputRef.current && chatResult && chatResult.choices),
[chatResult]
);

useEffect(() => {
if (outputRef.current && chatResult && chatResult.choices) {
outputRef.current.scrollIntoView();
@@ -23,11 +29,12 @@ export const ChatView = ({chat}: ChatViewProps) => {

return (
<div>
<HorizontalRuleWithLabel label="Input" />
<MessageList messages={chat.request.messages} />
<MessageList
messages={chat.request?.messages || []}
scrollLastMessage={scrollLastMessage}
/>
{chatResult && chatResult.choices && (
<div className="mt-12" ref={outputRef}>
<HorizontalRuleWithLabel label="Output" />
<div ref={outputRef}>
<ChoicesView
isStructuredOutput={chat.isStructuredOutput}
choices={chatResult.choices}
Original file line number Diff line number Diff line change
@@ -11,6 +11,11 @@ type ChoiceViewProps = {
export const ChoiceView = ({choice, isStructuredOutput}: ChoiceViewProps) => {
const {message} = choice;
return (
<MessagePanel message={message} isStructuredOutput={isStructuredOutput} />
<MessagePanel
index={choice.index}
message={message}
isStructuredOutput={isStructuredOutput}
isChoice
/>
);
};
Original file line number Diff line number Diff line change
@@ -1,17 +1,74 @@
import React from 'react';
import React, {useEffect, useRef} from 'react';

import {MessagePanel} from './MessagePanel';
import {Messages} from './types';
import {Message, Messages} from './types';

type MessageListProps = {
messages: Messages;
scrollLastMessage?: boolean;
};

export const MessageList = ({messages}: MessageListProps) => {
export const MessageList = ({
messages,
scrollLastMessage = false,
}: MessageListProps) => {
const lastMessageRef = useRef<HTMLDivElement>(null);
const processedMessages = [];

// This is ugly will refactor, associates tool calls with their responses
for (let i = 0; i < messages.length; i++) {
const message = messages[i];
if (!message.tool_calls) {
processedMessages.push({
...message,
original_index: message.original_index ?? i,
});
continue;
}
const toolCalls = message.tool_calls!;

const toolMessages = [];
// Get next messages where role = tool
while (i + 1 < messages.length && messages[i + 1].role === 'tool') {
toolMessages.push({
...messages[i + 1],
original_index: (messages[i + 1] as any).original_index ?? i + 1,
});
i++;
}
for (let j = 0; j < toolCalls.length; j++) {
let response: Message | undefined;
for (const toolMessage of toolMessages) {
if (toolMessage.tool_call_id === toolCalls[j].id) {
response = toolMessage;
break;
}
}
toolCalls[j] = {
...toolCalls[j],
response,
};
}
processedMessages.push({
...message,
tool_call: toolCalls,
});
}

useEffect(() => {
if (lastMessageRef.current && scrollLastMessage) {
lastMessageRef.current.scrollIntoView();
}
}, [messages.length, scrollLastMessage]);

return (
<div className="flex flex-col gap-36">
{messages.map((m, i) => (
<MessagePanel key={i} message={m} />
<div className="flex flex-col">
{processedMessages.map((m, i) => (
<div
ref={i === processedMessages.length - 1 ? lastMessageRef : null}
key={i}>
<MessagePanel index={m.original_index ?? i} message={m} />
</div>
))}
</div>
);
Loading