feat(genome): HypothesisAgentGenome with deterministic id and serde
Dataclass per genoma agente ipotesi con campi prompt/feature/temperature/ top_p/model_tier/lookback/style + parent_ids/generation. Id sha1[:16] deterministico su contenuto canonico (feature_access ordinate, float arrotondati). to_dict/from_dict per persistenza. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,72 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import hashlib
|
||||||
|
import json
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
from enum import StrEnum
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
|
||||||
|
class ModelTier(StrEnum):
|
||||||
|
B = "B" # Sonnet 4.6 via Anthropic
|
||||||
|
C = "C" # Qwen 2.5 72B via OpenRouter
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class HypothesisAgentGenome:
|
||||||
|
system_prompt: str
|
||||||
|
feature_access: list[str]
|
||||||
|
temperature: float
|
||||||
|
top_p: float
|
||||||
|
model_tier: ModelTier
|
||||||
|
lookback_window: int
|
||||||
|
cognitive_style: str
|
||||||
|
parent_ids: list[str] = field(default_factory=list)
|
||||||
|
generation: int = 0
|
||||||
|
id: str = ""
|
||||||
|
|
||||||
|
def __post_init__(self) -> None:
|
||||||
|
if not self.id:
|
||||||
|
self.id = self._compute_id()
|
||||||
|
|
||||||
|
def _compute_id(self) -> str:
|
||||||
|
payload = {
|
||||||
|
"system_prompt": self.system_prompt,
|
||||||
|
"feature_access": sorted(self.feature_access),
|
||||||
|
"temperature": round(self.temperature, 4),
|
||||||
|
"top_p": round(self.top_p, 4),
|
||||||
|
"model_tier": self.model_tier.value,
|
||||||
|
"lookback_window": self.lookback_window,
|
||||||
|
"cognitive_style": self.cognitive_style,
|
||||||
|
}
|
||||||
|
s = json.dumps(payload, sort_keys=True)
|
||||||
|
return hashlib.sha1(s.encode()).hexdigest()[:16]
|
||||||
|
|
||||||
|
def to_dict(self) -> dict[str, Any]:
|
||||||
|
return {
|
||||||
|
"id": self.id,
|
||||||
|
"system_prompt": self.system_prompt,
|
||||||
|
"feature_access": self.feature_access,
|
||||||
|
"temperature": self.temperature,
|
||||||
|
"top_p": self.top_p,
|
||||||
|
"model_tier": self.model_tier.value,
|
||||||
|
"lookback_window": self.lookback_window,
|
||||||
|
"cognitive_style": self.cognitive_style,
|
||||||
|
"parent_ids": self.parent_ids,
|
||||||
|
"generation": self.generation,
|
||||||
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, data: dict[str, Any]) -> HypothesisAgentGenome:
|
||||||
|
return cls(
|
||||||
|
system_prompt=data["system_prompt"],
|
||||||
|
feature_access=list(data["feature_access"]),
|
||||||
|
temperature=float(data["temperature"]),
|
||||||
|
top_p=float(data["top_p"]),
|
||||||
|
model_tier=ModelTier(data["model_tier"]),
|
||||||
|
lookback_window=int(data["lookback_window"]),
|
||||||
|
cognitive_style=data["cognitive_style"],
|
||||||
|
parent_ids=list(data.get("parent_ids", [])),
|
||||||
|
generation=int(data.get("generation", 0)),
|
||||||
|
id=data.get("id", ""),
|
||||||
|
)
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
from multi_swarm.genome.hypothesis import HypothesisAgentGenome, ModelTier
|
||||||
|
|
||||||
|
|
||||||
|
def test_genome_creation_defaults():
|
||||||
|
g = HypothesisAgentGenome(
|
||||||
|
system_prompt="Pensa come un fisico.",
|
||||||
|
feature_access=["close", "volume"],
|
||||||
|
temperature=0.9,
|
||||||
|
top_p=0.95,
|
||||||
|
model_tier=ModelTier.C,
|
||||||
|
lookback_window=200,
|
||||||
|
cognitive_style="physicist",
|
||||||
|
)
|
||||||
|
assert g.id is not None
|
||||||
|
assert g.parent_ids == []
|
||||||
|
assert g.generation == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_genome_serialization_roundtrip():
|
||||||
|
g = HypothesisAgentGenome(
|
||||||
|
system_prompt="Pensa come un biologo.",
|
||||||
|
feature_access=["close", "high", "low"],
|
||||||
|
temperature=1.1,
|
||||||
|
top_p=0.9,
|
||||||
|
model_tier=ModelTier.C,
|
||||||
|
lookback_window=300,
|
||||||
|
cognitive_style="biologist",
|
||||||
|
parent_ids=["abc"],
|
||||||
|
generation=5,
|
||||||
|
)
|
||||||
|
payload = g.to_dict()
|
||||||
|
g2 = HypothesisAgentGenome.from_dict(payload)
|
||||||
|
assert g2.system_prompt == g.system_prompt
|
||||||
|
assert g2.feature_access == g.feature_access
|
||||||
|
assert g2.temperature == g.temperature
|
||||||
|
assert g2.parent_ids == g.parent_ids
|
||||||
|
assert g2.generation == g.generation
|
||||||
|
assert g2.id == g.id
|
||||||
|
|
||||||
|
|
||||||
|
def test_genome_id_is_deterministic_on_content():
|
||||||
|
g1 = HypothesisAgentGenome(
|
||||||
|
system_prompt="X", feature_access=["close"], temperature=0.5,
|
||||||
|
top_p=0.9, model_tier=ModelTier.C, lookback_window=100, cognitive_style="x",
|
||||||
|
)
|
||||||
|
g2 = HypothesisAgentGenome(
|
||||||
|
system_prompt="X", feature_access=["close"], temperature=0.5,
|
||||||
|
top_p=0.9, model_tier=ModelTier.C, lookback_window=100, cognitive_style="x",
|
||||||
|
)
|
||||||
|
assert g1.id == g2.id
|
||||||
Reference in New Issue
Block a user