from __future__ import annotations from dataclasses import dataclass from typing import Any from skills_learning.models import FlowStep, FlowTemplateSkill from skills_learning.store import SkillStore @dataclass(frozen=True) class VersionDiff: structural_divergence: bool stored_sequence: list[str] executed_sequence: list[str] argument_differences: list[tuple[int, str, Any, Any]] @dataclass(frozen=True) class VersioningResult: skill: FlowTemplateSkill created_new_version: bool def diff_flow_versions( stored_steps: list[FlowStep], executed_steps: list[FlowStep], ) -> VersionDiff: stored_sequence = [step.tool_name for step in stored_steps] executed_sequence = [step.tool_name for step in executed_steps] structural = stored_sequence != executed_sequence differences: list[tuple[int, str, Any, Any]] = [] if not structural: for index, (stored_step, executed_step) in enumerate( zip(stored_steps, executed_steps, strict=True) ): keys = set(stored_step.args) | set(executed_step.args) for key in sorted(keys): stored_value = stored_step.args.get(key) executed_value = executed_step.args.get(key) if stored_value != executed_value: differences.append((index, key, stored_value, executed_value)) return VersionDiff( structural_divergence=structural, stored_sequence=stored_sequence, executed_sequence=executed_sequence, argument_differences=differences, ) def store_synthesized_skill( store: SkillStore, candidate: FlowTemplateSkill, ) -> VersioningResult: latest = store.get_latest_by_name(candidate.name) if latest is None: return VersioningResult(store.create_version(candidate), True) diff = diff_flow_versions(latest.steps, candidate.steps) if diff.structural_divergence: return VersioningResult(store.create_version(candidate, parent=latest), True) merged_parameters = { **latest.parameters, **candidate.parameters, } updated = latest.with_updates( steps=candidate.steps, parameters=merged_parameters, description=candidate.description, originating_goal=candidate.originating_goal, ) return VersioningResult(store.update_skill(updated), False)