Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions backend/internal/app/canvas_workflow_editor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -308,11 +308,10 @@ func TestCanvasWorkflowPromptEditValidatesCapability(t *testing.T) {
json.Unmarshal([]byte(task.InputJSON), &input)
config := input["config"].(map[string]any)
delete(config, "channelId")
profile := DefaultModelCapabilityConfigForModel("runninghub-workflow-video", "workflow-video")
profile := DefaultModelCapabilityConfigForModel("volcengine-ark-video", "seedance-2.5")
profile.Video.References.PromptMaxChars = 30
config["interfaceType"] = "runninghub-workflow-video"
config["interfaceType"] = "volcengine-ark-video"
config["capabilityConfig"] = profile
config["workflowId"] = "wf-1"
raw, _ := json.Marshal(input)
db.Model(&task).Update("input_json", string(raw))
created, e := s.workflowTemplates().Create("owner", cw.CreateRequest{Name: "Limit", Definition: d})
Expand Down
2 changes: 1 addition & 1 deletion backend/internal/app/creation_domain.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ func (s *Service) creationDomain() *creation.Service {
ValidateDocument: func(u string, repo *repository.Repository, raw json.RawMessage) error {
return canvas.New(repo, newCanvasHostWithRepo(s, repo)).ValidateCanvasMediaAssets(u, raw)
},
UsesWorkflow: taskInputUsesWorkflowProvider, UsesTextReplay: isTextReplayTaskRequest, FormatTask: taskForOutput,
UsesTextReplay: isTextReplayTaskRequest, FormatTask: taskForOutput,
})
}
func toCreationCommand(req CreationRequest) creation.Command {
Expand Down
4 changes: 2 additions & 2 deletions backend/internal/app/feature_availability_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,14 +76,14 @@ func TestPluginsForUserKeepsOfficialApplicationsAndHidesManagedPlugins(t *testin
svc.pluginRuntime = pluginRuntimeFromRecords(map[string]pluginRecord{
"bundled": {Source: "bundled", Metadata: protocol.Metadata{ID: "bundled", Name: "系统协议", Version: "1"}},
"uploaded": {Source: "uploaded", Metadata: protocol.Metadata{ID: "uploaded", Name: "自定义协议", Version: "1"}},
"app": {Source: "bundled", Metadata: protocol.Metadata{ID: WorkflowPluginRunningHub, Name: "官方应用", Version: "1"}},
"app": {Source: "bundled", Metadata: protocol.Metadata{ID: PluginAIArtCritique, Name: "官方应用", Version: "1"}},
})

visibleToUser, err := svc.PluginsForUser(&model.User{ID: "user-1", Role: model.UserRoleUser})
if err != nil {
t.Fatal(err)
}
if len(visibleToUser) != 1 || visibleToUser[0].Manifest.ID != WorkflowPluginRunningHub {
if len(visibleToUser) != 1 || visibleToUser[0].Manifest.ID != PluginAIArtCritique {
t.Fatalf("PluginsForUser(user) = %#v", visibleToUser)
}
visibleToAdmin, err := svc.PluginsForUser(admin)
Expand Down
1 change: 0 additions & 1 deletion backend/internal/app/generation_alias.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ type canonicalAgentRequest = generation.CanonicalAgentRequest
type providerTextMessage = generation.TextMessage
type providerConfig = generation.Config
type providerMedia = generation.Media
type WorkflowField = generation.WorkflowField
type imageResponse = generation.ImageResponse
type providerError = generation.UpstreamError
type providerPayloadError = generation.PayloadError
Expand Down
14 changes: 0 additions & 14 deletions backend/internal/app/generation_runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -203,13 +203,6 @@ func (p appConfigPort) ApplyCapabilities(ctx context.Context, input *generation.
return generation.ApplyStandardCapabilities(ctx, p.service.repo, p.service.ReadLocalModelConfig, input)
}

func (p appConfigPort) RequireWorkflow(interfaceType string) error {
if p.service == nil {
return errors.New("无法执行工作流任务,请重试")
}
return p.service.RequireWorkflowPluginForInterface(interfaceType)
}

func (p appConfigPort) SyncArkPrivateAssets(ctx context.Context, userID string, input *generation.Input) error {
if p.service == nil || input == nil {
return nil
Expand All @@ -226,12 +219,6 @@ func (p appStylePort) Apply(userID, projectID string, input *generation.Input) e
return p.service.applyGenerationStyleProfile(userID, projectID, input)
}

type appWorkflowPort struct{ service *Service }

func (p appWorkflowPort) Execute(ctx context.Context, input generation.Input) (map[string]interface{}, error) {
return p.service.runWorkflowProviderTask(ctx, input)
}

type appMediaProbe struct{}

func (appMediaProbe) ProbeSeedance2Video(config generation.Config, index int, media *generation.Media, data []byte) error {
Expand Down Expand Up @@ -262,7 +249,6 @@ func (s *Service) applyGenerationRuntime(ctx context.Context, meta generation.Ca
Receipts: appReceiptPort{service: s},
Images: appImagePort{service: s},
ManagedMedia: s.mediaAttempts().ManagedPort(),
Workflow: appWorkflowPort{service: s},
Prompt: appPromptPort{service: s},
Config: appConfigPort{service: s},
Style: appStylePort{service: s},
Expand Down
2 changes: 1 addition & 1 deletion backend/internal/app/generation_runtime_safety_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ func TestBindGenerationRuntimeAlwaysBindsTypedPorts(t *testing.T) {
if !ok {
t.Fatal("runtime missing")
}
if runtime.Limits == nil || runtime.Prompt == nil || runtime.Config == nil || runtime.Style == nil || runtime.Images == nil || runtime.Receipts == nil || runtime.Workflow == nil || runtime.Probe == nil {
if runtime.Limits == nil || runtime.Prompt == nil || runtime.Config == nil || runtime.Style == nil || runtime.Images == nil || runtime.Receipts == nil || runtime.Probe == nil {
t.Fatalf("typed ports missing on empty Service: %#v", runtime)
}
if runtime.Call.ProjectID != "project-1" || runtime.Call.TaskType != "canvas_image" {
Expand Down
7 changes: 1 addition & 6 deletions backend/internal/app/model_capability.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,7 @@ import (
)

func (s *Service) ValidateTaskCapability(input map[string]any) error {
return generation.ValidateTaskCapability(input, s.resolveVideoCapability, func(mode string, config providerConfig) (bool, error) {
if isWorkflowProviderInterface(config.InterfaceType) {
return true, validateWorkflowProviderConfig(mode, config)
}
return false, nil
}, s.channelModelLookup())
return generation.ValidateTaskCapability(input, s.resolveVideoCapability, s.channelModelLookup())
}

func (s *Service) channelModelLookup() modelcatalog.ChannelModelLookup {
Expand Down
19 changes: 0 additions & 19 deletions backend/internal/app/model_capability_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -496,25 +496,6 @@ func TestDefaultVideoPromptMaxCharsAllowsComposedCanvasPrompt(t *testing.T) {
}
}

func TestValidateTaskCapabilityRejectsWorkflowPromptAboveConfiguredLimit(t *testing.T) {
profile := DefaultModelCapabilityConfigForModel("runninghub-workflow-video", "workflow-video")
profile.Video.References.PromptMaxChars = 10_000
input := map[string]any{
"mode": "video",
"prompt": strings.Repeat("镜", 10_001),
"config": map[string]any{
"interfaceType": "runninghub-workflow-video",
"apiKey": "rh-key",
"workflowId": "wf-1",
"capabilityConfig": profile,
},
}

if err := (&Service{}).ValidateTaskCapability(input); err != nil {
t.Fatalf("ValidateTaskCapability() should delegate workflow prompt length to upstream, got error = %v", err)
}
}

func TestValidateImageTaskEnforcesGPTImage2CustomSizeLimits(t *testing.T) {
profile := DefaultImageCapabilityConfig("openai-image", "gpt-image-2")
profile.Size.AllowCustom = true
Expand Down
25 changes: 13 additions & 12 deletions backend/internal/app/plugin_management_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,36 +32,37 @@ func TestApplicationPluginUsesUserStateUnderPlatformAvailability(t *testing.T) {
if err != nil {
t.Fatal(err)
}
initial := states[WorkflowPluginRunningHub]
initial := states[PluginAIArtCritique]
if !initial.PlatformAvailable || initial.UserEnabled || initial.EffectiveEnabled || !initial.CanToggle {
t.Fatalf("initial RunningHub state = %#v", initial)
t.Fatalf("initial application state = %#v", initial)
}

enabled, err := svc.SetUserPluginEnabled(user, WorkflowPluginRunningHub, true)
enabled, err := svc.SetUserPluginEnabled(user, PluginAIArtCritique, true)
if err != nil {
t.Fatal(err)
}
if !enabled.UserConfigured || !enabled.UserEnabled || !enabled.EffectiveEnabled {
t.Fatalf("enabled RunningHub state = %#v", enabled)
t.Fatalf("enabled application state = %#v", enabled)
}
if _, err := svc.SetPluginPlatformAvailability(user, WorkflowPluginRunningHub, false); err == nil {
if _, err := svc.SetPluginPlatformAvailability(user, PluginAIArtCritique, false); err == nil {
t.Fatal("ordinary user changed platform plugin availability")
}
disabled, err := svc.SetPluginPlatformAvailability(admin, WorkflowPluginRunningHub, false)
disabled, err := svc.SetPluginPlatformAvailability(admin, PluginAIArtCritique, false)
if err != nil {
t.Fatal(err)
}
if disabled.PlatformAvailable || disabled.EffectiveEnabled || disabled.EnabledUserCount != 1 {
t.Fatalf("platform-disabled RunningHub state = %#v", disabled)
t.Fatalf("platform-disabled application state = %#v", disabled)
}
if err := svc.RequireWorkflowPluginForUser(user.ID, "runninghub-workflow-image"); err == nil {
t.Fatal("platform-disabled workflow was accepted for a new user request")
if _, err := svc.SetPluginPlatformAvailability(admin, PluginAIArtCritique, true); err != nil {
t.Fatal(err)
}
if _, err := svc.SetPluginPlatformAvailability(admin, WorkflowPluginRunningHub, true); err != nil {
states, err = svc.PluginDomain().StatesForUser(user)
if err != nil {
t.Fatal(err)
}
if err := svc.RequireWorkflowPluginForUser(user.ID, "runninghub-workflow-video"); err != nil {
t.Fatalf("restored user workflow rejected: %v", err)
if !states[PluginAIArtCritique].EffectiveEnabled {
t.Fatalf("restored application lost the user's choice: %#v", states[PluginAIArtCritique])
}
}

Expand Down
3 changes: 0 additions & 3 deletions backend/internal/app/protocol_plugins.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,3 @@ func mapPluginAccessError(err error) error {
return err
}

func bundledWorkflowPluginManifests() []protocol.Manifest {
return plugins.BundledWorkflowManifests()
}
31 changes: 0 additions & 31 deletions backend/internal/app/protocol_runtime_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,6 @@ func TestPluginViewIncludesDocumentationForEveryOfficialProtocol(t *testing.T) {
packageIDs[pkg.Manifest.Metadata.ID] = true
}
expectedCount := localPackages
for _, manifest := range bundledWorkflowPluginManifests() {
if !packageIDs[manifest.Metadata.ID] {
expectedCount++
}
}
// The plugin market and built-in LL2 channel plugin ship next to the protocol packages as preinstalled npm-format packages.
for _, builtinDir := range []string{"beeftv-plugin-market", "beeftv-plugin-ll2"} {
if _, err := os.Stat(filepath.Join("..", "..", "..", "plugin-packages", builtinDir, "package.json")); err == nil {
Expand Down Expand Up @@ -187,32 +182,6 @@ func TestPluginRuntimeUpgradesInstalledWanMediaTransport(t *testing.T) {
}
}

func TestBundledWorkflowPluginsControlAvailability(t *testing.T) {
center, err := newPluginRuntime(t.TempDir())
if err != nil {
t.Fatal(err)
}
svc := &Service{pluginRuntime: center}
if err := svc.RequireWorkflowPluginForInterface("runninghub-workflow-image"); err == nil {
t.Fatal("bundled RunningHub plugin was enabled by default")
}
if _, err := center.setEnabled(WorkflowPluginRunningHub, true); err != nil {
t.Fatal(err)
}
if err := svc.RequireWorkflowPluginForInterface("runninghub-workflow-image"); err != nil {
t.Fatalf("enabled RunningHub plugin rejected: %v", err)
}
if _, err := center.setEnabled(WorkflowPluginRunningHub, false); err != nil {
t.Fatal(err)
}
if err := svc.RequireWorkflowPluginForInterface("runninghub-workflow-video"); err == nil {
t.Fatal("disabled RunningHub plugin remained available")
}
if got := svc.WorkflowPluginStatuses()[WorkflowPluginRunningHub]; got != "disabled" {
t.Fatalf("RunningHub status = %q, want disabled", got)
}
}

func TestBundledProviderCatalogExposesUpstreamOperation(t *testing.T) {
center, err := newPluginRuntime(t.TempDir())
if err != nil {
Expand Down
4 changes: 0 additions & 4 deletions backend/internal/app/provider_task_recovery.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
"infinite-canvas/backend/internal/beefapi"
"infinite-canvas/backend/internal/generation"
"infinite-canvas/backend/internal/model"
"infinite-canvas/backend/internal/provider/workflow"
localtask "infinite-canvas/backend/internal/task"
)

Expand Down Expand Up @@ -86,9 +85,6 @@ func (s *Service) queryFailedVideoTask(ctx context.Context, task *model.Task, cl
return res, projection.Status, nil, qErr
}, nil
},
QueryWorkflowVideo: func(c context.Context, cfg generation.Config, reqID string) (map[string]interface{}, string, error) {
return s.workflowClient(nil).QueryVideo(c, workflowConfigFromProvider(cfg), workflow.RootURL(cfg.BaseURL), reqID)
},
RecordMissing: s.recordMissingVideoReceiptOutcome,
RecordUnrecoverable: s.recordUnrecoverableVideoOutcome,
ClassifyOutcome: classifyFailedVideoQueryOutcome,
Expand Down
2 changes: 1 addition & 1 deletion backend/internal/app/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2812,7 +2812,7 @@ func TestProcessResumedSeedanceVideoDoesNotRevalidateDeletedReferences(t *testin
defer server.Close()
input := canvasGenerationInput{Mode: "video", Prompt: "test", Config: providerConfig{BaseURL: server.URL, APIKey: "key", Model: "seedance-2.5", InterfaceType: "newapi-channel-2"}, ReferenceAudios: []providerMedia{{StorageKey: "resource:deleted-voice"}}}
raw, _ := json.Marshal(input)
service, task := workflowPaidTaskFixture(t)
service, task := runningVideoTaskFixture(t)
task.ProviderRequestID = "existing-provider-task"
ctx := withProviderAnalytics(context.Background(), service, task)
result, err := service.processCanvasGenerationTask(ctx, task.UserID, "", "canvas_video", "", string(raw))
Expand Down
63 changes: 0 additions & 63 deletions backend/internal/app/runninghub_management.go

This file was deleted.

37 changes: 24 additions & 13 deletions backend/internal/app/submission_transport_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,35 @@ import (
"errors"
"net/http"
"testing"
"time"

"infinite-canvas/backend/internal/generation"
"infinite-canvas/backend/internal/model"
"infinite-canvas/backend/internal/provider/workflow"
"infinite-canvas/backend/internal/platform"
)

func runningVideoTaskFixture(t *testing.T) (*Service, model.Task) {
t.Helper()
s, db := newTimelineTaskTestService(t)
s.coordinator = platform.NewLocalCoordinator()
expires := time.Now().Add(time.Hour)
task := model.Task{
ID: "paid-video-task",
UserID: "user",
Type: "canvas_video",
Status: model.TaskStatusRunning,
LeaseOwner: "owner",
LeaseExpiresAt: &expires,
InputJSON: `{"mode":"video"}`,
}
if err := db.Create(&task).Error; err != nil {
t.Fatal(err)
}
return s, task
}

func TestRejectedModelSubmissionSurvivesPersistenceWithoutRetryLock(t *testing.T) {
s, task := workflowPaidTaskFixture(t)
s, task := runningVideoTaskFixture(t)
cause := generation.UncertainVideoSubmission(context.Background(), generation.HTTPError{
StatusCode: http.StatusServiceUnavailable,
Body: `{"error":{"code":"model_not_found","message":"分组 企业 下模型 grok-imagine-video-1.5 无可用渠道(distributor)"}}`,
Expand All @@ -31,7 +52,7 @@ func TestRejectedModelSubmissionSurvivesPersistenceWithoutRetryLock(t *testing.T
}

func TestUnsentVideoFailureSurvivesPersistenceWithoutRetryLock(t *testing.T) {
s, task := workflowPaidTaskFixture(t)
s, task := runningVideoTaskFixture(t)
task.FailureDiagnostics = &model.TaskFailureDiagnostics{Requests: []model.TaskRequestEvidence{{
Operation: "submit", Method: "POST", Outcome: "not_dispatched", TransportPhase: "tls", TransportRoute: "direct",
}}}
Expand Down Expand Up @@ -65,13 +86,3 @@ func TestLegacyUnknownSubmissionStillRequiresProof(t *testing.T) {
}
}

func TestWorkflowPreservesConfirmedUnsentCreate(t *testing.T) {
err := (&Service{}).fenceWorkflowSubmission(context.Background(), workflow.Input{}, workflow.CreateUncertain{
Err: generation.RequestNotSentError{Cause: context.DeadlineExceeded},
})
failure := generation.ClassifyError(err)
var unknown generation.SubmissionUnknownError
if errors.As(err, &unknown) || failure.Category != generation.CategoryRequestNotSent || failure.BlocksAutomaticRetry() {
t.Fatalf("workflow lost unsent evidence: err=%v failure=%+v", err, failure)
}
}
3 changes: 0 additions & 3 deletions backend/internal/app/task_creation.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,3 @@ func taskInputUsesSystemChannel(input map[string]any) bool {
return modelcatalog.TaskInputUsesSystemChannel(input)
}

func taskInputUsesWorkflowProvider(input map[string]any) bool {
return modelcatalog.TaskInputUsesWorkflowProvider(input)
}
Loading
Loading