Skip to content

Commit

Permalink
display tool call and tool response messages
Browse files Browse the repository at this point in the history
  • Loading branch information
yinishi committed May 10, 2024
1 parent 298a92e commit ef83772
Show file tree
Hide file tree
Showing 5 changed files with 124 additions and 52 deletions.
19 changes: 9 additions & 10 deletions src/Views/ConversationView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { Waveform } from '@/components/Waveform';
import { JSONErrorMessage } from '@humeai/voice';
import type {
AssistantTranscriptMessage,
ToolCall,
ToolResponse,
UserTranscriptMessage,
} from '@humeai/voice-react';
import { useVoice } from '@humeai/voice-react';
Expand All @@ -23,7 +25,7 @@ export const ConversationView: FC<ConversationViewProps> = ({
onDisconnect,
onReconnect,
}) => {
const { lastVoiceMessage, messages, toolStatusStore, status } = useVoice();
const { lastVoiceMessage, messages, status } = useVoice();

const filteredMessages = useMemo(() => {
return messages
Expand All @@ -33,11 +35,15 @@ export const ConversationView: FC<ConversationViewProps> = ({
): message is
| UserTranscriptMessage
| AssistantTranscriptMessage
| JSONErrorMessage => {
| JSONErrorMessage
| ToolCall
| ToolResponse => {
return (
message.type === 'assistant_message' ||
message.type === 'user_message' ||
message.type === 'error'
message.type === 'error' ||
message.type === 'tool_call' ||
message.type === 'tool_response'
);
},
)
Expand All @@ -49,12 +55,6 @@ export const ConversationView: FC<ConversationViewProps> = ({
});
}, [messages]);

const pendingTools = useMemo(() => {
return Object.keys(toolStatusStore).filter((toolId) => {
return !toolStatusStore[toolId].resolved;
});
}, [toolStatusStore]);

return (
<>
<NavRail variant="dark" />
Expand All @@ -72,7 +72,6 @@ export const ConversationView: FC<ConversationViewProps> = ({
{filteredMessages.length > 0 ? (
<Messages
messages={filteredMessages}
hasPendingTools={pendingTools.length > 0}
status={status}
onReconnect={onReconnect}
/>
Expand Down
86 changes: 52 additions & 34 deletions src/components/Messages/index.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,32 @@
import { AgentMessage } from '@/components/AgentMessage';
import { ErrorMessage } from '@/components/ErrorMessage';
import { SearchInProgress } from '@/components/SearchInProgress';
import { SearchSucceeded } from '@/components/SearchSucceeded';
import { UserMessage } from '@/components/UserMessage';
import { cn } from '@/utils';
import { JSONErrorMessage } from '@humeai/voice';
import { JSONErrorMessage, ToolCall, ToolResponse } from '@humeai/voice';
import {
type AssistantTranscriptMessage,
type UserTranscriptMessage,
useVoice,
} from '@humeai/voice-react';
import type { ElementRef, FC } from 'react';
import { Fragment, useEffect, useRef } from 'react';
import { match } from 'ts-pattern';
import { useEffect, useRef } from 'react';

export type MessagesProps = {
messages: Array<
UserTranscriptMessage | AssistantTranscriptMessage | JSONErrorMessage
| UserTranscriptMessage
| AssistantTranscriptMessage
| JSONErrorMessage
| ToolCall
| ToolResponse
>;
hasPendingTools: boolean;
status: ReturnType<typeof useVoice>['status'];
onReconnect: () => void;
};

export const Messages: FC<MessagesProps> = ({
messages,
hasPendingTools,
status,
onReconnect,
}) => {
Expand Down Expand Up @@ -74,35 +76,51 @@ export const Messages: FC<MessagesProps> = ({
ref={messagesWrapper}
>
{messages.map((message) => {
return (
<Fragment key={message.receivedAt?.getTime()}>
{match(message.type)
.with('user_message', () => {
let messageContent =
typeof message.message !== 'string'
? message.message.content
: '';
return <UserMessage messageContent={messageContent} />;
})
.with('assistant_message', () => {
let messageContent =
typeof message.message !== 'string'
? message.message.content
: '';
return <AgentMessage messageContent={messageContent} />;
})
.with('error', () => {
let messageContent =
typeof message.message === 'string'
? message.message
: 'An unexpected error occurred.';
return <ErrorMessage messageContent={messageContent} />;
})
.exhaustive()}
</Fragment>
);
if (message.type === 'user_message') {
return (
<UserMessage
key={message.receivedAt?.getTime()}
messageContent={message.message.content}
/>
);
}

if (message.type === 'assistant_message') {
return (
<AgentMessage
key={message.receivedAt?.getTime()}
messageContent={message.message.content}
/>
);
}

if (message.type === 'error') {
return (
<ErrorMessage
key={message.receivedAt?.getTime()}
messageContent={message.message}
/>
);
}

if (message.type === 'tool_call') {
return (
<SearchInProgress
key={message.receivedAt?.getTime()}
message={message}
/>
);
}

if (message.type === 'tool_response') {
return (
<SearchSucceeded
key={`tool-response-${message.tool_call_id}`}
message={message}
/>
);
}
})}
{hasPendingTools ? <SearchInProgress /> : null}
{status.value === 'error' && (
<div>
<button
Expand Down
43 changes: 36 additions & 7 deletions src/components/SearchInProgress/index.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,50 @@
import { ToolCall } from '@humeai/voice';
import { AnimatePresence, motion } from 'framer-motion';
import { Hourglass } from 'lucide-react';
import { FC } from 'react';
import { Search } from 'lucide-react';
import { FC, useMemo } from 'react';

export type SearchInProgressProps = Record<never, never>;
export type SearchInProgressProps = {
message: ToolCall;
};

export const SearchInProgress: FC<SearchInProgressProps> = ({ message }) => {
const toolCallId = 'tool_call_id' in message ? message.tool_call_id : '';

const query = useMemo(() => {
try {
if ('parameters' in message && typeof message.parameters === 'string') {
const parameters = JSON.parse(message.parameters);
if (parameters.query) {
console.log('query', parameters.query);
return parameters.query;
}
}
} catch (e) {
return '';
}
}, [message]);

export const SearchInProgress: FC<SearchInProgressProps> = () => {
return (
<AnimatePresence>
<motion.div
className="my-4 ml-auto flex w-fit items-center justify-center gap-2 text-neutral-200"
className="my-4 ml-auto flex w-fit items-center justify-center gap-2 bg-black text-neutral-200"
data-tool-call-id={toolCallId}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.1 }}
>
<Hourglass className="animate-spin-slow" size={20} />
<span className="font-mono">Searching the web ...</span>
<Search size={20} />
{query ? (
<span>
<span>Searching the web for </span>
<span className="font-mono text-neutral-400">
&ldquo;{query}&rdquo;
</span>
</span>
) : (
<span>Searching the web ...</span>
)}
</motion.div>
</AnimatePresence>
);
Expand Down
26 changes: 26 additions & 0 deletions src/components/SearchSucceeded/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { ToolResponse } from '@humeai/voice';
import { AnimatePresence, motion } from 'framer-motion';
import { FC } from 'react';

export type SearchSucceededProps = {
message: ToolResponse;
};

export const SearchSucceeded: FC<SearchSucceededProps> = ({ message }) => {
const toolCallId = 'tool_call_id' in message ? message.tool_call_id : '';

return (
<AnimatePresence>
<motion.div
className="-mt-2 mb-4 ml-auto flex w-fit items-center justify-center gap-2 bg-black text-neutral-200"
data-tool-call-id={toolCallId}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.1 }}
>
<span>Found results!</span>
</motion.div>
</AnimatePresence>
);
};
2 changes: 1 addition & 1 deletion src/components/WaitingOnHost/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export const WaitingOnHost: FC<WaitingOnHostProps> = () => {
<div className="flex h-svh w-full items-center justify-center">
<AnimatePresence>
<motion.div
className="z-10 flex w-fit flex-col items-center justify-center gap-8 rounded-lg bg-black p-8 text-center text-xl text-neutral-100"
className="z-10 flex w-fit flex-col items-center justify-center gap-8 rounded-lg bg-black p-8 text-center text-xl text-neutral-100"
exit={{ opacity: 0 }}
>
<div className="flex">
Expand Down

0 comments on commit ef83772

Please sign in to comment.