feat: publish v1.0.0-rc.36-custom.1

This commit is contained in:
Codex committed 2026-09-11 05:07:48 +08:00
commit 3a7e8f3b54
2381 files changed
+551494

No files matched your search

+115
View File
@@ -0,0 +1,115 @@
package dto
type ChannelPinSource string
const (
PinSourceToken ChannelPinSource = "token" // Rank 0, highest
PinSourceOriginTask ChannelPinSource = "origin_task" // Rank 10
)
const (
PinRankToken = 0
PinRankOriginTask = 10
)
type PinRetryMode int
const (
PinRetrySameChannel PinRetryMode = iota
PinRetrySingleAttempt
)
func (m PinRetryMode) Stricter(other PinRetryMode) PinRetryMode {
if m == PinRetrySingleAttempt || other == PinRetrySingleAttempt {
return PinRetrySingleAttempt
}
return PinRetrySameChannel
}
type ChannelPin struct {
ChannelId int
Source ChannelPinSource
Rank int
RetryMode PinRetryMode
}
type ChannelFilterKind string
const (
FilterRequestPath ChannelFilterKind = "request_path"
FilterTaskPluginIdentity ChannelFilterKind = "task_plugin_identity"
)
type ChannelFilter struct {
Kind ChannelFilterKind
RequestPath string
TaskPluginKey string
TaskPluginChannelTypes []int
}
type ChannelConstraints struct {
Pins []ChannelPin
Filters []ChannelFilter
}
func (cc *ChannelConstraints) AddPin(p ChannelPin) {
if cc == nil {
return
}
cc.Pins = append(cc.Pins, p)
}
func (cc *ChannelConstraints) AddFilter(f ChannelFilter) {
if cc == nil {
return
}
cc.Filters = append(cc.Filters, f)
}
// ResolvedPin returns the winning pin after priority resolution.
// Lowest Rank wins. Pins that name the same channel are merged (stricter RetryMode).
// overridden lists pins that lost to a different channel (for warn logging).
func (cc *ChannelConstraints) ResolvedPin() (ChannelPin, bool, []ChannelPin) {
if cc == nil || len(cc.Pins) == 0 {
return ChannelPin{}, false, nil
}
merged := make(map[int]ChannelPin, len(cc.Pins))
order := make([]int, 0, len(cc.Pins))
for _, pin := range cc.Pins {
existing, seen := merged[pin.ChannelId]
if !seen {
merged[pin.ChannelId] = pin
order = append(order, pin.ChannelId)
continue
}
existing.RetryMode = existing.RetryMode.Stricter(pin.RetryMode)
if pin.Rank < existing.Rank {
existing.Rank = pin.Rank
existing.Source = pin.Source
}
merged[pin.ChannelId] = existing
}
winner := merged[order[0]]
for _, channelID := range order[1:] {
candidate := merged[channelID]
if candidate.Rank < winner.Rank {
winner = candidate
}
}
var overridden []ChannelPin
for _, channelID := range order {
candidate := merged[channelID]
if candidate.ChannelId != winner.ChannelId {
overridden = append(overridden, candidate)
}
}
return winner, true, overridden
}
func (cc *ChannelConstraints) SuppressesRetry() bool {
pin, found, _ := cc.ResolvedPin()
return found && pin.RetryMode == PinRetrySingleAttempt
}
+46
View File
@@ -0,0 +1,46 @@
package dto
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestResolvedPinPriorityAndMerge(t *testing.T) {
t.Run("token pin beats origin pin on a different channel", func(t *testing.T) {
constraints := &ChannelConstraints{}
constraints.AddPin(ChannelPin{ChannelId: 10, Source: PinSourceOriginTask, Rank: PinRankOriginTask, RetryMode: PinRetrySameChannel})
constraints.AddPin(ChannelPin{ChannelId: 1, Source: PinSourceToken, Rank: PinRankToken, RetryMode: PinRetrySingleAttempt})
pin, found, overridden := constraints.ResolvedPin()
require.True(t, found)
assert.Equal(t, 1, pin.ChannelId)
assert.Equal(t, PinSourceToken, pin.Source)
assert.Equal(t, PinRetrySingleAttempt, pin.RetryMode)
require.Len(t, overridden, 1)
assert.Equal(t, PinSourceOriginTask, overridden[0].Source)
assert.Equal(t, 10, overridden[0].ChannelId)
})
t.Run("same channel pins merge to the stricter retry mode", func(t *testing.T) {
constraints := &ChannelConstraints{}
constraints.AddPin(ChannelPin{ChannelId: 7, Source: PinSourceOriginTask, Rank: PinRankOriginTask, RetryMode: PinRetrySameChannel})
constraints.AddPin(ChannelPin{ChannelId: 7, Source: PinSourceToken, Rank: PinRankToken, RetryMode: PinRetrySingleAttempt})
pin, found, overridden := constraints.ResolvedPin()
require.True(t, found)
assert.Equal(t, 7, pin.ChannelId)
assert.Equal(t, PinSourceToken, pin.Source)
assert.Equal(t, PinRetrySingleAttempt, pin.RetryMode)
assert.Empty(t, overridden)
assert.True(t, constraints.SuppressesRetry())
})
t.Run("empty set has no pin", func(t *testing.T) {
pin, found, overridden := (*ChannelConstraints)(nil).ResolvedPin()
assert.False(t, found)
assert.Zero(t, pin.ChannelId)
assert.Nil(t, overridden)
})
}
+107
View File
@@ -0,0 +1,107 @@
package dto
//type SimpleMjRequest struct {
// Prompt string `json:"prompt"`
// CustomId string `json:"customId"`
// Action string `json:"action"`
// Content string `json:"content"`
//}
type SwapFaceRequest struct {
SourceBase64 string `json:"sourceBase64"`
TargetBase64 string `json:"targetBase64"`
}
type MidjourneyRequest struct {
Prompt string `json:"prompt"`
CustomId string `json:"customId"`
BotType string `json:"botType"`
NotifyHook string `json:"notifyHook"`
Action string `json:"action"`
Index int `json:"index"`
State string `json:"state"`
TaskId string `json:"taskId"`
Base64Array []string `json:"base64Array"`
Content string `json:"content"`
MaskBase64 string `json:"maskBase64"`
}
type MidjourneyResponse struct {
Code int `json:"code"`
Description string `json:"description"`
Properties any `json:"properties"`
Result string `json:"result"`
}
type MidjourneyUploadResponse struct {
Code int `json:"code"`
Description string `json:"description"`
Result []string `json:"result"`
}
type MidjourneyResponseWithStatusCode struct {
StatusCode int `json:"statusCode"`
Response MidjourneyResponse
}
type MidjourneyDto struct {
MjId string `json:"id"`
Action string `json:"action"`
CustomId string `json:"customId"`
BotType string `json:"botType"`
Prompt string `json:"prompt"`
PromptEn string `json:"promptEn"`
Description string `json:"description"`
State string `json:"state"`
SubmitTime int64 `json:"submitTime"`
StartTime int64 `json:"startTime"`
FinishTime int64 `json:"finishTime"`
ImageUrl string `json:"imageUrl"`
VideoUrl string `json:"videoUrl"`
VideoUrls []ImgUrls `json:"videoUrls"`
Status string `json:"status"`
Progress string `json:"progress"`
FailReason string `json:"failReason"`
Buttons any `json:"buttons"`
MaskBase64 string `json:"maskBase64"`
Properties *Properties `json:"properties"`
}
type ImgUrls struct {
Url string `json:"url"`
}
type MidjourneyStatus struct {
Status int `json:"status"`
}
type MidjourneyWithoutStatus struct {
Id int `json:"id"`
Code int `json:"code"`
UserId int `json:"user_id" gorm:"index"`
Action string `json:"action"`
MjId string `json:"mj_id" gorm:"index"`
Prompt string `json:"prompt"`
PromptEn string `json:"prompt_en"`
Description string `json:"description"`
State string `json:"state"`
SubmitTime int64 `json:"submit_time"`
StartTime int64 `json:"start_time"`
FinishTime int64 `json:"finish_time"`
ImageUrl string `json:"image_url"`
Progress string `json:"progress"`
FailReason string `json:"fail_reason"`
ChannelId int `json:"channel_id"`
}
type ActionButton struct {
CustomId any `json:"customId"`
Emoji any `json:"emoji"`
Label any `json:"label"`
Type any `json:"type"`
Style any `json:"style"`
}
type Properties struct {
FinalPrompt string `json:"finalPrompt"`
FinalZhPrompt string `json:"finalZhPrompt"`
}
+82
View File
@@ -0,0 +1,82 @@
package dto
// PluginResponsesResponse is the host-owned Responses facade used for stream
// snapshots and sanitized terminal failures. Non-stream success objects may
// retain additional validated plugin fields, but identifiers, lifecycle state,
// and retrieval metadata are always populated by the host.
type PluginResponsesResponse struct {
ID string `json:"id"`
Object string `json:"object"`
CreatedAt int64 `json:"created_at"`
Status string `json:"status"`
Error *PluginResponsesError `json:"error"`
IncompleteDetails *PluginResponsesIncompleteDetail `json:"incomplete_details"`
Instructions any `json:"instructions"`
Model string `json:"model"`
Output []PluginResponsesOutput `json:"output"`
ParallelToolCalls bool `json:"parallel_tool_calls"`
Temperature float64 `json:"temperature"`
ToolChoice any `json:"tool_choice"`
Tools []any `json:"tools"`
TopP float64 `json:"top_p"`
Metadata map[string]string `json:"metadata"`
Usage *PluginResponsesUsage `json:"usage"`
}
type PluginResponsesError struct {
Code string `json:"code"`
Message string `json:"message"`
}
type PluginResponsesIncompleteDetail struct {
Reason string `json:"reason"`
}
type PluginResponsesUsage struct {
InputTokens int `json:"input_tokens"`
InputTokensDetails PluginResponsesInputTokenDetails `json:"input_tokens_details"`
OutputTokens int `json:"output_tokens"`
OutputTokensDetails PluginResponsesOutputTokenDetails `json:"output_tokens_details"`
TotalTokens int `json:"total_tokens"`
}
type PluginResponsesInputTokenDetails struct {
CachedTokens int `json:"cached_tokens"`
}
type PluginResponsesOutputTokenDetails struct {
ReasoningTokens int `json:"reasoning_tokens"`
}
type PluginResponsesOutput struct {
ID string `json:"id"`
Type string `json:"type"`
Status string `json:"status"`
Role string `json:"role"`
Content []PluginResponsesContent `json:"content"`
}
type PluginResponsesContent struct {
ID string `json:"id"`
Type string `json:"type"`
Text string `json:"text"`
Annotations []any `json:"annotations"`
Logprobs []any `json:"logprobs"`
}
// PluginResponsesStreamEvent contains the exact event-specific fields used by
// the host Responses state machine. Pointer fields preserve required empty
// strings and arrays on delta/done events while omitting unrelated fields.
type PluginResponsesStreamEvent struct {
Type string `json:"type"`
SequenceNumber int `json:"sequence_number"`
Response *PluginResponsesResponse `json:"response,omitempty"`
OutputIndex *int `json:"output_index,omitempty"`
ContentIndex *int `json:"content_index,omitempty"`
ItemID string `json:"item_id,omitempty"`
Item *PluginResponsesOutput `json:"item,omitempty"`
Part *PluginResponsesContent `json:"part,omitempty"`
Delta *string `json:"delta,omitempty"`
Text *string `json:"text,omitempty"`
Logprobs *[]any `json:"logprobs,omitempty"`
}
+97
View File
@@ -0,0 +1,97 @@
package dto
import (
"encoding/json"
)
type SunoSubmitReq struct {
GptDescriptionPrompt string `json:"gpt_description_prompt,omitempty"`
Prompt string `json:"prompt,omitempty"`
Mv string `json:"mv,omitempty"`
Title string `json:"title,omitempty"`
Tags string `json:"tags,omitempty"`
ContinueAt float64 `json:"continue_at,omitempty"`
TaskID string `json:"task_id,omitempty"`
ContinueClipId string `json:"continue_clip_id,omitempty"`
MakeInstrumental bool `json:"make_instrumental"`
}
type SunoDataResponse struct {
TaskID string `json:"task_id" gorm:"type:varchar(50);index"`
Action string `json:"action" gorm:"type:varchar(40);index"` // 任务类型, song, lyrics, description-mode
Status string `json:"status" gorm:"type:varchar(20);index"` // 任务状态, submitted, queueing, processing, success, failed
FailReason string `json:"fail_reason"`
SubmitTime int64 `json:"submit_time" gorm:"index"`
StartTime int64 `json:"start_time" gorm:"index"`
FinishTime int64 `json:"finish_time" gorm:"index"`
Data json.RawMessage `json:"data" gorm:"type:json"`
}
type SunoSong struct {
ID string `json:"id"`
VideoURL string `json:"video_url"`
AudioURL string `json:"audio_url"`
ImageURL string `json:"image_url"`
ImageLargeURL string `json:"image_large_url"`
MajorModelVersion string `json:"major_model_version"`
ModelName string `json:"model_name"`
Status string `json:"status"`
Title string `json:"title"`
Text string `json:"text"`
Metadata SunoMetadata `json:"metadata"`
}
type SunoMetadata struct {
Tags string `json:"tags"`
Prompt string `json:"prompt"`
GPTDescriptionPrompt any `json:"gpt_description_prompt"`
AudioPromptID any `json:"audio_prompt_id"`
Duration any `json:"duration"`
ErrorType any `json:"error_type"`
ErrorMessage any `json:"error_message"`
}
type SunoLyrics struct {
ID string `json:"id"`
Status string `json:"status"`
Title string `json:"title"`
Text string `json:"text"`
}
type SunoGoAPISubmitReq struct {
CustomMode bool `json:"custom_mode"`
Input SunoGoAPISubmitReqInput `json:"input"`
NotifyHook string `json:"notify_hook,omitempty"`
}
type SunoGoAPISubmitReqInput struct {
GptDescriptionPrompt string `json:"gpt_description_prompt"`
Prompt string `json:"prompt"`
Mv string `json:"mv"`
Title string `json:"title"`
Tags string `json:"tags"`
ContinueAt float64 `json:"continue_at"`
TaskID string `json:"task_id"`
ContinueClipId string `json:"continue_clip_id"`
MakeInstrumental bool `json:"make_instrumental"`
}
type GoAPITaskResponse[T any] struct {
Code int `json:"code"`
Message string `json:"message"`
Data T `json:"data"`
ErrorMessage string `json:"error_message,omitempty"`
}
type GoAPITaskResponseData struct {
TaskID string `json:"task_id"`
}
type GoAPIFetchResponseData struct {
TaskID string `json:"task_id"`
Status string `json:"status"`
Input string `json:"input"`
Clips map[string]SunoSong `json:"clips"`
}
+91
View File
@@ -0,0 +1,91 @@
package dto
import (
"encoding/json"
)
type TaskError struct {
Code string `json:"code"`
Message string `json:"message"`
Data any `json:"data"`
StatusCode int `json:"-"`
LocalError bool `json:"-"`
Error error `json:"-"`
}
type TaskData interface {
SunoDataResponse | []SunoDataResponse | string | any
}
const TaskSuccessCode = "success"
type TaskResponse[T TaskData] struct {
Code string `json:"code"`
Message string `json:"message"`
Data T `json:"data"`
}
func (t *TaskResponse[T]) IsSuccess() bool {
return t.Code == TaskSuccessCode
}
type TaskDto struct {
ID int64 `json:"id"`
CreatedAt int64 `json:"created_at"`
UpdatedAt int64 `json:"updated_at"`
TaskID string `json:"task_id"`
Platform string `json:"platform"`
UserId int `json:"user_id"`
Group string `json:"group"`
ChannelId int `json:"channel_id"`
Quota int `json:"quota"`
Action string `json:"action"`
Status string `json:"status"`
FailReason string `json:"fail_reason"`
ResultURL string `json:"result_url,omitempty"` // 任务结果 URL(视频地址等)
LegacyVideoAvailable bool `json:"legacy_video_available,omitempty"`
SubmitTime int64 `json:"submit_time"`
StartTime int64 `json:"start_time"`
FinishTime int64 `json:"finish_time"`
Progress string `json:"progress"`
Properties any `json:"properties"`
Username string `json:"username,omitempty"`
Data json.RawMessage `json:"data"`
AdminInfo *TaskAdminInfo `json:"admin_info,omitempty"`
RootInfo *TaskRootInfo `json:"root_info,omitempty"`
}
type TaskPluginInfo struct {
Key string `json:"key"`
Name string `json:"name"`
Version string `json:"version,omitempty"`
Author *TaskPluginAuthorInfo `json:"author,omitempty"`
}
type TaskPluginAuthorInfo struct {
Name string `json:"name"`
URL string `json:"url,omitempty"`
}
type TaskPluginRuntimeInfo struct {
Key string `json:"key"`
Version string `json:"version"`
APIVersion int `json:"api_version"`
Generation uint64 `json:"generation"`
}
type TaskAdminInfo struct {
RequestID string `json:"request_id,omitempty"`
RequestPath string `json:"request_path,omitempty"`
TaskPlugin *TaskPluginInfo `json:"task_plugin,omitempty"`
}
type TaskRootInfo struct {
TaskPlugin *TaskPluginRuntimeInfo `json:"task_plugin,omitempty"`
UpstreamTaskID string `json:"upstream_task_id,omitempty"`
NodeName string `json:"node_name,omitempty"`
}
type FetchReq struct {
IDs []string `json:"ids"`
}
+23
View File
@@ -0,0 +1,23 @@
package dto
type TaskPluginError struct {
Code string `json:"code"`
Message string `json:"message"`
HTTPStatus int `json:"httpStatus"`
Retryable bool `json:"retryable"`
}
// TaskView is the only persisted-task shape exposed to JavaScript plugins.
// It deliberately excludes ownership, channel, quota, properties, and private
// upstream identifiers.
type TaskView struct {
TaskID string `json:"task_id"`
Platform string `json:"platform"`
Status string `json:"status"`
Progress string `json:"progress"`
FailReason string `json:"fail_reason"`
CreatedAt int64 `json:"created_at"`
UpdatedAt int64 `json:"updated_at,omitempty"`
FinishedAt int64 `json:"finished_at,omitempty"`
Data any `json:"data,omitempty"`
}
+47
View File
@@ -0,0 +1,47 @@
package dto
type VideoRequest struct {
Model string `json:"model,omitempty" example:"kling-v1"` // Model/style ID
Prompt string `json:"prompt,omitempty" example:"宇航员站起身走了"` // Text prompt
Image string `json:"image,omitempty" example:"https://h2.inkwai.com/bs2/upload-ylab-stunt/se/ai_portal_queue_mmu_image_upscale_aiweb/3214b798-e1b4-4b00-b7af-72b5b0417420_raw_image_0.jpg"` // Image input (URL/Base64)
Duration float64 `json:"duration" example:"5.0"` // Video duration (seconds)
Width int `json:"width" example:"512"` // Video width
Height int `json:"height" example:"512"` // Video height
Fps int `json:"fps,omitempty" example:"30"` // Video frame rate
Seed int `json:"seed,omitempty" example:"20231234"` // Random seed
N int `json:"n,omitempty" example:"1"` // Number of videos to generate
ResponseFormat string `json:"response_format,omitempty" example:"url"` // Response format
User string `json:"user,omitempty" example:"user-1234"` // User identifier
Metadata map[string]any `json:"metadata,omitempty"` // Vendor-specific/custom params (e.g. negative_prompt, style, quality_level, etc.)
}
// VideoResponse 视频生成提交任务后的响应
type VideoResponse struct {
TaskId string `json:"task_id"`
Status string `json:"status"`
}
// VideoTaskResponse 查询视频生成任务状态的响应
type VideoTaskResponse struct {
TaskId string `json:"task_id" example:"abcd1234efgh"` // 任务ID
Status string `json:"status" example:"succeeded"` // 任务状态
Url string `json:"url,omitempty"` // 视频资源URL(成功时)
Format string `json:"format,omitempty" example:"mp4"` // 视频格式
Metadata *VideoTaskMetadata `json:"metadata,omitempty"` // 结果元数据
Error *VideoTaskError `json:"error,omitempty"` // 错误信息(失败时)
}
// VideoTaskMetadata 视频任务元数据
type VideoTaskMetadata struct {
Duration float64 `json:"duration" example:"5.0"` // 实际生成的视频时长
Fps int `json:"fps" example:"30"` // 实际帧率
Width int `json:"width" example:"512"` // 实际宽度
Height int `json:"height" example:"512"` // 实际高度
Seed int `json:"seed" example:"20231234"` // 使用的随机种子
}
// VideoTaskError 视频任务错误信息
type VideoTaskError struct {
Code int `json:"code"`
Message string `json:"message"`
}