GitHub Copilot commited on
Commit
ad60bb2
·
1 Parent(s): d77237a

Sync: Deployed Dynamic Agent Onboarding logic to Space

Browse files
logos/agent_dispatcher.py CHANGED
@@ -347,6 +347,7 @@ class LogosSwarm:
347
  """
348
  Protocol 21: The Async Interference Bus.
349
  Agents pulse in parallel; the solution is the geometric intersection.
 
350
  """
351
  def __init__(self, base_url="http://localhost:1234/v1", model="google/gemma-3-4b"):
352
  self.connector = get_connector('local', base_url=base_url, model=model)
@@ -359,6 +360,39 @@ class LogosSwarm:
359
  self.oversight = DolphinOversight(self.state)
360
  self.metadata = GemmaMetadata()
361
  self.routing = RNJ1Routing(self.state, self.connector)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
362
 
363
  async def process(self, raw_input: str):
364
  print("\n🌊 [HOLOGRAPHIC BUS] PULSING WAVE FUNCTIONS...")
@@ -368,27 +402,41 @@ class LogosSwarm:
368
  user_packet = {"content": raw_input, "type": "text_command"}
369
  action, packet = self.oversight.ingest(user_packet)
370
 
371
- # 2. PARALLEL INTERFERENCE: Gemma (Semantic), RNJ-1 (Logic), and Gemini (Reasoning)
372
  # They "interfere" to find the true deterministic coordinate
373
  gemma_task = asyncio.create_task(self.metadata.process(packet))
374
  rnj1_task = asyncio.create_task(self.routing.calculate_position(packet))
375
 
376
- # reasoning_task: Pulse to LLM for high-level topological insights
377
  reasoning_task = asyncio.create_task(self.routing.connector.chat_async(
378
  f"Analyze this packet for topological resonance: {raw_input}",
379
  system_prompt="You are the SWARM_REASONER. Identify the high-level intent and potential manifold collisions."
380
  ))
381
-
382
- # Wait for the High-Frequency Triple Wave to align
 
 
 
 
 
 
 
 
383
  await asyncio.gather(gemma_task, rnj1_task, reasoning_task)
384
 
385
  gemma_status, mass = gemma_task.result()
386
  res = rnj1_task.result()
387
  reasoning, logprobs = reasoning_task.result()
388
 
389
- # Update Entropy Kill Switch with reasoning wave telemetry
 
 
 
 
 
 
 
390
  if logprobs:
391
- # logprobs from OpenAI usually contain 'content' if it's chat/completion
392
  log_content = logprobs.get('content', [])
393
  self.oversight.kill_switch.monitor_bulk(log_content)
394
 
@@ -472,15 +520,47 @@ class LogosSwarm:
472
  })
473
  except: pass
474
 
475
- ROUTER_PROMPT = """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
476
  Analyze the following user input and classify it into one of these categories:
477
  - 'fractal_architect': For code, debugging, storage, sharding, or technical implementation.
478
  - 'prime_navigator': For math, primes, geometry, calculations, or physics.
479
  - 'creative': For descriptions, stories, or documentation style.
480
  - 'general': For greetings or anything else.
 
481
 
482
  Reply with ONLY a JSON object in this format:
483
- {"intent": "category_name", "confidence": 0.9}
484
  Do not write any introductory text.
485
  """
486
 
 
347
  """
348
  Protocol 21: The Async Interference Bus.
349
  Agents pulse in parallel; the solution is the geometric intersection.
350
+ Protocol 26: Dynamic Agent Onboarding (Matroska Router Connection).
351
  """
352
  def __init__(self, base_url="http://localhost:1234/v1", model="google/gemma-3-4b"):
353
  self.connector = get_connector('local', base_url=base_url, model=model)
 
360
  self.oversight = DolphinOversight(self.state)
361
  self.metadata = GemmaMetadata()
362
  self.routing = RNJ1Routing(self.state, self.connector)
363
+
364
+ # Dynamic Agent Registry
365
+ self.agents: Dict[str, Any] = {}
366
+ self.discover_agents()
367
+
368
+ def discover_agents(self):
369
+ """
370
+ Onboards agents from logos/agents/ that implement BaseAgent.
371
+ """
372
+ import pkgutil
373
+ import importlib
374
+ import inspect
375
+ import logos.agents
376
+ from logos.agents.base_agent import BaseAgent
377
+
378
+ package = logos.agents
379
+ prefix = package.__name__ + "."
380
+
381
+ print("🔍 [SWARM] Scanning for Matroska Agents...")
382
+ for _, name, _ in pkgutil.iter_modules(package.__path__, prefix):
383
+ try:
384
+ module = importlib.import_module(name)
385
+ for name_cls, cls in inspect.getmembers(module, inspect.isclass):
386
+ if issubclass(cls, BaseAgent) and cls is not BaseAgent:
387
+ try:
388
+ instance = cls()
389
+ self.agents[instance.name.lower()] = instance
390
+ print(f" ✅ Onboarded: {instance.name} (Trigger: {instance.triggers})")
391
+ except Exception as e:
392
+ print(f" ⚠️ Failed to init {name_cls}: {e}")
393
+ except Exception as e:
394
+ print(f" ⚠️ Error loading module {name}: {e}")
395
+
396
 
397
  async def process(self, raw_input: str):
398
  print("\n🌊 [HOLOGRAPHIC BUS] PULSING WAVE FUNCTIONS...")
 
402
  user_packet = {"content": raw_input, "type": "text_command"}
403
  action, packet = self.oversight.ingest(user_packet)
404
 
405
+ # 2. PARALLEL INTERFERENCE: Gemma, RNJ-1, Reasoning, AND Dynamic Agents
406
  # They "interfere" to find the true deterministic coordinate
407
  gemma_task = asyncio.create_task(self.metadata.process(packet))
408
  rnj1_task = asyncio.create_task(self.routing.calculate_position(packet))
409
 
410
+ # reasoning_task: Pulse to LLM
411
  reasoning_task = asyncio.create_task(self.routing.connector.chat_async(
412
  f"Analyze this packet for topological resonance: {raw_input}",
413
  system_prompt="You are the SWARM_REASONER. Identify the high-level intent and potential manifold collisions."
414
  ))
415
+
416
+ # Dynamic Agent Task (if applicable)
417
+ agent_tasks = []
418
+ for agent_name, agent in self.agents.items():
419
+ # Check triggers or intent
420
+ if any(t in raw_input.lower() for t in agent.triggers):
421
+ print(f"🚀 [SWARM] Triggering Agent: {agent.name}")
422
+ agent_tasks.append(asyncio.create_task(agent.process(packet)))
423
+
424
+ # Wait for Core Triple Wave
425
  await asyncio.gather(gemma_task, rnj1_task, reasoning_task)
426
 
427
  gemma_status, mass = gemma_task.result()
428
  res = rnj1_task.result()
429
  reasoning, logprobs = reasoning_task.result()
430
 
431
+ # Process Agent Results
432
+ agent_results = []
433
+ if agent_tasks:
434
+ await asyncio.gather(*agent_tasks)
435
+ for t in agent_tasks:
436
+ agent_results.append(t.result())
437
+
438
+ # Update Entropy Kill Switch
439
  if logprobs:
 
440
  log_content = logprobs.get('content', [])
441
  self.oversight.kill_switch.monitor_bulk(log_content)
442
 
 
520
  })
521
  except: pass
522
 
523
+
524
+ def get_onboarded_agents() -> Dict[str, Any]:
525
+ """Helper to discover agents dynamically."""
526
+ import pkgutil
527
+ import importlib
528
+ import inspect
529
+ import logos.agents
530
+ from logos.agents.base_agent import BaseAgent
531
+
532
+ agents = {}
533
+ package = logos.agents
534
+ prefix = package.__name__ + "."
535
+
536
+ for _, name, _ in pkgutil.iter_modules(package.__path__, prefix):
537
+ try:
538
+ module = importlib.import_module(name)
539
+ for _, cls in inspect.getmembers(module, inspect.isclass):
540
+ if issubclass(cls, BaseAgent) and cls is not BaseAgent:
541
+ try:
542
+ instance = cls()
543
+ agents[instance.name.lower()] = instance
544
+ except: pass
545
+ except: pass
546
+ return agents
547
+
548
+ # Dynamic Prompt Construction
549
+ _onboarded = get_onboarded_agents()
550
+ _agent_lines = []
551
+ for name, agent in _onboarded.items():
552
+ _agent_lines.append(f"- '{name}': {agent.description}")
553
+
554
+ ROUTER_PROMPT = f"""
555
  Analyze the following user input and classify it into one of these categories:
556
  - 'fractal_architect': For code, debugging, storage, sharding, or technical implementation.
557
  - 'prime_navigator': For math, primes, geometry, calculations, or physics.
558
  - 'creative': For descriptions, stories, or documentation style.
559
  - 'general': For greetings or anything else.
560
+ {chr(10).join(_agent_lines)}
561
 
562
  Reply with ONLY a JSON object in this format:
563
+ {{"intent": "category_name", "confidence": 0.9}}
564
  Do not write any introductory text.
565
  """
566
 
logos/agents/base_agent.py ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ from abc import ABC, abstractmethod
3
+ from typing import Dict, Any, List, Optional
4
+
5
+ class BaseAgent(ABC):
6
+ """
7
+ Protocol 26: Standard Agent Interface for Matroska Swarm.
8
+ All agents must implement this to be auto-discovered by the Router.
9
+ """
10
+
11
+ @property
12
+ @abstractmethod
13
+ def name(self) -> str:
14
+ """Unique name of the agent (e.g., 'VideoAtomizer')"""
15
+ pass
16
+
17
+ @property
18
+ @abstractmethod
19
+ def description(self) -> str:
20
+ """Short description for the Router's system prompt"""
21
+ pass
22
+
23
+ @property
24
+ @abstractmethod
25
+ def triggers(self) -> List[str]:
26
+ """List of keywords or regex patterns that trigger this agent"""
27
+ pass
28
+
29
+ @abstractmethod
30
+ async def process(self, task: Dict[str, Any]) -> Dict[str, Any]:
31
+ """
32
+ Execute the agent's logic.
33
+ Args:
34
+ task: The input packet (e.g., {'content': '...', 'context': {}})
35
+ Returns:
36
+ Dict containing 'status', 'result', and 'tensor_updates'
37
+ """
38
+ pass
logos/agents/video_atomizer.py CHANGED
@@ -1,15 +1,43 @@
1
  import re
2
  import asyncio
3
  from youtube_transcript_api import YouTubeTranscriptApi
 
4
 
5
- class VideoAtomizer:
6
  """
7
  Role: V-NODE (Video Ingest)
8
  Function: Rips semantic atoms from video streams and collides them
9
  with the existing Project Manifold.
10
  """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  def __init__(self):
12
- self.name = "VideoAtomizer"
 
13
 
14
  def extract_video_id(self, url):
15
  # Extracts 'R9czY1uVq_k' from the URL
@@ -31,10 +59,30 @@ class VideoAtomizer:
31
 
32
  # 1. FETCH TRANSCRIPT (The Raw Atoms)
33
  try:
34
- # We use a thread pool for the blocking API call
 
 
 
35
  loop = asyncio.get_event_loop()
36
- transcript_list = await loop.run_in_executor(None, YouTubeTranscriptApi.get_transcript, video_id)
37
- full_text = " ".join([t['text'] for t in transcript_list])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
  except Exception as e:
39
  return {"error": f"Signal Lost: {e}"}
40
 
 
1
  import re
2
  import asyncio
3
  from youtube_transcript_api import YouTubeTranscriptApi
4
+ from logos.agents.base_agent import BaseAgent
5
 
6
+ class VideoAtomizer(BaseAgent):
7
  """
8
  Role: V-NODE (Video Ingest)
9
  Function: Rips semantic atoms from video streams and collides them
10
  with the existing Project Manifold.
11
  """
12
+ @property
13
+ def name(self) -> str:
14
+ return "VideoAtomizer"
15
+
16
+ @property
17
+ def description(self) -> str:
18
+ return "Ingests YouTube videos/playlists, extracts transcripts, and atomizes content into semantic tensors."
19
+
20
+ @property
21
+ def triggers(self) -> list:
22
+ return ["youtube", "playlist", "video", "transcript", "watch?v="]
23
+
24
+ async def process(self, task: dict) -> dict:
25
+ content = task.get('content', '')
26
+ project_dna = task.get('context', {}).get('dna', {})
27
+
28
+ # Extract URLs from content
29
+ urls = re.findall(r'(https?://(?:www\.|m\.)?youtube\.com/watch\?v=[\w-]+|https?://youtu\.be/[\w-]+)', content)
30
+
31
+ results = []
32
+ for url in urls:
33
+ res = await self.ingest_and_align(url, project_dna)
34
+ results.append(res)
35
+
36
+ return {"status": "COMPLETE", "result": results}
37
+
38
  def __init__(self):
39
+ self._name = "VideoAtomizer" # Internal generic
40
+
41
 
42
  def extract_video_id(self, url):
43
  # Extracts 'R9czY1uVq_k' from the URL
 
59
 
60
  # 1. FETCH TRANSCRIPT (The Raw Atoms)
61
  try:
62
+ # Instantiate the API wrapper (Local Environment Quirk)
63
+ yt_api = YouTubeTranscriptApi()
64
+
65
+ # Use 'fetch' method in thread executor
66
  loop = asyncio.get_event_loop()
67
+ transcript_list = await loop.run_in_executor(None, yt_api.fetch, video_id)
68
+
69
+ # Handle return types
70
+ if isinstance(transcript_list, dict):
71
+ full_text = str(transcript_list)
72
+ elif isinstance(transcript_list, list):
73
+ if len(transcript_list) > 0:
74
+ first = transcript_list[0]
75
+ if isinstance(first, str):
76
+ full_text = " ".join(transcript_list)
77
+ elif isinstance(first, dict):
78
+ full_text = " ".join([t.get('text', '') for t in transcript_list])
79
+ else:
80
+ full_text = str(transcript_list)
81
+ else:
82
+ full_text = ""
83
+ else:
84
+ full_text = str(transcript_list)
85
+
86
  except Exception as e:
87
  return {"error": f"Signal Lost: {e}"}
88