-
Notifications
You must be signed in to change notification settings - Fork 3.9k
[Payment due @shubham1206agra] Render thinking + draft-streaming UI for custom agent chats #90751
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
Changes from all commits
06e69be
048a874
e177895
c978370
0f7f2b2
5d1ec6b
b271898
bf544cc
c895293
ba8c3a2
b423f9a
0586e7a
14e1e94
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,4 @@ | ||
| import {getAgentAccountIDFlags, getReportParticipantAccountIDs} from '@selectors/AgentZeroChat'; | ||
| import {getReportChatType} from '@selectors/Report'; | ||
| import React, {createContext, useContext, useEffect} from 'react'; | ||
| import useAgentZeroStatusIndicator from '@hooks/useAgentZeroStatusIndicator'; | ||
|
|
@@ -16,6 +17,13 @@ type AgentZeroStatusState = { | |
|
|
||
| /** Debounced label shown in the thinking bubble */ | ||
| statusLabel: string; | ||
|
|
||
| /** | ||
| * The accountID of the AgentZero persona handling this chat. Concierge for Concierge DMs and | ||
| * #admins rooms; the agent's own accountID for custom-agent chats. Consumers use it to render | ||
| * the thinking-bubble avatar and to decide when a reply has actually landed. | ||
| */ | ||
| personaAccountID: number; | ||
| }; | ||
|
|
||
| type AgentZeroStatusActions = { | ||
|
|
@@ -27,6 +35,7 @@ const defaultState: AgentZeroStatusState = { | |
| isProcessing: false, | ||
| reasoningHistory: [], | ||
| statusLabel: '', | ||
| personaAccountID: CONST.ACCOUNT_ID.CONCIERGE, | ||
| }; | ||
|
|
||
| const defaultActions: AgentZeroStatusActions = { | ||
|
|
@@ -37,17 +46,26 @@ const AgentZeroStatusStateContext = createContext<AgentZeroStatusState>(defaultS | |
| const AgentZeroStatusActionsContext = createContext<AgentZeroStatusActions>(defaultActions); | ||
|
|
||
| /** | ||
| * Cheap outer guard — only subscribes to the scalar CONCIERGE_REPORT_ID. | ||
| * For non-AgentZero reports (the common case), returns children directly. | ||
| * Cheap outer guard — only subscribes to the scalar CONCIERGE_REPORT_ID and the report's chat | ||
| * metadata. For non-AgentZero reports (the common case), returns children directly. | ||
| * | ||
| * AgentZero chats include Concierge DMs and policy #admins rooms. | ||
| * AgentZero chats include Concierge DMs, policy #admins rooms, and custom-agent chats (any | ||
| * report with a participant whose accountID has a `SHARED_NVP_AGENT_PROMPT_<accountID>` entry, | ||
| * populated by `OpenAgentsPage` for agents the current user owns). | ||
| */ | ||
| function AgentZeroStatusProvider({reportID, children}: React.PropsWithChildren<{reportID: string | undefined}>) { | ||
| const [chatType] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, {selector: getReportChatType}); | ||
| const [participantAccountIDs] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, {selector: getReportParticipantAccountIDs}); | ||
| const [agentAccountIDFlags] = useOnyx(ONYXKEYS.COLLECTION.SHARED_NVP_AGENT_PROMPT, {selector: getAgentAccountIDFlags}); | ||
| const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); | ||
|
|
||
| const isConciergeChat = reportID === conciergeReportID; | ||
| const isAdmin = chatType === CONST.REPORT.CHAT_TYPE.POLICY_ADMINS; | ||
| const isAgentZeroChat = isConciergeChat || isAdmin; | ||
| // First participant whose accountID has a SHARED_NVP_AGENT_PROMPT entry. Both gates and | ||
| // identifies the persona in one pass. | ||
| const agentParticipantAccountID = participantAccountIDs?.find((accountID) => !!agentAccountIDFlags?.[accountID]); | ||
| const isCustomAgentChat = agentParticipantAccountID !== undefined; | ||
| const isAgentZeroChat = isConciergeChat || isAdmin || isCustomAgentChat; | ||
|
Comment on lines
+67
to
+68
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This change enables Useful? React with 👍 / 👎.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (Yuwen's Agent) Already addressed in commit e177895 (
Comment on lines
+64
to
+68
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Custom-agent detection is gated entirely on
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah good call. Fixed! Added an eager |
||
|
|
||
| if (!reportID || !isAgentZeroChat) { | ||
| return children; | ||
|
|
@@ -57,14 +75,16 @@ function AgentZeroStatusProvider({reportID, children}: React.PropsWithChildren<{ | |
| <AgentZeroStatusGate | ||
| key={reportID} | ||
| reportID={reportID} | ||
| personaAccountID={agentParticipantAccountID ?? CONST.ACCOUNT_ID.CONCIERGE} | ||
| > | ||
| {children} | ||
| </AgentZeroStatusGate> | ||
| ); | ||
| } | ||
|
|
||
| function AgentZeroStatusGate({reportID, children}: React.PropsWithChildren<{reportID: string}>) { | ||
| const {kickoffWaitingIndicator, ...stateValue} = useAgentZeroStatusIndicator(reportID); | ||
| function AgentZeroStatusGate({reportID, personaAccountID, children}: React.PropsWithChildren<{reportID: string; personaAccountID: number}>) { | ||
| const {kickoffWaitingIndicator, ...indicatorState} = useAgentZeroStatusIndicator(reportID, personaAccountID); | ||
| const stateValue = {...indicatorState, personaAccountID}; | ||
| const actionsValue = {kickoffWaitingIndicator}; | ||
|
|
||
| // Auto-kickoff "thinking" indicator when opened from search (where kickoffWaitingIndicator isn't accessible) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,4 @@ | ||
| import {getAgentAccountIDFlags, getReportParticipantAccountIDs} from '@selectors/AgentZeroChat'; | ||
| import {getReportChatType} from '@selectors/Report'; | ||
| import React, {createContext, useContext, useEffect, useState} from 'react'; | ||
| import useOnyx from '@hooks/useOnyx'; | ||
|
|
@@ -41,10 +42,16 @@ const ConciergeDraftActionsContext = createContext<ConciergeDraftActions>(defaul | |
|
|
||
| function ConciergeDraftProvider({reportID, children}: React.PropsWithChildren<{reportID: string | undefined}>) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ❌ CONSISTENCY-3 (docs)The custom-agent detection logic (subscribing to Extract a shared hook (e.g., Reviewed at: b423f9a | Please rate this suggestion with 👍 or 👎 to help us improve! Reactions are used to monitor reviewer efficiency. |
||
| const [chatType] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, {selector: getReportChatType}); | ||
| const [participantAccountIDs] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, {selector: getReportParticipantAccountIDs}); | ||
| const [agentAccountIDFlags] = useOnyx(ONYXKEYS.COLLECTION.SHARED_NVP_AGENT_PROMPT, {selector: getAgentAccountIDFlags}); | ||
| const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); | ||
|
|
||
| const isConciergeChat = reportID === conciergeReportID; | ||
| const isAdmin = chatType === CONST.REPORT.CHAT_TYPE.POLICY_ADMINS; | ||
| const isAgentZeroChat = isConciergeChat || isAdmin; | ||
| // See AgentZeroStatusContext for the rationale: agentAccountIDFlags reflects agents the | ||
| // user owns (populated by `OpenAgentsPage`). | ||
| const isCustomAgentChat = participantAccountIDs?.some((accountID) => !!agentAccountIDFlags?.[accountID]); | ||
| const isAgentZeroChat = isConciergeChat || isAdmin || isCustomAgentChat; | ||
|
Comment on lines
+53
to
+54
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
|
||
| if (!reportID || !isAgentZeroChat) { | ||
| return children; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; | ||
| import ONYXKEYS from '@src/ONYXKEYS'; | ||
| import type {AgentPrompt} from '@src/types/onyx'; | ||
| import type Report from '@src/types/onyx/Report'; | ||
|
|
||
| const getReportParticipantAccountIDs = (report: OnyxEntry<Report>): number[] => (report?.participants ? Object.keys(report.participants).map(Number) : []); | ||
|
|
||
| /** | ||
| * Reduces the SHARED_NVP_AGENT_PROMPT collection to a `Record<accountID, true>` so callers | ||
| * can do O(1) lookups without re-rendering on every prompt-content edit. | ||
| */ | ||
| const getAgentAccountIDFlags = (agentPrompts: OnyxCollection<AgentPrompt>): Record<number, true> => { | ||
| if (!agentPrompts) { | ||
| return {}; | ||
| } | ||
| const flags: Record<number, true> = {}; | ||
| for (const key of Object.keys(agentPrompts)) { | ||
| const accountID = Number(key.slice(ONYXKEYS.COLLECTION.SHARED_NVP_AGENT_PROMPT.length)); | ||
| if (!Number.isNaN(accountID)) { | ||
| flags[accountID] = true; | ||
| } | ||
| } | ||
| return flags; | ||
| }; | ||
|
|
||
| export {getReportParticipantAccountIDs, getAgentAccountIDFlags}; |
Uh oh!
There was an error while loading. Please reload this page.