feat(frontend): add sources to metadata (#2113)

# Description

Please include a summary of the changes and the related issue. Please
also include relevant motivation and context.

## Checklist before requesting a review

Please delete options that are not relevant.

- [ ] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my code
- [ ] I have commented hard-to-understand areas
- [ ] I have ideally added tests that prove my fix is effective or that
my feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] Any dependent changes have been merged

## Screenshots (if appropriate):
This commit is contained in:
Antoine Dewez 2024-01-29 07:55:38 -08:00 committed by GitHub
parent 37b9901fe8
commit 053ed0961d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 124 additions and 30 deletions

View File

@ -1,6 +1,7 @@
import React from "react";
import Icon from "@/lib/components/ui/Icon/Icon";
import { Source } from "@/lib/types/MessageMetadata";
import styles from "./MessageRow.module.scss";
import { CopyButton } from "./components/CopyButton";
@ -16,7 +17,7 @@ type MessageRowProps = {
promptName?: string | null;
children?: React.ReactNode;
metadata?: {
sources?: [string] | [];
sources?: Source[];
};
};

View File

@ -3,29 +3,58 @@
import { useEffect, useState } from "react";
import { useChatContext } from "@/lib/context";
import { CloseBrain } from "@/lib/types/MessageMetadata";
import { MessageMetadata, Source } from "@/lib/types/MessageMetadata";
import styles from "./DataPanel.module.scss";
import RelatedBrains from "./components/RelatedBrains/RelatedBrains";
import Sources from "./components/Sources/Sources";
import { ChatMessage } from "../../types";
const DataPanel = (): JSX.Element => {
const { messages } = useChatContext();
const [lastMessageRelatedBrain, setLastMessageRelatedBrain] = useState<
CloseBrain[]
>([]);
const [lastMessageMetadata, setLastMessageMetadata] =
useState<MessageMetadata>();
useEffect(() => {
if (messages.length > 0) {
const lastMessage = messages[messages.length - 1];
if (lastMessage?.metadata?.close_brains) {
setLastMessageRelatedBrain(lastMessage.metadata.close_brains);
}
const lastMessage: ChatMessage = messages[messages.length - 1];
const newSources: Source[] = (lastMessage.metadata?.sources ?? []).map(
(source: Source) => ({
...source,
frequency: 0,
})
);
const updatedSources: Source[] = [];
newSources.forEach((newSource) => {
const existingSourceIndex = updatedSources.findIndex(
(source) => source.source_url === newSource.source_url
);
if (existingSourceIndex !== -1) {
updatedSources[existingSourceIndex] = {
...updatedSources[existingSourceIndex],
frequency: updatedSources[existingSourceIndex].frequency + 1,
};
} else {
updatedSources.push(newSource);
}
});
updatedSources.sort((a, b) => b.frequency - a.frequency);
setLastMessageMetadata({
closeBrains: lastMessage.metadata?.close_brains ?? [],
sources: updatedSources,
});
}
}, [lastMessageRelatedBrain, messages]);
}, [messages]);
return (
<div className={styles.data_panel_wrapper}>
<RelatedBrains closeBrains={lastMessageRelatedBrain} />
<RelatedBrains closeBrains={lastMessageMetadata?.closeBrains} />
<Sources sources={lastMessageMetadata?.sources} />
</div>
);
};

View File

@ -20,6 +20,7 @@
display: flex;
align-items: center;
gap: Spacings.$spacing03;
overflow: hidden;
.copy_icon {
visibility: hidden;

View File

@ -8,7 +8,7 @@ import { CloseBrain } from "@/lib/types/MessageMetadata";
import styles from "./RelatedBrains.module.scss";
interface RelatedBrainsProps {
closeBrains: CloseBrain[];
closeBrains?: CloseBrain[];
}
interface CloseBrainProps {
@ -26,31 +26,32 @@ const RelatedBrains = ({ closeBrains }: RelatedBrainsProps): JSX.Element => {
};
useEffect(() => {
const newProps = closeBrains.map((brain) => {
const t = Math.pow(brain.similarity, 2);
const r = Math.round(lerp(211, 138, t));
const g = Math.round(lerp(211, 43, t));
const b = Math.round(lerp(211, 226, t));
const isCurrentBrain =
brain.id === messages[messages.length - 1]?.brain_id;
if (closeBrains) {
const newProps = closeBrains.map((brain) => {
const t = Math.pow(brain.similarity, 2);
const r = Math.round(lerp(211, 138, t));
const g = Math.round(lerp(211, 43, t));
const b = Math.round(lerp(211, 226, t));
const isCurrentBrain =
brain.id === messages[messages.length - 1]?.brain_id;
return { color: `rgb(${r}, ${g}, ${b})`, isCurrentBrain: isCurrentBrain };
});
setCloseBrainProps(newProps);
return {
color: `rgb(${r}, ${g}, ${b})`,
isCurrentBrain: isCurrentBrain,
};
});
setCloseBrainProps(newProps);
}
}, [closeBrains, messages.length]);
if (closeBrains.length === 0) {
return <></>;
}
return (
<FoldableSection
label="Related Brains (Beta)"
icon="brain"
foldedByDefault={true}
foldedByDefault={closeBrains?.length === 0}
>
<div className={styles.close_brains_wrapper}>
{closeBrains.map((brain, index) => (
{closeBrains?.map((brain, index) => (
<div className={styles.brain_line} key={index}>
<div className={styles.left}>
<div className={styles.copy_icon}>

View File

@ -0,0 +1,17 @@
@use "@/styles/Colors.module.scss";
@use "@/styles/Spacings.module.scss";
.sources_wrapper {
display: flex;
flex-direction: column;
padding-bottom: Spacings.$spacing03;
gap: Spacings.$spacing01;
overflow: hidden;
.source {
padding-inline: Spacings.$spacing05;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}

View File

@ -0,0 +1,35 @@
import { FoldableSection } from "@/lib/components/ui/FoldableSection/FoldableSection";
import { Source } from "@/lib/types/MessageMetadata";
import styles from "./Sources.module.scss";
interface SourcesProps {
sources?: Source[];
}
const Sources = ({ sources }: SourcesProps): JSX.Element => {
return (
<FoldableSection
label="Sources"
icon="file"
foldedByDefault={sources?.length === 0}
>
<div className={styles.sources_wrapper}>
{sources?.map((source, index) => (
<div className={styles.source_wrapper} key={index}>
<a
href={source.source_url}
key={index}
target="_blank"
rel="noopener noreferrer"
>
<div className={styles.source}>{source.name}</div>
</a>
</div>
))}
</div>
</FoldableSection>
);
};
export default Sources;

View File

@ -1,6 +1,6 @@
import { UUID } from "crypto";
import { CloseBrain } from "@/lib/types/MessageMetadata";
import { CloseBrain, Source } from "@/lib/types/MessageMetadata";
export type ChatQuestion = {
model?: string;
@ -20,7 +20,7 @@ export type ChatMessage = {
brain_name?: string;
brain_id?: UUID;
metadata?: {
sources?: [string];
sources?: Source[];
close_brains?: CloseBrain[];
};
};

View File

@ -7,6 +7,7 @@ import {
LuChevronDown,
LuChevronRight,
LuCopy,
LuFile,
LuPlusCircle,
LuSearch,
} from "react-icons/lu";
@ -19,6 +20,7 @@ export const iconList: { [name: string]: IconType } = {
chevronDown: LuChevronDown,
chevronRight: LuChevronRight,
copy: LuCopy,
file: LuFile,
followUp: FaArrowUpFromBracket,
hastag: RiHashtag,
loader: AiOutlineLoading3Quarters,

View File

@ -4,6 +4,14 @@ export interface CloseBrain {
name: string;
}
export interface Source {
frequency: number;
name: string;
source_url: string;
type: string;
}
export interface MessageMetadata {
closeBrains: CloseBrain[];
sources: Source[];
}