fix: declare Hermes auto image support and migrate running accounts

This commit is contained in:
snowspeeder committed 2026-10-02 16:17:29 -04:00
1 parent 6b8d55fc92
commit 437745bcca
4 files changed
+189 -4

No files matched your search

+72 -1
View File
@@ -19,6 +19,8 @@ class DockerBoundary:
def __init__(self):
self.containers, self.networks, self.created, self.deleted = {}, {}, [], []
self.fail_start = False
self.restarted = []
self.fail_restart = False
def __call__(self, request):
path, method = request.url.path, request.method
@@ -51,6 +53,13 @@ class DockerBoundary:
if method == 'GET':
return httpx.Response(200, json=info)
if method == 'POST':
if path.endswith('/restart'):
self.restarted.append(key)
if self.fail_restart:
self.fail_restart = False
return httpx.Response(500)
elif not path.endswith('/start'):
raise AssertionError(path)
if self.fail_start:
self.fail_start = False
return httpx.Response(500)
@@ -120,7 +129,7 @@ def test_account_isolation_and_runtime_configuration(boundary):
assert config['model'] == {'default': 'auto', 'provider': 'custom:hope-upstream'}
assert config['providers']['hope-upstream'] == {'api': 'http://192.168.86.5:2099/v1',
'key_env': 'HERMES_UPSTREAM_API_KEY', 'transport': 'codex_responses', 'default_model': 'auto',
'models': ['auto'], 'discover_models': False}
'models': {'auto': {'supports_vision': True}}, 'discover_models': False}
assert config['agent']['reasoning_effort'] == 'none'
assert config['terminal'] == {'backend': 'local', 'cwd': '/workspace'}
assert {'memory', 'browser', 'web', 'terminal', 'file', 'skills'} <= set(config['platform_toolsets']['api_server'])
@@ -209,6 +218,68 @@ def test_owned_container_cannot_rotate_missing_private_secret(boundary):
assert len(docker.created) == 1
@pytest.mark.parametrize('models', [
['auto', 'text-only'],
{'auto': {'supports_vision': False, 'context_window': 32768}, 'text-only': {'supports_vision': False}},
])
def test_auto_vision_migration_preserves_settings_and_reloads_once(boundary, models):
p, docker, _ = boundary
original = asyncio.run(p.ensure(ALICE))
private, home, workspace = p._paths(ALICE)
config = yaml.safe_load((home / 'config.yaml').read_text())
config['providers']['hope-upstream']['models'] = models
config['model']['default'] = 'text-only'
config['agent']['reasoning_effort'] = 'high'
config['retained'] = {'private': True}
(home / 'config.yaml').write_text(yaml.safe_dump(config))
(workspace / 'work.txt').write_text('retained')
assert not asyncio.run(p.ready(ALICE))
assert asyncio.run(p.ensure(ALICE)) == original
result = yaml.safe_load((home / 'config.yaml').read_text())
expected = copy.deepcopy(config)
if isinstance(models, list):
expected['providers']['hope-upstream']['models'] = {m: {} for m in models}
expected['providers']['hope-upstream']['models']['auto']['supports_vision'] = True
assert result == expected
assert 'supports_vision' not in result['model']
assert docker.restarted == ['id-' + container_name(ALICE)]
assert not docker.deleted and len(docker.created) == 1
assert (workspace / 'work.txt').read_text() == 'retained'
assert not p._reload_marker(ALICE).exists()
assert asyncio.run(p.ready(ALICE))
asyncio.run(p.ensure(ALICE))
assert len(docker.restarted) == 1
def test_failed_reload_is_retried_after_control_restart(boundary):
p, docker, _ = boundary
original = asyncio.run(p.ensure(ALICE))
_, home, _ = p._paths(ALICE)
config = yaml.safe_load((home / 'config.yaml').read_text())
config['providers']['hope-upstream']['models'] = ['auto']
(home / 'config.yaml').write_text(yaml.safe_dump(config))
docker.fail_restart = True
with pytest.raises(ProvisioningError, match='HTTP 500'):
asyncio.run(p.ensure(ALICE))
assert p._reload_marker(ALICE).exists()
assert not asyncio.run(p.ready(ALICE))
restarted = Provisioner(p.state_dir, docker_client=p.docker, health_client=p.health,
upstream_api_key='runtime-provider-secret', runtime_uid=p.runtime_uid, runtime_gid=p.runtime_gid)
assert asyncio.run(restarted.ensure(ALICE)) == original
assert len(docker.restarted) == 2 and not docker.deleted
assert not p._reload_marker(ALICE).exists()
def test_vision_patch_skips_unverified_models_and_preserves_metadata():
for config in ({'retained': True}, {'providers': {'hope-upstream': {'models': ['text-only']}}}):
original = copy.deepcopy(config)
assert Provisioner._declare_auto_vision(config) is False
assert config == original
for models in ('auto', [None], {'auto': None}):
with pytest.raises(ValueError):
Provisioner._declare_auto_vision({'providers': {'hope-upstream': {'models': models}}})
def test_upstream_runtime_overrides(monkeypatch):
monkeypatch.setenv('HERMES_UPSTREAM_URL', 'http://127.0.0.1:9999/v1')
monkeypatch.setenv('HERMES_UPSTREAM_MODEL', 'served-model')