From 04473cad1e73f236bc1431719adbad144b48e1c8 Mon Sep 17 00:00:00 2001 From: Nicholas Tindle Date: Tue, 8 Oct 2024 11:11:14 -0500 Subject: [PATCH] feat(docs): OAuth docs updates based on google block changes (#8243) * feat(frontend,backend): testing * feat: testing * feat(backend): it works for reading email * feat(backend): more docs on google * fix(frontend,backend): formatting * feat(backend): more logigin (i know this should be debug) * feat(backend): make real the default scopes * feat(backend): tests and linting * fix: code review prep * feat: sheets block * feat: liniting * Update route.ts * Update autogpt_platform/backend/backend/integrations/oauth/google.py Co-authored-by: Reinier van der Leer * Update autogpt_platform/backend/backend/server/routers/integrations.py Co-authored-by: Reinier van der Leer * fix: revert opener change * feat(frontend): add back opener required to work on mac edge * feat(frontend): drop typing list import from gmail * fix: code review comments * feat: code review changes * feat: code review changes * fix(backend): move from asserts to checks so they don't get optimized away in the future * fix(backend): code review changes * fix(backend): remove google specific check * fix: add typing * fix: only enable google blocks when oauth is configured for google * fix: errors are real and valid outputs always when output * fix(backend): add provider detail for debuging scope declines * Update autogpt_platform/frontend/src/components/integrations/credentials-input.tsx Co-authored-by: Reinier van der Leer * fix(frontend): enhance with comment, typeof error isn't known so this is best way to ensure the stringifyication will work * feat: code review change requests * fix: linting * fix: reduce error catching * fix: doc messages in code * fix: check the correct scopes object :smile: * fix: remove double (and not needed) try catch * fix: lint * fix: scopes * feat: handle the default scopes better * feat: better email objectification * feat: process attachements turns out an email doesn't need a body * fix: lint * Update google.py * Update autogpt_platform/backend/backend/data/block.py Co-authored-by: Reinier van der Leer * fix: quit trying and except failure * Update autogpt_platform/backend/backend/server/routers/integrations.py Co-authored-by: Reinier van der Leer * feat: don't allow expired states * fix: clarify function name and purpose * feat: code links updates * feat: additional docs on adding a block * fix: type hint missing which means the block won't work * fix: linting * fix: docs formatting * Update issues.py * fix: improve the naming * fix: formatting * Update new_blocks.md * Update new_blocks.md * feat: better docs on what the args mean * feat: more details on yield * Update new_blocks.md * fix: remove ignore from docs build --------- Co-authored-by: Reinier van der Leer Co-authored-by: Zamil Majdy --- .../backend/backend/blocks/github/issues.py | 4 + .../backend/backend/blocks/google/_auth.py | 3 +- .../backend/integrations/oauth/__init__.py | 2 + .../backend/integrations/oauth/base.py | 11 + .../backend/integrations/oauth/github.py | 4 + .../backend/integrations/oauth/google.py | 2 + .../backend/backend/util/settings.py | 2 + .../integrations/credentials-input.tsx | 2 + .../integrations/credentials-provider.tsx | 2 + .../src/lib/autogpt-server-api/types.ts | 2 + docs/content/server/new_blocks.md | 189 ++++++++++++------ docs/mkdocs.yml | 5 +- docs/netlify.toml | 1 - 13 files changed, 165 insertions(+), 64 deletions(-) diff --git a/autogpt_platform/backend/backend/blocks/github/issues.py b/autogpt_platform/backend/backend/blocks/github/issues.py index 97a469434..0ddeb547e 100644 --- a/autogpt_platform/backend/backend/blocks/github/issues.py +++ b/autogpt_platform/backend/backend/blocks/github/issues.py @@ -13,6 +13,7 @@ from ._auth import ( ) +# --8<-- [start:GithubCommentBlockExample] class GithubCommentBlock(Block): class Input(BlockSchema): credentials: GithubCredentialsInput = GithubCredentialsField("repo") @@ -104,6 +105,9 @@ class GithubCommentBlock(Block): yield "error", f"Failed to post comment: {str(e)}" +# --8<-- [end:GithubCommentBlockExample] + + class GithubMakeIssueBlock(Block): class Input(BlockSchema): credentials: GithubCredentialsInput = GithubCredentialsField("repo") diff --git a/autogpt_platform/backend/backend/blocks/google/_auth.py b/autogpt_platform/backend/backend/blocks/google/_auth.py index 207861847..742fcb36f 100644 --- a/autogpt_platform/backend/backend/blocks/google/_auth.py +++ b/autogpt_platform/backend/backend/blocks/google/_auth.py @@ -6,11 +6,12 @@ from pydantic import SecretStr from backend.data.model import CredentialsField, CredentialsMetaInput from backend.util.settings import Secrets +# --8<-- [start:GoogleOAuthIsConfigured] secrets = Secrets() GOOGLE_OAUTH_IS_CONFIGURED = bool( secrets.google_client_id and secrets.google_client_secret ) - +# --8<-- [end:GoogleOAuthIsConfigured] GoogleCredentials = OAuth2Credentials GoogleCredentialsInput = CredentialsMetaInput[Literal["google"], Literal["oauth2"]] diff --git a/autogpt_platform/backend/backend/integrations/oauth/__init__.py b/autogpt_platform/backend/backend/integrations/oauth/__init__.py index 3ce18050a..834293da9 100644 --- a/autogpt_platform/backend/backend/integrations/oauth/__init__.py +++ b/autogpt_platform/backend/backend/integrations/oauth/__init__.py @@ -3,6 +3,7 @@ from .github import GitHubOAuthHandler from .google import GoogleOAuthHandler from .notion import NotionOAuthHandler +# --8<-- [start:HANDLERS_BY_NAMEExample] HANDLERS_BY_NAME: dict[str, type[BaseOAuthHandler]] = { handler.PROVIDER_NAME: handler for handler in [ @@ -11,5 +12,6 @@ HANDLERS_BY_NAME: dict[str, type[BaseOAuthHandler]] = { NotionOAuthHandler, ] } +# --8<-- [end:HANDLERS_BY_NAMEExample] __all__ = ["HANDLERS_BY_NAME"] diff --git a/autogpt_platform/backend/backend/integrations/oauth/base.py b/autogpt_platform/backend/backend/integrations/oauth/base.py index fc278c7cf..61ea7e5b4 100644 --- a/autogpt_platform/backend/backend/integrations/oauth/base.py +++ b/autogpt_platform/backend/backend/integrations/oauth/base.py @@ -9,26 +9,37 @@ logger = logging.getLogger(__name__) class BaseOAuthHandler(ABC): + # --8<-- [start:BaseOAuthHandler1] PROVIDER_NAME: ClassVar[str] DEFAULT_SCOPES: ClassVar[list[str]] = [] + # --8<-- [end:BaseOAuthHandler1] @abstractmethod + # --8<-- [start:BaseOAuthHandler2] def __init__(self, client_id: str, client_secret: str, redirect_uri: str): ... + # --8<-- [end:BaseOAuthHandler2] + @abstractmethod + # --8<-- [start:BaseOAuthHandler3] def get_login_url(self, scopes: list[str], state: str) -> str: + # --8<-- [end:BaseOAuthHandler3] """Constructs a login URL that the user can be redirected to""" ... @abstractmethod + # --8<-- [start:BaseOAuthHandler4] def exchange_code_for_tokens( self, code: str, scopes: list[str] ) -> OAuth2Credentials: + # --8<-- [end:BaseOAuthHandler4] """Exchanges the acquired authorization code from login for a set of tokens""" ... @abstractmethod + # --8<-- [start:BaseOAuthHandler5] def _refresh_tokens(self, credentials: OAuth2Credentials) -> OAuth2Credentials: + # --8<-- [end:BaseOAuthHandler5] """Implements the token refresh mechanism""" ... diff --git a/autogpt_platform/backend/backend/integrations/oauth/github.py b/autogpt_platform/backend/backend/integrations/oauth/github.py index 9741a96be..03da15f40 100644 --- a/autogpt_platform/backend/backend/integrations/oauth/github.py +++ b/autogpt_platform/backend/backend/integrations/oauth/github.py @@ -8,6 +8,7 @@ from autogpt_libs.supabase_integration_credentials_store import OAuth2Credential from .base import BaseOAuthHandler +# --8<-- [start:GithubOAuthHandlerExample] class GitHubOAuthHandler(BaseOAuthHandler): """ Based on the documentation at: @@ -119,3 +120,6 @@ class GitHubOAuthHandler(BaseOAuthHandler): # Get the login (username) return response.json().get("login") + + +# --8<-- [end:GithubOAuthHandlerExample] diff --git a/autogpt_platform/backend/backend/integrations/oauth/google.py b/autogpt_platform/backend/backend/integrations/oauth/google.py index 81ab34a3d..642627d29 100644 --- a/autogpt_platform/backend/backend/integrations/oauth/google.py +++ b/autogpt_platform/backend/backend/integrations/oauth/google.py @@ -14,6 +14,7 @@ from .base import BaseOAuthHandler logger = logging.getLogger(__name__) +# --8<-- [start:GoogleOAuthHandlerExample] class GoogleOAuthHandler(BaseOAuthHandler): """ Based on the documentation at https://developers.google.com/identity/protocols/oauth2/web-server @@ -26,6 +27,7 @@ class GoogleOAuthHandler(BaseOAuthHandler): "https://www.googleapis.com/auth/userinfo.profile", "openid", ] + # --8<-- [end:GoogleOAuthHandlerExample] def __init__(self, client_id: str, client_secret: str, redirect_uri: str): self.client_id = client_id diff --git a/autogpt_platform/backend/backend/util/settings.py b/autogpt_platform/backend/backend/util/settings.py index 06222492b..14af86a8a 100644 --- a/autogpt_platform/backend/backend/util/settings.py +++ b/autogpt_platform/backend/backend/util/settings.py @@ -189,10 +189,12 @@ class Secrets(UpdateTrackingModel["Secrets"], BaseSettings): ) # OAuth server credentials for integrations + # --8<-- [start:OAuthServerCredentialsExample] github_client_id: str = Field(default="", description="GitHub OAuth client ID") github_client_secret: str = Field( default="", description="GitHub OAuth client secret" ) + # --8<-- [end:OAuthServerCredentialsExample] google_client_id: str = Field(default="", description="Google OAuth client ID") google_client_secret: str = Field( default="", description="Google OAuth client secret" diff --git a/autogpt_platform/frontend/src/components/integrations/credentials-input.tsx b/autogpt_platform/frontend/src/components/integrations/credentials-input.tsx index 8cb0b5240..dca42cb1b 100644 --- a/autogpt_platform/frontend/src/components/integrations/credentials-input.tsx +++ b/autogpt_platform/frontend/src/components/integrations/credentials-input.tsx @@ -44,11 +44,13 @@ import { SelectValue, } from "@/components/ui/select"; +// --8<-- [start:ProviderIconsEmbed] const providerIcons: Record> = { github: FaGithub, google: FaGoogle, notion: NotionLogoIcon, }; +// --8<-- [end:ProviderIconsEmbed] export type OAuthPopupResultMessage = { message_type: "oauth_popup_result" } & ( | { diff --git a/autogpt_platform/frontend/src/components/integrations/credentials-provider.tsx b/autogpt_platform/frontend/src/components/integrations/credentials-provider.tsx index 96a37e40c..4959c80e4 100644 --- a/autogpt_platform/frontend/src/components/integrations/credentials-provider.tsx +++ b/autogpt_platform/frontend/src/components/integrations/credentials-provider.tsx @@ -10,6 +10,7 @@ import { useState, } from "react"; +// --8<-- [start:CredentialsProviderNames] const CREDENTIALS_PROVIDER_NAMES = ["github", "google", "notion"] as const; type CredentialsProviderName = (typeof CREDENTIALS_PROVIDER_NAMES)[number]; @@ -19,6 +20,7 @@ const providerDisplayNames: Record = { google: "Google", notion: "Notion", }; +// --8<-- [end:CredentialsProviderNames] type APIKeyCredentialsCreatable = Omit< APIKeyCredentials, diff --git a/autogpt_platform/frontend/src/lib/autogpt-server-api/types.ts b/autogpt_platform/frontend/src/lib/autogpt-server-api/types.ts index 03a14f64c..3ff52839c 100644 --- a/autogpt_platform/frontend/src/lib/autogpt-server-api/types.ts +++ b/autogpt_platform/frontend/src/lib/autogpt-server-api/types.ts @@ -94,11 +94,13 @@ export type BlockIOBooleanSubSchema = BlockIOSubSchemaMeta & { export type CredentialsType = "api_key" | "oauth2"; +// --8<-- [start:BlockIOCredentialsSubSchema] export type BlockIOCredentialsSubSchema = BlockIOSubSchemaMeta & { credentials_provider: "github" | "google" | "notion"; credentials_scopes?: string[]; credentials_types: Array; }; +// --8<-- [end:BlockIOCredentialsSubSchema] export type BlockIONullSubSchema = BlockIOSubSchemaMeta & { type: "null"; diff --git a/docs/content/server/new_blocks.md b/docs/content/server/new_blocks.md index ae14f0bc3..a3085f1a7 100644 --- a/docs/content/server/new_blocks.md +++ b/docs/content/server/new_blocks.md @@ -14,72 +14,72 @@ Follow these steps to create and test a new block: 2. **Import necessary modules and create a class that inherits from `Block`**. Make sure to include all necessary imports for your block. - Every block should contain the following: + Every block should contain the following: - ```python - from backend.data.block import Block, BlockSchema, BlockOutput - ``` + ```python + from backend.data.block import Block, BlockSchema, BlockOutput + ``` - Example for the Wikipedia summary block: + Example for the Wikipedia summary block: - ```python - from backend.data.block import Block, BlockSchema, BlockOutput - from backend.utils.get_request import GetRequest - import requests + ```python + from backend.data.block import Block, BlockSchema, BlockOutput + from backend.utils.get_request import GetRequest + import requests - class WikipediaSummaryBlock(Block, GetRequest): - # Block implementation will go here - ``` + class WikipediaSummaryBlock(Block, GetRequest): + # Block implementation will go here + ``` 3. **Define the input and output schemas** using `BlockSchema`. These schemas specify the data structure that the block expects to receive (input) and produce (output). - The input schema defines the structure of the data the block will process. Each field in the schema represents a required piece of input data. - The output schema defines the structure of the data the block will return after processing. Each field in the schema represents a piece of output data. - Example: + Example: - ```python - class Input(BlockSchema): - topic: str # The topic to get the Wikipedia summary for + ```python + class Input(BlockSchema): + topic: str # The topic to get the Wikipedia summary for - class Output(BlockSchema): - summary: str # The summary of the topic from Wikipedia - error: str # Any error message if the request fails - ``` + class Output(BlockSchema): + summary: str # The summary of the topic from Wikipedia + error: str # Any error message if the request fails + ``` 4. **Implement the `__init__` method, including test data and mocks:** !!! important - Use UUID generator (e.g. https://www.uuidgenerator.net/) for every new block `id` and *do not* make up your own. Alternatively, you can run this python code to generate an uuid: `print(__import__('uuid').uuid4())` + Use UUID generator (e.g. https://www.uuidgenerator.net/) for every new block `id` and *do not* make up your own. Alternatively, you can run this python code to generate an uuid: `print(__import__('uuid').uuid4())` - ```python - def __init__(self): - super().__init__( - # Unique ID for the block, used across users for templates - # If you are an AI leave it as is or change to "generate-proper-uuid" - id="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", - input_schema=WikipediaSummaryBlock.Input, # Assign input schema - output_schema=WikipediaSummaryBlock.Output, # Assign output schema + ```python + def __init__(self): + super().__init__( + # Unique ID for the block, used across users for templates + # If you are an AI leave it as is or change to "generate-proper-uuid" + id="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + input_schema=WikipediaSummaryBlock.Input, # Assign input schema + output_schema=WikipediaSummaryBlock.Output, # Assign output schema - # Provide sample input, output and test mock for testing the block + # Provide sample input, output and test mock for testing the block - test_input={"topic": "Artificial Intelligence"}, - test_output=("summary", "summary content"), - test_mock={"get_request": lambda url, json: {"extract": "summary content"}}, - ) - ``` + test_input={"topic": "Artificial Intelligence"}, + test_output=("summary", "summary content"), + test_mock={"get_request": lambda url, json: {"extract": "summary content"}}, + ) + ``` - - `id`: A unique identifier for the block. + - `id`: A unique identifier for the block. - - `input_schema` and `output_schema`: Define the structure of the input and output data. + - `input_schema` and `output_schema`: Define the structure of the input and output data. - Let's break down the testing components: + Let's break down the testing components: - - `test_input`: This is a sample input that will be used to test the block. It should be a valid input according to your Input schema. + - `test_input`: This is a sample input that will be used to test the block. It should be a valid input according to your Input schema. - - `test_output`: This is the expected output when running the block with the `test_input`. It should match your Output schema. For non-deterministic outputs or when you only want to assert the type, you can use Python types instead of specific values. In this example, `("summary", str)` asserts that the output key is "summary" and its value is a string. + - `test_output`: This is the expected output when running the block with the `test_input`. It should match your Output schema. For non-deterministic outputs or when you only want to assert the type, you can use Python types instead of specific values. In this example, `("summary", str)` asserts that the output key is "summary" and its value is a string. - - `test_mock`: This is crucial for blocks that make network calls. It provides a mock function that replaces the actual network call during testing. + - `test_mock`: This is crucial for blocks that make network calls. It provides a mock function that replaces the actual network call during testing. In this case, we're mocking the `get_request` method to always return a dictionary with an 'extract' key, simulating a successful API response. This allows us to test the block's logic without making actual network requests, which could be slow, unreliable, or rate-limited. @@ -105,7 +105,7 @@ Follow these steps to create and test a new block: - **Try block**: Contains the main logic to fetch and process the Wikipedia summary. - **API request**: Send a GET request to the Wikipedia API. - **Error handling**: Handle various exceptions that might occur during the API request and data processing. - - **Yield**: Use `yield` to output the results. + - **Yield**: Use `yield` to output the results. Prefer to output one result object at a time. If you are calling a function that returns a list, you can yield each item in the list separately. You can also yield the whole list as well, but do both rather than yielding the list. For example: If you were writing a block that outputs emails, you'd yield each email as a separate result object, but you could also yield the whole list as an additional single result object. ### Blocks with authentication @@ -115,6 +115,7 @@ for a service that we already have OAuth2 support for. Implementing the block itself is relatively simple. On top of the instructions above, you're going to add a `credentials` parameter to the `Input` model and the `run` method: + ```python from autogpt_libs.supabase_integration_credentials_store.types import ( APIKeyCredentials, @@ -129,10 +130,11 @@ from backend.data.model import CredentialsField # API Key auth: class BlockWithAPIKeyAuth(Block): class Input(BlockSchema): - credentials = CredentialsField( + # Note that the type hint below is require or you will get a type error. + # The first argument is the provider name, the second is the credential type. + credentials: CredentialsMetaInput[Literal['github'], Literal['api_key']] = CredentialsField( provider="github", supported_credential_types={"api_key"}, - required_scopes={"repo"}, description="The GitHub integration can be used with " "any API key with sufficient permissions for the blocks it is used on.", ) @@ -151,7 +153,9 @@ class BlockWithAPIKeyAuth(Block): # OAuth: class BlockWithOAuth(Block): class Input(BlockSchema): - credentials = CredentialsField( + # Note that the type hint below is require or you will get a type error. + # The first argument is the provider name, the second is the credential type. + credentials: CredentialsMetaInput[Literal['github'], Literal['oauth2']] = CredentialsField( provider="github", supported_credential_types={"oauth2"}, required_scopes={"repo"}, @@ -172,7 +176,9 @@ class BlockWithOAuth(Block): # API Key auth + OAuth: class BlockWithAPIKeyAndOAuth(Block): class Input(BlockSchema): - credentials = CredentialsField( + # Note that the type hint below is require or you will get a type error. + # The first argument is the provider name, the second is the credential type. + credentials: CredentialsMetaInput[Literal['github'], Literal['api_key', 'oauth2']] = CredentialsField( provider="github", supported_credential_types={"api_key", "oauth2"}, required_scopes={"repo"}, @@ -191,10 +197,12 @@ class BlockWithAPIKeyAndOAuth(Block): ) -> BlockOutput: ... ``` + The credentials will be automagically injected by the executor in the back end. The `APIKeyCredentials` and `OAuth2Credentials` models are defined [here](https://github.com/Significant-Gravitas/AutoGPT/blob/master/rnd/autogpt_libs/autogpt_libs/supabase_integration_credentials_store/types.py). To use them in e.g. an API request, you can either access the token directly: + ```python # credentials: APIKeyCredentials response = requests.post( @@ -212,7 +220,9 @@ response = requests.post( }, ) ``` + or use the shortcut `credentials.bearer()`: + ```python # credentials: APIKeyCredentials | OAuth2Credentials response = requests.post( @@ -227,25 +237,84 @@ To add support for a new OAuth2-authenticated service, you'll need to add an `OA All our existing handlers and the base class can be found [here][OAuth2 handlers]. Every handler must implement the following parts of the [`BaseOAuthHandler`] interface: -- `PROVIDER_NAME` -- `__init__(client_id, client_secret, redirect_uri)` -- `get_login_url(scopes, state)` -- `exchange_code_for_tokens(code)` -- `_refresh_tokens(credentials)` + +```python title="autogpt_platform/backend/backend/integrations/oauth/base.py" +--8<-- "autogpt_platform/backend/backend/integrations/oauth/base.py:BaseOAuthHandler1" +--8<-- "autogpt_platform/backend/backend/integrations/oauth/base.py:BaseOAuthHandler2" +--8<-- "autogpt_platform/backend/backend/integrations/oauth/base.py:BaseOAuthHandler3" +--8<-- "autogpt_platform/backend/backend/integrations/oauth/base.py:BaseOAuthHandler4" +--8<-- "autogpt_platform/backend/backend/integrations/oauth/base.py:BaseOAuthHandler5" +``` As you can see, this is modeled after the standard OAuth2 flow. Aside from implementing the `OAuthHandler` itself, adding a handler into the system requires two more things: -- Adding the handler class to `HANDLERS_BY_NAME` [here](https://github.com/Significant-Gravitas/AutoGPT/blob/master/autogpt_platform/backend/backend/integrations/oauth/__init__.py) -- Adding `{provider}_client_id` and `{provider}_client_secret` to the application's `Secrets` [here](https://github.com/Significant-Gravitas/AutoGPT/blob/e3f35d79c7e9fc6ee0cabefcb73e0fad15a0ce2d/autogpt_platform/backend/backend/util/settings.py#L132) + +- Adding the handler class to `HANDLERS_BY_NAME` under [`integrations/oauth/__init__.py`](https://github.com/Significant-Gravitas/AutoGPT/blob/master/autogpt_platform/backend/backend/integrations/oauth/__init__.py) + +```python title="autogpt_platform/backend/backend/integrations/oauth/__init__.py" +--8<-- "autogpt_platform/backend/backend/integrations/oauth/__init__.py:HANDLERS_BY_NAMEExample" +``` + +- Adding `{provider}_client_id` and `{provider}_client_secret` to the application's `Secrets` under [`util/settings.py`](https://github.com/Significant-Gravitas/AutoGPT/blob/master/autogpt_platform/backend/backend/util/settings.py) + +```python title="autogpt_platform/backend/backend/util/settings.py" +--8<-- "autogpt_platform/backend/backend/util/settings.py:OAuthServerCredentialsExample" +``` [OAuth2 handlers]: https://github.com/Significant-Gravitas/AutoGPT/tree/master/autogpt_platform/backend/backend/integrations/oauth [`BaseOAuthHandler`]: https://github.com/Significant-Gravitas/AutoGPT/blob/master/autogpt_platform/backend/backend/integrations/oauth/base.py +#### Adding to the frontend + +You will need to add the provider (api or oauth) to the `CredentialsInput` component in [`frontend/src/components/integrations/credentials-input.tsx`](https://github.com/Significant-Gravitas/AutoGPT/blob/master/autogpt_platform/frontend/src/components/integrations/credentials-input.tsx). + +```ts title="frontend/src/components/integrations/credentials-input.tsx" +--8<-- "autogpt_platform/frontend/src/components/integrations/credentials-input.tsx:ProviderIconsEmbed" +``` + +You will also need to add the provider to the `CredentialsProvider` component in [`frontend/src/components/integrations/credentials-provider.tsx`](https://github.com/Significant-Gravitas/AutoGPT/blob/master/autogpt_platform/frontend/src/components/integrations/credentials-provider.tsx). + +```ts title="frontend/src/components/integrations/credentials-provider.tsx" +--8<-- "autogpt_platform/frontend/src/components/integrations/credentials-provider.tsx:CredentialsProviderNames" +``` + +Finally you will need to add the provider to the `CredentialsType` enum in [`frontend/src/lib/autogpt-server-api/types.ts`](https://github.com/Significant-Gravitas/AutoGPT/blob/master/autogpt_platform/frontend/src/lib/autogpt-server-api/types.ts). + +```ts title="frontend/src/lib/autogpt-server-api/types.ts" +--8<-- "autogpt_platform/frontend/src/lib/autogpt-server-api/types.ts:BlockIOCredentialsSubSchema" +``` + #### Example: GitHub integration + - GitHub blocks with API key + OAuth2 support: [`blocks/github`](https://github.com/Significant-Gravitas/AutoGPT/tree/master/autogpt_platform/backend/backend/blocks/github/) + +```python title="blocks/github/issues.py" +--8<-- "autogpt_platform/backend/backend/blocks/github/issues.py:GithubCommentBlockExample" +``` + - GitHub OAuth2 handler: [`integrations/oauth/github.py`](https://github.com/Significant-Gravitas/AutoGPT/blob/master/autogpt_platform/backend/backend/integrations/oauth/github.py) +```python title="blocks/github/github.py" +--8<-- "autogpt_platform/backend/backend/integrations/oauth/github.py:GithubOAuthHandlerExample" +``` + +#### Example: Google integration + +- Google OAuth2 handler: [`integrations/oauth/google.py`](https://github.com/Significant-Gravitas/AutoGPT/blob/master/autogpt_platform/backend/backend/integrations/oauth/google.py) + +```python title="integrations/oauth/google.py" +--8<-- "autogpt_platform/backend/backend/integrations/oauth/google.py:GoogleOAuthHandlerExample" +``` + +You can see that google has defined a `DEFAULT_SCOPES` variable, this is used to set the scopes that are requested no matter what the user asks for. + +```python title="blocks/google/_auth.py" +--8<-- "autogpt_platform/backend/backend/blocks/google/_auth.py:GoogleOAuthIsConfigured" +``` + +You can also see that `GOOGLE_OAUTH_IS_CONFIGURED` is used to disable the blocks that require OAuth if the oauth is not configured. This is in the `__init__` method of each block. This is because there is no api key fallback for google blocks so we need to make sure that the oauth is configured before we allow the user to use the blocks. + ## Key Points to Remember - **Unique ID**: Give your block a unique ID in the **init** method. @@ -275,7 +344,8 @@ This approach allows us to test the block's logic comprehensively without relyin 1. **Provide realistic test_input**: Ensure your test input covers typical use cases. -2. **Define appropriate test_output**: +2. **Define appropriate test_output**: + - For deterministic outputs, use specific expected values. - For non-deterministic outputs or when only the type matters, use Python types (e.g., `str`, `int`, `dict`). - You can mix specific values and types, e.g., `("key1", str), ("key2", 42)`. @@ -292,14 +362,14 @@ By following these steps, you can create new blocks that extend the functionalit ## Blocks we want to see -Below is a list of blocks that we would like to see implemented in the AutoGPT Agent Server. If you're interested in contributing, feel free to pick one of these blocks or suggest your own by editing [docs/content/server/new_blocks.md](https://github.com/Significant-Gravitas/AutoGPT/edit/master/docs/content/server/new_blocks.md) and opening a pull request. +Below is a list of blocks that we would like to see implemented in the AutoGPT Agent Server. If you're interested in contributing, feel free to pick one of these blocks or chose your own. If you would like to implement one of these blocks, open a pull request and we will start the review process. ### Consumer Services/Platforms -- Google sheets - Read/Append [Read in Progress](https://github.com/Significant-Gravitas/AutoGPT/pull/7521) -- Email - Read/Send with Gmail, Outlook, Yahoo, Proton, etc +- Google sheets - [~~Read/Append~~](https://github.com/Significant-Gravitas/AutoGPT/pull/8236) +- Email - Read/Send with [~~Gmail~~](https://github.com/Significant-Gravitas/AutoGPT/pull/8236), Outlook, Yahoo, Proton, etc - Calendar - Read/Write with Google Calendar, Outlook Calendar, etc - Home Assistant - Call Service, Get Status - Dominos - Order Pizza, Track Order @@ -346,7 +416,6 @@ If you would like to implement one of these blocks, open a pull request and we w ## Agent Templates we want to see - ### Data/Information - Summarize top news of today, of this week, this month via Apple News or other large media outlets BBC, TechCrunch, hackernews, etc @@ -358,9 +427,9 @@ If you would like to implement one of these blocks, open a pull request and we w - Get dates for specific shows across all streaming services - Suggest/Recommend/Get most watched shows in a given month, year, etc across all streaming platforms - Data analysis from xlsx data set - - Gather via Excel or Google Sheets data > Sample the data randomly (sample block takes top X, bottom X, randomly, etc) > pass that to LLM Block to generate a script for analysis of the full data > Python block to run the script> making a loop back through LLM Fix Block on error > create chart/visualization (potentially in the code block?) > show the image as output (this may require frontend changes to show) + - Gather via Excel or Google Sheets data > Sample the data randomly (sample block takes top X, bottom X, randomly, etc) > pass that to LLM Block to generate a script for analysis of the full data > Python block to run the script> making a loop back through LLM Fix Block on error > create chart/visualization (potentially in the code block?) > show the image as output (this may require frontend changes to show) - Tiktok video search and download -### Marketing +### Marketing - Portfolio site design and enhancements diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 28dc686c4..06025121a 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -136,8 +136,9 @@ markdown_extensions: - pymdownx.mark - pymdownx.smartsymbols - pymdownx.snippets: - auto_append: - - includes/abbreviations.md + base_path: ['.','../'] + check_paths: true + dedent_subsections: true - pymdownx.superfences: custom_fences: - name: mermaid diff --git a/docs/netlify.toml b/docs/netlify.toml index e542e1824..c07b733a5 100644 --- a/docs/netlify.toml +++ b/docs/netlify.toml @@ -3,4 +3,3 @@ [build] publish = "public/" command = "mkdocs build -d public" - ignore = "git diff --quiet $CACHED_COMMIT_REF $COMMIT_REF . ../CONTRIBUTING.md ../CODE_OF_CONDUCT.md"