fix(image_gen): resolve OpenAI and Meta image base URLs through the secret scope

Sibling sites of #119986's class: the openai and meta-ai image backends
resolved their API key through get_secret but the base URL through
os.environ, so on a multiplexed gateway a routed profile's key was sent to
the launch profile's endpoint. Both fields now come from the same scope
(get_secret_str, like the DeepInfra video backend after #119986).
This commit is contained in:
teknium1
2026-09-23 03:02:09 -07:00
committed by Teknium
parent f686368b2f
commit 6bbbc65b45
3 changed files with 24 additions and 5 deletions

View File

@@ -6,10 +6,9 @@ Selection: ``model`` kwarg → ``META_IMAGE_MODEL`` → ``image_gen.meta-ai.mode
from __future__ import annotations
import logging
import os
from typing import Any, Dict, List, Optional, Tuple
from agent.secret_scope import get_secret
from agent.secret_scope import get_secret, get_secret_str
from agent.image_gen_provider import (
DEFAULT_ASPECT_RATIO, resolve_aspect_ratio, save_b64_image, save_url_image, success_response)
from plugins.image_gen._common import (
@@ -32,7 +31,9 @@ def _resolve_api_key() -> Optional[str]:
def _resolve_base_url() -> str:
return (os.environ.get(BASE_URL_ENV) or "").strip() or DEFAULT_BASE_URL
# Through the secret scope like the key: under multiplexing os.environ holds the launch profile's
# endpoint, and a routed profile's key must never be sent to another profile's base URL.
return get_secret_str(BASE_URL_ENV).strip() or DEFAULT_BASE_URL
# Model ids are sent verbatim to ``/v1/images/generations``.

View File

@@ -12,7 +12,7 @@ import logging
import os
from typing import Any, Dict, List, Optional, Tuple
from agent.secret_scope import get_secret
from agent.secret_scope import get_secret, get_secret_str
from agent.image_gen_provider import DEFAULT_ASPECT_RATIO, resolve_aspect_ratio, success_response
from plugins.image_gen._common import (
GPT_IMAGE_2_API_MODEL as API_MODEL, GPT_IMAGE_2_DEFAULT as DEFAULT_MODEL, GPT_IMAGE_2_TIERS,
@@ -70,7 +70,7 @@ def _resolve_endpoint() -> Tuple[str, str]:
named = str(cfg.get("provider") or "").strip()
named_base, named_key = _named_endpoint(named) if named else ("", "")
base_url = (str(cfg.get("base_url") or "").strip().rstrip("/") or named_base
or os.environ.get("OPENAI_BASE_URL", "").strip())
or get_secret_str("OPENAI_BASE_URL").strip())
key_env = str(cfg.get("key_env") or "").strip()
api_key = (get_secret(key_env) if key_env else None) or named_key or get_secret("OPENAI_API_KEY") or ""
return base_url, api_key

View File

@@ -109,6 +109,24 @@ class TestResolution:
monkeypatch.setenv("META_BASE_URL", "https://proxy.internal/v1")
assert meta_plugin._resolve_base_url() == "https://proxy.internal/v1"
def test_base_url_follows_the_profile_secret_scope(self, monkeypatch):
"""Under multiplexing os.environ is the launch profile's: a routed profile's key must go to
ITS base URL, and a profile without an override gets the default — never the launch URL."""
from agent.secret_scope import reset_secret_scope, set_multiplex_active, set_secret_scope
monkeypatch.setenv("META_BASE_URL", "https://launch.example/v1")
set_multiplex_active(True)
try:
for scope, expected in (({"META_BASE_URL": "https://profile-b.example/v1"}, "https://profile-b.example/v1"),
({}, "https://api.meta.ai/v1")):
token = set_secret_scope(scope)
try:
assert meta_plugin._resolve_base_url() == expected
finally:
reset_secret_scope(token)
finally:
set_multiplex_active(False)
# ── Model resolution ──────────────────────────────────────────────────────────