diff --git a/src/lib/core/entity/kernel-models.ts b/src/lib/core/entity/kernel-models.ts index adbc572..a7ffdcc 100644 --- a/src/lib/core/entity/kernel-models.ts +++ b/src/lib/core/entity/kernel-models.ts @@ -15,23 +15,11 @@ export const ConversationSchema = z.object({ export type TConversation = z.infer; -export const BaseMessageSchema = z.object({ +export const MessageSchema = z.object({ id: z.number(), content: z.string(), timestamp: z.string(), sender: z.string(), }); -export const UserMessageSchema = z.object({ - senderType: z.literal("user"), -}); - -export const AgentMessageSchema = z.object({ - senderType: z.literal("agent"), -}); - -export const MessageSchema = z.discriminatedUnion("senderType", [ - UserMessageSchema, - AgentMessageSchema -]); export type TMessage = z.infer; \ No newline at end of file diff --git a/src/lib/core/ports/primary/send-message-to-conversation-primary-ports.ts b/src/lib/core/ports/primary/send-message-to-conversation-primary-ports.ts index fd2306a..4bf35f5 100644 --- a/src/lib/core/ports/primary/send-message-to-conversation-primary-ports.ts +++ b/src/lib/core/ports/primary/send-message-to-conversation-primary-ports.ts @@ -1,14 +1,20 @@ -import { type TSendMessageToConversationErrorResponse, type TSendMessageToConversationProgressResponse, type TSendMessageToConversationRequest, type TSendMessageToConversationSuccessResponse } from "../../usecase-models/send-message-to-conversation-usecase-models"; +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { type Signal } from "../../entity/signals"; +import { + type TSendMessageToConversationErrorResponse, + type TSendMessageToConversationProgressResponse, + type TSendMessageToConversationRequest, + type TSendMessageToConversationSuccessResponse, +} from "../../usecase-models/send-message-to-conversation-usecase-models"; +import { type TSendMessageToConversationViewModel } from "../../view-models/send-message-to-conversation-view-model"; export interface SendMessageToConversationInputPort { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - presenter: SendMessageToConversationOutputPort; - execute(request: TSendMessageToConversationRequest): Promise; + execute(request: TSendMessageToConversationRequest): Promise; } -export interface SendMessageToConversationOutputPort { - response: TResponse; - presentProgress(progress: TSendMessageToConversationProgressResponse): void; - presentSuccess(success: TSendMessageToConversationSuccessResponse): void; - presentError(error: TSendMessageToConversationErrorResponse): void; -} \ No newline at end of file +export interface SendMessageToConversationOutputPort { + response: Signal; + presentProgress(usecaseProgressResponse: TSendMessageToConversationProgressResponse): Promise; + presentSuccess(usecaseSuccessResponse: TSendMessageToConversationSuccessResponse): Promise; + presentError(usecaseErrorResponse: TSendMessageToConversationErrorResponse): Promise; +} diff --git a/src/lib/core/usecase-models/send-message-to-conversation-usecase-models.ts b/src/lib/core/usecase-models/send-message-to-conversation-usecase-models.ts index 9864ff0..9933e22 100644 --- a/src/lib/core/usecase-models/send-message-to-conversation-usecase-models.ts +++ b/src/lib/core/usecase-models/send-message-to-conversation-usecase-models.ts @@ -1,30 +1,38 @@ -import { z } from 'zod' -import { MessageSchema } from '../entity/kernel-models' +import { z } from "zod"; +import { MessageSchema } from "../entity/kernel-models"; export const SendMessageToConversationRequestSchema = z.object({ - researchContextID: z.string(), - conversationID: z.string(), - message: MessageSchema, -}) + status: z.literal("request"), + researchContextID: z.string(), + conversationID: z.string(), + messageToSendContent: z.string(), + messageToSendTimestamp: z.string(), +}); -export type TSendMessageToConversationRequest = z.infer +export type TSendMessageToConversationRequest = z.infer; export const SendMessageToConversationSuccessResponseSchema = z.object({ - message: MessageSchema, - response: MessageSchema, -}) -export type TSendMessageToConversationSuccessResponse = z.infer + status: z.literal("success"), + message: MessageSchema, + response: MessageSchema, +}); +export type TSendMessageToConversationSuccessResponse = z.infer; export const SendMessageToConversationErrorResponseSchema = z.object({ - operation: z.string(), - message: z.string(), - context: z.any(), -}) -export type TSendMessageToConversationErrorResponse = z.infer + status: z.literal("error"), + operation: z.string(), + message: z.string(), + context: z.any().optional(), +}); +export type TSendMessageToConversationErrorResponse = z.infer; export const SendMessageToConversationProgressResponseSchema = z.object({ - message: MessageSchema, - progress: z.string(), - context: z.any(), -}) -export type TSendMessageToConversationProgressResponse = z.infer + status: z.literal("progress"), + message: MessageSchema, + progress: z.string(), + context: z.any().optional(), +}); +export type TSendMessageToConversationProgressResponse = z.infer; + +export const SendMessageToConversationResponseSchema = z.discriminatedUnion("status", [SendMessageToConversationSuccessResponseSchema, SendMessageToConversationErrorResponseSchema, SendMessageToConversationProgressResponseSchema]); +export type TSendMessageToConversationResponse = z.infer; diff --git a/src/lib/core/usecase/send-message-to-conversation-usecase.ts b/src/lib/core/usecase/send-message-to-conversation-usecase.ts index 4e4e82e..165acb8 100644 --- a/src/lib/core/usecase/send-message-to-conversation-usecase.ts +++ b/src/lib/core/usecase/send-message-to-conversation-usecase.ts @@ -1,88 +1,108 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ +import { type TMessage } from "../entity/kernel-models"; import { type SendMessageToConversationInputPort, type SendMessageToConversationOutputPort } from "../ports/primary/send-message-to-conversation-primary-ports"; import type AgentGatewayOutputPort from "../ports/secondary/agent-gateway-output-port"; import type ConversationGatewayOutputPort from "../ports/secondary/conversation-gateway-output-port"; import { type TSendMessageToConversationRequest } from "../usecase-models/send-message-to-conversation-usecase-models"; export default class BrowserSendMessageToConversationUseCase implements SendMessageToConversationInputPort { - presenter: SendMessageToConversationOutputPort; - agentGateway: AgentGatewayOutputPort; - conversationGateway: ConversationGatewayOutputPort; - constructor(presenter: SendMessageToConversationOutputPort, agentGateway: AgentGatewayOutputPort, conversationGateway: ConversationGatewayOutputPort) { - this.presenter = presenter; - this.agentGateway = agentGateway - this.conversationGateway = conversationGateway; + presenter: SendMessageToConversationOutputPort; + agentGateway: AgentGatewayOutputPort; + conversationGateway: ConversationGatewayOutputPort; + constructor(presenter: SendMessageToConversationOutputPort, agentGateway: AgentGatewayOutputPort, conversationGateway: ConversationGatewayOutputPort) { + this.presenter = presenter; + this.agentGateway = agentGateway; + this.conversationGateway = conversationGateway; + } + + async execute(request: TSendMessageToConversationRequest): Promise { + + const messageToSend: TMessage = { + id: -1, // this should be assigned by KP at some point + content: request.messageToSendContent, + sender: "User", // this should be obtained from the auth gateway at some point + timestamp: request.messageToSendTimestamp, } - async execute(request: TSendMessageToConversationRequest): Promise { - // TODO: check if conversation exists - // send incoming message to the conversation - const registerIncomingMessageDTO = await this.conversationGateway.sendMessageToConversation(request.conversationID, request.message) + const dummyResponse: TMessage = { + id: -1, + content: "Dummy response", + sender: "Dummy", + timestamp: new Date().toDateString(), + }; - if (!registerIncomingMessageDTO.success) { - this.presenter.presentError( - { - operation: "usecase#send-message-to-conversation", - message: "", - context: {} - } - ) - return; - } + await this.presenter.presentSuccess({ + status: "success", + message: messageToSend, + response: dummyResponse, + }); - const prepareMessageDTO = await this.agentGateway.prepareMessageContext(request.researchContextID, request.conversationID, request.message) - if (!prepareMessageDTO.success) { - this.presenter.presentError( - { - operation: "usecase#send-message-to-conversation", - message: "", - context: {} - } - ) - return; - } + // TODO: finish secondary side, then come back here + //// TODO: check if conversation exists + //// send incoming message to the conversation + //const registerIncomingMessageDTO = await this.conversationGateway.sendMessageToConversation(request.conversationID, request.message); - this.presenter.presentProgress({ - message: request.message, - progress: "Sending message to agent", - context: prepareMessageDTO - }) + //if (!registerIncomingMessageDTO.success) { + //this.presenter.presentError({ + //status: "error", + //operation: "usecase#send-message-to-conversation", + //message: "", + //context: {}, + //}); + //return; + //} - const sendMessageToAgentDTO = await this.agentGateway.sendMessage(prepareMessageDTO.data, request.message) - if (!sendMessageToAgentDTO.success) { - this.presenter.presentError( - { - operation: "usecase#send-message-to-conversation", - message: "", - context: {} - } - ) - return; - } + //const prepareMessageDTO = await this.agentGateway.prepareMessageContext(request.researchContextID, request.conversationID, request.message); + //if (!prepareMessageDTO.success) { + //this.presenter.presentError({ + //status: "error", + //operation: "usecase#send-message-to-conversation", + //message: "", + //context: {}, + //}); + //return; + //} - this.presenter.presentProgress({ - message: request.message, - progress: "Agent replied to the message", - context: sendMessageToAgentDTO - }) + //this.presenter.presentProgress({ + //status: "progress", + //message: request.message, + //progress: "Sending message to agent", + //context: prepareMessageDTO, + //}); - const registerOutgoingMessageDTO = await this.conversationGateway.sendMessageToConversation(request.conversationID, sendMessageToAgentDTO.data) - if (!registerOutgoingMessageDTO.success) { - this.presenter.presentError( - { - operation: "usecase#send-message-to-conversation", - message: "", - context: {} - } - ) - return; - } + //const sendMessageToAgentDTO = await this.agentGateway.sendMessage(prepareMessageDTO.data, request.message); + //if (!sendMessageToAgentDTO.success) { + //this.presenter.presentError({ + //status: "error", + //operation: "usecase#send-message-to-conversation", + //message: "", + //context: {}, + //}); + //return; + //} - this.presenter.presentSuccess({ - message: request.message, - response: sendMessageToAgentDTO.data - }) - } -} \ No newline at end of file + //this.presenter.presentProgress({ + //status: "progress", + //message: request.message, + //progress: "Agent replied to the message", + //context: sendMessageToAgentDTO, + //}); + + //const registerOutgoingMessageDTO = await this.conversationGateway.sendMessageToConversation(request.conversationID, sendMessageToAgentDTO.data); + //if (!registerOutgoingMessageDTO.success) { + //this.presenter.presentError({ + //status: "error", + //operation: "usecase#send-message-to-conversation", + //message: "", + //context: {}, + //}); + //return; + //} + + //this.presenter.presentSuccess({ + //status: "success", + //message: request.message, + //response: sendMessageToAgentDTO.data, + //}); + } +} diff --git a/src/lib/core/view-models/send-message-to-conversation-view-model.ts b/src/lib/core/view-models/send-message-to-conversation-view-model.ts index c39458a..30ae52a 100644 --- a/src/lib/core/view-models/send-message-to-conversation-view-model.ts +++ b/src/lib/core/view-models/send-message-to-conversation-view-model.ts @@ -1,42 +1,39 @@ -import { z } from 'zod' -import { MessageSchema } from '../entity/kernel-models' +import { z } from "zod"; +import { MessageSchema } from "../entity/kernel-models"; export const SendMessageToConversationRequestViewModelSchema = z.object({ - status: z.enum(['request']), - conversationId: z.string(), - message: z.string(), -}) + status: z.enum(["request"]), + conversationID: z.string(), + message: z.string(), +}); -export type TSendMessageToConversationRequestViewModel = z.infer +export type TSendMessageToConversationRequestViewModel = z.infer; export const SendMessageToConversationSuccessViewModelSchema = z.object({ - status: z.enum(['success']), - message: MessageSchema, - response: MessageSchema, -}) -export type TSendMessageToConversationSuccessViewModel = z.infer + status: z.enum(["success"]), + message: MessageSchema, + response: MessageSchema, +}); +export type TSendMessageToConversationSuccessViewModel = z.infer; export const SendMessageToConversationErrorViewModelSchema = z.object({ - status: z.enum(['error']), - message: z.string(), - context: z.any(), -}) -export type TSendMessageToConversationErrorViewModel = z.infer + status: z.enum(["error"]), + message: z.string(), + context: z.any(), +}); +export type TSendMessageToConversationErrorViewModel = z.infer; export const SendMessageToConversationProgressViewModelSchema = z.object({ - status: z.enum(['progress']), - message: MessageSchema, - progressReport: z.string(), -}) -export type TSendMessageToConversationProgressViewModel = z.infer - -export const SendMessageToConversationViewModelSchema = z.discriminatedUnion('status', [ - SendMessageToConversationRequestViewModelSchema, - SendMessageToConversationSuccessViewModelSchema, - SendMessageToConversationErrorViewModelSchema, - SendMessageToConversationProgressViewModelSchema, -]) -export type TSendMessageToConversationViewModel = z.infer - - - + status: z.enum(["progress"]), + message: MessageSchema, + progressReport: z.string(), +}); +export type TSendMessageToConversationProgressViewModel = z.infer; + +export const SendMessageToConversationViewModelSchema = z.discriminatedUnion("status", [ + SendMessageToConversationRequestViewModelSchema, + SendMessageToConversationSuccessViewModelSchema, + SendMessageToConversationErrorViewModelSchema, + SendMessageToConversationProgressViewModelSchema, +]); +export type TSendMessageToConversationViewModel = z.infer; diff --git a/src/lib/infrastructure/client/controller/browser-send-message-to-conversation-controller.ts b/src/lib/infrastructure/client/controller/browser-send-message-to-conversation-controller.ts index a3c4ab0..f6bddc0 100644 --- a/src/lib/infrastructure/client/controller/browser-send-message-to-conversation-controller.ts +++ b/src/lib/infrastructure/client/controller/browser-send-message-to-conversation-controller.ts @@ -1,17 +1,34 @@ import { injectable } from "inversify"; import { Signal } from "~/lib/core/entity/signals"; +import { TSendMessageToConversationRequest } from "~/lib/core/usecase-models/send-message-to-conversation-usecase-models"; import { TSendMessageToConversationViewModel } from "~/lib/core/view-models/send-message-to-conversation-view-model"; +import clientContainer from "../config/ioc/client-container"; +import { SendMessageToConversationInputPort } from "~/lib/core/ports/primary/send-message-to-conversation-primary-ports"; +import { USECASE_FACTORY } from "../config/ioc/client-ioc-symbols"; export interface TBrowserSendMessageToConversationControllerParameters { - response: Signal; - researchContextId: string; - conversationId: string; - message: string; + response: Signal; + researchContextID: string; + conversationID: string; + messageToSendContent: string; + messageToSendTimestamp: string; } @injectable() export default class BrowserSendMessageToConversationController { - async execute(params: TBrowserSendMessageToConversationControllerParameters): Promise { - throw new Error("Method not implemented."); - } + async execute(controllerParameters: TBrowserSendMessageToConversationControllerParameters): Promise { + const { response, researchContextID, conversationID, messageToSendContent, messageToSendTimestamp } = controllerParameters; + + const request: TSendMessageToConversationRequest = { + status: "request", + researchContextID: researchContextID, + conversationID: conversationID, + messageToSendContent: messageToSendContent, + messageToSendTimestamp: messageToSendTimestamp, + }; + + const usecaseFactory = clientContainer.get<(response: Signal) => SendMessageToConversationInputPort>(USECASE_FACTORY.SEND_MESSAGE_TO_CONVERSATION); + const usecase = usecaseFactory(response); + await usecase.execute(request); + } } diff --git a/src/lib/infrastructure/client/presenter/browser-send-message-to-conversation-presenter.ts b/src/lib/infrastructure/client/presenter/browser-send-message-to-conversation-presenter.ts index 05460c6..6903559 100644 --- a/src/lib/infrastructure/client/presenter/browser-send-message-to-conversation-presenter.ts +++ b/src/lib/infrastructure/client/presenter/browser-send-message-to-conversation-presenter.ts @@ -1,48 +1,42 @@ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ -import { inject, injectable } from "inversify"; -import { ILogObj, Logger } from "tslog"; +import { type ILogObj, type Logger } from "tslog"; import { type Signal } from "~/lib/core/entity/signals"; import { type SendMessageToConversationOutputPort } from "~/lib/core/ports/primary/send-message-to-conversation-primary-ports"; import { type TSendMessageToConversationSuccessResponse, type TSendMessageToConversationErrorResponse, type TSendMessageToConversationProgressResponse } from "~/lib/core/usecase-models/send-message-to-conversation-usecase-models"; import { type TSendMessageToConversationViewModel } from "~/lib/core/view-models/send-message-to-conversation-view-model"; -import { UTILS } from "../config/ioc/client-ioc-symbols"; -@injectable() -export default class BrowserSendMessageToConversationPresenter implements SendMessageToConversationOutputPort> { - response: Signal; - private logger: Logger +export default class BrowserSendMessageToConversationPresenter implements SendMessageToConversationOutputPort { + logger: Logger; + response: Signal; + constructor(response: Signal, loggerFactory: (module: string) => Logger) { + this.response = response; + this.logger = loggerFactory("BrowserSendMessageToConversationPresenter"); + } - constructor( - response: Signal, - @inject(UTILS.LOGGER_FACTORY) private loggerFactory: (module: string) => Logger - ) { - this.response = response; - this.logger = this.loggerFactory("BrowserSendMessageToConversationPresenter"); - } + async presentProgress(usecaseProgressResponse: TSendMessageToConversationProgressResponse): Promise { + this.logger.info({ usecaseProgressResponse }, `Sending message to conversation progress: ${usecaseProgressResponse.progress}`); + this.response.update({ + status: "progress", + message: usecaseProgressResponse.message, + progressReport: usecaseProgressResponse.progress, + }); + } - presentProgress(progress: TSendMessageToConversationProgressResponse): void{ - this.logger.info(progress.context); - this.response.update({ - status: "progress", - message: progress.message, - progressReport: progress.progress, - }); - } + async presentError(usecaseErrorResponse: TSendMessageToConversationErrorResponse): Promise { + this.logger.error({ usecaseErrorResponse }, `Failed to send message to conversation: ${usecaseErrorResponse.message}`); + this.response.update({ + status: "error", + message: usecaseErrorResponse.message, + context: usecaseErrorResponse.context, + }); + } - presentSuccess(success: TSendMessageToConversationSuccessResponse): void { - this.response.update({ - status: "success", - message: success.message, - response: success.response, - }); - } - - presentError(error: TSendMessageToConversationErrorResponse): void { - this.response.update({ - status: "error", - message: error.message, - context: error.context, - }); - } - -} \ No newline at end of file + async presentSuccess(usecaseSuccessResponse: TSendMessageToConversationSuccessResponse): Promise { + this.logger.info({ usecaseSuccessResponse }, `Successfully sent message to conversation.`); + this.response.update({ + status: "success", + message: usecaseSuccessResponse.message, + response: usecaseSuccessResponse.response, + }); + } +} diff --git a/src/lib/infrastructure/server/controller/send-message-to-conversation-controller.ts b/src/lib/infrastructure/server/controller/send-message-to-conversation-controller.ts deleted file mode 100644 index 49eb006..0000000 --- a/src/lib/infrastructure/server/controller/send-message-to-conversation-controller.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { type Signal } from "~/lib/core/entity/signals"; -import { type SendMessageToConversationOutputPort } from "~/lib/core/ports/primary/send-message-to-conversation-primary-ports"; -import { type TSendMessageToConversationSuccessResponse, type TSendMessageToConversationErrorResponse, type TSendMessageToConversationProgressResponse } from "~/lib/core/usecase-models/send-message-to-conversation-usecase-models"; -import { type TSendMessageToConversationViewModel } from "~/lib/core/view-models/send-message-to-conversation-view-model"; - -export default class BrowserSendMessageToConversationPresenter implements SendMessageToConversationOutputPort> { - response: Signal; - constructor(response: Signal) { - this.response = response; - } - presentProgress(response: TSendMessageToConversationProgressResponse): void { - throw new Error("Method not implemented."); - } - presentSuccess(response: TSendMessageToConversationSuccessResponse): void { - throw new Error("Method not implemented."); - } - presentError(response: TSendMessageToConversationErrorResponse): void { - throw new Error("Method not implemented."); - } - -} \ No newline at end of file