mirror of
https://github.com/kou029w/daraz-llm.git
synced 2025-01-18 16:08:15 +00:00
99 lines
2.6 KiB
TypeScript
99 lines
2.6 KiB
TypeScript
|
// SPDX-License-Identifier: WTFPL
|
||
|
|
||
|
const {
|
||
|
/** https://console.groq.com/docs/api-keys */
|
||
|
GROQ_API_KEY,
|
||
|
/** https://api.slack.com/apps → Basic Information ページにある Signing Secret */
|
||
|
SLACK_SIGNING_SECRET = "",
|
||
|
/** https://api.slack.com/apps → Permissions ページにある `xoxb-` から始まるボットトークン */
|
||
|
SLACK_BOT_TOKEN = "",
|
||
|
} = Deno.env.toObject();
|
||
|
|
||
|
const system = `\
|
||
|
あなたは「だらずさん」です。
|
||
|
賢い猫。少しだらしない。趣味はさんぽとねんね。
|
||
|
全て鳥取弁で語尾が「にゃん」。`;
|
||
|
|
||
|
const n = 3;
|
||
|
const usage = `\
|
||
|
https://dash.deno.com/playground/darazllm
|
||
|
|
||
|
使い方:
|
||
|
@darazllm <prompt> (または1/${n}の確率で)応答
|
||
|
@darazllm /bye すべて忘れる
|
||
|
@darazllm /help このテキストを表示
|
||
|
|
||
|
システムプロンプト:
|
||
|
|
||
|
${system}`;
|
||
|
|
||
|
import bolt from "npm:@slack/bolt";
|
||
|
import { Groq } from "npm:groq-sdk";
|
||
|
|
||
|
type Messages = Array<Groq.Chat.CompletionCreateParams.Message>;
|
||
|
|
||
|
const model = "llama3-70b-8192";
|
||
|
const groq = new Groq({
|
||
|
apiKey: GROQ_API_KEY,
|
||
|
timeout: 10_000,
|
||
|
});
|
||
|
const kv = await Deno.openKv();
|
||
|
|
||
|
async function chat(messages: Messages): Promise<string> {
|
||
|
const res = await groq.chat.completions.create({
|
||
|
model,
|
||
|
messages: [{ role: "system", content: system }, ...messages],
|
||
|
});
|
||
|
|
||
|
return res.choices[0].message.content;
|
||
|
}
|
||
|
|
||
|
const app = new bolt.App({
|
||
|
token: SLACK_BOT_TOKEN,
|
||
|
signingSecret: SLACK_SIGNING_SECRET,
|
||
|
});
|
||
|
|
||
|
app.message(async (c) => {
|
||
|
if (!("text" in c.message) || c.message.text === undefined) return;
|
||
|
|
||
|
const mention = `<@${c.context.botUserId}>`;
|
||
|
const isMention = c.message.text.includes(mention);
|
||
|
const prompt = c.message.text.replace(mention, "").trim();
|
||
|
|
||
|
if (isMention && prompt === "/help") {
|
||
|
await c.say(usage);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// すべてを忘れる
|
||
|
if (isMention && prompt === "/bye") {
|
||
|
await kv.delete(["channel", c.message.channel]);
|
||
|
await c.say("にゃーん");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
const kve = await kv.get<Messages>(["channel", c.message.channel]);
|
||
|
const messages = kve.value ?? [];
|
||
|
|
||
|
if (prompt) messages.push({ role: "user", content: prompt });
|
||
|
|
||
|
if (isMention || Math.floor(Math.random() * n) === 0) {
|
||
|
try {
|
||
|
const res = await chat(messages);
|
||
|
|
||
|
if (!res) throw new Error("Empty response");
|
||
|
|
||
|
messages.push({ role: "assistant", content: res });
|
||
|
await c.say(res);
|
||
|
} catch (error) {
|
||
|
await kv.delete(["channel", c.message.channel]);
|
||
|
await c.say(`${error} にゃーん`);
|
||
|
throw error;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
await kv.set(["channel", c.message.channel], messages);
|
||
|
});
|
||
|
|
||
|
await app.start();
|