28780500394acc2ff7cbe171b9a97e314e5d4514

Author
TheEdgeOfRage <git@theedgeofrage.com>
Committer
TheEdgeOfRage <git@theedgeofrage.com>
Date

Message

Add support for setting reasoning level on OpenAI backend

Diff

  1diff --git a/README.md b/README.md
  2index 191e2394e4d44c4220ea6d2d5aa88a8299660edf..3184a8c26fd2a27daf4ee187960acf690196a22c 100644
  3--- a/README.md
  4+++ b/README.md
  5@@ -40,11 +40,14 @@ The default reviewer is OpenAI with model `gpt-5.4-nano`. `OPENAI_SMALL_FAST_MOD
  6 {
  7 	"reviewer": {
  8 		"kind": "openai",
  9-		"model": "gpt-5.4-nano"
 10+		"model": "gpt-5.6-luna",
 11+		"reasoningEffort": "low"
 12 	}
 13 }
 14 ```
 15 
 16+OpenAI reviewers accept `reasoningEffort`: `none`, `low`, `medium`, `high`, `xhigh`, or `max`. Support depends on the selected model.
 17+
 18 Use local llama.cpp:
 19 
 20 ```json
 21diff --git a/src/core/config.ts b/src/core/config.ts
 22index 11df1dd0c872430571e9ab6a3a6f586500e0ef54..b7437a880941e5048add7bd7fc402850f518de99 100644
 23--- a/src/core/config.ts
 24+++ b/src/core/config.ts
 25@@ -1,4 +1,4 @@
 26-import type { LlamaReasoningFormat, ReviewerConfig } from "./types"
 27+import type { LlamaReasoningFormat, OpenAIReasoningEffort, ReviewerConfig } from "./types"
 28 
 29 const DEFAULT_OPENAI_MODEL = "gpt-5.4-nano"
 30 const DEFAULT_LLAMA_BASE_URL = "http://127.0.0.1:9931"
 31@@ -29,6 +29,14 @@ function llamaReasoningFormat(value: unknown): LlamaReasoningFormat {
 32   throw new Error('reviewer.reasoningFormat must be "none", "deepseek", or "deepseek-legacy"')
 33 }
 34 
 35+function openAIReasoningEffort(value: unknown): OpenAIReasoningEffort | undefined {
 36+  if (value === undefined) return undefined
 37+  if (value === "none" || value === "low" || value === "medium" || value === "high" || value === "xhigh" || value === "max") {
 38+    return value
 39+  }
 40+  throw new Error('reviewer.reasoningEffort must be "none", "low", "medium", "high", "xhigh", or "max"')
 41+}
 42+
 43 function booleanValue(value: unknown, field: string, fallback: boolean): boolean {
 44   if (value === undefined) return fallback
 45   if (typeof value === "boolean") return value
 46@@ -39,6 +47,7 @@ function openAIConfig(value: Record<string, unknown>): ReviewerConfig {
 47   return {
 48     kind: "openai",
 49     model: stringValue(value.model) ?? process.env.OPENAI_SMALL_FAST_MODEL ?? DEFAULT_OPENAI_MODEL,
 50+    reasoningEffort: openAIReasoningEffort(value.reasoningEffort),
 51   }
 52 }
 53 
 54diff --git a/src/core/providers.ts b/src/core/providers.ts
 55index 63628af8619201321eb93ee45d6329ee8415746b..aecf623ff47a23d50dce4099355aaadbd37b7a76 100644
 56--- a/src/core/providers.ts
 57+++ b/src/core/providers.ts
 58@@ -42,6 +42,7 @@ async function callOpenAI(
 59   messages: ChatMessage[],
 60   maxTokens: number,
 61   apiKey?: string,
 62+  reasoningEffort?: "none" | "low" | "medium" | "high" | "xhigh" | "max",
 63 ): Promise<string> {
 64   const response = await fetch(completionUrl(baseUrl), {
 65     method: "POST",
 66@@ -54,6 +55,7 @@ async function callOpenAI(
 67       max_completion_tokens: maxTokens,
 68       messages,
 69       temperature: 0,
 70+      ...(reasoningEffort && { reasoning_effort: reasoningEffort }),
 71     }),
 72   })
 73   return responseText(response, "OpenAI")
 74@@ -108,5 +110,12 @@ export async function callReviewer(
 75     const apiKey = config.apiKeyEnv ? process.env[config.apiKeyEnv]?.trim() : undefined
 76     return callOpenAI(config.baseUrl, config.model, messages, maxTokens, apiKey)
 77   }
 78-  return callOpenAI("https://api.openai.com/v1", config.model, messages, maxTokens, resolveApiKey(sessionId))
 79+  return callOpenAI(
 80+    "https://api.openai.com/v1",
 81+    config.model,
 82+    messages,
 83+    maxTokens,
 84+    resolveApiKey(sessionId),
 85+    config.reasoningEffort,
 86+  )
 87 }
 88diff --git a/src/core/types.ts b/src/core/types.ts
 89index 5d84d31b0ef779a8c584a61fee2e878fccae14c2..7acf2fe6c34f03073af6228d2e740b22c8548965 100644
 90--- a/src/core/types.ts
 91+++ b/src/core/types.ts
 92@@ -64,9 +64,10 @@ export type PolicyReviewer = {
 93 }
 94 
 95 export type LlamaReasoningFormat = "none" | "deepseek" | "deepseek-legacy"
 96+export type OpenAIReasoningEffort = "none" | "low" | "medium" | "high" | "xhigh" | "max"
 97 
 98 export type ReviewerConfig =
 99-  | { kind: "openai"; model: string }
100+  | { kind: "openai"; model: string; reasoningEffort?: OpenAIReasoningEffort }
101   | {
102     kind: "llama.cpp"
103     baseUrl: string