oclaw/platform/llm/replay_policy.py
oliver 4a23b715a2 重构仓库目录为统一的 runtime 分层并清理历史 openclaw 残留。
本次迁移将网关/通道/工具/技能/脚本与协议资源集中到新结构,统一路径常量与脚本转发机制,减少顶层噪音并保证运行与测试行为一致。

Made-with: Cursor
2026-04-25 01:24:23 +08:00

91 lines
2.7 KiB
Python

"""Transcript replay policy for OpenAI-compatible Chat Completions.
Inspired by Oclaw (MIT) `src/agents/transcript-policy.ts` defaults for
`openai-completions`: enable strict tool-call-id sanitization so proxies and
multi-provider histories do not break on id format / length.
See OCLAW_MIT_LICENSE.txt in this package.
"""
from __future__ import annotations
import os
from dataclasses import dataclass
from typing import Any
from oclaw.platform.llm.tool_call_id import DEFAULT_MAX_OPENAI_TOOL_CALL_ID_LEN
@dataclass(frozen=True)
class ReplayPolicy:
"""Controls pre-request normalization of chat messages for tool-use flow."""
enabled: bool
sanitize_tool_call_ids: bool
repair_tool_pairing: bool
max_tool_call_id_len: int
def _env_bool(name: str, default: bool) -> bool:
raw = str(os.getenv(name) or "").strip().lower()
if raw in ("0", "false", "no", "off"):
return False
if raw in ("1", "true", "yes", "on"):
return True
return default
def resolve_replay_policy(
base_url: str | None,
model_id: str | None,
*,
llm_profile_mode: str | None = None,
) -> ReplayPolicy:
"""Resolve replay policy for OpenAI-compatible chat completions.
When enabled (default): sanitize assistant/tool ids before every request.
This matches oclaw's strict OpenAI-compatible replay defaults.
"""
del base_url, model_id, llm_profile_mode # reserved for provider-specific overrides
if not _env_bool("AIA_REPLAY_POLICY_ENABLED", True):
return ReplayPolicy(
enabled=False,
sanitize_tool_call_ids=False,
repair_tool_pairing=False,
max_tool_call_id_len=DEFAULT_MAX_OPENAI_TOOL_CALL_ID_LEN,
)
max_len = DEFAULT_MAX_OPENAI_TOOL_CALL_ID_LEN
raw_ml = str(os.getenv("AIA_TOOL_CALL_ID_MAX_LEN") or "").strip()
if raw_ml.isdigit():
max_len = max(8, min(int(raw_ml), 128))
repair = _env_bool("AIA_REPLAY_REPAIR_TOOL_PAIRING", True)
return ReplayPolicy(
enabled=True,
sanitize_tool_call_ids=True,
repair_tool_pairing=repair,
max_tool_call_id_len=max_len,
)
def apply_replay_policy_to_messages(
messages: list[dict[str, Any]],
policy: ReplayPolicy,
) -> list[dict[str, Any]]:
"""Apply repair + id sanitization in order."""
if not policy.enabled or not messages:
return messages
from oclaw.platform.llm.tool_call_id import (
repair_orphan_tool_messages,
rewrite_openai_chat_messages_tool_ids,
)
out = messages
if policy.repair_tool_pairing:
out = repair_orphan_tool_messages(out)
if policy.sanitize_tool_call_ids:
out = rewrite_openai_chat_messages_tool_ids(out, max_len=policy.max_tool_call_id_len)
return out