Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(platform, blocks): Webhook-triggered blocks #8358

Merged
merged 88 commits into from
Nov 25, 2024

Conversation

Pwuts
Copy link
Member

@Pwuts Pwuts commented Oct 16, 2024

Changes 🏗️

  • feat(blocks): Add GitHub Pull Request Trigger block

feat(platform): Add support for Webhook-triggered blocks

  • ⚠️ Add PLATFORM_BASE_URL setting

  • Add webhook config option and BlockType.WEBHOOK to Block

    • Add check to Block.__init__ to enforce type and shape of webhook event filter
    • Add check to Block.__init__ to enforce payload input on webhook blocks
    • Add check to Block.__init__ to disable webhook blocks if PLATFORM_BASE_URL is not set
  • Add Webhook model + CRUD functions in backend.data.integrations to represent webhooks created by our system

    • Add IntegrationWebhook to DB schema + reference AgentGraphNode.webhook_id
      • Add set_node_webhook(..) in backend.data.graph
  • Add webhook-related endpoints:

    • POST /integrations/{provider}/webhooks/{webhook_id}/ingress endpoint, to receive webhook payloads, and for all associated nodes create graph executions
      • Add Node.is_triggered_by_event_type(..) helper method
    • POST /integrations/{provider}/webhooks/{webhook_id}/ping endpoint, to allow testing a webhook
    • Add WebhookEvent + pub/sub functions in backend.data.integrations
  • Add backend.integrations.webhooks module, including:

    • graph_lifecycle_hooks, e.g. on_graph_activate(..), to handle corresponding webhook creation etc.
      • Add calls to these hooks in the graph create/update endpoints
    • BaseWebhooksManager + GithubWebhooksManager to handle creating + registering, removing + deregistering, and retrieving existing webhooks, and validating incoming payloads

Other improvements

  • fix(blocks): Allow having an input and output pin with the same name
  • fix(blocks): Add tooltip with description in places where block inputs are rendered without NodeHandle
  • feat(blocks): Allow hiding inputs (e.g. payload) with SchemaField(hidden=True)
  • fix(frontend): Fix MultiSelector component styling
  • feat(frontend): Add AlertDialog UI component
  • feat(frontend): Add NodeMultiSelectInput component
  • feat(backend/data): Add NodeModel with graph_id, graph_version; GraphModel with user_id
    • Add make_graph_model(..) helper function in backend.data.graph
  • refactor(backend/data): Make RedisEventQueue generic and move to backend.data.execution
  • refactor(frontend): Deduplicate & clean up code for different block types in generateInputHandles(..) in CustomNode
  • dx(backend): Add MissingConfigError, NeedConfirmation exception

How it works

  • When a graph is created, the on_graph_activate and on_node_activate hooks are called on the graph and its nodes
  • If a webhook-triggered node has presets for all the relevant inputs, on_node_activate will get/create a suitable webhook and link it by setting AgentGraphNode.webhook_id
    • on_node_activate uses webhook_manager.get_suitable_webhook(..), which tries to find a suitable webhook (with matching requirements) or creates it if none exists yet
  • When a graph is deactivated (in favor of a newer/other version) or deleted, on_graph_deactivate and on_node_deactivate are called on the graph and its nodes to clean up webhooks that are no longer in use
  • When a valid webhook payload is received, two things happen:
    1. It is broadcast on the Redis channel webhooks/{webhook_id}/{event_type}
    2. Graph executions are initiated for all nodes triggered by this webhook

TODO

Follow-up

@github-actions github-actions bot added platform/backend AutoGPT Platform - Back end size/m labels Oct 16, 2024
@Pwuts Pwuts force-pushed the reinier/open-1961-implement-github-on-pull-request-block branch from 59b467e to 74c3047 Compare October 17, 2024 00:15
@Pwuts Pwuts force-pushed the reinier/open-1961-implement-github-on-pull-request-block branch from 74c3047 to 2f405c3 Compare October 17, 2024 00:44
@github-actions github-actions bot added size/xl and removed size/l labels Oct 17, 2024
@Pwuts Pwuts self-assigned this Oct 17, 2024
@github-actions github-actions bot added the platform/frontend AutoGPT Platform - Front end label Oct 19, 2024
@Pwuts Pwuts force-pushed the reinier/open-1961-implement-github-on-pull-request-block branch from 7df8e68 to f117d3f Compare October 21, 2024 10:39
@Pwuts Pwuts force-pushed the reinier/open-1961-implement-github-on-pull-request-block branch from c415bd0 to 397ae0b Compare November 21, 2024 19:09
Resolves #8357

- Add user flow to explicitly confirm deleting credentials linked to in-use webhooks
- Add logic to deregister, remove, and unlink webhooks before deleting parent credentials
- backend: Add `NeedConfirmation` exception
- frontend: Add `AlertDialog` UI component
Resolves #8738

- Disable webhook blocks if `PLATFORM_BASE_URL` is not set
- Throw `MissingConfigError` when trying to set up a node-webhook-link if `PLATFORM_BASE_URL` is not set
- Add `MissingConfigError`
- Add field validator to `Config.platform_base_url` and `Config.frontend_base_url`
@Pwuts Pwuts force-pushed the reinier/open-1961-implement-github-on-pull-request-block branch from c2dd80b to 6eb643d Compare November 22, 2024 00:13
@Pwuts Pwuts requested a review from ntindle November 22, 2024 00:20
ntindle
ntindle previously approved these changes Nov 24, 2024
```
</details>

2. **Define event filter input** in your block's Input schema.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is an event filter input? What does it do? Do I need one? what if I can't filter my webhooks

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It currently is required, as it fits all the existing implementations. Making it optional wouldn't be hard, but also currently isn't necessary.

The angle to this PR is: it's the first version of the webhook system and will be iterated on further as we add support for more providers.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've amended the description. Making this input optional can be done when it becomes necessary in a follow-up PR.

- The name of the input field (`events` in this case) must match `webhook_config.event_filter_input`.
- The event filter itself must be a Pydantic model with only boolean fields.

3. **Include payload field** in your block's Input schema.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be a pydantic model?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In theory, sure. I'm not sure the existing implementation of webhook ingress endpoint + executor supports it though.

</details>

4. **Define `credentials` input** in your block's Input schema.
- Its scopes must be sufficient to manage a user's webhooks through the provider's API
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do we handle when there is no provider api?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(use this to make follow up ticket)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a question I left to answer when we actually have to deal with that

</details>

- The name of the input field (`events` in this case) must match `webhook_config.event_filter_input`.
- The event filter itself must be a Pydantic model with only boolean fields.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah this is kinda a weirdly restricting thing

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The restriction makes the rest of the implementation easier. Also, this structure allows adding individual descriptions to all the options, which I can definitely see being necessary for good UX if event names are not self-explanatory.

Comment on lines +325 to +335
- A `credentials` input with the scopes needed to manage webhooks
- Logic to turn the webhook payload into outputs for the webhook block
- The `WebhooksManager` for the corresponding webhook service provider, which handles:
- (De)registering webhooks with the provider
- Parsing and validating incoming webhook payloads
- The credentials system for the corresponding service provider, which may include an `OAuthHandler`

There is more going on under the hood, e.g. to store and retrieve webhooks and their
links to nodes, but to add a webhook-triggered block you shouldn't need to make changes
to those parts of the system.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes a heavily flawed assumption of all webhooks must be registered via an API. Provide a simple webhook manager that does not require this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#8750 is my attempt at this for context

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's fix that in your PR as a follow-up.

docs/content/platform/new_blocks.md Show resolved Hide resolved
@Pwuts Pwuts merged commit eef9bbe into dev Nov 25, 2024
17 checks passed
@Pwuts Pwuts deleted the reinier/open-1961-implement-github-on-pull-request-block branch November 25, 2024 17:42
Pwuts added a commit that referenced this pull request Nov 25, 2024
github-merge-queue bot pushed a commit that referenced this pull request Dec 5, 2024
- Resolves #8743
- Follow-up to #8358

### Demo


https://github.com/user-attachments/assets/f983dfa2-2dc2-4ab0-8373-e768ba17e6f7

### Changes 🏗️

- feat(frontend): Add webhook status indicator on `CustomNode`
   - Add `webhookId` to frontend node data model

- fix(backend): Fix webhook ping endpoint
   - Remove `provider` path parameter
   - Fix return values and error handling
   - Fix `WebhooksManager.trigger_ping(..)`
      - Add `credentials` parameter
      - Fix usage of credentials
   - Fix `.data.integrations.wait_for_webhook_event(..)`
      - Add `AsyncRedisEventBus.wait_for_event(..)`

- feat(frontend): Add `BackendAPIProvider` + `useBackendAPI`

- feat(frontend): Improve layout of node header

    Before:

![image](https://github.com/user-attachments/assets/17a33b94-65f0-4e34-a47d-2dd321edecae)
    After:

![image](https://github.com/user-attachments/assets/64afb1e4-e3f2-4ca9-8961-f1245f25477f)

- refactor(backend): Clean up `.data.integrations`
- refactor(backend): Fix naming in `.data.queue` for understandability

### Checklist 📋

#### For code changes:
- [x] I have clearly listed my changes in the PR description
- [x] I have made a test plan
- [x] I have tested my changes according to the test plan:
  <!-- Put your test plan here: -->
  - [x] Add webhook block, save -> gray indicator
  - [x] Add necessary info to webhook block, save -> green indicator
  - [x] Remove necessary info, save -> gray indicator

---------

Co-authored-by: Nicholas Tindle <nicholas.tindle@agpt.co>
github-merge-queue bot pushed a commit that referenced this pull request Dec 11, 2024
…ame` globally (#8725)

- Resolves #8931
- Follow-up to #8358

### Changes 🏗️
- Avoid double specifying provider and cred types on `credentials`
inputs
- Move `credentials` sub-schema validation from `CredentialsField` to
`CredentialsMetaInput.validate_credentials_field_schema(..)`, which is
called in `BlockSchema.__pydantic_init_subclass__`
- Use `ProviderName` enum globally
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Improvements or additions to documentation platform/backend AutoGPT Platform - Back end platform/blocks platform/frontend AutoGPT Platform - Front end Review effort [1-5]: 4 size/xl
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

6 participants