mirror of
https://github.com/hansjone/oclaw.git
synced 2026-10-11 10:33:18 +08:00
重构仓库目录为统一的 runtime 分层并清理历史 openclaw 残留。
本次迁移将网关/通道/工具/技能/脚本与协议资源集中到新结构,统一路径常量与脚本转发机制,减少顶层噪音并保证运行与测试行为一致。 Made-with: Cursor
This commit is contained in:
parent
ba3836f00f
commit
4a23b715a2
498 changed files with 2760 additions and 2200 deletions
150
runtime/extensions/anthropic/api.py
Normal file
150
runtime/extensions/anthropic/api.py
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Callable
|
||||
|
||||
|
||||
CLAUDE_CLI_BACKEND_ID = "claude-cli"
|
||||
|
||||
|
||||
def is_claude_cli_provider(provider_id: str) -> bool:
|
||||
return str(provider_id or "").strip().lower() == CLAUDE_CLI_BACKEND_ID
|
||||
|
||||
|
||||
def build_anthropic_provider(_api=None) -> dict:
|
||||
return {
|
||||
"id": "anthropic",
|
||||
"label": "Anthropic",
|
||||
"docs_path": "/providers/models",
|
||||
"hook_aliases": [CLAUDE_CLI_BACKEND_ID],
|
||||
"env_vars": ["ANTHROPIC_OAUTH_TOKEN", "ANTHROPIC_API_KEY"],
|
||||
# NOTE: Full auth, cli-backend, replay-policy, and stream wrappers are not ported yet.
|
||||
}
|
||||
|
||||
|
||||
def _parse_header_list(value: Any) -> list[str]:
|
||||
if not isinstance(value, str):
|
||||
return []
|
||||
return [x.strip() for x in value.split(",") if x.strip()]
|
||||
|
||||
|
||||
def _is_anthropic_oauth_api_key(api_key: Any) -> bool:
|
||||
return isinstance(api_key, str) and "sk-ant-oat" in api_key
|
||||
|
||||
|
||||
def _merge_anthropic_beta_header(headers: dict[str, str] | None, betas: list[str]) -> dict[str, str]:
|
||||
merged = dict(headers or {})
|
||||
existing_key = next((k for k in merged if str(k).lower() == "anthropic-beta"), None)
|
||||
existing = _parse_header_list(merged.get(existing_key, "")) if existing_key else []
|
||||
all_values = list(dict.fromkeys([*existing, *betas]))
|
||||
merged[existing_key or "anthropic-beta"] = ",".join(all_values)
|
||||
return merged
|
||||
|
||||
|
||||
def resolve_anthropic_betas(extra_params: dict | None, model_id: str) -> list[str] | None:
|
||||
extra_params = extra_params or {}
|
||||
out: list[str] = []
|
||||
configured = extra_params.get("anthropicBeta")
|
||||
if isinstance(configured, str) and configured.strip():
|
||||
out.append(configured.strip())
|
||||
elif isinstance(configured, list):
|
||||
out.extend([str(x).strip() for x in configured if str(x).strip()])
|
||||
if extra_params.get("context1m") is True and str(model_id).lower().startswith(
|
||||
("claude-opus-4", "claude-sonnet-4")
|
||||
):
|
||||
out.append("context-1m-2025-08-07")
|
||||
out = list(dict.fromkeys(out))
|
||||
return out or None
|
||||
|
||||
|
||||
def create_anthropic_beta_headers_wrapper(base_stream_fn: Callable | None, betas: list[str]) -> Callable:
|
||||
underlying = base_stream_fn or (lambda model, context, options=None: {"model": model, "context": context, "options": options or {}})
|
||||
pi_defaults = ["fine-grained-tool-streaming-2025-05-14", "interleaved-thinking-2025-05-14"]
|
||||
pi_oauth = ["claude-code-20250219", "oauth-2025-04-20", *pi_defaults]
|
||||
|
||||
def wrapped(model, context, options=None):
|
||||
opts = dict(options or {})
|
||||
is_oauth = _is_anthropic_oauth_api_key(opts.get("apiKey"))
|
||||
requested_context1m = "context-1m-2025-08-07" in betas
|
||||
effective_betas = [b for b in betas if not (is_oauth and requested_context1m and b == "context-1m-2025-08-07")]
|
||||
all_betas = list(dict.fromkeys([*(pi_oauth if is_oauth else pi_defaults), *effective_betas]))
|
||||
opts["headers"] = _merge_anthropic_beta_header(opts.get("headers"), all_betas)
|
||||
return underlying(model, context, opts)
|
||||
|
||||
return wrapped
|
||||
|
||||
|
||||
def _normalize_fast_mode(raw: Any) -> bool | None:
|
||||
if isinstance(raw, bool):
|
||||
return raw
|
||||
if raw is None:
|
||||
return None
|
||||
key = str(raw).strip().lower()
|
||||
if key in {"off", "false", "no", "0", "disable", "disabled", "normal"}:
|
||||
return False
|
||||
if key in {"on", "true", "yes", "1", "enable", "enabled", "fast"}:
|
||||
return True
|
||||
return None
|
||||
|
||||
|
||||
def resolve_anthropic_fast_mode(extra_params: dict | None) -> bool | None:
|
||||
extra_params = extra_params or {}
|
||||
return _normalize_fast_mode(extra_params.get("fastMode", extra_params.get("fast_mode")))
|
||||
|
||||
|
||||
def resolve_anthropic_service_tier(extra_params: dict | None) -> str | None:
|
||||
extra_params = extra_params or {}
|
||||
raw = extra_params.get("serviceTier", extra_params.get("service_tier"))
|
||||
if isinstance(raw, str):
|
||||
norm = raw.strip().lower()
|
||||
if norm in {"auto", "standard_only"}:
|
||||
return norm
|
||||
return None
|
||||
|
||||
|
||||
def create_anthropic_fast_mode_wrapper(base_stream_fn: Callable | None, enabled: bool) -> Callable:
|
||||
underlying = base_stream_fn or (lambda model, context, options=None: {"model": model, "context": context, "options": options or {}})
|
||||
|
||||
def wrapped(model, context, options=None):
|
||||
opts = dict(options or {})
|
||||
if _is_anthropic_oauth_api_key(opts.get("apiKey")):
|
||||
return underlying(model, context, opts)
|
||||
payload = dict(opts.get("payload") or {})
|
||||
payload["service_tier"] = "auto" if enabled else "standard_only"
|
||||
opts["payload"] = payload
|
||||
return underlying(model, context, opts)
|
||||
|
||||
return wrapped
|
||||
|
||||
|
||||
def create_anthropic_service_tier_wrapper(base_stream_fn: Callable | None, service_tier: str) -> Callable:
|
||||
underlying = base_stream_fn or (lambda model, context, options=None: {"model": model, "context": context, "options": options or {}})
|
||||
|
||||
def wrapped(model, context, options=None):
|
||||
opts = dict(options or {})
|
||||
if _is_anthropic_oauth_api_key(opts.get("apiKey")):
|
||||
return underlying(model, context, opts)
|
||||
payload = dict(opts.get("payload") or {})
|
||||
payload["service_tier"] = service_tier
|
||||
opts["payload"] = payload
|
||||
return underlying(model, context, opts)
|
||||
|
||||
return wrapped
|
||||
|
||||
|
||||
def wrap_anthropic_provider_stream(ctx: dict) -> Callable | None:
|
||||
stream_fn = ctx.get("streamFn")
|
||||
model_id = str(ctx.get("modelId") or "")
|
||||
extra_params = ctx.get("extraParams") or {}
|
||||
|
||||
betas = resolve_anthropic_betas(extra_params, model_id)
|
||||
service_tier = resolve_anthropic_service_tier(extra_params)
|
||||
fast_mode = resolve_anthropic_fast_mode(extra_params)
|
||||
|
||||
wrapped = stream_fn
|
||||
if betas:
|
||||
wrapped = create_anthropic_beta_headers_wrapper(wrapped, betas)
|
||||
if service_tier:
|
||||
wrapped = create_anthropic_service_tier_wrapper(wrapped, service_tier)
|
||||
if fast_mode is not None:
|
||||
wrapped = create_anthropic_fast_mode_wrapper(wrapped, fast_mode)
|
||||
return wrapped
|
||||
Loading…
Add table
Add a link
Reference in a new issue