Public
Edited
Apr 19, 2023
Insert cell
Insert cell
viewof DEFAULTS = {
const $apiKey = Inputs.password({
label: 'OpenAI API Key',
});

const $mode = Inputs.radio(['real', 'fake'], {
label: 'mode',
value: 'fake',
});

const $form = Inputs.form({
apiKey: $apiKey,
mode: $mode,
});

return Object.assign($form, {
$mode,
$apiKey,
});
}
Insert cell
// viewof OPENAI_API_KEY = Inputs.password({
// label: 'OpenAI API Key',
// })
Insert cell
// viewof CHAT_DEFAULT_MODE =
Insert cell
viewof DOCUMENT = Inputs.textarea({
rows: 24,
label: 'document',
});
Insert cell
viewof FRAGMENTS = {
const document = DOCUMENT;
const fragments = [];
for (const fragment of SPLIT(document, { nonOverlapTokens: 1024, overlapTokens: 128 })) {
fragments.push(fragment);
}

return Inputs.form(fragments.map((fragment, i) => {
return Inputs.textarea({
label: `fragments[${i}]`,
rows: 12,
value: fragment,
});
}));
}
Insert cell
function PHASE_1(fragment, { invalidation }) {
const messages = [];

messages.push({
role: `system`,
content: String.raw`You are a helpful assistant designed to aid a PhD student with writing their dissertation. Your job is to identify "rules of writing" that are used in a piece of text. Your job is split into multiple phases:

Phase 1) You will be given: A) a small selection of the paper. You will produce: B) a list of colon-separated human-readable names for "rules of writing" and the contents of their rules [like: this].

Phase 2) You will be given: B) a list of colon-separated human-readable names for "rules of writing" and the contents of their rules [like: this]. You will produce: C) an ordered-by-importance and deduplicated list of colon-separated human-readable names for "rules of writing" and the contents of their rules [like: this].

You are currently in Phase 1.`,
});

messages.push({
role: `user`,
content: String.raw`I will now provide you with A) a small selection of the paper.

${fragment}`,
});

messages.push({
role: `user`,
content: String.raw`Remember Phase 1) You will be given: A) a small selection of the paper. You will produce: B) a list of colon-separated human-readable names for "rules of writing" and the contents of their rules [like: this].

Give me B) a list of colon-separated human-readable names for "rules of writing" and the contents of their rules [like: this].`,
});

return Chat(messages, { invalidation });
}
Insert cell
viewof PHASE_1_FRAGMENT_1 = PHASE_1(FRAGMENTS[0], { invalidation });
Insert cell
viewof PHASE_1_FRAGMENTS = Inputs.form(FRAGMENTS.slice(1).map((fragment) => PHASE_1(fragment, { invalidation })));
Insert cell
viewof PHASE_1_SUMMARIES = Inputs.form([PHASE_1_FRAGMENT_1, ...PHASE_1_FRAGMENTS].map(({ response }) => {
return Inputs.textarea({
rows: 12,
value: response.choices[0].message.content,
});
}));
Insert cell
function MAKE_PHASE_2(summaries, { invalidation }) {
const messages = [];

messages.push({
role: `system`,
content: String.raw`You are a helpful assistant designed to aid a PhD student with writing their dissertation. Your job is to identify key terminology from a paper along with their definitions. Your job is split into multiple phases:

Phase 1) You will be given: A) a small selection of the paper. You will produce: B) a list of colon-separated terms and their definitions [like: this].

Phase 2) You will be given: B) a collection of lists of colon-separated terms and their definitions. You will produce: C) an ordered and deduplicated list of colon-separated key terms and their definitions.

You are currently in Phase 2.`,
});

for (let i=0, n=summaries.length; i<n; ++i) {
const summary = summaries[i];
messages.push({
role: `user`,
content: String.raw`I will now provide you with B) a collection of lists of colon-separated terms and their definitions.

${summary}`,
});
}
messages.push({
role: `user`,
content: String.raw`Remember Phase 2) You will be given: B) a collection of lists of colon-separated terms and their definitions. You will produce: C) an ordered and deduplicated list of colon-separated key terms and their definitions.

Give me C) an ordered and deduplicated list of colon-separated key terms and their definitions.`,
});

return Chat(messages, { invalidation });
}
Insert cell
viewof PHASE_2 = MAKE_PHASE_2(PHASE_1_SUMMARIES, { invalidation });
Insert cell
CHAT
Insert cell
function Chat(messages, {
url=`https://api.openai.com/v1/chat/completions`,
model=`gpt-3.5-turbo`,
temperature=1.0,

label='Chat',
rows=24,
invalidation,
}={}) {
const $messages = FORM({
label: 'messages',
inputs: messages.map(({ role, content }, i) => {
return FORM({
label: `messages[${i}]`,
inputs: {
role: Inputs.radio(['system', 'assistant', 'user'], {
label: 'role',
value: role,
}),
content: Inputs.textarea({
label: 'content',
value: content,
rows,
}),
},
});
}),
});

const $request = FORM({
label: 'Request',
inputs: {
apiKey: Inputs.bind(Inputs.password({
label: 'OpenAI API Key',
}), viewof DEFAULTS.$apiKey, invalidation),
mode: Inputs.bind(Inputs.radio(['real', 'fake'], {
label: 'mode',
}), viewof DEFAULTS.$mode, invalidation),
url: Inputs.text({
label: 'url',
value: url,
}),
model: Inputs.text({
label: 'model',
value: model,
}),
temperature: Inputs.number([0, 2], {
label: 'temperature',
value: temperature,
step: 0.1,
}),
messages: $messages,
},
});

const $response = FORM({
label: 'response',
inputs: {
choices: FORM({
label: 'choices',
inputs: [
/* 0 */ FORM({
label: 'choices[0]',
inputs: {
message: FORM({
label: 'message',
inputs: {
role: Inputs.text({
label: 'role',
}),
content: Inputs.textarea({
label: 'content',
rows,
}),
},
}),
},
}),
],
}),
},
});

const $send = Inputs.button('Send', {
reduce: async () => {
const request = $request.value;

const { mode } = request;

const func = mode === 'real' ? REAL : FAKE;

const response = await func(request);

$response.value = response;

$response.dispatchEvent(new CustomEvent('input'));
},
});

// Inputs.bind($send, $response, invalidation);

// Inputs.bind($request$input, $request$view, invalidation);
// Inputs.bind($response$view, $response$input, invalidation);

const $form = FORM({
label,
inputs: {
request: $request,
send: $send,
response: $response,
},
});

return Object.assign($form, {
$request,
$response,
});

function BIND({ target, source, invalidation }={}) {
return Inputs.bind(target, source, invalidation);
}

function FORM({ inputs, label=null }) {
return Inputs.form(inputs, {
template(inputs) {
if (!Array.isArray(inputs)) {
inputs = Object.values(inputs);
}

return htl.html`<div style="padding-left: 1rem">${inputs}`;
},
});
}

async function REAL(request) {
const { url, apiKey, model, temperature, messages } = request;

request = new Request(url, {
method: 'POST',
mode: 'cors',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${apiKey}`,
},
body: JSON.stringify({
model,
temperature,
messages,
}),
});

let response = await fetch(request);
if (response.status !== 200) throw `response.status = ${response.status}`;

response = await response.json();

return response;
}

async function FAKE(request) {
await new Promise((resolve) => setTimeout(resolve, 100));

return ({
choices: [
{
message: {
role: 'assistant',
content: 'auto-generated',
},
},
],
});
}
}
Insert cell
function SPLIT(document, {
maxTokens=null,
nonOverlapTokens=null,
overlapTokens=0,
tokenizer=new GPT3Tokenizer({ type: 'gpt3' }),
}={}) {
if (maxTokens === null && nonOverlapTokens === null) {
throw 'one of [maxTokens | nonOverlapTokens] is required';
} else if (maxTokens === null && nonOverlapTokens !== null) {
maxTokens = nonOverlapTokens + overlapTokens;
} else if (maxTokens !== null && nonOverlapTokens !== null) {
if (maxTokens !== nonOverlapTokens + overlapTokens) {
throw 'if both maxTokens and nonOverlapTokens are passed, then they need to be consistent';
}
}
const { text } = tokenizer.encode(document);

const fragments = [];
for (let i=0, n=text.length; i<n; i += maxTokens - overlapTokens) {
fragments.push(text.slice(i, i + maxTokens).join(''));
}

return fragments;
}
Insert cell
GPT3Tokenizer = (await import("https://cdn.skypack.dev/gpt3-tokenizer@1.1.5/dist-browser/gpt3-tokenizer.js")).default;
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more