fix: Enhance Kol Variable Rendering and Management

- Updated the regex for placeholder matching to support more flexible key formats.
- Refactored variable storage to allow both ID and key lookups in rendering.
- Improved schema validation to check for duplicate keys and enforce non-empty keys.
- Added new utility functions for variable value lookup and display name generation.
- Enhanced tests to cover new key-based variable scenarios.
- Refactored KolPrompt repository to simplify prompt storage and management, including the removal of obsolete revision handling.
- Introduced new API endpoints for saving, activating, and archiving prompts.
- Added new utility functions for handling Kol placeholders and platform options in the admin web.
- Implemented database migrations to simplify prompt storage structure and ensure data integrity.
This commit is contained in:
2026-04-18 00:47:57 +08:00
parent 614ca4a2ea
commit 3ef0807456
32 changed files with 2414 additions and 3518 deletions
@@ -99,16 +99,16 @@ func (s *KolGenerationService) WithCache(c sharedcache.Cache) *KolGenerationServ
}
func (s *KolGenerationService) GetSchema(ctx context.Context, actor auth.Actor, subPromptID int64) (*KolSubscriptionPromptSchemaResponse, error) {
row, revision, err := s.loadAuthorizedRevision(ctx, actor, subPromptID)
row, prompt, err := s.loadAuthorizedPrompt(ctx, actor, subPromptID)
if err != nil {
return nil, err
}
schema, err := decodeKolSchema(revision.SchemaJSON)
schema, err := decodeKolSchema(prompt.SchemaJSON)
if err != nil {
return nil, response.ErrInternal(50096, "kol_generation_schema_decode_failed", err.Error())
}
cardConfig, err := decodeKolCardConfig(revision.CardConfigJSON)
cardConfig, err := decodeKolCardConfig(prompt.CardConfigJSON)
if err != nil {
return nil, response.ErrInternal(50097, "kol_generation_card_config_decode_failed", err.Error())
}
@@ -126,7 +126,7 @@ func (s *KolGenerationService) GetSchema(ctx context.Context, actor auth.Actor,
}
func (s *KolGenerationService) Submit(ctx context.Context, actor auth.Actor, subPromptID int64, variables map[string]any) (*KolGenerationSubmitResponse, error) {
row, revision, err := s.loadAuthorizedRevision(ctx, actor, subPromptID)
row, prompt, err := s.loadAuthorizedPrompt(ctx, actor, subPromptID)
if err != nil {
return nil, err
}
@@ -135,12 +135,16 @@ func (s *KolGenerationService) Submit(ctx context.Context, actor auth.Actor, sub
variables = map[string]any{}
}
schema, err := decodeKolSchema(revision.SchemaJSON)
schema, err := decodeKolSchema(prompt.SchemaJSON)
if err != nil {
return nil, response.ErrInternal(50096, "kol_generation_schema_decode_failed", err.Error())
}
content, err := s.asset.Get(ctx, revision.PromptAssetKey)
if prompt.PromptAssetKey == nil || *prompt.PromptAssetKey == "" {
return nil, response.ErrInternal(50098, "kol_generation_prompt_asset_missing", "published prompt content not found")
}
content, err := s.asset.Get(ctx, *prompt.PromptAssetKey)
if err != nil {
return nil, response.ErrInternal(50098, "kol_generation_prompt_asset_load_failed", err.Error())
}
@@ -150,6 +154,10 @@ func (s *KolGenerationService) Submit(ctx context.Context, actor auth.Actor, sub
return nil, response.ErrBadRequest(40071, "kol_generation_variables_invalid", err.Error())
}
renderedPromptHash := HashPrompt(renderedPrompt)
promptRevisionNo := 1
if row.PublishedRevisionNo != nil && *row.PublishedRevisionNo > 0 {
promptRevisionNo = *row.PublishedRevisionNo
}
balance, err := s.quotaRepo.GetCurrentBalance(ctx, actor.TenantID, "article_generation")
if err != nil || balance < 1 {
@@ -161,10 +169,10 @@ func (s *KolGenerationService) Submit(ctx context.Context, actor auth.Actor, sub
SubscriptionID: row.SubscriptionID,
PackageID: row.PackageID,
PromptID: row.PromptID,
PromptRevisionNo: *row.PublishedRevisionNo,
PromptRevisionNo: promptRevisionNo,
Variables: variables,
SchemaSnapshot: json.RawMessage(revision.SchemaJSON),
CardConfigSnapshot: json.RawMessage(revision.CardConfigJSON),
SchemaSnapshot: json.RawMessage(prompt.SchemaJSON),
CardConfigSnapshot: json.RawMessage(prompt.CardConfigJSON),
RenderedPromptHash: renderedPromptHash,
RenderedPrompt: renderedPrompt,
})
@@ -259,10 +267,10 @@ func (s *KolGenerationService) Submit(ctx context.Context, actor auth.Actor, sub
SubscriptionPromptID: &row.ID,
PackageID: row.PackageID,
PromptID: row.PromptID,
PromptRevisionNo: *row.PublishedRevisionNo,
PromptRevisionNo: promptRevisionNo,
GenerationTaskID: &taskID,
VariablesSnapshot: variablesJSON,
SchemaSnapshot: revision.SchemaJSON,
SchemaSnapshot: prompt.SchemaJSON,
RenderedPromptHash: &renderedPromptHash,
})
if err != nil {
@@ -300,7 +308,7 @@ func (s *KolGenerationService) Submit(ctx context.Context, actor auth.Actor, sub
}, nil
}
func (s *KolGenerationService) loadAuthorizedRevision(ctx context.Context, actor auth.Actor, subPromptID int64) (*repository.KolSubscriptionPrompt, *repository.KolPromptRevision, error) {
func (s *KolGenerationService) loadAuthorizedPrompt(ctx context.Context, actor auth.Actor, subPromptID int64) (*repository.KolSubscriptionPrompt, *repository.KolPrompt, error) {
row, err := s.subRepo.GetSubscriptionPromptByID(ctx, actor.TenantID, subPromptID)
if err != nil {
return nil, nil, response.ErrInternal(50109, "kol_subscription_prompt_query_failed", err.Error())
@@ -319,19 +327,19 @@ func (s *KolGenerationService) loadAuthorizedRevision(ctx context.Context, actor
return nil, nil, ErrKolGenerationForbidden
case row.PackageStatus != "published":
return nil, nil, ErrKolGenerationForbidden
case row.PromptStatus != "active" || row.PublishedRevisionNo == nil:
case row.PromptStatus != "active":
return nil, nil, ErrKolGenerationForbidden
}
revision, err := s.promptRepo.GetRevision(ctx, row.CreatorTenantID, row.PromptID, *row.PublishedRevisionNo)
prompt, err := s.promptRepo.GetByID(ctx, row.CreatorTenantID, row.PromptID)
if err != nil {
return nil, nil, response.ErrInternal(50110, "kol_prompt_revision_query_failed", err.Error())
return nil, nil, response.ErrInternal(50110, "kol_prompt_query_failed", err.Error())
}
if revision == nil {
return nil, nil, response.ErrInternal(50111, "kol_prompt_revision_missing", "published prompt revision not found")
if prompt == nil || prompt.PromptAssetKey == nil || *prompt.PromptAssetKey == "" {
return nil, nil, response.ErrInternal(50111, "kol_prompt_content_missing", "published prompt content not found")
}
return row, revision, nil
return row, prompt, nil
}
func HandleKolGenerationTaskFailure(