init
This commit is contained in:
168
tools/commit_chapter.go
Normal file
168
tools/commit_chapter.go
Normal file
@@ -0,0 +1,168 @@
|
||||
package tools
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/voocel/agentcore/schema"
|
||||
"github.com/voocel/ainovel-cli/domain"
|
||||
"github.com/voocel/ainovel-cli/state"
|
||||
)
|
||||
|
||||
// CommitChapterTool 提交章节:加载正文 → 保存终稿 → 生成摘要 → 更新状态 → 更新进度。
|
||||
// 这是唯一允许写入 chapters/、summaries/、更新状态文件和进度的工具。
|
||||
type CommitChapterTool struct {
|
||||
store *state.Store
|
||||
}
|
||||
|
||||
func NewCommitChapterTool(store *state.Store) *CommitChapterTool {
|
||||
return &CommitChapterTool{store: store}
|
||||
}
|
||||
|
||||
func (t *CommitChapterTool) Name() string { return "commit_chapter" }
|
||||
func (t *CommitChapterTool) Description() string {
|
||||
return "提交章节。优先使用打磨版正文,同时更新时间线、伏笔、关系状态。返回结构化信号"
|
||||
}
|
||||
func (t *CommitChapterTool) Label() string { return "提交章节" }
|
||||
|
||||
func (t *CommitChapterTool) Schema() map[string]any {
|
||||
timelineSchema := schema.Object(
|
||||
schema.Property("time", schema.String("故事内时间")).Required(),
|
||||
schema.Property("event", schema.String("事件描述")).Required(),
|
||||
schema.Property("characters", schema.Array("涉及角色", schema.String(""))),
|
||||
)
|
||||
foreshadowSchema := schema.Object(
|
||||
schema.Property("id", schema.String("伏笔 ID(新埋设时自定义,推进/回收时使用已有 ID)")).Required(),
|
||||
schema.Property("action", schema.Enum("操作", "plant", "advance", "resolve")).Required(),
|
||||
schema.Property("description", schema.String("伏笔描述(仅 plant 时必需)")),
|
||||
)
|
||||
relationshipSchema := schema.Object(
|
||||
schema.Property("character_a", schema.String("角色 A")).Required(),
|
||||
schema.Property("character_b", schema.String("角色 B")).Required(),
|
||||
schema.Property("relation", schema.String("当前关系描述")).Required(),
|
||||
)
|
||||
return schema.Object(
|
||||
schema.Property("chapter", schema.Int("章节号")).Required(),
|
||||
schema.Property("summary", schema.String("本章内容摘要(200字以内)")).Required(),
|
||||
schema.Property("characters", schema.Array("本章出场角色名", schema.String(""))).Required(),
|
||||
schema.Property("key_events", schema.Array("本章关键事件", schema.String(""))).Required(),
|
||||
schema.Property("timeline_events", schema.Array("本章时间线事件", timelineSchema)),
|
||||
schema.Property("foreshadow_updates", schema.Array("伏笔操作", foreshadowSchema)),
|
||||
schema.Property("relationship_changes", schema.Array("关系变化", relationshipSchema)),
|
||||
)
|
||||
}
|
||||
|
||||
func (t *CommitChapterTool) Execute(_ context.Context, args json.RawMessage) (json.RawMessage, error) {
|
||||
var a struct {
|
||||
Chapter int `json:"chapter"`
|
||||
Summary string `json:"summary"`
|
||||
Characters []string `json:"characters"`
|
||||
KeyEvents []string `json:"key_events"`
|
||||
TimelineEvents []domain.TimelineEvent `json:"timeline_events"`
|
||||
ForeshadowUpdates []domain.ForeshadowUpdate `json:"foreshadow_updates"`
|
||||
RelationshipChanges []domain.RelationshipEntry `json:"relationship_changes"`
|
||||
}
|
||||
if err := json.Unmarshal(args, &a); err != nil {
|
||||
return nil, fmt.Errorf("invalid args: %w", err)
|
||||
}
|
||||
if a.Chapter <= 0 {
|
||||
return nil, fmt.Errorf("chapter must be > 0")
|
||||
}
|
||||
if err := t.store.ValidateChapterCommit(a.Chapter); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 1. 加载章节正文(polished 优先,否则 merge scenes)
|
||||
content, wordCount, err := t.store.LoadChapterContent(a.Chapter)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load chapter content: %w", err)
|
||||
}
|
||||
if content == "" {
|
||||
return nil, fmt.Errorf("no content found for chapter %d", a.Chapter)
|
||||
}
|
||||
|
||||
// 2. 保存终稿
|
||||
if err := t.store.SaveFinalChapter(a.Chapter, content); err != nil {
|
||||
return nil, fmt.Errorf("save final chapter: %w", err)
|
||||
}
|
||||
|
||||
// 3. 保存摘要
|
||||
summary := domain.ChapterSummary{
|
||||
Chapter: a.Chapter,
|
||||
Summary: a.Summary,
|
||||
Characters: a.Characters,
|
||||
KeyEvents: a.KeyEvents,
|
||||
}
|
||||
if err := t.store.SaveSummary(summary); err != nil {
|
||||
return nil, fmt.Errorf("save summary: %w", err)
|
||||
}
|
||||
|
||||
// 4. 更新状态增量
|
||||
if len(a.TimelineEvents) > 0 {
|
||||
for i := range a.TimelineEvents {
|
||||
a.TimelineEvents[i].Chapter = a.Chapter
|
||||
}
|
||||
if err := t.store.AppendTimelineEvents(a.TimelineEvents); err != nil {
|
||||
return nil, fmt.Errorf("append timeline: %w", err)
|
||||
}
|
||||
}
|
||||
if len(a.ForeshadowUpdates) > 0 {
|
||||
if err := t.store.UpdateForeshadow(a.Chapter, a.ForeshadowUpdates); err != nil {
|
||||
return nil, fmt.Errorf("update foreshadow: %w", err)
|
||||
}
|
||||
}
|
||||
if len(a.RelationshipChanges) > 0 {
|
||||
for i := range a.RelationshipChanges {
|
||||
a.RelationshipChanges[i].Chapter = a.Chapter
|
||||
}
|
||||
if err := t.store.UpdateRelationships(a.RelationshipChanges); err != nil {
|
||||
return nil, fmt.Errorf("update relationships: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// 5. 更新进度
|
||||
if err := t.store.MarkChapterComplete(a.Chapter, wordCount); err != nil {
|
||||
return nil, fmt.Errorf("mark chapter complete: %w", err)
|
||||
}
|
||||
|
||||
// 6. 判断是否需要审阅
|
||||
progress, err := t.store.LoadProgress()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load progress: %w", err)
|
||||
}
|
||||
completedCount := 0
|
||||
if progress != nil {
|
||||
completedCount = len(progress.CompletedChapters)
|
||||
}
|
||||
reviewRequired, reviewReason := domain.ShouldReview(completedCount)
|
||||
|
||||
// 7. 计算场景数
|
||||
sceneCount := 0
|
||||
if scenes, err := t.store.LoadSceneDrafts(a.Chapter); err == nil {
|
||||
sceneCount = len(scenes)
|
||||
}
|
||||
|
||||
// 8. 构造结构化信号
|
||||
result := domain.CommitResult{
|
||||
Chapter: a.Chapter,
|
||||
Committed: true,
|
||||
WordCount: wordCount,
|
||||
SceneCount: sceneCount,
|
||||
NextChapter: a.Chapter + 1,
|
||||
ReviewRequired: reviewRequired,
|
||||
ReviewReason: reviewReason,
|
||||
}
|
||||
|
||||
// 9. 写入信号文件供宿主程序读取(优先于清理操作,确保信号不丢失)
|
||||
if err := t.store.SaveLastCommit(result); err != nil {
|
||||
return nil, fmt.Errorf("save commit signal: %w", err)
|
||||
}
|
||||
|
||||
// 10. 清除场景级进度(章节已提交)
|
||||
if err := t.store.ClearInProgress(); err != nil {
|
||||
return nil, fmt.Errorf("clear in-progress: %w", err)
|
||||
}
|
||||
|
||||
return json.Marshal(result)
|
||||
}
|
||||
Reference in New Issue
Block a user