From 4f57ef1ce73509675dc85be1bd830bf4f4cba77c Mon Sep 17 00:00:00 2001 From: Beatrix Date: Fri, 13 Sep 2024 16:33:53 -0700 Subject: [PATCH 1/4] Chat: show context ignored reason in UI --- .../cells/contextCell/ContextCell.story.tsx | 35 ++ .../chat/cells/contextCell/ContextCell.tsx | 336 +++++++++++------- 2 files changed, 237 insertions(+), 134 deletions(-) diff --git a/vscode/webviews/chat/cells/contextCell/ContextCell.story.tsx b/vscode/webviews/chat/cells/contextCell/ContextCell.story.tsx index fc514283af37..60038b50f978 100644 --- a/vscode/webviews/chat/cells/contextCell/ContextCell.story.tsx +++ b/vscode/webviews/chat/cells/contextCell/ContextCell.story.tsx @@ -95,3 +95,38 @@ export const Loading: Story = { isForFirstMessage: false, }, } + +export const ExcludedContext: Story = { + args: { + contextItems: [ + { type: 'file', uri: URI.file('/foo/bar.go') }, + { type: 'file', uri: URI.file('/foo/qux.go') }, + { + type: 'file', + uri: URI.file('/internal/large.go'), + isTooLarge: true, + source: ContextItemSource.User, + }, + { + type: 'file', + uri: URI.file('/internal/ignored1.go'), + isIgnored: true, + source: ContextItemSource.User, + }, + { + type: 'file', + uri: URI.file('/internal/ignored2.go'), + isIgnored: true, + source: ContextItemSource.User, + }, + { + type: 'file', + uri: URI.file('/internal/large2.go'), + isTooLarge: true, + source: ContextItemSource.User, + }, + ], + isForFirstMessage: true, + __storybook__initialOpen: true, + }, +} diff --git a/vscode/webviews/chat/cells/contextCell/ContextCell.tsx b/vscode/webviews/chat/cells/contextCell/ContextCell.tsx index 22c8b4ab7c08..e400316a4a0b 100644 --- a/vscode/webviews/chat/cells/contextCell/ContextCell.tsx +++ b/vscode/webviews/chat/cells/contextCell/ContextCell.tsx @@ -73,23 +73,10 @@ export const ContextCell: FunctionComponent<{ contextItemsToDisplay = contextAlternatives[selectedAlternative].items } - const usedContext: ContextItem[] = [] - const excludedAtContext: ContextItem[] = [] - if (contextItemsToDisplay) { - for (const item of contextItemsToDisplay) { - if (item.isTooLarge || item.isIgnored) { - excludedAtContext.push(item) - } else { - usedContext.push(item) - } - } - } - - const itemCount = usedContext.length - const contextItemCountLabel = `${itemCount} ${!isForFirstMessage ? 'new ' : ''}${pluralize( - 'item', - itemCount - )}` + const { usedContext, excludedContext, itemCountLabel, excludedContextInfo } = getContextInfo( + contextItemsToDisplay, + isForFirstMessage + ) function logContextOpening() { getVSCodeAPI().postMessage({ @@ -97,7 +84,7 @@ export const ContextCell: FunctionComponent<{ eventName: 'CodyVSCodeExtension:chat:context:opened', properties: { fileCount: new Set(usedContext.map(file => file.uri.toString())).size, - excludedAtContext: excludedAtContext.length, + excludedAtContext: excludedContext.length, }, }) } @@ -106,124 +93,205 @@ export const ContextCell: FunctionComponent<{ config: { internalDebugContext }, } = useConfig() - return contextItemsToDisplay === undefined || contextItemsToDisplay.length !== 0 ? ( - - - - - - Context - - — {contextItemCountLabel} - - - - } - containerClassName={className} - contentClassName="tw-flex tw-flex-col tw-gap-4 tw-max-w-full" - data-testid="context" - > - {contextItems === undefined ? ( - - ) : ( - <> - - {internalDebugContext && contextAlternatives && ( -
- - {' '} - Ranking mechanism:{' '} - {selectedAlternative === undefined - ? 'actual' - : `${ - contextAlternatives[selectedAlternative].strategy - }: (${(selectedAlternative ?? -1) + 1} of ${ - contextAlternatives.length - })`} -
- )} -
    - {contextItemsToDisplay?.map((item, i) => ( -
  • - - {internalDebugContext && - item.metadata && - item.metadata.length > 0 && ( - - {item.metadata.join(', ')} - - )} -
  • - ))} - {!isForFirstMessage && ( - - - - Prior messages and context in this conversation + return ( +
    + {contextItemsToDisplay === undefined || + (contextItemsToDisplay.length !== 0 && ( + + + + + + Context + + — {itemCountLabel} - )} -
  • - - - - - Public knowledge - - - - - Information and general reasoning capabilities - trained into the model{' '} - {model && {model}} - - - -
  • -
-
- - )} -
-
-
- ) : null + + } + containerClassName={className} + contentClassName="tw-flex tw-flex-col tw-gap-4 tw-max-w-full" + data-testid="context" + > + {contextItems === undefined ? ( + + ) : ( + <> + + {internalDebugContext && contextAlternatives && ( +
+ + {' '} + Ranking mechanism:{' '} + {selectedAlternative === undefined + ? 'actual' + : `${ + contextAlternatives[ + selectedAlternative + ].strategy + }: (${ + (selectedAlternative ?? -1) + 1 + } of ${contextAlternatives.length})`} +
+ )} +
    + {contextItemsToDisplay?.map((item, i) => ( +
  • + + {internalDebugContext && + item.metadata && + item.metadata.length > 0 && ( + + {item.metadata.join(', ')} + + )} +
  • + ))} + {!isForFirstMessage && ( + + + + Prior messages and context in this + conversation + + + )} +
  • + + + + + Public knowledge + + + + + Information and general reasoning + capabilities trained into the model{' '} + {model && {model}} + + + +
  • +
+
+ + )} + + + + ))} + {excludedContextInfo && ( +
+ {excludedContextInfo.map(message => ( +
+ + {message} +
+ ))} +
+ )} + + ) }, isEqual ) + +const getContextInfo = (items?: ContextItem[], isNew?: boolean) => { + const { usedContext, excludedContext, count } = (items ?? []).reduce( + (acc, item) => { + if (item.isTooLarge || item.isIgnored) { + acc.excludedContext.push(item) + acc.count[item.isTooLarge ? 'token' : 'filtered']++ + } else { + acc.usedContext.push(item) + acc.count.used++ + } + return acc + }, + { + usedContext: [] as ContextItem[], + excludedContext: [] as ContextItem[], + count: { used: 0, token: 0, filtered: 0 }, + } + ) + + const itemCountLabel = `${count.used} ${isNew ? 'new ' : ''}${pluralize('item', count.used)}` + + return { + usedContext, + excludedContext, + itemCountLabel, + excludedContextInfo: generateExcludedInfo(count.token, count.filtered), + } +} + +const template = { + filter: 'filtered out by Cody Context Filters. Please contact your site admin for details.', + token: 'skipped due to token limit exceeded.', +} + +function generateExcludedInfo(token: number, filter: number): string[] { + const warnings = [] + if (token > 0) { + warnings.push(`${token} ${pluralize('item', token)} ${template.token}`) + } + if (filter > 0) { + warnings.push(`${filter} ${pluralize('item', filter)} ${template.filter}`) + } + return warnings +} From 4eb3622c35266b353f2302de0d515f15d81acc97 Mon Sep 17 00:00:00 2001 From: Beatrix Date: Fri, 13 Sep 2024 17:12:01 -0700 Subject: [PATCH 2/4] fix --- .../chat/cells/contextCell/ContextCell.tsx | 276 +++++++++--------- 1 file changed, 137 insertions(+), 139 deletions(-) diff --git a/vscode/webviews/chat/cells/contextCell/ContextCell.tsx b/vscode/webviews/chat/cells/contextCell/ContextCell.tsx index e400316a4a0b..b240890bcb5d 100644 --- a/vscode/webviews/chat/cells/contextCell/ContextCell.tsx +++ b/vscode/webviews/chat/cells/contextCell/ContextCell.tsx @@ -95,148 +95,146 @@ export const ContextCell: FunctionComponent<{ return (
- {contextItemsToDisplay === undefined || - (contextItemsToDisplay.length !== 0 && ( - - - - - - Context - - — {itemCountLabel} - + {(contextItemsToDisplay === undefined || contextItemsToDisplay.length !== 0) && ( + + + + + + Context + + — {itemCountLabel} - - } - containerClassName={className} - contentClassName="tw-flex tw-flex-col tw-gap-4 tw-max-w-full" - data-testid="context" - > - {contextItems === undefined ? ( - - ) : ( - <> - - {internalDebugContext && contextAlternatives && ( -
- - {' '} - Ranking mechanism:{' '} - {selectedAlternative === undefined - ? 'actual' - : `${ - contextAlternatives[ - selectedAlternative - ].strategy - }: (${ - (selectedAlternative ?? -1) + 1 - } of ${contextAlternatives.length})`} -
- )} -
    - {contextItemsToDisplay?.map((item, i) => ( -
  • - - {internalDebugContext && - item.metadata && - item.metadata.length > 0 && ( - - {item.metadata.join(', ')} - - )} -
  • - ))} - {!isForFirstMessage && ( - - - - Prior messages and context in this - conversation - - - )} -
  • - - + + + } + containerClassName={className} + contentClassName="tw-flex tw-flex-col tw-gap-4 tw-max-w-full" + data-testid="context" + > + {contextItems === undefined ? ( + + ) : ( + <> + + {internalDebugContext && contextAlternatives && ( +
    + + {' '} + Ranking mechanism:{' '} + {selectedAlternative === undefined + ? 'actual' + : `${ + contextAlternatives[selectedAlternative] + .strategy + }: (${(selectedAlternative ?? -1) + 1} of ${ + contextAlternatives.length + })`} +
    + )} +
      + {contextItemsToDisplay?.map((item, i) => ( +
    • + + {internalDebugContext && + item.metadata && + item.metadata.length > 0 && ( - - Public knowledge + {item.metadata.join(', ')} - - - - Information and general reasoning - capabilities trained into the model{' '} - {model && {model}} - - - + )}
    • -
    -
    - - )} - - - - ))} - {excludedContextInfo && ( -
    + ))} + {!isForFirstMessage && ( + + + + Prior messages and context in this + conversation + + + )} +
  • + + + + + Public knowledge + + + + + Information and general reasoning + capabilities trained into the model{' '} + {model && {model}} + + + +
  • +
+
+ + )} +
+
+
+ )} + {contextItemsToDisplay && excludedContextInfo.length && ( +
{excludedContextInfo.map(message => (
@@ -251,7 +249,7 @@ export const ContextCell: FunctionComponent<{ isEqual ) -const getContextInfo = (items?: ContextItem[], isNew?: boolean) => { +const getContextInfo = (items?: ContextItem[], isFirst?: boolean) => { const { usedContext, excludedContext, count } = (items ?? []).reduce( (acc, item) => { if (item.isTooLarge || item.isIgnored) { @@ -270,7 +268,7 @@ const getContextInfo = (items?: ContextItem[], isNew?: boolean) => { } ) - const itemCountLabel = `${count.used} ${isNew ? 'new ' : ''}${pluralize('item', count.used)}` + const itemCountLabel = `${count.used} ${isFirst ? '' : 'new '}${pluralize('item', count.used)}` return { usedContext, From 90f73ffb47aba83f221a1658af3d5bb3708b62de Mon Sep 17 00:00:00 2001 From: Beatrix Date: Fri, 13 Sep 2024 17:17:22 -0700 Subject: [PATCH 3/4] update styles --- vscode/webviews/chat/cells/contextCell/ContextCell.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vscode/webviews/chat/cells/contextCell/ContextCell.tsx b/vscode/webviews/chat/cells/contextCell/ContextCell.tsx index b240890bcb5d..f44aa0a6ac0c 100644 --- a/vscode/webviews/chat/cells/contextCell/ContextCell.tsx +++ b/vscode/webviews/chat/cells/contextCell/ContextCell.tsx @@ -234,10 +234,10 @@ export const ContextCell: FunctionComponent<{ )} {contextItemsToDisplay && excludedContextInfo.length && ( -
+
{excludedContextInfo.map(message => ( -
- +
+ {message}
))} From c98872ca9acd97afe6e0c8034eb07a585e8caffa Mon Sep 17 00:00:00 2001 From: Beatrix Date: Tue, 24 Sep 2024 11:16:39 -0700 Subject: [PATCH 4/4] Add link --- .../chat/cells/contextCell/ContextCell.tsx | 25 ++++++++++++++----- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/vscode/webviews/chat/cells/contextCell/ContextCell.tsx b/vscode/webviews/chat/cells/contextCell/ContextCell.tsx index f44aa0a6ac0c..34684f346152 100644 --- a/vscode/webviews/chat/cells/contextCell/ContextCell.tsx +++ b/vscode/webviews/chat/cells/contextCell/ContextCell.tsx @@ -236,10 +236,7 @@ export const ContextCell: FunctionComponent<{ {contextItemsToDisplay && excludedContextInfo.length && (
{excludedContextInfo.map(message => ( -
- - {message} -
+ ))}
)} @@ -279,8 +276,8 @@ const getContextInfo = (items?: ContextItem[], isFirst?: boolean) => { } const template = { - filter: 'filtered out by Cody Context Filters. Please contact your site admin for details.', - token: 'skipped due to token limit exceeded.', + filter: 'filtered out by Cody Context Filters. Please contact your site admin for details', + token: 'were retrieved but not used because they exceed the token limit. Learn more about token limits', } function generateExcludedInfo(token: number, filter: number): string[] { @@ -293,3 +290,19 @@ function generateExcludedInfo(token: number, filter: number): string[] { } return warnings } + +export const ExcludedContextWarning: React.FunctionComponent<{ message: string }> = ({ message }) => { + const type = message.includes(template.token) ? 'token' : 'filter' + return ( +
+ + + {message} + {type === 'token' && ( + here + )} + . + +
+ ) +}