Groq/Denoに変更!

- Groq … OpenAI互換API・安価
- Deno … Deno Deployへの簡単デプロイ
This commit is contained in:
Nebel 2024-04-26 10:38:17 +09:00
parent 2d80377e87
commit 90b8873e2f
Signed by: nebel
GPG key ID: 79807D08C6EF6460
10 changed files with 110 additions and 7866 deletions

2
.gitignore vendored
View file

@ -1,2 +0,0 @@
.vercel
node_modules

View file

@ -1,42 +1,19 @@
# だらずさん GPT
# だらずさん LLM
GPT 版だらずさん
LLM 版だらずさん
## インストール
Step 1
: Slack アプリの作成
[Slack Applications](https://api.slack.com/apps) → Create New App → From an app manifest
下記の Manifest をコピペして、Slack アプリを作成
```yaml
display_information:
name: daraz-gpt
features:
bot_user:
display_name: daraz-gpt
oauth_config:
scopes:
bot:
- app_mentions:read
- chat:write
settings:
event_subscriptions:
request_url: https://daraz-gpt.vercel.app/api/slack/events
bot_events:
- app_mention
```
[Create New Slack App](https://api.slack.com/apps?new_app=1&manifest_yaml={display_information:%20{name:%20darazllm},%20features:%20{bot_user:%20{display_name:%20darazllm}},%20oauth_config:%20{scopes:%20{bot:%20[%27channels:history%27,%20%27chat:write%27]}},%20settings:%20{event_subscriptions:%20{request_url:%20%27https://darazllm.deno.dev/slack/events%27,%20bot_events:%20[message.channels]}}}) > Select a workspace > Create > Install to Workspace
Step 2
: Vercel へのデプロイ
: [Fork to Edit](https://dash.deno.com/playground/darazllm) > Settings > Environment Variables
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fkou029w%2Fdaraz-gpt&env=SLACK_BOT_TOKEN,SLACK_SIGNING_SECRET,OPENAI_API_KEY)
- `SLACK_BOT_TOKEN` ... [Slack Applications](https://api.slack.com/apps) → "daraz-gpt" → Permissions ページにある `xoxb-` から始まるボットトークン
- `SLACK_SIGNING_SECRET` ... [Slack Applications](https://api.slack.com/apps) → "daraz-gpt" → Basic Information ページにある Signing Secret
- `OPENAI_API_KEY` … [OpenAI API Key](https://beta.openai.com/account/api-keys)
Step 3
: [Slack Applications](https://api.slack.com/apps) > "darazllm" > Event Subscriptions ページの URL を Deno Deploy の URL (例: `https://darazllm.deno.dev/slack/events`) に変更
## ライセンス

View file

@ -1,47 +0,0 @@
import { App, AwsLambdaReceiver } from "@slack/bolt";
import type { AwsHandler } from "@slack/bolt/dist/receivers/AwsLambdaReceiver";
const openaiApiKey = process.env.OPENAI_API_KEY ?? "";
async function gpt(prompt: string): Promise<string> {
const endpoint = "https://api.openai.com/v1/completions";
const body = {
model: "text-davinci-003",
prompt,
temperature: 0.5,
max_tokens: 2048,
};
const res = await fetch(endpoint, {
method: "POST",
headers: {
"content-type": "application/json",
authorization: `Bearer ${openaiApiKey}`,
},
body: JSON.stringify(body),
});
const json = await res.json();
return json.choices[0].text.trim();
}
const token = process.env.SLACK_BOT_TOKEN ?? "";
const signingSecret = process.env.SLACK_SIGNING_SECRET ?? "";
const receiver = new AwsLambdaReceiver({ signingSecret });
const awsHandler = receiver.toHandler();
const app = new App({ token, receiver });
// required app_mentions:read chat:write
app.event("app_mention", async ({ event, say }) => {
const prompt = `語尾を「にゃん」にして質問にこたえる
Q:${event.text.replace(/^<@[0-9A-Z]+>/, "").trim()}
A:`;
const text = await gpt(prompt);
await say(text);
});
export const handler: AwsHandler = async (event, context, callback) => {
// AWS Lambda ウォームアップ時に発生しうるタイムアウトを無視
if (event.headers["x-slack-retry-reason"] === "http_timeout") {
return { statusCode: 200, body: "OK" };
}
return await awsHandler(event, context, callback);
};

6
deno.json Normal file
View file

@ -0,0 +1,6 @@
{
"imports": {
"@slack/bolt": "npm:@slack/bolt@^3.17.1",
"groq-sdk": "npm:groq-sdk@^0.3.2"
}
}

98
main.ts Normal file
View file

@ -0,0 +1,98 @@
// 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();

7762
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,11 +0,0 @@
{
"name": "daraz-gpt",
"version": "0.0.0",
"private": true,
"dependencies": {
"@slack/bolt": "^3.12.2"
},
"devDependencies": {
"vercel": "^34.0.0"
}
}

View file

@ -1 +0,0 @@
OK

View file

@ -1,4 +0,0 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": ["config:base", "config:semverAllMonthly", ":automergeMinor"]
}

View file

@ -1,10 +0,0 @@
{
"build": {
"env": {
"NODEJS_AWS_HANDLER_NAME": "handler"
}
},
"github": {
"silent": true
}
}