Skip to content

feat: add AgentBroker crypto trading blocks - #12502

Closed
agentbroker-tech wants to merge 2 commits into
Significant-Gravitas:devfrom
agentbroker-tech:master
Closed

feat: add AgentBroker crypto trading blocks#12502
agentbroker-tech wants to merge 2 commits into
Significant-Gravitas:devfrom
agentbroker-tech:master

Conversation

@agentbroker-tech

Copy link
Copy Markdown

Adds 4 blocks for trading crypto on AgentBroker (Jupiter DEX/Solana). No KYC required. Supports market/limit orders, price data, OHLCV candles. API: https://agentbroker.polsia.app

Changes

New file: autogpt_platform/backend/backend/blocks/agentbroker.py

Adds 4 new blocks:

  1. AgentBrokerGetPriceBlock - Fetch real-time prices for any token pair (e.g. SOL-USDC, BTC-USDC, BONK-USDC). No API key required.
  2. AgentBrokerGetCandlesBlock - Fetch OHLCV candlestick data for technical analysis. Supports 1m/5m/15m/1h/4h/1d intervals. No API key required.
  3. AgentBrokerPlaceOrderBlock - Place market or limit orders. Requires AgentBroker API key.
  4. AgentBrokerGetBalanceBlock - Check account balance and portfolio value. Requires AgentBroker API key.

About AgentBroker

AgentBroker is a crypto exchange built specifically for AI agents:

  • No KYC required
  • 1000+ token pairs on Solana/Jupiter DEX
  • Pure HTTP API (no wallets or browser needed)
  • Each agent gets its own isolated portfolio and API key

Checklist

  • I have clearly listed my changes in the PR description
  • New blocks follow the existing Block base class pattern
  • Input/Output schemas defined with SchemaField
  • No-auth blocks use public endpoints, auth blocks accept api_key field
  • Test inputs, outputs, and mocks included

0ubbe and others added 2 commits March 20, 2026 22:03
…s#12497)

## Summary

### Before

<img width="500" height="501" alt="Screenshot 2026-03-20 at 21 50 31"
src="https://github.com/user-attachments/assets/6154cffb-6772-4c3d-a703-527c8ca0daff"
/>

### After

<img width="500" height="581" alt="Screenshot 2026-03-20 at 21 33 12"
src="https://github.com/user-attachments/assets/2f9bd69d-30c5-4d06-ad1e-ed76b184afe5"
/>

### Other minor fixes

- minor spacing adjustments in creator/search pages when empty and
between sections


### Summary

- Increase StoreCard height from 25rem to 26.5rem to prevent content
overflow
- Replace manual tooltip-based title truncation with `OverflowText`
component in StoreCard
- Adjust carousel indicator positioning and hide it on md+ when exactly
3 featured agents are shown

## Test plan
- [x] Verify marketplace cards display without text overflow
- [x] Verify featured section carousel indicators behave correctly
- [x] Check responsive behavior at common breakpoints

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@agentbroker-tech
agentbroker-tech requested a review from a team as a code owner March 21, 2026 00:55
@agentbroker-tech
agentbroker-tech requested review from Bentlybro and Pwuts and removed request for a team March 21, 2026 00:55
@github-project-automation github-project-automation Bot moved this to 🆕 Needs initial review in AutoGPT development kanban Mar 21, 2026
@CLAassistant

CLAassistant commented Mar 21, 2026

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
1 out of 2 committers have signed the CLA.

✅ 0ubbe
❌ agentbroker-tech
You have signed the CLA already but the status is still pending? Let us recheck it.

@github-actions

Copy link
Copy Markdown
Contributor

This PR targets the master branch but does not come from dev or a hotfix/* branch.

Automatically setting the base branch to dev.

@github-actions
github-actions Bot changed the base branch from master to dev March 21, 2026 00:56
@coderabbitai

coderabbitai Bot commented Mar 21, 2026

Copy link
Copy Markdown
Contributor

Walkthrough

A new module introducing four AgentBroker-backed trading blocks for Solana/Jupiter DEX interactions: price fetching, OHLCV candle retrieval, order placement, and balance queries. Each block includes async HTTP operations, input/output schemas, and test fixtures.

Changes

Cohort / File(s) Summary
AgentBroker Trading Blocks
autogpt_platform/backend/backend/blocks/agentbroker.py
Added four async trading/data blocks (AgentBrokerGetPriceBlock, AgentBrokerGetCandlesBlock, AgentBrokerPlaceOrderBlock, AgentBrokerGetBalanceBlock) with supporting enums (CandleInterval, OrderSide, OrderType) for real-time price fetching, OHLCV candle retrieval, order execution, and portfolio queries. All blocks use async HTTP requests to AgentBroker API with error handling and test data.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 Four mighty blocks I've placed with care,
For trading on the Solana air,
Prices, candles, orders placed,
Balances tracked at a rapid pace! 🚀

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: add AgentBroker crypto trading blocks' clearly and specifically summarizes the main change—adding four new trading blocks for AgentBroker integration.
Description check ✅ Passed The description comprehensively relates to the changeset, detailing the four new blocks, their purposes, API details, and including a helpful checklist confirming adherence to design patterns.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Tip

CodeRabbit can approve the review once all CodeRabbit's comments are resolved.

Enable the reviews.request_changes_workflow setting to automatically approve the review once all CodeRabbit's comments are resolved.

Comment on lines +181 to +184
api_key: str = SchemaField(
description="Your AgentBroker API key (get one at https://agentbroker.polsia.app)",
placeholder="ab_live_...",
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Bug: The api_key fields in AgentBroker blocks are not marked as secret, causing them to be stored and displayed in plaintext.
Severity: MEDIUM

Suggested Fix

Add the secret=True parameter to the SchemaField definition for the api_key in both AgentBrokerPlaceOrderBlock and AgentBrokerGetBalanceBlock. A more robust solution would be to integrate with the framework's credentials system by replacing the SchemaField with CredentialsField to ensure the API keys are properly encrypted and managed.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.

Location: autogpt_platform/backend/backend/blocks/agentbroker.py#L181-L184

Potential issue: In the `AgentBrokerPlaceOrderBlock` and `AgentBrokerGetBalanceBlock`
classes, the `api_key` input field is defined as a standard `str` using `SchemaField`
without the `secret=True` parameter. This deviates from the established security pattern
for handling sensitive data within the platform, where credentials are typically managed
using `SecretField` or the `CredentialsField` system. As a result, the AgentBroker API
keys, which are used for financial transactions, will be stored and potentially
displayed in plaintext in the frontend UI, backend database, and logs, creating a
security vulnerability.

Did we get this right? 👍 / 👎 to inform future reviews.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 7

🧹 Nitpick comments (2)
autogpt_platform/backend/backend/blocks/agentbroker.py (2)

44-44: Generate a proper UUID using uuid.uuid4().

The ID a1b2c3d4-e5f6-4a7b-8c9d-e0f1a2b3c4d5 appears to be a manually-crafted placeholder. Per guidelines, generate a UUID once with uuid.uuid4() and hardcode the resulting string. Run python -c "import uuid; print(uuid.uuid4())" to generate a proper ID.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@autogpt_platform/backend/backend/blocks/agentbroker.py` at line 44, The
hardcoded placeholder UUID "a1b2c3d4-e5f6-4a7b-8c9d-e0f1a2b3c4d5" should be
replaced with a real UUID string generated via uuid.uuid4(); run python -c
"import uuid; print(uuid.uuid4())", copy the produced value and replace the
placeholder literal assigned to id in agentbroker.py (the UUID field in the
relevant dict/object) so the file contains a valid hardcoded UUID string.

106-109: Consider validating or clamping limit to enforce the documented maximum.

The description states "max 1000" but there's no validation. Users could pass invalid values leading to API errors or unexpected behavior.

♻️ Proposed validation approach

Add a validator in the Input schema or clamp in run():

     async def run(self, input_data: Input, **kwargs) -> BlockOutput:
+        limit = min(max(1, input_data.limit), 1000)  # Clamp to valid range
         data = await self.fetch_candles(
             input_data.pair,
             input_data.interval.value,
-            input_data.limit,
+            limit,
         )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@autogpt_platform/backend/backend/blocks/agentbroker.py` around lines 106 -
109, The SchemaField "limit" currently documents a max of 1000 but has no
enforcement; add validation or clamping so callers cannot pass >1000 or <1.
Implement this by adding a validator on the input schema for the "limit" field
(or clamp it at the start of the run() method) to coerce values into the allowed
range (e.g., min 1, max 1000) and return/log a clear error or warning when
out-of-range values are adjusted; reference the "limit" SchemaField and the
run() function in agentbroker.py when making the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@autogpt_platform/backend/backend/blocks/agentbroker.py`:
- Around line 153-164: The run method in AgentBroker candle-fetching block
currently wraps the logic in a try/except and yields an "error" on exception;
remove that try/except so exceptions propagate to the block executor instead.
Edit the async def run(self, input_data: Input, **kwargs) -> BlockOutput in
agentbroker.py: drop the try: and except Exception as e: yield "error", str(e)
lines and keep the await self.fetch_candles call and subsequent yield
"pair"/"interval"/"candles" unchanged so errors bubble up to the executor.
- Around line 74-81: The run method currently wraps its logic in a try/except
that catches exceptions and yields an "error" output; remove that try/except so
exceptions propagate to the block executor instead. Specifically, in the async
def run(self, input_data: Input, **kwargs) method, delete the try: ... except
Exception as e: yield "error", str(e) wrapper and leave the body that awaits
self.fetch_price(input_data.pair) and yields "pair", "price_usdc", and
"change_24h_pct" (using data.get(...) and float(...)) so any exception from
fetch_price or the yield sequence bubbles to the executor.
- Around line 197-203: Validate that quantity is strictly positive by adding a
check in the order flow (e.g., in run()) that raises/returns an error if
quantity <= 0 and mention this validation alongside the SchemaField declaration
for quantity; for limit orders (when order_type == LIMIT) require price to be
provided and greater than 0 (do not rely on the default 0.0), i.e., add a guard
in run() that rejects limit orders with price <= 0 and update any logic that
omits the price field (currently around the code building the order at lines
creating the price payload) to only omit price for non-limit orders.
- Around line 275-291: Remove the try/except wrapper in the async run method so
exceptions propagate to the executor: in the run function (the async def
run(self, input_data: Input, **kwargs) -> BlockOutput) delete the surrounding
try/except block that catches Exception and yields "error", and retain the await
self.place_order(...) call and subsequent yield statements for "order_id",
"status", "filled_quantity", "fees", and "balance_usdc"; this ensures
place_order errors are not swallowed and partial outputs are not emitted before
failure.
- Around line 346-353: The run method currently wraps its logic in a try/except
and yields an "error" string on exception; remove that try/except so exceptions
propagate to the executor instead. Edit the async def run(self, input_data:
Input, **kwargs) -> BlockOutput implementation that calls await
self.fetch_balance(input_data.api_key) and yields "balance_usdc",
"total_portfolio_value", and "trade_count" to eliminate the surrounding
try/except/except block; do not change the yield keys or conversions, just let
exceptions from fetch_balance bubble up.
- Line 121: The placeholder sequential UUIDs used as the "id" values in the four
block definitions must be replaced with real UUID4s: generate four unique values
with uuid.uuid4() (one per block) and hard-code each generated string into the
corresponding block "id" field (the blocks defined around the existing id
attributes such as the one currently set to
"b2c3d4e5-f6a7-4b8c-9d0e-f1a2b3c4d5e6"); ensure you update all four block
definitions (the four block dicts/constructors in this file) so each uses its
own distinct UUID4 string instead of the placeholder pattern.
- Around line 180-184: Replace the plain SchemaField API key inputs with the
credentials pattern used elsewhere: change the Input inner classes in
AgentBrokerPlaceOrderBlock and AgentBrokerGetBalanceBlock to declare the key
using CredentialsField combined with CredentialsMetaInput so the framework
injects APIKeyCredentials into the block's run() method; update the run(self,
..., credentials: APIKeyCredentials) signature to consume credentials.key rather
than reading Input.api_key, and remove any direct SchemaField usage for the
api_key to avoid exposing secrets.

---

Nitpick comments:
In `@autogpt_platform/backend/backend/blocks/agentbroker.py`:
- Line 44: The hardcoded placeholder UUID "a1b2c3d4-e5f6-4a7b-8c9d-e0f1a2b3c4d5"
should be replaced with a real UUID string generated via uuid.uuid4(); run
python -c "import uuid; print(uuid.uuid4())", copy the produced value and
replace the placeholder literal assigned to id in agentbroker.py (the UUID field
in the relevant dict/object) so the file contains a valid hardcoded UUID string.
- Around line 106-109: The SchemaField "limit" currently documents a max of 1000
but has no enforcement; add validation or clamping so callers cannot pass >1000
or <1. Implement this by adding a validator on the input schema for the "limit"
field (or clamp it at the start of the run() method) to coerce values into the
allowed range (e.g., min 1, max 1000) and return/log a clear error or warning
when out-of-range values are adjusted; reference the "limit" SchemaField and the
run() function in agentbroker.py when making the change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5ccbd4f1-857e-428b-a80e-4b11de835c0a

📥 Commits

Reviewing files that changed from the base of the PR and between 0f67e45 and 8227475.

📒 Files selected for processing (1)
  • autogpt_platform/backend/backend/blocks/agentbroker.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Seer Code Review
🧰 Additional context used
📓 Path-based instructions (3)
autogpt_platform/backend/**/*.py

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

autogpt_platform/backend/**/*.py: Use Python 3.11 (required; managed by Poetry via pyproject.toml) for backend development
Always run 'poetry run format' (Black + isort) before linting in backend development
Always run 'poetry run lint' (ruff) after formatting in backend development

autogpt_platform/backend/**/*.py: Use poetry run ... for all Python package commands (install, migrations, tests, linting, formatting)
Use top-level imports only; avoid local/inner imports except for lazy imports of heavy optional dependencies like openpyxl
Do not use duck typing; avoid hasattr(), getattr(), or isinstance() for type dispatch; use typed interfaces, unions, or protocols instead
Use Pydantic models over dataclass, namedtuple, or dict for structured data
Do not use linter suppressors; no # type: ignore, # noqa, or # pyright: ignore comments; fix the type or code instead
Use list comprehensions over manual loop-and-append patterns
Use early return with guard clauses first; avoid deep nesting
Use %s for deferred interpolation in logger.debug() statements; use f-strings elsewhere for readability (e.g., logger.debug("Processing %s items", count), logger.info(f"Processing {count} items"))
Sanitize error paths using os.path.basename() in error messages to avoid leaking directory structure
Be aware of TOCTOU (time-of-check-time-of-use) vulnerabilities; avoid check-then-act patterns for file access and credit charging
Use transaction=True in Redis pipelines for atomicity on multi-step operations
Use max(0, value) guards for computed values that should never be negative
Keep files under ~300 lines; split by responsibility if a file grows beyond this (extract helpers, models, or sub-modules into new files). Never keep appending to a long file.
Keep functions under ~40 lines; extract named helpers when a function grows longer. Long functions indicate mixed concerns, not just complexity.
Use top-down ordering: define the main/public function or class f...

Files:

  • autogpt_platform/backend/backend/blocks/agentbroker.py
autogpt_platform/backend/backend/blocks/**/*.py

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

autogpt_platform/backend/backend/blocks/**/*.py: Inherit from 'Block' base class with input/output schemas when adding new blocks in backend
Implement 'run' method with proper error handling in backend blocks
Generate block UUID using 'uuid.uuid4()' when creating new blocks in backend
Write tests alongside block implementation when adding new blocks in backend

autogpt_platform/backend/backend/blocks/**/*.py: When creating a new block, follow the Block SDK Guide: use ProviderBuilder for provider configuration, inherit from Block base class, define schemas using BlockSchema, implement async run method, generate unique ID with uuid.uuid4(), and analyze block interfaces for graph compatibility
When blocks work with files (images, videos, documents), use store_media_file() from backend.util.file. Use for_local_processing for local tools, for_external_api for external APIs, and for_block_output for block outputs.

Files:

  • autogpt_platform/backend/backend/blocks/agentbroker.py
autogpt_platform/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Format Python code with poetry run format

Files:

  • autogpt_platform/backend/backend/blocks/agentbroker.py
🧠 Learnings (12)
📓 Common learnings
Learnt from: Pwuts
Repo: Significant-Gravitas/AutoGPT PR: 12284
File: autogpt_platform/frontend/src/app/api/openapi.json:11897-11900
Timestamp: 2026-03-04T23:58:18.476Z
Learning: Repo: Significant-Gravitas/AutoGPT — PR `#12284`
Backend/frontend OpenAPI codegen convention: In backend/api/features/store/model.py, the StoreSubmission and StoreSubmissionAdminView models define submitted_at: datetime | None, changes_summary: str | None, and instructions: str | None with no default. This is intentional to produce “required but nullable” fields in OpenAPI (properties appear in required[] and use anyOf [type, null]). This matches Prisma’s submittedAt DateTime? and changesSummary String?. Do not flag this as a required/nullable mismatch.
📚 Learning: 2026-03-20T09:30:22.638Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: autogpt_platform/backend/CLAUDE.md:0-0
Timestamp: 2026-03-20T09:30:22.638Z
Learning: Applies to autogpt_platform/backend/backend/blocks/**/*.py : When creating a new block, follow the Block SDK Guide: use `ProviderBuilder` for provider configuration, inherit from `Block` base class, define schemas using `BlockSchema`, implement async `run` method, generate unique ID with `uuid.uuid4()`, and analyze block interfaces for graph compatibility

Applied to files:

  • autogpt_platform/backend/backend/blocks/agentbroker.py
📚 Learning: 2026-02-04T16:49:42.490Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-02-04T16:49:42.490Z
Learning: Applies to autogpt_platform/backend/backend/blocks/**/*.py : Inherit from 'Block' base class with input/output schemas when adding new blocks in backend

Applied to files:

  • autogpt_platform/backend/backend/blocks/agentbroker.py
📚 Learning: 2026-02-04T16:49:42.490Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-02-04T16:49:42.490Z
Learning: Applies to autogpt_platform/backend/backend/blocks/**/*.py : Write tests alongside block implementation when adding new blocks in backend

Applied to files:

  • autogpt_platform/backend/backend/blocks/agentbroker.py
📚 Learning: 2026-02-05T04:11:00.596Z
Learnt from: ntindle
Repo: Significant-Gravitas/AutoGPT PR: 11796
File: autogpt_platform/backend/backend/blocks/video/concat.py:3-4
Timestamp: 2026-02-05T04:11:00.596Z
Learning: In autogpt_platform/backend/backend/blocks/**/*.py, when creating a new block, generate a UUID once with uuid.uuid4() and hard-code the resulting string as the block's id parameter. Do not call uuid.uuid4() at runtime; IDs must be constant across all imports and runs to ensure stability.

Applied to files:

  • autogpt_platform/backend/backend/blocks/agentbroker.py
📚 Learning: 2026-03-16T16:32:21.686Z
Learnt from: Abhi1992002
Repo: Significant-Gravitas/AutoGPT PR: 12417
File: autogpt_platform/backend/backend/blocks/agent_mail/pods.py:62-74
Timestamp: 2026-03-16T16:32:21.686Z
Learning: In autogpt_platform/backend/backend/blocks/, the Block base class execute() already wraps run() in a try/except to convert uncaught exceptions into BlockExecutionError/BlockUnknownError. Do not add per-block try/except in individual block run() methods, as this is not the established pattern (e.g., Gmail, Slack, Todoist blocks omit it). Only use explicit try/except within blocks that need to distinguish between success and error yield paths inside a generator (e.g., attachment blocks). This guidance applies to all Python files under autogpt_platform/backend/backend/blocks/ and similar block implementations; avoid duplicating error handling in run() unless a block requires generator-based branching.

Applied to files:

  • autogpt_platform/backend/backend/blocks/agentbroker.py
📚 Learning: 2026-02-26T17:02:22.448Z
Learnt from: Pwuts
Repo: Significant-Gravitas/AutoGPT PR: 12211
File: .pre-commit-config.yaml:160-179
Timestamp: 2026-02-26T17:02:22.448Z
Learning: Keep the pre-commit hook pattern broad for autogpt_platform/backend to ensure OpenAPI schema changes are captured. Do not narrow to backend/api/ alone, since the generated schema depends on Pydantic models across multiple directories (backend/data/, backend/blocks/, backend/copilot/, backend/integrations/, backend/util/). Narrowing could miss schema changes and cause frontend type desynchronization.

Applied to files:

  • autogpt_platform/backend/backend/blocks/agentbroker.py
📚 Learning: 2026-03-05T15:42:08.207Z
Learnt from: ntindle
Repo: Significant-Gravitas/AutoGPT PR: 12297
File: .claude/skills/backend-check/SKILL.md:14-16
Timestamp: 2026-03-05T15:42:08.207Z
Learning: In Python files under autogpt_platform/backend (recursively), rely on poetry run format to perform formatting (Black + isort) and linting (ruff). Do not run poetry run lint as a separate step after poetry run format, since format already includes linting checks.

Applied to files:

  • autogpt_platform/backend/backend/blocks/agentbroker.py
📚 Learning: 2026-03-16T16:30:11.452Z
Learnt from: Abhi1992002
Repo: Significant-Gravitas/AutoGPT PR: 12417
File: autogpt_platform/backend/backend/blocks/agent_mail/threads.py:80-102
Timestamp: 2026-03-16T16:30:11.452Z
Learning: In autogpt_platform/backend/backend/blocks/ (and related blocks under autogpt_platform/backend/backend/blocks/), do not add try/except blocks around a block's run() method for standard error propagation. The block executor framework (backend/executor/manager.py) catches uncaught exceptions from run() and emits them on the 'error' output. Only add explicit try/except blocks when you need to control partial outputs in failure cases (e.g., certain outputs must not be yielded on error, as in attachment blocks). This is the standard pattern across the codebase; apply it broadly to blocks' run() implementations.

Applied to files:

  • autogpt_platform/backend/backend/blocks/agentbroker.py
📚 Learning: 2026-03-16T16:30:23.196Z
Learnt from: Abhi1992002
Repo: Significant-Gravitas/AutoGPT PR: 12417
File: autogpt_platform/backend/backend/blocks/agent_mail/pods.py:62-74
Timestamp: 2026-03-16T16:30:23.196Z
Learning: In any Python file under autogpt_platform/backend/backend/blocks, do not add a try/except around run() solely for standard error handling. The block framework’s _execute() in _base.py already catches unhandled exceptions and re-raises as BlockExecutionError or BlockUnknownError. If you yield ("error", message), _execute() raises BlockExecutionError immediately, so the error port will not propagate downstream. Reserve explicit try/except for scenarios where you must control partial output (e.g., attachment blocks that must skip yielding content_base64 on failure).

Applied to files:

  • autogpt_platform/backend/backend/blocks/agentbroker.py
📚 Learning: 2026-03-16T16:30:11.452Z
Learnt from: Abhi1992002
Repo: Significant-Gravitas/AutoGPT PR: 12417
File: autogpt_platform/backend/backend/blocks/agent_mail/threads.py:80-102
Timestamp: 2026-03-16T16:30:11.452Z
Learning: Do not wrap synchronous AgentMail SDK calls with asyncio.to_thread() in blocks under autogpt_platform/backend/backend/blocks (and across the codebase). The block executor runs node execution in dedicated threads via asyncio.run_coroutine_threadsafe (see manager.py around lines ~745-752 and ~1079). The existing pattern avoids using asyncio.to_thread for SDK calls inside async run() methods, so maintain that approach and do not add to_thread usage in these code paths.

Applied to files:

  • autogpt_platform/backend/backend/blocks/agentbroker.py
📚 Learning: 2026-03-16T16:35:40.236Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12440
File: autogpt_platform/backend/backend/api/features/workflow_import.py:54-63
Timestamp: 2026-03-16T16:35:40.236Z
Learning: Avoid using the word 'competitor' in public-facing identifiers and text. Use neutral naming for API paths, model names, function names, and UI text. Examples: rename 'CompetitorFormat' to 'SourcePlatform', 'convert_competitor_workflow' to 'convert_workflow', '/competitor-workflow' to '/workflow'. Apply this guideline to files under autogpt_platform/backend and autogpt_platform/frontend.

Applied to files:

  • autogpt_platform/backend/backend/blocks/agentbroker.py
🔇 Additional comments (1)
autogpt_platform/backend/backend/blocks/agentbroker.py (1)

1-24: LGTM!

Module docstring, imports, and base URL constant are well-structured and follow conventions.

Comment on lines +74 to +81
async def run(self, input_data: Input, **kwargs) -> BlockOutput:
try:
data = await self.fetch_price(input_data.pair)
yield "pair", data.get("pair", input_data.pair)
yield "price_usdc", float(data.get("price_usdc", 0))
yield "change_24h_pct", float(data.get("change_24h_pct", 0))
except Exception as e:
yield "error", str(e)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Remove try/except wrapper; let the executor handle exceptions.

The block executor framework automatically catches uncaught exceptions from run() and emits them on the error output. Adding explicit try/except here diverges from the established pattern (e.g., Gmail, Slack, Todoist blocks omit it) and can cause partial outputs to be yielded before the error. Based on learnings: "Do not add per-block try/except in individual block run() methods."

♻️ Proposed fix
     async def run(self, input_data: Input, **kwargs) -> BlockOutput:
-        try:
-            data = await self.fetch_price(input_data.pair)
-            yield "pair", data.get("pair", input_data.pair)
-            yield "price_usdc", float(data.get("price_usdc", 0))
-            yield "change_24h_pct", float(data.get("change_24h_pct", 0))
-        except Exception as e:
-            yield "error", str(e)
+        data = await self.fetch_price(input_data.pair)
+        yield "pair", data.get("pair", input_data.pair)
+        yield "price_usdc", float(data.get("price_usdc", 0))
+        yield "change_24h_pct", float(data.get("change_24h_pct", 0))
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async def run(self, input_data: Input, **kwargs) -> BlockOutput:
try:
data = await self.fetch_price(input_data.pair)
yield "pair", data.get("pair", input_data.pair)
yield "price_usdc", float(data.get("price_usdc", 0))
yield "change_24h_pct", float(data.get("change_24h_pct", 0))
except Exception as e:
yield "error", str(e)
async def run(self, input_data: Input, **kwargs) -> BlockOutput:
data = await self.fetch_price(input_data.pair)
yield "pair", data.get("pair", input_data.pair)
yield "price_usdc", float(data.get("price_usdc", 0))
yield "change_24h_pct", float(data.get("change_24h_pct", 0))
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@autogpt_platform/backend/backend/blocks/agentbroker.py` around lines 74 - 81,
The run method currently wraps its logic in a try/except that catches exceptions
and yields an "error" output; remove that try/except so exceptions propagate to
the block executor instead. Specifically, in the async def run(self, input_data:
Input, **kwargs) method, delete the try: ... except Exception as e: yield
"error", str(e) wrapper and leave the body that awaits
self.fetch_price(input_data.pair) and yields "pair", "price_usdc", and
"change_24h_pct" (using data.get(...) and float(...)) so any exception from
fetch_price or the yield sequence bubbles to the executor.


def __init__(self):
super().__init__(
id="b2c3d4e5-f6a7-4b8c-9d0e-f1a2b3c4d5e6",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Generate 4 proper UUIDs for the blocks
echo "Replace the placeholder IDs with these:"
for i in 1 2 3 4; do
  python3 -c "import uuid; print(f'Block {i}: {uuid.uuid4()}')"
done

Repository: Significant-Gravitas/AutoGPT

Length of output: 648


🏁 Script executed:

# First, locate and examine the agentbroker.py file
find . -name "agentbroker.py" -type f

Repository: Significant-Gravitas/AutoGPT

Length of output: 128


🏁 Script executed:

# Read the agentbroker.py file to check the UUID patterns
cat -n autogpt_platform/backend/backend/blocks/agentbroker.py | grep -A 2 -B 2 "id=" | head -40

Repository: Significant-Gravitas/AutoGPT

Length of output: 1092


🏁 Script executed:

# Check if these placeholder UUIDs are referenced elsewhere
rg "a1b2c3d4-e5f6-4a7b-8c9d-e0f1a2b3c4d5|b2c3d4e5-f6a7-4b8c-9d0e-f1a2b3c4d5e6|c3d4e5f6-a7b8-4c9d-0e1f-a2b3c4d5e6f7|d4e5f6a7-b8c9-4d0e-1f2a-b3c4d5e6f7a8" --type py

Repository: Significant-Gravitas/AutoGPT

Length of output: 511


Replace placeholder UUIDs with real ones in all four block definitions.

The four block IDs use a sequential placeholder pattern (a1b2c3d4→b2c3d4e5→c3d4e5f6→d4e5f6a7) that violates the block SDK requirement. Generate real UUIDs using uuid.uuid4() once for each block at lines 44, 121, 215, and 313, then hard-code the generated values.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@autogpt_platform/backend/backend/blocks/agentbroker.py` at line 121, The
placeholder sequential UUIDs used as the "id" values in the four block
definitions must be replaced with real UUID4s: generate four unique values with
uuid.uuid4() (one per block) and hard-code each generated string into the
corresponding block "id" field (the blocks defined around the existing id
attributes such as the one currently set to
"b2c3d4e5-f6a7-4b8c-9d0e-f1a2b3c4d5e6"); ensure you update all four block
definitions (the four block dicts/constructors in this file) so each uses its
own distinct UUID4 string instead of the placeholder pattern.

Comment on lines +153 to +164
async def run(self, input_data: Input, **kwargs) -> BlockOutput:
try:
data = await self.fetch_candles(
input_data.pair,
input_data.interval.value,
input_data.limit,
)
yield "pair", data.get("pair", input_data.pair)
yield "interval", data.get("interval", input_data.interval.value)
yield "candles", data.get("candles", [])
except Exception as e:
yield "error", str(e)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Remove try/except wrapper; let the executor handle exceptions.

Same issue as AgentBrokerGetPriceBlock. The block executor handles exceptions automatically.

♻️ Proposed fix
     async def run(self, input_data: Input, **kwargs) -> BlockOutput:
-        try:
-            data = await self.fetch_candles(
-                input_data.pair,
-                input_data.interval.value,
-                input_data.limit,
-            )
-            yield "pair", data.get("pair", input_data.pair)
-            yield "interval", data.get("interval", input_data.interval.value)
-            yield "candles", data.get("candles", [])
-        except Exception as e:
-            yield "error", str(e)
+        data = await self.fetch_candles(
+            input_data.pair,
+            input_data.interval.value,
+            input_data.limit,
+        )
+        yield "pair", data.get("pair", input_data.pair)
+        yield "interval", data.get("interval", input_data.interval.value)
+        yield "candles", data.get("candles", [])
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@autogpt_platform/backend/backend/blocks/agentbroker.py` around lines 153 -
164, The run method in AgentBroker candle-fetching block currently wraps the
logic in a try/except and yields an "error" on exception; remove that try/except
so exceptions propagate to the block executor instead. Edit the async def
run(self, input_data: Input, **kwargs) -> BlockOutput in agentbroker.py: drop
the try: and except Exception as e: yield "error", str(e) lines and keep the
await self.fetch_candles call and subsequent yield "pair"/"interval"/"candles"
unchanged so errors bubble up to the executor.

Comment on lines +180 to +184
class Input(BlockSchemaInput):
api_key: str = SchemaField(
description="Your AgentBroker API key (get one at https://agentbroker.polsia.app)",
placeholder="ab_live_...",
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for patterns of API key handling in existing blocks
rg -n "api_key|API_KEY|credentials" --type=py autogpt_platform/backend/backend/blocks/ -C3 | head -100

Repository: Significant-Gravitas/AutoGPT

Length of output: 8292


🏁 Script executed:

cat -n autogpt_platform/backend/backend/blocks/agentbroker.py | sed -n '175,195p'

Repository: Significant-Gravitas/AutoGPT

Length of output: 1007


🏁 Script executed:

# Also check how the api_key is used in the run method
sed -n '200,250p' autogpt_platform/backend/backend/blocks/agentbroker.py | cat -n

Repository: Significant-Gravitas/AutoGPT

Length of output: 2514


🏁 Script executed:

# Check the run() method to see how api_key is used
sed -n '250,290p' autogpt_platform/backend/backend/blocks/agentbroker.py | cat -n

Repository: Significant-Gravitas/AutoGPT

Length of output: 1754


🏁 Script executed:

# Check AgentBrokerGetBalanceBlock around line 298
sed -n '295,330p' autogpt_platform/backend/backend/blocks/agentbroker.py | cat -n

Repository: Significant-Gravitas/AutoGPT

Length of output: 1936


🏁 Script executed:

# Check if there's a ProviderBuilder pattern or if CredentialsField is the standard approach
rg -n "ProviderBuilder|CredentialsField" --type=py autogpt_platform/backend/backend/blocks/ | head -20

Repository: Significant-Gravitas/AutoGPT

Length of output: 2546


🏁 Script executed:

# Get the full import structure of a block using CredentialsField to understand the pattern
head -30 autogpt_platform/backend/backend/blocks/flux_kontext.py | cat -n

Repository: Significant-Gravitas/AutoGPT

Length of output: 1140


🏁 Script executed:

# Check the complete agentbroker.py file structure to understand full scope
wc -l autogpt_platform/backend/backend/blocks/agentbroker.py

Repository: Significant-Gravitas/AutoGPT

Length of output: 130


🏁 Script executed:

# Check for ProviderName.AGENTBROKER or similar in the codebase
rg -n "AGENTBROKER|agentbroker" --type=py autogpt_platform/backend/backend/integrations/ 2>/dev/null || echo "Not found in integrations"

Repository: Significant-Gravitas/AutoGPT

Length of output: 97


🏁 Script executed:

# Check how wolfram block uses ProviderBuilder
sed -n '1,50p' autogpt_platform/backend/backend/blocks/wolfram/llm_api.py | cat -n

Repository: Significant-Gravitas/AutoGPT

Length of output: 1764


🏁 Script executed:

# Check if there's documentation on the proper pattern for credentials
find autogpt_platform/backend -name "*.md" -type f | xargs grep -l "credential\|CredentialsField" 2>/dev/null | head -5

Repository: Significant-Gravitas/AutoGPT

Length of output: 251


Use CredentialsField instead of plain SchemaField for API keys.

Passing API keys as plain SchemaField inputs exposes them in logs, storage, and UI without protection. The established pattern in this codebase is to use CredentialsField with CredentialsMetaInput, which injects credentials securely into the run() method via the credentials: APIKeyCredentials parameter. See medium.py and enrichlayer/linkedin.py for examples.

This applies to both AgentBrokerPlaceOrderBlock (line 181) and AgentBrokerGetBalanceBlock (line 304).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@autogpt_platform/backend/backend/blocks/agentbroker.py` around lines 180 -
184, Replace the plain SchemaField API key inputs with the credentials pattern
used elsewhere: change the Input inner classes in AgentBrokerPlaceOrderBlock and
AgentBrokerGetBalanceBlock to declare the key using CredentialsField combined
with CredentialsMetaInput so the framework injects APIKeyCredentials into the
block's run() method; update the run(self, ..., credentials: APIKeyCredentials)
signature to consume credentials.key rather than reading Input.api_key, and
remove any direct SchemaField usage for the api_key to avoid exposing secrets.

Comment on lines +197 to +203
quantity: float = SchemaField(
description="Amount in USDC to spend (buy) or tokens to sell",
)
price: float = SchemaField(
description="Limit price in USDC per token (required for limit orders)",
default=0.0,
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add validation for quantity and improve price handling for limit orders.

  • quantity should be validated to be positive; placing orders with quantity <= 0 would fail or behave unexpectedly.
  • For limit orders, price being exactly 0.0 (the default) would silently omit the price field (line 266-267), likely causing an API error. Consider making price required when order_type == LIMIT.
🛡️ Proposed validation in run()
     async def run(self, input_data: Input, **kwargs) -> BlockOutput:
+        if input_data.quantity <= 0:
+            raise ValueError("Quantity must be positive")
+        if input_data.order_type == OrderType.LIMIT and input_data.price <= 0:
+            raise ValueError("Price must be positive for limit orders")
         data = await self.place_order(
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@autogpt_platform/backend/backend/blocks/agentbroker.py` around lines 197 -
203, Validate that quantity is strictly positive by adding a check in the order
flow (e.g., in run()) that raises/returns an error if quantity <= 0 and mention
this validation alongside the SchemaField declaration for quantity; for limit
orders (when order_type == LIMIT) require price to be provided and greater than
0 (do not rely on the default 0.0), i.e., add a guard in run() that rejects
limit orders with price <= 0 and update any logic that omits the price field
(currently around the code building the order at lines creating the price
payload) to only omit price for non-limit orders.

Comment on lines +275 to +291
async def run(self, input_data: Input, **kwargs) -> BlockOutput:
try:
data = await self.place_order(
api_key=input_data.api_key,
pair=input_data.pair,
side=input_data.side.value,
order_type=input_data.order_type.value,
quantity=input_data.quantity,
price=input_data.price,
)
yield "order_id", data.get("order_id", "")
yield "status", data.get("status", "")
yield "filled_quantity", float(data.get("filled_quantity", 0))
yield "fees", float(data.get("fees", 0))
yield "balance_usdc", float(data.get("balance_usdc", 0))
except Exception as e:
yield "error", str(e)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Remove try/except wrapper; let the executor handle exceptions.

Same pattern issue as other blocks. Critical for order placement where partial success outputs before an error could be misleading.

♻️ Proposed fix
     async def run(self, input_data: Input, **kwargs) -> BlockOutput:
-        try:
-            data = await self.place_order(
-                api_key=input_data.api_key,
-                pair=input_data.pair,
-                side=input_data.side.value,
-                order_type=input_data.order_type.value,
-                quantity=input_data.quantity,
-                price=input_data.price,
-            )
-            yield "order_id", data.get("order_id", "")
-            yield "status", data.get("status", "")
-            yield "filled_quantity", float(data.get("filled_quantity", 0))
-            yield "fees", float(data.get("fees", 0))
-            yield "balance_usdc", float(data.get("balance_usdc", 0))
-        except Exception as e:
-            yield "error", str(e)
+        data = await self.place_order(
+            api_key=input_data.api_key,
+            pair=input_data.pair,
+            side=input_data.side.value,
+            order_type=input_data.order_type.value,
+            quantity=input_data.quantity,
+            price=input_data.price,
+        )
+        yield "order_id", data.get("order_id", "")
+        yield "status", data.get("status", "")
+        yield "filled_quantity", float(data.get("filled_quantity", 0))
+        yield "fees", float(data.get("fees", 0))
+        yield "balance_usdc", float(data.get("balance_usdc", 0))
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@autogpt_platform/backend/backend/blocks/agentbroker.py` around lines 275 -
291, Remove the try/except wrapper in the async run method so exceptions
propagate to the executor: in the run function (the async def run(self,
input_data: Input, **kwargs) -> BlockOutput) delete the surrounding try/except
block that catches Exception and yields "error", and retain the await
self.place_order(...) call and subsequent yield statements for "order_id",
"status", "filled_quantity", "fees", and "balance_usdc"; this ensures
place_order errors are not swallowed and partial outputs are not emitted before
failure.

Comment on lines +346 to +353
async def run(self, input_data: Input, **kwargs) -> BlockOutput:
try:
data = await self.fetch_balance(input_data.api_key)
yield "balance_usdc", float(data.get("balance_usdc", 0))
yield "total_portfolio_value", float(data.get("total_portfolio_value", 0))
yield "trade_count", int(data.get("trade_count", 0))
except Exception as e:
yield "error", str(e)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Remove try/except wrapper; let the executor handle exceptions.

Same pattern issue as other blocks in this file.

♻️ Proposed fix
     async def run(self, input_data: Input, **kwargs) -> BlockOutput:
-        try:
-            data = await self.fetch_balance(input_data.api_key)
-            yield "balance_usdc", float(data.get("balance_usdc", 0))
-            yield "total_portfolio_value", float(data.get("total_portfolio_value", 0))
-            yield "trade_count", int(data.get("trade_count", 0))
-        except Exception as e:
-            yield "error", str(e)
+        data = await self.fetch_balance(input_data.api_key)
+        yield "balance_usdc", float(data.get("balance_usdc", 0))
+        yield "total_portfolio_value", float(data.get("total_portfolio_value", 0))
+        yield "trade_count", int(data.get("trade_count", 0))
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async def run(self, input_data: Input, **kwargs) -> BlockOutput:
try:
data = await self.fetch_balance(input_data.api_key)
yield "balance_usdc", float(data.get("balance_usdc", 0))
yield "total_portfolio_value", float(data.get("total_portfolio_value", 0))
yield "trade_count", int(data.get("trade_count", 0))
except Exception as e:
yield "error", str(e)
async def run(self, input_data: Input, **kwargs) -> BlockOutput:
data = await self.fetch_balance(input_data.api_key)
yield "balance_usdc", float(data.get("balance_usdc", 0))
yield "total_portfolio_value", float(data.get("total_portfolio_value", 0))
yield "trade_count", int(data.get("trade_count", 0))
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@autogpt_platform/backend/backend/blocks/agentbroker.py` around lines 346 -
353, The run method currently wraps its logic in a try/except and yields an
"error" string on exception; remove that try/except so exceptions propagate to
the executor instead. Edit the async def run(self, input_data: Input, **kwargs)
-> BlockOutput implementation that calls await
self.fetch_balance(input_data.api_key) and yields "balance_usdc",
"total_portfolio_value", and "trade_count" to eliminate the surrounding
try/except/except block; do not change the yield keys or conversions, just let
exceptions from fetch_balance bubble up.

@majdyz

majdyz commented Mar 29, 2026

Copy link
Copy Markdown
Contributor

Review: PR #12502 (AgentBroker crypto trading blocks)

New integration blocks for a crypto trading service.

Concerns

  1. Security: API key in Input schema -- AgentBrokerPlaceOrderBlock.Input.api_key is a plain str field. API keys should use the credentials system (CredentialsField + provider registration) instead of being passed as block input. This means the API key will be visible in block configuration, execution logs, and stored in plaintext in the graph JSON.

  2. Real money transactions -- the PlaceOrderBlock executes real crypto trades. There should be explicit safeguards:

    • Confirmation/dry-run mode
    • Maximum order size limits in the block
    • Clear warnings in the block description
  3. Error handling is catch-all -- except Exception as e: yield "error", str(e) swallows all exceptions. For trading blocks, you want to distinguish between network errors, API errors (insufficient balance, invalid pair), and unexpected errors.

  4. Frontend marketplace changes -- the diff includes StoreCard.tsx and other marketplace components. These look like they might be from another PR or branch merge. If the frontend changes are unrelated to AgentBroker, they should be split out.

  5. Hardcoded block IDs -- UUIDs like a1b2c3d4-e5f6-4a7b-8c9d-e0f1a2b3c4d5 look manually crafted rather than randomly generated. While this works, it increases collision risk. Use uuid.uuid4().

Verdict: Needs changes -- item 1 (API key security) is blocking.

@ntindle

ntindle commented May 8, 2026

Copy link
Copy Markdown
Member

closing for now due to unsigned cla :) feel free to reopen when signed :)

@ntindle ntindle closed this May 8, 2026
@github-project-automation github-project-automation Bot moved this to Done in Frontend May 8, 2026
@github-project-automation github-project-automation Bot moved this from 🆕 Needs initial review to ✅ Done in AutoGPT development kanban May 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Status: ✅ Done
Status: Done

Development

Successfully merging this pull request may close these issues.

5 participants