mirror of
https://github.com/wassname/stampy-chat.git
synced 2026-09-11 12:50:34 +08:00
Merge pull request #73 from StampyAI/glossary
Inject glossary into generated answers
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
import { createContext, useContext } from "react";
|
||||
|
||||
type GlossaryItem = {
|
||||
term: string;
|
||||
pageid: string;
|
||||
contents: string;
|
||||
};
|
||||
|
||||
export type Glossary = Map<string, GlossaryItem>;
|
||||
|
||||
export const GlossaryContext = createContext<{g: Glossary, r: RegExp} | null>(null);
|
||||
|
||||
// A component which wraps arbitrary html in a span, and injects glossary terms
|
||||
// into it as hoverable pop-up links. The text is immediately rendered normally,
|
||||
// but after the glossary is loaded (which happens once per page, asynchronously),
|
||||
// the glossary terms are replaced with elements.
|
||||
|
||||
export const GlossarySpan: React.FC<{content: string}> = ({content}) => {
|
||||
|
||||
const g = useContext(GlossaryContext);
|
||||
|
||||
// If the glossary hasn't loaded yet, just render the text normally.
|
||||
if (g == null) {
|
||||
return <span dangerouslySetInnerHTML={{__html: content}} />;
|
||||
}
|
||||
|
||||
const glossary = g.g;
|
||||
const glossaryRegex = g.r;
|
||||
|
||||
// Otherwise, replace glossary terms with links. We can do this in
|
||||
// O(n * sum of term lengths) by finding String.prototype.indexOf of
|
||||
// each term in the glossary (since that'd probably be backed by KMP)
|
||||
// but I think it should be faster to compile a regex state machine
|
||||
// once and use that instead.
|
||||
|
||||
return <span dangerouslySetInnerHTML={{__html: content.replace(glossaryRegex!, (match) => {
|
||||
|
||||
const item = glossary.get(match.toLowerCase());
|
||||
if (item == undefined) return match;
|
||||
|
||||
const hover_content = item.contents;
|
||||
const pageid = item.pageid;
|
||||
|
||||
if (pageid == undefined || pageid.trim() == "") {
|
||||
return `
|
||||
<div class="glossary-hover" nowrap>${hover_content}</div>
|
||||
<span class="glossary-link">${match}</span>
|
||||
`;
|
||||
} else {
|
||||
return `
|
||||
<div class="glossary-hover" nowrap>${hover_content}</div>
|
||||
<a href="https://aisafety.info/?state=${pageid}"
|
||||
target="_blank"
|
||||
class="glossary-link">
|
||||
${match}
|
||||
</a>
|
||||
`;
|
||||
}
|
||||
|
||||
})}} />;
|
||||
}
|
||||
+42
-1
@@ -1,9 +1,50 @@
|
||||
import { type AppType } from "next/dist/shared/lib/utils";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
import "~/styles/globals.css";
|
||||
|
||||
import { Glossary, GlossaryContext } from "../glossary";
|
||||
|
||||
const MyApp: AppType = ({ Component, pageProps }) => {
|
||||
return <Component {...pageProps} />;
|
||||
const [glossary, setGlossary] = useState<{ g: Glossary, r: RegExp } | null>(null);
|
||||
|
||||
// fetch glossary and compile regex once on load
|
||||
useEffect(() => {
|
||||
if (glossary === null)
|
||||
tempHackFetch("/questions/glossary")
|
||||
.then((res) => res.json())
|
||||
.then((data) => {
|
||||
const glossary: Glossary = new Map(Object.entries(data));
|
||||
const keys = Array.from(glossary.keys())
|
||||
.sort((a, b) => b.length - a.length) // sort by length descending
|
||||
.map((k) => k.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&')) // escape regex chars
|
||||
.map((k) => `\\b${k}\\b`); // add word boundaries
|
||||
|
||||
const regex = new RegExp(keys.join("|"), "gim");
|
||||
setGlossary({ g: glossary, r: regex });
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<GlossaryContext.Provider value={glossary}>
|
||||
<Component {...pageProps} />
|
||||
</GlossaryContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export default MyApp;
|
||||
|
||||
// ------------------- hack until server endpoint is working -------------------
|
||||
|
||||
const GLOSSARY_JSON = {"chain of thought prompting":{"term":"chain of thought prompting","pageid":"8EL7","contents":"<p>Chain-of-thought prompting is a technique which makes a language model generate intermediate reasoning steps in its output.</p>\n"},"chain-of-thought":{"term":"chain-of-thought","pageid":"8EL7","contents":"<p>Chain-of-thought prompting is a technique which makes a language model generate intermediate reasoning steps in its output.</p>\n"},"goodhart's law":{"term":"goodhart's law","pageid":"8185","contents":"<p>Goodhart’s law states that when a measure becomes a target, it ceases to be a good measure.</p>\n"},"the big g,":{"term":"the big g,","pageid":"8185","contents":"<p>Goodhart’s law states that when a measure becomes a target, it ceases to be a good measure.</p>\n"},"terminal goals":{"term":"terminal goals","pageid":"","contents":"<p>Goals which are valued as ends in themselves, rather than as instrumental to something else.</p>\n"},"terminal goal":{"term":"terminal goal","pageid":"","contents":"<p>Goals which are valued as ends in themselves, rather than as instrumental to something else.</p>\n"},"orthogonality thesis":{"term":"orthogonality thesis","pageid":"6568","contents":"<p>The thesis that any level of intelligence is compatible with any terminal goals. This implies that intelligence alone is not enough to make a system moral.</p>\n"},"instrumental convergence":{"term":"instrumental convergence","pageid":"897I","contents":"<p>Instrumental convergence is the idea that different AI agents, each with distinct terminal goals, will end up adopting many of the same instrumental goals.</p>\n"},"instrumentally convergent goals":{"term":"instrumentally convergent goals","pageid":"897I","contents":"<p>Instrumental convergence is the idea that different AI agents, each with distinct terminal goals, will end up adopting many of the same instrumental goals.</p>\n"},"llm":{"term":"llm","pageid":"","contents":"<p>A large language model is an AI model which has been trained on a large body of text, in order to produce texts in a human-like way.</p>\n"},"large language model":{"term":"large language model","pageid":"","contents":"<p>A large language model is an AI model which has been trained on a large body of text, in order to produce texts in a human-like way.</p>\n"},"goal misgeneralization":{"term":"goal misgeneralization","pageid":"","contents":"<p>pursuing a different goal during deployment from the one that was pursued during training due to distribution shift</p>\n"},"interpretability":{"term":"interpretability","pageid":"8241","contents":"<p>Interpretability is an area of alignment research that aims to make machine learning systems easier for humans to understand.</p>\n"},"existential risk":{"term":"existential risk","pageid":"89LL","contents":"<p>risks that threaten the destruction of humanity's long-term potential, including human extinction</p>\n"}}
|
||||
|
||||
const tempHackFetch = (_url: string) => {
|
||||
return new Promise<Response>((resolve, _reject) => {
|
||||
setTimeout(() => {
|
||||
resolve({
|
||||
ok: true,
|
||||
json: () => Promise.resolve(GLOSSARY_JSON),
|
||||
} as unknown as Response);
|
||||
}, 1000);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import Image from 'next/image';
|
||||
import Header from "../header";
|
||||
import { SearchBox, Followup } from "../searchbox";
|
||||
import logo from "../logo.svg"
|
||||
import { GlossarySpan } from "../glossary";
|
||||
|
||||
type Citation = {
|
||||
title: string;
|
||||
@@ -205,7 +206,7 @@ const ShowAssistantEntry: React.FC<{entry: AssistantEntry}> = ({entry}) => {
|
||||
response.split("\n").map(paragraph => ( <p> {
|
||||
paragraph.split(in_text_citation_regex).map((text, i) => {
|
||||
if (i % 2 === 0) {
|
||||
return text.trim();
|
||||
return <GlossarySpan content={text.trim()} />;
|
||||
}
|
||||
i = parseInt(text) - 1;
|
||||
if (!citations.has(i)) return `[${text}]`;
|
||||
@@ -542,7 +543,7 @@ const Home: NextPage = () => {
|
||||
maxWidth: "99.8%",
|
||||
}}
|
||||
>
|
||||
<div dangerouslySetInnerHTML={{__html: entry.content}} />
|
||||
<div><GlossarySpan content={entry.content} /></div>
|
||||
<div className="mb-3 flex justify-end">
|
||||
<a href={entry.url} target="_blank"
|
||||
className="flex items-center space-x-1">
|
||||
|
||||
@@ -37,3 +37,23 @@ ol {
|
||||
@apply list-decimal;
|
||||
}
|
||||
|
||||
|
||||
/* glossary terms with a definition that shows on hover */
|
||||
.glossary-hover {
|
||||
position: absolute;
|
||||
display: none;
|
||||
width: 300px;
|
||||
transform: translateY(1.7rem);
|
||||
@apply bg-white hover:bg-gray-300 h-fit px-3;
|
||||
@apply border border-gray-300;
|
||||
@apply text-black;
|
||||
}
|
||||
|
||||
/* .glossary-link:hover + .glossary-hover { */
|
||||
.glossary-hover:has(+ .glossary-link:hover) {
|
||||
display: initial;
|
||||
}
|
||||
|
||||
.glossary-link {
|
||||
@apply underline hover:no-underline;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user