mirror of
https://github.com/StanGirard/quivr.git
synced 2024-11-26 12:55:01 +03:00
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:
parent
37b9901fe8
commit
053ed0961d
@ -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[];
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
@ -20,6 +20,7 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: Spacings.$spacing03;
|
||||
overflow: hidden;
|
||||
|
||||
.copy_icon {
|
||||
visibility: hidden;
|
||||
|
@ -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}>
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
@ -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[];
|
||||
};
|
||||
};
|
||||
|
@ -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,
|
||||
|
@ -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[];
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user