feat(kol): add SSE streaming for prompt assist
- Extract assist runtime (prompt building, response parsing, finalize) from the worker into kol_assist_runtime so the sync streaming path and the async task path share the same logic. - Add POST /kol/manage/assist/stream as a Server-Sent Events endpoint emitting start/delta/completed/error events, and wire it to a streamAssist helper on kolManageApi that reports ApiClientError with the server's error envelope. - Stream generated content live in KolPromptEditor and KolGenerateView, switching KolPromptEditArea to a boolean aiBusy flag and refining the generator page layout. - Add KolAssistStreamEvent to shared types. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestFinalizeKolAssistResultRepairsDuplicateVariableIDs(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
result, err := FinalizeKolAssistResult(
|
||||
AssistRequest{Mode: kolAssistModeGenerate},
|
||||
&KolAssistResult{
|
||||
Content: "围绕 {{城市}} 和 {{目标词}} 输出内容",
|
||||
Schema: KolSchemaJSON{
|
||||
Variables: []KolVariableDefinition{
|
||||
{ID: "var_dup", Key: "城市", Label: "城市", Type: KolVariableTypeInput, Required: true},
|
||||
{ID: "var_dup", Key: "目标词", Label: "目标词", Type: KolVariableTypeInput, Required: true},
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
require.NoError(t, err)
|
||||
require.Len(t, result.Schema.Variables, 2)
|
||||
require.NotEqual(t, result.Schema.Variables[0].ID, result.Schema.Variables[1].ID)
|
||||
require.Equal(t, "城市", result.Schema.Variables[0].Key)
|
||||
require.Equal(t, "目标词", result.Schema.Variables[1].Key)
|
||||
}
|
||||
|
||||
func TestFinalizeKolAssistResultOptimizePreservesExistingSchemaByKey(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
originalMaxLength := 80
|
||||
|
||||
result, err := FinalizeKolAssistResult(
|
||||
AssistRequest{
|
||||
Mode: kolAssistModeOptimize,
|
||||
Schema: &KolSchemaJSON{
|
||||
Variables: []KolVariableDefinition{
|
||||
{ID: "var_city", Key: "城市", Label: "城市", Type: KolVariableTypeInput, Required: true, MaxLength: intPtr(originalMaxLength)},
|
||||
},
|
||||
},
|
||||
},
|
||||
&KolAssistResult{
|
||||
Content: "围绕 {{城市}} 优化提示词",
|
||||
Schema: KolSchemaJSON{
|
||||
Variables: []KolVariableDefinition{
|
||||
{ID: "var_dup", Key: "城市", Label: "城市", Type: KolVariableTypeTextarea, Required: false},
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
require.NoError(t, err)
|
||||
require.Len(t, result.Schema.Variables, 1)
|
||||
require.Equal(t, "var_city", result.Schema.Variables[0].ID)
|
||||
require.Equal(t, KolVariableTypeInput, result.Schema.Variables[0].Type)
|
||||
require.NotNil(t, result.Schema.Variables[0].MaxLength)
|
||||
require.Equal(t, originalMaxLength, *result.Schema.Variables[0].MaxLength)
|
||||
}
|
||||
|
||||
func TestParseKolAssistResponseSupportsStringVariables(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
result, err := ParseKolAssistResponse(`{"content":"围绕 {{城市}} 写内容","schema":{"variables":["城市"]}}`)
|
||||
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "围绕 {{城市}} 写内容", result.Content)
|
||||
require.Len(t, result.Schema.Variables, 1)
|
||||
require.Equal(t, "城市", result.Schema.Variables[0].Key)
|
||||
}
|
||||
|
||||
func TestExtractKolAssistContentFromPartialJSON(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
content := extractKolAssistContentFromPartialJSON("{\"content\":\"第一段\\n第二")
|
||||
|
||||
require.Equal(t, "第一段\n第二", content)
|
||||
}
|
||||
Reference in New Issue
Block a user