Skip to content

Commit 2169b43

Browse files
mewaeltsegaymajdyzntindleclaude
authored
feat(backend/blocks): add ConcatenateListsBlock (#11567)
# feat(backend/blocks): add ConcatenateListsBlock ## Description This PR implements a new block `ConcatenateListsBlock` that concatenates multiple lists into a single list. This addresses the "good first issue" for implementing a list concatenation block in the platform/blocks area. The block takes a list of lists as input and combines all elements in order into a single concatenated list. This is useful for workflows that need to merge data from multiple sources or combine results from different operations. ### Changes 🏗️ - **Added `ConcatenateListsBlock` class** in `autogpt_platform/backend/backend/blocks/data_manipulation.py` - Input: `lists: List[List[Any]]` - accepts a list of lists to concatenate - Output: `concatenated_list: List[Any]` - returns a single concatenated list - Error output: `error: str` - provides clear error messages for invalid input types - Block ID: `3cf9298b-5817-4141-9d80-7c2cc5199c8e` - Category: `BlockCategory.BASIC` (consistent with other list manipulation blocks) - **Added comprehensive test suite** in `autogpt_platform/backend/test/blocks/test_concatenate_lists.py` - Tests using built-in `test_input`/`test_output` validation - Manual test cases covering edge cases (empty lists, single list, empty input) - Error handling tests for invalid input types - Category consistency verification - All tests passing - **Implementation details:** - Uses `extend()` method for efficient list concatenation - Preserves element order from all input lists - **Runtime type validation**: Explicitly checks `isinstance(lst, list)` before calling `extend()` to prevent: - Strings being iterated character-by-character (e.g., `extend("abc")` → `['a', 'b', 'c']`) - Non-iterable types causing `TypeError` (e.g., `extend(1)`) - Clear error messages indicating which index has invalid input - Handles edge cases: empty lists, empty input, single list, None values - Follows existing block patterns and conventions ### 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: - [x] Run `poetry run pytest test/blocks/test_concatenate_lists.py -v` - all tests pass - [x] Verified block can be imported and instantiated - [x] Tested with built-in test cases (4 test scenarios) - [x] Tested manual edge cases (empty lists, single list, empty input) - [x] Tested error handling for invalid input types - [x] Verified category is `BASIC` for consistency - [x] Verified no linting errors - [x] Confirmed block follows same patterns as other blocks in `data_manipulation.py` #### Code Quality: - [x] Code follows existing patterns and conventions - [x] Type hints are properly used - [x] Documentation strings are clear and descriptive - [x] Runtime type validation implemented - [x] Error handling with clear error messages - [x] No linting errors - [x] Prisma client generated successfully ### Testing **Test Results:** ``` test/blocks/test_concatenate_lists.py::test_concatenate_lists_block_builtin_tests PASSED test/blocks/test_concatenate_lists.py::test_concatenate_lists_manual PASSED ============================== 2 passed in 8.35s ============================== ``` **Test Coverage:** - Basic concatenation: `[[1, 2, 3], [4, 5, 6]]` → `[1, 2, 3, 4, 5, 6]` - Mixed types: `[["a", "b"], ["c"], ["d", "e", "f"]]` → `["a", "b", "c", "d", "e", "f"]` - Empty list handling: `[[1, 2], []]` → `[1, 2]` - Empty input: `[]` → `[]` - Single list: `[[1, 2, 3]]` → `[1, 2, 3]` - Error handling: Invalid input types (strings, non-lists) produce clear error messages - Category verification: Confirmed `BlockCategory.BASIC` for consistency ### Review Feedback Addressed - **Category Consistency**: Changed from `BlockCategory.DATA` to `BlockCategory.BASIC` to match other list manipulation blocks (`AddToListBlock`, `FindInListBlock`, etc.) - **Type Robustness**: Added explicit runtime validation with `isinstance(lst, list)` check before calling `extend()` to prevent: - Strings being iterated character-by-character - Non-iterable types causing `TypeError` - **Error Handling**: Added `error` output field with clear, descriptive error messages indicating which index has invalid input - **Test Coverage**: Added test case for error handling with invalid input types ### Related Issues - Addresses: "Implement block to concatenate lists" (good first issue, platform/blocks, hacktoberfest) ### Notes - This is a straightforward data manipulation block that doesn't require external dependencies - The block will be automatically discovered by the block loading system - No database or configuration changes required - Compatible with existing workflow system - All review feedback has been addressed and incorporated <!-- CURSOR_SUMMARY --> --- > [!NOTE] > Adds a new list utility and updates docs. > > - **New block**: `ConcatenateListsBlock` in `backend/blocks/data_manipulation.py` > - Input `lists: List[List[Any]]`; outputs `concatenated_list` or `error` > - Skips `None` entries; emits error for non-list items; preserves order > - **Docs**: Adds "Concatenate Lists" section to `docs/integrations/basic.md` and links it in `docs/integrations/README.md` > - **Contributor guide**: New `docs/CLAUDE.md` with manual doc section guidelines > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 4f56dd8. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Zamil Majdy <zamil.majdy@agpt.co> Co-authored-by: Nicholas Tindle <nicholas.tindle@agpt.co> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
1 parent fa0b702 commit 2169b43

File tree

4 files changed

+136
-0
lines changed

4 files changed

+136
-0
lines changed

autogpt_platform/backend/backend/blocks/data_manipulation.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -680,3 +680,58 @@ def __init__(self):
680680

681681
async def run(self, input_data: Input, **kwargs) -> BlockOutput:
682682
yield "is_empty", len(input_data.list) == 0
683+
684+
685+
class ConcatenateListsBlock(Block):
686+
class Input(BlockSchemaInput):
687+
lists: List[List[Any]] = SchemaField(
688+
description="A list of lists to concatenate together. All lists will be combined in order into a single list.",
689+
placeholder="e.g., [[1, 2], [3, 4], [5, 6]]",
690+
)
691+
692+
class Output(BlockSchemaOutput):
693+
concatenated_list: List[Any] = SchemaField(
694+
description="The concatenated list containing all elements from all input lists in order."
695+
)
696+
error: str = SchemaField(
697+
description="Error message if concatenation failed due to invalid input types."
698+
)
699+
700+
def __init__(self):
701+
super().__init__(
702+
id="3cf9298b-5817-4141-9d80-7c2cc5199c8e",
703+
description="Concatenates multiple lists into a single list. All elements from all input lists are combined in order.",
704+
categories={BlockCategory.BASIC},
705+
input_schema=ConcatenateListsBlock.Input,
706+
output_schema=ConcatenateListsBlock.Output,
707+
test_input=[
708+
{"lists": [[1, 2, 3], [4, 5, 6]]},
709+
{"lists": [["a", "b"], ["c"], ["d", "e", "f"]]},
710+
{"lists": [[1, 2], []]},
711+
{"lists": []},
712+
],
713+
test_output=[
714+
("concatenated_list", [1, 2, 3, 4, 5, 6]),
715+
("concatenated_list", ["a", "b", "c", "d", "e", "f"]),
716+
("concatenated_list", [1, 2]),
717+
("concatenated_list", []),
718+
],
719+
)
720+
721+
async def run(self, input_data: Input, **kwargs) -> BlockOutput:
722+
concatenated = []
723+
for idx, lst in enumerate(input_data.lists):
724+
if lst is None:
725+
# Skip None values to avoid errors
726+
continue
727+
if not isinstance(lst, list):
728+
# Type validation: each item must be a list
729+
# Strings are iterable and would cause extend() to iterate character-by-character
730+
# Non-iterable types would raise TypeError
731+
yield "error", (
732+
f"Invalid input at index {idx}: expected a list, got {type(lst).__name__}. "
733+
f"All items in 'lists' must be lists (e.g., [[1, 2], [3, 4]])."
734+
)
735+
return
736+
concatenated.extend(lst)
737+
yield "concatenated_list", concatenated

docs/CLAUDE.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Documentation Guidelines
2+
3+
## Block Documentation Manual Sections
4+
5+
When updating manual sections (`<!-- MANUAL: ... -->`) in block documentation files (e.g., `docs/integrations/basic.md`), follow these formats:
6+
7+
### How It Works Section
8+
9+
Provide a technical explanation of how the block functions:
10+
- Describe the processing logic in 1-2 paragraphs
11+
- Mention any validation, error handling, or edge cases
12+
- Use code examples with backticks when helpful (e.g., `[[1, 2], [3, 4]]` becomes `[1, 2, 3, 4]`)
13+
14+
Example:
15+
```markdown
16+
<!-- MANUAL: how_it_works -->
17+
The block iterates through each list in the input and extends a result list with all elements from each one. It processes lists in order, so `[[1, 2], [3, 4]]` becomes `[1, 2, 3, 4]`.
18+
19+
The block includes validation to ensure each item is actually a list. If a non-list value is encountered, the block outputs an error message instead of proceeding.
20+
<!-- END MANUAL -->
21+
```
22+
23+
### Use Case Section
24+
25+
Provide 3 practical use cases in this format:
26+
- **Bold Heading**: Short one-sentence description
27+
28+
Example:
29+
```markdown
30+
<!-- MANUAL: use_case -->
31+
**Paginated API Merging**: Combine results from multiple API pages into a single list for batch processing or display.
32+
33+
**Parallel Task Aggregation**: Merge outputs from parallel workflow branches that each produce a list of results.
34+
35+
**Multi-Source Data Collection**: Combine data collected from different sources (like multiple RSS feeds or API endpoints) into one unified list.
36+
<!-- END MANUAL -->
37+
```
38+
39+
### Style Guidelines
40+
41+
- Keep descriptions concise and action-oriented
42+
- Focus on practical, real-world scenarios
43+
- Use consistent terminology with other blocks
44+
- Avoid overly technical jargon unless necessary

docs/integrations/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ Below is a comprehensive list of all available blocks, categorized by their prim
3131
| [Agent Time Input](basic.md#agent-time-input) | Block for time input |
3232
| [Agent Toggle Input](basic.md#agent-toggle-input) | Block for boolean toggle input |
3333
| [Block Installation](basic.md#block-installation) | Given a code string, this block allows the verification and installation of a block code into the system |
34+
| [Concatenate Lists](basic.md#concatenate-lists) | Concatenates multiple lists into a single list |
3435
| [Dictionary Is Empty](basic.md#dictionary-is-empty) | Checks if a dictionary is empty |
3536
| [File Store](basic.md#file-store) | Stores the input file in the temporary directory |
3637
| [Find In Dictionary](basic.md#find-in-dictionary) | A block that looks up a value in a dictionary, list, or object by key or index and returns the corresponding value |

docs/integrations/basic.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -634,6 +634,42 @@ This enables extensibility by allowing custom blocks to be added without modifyi
634634

635635
---
636636

637+
## Concatenate Lists
638+
639+
### What it is
640+
Concatenates multiple lists into a single list. All elements from all input lists are combined in order.
641+
642+
### How it works
643+
<!-- MANUAL: how_it_works -->
644+
The block iterates through each list in the input and extends a result list with all elements from each one. It processes lists in order, so `[[1, 2], [3, 4]]` becomes `[1, 2, 3, 4]`.
645+
646+
The block includes validation to ensure each item is actually a list. If a non-list value (like a string or number) is encountered, the block outputs an error message instead of proceeding. None values are skipped automatically.
647+
<!-- END MANUAL -->
648+
649+
### Inputs
650+
651+
| Input | Description | Type | Required |
652+
|-------|-------------|------|----------|
653+
| lists | A list of lists to concatenate together. All lists will be combined in order into a single list. | List[List[Any]] | Yes |
654+
655+
### Outputs
656+
657+
| Output | Description | Type |
658+
|--------|-------------|------|
659+
| error | Error message if concatenation failed due to invalid input types. | str |
660+
| concatenated_list | The concatenated list containing all elements from all input lists in order. | List[Any] |
661+
662+
### Possible use case
663+
<!-- MANUAL: use_case -->
664+
**Paginated API Merging**: Combine results from multiple API pages into a single list for batch processing or display.
665+
666+
**Parallel Task Aggregation**: Merge outputs from parallel workflow branches that each produce a list of results.
667+
668+
**Multi-Source Data Collection**: Combine data collected from different sources (like multiple RSS feeds or API endpoints) into one unified list.
669+
<!-- END MANUAL -->
670+
671+
---
672+
637673
## Dictionary Is Empty
638674

639675
### What it is

0 commit comments

Comments
 (0)