mirror of
https://github.com/langflow-ai/langflow.git
synced 2026-07-25 10:06:43 +08:00
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:
File diff suppressed because one or more lines are too long
@ -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']}"
|
||||
)
|
||||
|
||||
@ -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
@ -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 = {
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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"
|
||||
|
||||
|
||||
@ -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")
|
||||
|
||||
Reference in New Issue
Block a user