mirror of
https://github.com/hansjone/oclaw.git
synced 2026-10-10 11:50:45 +08:00
Synthesize playbooks from structured prompts and inject prior-run context.
Legacy jobs with empty recipes (e.g. dying-gasp algorithms in prompt_text) now compile into durable playbooks on create/enqueue, and each run gets a short previous-run summary for continuity. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
4898adf7d0
commit
51eef09801
8 changed files with 516 additions and 27 deletions
|
|
@ -7,7 +7,12 @@ import uuid
|
|||
from datetime import datetime, timezone
|
||||
from typing import Any
|
||||
|
||||
from runtime.scheduler.recipe import load_recipe_from_job, recipe_has_playbook
|
||||
from runtime.scheduler.recipe import (
|
||||
load_recipe_from_job,
|
||||
recipe_has_playbook,
|
||||
recipe_is_empty,
|
||||
resolve_effective_playbook_recipe,
|
||||
)
|
||||
from runtime.scheduler.session_resolver import resolve_scheduled_session, resolve_scheduled_viewer_username
|
||||
from runtime.scheduler.turn_text import build_scheduled_turn_instruction
|
||||
from runtime.worker import ensure_worker_started
|
||||
|
|
@ -27,6 +32,70 @@ def _tick_interval_seconds() -> float:
|
|||
return 30.0
|
||||
|
||||
|
||||
def _load_previous_run_context(
|
||||
store: Any,
|
||||
*,
|
||||
job_id: str,
|
||||
tenant_id: str,
|
||||
current_run_id: str = "",
|
||||
) -> dict[str, Any] | None:
|
||||
"""Latest finished run for this job (excluding the run currently being queued)."""
|
||||
lister = getattr(store, "scheduled_job_run_list", None)
|
||||
if not callable(lister):
|
||||
return None
|
||||
try:
|
||||
rows = lister(job_id=str(job_id), tenant_id=str(tenant_id), limit=8) or []
|
||||
except Exception:
|
||||
return None
|
||||
cur = str(current_run_id or "").strip()
|
||||
for row in rows:
|
||||
rid = str(getattr(row, "id", "") or "")
|
||||
if cur and rid == cur:
|
||||
continue
|
||||
status = str(getattr(row, "status", "") or "").strip().lower()
|
||||
if status in {"queued", "running", ""}:
|
||||
continue
|
||||
return {
|
||||
"id": rid,
|
||||
"status": status,
|
||||
"finished_at": str(getattr(row, "finished_at", "") or "") or None,
|
||||
"created_at": str(getattr(row, "created_at", "") or "") or None,
|
||||
"reply_text": str(getattr(row, "reply_text", "") or ""),
|
||||
"error": str(getattr(row, "error", "") or ""),
|
||||
}
|
||||
return None
|
||||
|
||||
|
||||
def _maybe_backfill_job_recipe(
|
||||
store: Any,
|
||||
*,
|
||||
job: Any,
|
||||
recipe: dict[str, Any],
|
||||
) -> None:
|
||||
"""Persist synthesized recipe onto jobs that only stored a long prompt_text."""
|
||||
if not recipe_has_playbook(recipe):
|
||||
return
|
||||
if not recipe_is_empty(load_recipe_from_job(job)):
|
||||
return
|
||||
updater = getattr(store, "scheduled_job_update", None)
|
||||
if not callable(updater):
|
||||
return
|
||||
try:
|
||||
stamped = dict(recipe)
|
||||
src = dict(stamped.get("source") or {})
|
||||
if not str(src.get("compiled_at") or "").strip():
|
||||
src["compiled_at"] = datetime.now(timezone.utc).isoformat()
|
||||
src["compiled_from"] = str(src.get("compiled_from") or "prompt_text")
|
||||
stamped["source"] = src
|
||||
updater(
|
||||
job_id=str(getattr(job, "id") or ""),
|
||||
tenant_id=str(getattr(job, "tenant_id") or ""),
|
||||
patch={"recipe": stamped},
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def enqueue_scheduled_job_run(
|
||||
store: Any,
|
||||
*,
|
||||
|
|
@ -77,13 +146,27 @@ def enqueue_scheduled_job_run(
|
|||
agent_run_id = uuid.uuid4().hex
|
||||
prompt_text = str(getattr(job, "prompt_text", "") or "").strip()
|
||||
lang = str(getattr(job, "lang", "") or "en")
|
||||
recipe = load_recipe_from_job(job)
|
||||
stored_recipe = load_recipe_from_job(job)
|
||||
recipe = resolve_effective_playbook_recipe(
|
||||
recipe=stored_recipe,
|
||||
prompt_text=prompt_text,
|
||||
session_id=str(getattr(job, "source_session_id", "") or ""),
|
||||
)
|
||||
playbook = recipe_has_playbook(recipe)
|
||||
if playbook and recipe is not None:
|
||||
_maybe_backfill_job_recipe(store, job=job, recipe=recipe)
|
||||
previous_run = _load_previous_run_context(
|
||||
store,
|
||||
job_id=job_id,
|
||||
tenant_id=tenant_id,
|
||||
current_run_id=str(getattr(run, "id", "") or ""),
|
||||
)
|
||||
user_text = build_scheduled_turn_instruction(
|
||||
prompt_text=prompt_text,
|
||||
mode=mode,
|
||||
lang=lang,
|
||||
recipe=recipe if playbook else None,
|
||||
previous_run=previous_run,
|
||||
)
|
||||
viewer_username = resolve_scheduled_viewer_username(
|
||||
store,
|
||||
|
|
@ -105,6 +188,7 @@ def enqueue_scheduled_job_run(
|
|||
"text": user_text,
|
||||
"prompt_text": prompt_text,
|
||||
"recipe": recipe if playbook else {},
|
||||
"previous_run": previous_run or {},
|
||||
"attachments": [],
|
||||
"metadata": {
|
||||
"scheduled_job_id": job_id,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue