Repository: Significant-Gravitas/AutoGPT
Pull request #9742: feat(backend, libs): Tell uvicorn to use our logger + always log to stdout+stderr
Author: login: ntindle
Labels: name: size/m
name: platform/backend
description: AutoGPT Platform - Back end
name: Review effort 2/5
Created: 2025-04-02T19:12:31Z | Merged: 2025-04-03T17:03:17Z

<!-- Clearly explain the need for these changes: -->

Uvicorn and our logs were ending up in different places, this pr enures uvicorn using our logging config, not their own.

### Changes 🏗️
- Clears uvicorn's loggers for rest, ws
- always log to stdout,stderr and additionally log to gcp is appropriate
<!-- Concisely describe all of the changes made in this pull request: -->

### 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] Test all possible variants of the log cloud vs not and ensure that uvicorn logs show up in the same place that rest of the system logs do for all



Diff:
diff --git a/autogpt_platform/autogpt_libs/autogpt_libs/logging/config.py b/autogpt_platform/autogpt_libs/autogpt_libs/logging/config.py
index dcbcc6ecf842..dda6c58547a5 100644
--- a/autogpt_platform/autogpt_libs/autogpt_libs/logging/config.py
+++ b/autogpt_platform/autogpt_libs/autogpt_libs/logging/config.py
@@ -8,7 +8,7 @@
 from pydantic_settings import BaseSettings, SettingsConfigDict
 
 from .filters import BelowLevelFilter
-from .formatters import AGPTFormatter, StructuredLoggingFormatter
+from .formatters import AGPTFormatter
 
 LOG_DIR = Path(__file__).parent.parent.parent.parent / "logs"
 LOG_FILE = "activity.log"
@@ -81,9 +81,26 @@ def configure_logging(force_cloud_logging: bool = False) -> None:
     """
 
     config = LoggingConfig()
-
     log_handlers: list[logging.Handler] = []
 
+    # Console output handlers
+    stdout = logging.StreamHandler(stream=sys.stdout)
+    stdout.setLevel(config.level)
+    stdout.addFilter(BelowLevelFilter(logging.WARNING))
+    if config.level == logging.DEBUG:
+        stdout.setFormatter(AGPTFormatter(DEBUG_LOG_FORMAT))
+    else:
+        stdout.setFormatter(AGPTFormatter(SIMPLE_LOG_FORMAT))
+
+    stderr = logging.StreamHandler()
+    stderr.setLevel(logging.WARNING)
+    if config.level == logging.DEBUG:
+        stderr.setFormatter(AGPTFormatter(DEBUG_LOG_FORMAT))
+    else:
+        stderr.setFormatter(AGPTFormatter(SIMPLE_LOG_FORMAT))
+
+    log_handlers += [stdout, stderr]
+
     # Cloud logging setup
     if config.enable_cloud_logging or force_cloud_logging:
         import google.cloud.logging
@@ -97,26 +114,7 @@ def configure_logging(force_cloud_logging: bool = False) -> None:
             transport=SyncTransport,
         )
         cloud_handler.setLevel(config.level)
-        cloud_handler.setFormatter(StructuredLoggingFormatter())
         log_handlers.append(cloud_handler)
-    else:
-        # Console output handlers
-        stdout = logging.StreamHandler(stream=sys.stdout)
-        stdout.setLevel(config.level)
-        stdout.addFilter(BelowLevelFilter(logging.WARNING))
-        if config.level == logging.DEBUG:
-            stdout.setFormatter(AGPTFormatter(DEBUG_LOG_FORMAT))
-        else:
-            stdout.setFormatter(AGPTFormatter(SIMPLE_LOG_FORMAT))
-
-        stderr = logging.StreamHandler()
-        stderr.setLevel(logging.WARNING)
-        if config.level == logging.DEBUG:
-            stderr.setFormatter(AGPTFormatter(DEBUG_LOG_FORMAT))
-        else:
-            stderr.setFormatter(AGPTFormatter(SIMPLE_LOG_FORMAT))
-
-        log_handlers += [stdout, stderr]
 
     # File logging setup
     if config.enable_file_logging:
diff --git a/autogpt_platform/autogpt_libs/autogpt_libs/logging/formatters.py b/autogpt_platform/autogpt_libs/autogpt_libs/logging/formatters.py
index cf27bc4667fd..28b998f2cecf 100644
--- a/autogpt_platform/autogpt_libs/autogpt_libs/logging/formatters.py
+++ b/autogpt_platform/autogpt_libs/autogpt_libs/logging/formatters.py
@@ -1,7 +1,6 @@
 import logging
 
 from colorama import Fore, Style
-from google.cloud.logging_v2.handlers import CloudLoggingFilter, StructuredLogHandler
 
 from .utils import remove_color_codes
 
@@ -80,16 +79,3 @@ def format(self, record: logging.LogRecord) -> str:
             return remove_color_codes(super().format(record))
         else:
             return super().format(record)
-
-
-class StructuredLoggingFormatter(StructuredLogHandler, logging.Formatter):
-    def __init__(self):
-        # Set up CloudLoggingFilter to add diagnostic info to the log records
-        self.cloud_logging_filter = CloudLoggingFilter()
-
-        # Init StructuredLogHandler
-        super().__init__()
-
-    def format(self, record: logging.LogRecord) -> str:
-        self.cloud_logging_filter.filter(record)
-        return super().format(record)
diff --git a/autogpt_platform/autogpt_libs/autogpt_libs/logging/utils.py b/autogpt_platform/autogpt_libs/autogpt_libs/logging/utils.py
index 5c5c09221c32..4d7481da515d 100644
--- a/autogpt_platform/autogpt_libs/autogpt_libs/logging/utils.py
+++ b/autogpt_platform/autogpt_libs/autogpt_libs/logging/utils.py
@@ -1,7 +1,7 @@
 import logging
 import re
 from typing import Any
-
+import uvicorn.config
 from colorama import Fore
 
 
@@ -25,3 +25,14 @@ def print_attribute(
             "color": value_color,
         },
     )
+
+
+def generate_uvicorn_config():
+    """
+    Generates a uvicorn logging config that silences uvicorn's default logging and tells it to use the native logging module.
+    """
+    log_config = dict(uvicorn.config.LOGGING_CONFIG)
+    log_config["loggers"]["uvicorn"] = {"handlers": []}
+    log_config["loggers"]["uvicorn.error"] = {"handlers": []}
+    log_config["loggers"]["uvicorn.access"] = {"handlers": []}
+    return log_config
diff --git a/autogpt_platform/backend/backend/server/rest_api.py b/autogpt_platform/backend/backend/server/rest_api.py
index b1c1dcfbf565..dafe5d987180 100644
--- a/autogpt_platform/backend/backend/server/rest_api.py
+++ b/autogpt_platform/backend/backend/server/rest_api.py
@@ -3,6 +3,7 @@
 from typing import Any, Optional
 
 import autogpt_libs.auth.models
+from autogpt_libs.logging.utils import generate_uvicorn_config
 import fastapi
 import fastapi.responses
 import starlette.middleware.cors
@@ -141,6 +142,7 @@ def run(self):
             server_app,
             host=backend.util.settings.Config().agent_api_host,
             port=backend.util.settings.Config().agent_api_port,
+            log_config=generate_uvicorn_config(),
         )
 
     @staticmethod
diff --git a/autogpt_platform/backend/backend/server/ws_api.py b/autogpt_platform/backend/backend/server/ws_api.py
index 56d16ab1ef85..d98632cb0512 100644
--- a/autogpt_platform/backend/backend/server/ws_api.py
+++ b/autogpt_platform/backend/backend/server/ws_api.py
@@ -3,6 +3,7 @@
 from contextlib import asynccontextmanager
 from typing import Protocol
 
+from autogpt_libs.logging.utils import generate_uvicorn_config
 import uvicorn
 from autogpt_libs.auth import parse_jwt_token
 from autogpt_libs.utils.cache import thread_cached
@@ -286,8 +287,10 @@ def run(self):
             allow_methods=["*"],
             allow_headers=["*"],
         )
+
         uvicorn.run(
             server_app,
             host=Config().websocket_server_host,
             port=Config().websocket_server_port,
+            log_config=generate_uvicorn_config(),
         )


Reviews:
user: login: majdyz
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-03T06:15:47Z
user: login: Pwuts
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-03T10:15:14Z
user: login: Pwuts
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-03T10:15:40Z
user: login: Pwuts
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-03T10:18:01Z
user: login: Pwuts
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-03T10:18:26Z
user: login: ntindle
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-03T15:17:26Z
user: login: ntindle
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-03T15:21:44Z
user: login: ntindle
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-03T15:31:21Z
user: login: ntindle
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-03T15:32:22Z
user: login: ntindle
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-03T15:33:42Z
user: login: ntindle
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-03T15:48:00Z
user: login: majdyz
state: DISMISSED
author_association: CONTRIBUTOR
submitted_at: 2025-04-03T15:52:41Z
user: login: Pwuts
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-03T16:09:47Z
user: login: Pwuts
state: DISMISSED
author_association: MEMBER
submitted_at: 2025-04-03T16:09:59Z
user: login: Pwuts
state: APPROVED
author_association: MEMBER
submitted_at: 2025-04-03T16:35:16Z

Review comments:
diff_hunk: @@ -137,10 +138,15 @@ def run(self):
             allow_methods=["*"],  # Allows all methods
             allow_headers=["*"],  # Allows all headers
         )
+        log_config = dict(uvicorn.config.LOGGING_CONFIG)
path: autogpt_platform/backend/backend/server/rest_api.py
user: login: majdyz
body: Can't we move this replicated logic to configure_logging or other shared helper?
This kind of hinting that we also need this on all services/processes. we have notification service, scheduler, database manager, execution manager, but I guess this might have been somehow covered by AppService?
created_at: 2025-04-03T06:15:46Z
author_association: CONTRIBUTOR
diff_hunk: @@ -97,26 +114,7 @@ def configure_logging(force_cloud_logging: bool = False) -> None:
             transport=SyncTransport,
         )
         cloud_handler.setLevel(config.level)
-        cloud_handler.setFormatter(StructuredLoggingFormatter())
path: autogpt_platform/autogpt_libs/autogpt_libs/logging/config.py
user: login: Pwuts
body: ????????
created_at: 2025-04-03T10:15:14Z
author_association: MEMBER
path: autogpt_platform/autogpt_libs/autogpt_libs/logging/formatters.py
user: login: Pwuts
body: ???????
created_at: 2025-04-03T10:15:39Z
author_association: MEMBER
diff_hunk: @@ -137,10 +138,15 @@ def run(self):
             allow_methods=["*"],  # Allows all methods
             allow_headers=["*"],  # Allows all headers
         )
+        log_config = dict(uvicorn.config.LOGGING_CONFIG)
+        log_config["loggers"]["uvicorn"] = {"handlers": []}
+        log_config["loggers"]["uvicorn.error"] = {"handlers": []}
+        log_config["loggers"]["uvicorn.access"] = {"handlers": []}
         uvicorn.run(
             server_app,
             host=backend.util.settings.Config().agent_api_host,
             port=backend.util.settings.Config().agent_api_port,
+            log_config=log_config,
path: autogpt_platform/backend/backend/server/rest_api.py
user: login: Pwuts
body: IIRC you can achieve all of this with just `log_config=None`. I did that a while ago and it worked; I'm not sure when it was unfixed / where that fixed code went.
```suggestion
        uvicorn.run(
            server_app,
            host=backend.util.settings.Config().agent_api_host,
            port=backend.util.settings.Config().agent_api_port,
            log_config=None,
```
created_at: 2025-04-03T10:18:01Z
author_association: MEMBER
diff_hunk: @@ -3,7 +3,9 @@
 from contextlib import asynccontextmanager
 from typing import Protocol
 
+from backend.util.logging import configure_logging
path: autogpt_platform/backend/backend/server/ws_api.py
user: login: Pwuts
body: unused import?
created_at: 2025-04-03T10:18:26Z
author_association: MEMBER
diff_hunk: @@ -137,10 +138,15 @@ def run(self):
             allow_methods=["*"],  # Allows all methods
             allow_headers=["*"],  # Allows all headers
         )
+        log_config = dict(uvicorn.config.LOGGING_CONFIG)
path: autogpt_platform/backend/backend/server/rest_api.py
user: login: ntindle
body: uvicorn uses its own logging config by default, the rest of ours don't suggest one and inherit the parent logger
created_at: 2025-04-03T15:17:26Z
author_association: MEMBER
diff_hunk: @@ -137,10 +138,15 @@ def run(self):
             allow_methods=["*"],  # Allows all methods
             allow_headers=["*"],  # Allows all headers
         )
+        log_config = dict(uvicorn.config.LOGGING_CONFIG)
+        log_config["loggers"]["uvicorn"] = {"handlers": []}
+        log_config["loggers"]["uvicorn.error"] = {"handlers": []}
+        log_config["loggers"]["uvicorn.access"] = {"handlers": []}
         uvicorn.run(
             server_app,
             host=backend.util.settings.Config().agent_api_host,
             port=backend.util.settings.Config().agent_api_port,
+            log_config=log_config,
path: autogpt_platform/backend/backend/server/rest_api.py
user: login: ntindle
body: it was removed in this pr 
https://github.com/Significant-Gravitas/AutoGPT/pull/8519/files
created_at: 2025-04-03T15:21:44Z
author_association: MEMBER
diff_hunk: @@ -137,10 +138,15 @@ def run(self):
             allow_methods=["*"],  # Allows all methods
             allow_headers=["*"],  # Allows all headers
         )
+        log_config = dict(uvicorn.config.LOGGING_CONFIG)
+        log_config["loggers"]["uvicorn"] = {"handlers": []}
+        log_config["loggers"]["uvicorn.error"] = {"handlers": []}
+        log_config["loggers"]["uvicorn.access"] = {"handlers": []}
         uvicorn.run(
             server_app,
             host=backend.util.settings.Config().agent_api_host,
             port=backend.util.settings.Config().agent_api_port,
+            log_config=log_config,
path: autogpt_platform/backend/backend/server/rest_api.py
user: login: ntindle
body: those aren't the same behavior 

Setting to None removes the logging config and depends on yours entirely

I want uvicorn's configs but my handlers for its sinks. Below is the default, I'm setting each of the sinks to use our handlers

```py
LOGGING_CONFIG: dict[str, Any] = {
    "version": 1,
    "disable_existing_loggers": False,
    "formatters": {
        "default": {
            "()": "uvicorn.logging.DefaultFormatter",
            "fmt": "%(levelprefix)s %(message)s",
            "use_colors": None,
        },
        "access": {
            "()": "uvicorn.logging.AccessFormatter",
            "fmt": '%(levelprefix)s %(client_addr)s - "%(request_line)s" %(status_code)s',  # noqa: E501
        },
    },
    "handlers": {
        "default": {
            "formatter": "default",
            "class": "logging.StreamHandler",
            "stream": "ext://sys.stderr",
        },
        "access": {
            "formatter": "access",
            "class": "logging.StreamHandler",
            "stream": "ext://sys.stdout",
        },
    },
    "loggers": {
        "uvicorn": {"handlers": ["default"], "level": "INFO", "propagate": False},
        "uvicorn.error": {"level": "INFO"},
        "uvicorn.access": {"handlers": ["access"], "level": "INFO", "propagate": False},
    },
}
```
created_at: 2025-04-03T15:31:20Z
author_association: MEMBER
diff_hunk: @@ -97,26 +114,7 @@ def configure_logging(force_cloud_logging: bool = False) -> None:
             transport=SyncTransport,
         )
         cloud_handler.setLevel(config.level)
-        cloud_handler.setFormatter(StructuredLoggingFormatter())
path: autogpt_platform/autogpt_libs/autogpt_libs/logging/config.py
user: login: ntindle
body: this was double wrapping messages. GCP logger has this included and our formatter was breaking the messages so they were unreadable in gcp
created_at: 2025-04-03T15:32:22Z
author_association: MEMBER
path: autogpt_platform/autogpt_libs/autogpt_libs/logging/formatters.py
user: login: ntindle
body: See the structured log handler import here? 

what we are doing is rebuilding internal structures in that existing handler

```py

class CloudLoggingHandler(logging.StreamHandler):
    """Handler that directly makes Cloud Logging API calls.

    This is a Python standard ``logging`` handler using that can be used to
    route Python standard logging messages directly to the Stackdriver
    Logging API.

    This handler is used when not in GAE or GKE environment.

    This handler supports both an asynchronous and synchronous transport.

    Example:

    .. code-block:: python

        import logging
        import google.cloud.logging
        from google.cloud.logging_v2.handlers import CloudLoggingHandler

        client = google.cloud.logging.Client()
        handler = CloudLoggingHandler(client)

        cloud_logger = logging.getLogger('cloudLogger')
        cloud_logger.setLevel(logging.INFO)
        cloud_logger.addHandler(handler)

        cloud_logger.error('bad news')  # API call
    """

    def __init__(
        self,
        client,
        *,
        name: str = DEFAULT_LOGGER_NAME,
        transport: Type[Transport] = BackgroundThreadTransport,
        resource: Resource = None,
        labels: Optional[dict] = None,
        stream: Optional[IO] = None,
        **kwargs,
    ):
        """
        Args:
            client (~logging_v2.client.Client):
                The authenticated Google Cloud Logging client for this
                handler to use.
            name (str): the name of the custom log in Cloud Logging.
                Defaults to 'python'. The name of the Python logger will be represented
                in the ``python_logger`` field.
            transport (~logging_v2.transports.Transport):
                Class for creating new transport objects. It should
                extend from the base :class:`.Transport` type and
                implement :meth`.Transport.send`. Defaults to
                :class:`.BackgroundThreadTransport`. The other
                option is :class:`.SyncTransport`.
            resource (~logging_v2.resource.Resource):
                Resource for this Handler. If not given, will be inferred from the environment.
            labels (Optional[dict]): Additional labels to attach to logs.
            stream (Optional[IO]): Stream to be used by the handler.
        """
        super(CloudLoggingHandler, self).__init__(stream)
        if not resource:
            # infer the correct monitored resource from the local environment
            resource = detect_resource(client.project)
        self.name = name
        self.client = client
        self.transport = transport(client, name, resource=resource)
        self.project_id = client.project
        self.resource = resource
        self.labels = labels
        # add extra keys to log record
        log_filter = CloudLoggingFilter(project=self.project_id, default_labels=labels)
        self.addFilter(log_filter)
```
created_at: 2025-04-03T15:33:42Z
author_association: MEMBER
diff_hunk: @@ -137,10 +138,15 @@ def run(self):
             allow_methods=["*"],  # Allows all methods
             allow_headers=["*"],  # Allows all headers
         )
+        log_config = dict(uvicorn.config.LOGGING_CONFIG)
path: autogpt_platform/backend/backend/server/rest_api.py
user: login: ntindle
body: Replicated logic moved
created_at: 2025-04-03T15:48:00Z
author_association: MEMBER
diff_hunk: @@ -137,10 +139,12 @@ def run(self):
             allow_methods=["*"],  # Allows all methods
             allow_headers=["*"],  # Allows all headers
         )
+        log_config = generate_uvicorn_config()
         uvicorn.run(
             server_app,
             host=backend.util.settings.Config().agent_api_host,
             port=backend.util.settings.Config().agent_api_port,
+            log_config=log_config,
path: autogpt_platform/backend/backend/server/rest_api.py
user: login: majdyz
body: nit: why the need of local variable, we can assign it directly to the log_config param


created_at: 2025-04-03T15:52:33Z
author_association: CONTRIBUTOR
diff_hunk: @@ -137,10 +138,15 @@ def run(self):
             allow_methods=["*"],  # Allows all methods
             allow_headers=["*"],  # Allows all headers
         )
+        log_config = dict(uvicorn.config.LOGGING_CONFIG)
+        log_config["loggers"]["uvicorn"] = {"handlers": []}
+        log_config["loggers"]["uvicorn.error"] = {"handlers": []}
+        log_config["loggers"]["uvicorn.access"] = {"handlers": []}
         uvicorn.run(
             server_app,
             host=backend.util.settings.Config().agent_api_host,
             port=backend.util.settings.Config().agent_api_port,
+            log_config=log_config,
path: autogpt_platform/backend/backend/server/rest_api.py
user: login: Pwuts
body: Don't we also want uvicorn to use the same logging format as the rest of the app?
created_at: 2025-04-03T16:09:47Z
author_association: MEMBER

PR comments:
user: login: qodo-code-review[bot]
created_at: 2025-04-02T19:12:47Z
body: ## PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

<table>
<tr><td>⏱️&nbsp;<strong>Estimated effort to review</strong>: 2 🔵🔵⚪⚪⚪</td></tr>
<tr><td>🧪&nbsp;<strong>No relevant tests</strong></td></tr>
<tr><td>🔒&nbsp;<strong>No security concerns identified</strong></td></tr>
<tr><td>⚡&nbsp;<strong>Recommended focus areas for review</strong><br><br>

<details><summary><a href='https://github.com/Significant-Gravitas/AutoGPT/pull/9742/files#diff-66a977c6f84d7471551bab9a71ab415b52ad8b8a825cf528d8bb283d5f976f4dR18-R21'><strong>Log Format Prefix</strong></a>

The log format strings now include a "horse" prefix at the beginning of both SIMPLE_LOG_FORMAT and DEBUG_LOG_FORMAT. This appears to be unintentional and could affect log parsing and readability.
</summary>

```python
SIMPLE_LOG_FORMAT = "horse %(asctime)s %(levelname)s  %(title)s%(message)s"

DEBUG_LOG_FORMAT = (
    "horse %(asctime)s %(levelname)s %(filename)s:%(lineno)d  %(title)s%(message)s"
```

</details>

<details><summary><a href='https://github.com/Significant-Gravitas/AutoGPT/pull/9742/files#diff-6a9eb24910dc70ac04809055174ffd093ce7041e313e02d2589df2f911423a4aR6-R6'><strong>Unused Import</strong></a>

The configure_logging import is added but never used in the file. Consider removing it if not needed.
</summary>

```python
from backend.util.logging import configure_logging
```

</details>

</td></tr>
</table>

author_association: NONE
user: login: netlify[bot]
created_at: 2025-04-02T19:12:56Z
body: ### <span aria-hidden="true">✅</span> Deploy Preview for *auto-gpt-docs-dev* canceled.


|  Name | Link |
|:-:|------------------------|
|<span aria-hidden="true">🔨</span> Latest commit | cd275952708b85adde4da873ecfabbd0a965c519 |
|<span aria-hidden="true">🔍</span> Latest deploy log | https://app.netlify.com/sites/auto-gpt-docs-dev/deploys/67eeb87f9d502d0008a0284a |
author_association: NONE
user: login: netlify[bot]
created_at: 2025-04-02T19:13:14Z
body: ### <span aria-hidden="true">✅</span> Deploy Preview for *auto-gpt-docs* canceled.


|  Name | Link |
|:-:|------------------------|
|<span aria-hidden="true">🔨</span> Latest commit | cd275952708b85adde4da873ecfabbd0a965c519 |
|<span aria-hidden="true">🔍</span> Latest deploy log | https://app.netlify.com/sites/auto-gpt-docs/deploys/67eeb87f73287e0008d5b99f |
author_association: NONE
user: login: deepsource-io[bot]
created_at: 2025-04-02T19:13:53Z
body: <div><p>Here's the code health analysis summary for commits <code>4a82edb..cd27595</code>. <a href="https://app.deepsource.com/gh/Significant-Gravitas/AutoGPT/run/1906a1d0-2839-4533-84d5-6303f1de69a1/">View details on DeepSource</a>&nbsp;↗.</p><h3>Analysis Summary</h3><table><thead><tr><th>Analyzer</th><th>Status</th><th>Summary</th><th>Link</th></tr></thead><tbody><tr><td><img src="https://static.deepsource.com/analyzer_logos/javascript.svg" alt="DeepSource JavaScript Logo" width="16px" height="16px"/><strong>JavaScript</strong></td><td><span>✅&nbsp;</span><span>Success</span></td><td></td><td><a href="https://app.deepsource.com/gh/Significant-Gravitas/AutoGPT/run/1906a1d0-2839-4533-84d5-6303f1de69a1/javascript/">View Check</a>&nbsp;↗</td></tr><tr><td><img src="https://static.deepsource.com/analyzer_logos/python.svg" alt="DeepSource Python Logo" width="16px" height="16px"/><strong>Python</strong></td><td><span>✅&nbsp;</span><span>Success</span></td><td><div>❗ 15 occurences introduced</div><div>🎯 12 occurences resolved</div></td><td><a href="https://app.deepsource.com/gh/Significant-Gravitas/AutoGPT/run/1906a1d0-2839-4533-84d5-6303f1de69a1/python/">View Check</a>&nbsp;↗</td></tr></tbody></table><hr/><blockquote><div>💡 If you’re a repository administrator, you can configure the quality gates from the <a href="https://app.deepsource.com/gh/Significant-Gravitas/AutoGPT/settings/reporting">settings</a>.</div></blockquote></div>

author_association: NONE