adobe openai更新
This commit is contained in:
@@ -104,12 +104,24 @@ func (c *Client) GenerateImage(ctx context.Context, accessToken, prompt, model,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
// The generation submit is the only request that egresses via the proxy.
|
// The generation submit is the only request that egresses via the proxy. A
|
||||||
submitSession, err := c.newSession(accessToken)
|
// bare connection error here (EOF / connection reset) is almost always the
|
||||||
if err != nil {
|
// proxy hop flaking rather than the account, so retry the submit on a fresh
|
||||||
return nil, nil, err
|
// connection a few times before surfacing it — otherwise a momentary network
|
||||||
|
// blip fails over accounts (burning their quota) or fails the request.
|
||||||
|
var conversationID string
|
||||||
|
var fileIDs, sedimentIDs []string
|
||||||
|
for attempt := 0; ; attempt++ {
|
||||||
|
submitSession, sErr := c.newSession(accessToken)
|
||||||
|
if sErr != nil {
|
||||||
|
return nil, nil, sErr
|
||||||
|
}
|
||||||
|
conversationID, fileIDs, sedimentIDs, err = c.startImageGeneration(ctx, submitSession, accessToken, effectivePrompt, reqs, conduitToken, model, uploadedRefs)
|
||||||
|
if err == nil || attempt >= submitConnectRetries || ctx.Err() != nil || !isConnResetErr(err) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
time.Sleep(submitRetryBackoff)
|
||||||
}
|
}
|
||||||
conversationID, fileIDs, sedimentIDs, err := c.startImageGeneration(ctx, submitSession, accessToken, effectivePrompt, reqs, conduitToken, model, uploadedRefs)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
@@ -809,26 +821,39 @@ func (c *Client) prepareImageConversation(ctx context.Context, session tlsclient
|
|||||||
|
|
||||||
func (c *Client) startImageGeneration(ctx context.Context, session tlsclient.HttpClient, accessToken, prompt string, reqs *chatRequirements, conduitToken, model string, refs []uploadedReference) (string, []string, []string, error) {
|
func (c *Client) startImageGeneration(ctx context.Context, session tlsclient.HttpClient, accessToken, prompt string, reqs *chatRequirements, conduitToken, model string, refs []uploadedReference) (string, []string, []string, error) {
|
||||||
path := "/backend-api/f/conversation"
|
path := "/backend-api/f/conversation"
|
||||||
content := map[string]any{"content_type": "text", "parts": []string{prompt}}
|
// Match the web app: a picture_v2 prompt is sent as the "@创建图片" ecosystem
|
||||||
|
// mention followed by the prompt, with the mention marked via a
|
||||||
|
// custom_symbol_offset so the server routes the turn to image generation.
|
||||||
|
content := map[string]any{"content_type": "text", "parts": []string{pictureV2Command + " " + prompt}}
|
||||||
metadata := map[string]any{
|
metadata := map[string]any{
|
||||||
"selected_github_repos": []any{},
|
|
||||||
"selected_all_github_repos": false,
|
|
||||||
"system_hints": []string{"picture_v2"},
|
"system_hints": []string{"picture_v2"},
|
||||||
"serialization_metadata": map[string]any{"custom_symbol_offsets": []any{}},
|
"serialization_metadata": map[string]any{
|
||||||
|
"custom_symbol_offsets": []any{
|
||||||
|
map[string]any{
|
||||||
|
"id": "picture_v2",
|
||||||
|
"symbol": "ecosystemMention",
|
||||||
|
"startIndex": 0,
|
||||||
|
"endIndex": pictureV2MentionEnd,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
if len(refs) > 0 {
|
if len(refs) > 0 {
|
||||||
|
// Reference-image turns carry image parts and no leading mention, so keep
|
||||||
|
// the multimodal shape and drop the ecosystemMention offset.
|
||||||
content = map[string]any{
|
content = map[string]any{
|
||||||
"content_type": "multimodal_text",
|
"content_type": "multimodal_text",
|
||||||
"parts": buildMultimodalParts(refs, prompt),
|
"parts": buildMultimodalParts(refs, prompt),
|
||||||
}
|
}
|
||||||
metadata["attachments"] = buildAttachmentMetadata(refs)
|
metadata["attachments"] = buildAttachmentMetadata(refs)
|
||||||
|
metadata["serialization_metadata"] = map[string]any{"custom_symbol_offsets": []any{}}
|
||||||
}
|
}
|
||||||
payload := map[string]any{
|
payload := map[string]any{
|
||||||
"action": "next",
|
"action": "next",
|
||||||
"messages": []map[string]any{{
|
"messages": []map[string]any{{
|
||||||
"id": newUUID(),
|
"id": newUUID(),
|
||||||
"author": map[string]any{"role": "user"},
|
"author": map[string]any{"role": "user"},
|
||||||
"create_time": float64(time.Now().Unix()),
|
"create_time": float64(timeMillis()) / 1000.0,
|
||||||
"content": content,
|
"content": content,
|
||||||
"metadata": metadata,
|
"metadata": metadata,
|
||||||
}},
|
}},
|
||||||
@@ -842,9 +867,10 @@ func (c *Client) startImageGeneration(ctx context.Context, session tlsclient.Htt
|
|||||||
"system_hints": []string{"picture_v2"},
|
"system_hints": []string{"picture_v2"},
|
||||||
"supports_buffering": true,
|
"supports_buffering": true,
|
||||||
"supported_encodings": []string{"v1"},
|
"supported_encodings": []string{"v1"},
|
||||||
"client_contextual_info": map[string]any{"is_dark_mode": false, "time_since_loaded": 1200, "page_height": 1072, "page_width": 1724, "pixel_ratio": 1.2, "screen_height": 1440, "screen_width": 2560, "app_name": "chatgpt.com"},
|
"client_contextual_info": map[string]any{"is_dark_mode": false, "time_since_loaded": 1200, "page_height": 1072, "page_width": 1724, "pixel_ratio": 1.2, "screen_height": 1440, "screen_width": 2560, "app_name": "chatgpt.com", "has_web_push_capabilities": true, "web_push_notification_permission": "default"},
|
||||||
"paragen_cot_summary_display_override": "allow",
|
"paragen_cot_summary_display_override": "allow",
|
||||||
"force_parallel_switch": "auto",
|
"force_parallel_switch": "auto",
|
||||||
|
"local_function_names": []string{"local.continue_in_work"},
|
||||||
}
|
}
|
||||||
body, _ := json.Marshal(payload)
|
body, _ := json.Marshal(payload)
|
||||||
req, err := http.NewRequest(http.MethodPost, baseURL+path, bytes.NewReader(body))
|
req, err := http.NewRequest(http.MethodPost, baseURL+path, bytes.NewReader(body))
|
||||||
|
|||||||
@@ -24,6 +24,23 @@ const (
|
|||||||
// on gpt-5-5-thinking) we must not hold the stream open for the whole ctx
|
// on gpt-5-5-thinking) we must not hold the stream open for the whole ctx
|
||||||
// budget, so we break after this grace and fall through to polling.
|
// budget, so we break after this grace and fall through to polling.
|
||||||
sseAsyncGrace = 10 * time.Second
|
sseAsyncGrace = 10 * time.Second
|
||||||
|
|
||||||
|
// pictureV2Command is the "@创建图片" ecosystem mention the ChatGPT web app
|
||||||
|
// prepends to an image (picture_v2) prompt; the server identifies the image
|
||||||
|
// command from this mention plus its custom_symbol_offset, so we send it to
|
||||||
|
// match the browser exactly.
|
||||||
|
pictureV2Command = "@创建图片"
|
||||||
|
// pictureV2MentionEnd is the UTF-16 length of pictureV2Command — the
|
||||||
|
// ecosystemMention custom_symbol_offset endIndex the web app reports.
|
||||||
|
pictureV2MentionEnd = 5
|
||||||
|
|
||||||
|
// submitConnectRetries / submitRetryBackoff bound how many times the image
|
||||||
|
// submit (the proxied /backend-api/f/conversation POST) is retried on a bare
|
||||||
|
// connection error (EOF / reset) before surfacing it. The proxy hop
|
||||||
|
// occasionally drops the connection; a fresh retry usually succeeds without
|
||||||
|
// failing over to another account.
|
||||||
|
submitConnectRetries = 2
|
||||||
|
submitRetryBackoff = 500 * time.Millisecond
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -195,6 +212,23 @@ func conversationEndedWithoutImage(conversation map[string]any) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isConnResetErr reports whether err is a bare connection-level failure (the
|
||||||
|
// peer/proxy closed the connection before responding) rather than an HTTP-level
|
||||||
|
// error. Such failures are safe to retry: no response was received, so no
|
||||||
|
// conversation was created upstream.
|
||||||
|
func isConnResetErr(err error) bool {
|
||||||
|
if err == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
msg := strings.ToLower(err.Error())
|
||||||
|
for _, m := range []string{"eof", "connection reset", "reset by peer", "broken pipe", "connection refused"} {
|
||||||
|
if strings.Contains(msg, m) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func stringValue(v any) string {
|
func stringValue(v any) string {
|
||||||
switch x := v.(type) {
|
switch x := v.(type) {
|
||||||
case string:
|
case string:
|
||||||
|
|||||||
Reference in New Issue
Block a user