Skip to content
Closed
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
8 changes: 8 additions & 0 deletions src/sast_agent_workflow/configs/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,11 @@ general:
level: INFO
front_end:
type: console
telemetry:
tracing:
galileo:
_type: galileo
endpoint: galileo_endpoint
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change to env variable

project: sast-workflow-project-name
logstream: default
api_key: ${GALILEO_API_KEY}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where do we define this key?
Guess we need to add it to conf and tekton pipeline?

37 changes: 33 additions & 4 deletions src/sast_agent_workflow/register.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ def convert_str_to_sast_tracker(input_str: str) -> SASTWorkflowTracker:
return empty_state

def convert_sast_tracker_to_str(tracker: SASTWorkflowTracker) -> str:
"""Convert SASTWorkflowTracker to summary statistics string"""
"""Convert SASTWorkflowTracker to summary statistics string including metrics"""
logger.debug("Converting SASTWorkflowTracker to summary stats")
try:
# For debug, print the tracker to the console
Expand All @@ -140,14 +140,43 @@ def convert_sast_tracker_to_str(tracker: SASTWorkflowTracker) -> str:
# Calculate summary statistics
counter = categorize_issues_by_status(tracker.issues)

return json.dumps(counter, indent=2)

# Include metrics if available (only accuracy, recall, precision, f1_score)
output = dict(counter)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Q: This output is per package? when can we have lists in the metrics?

if tracker.metrics:
# Filter to only show key performance metrics
filtered_metrics = {
"accuracy": tracker.metrics.get("accuracy"),
"recall": tracker.metrics.get("recall"),
"precision": tracker.metrics.get("precision"),
"f1_score": tracker.metrics.get("f1_score")
}
output["metrics"] = _make_json_serializable(filtered_metrics)

return json.dumps(output, indent=2)

except Exception as e:
logger.error("Failed to convert SASTWorkflowTracker to summary stats: %s", e)
raise e

def _make_json_serializable(obj):
"""Recursively convert sets to lists and numpy types to native Python types for JSON serialization"""
import numpy as np
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you move the import to the beginning of the file?


if isinstance(obj, dict):
return {k: _make_json_serializable(v) for k, v in obj.items()}
elif isinstance(obj, (list, tuple)):
return [_make_json_serializable(item) for item in obj]
elif isinstance(obj, set):
return list(obj)
elif isinstance(obj, (np.integer, np.int64, np.int32)):
return int(obj)
elif isinstance(obj, (np.floating, np.float64, np.float32)):
return float(obj)
else:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And if you want (more pythonic) - you can use json.dumps(filtered_metrics, indent=2, default= _make_json_serializable)

and modify _make_json_serializable to:

def json_encoder(obj):
    """Custom encoder for json.dumps default parameter"""
    if isinstance(obj, set):
        return list(obj)
    if isinstance(obj, (np.integer, np.int64, np.int32)):
        return int(obj)
    if isinstance(obj, (np.floating, np.float64, np.float32)):
        return float(obj)
    raise TypeError(f"Object of type {type(obj)} is not JSON serializable")

return obj

async def _response_fn(input_message: SASTWorkflowTracker) -> SASTWorkflowTracker:
"""Main response function that runs the LangGraph workflow"""
"""Main response function that runs the LangGraph workflow"""
results = await graph.ainvoke(input_message)
graph_output = SASTWorkflowTracker(**results)
return graph_output
Expand Down
4 changes: 4 additions & 0 deletions src/sast_agent_workflow/tools/write_results.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,12 @@ def _create_mock_evaluation_summary(summary_data, config, metrics):
mock_summary.tn = confusion_matrix.get(METRICS_FIELD_TRUE_NEGATIVES, 0)
mock_summary.fp = confusion_matrix.get(METRICS_FIELD_FALSE_POSITIVES, 0)
mock_summary.fn = confusion_matrix.get(METRICS_FIELD_FALSE_NEGATIVES, 0)
# CRITICAL: Set ground_truth to non-None if we have confusion matrix metrics
# This signals to Excel writer that GT comparison was performed
mock_summary.ground_truth = "calculated"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about changing the naming to something like 'has_ground_truth' - so it is clear that it is a flag?
(and then set the value to True/False)

else:
mock_summary.tp = mock_summary.tn = mock_summary.fp = mock_summary.fn = 0
mock_summary.ground_truth = None
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Q: where do we use the flag?


# ExcelWriter expects these to be collections that support len()
# Map from our stored sets/lists back to the expected attributes
Expand Down
Loading