11import type { StreamFn } from "@mariozechner/pi-agent-core" ;
22import type { Context , Model , SimpleStreamOptions } from "@mariozechner/pi-ai" ;
3- import { describe , expect , it , vi } from "vitest" ;
3+ import { beforeEach , describe , expect , it , vi } from "vitest" ;
44import { buildOpenAICodexProviderPlugin } from "./openai-codex-provider.js" ;
55import { buildOpenAIProvider } from "./openai-provider.js" ;
66
7- const refreshOpenAICodexTokenMock = vi . hoisted ( ( ) => vi . fn ( ) ) ;
7+ const mocks = vi . hoisted ( ( ) => ( {
8+ refreshOpenAICodexToken : vi . fn ( ) ,
9+ openAIResponsesTransportStreamFn : vi . fn ( ) ,
10+ } ) ) ;
811
912vi . mock ( "./openai-codex-provider.runtime.js" , ( ) => ( {
10- refreshOpenAICodexToken : refreshOpenAICodexTokenMock ,
13+ refreshOpenAICodexToken : mocks . refreshOpenAICodexToken ,
1114} ) ) ;
1215
16+ vi . mock ( "openclaw/plugin-sdk/provider-stream-family" , async ( importOriginal ) => {
17+ const actual =
18+ await importOriginal < typeof import ( "openclaw/plugin-sdk/provider-stream-family" ) > ( ) ;
19+ const wrapStreamFn : NonNullable < typeof actual . OPENAI_RESPONSES_STREAM_HOOKS . wrapStreamFn > = (
20+ ctx ,
21+ ) => {
22+ let nextStreamFn = actual . createOpenAIAttributionHeadersWrapper ( ctx . streamFn , {
23+ codexNativeTransportStreamFn : mocks . openAIResponsesTransportStreamFn ,
24+ } ) ;
25+
26+ if ( actual . resolveOpenAIFastMode ( ctx . extraParams ) ) {
27+ nextStreamFn = actual . createOpenAIFastModeWrapper ( nextStreamFn ) ;
28+ }
29+
30+ const serviceTier = actual . resolveOpenAIServiceTier ( ctx . extraParams ) ;
31+ if ( serviceTier ) {
32+ nextStreamFn = actual . createOpenAIServiceTierWrapper ( nextStreamFn , serviceTier ) ;
33+ }
34+
35+ const textVerbosity = actual . resolveOpenAITextVerbosity ( ctx . extraParams ) ;
36+ if ( textVerbosity ) {
37+ nextStreamFn = actual . createOpenAITextVerbosityWrapper ( nextStreamFn , textVerbosity ) ;
38+ }
39+
40+ nextStreamFn = actual . createCodexNativeWebSearchWrapper ( nextStreamFn , {
41+ config : ctx . config ,
42+ agentDir : ctx . agentDir ,
43+ } ) ;
44+ return actual . createOpenAIResponsesContextManagementWrapper (
45+ actual . createOpenAIReasoningCompatibilityWrapper ( nextStreamFn ) ,
46+ ctx . extraParams ,
47+ ) ;
48+ } ;
49+
50+ return {
51+ ...actual ,
52+ OPENAI_RESPONSES_STREAM_HOOKS : {
53+ ...actual . OPENAI_RESPONSES_STREAM_HOOKS ,
54+ wrapStreamFn,
55+ } ,
56+ } ;
57+ } ) ;
58+
1359function runWrappedPayloadCase ( params : {
1460 wrap : NonNullable < ReturnType < typeof buildOpenAIProvider > [ "wrapStreamFn" ] > ;
1561 provider : string ;
@@ -49,6 +95,13 @@ function runWrappedPayloadCase(params: {
4995}
5096
5197describe ( "buildOpenAIProvider" , ( ) => {
98+ beforeEach ( ( ) => {
99+ mocks . openAIResponsesTransportStreamFn . mockReset ( ) ;
100+ mocks . openAIResponsesTransportStreamFn . mockImplementation ( ( ) => {
101+ throw new Error ( "unexpected native OpenAI Responses transport call" ) ;
102+ } ) ;
103+ } ) ;
104+
52105 it ( "exposes grouped model/auth picker labels for API key setup" , ( ) => {
53106 const provider = buildOpenAIProvider ( ) ;
54107 const apiKey = provider . auth . find ( ( method ) => method . id === "api-key" ) ;
@@ -683,6 +736,15 @@ describe("buildOpenAIProvider", () => {
683736 if ( ! wrap ) {
684737 throw new Error ( "expected Codex wrapper" ) ;
685738 }
739+ const payload = {
740+ store : false ,
741+ text : { verbosity : "medium" } ,
742+ tools : [ { type : "function" , name : "read" } ] ,
743+ } ;
744+ mocks . openAIResponsesTransportStreamFn . mockImplementation ( ( model , _context , options ) => {
745+ options ?. onPayload ?.( payload , model ) ;
746+ return { } as ReturnType < StreamFn > ;
747+ } ) ;
686748 const result = runWrappedPayloadCase ( {
687749 wrap,
688750 provider : "openai-codex" ,
@@ -720,13 +782,10 @@ describe("buildOpenAIProvider", () => {
720782 id : "gpt-5.4" ,
721783 baseUrl : "https://chatgpt.com/backend-api" ,
722784 } as Model < "openai-codex-responses" > ,
723- payload : {
724- store : false ,
725- text : { verbosity : "medium" } ,
726- tools : [ { type : "function" , name : "read" } ] ,
727- } ,
785+ payload,
728786 } ) ;
729787
788+ expect ( mocks . openAIResponsesTransportStreamFn ) . toHaveBeenCalledTimes ( 1 ) ;
730789 expect ( result . payload . store ) . toBe ( false ) ;
731790 expect ( result . payload . service_tier ) . toBe ( "priority" ) ;
732791 expect ( result . payload . text ) . toEqual ( { verbosity : "high" } ) ;
@@ -749,8 +808,8 @@ describe("buildOpenAIProvider", () => {
749808 expires : Date . now ( ) - 60_000 ,
750809 } ;
751810
752- refreshOpenAICodexTokenMock . mockReset ( ) ;
753- refreshOpenAICodexTokenMock . mockRejectedValueOnce (
811+ mocks . refreshOpenAICodexToken . mockReset ( ) ;
812+ mocks . refreshOpenAICodexToken . mockRejectedValueOnce (
754813 new Error ( "Failed to extract accountId from token" ) ,
755814 ) ;
756815
0 commit comments