This repository has been archived on 2026-03-24. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
Timmy-time-dashboard/tests/test_swarm_personas.py
Claude 1103da339c feat: add full creative studio + DevOps tools (Pixel, Lyra, Reel personas)
Adds 3 new personas (Pixel, Lyra, Reel) and 5 new tool modules:

- Git/DevOps tools (GitPython): clone, status, diff, log, blame, branch,
  add, commit, push, pull, stash — wired to Forge and Helm personas
- Image generation (FLUX via diffusers): text-to-image, storyboards,
  variations — Pixel persona
- Music generation (ACE-Step 1.5): full songs with vocals+instrumentals,
  instrumental tracks, vocal-only tracks — Lyra persona
- Video generation (Wan 2.1 via diffusers): text-to-video, image-to-video
  clips — Reel persona
- Creative Director pipeline: multi-step orchestration that chains
  storyboard → music → video → assembly into 3+ minute final videos
- Video assembler (MoviePy + FFmpeg): stitch clips, overlay audio,
  title cards, subtitles, final export

Also includes:
- Spark Intelligence tool-level + creative pipeline event capture
- Creative Studio dashboard page (/creative/ui) with 4 tabs
- Config settings for all new models and output directories
- pyproject.toml creative optional extra for GPU dependencies
- 107 new tests covering all modules (624 total, all passing)

https://claude.ai/code/session_01KJm6jQkNi3aA3yoQJn636c
2026-02-24 16:31:47 +00:00

223 lines
8.7 KiB
Python

"""Tests for swarm.personas and swarm.persona_node."""
import pytest
from unittest.mock import MagicMock
# ── Fixture: redirect SQLite DB to a temp directory ──────────────────────────
@pytest.fixture(autouse=True)
def tmp_swarm_db(tmp_path, monkeypatch):
db_path = tmp_path / "swarm.db"
monkeypatch.setattr("swarm.tasks.DB_PATH", db_path)
monkeypatch.setattr("swarm.registry.DB_PATH", db_path)
monkeypatch.setattr("swarm.stats.DB_PATH", db_path)
monkeypatch.setattr("swarm.learner.DB_PATH", db_path)
yield db_path
# ── personas.py ───────────────────────────────────────────────────────────────
def test_all_nine_personas_defined():
from swarm.personas import PERSONAS
expected = {"echo", "mace", "helm", "seer", "forge", "quill", "pixel", "lyra", "reel"}
assert expected == set(PERSONAS.keys())
def test_persona_has_required_fields():
from swarm.personas import PERSONAS
required = {"id", "name", "role", "description", "capabilities",
"rate_sats", "bid_base", "bid_jitter", "preferred_keywords"}
for pid, meta in PERSONAS.items():
missing = required - set(meta.keys())
assert not missing, f"Persona {pid!r} missing: {missing}"
def test_get_persona_returns_correct_entry():
from swarm.personas import get_persona
echo = get_persona("echo")
assert echo is not None
assert echo["name"] == "Echo"
assert echo["role"] == "Research Analyst"
def test_get_persona_returns_none_for_unknown():
from swarm.personas import get_persona
assert get_persona("bogus") is None
def test_list_personas_returns_all_nine():
from swarm.personas import list_personas
personas = list_personas()
assert len(personas) == 9
def test_persona_capabilities_are_comma_strings():
from swarm.personas import PERSONAS
for pid, meta in PERSONAS.items():
assert isinstance(meta["capabilities"], str), \
f"{pid} capabilities should be a comma-separated string"
assert "," in meta["capabilities"] or len(meta["capabilities"]) > 0
def test_persona_preferred_keywords_nonempty():
from swarm.personas import PERSONAS
for pid, meta in PERSONAS.items():
assert len(meta["preferred_keywords"]) > 0, \
f"{pid} must have at least one preferred keyword"
# ── persona_node.py ───────────────────────────────────────────────────────────
def _make_persona_node(persona_id="echo", agent_id="persona-1"):
from swarm.comms import SwarmComms
from swarm.persona_node import PersonaNode
comms = SwarmComms(redis_url="redis://localhost:9999") # in-memory fallback
return PersonaNode(persona_id=persona_id, agent_id=agent_id, comms=comms)
def test_persona_node_inherits_name():
node = _make_persona_node("echo")
assert node.name == "Echo"
def test_persona_node_inherits_capabilities():
node = _make_persona_node("mace")
assert "security" in node.capabilities
def test_persona_node_has_rate_sats():
node = _make_persona_node("quill")
from swarm.personas import PERSONAS
assert node.rate_sats == PERSONAS["quill"]["rate_sats"]
def test_persona_node_raises_on_unknown_persona():
from swarm.comms import SwarmComms
from swarm.persona_node import PersonaNode
comms = SwarmComms(redis_url="redis://localhost:9999")
with pytest.raises(KeyError):
PersonaNode(persona_id="ghost", agent_id="x", comms=comms)
def test_persona_node_bids_low_on_preferred_task():
node = _make_persona_node("echo") # prefers research/summarize
bids = [node._compute_bid("please research and summarize this topic") for _ in range(20)]
avg = sum(bids) / len(bids)
# Should cluster around bid_base (35) not the off-spec inflated value
assert avg < 80, f"Expected low bids on preferred task, got avg={avg:.1f}"
def test_persona_node_bids_higher_on_off_spec_task():
node = _make_persona_node("echo") # echo doesn't prefer "deploy kubernetes"
bids = [node._compute_bid("deploy kubernetes cluster") for _ in range(20)]
avg = sum(bids) / len(bids)
# Off-spec: bid inflated by _OFF_SPEC_MULTIPLIER
assert avg > 40, f"Expected higher bids on off-spec task, got avg={avg:.1f}"
def test_persona_node_preferred_beats_offspec():
"""A preferred-task bid should be lower than an off-spec bid on average."""
node = _make_persona_node("forge") # prefers code/bug/test
on_spec = [node._compute_bid("write tests and fix bugs in the code") for _ in range(30)]
off_spec = [node._compute_bid("research market trends in finance") for _ in range(30)]
assert sum(on_spec) / 30 < sum(off_spec) / 30
@pytest.mark.asyncio
async def test_persona_node_join_registers_in_registry():
from swarm import registry
node = _make_persona_node("helm", agent_id="helm-join-test")
await node.join()
assert node.is_joined is True
rec = registry.get_agent("helm-join-test")
assert rec is not None
assert rec.name == "Helm"
assert "devops" in rec.capabilities
@pytest.mark.asyncio
async def test_persona_node_submits_bid_on_task():
from swarm.comms import SwarmComms, CHANNEL_BIDS
comms = SwarmComms(redis_url="redis://localhost:9999")
from swarm.persona_node import PersonaNode
node = PersonaNode(persona_id="quill", agent_id="quill-bid-1", comms=comms)
await node.join()
received = []
comms.subscribe(CHANNEL_BIDS, lambda msg: received.append(msg))
comms.post_task("task-quill-1", "write documentation for the API")
assert len(received) == 1
assert received[0].data["agent_id"] == "quill-bid-1"
assert received[0].data["bid_sats"] >= 1
# ── coordinator.spawn_persona ─────────────────────────────────────────────────
def test_coordinator_spawn_persona_registers_agent():
from swarm.coordinator import SwarmCoordinator
from swarm import registry
coord = SwarmCoordinator()
result = coord.spawn_persona("seer")
assert result["name"] == "Seer"
assert result["persona_id"] == "seer"
assert "agent_id" in result
agents = registry.list_agents()
assert any(a.name == "Seer" for a in agents)
def test_coordinator_spawn_persona_raises_on_unknown():
from swarm.coordinator import SwarmCoordinator
coord = SwarmCoordinator()
with pytest.raises(ValueError, match="Unknown persona"):
coord.spawn_persona("ghost")
def test_coordinator_spawn_all_personas():
from swarm.coordinator import SwarmCoordinator
from swarm import registry
coord = SwarmCoordinator()
names = []
for pid in ["echo", "mace", "helm", "seer", "forge", "quill", "pixel", "lyra", "reel"]:
result = coord.spawn_persona(pid)
names.append(result["name"])
agents = registry.list_agents()
registered = {a.name for a in agents}
for name in names:
assert name in registered
# ── Adaptive bidding via learner ────────────────────────────────────────────
def test_persona_node_adaptive_bid_adjusts_with_history():
"""After enough outcomes, the learner should shift bids."""
from swarm.learner import record_outcome, record_task_result
node = _make_persona_node("echo", agent_id="echo-adaptive")
# Record enough winning history on research tasks
for i in range(5):
record_outcome(
f"adapt-{i}", "echo-adaptive",
"research and summarize topic", 30,
won_auction=True, task_succeeded=True,
)
# With high win rate + high success rate, bid should differ from static
bids_adaptive = [node._compute_bid("research and summarize findings") for _ in range(20)]
# The learner should adjust — exact direction depends on win/success balance
# but the bid should not equal the static value every time
assert len(set(bids_adaptive)) >= 1 # at minimum it returns something valid
assert all(b >= 1 for b in bids_adaptive)
def test_persona_node_without_learner_uses_static_bid():
from swarm.persona_node import PersonaNode
from swarm.comms import SwarmComms
comms = SwarmComms(redis_url="redis://localhost:9999")
node = PersonaNode(persona_id="echo", agent_id="echo-static", comms=comms, use_learner=False)
bids = [node._compute_bid("research and summarize topic") for _ in range(20)]
# Static bids should be within the persona's base ± jitter range
for b in bids:
assert 20 <= b <= 50 # echo: bid_base=35, jitter=15 → range [20, 35]