fix: add migration for 'deriver' to 'reasoning' in ResolvedConfiguration (#352)

This commit is contained in:
doria 2026-01-27 11:17:49 -05:00 committed by GitHub
parent 110787cdca
commit a4a341e8b5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 78 additions and 1 deletions

View File

@ -1,7 +1,7 @@
import datetime
import ipaddress
from enum import Enum
from typing import Annotated, Any, Self
from typing import Annotated, Any, Self, cast
from urllib.parse import urlparse
import tiktoken
@ -173,6 +173,20 @@ class ResolvedConfiguration(BaseModel):
summary: ResolvedSummaryConfiguration
dream: ResolvedDreamConfiguration
@model_validator(mode="before")
@classmethod
def migrate_deriver_to_reasoning(cls, data: Any) -> Any:
"""Handle v3.0.0 migration: 'deriver' was renamed to 'reasoning'."""
if not isinstance(data, dict):
return data
config = cast(dict[str, Any], data)
if "deriver" in config and "reasoning" not in config:
config["reasoning"] = config.pop("deriver")
return config
class PeerConfig(BaseModel):
# TODO: Update description - should say "Whether honcho forms a representation of the peer itself"

View File

@ -1,3 +1,5 @@
from typing import Any
import pytest
from pydantic import ValidationError
@ -6,6 +8,7 @@ from src.schemas import (
DocumentMetadata,
MessageCreate,
PeerCreate,
ResolvedConfiguration,
SessionCreate,
WorkspaceCreate,
)
@ -140,3 +143,63 @@ class TestDocumentValidations:
)
error_dict = exc_info.value.errors()[0]
assert error_dict["type"] == "string_too_long"
class TestResolvedConfigurationMigration:
"""Test backward compatibility for queue items created before v3.0.0.
In v3.0.0, the 'deriver' field was renamed to 'reasoning'. Old queue items
may still have the 'deriver' field and need to be migrated at validation time.
"""
def _make_config(self, **overrides: dict[str, Any]):
"""Helper to create a valid config dict with overrides."""
base = {
"reasoning": {"enabled": True},
"peer_card": {"use": True, "create": True},
"summary": {
"enabled": True,
"messages_per_short_summary": 20,
"messages_per_long_summary": 60,
},
"dream": {"enabled": False},
}
base.update(overrides)
return base
def test_old_queue_item_with_deriver_field(self):
"""Old queue items with 'deriver' should be migrated to 'reasoning'."""
old_payload = self._make_config()
del old_payload["reasoning"]
old_payload["deriver"] = {"enabled": True}
config = ResolvedConfiguration.model_validate(old_payload)
assert config.reasoning.enabled is True
def test_new_queue_item_with_reasoning_field(self):
"""New queue items with 'reasoning' should work normally."""
new_payload = self._make_config(reasoning={"enabled": False})
config = ResolvedConfiguration.model_validate(new_payload)
assert config.reasoning.enabled is False
def test_migration_does_not_override_reasoning(self):
"""If both 'deriver' and 'reasoning' exist, 'reasoning' takes precedence."""
payload = self._make_config(reasoning={"enabled": False})
payload["deriver"] = {"enabled": True}
config = ResolvedConfiguration.model_validate(payload)
assert config.reasoning.enabled is False
def test_missing_reasoning_and_deriver_fails(self):
"""Payload missing both 'reasoning' and 'deriver' should fail validation."""
payload = self._make_config()
del payload["reasoning"]
with pytest.raises(ValidationError) as exc_info:
ResolvedConfiguration.model_validate(payload)
assert any(e["loc"] == ("reasoning",) for e in exc_info.value.errors())