mirror of
https://github.com/kou029w/daraz-llm.git
synced 2025-01-18 16:08:15 +00:00
Groq/Denoに変更!
- Groq … OpenAI互換API・安価 - Deno … Deno Deployへの簡単デプロイ
This commit is contained in:
parent
2d80377e87
commit
90b8873e2f
10 changed files with 110 additions and 7866 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,2 +0,0 @@
|
||||||
.vercel
|
|
||||||
node_modules
|
|
35
README.md
35
README.md
|
@ -1,42 +1,19 @@
|
||||||
# だらずさん GPT
|
# だらずさん LLM
|
||||||
|
|
||||||
GPT 版だらずさん
|
LLM 版だらずさん
|
||||||
|
|
||||||
## インストール
|
## インストール
|
||||||
|
|
||||||
Step 1
|
Step 1
|
||||||
: Slack アプリの作成
|
: Slack アプリの作成
|
||||||
|
|
||||||
[Slack Applications](https://api.slack.com/apps) → Create New App → From an app manifest
|
[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
|
||||||
|
|
||||||
下記の 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
|
|
||||||
```
|
|
||||||
|
|
||||||
Step 2
|
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)
|
Step 3
|
||||||
|
: [Slack Applications](https://api.slack.com/apps) > "darazllm" > Event Subscriptions ページの URL を Deno Deploy の URL (例: `https://darazllm.deno.dev/slack/events`) に変更
|
||||||
- `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)
|
|
||||||
|
|
||||||
## ライセンス
|
## ライセンス
|
||||||
|
|
||||||
|
|
|
@ -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
6
deno.json
Normal 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
98
main.ts
Normal 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
7762
package-lock.json
generated
File diff suppressed because it is too large
Load diff
11
package.json
11
package.json
|
@ -1,11 +0,0 @@
|
||||||
{
|
|
||||||
"name": "daraz-gpt",
|
|
||||||
"version": "0.0.0",
|
|
||||||
"private": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@slack/bolt": "^3.12.2"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"vercel": "^34.0.0"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
OK
|
|
|
@ -1,4 +0,0 @@
|
||||||
{
|
|
||||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
|
||||||
"extends": ["config:base", "config:semverAllMonthly", ":automergeMinor"]
|
|
||||||
}
|
|
10
vercel.json
10
vercel.json
|
@ -1,10 +0,0 @@
|
||||||
{
|
|
||||||
"build": {
|
|
||||||
"env": {
|
|
||||||
"NODEJS_AWS_HANDLER_NAME": "handler"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"github": {
|
|
||||||
"silent": true
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Add table
Reference in a new issue