v0.20 alpha skill-driven python core

This commit is contained in:
kai
2026-05-06 16:26:41 +08:00
parent d1169646b8
commit db626f1d58
87 changed files with 5213 additions and 2865 deletions
+5 -2
View File
@@ -47,7 +47,9 @@ def resolve_model_profile(
if selected not in profiles:
raise ModelConfigError(f"unknown model profile: {selected}")
roles = dict((profiles[selected] or {}).get("roles") or {})
selected_profile = profiles[selected] or {}
roles = dict(selected_profile.get("roles") or {})
task_types = dict(selected_profile.get("task_types") or defaults.get("task_types") or {})
if defaults.get("script_models"):
for role, model in (defaults.get("script_models") or {}).items():
roles.setdefault(role, model)
@@ -56,8 +58,9 @@ def resolve_model_profile(
return {
"profile": selected,
"description": (profiles[selected] or {}).get("description", ""),
"description": selected_profile.get("description", ""),
"roles": roles,
"task_types": task_types,
}
+42 -8
View File
@@ -29,6 +29,35 @@ MAX_RETRIES = 5
RETRYABLE_STATUSES = {408, 429, 500, 502, 503, 504, 520, 524}
_ANTHROPIC_MODEL_ALIASES = {
"anthropic/claude-opus-4-7": "anthropic/claude-opus-4.7",
"anthropic/claude-opus-4-6": "anthropic/claude-opus-4.6",
"anthropic/claude-opus-4-5": "anthropic/claude-opus-4.5",
"anthropic/claude-opus-4-1": "anthropic/claude-opus-4.1",
"anthropic/claude-sonnet-4-6": "anthropic/claude-sonnet-4.6",
"anthropic/claude-sonnet-4-5": "anthropic/claude-sonnet-4.5",
"anthropic/claude-haiku-4-5": "anthropic/claude-haiku-4.5",
}
_MODELS_WITHOUT_TEMPERATURE = {
"anthropic/claude-opus-4.7",
}
def normalize_zenmux_model(model: str) -> str:
"""Convert adapter-facing model IDs to ZenMux OpenAI API model IDs."""
normalized = model.strip()
if normalized.startswith("zenmux-anthropic/"):
normalized = "anthropic/" + normalized.removeprefix("zenmux-anthropic/")
elif normalized.startswith("zenmux/"):
normalized = normalized.removeprefix("zenmux/")
return _ANTHROPIC_MODEL_ALIASES.get(normalized, normalized)
def model_accepts_temperature(model: str) -> bool:
"""Return whether the ZenMux API accepts `temperature` for this model."""
return normalize_zenmux_model(model) not in _MODELS_WITHOUT_TEMPERATURE
@dataclass
class UsageStats:
"""聚合一次脚本运行的 token 消耗。"""
@@ -163,12 +192,14 @@ class ZenMuxClient:
messages.extend(extra_messages)
messages.append({"role": "user", "content": user})
api_model = normalize_zenmux_model(model)
body: dict[str, Any] = {
"model": model,
"model": api_model,
"messages": messages,
"temperature": temperature,
"max_tokens": max_tokens,
}
if model_accepts_temperature(api_model):
body["temperature"] = temperature
if web_search:
body["web_search_options"] = web_search_options or {}
headers = {
@@ -200,14 +231,14 @@ class ZenMuxClient:
raise ZenMuxError(f"invalid JSON from zenmux: {e}; body={resp.text[:500]}")
usage = data.get("usage", {}) or {}
with self._usage_lock:
self.usage.add(model, usage)
self.usage.add(api_model, usage)
content = ""
choices = data.get("choices") or []
if choices:
msg = choices[0].get("message") or {}
content = msg.get("content") or ""
self._log({
"tag": tag, "model": model, "attempt": attempt,
"tag": tag, "model": api_model, "requested_model": model, "attempt": attempt,
"elapsed": round(elapsed, 2),
"usage": usage,
"out_chars": len(content),
@@ -256,12 +287,14 @@ class ZenMuxClient:
messages.extend(extra_messages)
messages.append({"role": "user", "content": user})
api_model = normalize_zenmux_model(model)
body: dict[str, Any] = {
"model": model,
"model": api_model,
"messages": messages,
"temperature": temperature,
"max_tokens": max_tokens,
}
if model_accepts_temperature(api_model):
body["temperature"] = temperature
if web_search:
body["web_search_options"] = web_search_options or {}
@@ -309,7 +342,7 @@ class ZenMuxClient:
usage = data.get("usage", {}) or {}
with self._usage_lock:
self.usage.add(model, usage)
self.usage.add(api_model, usage)
message = ((data.get("choices") or [{}])[0].get("message") or {})
content = message.get("content") or ""
@@ -324,7 +357,8 @@ class ZenMuxClient:
urls.append(url_item)
self._log({
"tag": tag,
"model": model,
"model": api_model,
"requested_model": model,
"attempt": attempt,
"elapsed": round(elapsed, 2),
"usage": usage,