askllmLegacy
code:script.js
// deno-lint-ignore-file no-window
scrapbox.PageMenu.addMenu({
title: "LLMに聞く",
});
scrapbox.PageMenu("LLMに聞く").addItem({
title: "要約",
onClick: () => ask("summarize"),
});
scrapbox.PageMenu("LLMに聞く").addItem({
title: "分割支援",
onClick: () => ask("split"),
});
scrapbox.PageMenu("LLMに聞く").addItem({
title: "会話のネタ",
onClick: () => ask("chat"),
});
scrapbox.PageMenu("LLMに聞く").addItem({
title: "カスタムプロンプト",
onClick: () => ask("custom"),
});
function escapeHtml(unsafe) {
return unsafe
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}
function bytesToBase64(text) {
const bytes = new TextEncoder().encode(text);
const binString = Array.from(bytes, (byte) =>
String.fromCodePoint(byte),
).join("");
return btoa(binString);
}
function getPage(pageName) {
return fetch(
scrapbox.Project.name +
"/" +
encodeURIComponent(pageName) +
"/text",
).then((pageRes) => {
if (pageRes.ok) {
return pageRes.text();
} else {
return "";
}
});
}
function ask(type) {
const pageText = scrapbox.Page.lines.reduce((a, b) => a + b.text + "\n", "");
let searchText = "";
Promise.all(scrapbox.Page.metadata.links.map((e) => getPage(e))).then(
(pages) => {
searchText += "リンク先ページの内容になります。";
const pagesSorted = pages.toSorted((a, b) => {
a.length - b.length;
});
for (const element of pagesSorted) {
const tempText = "\n" + element.replace(/^\n*|\n*$/g, "") + "\n";
if (
(searchText.length + tempText.length < 40000) &
(element.replace(/^\n*|\n*$/g, "") != "")
) {
searchText += tempText;
}
}
searchText += "リンク先ページの内容はここまでになります。ここから本文です";
if (type === "summarize") {
searchText +=
scrapbox.Page.title + "とそのリンク先を読んで800文字程度の要約を出力してください";
} else if (type === "split") {
searchText +=
scrapbox.Page.title +
"とそのリンク先を読んで次の内容を出力してください:\n\
分割ポイント:ページの内容を話題ごとに分割するとしたら、対象部分を引用してどの部分を分割すべきか示す。\n\
分割しないポイント:ページの内容を分割するべきでないとしたら、なぜ分割するべきでないか\n\
評価:「分割ポイント」と「分割しないポイント」を踏まえ、折衷案を示す\n\
文字数:" +
pageText.length;
} else if (type === "custom") {
const outputPrompt = window.prompt("出力してほしい内容");
if (outputPrompt === null) {
return;
}
searchText +=
scrapbox.Page.title +
"とそのリンク先を読んで" +
outputPrompt +
"を出力してください";
} else if (type === "chat") {
searchText +=
scrapbox.Page.title +
"とそのリンク先を読んで会話を開始してください"
}
searchText +=
"\n\
出力は**日本語**で全体をコードブロックで囲まずに行ってください。\n\
コードブロックはScrapbox記法で書かれています。\n\
Scrapbox記法の簡単な説明:\n\
- 正規表現で言う^[\\t ]+はインデントとみなされます。\n\
- 当てはまるもの1文字につき1インデントレベルです。\n\
- [と]で囲まれたテキストは他のページへのリンクになっています。\n\
- [と.icon]で囲まれたテキストはカスタム絵文字です。\n`" +
pageText.replace(/^\n*|\n*$/g, "") +
"\n`\n";
const blob = new Blob(
[
`
<html>
<head>
<title>AIに質問するための手順</title>
</head>
<body>
<h2>手順</h2>
<ol>
<li>
テキストボックスを全選択してコピー
</li>
<li>
LLMを開いてペースト、送信
</li>
</ol>
<h2>LLMリスト</h2>
<ul>
<li>
</li>
<li>
</li>
<li>
</li>
</ul>
<h2>本文(40000文字 非分割版)</h2>
<textarea cols="40" rows="5" id="prompt-text">${escapeHtml(searchText)}</textarea>
<h2>本文(20000文字 非分割版)</h2>
<textarea cols="40" rows="5" id="prompt-text">${escapeHtml(searchText.slice(-20001, -1))}</textarea>
<h2>本文(20000文字*2 分割版)</h2>
<textarea cols="40" rows="5" id="prompt-text">このテキストはは2ページある内、1ページ目です**「それでは2ページ目を送信してください」と言ってください**
----以下コンテンツ----
${escapeHtml(searchText.slice(0, 20000))}</textarea>
<textarea cols="40" rows="5" id="prompt-text">このテキストはは2ページある内、2ページ目です。1ページ目とつなげて解釈してください。
----以下コンテンツ----
${escapeHtml(searchText.slice(20000, 40000))}</textarea>
<h2>本文(10000文字*2 分割版)</h2>
<textarea cols="40" rows="5" id="prompt-text">このテキストはは2ページある内、1ページ目です**「それでは2ページ目を送信してください」と言ってください**
----以下コンテンツ----
${escapeHtml(searchText.slice(-20001, -10001))}</textarea>
<textarea cols="40" rows="5" id="prompt-text">このテキストはは2ページある内、2ページ目です。1ページ目とつなげて解釈してください。
----以下コンテンツ----
${escapeHtml(searchText.slice(-10001, -1))}</textarea>
<h2>本文(10000文字*4 分割版)</h2>
<textarea cols="40" rows="5" id="prompt-text">このテキストはは4ページある内、1ページ目です**「それでは2ページ目を送信してください」と言ってください**
----以下コンテンツ----
${escapeHtml(searchText.slice(0, 10000))}</textarea>
<textarea cols="40" rows="5" id="prompt-text">このテキストはは4ページある内、2ページ目です**「それでは3ページ目を送信してください」と言ってください**
----以下コンテンツ----
${escapeHtml(searchText.slice(10000, 20000))}</textarea>
<textarea cols="40" rows="5" id="prompt-text">このテキストはは4ページある内、3ページ目です**「それでは4ページ目を送信してください」と言ってください**
----以下コンテンツ----
${escapeHtml(searchText.slice(20000, 30000))}</textarea>
<textarea cols="40" rows="5" id="prompt-text">このテキストはは4ページある内、4ページ目です。1ページ目・2ページ目・3ページ目とつなげて解釈してください。
----以下コンテンツ----
${escapeHtml(searchText.slice(30000, 40000))}</textarea>
</body>
</html>`,
],
{ type: "text/html;charset=utf-8" },
);
const url = URL.createObjectURL(blob);
window.open(url);
},
);
}