Skip to content

Commit

Permalink
feat: upgrade demo from STT/LLM/TTS to Agent API (#56)
Browse files Browse the repository at this point in the history
* feat: added anonymous session to secure serverless endpoints (#54)

* feat: upgrade from stt-llm-tts to agent-api (#57)

* feat: added anonymous session to secure serverless endpoints

* feat: integrated anonymous session in client for specific route

* feat: added full authentication and hold some code for a dirty error

* fix: recommit fixed package-lock.json and added authentication

* chore: removed all redundent code and settings message
---------
Co-authored-by: TechAlchmy <worldsmileswithyou@gmail.com>
Co-authored-by: TechAlchemist <114001662+TechAlchmy@users.noreply.github.com>
Co-authored-by: lukeocodes <luke@lukeoliff.com>

* fix: style tweak

* feat: add banner to click back to v1

---------

Co-authored-by: TechAlchemist <114001662+TechAlchmy@users.noreply.github.com>
Co-authored-by: TechAlchemy423 <ericw5413012@gmail.com>

BREAKING CHANGE: switching APIs from STT/LLM/TTS to Agent API changes the entire purpose of this demo
  • Loading branch information
lukeocodes authored Nov 11, 2024
1 parent ddbcdd8 commit 4d1a87f
Show file tree
Hide file tree
Showing 41 changed files with 5,337 additions and 5,919 deletions.
12 changes: 10 additions & 2 deletions app/api/authenticate/route.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,27 @@
import { config } from "@/app/config";
import { verifyJWT } from "@/app/lib/authMiddleware";
import { DeepgramError, createClient } from "@deepgram/sdk";
import { NextResponse, type NextRequest } from "next/server";

export const revalidate = 0;

export async function GET(request: NextRequest) {
// verify jwt token
const authResponse = verifyJWT(request);
if (authResponse.status !== 200) {
return authResponse;
}

// exit early so we don't request 70000000 keys while in devmode
if (process.env.DEEPGRAM_ENV === "development") {
return NextResponse.json({
key: process.env.DEEPGRAM_API_KEY ?? "",
key: config.deepGramApiKey ?? "",
});
}

// gotta use the request object to invalidate the cache every request :vomit:
const url = request.url;
const deepgram = createClient(process.env.DEEPGRAM_API_KEY ?? "");
const deepgram = createClient(config.deepGramApiKey ?? "");

let { result: projectsResult, error: projectsError } =
await deepgram.manage.getProjects();
Expand Down
36 changes: 0 additions & 36 deletions app/api/brain/route.ts

This file was deleted.

9 changes: 9 additions & 0 deletions app/api/generate-token/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { generateToken } from "@/app/lib/jwt";
import { NextResponse } from "next/server";

export async function POST() {
const payload = { role: "anonymous" + Date.now() };

const token = generateToken(payload);
return NextResponse.json({ token });
}
61 changes: 0 additions & 61 deletions app/api/speak/route.ts

This file was deleted.

11 changes: 11 additions & 0 deletions app/api/verify-token/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { verifyJWT } from "@/app/lib/authMiddleware";
import { NextRequest } from "next/server";

export async function GET(req: NextRequest) {
// verify jwt token
const authResponse = verifyJWT(req);
verifyJWT;
if (authResponse.status !== 200) {
return authResponse;
}
}
30 changes: 0 additions & 30 deletions app/components/AgentAvatar.tsx

This file was deleted.

79 changes: 79 additions & 0 deletions app/components/AgentControls.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { Tooltip } from "@nextui-org/react";
import { useCallback } from "react";
import { MicrophoneIcon } from "./icons/MicrophoneIcon";
import { useWebSocketContext } from "../context/WebSocketContext";

export const AgentSettings = () => {
return (
<>
<div className="flex items-center gap-2.5 text-sm mr-4">
<span className="hidden md:inline-block text-white/50 font-inter">
LLM: <span className="text-white">Claude 3 Haiku</span>
</span>
<span className="hidden md:inline-block text-white/50 font-inter">
Voice: <span className="text-white">Asteria</span>
</span>
</div>
</>
);
};

export const AgentControls = () => {
const { startStreaming, stopStreaming, microphoneOpen } =
useWebSocketContext();

const microphoneToggle = useCallback(
async (e: Event) => {
e.preventDefault();
console.log("toogle the control");
if (!microphoneOpen) {
startStreaming();
} else {
stopStreaming();
}
},
[microphoneOpen, startStreaming, stopStreaming]
);

console.log("microphone control rendering");

return (
<div className="relative">
<div className="absolute w-full -top-[4.5rem] py-4 flex justify-center">
<AgentSettings />
</div>
<div className="flex bg-[#101014] rounded-full justify-center">
<span
className={`rounded-full p-0.5 ${
microphoneOpen
? "bg-gradient-to-r bg-gradient to-[#13EF93]/50 from-red-500"
: "bg-gradient-to-r bg-gradient to-[#13EF93]/50 from-[#149AFB]/80"
}`}
>
<Tooltip showArrow content="Toggle microphone on/off.">
<a
href="#"
onClick={(e: any) => microphoneToggle(e)}
className={`rounded-full w-16 md:w-20 sm:w-24 py-2 md:py-4 px-2 h-full sm:px-8 font-bold bg-[#101014] text-light-900 text-sm sm:text-base flex items-center justify-center group`}
>
{microphoneOpen && (
<div className="w-auto items-center justify-center hidden sm:flex absolute shrink-0">
<MicrophoneIcon
micOpen={microphoneOpen}
className="h-5 md:h-6 animate-ping-infinite"
/>
</div>
)}
<div className="w-auto flex items-center justify-center shrink-0">
<MicrophoneIcon
micOpen={microphoneOpen}
className="h-5 md:h-6 "
/>
</div>
</a>
</Tooltip>
</span>
</div>
</div>
);
};
60 changes: 39 additions & 21 deletions app/components/ChatBubble.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,46 @@
import { LeftBubble } from "./LeftBubble";
import { Message } from "ai";
import { RightBubble } from "./RightBubble";
import { DgSvg } from "./DgSvg";
import { TextContent } from "./TextContext";
import { AgentMessageHeader } from "./MessageHeader";
import { AgentMessageAudio } from "./MessageAudio";
import { Avatar } from "@nextui-org/react";

// const isMessage = (message: Message | Metadata): message is Message {
// return typeof message === 'Message';
// }

function isUserMessage(message: any): message is Message {
return message.role === "user";
}

function isAssistantMessage(message: any): message is Message {
return message.role === "assistant";
}

export const ChatBubble = ({ message }: { message: any }) => {
if (isUserMessage(message)) {
export const AgentChatBubble = ({ message }: { message: any }) => {
if (message?.role === "user") {
// chat user
return <RightBubble message={message} />;
} else if (isAssistantMessage(message)) {
// chat assistant
return <LeftBubble message={message} />;
} else {
// other as-yet unlabelled messages
return <></>;
// chat assistant
return (
<>
<div className="col-start-1 col-end-13 sm:col-end-11 md:col-end-9 lg:col-end-8 xl:col-end-7 md:px-3 pt-3">
<div className="flex items-start gap-2 flex-col md:flex-row">
<div className="flex items-start gap-2 flex-col md:flex-row max-w-full md:max-w-none">
<div className="min-w-12 text-white shrink-0">
{message?.voice ? (
<Avatar src={"/aura-asteria-en.svg"} />
) : (
<DgSvg />
)}
</div>
<div className="glass flex p-4 rounded-e-xl rounded-es-xl max-w-full md:max-w-none">
<div className="flex flex-col overflow-hidden pre-overflow-y-auto">
<AgentMessageHeader message={message} />
<div className="text-sm font-normal pt-2 text-white/80 markdown">
<TextContent text={message.content} />
</div>
</div>
</div>
</div>
<div className="md:px-1 pb-3 flex gap-2 self-start md:self-center">
<div className="h-6 w-6 shrink-0">
<AgentMessageAudio message={message} />
</div>
</div>
</div>
</div>
<div className="hidden col-start-1 col-end-13 md:px-3 pb-3 md:flex gap-2"></div>
</>
);
}
};
Loading

1 comment on commit 4d1a87f

@vercel
Copy link

@vercel vercel bot commented on 4d1a87f Nov 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.