sdawdsdw commited on
Commit
e547cb9
·
verified ·
1 Parent(s): ef3ae47

Update graph_engine.py

Browse files
Files changed (1) hide show
  1. graph_engine.py +55 -24
graph_engine.py CHANGED
@@ -1,18 +1,19 @@
1
  #!/usr/bin/env python
2
  # coding: utf-8
3
 
4
- import numpy as np
5
- import hashlib
6
  import json
 
 
7
  from typing import List, Dict, Any, Optional
 
8
 
9
  class Node:
10
  def __init__(self, node_id: str, node_type: str, text: str,
11
  expected_keys: List[str] = None, runs: List[str] = None,
12
  execution_time_ms: float = None):
13
  self.id = node_id
14
- self.type = node_type
15
- self.text = text
16
  self.embedding = None
17
  self.is_valid_format = True
18
  self.validation_error = None
@@ -21,6 +22,7 @@ class Node:
21
  self.consistency_score = 100.0
22
  self.instability_index = 0.0
23
  self.execution_time_ms = execution_time_ms
 
24
 
25
  def get_signature(self) -> str:
26
  content = f"{self.type}:{self.text.strip().lower()}"
@@ -50,9 +52,10 @@ class TrajectoryGraph:
50
  edge = Edge(from_node_id, to_node_id)
51
  self.edges.append(edge)
52
  else:
53
- raise ValueError('Both nodes must exist in the graph before creating an edge.')
54
 
55
  def validate_tool_calls(self):
 
56
  for node in self.nodes.values():
57
  if node.type == 'tool':
58
  stripped_text = node.text.strip()
@@ -61,6 +64,13 @@ class TrajectoryGraph:
61
  node.validation_error = "Empty tool execution response"
62
  continue
63
 
 
 
 
 
 
 
 
64
  if stripped_text.startswith('{') or stripped_text.startswith('['):
65
  try:
66
  data = json.loads(stripped_text)
@@ -69,35 +79,53 @@ class TrajectoryGraph:
69
  missing_keys = [key for key in node.expected_keys if key not in data]
70
  if missing_keys:
71
  node.is_valid_format = False
72
- node.validation_error = f"Missing required fields: {missing_keys}"
73
  except json.JSONDecodeError as e:
74
  node.is_valid_format = False
75
- node.validation_error = f'Malformed JSON structure: {str(e)}'
76
  else:
77
  node.is_valid_format = True
78
 
79
  def detect_trajectory_cycles(self) -> List[Dict[str, Any]]:
 
80
  detected_loops = []
81
- seen_signatures = {}
 
82
 
83
- for node in self.nodes.values():
84
- sig = node.get_signature()
85
- if sig in seen_signatures:
86
- prev_node = seen_signatures[sig]
 
87
  detected_loops.append({
88
  'failure_type': 'INFINITE_EXECUTION_LOOP',
89
- 'from_node': prev_node.id,
90
- 'to_node': node.id,
91
- 'reason': f"Infinite Loop: Node [{node.id}] repeated execution pattern seen at [{prev_node.id}].",
92
- 'details': f"Duplicate action: '{node.text[:80]}...'"
93
  })
94
- else:
95
- seen_signatures[sig] = node
96
 
97
  return detected_loops
98
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
  def calculate_node_consistency(self, get_embedding_func):
100
- from sklearn.metrics.pairwise import cosine_similarity
101
  for node in self.nodes.values():
102
  if node.runs and len(node.runs) >= 2:
103
  embeddings = [get_embedding_func(run) for run in node.runs]
@@ -107,11 +135,12 @@ class TrajectoryGraph:
107
  sim = cosine_similarity([embeddings[i]], [embeddings[j]])[0][0]
108
  similarities.append(float(sim))
109
 
110
- avg_sim = float(np.mean(similarities))
111
- node.consistency_score = avg_sim * 100.0
112
- node.instability_index = 100.0 - node.consistency_score
113
 
114
  def calculate_drift_scores(self):
 
115
  for edge in self.edges:
116
  node_from = self.nodes[edge.from_node_id]
117
  node_to = self.nodes[edge.to_node_id]
@@ -121,5 +150,7 @@ class TrajectoryGraph:
121
  norm_a = np.linalg.norm(node_from.embedding)
122
  norm_b = np.linalg.norm(node_to.embedding)
123
  sim = dot / (norm_a * norm_b) if (norm_a * norm_b) > 0 else 0.0
124
- similarity_percentage = float(sim) * 100.0
125
- edge.drift_score = 100.0 - similarity_percentage
 
 
 
1
  #!/usr/bin/env python
2
  # coding: utf-8
3
 
 
 
4
  import json
5
+ import hashlib
6
+ import numpy as np
7
  from typing import List, Dict, Any, Optional
8
+ from sklearn.metrics.pairwise import cosine_similarity
9
 
10
  class Node:
11
  def __init__(self, node_id: str, node_type: str, text: str,
12
  expected_keys: List[str] = None, runs: List[str] = None,
13
  execution_time_ms: float = None):
14
  self.id = node_id
15
+ self.type = node_type.lower()
16
+ self.text = str(text or "")
17
  self.embedding = None
18
  self.is_valid_format = True
19
  self.validation_error = None
 
22
  self.consistency_score = 100.0
23
  self.instability_index = 0.0
24
  self.execution_time_ms = execution_time_ms
25
+ self.token_count = max(1, len(self.text.split()))
26
 
27
  def get_signature(self) -> str:
28
  content = f"{self.type}:{self.text.strip().lower()}"
 
52
  edge = Edge(from_node_id, to_node_id)
53
  self.edges.append(edge)
54
  else:
55
+ raise ValueError(f"Both nodes [{from_node_id}, {to_node_id}] must exist before creating an edge.")
56
 
57
  def validate_tool_calls(self):
58
+ """Valideaza payload-urile JSON si detecteaza erori de executie ale uneltelor."""
59
  for node in self.nodes.values():
60
  if node.type == 'tool':
61
  stripped_text = node.text.strip()
 
64
  node.validation_error = "Empty tool execution response"
65
  continue
66
 
67
+ # Verificare erori standard de sistem
68
+ error_signatures = ["traceback (most recent call last)", "error:", "exception:", "unauthorized", "timed out"]
69
+ if any(sig in stripped_text.lower() for sig in error_signatures):
70
+ node.is_valid_format = False
71
+ node.validation_error = f"Tool Execution Failure: {stripped_text[:100]}"
72
+ continue
73
+
74
  if stripped_text.startswith('{') or stripped_text.startswith('['):
75
  try:
76
  data = json.loads(stripped_text)
 
79
  missing_keys = [key for key in node.expected_keys if key not in data]
80
  if missing_keys:
81
  node.is_valid_format = False
82
+ node.validation_error = f"Missing required fields in tool output: {missing_keys}"
83
  except json.JSONDecodeError as e:
84
  node.is_valid_format = False
85
+ node.validation_error = f"Malformed JSON structure: {str(e)}"
86
  else:
87
  node.is_valid_format = True
88
 
89
  def detect_trajectory_cycles(self) -> List[Dict[str, Any]]:
90
+ """Detecteaza cicluri repetitive de tip Ping-Pong sau bucle infinite consecutive."""
91
  detected_loops = []
92
+ if len(self.edges) < 2:
93
+ return detected_loops
94
 
95
+ # Cautam secvente consecutive repetitive: A -> B urmat din nou de A -> B
96
+ transition_history = []
97
+ for edge in self.edges:
98
+ transition = f"{self.nodes[edge.from_node_id].get_signature()}->{self.nodes[edge.to_node_id].get_signature()}"
99
+ if transition in transition_history:
100
  detected_loops.append({
101
  'failure_type': 'INFINITE_EXECUTION_LOOP',
102
+ 'from_node': edge.from_node_id,
103
+ 'to_node': edge.to_node_id,
104
+ 'reason': f"Infinite State Cycle: Repeated transition pattern detected between [{edge.from_node_id}] and [{edge.to_node_id}].",
105
+ 'details': f"Cycle Signature: {transition[:16]}..."
106
  })
107
+ transition_history.append(transition)
 
108
 
109
  return detected_loops
110
 
111
+ def detect_stagnation(self, min_drift_threshold: float = 1.5) -> List[Dict[str, Any]]:
112
+ """Detecteaza daca agentul bate pasul pe loc fara progres semantic intre unelte."""
113
+ stagnations = []
114
+ for edge in self.edges:
115
+ n_from = self.nodes[edge.from_node_id]
116
+ n_to = self.nodes[edge.to_node_id]
117
+ if n_from.type in ['tool', 'thought'] and n_to.type in ['tool', 'thought']:
118
+ if edge.drift_score < min_drift_threshold:
119
+ stagnations.append({
120
+ 'failure_type': 'AGENT_STAGNATION',
121
+ 'from_node': edge.from_node_id,
122
+ 'to_node': edge.to_node_id,
123
+ 'reason': f"Semantic Stagnation: Minimal cognitive drift ({edge.drift_score:.2f}%) between consecutive tool steps. Redundant execution suspected."
124
+ })
125
+ return stagnations
126
+
127
  def calculate_node_consistency(self, get_embedding_func):
128
+ """Calculeaza stabilitatea nodului intre multiple rulari."""
129
  for node in self.nodes.values():
130
  if node.runs and len(node.runs) >= 2:
131
  embeddings = [get_embedding_func(run) for run in node.runs]
 
135
  sim = cosine_similarity([embeddings[i]], [embeddings[j]])[0][0]
136
  similarities.append(float(sim))
137
 
138
+ avg_sim = float(np.mean(similarities)) if similarities else 1.0
139
+ node.consistency_score = float(avg_sim * 100.0)
140
+ node.instability_index = float(100.0 - node.consistency_score)
141
 
142
  def calculate_drift_scores(self):
143
+ """Calculeaza si normalizeaza driftul semantic (0 - 100%) pe fiecare tranzitie."""
144
  for edge in self.edges:
145
  node_from = self.nodes[edge.from_node_id]
146
  node_to = self.nodes[edge.to_node_id]
 
150
  norm_a = np.linalg.norm(node_from.embedding)
151
  norm_b = np.linalg.norm(node_to.embedding)
152
  sim = dot / (norm_a * norm_b) if (norm_a * norm_b) > 0 else 0.0
153
+
154
+ # Normalizare sigura intre 0% si 100%
155
+ similarity_percentage = float(np.clip(sim, -1.0, 1.0)) * 100.0
156
+ edge.drift_score = float(np.clip(100.0 - similarity_percentage, 0.0, 100.0))