ubiquity-os / ubiquity-os-kernel

1 stars 13 forks source link

Structuring how to build plugins #25

Closed Keyrxng closed 1 month ago

Keyrxng commented 7 months ago

I've extracted the /ask command into a plugin with repo here

I've assumed based on https://github.com/ubiquibot/permit-generation that each plugin should first start with a fork of the ts-template repo.

So when in developing a plugin will there be a forkable repo either a monorepo of all developed plugins or a repo that PRs can be made against specifically for building plugins?

Maybe the repo should have a branch that has the template within a dir, the user forks from there then copies and renames the dir to the name of their plugin and then prs can be made against the default branch building up a monorepo?

This would make publishing each new plugin as a package from @ubiquibot easier if that's the intention and would create a great place for a how-to-build plugins docs and examples area

What's the idea with it at the moment?

0x4007 commented 7 months ago

I've extracted the /ask command into a https://github.com/ubq-testing/bot-ai/issues/25 with repo here

This is great to see! I was thinking maybe /research but we can make adjustments later.

My vision for the slash commands is that we will have some type of special circumstance (perhaps accommodated from within the bot kernel) that will first regex parse on any issues_comment.created event. It will recognize the command being invoked, and then it will look up from the ubiquibot-config.yml which plugin to relay the webhook to.

With this approach, partners can easily define any arbitrary commands and then configure what plugin to invoke from that command. We can recommend a "default configuration" to onboard new partners with our favorite plugins.

Originally I was thinking of hosting separate repositories under the @ubiquibot organization as "official plugins" and ping those directly (via GitHub Actions) to host and serve the plugins.

We also need to figure a means for monetization and make it as simple as possible for plugin developers. Ideally if the bot kernel (@ubiquity) can get a cut and then also give a cut to any plugins that charge money.

This should keep plugin development as simple as possible, if they are separate repositories.

0x4007 commented 7 months ago

I just saw your example in your readme and just to make it very clear, the kernel should have the ability to read from the ubiquibot-config.yml and then pass along the webhook/event to the separate plugin repository. We should not have to do any custom mounting from within the kernel, for example.

0x4007 commented 7 months ago

@whilefoo rfc

Keyrxng commented 7 months ago

Okay understood so an example config might look like

plugins:
  donate
  createComment
  logger:
    backendUrl: https...

The kernel reads these plugins using the plugin reader module, each plugin needs a plugin-config.yml stating it's requirements such as regex or additional reqs, the reader extracts the regex from the plugin's config gives the green light and then calls the plugin function, once complete the reader sends the results to pipeOutputTo? Would be better if the kernel had access to the regex's without looking into the belonging repo but I can't think of how unless the partner reads from the plugin-installation.md where it states what to add into the ubq-config.yml as part of the plugin like

 donate:
   regex: /donate

As for the piping schema, maybe something like this?

const plugin = T.Object({
  command: T.String(), // eg research
  args: T.Optional(T.Array(T.Any())), // eg always the event & other args
  pipeOutputTo: T.Optional(T.String()), // eg createComment, other or null returns data to the invoking function
  onError: T.Optional(T.String()), // eg log, comment or null
  requirements: [] // anything user provided needed to make it work, URLs, addrs, whatever
});

type Plugin = StaticDecode<typeof plugin>

So as a dev I could build something like


/*
plugin-config.yml:
   commentRegex: null  // never parses comments, is only piped to

ubq-config.yml:
   plugins:
     createComment // doesn't require anything to work, just plug and play   
*/
type CreateComment extends Plugin {
 command: "createComment",
 args: string,
 pipeOutputTo: null, // returns the output to the invoker
 onError: "logger",
 requirements: []
}

/*
 plugin-config.yml
   commentRegex: null // never parses comments, is only piped to

 ubq-config.yml
   plugins:
     logger:
       backendUrl: https... // my favourite logging provider/db/analytics/whatever
*/
type Logger extends Plugin {
  command: "logger",
  args: {...args},
  pipeOutputTo: null, // returns data to invoker
  onError: "createComment", 
  requirements: [backendUrl]
}

/*
plugin-config.yml:
   commentRegex: "/donate" // only works via comment parsing

ubq-config.yml:
   plugins:
     donate // doesn't require anything to work, just plug and play   
*/
type Donate extends Plugin {
 command: "donate",
 args: {username: string, amount: string, token: string},
 pipeOutputTo: "createComment", // passes all data to createComment and none to invoker
 onError: "logger",
 requirements: []
}

So Donate would be an invoked slash command while logger and createComment is only piped-to. Donate might be invoked by partners to bump a hunter's payout without adjusting price labels, as such it would invoke the permit generation module.

  1. Partner comments "/donate keyrxng 10 DAI"
  2. Parse plugins from ubq-config.yml, gather and apply regex's
  3. Call plugin(s)
  4. Donate calls permitGeneration with pipeOutputTo as null returning the permit string to the Donate plugin.
  5. Donate would then call it's pipeOutputTo passing in the permit and any comment styling
  6. Plugin reader would exit (never receives data from the plugin call)
  7. This way the plugin-reader doesn't have to handle any data or function calling beyond that in step 3

Trying to wrap my head around the vision as I intend to catch that juicy bounty for piping schema and logic but it's relevant here too for sure

0x4007 commented 7 months ago

I spent some time designing a config. I took the existing org config and coerced them into a plugins oriented config:

View configuration ```yaml plugins: "*": - name: "Wildcard" description: "Runs after every handler." # command: "^\/wildcard$" # example: "/wildcard" uses: - ubiquibot/wildcard@1.0.0 with: reviewDelayTolerance: 30 days taskStaleTimeoutDuration: 30 days taskFollowUpDuration: 3.5 days taskDisqualifyDuration: 7 days issues_label.added: - name: "Help Menu" description: "This will parse the config and display the help menu for slash commands." command: "^\/help$" example: "/help" uses: - ubiquibot/command-display-help-menu@66e5590 - name: "Assistive Pricing" description: "Set pricing based on Time and Priority labels." # command: "^\/price$" # example: "/price" uses: - ubiquibot/assistive-pricing@9d1e5b0 with: publicSetLabel: true labels: time: - "Time: <1 Hour" - "Time: <2 Hours" - "Time: <4 Hours" - "Time: <1 Day" - "Time: <1 Week" priority: - "Priority: 1 (Normal)" - "Priority: 2 (Medium)" - "Priority: 3 (High)" - "Priority: 4 (Urgent)" - "Priority: 5 (Emergency)" issues.closed: - name: "Payout" description: "Payout for closed as completed issues." # command: "^\/payout$" # example: "/payout" uses: - ubiquibot/conversation-incentives@d2ef733 # the magic is that this will only return a `{ username: string; reward: number }[]` - ubiquibot/permit-generation@9a16ad0 # this will consume the above output and return only the permit information - ubiquibot/conversation-incentives-comment@fb32084 # this will consume the above output and return only the conversation rewards with: incentives: comment: elements: code: 1 img: 0 h1: 1 h2: 1 h3: 1 h4: 1 h5: 1 h6: 1 li: 0.5 a: 1 blockquote: 0 i: 0 totals: word: 0.1 fundExternalClosedIssue: true promotionComment: "
If you've enjoyed your experience in the DevPool, we'd appreciate your support. Follow Ubiquity on GitHub and star this repo. Your endorsement means the world to us and helps us grow!
We are excited to announce that the DevPool and UbiquiBot are now available to partners! Our ideal collaborators are globally distributed crypto-native organizations, who actively work on open source on GitHub, and excel in research & development. If you can introduce us to the repository maintainers in these types of companies, we have a special bonus in store for you!
" maxPermitPrice: 1000 evmNetworkId: 100 basePriceMultiplier: 2 issueCreatorMultiplier: 3 evmPrivateEncrypted: A3_ALEuhyNzvhgu7oH0qCyG59WTsP7NCmOGZ651cd0FKRl8Zd1bQMsciJU0WAXhAAQaWUGUArDygtwrTDF_VF_peS6Sqgv-j1f5doUeZUdAMsp9qddd4eggFR0pMbAtFOjf2FM5Ckwz2bztzJYDnrg issues_comment.created: - name: "New contributor greeting" description: "This will automatically display the help menu for first time commentators in a repository." # command: "^\/greeting$" # example: "/greeting" uses: - ubiquibot/new-commentator-greeting@7c181d2 - name: "Wallet registration" description: "Register your wallet for payouts." command: "^\/wallet\\s+((0x[a-fA-F0-9]{40})|([a-zA-Z0-9]{4,})|([a-zA-Z0-9]{3,}\\.eth))$" example: "/wallet " uses: - ubiquibot/command-wallet@471fcd5 with: registerWalletWithVerification: false - name: "User query" description: "Check a user's registered DevPool profile information." command: "^\/query @([a-zA-Z0-9]+)$" example: "/query @username" uses: - ubiquibot/command-query - name: "start" description: "Start working on the task." command: "^\/start$" example: "/start" uses: - ubiquibot/command-start@5ab8115 with: maxConcurrentTasks: 2 ```

Remarks

Wildcard not sure how to handle this better. But the names of the others are based on my deep research/understanding of all of the official GitHub webhook events that are transmitted to GitHub Apps.

I also tried conforming to GitHub Actions syntax with name: description: with: and uses:

Interoperability

I think the most interesting part is the permit generation segment:

      uses:
        - ubiquibot/conversation-incentives@d2ef733 # the magic is that this will only return a `{ username: string; reward: number }[]`
        - ubiquibot/permit-generation@9a16ad0 # this will consume the above output and return only the permit information
        - ubiquibot/conversation-incentives-comment@fb32084 # this will consume the above output and return only the conversation rewards

There will be lag time setting up and tearing down each GitHub Actions runner. If this is an issue, we can theoretically consolidate this specific flow to a Cloudflare Action etc which just bundles all three plugins as submodules and runs them in a single runtime. I prefer the separate codebases to optimize for interoperability.

A plugin developer can send any arbitrary rewards request to the permit-generation plugin and parse its output and do something custom with it for example.


The logger is built into GitHub Actions no need to configure.

whilefoo commented 7 months ago

Why not have a separate property for commands, because any way all commands will be inside issues_comment.created. Something like


events:
  "*":
    - name: "Wildcard"
      description: "Runs after every handler."
      uses:
        - ubiquibot/wildcard@1.0.0
      with:
          reviewDelayTolerance: 30 days
          taskStaleTimeoutDuration: 30 days
          taskFollowUpDuration: 3.5 days
          taskDisqualifyDuration: 7 days
commands:
    - name: "User query"
      description: "Check a user's registered DevPool profile information."
      command: "^\/query @([a-zA-Z0-9]+)$"
      example: "/query @username"
      uses:
        - ubiquibot/command-query

    - name: "start"
      description: "Start working on the task."
      command: "^\/start$"
      example: "/start"
      uses:
        - ubiquibot/command-start@5ab8115
      with: 
        maxConcurrentTasks: 2
0x4007 commented 7 months ago

I originally started with that in my design but decided that it doesn't make sense to add custom extra logic to accomodate this. Currently we can ALMOST 1:1 map the config directly on to GitHub webhook events. The only exception is wildcard. The closest thing to running regularly is GitHub's ping event, which runs every ~15 seconds, but it does not include the authentication details, namely, the installation_id which was an issue in my earlier research using the kernel and monitoring incoming webhook events. Each organization that adds the bot has a unique installation_id.

0x4007 commented 7 months ago

Why not have a separate property for commands, because any way all commands will be inside issues_comment.created.

It does seem redundant if the user can also configure issues_comment.created

The user can configure issues_comment (for all) issues_comment.created .edited and I believe there is one more off hand, possibly .deleted. It makes more sense to me to try and stay as close as possible with the official webhook event names. I just wish we could have a better solution for "wildcard"

rndquu commented 7 months ago

A few notes on this config:

  1. Name, description, example usage are up to plugin creators so it makes sense to fetch them dynamically (from some plugin-config.yml file) from the plugin repo (or even from https://github.com/ubiquibot/conversation-rewards/blob/development/action.yml)
  2. We're basically going to allow 3rd parties to hook into the bot's execution so we'll have to somehow review the plugins to make sure there is nothing malicious (which takes time)
  3. Plugin developers may introduce similar commands (like /register or /wallet). Perhaps it makes sense to force a prefix for all plugin developers. So for ubiquity, for example, the commands would be similar to ubiquibot/kernel /wallet. For https://github.com/ubq-testing/ubiquibot-ask-plugin it would be ubq-testing/ubiquibot-ask-plugin /ask.
0x4007 commented 7 months ago

A few notes on this config:

  1. Name, description, example usage are up to plugin creators so it makes sense to fetch them dynamically (from some plugin-config.yml file) from the plugin repo (or even from https://github.com/ubiquibot/conversation-rewards/blob/development/action.yml)

Agreed

  1. We're basically going to allow 3rd parties to hook into the bot's execution so we'll have to somehow review the plugins to make sure there is nothing malicious (which takes time)

We just "approve" of the ones we fork into @ubiquibot. The rest is at the risk of projects if they want to use. We should not spend resources on reviewing every plugin.

  1. Plugin developers may introduce similar commands (like /register or /wallet). Perhaps it makes sense to force a prefix for all plugin developers. So for ubiquity, for example, the commands would be similar to ubiquibot/kernel /wallet. For

No this should be assignable in the ubiquibot-config.yml, as I have it expressed via the regex. We can consider built in defaults, but I envision that we would essentially generate "starter" configs (ubiquibot-config.yml) with specific recipes and slash commands.

Advanced users can override defaults with the regex match feature I proposed.

https://github.com/ubq-testing/ubiquibot-ask-plugin it would be ubq-testing/ubiquibot-ask-plugin /ask.

rndquu commented 6 months ago

To make it clear, this plugin config will be part of the organization wide config set by every partner, right?

0x4007 commented 6 months ago

Org and repo config needs to be basically the same schema. So the config I proposed should be usable in both contexts, the same way we have it working now.

ubiquibot-dev[bot] commented 1 month ago
# Issue was not closed as completed. Skipping.