package gateway import "git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/model" // This file owns the conversation-tail helpers of the session orchestrator // (design/15 §2.2/§4): the bounded window that continuation resumes from and the // emptiness check that keeps a vacuous turn from marking a session busy. They are // pure functions over []model.Message, kept apart from the state machine so the // orchestrator file stays small. // boundedTail returns a fresh copy of the last maxTail messages, so continuation // stays incremental and bounded (design/15 §2.2, §4) and never aliases the input // slice or another state's tail. func (o *Orchestrator) boundedTail(msgs []model.Message) []model.Message { if len(msgs) <= o.maxTail { return append([]model.Message(nil), msgs...) } out := make([]model.Message, o.maxTail) copy(out, msgs[len(msgs)-o.maxTail:]) return out } // messagesEmpty reports whether a turn carries no usable text or image, so the // orchestrator does not mark a session busy on a vacuous message. func (o *Orchestrator) messagesEmpty(msgs []model.Message) bool { for _, m := range msgs { for _, c := range m.Content { if c.Type == model.ContentTypeText && c.Text != "" { return false } if c.Type == model.ContentTypeImage && c.ImageURL != "" { return false } } } return true } // concatMessages returns a new slice combining a and b (never reusing a or b // backing arrays) so the continuation tail is safe to hand to a concurrent run. func concatMessages(a []model.Message, b ...model.Message) []model.Message { out := make([]model.Message, 0, len(a)+len(b)) out = append(out, a...) out = append(out, b...) return out }