mirror of
https://github.com/RuoJi6/ARTEX.git
synced 2026-11-04 19:43:50 +08:00
fix: 自动标题取消语义、清洗循环与拒绝占位回写
spawn_task 标题 20s 超时且父 ctx 取消不建任务;清洗循环剥前缀内外引号; 异步标题拒绝「新对话」并用条件更新防手改竞态;会话 profile 失败回退全局。
This commit is contained in:
1 parent
6c781f21f4
commit
3d3ee8827e
6 files changed
+93
-39
No files matched your search
@@ -12,3 +12,14 @@ func TestAutoTitleMayReplace(t *testing.T) {
|
||||
t.Fatal("manual title must not be overwritten")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAutoTitleIsUsable(t *testing.T) {
|
||||
for _, generated := range []string{"", "新对话"} {
|
||||
if autoTitleIsUsable(generated) {
|
||||
t.Fatalf("placeholder title %q must be rejected", generated)
|
||||
}
|
||||
}
|
||||
if !autoTitleIsUsable("整理项目设置") {
|
||||
t.Fatal("generated title should be accepted")
|
||||
}
|
||||
}
|
||||
+20
-14
@@ -17,6 +17,7 @@ import (
|
||||
"github.com/Autumn-27/artex/db"
|
||||
"github.com/Autumn-27/artex/intercept"
|
||||
"github.com/Autumn-27/artex/sidequestion"
|
||||
"github.com/Autumn-27/norma/llm"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -469,33 +470,34 @@ func (s *Server) pgSendConversationMessage(w http.ResponseWriter, r *http.Reques
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||||
defer cancel()
|
||||
profileID := s.effectiveProfileForAgent(agentKey, pinnedProfile)
|
||||
if profileID == nil {
|
||||
if active, err := s.m.pg.ActiveProfile(); err == nil && active != nil {
|
||||
var prov llm.Provider
|
||||
providerOK := false
|
||||
if profileID != nil {
|
||||
prov, _, providerOK = s.providerForProfile(*profileID)
|
||||
}
|
||||
if !providerOK {
|
||||
if active, activeErr := s.m.pg.ActiveProfile(); activeErr == nil && active != nil {
|
||||
profileID = &active.ID
|
||||
prov, _, providerOK = s.providerForProfile(active.ID)
|
||||
} else if activeErr != nil {
|
||||
log.Printf("[auto-title] conversation %d active profile lookup failed: %v", conversationID, activeErr)
|
||||
}
|
||||
}
|
||||
if profileID == nil {
|
||||
if profileID == nil || !providerOK || prov == nil {
|
||||
log.Printf("[auto-title] conversation %d: no effective profile", conversationID)
|
||||
return
|
||||
}
|
||||
prov, _, ok := s.providerForProfile(*profileID)
|
||||
if !ok {
|
||||
log.Printf("[auto-title] conversation %d: provider unavailable for profile %d", conversationID, *profileID)
|
||||
return
|
||||
}
|
||||
generated, err := agent.GenerateAutoTitle(ctx, prov, promptContent)
|
||||
if err != nil {
|
||||
log.Printf("[auto-title] conversation %d: %v", conversationID, err)
|
||||
return
|
||||
}
|
||||
current, err := s.m.pg.GetConversation(conversationID)
|
||||
if err != nil || current == nil {
|
||||
if !autoTitleIsUsable(generated) {
|
||||
log.Printf("[auto-title] conversation %d: generated title is empty or placeholder", conversationID)
|
||||
return
|
||||
}
|
||||
if autoTitleMayReplace(current.Title, title) {
|
||||
if err := s.m.pg.RenameConversation(conversationID, generated); err != nil {
|
||||
log.Printf("[auto-title] conversation %d rename failed: %v", conversationID, err)
|
||||
}
|
||||
if err := s.m.pg.RenameConversationIfTitle(conversationID, generated, title); err != nil {
|
||||
log.Printf("[auto-title] conversation %d rename failed: %v", conversationID, err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
@@ -517,6 +519,10 @@ func autoTitleMayReplace(current, initial string) bool {
|
||||
return current == "" || current == "新对话" || current == initial
|
||||
}
|
||||
|
||||
func autoTitleIsUsable(generated string) bool {
|
||||
return generated != "" && generated != "新对话"
|
||||
}
|
||||
|
||||
// runConversation runs ONE agent turn on a conversation in a background goroutine
|
||||
// (each conversation is independent → parallel). Steps stream to
|
||||
// conversation_activities. Shared by the chat HTTP handler and the P3 scheduler.
|
||||
|
||||
+28
-10
@@ -306,17 +306,35 @@ func (s *Server) toolSpawnTask() actool.CoreTool {
|
||||
llmIDs = []int64{*pin}
|
||||
}
|
||||
name := ""
|
||||
if s.m.pg.GetBool(db.SettingTaskAutoTitle, false) {
|
||||
if active, activeErr := s.m.pg.ActiveProfile(); activeErr == nil && active != nil {
|
||||
if prov, _, providerOK := s.providerForProfile(active.ID); providerOK {
|
||||
if generated, titleErr := agent.GenerateAutoTitle(ctx, prov, a.Description+"\n"+a.Goal); titleErr == nil {
|
||||
name = generated
|
||||
} else {
|
||||
log.Printf("[auto-title] task %s: %v", a.Description, titleErr)
|
||||
}
|
||||
} else {
|
||||
log.Printf("[auto-title] task provider unavailable for active profile %d", active.ID)
|
||||
autoTitleEnabled := s.m.pg.GetBool(db.SettingTaskAutoTitle, false)
|
||||
if autoTitleEnabled {
|
||||
if err := ctx.Err(); err != nil {
|
||||
return actool.Errorf(err.Error()), nil
|
||||
}
|
||||
active, activeErr := s.m.pg.ActiveProfile()
|
||||
if activeErr != nil {
|
||||
log.Printf("[auto-title] active profile lookup failed: %v", activeErr)
|
||||
} else if active == nil {
|
||||
log.Printf("[auto-title] no active profile")
|
||||
} else if prov, _, providerOK := s.providerForProfile(active.ID); providerOK {
|
||||
titleCtx, cancel := context.WithTimeout(ctx, 20*time.Second)
|
||||
defer cancel()
|
||||
generated, titleErr := agent.GenerateAutoTitle(titleCtx, prov, a.Description+"\n"+a.Goal)
|
||||
if err := ctx.Err(); err != nil {
|
||||
return actool.Errorf(err.Error()), nil
|
||||
}
|
||||
if titleErr == nil {
|
||||
name = generated
|
||||
} else {
|
||||
log.Printf("[auto-title] task title generation failed: %v", titleErr)
|
||||
}
|
||||
} else {
|
||||
log.Printf("[auto-title] task provider unavailable for active profile %d", active.ID)
|
||||
}
|
||||
}
|
||||
if autoTitleEnabled {
|
||||
if err := ctx.Err(); err != nil {
|
||||
return actool.Errorf(err.Error()), nil
|
||||
}
|
||||
}
|
||||
t, err := s.m.CreateTaskWithOptions(a.Description, a.Goal, db.TaskCreateOptions{
|
||||
|
||||
Reference in new issue
Block a user