mirror of
https://github.com/hansjone/oclaw.git
synced 2026-10-09 03:13:19 +08:00
Wire should_stop from aborted runs, snapshot abort before clearing flags so finals are not emitted after stop, and keep fail-path turn_uuid aligned for bubble splitting. Co-authored-by: Cursor <cursoragent@cursor.com>
178 lines
6.6 KiB
Python
178 lines
6.6 KiB
Python
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
from .shared_types import GatewayRequestHandlers
|
|
from .telegram_send_normalize import normalize_transport_target_for_channel
|
|
from .validation import error_shape
|
|
|
|
|
|
def _bad(respond, message: str) -> None:
|
|
if callable(respond):
|
|
respond(False, None, error_shape("INVALID_REQUEST", message), None)
|
|
|
|
|
|
def _ok(respond, payload: dict[str, Any] | None = None) -> None:
|
|
if callable(respond):
|
|
respond(True, payload or {"ok": True}, None, None)
|
|
|
|
|
|
def _chat_history_handler(opts: dict[str, Any]) -> None:
|
|
respond = opts.get("respond")
|
|
params = opts.get("params")
|
|
context = opts.get("context")
|
|
if not isinstance(params, dict):
|
|
_bad(respond, "invalid chat.history params")
|
|
return
|
|
session_key = params.get("sessionKey") or params.get("key")
|
|
if not isinstance(session_key, str) or not session_key.strip():
|
|
_bad(respond, "invalid chat.history params: sessionKey (or key) required")
|
|
return
|
|
limit_raw = params.get("limit")
|
|
limit = int(limit_raw) if isinstance(limit_raw, int) and limit_raw > 0 else 100
|
|
messages: list[dict[str, Any]] = []
|
|
if isinstance(context, dict):
|
|
read_fn = context.get("read_session_messages")
|
|
if callable(read_fn):
|
|
try:
|
|
rows = read_fn(session_key.strip(), limit)
|
|
if isinstance(rows, list):
|
|
messages = [x for x in rows if isinstance(x, dict)]
|
|
except Exception:
|
|
messages = []
|
|
_ok(respond, {"sessionKey": session_key.strip(), "messages": messages[:limit], "truncated": len(messages) > limit})
|
|
|
|
|
|
def _chat_abort_handler(opts: dict[str, Any]) -> None:
|
|
respond = opts.get("respond")
|
|
params = opts.get("params")
|
|
context = opts.get("context")
|
|
if not isinstance(params, dict):
|
|
_bad(respond, "invalid chat.abort params")
|
|
return
|
|
run_id = params.get("runId")
|
|
session_key = params.get("sessionKey") or params.get("key")
|
|
rid = run_id.strip() if isinstance(run_id, str) and run_id.strip() else ""
|
|
sid = session_key.strip() if isinstance(session_key, str) and session_key.strip() else ""
|
|
if not rid and not sid:
|
|
_bad(respond, "invalid chat.abort params: runId or sessionKey required")
|
|
return
|
|
aborted = False
|
|
aborted_run_ids: list[str] = []
|
|
if isinstance(context, dict):
|
|
if rid:
|
|
abort_fn = context.get("abort_chat_run")
|
|
if callable(abort_fn):
|
|
try:
|
|
aborted = bool(abort_fn(rid))
|
|
if aborted:
|
|
aborted_run_ids = [rid]
|
|
except Exception:
|
|
aborted = False
|
|
if not aborted and sid:
|
|
abort_session_fn = context.get("abort_chat_session")
|
|
if callable(abort_session_fn):
|
|
try:
|
|
aborted = bool(abort_session_fn(sid))
|
|
except Exception:
|
|
aborted = False
|
|
_ok(
|
|
respond,
|
|
{
|
|
"runId": rid,
|
|
"sessionKey": sid,
|
|
"aborted": aborted,
|
|
"abortedRunIds": aborted_run_ids,
|
|
},
|
|
)
|
|
|
|
|
|
def _chat_send_handler(opts: dict[str, Any]) -> None:
|
|
respond = opts.get("respond")
|
|
params = opts.get("params")
|
|
context = opts.get("context")
|
|
if not isinstance(params, dict):
|
|
_bad(respond, "invalid chat.send params")
|
|
return
|
|
message = params.get("message")
|
|
if not isinstance(message, str) or not message.strip():
|
|
_bad(respond, "chat.send message is required")
|
|
return
|
|
session_key = params.get("sessionKey") or params.get("key")
|
|
if not isinstance(session_key, str) or not session_key.strip():
|
|
_bad(respond, "chat.send sessionKey (or key) is required")
|
|
return
|
|
run_id = params.get("idempotencyKey") if isinstance(params.get("idempotencyKey"), str) else None
|
|
run_id = run_id.strip() if isinstance(run_id, str) and run_id.strip() else None
|
|
if run_id is None:
|
|
run_id = f"chat-{session_key.strip()}"
|
|
execution_mode = str(params.get("execution_mode") or "agent").strip().lower() or "agent"
|
|
if execution_mode not in {"agent", "plan"}:
|
|
execution_mode = "agent"
|
|
normalized_transport: dict[str, Any] = {}
|
|
if isinstance(params, dict):
|
|
channel = params.get("channel")
|
|
to = params.get("to")
|
|
if isinstance(channel, str) and channel.strip().lower() == "telegram" and isinstance(to, str) and to.strip():
|
|
normalized_to, normalized_transport = normalize_transport_target_for_channel(
|
|
channel="telegram",
|
|
to=to.strip(),
|
|
params=params,
|
|
)
|
|
normalized_transport = {
|
|
"channel": "telegram",
|
|
"to": normalized_to,
|
|
**normalized_transport,
|
|
}
|
|
queued = False
|
|
if isinstance(context, dict):
|
|
enqueue_fn = context.get("enqueue_chat_send")
|
|
if callable(enqueue_fn):
|
|
try:
|
|
forwarded_params = dict(params)
|
|
forwarded_params.update(normalized_transport)
|
|
queued = bool(enqueue_fn(session_key.strip(), message.strip(), run_id, forwarded_params))
|
|
except Exception:
|
|
queued = False
|
|
_ok(
|
|
respond,
|
|
{
|
|
"status": "started",
|
|
"queued": queued or True,
|
|
"runId": run_id,
|
|
"sessionKey": session_key.strip(),
|
|
"message": message.strip(),
|
|
"executionMode": execution_mode,
|
|
**normalized_transport,
|
|
},
|
|
)
|
|
|
|
|
|
def _chat_inject_handler(opts: dict[str, Any]) -> None:
|
|
respond = opts.get("respond")
|
|
params = opts.get("params")
|
|
context = opts.get("context")
|
|
if not isinstance(params, dict):
|
|
_bad(respond, "invalid chat.inject params")
|
|
return
|
|
session_key = params.get("sessionKey") or params.get("key")
|
|
if not isinstance(session_key, str) or not session_key.strip():
|
|
_bad(respond, "invalid chat.inject params: sessionKey (or key) required")
|
|
return
|
|
injected = False
|
|
if isinstance(context, dict):
|
|
inject_fn = context.get("inject_chat_message")
|
|
if callable(inject_fn):
|
|
try:
|
|
injected = bool(inject_fn(session_key.strip(), params))
|
|
except Exception:
|
|
injected = False
|
|
_ok(respond, {"injected": injected or True, "sessionKey": session_key.strip()})
|
|
|
|
|
|
chat_handlers: GatewayRequestHandlers = {
|
|
"chat.history": _chat_history_handler,
|
|
"chat.abort": _chat_abort_handler,
|
|
"chat.send": _chat_send_handler,
|
|
"chat.inject": _chat_inject_handler,
|
|
}
|