adeem turky commited on
Commit
e4e2691
·
verified ·
1 Parent(s): 32f9ac4

Upload 4 files

Browse files
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ Assigment[[:space:]]Reflection.pdf filter=lfs diff=lfs merge=lfs -text
Assigment Reflection.pdf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:b75a3e0ee0412fe45b3640911cab75be84f69ba7f7bc325fec366289328615eb
3
+ size 1060397
app-2.py ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Flask, render_template, request, Response, stream_with_context
2
+ import json
3
+ import time
4
+ from workflow_orchestrator import WorkflowOrchestrator
5
+
6
+ app = Flask(__name__)
7
+
8
+ # ==================== CONFIGURATION ====================
9
+ COHERE_API_KEY = "KEY"
10
+
11
+ # ==================== FLASK ROUTES ====================
12
+
13
+ @app.route('/')
14
+ def index():
15
+ return render_template('index.html')
16
+
17
+ @app.route('/stream_workflow')
18
+ def stream_workflow():
19
+ task = request.args.get('task', 'Search about KSA Vision 2030')
20
+
21
+ def generate():
22
+ orchestrator = WorkflowOrchestrator(COHERE_API_KEY)
23
+
24
+ def on_event(event_type, payload):
25
+ data = {
26
+ 'type': event_type,
27
+ }
28
+
29
+ if isinstance(payload, dict):
30
+ data.update(payload)
31
+
32
+ # 1. Start
33
+ yield f"data: {json.dumps({'type': 'status', 'node': 'start', 'msg': 'Connecting to Neural Network...'})}\n\n"
34
+
35
+ yield f"data: {json.dumps({'type': 'activate', 'node': 'planner', 'msg': 'Planner: Analyzing complexity...'})}\n\n"
36
+ plan = orchestrator.agents["planner"].execute(task, {})
37
+ steps = plan['steps']
38
+ yield f"data: {json.dumps({'type': 'log', 'msg': f'Strategized {len(steps)} execution steps based on real analysis.', 'role': 'planner'})}\n\n"
39
+
40
+ accumulated_data = ""
41
+
42
+ # 2. Execution Loop
43
+ for i, step in enumerate(steps):
44
+ yield f"data: {json.dumps({'type': 'activate', 'node': 'executor', 'msg': f'Researching: {step}'})}\n\n"
45
+
46
+ exec_res = orchestrator.agents["executor"].execute(step, {})
47
+ accumulated_data += f"\nSection {i+1}: {step}\n{exec_res['output']}\n"
48
+
49
+ yield f"data: {json.dumps({'type': 'activate', 'node': 'validator', 'msg': 'Verifying sources...'})}\n\n"
50
+ val_res = orchestrator.agents["validator"].execute(exec_res, {})
51
+
52
+ yield f"data: {json.dumps({'type': 'activate', 'node': 'decision', 'msg': 'Quality Gate'})}\n\n"
53
+
54
+ if val_res['is_valid']:
55
+ citations_count = len(exec_res.get('citations', []))
56
+ yield f"data: {json.dumps({'type': 'log', 'msg': f'✅ Validated with {citations_count} citations.', 'role': 'success'})}\n\n"
57
+ else:
58
+ yield f"data: {json.dumps({'type': 'log', 'msg': f'⚠️ Low confidence data.', 'role': 'warning'})}\n\n"
59
+
60
+ time.sleep(0.5)
61
+
62
+ # 3. Final Report
63
+ yield f"data: {json.dumps({'type': 'activate', 'node': 'end', 'msg': 'Generating Final Report...'})}\n\n"
64
+
65
+ final_prompt = f"""
66
+ You are an AI analyst. The user asked: "{task}".
67
+ Based on the following research data, write a comprehensive executive summary in Markdown:
68
+
69
+ {accumulated_data}
70
+ """
71
+ final_report = orchestrator.co.chat(message=final_prompt, model="command-a-03-2025", temperature=0.3).text
72
+
73
+ yield f"data: {json.dumps({'type': 'finish', 'report': final_report})}\n\n"
74
+
75
+ return Response(stream_with_context(generate()), mimetype='text/event-stream')
76
+
77
+ if __name__ == '__main__':
78
+ app.run(debug=True, port=5000)
templates/index.html ADDED
@@ -0,0 +1,274 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="ar" dir="ltr">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>AISA Workflow Engine</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <style>
9
+ /* Custom Animations & Glows */
10
+ @import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700&display=swap');
11
+
12
+ body { font-family: 'JetBrains Mono', monospace; background-color: #0B1120; }
13
+
14
+ .node {
15
+ transition: all 0.4s ease;
16
+ box-shadow: 0 0 10px rgba(0,0,0,0.5);
17
+ }
18
+
19
+ .node.active {
20
+ border-color: #38bdf8;
21
+ box-shadow: 0 0 20px #38bdf8, inset 0 0 10px rgba(56, 189, 248, 0.2);
22
+ transform: scale(1.05);
23
+ color: #fff;
24
+ }
25
+
26
+ .connector {
27
+ width: 2px;
28
+ background-color: #334155;
29
+ transition: background-color 0.3s;
30
+ }
31
+ .connector.active {
32
+ background-color: #38bdf8;
33
+ box-shadow: 0 0 8px #38bdf8;
34
+ }
35
+
36
+ /* Scrollbar Styling */
37
+ ::-webkit-scrollbar { width: 8px; }
38
+ ::-webkit-scrollbar-track { background: #1e293b; }
39
+ ::-webkit-scrollbar-thumb { background: #475569; border-radius: 4px; }
40
+ ::-webkit-scrollbar-thumb:hover { background: #64748b; }
41
+ </style>
42
+ </head>
43
+ <body class="text-slate-200 h-screen flex flex-col overflow-hidden">
44
+
45
+ <header class="bg-slate-900 border-b border-slate-800 p-4 flex justify-between items-center shadow-lg z-10">
46
+ <div class="flex items-center gap-3">
47
+ <div class="w-3 h-3 rounded-full bg-blue-500 animate-pulse"></div>
48
+ <h1 class="text-xl font-bold bg-clip-text text-transparent bg-gradient-to-r from-blue-400 to-cyan-300">
49
+ AISA Orchestrator
50
+ </h1>
51
+ </div>
52
+ <div id="status-badge" class="px-3 py-1 text-xs rounded-full bg-slate-800 text-slate-400 border border-slate-700">
53
+ System Idle
54
+ </div>
55
+ </header>
56
+
57
+ <main class="flex flex-1 relative">
58
+
59
+ <div class="w-1/2 flex flex-col items-center justify-center bg-slate-900/50 p-10 relative">
60
+ <div class="absolute inset-0 bg-[url('https://www.transparenttextures.com/patterns/carbon-fibre.png')] opacity-10 pointer-events-none"></div>
61
+
62
+ <div id="node-start" class="node w-32 h-12 rounded-full border-2 border-slate-600 bg-slate-800 flex items-center justify-center text-sm mb-2 z-10">
63
+ Start
64
+ </div>
65
+ <div class="connector h-8 mb-2"></div>
66
+
67
+ <div id="node-planner" class="node w-48 h-16 rounded-lg border-2 border-slate-600 bg-slate-800 flex items-center justify-center gap-2 mb-2 z-10">
68
+ <span>🧠</span> Planner
69
+ </div>
70
+ <div class="connector h-8 mb-2"></div>
71
+
72
+ <div class="p-6 rounded-xl border border-dashed border-slate-700 bg-slate-800/30 flex flex-col items-center w-full max-w-sm relative">
73
+ <span class="absolute -top-3 left-4 bg-slate-900 px-2 text-xs text-slate-500">Execution Cycle</span>
74
+
75
+ <div id="node-executor" class="node w-48 h-16 rounded-lg border-2 border-slate-600 bg-slate-800 flex items-center justify-center gap-2 mb-4 z-10">
76
+ <span>🔨</span> Executor
77
+ </div>
78
+
79
+ <div class="connector h-6 mb-4"></div>
80
+
81
+ <div id="node-validator" class="node w-48 h-16 rounded-lg border-2 border-slate-600 bg-slate-800 flex items-center justify-center gap-2 mb-4 z-10">
82
+ <span>🛡️</span> Validator
83
+ </div>
84
+
85
+ <div class="connector h-6 mb-4"></div>
86
+
87
+ <div id="node-decision" class="node w-12 h-12 rotate-45 border-2 border-slate-600 bg-slate-800 flex items-center justify-center z-10">
88
+ <span class="-rotate-45 text-xs">⚖️</span>
89
+ </div>
90
+ </div>
91
+
92
+ <div class="connector h-8 mt-2 mb-2"></div>
93
+
94
+ <div id="node-end" class="node w-32 h-12 rounded-full border-2 border-slate-600 bg-slate-800 flex items-center justify-center text-sm z-10">
95
+ 🚀 Output
96
+ </div>
97
+ </div>
98
+
99
+ <div class="w-1/2 flex flex-col border-l border-slate-800 bg-slate-900 overflow-y-auto custom-scrollbar">
100
+
101
+ <div class="p-6 space-y-6">
102
+ <div>
103
+ <label class="block text-xs text-blue-400 mb-2 font-bold uppercase tracking-wider">Mission Objective</label>
104
+ <div class="flex gap-2">
105
+ <input type="text" id="taskInput" value="Search about KSA Vision 2030"
106
+ class="flex-1 bg-slate-800 border border-slate-700 rounded-lg px-4 py-3 focus:outline-none focus:border-blue-500 text-sm transition">
107
+ <button onclick="startWorkflow()" id="startBtn"
108
+ class="bg-blue-600 hover:bg-blue-500 text-white px-6 py-3 rounded-lg font-bold transition shadow-lg shadow-blue-900/50 flex items-center gap-2">
109
+ <span>RUN</span>
110
+ </button>
111
+ </div>
112
+ </div>
113
+
114
+ <div class="flex flex-col bg-slate-950 rounded-lg border border-slate-800 overflow-hidden shadow-inner h-80 shrink-0">
115
+ <div class="bg-slate-900 px-4 py-2 border-b border-slate-800 flex justify-between items-center">
116
+ <span class="text-xs text-slate-400">System Logs</span>
117
+ <div class="flex gap-1">
118
+ <span class="w-2 h-2 rounded-full bg-red-500"></span>
119
+ <span class="w-2 h-2 rounded-full bg-yellow-500"></span>
120
+ <span class="w-2 h-2 rounded-full bg-green-500"></span>
121
+ </div>
122
+ </div>
123
+ <div id="logs" class="flex-1 p-4 overflow-y-auto font-mono text-sm space-y-2 scroll-smooth">
124
+ <div class="text-slate-600 italic">> System ready. Waiting for input...</div>
125
+ </div>
126
+ </div>
127
+
128
+ <div id="resultBox" class="hidden bg-slate-800/80 border border-green-500/30 rounded-lg p-6 shadow-lg shadow-green-900/20 backdrop-blur-sm flex flex-col max-h-[60vh]">
129
+ <h3 class="text-green-400 font-bold text-lg mb-4 flex items-center gap-2 border-b border-slate-700 pb-2 shrink-0">
130
+ <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>
131
+ Final Report Generated
132
+ </h3>
133
+
134
+ <div class="overflow-y-auto custom-scrollbar pr-2">
135
+ <div id="finalReport" class="text-sm text-slate-300 whitespace-pre-wrap font-sans leading-relaxed"></div>
136
+ </div>
137
+ </div>
138
+
139
+ <div class="h-10"></div>
140
+ </div>
141
+ </div>
142
+ </main>
143
+
144
+ <script>
145
+ function startWorkflow() {
146
+ const task = document.getElementById('taskInput').value;
147
+ const btn = document.getElementById('startBtn');
148
+ const logs = document.getElementById('logs');
149
+ const resultBox = document.getElementById('resultBox');
150
+
151
+ // 1. Reset UI
152
+ logs.innerHTML = '';
153
+ resultBox.classList.add('hidden');
154
+ document.getElementById('finalReport').innerHTML = '';
155
+
156
+ // Disable Button
157
+ btn.disabled = true;
158
+ btn.classList.add('opacity-50', 'cursor-not-allowed');
159
+ btn.innerHTML = '<span>⏳ Processing...</span>';
160
+ resetNodes();
161
+
162
+ // 2. Connect to Stream
163
+ const eventSource = new EventSource(`/stream_workflow?task=${encodeURIComponent(task)}`);
164
+
165
+ // Helper to clean up state
166
+ function stopExecution(success = true) {
167
+ eventSource.close();
168
+ btn.disabled = false;
169
+ btn.classList.remove('opacity-50', 'cursor-not-allowed');
170
+ btn.innerHTML = '<span>RUN</span>';
171
+
172
+ if (success) {
173
+ updateStatus("Mission Completed");
174
+ document.getElementById('node-end').classList.add('active');
175
+ } else {
176
+ updateStatus("Stopped / Error");
177
+ }
178
+ }
179
+
180
+ eventSource.onmessage = function(e) {
181
+ let data;
182
+ try {
183
+ data = JSON.parse(e.data);
184
+ } catch (err) {
185
+ console.error("Parse Error", err);
186
+ return;
187
+ }
188
+
189
+ if (data.type === 'activate') {
190
+ highlightNode(data.node);
191
+ addLog(data.msg, 'info');
192
+ updateStatus(data.msg);
193
+ }
194
+ else if (data.type === 'log') {
195
+ addLog(data.msg, data.role);
196
+ }
197
+ else if (data.type === 'retry_animation') {
198
+ flashDecisionNode();
199
+ }
200
+ else if (data.type === 'finish') {
201
+ // Format Markdown
202
+ let formattedReport = data.report
203
+ .replace(/### (.*?)\n/g, '<h3 class="text-blue-400 font-bold text-lg mt-6 mb-2 border-b border-slate-700 pb-1">$1</h3>')
204
+ .replace(/\*\*(.*?)\*\*/g, '<strong class="text-white font-semibold">$1</strong>')
205
+ .replace(/- (.*?)\n/g, '<li class="ml-4 text-slate-300 list-disc marker:text-blue-500 mb-1">$1</li>')
206
+ .replace(/\n/g, '<br>');
207
+
208
+ document.getElementById('finalReport').innerHTML = formattedReport;
209
+ resultBox.classList.remove('hidden');
210
+ addLog("Workflow Finished Successfully.", "success");
211
+
212
+ stopExecution(true); // Close safely
213
+ }
214
+ };
215
+
216
+ eventSource.onerror = function() {
217
+ addLog("Stream connection closed or interrupted.", "warning");
218
+ stopExecution(false); // Force button enable
219
+ };
220
+ }
221
+
222
+ // --- Helper Functions ---
223
+
224
+ function highlightNode(nodeId) {
225
+ document.querySelectorAll('.node').forEach(n => n.classList.remove('active'));
226
+ document.querySelectorAll('.connector').forEach(c => c.classList.remove('active'));
227
+
228
+ const el = document.getElementById(`node-${nodeId}`);
229
+ if (el) {
230
+ el.classList.add('active');
231
+ if (el.previousElementSibling && el.previousElementSibling.classList.contains('connector')) {
232
+ el.previousElementSibling.classList.add('active');
233
+ }
234
+ }
235
+ }
236
+
237
+ function resetNodes() {
238
+ document.querySelectorAll('.node').forEach(n => n.classList.remove('active'));
239
+ document.querySelectorAll('.connector').forEach(c => c.classList.remove('active'));
240
+ }
241
+
242
+ function flashDecisionNode() {
243
+ const decision = document.getElementById('node-decision');
244
+ decision.style.borderColor = '#ef4444';
245
+ decision.style.boxShadow = '0 0 15px #ef4444';
246
+ setTimeout(() => {
247
+ decision.style.borderColor = '';
248
+ decision.style.boxShadow = '';
249
+ }, 500);
250
+ }
251
+
252
+ function updateStatus(text) {
253
+ const badge = document.getElementById('status-badge');
254
+ badge.textContent = text;
255
+ badge.classList.remove('text-slate-400');
256
+ badge.classList.add('text-blue-400', 'border-blue-500');
257
+ }
258
+
259
+ function addLog(msg, type) {
260
+ const logs = document.getElementById('logs');
261
+ const div = document.createElement('div');
262
+ div.className = 'border-l-2 pl-2 text-xs py-1 animate-fade-in';
263
+
264
+ if (type === 'error') { div.classList.add('border-red-500', 'text-red-400'); }
265
+ else if (type === 'success') { div.classList.add('border-green-500', 'text-green-400'); }
266
+ else if (type === 'warning') { div.classList.add('border-yellow-500', 'text-yellow-400'); }
267
+ else { div.classList.add('border-blue-500', 'text-slate-300'); }
268
+
269
+ div.innerHTML = `> ${msg}`;
270
+ logs.appendChild(div);
271
+ logs.scrollTop = logs.scrollHeight;
272
+ }
273
+ </script>
274
+ </body>
workflow_orchestrator.py ADDED
@@ -0,0 +1,191 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Dict, List, Any, Optional, Callable
2
+ from dataclasses import dataclass, field
3
+ from enum import Enum
4
+ from datetime import datetime
5
+ import time
6
+ import cohere
7
+
8
+ # ==================== AISA: State Coordination Layer ====================
9
+ class WorkflowStatus(Enum):
10
+ PENDING = "pending"
11
+ IN_PROGRESS = "in_progress"
12
+ COMPLETED = "completed"
13
+ FAILED = "failed"
14
+
15
+ @dataclass
16
+ class WorkflowState:
17
+ workflow_id: str
18
+ task: str
19
+ status: WorkflowStatus
20
+ current_step: int = 0
21
+ total_steps: int = 0
22
+ steps_completed: List[str] = field(default_factory=list)
23
+ step_results: Dict[str, Any] = field(default_factory=dict)
24
+ execution_log: List[Dict[str, Any]] = field(default_factory=list)
25
+ start_time: Optional[datetime] = None
26
+ end_time: Optional[datetime] = None
27
+
28
+ def log_event(self, event_type: str, message: str, data: Optional[Dict] = None):
29
+ if len(self.execution_log) >= 500:
30
+ self.execution_log = self.execution_log[-400:]
31
+ self.execution_log.append({
32
+ "timestamp": datetime.now().isoformat(),
33
+ "event_type": event_type,
34
+ "message": message,
35
+ "data": data or {}
36
+ })
37
+
38
+ def get_execution_time(self) -> Optional[str]:
39
+ if self.start_time and self.end_time:
40
+ duration = self.end_time - self.start_time
41
+ seconds = duration.total_seconds()
42
+ return f"{seconds:.1f}s" if seconds < 60 else f"{seconds/60:.1f}m"
43
+ return None
44
+
45
+ # ==================== AISA: Cognitive Agent Layer ====================
46
+ class BaseAgent:
47
+ def __init__(self, name: str, cohere_client=None):
48
+ self.name = name
49
+ self.agent_id = f"{name}_{id(self)}"
50
+ self.co = cohere_client
51
+
52
+ def execute(self, input_data: Any, context: Dict[str, Any]) -> Dict[str, Any]:
53
+ raise NotImplementedError
54
+
55
+ class PlannerAgent(BaseAgent):
56
+ def execute(self, task: str, context: Dict[str, Any]) -> Dict[str, Any]:
57
+ prompt = f"""You are a Strategic Planner Agent. Break down this task: "{task}"
58
+ into 3 to 4 sequential, actionable search/analysis steps.
59
+ Format: Return ONLY the steps separated by newlines."""
60
+
61
+ try:
62
+ response = self.co.chat(message=prompt, temperature=0.3)
63
+ steps = [s.strip('- ').strip() for s in response.text.split('\n') if s.strip()]
64
+ complexity = "high" if len(steps) > 3 else "medium"
65
+ except:
66
+ # Fallback if API fails
67
+ steps = ["Research the topic overview", "Analyze key trends and data", "Synthesize findings"]
68
+ complexity = "medium"
69
+
70
+ return {
71
+ "steps": steps,
72
+ "complexity": complexity,
73
+ "output_type": "plan"
74
+ }
75
+
76
+ class ExecutorAgent(BaseAgent):
77
+ def execute(self, step: str, context: Dict[str, Any]) -> Dict[str, Any]:
78
+ try:
79
+ response = self.co.chat(
80
+ message=f"Perform this task in detail: {step}",
81
+ connectors=[{"id": "web-search"}],
82
+ temperature=0.3
83
+ )
84
+
85
+ output_text = response.text
86
+ has_citations = hasattr(response, 'citations') and len(response.citations) > 0
87
+ confidence = 0.95 if has_citations else 0.75
88
+
89
+ return {
90
+ "status": "success",
91
+ "output": output_text,
92
+ "confidence": confidence,
93
+ "citations": [c for c in response.citations] if has_citations else []
94
+ }
95
+ except Exception as e:
96
+ return {
97
+ "status": "failed",
98
+ "output": str(e),
99
+ "confidence": 0.0
100
+ }
101
+
102
+ class ValidatorAgent(BaseAgent):
103
+ def execute(self, result: Dict[str, Any], context: Dict[str, Any]) -> Dict[str, Any]:
104
+ output_len = len(result.get("output", ""))
105
+ base_conf = result.get("confidence", 0.5)
106
+
107
+ is_valid = output_len > 50 and base_conf > 0.6
108
+
109
+ if is_valid:
110
+ feedback = "Content verified successfully."
111
+ else:
112
+ feedback = "Content too short or lacks citations."
113
+
114
+ return {
115
+ "is_valid": is_valid,
116
+ "confidence": base_conf,
117
+ "feedback": feedback
118
+ }
119
+
120
+ # ==================== AISA: Agentic Infrastructure Layer ====================
121
+ class WorkflowOrchestrator:
122
+ def __init__(self, api_key: str):
123
+ self.co = cohere.Client(api_key)
124
+ self.agents = {
125
+ "planner": PlannerAgent("Planner", self.co),
126
+ "executor": ExecutorAgent("Executor", self.co),
127
+ "validator": ValidatorAgent("Validator", self.co)
128
+ }
129
+
130
+ def execute_workflow(self, task: str, event_callback: Callable[[str, Dict], None]):
131
+ workflow_id = f"wf_{int(time.time())}"
132
+ state = WorkflowState(workflow_id, task, WorkflowStatus.PENDING)
133
+
134
+ # Helper to send events to UI
135
+ def emit(type_, msg, role='info', node=None):
136
+ event_callback(type_, {"msg": msg, "role": role, "node": node})
137
+
138
+ try:
139
+ emit('status', 'System Initialized.', node='start')
140
+ state.start_time = datetime.now()
141
+
142
+ # 1. Planning
143
+ emit('activate', 'Analyzing Task Strategy...', node='planner')
144
+ plan = self.agents["planner"].execute(task, {})
145
+ steps = plan['steps']
146
+ state.total_steps = len(steps)
147
+
148
+ emit('log', f"Strategy formed with {len(steps)} phases.", role='planner')
149
+ time.sleep(1)
150
+
151
+ # 2. Execution Loop
152
+ accumulated_report = []
153
+
154
+ for i, step in enumerate(steps):
155
+ emit('activate', f"Executing: {step}", node='executor')
156
+
157
+ # Execution (Real Search)
158
+ exec_res = self.agents["executor"].execute(step, {})
159
+
160
+ if exec_res['status'] == 'failed':
161
+ emit('log', f"⚠️ Step failed: {exec_res['output']}", role='error')
162
+ continue
163
+
164
+ # Validation
165
+ emit('activate', 'Verifying Data Integrity...', node='validator')
166
+ val_res = self.agents["validator"].execute(exec_res, {})
167
+
168
+ emit('activate', 'Quality Gate Decision', node='decision')
169
+ time.sleep(0.5)
170
+
171
+ if val_res['is_valid']:
172
+ emit('log', f"✅ Phase {i+1} Verified (Confidence: {exec_res['confidence']:.0%})", role='success')
173
+ accumulated_report.append(f"### {step}\n{exec_res['output']}\n")
174
+ else:
175
+ emit('log', f"⚠️ Quality Warning: {val_res['feedback']}", role='warning')
176
+ accumulated_report.append(f"### {step}\n{exec_res['output']}\n")
177
+
178
+ # 3. Final Generation
179
+ emit('activate', 'Synthesizing Final Intelligence Report...', node='end')
180
+
181
+ full_context = "\n".join(accumulated_report)
182
+ final_prompt = f"""Based on the following research segments about '{task}', write a cohesive, professional markdown report:\n\n{full_context}"""
183
+
184
+ final_response = self.co.chat(message=final_prompt, model="command-r", temperature=0.3)
185
+
186
+ emit('finish', {'report': final_response.text})
187
+ state.status = WorkflowStatus.COMPLETED
188
+
189
+ except Exception as e:
190
+ emit('log', f"Critical System Failure: {str(e)}", role='error')
191
+ state.status = WorkflowStatus.FAILED