Skip to content

Commit

Permalink
Adding refinements to the RHS (#93)
Browse files Browse the repository at this point in the history
* Fix RHS styling

* Fix styling on threads list.

* Add titles.

* ignore png
  • Loading branch information
crspeller authored Nov 24, 2023
1 parent 35cd36e commit 82b791b
Show file tree
Hide file tree
Showing 10 changed files with 197 additions and 119 deletions.
24 changes: 2 additions & 22 deletions server/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package main
import (
"net/http"

sq "github.com/Masterminds/squirrel"
"github.com/gin-gonic/gin"
"github.com/mattermost/mattermost/server/public/model"
"github.com/mattermost/mattermost/server/public/plugin"
Expand Down Expand Up @@ -83,27 +82,8 @@ func (p *Plugin) handleGetAIThreads(c *gin.Context) {
return
}

var posts []struct {
ID string
Message string
ReplyCount int
UpdateAt int64
}
if err := p.doQuery(&posts, p.builder.
Select(
"p.Id",
"p.Message",
"(SELECT COUNT(*) FROM Posts WHERE Posts.RootId = p.Id AND DeleteAt = 0) AS ReplyCount",
"p.UpdateAt",
).
From("Posts as p").
Where(sq.Eq{"ChannelID": botDMChannel.Id}).
Where(sq.Eq{"RootId": ""}).
Where(sq.Eq{"DeleteAt": 0}).
OrderBy("CreateAt DESC").
Limit(60).
Offset(0),
); err != nil {
posts, err := p.getAIThreads(botDMChannel.Id)
if err != nil {
c.AbortWithError(http.StatusInternalServerError, errors.Wrap(err, "failed to get posts for bot DM"))
return
}
Expand Down
26 changes: 26 additions & 0 deletions server/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,32 @@ func (p *Plugin) newConversation(context ai.ConversationContext) error {
return err
}

go func() {
if err := p.generateTitle(context); err != nil {
p.API.LogError("Failed to generate title", "error", err.Error())
return
}
}()

return nil
}

func (p *Plugin) generateTitle(context ai.ConversationContext) error {
titleRequest := ai.BotConversation{
Posts: []ai.Post{{Role: ai.PostRoleUser, Message: "Write a short title for the following request. Include only the title and nothing else, no quotations. Request:\n" + context.Post.Message}},
Context: context,
}
conversationTitle, err := p.getLLM().ChatCompletionNoStream(titleRequest, ai.WithmaxTokens(25))

Check failure on line 62 in server/service.go

View workflow job for this annotation

GitHub Actions / test

undefined: ai.WithmaxTokens

This comment has been minimized.

Copy link
@nosyn

nosyn Nov 27, 2023

Contributor

I made some changes to the method a couple of days ago. Sorry for breaking your MR. WithmaxTokens is now WithMaxTokens

conversationTitle, err := p.getLLM().ChatCompletionNoStream(titleRequest, ai.WithMaxTokens(25))

if err != nil {
return errors.Wrap(err, "failed to get title")
}

conversationTitle = strings.Trim(conversationTitle, "\n \"'")

if err := p.saveTitle(context.Post.Id, conversationTitle); err != nil {
return errors.Wrap(err, "failed to save title")
}

return nil
}

Expand Down
50 changes: 50 additions & 0 deletions server/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,5 +64,55 @@ func (p *Plugin) SetupTables() error {
return errors.Wrap(err, "can't create feeback table")
}

if _, err := p.db.Exec(`
CREATE TABLE IF NOT EXISTS LLM_Threads (
RootPostID TEXT NOT NULL REFERENCES Posts(ID) PRIMARY KEY,
Title TEXT NOT NULL
);
`); err != nil {
return errors.Wrap(err, "can't create feeback table")
}

return nil
}

func (p *Plugin) saveTitle(threadID, title string) error {
_, err := p.execBuilder(p.builder.Insert("LLM_Threads").
Columns("RootPostID", "Title").
Values(threadID, title).
Suffix("ON CONFLICT (RootPostID) DO UPDATE SET Title = ?", title))
return err
}

type AIThread struct {
ID string
Message string
Title string
ReplyCount int
UpdateAt int64
}

func (p *Plugin) getAIThreads(dmChannelID string) ([]AIThread, error) {
var posts []AIThread
if err := p.doQuery(&posts, p.builder.
Select(
"p.Id",
"p.Message",
"COALESCE(t.Title, '') as Title",
"(SELECT COUNT(*) FROM Posts WHERE Posts.RootId = p.Id AND DeleteAt = 0) AS ReplyCount",
"p.UpdateAt",
).
From("Posts as p").
Where(sq.Eq{"ChannelID": dmChannelID}).
Where(sq.Eq{"RootId": ""}).
Where(sq.Eq{"DeleteAt": 0}).
LeftJoin("LLM_Threads as t ON t.RootPostID = p.Id").
OrderBy("CreateAt DESC").
Limit(60).
Offset(0),
); err != nil {
return nil, errors.Wrap(err, "failed to get posts for bot DM")
}

return posts, nil
}
9 changes: 6 additions & 3 deletions webapp/src/components/rhs/common.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,7 @@ export const Button = styled.button`
font-weight: 600;
font-size: 12px;
background-color: rgb(var(--center-channel-bg-rgb));
color: rgba(var(--center-channel-color), 0.6);
width: 172px;
border: 0;
margin: 0 8px 8px 0;
&:hover {
background-color: rgba(var(--button-bg-rgb), 0.08);
Expand All @@ -25,4 +22,10 @@ export const Button = styled.button`
fill: rgb(var(--center-channel-color));
margin-right: 6px;
}
i {
display: flex;
font-size: 14px;
margin-right: 2px;
}
`;
3 changes: 2 additions & 1 deletion webapp/src/components/rhs/rhs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const RhsContainer = styled.div`
export interface AIThread {
ID: string;
Message: string;
Title: string;
ReplyCount: number;
UpdateAt: number;
}
Expand Down Expand Up @@ -72,8 +73,8 @@ export default function RHS() {
{threads.map((p) => (
<ThreadItem
key={p.ID}
postTitle={p.Title}
postMessage={p.Message}
postFirstReply={p.Message.split('\n').slice(1).join('\n').slice(1, 300)}
repliesCount={p.ReplyCount}
lastActivityDate={p.UpdateAt}
onClick={() => {
Expand Down
89 changes: 40 additions & 49 deletions webapp/src/components/rhs/rhs_header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,40 +3,30 @@ import styled from 'styled-components';

import {Button} from './common';

const MenuButton = styled(Button)`
display: flex;
margin-bottom: 12px;
width: auto;
&.new-button {
color: rgb(var(--link-color-rgb));
&:hover {
background: transparent;
color: rgb(var(--link-color-rgb));
}
}
&.no-clickable {
&:hover {
background: transparent;
color: rgb(var(--center-channel-color));
cursor: unset;
}
}
.thread-title {
display: inline-block;
max-width: 220px;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
const HistoryButton = styled(Button)`
color: rgba(var(--center-channel-color-rgb), 0.64);
`;

const HeaderSpacer = styled.div`
flex-grow: 1;
const ButtonDisabled = styled(Button)`
&:hover {
background: transparent;
color: rgb(var(--center-channel-color));
cursor: unset;
}
`;

const NewChatButton = styled(Button)`
color: rgb(var(--link-color-rgb));
&:hover {
background: transparent;
color: rgb(var(--link-color-rgb));
}
`;

const Header = styled.div`
display: flex;
padding: 12px 12px 0 12px;
padding 8px;
justify-content: space-between;
border-bottom: 1px solid rgba(var(--center-channel-color-rgb), 0.12);
flex-wrap: wrap;
`;
Expand All @@ -48,37 +38,38 @@ type Props = {
}

const RHSHeader = ({currentTab, setCurrentTab, selectPost}: Props) => {
let historyButton = null;
if (currentTab === 'threads') {
historyButton = (
<ButtonDisabled>
<i className='icon-clock-outline'/> {'Chat history'}
</ButtonDisabled>
);
} else {
historyButton = (
<HistoryButton
onClick={() => {
setCurrentTab('threads');
selectPost('');
}}
>
<i className='icon-clock-outline'/> {'View chat history'}
</HistoryButton>
);
}
return (
<Header>

{currentTab === 'threads' && (
<MenuButton className='no-clickable'>
<i className='icon icon-clock-outline'/> {'Chat history'}
</MenuButton>
)}

{currentTab !== 'threads' && (
<MenuButton
onClick={() => {
setCurrentTab('threads');
selectPost('');
}}
>
<i className='icon icon-clock-outline'/> {'View chat history'}
</MenuButton>)}

<HeaderSpacer/>

{historyButton}
{currentTab !== 'new' && (
<MenuButton
<NewChatButton
className='new-button'
onClick={() => {
setCurrentTab('new');
selectPost('');
}}
>
<i className='icon icon-pencil-outline'/> {'New chat'}
</MenuButton>
</NewChatButton>
)}
</Header>
);
Expand Down
53 changes: 34 additions & 19 deletions webapp/src/components/rhs/rhs_new_tab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,17 @@ import {createPost} from '@/client';

import {Button} from './common';

const CreatePost = (window as any).Components.CreatePost && styled((window as any).Components.CreatePost)`
padding: 0px;
const CreatePost = (window as any).Components.CreatePost;

const CreatePostContainer = styled.div`
.custom-textarea {
padding-top: 13px;
padding-bottom: 13px;
passing-left: 16px;
}
.AdvancedTextEditor {
padding: 0px;
}
`;

const OptionButton = styled(Button)`
Expand All @@ -23,16 +32,17 @@ const OptionButton = styled(Button)`
svg {
fill: rgb(var(--link-color-rgb));
}
font-weight: 600;
line-height: 16px;
font-size: 12px;
`;

const NewQuestion = styled.div`
padding: 12px;
margin: 0 24px;
margin-top: 16px;
display: flex;
flex-direction: column;
flex-grow: 1;
.AdvancedTextEditor {
padding: 0px;
}
gap: 8px;
`;

const QuestionTitle = styled.div`
Expand All @@ -47,6 +57,9 @@ const QuestionDescription = styled.div`

const QuestionOptions = styled.div`
display: flex;
gap: 8px;
margin-top: 16px;
margin-bottom: 24px;
flex-wrap: wrap;
`;

Expand Down Expand Up @@ -93,23 +106,25 @@ const RHSNewTab = ({botChannelId, selectPost, setCurrentTab}: Props) => {
<NewQuestion>
<RHSImage/>
<QuestionTitle>{'Ask AI Assistant anything'}</QuestionTitle>
<QuestionDescription>{'The AI Assistant can help you with almost anything. Choose from the prompts below or write your own.'}</QuestionDescription>
<QuestionDescription>{'The AI Assistant is here to help. Choose from the prompts below or write your own.'}</QuestionDescription>
<QuestionOptions>
<OptionButton onClick={addBrainstormingIdeas}><LightbulbOutlineIcon/>{'Brainstorm ideas'}</OptionButton>
<OptionButton onClick={addMeetingAgenda}><FormatListNumberedIcon/>{'Meeting agenda'}</OptionButton>
<OptionButton onClick={addToDoList}><PlaylistCheckIcon/>{'To-do list'}</OptionButton>
<OptionButton onClick={addProsAndCons}><PlusMinus className='icon'>{'±'}</PlusMinus>{'Pros and Cons'}</OptionButton>
<OptionButton onClick={addToDoList}><PlaylistCheckIcon/>{'To-do list'}</OptionButton>
</QuestionOptions>
<CreatePost
placeholder={'Ask AI Assistant anything...'}
onSubmit={async (p: any) => {
p.channel_id = botChannelId || '';
p.props = {};
const created = await createPost(p);
selectPost(created.id);
setCurrentTab('thread');
}}
/>
<CreatePostContainer>
<CreatePost
placeholder={'Ask AI Assistant anything...'}
onSubmit={async (p: any) => {
p.channel_id = botChannelId || '';
p.props = {};
const created = await createPost(p);
selectPost(created.id);
setCurrentTab('thread');
}}
/>
</CreatePostContainer>
</NewQuestion>
);
};
Expand Down
Loading

0 comments on commit 82b791b

Please sign in to comment.