-
Notifications
You must be signed in to change notification settings - Fork 11k
Codex/dreambrand model aliases #6529
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
yuansirrrrr
wants to merge
26
commits into
QuantumNous:main
Choose a base branch
from
yuansirrrrr:codex/dreambrand-model-aliases
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
26 commits
Select commit
Hold shift + click to select a range
7b80a5e
修改端口
yuansirrrrr 62961b2
feat: add ZLHub Seedance video adapter
yuansirrrrr 7b45682
fix: build custom deployment image from source
yuansirrrrr 5f1270c
fix: use configurable Go proxy for Docker builds
yuansirrrrr cafa13e
切换default的ui
yuansirrrrr d314987
fix_docker_compose
yuansirrrrr 98a0bd0
feat: add official Alipay topup support
yuansirrrrr fdf72ef
增加支付宝官方渠道
yuansirrrrr 6d68175
修改首页ui_v1
yuansirrrrr cfc97d5
延长视频代理超时时间
yuansirrrrr ef6e164
Add ZLHub asset audit relay
yuansirrrrr 85368d5
Fix Codex OAuth channel proxy flow
yuansirrrrr 8cc8e2f
Handle saved Codex OAuth channel credentials
yuansirrrrr 0e8df73
Accept saved Codex OAuth response
yuansirrrrr ef5c246
Add Deeprouter user guide docs
yuansirrrrr bbec5a8
修改用户指南为mdx
yuansirrrrr 0454bbe
添加文档语言切换
yuansirrrrr dc59654
Add ZLHub Seedance 2.0 Mini support
yuansirrrrr 51100eb
feat(web): refresh deeprouter homepage
yuansirrrrr 4104833
feat: add DreamBrand video channel adapter
yuansirrrrr 746c2fb
feat: map DreamBrand Seedance model aliases
yuansirrrrr 54b5ec7
feat: support DreamBrand Seedream generation tasks
yuansirrrrr 38f5634
feat: align DreamBrand models with unified APIs
yuansirrrrr c689a63
feat: support official Seedream and Seedance requests
yuansirrrrr d97a5cb
fix: defer DreamBrand resolution limits upstream
yuansirrrrr 5f2eba1
fix: extend DreamBrand image polling timeout
yuansirrrrr File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| package constant | ||
|
|
||
| const AssetAuditModelName = "zlhub-asset-audit" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,212 @@ | ||
| package controller | ||
|
|
||
| import ( | ||
| "bytes" | ||
| "crypto/rand" | ||
| "encoding/hex" | ||
| "fmt" | ||
| "io" | ||
| "net/http" | ||
| "net/url" | ||
| "strings" | ||
| "time" | ||
|
|
||
| "github.com/QuantumNous/new-api/common" | ||
| "github.com/QuantumNous/new-api/constant" | ||
| "github.com/QuantumNous/new-api/dto" | ||
| "github.com/QuantumNous/new-api/logger" | ||
| "github.com/QuantumNous/new-api/model" | ||
| relaycommon "github.com/QuantumNous/new-api/relay/common" | ||
| "github.com/QuantumNous/new-api/relay/helper" | ||
| "github.com/QuantumNous/new-api/service" | ||
| "github.com/QuantumNous/new-api/types" | ||
|
|
||
| "github.com/gin-gonic/gin" | ||
| ) | ||
|
|
||
| const assetAuditTrackIDBytes = 16 | ||
|
|
||
| func RelayAssetAuditUploadSync(c *gin.Context) { | ||
| relayAssetAudit(c, http.MethodPost, "/api/asset/upload/sync") | ||
| } | ||
|
|
||
| func RelayAssetAuditUploadAsync(c *gin.Context) { | ||
| relayAssetAudit(c, http.MethodPost, "/api/asset/upload/async") | ||
| } | ||
|
|
||
| func RelayAssetAuditTask(c *gin.Context) { | ||
| taskID := strings.TrimSpace(c.Param("task_id")) | ||
| if taskID == "" { | ||
| c.JSON(http.StatusBadRequest, gin.H{ | ||
| "code": "invalid_request", | ||
| "message": "task_id is required", | ||
| }) | ||
| return | ||
| } | ||
| relayAssetAudit(c, http.MethodGet, "/api/task/"+url.PathEscape(taskID)) | ||
| } | ||
|
|
||
| func relayAssetAudit(c *gin.Context, method string, upstreamPath string) { | ||
| if common.GetContextKeyInt(c, constant.ContextKeyChannelType) != constant.ChannelTypeZLHubAsset { | ||
| c.JSON(http.StatusBadRequest, gin.H{ | ||
| "code": "invalid_channel_type", | ||
| "message": "asset audit requests require a ZLHubAsset channel", | ||
| }) | ||
| return | ||
| } | ||
|
|
||
| info := newAssetAuditRelayInfo(c) | ||
| priceData, err := helper.ModelPriceHelperPerCall(c, info) | ||
| if err != nil { | ||
| c.JSON(http.StatusBadRequest, gin.H{ | ||
| "code": "model_price_error", | ||
| "message": err.Error(), | ||
| }) | ||
| return | ||
| } | ||
| info.PriceData = priceData | ||
|
|
||
| if !priceData.FreeModel { | ||
| if apiErr := service.PreConsumeBilling(c, priceData.Quota, info); apiErr != nil { | ||
| c.JSON(apiErr.StatusCode, gin.H{"error": apiErr.ToOpenAIError()}) | ||
| return | ||
| } | ||
| } | ||
|
|
||
| resp, respBody, err := doAssetAuditRequest(c, method, upstreamPath) | ||
| if err != nil { | ||
| if info.Billing != nil { | ||
| info.Billing.Refund(c) | ||
| } | ||
| c.JSON(http.StatusInternalServerError, gin.H{ | ||
| "code": "do_request_failed", | ||
| "message": err.Error(), | ||
| }) | ||
| return | ||
| } | ||
| defer resp.Body.Close() | ||
|
|
||
| if resp.StatusCode >= http.StatusBadRequest { | ||
| if info.Billing != nil { | ||
| info.Billing.Refund(c) | ||
| } | ||
| service.IOCopyBytesGracefully(c, resp, respBody) | ||
| return | ||
| } | ||
|
|
||
| if err := service.SettleBilling(c, info, priceData.Quota); err != nil { | ||
| logger.LogError(c, fmt.Sprintf("asset audit settle billing failed: %v", err)) | ||
| } | ||
| recordAssetAuditConsumeLog(c, info, priceData.Quota, upstreamPath) | ||
|
|
||
| service.IOCopyBytesGracefully(c, resp, respBody) | ||
| } | ||
|
|
||
| func newAssetAuditRelayInfo(c *gin.Context) *relaycommon.RelayInfo { | ||
| startTime := common.GetContextKeyTime(c, constant.ContextKeyRequestStartTime) | ||
| if startTime.IsZero() { | ||
| startTime = time.Now() | ||
| } | ||
| tokenGroup := common.GetContextKeyString(c, constant.ContextKeyTokenGroup) | ||
| if tokenGroup == "" { | ||
| tokenGroup = common.GetContextKeyString(c, constant.ContextKeyUserGroup) | ||
| } | ||
| info := &relaycommon.RelayInfo{ | ||
| RequestId: c.GetString(common.RequestIdKey), | ||
| UserId: common.GetContextKeyInt(c, constant.ContextKeyUserId), | ||
| UsingGroup: common.GetContextKeyString(c, constant.ContextKeyUsingGroup), | ||
| UserGroup: common.GetContextKeyString(c, constant.ContextKeyUserGroup), | ||
| UserQuota: common.GetContextKeyInt(c, constant.ContextKeyUserQuota), | ||
| UserEmail: common.GetContextKeyString(c, constant.ContextKeyUserEmail), | ||
| TokenId: common.GetContextKeyInt(c, constant.ContextKeyTokenId), | ||
| TokenKey: common.GetContextKeyString(c, constant.ContextKeyTokenKey), | ||
| TokenUnlimited: common.GetContextKeyBool(c, constant.ContextKeyTokenUnlimited), | ||
| TokenGroup: tokenGroup, | ||
| OriginModelName: constant.AssetAuditModelName, | ||
| StartTime: startTime, | ||
| RelayFormat: types.RelayFormatOpenAI, | ||
| } | ||
| info.InitChannelMeta(c) | ||
| return info | ||
| } | ||
|
|
||
| func doAssetAuditRequest(c *gin.Context, method string, upstreamPath string) (*http.Response, []byte, error) { | ||
| var body io.Reader | ||
| if method != http.MethodGet { | ||
| storage, err := common.GetBodyStorage(c) | ||
| if err != nil { | ||
| return nil, nil, err | ||
| } | ||
| bodyBytes, err := storage.Bytes() | ||
| if err != nil { | ||
| return nil, nil, err | ||
| } | ||
| body = bytes.NewReader(bodyBytes) | ||
| } | ||
|
|
||
| baseURL := strings.TrimRight(common.GetContextKeyString(c, constant.ContextKeyChannelBaseUrl), "/") | ||
| if baseURL == "" { | ||
| baseURL = strings.TrimRight(constant.ChannelBaseURLs[constant.ChannelTypeZLHubAsset], "/") | ||
| } | ||
| req, err := http.NewRequestWithContext(c.Request.Context(), method, baseURL+upstreamPath, body) | ||
| if err != nil { | ||
| return nil, nil, err | ||
| } | ||
|
|
||
| trackID, err := newAssetAuditTrackID() | ||
| if err != nil { | ||
| return nil, nil, err | ||
| } | ||
| req.Header.Set("Content-Type", "application/json") | ||
| req.Header.Set("Accept", "application/json") | ||
| req.Header.Set("X-Access-Token", common.GetContextKeyString(c, constant.ContextKeyChannelKey)) | ||
| req.Header.Set("X-Track-Id", trackID) | ||
|
|
||
| proxy := "" | ||
| if setting, ok := common.GetContextKeyType[dto.ChannelSettings](c, constant.ContextKeyChannelSetting); ok { | ||
| proxy = setting.Proxy | ||
| } | ||
| client, err := service.GetHttpClientWithProxy(proxy) | ||
| if err != nil { | ||
| return nil, nil, err | ||
| } | ||
| resp, err := client.Do(req) | ||
| if err != nil { | ||
| if resp != nil && resp.Body != nil { | ||
| _ = resp.Body.Close() | ||
| } | ||
| return nil, nil, err | ||
| } | ||
| respBody, err := io.ReadAll(resp.Body) | ||
| if err != nil { | ||
| _ = resp.Body.Close() | ||
| return nil, nil, err | ||
| } | ||
| resp.Body = io.NopCloser(bytes.NewReader(respBody)) | ||
| return resp, respBody, nil | ||
| } | ||
|
|
||
| func newAssetAuditTrackID() (string, error) { | ||
| buf := make([]byte, assetAuditTrackIDBytes) | ||
| if _, err := rand.Read(buf); err != nil { | ||
| return "", err | ||
| } | ||
| return hex.EncodeToString(buf), nil | ||
| } | ||
|
|
||
| func recordAssetAuditConsumeLog(c *gin.Context, info *relaycommon.RelayInfo, quota int, upstreamPath string) { | ||
| model.RecordConsumeLog(c, info.UserId, model.RecordConsumeLogParams{ | ||
| ChannelId: info.ChannelId, | ||
| ModelName: constant.AssetAuditModelName, | ||
| TokenName: c.GetString("token_name"), | ||
| Quota: quota, | ||
| Content: fmt.Sprintf("asset audit request: %s", upstreamPath), | ||
| TokenId: info.TokenId, | ||
| UseTimeSeconds: int(time.Since(info.StartTime).Seconds()), | ||
| Group: info.UsingGroup, | ||
| Other: map[string]interface{}{ | ||
| "asset_audit": true, | ||
| "path": upstreamPath, | ||
| }, | ||
| }) | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift
Preserve quota-saturation metadata before logging.
This flow never calls
attachQuotaSaturationbefore Line 100 writes the consume log, and no clamp result is propagated into that log. Use the checked quota result and attach its saturation metadata before recording consumption.As per coding guidelines, “Billing paths must use checked quota helpers, propagate clamp information, and call
attachQuotaSaturationbefore writing consume or task logs so saturation is auditable.”🤖 Prompt for AI Agents
Source: Coding guidelines