From 20b0adb4d31ba7364f56ff2ae0336c2cb26d07b2 Mon Sep 17 00:00:00 2001 From: comoser Date: Fri, 8 Sep 2023 14:58:10 +0100 Subject: [PATCH] feat: add model name to chat window --- packages/api/src/ai/ai.controller.ts | 16 ++++- packages/api/src/ai/ai.module.ts | 2 + .../src/ai/usecases/find-ai-model.usecase.ts | 20 ++++++ packages/web-ui/api/endpoints.ts | 1 + packages/web-ui/app/rooms/[room]/layout.tsx | 68 ++++++++++++++++--- packages/web-ui/app/rooms/[room]/page.tsx | 1 + .../web-ui/app/rooms/[room]/settings/page.tsx | 29 ++++---- packages/web-ui/components/room-header.tsx | 10 ++- 8 files changed, 119 insertions(+), 28 deletions(-) create mode 100644 packages/api/src/ai/usecases/find-ai-model.usecase.ts diff --git a/packages/api/src/ai/ai.controller.ts b/packages/api/src/ai/ai.controller.ts index f4d32a4..a4924fe 100644 --- a/packages/api/src/ai/ai.controller.ts +++ b/packages/api/src/ai/ai.controller.ts @@ -1,15 +1,19 @@ import { AiModelResponseDto } from '@/ai/dtos/ai-model.response.dto'; +import { FindAiModelUsecase } from '@/ai/usecases/find-ai-model.usecase'; import { FindAiModelsUsecase } from '@/ai/usecases/find-ai-models.usecase'; import { ClerkAuthGuard } from '@/auth/guards/clerk/clerk.auth.guard'; import { ApiClerkAuthHeaders } from '@/auth/guards/clerk/open-api-clerk-headers.decorator'; -import { Controller, Get, UseGuards } from '@nestjs/common'; +import { Controller, Get, Param, UseGuards } from '@nestjs/common'; import { ApiOkResponse, ApiOperation, ApiTags } from '@nestjs/swagger'; @Controller({ path: 'ai', version: '1' }) @ApiTags('ai') @UseGuards(ClerkAuthGuard) export class AiController { - constructor(private readonly findAiModelsUsecase: FindAiModelsUsecase) {} + constructor( + private readonly findAiModelsUsecase: FindAiModelsUsecase, + private readonly findAiModelUsecase: FindAiModelUsecase + ) {} @Get('ai-models') @ApiClerkAuthHeaders() @@ -18,4 +22,12 @@ export class AiController { async findAiModels(): Promise { return this.findAiModelsUsecase.execute(); } + + @Get('ai-models/:id') + @ApiClerkAuthHeaders() + @ApiOkResponse({ type: AiModelResponseDto }) + @ApiOperation({ description: 'Get an AI Model' }) + async findAiModel(@Param('id') id: string): Promise { + return this.findAiModelUsecase.execute(id); + } } diff --git a/packages/api/src/ai/ai.module.ts b/packages/api/src/ai/ai.module.ts index 4bad8da..f0d6594 100644 --- a/packages/api/src/ai/ai.module.ts +++ b/packages/api/src/ai/ai.module.ts @@ -12,6 +12,7 @@ import { ToolService } from '@/ai/services/tool.service'; import { VectorDbService } from '@/ai/services/vector-db.service'; import { AdminAddAiModelUsecase } from '@/ai/usecases/admin-add-ai-model.usecase'; import { AdminFindAiModelsUsecase } from '@/ai/usecases/admin-find-ai-models.usecase'; +import { FindAiModelUsecase } from '@/ai/usecases/find-ai-model.usecase'; import { FindAiModelsUsecase } from '@/ai/usecases/find-ai-models.usecase'; import { AppConfigModule } from '@/app-config/app-config.module'; import { ClerkAuthGuard } from '@/auth/guards/clerk/clerk.auth.guard'; @@ -41,6 +42,7 @@ import { ScheduleModule } from '@nestjs/schedule'; AdminAddAiModelUsecase, AdminFindAiModelsUsecase, FindAiModelsUsecase, + FindAiModelUsecase, // Private services AiModelsRepository, MemoryService, diff --git a/packages/api/src/ai/usecases/find-ai-model.usecase.ts b/packages/api/src/ai/usecases/find-ai-model.usecase.ts new file mode 100644 index 0000000..d2eba2c --- /dev/null +++ b/packages/api/src/ai/usecases/find-ai-model.usecase.ts @@ -0,0 +1,20 @@ +import { AiModelsRepository } from '@/ai/ai-models.repository'; +import { AiModelResponseDto } from '@/ai/dtos/ai-model.response.dto'; +import { InternalServerErrorException } from '@/common/exceptions/internal-server-error.exception'; +import { Usecase } from '@/common/types/usecase'; +import { AiModelResponseSchema } from '@/contract/ai/ai-model.response.dto'; +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class FindAiModelUsecase implements Usecase { + constructor(private readonly aiModelsRepository: AiModelsRepository) {} + + async execute(id: string): Promise { + try { + const aiModel = await this.aiModelsRepository.findAiModel(id); + return AiModelResponseSchema.parse(aiModel); + } catch (e) { + throw new InternalServerErrorException(e.message); + } + } +} diff --git a/packages/web-ui/api/endpoints.ts b/packages/web-ui/api/endpoints.ts index 0a29746..af6f5c4 100644 --- a/packages/web-ui/api/endpoints.ts +++ b/packages/web-ui/api/endpoints.ts @@ -36,6 +36,7 @@ const Endpoints = { }, ai: { getAiModels: () => '/ai/ai-models', + getAiModel: (aiModelId: string) => `/ai/ai-models/${aiModelId}`, }, }; export default Endpoints; diff --git a/packages/web-ui/app/rooms/[room]/layout.tsx b/packages/web-ui/app/rooms/[room]/layout.tsx index ce85786..ce43626 100644 --- a/packages/web-ui/app/rooms/[room]/layout.tsx +++ b/packages/web-ui/app/rooms/[room]/layout.tsx @@ -1,9 +1,11 @@ import '@/styles/globals.css'; import { ReactNode } from 'react'; -import { cookies } from 'next/headers'; import { redirect } from 'next/navigation'; import { apiClient } from '@/api/apiClient'; import Endpoints from '@/api/endpoints'; +import { AiModelResponseDto } from '@/contract/ai/ai-model.response.dto.d'; +import { ChatResponseDto } from '@/contract/chats/chat.response.dto.d'; +import { RoomResponseDto } from '@/contract/rooms/room.response.dto.d'; import { auth } from '@clerk/nextjs'; import { RoomHeader } from '@/components/room-header'; @@ -12,16 +14,61 @@ interface RootLayoutProps { children: React.ReactNode; } -const getRoom = async (roomId: string) => { +const getRoom = async ( + roomId: string +): Promise => { + const { sessionId, getToken } = auth(); + try { - const nextCookies = cookies(); - const clerkJwtToken = nextCookies.get('__session'); - const { sessionId } = auth(); const res = await apiClient({ url: Endpoints.rooms.getRoomById(roomId), options: { method: 'GET', cache: 'no-store' }, sessionId: sessionId ?? '', - jwtToken: clerkJwtToken?.value ?? '', + jwtToken: (await getToken()) ?? '', + }); + if (!res.ok) { + return; + } + + return res.json(); + } catch (e) { + console.log(e); + } +}; + +const getChat = async ( + roomId: string +): Promise => { + const { sessionId, getToken } = auth(); + + try { + const res = await apiClient({ + url: Endpoints.chats.getChat(roomId), + options: { method: 'GET' }, + sessionId: sessionId ?? '', + jwtToken: (await getToken()) ?? '', + }); + if (!res.ok) { + return; + } + + return res.json(); + } catch (e) { + console.log(e); + } +}; + +const getAiModel = async ( + aiModelId: string +): Promise => { + const { sessionId, getToken } = auth(); + + try { + const res = await apiClient({ + url: Endpoints.ai.getAiModel(aiModelId), + options: { method: 'GET' }, + sessionId: sessionId ?? '', + jwtToken: (await getToken()) ?? '', }); if (!res.ok) { return; @@ -40,7 +87,12 @@ export default async function RoomLayout({ children: ReactNode; params: { room: string }; }) { - const room = await getRoom(params.room); + const [room, chat] = await Promise.all([ + getRoom(params.room), + getChat(params.room), + ]); + + const aiModel = await getAiModel(chat?.aiModelId ?? ''); if (!room) { redirect('/'); @@ -48,7 +100,7 @@ export default async function RoomLayout({ return (
- +
{children}
); diff --git a/packages/web-ui/app/rooms/[room]/page.tsx b/packages/web-ui/app/rooms/[room]/page.tsx index ea818e5..6384f8a 100644 --- a/packages/web-ui/app/rooms/[room]/page.tsx +++ b/packages/web-ui/app/rooms/[room]/page.tsx @@ -44,6 +44,7 @@ const getChat = async ( console.log(e); } }; + const getOwner = async ( sessionId: string, clerkJwtToken: string, diff --git a/packages/web-ui/app/rooms/[room]/settings/page.tsx b/packages/web-ui/app/rooms/[room]/settings/page.tsx index 54fa79a..fe5265f 100644 --- a/packages/web-ui/app/rooms/[room]/settings/page.tsx +++ b/packages/web-ui/app/rooms/[room]/settings/page.tsx @@ -60,7 +60,7 @@ const getOwner = async ( sessionId: string, clerkJwtToken: string, ownerId: string -) => { +): Promise => { try { const res = await apiClient({ url: Endpoints.users.getUser(ownerId), @@ -74,7 +74,10 @@ const getOwner = async ( } }; -const getAllUsers = async (sessionId: string, clerkJwtToken: string) => { +const getAllUsers = async ( + sessionId: string, + clerkJwtToken: string +): Promise => { try { const res = await apiClient({ url: Endpoints.users.getUsers(), @@ -92,7 +95,7 @@ const getRoomMembers = async ( sessionId: string, clerkJwtToken: string, members: string[] -) => { +): Promise => { try { const res = await apiClient({ url: Endpoints.users.getUsers(members), @@ -146,13 +149,7 @@ export default async function Settings({ params.room ); - const [members, allUsers, owner] = await Promise.all< - [ - Promise, - Promise, - Promise - ] - >([ + const [members = [], allUsers = [], owner] = await Promise.all([ getRoomMembers(sessionId!, clerkJwtToken!.value, room.members), getAllUsers(sessionId!, clerkJwtToken!.value), getOwner(sessionId!, clerkJwtToken!.value, room.ownerId), @@ -164,7 +161,7 @@ export default async function Settings({ const membersSearchList = getUserList(members); - const isOwner = owner.id === userId; + const isOwner = owner?.id === userId; return (
@@ -180,22 +177,22 @@ export default async function Settings({

Room owner

- +

{`Name: ${ - owner.firstName && owner.lastName + owner?.firstName && owner.lastName ? `${owner.firstName} ${owner.lastName}` : ' - ' }`}

{`Email: ${ - owner.emailAddresses.find( + owner?.emailAddresses.find( (email) => owner.primaryEmailAddressId === email.id )?.emailAddress ?? ' - ' }`}

-

{`Username: ${owner.username}`}

+

{`Username: ${owner?.username}`}

@@ -210,7 +207,7 @@ export default async function Settings({ diff --git a/packages/web-ui/components/room-header.tsx b/packages/web-ui/components/room-header.tsx index bf405c4..87fe877 100644 --- a/packages/web-ui/components/room-header.tsx +++ b/packages/web-ui/components/room-header.tsx @@ -3,6 +3,7 @@ import React, { useEffect } from 'react'; import Link from 'next/link'; import { usePathname, useRouter } from 'next/navigation'; +import { AiModelResponseDto } from '@/contract/ai/ai-model.response.dto.d'; import { ArrowLeft, Command, Settings } from 'lucide-react'; import { useBrowserInfo } from '@/hooks/use-browser-info'; @@ -15,9 +16,10 @@ import { interface RoomHeaderProps { id: string; name: string; + llmModel: AiModelResponseDto; } -export function RoomHeader({ id, name }: RoomHeaderProps) { +export function RoomHeader({ id, name, llmModel }: RoomHeaderProps) { const router = useRouter(); const pathname = usePathname(); const { isMacUser } = useBrowserInfo(); @@ -36,6 +38,7 @@ export function RoomHeader({ id, name }: RoomHeaderProps) { return () => document.removeEventListener('keydown', down); } }, [isSettingsPage]); + return (
{isSettingsPage ? ( @@ -47,7 +50,10 @@ export function RoomHeader({ id, name }: RoomHeaderProps) { {name} | settings ) : ( -

{name}

+ <> +

{name}

+

{`${llmModel.chatLlmName} - ${llmModel.alias}`}

+ )} {!isSettingsPage && (