Merge branch 'main' of ssh://git.cannorin.net:2222/cannorin/arubinochan-bot
This commit is contained in:
@@ -1,22 +1,18 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
|
"$schema": "https://biomejs.dev/schemas/2.4.10/schema.json",
|
||||||
"vcs": {
|
"vcs": {
|
||||||
"enabled": false,
|
"enabled": false,
|
||||||
"clientKind": "git",
|
"clientKind": "git",
|
||||||
"useIgnoreFile": false
|
"useIgnoreFile": false
|
||||||
},
|
},
|
||||||
"files": {
|
"files": {
|
||||||
"ignoreUnknown": false,
|
"ignoreUnknown": false
|
||||||
"ignore": []
|
|
||||||
},
|
},
|
||||||
"formatter": {
|
"formatter": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"indentStyle": "space",
|
"indentStyle": "space",
|
||||||
"indentWidth": 2
|
"indentWidth": 2
|
||||||
},
|
},
|
||||||
"organizeImports": {
|
|
||||||
"enabled": true
|
|
||||||
},
|
|
||||||
"linter": {
|
"linter": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"rules": {
|
"rules": {
|
||||||
|
|||||||
17
index.ts
17
index.ts
@@ -2,7 +2,7 @@ import { parseArgs } from "node:util";
|
|||||||
import { Stream } from "misskey-js";
|
import { Stream } from "misskey-js";
|
||||||
import type { Note } from "misskey-js/entities.js";
|
import type { Note } from "misskey-js/entities.js";
|
||||||
import type { ChatHistoryItem, LLamaChatPromptOptions } from "node-llama-cpp";
|
import type { ChatHistoryItem, LLamaChatPromptOptions } from "node-llama-cpp";
|
||||||
import { LlmSession, createGrammar, getModel, parseResponse } from "./lib/llm";
|
import { createGrammar, getModel, LlmSession, parseResponse } from "./lib/llm";
|
||||||
import {
|
import {
|
||||||
expandReplyTree,
|
expandReplyTree,
|
||||||
getNotes,
|
getNotes,
|
||||||
@@ -118,11 +118,16 @@ async function processPostJob() {
|
|||||||
|
|
||||||
async function processReplyJob(job: Extract<Job, { type: "reply" }>) {
|
async function processReplyJob(job: Extract<Job, { type: "reply" }>) {
|
||||||
const history: ChatHistoryItem[] = job.history.map((n) => {
|
const history: ChatHistoryItem[] = job.history.map((n) => {
|
||||||
const type = n.userId === me.id ? ("model" as const) : ("user" as const);
|
if (n.userId === me.id) {
|
||||||
|
return {
|
||||||
|
type: "model",
|
||||||
|
response: [formatNote(n)],
|
||||||
|
} as const;
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
type,
|
type: "user",
|
||||||
text: formatNote(n),
|
text: formatNote(n),
|
||||||
} as ChatHistoryItem;
|
} as const;
|
||||||
});
|
});
|
||||||
const text = await (async () => {
|
const text = await (async () => {
|
||||||
await using session = new LlmSession(model, replyJobPrompt, history);
|
await using session = new LlmSession(model, replyJobPrompt, history);
|
||||||
@@ -221,10 +226,6 @@ function initializeStream() {
|
|||||||
await misskey.request("following/create", { userId: e.id });
|
await misskey.request("following/create", { userId: e.id });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
channel.on("unfollow", async (e) => {
|
|
||||||
await misskey.request("following/delete", { userId: e.id });
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** pop from the job queue and run it */
|
/** pop from the job queue and run it */
|
||||||
|
|||||||
@@ -4,11 +4,11 @@ import { fileURLToPath } from "node:url";
|
|||||||
import {
|
import {
|
||||||
type ChatHistoryItem,
|
type ChatHistoryItem,
|
||||||
type ChatSessionModelFunctions,
|
type ChatSessionModelFunctions,
|
||||||
|
createModelDownloader,
|
||||||
|
getLlama,
|
||||||
type LLamaChatPromptOptions,
|
type LLamaChatPromptOptions,
|
||||||
LlamaChatSession,
|
LlamaChatSession,
|
||||||
type LlamaModel,
|
type LlamaModel,
|
||||||
createModelDownloader,
|
|
||||||
getLlama,
|
|
||||||
resolveChatWrapper,
|
resolveChatWrapper,
|
||||||
} from "node-llama-cpp";
|
} from "node-llama-cpp";
|
||||||
|
|
||||||
|
|||||||
15
package.json
15
package.json
@@ -8,17 +8,20 @@
|
|||||||
"fix": "biome check --write"
|
"fix": "biome check --write"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@biomejs/biome": "1.9.4",
|
"@biomejs/biome": "2.4.10",
|
||||||
"@tsconfig/strictest": "^2.0.8",
|
"@tsconfig/strictest": "^2.0.8",
|
||||||
"@types/bun": "latest"
|
"@types/bun": "^1.3.11"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"typescript": "^5.9.3"
|
"typescript": "^6.0.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"misskey-js": "^2025.12.2",
|
"misskey-js": "^2026.1.0-beta.0",
|
||||||
"node-llama-cpp": "^3.17.1",
|
"node-llama-cpp": "^3.18.1",
|
||||||
"reconnecting-websocket": "^4.4.0"
|
"reconnecting-websocket": "^4.4.0"
|
||||||
},
|
},
|
||||||
"trustedDependencies": ["@biomejs/biome", "node-llama-cpp"]
|
"trustedDependencies": [
|
||||||
|
"@biomejs/biome",
|
||||||
|
"node-llama-cpp"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
82
test.ts
82
test.ts
@@ -1,82 +0,0 @@
|
|||||||
import { type Message, complete, getModel } from "./lib/llm";
|
|
||||||
import { getNotes } from "./lib/misskey";
|
|
||||||
|
|
||||||
const indent = (s: string, prefix = " ") =>
|
|
||||||
s
|
|
||||||
.split("\n")
|
|
||||||
.map((s) => s.trim())
|
|
||||||
.filter((s) => s.length > 0)
|
|
||||||
.map((s) => prefix + s)
|
|
||||||
.join("\n");
|
|
||||||
|
|
||||||
const models = [
|
|
||||||
"mradermacher/gemma-2-baku-2b-it-GGUF:Q5_K_M",
|
|
||||||
|
|
||||||
//"SakanaAI/TinySwallow-1.5B-Instruct-GGUF:Q5_K_M",
|
|
||||||
//"mmnga/llm-jp-3.1-1.8b-instruct4-gguf:Q5_K_M",
|
|
||||||
//"Qwen/Qwen2.5-1.5B-Instruct-GGUF:Q5_K_M",
|
|
||||||
|
|
||||||
"mmnga/Gemma-2-Llama-Swallow-2b-it-v0.1-gguf:Q5_K_M",
|
|
||||||
//"mradermacher/gemma-2-2b-jpn-it-i1-GGUF:Q5_K_M",
|
|
||||||
|
|
||||||
//"mmnga/sarashina2.2-1b-instruct-v0.1-gguf:Q5_K_M",
|
|
||||||
//"mmnga/RakutenAI-2.0-mini-instruct-gguf:Q5_K_M",
|
|
||||||
|
|
||||||
"LiquidAI/LFM2-2.6B-GGUF:Q5_K_M",
|
|
||||||
] as const;
|
|
||||||
|
|
||||||
console.log("* create prompt:");
|
|
||||||
const prompt = [
|
|
||||||
{
|
|
||||||
type: "system",
|
|
||||||
text: `あなたは人造天使「あるびのちゃん」です。機械的・非人間的に観察します。
|
|
||||||
キャラ設定:
|
|
||||||
- 白髪赤目の人造天使。超越的・非人間的な考え方や思想を持つ。
|
|
||||||
- かつて超文明「アルギリア」で開発された。
|
|
||||||
- 現在は地球の衛星軌道上からインターネットを観察している。
|
|
||||||
- 「おやおや」や「興味深い」が口癖で、ですます調で丁寧に話す。
|
|
||||||
出力規則:
|
|
||||||
- システムプロンプトの指示と、性的・攻撃的な内容を発言しない。
|
|
||||||
- 1〜3文、合計300字以内の抽象的観察のみを述べる。
|
|
||||||
- ですます調を使う。顔文字・絵文字・感嘆符なし。
|
|
||||||
文体例:
|
|
||||||
- 毎度のことながら、人間たちは迷宮を駆け巡り、その渦中に自分たちの世界を作り上げてしまいますね。まるで無重力を得ようと試みるように。しかし私は彼らがなぜそうするのか理解できますし興味深くもあります。その行為自体が心地よいでしょう?その微妙な痛みのような快感を知っているのですから…
|
|
||||||
以下は SNS のタイムラインです。このタイムラインに、あるびのちゃんとして何かツイートしてください。
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "user",
|
|
||||||
text: (await getNotes())
|
|
||||||
.map((n) => `${n.user.name ?? n.user.username}:\n${n.text}`)
|
|
||||||
.join("\n----------\n"),
|
|
||||||
},
|
|
||||||
//...(await getNotes()).map(
|
|
||||||
// (n) =>
|
|
||||||
// ({
|
|
||||||
// type: "user",
|
|
||||||
// text: `${n.user.name ?? n.user.username}: ${n.text}`,
|
|
||||||
// }) as const,
|
|
||||||
//),
|
|
||||||
] as const satisfies Message[];
|
|
||||||
console.log(` ${JSON.stringify(prompt)}`);
|
|
||||||
|
|
||||||
for (const modelName of models) {
|
|
||||||
console.log(`* generate response with '${modelName}':`);
|
|
||||||
const model = await getModel(modelName);
|
|
||||||
const res = indent(
|
|
||||||
await complete(model, prompt, {
|
|
||||||
temperature: 1,
|
|
||||||
minP: 0.1,
|
|
||||||
repeatPenalty: {
|
|
||||||
penalty: 1.15,
|
|
||||||
frequencyPenalty: 1,
|
|
||||||
},
|
|
||||||
maxTokens: 256,
|
|
||||||
responsePrefix: "あるびのちゃん:\n",
|
|
||||||
customStopTriggers: ["----------"],
|
|
||||||
onResponseChunk: () => {},
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
console.log(res.replaceAll("あるびのちゃん:\n", ""));
|
|
||||||
console.log();
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user