feat(tenant): bill AI points by output characters
Previously AI point cost was computed from request (input) characters at reservation time. Switch to reserving a single point up front and settling the final cost from the generated output length on completion. - Reserve 1 point (or FixedPoints) instead of pricing on request chars - On completion, recompute points from output chars vs base chars, update the reservation amounts, and post a ledger delta for the top-up/refund; invalidate the workspace quota summary cache - MarkCompleted now persists final request_chars/base_chars/points - Use ceil division in calculateAIPointCost so an exact base boundary charges one point instead of rolling over - Thread output text through all CompleteAIPoints callers (article selection, KOL assist, question expansion, template assist, compliance judge) and return the settled reservation - Add unit tests plus an integration test gated on TEST_DATABASE_URL - Update the user manual: billing is by output characters Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -430,7 +430,7 @@ func (s *TemplateService) IsAssistTaskTerminal(ctx context.Context, job AssistJo
|
||||
}
|
||||
switch record.Status {
|
||||
case "completed":
|
||||
s.completeTemplateAssistAIPoints(ctx, job.TenantID, job.TaskID, "")
|
||||
s.completeTemplateAssistAIPoints(ctx, job.TenantID, job.TaskID, "", string(record.ResultJSON))
|
||||
return true, nil
|
||||
case "failed":
|
||||
if isTemplateAssistAIPointTask(job.TaskType) {
|
||||
@@ -479,7 +479,7 @@ func (s *TemplateService) executeAnalyzeTask(ctx context.Context, repo repositor
|
||||
s.failAssist(ctx, job.TaskID, job.TaskType, job.TenantID, err)
|
||||
return
|
||||
}
|
||||
s.completeTemplateAssistAIPoints(ctx, job.TenantID, job.TaskID, result.Model)
|
||||
s.completeTemplateAssistAIPoints(ctx, job.TenantID, job.TaskID, result.Model, result.Content)
|
||||
}
|
||||
|
||||
func (s *TemplateService) executeTitleTask(ctx context.Context, repo repository.TemplateAssistTaskRepository, job assistJob) {
|
||||
@@ -518,7 +518,7 @@ func (s *TemplateService) executeTitleTask(ctx context.Context, repo repository.
|
||||
s.failAssist(ctx, job.TaskID, job.TaskType, job.TenantID, err)
|
||||
return
|
||||
}
|
||||
s.completeTemplateAssistAIPoints(ctx, job.TenantID, job.TaskID, result.Model)
|
||||
s.completeTemplateAssistAIPoints(ctx, job.TenantID, job.TaskID, result.Model, result.Content)
|
||||
}
|
||||
|
||||
func (s *TemplateService) executeOutlineTask(ctx context.Context, repo repository.TemplateAssistTaskRepository, job assistJob) {
|
||||
@@ -562,7 +562,7 @@ func (s *TemplateService) executeOutlineTask(ctx context.Context, repo repositor
|
||||
s.failAssist(ctx, job.TaskID, job.TaskType, job.TenantID, err)
|
||||
return
|
||||
}
|
||||
s.completeTemplateAssistAIPoints(ctx, job.TenantID, job.TaskID, result.Model)
|
||||
s.completeTemplateAssistAIPoints(ctx, job.TenantID, job.TaskID, result.Model, result.Content)
|
||||
}
|
||||
|
||||
func (s *TemplateService) failAssist(ctx context.Context, taskID, taskType string, tenantID int64, err error) {
|
||||
@@ -619,10 +619,10 @@ func (s *TemplateService) refundReservedAIPoints(ctx context.Context, tenantID,
|
||||
_ = RefundAIPoints(ctx, s.pool, s.cache, tenantID, operatorID, *reservation, errorMessage)
|
||||
}
|
||||
|
||||
func (s *TemplateService) completeTemplateAssistAIPoints(ctx context.Context, tenantID int64, taskID string, model string) {
|
||||
func (s *TemplateService) completeTemplateAssistAIPoints(ctx context.Context, tenantID int64, taskID string, model string, meteredOutputText string) {
|
||||
resourceType := "template_assist_task"
|
||||
resourceUID := taskID
|
||||
_ = CompletePendingAIPointsByResource(ctx, s.pool, tenantID, resourceType, nil, &resourceUID, model)
|
||||
_, _ = CompletePendingAIPointsByResource(ctx, s.pool, s.cache, tenantID, resourceType, nil, &resourceUID, model, meteredOutputText)
|
||||
}
|
||||
|
||||
func isTemplateAssistAIPointTask(taskType string) bool {
|
||||
|
||||
Reference in New Issue
Block a user