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

V0 humeai #194

Open
wants to merge 2 commits into
base: feat/v0_HumeAI
Choose a base branch
from
Open
Show file tree
Hide file tree
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
14 changes: 14 additions & 0 deletions common/ai/ai.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { fetchAccessToken } from "@humeai/voice";

export const getHumeAccessToken = async () => {
const accessToken = await fetchAccessToken({
apiKey: String(process.env.HUME_API_KEY),
secretKey: String(process.env.HUME_CLIENT_SECRET),
});

if (accessToken === 'undefined') {
return null;
}

return accessToken ?? null;
}
59 changes: 59 additions & 0 deletions common/ai/cryptoPriceTool.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { Hume } from "hume"
import { ToolErrorMessage, ToolResponseMessage } from "hume/api/resources/empathicVoice";

async function fetchPriceData(currency: string) {
const response = await fetch(`https://api.coingecko.com/api/v3/simple/price?ids=${currency}&vs_currencies=usd`);
const data = await response.json();

return data[currency].usd;
}

export async function handleToolCallMessage(
toolCallMessage: Hume.empathicVoice.ToolCallMessage): Promise<any> {
if (toolCallMessage.name === "coingecko") {
try{
// parse the parameters from the ToolCall message
const args = JSON.parse(toolCallMessage.parameters) as {
currency: string;
};

// extract the individual arguments
const { currency } = args;

// call weather fetching function with extracted arguments
console.log(currency)
const price = await fetchPriceData(currency.toLowerCase());
console.log(price)
// send ToolResponse message to the WebSocket
const toolResponseMessage = {
type: "tool_response",
toolCallId: toolCallMessage.toolCallId,
content: price.toString(),
};

// socket?.sendToolResponseMessage(toolResponseMessage);
return toolResponseMessage;
} catch (error) {
// send ToolError message to the WebSocket if there was an error fetching the weather
const cryptoPriceToolErrorMessage = {
type: "tool_error",
toolCallId: toolCallMessage.toolCallId,
error: "Crypto Price tool error",
content: "There was an error with the crypto price tool",
};
console.log(cryptoPriceToolErrorMessage);
console.log(error)
return cryptoPriceToolErrorMessage;
}
} else {
// send ToolError message to the WebSocket if the requested tool was not found
const toolNotFoundErrorMessage = {
type: "tool_error",
toolCallId: toolCallMessage.toolCallId,
error: "Tool not found",
content: "The tool you requested was not found",
};

return toolNotFoundErrorMessage;
}
}
1 change: 1 addition & 0 deletions common/ai/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { getHumeAccessToken } from './ai'
1 change: 1 addition & 0 deletions common/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,5 @@ export {
findChainIconByChainId,
findChainNameByChainId,
mapIndexed,
cn,
} from './utils'
7 changes: 7 additions & 0 deletions common/utils/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ import {
reject,
find,
} from 'ramda'
import { type ClassValue, clsx } from "clsx"
import { twMerge } from "tailwind-merge"

import { Network } from '../types'
import { supportedChains } from '../config'

Expand Down Expand Up @@ -235,3 +238,7 @@ export const findChainNameByChainId = (chainId: number) =>
pipe(find(propEq('id', chainId)), prop('routePrefix'))(supportedChains)

export const delay = (time: number) => new Promise(resolve => setTimeout(resolve, time))

export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
85 changes: 85 additions & 0 deletions modules/Controls/Controls.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import React, { FC } from 'react'
import { useVoice } from "@humeai/voice-react";
import { Mic, MicOff, Phone } from "lucide-react";
import { AnimatePresence, motion } from "framer-motion";
import { Button } from '../Button';
import { cn } from '../../common/utils';
import { MicFFT } from '../MicFFT';
import { Toggle } from '../MicToggle';

export const Controls:FC = () => {
const { disconnect, status, isMuted, unmute, mute, micFft } = useVoice();

return (
<div
className={
cn(
"fixed bottom-0 left-0 w-full p-4 flex items-center justify-center z-50",
"bg-gradient-to-t from-card via-card/90 to-card/0",
)
}
>
<AnimatePresence>
{status.value === "connected" ? (
<motion.div
initial={{
y: "100%",
opacity: 0,
}}
animate={{
y: 0,
opacity: 1,
}}
exit={{
y: "100%",
opacity: 0,
}}
className={
"p-4 border border-border rounded shadow-sm flex items-center gap-4 bg-white"
}
>
<Toggle
pressed={!isMuted}
onPressedChange={() => {
if (isMuted) {
unmute();
} else {
mute();
}
}}
>
{isMuted ? (
<MicOff className={"size-4 border-black stroke-black"} />
) : (
<Mic className={"size-4 stroke-black"} />
)}
</Toggle>

<div className={"relative grid h-8 w-48 shrink grow-0"}>
<MicFFT fft={micFft} className={" fill-current"} />
</div>

<Button
className={"flex items-center"}
onClick={() => {
disconnect();
}}
>
{/* <div className='flex flex-row'>
<span className='mr-2'>
<Phone
className={"size-4 opacity-50"}
strokeWidth={2}
stroke={"currentColor"}
/>
</span>
<span>End Call</span>
</div> */}
End Call
</Button>
</motion.div>
) : null}
</AnimatePresence>
</div>
);
}
1 change: 1 addition & 0 deletions modules/Controls/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { Controls } from './Controls'
43 changes: 43 additions & 0 deletions modules/MicFFT/MicFFT.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { motion } from "framer-motion";
import { AutoSizer } from "react-virtualized";
import { cn } from "../../common/utils";

export default function MicFFT({
fft,
className,
}: {
fft: number[];
className?: string;
}) {
return (
<div className={"relative size-full"}>
<AutoSizer>
{({ width, height }) => (
<motion.svg
viewBox={`0 0 ${width} ${height}`}
width={width}
height={height}
className={cn("absolute !inset-0 !size-full", className)}
>
{Array.from({ length: 24 }).map((_, index) => {
const value = (fft[index] ?? 0) / 4;
const h = Math.min(Math.max(height * value, 2), height);
const yOffset = height * 0.5 - h * 0.5;

return (
<motion.rect
key={`mic-fft-${index}`}
height={h}
width={2}
x={2 + (index * width - 4) / 24}
y={yOffset}
rx={4}
/>
);
})}
</motion.svg>
)}
</AutoSizer>
</div>
);
}
1 change: 1 addition & 0 deletions modules/MicFFT/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as MicFFT } from './MicFFT'
42 changes: 42 additions & 0 deletions modules/MicToggle/MicToggle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import * as React from "react"
import * as TogglePrimitive from "@radix-ui/react-toggle"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "../../common/utils"

const toggleVariants = cva(
"inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors hover:bg-muted hover:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground",
{
variants: {
variant: {
default: "bg-transparent",
outline:
"border border-input bg-transparent hover:bg-accent hover:text-accent-foreground",
},
size: {
default: "h-10 px-3",
sm: "h-9 px-2.5",
lg: "h-11 px-5",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
)

const Toggle = React.forwardRef<
React.ElementRef<typeof TogglePrimitive.Root>,
React.ComponentPropsWithoutRef<typeof TogglePrimitive.Root> &
VariantProps<typeof toggleVariants>
>(({ className, variant, size, ...props }, ref) => (
<TogglePrimitive.Root
ref={ref}
className={cn(toggleVariants({ variant, size, className }))}
{...props}
/>
))

Toggle.displayName = TogglePrimitive.Root.displayName

export { Toggle, toggleVariants }
1 change: 1 addition & 0 deletions modules/MicToggle/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { Toggle } from './MicToggle'
29 changes: 29 additions & 0 deletions modules/StartCall/StartCall.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import React, { FC } from 'react'
import { useVoice } from "@humeai/voice-react";

import { Button } from '../Button';

export const StartCall:FC = () => {
const { status, connect } = useVoice();

return(
<Button
className={"z-50 flex items-center gap-1.5"}
onClick={() => {
connect()
.then(() => {})
.catch(() => {})
.finally(() => {});
}}
>
{/* <span>
<Phone
className={"size-4 opacity-50"}
strokeWidth={2}
stroke={"currentColor"}
/>
</span> */}
<span>Start Call</span>
</Button>
)
}
1 change: 1 addition & 0 deletions modules/StartCall/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { StartCall } from './StartCall'
Loading