Version: 0.9.59.dev.260430

后端:
1. 主动调度预览确认主链路落地——新增主动调度数据模型、DAO 与事件契约;接入 dry-run pipeline 与任务触发的 job upsert/cancel;新增 preview 查询与 confirm API,支持 apply_id 幂等确认并同步写入 task_pool 日程
2. 同步更新主动调度实施文档的阶段状态与验收记录

前端:
3. AssistantPanel 脚本层继续解耦——私有类型迁移到独立类型文件,并抽离会话、工具轨迹、思考摘要、任务表单等纯函数辅助逻辑;保持助手面板模板与样式不变,降低表现层回归风险
This commit is contained in:
LoveLosita
2026-04-30 12:05:15 +08:00
parent 1555042e80
commit e945578fbf
38 changed files with 10267 additions and 580 deletions

View File

@@ -0,0 +1,92 @@
package service
import (
"context"
"errors"
"time"
"github.com/LoveLosita/smartflow/backend/active_scheduler/candidate"
schedulercontext "github.com/LoveLosita/smartflow/backend/active_scheduler/context"
"github.com/LoveLosita/smartflow/backend/active_scheduler/observe"
"github.com/LoveLosita/smartflow/backend/active_scheduler/ports"
"github.com/LoveLosita/smartflow/backend/active_scheduler/trigger"
)
// DryRunResult 是 API dry-run / worker 测试入口可直接消费的同步结果。
type DryRunResult struct {
Context *schedulercontext.ActiveScheduleContext
Observation observe.Result
Candidates []candidate.Candidate
}
// DryRunService 编排主动调度 dry-run 主链路。
//
// 职责边界:
// 1. 固定执行 BuildContext -> Observe -> GenerateCandidates
// 2. 不调用 LLM、不写 preview、不发 notification、不正式写日程
// 3. 后续 API / worker 应复用该入口,避免出现第二套 dry-run 诊断逻辑。
type DryRunService struct {
builder *schedulercontext.Builder
analyzer *observe.Analyzer
generator *candidate.Generator
}
// NewDryRunService 创建主动调度 dry-run 服务。
func NewDryRunService(readers ports.Readers) (*DryRunService, error) {
builder, err := schedulercontext.NewBuilder(readers)
if err != nil {
return nil, err
}
return &DryRunService{
builder: builder,
analyzer: observe.NewAnalyzer(),
generator: candidate.NewGenerator(),
}, nil
}
// SetClock 注入测试时钟。
func (s *DryRunService) SetClock(clock func() time.Time) {
if s != nil && s.builder != nil {
s.builder.SetClock(clock)
}
}
// DryRun 执行主动调度同步诊断。
func (s *DryRunService) DryRun(ctx context.Context, trig trigger.ActiveScheduleTrigger) (*DryRunResult, error) {
if s == nil || s.builder == nil || s.analyzer == nil || s.generator == nil {
return nil, errors.New("DryRunService 尚未正确初始化")
}
// 1. 构造上下文:读取 task / schedule / feedback 的只读事实快照。
activeContext, err := s.builder.BuildContext(ctx, trig)
if err != nil {
return nil, err
}
// 2. 主动观测:生成 metrics、issues 和初步裁决,不生成正式变更。
observation := s.analyzer.Observe(activeContext)
// 3. 候选生成:只枚举第一版允许的确定性候选,压缩融合保持关闭。
candidates := s.generator.GenerateCandidates(activeContext, observation)
fallbackCandidateID := ""
if len(candidates) > 0 {
fallbackCandidateID = candidates[0].CandidateID
}
observation = s.analyzer.FinalizeDecision(observation, len(applicableCandidates(candidates)), fallbackCandidateID)
return &DryRunResult{
Context: activeContext,
Observation: observation,
Candidates: candidates,
}, nil
}
func applicableCandidates(candidates []candidate.Candidate) []candidate.Candidate {
result := make([]candidate.Candidate, 0, len(candidates))
for _, item := range candidates {
if item.CandidateType == candidate.TypeAddTaskPoolToSchedule || item.CandidateType == candidate.TypeCreateMakeup {
result = append(result, item)
}
}
return result
}