mirror of
https://github.com/Mintplex-Labs/anything-llm.git
synced 2025-03-21 01:22:23 +00:00
[FEAT] show chunk score on citations (#773)
* show similarity score in citation chunks * refactor combine like sources to handle separate similarity scores and improve UI for displaying chunks * fix parseChunkSource function to work with links * destructure properties in chunks mapping * check chunk length in parseChunkSource * change UI on how score is shown * remove duplicate import --------- Co-authored-by: timothycarambat <rambat1010@gmail.com>
This commit is contained in:
parent
80ced5eba4
commit
c87ef5b674
2 changed files with 58 additions and 16 deletions
frontend/src
|
@ -1,31 +1,37 @@
|
||||||
import { memo, useState } from "react";
|
import { memo, useState } from "react";
|
||||||
import { v4 } from "uuid";
|
import { v4 } from "uuid";
|
||||||
import { decode as HTMLDecode } from "he";
|
import { decode as HTMLDecode } from "he";
|
||||||
import { CaretRight, FileText } from "@phosphor-icons/react";
|
|
||||||
import truncate from "truncate";
|
import truncate from "truncate";
|
||||||
import ModalWrapper from "@/components/ModalWrapper";
|
import ModalWrapper from "@/components/ModalWrapper";
|
||||||
import { middleTruncate } from "@/utils/directories";
|
import { middleTruncate } from "@/utils/directories";
|
||||||
import {
|
import {
|
||||||
|
CaretRight,
|
||||||
|
FileText,
|
||||||
|
Info,
|
||||||
ArrowSquareOut,
|
ArrowSquareOut,
|
||||||
GithubLogo,
|
GithubLogo,
|
||||||
Link,
|
Link,
|
||||||
X,
|
X,
|
||||||
YoutubeLogo,
|
YoutubeLogo,
|
||||||
} from "@phosphor-icons/react";
|
} from "@phosphor-icons/react";
|
||||||
|
import { Tooltip } from "react-tooltip";
|
||||||
|
import { toPercentString } from "@/utils/numbers";
|
||||||
|
|
||||||
function combineLikeSources(sources) {
|
function combineLikeSources(sources) {
|
||||||
const combined = {};
|
const combined = {};
|
||||||
sources.forEach((source) => {
|
sources.forEach((source) => {
|
||||||
const { id, title, text, chunkSource = "" } = source;
|
const { id, title, text, chunkSource = "", score = null } = source;
|
||||||
if (combined.hasOwnProperty(title)) {
|
if (combined.hasOwnProperty(title)) {
|
||||||
combined[title].text += `\n\n ---- Chunk ${id || ""} ---- \n\n${text}`;
|
combined[title].chunks.push({ id, text, chunkSource, score });
|
||||||
combined[title].references += 1;
|
combined[title].references += 1;
|
||||||
combined[title].chunkSource = chunkSource;
|
|
||||||
} else {
|
} else {
|
||||||
combined[title] = { title, text, chunkSource, references: 1 };
|
combined[title] = {
|
||||||
|
title,
|
||||||
|
chunks: [{ id, text, chunkSource, score }],
|
||||||
|
references: 1,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return Object.values(combined);
|
return Object.values(combined);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,7 +115,7 @@ function SkeletonLine() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function CitationDetailModal({ source, onClose }) {
|
function CitationDetailModal({ source, onClose }) {
|
||||||
const { references, title, text } = source;
|
const { references, title, chunks } = source;
|
||||||
const { isUrl, text: webpageUrl, href: linkTo } = parseChunkSource(source);
|
const { isUrl, text: webpageUrl, href: linkTo } = parseChunkSource(source);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -156,12 +162,39 @@ function CitationDetailModal({ source, onClose }) {
|
||||||
{[...Array(3)].map((_, idx) => (
|
{[...Array(3)].map((_, idx) => (
|
||||||
<SkeletonLine key={idx} />
|
<SkeletonLine key={idx} />
|
||||||
))}
|
))}
|
||||||
<p className="text-white whitespace-pre-line">{HTMLDecode(text)}</p>
|
{chunks.map(({ text, score }, idx) => (
|
||||||
<div className="mb-6">
|
<div key={idx} className="pt-6 text-white">
|
||||||
{[...Array(3)].map((_, idx) => (
|
<div className="flex flex-col w-full justify-start pb-6 gap-y-1">
|
||||||
<SkeletonLine key={idx} />
|
<p className="text-white whitespace-pre-line">
|
||||||
))}
|
{HTMLDecode(text)}
|
||||||
</div>
|
</p>
|
||||||
|
|
||||||
|
{!!score && (
|
||||||
|
<>
|
||||||
|
<div className="w-full flex items-center text-xs text-white/60 gap-x-2 cursor-default">
|
||||||
|
<div
|
||||||
|
data-tooltip-id="similarity-score"
|
||||||
|
data-tooltip-content={`This is the semantic similarity score of this chunk of text compared to your query calculated by the vector database.`}
|
||||||
|
className="flex items-center gap-x-1"
|
||||||
|
>
|
||||||
|
<Info size={14} />
|
||||||
|
<p>{toPercentString(score)} match</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Tooltip
|
||||||
|
id="similarity-score"
|
||||||
|
place="top"
|
||||||
|
delayShow={100}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{[...Array(3)].map((_, idx) => (
|
||||||
|
<SkeletonLine key={idx} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
<div className="mb-6"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -180,7 +213,7 @@ const ICONS = {
|
||||||
// which contain valid outbound links that can be clicked by the
|
// which contain valid outbound links that can be clicked by the
|
||||||
// user when viewing a citation. Optionally allows various icons
|
// user when viewing a citation. Optionally allows various icons
|
||||||
// to show distinct types of sources.
|
// to show distinct types of sources.
|
||||||
function parseChunkSource({ title = "", chunkSource = "" }) {
|
function parseChunkSource({ title = "", chunks = [] }) {
|
||||||
const nullResponse = {
|
const nullResponse = {
|
||||||
isUrl: false,
|
isUrl: false,
|
||||||
text: null,
|
text: null,
|
||||||
|
@ -188,9 +221,10 @@ function parseChunkSource({ title = "", chunkSource = "" }) {
|
||||||
icon: "file",
|
icon: "file",
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!chunkSource.startsWith("link://")) return nullResponse;
|
if (!chunks.length || !chunks[0].chunkSource.startsWith("link://"))
|
||||||
|
return nullResponse;
|
||||||
try {
|
try {
|
||||||
const url = new URL(chunkSource.split("link://")[1]);
|
const url = new URL(chunks[0].chunkSource.split("link://")[1]);
|
||||||
let text = url.host + url.pathname;
|
let text = url.host + url.pathname;
|
||||||
let icon = "link";
|
let icon = "link";
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,14 @@ export function dollarFormat(input) {
|
||||||
}).format(input);
|
}).format(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function toPercentString(input = null, decimals = 0) {
|
||||||
|
if (isNaN(input) || input === null) return "";
|
||||||
|
const percentage = Math.round(input * 100);
|
||||||
|
return (
|
||||||
|
(decimals > 0 ? percentage.toFixed(decimals) : percentage.toString()) + "%"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function humanFileSize(bytes, si = false, dp = 1) {
|
export function humanFileSize(bytes, si = false, dp = 1) {
|
||||||
const thresh = si ? 1000 : 1024;
|
const thresh = si ? 1000 : 1024;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue