Skip to content

Commit

Permalink
Merge pull request #47 from ubiquibot/development
Browse files Browse the repository at this point in the history
Merge develop into main
  • Loading branch information
gentlementlegen authored Jul 7, 2024
2 parents 2a0252a + 48eeb02 commit ebc6c7b
Show file tree
Hide file tree
Showing 17 changed files with 12,312 additions and 8,781 deletions.
19 changes: 7 additions & 12 deletions .github/workflows/jest-testing.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
name: Run Jest Tests

name: Run Jest testing suite
on:
workflow_dispatch:
workflow_run:
workflows: ["Knip"]
types:
- completed
pull_request:

env:
NODE_ENV: "test"
Expand Down Expand Up @@ -33,10 +29,9 @@ jobs:
with:
fetch-depth: 0

- name: Jest With Coverage Comment
# Ensures this step is run even on previous step failure (e.g. test failed)
- name: Jest With Coverage
run: yarn install --immutable --immutable-cache --check-cache && yarn test

- name: Add Jest Report to Summary
if: always()
uses: ArtiomTr/jest-coverage-report-action@v2
with:
package-manager: yarn
prnumber: ${{ github.event.pull_request.number || github.event.workflow_run.pull_requests[0].number }}
run: echo "$(cat test-dashboard.md)" >> $GITHUB_STEP_SUMMARY
2 changes: 1 addition & 1 deletion .github/workflows/knip-reporter.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ on:
jobs:
knip-reporter:
runs-on: ubuntu-latest
if: ${{ github.event.workflow_run.conclusion != 'success' }}
steps:
- name: Checkout
uses: actions/checkout@v4
Expand All @@ -35,7 +36,6 @@ jobs:
trim: true

- name: Report knip results to pull request
if: ${{ github.event.workflow_run.conclusion != 'success' }}
uses: gitcoindev/knip-reporter@main
with:
verbose: true
Expand Down
47 changes: 47 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Changelog

## 1.0.0 (2024-06-11)


### Features

* cli argument to pass the issue URL to the program ([d4e9116](https://github.com/ubiquibot/conversation-rewards/commit/d4e91169ffd22b0f3bd0c26adc5829391c37437f))
* collect all user events ([147ba83](https://github.com/ubiquibot/conversation-rewards/commit/147ba83525c8626ebfccae97c30f368e087f4029))
* command line parsing arguments ([af93229](https://github.com/ubiquibot/conversation-rewards/commit/af932291d1b17f535b2cc5e5c02ce2ad4cfe7028))
* configuration parser ([0e6f3d1](https://github.com/ubiquibot/conversation-rewards/commit/0e6f3d192713bf5803b82aa5c80f73d8fab0989a))
* formatting is evaluated and influences the final score ([45d2831](https://github.com/ubiquibot/conversation-rewards/commit/45d2831ffb0337a68d4d4280f6a550c12c712d68))
* **get-activity:** properly fetches everything according to test ([6e067f7](https://github.com/ubiquibot/conversation-rewards/commit/6e067f71b69f58f1f1391ccce522c67fafd8fb94))
* github app login ([f4f4896](https://github.com/ubiquibot/conversation-rewards/commit/f4f4896b8611acd53f61685a6774665b5dfb8928))
* github app login ([b2efc68](https://github.com/ubiquibot/conversation-rewards/commit/b2efc68d996d9202ff4bd6a3385e9922e8eda846))
* github app login ([df34aa7](https://github.com/ubiquibot/conversation-rewards/commit/df34aa71a1c36563f34a14ea1fb4220642332012))
* github app login ([8add964](https://github.com/ubiquibot/conversation-rewards/commit/8add9648f2717d71b6fb32b806fb97fd7cad800c))
* github app login ([ad16266](https://github.com/ubiquibot/conversation-rewards/commit/ad1626672a42d5e2ba3f6404cb51db6d233e0c9c))
* github app login ([013519d](https://github.com/ubiquibot/conversation-rewards/commit/013519d80fad987f7ca7bfb2774f7d5ed00d9468))
* github app login ([39fa39d](https://github.com/ubiquibot/conversation-rewards/commit/39fa39d58f38e984e3b3120d09338becef753e36))
* link pull request from issue (not other way around) ([e6aa979](https://github.com/ubiquibot/conversation-rewards/commit/e6aa97973e7b8bb64551bd060ab6e2e005b6d4d3))
* moved tsx to production dependencies ([423f49e](https://github.com/ubiquibot/conversation-rewards/commit/423f49e2dfaff1b8ca4603100cd89aa41b0b6e52))
* pass in token from kernel to authenticate octokit client ([1f4ba00](https://github.com/ubiquibot/conversation-rewards/commit/1f4ba009bd81b3cbea79e8cde1735407d0504037))
* permit generation module ([925243f](https://github.com/ubiquibot/conversation-rewards/commit/925243f8ac5cc847b4b63ac76195d0d3de3c9fed))
* permit generation module ([50b396b](https://github.com/ubiquibot/conversation-rewards/commit/50b396b26e1bec433f193481004a7db6505f5ba5))
* read inputs from GitHub inputs instead of CLI ([30ac759](https://github.com/ubiquibot/conversation-rewards/commit/30ac759a2e81633304f91ff127a7d6848af420d2))
* saving permit to database ([aecd4e1](https://github.com/ubiquibot/conversation-rewards/commit/aecd4e127e9341ae18c18b14bf7c1c5dc8f98a6b))
* untested class to get all information ([f5104e1](https://github.com/ubiquibot/conversation-rewards/commit/f5104e14034cf2b6174bff1c6d3669aa177e438c))
* untested class to get all information ([a86c62f](https://github.com/ubiquibot/conversation-rewards/commit/a86c62f67c48a129dcb904d6fd69663c9e847f0d))
* updated jest test workflow ([a69f8d9](https://github.com/ubiquibot/conversation-rewards/commit/a69f8d9c82a8316b90f4c9f14b177185ebefcb25))
* updated jest test workflow ([834f821](https://github.com/ubiquibot/conversation-rewards/commit/834f821b42079c30d8e194749e6538e2d5a17ceb))


### Bug Fixes

* all tests pass ([db1868e](https://github.com/ubiquibot/conversation-rewards/commit/db1868e60fe96ea9f8a30a347d40e1cac7c9e067))
* **ci:** auth ([e897fdb](https://github.com/ubiquibot/conversation-rewards/commit/e897fdb4c0bcaeecbd6b6445a85a58d26b613338))
* cspell ([890a4a4](https://github.com/ubiquibot/conversation-rewards/commit/890a4a4c250d40d99fb6e127664c02544eef0826))
* cspell ignore words ([1160614](https://github.com/ubiquibot/conversation-rewards/commit/11606142d26cbd57c7c33f9e08d0e0a6bab689d2))
* fixed path for evm values ([2f3c2ee](https://github.com/ubiquibot/conversation-rewards/commit/2f3c2ee229400031e1fd95324d91677eda84925e))
* more characters are escaped (backtick, ampersand) to avoid display issues ([550869c](https://github.com/ubiquibot/conversation-rewards/commit/550869c13e48e4bb2865acb629bed66b6a3ab1e6))
* permit generation is skipped if price label is missing (configurable) ([d59cb0a](https://github.com/ubiquibot/conversation-rewards/commit/d59cb0a93c50770ec946514627ca34406e3da2e0))
* remove build action and changed trigger for Jest testing ([146580e](https://github.com/ubiquibot/conversation-rewards/commit/146580efc68b6d8ccaf56ba3873bc2dead03bd68))
* skip on issue close not completed status ([2c15e7c](https://github.com/ubiquibot/conversation-rewards/commit/2c15e7c44ea878221cce0afba4b93ffa3f4da067))
* **test:** resolve promises ([9d57104](https://github.com/ubiquibot/conversation-rewards/commit/9d571040cc8219c23a506ff8809273b991058f49))
* **test:** resolve promises ([1d62274](https://github.com/ubiquibot/conversation-rewards/commit/1d62274efb1cafea37356cf7d59069a4413bc436))
* types ([c4ad907](https://github.com/ubiquibot/conversation-rewards/commit/c4ad90732a3ba25098866ecb09103d8b780f05c8))
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ plugin: ubiquibot/conversation-rewards
with:
evmNetworkId: 100
evmPrivateEncrypted: "encrypted-key"
erc20RewardToken: "0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d"
incentives:
enabled: true
requirePriceLabel: true
Expand Down Expand Up @@ -115,7 +116,7 @@ with:
formattingMultiplier: 0.25
wordValue: 0.1
permitGeneration:
enabled: false
enabled: true
githubComment:
enabled: true
post: true
Expand Down
3 changes: 2 additions & 1 deletion jest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ const cfg: Config = {
coveragePathIgnorePatterns: ["node_modules", "mocks"],
collectCoverage: true,
coverageReporters: ["json", "lcov", "text", "clover", "json-summary"],
reporters: ["default", "jest-junit"],
reporters: ["default", "jest-junit", "jest-md-dashboard"],
coverageDirectory: "coverage",
testTimeout: 10000,
};

export default cfg;
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,11 @@
"@octokit/webhooks": "13.2.7",
"@sinclair/typebox": "0.32.23",
"@supabase/supabase-js": "2.42.0",
"@ubiquibot/permit-generation": "1.2.2",
"@ubiquibot/permit-generation": "1.3.1",
"@ubiquity-dao/rpc-handler": "^1.1.0",
"decimal.js": "10.4.3",
"dotenv": "16.4.5",
"js-tiktoken": "1.0.10",
"ethers": "^6.13.0",
"jsdom": "24.0.0",
"lodash": "4.17.21",
"markdown-it": "14.1.0",
Expand Down Expand Up @@ -66,6 +67,7 @@
"husky": "8.0.3",
"jest": "29.7.0",
"jest-junit": "16.0.0",
"jest-md-dashboard": "0.8.0",
"knip": "5.7.0",
"lint-staged": "15.2.2",
"msw": "2.2.14",
Expand Down
4 changes: 4 additions & 0 deletions src/configuration/incentives.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ export const incentivesConfigurationSchema = T.Object({
* The encrypted key to use for permit generation
*/
evmPrivateEncrypted: T.String(),
/**
* Reward token for ERC20 permits, default WXDAI for gnosis chain
*/
erc20RewardToken: T.String({ default: "0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d" }),
incentives: T.Object({
/**
* Enables or disables the incentive plugin
Expand Down
26 changes: 26 additions & 0 deletions src/helpers/web3.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { RPCHandler, HandlerConstructorConfig } from "@ubiquity-dao/rpc-handler/";
import { ethers } from "ethers";

/**
* Returns ERC20 token symbol
* @param networkId Network id
* @param tokenAddress ERC20 token address
* @returns ERC20 token symbol
*/
export async function getERC20TokenSymbol(networkId: number, tokenAddress: string) {
const abi = ["function symbol() view returns (string)"];

// get fastest RPC
const config: HandlerConstructorConfig = {
networkId: networkId,
rpcTimeout: 1500,
autoStorage: false,
cacheRefreshCycles: 10,
};
const handler = new RPCHandler(config);
const provider = await handler.getFastestRpcProvider();

// fetch token symbol
const contract = new ethers.Contract(tokenAddress, abi, provider);
return await contract.symbol();
}
4 changes: 3 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import * as core from "@actions/core";
import { run } from "./run";

run()
export default run()
.then((result) => {
core?.setOutput("result", result);
return result;
})
.catch((e) => {
console.error("Failed to run comment evaluation:", e);
core?.setFailed(e.toString());
return e;
});
50 changes: 5 additions & 45 deletions src/parser/content-evaluator-module.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import Decimal from "decimal.js";
import { encodingForModel } from "js-tiktoken";
import OpenAI from "openai";
import configuration from "../configuration/config-reader";
import { OPENAI_API_KEY } from "../configuration/constants";
Expand Down Expand Up @@ -50,7 +49,7 @@ export class ContentEvaluatorModule implements Module {
async _processComment(comments: Readonly<GithubCommentScore>[], specificationBody: string) {
const commentsWithScore: GithubCommentScore[] = [...comments];
const commentsBody = commentsWithScore.map((comment) => comment.content);
const relevance = await this._sampleRelevanceScoreResults(specificationBody, commentsBody);
const relevance = await this._evaluateComments(specificationBody, commentsBody);

if (relevance.length !== commentsWithScore.length) {
console.error("Relevance / Comment length mismatch! Skipping.");
Expand All @@ -71,12 +70,12 @@ export class ContentEvaluatorModule implements Module {
return commentsWithScore;
}

async _evaluateComments(specification: string, comments: string[]) {
async _evaluateComments(specification: string, comments: string[]): Promise<Decimal[]> {
const prompt = this._generatePrompt(specification, comments);

try {
const response: OpenAI.Chat.ChatCompletion = await this._openAi.chat.completions.create({
model: this._getOptimalModel(prompt),
model: "gpt-4o",
messages: [
{
role: "system",
Expand All @@ -99,44 +98,6 @@ export class ContentEvaluatorModule implements Module {
}
}

_getOptimalModel(prompt: string) {
const encoder = encodingForModel("gpt-3.5-turbo");
const totalSumOfTokens = encoder.encode(prompt).length;

if (totalSumOfTokens <= 4097) {
return "gpt-3.5-turbo";
} else if (totalSumOfTokens <= 16385) {
return "gpt-3.5-turbo-16k";
} else {
console.warn("Backup plan for development purposes only, but using gpt-4 due to huge context size");
return "gpt-4-turbo-preview";
}
}

async _sampleRelevanceScoreResults(specification: string, comments: string[]) {
const BATCH_SIZE = 10;
const evaluationPromises: ReturnType<typeof this._evaluateComments>[] = [];

for (let i = 0; i < BATCH_SIZE; ++i) {
evaluationPromises.push(this._evaluateComments(specification, comments));
}

const results = await Promise.all(evaluationPromises);

// Calculate the sum of each column
const columnSums: Decimal[] = [];
for (let j = 0; j < results[0].length; j++) {
let sum = new Decimal(0);
for (let i = 0; i < results.length; i++) {
sum = sum.plus(results[i][j] || 0);
}
columnSums.push(sum);
}

// Return the average of each column
return columnSums.map((sum) => sum.dividedBy(results.length));
}

_generatePrompt(issue: string, comments: string[]) {
if (!issue?.length) {
throw new Error("Issue specification comment is missing or empty");
Expand All @@ -145,8 +106,7 @@ export class ContentEvaluatorModule implements Module {
.map((comment) => comment)
.join(
"\n"
)}\n\`\`\`\n\n\nTo what degree are each of the comments in the conversation relevant and valuable to further defining the issue specification? Please reply with ONLY an array of float numbers between 0 and 1, corresponding to each comment in the order they appear. Each float should represent the degree of relevance and added value of the comment to the issue. The total length of the array in your response should equal exactly ${
comments.length
} elements.`;
)}\n\`\`\`\n\n\nTo what degree are each of the comments in the conversation relevant and valuable to further defining the issue specification? Please reply with ONLY an array of float numbers between 0 and 1, corresponding to each comment in the order they appear. Each float should represent the degree of relevance and added value of the comment to the issue. The total length of the array in your response should equal exactly ${comments.length
} elements.`;
}
}
10 changes: 6 additions & 4 deletions src/parser/github-comment-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import { GithubCommentConfiguration, githubCommentConfigurationType } from "../c
import { getOctokitInstance } from "../get-authentication-token";
import { IssueActivity } from "../issue-activity";
import { parseGitHubUrl } from "../start";
import { getPayoutConfigByNetworkId } from "../types/payout";
import program from "./command-line";
import { GithubCommentScore, Module, Result } from "./processor";
import { getERC20TokenSymbol } from "../helpers/web3";

interface SortedTasks {
issues: { specification: GithubCommentScore | null; comments: GithubCommentScore[] };
Expand All @@ -28,7 +28,7 @@ export class GithubCommentModule implements Module {
const bodyArray: (string | undefined)[] = [];

for (const [key, value] of Object.entries(result)) {
result[key].evaluationCommentHtml = this._generateHtml(key, value);
result[key].evaluationCommentHtml = await this._generateHtml(key, value);
bodyArray.push(result[key].evaluationCommentHtml);
}
const body = bodyArray.join("");
Expand Down Expand Up @@ -167,7 +167,7 @@ export class GithubCommentModule implements Module {
return content.join("");
}

_generateHtml(username: string, result: Result[0]) {
async _generateHtml(username: string, result: Result[0]) {
const sortedTasks = result.comments?.reduce<SortedTasks>(
(acc, curr) => {
if (curr.type & CommentType.ISSUE) {
Expand All @@ -184,13 +184,15 @@ export class GithubCommentModule implements Module {
{ issues: { specification: null, comments: [] }, reviews: [] }
);

const tokenSymbol = await getERC20TokenSymbol(configuration.evmNetworkId, configuration.erc20RewardToken);

return `
<details>
<summary>
<b>
<h3>
<a href="${result.permitUrl}" target="_blank" rel="noopener">
[ ${result.total} ${getPayoutConfigByNetworkId(configuration.evmNetworkId).symbol} ]
[ ${result.total} ${tokenSymbol} ]
</a>
</h3>
<h6>
Expand Down
11 changes: 7 additions & 4 deletions src/parser/permit-generation-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
Database,
encodePermits,
generatePayoutPermit,
Permit,
PermitReward,
SupportedEvents,
TokenType,
} from "@ubiquibot/permit-generation/core";
Expand All @@ -27,6 +27,7 @@ interface Payload {
evmNetworkId: number;
issueUrl: string;
evmPrivateEncrypted: string;
erc20RewardToken: string;
issue: { id: number };
}

Expand All @@ -40,6 +41,7 @@ export class PermitGenerationModule implements Module {
issueUrl: program.eventPayload.issue.html_url,
evmPrivateEncrypted: configuration.evmPrivateEncrypted,
evmNetworkId: configuration.evmNetworkId,
erc20RewardToken: configuration.erc20RewardToken,
};
const issueId = Number(payload.issueUrl.match(/[0-9]+$/)?.[0]);
payload.issue = {
Expand Down Expand Up @@ -78,6 +80,7 @@ export class PermitGenerationModule implements Module {
username: key,
contributionType: "",
type: TokenType.ERC20,
tokenAddress: payload.erc20RewardToken,
},
],
};
Expand Down Expand Up @@ -146,7 +149,7 @@ export class PermitGenerationModule implements Module {
return locationId;
}

async _savePermitsToDatabase(userId: number, issue: { issueId: number; issueUrl: string }, permits: Permit[]) {
async _savePermitsToDatabase(userId: number, issue: { issueId: number; issueUrl: string }, permits: PermitReward[]) {
for (const permit of permits) {
try {
const { data: userData } = await this._supabase.from("users").select("id").eq("id", userId).single();
Expand All @@ -155,8 +158,8 @@ export class PermitGenerationModule implements Module {
if (userData) {
const { error } = await this._supabase.from("permits").insert({
amount: permit.amount.toString(),
nonce: permit.nonce,
deadline: permit.deadline,
nonce: permit.nonce.toString(),
deadline: permit.deadline.toString(),
signature: permit.signature,
beneficiary_id: userData.id,
location_id: locationId,
Expand Down
Loading

0 comments on commit ebc6c7b

Please sign in to comment.