mirror of
https://github.com/hansjone/oclaw.git
synced 2026-10-09 03:30:48 +08:00
重构主控编排与运行时预热链路,统一工作区提示词/专家调度协议并补齐 wiki 记忆注入与写回闭环。
同时收敛启动与运维脚本默认行为(含 wiki worker)、更新 Admin 可观测性与相关测试,降低首轮时延并提高运行稳定性。 Made-with: Cursor
This commit is contained in:
parent
4a23b715a2
commit
dbbe3add6a
14438 changed files with 2693620 additions and 2546 deletions
|
|
@ -8,6 +8,8 @@ from oclaw.runtime.chat.tool_runtime import (
|
|||
ToolExecutionContext,
|
||||
ToolExecutor,
|
||||
)
|
||||
from oclaw.runtime.hooks.hook_types import HookEligibilityContext
|
||||
from oclaw.runtime.hooks_runtime import get_active_hooks_config, initialize_hooks_runtime, trigger_hook_event
|
||||
from oclaw.runtime.orchestration.trace import new_span_id
|
||||
from oclaw.platform.llm.chat_models import LLMToolCall
|
||||
from oclaw.runtime.tools.base import ToolRegistry
|
||||
|
|
@ -23,18 +25,21 @@ class SkillExecutionContext:
|
|||
specialist: str = "oclaw"
|
||||
trace_id: str | None = None
|
||||
parent_span_id: str | None = None
|
||||
workspace_dir: str | None = None
|
||||
workspace_owner_session_id: str | None = None
|
||||
path_policy_tenant_id: str | None = None
|
||||
path_policy_user_id: str | None = None
|
||||
run_id: str | None = None
|
||||
attempt_no: int | None = None
|
||||
turn_uuid: str | None = None
|
||||
hook_eligibility: HookEligibilityContext | None = None
|
||||
|
||||
|
||||
class SkillExecutor:
|
||||
"""Skill-oriented execution bridge.
|
||||
|
||||
Phase-1 delegates execution to ToolExecutor while emitting skill_ui events.
|
||||
Internal hooks: ``skill:before`` and ``skill:after`` (``type:action`` keys).
|
||||
"""
|
||||
|
||||
def __init__(self, *, config: ToolExecutionConfig | None = None):
|
||||
|
|
@ -62,6 +67,47 @@ class SkillExecutor:
|
|||
except Exception:
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def _hook_base_context(ctx: SkillExecutionContext) -> dict[str, Any]:
|
||||
out: dict[str, Any] = {
|
||||
"sessionId": ctx.session_id,
|
||||
"lang": ctx.lang,
|
||||
"specialist": ctx.specialist,
|
||||
"traceId": ctx.trace_id,
|
||||
"parentSpanId": ctx.parent_span_id,
|
||||
"runId": ctx.run_id,
|
||||
"attemptNo": ctx.attempt_no,
|
||||
"turnUuid": ctx.turn_uuid,
|
||||
}
|
||||
ws = str(ctx.workspace_dir or "").strip()
|
||||
if ws:
|
||||
out["workspaceDir"] = ws
|
||||
try:
|
||||
out["cfg"] = get_active_hooks_config()
|
||||
except Exception:
|
||||
out["cfg"] = {}
|
||||
return out
|
||||
|
||||
@staticmethod
|
||||
def _maybe_init_hooks_for_workspace(ctx: SkillExecutionContext) -> None:
|
||||
ws = str(ctx.workspace_dir or "").strip()
|
||||
if not ws:
|
||||
return
|
||||
try:
|
||||
initialize_hooks_runtime(cfg=None, workspace_dir=ws, eligibility=ctx.hook_eligibility)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def _summarize_result_for_hook(result: Any) -> dict[str, Any]:
|
||||
if not isinstance(result, dict):
|
||||
return {"_type": type(result).__name__}
|
||||
out: dict[str, Any] = {}
|
||||
for k in ("ok", "error_code", "source_provider", "source_version", "source_kind"):
|
||||
if k in result:
|
||||
out[k] = result.get(k)
|
||||
return out
|
||||
|
||||
def execute_skill_uses(
|
||||
self,
|
||||
*,
|
||||
|
|
@ -73,6 +119,8 @@ class SkillExecutor:
|
|||
should_stop: Optional[Callable[[], bool]] = None,
|
||||
signature_budget: int = 2,
|
||||
) -> tuple[list[dict[str, Any]], dict[str, tuple[dict[str, Any], int]]]:
|
||||
self._maybe_init_hooks_for_workspace(ctx)
|
||||
base_h = self._hook_base_context(ctx)
|
||||
for su in skill_uses or []:
|
||||
self._trace(
|
||||
ctx,
|
||||
|
|
@ -82,6 +130,18 @@ class SkillExecutor:
|
|||
"skill_call_id": str(getattr(su, "id", "") or ""),
|
||||
},
|
||||
)
|
||||
hctx = {
|
||||
**base_h,
|
||||
"skillName": str(getattr(su, "name", "") or ""),
|
||||
"skillCallId": str(getattr(su, "id", "") or ""),
|
||||
"arguments": dict(getattr(su, "arguments", {}) or {}),
|
||||
}
|
||||
trigger_hook_event(
|
||||
event_type="skill",
|
||||
action="before",
|
||||
session_key=str(ctx.session_id or "unknown"),
|
||||
context=hctx,
|
||||
)
|
||||
|
||||
def _emit(event: str, payload: dict[str, Any]) -> None:
|
||||
if on_tool_ui:
|
||||
|
|
@ -126,8 +186,25 @@ class SkillExecutor:
|
|||
"ok": bool((result or {}).get("ok")) if isinstance(result, dict) else None,
|
||||
"duration_ms": int(dur or 0),
|
||||
"error_code": str((result or {}).get("error_code") or "") if isinstance(result, dict) else "",
|
||||
"source_provider": str((result or {}).get("source_provider") or "") if isinstance(result, dict) else "",
|
||||
"source_version": str((result or {}).get("source_version") or "") if isinstance(result, dict) else "",
|
||||
"source_kind": str((result or {}).get("source_kind") or "") if isinstance(result, dict) else "",
|
||||
},
|
||||
)
|
||||
actx = {
|
||||
**self._hook_base_context(ctx),
|
||||
"skillName": str(getattr(su, "name", "") or ""),
|
||||
"skillCallId": str(getattr(su, "id", "") or ""),
|
||||
"arguments": dict(getattr(su, "arguments", {}) or {}),
|
||||
"durationMs": int(dur or 0),
|
||||
"resultSummary": self._summarize_result_for_hook(result),
|
||||
}
|
||||
trigger_hook_event(
|
||||
event_type="skill",
|
||||
action="after",
|
||||
session_key=str(ctx.session_id or "unknown"),
|
||||
context=actx,
|
||||
)
|
||||
return tool_messages, results_by_id
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue