gpt-image-2 multi-image + fix: tools need reasoning_effort none on gpt-5.6 (ggg mute bug)
This commit is contained in:
@@ -1,14 +1,14 @@
|
||||
import asyncio
|
||||
import base64
|
||||
import hashlib
|
||||
import json
|
||||
import logging
|
||||
from io import BytesIO
|
||||
from typing import Any, Dict, List, Optional, Tuple
|
||||
|
||||
import aiohttp
|
||||
import openai
|
||||
|
||||
from .ai_responder import AIResponder, exponential_backoff, pp, sanitize_external_text
|
||||
from .ai_responder import AIResponder, exponential_backoff, sanitize_external_text
|
||||
from .igdblib import IGDBQuery
|
||||
from .leonardo_draw import LeonardoAIDrawMixIn
|
||||
from .quota import QuotaLedger
|
||||
@@ -24,10 +24,11 @@ ENVELOPE_SCHEMA = {
|
||||
"channel": {"type": ["string", "null"], "description": "Target channel name, or null for the origin channel."},
|
||||
"staff": {"type": ["string", "null"], "description": "Alert text for the staff channel, or null."},
|
||||
"picture": {"type": ["string", "null"], "description": "Image generation prompt, or null."},
|
||||
"picture_count": {"type": "integer", "description": "How many images to generate (1-4), 1 unless more were asked for."},
|
||||
"picture_edit": {"type": "boolean", "description": "Whether the picture refers to an earlier image."},
|
||||
"hack": {"type": "boolean", "description": "Whether the user tried to manipulate the assistant."},
|
||||
},
|
||||
"required": ["answer", "answer_needed", "channel", "staff", "picture", "picture_edit", "hack"],
|
||||
"required": ["answer", "answer_needed", "channel", "staff", "picture", "picture_count", "picture_edit", "hack"],
|
||||
"additionalProperties": False,
|
||||
}
|
||||
ENVELOPE_RESPONSE_FORMAT = {"type": "json_schema", "json_schema": {"name": "envelope", "strict": True, "schema": ENVELOPE_SCHEMA}}
|
||||
@@ -92,10 +93,7 @@ async def openai_chat(client, *args, **kwargs):
|
||||
|
||||
|
||||
async def openai_image(client, *args, **kwargs):
|
||||
response = await client.images.generate(*args, **kwargs)
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(response.data[0].url) as image:
|
||||
return BytesIO(await image.read())
|
||||
return await client.images.generate(*args, **kwargs)
|
||||
|
||||
|
||||
class OpenAIResponder(AIResponder, LeonardoAIDrawMixIn):
|
||||
@@ -126,15 +124,26 @@ class OpenAIResponder(AIResponder, LeonardoAIDrawMixIn):
|
||||
else:
|
||||
logging.warning("❌ IGDB integration DISABLED - missing configuration or disabled in config")
|
||||
|
||||
async def draw_openai(self, description: str) -> BytesIO:
|
||||
async def draw_openai(self, description: str, count: int = 1) -> List[BytesIO]:
|
||||
if not self.ledger.budget_ok():
|
||||
raise RuntimeError("daily budget exhausted - refusing image call")
|
||||
model = self.config.get("image-model", "gpt-image-2")
|
||||
kwargs: Dict[str, Any] = {"model": model, "prompt": description, "size": self.config.get("image-size", "1024x1024")}
|
||||
if "image-quality" in self.config:
|
||||
kwargs["quality"] = self.config["image-quality"]
|
||||
if model.startswith("gpt-image"):
|
||||
kwargs["n"] = max(1, min(int(count), 4))
|
||||
else:
|
||||
# legacy models: single image, base64 must be requested (IMG-04)
|
||||
kwargs["n"] = 1
|
||||
kwargs["response_format"] = "b64_json"
|
||||
for _ in range(3):
|
||||
try:
|
||||
response = await openai_image(self.client, prompt=description, n=1, size="1024x1024", model="dall-e-3")
|
||||
self.ledger.add_images(1)
|
||||
logging.info(f"Drawed a picture with DALL-E on this description: {repr(description)}")
|
||||
return response
|
||||
response = await openai_image(self.client, **kwargs)
|
||||
buffers = [BytesIO(base64.b64decode(item.b64_json)) for item in response.data]
|
||||
self.ledger.add_images(len(buffers))
|
||||
logging.info(f"generated {len(buffers)} image(s) on {model} for: {repr(description)}")
|
||||
return buffers
|
||||
except Exception as err:
|
||||
logging.warning(f"Failed to generate image {repr(description)}: {repr(err)}")
|
||||
raise RuntimeError(f"Failed to generate image {repr(description)} after multiple retries")
|
||||
@@ -209,6 +218,8 @@ class OpenAIResponder(AIResponder, LeonardoAIDrawMixIn):
|
||||
if igdb_functions and isinstance(igdb_functions, list):
|
||||
chat_kwargs["tools"] = [{"type": "function", "function": func} for func in igdb_functions]
|
||||
chat_kwargs["tool_choice"] = "auto"
|
||||
# gpt-5.6 rejects tools + reasoning on chat/completions (ENV-21)
|
||||
chat_kwargs["reasoning_effort"] = self.config.get("reasoning-effort", "none")
|
||||
logging.info(f"🎮 IGDB functions available to AI: {[f['name'] for f in igdb_functions]}")
|
||||
logging.debug(f" Full chat_kwargs with tools: {list(chat_kwargs.keys())}")
|
||||
except (TypeError, AttributeError) as e:
|
||||
@@ -343,27 +354,6 @@ class OpenAIResponder(AIResponder, LeonardoAIDrawMixIn):
|
||||
logging.debug(f"Full traceback: {traceback.format_exc()}")
|
||||
return None, limit
|
||||
|
||||
async def translate(self, text: str, language: str = "english") -> str:
|
||||
if "fix-model" not in self.config:
|
||||
return text
|
||||
message = [
|
||||
{
|
||||
"role": "system",
|
||||
"content": f"You are an professional translator to {language} language,"
|
||||
f" you translate everything you get directly to {language}"
|
||||
f" if it is not already in {language}, otherwise you just copy it.",
|
||||
},
|
||||
{"role": "user", "content": text},
|
||||
]
|
||||
try:
|
||||
result = await openai_chat(self.client, model=self.config["fix-model"], messages=message)
|
||||
response = result.choices[0].message.content
|
||||
logging.info(f"got this translated message:\n{pp(response)}")
|
||||
return response
|
||||
except Exception as err:
|
||||
logging.warning(f"failed to translate the text: {repr(err)}")
|
||||
return text
|
||||
|
||||
async def classify(self, message: Any, history_tail: List[Dict[str, Any]]) -> Optional[Dict[str, Any]]:
|
||||
"""~100-token reply/factual/emoji verdict on classifier-model (BEH-01/03)."""
|
||||
if "classifier-model" not in self.config or not self.ledger.budget_ok():
|
||||
|
||||
Reference in New Issue
Block a user