Default PaintPlus OpenAI provider to gpt-image-2, fix GPU quick-start

dall-e-2/dall-e-3 retired May 12 2026 and gpt-image-1 deprecates Oct 23
2026, so move every OpenAI default (config.py, both provider classes,
both compose files, .env.example, the in-app provider-settings dropdown,
README) to gpt-image-2 for both generation and edits. Also fix response
parsing in ai_provider.py's OpenAIProvider, which never sent a model
param and assumed a url response — gpt-image-1/2 only return b64_json.

Separately, .env.example shipped AI_PROVIDER=replicate by default, but
replicate has no driver in remote_provider.py, so following the
documented "cp .env.example .env" setup silently broke every AI call
and defeated the GPU quick-start (an explicit non-empty .env value
overrides docker-compose.gpu.yml's own local_gpu fallback). Default to
local_gpu instead, mark replicate/stability as not-yet-implemented, and
recommend Lykon/dreamshaper-8-inpainting as a hands/face-tuned
HF_MODEL_INPAINT override for 4-6GB cards (Quadro P2200, GTX 1060/1660).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nb2vJ8W7bHKx1JXVvpCraH
This commit is contained in:
Claude
2026-06-26 15:17:18 +00:00
parent 36a126bee7
commit 43c66ea860
8 changed files with 55 additions and 46 deletions
@@ -28,12 +28,11 @@ class RemoteAIProvider(ABC):
class OpenAIRemoteProvider(RemoteAIProvider):
"""OpenAI image API — gpt-image-1 / dall-e-3."""
"""OpenAI image API — gpt-image-2 (generation + edits, same model/endpoint family)."""
def __init__(self, api_key: str, model: str = "dall-e-3", edit_model: str = "gpt-image-1"):
def __init__(self, api_key: str, model: str = "gpt-image-2", edit_model: str = "gpt-image-2"):
self.api_key = api_key
self.model = model
# dall-e-3 has no edits/inpaint support at all — edits need a model of their own.
self.edit_model = edit_model
self.base_url = "https://api.openai.com/v1"
@@ -41,7 +40,7 @@ class OpenAIRemoteProvider(RemoteAIProvider):
return {"Authorization": f"Bearer {self.api_key}"}
async def _fetch_result(self, client: httpx.AsyncClient, item: dict) -> bytes:
# gpt-image-1 only ever returns b64_json; dall-e-2/dall-e-3 default to a url.
# gpt-image-1/2 only ever return b64_json; dall-e-2/dall-e-3 default to a url.
if item.get("b64_json"):
return base64.b64decode(item["b64_json"])
img_r = await client.get(item["url"])