-
Notifications
You must be signed in to change notification settings - Fork 115
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
This reverts commit 7bf8f76.
- Loading branch information
Showing
3 changed files
with
248 additions
and
1 deletion.
There are no files selected for viewing
135 changes: 135 additions & 0 deletions
135
front/pages/api/v1/w/[wId]/assistant/[cId]/messages/[mId]/events.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
import { NextApiRequest, NextApiResponse } from "next"; | ||
|
||
import { getConversation } from "@app/lib/api/assistant/conversation"; | ||
import { getMessagesEvents } from "@app/lib/api/assistant/pubsub"; | ||
import { Authenticator, getAPIKey } from "@app/lib/auth"; | ||
import { ReturnedAPIErrorType } from "@app/lib/error"; | ||
import { apiError, withLogging } from "@app/logger/withlogging"; | ||
import { isAgentMessageType } from "@app/types/assistant/conversation"; | ||
|
||
async function handler( | ||
req: NextApiRequest, | ||
res: NextApiResponse<ReturnedAPIErrorType> | ||
): Promise<void> { | ||
const keyRes = await getAPIKey(req); | ||
if (keyRes.isErr()) { | ||
return apiError(req, res, keyRes.error); | ||
} | ||
|
||
const { auth, keyWorkspaceId } = await Authenticator.fromKey( | ||
keyRes.value, | ||
req.query.wId as string | ||
); | ||
|
||
if (!auth.isBuilder() || keyWorkspaceId !== req.query.wId) { | ||
return apiError(req, res, { | ||
status_code: 400, | ||
api_error: { | ||
type: "invalid_request_error", | ||
message: "The Assistant API is only available on your own workspace.", | ||
}, | ||
}); | ||
} | ||
|
||
const owner = auth.workspace(); | ||
if (!owner) { | ||
return apiError(req, res, { | ||
status_code: 404, | ||
api_error: { | ||
type: "workspace_not_found", | ||
message: "The workspace you're trying to modify was not found.", | ||
}, | ||
}); | ||
} | ||
|
||
const conversation = await getConversation(auth, req.query.cId as string); | ||
|
||
if (!conversation) { | ||
return apiError(req, res, { | ||
status_code: 404, | ||
api_error: { | ||
type: "conversation_not_found", | ||
message: "The conversation you're trying to access was not found.", | ||
}, | ||
}); | ||
} | ||
|
||
if (!(typeof req.query.mId === "string")) { | ||
return apiError(req, res, { | ||
status_code: 400, | ||
api_error: { | ||
type: "invalid_request_error", | ||
message: "Invalid query parameters, `cId` (string) is required.", | ||
}, | ||
}); | ||
} | ||
|
||
const messageId = req.query.mId; | ||
|
||
const message = conversation.content | ||
.flat() | ||
.find((message) => message.sId === messageId); | ||
|
||
if (!message) { | ||
return apiError(req, res, { | ||
status_code: 404, | ||
api_error: { | ||
type: "message_not_found", | ||
message: "The message you're trying to access was not found.", | ||
}, | ||
}); | ||
} | ||
if (!isAgentMessageType(message)) { | ||
return apiError(req, res, { | ||
status_code: 400, | ||
api_error: { | ||
type: "invalid_request_error", | ||
message: "Events are only available for agent messages.", | ||
}, | ||
}); | ||
} | ||
|
||
const lastEventId = req.query.lastEventId || null; | ||
if (lastEventId && typeof lastEventId !== "string") { | ||
return apiError(req, res, { | ||
status_code: 400, | ||
api_error: { | ||
type: "invalid_request_error", | ||
message: | ||
"Invalid query parameters, `lastEventId` should be string if specified.", | ||
}, | ||
}); | ||
} | ||
|
||
switch (req.method) { | ||
case "GET": | ||
const eventStream = getMessagesEvents(messageId, lastEventId); | ||
|
||
res.writeHead(200, { | ||
"Content-Type": "text/event-stream", | ||
"Cache-Control": "no-cache", | ||
Connection: "keep-alive", | ||
}); | ||
res.flushHeaders(); | ||
|
||
for await (const event of eventStream) { | ||
res.write(`data: ${JSON.stringify(event)}\n\n`); | ||
// @ts-expect-error - We need it for streaming but it does not exists in the types. | ||
res.flush(); | ||
} | ||
|
||
res.status(200).end(); | ||
return; | ||
|
||
default: | ||
return apiError(req, res, { | ||
status_code: 405, | ||
api_error: { | ||
type: "method_not_supported_error", | ||
message: "The method passed is not supported, GET is expected.", | ||
}, | ||
}); | ||
} | ||
} | ||
|
||
export default withLogging(handler, true); |
112 changes: 112 additions & 0 deletions
112
front/pages/api/v1/w/[wId]/assistant/[cId]/messages/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
import { isLeft } from "fp-ts/lib/Either"; | ||
import * as t from "io-ts"; | ||
import * as reporter from "io-ts-reporters"; | ||
import { NextApiRequest, NextApiResponse } from "next"; | ||
|
||
import { getConversation } from "@app/lib/api/assistant/conversation"; | ||
import { postUserMessageWithPubSub } from "@app/lib/api/assistant/pubsub"; | ||
import { Authenticator, getAPIKey } from "@app/lib/auth"; | ||
import { ReturnedAPIErrorType } from "@app/lib/error"; | ||
import { apiError, withLogging } from "@app/logger/withlogging"; | ||
import { UserMessageType } from "@app/types/assistant/conversation"; | ||
|
||
export type PostMessagesResponseBody = { | ||
message: UserMessageType; | ||
}; | ||
|
||
export const PostMessagesRequestBodySchema = t.type({ | ||
content: t.string, | ||
mentions: t.array( | ||
t.union([ | ||
t.type({ configurationId: t.string }), | ||
t.type({ | ||
provider: t.string, | ||
providerId: t.string, | ||
}), | ||
]) | ||
), | ||
context: t.type({ | ||
timezone: t.string, | ||
username: t.string, | ||
fullName: t.union([t.string, t.null]), | ||
email: t.union([t.string, t.null]), | ||
profilePictureUrl: t.union([t.string, t.null]), | ||
}), | ||
}); | ||
|
||
async function handler( | ||
req: NextApiRequest, | ||
res: NextApiResponse<{ message: UserMessageType } | ReturnedAPIErrorType> | ||
): Promise<void> { | ||
const keyRes = await getAPIKey(req); | ||
if (keyRes.isErr()) { | ||
return apiError(req, res, keyRes.error); | ||
} | ||
|
||
const { auth, keyWorkspaceId } = await Authenticator.fromKey( | ||
keyRes.value, | ||
req.query.wId as string | ||
); | ||
|
||
if (!auth.isBuilder() || keyWorkspaceId !== req.query.wId) { | ||
return apiError(req, res, { | ||
status_code: 400, | ||
api_error: { | ||
type: "invalid_request_error", | ||
message: "The Assistant API is only available on your own workspace.", | ||
}, | ||
}); | ||
} | ||
|
||
const conversation = await getConversation(auth, req.query.cId as string); | ||
if (!conversation) { | ||
return apiError(req, res, { | ||
status_code: 404, | ||
api_error: { | ||
type: "conversation_not_found", | ||
message: "Conversation not found.", | ||
}, | ||
}); | ||
} | ||
|
||
switch (req.method) { | ||
case "POST": | ||
const bodyValidation = PostMessagesRequestBodySchema.decode(req.body); | ||
if (isLeft(bodyValidation)) { | ||
const pathError = reporter.formatValidationErrors(bodyValidation.left); | ||
return apiError(req, res, { | ||
status_code: 400, | ||
api_error: { | ||
type: "invalid_request_error", | ||
message: `Invalid request body: ${pathError}`, | ||
}, | ||
}); | ||
} | ||
|
||
const { content, context, mentions } = bodyValidation.right; | ||
|
||
const messageRes = await postUserMessageWithPubSub(auth, { | ||
conversation, | ||
content, | ||
mentions, | ||
context, | ||
}); | ||
if (messageRes.isErr()) { | ||
return apiError(req, res, messageRes.error); | ||
} | ||
|
||
res.status(200).json({ message: messageRes.value }); | ||
return; | ||
|
||
default: | ||
return apiError(req, res, { | ||
status_code: 405, | ||
api_error: { | ||
type: "method_not_supported_error", | ||
message: "The method passed is not supported, POST is expected.", | ||
}, | ||
}); | ||
} | ||
} | ||
|
||
export default withLogging(handler); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters