Skip to content

Commit

Permalink
Working end to end but a lot of things to review / clean up.
Browse files Browse the repository at this point in the history
  • Loading branch information
lasryaric committed Nov 15, 2023
1 parent 7e4981c commit 40ceb92
Show file tree
Hide file tree
Showing 12 changed files with 317 additions and 11 deletions.
4 changes: 2 additions & 2 deletions front/components/assistant/conversation/ContentFragment.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ export function ContentFragment({
user={user}
conversationId={conversation.sId}
messageId={message.sId}
pictureUrl={""}
name={"sisi"}
pictureUrl={message.context.profilePictureUrl}
name={message.context.fullName}
enableEmojis={false}
reactions={[]}
>
Expand Down
13 changes: 10 additions & 3 deletions front/components/assistant/conversation/InputBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ import { mutate } from "swr";
import { AssistantPicker } from "@app/components/assistant/AssistantPicker";
import { GenerationContext } from "@app/components/assistant/conversation/GenerationContextProvider";
import { SendNotificationsContext } from "@app/components/sparkle/Notification";
import { PostContentFragmentRequestBody } from "@app/lib/api/assistant/types";
import { compareAgentsForSort } from "@app/lib/assistant";
import { useAgentConfigurations } from "@app/lib/swr";
import { classNames, subFilter } from "@app/lib/utils";
Expand Down Expand Up @@ -381,8 +380,14 @@ export function AssistantInputBar({

content = content.trim();
content = content.replace(/\u200B/g, "");
let contentFragment: PostContentFragmentRequestBody | undefined =
undefined;
let contentFragment:
| {
title: string;
content: string;
url: string | null;
contentType: string;
}
| undefined = undefined;
if (contentFragmentBody && contentFragmentFilename) {
contentFragment = {
title: contentFragmentFilename,
Expand Down Expand Up @@ -496,6 +501,8 @@ export function AssistantInputBar({
ref={fileInputRef}
style={{ display: "none" }}
onChange={async (e) => {
// focus on the input text after the file selection interaction is over
inputRef.current?.focus();
const file = e?.target?.files?.[0];
if (!file) return;
await handleFileUpload(file);
Expand Down
24 changes: 23 additions & 1 deletion front/lib/api/assistant/conversation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import logger from "@app/logger/logger";
import {
AgentMessageType,
ContentFragmentContentType,
ContentFragmentContextType,
ContentFragmentType,
ConversationType,
ConversationVisibility,
Expand Down Expand Up @@ -339,6 +340,13 @@ function renderContentFragment({
content: contentFragment.content,
url: contentFragment.url,
contentType: contentFragment.contentType,
context:{
timezone: contentFragment.userContextTimezone,
profilePictureUrl: contentFragment.userContextProfilePictureUrl,
fullName: contentFragment.userContextFullName,
email: contentFragment.userContextEmail,
username: contentFragment.userContextUsername,
}
};
}

Expand Down Expand Up @@ -1675,12 +1683,14 @@ export async function postNewContentFragment(
content,
url,
contentType,
context,
}: {
conversation: ConversationType;
title: string;
content: string;
url: string | null;
contentType: ContentFragmentContentType;
context: ContentFragmentContextType;
}
): Promise<ContentFragmentType> {
const owner = auth.workspace();
Expand All @@ -1694,7 +1704,19 @@ export async function postNewContentFragment(
await getConversationRankVersionLock(conversation, t);

const contentFragmentRow = await ContentFragment.create(
{ content, title, url, contentType },
{
content,
title,
url,
contentType,
userId: auth.user()?.id,
userContextProfilePictureUrl: context.profilePictureUrl,
userContextEmail: context.email,
userContextFullName: context.fullName,
userContextTimezone: context.timezone,
userContextUsername: context.username,

},
{ transaction: t }
);
const nextMessageRank =
Expand Down
4 changes: 4 additions & 0 deletions front/lib/api/assistant/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ export const PostContentFragmentRequestBodySchema = t.type({
t.literal("slack_thread_content"),
t.literal("file_attachment"),
]),
context: t.type({
timezone: t.string,
profilePictureUrl: t.union([t.string, t.null]),
}),
});

export type PostContentFragmentRequestBody = t.TypeOf<
Expand Down
36 changes: 36 additions & 0 deletions front/lib/models/assistant/conversation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,15 @@ export class ContentFragment extends Model<
declare content: string;
declare url: string | null;
declare contentType: ContentFragmentContentType;

declare userContextUsername: string|null;
declare userContextTimezone: string|null;
declare userContextFullName: string | null;
declare userContextEmail: string | null;
declare userContextProfilePictureUrl: string | null;


declare userId: ForeignKey<User["id"]> | null;
}

ContentFragment.init(
Expand Down Expand Up @@ -386,13 +395,40 @@ ContentFragment.init(
type: DataTypes.STRING,
allowNull: false,
},
userContextProfilePictureUrl: {
type: DataTypes.STRING,
allowNull: true,
},
userContextUsername: {
type: DataTypes.STRING,
allowNull: true,
},
userContextTimezone: {
type: DataTypes.STRING,
allowNull: true,
},
userContextFullName: {
type: DataTypes.STRING,
allowNull: true,
},
userContextEmail: {
type: DataTypes.STRING,
allowNull: true,
},
},
{
modelName: "content_fragment",
sequelize: front_sequelize,
}
);

User.hasMany(ContentFragment, {
foreignKey: { name: "userId", allowNull: true }, // null = message is not associated with a user
});
ContentFragment.belongsTo(User, {
foreignKey: { name: "userId", allowNull: true },
});

export class Message extends Model<
InferAttributes<Message>,
InferCreationAttributes<Message>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,14 @@ async function handler(
content,
url,
contentType,
context: {
// @todo(aric) PR time: do we want to put the user context here when it's coming from the API (basically from Slack today?)
username: null,
timezone: null,
fullName: null,
email: null,
profilePictureUrl: null,
}
});

res.status(200).json({ contentFragment });
Expand Down
7 changes: 7 additions & 0 deletions front/pages/api/v1/w/[wId]/assistant/conversations/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,13 @@ async function handler(
content: contentFragment.content,
url: contentFragment.url,
contentType: contentFragment.contentType,
context:{
username: null,
timezone:null,
fullName: null,
email: null,
profilePictureUrl: contentFragment.context.profilePictureUrl,
}
});

newContentFragment = cf;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
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, postNewContentFragment } from "@app/lib/api/assistant/conversation";
import { PostContentFragmentRequestBodySchema } from "@app/lib/api/assistant/types";
import { Authenticator, getSession } from "@app/lib/auth";
import { ReturnedAPIErrorType } from "@app/lib/error";
import { apiError, withLogging } from "@app/logger/withlogging";
import { ContentFragmentType } from "@app/types/assistant/conversation";

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,
profilePictureUrl: t.union([t.string, t.null]),
}),
});

async function handler(
req: NextApiRequest,
res: NextApiResponse<{ contentFragment: ContentFragmentType; } | ReturnedAPIErrorType>
): Promise<void> {
const session = await getSession(req, res);
const auth = await Authenticator.fromSession(
session,
req.query.wId as string
);

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 user = auth.user();
if (!user) {
return apiError(req, res, {
status_code: 404,
api_error: {
type: "workspace_user_not_found",
message: "Could not find the user of the current session.",
},
});
}

if (!auth.isUser()) {
return apiError(req, res, {
status_code: 403,
api_error: {
type: "workspace_auth_error",
message:
"Only users of the current workspace can update chat sessions.",
},
});
}
if (!(typeof req.query.cId === "string")) {
return apiError(req, res, {
status_code: 400,
api_error: {
type: "invalid_request_error",
message: "Invalid query parameters, `cId` (string) is required.",
},
});
}

const conversationId = req.query.cId;
const conversation = await getConversation(auth, conversationId);
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 = PostContentFragmentRequestBodySchema.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 contentFragmentPayload = bodyValidation.right;

const contentFragment = await postNewContentFragment(auth, {
conversation,
title: contentFragmentPayload.title,
content: contentFragmentPayload.content,
url: contentFragmentPayload.url,
contentType: contentFragmentPayload.contentType,
context: {
timezone: contentFragmentPayload.context.timezone,
username: user.username,
fullName: user.fullName,
email: user.email,
profilePictureUrl: contentFragmentPayload.context.profilePictureUrl,

},
});


res.status(200).json({ contentFragment });
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);
8 changes: 8 additions & 0 deletions front/pages/api/w/[wId]/assistant/conversations/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,14 @@ async function handler(
content: contentFragment.content,
url: contentFragment.url,
contentType: contentFragment.contentType,
context: {
timezone: contentFragment.context.timezone,
username: user.username,
fullName: user.fullName,
email: user.email,
profilePictureUrl: contentFragment.context.profilePictureUrl,

},
});

newContentFragment = cf;
Expand Down
Loading

0 comments on commit 40ceb92

Please sign in to comment.