From 4331547f6c8692e560b1c45bf6c6f99329400a1b Mon Sep 17 00:00:00 2001 From: TheSisb Date: Thu, 5 Oct 2023 17:28:10 -0500 Subject: [PATCH] fix: rewrite in deno --- .github/workflows/on_discussion_create.yml | 34 ++--- package.json | 3 +- tools/github/autoresponder.ts | 137 +++++++++++++++++++++ 3 files changed, 146 insertions(+), 28 deletions(-) create mode 100644 tools/github/autoresponder.ts diff --git a/.github/workflows/on_discussion_create.yml b/.github/workflows/on_discussion_create.yml index 9a14002b84..293159ace4 100644 --- a/.github/workflows/on_discussion_create.yml +++ b/.github/workflows/on_discussion_create.yml @@ -1,40 +1,20 @@ name: Discussion auto-responder - on: discussion: types: [created] - env: GITHUB_TOKEN: ${{ github.token }} - jobs: on_discussion_open: runs-on: ubuntu-latest permissions: discussions: write steps: - - name: Get answer from Discussion - id: httpRequest - uses: fjogeleit/http-request-action@v1 + - name: Checkout repository + uses: actions/checkout@v2 + - name: Set up Deno + uses: denolib/setup-deno@v2 with: - url: "https://paste.twilio.design/api/ai" - method: "POST" - customHeaders: '{"Content-Type": "application/json"}' - data: '{"secret": "${{ secrets.OPENAI_API_SECRET }}", "prompt": "${{ github.event.discussion.body }}"}' - - name: Show Response - run: | - echo ${{ steps.httpRequest.outputs.response }} - - name: Add bot comment to discussion - uses: octokit/graphql-action@v2.x - with: - query: | - mutation discussion($discussionid:ID!,$body:String!) { - addDiscussionComment(input: { discussionId:$discussionid,body:$body}) { - comment { - id - } - } - } - variables: | - discussionid: ${{ github.event.discussion.node_id }} - body: ${{ steps.httpRequest.outputs.response }} + deno-version: "v1.x" + - name: Run Deno script + run: npm run ci:autorespond -- "${{ github.event.discussion.body }}" "${{ github.event.discussion.node_id }}" diff --git a/package.json b/package.json index 00f1b2e676..41ef9e98ee 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/tools/github/autoresponder.ts b/tools/github/autoresponder.ts new file mode 100644 index 0000000000..3bebdf9b5e --- /dev/null +++ b/tools/github/autoresponder.ts @@ -0,0 +1,137 @@ +/* eslint-disable no-console,import/no-unresolved,import/extensions */ +// @ts-expect-error deno +import { config } from "https://deno.land/x/dotenv/mod.ts"; + +// Load .env file +const env = config(); +const { API_SECRET, GH_SERVICE_ACC_DISCUSSIONS_TOKEN } = env; + +// @ts-expect-error deno +const discussionBody = Deno.args[0]; +// @ts-expect-error deno +const discussionNodeId = Deno.args[1]; + +console.log("discussion body:", discussionBody); +console.log("discussion node_id:", discussionNodeId); + + +type Discussion = { + path: string; + content: string; + similarity: number; + source: string; + type: string; + meta: Record; + heading: string; + slug: string; +}; +type DiscussionsResponse = { + data: Discussion[]; +}; +interface SimilarDiscussion { + title: string; + url: string; + similarity: number; + updatedAt: string; +} + +const getSimilarDiscussions = async (secret: string, question: string): Promise => { + 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 = await response.json(); + + return ( + // Get the top 3 results at most + responseJson.data + .slice(0, 3) + // Remove unnecessary data from the response + .map((item: Discussion) => ({ + title: item.heading, + url: item.path, + similarity: item.similarity, + updatedAt: item.meta.updatedAt, + })) + // We only want results that are fairly similar, not guesses + .filter((item: SimilarDiscussion) => item.similarity > 0.78) + // Convert to markdown + .map((item: SimilarDiscussion) => `[${item.title}](${item.url}) (updated: ${item.updatedAt}, similarity score: ${item.similarity.toFixed(2)})`) + // Convert to string + .join("\n - ") + ); +}; + +const getAnswerFromAi = async (secret: string, question: string): Promise => { + 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(); +}; + +// @ts-expect-error deno +const similarDiscussions = await getSimilarDiscussions(API_SECRET, discussionBody); + +console.log("Similar discussions:", similarDiscussions); + +// @ts-expect-error deno +const answerFromAi = await getAnswerFromAi(API_SECRET, discussionBody); + +console.log("AI answer:", answerFromAi); + + +const writeAnswerToDiscussion = async (discussionId: string, body: string): Promise => { + + const mutation = ` + mutation { + addDiscussionComment(input: { discussionId:${discussionId},body:${body}}) { + comment { + id + } + } + } + `; + + const response = await fetch(`https://api.github.com/graphql`, { + method: "POST", + headers: { + Authorization: `Bearer ${GH_SERVICE_ACC_DISCUSSIONS_TOKEN}`, + "Content-Type": "application/json", + }, + body: JSON.stringify({ query: mutation }), + }); + + return response.json(); +}; + + +const commentHeader = ` + This is a very experimental bot that uses OpenAI's GPT-4 to respond to discussions. The answers are not guaranteed to be correct. + + We hope it provides a quicker way to get or find answers to your questions. Please wait for a member of the Design System team to confirm the answer. + + --- + +`; +const fullBody = `${commentHeader}${answerFromAi}\n\n---\n\n${similarDiscussions}`; + +// Call the fetchGraphQL function and log the response +writeAnswerToDiscussion(discussionNodeId, fullBody) + .then((result) => console.log("Write to discussion:", result)) + .catch((error) => console.log(error)); + +/* eslint-enable no-console,import/no-unresolved,import/extensions */