ReMe/reme/schema/dream.py
imrewce 354837f9af
feat(proactive): separate proactive refresh from auto dream (#488)
* refractor(proactive): upgrade proactive feature with disentangled job and steps

* refactor(proactive): apply audit fixes

- rename read-side job 'proactive' -> 'proactive_read' (less confusing vs the refresh pipeline)
- drop dedicated agent_wrapper.proactive; extraction reuses the default wrapper
- simplify schema: remove unused ProactiveExtractOutput/TopicUpdate, drop resource_paths
- extract no longer scans resource/ directly (daily notes already carry resource content)
- update tests and docs accordingly

* feat(proactive): strict extract-output gate and prompt total budget

- parse_extract_reply now requires a contract section (follow_ups/extends/updates
  as a list); non-empty replies with misspelled section names trigger the
  existing one-shot retry instead of silently checkpointing changed files
- pack_paths gains max_total_chars; extract packs newest daily material first,
  keeps the first file on overflow, and records omitted files in a trailer
  (default budget 300000 chars, configurable via max_total_chars)
- tests: schema gate unit, schema-error retry e2e, budget unit + e2e

* feat(proactive): add scenario-card plan step and generative agenda step

* feat(proactive): digest-personal profile personalization and leaner LLM contract

- extract/plan/agenda now draw a user profile block from <digest_dir>/personal/*.md
  (frontmatter description + body excerpt, per-file budget, profile.md fallback)
- all daily access honours the configured daily_dir (prompt paths parameterized,
  config-driven fallbacks) so workspaces using e.g. memory/ work unchanged
- schema trim: drop dead fields errors/material_paths, carry_forward_all -> count
- shrink LLM output contract: new topics emit title/reason/confidence/paths only;
  keywords removed end-to-end, evidence derived from paths[0] (updates keep it)

* fix(proactive): skip checkpoint when extract reply stays unusable after retry

Two consecutive unparseable replies now short-circuit the round without
checkpointing, so the same material is retried next round instead of being
silently consumed (closes the residual audit #1 gap: the structural gate
detected schema-wrong output but a double failure still checkpointed).

* fix(proactive): replace running bool with reference-counted job activity tracker for the idle gate

* refactor(proactive): remove job activity tracking and idle gate, restore job tree to upstream

* fix(proactive): address second audit round (readonly reader, mtime checkpoint, wider fallbacks, profile containment, horizon content, expiry boundary)

* refactor(dream): strip interests.yaml ownership from dream, proactive is now the sole writer

* refactor(dream): separate proactive topic generation

* ci: update renamed auto dream smoke test

* fix(proactive): complete refresh migration and docs

---------

Co-authored-by: jinli.yl <jinli.yl@alibaba-inc.com>
2026-09-07 17:23:37 +08:00

69 lines
2.7 KiB
Python

"""Auto-dream schemas."""
from typing import Literal
from pydantic import BaseModel, Field
from ..enumeration import DreamBucketEnum
# ProactiveResult moved to reme.schema.proactive; re-exported for import compatibility.
from .proactive import ProactiveResult # noqa: F401 # pylint: disable=unused-import
class DreamUnit(BaseModel):
"""One cross-file memory unit emitted by global extract."""
name: str = Field(description="Short kebab-case handle for the abstraction.")
bucket: DreamBucketEnum = Field(description="Digest bucket; unknown raw values route to wiki before validation.")
summary: str = Field(description="Grounded abstraction summary with evidence pointers.")
paths: list[str] = Field(default_factory=list, description="Workspace-relative source paths.")
class DreamExtractOutput(BaseModel):
"""Structured output for ``dream_extract_step``."""
units: list[DreamUnit] = Field(default_factory=list)
class IntegrateOutcome(BaseModel):
"""Structured output for one unit integration."""
action: Literal["CREATE", "CORROBORATE", "REFINE", "CORRECT"] = Field(description="Write decision.")
target_path: str = Field(description="Digest path written or edited.")
note: str = Field(default="", description="Short summary of what landed.")
class DreamState(BaseModel):
"""Shared state passed across the dream steps."""
date: str = ""
dates: list[str] = Field(default_factory=list)
scan_days: int = 2
hint: str = ""
daily_dir: str = ""
workspace: str = ""
files_scanned: int = 0
files_unchanged: int = 0
files_changed: int = 0
files_deleted: int = 0
changed_paths: list[str] = Field(default_factory=list)
unchanged_paths: list[str] = Field(default_factory=list)
deleted_paths: list[str] = Field(default_factory=list)
existing: dict[str, float] = Field(default_factory=dict)
indexed: dict[str, float] = Field(default_factory=dict)
units: list[dict] = Field(default_factory=list)
extract_summary: str = ""
integrate_results: list[dict] = Field(default_factory=list)
skipped_units: list[dict] = Field(default_factory=list)
nodes_created: list[str] = Field(default_factory=list)
nodes_updated: list[str] = Field(default_factory=list)
modified_paths: list[str] = Field(
default_factory=list,
description="Durable digest files detected as created or changed during this run.",
)
failed_units: list[dict] = Field(default_factory=list)
failed_paths: list[str] = Field(default_factory=list)
checkpoint_paths: list[str] = Field(default_factory=list)
warnings: list[str] = Field(default_factory=list)
errors: list[str] = Field(default_factory=list)
summary: str = ""