Skip to content
This repository has been archived by the owner on Sep 19, 2024. It is now read-only.

Commit

Permalink
feat: ask Command (#663)
Browse files Browse the repository at this point in the history
* feat(deps): added openai

    added openAI

* feat: added ask command

    Simple prompt engineering, one response per user per question.

* fix: update prompt

better initial prompt

* fix: update system prompt

update prompt

* feat: simple memory

original context indexable across repos, issues and pull requests

* fix: comments

comments etc

* fix: streamlined context

waiting for more input

* chore: pulling context

pulls from sources using the hashtag in issue body

* chore: tokenization

total token usage now displayed

* chore: diff comparison

feed gpt the spec and diff for it to analyze

* fix: ci and spec check

will only spec check once now fixed tsc issues

* fix: merge mistake

sort import

* fix: key from config

allow it to be set in org and repo config

* fix: set ask to false

reset default

* fix(deps): langchain

unused dep

* fix: apikey undefined

no longer undefined

* fix: using token limit

set to 8k in config 4k by default

* chore: improve ask command

improved pr-review and context for both ask and pr-review commands

* fix: gpt helpers

moved base ask and context call into helpers

* chore: use error diff

replace text error with diff

* fix: remove unused

requested changes

* fix: remove unused

missed the shared default

* fix: schema

new types

* fix: nullable and error

start with a bang and clear api key error

* fix: duplicated

---------

Co-authored-by: 0xcodercrane <[email protected]>
  • Loading branch information
Keyrxng and 0xcodercrane authored Sep 21, 2023
1 parent 3966177 commit c40efc5
Show file tree
Hide file tree
Showing 15 changed files with 1,627 additions and 1,110 deletions.
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ SUPABASE_KEY=
AUTO_PAY_MODE=
ANALYTICS_MODE=


# Use `trace` to get verbose logging or `info` to show less
LOG_LEVEL=debug
LOGDNA_INGESTION_KEY=
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
"node-html-parser": "^6.1.5",
"node-html-to-image": "^3.3.0",
"nodemon": "^2.0.19",
"openai": "^4.2.0",
"parse5": "^7.1.2",
"prettier": "^2.7.1",
"probot": "^12.2.4",
Expand Down
6 changes: 6 additions & 0 deletions src/bindings/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ export const loadConfig = async (context: Context): Promise<BotConfig> => {
registerWalletWithVerification,
staleBountyTime,
enableAccessControl,
openAIKey,
openAITokenLimit,
} = await getWideConfig(context);

const publicKey = await getScalarKey(process.env.X25519_PRIVATE_KEY);
Expand Down Expand Up @@ -99,6 +101,10 @@ export const loadConfig = async (context: Context): Promise<BotConfig> => {
wallet: {
registerWalletWithVerification: registerWalletWithVerification,
},
ask: {
apiKey: openAIKey,
tokenLimit: openAITokenLimit,
},
accessControl: enableAccessControl,
};

Expand Down
2 changes: 1 addition & 1 deletion src/configs/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export * from "./shared";
export * from "./strings";
export * from "./abis";
export * from "./ubiquibot-config-default";
export * from "./ubiquibot-config-default";
92 changes: 49 additions & 43 deletions src/configs/ubiquibot-config-default.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { MergedConfig } from "../types";

export const DefaultConfig : MergedConfig = {
export const DefaultConfig: MergedConfig = {
"evm-network-id": 100,
"price-multiplier": 1,
"issue-creator-multiplier": 2,
Expand All @@ -10,88 +10,94 @@ export const DefaultConfig : MergedConfig = {
"disable-analytics": false,
"comment-incentives": false,
"register-wallet-with-verification": false,
"promotion-comment": "\n<h6>If you enjoy the DevPool experience, please follow <a href='https://github.com/ubiquity'>Ubiquity on GitHub</a> and star <a href='https://github.com/ubiquity/devpool-directory'>this repo</a> to show your support. It helps a lot!</h6>",
"openai-api-key": null,
"openai-token-limit": 8000,
"stale-bounty-time": "0d",
"promotion-comment":
"\n<h6>If you enjoy the DevPool experience, please follow <a href='https://github.com/ubiquity'>Ubiquity on GitHub</a> and star <a href='https://github.com/ubiquity/devpool-directory'>this repo</a> to show your support. It helps a lot!</h6>",
"default-labels": [],
"time-labels": [
{
"name": "Time: <1 Hour"
name: "Time: <1 Hour",
},
{
"name": "Time: <1 Day"
name: "Time: <1 Day",
},
{
"name": "Time: <1 Week"
name: "Time: <1 Week",
},
{
"name": "Time: <2 Weeks"
name: "Time: <2 Weeks",
},
{
"name": "Time: <1 Month"
}
name: "Time: <1 Month",
},
],
"priority-labels": [
{
"name": "Priority: 1 (Normal)"
name: "Priority: 1 (Normal)",
},
{
"name": "Priority: 2 (Medium)"
name: "Priority: 2 (Medium)",
},
{
"name": "Priority: 3 (High)"
name: "Priority: 3 (High)",
},
{
"name": "Priority: 4 (Urgent)"
name: "Priority: 4 (Urgent)",
},
{
"name": "Priority: 5 (Emergency)"
}
name: "Priority: 5 (Emergency)",
},
],
"command-settings": [
{
"name": "start",
"enabled": false
name: "start",
enabled: false,
},
{
name: "stop",
enabled: false,
},
{
"name": "stop",
"enabled": false
name: "wallet",
enabled: false,
},
{
"name": "wallet",
"enabled": false
name: "payout",
enabled: false,
},
{
"name": "payout",
"enabled": false
name: "multiplier",
enabled: false,
},
{
"name": "multiplier",
"enabled": false
name: "query",
enabled: false,
},
{
"name": "query",
"enabled": false
name: "ask",
enabled: false,
},
{
"name": "allow",
"enabled": false
name: "allow",
enabled: false,
},
{
"name": "autopay",
"enabled": false
}
name: "autopay",
enabled: false,
},
],
"incentives": {
"comment": {
"elements": {},
"totals": {
"word": 0
}
}
incentives: {
comment: {
elements: {},
totals: {
word: 0,
},
},
},
"enable-access-control": {
"label": false,
"organization": true
label: false,
organization: true,
},
"stale-bounty-time":"0d"
}

};
1 change: 1 addition & 0 deletions src/handlers/comment/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export enum IssueCommentCommands {
PAYOUT = "/payout", // request permit payout
MULTIPLIER = "/multiplier", // set bounty multiplier (for treasury)
QUERY = "/query",
ASK = "/ask", // ask GPT a question
// Access Controls

ALLOW = "/allow",
Expand Down
123 changes: 123 additions & 0 deletions src/handlers/comment/handlers/ask.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import { getBotContext, getLogger } from "../../../bindings";
import { Payload, StreamlinedComment, UserType } from "../../../types";
import { getAllIssueComments, getAllLinkedIssuesAndPullsInBody } from "../../../helpers";
import { CreateChatCompletionRequestMessage } from "openai/resources/chat";
import { askGPT, decideContextGPT, sysMsg } from "../../../helpers/gpt";
import { ErrorDiff } from "../../../utils/helpers";

/**
* @param body The question to ask
*/
export const ask = async (body: string) => {
const context = getBotContext();
const logger = getLogger();

const payload = context.payload as Payload;
const sender = payload.sender.login;
const issue = payload.issue;

if (!body) {
return `Please ask a question`;
}

if (!issue) {
return `This command can only be used on issues`;
}

const chatHistory: CreateChatCompletionRequestMessage[] = [];
const streamlined: StreamlinedComment[] = [];
let linkedPRStreamlined: StreamlinedComment[] = [];
let linkedIssueStreamlined: StreamlinedComment[] = [];

const regex = /^\/ask\s(.+)$/;
const matches = body.match(regex);

if (matches) {
const [, body] = matches;

// standard comments
const comments = await getAllIssueComments(issue.number);
// raw so we can grab the <!--- { 'UbiquityAI': 'answer' } ---> tag
const commentsRaw = await getAllIssueComments(issue.number, "raw");

if (!comments) {
logger.info(`Error getting issue comments`);
return ErrorDiff(`Error getting issue comments`);
}

// add the first comment of the issue/pull request
streamlined.push({
login: issue.user.login,
body: issue.body,
});

// add the rest
comments.forEach(async (comment, i) => {
if (comment.user.type == UserType.User || commentsRaw[i].body.includes("<!--- { 'UbiquityAI': 'answer' } --->")) {
streamlined.push({
login: comment.user.login,
body: comment.body,
});
}
});

// returns the conversational context from all linked issues and prs
const links = await getAllLinkedIssuesAndPullsInBody(issue.number);

if (typeof links === "string") {
logger.info(`Error getting linked issues or prs: ${links}`);
} else {
linkedIssueStreamlined = links.linkedIssues;
linkedPRStreamlined = links.linkedPrs;
}

// let chatgpt deduce what is the most relevant context
const gptDecidedContext = await decideContextGPT(chatHistory, streamlined, linkedPRStreamlined, linkedIssueStreamlined);

if (linkedIssueStreamlined.length == 0 && linkedPRStreamlined.length == 0) {
// No external context to add
chatHistory.push(
{
role: "system",
content: sysMsg,
name: "UbiquityAI",
} as CreateChatCompletionRequestMessage,
{
role: "user",
content: body,
name: sender,
} as CreateChatCompletionRequestMessage
);
} else {
chatHistory.push(
{
role: "system",
content: sysMsg, // provide the answer template
name: "UbiquityAI",
} as CreateChatCompletionRequestMessage,
{
role: "system",
content: "Original Context: " + JSON.stringify(gptDecidedContext), // provide the context
name: "system",
} as CreateChatCompletionRequestMessage,
{
role: "user",
content: "Question: " + JSON.stringify(body), // provide the question
name: "user",
} as CreateChatCompletionRequestMessage
);
}

const gptResponse = await askGPT(body, chatHistory);

if (typeof gptResponse === "string") {
return gptResponse;
} else if (gptResponse.answer) {
return gptResponse.answer;
} else {
return ErrorDiff(`Error getting response from GPT`);
}
} else {
return "Invalid syntax for ask \n usage: '/ask What is pi?";
}
};
8 changes: 8 additions & 0 deletions src/handlers/comment/handlers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { listAvailableCommands } from "./help";
import { unassign } from "./unassign";
import { registerWallet } from "./wallet";
import { setAccess } from "./allow";
import { ask } from "./ask";
import { multiplier } from "./multiplier";
import { BigNumber, ethers } from "ethers";
import { addPenalty } from "../../../adapters/supabase";
Expand Down Expand Up @@ -44,6 +45,7 @@ export * from "./payout";
export * from "./help";
export * from "./multiplier";
export * from "./query";
export * from "./ask";

export interface RewardsResponse {
error: string | null;
Expand Down Expand Up @@ -284,6 +286,12 @@ export const userCommands = (): UserCommands[] => {
handler: query,
callback: commandCallback,
},
{
id: IssueCommentCommands.ASK,
description: `Ask a technical question to the Ubiquity AI. \n example usage: "/ask How do I do X?"`,
handler: ask,
callback: commandCallback,
},
{
id: IssueCommentCommands.MULTIPLIER,
description: `Set the bounty payout multiplier for a specific contributor, and provide the reason for why. \n example usage: "/wallet @user 0.5 'Multiplier reason'"`,
Expand Down
Loading

0 comments on commit c40efc5

Please sign in to comment.