llm fix
This commit is contained in:
112
src/fgai/llm.py
112
src/fgai/llm.py
@@ -76,23 +76,117 @@ def ollama_summary(
|
|||||||
return str(data.get("response", "")).strip()
|
return str(data.get("response", "")).strip()
|
||||||
|
|
||||||
|
|
||||||
def ollama_dashboard_assessment(analysis: dict[str, object], model: str | None = None, timeout: int | None = None) -> str:
|
def _compact_field_deviations(field_deviations: object, *, limit: int = 12) -> list[dict[str, object]]:
|
||||||
compact = {
|
rows: list[dict[str, object]] = []
|
||||||
|
if not isinstance(field_deviations, dict):
|
||||||
|
return rows
|
||||||
|
for entity, deviations in field_deviations.items():
|
||||||
|
if not isinstance(deviations, list):
|
||||||
|
continue
|
||||||
|
for item in deviations:
|
||||||
|
if not isinstance(item, dict):
|
||||||
|
continue
|
||||||
|
rows.append({
|
||||||
|
"entity": entity,
|
||||||
|
"stream": item.get("stream_name") or item.get("stream_title") or item.get("stream_id"),
|
||||||
|
"detector": item.get("detector"),
|
||||||
|
"field": item.get("field"),
|
||||||
|
"score": item.get("score"),
|
||||||
|
"confidence": item.get("confidence"),
|
||||||
|
"reason": item.get("reason"),
|
||||||
|
"current": item.get("current"),
|
||||||
|
"baseline": item.get("baseline"),
|
||||||
|
"baseline_age_days": item.get("baseline_age_days"),
|
||||||
|
"sample_values": item.get("sample_values", [])[:5] if isinstance(item.get("sample_values"), list) else [],
|
||||||
|
"feedback": item.get("feedback", "unreviewed"),
|
||||||
|
})
|
||||||
|
return sorted(rows, key=lambda item: int(item.get("score") or 0), reverse=True)[:limit]
|
||||||
|
|
||||||
|
|
||||||
|
def _compact_event_context(event_context: object) -> dict[str, object]:
|
||||||
|
if not isinstance(event_context, dict):
|
||||||
|
return {}
|
||||||
|
return {
|
||||||
|
"source_profiles": event_context.get("source_profiles", [])[:10] if isinstance(event_context.get("source_profiles"), list) else [],
|
||||||
|
"related_activity": event_context.get("related_activity", [])[:20] if isinstance(event_context.get("related_activity"), list) else [],
|
||||||
|
"entity_labels": event_context.get("entity_labels", {}) if isinstance(event_context.get("entity_labels"), dict) else {},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _compact_mcp_capabilities(capabilities: object) -> dict[str, object]:
|
||||||
|
if not isinstance(capabilities, dict):
|
||||||
|
return {}
|
||||||
|
mcp = capabilities.get("graylog_mcp", {}) if isinstance(capabilities.get("graylog_mcp"), dict) else {}
|
||||||
|
profile_advisor = capabilities.get("profile_advisor", {}) if isinstance(capabilities.get("profile_advisor"), dict) else {}
|
||||||
|
threat_intel = capabilities.get("threat_intel", {}) if isinstance(capabilities.get("threat_intel"), dict) else {}
|
||||||
|
return {
|
||||||
|
"graylog_mcp": {
|
||||||
|
"status": mcp.get("status"),
|
||||||
|
"fetch_mode": mcp.get("fetch_mode"),
|
||||||
|
"coverage_status": mcp.get("coverage_status"),
|
||||||
|
"aggregate_events": mcp.get("aggregate_events"),
|
||||||
|
"raw_events_fetched": mcp.get("raw_events_fetched") or mcp.get("events_fetched"),
|
||||||
|
"partial_streams": mcp.get("partial_streams"),
|
||||||
|
"skipped_streams": mcp.get("skipped_streams"),
|
||||||
|
"coverage_warning": mcp.get("coverage_warning"),
|
||||||
|
"error": mcp.get("error"),
|
||||||
|
},
|
||||||
|
"profile_advisor": {
|
||||||
|
"status": profile_advisor.get("status"),
|
||||||
|
"error": profile_advisor.get("error"),
|
||||||
|
"candidates": profile_advisor.get("candidates"),
|
||||||
|
},
|
||||||
|
"threat_intel": {
|
||||||
|
"status": threat_intel.get("status"),
|
||||||
|
"provider": threat_intel.get("provider"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _compact_profile_suggestion_for_dashboard(item: object) -> dict[str, object]:
|
||||||
|
if not isinstance(item, dict):
|
||||||
|
return {}
|
||||||
|
profile = item.get("profile", {}) if isinstance(item.get("profile"), dict) else {}
|
||||||
|
advisor = item.get("profile_advisor", {}) if isinstance(item.get("profile_advisor"), dict) else {}
|
||||||
|
return {
|
||||||
|
"stream_name": item.get("stream_name"),
|
||||||
|
"detected_log_type": item.get("detected_log_type"),
|
||||||
|
"confidence": item.get("confidence"),
|
||||||
|
"profile_exists": item.get("profile_exists"),
|
||||||
|
"advisor_status": advisor.get("status"),
|
||||||
|
"entity_fields": profile.get("entity_fields") or ([profile.get("entity_field")] if profile.get("entity_field") else []),
|
||||||
|
"timestamp_field": profile.get("timestamp_field"),
|
||||||
|
"categorical_fields": profile.get("categorical_fields", [])[:8],
|
||||||
|
"numeric_fields": profile.get("numeric_fields", [])[:5],
|
||||||
|
"relationship_fields": profile.get("relationship_fields", [])[:5],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def compact_dashboard_analysis(analysis: dict[str, object]) -> dict[str, object]:
|
||||||
|
return {
|
||||||
"summary": analysis.get("summary", {}),
|
"summary": analysis.get("summary", {}),
|
||||||
"anomaly_summary": analysis.get("anomaly_summary", {}),
|
"anomaly_summary": analysis.get("anomaly_summary", {}),
|
||||||
|
"baseline": analysis.get("baseline", {}),
|
||||||
"top_anomalies": analysis.get("anomalies", [])[:5],
|
"top_anomalies": analysis.get("anomalies", [])[:5],
|
||||||
"top_recommendations": analysis.get("recommendations", [])[:5],
|
"top_recommendations": analysis.get("recommendations", [])[:5],
|
||||||
"block_candidates": analysis.get("block_candidates", [])[:5],
|
"block_candidates": analysis.get("block_candidates", [])[:5],
|
||||||
"policy_findings": analysis.get("policy_findings", [])[:5],
|
"policy_findings": analysis.get("policy_findings", [])[:5],
|
||||||
"event_context": analysis.get("event_context", {}),
|
"event_context": _compact_event_context(analysis.get("event_context", {})),
|
||||||
"diagnostics": analysis.get("diagnostics", {}),
|
"diagnostics": analysis.get("diagnostics", {}),
|
||||||
"capabilities": analysis.get("capabilities", {}),
|
"capabilities": _compact_mcp_capabilities(analysis.get("capabilities", {})),
|
||||||
"cross_source_correlations": analysis.get("cross_source_correlations", [])[:20],
|
"cross_source_correlations": analysis.get("cross_source_correlations", [])[:10],
|
||||||
"incidents": analysis.get("incidents", [])[:10],
|
"incidents": analysis.get("incidents", [])[:8],
|
||||||
"profile_suggestions": analysis.get("profile_suggestions", [])[:10],
|
"profile_suggestions": [
|
||||||
"field_deviations": analysis.get("field_deviations", {}),
|
_compact_profile_suggestion_for_dashboard(item)
|
||||||
"feedback": analysis.get("feedback", []),
|
for item in analysis.get("profile_suggestions", [])[:8]
|
||||||
|
],
|
||||||
|
"field_deviations": _compact_field_deviations(analysis.get("field_deviations", {})),
|
||||||
|
"feedback": analysis.get("feedback", [])[:10] if isinstance(analysis.get("feedback"), list) else [],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def ollama_dashboard_assessment(analysis: dict[str, object], model: str | None = None, timeout: int | None = None) -> str:
|
||||||
|
compact = compact_dashboard_analysis(analysis)
|
||||||
return ollama_summary(
|
return ollama_summary(
|
||||||
[],
|
[],
|
||||||
[],
|
[],
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from fgai.llm import _json_object_from_text
|
from fgai.llm import _json_object_from_text, compact_dashboard_analysis
|
||||||
|
|
||||||
|
|
||||||
class LlmTests(unittest.TestCase):
|
class LlmTests(unittest.TestCase):
|
||||||
@@ -18,6 +18,45 @@ class LlmTests(unittest.TestCase):
|
|||||||
|
|
||||||
self.assertEqual(payload["profiles"][0]["stream_id"], "firewall")
|
self.assertEqual(payload["profiles"][0]["stream_id"], "firewall")
|
||||||
|
|
||||||
|
def test_dashboard_compaction_removes_heavy_evidence_payloads(self):
|
||||||
|
status = {
|
||||||
|
"summary": {"total": 1000},
|
||||||
|
"capabilities": {
|
||||||
|
"graylog_mcp": {
|
||||||
|
"status": "connected",
|
||||||
|
"streams": [{"stream_id": f"stream-{index}", "events": index} for index in range(100)],
|
||||||
|
"aggregate_events": 1000,
|
||||||
|
"raw_events_fetched": 50,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"event_context": {
|
||||||
|
"source_profiles": [{"entity": f"10.0.0.{index}"} for index in range(20)],
|
||||||
|
"related_activity": [{"entity": f"10.0.0.{index}"} for index in range(40)],
|
||||||
|
},
|
||||||
|
"field_deviations": {
|
||||||
|
"alice": [
|
||||||
|
{
|
||||||
|
"stream_name": "Windows",
|
||||||
|
"detector": "new_relationship",
|
||||||
|
"field": "relationship:username->srcip",
|
||||||
|
"score": 90,
|
||||||
|
"reason": "new srcip value",
|
||||||
|
"sample_events": [{"message": "very large raw event", "graylog_query": "username:alice"}],
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
compact = compact_dashboard_analysis(status)
|
||||||
|
|
||||||
|
self.assertEqual(compact["capabilities"]["graylog_mcp"]["status"], "connected")
|
||||||
|
self.assertNotIn("streams", compact["capabilities"]["graylog_mcp"])
|
||||||
|
self.assertEqual(len(compact["event_context"]["source_profiles"]), 10)
|
||||||
|
self.assertEqual(len(compact["event_context"]["related_activity"]), 20)
|
||||||
|
self.assertEqual(compact["field_deviations"][0]["entity"], "alice")
|
||||||
|
self.assertNotIn("sample_events", compact["field_deviations"][0])
|
||||||
|
self.assertNotIn("graylog_query", str(compact))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|||||||
Reference in New Issue
Block a user