mirror of
https://github.com/StanGirard/quivr.git
synced 2024-09-11 13:15:41 +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 React from "react";
|
||||||
|
|
||||||
import Icon from "@/lib/components/ui/Icon/Icon";
|
import Icon from "@/lib/components/ui/Icon/Icon";
|
||||||
|
import { Source } from "@/lib/types/MessageMetadata";
|
||||||
|
|
||||||
import styles from "./MessageRow.module.scss";
|
import styles from "./MessageRow.module.scss";
|
||||||
import { CopyButton } from "./components/CopyButton";
|
import { CopyButton } from "./components/CopyButton";
|
||||||
@ -16,7 +17,7 @@ type MessageRowProps = {
|
|||||||
promptName?: string | null;
|
promptName?: string | null;
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
metadata?: {
|
metadata?: {
|
||||||
sources?: [string] | [];
|
sources?: Source[];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -3,29 +3,58 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
import { useChatContext } from "@/lib/context";
|
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 styles from "./DataPanel.module.scss";
|
||||||
import RelatedBrains from "./components/RelatedBrains/RelatedBrains";
|
import RelatedBrains from "./components/RelatedBrains/RelatedBrains";
|
||||||
|
import Sources from "./components/Sources/Sources";
|
||||||
|
|
||||||
|
import { ChatMessage } from "../../types";
|
||||||
|
|
||||||
const DataPanel = (): JSX.Element => {
|
const DataPanel = (): JSX.Element => {
|
||||||
const { messages } = useChatContext();
|
const { messages } = useChatContext();
|
||||||
const [lastMessageRelatedBrain, setLastMessageRelatedBrain] = useState<
|
const [lastMessageMetadata, setLastMessageMetadata] =
|
||||||
CloseBrain[]
|
useState<MessageMetadata>();
|
||||||
>([]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (messages.length > 0) {
|
if (messages.length > 0) {
|
||||||
const lastMessage = messages[messages.length - 1];
|
const lastMessage: ChatMessage = messages[messages.length - 1];
|
||||||
if (lastMessage?.metadata?.close_brains) {
|
const newSources: Source[] = (lastMessage.metadata?.sources ?? []).map(
|
||||||
setLastMessageRelatedBrain(lastMessage.metadata.close_brains);
|
(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 (
|
return (
|
||||||
<div className={styles.data_panel_wrapper}>
|
<div className={styles.data_panel_wrapper}>
|
||||||
<RelatedBrains closeBrains={lastMessageRelatedBrain} />
|
<RelatedBrains closeBrains={lastMessageMetadata?.closeBrains} />
|
||||||
|
<Sources sources={lastMessageMetadata?.sources} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: Spacings.$spacing03;
|
gap: Spacings.$spacing03;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
.copy_icon {
|
.copy_icon {
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
|
@ -8,7 +8,7 @@ import { CloseBrain } from "@/lib/types/MessageMetadata";
|
|||||||
import styles from "./RelatedBrains.module.scss";
|
import styles from "./RelatedBrains.module.scss";
|
||||||
|
|
||||||
interface RelatedBrainsProps {
|
interface RelatedBrainsProps {
|
||||||
closeBrains: CloseBrain[];
|
closeBrains?: CloseBrain[];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CloseBrainProps {
|
interface CloseBrainProps {
|
||||||
@ -26,31 +26,32 @@ const RelatedBrains = ({ closeBrains }: RelatedBrainsProps): JSX.Element => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const newProps = closeBrains.map((brain) => {
|
if (closeBrains) {
|
||||||
const t = Math.pow(brain.similarity, 2);
|
const newProps = closeBrains.map((brain) => {
|
||||||
const r = Math.round(lerp(211, 138, t));
|
const t = Math.pow(brain.similarity, 2);
|
||||||
const g = Math.round(lerp(211, 43, t));
|
const r = Math.round(lerp(211, 138, t));
|
||||||
const b = Math.round(lerp(211, 226, t));
|
const g = Math.round(lerp(211, 43, t));
|
||||||
const isCurrentBrain =
|
const b = Math.round(lerp(211, 226, t));
|
||||||
brain.id === messages[messages.length - 1]?.brain_id;
|
const isCurrentBrain =
|
||||||
|
brain.id === messages[messages.length - 1]?.brain_id;
|
||||||
|
|
||||||
return { color: `rgb(${r}, ${g}, ${b})`, isCurrentBrain: isCurrentBrain };
|
return {
|
||||||
});
|
color: `rgb(${r}, ${g}, ${b})`,
|
||||||
setCloseBrainProps(newProps);
|
isCurrentBrain: isCurrentBrain,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
setCloseBrainProps(newProps);
|
||||||
|
}
|
||||||
}, [closeBrains, messages.length]);
|
}, [closeBrains, messages.length]);
|
||||||
|
|
||||||
if (closeBrains.length === 0) {
|
|
||||||
return <></>;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FoldableSection
|
<FoldableSection
|
||||||
label="Related Brains (Beta)"
|
label="Related Brains (Beta)"
|
||||||
icon="brain"
|
icon="brain"
|
||||||
foldedByDefault={true}
|
foldedByDefault={closeBrains?.length === 0}
|
||||||
>
|
>
|
||||||
<div className={styles.close_brains_wrapper}>
|
<div className={styles.close_brains_wrapper}>
|
||||||
{closeBrains.map((brain, index) => (
|
{closeBrains?.map((brain, index) => (
|
||||||
<div className={styles.brain_line} key={index}>
|
<div className={styles.brain_line} key={index}>
|
||||||
<div className={styles.left}>
|
<div className={styles.left}>
|
||||||
<div className={styles.copy_icon}>
|
<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 { UUID } from "crypto";
|
||||||
|
|
||||||
import { CloseBrain } from "@/lib/types/MessageMetadata";
|
import { CloseBrain, Source } from "@/lib/types/MessageMetadata";
|
||||||
|
|
||||||
export type ChatQuestion = {
|
export type ChatQuestion = {
|
||||||
model?: string;
|
model?: string;
|
||||||
@ -20,7 +20,7 @@ export type ChatMessage = {
|
|||||||
brain_name?: string;
|
brain_name?: string;
|
||||||
brain_id?: UUID;
|
brain_id?: UUID;
|
||||||
metadata?: {
|
metadata?: {
|
||||||
sources?: [string];
|
sources?: Source[];
|
||||||
close_brains?: CloseBrain[];
|
close_brains?: CloseBrain[];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -7,6 +7,7 @@ import {
|
|||||||
LuChevronDown,
|
LuChevronDown,
|
||||||
LuChevronRight,
|
LuChevronRight,
|
||||||
LuCopy,
|
LuCopy,
|
||||||
|
LuFile,
|
||||||
LuPlusCircle,
|
LuPlusCircle,
|
||||||
LuSearch,
|
LuSearch,
|
||||||
} from "react-icons/lu";
|
} from "react-icons/lu";
|
||||||
@ -19,6 +20,7 @@ export const iconList: { [name: string]: IconType } = {
|
|||||||
chevronDown: LuChevronDown,
|
chevronDown: LuChevronDown,
|
||||||
chevronRight: LuChevronRight,
|
chevronRight: LuChevronRight,
|
||||||
copy: LuCopy,
|
copy: LuCopy,
|
||||||
|
file: LuFile,
|
||||||
followUp: FaArrowUpFromBracket,
|
followUp: FaArrowUpFromBracket,
|
||||||
hastag: RiHashtag,
|
hastag: RiHashtag,
|
||||||
loader: AiOutlineLoading3Quarters,
|
loader: AiOutlineLoading3Quarters,
|
||||||
|
@ -4,6 +4,14 @@ export interface CloseBrain {
|
|||||||
name: string;
|
name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface Source {
|
||||||
|
frequency: number;
|
||||||
|
name: string;
|
||||||
|
source_url: string;
|
||||||
|
type: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface MessageMetadata {
|
export interface MessageMetadata {
|
||||||
closeBrains: CloseBrain[];
|
closeBrains: CloseBrain[];
|
||||||
|
sources: Source[];
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user