@@ -9,6 +9,7 @@ vitest.mock("@roo-code/telemetry", () => ({
99} ) )
1010
1111import { Anthropic } from "@anthropic-ai/sdk"
12+ import { OPEN_AI_CODEX_SERVICE_TIER_KEY , OpenAiCodexServiceTier , SERVICE_TIER_KEY } from "@roo-code/types"
1213import { OpenAiCodexHandler , transformLunaResponsesLiteBody } from "../openai-codex"
1314import { openAiCodexOAuthManager } from "../../../integrations/openai-codex/oauth"
1415
@@ -76,6 +77,84 @@ describe("OpenAiCodexHandler.getModel", () => {
7677} )
7778
7879describe ( "OpenAiCodexHandler.createMessage" , ( ) => {
80+ afterEach ( ( ) => {
81+ vitest . restoreAllMocks ( )
82+ vitest . unstubAllGlobals ( )
83+ } )
84+
85+ it ( "sends the priority service tier in streaming SDK requests when Fast is selected" , async ( ) => {
86+ const handler = new OpenAiCodexHandler ( {
87+ apiModelId : "gpt-5.6-sol" ,
88+ [ OPEN_AI_CODEX_SERVICE_TIER_KEY ] : OpenAiCodexServiceTier . Priority ,
89+ } )
90+ vitest . spyOn ( openAiCodexOAuthManager , "getAccessToken" ) . mockResolvedValue ( "test-token" )
91+ vitest . spyOn ( openAiCodexOAuthManager , "getAccountId" ) . mockResolvedValue ( "acct_test" )
92+ const mockCreate = vitest . fn ( ) . mockResolvedValue ( createCompletedStream ( ) )
93+ Reflect . set ( handler , "client" , { responses : { create : mockCreate } } )
94+
95+ await drainStream ( handler . createMessage ( "System prompt" , [ ] ) )
96+
97+ const [ body ] = mockCreate . mock . calls [ 0 ]
98+ expect ( body ) . toMatchObject ( {
99+ stream : true ,
100+ [ SERVICE_TIER_KEY ] : OpenAiCodexServiceTier . Priority ,
101+ } )
102+ } )
103+
104+ it . each ( [
105+ [ "an absent preference" , { } ] ,
106+ [
107+ "an explicit Standard preference from an older profile" ,
108+ { [ OPEN_AI_CODEX_SERVICE_TIER_KEY ] : OpenAiCodexServiceTier . Default } ,
109+ ] ,
110+ ] ) ( "omits the service tier in streaming SDK requests for %s" , async ( _description , serviceTierOptions ) => {
111+ const handler = new OpenAiCodexHandler ( {
112+ apiModelId : "gpt-5.6-sol" ,
113+ ...serviceTierOptions ,
114+ } as ConstructorParameters < typeof OpenAiCodexHandler > [ 0 ] )
115+ vitest . spyOn ( openAiCodexOAuthManager , "getAccessToken" ) . mockResolvedValue ( "test-token" )
116+ vitest . spyOn ( openAiCodexOAuthManager , "getAccountId" ) . mockResolvedValue ( "acct_test" )
117+ const mockCreate = vitest . fn ( ) . mockResolvedValue ( createCompletedStream ( ) )
118+ Reflect . set ( handler , "client" , { responses : { create : mockCreate } } )
119+
120+ await drainStream ( handler . createMessage ( "System prompt" , [ ] ) )
121+
122+ expect ( mockCreate . mock . calls [ 0 ] [ 0 ] ) . not . toHaveProperty ( SERVICE_TIER_KEY )
123+ } )
124+
125+ it ( "preserves the priority service tier in the manual streaming fallback" , async ( ) => {
126+ const handler = new OpenAiCodexHandler ( {
127+ apiModelId : "gpt-5.6-sol" ,
128+ [ OPEN_AI_CODEX_SERVICE_TIER_KEY ] : OpenAiCodexServiceTier . Priority ,
129+ } )
130+ vitest . spyOn ( openAiCodexOAuthManager , "getAccessToken" ) . mockResolvedValue ( "test-token" )
131+ vitest . spyOn ( openAiCodexOAuthManager , "getAccountId" ) . mockResolvedValue ( "acct_test" )
132+ Reflect . set ( handler , "client" , {
133+ responses : { create : vitest . fn ( ) . mockRejectedValue ( new Error ( "SDK unavailable" ) ) } ,
134+ } )
135+ const mockFetch = vitest . fn ( ) . mockResolvedValue ( {
136+ ok : true ,
137+ body : new ReadableStream ( {
138+ start ( controller ) {
139+ controller . enqueue (
140+ new TextEncoder ( ) . encode (
141+ 'data: {"type":"response.completed","response":{"output":[],"usage":{"input_tokens":1,"output_tokens":1}}}\n\n' ,
142+ ) ,
143+ )
144+ controller . close ( )
145+ } ,
146+ } ) ,
147+ } )
148+ vitest . stubGlobal ( "fetch" , mockFetch )
149+
150+ await drainStream ( handler . createMessage ( "System prompt" , [ ] ) )
151+
152+ expect ( JSON . parse ( mockFetch . mock . calls [ 0 ] [ 1 ] . body ) ) . toMatchObject ( {
153+ stream : true ,
154+ [ SERVICE_TIER_KEY ] : OpenAiCodexServiceTier . Priority ,
155+ } )
156+ } )
157+
79158 it ( "should skip URL-sourced images in formatFullConversation (only base64 emits input_image)" , async ( ) => {
80159 const handler = new OpenAiCodexHandler ( { apiModelId : "gpt-5.1-codex" } )
81160
@@ -176,6 +255,40 @@ describe("OpenAiCodexHandler.createMessage", () => {
176255 } )
177256} )
178257
258+ describe ( "OpenAiCodexHandler.completePrompt service tier" , ( ) => {
259+ afterEach ( ( ) => {
260+ vitest . restoreAllMocks ( )
261+ vitest . unstubAllGlobals ( )
262+ } )
263+
264+ it . each < [ string , OpenAiCodexServiceTier . Priority | undefined , OpenAiCodexServiceTier . Priority | undefined ] > ( [
265+ [ "Fast" , OpenAiCodexServiceTier . Priority , OpenAiCodexServiceTier . Priority ] ,
266+ [ "Standard" , undefined , undefined ] ,
267+ ] ) ( "uses the %s preference in non-streaming requests" , async ( _mode , configuredTier , expectedTier ) => {
268+ const handler = new OpenAiCodexHandler ( {
269+ apiModelId : "gpt-5.6-sol" ,
270+ ...( configuredTier ? { [ OPEN_AI_CODEX_SERVICE_TIER_KEY ] : configuredTier } : { } ) ,
271+ } )
272+ vitest . spyOn ( openAiCodexOAuthManager , "getAccessToken" ) . mockResolvedValue ( "test-token" )
273+ vitest . spyOn ( openAiCodexOAuthManager , "getAccountId" ) . mockResolvedValue ( "acct_test" )
274+ const mockFetch = vitest . fn ( ) . mockResolvedValue ( {
275+ ok : true ,
276+ json : vitest . fn ( ) . mockResolvedValue ( { text : "Complete" } ) ,
277+ } )
278+ vitest . stubGlobal ( "fetch" , mockFetch )
279+
280+ await expect ( handler . completePrompt ( "Hello" ) ) . resolves . toBe ( "Complete" )
281+
282+ const body = JSON . parse ( mockFetch . mock . calls [ 0 ] [ 1 ] . body )
283+ expect ( body . stream ) . toBe ( false )
284+ if ( expectedTier ) {
285+ expect ( body [ SERVICE_TIER_KEY ] ) . toBe ( expectedTier )
286+ } else {
287+ expect ( body ) . not . toHaveProperty ( SERVICE_TIER_KEY )
288+ }
289+ } )
290+ } )
291+
179292describe ( "transformLunaResponsesLiteBody" , ( ) => {
180293 it ( "creates the exact Responses Lite body while preserving unrelated fields and reasoning" , ( ) => {
181294 const tools = [ { type : "function" , name : "read_file" , parameters : { type : "object" } } ]
0 commit comments