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

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

93 lines
3.0 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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
}