diff --git a/.dockerignore b/.dockerignore index 1943d61..35f68de 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,3 +1,2 @@ * -!llm.py -!kitt.py \ No newline at end of file +!server/**.py \ No newline at end of file diff --git a/.env.example b/.env.example index aeec59b..10a18d1 100644 --- a/.env.example +++ b/.env.example @@ -1,10 +1,10 @@ LIVEKIT_API_KEY="" LIVEKIT_API_SECRET="" -LIVEKIT_WS_URL="" +LIVEKIT_URL="" NEXT_PUBLIC_LK_TOKEN_ENDPOINT="" OPENAI_API_KEY="" OPENAI_ORG_ID="" -ELEVENLABS_API_KEY="" +ELEVEN_API_KEY="" DEEPGRAM_API_KEY="" diff --git a/.gitignore b/.gitignore index e811ba0..e17cb68 100644 --- a/.gitignore +++ b/.gitignore @@ -49,3 +49,9 @@ yarn-error.log* # typescript *.tsbuildinfo + +# python +.cenv +.venv +venv/ +__pycache__ \ No newline at end of file diff --git a/app/api/get-participant-token/route.ts b/app/api/get-participant-token/route.ts index 3d71781..cf4dd06 100644 --- a/app/api/get-participant-token/route.ts +++ b/app/api/get-participant-token/route.ts @@ -10,7 +10,7 @@ import { TokenResult } from '~/utils/types'; const apiKey = process.env.LIVEKIT_API_KEY; const apiSecret = process.env.LIVEKIT_API_SECRET; -const livekitWsUrl = process.env.LIVEKIT_WS_URL; +const livekitWsUrl = process.env.LIVEKIT_URL; const createToken = (userInfo: AccessTokenOptions, grant: VideoGrant) => { const at = new AccessToken(apiKey, apiSecret, userInfo); @@ -19,12 +19,12 @@ const createToken = (userInfo: AccessTokenOptions, grant: VideoGrant) => { return at.toJwt(); }; -const roomPattern = /\w{4}\-\w{4}\-\w{4}/; +const roomPattern = /\w{4}\-\w{4}/; export async function GET(req: NextRequest) { const room = req.nextUrl.searchParams.get('roomName'); const identity = req.nextUrl.searchParams.get('identity'); - const name = req.nextUrl.searchParams.get('name'); + const name = req.nextUrl.searchParams.get('name') ?? 'human'; const metadata = req.nextUrl.searchParams.get('metadata'); if (!room) { diff --git a/app/page.tsx b/app/page.tsx index 21ed469..b8221e4 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -49,7 +49,7 @@ export default function Page() { setMetadata(md); }, [liveKitUrl, roomName, tokenOptions]); - const token = useToken('/api/token', roomName, tokenOptions); + const token = useToken('/api/get-participant-token', roomName, tokenOptions); const appConfig = useAppConfig(); const outputs = [ appConfig?.outputs.audio && PlaygroundOutputs.Audio, @@ -58,6 +58,8 @@ export default function Page() { ].filter((item) => typeof item !== 'boolean') as PlaygroundOutputs[]; const handleConnect = useCallback((connect: boolean, opts?: { url: string; token: string }) => { + console.log('connect', connect); + console.log('connect opts', opts); if (connect && opts) { setLiveKitUrl(opts.url); } @@ -97,10 +99,7 @@ export default function Page() { }} > { return [generateRandomAlphanumeric(4), generateRandomAlphanumeric(4)].join('-'); -} +}; diff --git a/components/ActiveRoom.tsx b/components/ActiveRoom.tsx deleted file mode 100644 index 3e601c0..0000000 --- a/components/ActiveRoom.tsx +++ /dev/null @@ -1,118 +0,0 @@ -'use client'; -import { - LiveKitRoom, - LocalUserChoices, - VideoConference, - formatChatMessageLinks, - useToken, -} from '@livekit/components-react'; - -import { - ExternalE2EEKeyProvider, - LogLevel, - Room, - RoomConnectOptions, - RoomOptions, - VideoCodec, - VideoPresets, -} from 'livekit-client'; - -import { useRouter, useSearchParams } from 'next/navigation'; -import { useMemo } from 'react'; -import { decodePassphrase, useServerUrl } from '~/utils/lksdk'; -import { DebugMode } from './Debug'; - -export type ActiveRoomProps = { - userChoices: LocalUserChoices; - roomName: string; - region?: string; -}; - -export const ActiveRoom = ({ roomName, userChoices }: ActiveRoomProps) => { - const router = useRouter(); - - const token = useToken(process.env.NEXT_PUBLIC_LK_TOKEN_ENDPOINT, roomName, { - userInfo: { - identity: userChoices.username, - name: userChoices.username, - }, - }); - - const searchParams = useSearchParams(); - const region = searchParams.get('region'); - const hq = searchParams.get('hq'); - const codec = searchParams.get('codec') ?? 'vp9'; - - const liveKitUrl = useServerUrl(region as string | undefined); - - const worker = - typeof window !== 'undefined' && - userChoices.e2ee && - new Worker(new URL('livekit-client/e2ee-worker', import.meta.url)); - - const e2eeEnabled = !!(userChoices.e2ee && worker); - const keyProvider = new ExternalE2EEKeyProvider(); - - const roomOptions = useMemo((): RoomOptions => { - return { - videoCaptureDefaults: { - deviceId: userChoices.videoDeviceId ?? undefined, - resolution: hq === 'true' ? VideoPresets.h2160 : VideoPresets.h720, - }, - publishDefaults: { - dtx: false, - videoSimulcastLayers: - hq === 'true' - ? [VideoPresets.h1080, VideoPresets.h720] - : [VideoPresets.h540, VideoPresets.h216], - red: !e2eeEnabled, - videoCodec: codec as VideoCodec | undefined, - }, - audioCaptureDefaults: { - deviceId: userChoices.audioDeviceId ?? undefined, - }, - adaptiveStream: { pixelDensity: 'screen' }, - dynacast: true, - e2ee: e2eeEnabled - ? { - keyProvider, - worker, - } - : undefined, - }; - }, [userChoices, hq, codec]); - - const room = useMemo(() => new Room(roomOptions), []); - - if (e2eeEnabled) { - keyProvider.setKey(decodePassphrase(userChoices.sharedPassphrase)); - room.setE2EEEnabled(true); - } - const connectOptions = useMemo((): RoomConnectOptions => { - return { - autoSubscribe: true, - }; - }, []); - - return ( - <> - {liveKitUrl && ( - { - console.log('leaving room'); - router.push('/r'); - }} - > - - - - )} - - ); -}; diff --git a/components/Playground.tsx b/components/Playground.tsx index 6838e9a..4ecd715 100644 --- a/components/Playground.tsx +++ b/components/Playground.tsx @@ -52,11 +52,7 @@ export interface PlaygroundProps { const headerHeight = 56; export default function Playground({ - logo, - title, - githubLink, outputs, - showQR, themeColors, defaultColor, onConnect, @@ -327,7 +323,6 @@ export default function Playground({ roomState, themeColor, themeColors, - showQR, ]); let mobileTabs: PlaygroundTab[] = []; diff --git a/requirements.txt b/requirements.txt index 1320643..464c8b8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,4 +3,4 @@ livekit-api livekit-agents>=0.4.0 livekit-plugins-deepgram>=0.2.0 livekit-plugins-elevenlabs>=0.2.0 -openai \ No newline at end of file +openai diff --git a/kitt.py b/server/agent.py similarity index 99% rename from kitt.py rename to server/agent.py index ced1594..5ffaf7d 100644 --- a/kitt.py +++ b/server/agent.py @@ -11,6 +11,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. + import asyncio from datetime import datetime @@ -21,7 +22,7 @@ from livekit import rtc, agents from livekit.agents.tts import SynthesisEvent, SynthesisEventType -from chatgpt import ( +from llm import ( ChatGPTMessage, ChatGPTMessageRole, ChatGPTPlugin, @@ -40,6 +41,8 @@ Feel free to ask me anything — I'm here to help! Just start talking." + + # convert intro response to a stream async def intro_text_stream(sip: bool): if sip: @@ -215,4 +218,4 @@ async def job_request_cb(job_request: agents.JobRequest): ) worker = agents.Worker(request_handler=job_request_cb) - agents.run_app(worker) \ No newline at end of file + agents.run_app(worker) diff --git a/llm.py b/server/llm.py similarity index 100% rename from llm.py rename to server/llm.py diff --git a/utils/lksdk.ts b/utils/lksdk.ts deleted file mode 100644 index 8d35a75..0000000 --- a/utils/lksdk.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { useEffect, useState } from 'react'; - -export function useServerUrl(region?: string) { - const [serverUrl, setServerUrl] = useState(); - useEffect(() => { - let endpoint = `/api/url`; - if (region) { - endpoint += `?region=${region}`; - } - - fetch(endpoint).then(async (res) => { - if (res.ok) { - const body = await res.json(); - console.log(body); - setServerUrl(body.url); - } else { - throw Error('Error fetching server url, check server logs'); - } - }); - }); - return serverUrl; -} - -export function encodePassphrase(passphrase: string) { - return encodeURIComponent(passphrase); -} - -export function decodePassphrase(base64String: string) { - return decodeURIComponent(base64String); -} - -// DELETE LATER -export function generateRoomId(): string { - return `${randomString(4)}-${randomString(4)}-${randomString(4)}`; -} - -export function randomString(length: number): string { - let result = ''; - const characters = 'abcdefghijklmnopqrstuvwxyz0123456789'; - const charactersLength = characters.length; - for (let i = 0; i < length; i++) { - result += characters.charAt(Math.floor(Math.random() * charactersLength)); - } - return result; -} diff --git a/utils/server.ts b/utils/server.ts index 0a9e8f7..1ea1c31 100644 --- a/utils/server.ts +++ b/utils/server.ts @@ -6,9 +6,9 @@ export function getRoomClient(): RoomServiceClient { } export function getLiveKitURL(region?: string | string[]): string { - let targetKey = 'LIVEKIT_WS_URL'; + let targetKey = 'LIVEKIT_URL'; if (region && !Array.isArray(region)) { - targetKey = `LIVEKIT_WS_URL_${region}`.toUpperCase(); + targetKey = `LIVEKIT_URL_${region}`.toUpperCase(); } const url = process.env[targetKey]; if (!url) {