Skip to content

Commit

Permalink
Merge pull request #76 from uberduck-ai/zach-retries-and-fe-improvements
Browse files Browse the repository at this point in the history
improvements
  • Loading branch information
zachwe authored Mar 19, 2024
2 parents 65bca17 + bd1e794 commit fd42cb5
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 101 deletions.
16 changes: 12 additions & 4 deletions openduck-py/openduck_py/routers/voice.py
Original file line number Diff line number Diff line change
Expand Up @@ -285,10 +285,18 @@ async def start_response(
messages = chat.history_json["messages"]
messages.append(new_message)

response = await acompletion(
CHAT_MODEL, messages, temperature=0.3, stream=True
)

# NOTE(zach): retries
response = None
for _retry in range(3):
try:
response = await acompletion(
CHAT_MODEL, messages, temperature=0.3, stream=True
)
except Exception:
if _retry == 2:
raise
else:
break
complete_sentence = ""
full_response = ""
async for chunk in response:
Expand Down
196 changes: 99 additions & 97 deletions web/src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,37 +71,84 @@ function Tile({
isScreenShare,
isLocal = false,
isAlone,
toggleMic,
micOn,
}: {
id: string;
isScreenShare?: boolean;
isLocal?: boolean;
isAlone?: boolean;
toggleMic?: () => void;
micOn?: boolean;
}) {
const videoState = useVideoTrack(id);

let containerCssClasses = "rounded-lg overflow-hidden shadow-lg m-2 ";
let containerCssClasses =
"rounded-lg overflow-hidden shadow-lg m-2 border-2 ";
containerCssClasses += isScreenShare ? "bg-blue-100" : "bg-gray-100";

if (isLocal) {
containerCssClasses += " border-2 border-green-500";
containerCssClasses += " border-green-500 ";
if (isAlone) {
containerCssClasses += " opacity-50";
}
} else {
containerCssClasses += " border-gray-300 ";
}

if (videoState.isOff) {
containerCssClasses += " bg-gray-300";
}

let micButtonClasses = "absolute bottom-4 right-4 px-2 py-1 ";
micButtonClasses += micOn
? "bg-green-500 text-white"
: "bg-red-500 text-white";

return (
<div className={containerCssClasses}>
<div className="p-4">
<div className="flex h-16 p-4 flex-row items-center">
{!isScreenShare && <Username id={id} isLocal={isLocal} />}
{isLocal && (
<Button
variant={micOn ? "success" : "danger"}
className={"text-xs ml-4"}
// className={micButtonClasses}
onClick={toggleMic}
>
{micOn ? "unmute" : "mute"}
</Button>
)}
</div>
</div>
);
}

function Spinner() {
return (
<svg
className="animate-spin -ml-1 mr-3 h-5 w-5 text-black"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
>
<circle
className="opacity-25"
cx="12"
cy="12"
r="10"
stroke="currentColor"
strokeWidth="4"
></circle>
<path
className="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
></path>
</svg>
);
}

interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
variant: "success" | "danger" | "primary" | "secondary";
children: React.ReactNode;
Expand Down Expand Up @@ -144,8 +191,11 @@ function Button({ variant, children, ...rest }: ButtonProps) {
);
}

function Call() {
function Call({ toggleMic, micOn }: { toggleMic: () => void; micOn: boolean }) {
const [getUserMediaError, setGetUserMediaError] = useState(false);
const meetingState = useMeetingState();

console.log("Meeting State: ", meetingState);

useDailyEvent(
"camera-error",
Expand All @@ -162,16 +212,25 @@ function Call() {

const renderCallScreen = () => (
<div className="flex flex-wrap justify-center items-center p-4">
{localSessionId && <Tile id={localSessionId} isLocal isAlone={isAlone} />}
{localSessionId && (
<Tile
id={localSessionId}
isLocal
isAlone={isAlone}
toggleMic={toggleMic}
micOn={micOn}
/>
)}
{remoteParticipantIds.map((id) => (
<Tile key={id} id={id} />
))}
{screens.map((screen) => (
<Tile key={screen.screenId} id={screen.session_id} isScreenShare />
))}
{isAlone && (
<div className="text-center p-4 m-4 rounded-lg shadow-lg bg-yellow-100">
{isAlone && meetingState === "joined-meeting" && (
<div className="text-center p-4 m-4 rounded-lg shadow-lg bg-yellow-100 flex flex-col items-center">
<h1 className="text-lg font-semibold">Waiting for others</h1>
<Spinner />
</div>
)}
</div>
Expand All @@ -184,9 +243,6 @@ const AudioCall = ({ callObject }: { callObject: DailyCall | null }) => {
const [roomUrl, setRoomUrl] = useState<string>("");
const [joinedRoom, setJoinedRoom] = useState(false);
const [micOn, setMicOn] = useState(true);
const meetingState = useMeetingState();

console.log("meeting state", meetingState);

const toggleMic = () => {
callObject?.setLocalAudio(!callObject?.localAudio());
Expand All @@ -198,104 +254,50 @@ const AudioCall = ({ callObject }: { callObject: DailyCall | null }) => {
setJoinedRoom(false);
}, [callObject]);

const joinCall = useCallback(
(userName: string) => {
console.log("[DAILY] Joining room", roomUrl);
callObject?.join({ url: roomUrl, userName });
setJoinedRoom(true);
},
[callObject, roomUrl]
);

const startHairCheck = useCallback(
async (url: string) => {
if (!url) return;
if (!callObject) {
console.log("No call object");
return;
const handleOrbClick = useCallback(async () => {
if (joinedRoom) {
leaveCall();
} else {
try {
const response = await fetch(`${apiHost}/audio/start`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
});
const room = await response.json();
if (room.url) {
console.log("Room created and joining:", room.url);
setRoomUrl(room.url);
callObject?.join({ url: room.url, userName: "User" });
setJoinedRoom(true);
} else {
console.error("Failed to create room");
}
} catch (error) {
console.error("Error creating room:", error);
}
await callObject.preAuth({ url });
await callObject.startCamera();
},
[callObject]
);

const createRoom = useCallback(async () => {
console.log("creatre rom");
try {
const response = await fetch(`${apiHost}/audio/start`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
});
const room = await response.json();
if (room.url) {
console.log("Room created:", room.url);
setRoomUrl(room.url);
} else {
console.error("Failed to create room");
}
} catch (error) {
console.error("Error creating room:", error);
}
}, []);

useEffect(() => {
startHairCheck(roomUrl);
}, []);
}, [callObject, roomUrl]);

return (
<div className="flex flex-col space-y-4 p-4">
<input
type="text"
className="form-input px-4 py-2 border rounded"
value={roomUrl}
onChange={(e) => setRoomUrl(e.target.value)}
placeholder="Enter room URL"
/>
<div className="flex space-x-2">
<Button variant="success" onClick={createRoom}>
Create Room
</Button>
{joinedRoom ? (
<Button variant="danger" onClick={leaveCall}>
Leave Room
</Button>
) : (
<Button
className={`${
roomUrl &&
roomUrl.match(
/^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/
)
? "bg-blue-500 hover:bg-blue-600"
: "bg-blue-300"
} text-white`}
onClick={() => joinCall("User")}
disabled={
!roomUrl ||
!roomUrl.match(
/^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/
)
}
variant="primary"
>
Join Room
</Button>
)}
<Button variant="secondary" onClick={toggleMic}>
{micOn ? "Mic On" : "Mic Off"}
</Button>
<div className="flex flex-col items-center space-y-4 p-4">
<div
className="orb-button bg-blue-500 hover:bg-blue-600 text-white rounded-full p-4 cursor-pointer shadow-lg transform hover:scale-110 transition-transform duration-300 ease-in-out"
onClick={handleOrbClick}
onMouseOver={(e) => e.currentTarget.classList.add("hover:shadow-xl")}
onMouseOut={(e) => e.currentTarget.classList.remove("hover:shadow-xl")}
>
{joinedRoom ? "Leave Room" : "Create & Join Room"}
</div>
{roomUrl && false && <div className="text-sm">Room URL: {roomUrl}</div>}
<div>
<Call />
<Call toggleMic={toggleMic} micOn={micOn} />
<DailyAudio />
</div>
</div>
);
};

export default function Home() {
const callObject = useCallObject({});
return (
Expand Down

0 comments on commit fd42cb5

Please sign in to comment.