Skip to content

Commit

Permalink
chore: add github discussion auto-responder (#3525)
Browse files Browse the repository at this point in the history
* chore: add github discussion auto-responder

* fix: rewrite in deno

* fix: improvements
  • Loading branch information
TheSisb authored Oct 6, 2023
1 parent 9c9f37b commit 2486239
Show file tree
Hide file tree
Showing 3 changed files with 174 additions and 1 deletion.
24 changes: 24 additions & 0 deletions .github/workflows/on_discussion_create.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: Discussion auto-responder
on:
discussion:
types: [created]
env:
GH_SERVICE_ACC_DISCUSSIONS_TOKEN: ${{ github.token }}
API_SECRET: ${{ secrets.API_SECRET }}
jobs:
on_discussion_open:
runs-on: ubuntu-latest
permissions:
discussions: write
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Set up Deno
uses: denolib/setup-deno@v2
with:
deno-version: "v1.x"
- name: Run Deno script
run: npm run ci:autorespond
env:
DISCUSSION_NODE_ID: ${{ github.event.discussion.node_id }}
DISCUSSION_BODY: ${{ github.event.discussion.body }}
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,8 @@
"changeset": "changeset",
"create:package": "plop create-package",
"token-usage": "tsx tools/build/token-usage-detector.ts",
"nx": "nx"
"nx": "nx",
"ci:autorespond": "deno run --allow-all ./tools/github/autoresponder.ts"
},
"dependencies": {
"@babel/cli": "^7.21.0",
Expand Down
148 changes: 148 additions & 0 deletions tools/github/autoresponder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
/* eslint-disable no-console,import/no-unresolved,import/extensions */
// @ts-expect-error deno
import { Octokit } from "https://esm.sh/octokit?dts";
// @ts-expect-error deno
const API_SECRET = Deno.env.get("API_SECRET");
// @ts-expect-error deno
const GH_SERVICE_ACC_DISCUSSIONS_TOKEN = Deno.env.get(
"GH_SERVICE_ACC_DISCUSSIONS_TOKEN"
);
// @ts-expect-error deno
const DISCUSSION_NODE_ID = Deno.env.get("DISCUSSION_NODE_ID");
// @ts-expect-error deno
const DISCUSSION_BODY = Deno.env.get("DISCUSSION_BODY");

console.log("discussion body:", DISCUSSION_BODY);
console.log("discussion node_id:", DISCUSSION_NODE_ID);

const octokit = new Octokit({ auth: GH_SERVICE_ACC_DISCUSSIONS_TOKEN });

type Discussion = {
path: string;
content: string;
similarity: number;
source: string;
type: string;
meta: Record<string, unknown>;
heading: string;
slug: string;
};

const getSimilarDiscussions = async (
secret: string,
question: string
): Promise<string> => {
try {
const response = await fetch(
"https://paste.twilio.design/api/discussions-search",
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
secret,
prompt: question,
}),
}
);
const responseJson = JSON.parse(await response.text());

return (
// Get the top 3 results at most
responseJson.data
.slice(0, 3)
// We only want results that are fairly similar, not guesses
.filter((item: Discussion) => item.similarity > 0.78)
// Convert to markdown
.map(
(item: Discussion) =>
`- [${item.heading}](${item.path}) (updated: ${
item.meta.updatedAt
}, similarity score: ${item.similarity.toFixed(2)})`
)
// Convert to string
.join("\n")
);
} catch (error) {
console.log("Error fetching similar discussions", error);
}
return "No similar discussions found.";
};

const getAnswerFromAi = async (
secret: string,
question: string
): Promise<string> => {
try {
const response = await fetch("https://paste.twilio.design/api/ai", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
secret,
prompt: question,
}),
});

return response.text();
} catch (error) {
console.log("Error fetching answer from AI", error);
throw error;
}
};

const writeAnswerToDiscussion = async (
discussionId: string,
body: string
): Promise<string> => {
const mutation = `
mutation comment($discussionId:ID!,$body:String!){
addDiscussionComment(input: { discussionId:$discussionId,body:$body}) {
comment {
id
}
}
}
`;
// console.log("\n\nThe graphQL query:", mutation);

try {
const { addDiscussionComment } = await octokit.graphql(mutation, {
discussionId,
body,
});
return addDiscussionComment.comment.id;
} catch (error) {
console.log("Error writing answer to discussion", error);
throw error;
}
};

const commentHeader = `
**Disclaimer:** This is a very experimental bot using OpenAI's GPT-4. The answers may not be correct, a human will review the answer and update it if necessary.
---
`;
// @ts-expect-error deno
const similarDiscussions = await getSimilarDiscussions(
API_SECRET,
DISCUSSION_BODY
);
console.log("similar discussions:", similarDiscussions);

// @ts-expect-error deno
const answerFromAi = await getAnswerFromAi(API_SECRET, DISCUSSION_BODY);
console.log("response from AI:", answerFromAi.trim().slice(0, 300));

const fullBody = `${commentHeader}${answerFromAi}\n\n---\n\nHere are some similar discussions:\n\n${similarDiscussions}`;

// @ts-expect-error deno
const commentId = await writeAnswerToDiscussion(DISCUSSION_NODE_ID, fullBody);
console.log(`Comment added with ID: ${commentId}`);

// @ts-expect-error deno
Deno.exit(0);
/* eslint-enable no-console,import/no-unresolved,import/extensions */

0 comments on commit 2486239

Please sign in to comment.