Files
magpie/internal/agent/codex_image_tool_test.go
maYandyetone 6fe8169502 gateway, agent: Codex draws with its native image tool through magpie (#870)
* fix(agent): give magpie's provider table the actor-authorization header

Codex offers its built-in image tool (image_gen.imagegen) — and the other
OpenAI-provider tools — only to a provider it reads as the OpenAI actor:
ModelProviderInfo::uses_openai_actor_authorization(), i.e. a provider that
does not require OpenAI auth but carries x-openai-actor-authorization.
magpie writes [model_providers.magpie] but carried neither signal, so a
Codex on magpie was never given the tool and could not draw through a
Codex backend such as sub2api's.

Write http_headers = { "x-openai-actor-authorization" = "magpie" } into
that table. magpie does not forward the header upstream: codexHeader
rejects anything naming "authorization".

Test: TestCodexMagpieProviderCarriesActorAuthorization.

* gateway/provider: let a Responses-only relay (Codex backend, sub2api) draw

A provider added as 'OpenAI Responses' only (what a Codex backend such as
sub2api speaks) has no chat base, so Drawers() returned nil before ever
looking at the vendor's list, draw() refused with 'can't draw', and
drawImages() posted to an empty base. The vendor's own /v1/models does list
its image models (sub2api serves gpt-image-2.5), and they are meant to be
picked in Settings → Images.

- Drawers: a Responses-only provider draws when it has any OpenAI-shaped
  base, not only a chat one.
- draw: refuse only when neither Chat nor Responses is set.
- drawImages: fall back to the Responses base when there is no chat base.
- List: the add form's 'Fetch models' keeps the vendor's image models so
  one can be picked while the provider is added.

Tests: TestResponsesOnlyProviderDraws, TestListOffersImageModels.

* fix: route Codex native images through its turn provider

---------

Co-authored-by: yetone <yetoneful@gmail.com>
2026-10-05 17:19:42 +08:00

38 lines
1.5 KiB
Go

package agent
import (
"strings"
"testing"
)
// Codex gives the model its built-in image tool (image_gen.imagegen) only to
// a provider it reads as the OpenAI actor: one that does not require OpenAI
// auth but carries x-openai-actor-authorization
// (ModelProviderInfo::uses_openai_actor_authorization). So magpie's own
// provider table must carry that header and must not require OpenAI auth, or
// a Codex on magpie never has the tool at all and cannot draw through a Codex
// backend (sub2api's).
func TestCodexMagpieProviderCarriesActorAuthorization(t *testing.T) {
home, read := codexHome(t, `{"OPENAI_API_KEY":"sk-relay"}`,
"model = \"gpt-5.5\"\nmodel_provider = \"relay\"\n\n[model_providers.relay]\nname = \"relay\"\nbase_url = \"https://relay.example/v1\"\nwire_api = \"responses\"\n")
cx := codex(home)
if err := cx.Fields[0].Set("fake/m1"); err != nil {
t.Fatal(err)
}
cfg := read()
if !strings.Contains(cfg, "[model_providers.magpie]") {
t.Fatalf("no magpie provider table:\n%s", cfg)
}
if !strings.Contains(cfg, `"x-openai-actor-authorization" = "magpie"`) {
t.Fatalf("magpie provider table carries no actor-authorization header:\n%s", cfg)
}
// uses_openai_actor_authorization() also requires !requires_openai_auth:
// the header beside that key would turn the flag off again.
if strings.Contains(cfg, "requires_openai_auth") {
t.Fatalf("magpie provider table requires OpenAI auth, hiding the actor header:\n%s", cfg)
}
if c := cx.Check(); c != "" {
t.Fatal(c)
}
}