Skip to content
Merged
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
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ docker compose up -d
| :-- | :-- |
| `app` | `app_key`, `app_url`, `api_key`, `webui_enabled`, `webui_key` |
| `logging` | `file_level`, `max_files` |
| `features` | `temporary`, `memory`, `stream`, `thinking`, `auto_chat_mode_fallback`, `thinking_summary`, `dynamic_statsig`, `enable_nsfw`, `show_search_sources`, `custom_instruction`, `image_format`, `video_format` |
| `features` | `temporary`, `memory`, `stream`, `thinking`, `auto_chat_mode_fallback`, `thinking_summary`, `dynamic_statsig`, `enable_nsfw`, `show_search_sources`, `custom_instruction`, `image_format`, `imagine_public_image_proxy`, `video_format` |
| `proxy.egress` | `mode`, `proxy_url`, `proxy_pool`, `resource_proxy_url`, `resource_proxy_pool`, `skip_ssl_verify` |
| `proxy.clearance` | `mode`, `cf_cookies`, `user_agent`, `browser`, `flaresolverr_url`, `timeout_sec`, `refresh_interval` |
| `retry` | `reset_session_status_codes`, `max_retries`, `on_codes` |
Expand All @@ -216,6 +216,7 @@ docker compose up -d
| 配置项 | 可选值 |
| :-- | :-- |
| `features.image_format` | `grok_url`, `local_url`, `grok_md`, `local_md`, `base64` |
| `features.imagine_public_image_proxy` | `true`, `false` |
| `features.video_format` | `grok_url`, `local_url`, `grok_html`, `local_html` |

<br>
Expand Down
1 change: 0 additions & 1 deletion app/dataplane/reverse/protocol/xai_image_edit.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ def build_image_edit_payload(
"enableImageStreaming": True,
"imageGenerationCount": IMAGE_EDIT_GENERATION_COUNT,
"forceConcise": False,
"toolOverrides": {"imageGen": True},
"enableSideBySide": True,
"sendFinalMetadata": True,
"isReasoning": False,
Expand Down
44 changes: 26 additions & 18 deletions app/dataplane/reverse/transport/assets.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,23 @@
import asyncio
from typing import Any, AsyncGenerator, Dict, Optional

from app.platform.logging.logger import logger
from app.control.proxy.models import ProxyFeedback, ProxyFeedbackKind, ProxyScope, RequestKind
from app.dataplane.proxy import get_proxy_runtime
from app.dataplane.reverse.protocol.xai_assets import (
ASSETS_LIST_URL,
asset_delete_url,
infer_content_type,
resolve_download_url,
)
from app.dataplane.reverse.transport._proxy_feedback import upstream_feedback
from app.dataplane.reverse.transport.http import (
delete_json,
get_bytes_stream,
get_json,
)
from app.platform.config.snapshot import get_config
from app.platform.errors import UpstreamError
from app.platform.logging.logger import logger

# Global semaphores — limit concurrent transport calls across all callers.
# Lazily initialised so the event loop is guaranteed to be running on first use.
Expand All @@ -28,21 +43,6 @@ def _get_delete_sem() -> asyncio.Semaphore:
n = max(1, int(get_config("batch.asset_delete_concurrency", 50)))
_delete_sem = asyncio.Semaphore(n)
return _delete_sem
from app.platform.errors import UpstreamError
from app.control.proxy.models import ProxyFeedback, ProxyFeedbackKind, ProxyScope, RequestKind
from app.dataplane.reverse.transport._proxy_feedback import upstream_feedback
from app.dataplane.proxy import get_proxy_runtime
from app.dataplane.reverse.protocol.xai_assets import (
ASSETS_LIST_URL,
asset_delete_url,
infer_content_type,
resolve_download_url,
)
from app.dataplane.reverse.transport.http import (
delete_json,
get_bytes_stream,
get_json,
)


# ------------------------------------------------------------------
Expand Down Expand Up @@ -174,16 +174,24 @@ async def download_asset(
url, origin, referer = resolve_download_url(file_path)
content_type = infer_content_type(url)

if content_type and content_type.startswith("video/"):
accept = "video/mp4,video/*,*/*;q=0.8"
elif content_type and content_type.startswith("image/"):
accept = "image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8"
else:
accept = "*/*"

extra: Dict[str, str] = {
"Accept": accept,
"Cache-Control": "no-cache",
"Pragma": "no-cache",
"Priority": "u=0, i",
"Sec-Fetch-Dest": "document",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-Site": "none",
"Sec-Fetch-User": "?1",
"Upgrade-Insecure-Requests": "1",
}
if content_type:
extra["Content-Type"] = content_type

proxy = await get_proxy_runtime()
lease = await proxy.acquire(scope=ProxyScope.ASSET, kind=RequestKind.HTTP)
Expand Down
4 changes: 3 additions & 1 deletion app/dataplane/reverse/transport/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,9 @@ async def get_bytes_stream(
)
if extra_headers:
headers.update(extra_headers)
if headers.get("Sec-Fetch-Mode") == "navigate":
headers.pop("Content-Type", None)
headers.pop("Origin", None)
kwargs = build_session_kwargs(lease=lease)

session = ResettableSession(**kwargs)
Expand All @@ -259,7 +262,6 @@ async def get_bytes_stream(
stream=True,
allow_redirects=True,
)

if response.status_code != 200:
try:
body = (response.content).decode("utf-8", "replace")[:400]
Expand Down
22 changes: 18 additions & 4 deletions app/products/openai/chat.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import base64
import re
from typing import Any, AsyncGenerator
from urllib.parse import urlparse

import orjson

Expand Down Expand Up @@ -213,6 +214,14 @@ def _save_image(raw: bytes, mime: str, image_id: str) -> str:
return save_local_image(raw, mime, image_id)


def _is_imagine_public_url(url: str) -> bool:
try:
host = urlparse(url or "").hostname or ""
except Exception:
return False
return host.startswith("imagine-public")


async def _resolve_image(token: str, url: str, image_id: str) -> str:
"""Return the image embed text for the response body based on image_format config.

Expand All @@ -226,10 +235,15 @@ async def _resolve_image(token: str, url: str, image_id: str) -> str:
cfg = get_config()
fmt = _normalize_image_format(cfg.get_str("features.image_format", "grok_url"))

proxy_imagine_public = (
_is_imagine_public_url(url)
and cfg.get_bool("features.imagine_public_image_proxy", False)
)

# Formats that don't need downloading
if fmt == "grok_url":
if fmt == "grok_url" and not proxy_imagine_public:
return url
if fmt == "grok_md":
if fmt == "grok_md" and not proxy_imagine_public:
return f"![image]({url})"

# Formats that require downloading
Expand All @@ -254,9 +268,9 @@ async def _resolve_image(token: str, url: str, image_id: str) -> str:
else f"/v1/files/image?id={file_id}"
)

if fmt == "local_url":
if fmt in {"grok_url", "local_url"}:
return local_url
return f"![image]({local_url})" # local_md
return f"![image]({local_url})" # grok_md / local_md


def _normalize_image_format(value: str | None) -> str:
Expand Down
Loading
Loading