Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: release suggest groups #10615

Merged
merged 14 commits into from
Dec 19, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -128,9 +128,11 @@ const ReflectionGroupTitleEditor = (props: Props) => {
const {id: reflectionGroupId, title} = reflectionGroup
const dirtyRef = useRef(false)
const initialTitleRef = useRef(title)
const isLoading = title === ''

const isLoading = title === '' && !dirtyRef.current

const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
dirtyRef.current = true
const title = e.target.value
commitLocalUpdate(atmosphere, (store) => {
const reflectionGroup = store.get(reflectionGroupId)
Expand Down
6 changes: 3 additions & 3 deletions packages/client/components/RetroGroupPhase.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ const RetroGroupPhase = (props: Props) => {
}
organization {
tier
hasSuggestGroupsFlag: featureFlag(featureName: "suggestGroups")
useAI
}
}
`,
Expand All @@ -80,7 +80,7 @@ const RetroGroupPhase = (props: Props) => {
autogroupReflectionGroups,
resetReflectionGroups
} = meeting
const {hasSuggestGroupsFlag, tier} = organization
const {useAI, tier} = organization
const {openTooltip, closeTooltip, tooltipPortal, originRef} = useTooltip<HTMLDivElement>(
MenuPosition.UPPER_CENTER
)
Expand Down Expand Up @@ -114,7 +114,7 @@ const RetroGroupPhase = (props: Props) => {
<PhaseHeaderDescription>
{'Drag cards to group by common topics'}
</PhaseHeaderDescription>
{hasSuggestGroupsFlag &&
{useAI &&
(showSuggestGroups ? (
<ButtonWrapper>
<StyledButton
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,7 @@ const FEATURE_NAME_LOOKUP: Record<string, string> = {
insights: 'Team Insights',
publicTeams: 'Public Teams',
relatedDiscussions: 'Related Discussions',
standupAISummary: 'Standup AI Summary',
suggestGroups: 'AI Reflection Group Suggestions'
standupAISummary: 'Standup AI Summary'
}

interface Props {
Expand Down
1 change: 1 addition & 0 deletions packages/client/mutations/AutogroupMutation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ graphql`
reflectionGroups {
id
title
smartTitle
reflections {
id
plaintextContent
Expand Down
17 changes: 1 addition & 16 deletions packages/client/mutations/EndRetrospectiveMutation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,6 @@ graphql`
reflectionCount
taskCount
topicCount
autogroupReflectionGroups {
groupTitle
}
organization {
useAI
}
Expand Down Expand Up @@ -97,14 +94,7 @@ export const endRetrospectiveTeamOnNext: OnNextHandler<
const {isKill, meeting} = payload
const {atmosphere, history} = context
if (!meeting) return
const {
id: meetingId,
teamId,
reflectionGroups,
phases,
autogroupReflectionGroups,
organization
} = meeting
const {id: meetingId, teamId, reflectionGroups, phases, organization} = meeting
if (meetingId === RetroDemo.MEETING_ID) {
if (isKill) {
window.localStorage.removeItem('retroDemo')
Expand All @@ -124,11 +114,6 @@ export const endRetrospectiveTeamOnNext: OnNextHandler<
const hasTeamHealth = phases.some((phase) => phase.phaseType === 'TEAM_HEALTH')
const pathname = `/new-summary/${meetingId}`
const search = new URLSearchParams()
const hasSuggestGroups = !!autogroupReflectionGroups?.length
if (hasSuggestGroups) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this was used to track usage of the feature, but we already have the autogroup mutation tracking this, so it can be removed

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was used for a hubspot chatbot #8547, I don't think we currently use any chatbot, so fine to remove.

const suggestGroupsStr = reflections.length > 40 ? 'sg-xl' : 'sg'
search.append(suggestGroupsStr, 'true')
}
if (hasOpenAISummary) {
search.append('ai', 'true')
}
Expand Down
6 changes: 2 additions & 4 deletions packages/server/graphql/mutations/helpers/generateGroups.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import OpenAIServerManager from '../../../utils/OpenAIServerManager'
import {analytics} from '../../../utils/analytics/analytics'
import publish from '../../../utils/publish'
import {DataLoaderWorker} from '../../graphql'
import canAccessAI from './canAccessAI'

const generateGroups = async (
reflections: RetroReflection[],
Expand All @@ -15,10 +16,7 @@ const generateGroups = async (
if (reflections.length === 0) return
const {meetingId} = reflections[0]!
const team = await dataLoader.get('teams').loadNonNull(teamId)
const hasSuggestGroupsFlag = await dataLoader
.get('featureFlagByOwnerId')
.load({ownerId: team.orgId, featureName: 'suggestGroups'})
if (!hasSuggestGroupsFlag) return
if (!(await canAccessAI(team, 'retrospective', dataLoader))) return
const groupReflectionsInput = reflections.map((reflection) => reflection.plaintextContent)
const manager = new OpenAIServerManager()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import dndNoise from 'parabol-client/utils/dndNoise'
import getKysely from '../../../../postgres/getKysely'
import updateGroupTitle from '../updateGroupTitle'
import {GQLContext} from './../../../graphql'
import updateSmartGroupTitle from './updateSmartGroupTitle'

const addReflectionToGroup = async (
reflectionId: string,
Expand All @@ -13,6 +14,7 @@ const addReflectionToGroup = async (
const now = new Date()
const reflection = await dataLoader.get('retroReflections').load(reflectionId)
if (!reflection) throw new Error('Reflection not found')

const {reflectionGroupId: oldReflectionGroupId, meetingId: reflectionMeetingId} = reflection
const [reflectionGroup, oldReflectionGroup] = await Promise.all([
dataLoader.get('retroReflectionGroups').loadNonNull(reflectionGroupId),
Expand Down Expand Up @@ -67,6 +69,11 @@ const addReflectionToGroup = async (
.set({title: oldReflectionGroup.title, smartTitle: smartTitle ?? ''})
.where('id', '=', reflectionGroupId)
.execute()
} else if (smartTitle) {
// smartTitle exists when autogrouping or resetting groups
await updateSmartGroupTitle(reflectionGroupId, smartTitle)
reflectionGroup.smartTitle = smartTitle
reflectionGroup.title = smartTitle
} else {
const meeting = await dataLoader.get('newMeetings').loadNonNull(meetingId)
await updateGroupTitle({
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import type {Kysely} from 'kysely'

export async function up(db: Kysely<any>): Promise<void> {
await db.deleteFrom('FeatureFlag').where('featureName', '=', 'suggestGroups').execute()
}

export async function down(db: Kysely<any>): Promise<void> {
await db
.insertInto('FeatureFlag')
.values({
featureName: 'suggestGroups',
description: 'Auto-group reflections using AI',
expiresAt: new Date('2025-01-31T00:00:00.000Z'),
scope: 'Organization'
})
.execute()
}
8 changes: 5 additions & 3 deletions packages/server/utils/OpenAIServerManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -420,11 +420,11 @@ class OpenAIServerManager {

async generateGroupTitle(reflections: {plaintextContent: string}[]) {
if (!this.openAIApi) return null
const prompt = `Given these related retrospective comments, generate a short (2-4 words) theme or title that captures their essence. The title should be clear and actionable:
const prompt = `Generate a short (2-4 words) theme or title that captures the essence of these related retrospective comments. The title should be clear and actionable.

${reflections.map((r) => r.plaintextContent).join('\n')}

Return only the title, nothing else. Do not include quote marks around the title.`
Important: Respond with ONLY the title itself. Do not include any prefixes like "Title:" or any quote marks. Do not provide any additional explanation.`

try {
const response = await this.openAIApi.chat.completions.create({
Expand All @@ -442,7 +442,9 @@ Return only the title, nothing else. Do not include quote marks around the title
presence_penalty: 0
})
const title =
(response.choices[0]?.message?.content?.trim() as string)?.replaceAll(/['"]/g, '') ?? null
(response.choices[0]?.message?.content?.trim() as string)
?.replace(/^[Tt]itle:*\s*/gi, '') // Remove "Title:" prefix
?.replaceAll(/['"]/g, '') ?? null

return title
} catch (e) {
Expand Down
Loading