feat(generation): migrate article generation and template-assist to RabbitMQ queues
Replace in-process generation with queue-based async execution via RabbitMQ. Add generation task runtime for lifecycle management, article generation payload/queue abstractions, and template-assist queue publisher. Refactor prompt generation service to support batch generation with per-item error tracking. Update stream hub to bridge queue events to SSE. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -51,6 +51,7 @@ func NewArticleHandler(a *bootstrap.App) *ArticleHandler {
|
||||
promptGenerateSvc: app.NewPromptRuleGenerationService(
|
||||
a.DB,
|
||||
a.LLM,
|
||||
a.RabbitMQ,
|
||||
knowledgeSvc,
|
||||
a.GenerationStreams,
|
||||
a.Config.Generation,
|
||||
@@ -369,6 +370,11 @@ func (h *ArticleHandler) Stream(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
fallback := buildGenerationSnapshot(detail)
|
||||
if snapshot, ok := h.streams.Snapshot(id); !ok || (fallback.Done && snapshot.Status != fallback.Status) {
|
||||
h.streams.Seed(fallback)
|
||||
}
|
||||
|
||||
events, snapshot, cancel := h.streams.Subscribe(id)
|
||||
defer cancel()
|
||||
|
||||
@@ -377,20 +383,10 @@ func (h *ArticleHandler) Stream(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
flusher.Flush()
|
||||
if snapshot.Done {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
fallback := stream.GenerationEvent{
|
||||
Type: "snapshot",
|
||||
ArticleID: detail.ID,
|
||||
Status: detail.GenerateStatus,
|
||||
Done: detail.GenerateStatus == "completed" || detail.GenerateStatus == "failed",
|
||||
UpdatedAt: time.Now().UTC(),
|
||||
}
|
||||
if detail.Title != nil {
|
||||
fallback.Title = *detail.Title
|
||||
}
|
||||
if detail.MarkdownContent != nil {
|
||||
fallback.Content = *detail.MarkdownContent
|
||||
}
|
||||
if err := writeSSE(c, fallback.Type, fallback); err != nil {
|
||||
return
|
||||
}
|
||||
@@ -455,6 +451,32 @@ func writeSSE(c *gin.Context, event string, payload interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func buildGenerationSnapshot(detail *app.ArticleDetailResponse) stream.GenerationEvent {
|
||||
event := stream.GenerationEvent{
|
||||
Type: "snapshot",
|
||||
ArticleID: detail.ID,
|
||||
Status: detail.GenerateStatus,
|
||||
Done: detail.GenerateStatus == "completed" || detail.GenerateStatus == "failed",
|
||||
UpdatedAt: time.Now().UTC(),
|
||||
}
|
||||
if detail.Title != nil {
|
||||
event.Title = *detail.Title
|
||||
}
|
||||
if detail.MarkdownContent != nil {
|
||||
event.Content = *detail.MarkdownContent
|
||||
}
|
||||
if detail.GenerateStatus == "completed" {
|
||||
event.Type = "completed"
|
||||
}
|
||||
if detail.GenerateStatus == "failed" {
|
||||
event.Type = "error"
|
||||
if detail.GenerationErrorMessage != nil {
|
||||
event.Error = *detail.GenerationErrorMessage
|
||||
}
|
||||
}
|
||||
return event
|
||||
}
|
||||
|
||||
func resolveOptimizeStreamErrorMessage(appErr *response.AppError) string {
|
||||
if appErr == nil {
|
||||
return "optimize selection failed"
|
||||
|
||||
Reference in New Issue
Block a user