=> {
if (query_source === "search") {
const history = entries
.filter((entry) => entry.role !== "error")
@@ -205,13 +207,7 @@ export const runSearch = async (
content: entry.content.trim(),
}));
- return await queryLLM(
- query,
- mode,
- history,
- setCurrent,
- sessionId
- );
+ return await queryLLM(query, mode, history, setCurrent, sessionId);
} else {
// ----------------- HUMAN AUTHORED CONTENT RETRIEVAL ------------------
const [questionId] = query.split("\n", 2);
diff --git a/web/src/pages/_app.tsx b/web/src/pages/_app.tsx
index 792401c..c03e5ad 100644
--- a/web/src/pages/_app.tsx
+++ b/web/src/pages/_app.tsx
@@ -6,7 +6,9 @@ import "~/styles/globals.css";
import { Glossary, GlossaryContext } from "../components/glossary";
const MyApp: AppType = ({ Component, pageProps }) => {
- const [glossary, setGlossary] = useState<{ g: Glossary, r: RegExp } | null>(null);
+ const [glossary, setGlossary] = useState<{ g: Glossary; r: RegExp } | null>(
+ null
+ );
// fetch glossary and compile regex once on load
useEffect(() => {
@@ -16,9 +18,9 @@ const MyApp: AppType = ({ Component, pageProps }) => {
.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
+ .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 });
@@ -36,7 +38,92 @@ export default MyApp;
// ------------------- hack until server endpoint is working -------------------
-const GLOSSARY_JSON = {"chain of thought prompting":{"term":"chain of thought prompting","pageid":"8EL7","contents":"Chain-of-thought prompting is a technique which makes a language model generate intermediate reasoning steps in its output.
\n"},"chain-of-thought":{"term":"chain-of-thought","pageid":"8EL7","contents":"Chain-of-thought prompting is a technique which makes a language model generate intermediate reasoning steps in its output.
\n"},"goodhart's law":{"term":"goodhart's law","pageid":"8185","contents":"Goodhart’s law states that when a measure becomes a target, it ceases to be a good measure.
\n"},"the big g,":{"term":"the big g,","pageid":"8185","contents":"Goodhart’s law states that when a measure becomes a target, it ceases to be a good measure.
\n"},"terminal goals":{"term":"terminal goals","pageid":"","contents":"Goals which are valued as ends in themselves, rather than as instrumental to something else.
\n"},"terminal goal":{"term":"terminal goal","pageid":"","contents":"Goals which are valued as ends in themselves, rather than as instrumental to something else.
\n"},"orthogonality thesis":{"term":"orthogonality thesis","pageid":"6568","contents":"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.
\n"},"instrumental convergence":{"term":"instrumental convergence","pageid":"897I","contents":"Instrumental convergence is the idea that different AI agents, each with distinct terminal goals, will end up adopting many of the same instrumental goals.
\n"},"instrumentally convergent goals":{"term":"instrumentally convergent goals","pageid":"897I","contents":"Instrumental convergence is the idea that different AI agents, each with distinct terminal goals, will end up adopting many of the same instrumental goals.
\n"},"llm":{"term":"llm","pageid":"","contents":"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.
\n"},"large language model":{"term":"large language model","pageid":"","contents":"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.
\n"},"goal misgeneralization":{"term":"goal misgeneralization","pageid":"","contents":"pursuing a different goal during deployment from the one that was pursued during training due to distribution shift
\n"},"interpretability":{"term":"interpretability","pageid":"8241","contents":"Interpretability is an area of alignment research that aims to make machine learning systems easier for humans to understand.
\n"},"existential risk":{"term":"existential risk","pageid":"89LL","contents":"risks that threaten the destruction of humanity's long-term potential, including human extinction
\n"}}
+const GLOSSARY_JSON = {
+ "chain of thought prompting": {
+ term: "chain of thought prompting",
+ pageid: "8EL7",
+ contents:
+ "Chain-of-thought prompting is a technique which makes a language model generate intermediate reasoning steps in its output.
\n",
+ },
+ "chain-of-thought": {
+ term: "chain-of-thought",
+ pageid: "8EL7",
+ contents:
+ "Chain-of-thought prompting is a technique which makes a language model generate intermediate reasoning steps in its output.
\n",
+ },
+ "goodhart's law": {
+ term: "goodhart's law",
+ pageid: "8185",
+ contents:
+ "Goodhart’s law states that when a measure becomes a target, it ceases to be a good measure.
\n",
+ },
+ "the big g,": {
+ term: "the big g,",
+ pageid: "8185",
+ contents:
+ "Goodhart’s law states that when a measure becomes a target, it ceases to be a good measure.
\n",
+ },
+ "terminal goals": {
+ term: "terminal goals",
+ pageid: "",
+ contents:
+ "Goals which are valued as ends in themselves, rather than as instrumental to something else.
\n",
+ },
+ "terminal goal": {
+ term: "terminal goal",
+ pageid: "",
+ contents:
+ "Goals which are valued as ends in themselves, rather than as instrumental to something else.
\n",
+ },
+ "orthogonality thesis": {
+ term: "orthogonality thesis",
+ pageid: "6568",
+ contents:
+ "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.
\n",
+ },
+ "instrumental convergence": {
+ term: "instrumental convergence",
+ pageid: "897I",
+ contents:
+ "Instrumental convergence is the idea that different AI agents, each with distinct terminal goals, will end up adopting many of the same instrumental goals.
\n",
+ },
+ "instrumentally convergent goals": {
+ term: "instrumentally convergent goals",
+ pageid: "897I",
+ contents:
+ "Instrumental convergence is the idea that different AI agents, each with distinct terminal goals, will end up adopting many of the same instrumental goals.
\n",
+ },
+ llm: {
+ term: "llm",
+ pageid: "",
+ contents:
+ "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.
\n",
+ },
+ "large language model": {
+ term: "large language model",
+ pageid: "",
+ contents:
+ "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.
\n",
+ },
+ "goal misgeneralization": {
+ term: "goal misgeneralization",
+ pageid: "",
+ contents:
+ "pursuing a different goal during deployment from the one that was pursued during training due to distribution shift
\n",
+ },
+ interpretability: {
+ term: "interpretability",
+ pageid: "8241",
+ contents:
+ "Interpretability is an area of alignment research that aims to make machine learning systems easier for humans to understand.
\n",
+ },
+ "existential risk": {
+ term: "existential risk",
+ pageid: "89LL",
+ contents:
+ "risks that threaten the destruction of humanity's long-term potential, including human extinction
\n",
+ },
+};
const tempHackFetch = (_url: string) => {
return new Promise((resolve, _reject) => {
@@ -47,4 +134,4 @@ const tempHackFetch = (_url: string) => {
} as unknown as Response);
}, 1000);
});
-}
+};
diff --git a/web/src/pages/index.tsx b/web/src/pages/index.tsx
index 33c46f9..aa584f4 100644
--- a/web/src/pages/index.tsx
+++ b/web/src/pages/index.tsx
@@ -1,21 +1,22 @@
import { type NextPage } from "next";
import { useState, useEffect } from "react";
import Link from "next/link";
-import Image from 'next/image';
+import Image from "next/image";
-import Page from "../components/page"
-import { API_URL } from "../settings"
+import Page from "../components/page";
+import { API_URL } from "../settings";
import { queryLLM, getStampyContent, runSearch } from "../hooks/useSearch";
import type {
- CurrentSearch,
- Citation,
- Entry,
- UserEntry,
- AssistantEntry as AssistantEntryType,
- ErrorMessage,
- StampyMessage
+ CurrentSearch,
+ Citation,
+ Entry,
+ UserEntry,
+ AssistantEntry as AssistantEntryType,
+ ErrorMessage,
+ StampyMessage,
+ Followup,
} from "../types";
-import { SearchBox, Followup } from "../components/searchbox";
+import { SearchBox } from "../components/searchbox";
import { GlossarySpan } from "../components/glossary";
import { Controls, Mode } from "../components/controls";
import { AssistantEntry } from "../components/assistant";
@@ -23,36 +24,36 @@ import { Entry as EntryTag } from "../components/entry";
const MAX_FOLLOWUPS = 4;
-type State = {
- state: "idle";
-} | {
- state: "loading";
- phase: "semantic" | "prompt" | "llm";
- citations: Citation[];
-} | {
- state: "streaming";
- response: AssistantEntryType;
-};
-
-type Mode = "rookie" | "concise" | "default";
-
+type State =
+ | {
+ state: "idle";
+ }
+ | {
+ state: "loading";
+ phase: "semantic" | "prompt" | "llm";
+ citations: Citation[];
+ }
+ | {
+ state: "streaming";
+ response: AssistantEntryType;
+ };
// smooth-scroll to the bottom of the window if we're already less than 30% a screen away
// note: finicky interaction with "smooth" - maybe fix later.
function scroll30() {
- if (
- document.documentElement.scrollHeight - window.scrollY >
- window.innerHeight * 1.3
- )
- return;
- window.scrollTo({ top: document.body.scrollHeight, behavior: "smooth" });
+ if (
+ document.documentElement.scrollHeight - window.scrollY >
+ window.innerHeight * 1.3
+ )
+ return;
+ window.scrollTo({ top: document.body.scrollHeight, behavior: "smooth" });
}
const Home: NextPage = () => {
const [entries, setEntries] = useState([]);
const [current, setCurrent] = useState();
- const [sessionId, setSessionId] = useState()
- const [citations, setCitations] = useState([])
+ const [sessionId, setSessionId] = useState("");
+ const [citations, setCitations] = useState([]);
// [state, ready to save to localstorage]
const [mode, setMode] = useState<[Mode, boolean]>(["default", false]);
@@ -60,12 +61,11 @@ const Home: NextPage = () => {
// store mode in localstorage
useEffect(() => {
if (mode[1]) localStorage.setItem("chat_mode", mode[0]);
-
}, [mode]);
// initial load
useEffect(() => {
- const mode = localStorage.getItem("chat_mode") as Mode || "default";
+ const mode = (localStorage.getItem("chat_mode") as Mode) || "default";
setMode([mode, true]);
setSessionId(crypto.randomUUID());
}, []);
@@ -77,38 +77,45 @@ const Home: NextPage = () => {
}
};
- const updateCitations = (allCitations: Citation[], current: CurrentSearch) => {
- const entryCitations = Array.from(current.citationsMap.values());
- if (!entryCitations.some(c => !c.index)) {
+ const updateCitations = (
+ allCitations: Citation[],
+ current: CurrentSearch
+ ) => {
+ if (!current) return;
+
+ const entryCitations = Array.from(
+ current.citationsMap.values()
+ ) as Citation[];
+ if (!entryCitations.some((c) => !c.index)) {
// All of the entries citations have indexes, so there weren't any changes since the last check
- return
+ return;
}
// Get a mapping of all known citations, so as to reuse them if they appear again
- const citationsMapping = Object.fromEntries(allCitations.map(c => ([c.title + c.url, c.index])));
+ const citationsMapping = Object.fromEntries(
+ allCitations.map((c) => [c.title + c.url, c.index])
+ );
- entryCitations.forEach(
- (c) => {
- const hash = c.title + c.url;
- if (!citationsMapping[hash]) {
- c.index = allCitations.length + 1;
- allCitations.push(c);
- } else {
- c.index = citationsMapping[hash];
- }
+ entryCitations.forEach((c) => {
+ const hash = c.title + c.url;
+ const index = citationsMapping[hash];
+ if (index !== undefined) {
+ c.index = index;
+ } else {
+ c.index = allCitations.length + 1;
+ allCitations.push(c);
}
- )
- setCitations(allCitations)
- setCurrent(current)
- }
+ });
+ setCitations(allCitations);
+ setCurrent(current);
+ };
const search = async (
query: string,
query_source: "search" | "followups",
disable: () => void,
- enable: (f_set: Followup[] | ((fs: Followup[]) => Followup[])) => void,
+ enable: (f_set: Followup[] | ((fs: Followup[]) => Followup[])) => void
) => {
-
// clear the query box, append to entries
const userEntry: Entry = {
role: "user",
@@ -123,7 +130,7 @@ const Home: NextPage = () => {
mode[0],
entries,
updateCurrent,
- sessionId,
+ sessionId
);
setCurrent(undefined);
@@ -144,14 +151,16 @@ const Home: NextPage = () => {
last_entry = Loading: Waiting for LLM...
;
break;
case "streaming":
- updateCitations(citations, current)
+ updateCitations(citations, current);
last_entry = ;
break;
case "followups":
- last_entry = <>
-
- Checking for followups...
- >;
+ last_entry = (
+ <>
+
+ Checking for followups...
+ >
+ );
break;
}
@@ -159,8 +168,13 @@ const Home: NextPage = () => {
- WARNING : This is a very early prototype . Feedback welcomed.
-
+
+ WARNING : This is a very early prototype .{" "}
+
+ Feedback
+ {" "}
+ welcomed.
+
{entries.map((entry, i) => (
@@ -168,8 +182,7 @@ const Home: NextPage = () => {
))}
- { last_entry }
-
+ {last_entry}
);
diff --git a/web/src/pages/semantic.tsx b/web/src/pages/semantic.tsx
index f87bdc0..e50c07c 100644
--- a/web/src/pages/semantic.tsx
+++ b/web/src/pages/semantic.tsx
@@ -1,28 +1,29 @@
import { type NextPage } from "next";
-import React from "react";
-import Page from "../components/page"
-import { SearchBox, Followup } from "../components/searchbox";
-import { useState } from "react";
-import { API_URL } from "../settings"
+import React, { useState } from "react";
+import { API_URL } from "../settings";
+import type { Followup } from "../types";
+import Page from "../components/page";
+import { SearchBox } from "../components/searchbox";
const Semantic: NextPage = () => {
-
const [results, setResults] = useState([]);
const semantic_search = async (
query: string,
_query_source: "search" | "followups",
disable: () => void,
- enable: (f_set: Followup[]) => void,
+ enable: (f_set: Followup[]) => void
) => {
-
disable();
const res = await fetch(API_URL + "/semantic", {
method: "POST",
- headers: { "Content-Type": "application/json", "Access-Control-Allow-Origin": "*", },
- body: JSON.stringify({query: query}),
- })
+ headers: {
+ "Content-Type": "application/json",
+ "Access-Control-Allow-Origin": "*",
+ },
+ body: JSON.stringify({ query: query }),
+ });
if (!res.ok) {
enable([]);
@@ -33,7 +34,6 @@ const Semantic: NextPage = () => {
setResults(data);
enable([]);
-
};
return (
@@ -65,28 +65,31 @@ type SemanticEntry = {
text: string;
};
-const ShowSemanticEntry: React.FC<{entry: SemanticEntry}> = ({entry}) => {
-
+const ShowSemanticEntry: React.FC<{ entry: SemanticEntry }> = ({ entry }) => {
return (
-
{/* horizontally split first row, title on left, authors on right */}
-
{entry.title}
-
{entry.authors.join(', ')} - {entry.date}
+
{entry.title}
+
+ {entry.authors.join(", ")} - {entry.date}
+
- { entry.text.split("\n").map((paragraph, i) => {
+ {entry.text.split("\n").map((paragraph, i) => {
const p = paragraph.trim();
if (p === "") return <>>;
if (p === ".....") return
;
- return
{paragraph}
- })
- }
+ return (
+
+ {" "}
+ {paragraph}{" "}
+
+ );
+ })}
Read more
);
};
-
export default Semantic;
diff --git a/web/src/settings.ts b/web/src/settings.ts
index abb4211..5dd7442 100644
--- a/web/src/settings.ts
+++ b/web/src/settings.ts
@@ -1,3 +1,5 @@
-export const API_URL = process.env.NEXT_PUBLIC_API_URL || "http://127.0.0.1:3001";
-export const STAMPY_URL = process.env.STAMPY_URL || "https://aisafety.info"
-export const STAMPY_CONTENT_URL = process.env.STAMPY_CONTENT_URL || `${API_URL}/human`
+export const API_URL =
+ process.env.NEXT_PUBLIC_API_URL || "http://127.0.0.1:3001";
+export const STAMPY_URL = process.env.STAMPY_URL || "https://aisafety.info";
+export const STAMPY_CONTENT_URL =
+ process.env.STAMPY_CONTENT_URL || `${API_URL}/human`;
diff --git a/web/src/styles/globals.css b/web/src/styles/globals.css
index c9c3612..314f607 100644
--- a/web/src/styles/globals.css
+++ b/web/src/styles/globals.css
@@ -3,11 +3,11 @@
@tailwind utilities;
h1 {
- @apply text-4xl font-bold my-4;
+ @apply my-4 text-4xl font-bold;
}
h2 {
- @apply text-xl font-semibold my-4;
+ @apply my-4 text-xl font-semibold;
}
main {
@@ -28,7 +28,7 @@ p {
}
button {
- @apply bg-white hover:bg-gray-300 px-0.5 py-0 w-fit h-fit;
+ @apply h-fit w-fit bg-white px-0.5 py-0 hover:bg-gray-300;
@apply border border-gray-300;
@apply text-gray-700;
}
@@ -37,14 +37,13 @@ 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 h-fit bg-white px-3 hover:bg-gray-300;
@apply border border-gray-300;
@apply text-black;
}
diff --git a/web/src/types.ts b/web/src/types.ts
index 5cafe64..b7fe0c4 100644
--- a/web/src/types.ts
+++ b/web/src/types.ts
@@ -1,38 +1,44 @@
export type Citation = {
- title: string;
- authors: string[];
- date: string;
- url: string;
- index: number;
-}
+ title: string;
+ authors: string[];
+ date: string;
+ url: string;
+ index: number;
+};
export type Followup = {
- text: string;
- pageid: string;
- score: number;
-}
+ text: string;
+ pageid: string;
+ score: number;
+};
export type Entry = UserEntry | AssistantEntry | ErrorMessage | StampyMessage;
export type UserEntry = {
- role: "user";
- content: string;
-}
+ role: "user";
+ content: string;
+};
export type AssistantEntry = {
- role: "assistant";
- content: string;
- citations: Citation[];
- base_count: number; // the number to start counting citations at
-}
+ role: "assistant";
+ content: string;
+ citations: Citation[];
+ citationsMap: Map;
+};
export type ErrorMessage = {
- role: "error";
- content: string;
-}
+ role: "error";
+ content: string;
+};
export type StampyMessage = {
- role: "stampy";
- content: string;
- url: string;
-}
+ role: "stampy";
+ content: string;
+ url: string;
+};
+
+export type SearchResult = {
+ followups?: Followup[] | ((f: Followup[]) => Followup[]);
+ result: Entry;
+};
+export type CurrentSearch = (AssistantEntry & { phase?: string }) | undefined;