);
};
diff --git a/web/src/components/controls.tsx b/web/src/components/controls.tsx
index a92ae63..99524e6 100644
--- a/web/src/components/controls.tsx
+++ b/web/src/components/controls.tsx
@@ -1,57 +1,39 @@
import type { Mode } from "../types";
-export const Controls = ({
- mode,
- setMode,
-}: {
- mode: [Mode, boolean];
+const MODES = {
+ rookie:
+ "For people who are new to the field of AI alignment. The " +
+ "answer might be longer, since technical terms will be " +
+ "explained in more detail and less background will be assumed.",
+ concise:
+ "Quick and to the point. Followup questions may need to be " +
+ "asked to get the full picture of what's going on.",
+ default: "A balanced default mode.",
+};
+
+type ControlsType = {
+ mode: Mode;
setMode: (m: any) => void;
-}) => {
+};
+export const Controls = ({ mode, setMode }: ControlsType) => {
{
/* three buttons for the three modes, place far right, 1rem between each */
}
return (
);
};
diff --git a/web/src/components/header.tsx b/web/src/components/header.tsx
index 1941361..641a53c 100644
--- a/web/src/components/header.tsx
+++ b/web/src/components/header.tsx
@@ -3,9 +3,9 @@ import Link from "next/link";
import Image from "next/image";
import logo from "../logo.svg";
-const Header: React.FC<{ page: "index" | "semantic" | "playground" }> = ({
- page,
-}) => {
+export type Page = "index" | "semantic" | "playground" | "tester";
+
+const Header: React.FC<{ page: Page }> = ({ page }) => {
const sidebar =
page === "index" ? (
diff --git a/web/src/components/html.tsx b/web/src/components/html.tsx
index 8312bb7..35e0a00 100644
--- a/web/src/components/html.tsx
+++ b/web/src/components/html.tsx
@@ -1,8 +1,22 @@
+import { ChangeEvent } from "react";
+import type { Parseable } from "../types";
+
// const Colours = ["blue", "cyan", "teal", "green", "amber"].map(
// colour => `bg-${colour}-100 border-${colour}-300 text-${colour}-800`
// );
// this would be nice, but Tailwind needs te actual string of the class to be in
// the source file for it to be included in the build
+type NumberParser = (v: Parseable) => number;
+type InputFields = {
+ field: string;
+ label: string;
+ value?: Parseable;
+ min?: string | number;
+ max?: string | number;
+ step?: string | number;
+ parser?: NumberParser;
+ updater: (v: any) => any;
+};
export const Colours = [
"bg-red-100 border-red-300 text-red-800",
@@ -30,3 +44,78 @@ export const A: React.FC<{
{children}
);
};
+
+const between =
+ (
+ min: Parseable,
+ max: Parseable,
+ parser: NumberParser,
+ updater: (v: any) => any
+ ) =>
+ (event: ChangeEvent) => {
+ let num = parser((event.target as HTMLInputElement).value);
+ if (isNaN(num)) {
+ return;
+ } else if (min !== undefined && num < parser(min)) {
+ num = parser(min);
+ } else if (max !== undefined && num > parser(max)) {
+ num = parser(max);
+ }
+ updater(num);
+ };
+
+export const SectionHeader = ({ text }: { text: string }) => (
+
{text}
+);
+
+export const NumberInput = ({
+ field,
+ value,
+ label,
+ min,
+ max,
+ updater,
+ // this cast is just to satisfy typescript - it can handle numbers, strings and undefined just fine
+ parser = (v) => parseInt(v as string, 10),
+}: InputFields) => (
+ <>
+
+
+ >
+);
+
+export const Slider = ({
+ field,
+ value,
+ label,
+ min = 0,
+ max = 1,
+ step = 0.01,
+ // this cast is just to satisfy typescript - it can handle numbers, strings and undefined just fine
+ parser = (v) => parseFloat(v as string),
+ updater,
+}: InputFields) => (
+ <>
+
+
+ >
+);
diff --git a/web/src/components/page.tsx b/web/src/components/page.tsx
index f64337b..8f3a486 100644
--- a/web/src/components/page.tsx
+++ b/web/src/components/page.tsx
@@ -1,17 +1,19 @@
import React, { ReactNode } from "react";
import Head from "next/head";
+import type { Page as PageType } from "./header";
import Header from "./header";
-const Page: React.FC<{ children: ReactNode; page: "index" | "semantic" }> = ({
- page,
- children,
-}) => {
+const Page: React.FC<{
+ children: ReactNode;
+ widescreen?: boolean;
+ page: PageType;
+}> = ({ page, children, widescreen = false }) => {
return (
<>
AI Safety Info
-
+
{children}
diff --git a/web/src/components/searchbox.tsx b/web/src/components/searchbox.tsx
index 1e91c47..0b7bd61 100644
--- a/web/src/components/searchbox.tsx
+++ b/web/src/components/searchbox.tsx
@@ -1,33 +1,10 @@
import React from "react";
import { useState, useEffect } from "react";
+import { initialQuestions } from "../settings";
import type { Followup } from "../types";
import TextareaAutosize from "react-textarea-autosize";
import dynamic from "next/dynamic";
-// initial questions to fill the search box with.
-export const initialQuestions: string[] = [
- "Are there any regulatory efforts aimed at addressing AI safety and alignment concerns?",
- "How can I help with AI safety and alignment?",
- "How could a predictive model - like an LLM - act like an agent?",
- "How could an AI possibly be an x-risk when some populations aren't even connected to the internet?",
- "I'm not convinced, why is this important?",
- "Summarize the differences in opinion between Eliezer Yudkowsky and Paul Christiano.",
- 'What are "RAAPs"?',
- 'What are "scaling laws" and how are they relevant to safety?',
- "What are some of the different research approaches?",
- "What are the differences between Inner and Outer alignment?",
- 'What does the term "x-risk" mean?',
- 'What is "FOOM"?',
- 'What is "instrumental convergence"?',
- "What is a hard takeoff?",
- "What is a mesa-optimizer?",
- "What is AI safety and alignment?",
- "What is an AI arms race?",
- "What is an Intelligence Explosion?",
- 'What is the "orthogonality thesis"?',
- 'Why would we expect AI to be "misaligned by default"?',
-];
-
const SearchBoxInternal: React.FC<{
search: (
query: string,
diff --git a/web/src/components/settings.tsx b/web/src/components/settings.tsx
new file mode 100644
index 0000000..2b3ffc5
--- /dev/null
+++ b/web/src/components/settings.tsx
@@ -0,0 +1,201 @@
+import { ChangeEvent } from "react";
+import TextareaAutosize from "react-textarea-autosize";
+
+import type { Parseable, LLMSettings, Entry, Mode } from "../types";
+import { MODELS, ENCODERS } from "../hooks/useSettings";
+import { SectionHeader, NumberInput, Slider } from "../components/html";
+
+type ChatSettingsParams = {
+ settings: LLMSettings;
+ changeSetting: (path: string[], value: any) => void;
+};
+
+export const ChatSettings = ({
+ settings,
+ changeSetting,
+}: ChatSettingsParams) => {
+ const changeVal = (field: string, value: any) =>
+ changeSetting([field], value);
+ const update = (field: string) => (event: ChangeEvent) =>
+ changeVal(field, (event.target as HTMLInputElement).value);
+ const updateNum = (field: string) => (num: Parseable) =>
+ changeVal(field, num);
+
+ return (
+
+ );
+};
diff --git a/web/src/hooks/useCitations.ts b/web/src/hooks/useCitations.ts
new file mode 100644
index 0000000..4017859
--- /dev/null
+++ b/web/src/hooks/useCitations.ts
@@ -0,0 +1,47 @@
+import { useState } from "react";
+
+import type { CurrentSearch, Citation } from "../types";
+
+const updateCitations = (
+ allCitations: Citation[],
+ setCitations: (citations: Citation[]) => any,
+ entry?: CurrentSearch
+) => {
+ if (!entry) return entry;
+
+ const entryCitations = Array.from(entry.citationsMap.values());
+ if (!entryCitations.some((c) => !c.index)) {
+ // All of the entries citations have indexes, so there weren't any changes since the last check
+ return entry;
+ }
+
+ // 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])
+ );
+
+ entryCitations.forEach((c) => {
+ const hash = c.title + c.url;
+ const index = citationsMapping[hash];
+ if (!index) {
+ c.index = allCitations.length + 1;
+ allCitations.push(c);
+ } else {
+ c.index = index;
+ }
+ });
+ setCitations(allCitations);
+ return entry;
+};
+
+export default function useCitations() {
+ const [citations, setCitations] = useState([]);
+
+ const setEntryCitations = (entry: CurrentSearch) =>
+ updateCitations(citations, setCitations, entry);
+
+ return {
+ citations,
+ setEntryCitations,
+ };
+}
diff --git a/web/src/hooks/useSearch.ts b/web/src/hooks/useSearch.ts
index de0a62a..9c7b166 100644
--- a/web/src/hooks/useSearch.ts
+++ b/web/src/hooks/useSearch.ts
@@ -57,16 +57,19 @@ export async function* iterateData(res: Response) {
}
}
-export const extractAnswer = async (
- res: Response,
- setCurrent: (e: CurrentSearch) => void
-): Promise => {
- var result: AssistantEntry = {
+const makeEntry = () =>
+ ({
role: "assistant",
content: "",
citations: [],
citationsMap: new Map(),
- };
+ } as AssistantEntry);
+
+export const extractAnswer = async (
+ res: Response,
+ setCurrent: (e: CurrentSearch) => void
+): Promise => {
+ var result: AssistantEntry = makeEntry();
var followups: Followup[] = [];
for await (var data of iterateData(res)) {
switch (data.state) {
@@ -135,6 +138,7 @@ export const queryLLM = async (
sessionId: string,
controller: AbortController
): Promise => {
+ setCurrent({ ...makeEntry(), phase: "started" });
// do SSE on a POST request.
const res = await fetchLLM(sessionId, query, settings, history, controller);
diff --git a/web/src/hooks/useSettings.ts b/web/src/hooks/useSettings.ts
new file mode 100644
index 0000000..e96b159
--- /dev/null
+++ b/web/src/hooks/useSettings.ts
@@ -0,0 +1,172 @@
+import { useRouter } from "next/router";
+import { useState, useEffect } from "react";
+
+import type { CurrentSearch, Mode, Entry, LLMSettings } from "../types";
+
+type LLMSettingsParsers = {
+ [key: string]:
+ | ((v: number | undefined) => any)
+ | ((v: string | undefined) => any)
+ | ((v: object | undefined) => any);
+};
+
+const DEFAULT_PROMPTS = {
+ context:
+ "You are a helpful assistant knowledgeable about AI Alignment and Safety. " +
+ 'Please give a clear and coherent answer to the user\'s questions.(written after "Q:") ' +
+ "using the following sources. Each source is labeled with a letter. Feel free to " +
+ "use the sources in any order, and try to use multiple sources in your answers.\n\n",
+ history:
+ "\n\n" +
+ 'Before the question ("Q: "), there will be a history of previous questions and answers. ' +
+ "These sources only apply to the last question. any sources used in previous answers " +
+ "are invalid.",
+ question:
+ "In your answer, please cite any claims you make back to each source " +
+ "using the format: [a], [b], etc. If you use multiple sources to make a claim " +
+ 'cite all of them. For example: "AGI is concerning [c, d, e]."\n\n',
+ modes: {
+ default: "",
+ concise:
+ "Answer very concisely, getting to the crux of the matter in as " +
+ "few words as possible. Limit your answer to 1-2 sentences.\n\n",
+ rookie:
+ "This user is new to the field of AI Alignment and Safety - don't " +
+ "assume they know any technical terms or jargon. Still give a complete answer " +
+ "without patronizing the user, but take any extra time needed to " +
+ "explain new concepts or to illustrate your answer with examples. " +
+ "Put extra effort into explaining the intuition behind concepts " +
+ "rather than just giving a formal definition.\n\n",
+ },
+};
+export const MODELS = {
+ "gpt-3.5-turbo": { maxNumTokens: 4095, topKBlocks: 10 },
+ "gpt-3.5-turbo-16k": { maxNumTokens: 16385, topKBlocks: 30 },
+ "gpt-4": { maxNumTokens: 8192, topKBlocks: 20 },
+ /* 'gpt-4-32k': {maxNumTokens: 32768, topKBlocks: 30}, */
+};
+export const ENCODERS = ["cl100k_base"];
+
+/** Update the given `obj` so that it has `val` at the given path.
+ *
+ * e.g.
+ * updateIn({a: {b: 123}}, ['a', 'b', 'c'], 42) == {a: {b: 123, c: 42}}
+ * updateIn({a: {b: 123}}, ['z', 'y', 'x'], 42) == {a: {b: 123}, z: {y: {x: 42}}}
+ */
+export const updateIn = (
+ obj: { [key: string]: any },
+ [head, ...rest]: string[],
+ val: any
+) => {
+ if (!head) {
+ // No path provided - do nothing
+ } else if (!rest || rest.length == 0) {
+ obj[head] = val;
+ } else {
+ if (obj[head] === undefined) {
+ obj[head] = {};
+ }
+ updateIn(obj[head], rest, val);
+ }
+ return obj;
+};
+
+/** Create a settings object in which all items in the `overrides` object will be parsed appropriately
+ *
+ * `parsers` should be an object mapping settings fields to functions that will return a valid setting.
+ * The parser functions should have default values that will be used if the provided value is undefined.
+ */
+const parseSettings = (overrides: LLMSettings, parsers: LLMSettingsParsers) =>
+ Object.entries(parsers).reduce(
+ (settings, [key, parser]) =>
+ updateIn(settings, [key], parser(overrides[key])),
+ {}
+ );
+
+/** Make a parser function from the provided `defaultVal`.
+ *
+ * If the parsed value is undefined, `defaultVal` will be returned, otherwise it will be parsed as
+ * a value of the same type as `defaultVal`.
+ * If `defaultVal` is an object, it will return a parser that will recursively search for appropriate keys.
+ */
+const withDefault = (defaultVal: any) => {
+ if (typeof defaultVal === "number" && defaultVal % 1 === 0) {
+ return (v: string | undefined): number =>
+ v !== undefined ? parseInt(v, 10) : defaultVal;
+ } else if (typeof defaultVal === "number") {
+ return (v: string | undefined): number =>
+ v !== undefined ? parseFloat(v) : defaultVal;
+ } else if (typeof defaultVal === "object") {
+ const parsers = Object.entries(defaultVal).reduce(
+ (parsers, [key, val]) => updateIn(parsers, [key], withDefault(val)),
+ {}
+ );
+ return (v: object | undefined): object => parseSettings(v || {}, parsers);
+ } else {
+ return (v: any | undefined): any => v || defaultVal;
+ }
+};
+
+const SETTINGS_PARSERS = {
+ prompts: withDefault(DEFAULT_PROMPTS),
+ mode: (v: string | undefined) => (v || "default") as Mode,
+ completions: withDefault("gpt-3.5-turbo"),
+ encoder: withDefault("cl100k_base"),
+ topKBlocks: withDefault(MODELS["gpt-3.5-turbo"].topKBlocks), // the number of blocks to use as citations
+ maxNumTokens: withDefault(MODELS["gpt-3.5-turbo"].maxNumTokens),
+ tokensBuffer: withDefault(50), // the number of tokens to leave as a buffer when calculating remaining tokens
+ maxHistory: withDefault(10), // the max number of previous items to use as history
+ historyFraction: withDefault(0.25), // the (approximate) fraction of num_tokens to use for history text before truncating
+ contextFraction: withDefault(0.5), // the (approximate) fraction of num_tokens to use for context text before truncating
+};
+
+export const makeSettings = (overrides: LLMSettings) =>
+ parseSettings(
+ Object.entries(overrides).reduce(
+ (acc, [key, val]) => updateIn(acc, key.split("."), val),
+ {}
+ ),
+ SETTINGS_PARSERS
+ );
+
+type ChatSettingsParams = {
+ settings: LLMSettings;
+ changeSetting: (path: string[], value: any) => void;
+};
+
+export default function useSettings() {
+ const [settings, updateSettings] = useState(makeSettings({}));
+ const router = useRouter();
+
+ const updateInUrl = (path: string[], value: any) =>
+ router.replace({
+ pathname: router.pathname,
+ query: {
+ ...router.query,
+ [path.join(".")]: value.toString(),
+ },
+ });
+
+ const changeSetting = (path: string[], value: any) => {
+ updateInUrl(path, value);
+ updateSettings((settings) => ({ ...updateIn(settings, path, value) }));
+ };
+
+ const setMode = (mode: Mode | undefined) => {
+ if (mode) {
+ updateSettings({ ...settings, mode: mode });
+ localStorage.setItem("chat_mode", mode);
+ }
+ };
+
+ useEffect(() => {
+ const mode = (localStorage.getItem("chat_mode") as Mode) || "default";
+ updateSettings(makeSettings({ ...router.query, mode: mode }));
+ }, [updateSettings, router]);
+
+ return {
+ settings,
+ changeSetting,
+ setMode,
+ };
+}
diff --git a/web/src/pages/index.tsx b/web/src/pages/index.tsx
index 41482d1..d1f9071 100644
--- a/web/src/pages/index.tsx
+++ b/web/src/pages/index.tsx
@@ -3,6 +3,7 @@ import { useState, useEffect } from "react";
import Link from "next/link";
import { queryLLM, getStampyContent, runSearch } from "../hooks/useSearch";
+import useSettings from "../hooks/useSettings";
import type { Mode } from "../types";
import Page from "../components/page";
import Chat from "../components/chat";
@@ -12,23 +13,16 @@ const MAX_FOLLOWUPS = 4;
const Home: NextPage = () => {
const [sessionId, setSessionId] = useState("");
- const [mode, setMode] = useState<[Mode, boolean]>(["default", false]);
-
- // store mode in localstorage
- useEffect(() => {
- if (mode[1]) localStorage.setItem("chat_mode", mode[0]);
- }, [mode]);
+ const { settings, setMode } = useSettings();
// initial load
useEffect(() => {
- const mode = (localStorage.getItem("chat_mode") as Mode) || "default";
- setMode([mode, true]);
setSessionId(crypto.randomUUID());
}, []);
return (
-
+
WARNING: This is a very early prototype.{" "}
@@ -38,7 +32,7 @@ const Home: NextPage = () => {
welcomed.
-
+
);
};
diff --git a/web/src/pages/playground.tsx b/web/src/pages/playground.tsx
index bb04f1d..a90ba0e 100644
--- a/web/src/pages/playground.tsx
+++ b/web/src/pages/playground.tsx
@@ -1,412 +1,44 @@
import type { NextPage } from "next";
-import { useState, useEffect, ChangeEvent } from "react";
-import TextareaAutosize from "react-textarea-autosize";
-import Head from "next/head";
-import Link from "next/link";
+import { useState, useEffect } from "react";
+import Page from "../components/page";
-import { queryLLM, getStampyContent, runSearch } from "../hooks/useSearch";
-import type { Mode, Entry, LLMSettings } from "../types";
-import Header from "../components/header";
+import useSettings from "../hooks/useSettings";
+import type { Entry } from "../types";
import Chat from "../components/chat";
import { Controls } from "../components/controls";
-
-const MAX_FOLLOWUPS = 4;
-const DEFAULT_PROMPTS = {
- context:
- "You are a helpful assistant knowledgeable about AI Alignment and Safety. " +
- 'Please give a clear and coherent answer to the user\'s questions.(written after "Q:") ' +
- "using the following sources. Each source is labeled with a letter. Feel free to " +
- "use the sources in any order, and try to use multiple sources in your answers.\n\n",
- history:
- "\n\n" +
- 'Before the question ("Q: "), there will be a history of previous questions and answers. ' +
- "These sources only apply to the last question. Any sources used in previous answers " +
- "are invalid.",
- question:
- "In your answer, please cite any claims you make back to each source " +
- "using the format: [a], [b], etc. If you use multiple sources to make a claim " +
- 'cite all of them. For example: "AGI is concerning [c, d, e]."\n\n',
- modes: {
- default: "",
- concise:
- "Answer very concisely, getting to the crux of the matter in as " +
- "few words as possible. Limit your answer to 1-2 sentences.\n\n",
- rookie:
- "This user is new to the field of AI Alignment and Safety - don't " +
- "assume they know any technical terms or jargon. Still give a complete answer " +
- "without patronizing the user, but take any extra time needed to " +
- "explain new concepts or to illustrate your answer with examples. " +
- "Put extra effort into explaining the intuition behind concepts " +
- "rather than just giving a formal definition.\n\n",
- },
-};
-const MODELS = {
- "gpt-3.5-turbo": { maxNumTokens: 4095, topKBlocks: 10 },
- "gpt-3.5-turbo-16k": { maxNumTokens: 16385, topKBlocks: 30 },
- "gpt-4": { maxNumTokens: 8192, topKBlocks: 20 },
- /* 'gpt-4-32k': {maxNumTokens: 32768, topKBlocks: 30}, */
-};
-const DEFAULT_SETTINGS = {
- prompts: DEFAULT_PROMPTS,
- mode: "default" as Mode,
- completions: "gpt-3.5-turbo",
- encoder: "cl100k_base",
- topKBlocks: MODELS["gpt-3.5-turbo"].topKBlocks, // the number of blocks to use as citations
- maxNumTokens: MODELS["gpt-3.5-turbo"].maxNumTokens,
- tokensBuffer: 50, // the number of tokens to leave as a buffer when calculating remaining tokens
- maxHistory: 10, // the max number of previous items to use as history
- historyFraction: 0.25, // the (approximate) fraction of num_tokens to use for history text before truncating
- contextFraction: 0.5, // the (approximate) fraction of num_tokens to use for context text before truncating
-};
-const ENCODERS = ["cl100k_base"];
-
-const updateIn = (
- obj: { [key: string]: any },
- [head, ...rest]: string[],
- val: any
-) => {
- if (!head) {
- // No path provided - do nothing
- } else if (!rest || rest.length == 0) {
- obj[head] = val;
- } else {
- updateIn(obj[head], rest, val);
- }
- return obj;
-};
-
-type Parseable = string | number | undefined;
-type NumberParser = (v: Parseable) => number;
-type InputFields = {
- field: string;
- label: string;
- value?: Parseable;
- min?: string | number;
- max?: string | number;
- step?: string | number;
- parser?: NumberParser;
- updater: (v: any) => any;
-};
-
-const between =
- (
- min: Parseable,
- max: Parseable,
- parser: NumberParser,
- updater: (v: any) => any
- ) =>
- (event: ChangeEvent) => {
- let num = parser((event.target as HTMLInputElement).value);
- if (isNaN(num)) {
- return;
- } else if (min !== undefined && num < parser(min)) {
- num = parser(min);
- } else if (max !== undefined && num > parser(max)) {
- num = parser(max);
- }
- updater(num);
- };
-
-const SectionHeader = ({ text }: { text: string }) => (
-
{text}
-);
-
-const NumberInput = ({
- field,
- value,
- label,
- min,
- max,
- updater,
- // this cast is just to satisfy typescript - it can handle numbers, strings and undefined just fine
- parser = (v) => parseInt(v as string, 10),
-}: InputFields) => (
- <>
-
-
- >
-);
-
-const Slider = ({
- field,
- value,
- label,
- min = 0,
- max = 1,
- step = 0.01,
- // this cast is just to satisfy typescript - it can handle numbers, strings and undefined just fine
- parser = (v) => parseFloat(v as string),
- updater,
-}: InputFields) => (
- <>
-
-
- >
-);
-
-type ChatSettingsParams = {
- settings: LLMSettings;
- updateSettings: (updater: (settings: LLMSettings) => LLMSettings) => void;
-};
-
-const ChatSettings = ({ settings, updateSettings }: ChatSettingsParams) => {
- const changeVal = (field: string, value: any) =>
- updateSettings((prev) => ({ ...prev, [field]: value }));
- const update = (setting: string) => (event: ChangeEvent) => {
- changeVal(setting, (event.target as HTMLInputElement).value);
- };
- const updateNum = (field: string) => (num: Parseable) =>
- changeVal(field, num);
-
- return (
-
+
);
};
diff --git a/web/src/settings.ts b/web/src/settings.ts
index 5dd7442..e1aa14a 100644
--- a/web/src/settings.ts
+++ b/web/src/settings.ts
@@ -3,3 +3,27 @@ export const API_URL =
export const STAMPY_URL = process.env.STAMPY_URL || "https://aisafety.info";
export const STAMPY_CONTENT_URL =
process.env.STAMPY_CONTENT_URL || `${API_URL}/human`;
+
+// initial questions to fill the search box with.
+export const initialQuestions: string[] = [
+ "Are there any regulatory efforts aimed at addressing AI safety and alignment concerns?",
+ "How can I help with AI safety and alignment?",
+ "How could a predictive model - like an LLM - act like an agent?",
+ "How could an AI possibly be an x-risk when some populations aren't even connected to the internet?",
+ "I'm not convinced, why is this important?",
+ "Summarize the differences in opinion between Eliezer Yudkowsky and Paul Christiano.",
+ 'What are "RAAPs"?',
+ 'What are "scaling laws" and how are they relevant to safety?',
+ "What are some of the different research approaches?",
+ "What are the differences between Inner and Outer alignment?",
+ 'What does the term "x-risk" mean?',
+ 'What is "FOOM"?',
+ 'What is "instrumental convergence"?',
+ "What is a hard takeoff?",
+ "What is a mesa-optimizer?",
+ "What is AI safety and alignment?",
+ "What is an AI arms race?",
+ "What is an Intelligence Explosion?",
+ 'What is the "orthogonality thesis"?',
+ 'Why would we expect AI to be "misaligned by default"?',
+];
diff --git a/web/src/types.ts b/web/src/types.ts
index aa99fc1..e751c66 100644
--- a/web/src/types.ts
+++ b/web/src/types.ts
@@ -23,7 +23,7 @@ export type UserEntry = {
export type AssistantEntry = {
role: "assistant";
content: string;
- citations: Citation[];
+ citations?: Citation[];
citationsMap: Map;
deleted?: boolean;
};
@@ -64,3 +64,5 @@ export type LLMSettings = {
contextFraction?: number;
[key: string]: any;
};
+
+export type Parseable = string | number | undefined;