You've already forked agentic-coding-workflow
bugfix
This commit is contained in:
@@ -26,18 +26,20 @@ type MessageServiceConfig struct {
|
||||
}
|
||||
|
||||
type messageService struct {
|
||||
repo Repository
|
||||
storage storage.Storage
|
||||
hub StreamerHub
|
||||
auditor audit.Recorder
|
||||
log *slog.Logger
|
||||
mimeWhite map[string]bool
|
||||
maxFile int64
|
||||
maxMsg int64
|
||||
safetyPct float64
|
||||
repo Repository
|
||||
storage storage.Storage
|
||||
hub StreamerHub
|
||||
auditor audit.Recorder
|
||||
log *slog.Logger
|
||||
mimeWhite map[string]bool
|
||||
maxFile int64
|
||||
maxMsg int64
|
||||
safetyPct float64
|
||||
briefReader ContextReader // 可为 nil;非 nil 时 composeHistory 注入 FK 上下文
|
||||
}
|
||||
|
||||
// NewMessageService constructs a MessageService.
|
||||
// NewMessageService constructs a MessageService. briefReader 可为 nil:nil 时
|
||||
// composeHistory 完全保留旧行为(仅用 conv.SystemPrompt),不做 FK 上下文注入。
|
||||
func NewMessageService(
|
||||
repo Repository,
|
||||
st storage.Storage,
|
||||
@@ -45,21 +47,23 @@ func NewMessageService(
|
||||
auditor audit.Recorder,
|
||||
log *slog.Logger,
|
||||
cfg MessageServiceConfig,
|
||||
briefReader ContextReader,
|
||||
) MessageService {
|
||||
mimeWhite := make(map[string]bool, len(cfg.MimeWhitelist))
|
||||
for _, m := range cfg.MimeWhitelist {
|
||||
mimeWhite[m] = true
|
||||
}
|
||||
return &messageService{
|
||||
repo: repo,
|
||||
storage: st,
|
||||
hub: hub,
|
||||
auditor: auditor,
|
||||
log: log,
|
||||
mimeWhite: mimeWhite,
|
||||
maxFile: cfg.MaxFileBytes,
|
||||
maxMsg: cfg.MaxMsgBytes,
|
||||
safetyPct: cfg.SafetyPct,
|
||||
repo: repo,
|
||||
storage: st,
|
||||
hub: hub,
|
||||
auditor: auditor,
|
||||
log: log,
|
||||
mimeWhite: mimeWhite,
|
||||
maxFile: cfg.MaxFileBytes,
|
||||
maxMsg: cfg.MaxMsgBytes,
|
||||
safetyPct: cfg.SafetyPct,
|
||||
briefReader: briefReader,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -483,20 +487,52 @@ func (s *messageService) composeHistory(ctx context.Context, conv *Conversation,
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve the effective system prompt by merging conv.SystemPrompt with the
|
||||
// FK-derived context brief (requirement/issue/project mounts). The brief is
|
||||
// pre-bounded by the composer, so it cannot starve message history to zero
|
||||
// (truncateByTokens still reserves the remaining budget for messages).
|
||||
effectiveSystemPrompt := s.effectiveSystemPrompt(ctx, conv)
|
||||
|
||||
// Compute token budget: context window minus max output tokens, then apply safety margin.
|
||||
budget := model.ContextWindow - model.MaxOutputTokens
|
||||
budget = int(float64(budget) * (1 - s.safetyPct))
|
||||
|
||||
msgs = truncateByTokens(msgs, conv.SystemPrompt, budget)
|
||||
msgs = truncateByTokens(msgs, effectiveSystemPrompt, budget)
|
||||
|
||||
return llm.Request{
|
||||
SystemPrompt: conv.SystemPrompt,
|
||||
SystemPrompt: effectiveSystemPrompt,
|
||||
Messages: msgs,
|
||||
MaxOutputTokens: model.MaxOutputTokens,
|
||||
Reasoning: model.Capabilities.Reasoning,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// effectiveSystemPrompt 把会话静态 system prompt 与 FK 派生上下文简报合并。
|
||||
// briefReader 为 nil、会话无任何 FK 挂载、或简报为空时,直接返回 conv.SystemPrompt
|
||||
// (与旧行为完全一致)。合并是追加式的(FK 简报置于静态 prompt 之后),便于
|
||||
// 让旧会话(曾把角色 prompt 烘焙进 SystemPrompt)与新会话都能正常工作。
|
||||
func (s *messageService) effectiveSystemPrompt(ctx context.Context, conv *Conversation) string {
|
||||
if s.briefReader == nil || !conv.HasFKMount() {
|
||||
return conv.SystemPrompt
|
||||
}
|
||||
fkBrief, err := s.briefReader.BriefForConversation(ctx, conv)
|
||||
if err != nil {
|
||||
if s.log != nil {
|
||||
s.log.Warn("chat.compose_history.brief_failed",
|
||||
"conversation_id", conv.ID, "err", err.Error())
|
||||
}
|
||||
return conv.SystemPrompt
|
||||
}
|
||||
fkBrief = strings.TrimSpace(fkBrief)
|
||||
if fkBrief == "" {
|
||||
return conv.SystemPrompt
|
||||
}
|
||||
if strings.TrimSpace(conv.SystemPrompt) == "" {
|
||||
return fkBrief
|
||||
}
|
||||
return conv.SystemPrompt + "\n\n" + fkBrief
|
||||
}
|
||||
|
||||
// truncateByTokens walks messages backwards and drops oldest messages until the
|
||||
// estimated token count (system prompt + messages) fits within budget.
|
||||
func truncateByTokens(msgs []llm.Message, systemPrompt string, budget int) []llm.Message {
|
||||
|
||||
Reference in New Issue
Block a user