Skip to content

feat(adk): dream improvements - #1124

Open
N3kox wants to merge 115 commits into
alpha/10from
feat/dream
Open

feat(adk): dream improvements#1124
N3kox wants to merge 115 commits into
alpha/10from
feat/dream

Conversation

@N3kox

@N3kox N3kox commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

What type of PR is this?

Check the PR title.

  • This PR title match the format: <type>(optional scope): <description>
  • The description of this PR title is user-oriented and clear enough for others to understand.
  • Attach the PR updating the user documentation if the current PR requires user awareness at the usage level. User docs repo

(Optional) Translate the PR title into Chinese.

(Optional) More detailed description for this PR(en: English/zh: Chinese).

en:
zh(optional):

(Optional) Which issue(s) this PR fixes:

(optional) The PR that updates user documentation:

Change-Id: I09b9754cf51f10fe662fb6cb33935ad92a4d1656
…mance

- Add HumanReadableSerializer that produces human-readable JSON output
- Add GobSerializer for comparison benchmarks
- Refactor serialization_test.go to use table-driven tests for both serializers
- Add comprehensive benchmarks comparing InternalSerializer, HumanReadableSerializer, and GobSerializer

Performance improvements over InternalSerializer:
- 30-68% faster marshal/unmarshal operations
- 17-49% less memory allocation
- 30-76% fewer allocations
- 33-59% smaller serialized output size

The HumanReadableSerializer uses standard JSON encoding with type annotations
only for interface{} fields, making the output both human-readable and
type-preserving for round-trip serialization.~

Change-Id: Ifeae12484fc73b74067a0ac3a496e73e0674b975
Change-Id: Iecbcfd8ab5905e61df9a5578e8d3c99387dace85
Change-Id: I5ac5d8b60be92a28d1e57929f03fa4201649fbd0
Change-Id: I318ff16a6bd81bb57b38791eeea17b3385b1fde4
Change-Id: Iefb047967d4cb4796edd363e7e6ba0a9c4f49148
Change-Id: I3de2ba37914fe779cd880b38ce067acf5fbde7c8
The runner previously wrote the interrupt checkpoint inline, before the
session event persister had flushed. This risks a checkpoint that
references events not yet durable in the SessionStore.

Introduce deferredRunnerCheckpoint: on the interrupt/cancel path the
checkpoint payload is captured but not written until finalize() confirms
persister.closeAndWait succeeded. If event persistence failed, the
checkpoint write is skipped entirely (fail-closed).

Also adds regression tests for checkpoint ordering invariants and the
InMemoryStore checkpoint round-trip.

Change-Id: Ib29263add4a261513f1349a9173a913c74ee65ef
Resume previously assumed the agent_tool interrupt state was always the
JSON envelope (agentToolInterruptState) introduced for SessionID-based
event filtering. Pre-envelope checkpoints stored raw gob bridge bytes,
so json.Unmarshal failed outright and resume errored.

Try the JSON envelope first; on parse failure or empty BridgeCheckpoint,
treat the raw bytes as the legacy bridge checkpoint and synthesize a
fresh childSessionID. Pre-envelope checkpoints predate session
persistence and have no parent-session filter to coordinate with, so
the synthesized ID is harmless.

Un-skip the v0.7.37, v0.8.2, v0.8.3, v0.8.4 compat fixtures so the
resume path is now exercised for real on-disk legacy bytes.

Change-Id: I5000424ba028e9fb9fe3843ea9673a3dd23a7860
Merge AfterCursor and PageToken into a single `After` field in
LoadEventsOptions. Rename NextPageToken to `Next` in LoadEventsResult.
Rename afterEventCursor to afterCursor in LoadLatestTurnEnd returns.

One concept, one name: the caller seeds After from LoadLatestTurnEnd,
then passes res.Next back as After on subsequent pages.

Change-Id: Icd0960940b7f69938e2b4a5062d220c709bc0e66
The Options suffix implies a functional-options pattern; Request better
reflects the struct-pointer parameter style and pairs with LoadEventsResult.
Also removes dead variable in conformance test.

Change-Id: Ia28d8a1f92f4307182508dfba56e2b3c454acfa4
…ializer

Correct the tool-call middleware ordering in chatmodel.go doc comments
(cancelMonitor wraps around user handlers, not inside). Add architectural
doc comment to newTypedInvokableAgentToolRunner explaining why AgentTool
lacks its own SessionStore. Remove the unused GobSerializer type alias
from schema/serialization.go (already aliased internally). Update
conformance test variable names to match LoadEventsRequest rename.

Change-Id: I2251e190c9d343dbd1e57cbebfc236840be4a0fa
…igurable page size

AppendEvents failures in the session persister now retry with exponential
backoff (default 3 retries, 50ms initial delay, 2x multiplier, 25% jitter)
before latching the error. This prevents transient store failures from
causing irrecoverable session log corruption.

Also extracts the hard-coded page-size=100 in reconstructFromEventLog and
replayTailEvents into the configurable LoadPageSize field on
SessionPersistenceConfig (default 100, preserving current behavior).

New config fields on SessionPersistenceConfig:
- MaxFlushRetries (default 3, set to -1 to disable)
- FlushRetryInitialBackoff (default 50ms)
- LoadPageSize (default 100)

Change-Id: I407b6038fd61df74420a5ddd16ce8ec0f8860948
…preservation

When a SessionStore is configured, the Runner now reuses the exact tool
list from the previous turn's TurnEndState to feed the model, ensuring
byte-exact prompt cache hits across turns. The ToolSearch middleware
skips its initialization strip logic when it detects pre-seeded tool
infos via the new ToolInfosPreSeededKey RunLocalValue.

Users can opt out with WithRefreshToolInfos() when tools have genuinely
changed between turns.

Change-Id: I95585e105d41689f08116325478f21552482b79b
Change-Id: Id35944259eaee9254bc9ffa7288fe6bc43bc021b
…ion mode

When sessions are enabled, TurnEndState.Messages carries the previous
turn's system message. The Runner prepends this history to the new input,
then defaultGenModelInput unconditionally prepends a fresh system message,
causing duplication. Strip the leading system message from history before
prepending the fresh instruction so that dynamic SessionValues are always
re-evaluated without duplicating the system prompt.

Change-Id: Id14acc6aa5f2941ea64c4de82393a36bcea2fb36
…to single file

Merge three scattered test files (human_readable_test.go,
human_readable_edge_test.go, schema/toolinfo_humanreadable_test.go) into
one unified file in internal/serialization. Replace schema.ToolInfo usage
with a local mock type to eliminate the circular dependency, and remove
duplicate test cases that exercised identical code paths.

Change-Id: I0b1fa9d001201382917abb8bf4b3bd220ffdf13d
Reduce SessionStore from 4 methods to 2 (AppendEvents + LoadEvents) by
merging TurnEndState into the event log as a SessionEvent variant. This
eliminates duplicate message storage and unifies reconstruction into a
single reverse-scan algorithm.

- Rewrite session/in_memory_store.go with forward/reverse pagination
- Remove SaveTurnEnd/LoadLatestTurnEnd from all test mocks
- Replace encodeTurnEndState/decodeTurnEndState with encodeSessionEvent
- Replace reconstructFromEventLog with reconstructSessionState

Change-Id: I979e88727dd33bfaa6983241b7e90b7bbbe040e0
Replace the opaque integer-index cursor with the per-event UUIDv4
event_id, giving SSE consumers a stable identity for Last-Event-ID
resume and de-duplication. AppendEvents becomes idempotent
(first-write-wins on duplicate event_id) so persister retries no longer
double-write. Introduce ErrInvalidEventID and ErrEventIDOutOfRange
sentinels with isProtocolError classification, so the persister
fail-fasts protocol violations while still retrying infrastructure
errors. Stores treat event_id as an opaque non-empty string; UUIDv4 is
the Runner allocation convention, not a store-enforced format.

Change-Id: I70c277af5505d57c7354e8056372c1021d6009eb
Allocate EventID once at the AgentEvent emission boundary (execCtx.send)
so user-land stream consumers and persisted SessionStore records share
the same identity. SSE adapters can now use AgentEvent.EventID as the
SSE id: line, and clients reconnecting with Last-Event-ID can pass that
value directly to SessionStore.LoadEvents(After: ...) without going
through the store.

toSessionEvent reuses event.EventID instead of minting a new UUID, with
a defensive fallback for test fixtures that construct events directly.
makeInputSessionEvent is unchanged (no upstream AgentEvent).

Change-Id: I525675f948aef55d6afc0dcdc46bd7e255a68311
Persist lifecycle, model span, retry/failover, and tool observation events through the managed session log while preserving the EventID identity contract between live AgentEvents and stored SessionEvents.

Add focused coverage for replay boundaries, timeline exposure, retry/failover observability, model usage metadata, gob compatibility, and persistence guards.

Change-Id: Id78c137b40265324d315237067a82e8ea1ffc8ef
Change-Id: I84f8155f66807d6e229741b9f5b3f6345f58f1d5
Change-Id: Id53b725579b6e7ceaeb9f4edd3c65a11e092f8e1
Delay loaded checkpoint abandonment until fresh-turn agent preparation succeeds, and fail before execution if checkpoint deletion fails.

Fold durable attack coverage into normal ADK test suites and remove the standalone attack test file.

Change-Id: I43fa3cd8d25dbd4ea3d1ca4c845191fa5a9c503d
Change-Id: I6714153ef2f58bed54dce9dfe3fe73bace3f6f04
Change-Id: I6fdded746daacdaa973c1914bfb789230254b17f
Change-Id: Ib830e9e647f9f2f9429d50136153cd253305c382
Add a JSONL-backed session store, expose schema serializers for session persistence, and wire configurable session event serialization through runner reconstruction and persistence.

Change-Id: Ic6bf3905162c2f730428cf1bc5b65f1c8d8567cd
Add exported helper docs, reduce TurnLoop.run length by extracting input collection, and group model span end parameters to satisfy revive in CI.

Change-Id: Ie23013d91d3d502d50c3787fa3e9ec191feeaf35
Replace the blocking ErrPendingSessionCheckpoint behavior with automatic
checkpoint deletion via deleteCheckPointIfSupported. Calling Run is an
explicit intent to start a new turn; session correctness is guaranteed
by event log replay, not checkpoint presence.

Change-Id: I9a19b4b44c90d134c28a4d2cc2746cd44d705144
N3kox and others added 14 commits June 29, 2026 18:55
…nt model (#1106)

* refactor(adk): introduce SessionEventVariant and simplify session event model

- Replace SessionEvent pointer in TypedAgentEvent with SessionEventVariant sum type
- SessionEventVariant carries either materialized SessionEvent or MessageStreamRef for streaming
- Move SessionID from durable SessionEvent to live SessionEventVariant metadata
- Remove EventID and Timestamp from TypedAgentEvent (already in SessionEvent/MessageStreamRef)
- Simplify store interface: pass sessionID as first-class arg, remove AppendEventsRequest
- Rename UserObservation/UserInterrupt/AgentInterrupt to Cancel/Interrupt
- Remove SessionRunStateRescheduled (only running and idle remain)

Change-Id: Iab036b8ac7c445fde8cb54215aefab12f51eef1c

* fix(adk): resolve dead code in toSessionEventChecked and add doc comments

- Remove unused SessionEvent construction in fallback path of toSessionEventChecked
- Add doc comments for MessageStreamRef, CancelEvent, and SessionEventVariant methods

Change-Id: I4ba1ff5faf2abfa13ddc59fafb065a73a17d0345

* fix(adk): snapshot leading system message before genModelInput

- deep copy leading system message before calling GenModelInput to detect
  in-place mutations (Extra map, Content, etc.) that would otherwise be
  missed by sameSystemMessage comparison
- remove dead code in streaming-complete persistence branch (persistMV /
  persistOutput were constructed and immediately discarded)
- add regression tests for in-place Extra and Content mutation in GenModelInput
- document SessionEventVariant invariant and turn_end legacy compatibility

Change-Id: Id8d6d3f91e013642a7573502ae4ad562239cbc07

* refactor(adk): replace turn_end bypass with generic unknown-kind tolerance

replace hardcoded "turn_end" compatibility shim with a general mechanism
that tolerates any unrecognized event kind carrying no
payload:

- add knownSessionEventKinds set and isKnownSessionEventKind helper
- add countActiveSessionEventPayloads helper for structural check
- tolerate unknown kinds with zero recognized payloads (forward/backward compat)
- known kinds with missing/wrong payloads still error as before

Change-Id: I48a74dc546a3c3b5a1f21a6a9a23ccae32aaf7fe

* chore(adk): remove unreachable gob registrations for SessionEventVariant and MessageStreamRef

checkpoint sanitizers strip SessionEventVariant before gob encoding,
and the store serializer only encodes *SessionEvent[M]. add a comment
explaining why these types are not registered.

Change-Id: I55e2726a95ed3e31abc28719b8ed4c2f8106114e

* test(adk): align variant serialization test with durable payload

Change-Id: I0b9607028aebc61976529082b67f2d9751196563
…ng (#1107)

* feat(adk): background-task manager with subagent/filesystem/deep wiring

Introduce a shared, domain-agnostic background-task engine and wire it into the
subagent and filesystem middlewares and the deep prebuilt agent.

adk/backgroundtask (engine):
- Manager tracks foreground/background/auto-background runs under one task-ID
  space; Run blocks with an optional foreground budget, then either completes,
  auto-backgrounds (Config.ShouldAutoBackground), or times out.
- RunStream + StreamWorkFunc forward a run's output to the caller in real time
  during the foreground phase, then on auto-background inject a generic notice
  and drain the rest into the task result.
- Cancellation records a reason on Task.Error and the foreground caller reports
  StatusCanceled (not a StatusFailed ctx error).
- Task ids are TaskType_base62(int64), where the int64 packs a ms timestamp, a
  per-ms sequence (spinning to the next ms on overflow), and random low bits —
  unique within a process and self-describing by type.
- Optional OutputStore persists completed results (filesystem.Backend satisfies
  it directly); WaitForTask/WaitAllDone for lifecycle waits.

adk/middlewares/backgroundtask (control tools):
- Injects task_output/task_stop once, bound to a Config{Manager}. task_output
  supports CC-aligned block/timeout inputs.

adk/middlewares/subagent + filesystem:
- subagent agent tool and filesystem execute tool route through a shared Manager
  when configured, gaining run_in_background; the streaming execute tool streams
  its foreground output via RunStream. filesystem.Shell now documents the
  ctx-cancellation contract.

adk/prebuilt/deep:
- deep.New accepts a Manager, wiring it into the top-level subagent + filesystem
  middlewares and injecting the control tools once; sub-agents stay
  foreground-only. Replaces the old task_tool with the subagent middleware.

Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>

* feat: adjust background.Manager

* refactor(adk): clarify background task events

* feat: simplify done check

* feat: reduce one goroutine for direct run_in_background

* refactor: enchance background task with direct run_in_background

* refactor(adk): make background task output file worker-owned

The background-task Manager previously declared an output file globally and
wrote it once at completion, which broke its "interim output" promise and made
the file redundant with Task.Result. Move output-file ownership to the launching
adapters (execute / agent tools): the Manager only records RunInput.OutputFile,
while the worker writes — shell runs tee interim output as it streams, sub-agent
runs append their final result.

- backgroundtask: drop Config.OutputStore/OutputDir and persistOutput; add
  RunInput.OutputFile (path only, Manager never writes)
- filesystem: add Appender optional interface + AppendRequest (InMemoryBackend
  implements it); output files require an Appender, no rewrite fallback
- bundle Manager + output config into a nested BackgroundConfig across the
  filesystem, subagent, and deep configs
- name output files after the launching tool-call id (matching Task.ToolUseID),
  with a uuid fallback when absent
- task_output's formatTask points at the file when present instead of inlining
  the result

Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>

* feat: support mark output file

* fix: golangci-lint

* refactor(adk): hand WorkFunc a TaskInfo and key output-file failures by id

The launcher needs the Manager-assigned task id at write time to report an
output-file write failure, but the id is generated inside createTask, after the
work closure is already built. Pass a TaskInfo (read-only snapshot of
creation-time identity) as an explicit WorkFunc/StreamWorkFunc parameter so the
work receives the id directly; MarkOutputFileUnreliable then keys by id (O(1)
map lookup) instead of scanning all tasks by output-file path.

Also make the failed-write reporting honest: when a write fails, neither the
partial file nor the in-memory Result is the authoritative complete output
(Result may be empty while the task runs, or a partial projection of the file
for sub-agent runs). formatTask and the OutputFileErr doc no longer claim Result
is always the full copy.

Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4 <noreply@anthropic.com>
@@ -0,0 +1,117 @@
/*
* Copyright 2026 CloudWeGo Authors

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚨 Breaking API Changes Detected

Package: github.com/cloudwego/eino/adk/middlewares/automemory/dream

Incompatible changes:

  • Config.Schedule: removed
  • New: changed from func(context.Context, *Config[M]) (github.com/cloudwego/eino/adk.TypedChatModelAgentMiddleware[M], error) to func(context.Context, *MiddlewareConfig[M]) (github.com/cloudwego/eino/adk.TypedChatModelAgentMiddleware[M], error)
  • NewLocalStore: removed
  • OnError: changed from func(context.Context, string, error) to func(context.Context, ErrorStage, error)
  • Run: changed from func(context.Context, *Config[M], *RunRequest) error to func(context.Context, *Config[M], *RunRequest) (string, error)
  • ScheduleConfig: removed
  • Store: removed
Review Guidelines

Please ensure that:

  • The changes are absolutely necessary
  • They are properly documented
  • Migration guides are provided if needed

⚠️ Please resolve this thread after reviewing the breaking changes.

@codecov

codecov Bot commented Jun 30, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 75.56326% with 141 lines in your changes missing coverage. Please review.
⚠️ Please upload report for BASE (alpha/10@4dc69f1). Learn more about missing BASE report.

Files with missing lines Patch % Lines
adk/middlewares/automemory/dream/middleware.go 70.44% 65 Missing and 42 partials ⚠️
adk/middlewares/automemory/dream/store.go 80.19% 12 Missing and 8 partials ⚠️
adk/middlewares/automemory/dream/api.go 83.67% 4 Missing and 4 partials ⚠️
adk/middlewares/automemory/dream/lifecycle.go 90.24% 3 Missing and 1 partial ⚠️
adk/middlewares/automemory/dream/config.go 90.90% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##             alpha/10    #1124   +/-   ##
===========================================
  Coverage            ?   81.66%           
===========================================
  Files               ?      190           
  Lines               ?    30903           
  Branches            ?        0           
===========================================
  Hits                ?    25237           
  Misses              ?     3827           
  Partials            ?     1839           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

shentongmartin and others added 3 commits June 30, 2026 19:11
…rated leading system messages (#1121)

* fix(schema): remove gob registration for map[string]any and []any

Registering map[string]any / []any with custom _eino_ prefixed names in
init() causes gob to panic when other libraries (e.g. go-openapi/spec)
register the same types with their default names, since gob enforces
one name per concrete type globally.

These registrations were added preemptively in commit 318c253 (model
timeout feature) so that GobSerializer could round-trip nested maps/
slices inside Extra/MetaData fields. However:
- No eino-internal code puts nested map[string]any / []any into Extra
- All existing tests pass without them
- The conflict with third-party libraries is a real init-time failure

GobSerializer will still fail at runtime if users store nested
map[string]any / []any in Extra, but that is a runtime error surface
rather than an unconditional init-time panic.

Change-Id: Id4594751cb0a7ffb4666e07749ee2104f04a0267

* refactor(adk): remove session turn ids

Use committed idle event IDs as rollback boundaries and remove runner-side turn ID generation, checkpointing, and session event stamping.

Change-Id: Ica138789eae5cc901a9af23572d5f6aee801b416

* fix(adk): stop persisting runtime generated leading system messages in session events

Generated leading system messages from GenModelInput are runtime model
input scaffolding, not durable conversation history. They should be
recalculated on each run via applyBeforeAgent -> GenModelInput rather
than reconstructed from SessionEventStore.

- Add runtime provenance marker (_eino_adk_runtime_generated_system_message)
  to distinguish generated vs caller-supplied leading system messages
- Remove syncLeadingSystemMessageSessionEvent from all 3 run paths
  (no-tools, message ReAct, agentic ReAct)
- Summarization Middleware strips marked messages from MessagesReplaced
  event payload while preserving them in runtime state
- Caller-supplied leading system messages (unmarked) remain durable

Change-Id: I4a9ddd895b1d5fd32ad15345e3f7d43417342f4d

* chore: format code

Change-Id: I4d8b7e4d78f688255142a1823462ca8864d8468d

* refactor(adk): remove TurnLoop managed resume mode

Remove the redundant managed interrupt resume mode where business
interrupts kept the TurnLoop alive and waited for explicit Resume().
Business interrupts now always exit with *InterruptError and persist
a checkpoint when Store + CheckpointID are configured, consistent with
the normal interrupt-exit path.

Restored checkpoint resume via GenResume remains intact.

Deleted:
- TurnLoopInterruptMode type and constants
- InterruptMode and ResumeWaitTimeout from TurnLoopConfig
- TurnLoop.Resume() method and sentinel errors
- turnLoopPendingResumeSource enum and managed-only fields/helpers
- Managed parking loop in takePendingResume
- Managed-mode branch in run() and proxy iterator
- Resume-wait watcher and timeout logic
- InterruptContexts from turnLoopCheckpoint
- ~2000 lines of managed-mode tests and helpers

Change-Id: I4cc772012939ed8d0c768d00b9472ea8887f937f

* fix(adk): use json canonical comparison for model context tool equality

reflect.DeepEqual incorrectly reports tool change when persisted ToolInfo
numbers decoded as float64 differ from runtime int values, causing
redundant model context events every model call. Fall back to comparing
canonical JSON form to keep semantic equivalence across persist/reload.

Change-Id: I3baffe7decdf0d30217ccf45db354dc01a7779f5

* refactor(adk): skip persisting ModelContext session event

Change-Id: I7ebc6ceb603b4d236c1978a5db2e64e6d5afb413
#1123)

fix(summarization): extract messageUserTextContent helper to prefer UserInputMultiContent

Refactored user text content extraction into a reusable internal helper
that prioritizes UserInputMultiContent text parts (joined with "\n")
over the raw Content field. Applied it in both getUserMsgTextContent
and extractSkillInfos to ensure consistent behavior.
…#1091)

* chore: ensure that tool calls match tool results and correct comments

* chore: adjust condition
@@ -0,0 +1,132 @@
/*
* Copyright 2026 CloudWeGo Authors

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚨 Breaking API Changes Detected

Package: github.com/cloudwego/eino/adk

Incompatible changes:

  • RollbackSessionOptions.ExpectedHeadEventID: removed
  • WithRollbackSessionExpectedHeadEventID: removed
Review Guidelines

Please ensure that:

  • The changes are absolutely necessary
  • They are properly documented
  • Migration guides are provided if needed

⚠️ Please resolve this thread after reviewing the breaking changes.

shentongmartin and others added 2 commits July 1, 2026 18:05
@@ -0,0 +1,132 @@
/*
* Copyright 2026 CloudWeGo Authors

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚨 Breaking API Changes Detected

Package: github.com/cloudwego/eino/compose

Incompatible changes:

  • AddressSegmentMiddleware: removed
  • ToolMiddleware.Name: removed
Review Guidelines

Please ensure that:

  • The changes are absolutely necessary
  • They are properly documented
  • Migration guides are provided if needed

⚠️ Please resolve this thread after reviewing the breaking changes.

@shentongmartin
shentongmartin force-pushed the alpha/10 branch 3 times, most recently from 509e919 to 9a6766d Compare July 22, 2026 09:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

5 participants