From 446ca9b749f28c39071b817b2adf68505a055225 Mon Sep 17 00:00:00 2001 From: namedfarouk Date: Fri, 20 Feb 2026 02:35:13 +0100 Subject: [PATCH 1/3] fix: correct template filename and remove non-existent testutils directory from docs --- CLAUDE.md | 2 +- README.md | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 39732b4..30f4d59 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -41,7 +41,7 @@ Agent executes tools → LLM inference → Post-process response → Return Agen - **agent/**: Agent orchestration - `agent_executors.py` creates LangGraph ReAct agents, `tools.py` defines agent tools, `prompts.py` loads Jinja2 templates - **server/**: FastAPI app in `fastapi_server.py`, auth, validation, activity tracking - **onchain/**: Blockchain data layer - `pools/` for DeFi protocol abstraction, `tokens/` for metadata, `portfolio/` for wallet analysis, `analytics/` for metrics -- **templates/**: Jinja2 prompt templates for agents (`analyst_agent.jinja2`, `investor_agent.jinja2`) +- **templates/**: Jinja2 prompt templates for agents (`analytics_agent.jinja2`, `investor_agent.jinja2`) - **api/api_types.py**: All Pydantic models (Token, Pool, Portfolio, Message types) ### Protocol System diff --git a/README.md b/README.md index a160704..a702dba 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,6 @@ server/ # Flask server exposing the API static/ # Static assets for web interface templates/ # LLM prompt templates for agent testclient/ # Client for testing the API -testutils/ # Utility functions for testing ``` ### Agents From b9fe8a84077ec8ea61493b278254b1f2249e530a Mon Sep 17 00:00:00 2001 From: namedfarouk Date: Fri, 20 Feb 2026 02:46:50 +0100 Subject: [PATCH 2/3] fix: resolve variable shadowing in search_token function The 'token' parameter (str) was being overwritten by a TokenMetadata result using the same variable name. Renamed the result variable to 'result' to improve code clarity and fix the type shadowing issue. --- agent/tools.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/agent/tools.py b/agent/tools.py index 6b3100c..6fdfe80 100644 --- a/agent/tools.py +++ b/agent/tools.py @@ -71,19 +71,19 @@ async def search_token( token: str, chain: Optional[str] = None ) -> Optional[TokenMetadata]: """Search for a token by name or symbol. Returns metadata for the first token found.""" - token: Optional[TokenMetadata] = await token_metadata_repo.search_token( + result: Optional[TokenMetadata] = await token_metadata_repo.search_token( token, chain ) - if not token: + if not result: return "No token found." return { - "id": f"{token.chain}:{token.address}", - "address": token.address, - "name": token.name, - "symbol": token.symbol, - "price_usd": token.price, - "chain": token.chain, + "id": f"{result.chain}:{result.address}", + "address": result.address, + "name": result.name, + "symbol": result.symbol, + "price_usd": result.price, + "chain": result.chain, } return [ From a2bd4954a321872dbd2615f521a4ca9009f2db60 Mon Sep 17 00:00:00 2001 From: namedfarouk Date: Fri, 20 Feb 2026 02:46:50 +0100 Subject: [PATCH 3/3] test: add unit tests for search_token to verify fix --- test_search_token.py | 56 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 test_search_token.py diff --git a/test_search_token.py b/test_search_token.py new file mode 100644 index 0000000..4fbfec3 --- /dev/null +++ b/test_search_token.py @@ -0,0 +1,56 @@ +""" +Test to verify search_token function does not shadow the input parameter. +""" +import unittest +from unittest.mock import AsyncMock, MagicMock + + +class TestSearchToken(unittest.IsolatedAsyncioTestCase): + async def test_search_token_no_variable_shadowing(self): + """ + Ensure search_token uses 'result' variable instead of shadowing + the 'token' string parameter with a TokenMetadata object. + """ + # Mock the token metadata repo + mock_repo = MagicMock() + mock_token = MagicMock() + mock_token.chain = "solana" + mock_token.address = "So111111111" + mock_token.name = "Wrapped SOL" + mock_token.symbol = "SOL" + mock_token.price = 150.0 + mock_repo.search_token = AsyncMock(return_value=mock_token) + + # Import and create the toolkit + from agent.tools import create_analytics_agent_toolkit + toolkit = create_analytics_agent_toolkit(mock_repo) + + # Find the search_token tool + search_tool = next(t for t in toolkit if t.name == "search_token") + + # Call with a string query + result = await search_tool.ainvoke({"token": "SOL", "chain": "solana"}) + + # Verify result is correct + self.assertEqual(result["symbol"], "SOL") + self.assertEqual(result["name"], "Wrapped SOL") + self.assertEqual(result["chain"], "solana") + + async def test_search_token_not_found(self): + """ + Ensure search_token returns correct message when token not found. + """ + mock_repo = MagicMock() + mock_repo.search_token = AsyncMock(return_value=None) + + from agent.tools import create_analytics_agent_toolkit + toolkit = create_analytics_agent_toolkit(mock_repo) + + search_tool = next(t for t in toolkit if t.name == "search_token") + result = await search_tool.ainvoke({"token": "UNKNOWN"}) + + self.assertEqual(result, "No token found.") + + +if __name__ == "__main__": + unittest.main()