Files
story/internal/memoir/dify_workflow.go
T

100 lines
2.6 KiB
Go
Raw Normal View History

package memoir
import (
"context"
"data-terminal-cloud/internal/dify"
"data-terminal-cloud/internal/extraction"
"sync"
)
type BookWorkflow interface {
Generate(context.Context, []Source, func(Document) error) (Document, [][]byte, error)
}
type DifyWorkflow struct {
Client *dify.Client
Bridge *dify.Bridge
}
func NewDifyWorkflow(base, key, bridgeURL, bridgeSecret string) (*DifyWorkflow, error) {
client, err := dify.NewClient(base, key)
if err != nil {
return nil, err
}
return &DifyWorkflow{client, dify.NewBridge(bridgeURL, bridgeSecret)}, nil
}
func (g *DifyWorkflow) Generate(ctx context.Context, sources []Source, publish func(Document) error) (Document, [][]byte, error) {
ticket, err := g.Bridge.Register(ctx, "memoir", map[string]any{"stories": sources})
if err != nil {
return Document{}, nil, err
}
defer g.Bridge.Forget(ticket)
var response struct {
Document Document `json:"document"`
ErrorCode string `json:"error_code"`
}
published := false
run, runErr := g.Client.Run(ctx, map[string]any{"context_id": ticket.ID, "step_token": ticket.Token}, func(e dify.Event) error {
if e.Data.NodeID != "validate-book" || published {
return nil
}
if err := g.Bridge.Result(ctx, ticket, &response); err != nil {
return err
}
if err := ValidateDocument(response.Document, sources); err != nil {
return err
}
response.Document.WorkflowRunID = e.WorkflowRunID
if err := publish(response.Document); err != nil {
return err
}
published = true
return nil
})
if runErr != nil {
_ = g.Bridge.Result(ctx, ticket, &response)
if response.ErrorCode == "MODEL_ACCOUNT_OVERDUE" {
return Document{}, nil, extraction.ErrModelAccountOverdue
}
return Document{}, nil, runErr
}
if err = g.Bridge.Result(ctx, ticket, &response); err != nil {
return Document{}, nil, err
}
if err = ValidateDocument(response.Document, sources); err != nil {
return Document{}, nil, err
}
response.Document.WorkflowRunID = run.ID
if !published {
if err = publish(response.Document); err != nil {
return Document{}, nil, err
}
}
pictures := make([][]byte, len(response.Document.Chapters))
sem := make(chan struct{}, 3)
var wg sync.WaitGroup
var mu sync.Mutex
var imageErr error
for i := range pictures {
wg.Add(1)
go func(i int) {
defer wg.Done()
select {
case sem <- struct{}{}:
case <-ctx.Done():
return
}
defer func() { <-sem }()
b, err := g.Bridge.Image(ctx, ticket, i)
mu.Lock()
defer mu.Unlock()
if err != nil {
imageErr = err
} else {
pictures[i] = b
}
}(i)
}
wg.Wait()
return response.Document, pictures, imageErr
}