mirror of
https://github.com/headroomlabs-ai/headroom.git
synced 2026-08-27 14:17:10 -04:00
Merge upstream/main into feat/docker-e2e-wrap-validation
Resolve the PR merge conflict by carrying forward the secure TLS fail-closed rtk download behavior, bring in the latest upstream proxy-handler updates, and fix the OpenClaw linked-install fallback so it copies the required hook-shim directory with regression coverage. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
commit
addcfcffcd
7 changed files with 107 additions and 8 deletions
|
|
@ -436,14 +436,23 @@ def _copy_openclaw_plugin_into_extensions(
|
|||
raise click.ClickException(
|
||||
f"Plugin dist folder missing at {dist_dir}. Build the plugin first."
|
||||
)
|
||||
hook_shim_dir = plugin_dir / "hook-shim"
|
||||
if not hook_shim_dir.exists():
|
||||
raise click.ClickException(
|
||||
f"Plugin hook-shim folder missing at {hook_shim_dir}. Build the plugin first."
|
||||
)
|
||||
|
||||
extensions_dir = _resolve_openclaw_extensions_dir(openclaw_bin)
|
||||
target_dir = extensions_dir / "headroom"
|
||||
target_dist = target_dir / "dist"
|
||||
target_hook_shim = target_dir / "hook-shim"
|
||||
target_dir.mkdir(parents=True, exist_ok=True)
|
||||
if target_dist.exists():
|
||||
shutil.rmtree(target_dist)
|
||||
if target_hook_shim.exists():
|
||||
shutil.rmtree(target_hook_shim)
|
||||
shutil.copytree(dist_dir, target_dist)
|
||||
shutil.copytree(hook_shim_dir, target_hook_shim)
|
||||
|
||||
for filename in ("openclaw.plugin.json", "package.json", "README.md"):
|
||||
source = plugin_dir / filename
|
||||
|
|
|
|||
|
|
@ -681,6 +681,17 @@ class AnthropicHandlerMixin:
|
|||
# Flag compression failure for observability
|
||||
_compression_failed = True
|
||||
|
||||
# Guard: if "optimization" inflated tokens, revert to originals.
|
||||
# Skip in cache mode where prefix-stability may legitimately shift counts.
|
||||
if optimized_tokens > original_tokens and not is_cache_mode(self.config.mode):
|
||||
logger.warning(
|
||||
f"[{request_id}] Optimization inflated tokens "
|
||||
f"({original_tokens} -> {optimized_tokens}), reverting to original messages"
|
||||
)
|
||||
optimized_messages = original_messages
|
||||
optimized_tokens = original_tokens
|
||||
transforms_applied = []
|
||||
|
||||
tokens_saved = max(0, original_tokens - optimized_tokens)
|
||||
optimization_latency = (time.time() - start_time) * 1000
|
||||
|
||||
|
|
@ -1592,9 +1603,18 @@ class AnthropicHandlerMixin:
|
|||
# Use pipeline's token counts for consistency with pipeline logs
|
||||
original_tokens = result.tokens_before
|
||||
optimized_tokens = result.tokens_after
|
||||
# Guard: if "optimization" inflated tokens, revert to originals
|
||||
if optimized_tokens > original_tokens:
|
||||
logger.warning(
|
||||
f"[{request_id}] Batch item optimization inflated tokens "
|
||||
f"({original_tokens} -> {optimized_tokens}), reverting"
|
||||
)
|
||||
optimized_messages = messages
|
||||
optimized_tokens = original_tokens
|
||||
|
||||
total_original_tokens += original_tokens
|
||||
total_optimized_tokens += optimized_tokens
|
||||
tokens_saved = max(0, original_tokens - optimized_tokens)
|
||||
tokens_saved = original_tokens - optimized_tokens
|
||||
total_tokens_saved += tokens_saved
|
||||
|
||||
# CCR Tool Injection: Inject retrieval tool if compression occurred
|
||||
|
|
|
|||
|
|
@ -158,9 +158,18 @@ class BatchHandlerMixin:
|
|||
# Use pipeline's token counts for consistency with pipeline logs
|
||||
original_tokens = result.tokens_before
|
||||
optimized_tokens = result.tokens_after
|
||||
# Guard: if "optimization" inflated tokens, revert to originals
|
||||
if optimized_tokens > original_tokens:
|
||||
logger.warning(
|
||||
f"[{request_id}] Batch item optimization inflated tokens "
|
||||
f"({original_tokens} -> {optimized_tokens}), reverting"
|
||||
)
|
||||
optimized_messages = messages
|
||||
optimized_tokens = original_tokens
|
||||
|
||||
total_original_tokens += original_tokens
|
||||
total_optimized_tokens += optimized_tokens
|
||||
tokens_saved = max(0, original_tokens - optimized_tokens)
|
||||
tokens_saved = original_tokens - optimized_tokens
|
||||
total_tokens_saved += tokens_saved
|
||||
|
||||
# CCR Tool Injection: Inject retrieval tool if compression occurred
|
||||
|
|
|
|||
|
|
@ -265,7 +265,17 @@ class GeminiHandlerMixin:
|
|||
_compression_failed = True
|
||||
logger.warning(f"[{request_id}] Gemini optimization failed: {e}")
|
||||
|
||||
tokens_saved = max(0, original_tokens - optimized_tokens)
|
||||
# Guard: if "optimization" inflated tokens, revert to originals
|
||||
if optimized_tokens > original_tokens:
|
||||
logger.warning(
|
||||
f"[{request_id}] Optimization inflated tokens "
|
||||
f"({original_tokens} -> {optimized_tokens}), reverting to original messages"
|
||||
)
|
||||
optimized_messages = messages
|
||||
optimized_tokens = original_tokens
|
||||
transforms_applied = []
|
||||
|
||||
tokens_saved = original_tokens - optimized_tokens
|
||||
optimization_latency = (time.time() - start_time) * 1000
|
||||
|
||||
# Query Echo: disabled — hurts prefix caching in long conversations.
|
||||
|
|
|
|||
|
|
@ -298,7 +298,17 @@ class OpenAIHandlerMixin:
|
|||
# Flag compression failure for observability
|
||||
_compression_failed = True
|
||||
|
||||
tokens_saved = max(0, original_tokens - optimized_tokens)
|
||||
# Guard: if "optimization" inflated tokens, revert to originals
|
||||
if optimized_tokens > original_tokens:
|
||||
logger.warning(
|
||||
f"[{request_id}] Optimization inflated tokens "
|
||||
f"({original_tokens} -> {optimized_tokens}), reverting to original messages"
|
||||
)
|
||||
optimized_messages = original_messages
|
||||
optimized_tokens = original_tokens
|
||||
transforms_applied = []
|
||||
|
||||
tokens_saved = original_tokens - optimized_tokens
|
||||
optimization_latency = (time.time() - start_time) * 1000
|
||||
|
||||
# Hook: post_compress
|
||||
|
|
@ -795,7 +805,17 @@ class OpenAIHandlerMixin:
|
|||
except Exception as e:
|
||||
logger.warning(f"[{request_id}] Responses API optimization failed: {e}")
|
||||
|
||||
tokens_saved = max(0, original_tokens - optimized_tokens)
|
||||
# Guard: if "optimization" inflated tokens, revert to originals
|
||||
if optimized_tokens > original_tokens:
|
||||
logger.warning(
|
||||
f"[{request_id}] Optimization inflated tokens "
|
||||
f"({original_tokens} -> {optimized_tokens}), reverting to original messages"
|
||||
)
|
||||
optimized_messages = messages
|
||||
optimized_tokens = original_tokens
|
||||
transforms_applied = []
|
||||
|
||||
tokens_saved = original_tokens - optimized_tokens
|
||||
optimization_latency = (time.time() - start_time) * 1000
|
||||
|
||||
# Convert compressed messages back to Responses API items
|
||||
|
|
@ -1060,7 +1080,15 @@ class OpenAIHandlerMixin:
|
|||
if instructions and opt and opt[0].get("role") == "system":
|
||||
body["instructions"] = opt[0]["content"]
|
||||
opt = opt[1:]
|
||||
body["input"] = messages_to_responses_items(opt, input_data, preserved)
|
||||
if result.tokens_after <= original_tokens:
|
||||
body["input"] = messages_to_responses_items(
|
||||
opt, input_data, preserved
|
||||
)
|
||||
else:
|
||||
logger.warning(
|
||||
f"[{request_id}] WS optimization inflated tokens "
|
||||
f"({original_tokens} -> {result.tokens_after}), reverting"
|
||||
)
|
||||
tokens_saved = max(0, original_tokens - result.tokens_after)
|
||||
first_msg_raw = json.dumps(body)
|
||||
logger.info(
|
||||
|
|
|
|||
|
|
@ -78,8 +78,13 @@ def _setup_file_logging() -> None:
|
|||
handler.setFormatter(
|
||||
logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
|
||||
)
|
||||
# Attach to the headroom root logger so all sub-loggers are captured
|
||||
logging.getLogger("headroom").addHandler(handler)
|
||||
# Attach to the headroom root logger so all sub-loggers are captured.
|
||||
# Disable propagation to root to avoid duplicate writes when
|
||||
# wrap.py redirects stderr to the same log file.
|
||||
headroom_logger = logging.getLogger("headroom")
|
||||
if not any(isinstance(h, RotatingFileHandler) for h in headroom_logger.handlers):
|
||||
headroom_logger.addHandler(handler)
|
||||
headroom_logger.propagate = False
|
||||
except OSError:
|
||||
# Non-fatal: can't write logs (read-only fs, permissions, etc.)
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -25,6 +25,9 @@ def plugin_dir(tmp_path: Path) -> Path:
|
|||
plugin.mkdir(parents=True)
|
||||
(plugin / "package.json").write_text('{"name":"headroom-openclaw"}\n')
|
||||
(plugin / "openclaw.plugin.json").write_text('{"id":"headroom"}\n')
|
||||
hook_shim = plugin / "hook-shim"
|
||||
hook_shim.mkdir()
|
||||
(hook_shim / "index.js").write_text("export default {};\n")
|
||||
return plugin
|
||||
|
||||
|
||||
|
|
@ -416,11 +419,24 @@ def test_copy_openclaw_plugin_into_extensions_handles_missing_and_existing_dist(
|
|||
(plugin / "package.json").write_text("{}\n")
|
||||
(plugin / "openclaw.plugin.json").write_text("{}\n")
|
||||
|
||||
with patch("headroom.cli.wrap._resolve_openclaw_extensions_dir", return_value=tmp_path):
|
||||
with pytest.raises(Exception, match="Plugin hook-shim folder missing"):
|
||||
wrap_cli._copy_openclaw_plugin_into_extensions(
|
||||
plugin_dir=plugin, openclaw_bin="openclaw"
|
||||
)
|
||||
|
||||
hook_shim = plugin / "hook-shim"
|
||||
hook_shim.mkdir()
|
||||
(hook_shim / "index.js").write_text("shim\n")
|
||||
|
||||
ext_root = tmp_path / ".openclaw" / "extensions"
|
||||
target_headroom = ext_root / "headroom"
|
||||
target_dist = target_headroom / "dist"
|
||||
target_hook_shim = target_headroom / "hook-shim"
|
||||
target_dist.mkdir(parents=True)
|
||||
(target_dist / "old.js").write_text("old\n")
|
||||
target_hook_shim.mkdir(parents=True)
|
||||
(target_hook_shim / "old.js").write_text("old-shim\n")
|
||||
|
||||
with patch("headroom.cli.wrap._resolve_openclaw_extensions_dir", return_value=ext_root):
|
||||
out = wrap_cli._copy_openclaw_plugin_into_extensions(
|
||||
|
|
@ -430,3 +446,5 @@ def test_copy_openclaw_plugin_into_extensions_handles_missing_and_existing_dist(
|
|||
assert out == target_headroom
|
||||
assert (target_dist / "index.js").exists()
|
||||
assert not (target_dist / "old.js").exists()
|
||||
assert (target_hook_shim / "index.js").exists()
|
||||
assert not (target_hook_shim / "old.js").exists()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue