Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 23 additions & 26 deletions sub_agents/e2e_test_analyst/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,17 +135,15 @@ def generate_source_code_links(test_name: str, commit_hash: Optional[str] = None

return links

async def get_e2e_test_logs_async(job_name: str, build_id: str) -> str:
async def get_e2e_test_logs_async(job_name: str, build_id: str, test_name: str) -> str:
"""Get e2e test logs from Prow."""
# Extract job short name from full job name
job_parts = job_name.split('-')
if len(job_parts) >= 8:
job_short_name = '-'.join(job_parts[7:]) # Everything after the 7th part
else:
job_short_name = job_name.split('-')[-1] # Fallback to last part


# E2E test logs are typically in openshift-e2e-test directory
e2e_test_path = f"artifacts/{job_short_name}/openshift-e2e-test/build-log.txt"
if "sno" in test_name:
e2e_test_path = f"artifacts/{test_name}/single-node-e2e-test/build-log.txt"
else:
e2e_test_path = f"artifacts/{test_name}/openshift-e2e-test/build-log.txt"

base_url = f"{GCS_URL}/{job_name}/{build_id}"
e2e_test_url = f"{base_url}/{e2e_test_path}"
Expand All @@ -165,7 +163,7 @@ async def get_e2e_test_logs_async(job_name: str, build_id: str) -> str:
Build ID: {build_id}

🔍 DEBUGGING INFO:
- Job short name extracted: {job_short_name}
- test_name: {test_name}
- Base URL: {base_url}
- Tried path: {e2e_test_path}

Expand Down Expand Up @@ -244,7 +242,7 @@ async def get_e2e_test_logs_async(job_name: str, build_id: str) -> str:
Build ID: {build_id}

🔍 DEBUGGING INFO:
- Job short name extracted: {job_short_name}
- test_name: {test_name}
- Base URL: {base_url}
- Tried path: {e2e_test_path}
- HTTP Error: {str(e)}
Expand All @@ -265,27 +263,26 @@ async def get_e2e_test_logs_async(job_name: str, build_id: str) -> str:
except Exception as e:
return f"❌ E2E TEST ANALYSIS ERROR: {str(e)}"

async def get_junit_results_async(job_name: str, build_id: str) -> str:
async def get_junit_results_async(job_name: str, build_id: str, test_name: str) -> str:
"""Get JUnit test results from Prow."""
# Extract job short name from full job name
job_parts = job_name.split('-')
if len(job_parts) >= 8:
job_short_name = '-'.join(job_parts[7:]) # Everything after the 7th part
# E2E test logs are typically in openshift-e2e-test directory
if "sno" in test_name:
e2e_test_path = f"artifacts/{test_name}/single-node-e2e-test"
else:
job_short_name = job_name.split('-')[-1] # Fallback to last part

# JUnit results are typically in junit directory
junit_path = f"artifacts/{job_short_name}/openshift-e2e-test/junit_e2e_*.xml"
e2e_test_path = f"artifacts/{test_name}/openshift-e2e-test"


base_url = f"{GCS_URL}/{job_name}/{build_id}"

async with httpx.AsyncClient() as client:
try:
# Try common JUnit file patterns
junit_patterns = [
f"artifacts/{job_short_name}/openshift-e2e-test/junit_e2e.xml",
f"artifacts/{job_short_name}/openshift-e2e-test/junit_e2e_20*.xml",
f"artifacts/{job_short_name}/openshift-e2e-test/artifacts/junit_e2e.xml"
f"{e2e_test_path}/junit_e2e.xml",
f"{e2e_test_path}/junit_e2e_20*.xml",
f"{e2e_test_path}/artifacts/junit_e2e.xml",
f"{e2e_test_path}/artifacts/junit/junit_e2e.xml",
f"{e2e_test_path}/artifacts/junit/junit_e2e_20*.xml"
]

for pattern in junit_patterns:
Expand Down Expand Up @@ -321,13 +318,13 @@ def get_job_metadata_tool(job_name: str, build_id: str):
"""Get metadata and status for a specific Prow job name and build ID."""
return run_async_in_thread(get_job_metadata_async(job_name, build_id))

def get_e2e_test_logs_tool(job_name: str, build_id: str):
def get_e2e_test_logs_tool(job_name: str, build_id: str, test_name: str):
"""Get e2e test logs from the openshift-e2e-test directory with commit info and source code links."""
return run_async_in_thread(get_e2e_test_logs_async(job_name, build_id))
return run_async_in_thread(get_e2e_test_logs_async(job_name, build_id, test_name))

def get_junit_results_tool(job_name: str, build_id: str):
def get_junit_results_tool(job_name: str, build_id: str, test_name: str):
"""Get JUnit test results from the e2e test artifacts."""
return run_async_in_thread(get_junit_results_async(job_name, build_id))
return run_async_in_thread(get_junit_results_async(job_name, build_id, test_name))

e2e_test_analyst_agent = Agent(
model=MODEL,
Expand Down
27 changes: 10 additions & 17 deletions sub_agents/installation_analyst/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,28 +169,21 @@ async def get_job_metadata_async(job_name: str, build_id: str) -> Dict[str, Any]
except Exception as e:
return {"error": f"Failed to fetch job info: {str(e)}"}

async def get_install_logs_async(job_name: str, build_id: str) -> str:
async def get_install_logs_async(job_name: str, build_id: str, test_name: str) -> str:
"""Get installation logs from build-log.txt in installation directories."""
# Extract job short name from full job name
job_parts = job_name.split('-')
if len(job_parts) >= 8:
job_short_name = '-'.join(job_parts[7:]) # Everything after the 7th part
else:
job_short_name = job_name.split('-')[-1] # Fallback to last part

# Try both possible installation directory patterns
# List of possible installation directory patterns
install_dirs = [
f"artifacts/{job_short_name}/ipi-install-install",
f"artifacts/{job_short_name}/ipi-install-install-stableinitial"
"ipi-install-install",
"ipi-install-install-stableinitial"
]

base_url = f"{GCS_URL}/{job_name}/{build_id}"

# Construct the base artifacts URL
artifacts_url = f"{base_url}/artifacts"
async with httpx.AsyncClient() as client:
for install_dir in install_dirs:
try:
# Get the build-log.txt from this installation directory
log_url = f"{base_url}/{install_dir}/build-log.txt"
log_url = f"{artifacts_url}/{test_name}/{install_dir}/build-log.txt"

response = await client.get(log_url)
response.raise_for_status()
Expand Down Expand Up @@ -281,7 +274,7 @@ async def get_install_logs_async(job_name: str, build_id: str) -> str:
Build ID: {build_id}

🔍 DEBUGGING INFO:
- Job short name extracted: {job_short_name}
- test_name: {test_name}
- Base URL: {base_url}
- Tried directories: {', '.join(install_dirs)}

Expand Down Expand Up @@ -318,9 +311,9 @@ def get_job_metadata_tool(job_name: str, build_id: str):
"""Get metadata and status for a specific Prow job name and build ID."""
return run_async_in_thread(get_job_metadata_async(job_name, build_id))

def get_install_logs_tool(job_name: str, build_id: str):
def get_install_logs_tool(job_name: str, build_id: str, test_name: str):
"""Get installation logs from build-log.txt in installation directories with detailed analysis."""
return run_async_in_thread(get_install_logs_async(job_name, build_id))
return run_async_in_thread(get_install_logs_async(job_name, build_id, test_name))

installation_analyst_agent = Agent(
model=MODEL,
Expand Down
4 changes: 3 additions & 1 deletion sub_agents/mustgather_analyst/agent.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
from google.adk import Agent
from . import prompt
from google.adk.models.lite_llm import LiteLlm

from .must_gather import get_must_gather, list_directory, read_drained_file, get_file_info, search_files
MODEL = "ollama/qwen3:4b"

mustgather_analyst_agent = Agent(
model=MODEL,
model=LiteLlm(model=MODEL),
name="mustgather_analyst_agent",
instruction=prompt.MUST_GATHER_SPECIALIST_PROMPT,
output_key="must_gather_analysis_output",
Expand Down