chore(ai): remove ADK references and migrate to PydanticAI
This commit completes the cleanup of Google ADK references after migrating to PydanticAI. Changes: - Removed ADK agent implementation (adk_agent.py) - Removed ADK test files (test_06, test_07, test_10) - Removed ADK diagnostic files - Updated config to use pydantic_system_prompt_variant instead of adk_system_prompt_variant - Updated prompts.py to rename adk_agent to pydantic_agent - Updated tool registry and tools.py docstrings to remove ADK references - Added new comprehensive PydanticAI tests (test_06, test_07, test_10) - Marked legacy ADK functions as deprecated for backwards compatibility The codebase is now clean and stable with PydanticAI as the primary agent framework. Docker container builds successfully with no ADK import errors. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,131 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Layer 6: ADK Setup Tests
|
||||
Tests that Google ADK initializes correctly and can handle basic completions.
|
||||
"""
|
||||
import pytest
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# Add parent directory to path
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent))
|
||||
|
||||
from src.config import get_settings
|
||||
from src.prompts import get_prompt
|
||||
from src.agents import ADK_AVAILABLE
|
||||
|
||||
if not ADK_AVAILABLE:
|
||||
pytest.skip("Google ADK not available", allow_module_level=True)
|
||||
|
||||
from src.agents import ADKAgent
|
||||
|
||||
|
||||
def test_adk_import():
|
||||
"""Test that ADK can be imported"""
|
||||
assert ADK_AVAILABLE, "ADK should be available"
|
||||
print("✓ ADK imports successful")
|
||||
|
||||
|
||||
def test_adk_prompt_exists():
|
||||
"""Test that ADK prompt variant exists"""
|
||||
settings = get_settings()
|
||||
prompt = get_prompt(settings.adk_system_prompt_variant)
|
||||
|
||||
assert prompt is not None, "ADK prompt should exist"
|
||||
assert len(prompt) > 0, "ADK prompt should not be empty"
|
||||
assert "assistant" in prompt.lower() or "tools" in prompt.lower(), "ADK prompt should mention tools/assistant"
|
||||
|
||||
print(f"✓ ADK prompt variant '{settings.adk_system_prompt_variant}' exists")
|
||||
print(f" Prompt: {prompt[:100]}...")
|
||||
|
||||
|
||||
def test_adk_agent_initialization():
|
||||
"""Test that ADK agent can be initialized without tools"""
|
||||
try:
|
||||
agent = ADKAgent(tools=[])
|
||||
assert agent is not None, "Agent should be initialized"
|
||||
assert agent.llm is not None, "LLM should be initialized"
|
||||
assert agent.agent is not None, "ADK agent should be initialized"
|
||||
assert agent.tools == [], "Tools should be empty"
|
||||
|
||||
print("✓ ADK agent initialized successfully")
|
||||
print(f" LLM: {agent.llm}")
|
||||
print(f" Tools: {len(agent.tools)}")
|
||||
|
||||
except Exception as e:
|
||||
pytest.fail(f"ADK agent initialization failed: {e}")
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_adk_simple_completion():
|
||||
"""Test ADK agent with a simple question (no tools needed)"""
|
||||
agent = ADKAgent(tools=[])
|
||||
|
||||
messages = [{"role": "user", "content": "What is 2+2? Answer with just the number."}]
|
||||
|
||||
print(f"\n→ Testing ADK completion...")
|
||||
response = await agent.chat_completion(messages=messages)
|
||||
|
||||
assert response is not None, "Response should not be None"
|
||||
assert len(response) > 0, "Response should not be empty"
|
||||
assert "4" in response, f"Expected '4' in response, got: {response}"
|
||||
|
||||
print(f"✓ ADK response: {response}")
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_adk_streaming():
|
||||
"""Test ADK agent streaming mode"""
|
||||
agent = ADKAgent(tools=[])
|
||||
|
||||
messages = [{"role": "user", "content": "Count from 1 to 3. Just the numbers."}]
|
||||
|
||||
chunks = []
|
||||
event_count = 0
|
||||
|
||||
print(f"\n→ Testing ADK streaming...")
|
||||
async for chunk in agent.chat(messages=messages, stream=True):
|
||||
event_count += 1
|
||||
if chunk.get("type") == "content" and chunk.get("content"):
|
||||
chunks.append(chunk["content"])
|
||||
|
||||
full_content = "".join(chunks)
|
||||
|
||||
assert event_count > 0, "Should receive events"
|
||||
assert len(full_content) > 0, "Should receive content"
|
||||
|
||||
print(f"✓ Received {event_count} events")
|
||||
print(f"✓ Content: {full_content}")
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_adk_capital_of_france():
|
||||
"""Test ADK with the standard 'capital of France' question"""
|
||||
agent = ADKAgent(tools=[])
|
||||
|
||||
messages = [{"role": "user", "content": "What is the capital of France?"}]
|
||||
|
||||
print(f"\n→ Testing 'What is the capital of France?' with ADK...")
|
||||
response = await agent.chat_completion(messages=messages)
|
||||
|
||||
assert response is not None, "Response should not be None"
|
||||
assert len(response) > 0, "Response should not be empty"
|
||||
assert "paris" in response.lower(), f"Expected 'Paris' in answer, got: {response}"
|
||||
|
||||
print(f"✓ Correct answer: {response}")
|
||||
|
||||
|
||||
def test_adk_system_prompt_loading():
|
||||
"""Test that ADK agent loads correct system prompt"""
|
||||
agent = ADKAgent(tools=[])
|
||||
|
||||
settings = get_settings()
|
||||
expected_prompt = get_prompt(settings.adk_system_prompt_variant)
|
||||
|
||||
assert agent.system_prompt == expected_prompt, "System prompt should match config"
|
||||
print(f"✓ System prompt loaded correctly")
|
||||
print(f" Variant: {settings.adk_system_prompt_variant}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
pytest.main([__file__, "-v", "-s"])
|
||||
@@ -0,0 +1,131 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Layer 6: PydanticAI Setup Tests
|
||||
Tests that PydanticAI initializes correctly and can handle basic completions.
|
||||
"""
|
||||
import pytest
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# Add parent directory to path
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent))
|
||||
|
||||
from src.config import get_settings
|
||||
from src.prompts import get_prompt
|
||||
from src.agents import PYDANTIC_AI_AVAILABLE
|
||||
|
||||
if not PYDANTIC_AI_AVAILABLE:
|
||||
pytest.skip("PydanticAI not available", allow_module_level=True)
|
||||
|
||||
from src.agents import PydanticAgent
|
||||
|
||||
|
||||
def test_pydantic_import():
|
||||
"""Test that PydanticAI can be imported"""
|
||||
assert PYDANTIC_AI_AVAILABLE, "PydanticAI should be available"
|
||||
print("✓ PydanticAI imports successful")
|
||||
|
||||
|
||||
def test_pydantic_prompt_exists():
|
||||
"""Test that PydanticAI prompt variant exists"""
|
||||
settings = get_settings()
|
||||
prompt = get_prompt(settings.pydantic_system_prompt_variant)
|
||||
|
||||
assert prompt is not None, "PydanticAI prompt should exist"
|
||||
assert len(prompt) > 0, "PydanticAI prompt should not be empty"
|
||||
assert "assistant" in prompt.lower() or "tools" in prompt.lower(), "PydanticAI prompt should mention tools/assistant"
|
||||
|
||||
print(f"✓ PydanticAI prompt variant '{settings.pydantic_system_prompt_variant}' exists")
|
||||
print(f" Prompt: {prompt[:100]}...")
|
||||
|
||||
|
||||
def test_pydantic_agent_initialization():
|
||||
"""Test that PydanticAI agent can be initialized without tools"""
|
||||
try:
|
||||
agent = PydanticAgent(tools=[], enable_memory=False)
|
||||
assert agent is not None, "Agent should be initialized"
|
||||
assert agent.model is not None, "Model should be initialized"
|
||||
assert agent.agent is not None, "PydanticAI agent should be initialized"
|
||||
assert agent.tools == [], "Tools should be empty"
|
||||
|
||||
print("✓ PydanticAI agent initialized successfully")
|
||||
print(f" Model: {agent.model}")
|
||||
print(f" Tools: {len(agent.tools)}")
|
||||
|
||||
except Exception as e:
|
||||
pytest.fail(f"PydanticAI agent initialization failed: {e}")
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_pydantic_simple_completion():
|
||||
"""Test PydanticAI agent with a simple question (no tools needed)"""
|
||||
agent = PydanticAgent(tools=[], enable_memory=False)
|
||||
|
||||
messages = [{"role": "user", "content": "What is 2+2? Answer with just the number."}]
|
||||
|
||||
print(f"\n→ Testing PydanticAI completion...")
|
||||
response = await agent.chat_completion(messages=messages)
|
||||
|
||||
assert response is not None, "Response should not be None"
|
||||
assert len(response) > 0, "Response should not be empty"
|
||||
assert "4" in response, f"Expected '4' in response, got: {response}"
|
||||
|
||||
print(f"✓ PydanticAI response: {response}")
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_pydantic_streaming():
|
||||
"""Test PydanticAI agent streaming mode"""
|
||||
agent = PydanticAgent(tools=[], enable_memory=False)
|
||||
|
||||
messages = [{"role": "user", "content": "Count from 1 to 3. Just the numbers."}]
|
||||
|
||||
chunks = []
|
||||
event_count = 0
|
||||
|
||||
print(f"\n→ Testing PydanticAI streaming...")
|
||||
async for chunk in agent.chat(messages=messages, stream=True):
|
||||
event_count += 1
|
||||
if chunk.get("type") == "content" and chunk.get("content"):
|
||||
chunks.append(chunk["content"])
|
||||
|
||||
full_content = "".join(chunks)
|
||||
|
||||
assert event_count > 0, "Should receive events"
|
||||
assert len(full_content) > 0, "Should receive content"
|
||||
|
||||
print(f"✓ Received {event_count} events")
|
||||
print(f"✓ Content: {full_content}")
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_pydantic_capital_of_france():
|
||||
"""Test PydanticAI with the standard 'capital of France' question"""
|
||||
agent = PydanticAgent(tools=[], enable_memory=False)
|
||||
|
||||
messages = [{"role": "user", "content": "What is the capital of France?"}]
|
||||
|
||||
print(f"\n→ Testing 'What is the capital of France?' with PydanticAI...")
|
||||
response = await agent.chat_completion(messages=messages)
|
||||
|
||||
assert response is not None, "Response should not be None"
|
||||
assert len(response) > 0, "Response should not be empty"
|
||||
assert "paris" in response.lower(), f"Expected 'Paris' in answer, got: {response}"
|
||||
|
||||
print(f"✓ Correct answer: {response}")
|
||||
|
||||
|
||||
def test_pydantic_system_prompt_loading():
|
||||
"""Test that PydanticAI agent loads correct system prompt"""
|
||||
agent = PydanticAgent(tools=[], enable_memory=False)
|
||||
|
||||
settings = get_settings()
|
||||
expected_prompt = get_prompt(settings.pydantic_system_prompt_variant)
|
||||
|
||||
assert agent.system_prompt == expected_prompt, "System prompt should match config"
|
||||
print(f"✓ System prompt loaded correctly")
|
||||
print(f" Variant: {settings.pydantic_system_prompt_variant}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
pytest.main([__file__, "-v", "-s"])
|
||||
+14
-29
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Layer 7: ADK Tools Tests
|
||||
Tests that local tools are registered and work with the ADK agent.
|
||||
Layer 7: PydanticAI Tools Tests
|
||||
Tests that local tools are registered and work with the PydanticAI agent.
|
||||
"""
|
||||
import pytest
|
||||
import sys
|
||||
@@ -11,13 +11,13 @@ from pathlib import Path
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent))
|
||||
|
||||
from src.config import get_settings
|
||||
from src.tools import get_all_tools, get_agent_tools, clear_registry
|
||||
from src.agents import ADK_AVAILABLE
|
||||
from src.tools.registry import get_all_tools, clear_registry
|
||||
from src.agents import PYDANTIC_AI_AVAILABLE
|
||||
|
||||
if not ADK_AVAILABLE:
|
||||
pytest.skip("Google ADK not available", allow_module_level=True)
|
||||
if not PYDANTIC_AI_AVAILABLE:
|
||||
pytest.skip("PydanticAI not available", allow_module_level=True)
|
||||
|
||||
from src.agents import ADKAgent
|
||||
from src.agents import PydanticAgent
|
||||
|
||||
|
||||
def test_local_tools_registered():
|
||||
@@ -82,37 +82,22 @@ async def test_calculator_security():
|
||||
print(f"✓ Blocked dangerous expression: {expr}")
|
||||
|
||||
|
||||
def test_adk_tool_conversion():
|
||||
"""Test that tools can be converted to ADK format"""
|
||||
adk_tools = get_agent_tools()
|
||||
|
||||
assert len(adk_tools) > 0, "Should have at least some tools"
|
||||
print(f"✓ Converted {len(adk_tools)} tools to ADK format")
|
||||
|
||||
# All tools should be FunctionTool instances
|
||||
from google.adk.tools import FunctionTool
|
||||
for tool in adk_tools:
|
||||
assert isinstance(tool, FunctionTool), f"Tool should be FunctionTool, got {type(tool)}"
|
||||
|
||||
print(f"✓ All tools are valid FunctionTool instances")
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_adk_agent_with_tools():
|
||||
"""Test ADK agent initialization with tools"""
|
||||
agent = ADKAgent(discover_tools=True)
|
||||
async def test_pydantic_agent_with_tools():
|
||||
"""Test PydanticAI agent initialization with tools"""
|
||||
agent = PydanticAgent(discover_tools=True, enable_memory=False)
|
||||
|
||||
assert len(agent.tools) > 0, "Agent should have tools"
|
||||
print(f"✓ ADK agent initialized with {len(agent.tools)} tools")
|
||||
print(f"✓ PydanticAI agent initialized with {len(agent.tools)} tools")
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_tool_calling_integration():
|
||||
"""Test that ADK agent can use tools to answer questions"""
|
||||
agent = ADKAgent(discover_tools=True)
|
||||
"""Test that PydanticAI agent can use tools to answer questions"""
|
||||
agent = PydanticAgent(discover_tools=True, enable_memory=False)
|
||||
|
||||
# Ask a question that requires the calculator tool
|
||||
messages = [{"role": "user", "content": "What is 15 + 27? Use the calculator tool."}]
|
||||
messages = [{"role": "user", "content": "What is 15 + 27? Use the calculate tool."}]
|
||||
|
||||
print(f"\n→ Testing tool calling with: {messages[0]['content']}")
|
||||
response = await agent.chat_completion(messages=messages)
|
||||
+43
-24
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Layer 10: ADK API Tests
|
||||
Tests HTTP endpoints for both simple and ADK agents.
|
||||
Layer 10: PydanticAI API Tests
|
||||
Tests HTTP endpoints for both simple and PydanticAI agents.
|
||||
"""
|
||||
import pytest
|
||||
import sys
|
||||
@@ -11,7 +11,7 @@ from pathlib import Path
|
||||
# Add parent directory to path
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent))
|
||||
|
||||
from src.agents import ADK_AVAILABLE
|
||||
from src.agents import PYDANTIC_AI_AVAILABLE
|
||||
|
||||
# Base URL for the service
|
||||
BASE_URL = "http://localhost:8086"
|
||||
@@ -54,8 +54,8 @@ async def test_list_tools():
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_chat_completions_simple():
|
||||
"""Test /v1/chat/completions endpoint (default)"""
|
||||
async def test_chat_completions_default():
|
||||
"""Test /v1/chat/completions endpoint (default - should use PydanticAI)"""
|
||||
async with httpx.AsyncClient(timeout=30.0) as client:
|
||||
response = await client.post(
|
||||
f"{BASE_URL}/v1/chat/completions",
|
||||
@@ -104,14 +104,14 @@ async def test_chat_simple_endpoint():
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_chat_adk_endpoint():
|
||||
"""Test /v1/chat/adk endpoint"""
|
||||
if not ADK_AVAILABLE:
|
||||
pytest.skip("ADK not available")
|
||||
async def test_chat_pydantic_endpoint():
|
||||
"""Test /v1/chat/pydantic endpoint"""
|
||||
if not PYDANTIC_AI_AVAILABLE:
|
||||
pytest.skip("PydanticAI not available")
|
||||
|
||||
async with httpx.AsyncClient(timeout=30.0) as client:
|
||||
response = await client.post(
|
||||
f"{BASE_URL}/v1/chat/adk",
|
||||
f"{BASE_URL}/v1/chat/pydantic",
|
||||
json={
|
||||
"messages": [
|
||||
{"role": "user", "content": "What is the current date?"}
|
||||
@@ -124,26 +124,26 @@ async def test_chat_adk_endpoint():
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
|
||||
assert data["model"] == "adk"
|
||||
assert data["model"] == "pydantic"
|
||||
assert "choices" in data
|
||||
assert "tools_enabled" in data
|
||||
assert "tools_count" in data
|
||||
|
||||
content = data["choices"][0]["message"]["content"]
|
||||
print(f"✓ /v1/chat/adk response: {content[:200]}...")
|
||||
print(f"✓ /v1/chat/pydantic response: {content[:200]}...")
|
||||
print(f" Tools enabled: {data['tools_enabled']}")
|
||||
print(f" Tools count: {data['tools_count']}")
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_chat_adk_with_calculator():
|
||||
"""Test ADK endpoint using calculator tool"""
|
||||
if not ADK_AVAILABLE:
|
||||
pytest.skip("ADK not available")
|
||||
async def test_chat_pydantic_with_calculator():
|
||||
"""Test PydanticAI endpoint using calculator tool"""
|
||||
if not PYDANTIC_AI_AVAILABLE:
|
||||
pytest.skip("PydanticAI not available")
|
||||
|
||||
async with httpx.AsyncClient(timeout=60.0) as client:
|
||||
response = await client.post(
|
||||
f"{BASE_URL}/v1/chat/adk",
|
||||
f"{BASE_URL}/v1/chat/pydantic",
|
||||
json={
|
||||
"messages": [
|
||||
{"role": "user", "content": "What is 123 + 456? Use the calculate tool."}
|
||||
@@ -157,7 +157,7 @@ async def test_chat_adk_with_calculator():
|
||||
data = response.json()
|
||||
|
||||
content = data["choices"][0]["message"]["content"]
|
||||
print(f"✓ ADK with calculator: {content}")
|
||||
print(f"✓ PydanticAI with calculator: {content}")
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@@ -198,14 +198,14 @@ async def test_streaming_simple():
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_adk_without_tools():
|
||||
"""Test ADK endpoint with tools disabled"""
|
||||
if not ADK_AVAILABLE:
|
||||
pytest.skip("ADK not available")
|
||||
async def test_pydantic_without_tools():
|
||||
"""Test PydanticAI endpoint with tools disabled"""
|
||||
if not PYDANTIC_AI_AVAILABLE:
|
||||
pytest.skip("PydanticAI not available")
|
||||
|
||||
async with httpx.AsyncClient(timeout=30.0) as client:
|
||||
response = await client.post(
|
||||
f"{BASE_URL}/v1/chat/adk",
|
||||
f"{BASE_URL}/v1/chat/pydantic",
|
||||
json={
|
||||
"messages": [
|
||||
{"role": "user", "content": "Hello!"}
|
||||
@@ -221,7 +221,26 @@ async def test_adk_without_tools():
|
||||
assert data["tools_enabled"] is False
|
||||
assert data["tools_count"] == 0
|
||||
|
||||
print(f"✓ ADK without tools works")
|
||||
print(f"✓ PydanticAI without tools works")
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_list_models():
|
||||
"""Test the /v1/models endpoint"""
|
||||
async with httpx.AsyncClient() as client:
|
||||
response = await client.get(f"{BASE_URL}/v1/models")
|
||||
assert response.status_code == 200
|
||||
|
||||
data = response.json()
|
||||
assert "object" in data
|
||||
assert data["object"] == "list"
|
||||
assert "data" in data
|
||||
assert len(data["data"]) > 0
|
||||
|
||||
print(f"✓ Models endpoint OK")
|
||||
print(f" Available models:")
|
||||
for model in data["data"]:
|
||||
print(f" - {model['id']}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
Reference in New Issue
Block a user