mirror of
https://github.com/memohai/Memoh.git
synced 2026-04-25 07:00:48 +09:00
feat(agent): add background task execution and notifications (#365)
This commit is contained in:
@@ -11,6 +11,7 @@ import (
|
||||
|
||||
sdk "github.com/memohai/twilight-ai/sdk"
|
||||
|
||||
"github.com/memohai/memoh/internal/agent/background"
|
||||
"github.com/memohai/memoh/internal/agent/tools"
|
||||
"github.com/memohai/memoh/internal/models"
|
||||
"github.com/memohai/memoh/internal/workspace/bridge"
|
||||
@@ -169,6 +170,23 @@ func (a *Agent) runStream(ctx context.Context, cfg RunConfig, ch chan<- StreamEv
|
||||
}
|
||||
}
|
||||
|
||||
// Drain background task notifications at step boundaries.
|
||||
// Each notification is injected as a user message so the model
|
||||
// discovers completed background work naturally.
|
||||
if cfg.BackgroundManager != nil {
|
||||
basePrepare := prepareStep
|
||||
baseSystem := cfg.System // capture original system prompt to avoid accumulation
|
||||
prepareStep = func(p *sdk.GenerateParams) *sdk.GenerateParams {
|
||||
if basePrepare != nil {
|
||||
if override := basePrepare(p); override != nil {
|
||||
p = override
|
||||
}
|
||||
}
|
||||
p = drainBackgroundNotifications(p, cfg.BackgroundManager, baseSystem, cfg.Identity.BotID, cfg.Identity.SessionID, a.logger)
|
||||
return p
|
||||
}
|
||||
}
|
||||
|
||||
opts := a.buildGenerateOptions(cfg, sdkTools, prepareStep)
|
||||
|
||||
retryCfg := cfg.Retry
|
||||
@@ -452,6 +470,22 @@ func (a *Agent) runGenerate(ctx context.Context, cfg RunConfig) (*GenerateResult
|
||||
if readMediaState != nil {
|
||||
prepareStep = readMediaState.prepareStep
|
||||
}
|
||||
|
||||
// Drain background task notifications at step boundaries (non-streaming).
|
||||
if cfg.BackgroundManager != nil {
|
||||
basePrepare := prepareStep
|
||||
baseSystem := cfg.System
|
||||
prepareStep = func(p *sdk.GenerateParams) *sdk.GenerateParams {
|
||||
if basePrepare != nil {
|
||||
if override := basePrepare(p); override != nil {
|
||||
p = override
|
||||
}
|
||||
}
|
||||
p = drainBackgroundNotifications(p, cfg.BackgroundManager, baseSystem, cfg.Identity.BotID, cfg.Identity.SessionID, a.logger)
|
||||
return p
|
||||
}
|
||||
}
|
||||
|
||||
opts := a.buildGenerateOptions(cfg, sdkTools, prepareStep)
|
||||
opts = append(opts,
|
||||
sdk.WithOnStep(func(step *sdk.StepResult) *sdk.GenerateParams {
|
||||
@@ -635,6 +669,38 @@ func toolStreamEventToAgentEvent(evt tools.ToolStreamEvent) StreamEvent {
|
||||
}
|
||||
}
|
||||
|
||||
// drainBackgroundNotifications non-blockingly drains pending background task
|
||||
// notifications for the given bot+session and injects them as user messages
|
||||
// into the next LLM step at step boundaries.
|
||||
func drainBackgroundNotifications(
|
||||
p *sdk.GenerateParams,
|
||||
mgr *background.Manager,
|
||||
baseSystem string,
|
||||
botID, sessionID string,
|
||||
logger *slog.Logger,
|
||||
) *sdk.GenerateParams {
|
||||
// Inject running tasks summary into system prompt so the model
|
||||
// knows about ongoing background work even after compaction.
|
||||
// Always start from baseSystem to avoid accumulating summaries across steps.
|
||||
if summary := mgr.RunningTasksSummary(botID, sessionID); summary != "" {
|
||||
p.System = baseSystem + "\n\n" + summary
|
||||
} else {
|
||||
p.System = baseSystem
|
||||
}
|
||||
|
||||
notifications := mgr.DrainNotifications(botID, sessionID)
|
||||
for _, n := range notifications {
|
||||
p.Messages = append(p.Messages, sdk.UserMessage(n.MessageText()))
|
||||
logger.Info("injected background task notification",
|
||||
slog.String("task_id", n.TaskID),
|
||||
slog.String("status", string(n.Status)),
|
||||
slog.Bool("stalled", n.Stalled),
|
||||
slog.String("bot_id", botID),
|
||||
)
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
func wrapToolsWithLoopGuard(tools []sdk.Tool, guard *ToolLoopGuard, abortCallIDs map[string]struct{}) []sdk.Tool {
|
||||
wrapped := make([]sdk.Tool, len(tools))
|
||||
for i, tool := range tools {
|
||||
|
||||
Reference in New Issue
Block a user