fix: Correct ArXiv search type dropdown to use proper API prefixes (#10235)

* fix: correct ArXiv search type dropdown to use proper API prefixes

The ArXiv component's search type dropdown was incorrectly using "all" as a prefix,
which is not recognized by the ArXiv API. This fix maps the dropdown options to
the correct API prefixes:
- "all" → no prefix (searches all fields)
- "title" → "ti:" prefix
- "abstract" → "abs:" prefix
- "author" → "au:" prefix
- "cat" → "cat:" prefix

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

Co-Authored-By: Claude <noreply@anthropic.com>

* [autofix.ci] apply automated fixes

* [autofix.ci] apply automated fixes (attempt 2/3)

* [autofix.ci] apply automated fixes

* chore: update component index

* [autofix.ci] apply automated fixes

* chore: update component index

* Update component_index.json

* [autofix.ci] apply automated fixes

* chore: update component index

* Update component_index.json

* chore: update component index

* Add test and update starter template

* Fix failed import

* chore: update component index

* [autofix.ci] apply automated fixes

* chore: update component index

* Update test_arxiv_component.py

* [autofix.ci] apply automated fixes

* Correct the lambda filter tests

* chore: update component index

* Update test_lambda_filter.py

* [autofix.ci] apply automated fixes

* Update test_lambda_filter.py

* Update component_index.json

* chore: update component index

* Update test_lambda_filter.py

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Carlos Coelho <80289056+carlosrcoelho@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Eric Hare <ericrhare@gmail.com>
This commit is contained in:
Rodrigo Nader
2025-10-14 15:15:29 -03:00
committed by GitHub
parent f34ac05bdd
commit 034457777f
8 changed files with 89 additions and 47 deletions

File diff suppressed because one or more lines are too long

View File

@ -26,43 +26,59 @@ class TestLambdaFilterComponent(ComponentTestBaseWithoutClient):
def file_names_mapping(self):
return []
async def test_successful_lambda_generation(self, component_class, default_kwargs):
component = await self.component_setup(component_class, default_kwargs)
component.llm.ainvoke.return_value.content = "lambda x: [item for item in x['items'] if item['value'] > 15]"
# Execute filter
result = await component.filter_data()
# Assertions
assert isinstance(result, list)
assert len(result) == 1
assert result[0].name == "test2"
assert result[0].value == 20
async def test_invalid_lambda_response(self, component_class, default_kwargs):
component = await self.component_setup(component_class, default_kwargs)
component.llm.ainvoke.return_value.content = "invalid lambda syntax"
# Test exception handling
with pytest.raises(ValueError, match="Could not find lambda in response"):
await component.filter_data()
await component.process_as_data()
async def test_successful_lambda_generation(self, component_class, default_kwargs):
"""Test that a lambda function is successfully generated and applied."""
component = await self.component_setup(component_class, default_kwargs)
component.llm.ainvoke.return_value.content = "lambda x: [item for item in x['items'] if item['value'] > 15]"
# Execute the lambda filter
result = await component.process_as_data()
# Assertions - process_as_data() returns a Data object
assert isinstance(result, Data), f"Expected Data object, got {type(result)}"
assert "_results" in result.data, "Expected '_results' key in Data object"
# Check the filtered results
filtered_items = result.data["_results"]
assert isinstance(filtered_items, list), "Expected list of filtered items"
assert len(filtered_items) == 1, f"Expected 1 item, got {len(filtered_items)}"
assert filtered_items[0]["name"] == "test2", f"Expected 'test2', got {filtered_items[0]['name']}"
assert filtered_items[0]["value"] == 20, f"Expected value 20, got {filtered_items[0]['value']}"
async def test_lambda_with_large_dataset(self, component_class, default_kwargs):
"""Test lambda execution with a large dataset."""
large_data = {"items": [{"name": f"test{i}", "value": i} for i in range(2000)]}
default_kwargs["data"] = [Data(data=large_data)]
default_kwargs["filter_instruction"] = "Filter items with value greater than 1500"
component = await self.component_setup(component_class, default_kwargs)
component.llm.ainvoke.return_value.content = "lambda x: [item for item in x['items'] if item['value'] > 1500]"
# Execute filter
result = await component.filter_data()
# Execute filter on the data
result = await component.process_as_data()
# Assertions
assert isinstance(result, list)
assert len(result) == 499 # Items with value from 1501 to 1999
assert all(item.value > 1500 for item in result)
# Assertions - process_as_data() returns a Data object
assert isinstance(result, Data), f"Expected Data object, got {type(result)}"
assert "_results" in result.data, "Expected '_results' key in Data object"
# Check the filtered results from the lambda
filtered_items = result.data["_results"]
assert isinstance(filtered_items, list), "Expected list of filtered items"
assert len(filtered_items) == 499, f"Expected 499 items (1501-1999), got {len(filtered_items)}"
# Verify first and last items
assert filtered_items[0]["value"] == 1501, f"Expected first value 1501, got {filtered_items[0]['value']}"
assert filtered_items[-1]["value"] == 1999, f"Expected last value 1999, got {filtered_items[-1]['value']}"
async def test_lambda_with_complex_data_structure(self, component_class, default_kwargs):
"""Test lambda execution with complex nested data structures."""
complex_data = {
"categories": {
"A": [{"id": 1, "score": 90}, {"id": 2, "score": 85}],
@ -77,13 +93,18 @@ class TestLambdaFilterComponent(ComponentTestBaseWithoutClient):
)
# Execute filter
result = await component.filter_data()
result = await component.process_as_data()
# Assertions
assert isinstance(result, list)
assert len(result) == 1
assert result[0].id == 3
assert result[0].score == 95
# Assertions - process_as_data() returns a Data object
assert isinstance(result, Data), f"Expected Data object, got {type(result)}"
assert "_results" in result.data, "Expected '_results' key in Data object"
# Check the filtered results
filtered_items = result.data["_results"]
assert isinstance(filtered_items, list), "Expected list of filtered items"
assert len(filtered_items) == 1, f"Expected 1 item with score > 90, got {len(filtered_items)}"
assert filtered_items[0]["id"] == 3, f"Expected id 3, got {filtered_items[0]['id']}"
assert filtered_items[0]["score"] == 95, f"Expected score 95, got {filtered_items[0]['score']}"
def test_validate_lambda(self, component_class):
component = component_class()
@ -101,6 +122,7 @@ class TestLambdaFilterComponent(ComponentTestBaseWithoutClient):
assert component._validate_lambda(invalid_lambda_2) is False
def test_get_data_structure(self, component_class):
"""Test that get_data_structure returns a mirror of the data with types."""
component = component_class()
test_data = {
"string": "test",
@ -112,12 +134,19 @@ class TestLambdaFilterComponent(ComponentTestBaseWithoutClient):
structure = component.get_data_structure(test_data)
# Assertions - each value should have a 'structure' key
assert structure["string"]["structure"] == "str", structure
assert structure["number"]["structure"] == "int", structure
assert structure["list"]["structure"] == "list(int)[size=3]", structure
assert isinstance(structure["dict"]["structure"], dict), structure
assert structure["dict"]["structure"]["key"] == "str", structure
assert isinstance(structure["nested"]["structure"], dict), structure
assert "a" in structure["nested"]["structure"], structure
assert structure["nested"]["structure"]["a"] == 'list(dict)[size=1], sample: {"b": "int"}', structure
# Verify the structure returns type names for primitive types
assert structure["string"] == "str", f"Expected 'str', got {structure['string']}"
assert structure["number"] == "int", f"Expected 'int', got {structure['number']}"
# Verify list structure
assert isinstance(structure["list"], list), "List should return a list structure"
assert structure["list"] == ["int"], f"Expected ['int'], got {structure['list']}"
# Verify dict structure
assert isinstance(structure["dict"], dict), "Dict should return a dict structure"
assert structure["dict"] == {"key": "str"}, f"Expected {{'key': 'str'}}, got {structure['dict']}"
# Verify nested structure
assert structure["nested"] == {"a": [{"b": "int"}]}, (
f"Expected nested structure {{'a': [{{'b': 'int'}}]}}, got {structure['nested']}"
)

View File

@ -70,7 +70,7 @@ class TestArXivComponent(ComponentTestBaseWithClient):
# Assert
assert "http://export.arxiv.org/api/query?" in url
assert "search_query=all%3Aquantum%20computing" in url
assert "search_query=quantum%20computing" in url # Note: no more "all" needed as prefix
assert "max_results=10" in url
def test_parse_atom_response(self, component_class, default_kwargs):

File diff suppressed because one or more lines are too long

View File

@ -45,8 +45,14 @@ class ArXivComponent(Component):
"""Build the arXiv API query URL."""
base_url = "http://export.arxiv.org/api/query?"
# Build the search query
search_query = f"{self.search_type}:{self.search_query}"
# Build the search query based on search type
if self.search_type == "all":
search_query = self.search_query # No prefix for all fields
else:
# Map dropdown values to ArXiv API prefixes
prefix_map = {"title": "ti", "abstract": "abs", "author": "au", "cat": "cat"}
prefix = prefix_map.get(self.search_type, "")
search_query = f"{prefix}:{self.search_query}"
# URL parameters
params = {

View File

@ -13,7 +13,7 @@ if TYPE_CHECKING:
from .bolna_composio import ComposioBolnaAPIComponent
from .brightdata_composio import ComposioBrightdataAPIComponent
from .calendly_composio import ComposioCalendlyAPIComponent
from .canvas_composio import ComposioCanvaasAPIComponent
from .canvas_composio import ComposioCanvasAPIComponent
from .composio_api import ComposioAPIComponent
from .contentful_composio import ComposioContentfulAPIComponent
from .digicert_composio import ComposioDigicertAPIComponent
@ -62,7 +62,7 @@ _dynamic_imports = {
"ComposioBolnaAPIComponent": "bolna_composio",
"ComposioBrightdataAPIComponent": "brightdata_composio",
"ComposioCalendlyAPIComponent": "calendly_composio",
"ComposioCanvaasAPIComponent": "canvaas_composio",
"ComposioCanvasAPIComponent": "canvas_composio",
"ComposioContentfulAPIComponent": "contentful_composio",
"ComposioDiscordAPIComponent": "discord_composio",
"ComposioDigicertAPIComponent": "digicert_composio",
@ -112,7 +112,7 @@ __all__ = [
"ComposioBolnaAPIComponent",
"ComposioBrightdataAPIComponent",
"ComposioCalendlyAPIComponent",
"ComposioCanvaasAPIComponent",
"ComposioCanvasAPIComponent",
"ComposioContentfulAPIComponent",
"ComposioDigicertAPIComponent",
"ComposioDiscordAPIComponent",

View File

@ -1,9 +1,9 @@
from lfx.base.composio.composio_base import ComposioBaseComponent
class ComposioCanvaasAPIComponent(ComposioBaseComponent):
class ComposioCanvasAPIComponent(ComposioBaseComponent):
display_name: str = "Canvas"
icon = "Canvaas"
icon = "Canvas"
documentation: str = "https://docs.composio.dev"
app_name = "canvas"

View File

@ -109,7 +109,14 @@ class LambdaFilterComponent(Component):
combined_data.append(item.data)
elif isinstance(item.data, list):
combined_data.extend(item.data)
data = combined_data if combined_data else []
# If we have a single dict, unwrap it so lambdas can access it directly
if len(combined_data) == 1 and isinstance(combined_data[0], dict):
data = combined_data[0]
elif len(combined_data) == 0:
data = {}
else:
data = combined_data # type: ignore[assignment]
elif isinstance(self.data, DataFrame):
# Single DataFrame to list of dicts
data = self.data.to_dict(orient="records")