kronector / tests /test_integration_agent_predict.py
Prathamesh Bhamare
Initial commit: KRONECTOR MLOps & Multi-Agent AI system
2532605
Raw
History Blame Contribute Delete
5.76 kB
"""
Integration test: DataAgent β†’ FeatureEngineering β†’ Prediction
Demonstrates complete flow from natural language query to win probability.
Run with:
python -m pytest tests/test_integration_agent_predict.py -v -s
"""
import json
import os
from pathlib import Path
import pandas as pd
import pytest
from agents.data_agent import data_agent, QueryIntent
from ml.feature_engineering import prepare_model_data
from ml.predict import predict_dataframe, load_model_and_encoders
@pytest.fixture
def minimal_model_and_encoders():
"""Load real trained model from MLflow."""
try:
run_id = os.getenv("KRONECTOR_TEST_RUN_ID")
if not run_id:
pytest.skip("KRONECTOR_TEST_RUN_ID not set")
model, encoders = load_model_and_encoders(run_id)
return model, encoders
except Exception as e:
pytest.skip(f"Model not available: {e}")
def test_agent_to_prediction_pipeline():
"""
Test: Natural language β†’ Intent β†’ DataFrame β†’ Predictions
Uses mock parser (no API key needed) to verify the full pipeline.
"""
# Mock parser for reproducibility
def mock_parser(query: str) -> QueryIntent:
"""For this test, always return 2023 Bahrain Verstappen."""
return {
"season": 2023,
"round": 1,
"driver_id": "VER",
"driver_name": "Max Verstappen",
}
# Step 1: Natural language to structured data
query = "Will Max Verstappen win at Bahrain 2023?"
result = data_agent(query, parser=mock_parser)
# Verify intent extraction
assert result["intent"]["season"] == 2023
assert result["intent"]["round"] == 1
assert result["intent"]["driver_id"] == "VER"
print(f"βœ“ Intent parsed: {result['intent']}")
# Step 2: Verify DataFrame is prediction-compatible
df = result["dataframe"]
assert len(df) == 1
assert "driver_id" in df.columns
assert df.iloc[0]["driver_id"] == "VER"
print(f"βœ“ DataFrame shape: {df.shape}")
print(f"βœ“ Columns: {list(df.columns)[:5]}... ({len(df.columns)} total)")
# Step 3: Feature engineering (no encoders needed for shape test)
bundle, encoders = prepare_model_data(df)
assert len(bundle.X) == 1
assert len(bundle.feature_columns) > 0
assert "grid_position" in bundle.feature_columns
assert "win_probability" not in bundle.feature_columns
print(f"βœ“ Features: {len(bundle.feature_columns)} columns, shape {bundle.X.shape}")
# Step 4: Can generate predictions (if model available)
try:
run_id = os.getenv("KRONECTOR_TEST_RUN_ID")
if not run_id:
print("β„Ή Model inference skipped: KRONECTOR_TEST_RUN_ID not set")
return
model, fitted_encoders = load_model_and_encoders(run_id)
predictions = predict_dataframe(df, model, fitted_encoders)
win_prob = predictions.iloc[0]["win_probability"]
print(f"βœ“ Prediction: {win_prob:.2%} win probability for Verstappen")
assert 0 <= win_prob <= 1
except Exception as e:
print(f"β„Ή Model inference skipped: {e}")
print(" (This is expected if MLflow model is not available)")
def test_multiple_drivers_same_race():
"""Test querying all drivers in a race without driver filter."""
def mock_parser(query: str) -> QueryIntent:
"""Return race without driver filter."""
return {
"season": 2023,
"round": 1,
"driver_id": None, # Get all drivers
"driver_name": None,
}
result = data_agent("Predict Bahrain 2023 standings", parser=mock_parser)
# Should have multiple drivers
num_drivers = len(result["dataframe"])
assert num_drivers > 1
print(f"βœ“ Queried {num_drivers} drivers for 2023 Bahrain")
# All rows should have different driver_ids
driver_ids = result["dataframe"]["driver_id"].unique()
assert len(driver_ids) == num_drivers
print(f"βœ“ Unique drivers: {', '.join(sorted(driver_ids))}")
def test_agent_output_matches_prediction_schema():
"""Verify agent output matches PredictionInputRow schema."""
def mock_parser(query: str) -> QueryIntent:
return {"season": 2023, "round": 1, "driver_id": "HAM"}
result = data_agent("Hamilton Bahrain", parser=mock_parser)
rows = result["rows"]
# Verify required fields exist
required_fields = {
"season", "round", "driver_id", "team",
"grid_position", "finish_position", "circuit_id"
}
for row in rows:
for field in required_fields:
assert field in row, f"Missing field: {field}"
print(f"βœ“ All {len(required_fields)} required fields present")
print(f"βœ“ Sample row keys: {list(rows[0].keys())}")
def test_agent_error_handling():
"""Test agent gracefully handles invalid queries."""
def mock_parser(query: str) -> QueryIntent:
# Return an impossible race (1000 rounds doesn't exist)
return {"season": 2099, "round": 1000, "driver_id": None}
with pytest.raises(ValueError, match="No rows found"):
data_agent("Impossible race", parser=mock_parser)
print("βœ“ Agent properly rejects non-existent races")
if __name__ == "__main__":
# Run standalone (without pytest)
print("=" * 60)
print("KRONECTOR: DataAgent Integration Test")
print("=" * 60)
test_agent_to_prediction_pipeline()
print()
test_multiple_drivers_same_race()
print()
test_agent_output_matches_prediction_schema()
print()
test_agent_error_handling()
print()
print("=" * 60)
print("All integration tests passed!")
print("=" * 60)