mirror of
https://github.com/openai/codex.git
synced 2026-05-16 17:23:57 +00:00
Add high-level Python SDK approval mode
Expose approval_mode with deny_all and auto_review options on the high-level Python SDK, and map those choices to generated app-server approval params internally. Update examples, docs, notebooks, and public API tests to use the new mode instead of raw generated approval fields. Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
@@ -18,11 +18,11 @@ ensure_local_sdk_src()
|
||||
import asyncio
|
||||
|
||||
from openai_codex import (
|
||||
ApprovalMode,
|
||||
AsyncCodex,
|
||||
TextInput,
|
||||
)
|
||||
from openai_codex.types import (
|
||||
AskForApproval,
|
||||
Personality,
|
||||
ReasoningSummary,
|
||||
)
|
||||
@@ -46,16 +46,18 @@ PROMPT = (
|
||||
"Analyze a safe rollout plan for enabling a feature flag in production. "
|
||||
"Return JSON matching the requested schema."
|
||||
)
|
||||
APPROVAL_POLICY = AskForApproval.never
|
||||
APPROVAL_MODE = ApprovalMode.auto_review
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
async with AsyncCodex(config=runtime_config()) as codex:
|
||||
thread = await codex.thread_start(model="gpt-5.4", config={"model_reasoning_effort": "high"})
|
||||
thread = await codex.thread_start(
|
||||
model="gpt-5.4", config={"model_reasoning_effort": "high"}
|
||||
)
|
||||
|
||||
turn = await thread.turn(
|
||||
TextInput(PROMPT),
|
||||
approval_policy=APPROVAL_POLICY,
|
||||
approval_mode=APPROVAL_MODE,
|
||||
output_schema=OUTPUT_SCHEMA,
|
||||
personality=Personality.pragmatic,
|
||||
summary=SUMMARY,
|
||||
@@ -67,12 +69,16 @@ async def main() -> None:
|
||||
try:
|
||||
structured = json.loads(structured_text)
|
||||
except json.JSONDecodeError as exc:
|
||||
raise RuntimeError(f"Expected JSON matching OUTPUT_SCHEMA, got: {structured_text!r}") from exc
|
||||
raise RuntimeError(
|
||||
f"Expected JSON matching OUTPUT_SCHEMA, got: {structured_text!r}"
|
||||
) from exc
|
||||
|
||||
summary = structured.get("summary")
|
||||
actions = structured.get("actions")
|
||||
if not isinstance(summary, str) or not isinstance(actions, list) or not all(
|
||||
isinstance(action, str) for action in actions
|
||||
if (
|
||||
not isinstance(summary, str)
|
||||
or not isinstance(actions, list)
|
||||
or not all(isinstance(action, str) for action in actions)
|
||||
):
|
||||
raise RuntimeError(
|
||||
f"Expected structured output with string summary/actions, got: {structured!r}"
|
||||
@@ -83,7 +89,9 @@ async def main() -> None:
|
||||
print("actions:")
|
||||
for action in actions:
|
||||
print("-", action)
|
||||
print("Items:", 0 if persisted_turn is None else len(persisted_turn.items or []))
|
||||
print(
|
||||
"Items:", 0 if persisted_turn is None else len(persisted_turn.items or [])
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -16,11 +16,11 @@ from _bootstrap import (
|
||||
ensure_local_sdk_src()
|
||||
|
||||
from openai_codex import (
|
||||
ApprovalMode,
|
||||
Codex,
|
||||
TextInput,
|
||||
)
|
||||
from openai_codex.types import (
|
||||
AskForApproval,
|
||||
Personality,
|
||||
ReasoningSummary,
|
||||
)
|
||||
@@ -44,14 +44,16 @@ PROMPT = (
|
||||
"Analyze a safe rollout plan for enabling a feature flag in production. "
|
||||
"Return JSON matching the requested schema."
|
||||
)
|
||||
APPROVAL_POLICY = AskForApproval.never
|
||||
APPROVAL_MODE = ApprovalMode.auto_review
|
||||
|
||||
with Codex(config=runtime_config()) as codex:
|
||||
thread = codex.thread_start(model="gpt-5.4", config={"model_reasoning_effort": "high"})
|
||||
thread = codex.thread_start(
|
||||
model="gpt-5.4", config={"model_reasoning_effort": "high"}
|
||||
)
|
||||
|
||||
turn = thread.turn(
|
||||
TextInput(PROMPT),
|
||||
approval_policy=APPROVAL_POLICY,
|
||||
approval_mode=APPROVAL_MODE,
|
||||
output_schema=OUTPUT_SCHEMA,
|
||||
personality=Personality.pragmatic,
|
||||
summary=SUMMARY,
|
||||
@@ -63,14 +65,20 @@ with Codex(config=runtime_config()) as codex:
|
||||
try:
|
||||
structured = json.loads(structured_text)
|
||||
except json.JSONDecodeError as exc:
|
||||
raise RuntimeError(f"Expected JSON matching OUTPUT_SCHEMA, got: {structured_text!r}") from exc
|
||||
raise RuntimeError(
|
||||
f"Expected JSON matching OUTPUT_SCHEMA, got: {structured_text!r}"
|
||||
) from exc
|
||||
|
||||
summary = structured.get("summary")
|
||||
actions = structured.get("actions")
|
||||
if not isinstance(summary, str) or not isinstance(actions, list) or not all(
|
||||
isinstance(action, str) for action in actions
|
||||
if (
|
||||
not isinstance(summary, str)
|
||||
or not isinstance(actions, list)
|
||||
or not all(isinstance(action, str) for action in actions)
|
||||
):
|
||||
raise RuntimeError(f"Expected structured output with string summary/actions, got: {structured!r}")
|
||||
raise RuntimeError(
|
||||
f"Expected structured output with string summary/actions, got: {structured!r}"
|
||||
)
|
||||
|
||||
print("Status:", result.status)
|
||||
print("summary:", summary)
|
||||
|
||||
@@ -5,18 +5,23 @@ _EXAMPLES_ROOT = Path(__file__).resolve().parents[1]
|
||||
if str(_EXAMPLES_ROOT) not in sys.path:
|
||||
sys.path.insert(0, str(_EXAMPLES_ROOT))
|
||||
|
||||
from _bootstrap import assistant_text_from_turn, ensure_local_sdk_src, find_turn_by_id, runtime_config
|
||||
from _bootstrap import (
|
||||
assistant_text_from_turn,
|
||||
ensure_local_sdk_src,
|
||||
find_turn_by_id,
|
||||
runtime_config,
|
||||
)
|
||||
|
||||
ensure_local_sdk_src()
|
||||
|
||||
import asyncio
|
||||
|
||||
from openai_codex import (
|
||||
ApprovalMode,
|
||||
AsyncCodex,
|
||||
TextInput,
|
||||
)
|
||||
from openai_codex.types import (
|
||||
AskForApproval,
|
||||
Personality,
|
||||
ReasoningEffort,
|
||||
ReasoningSummary,
|
||||
@@ -36,11 +41,16 @@ PREFERRED_MODEL = "gpt-5.4"
|
||||
|
||||
def _pick_highest_model(models):
|
||||
visible = [m for m in models if not m.hidden] or models
|
||||
preferred = next((m for m in visible if m.model == PREFERRED_MODEL or m.id == PREFERRED_MODEL), None)
|
||||
preferred = next(
|
||||
(m for m in visible if m.model == PREFERRED_MODEL or m.id == PREFERRED_MODEL),
|
||||
None,
|
||||
)
|
||||
if preferred is not None:
|
||||
return preferred
|
||||
known_names = {m.id for m in visible} | {m.model for m in visible}
|
||||
top_candidates = [m for m in visible if not (m.upgrade and m.upgrade in known_names)]
|
||||
top_candidates = [
|
||||
m for m in visible if not (m.upgrade and m.upgrade in known_names)
|
||||
]
|
||||
pool = top_candidates or visible
|
||||
return max(pool, key=lambda m: (m.model, m.id))
|
||||
|
||||
@@ -75,7 +85,7 @@ SANDBOX_POLICY = SandboxPolicy.model_validate(
|
||||
"access": {"type": "fullAccess"},
|
||||
}
|
||||
)
|
||||
APPROVAL_POLICY = AskForApproval.never
|
||||
APPROVAL_MODE = ApprovalMode.auto_review
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
@@ -102,11 +112,16 @@ async def main() -> None:
|
||||
first_persisted_turn = find_turn_by_id(persisted.thread.turns, first.id)
|
||||
|
||||
print("agent.message:", assistant_text_from_turn(first_persisted_turn))
|
||||
print("items:", 0 if first_persisted_turn is None else len(first_persisted_turn.items or []))
|
||||
print(
|
||||
"items:",
|
||||
0
|
||||
if first_persisted_turn is None
|
||||
else len(first_persisted_turn.items or []),
|
||||
)
|
||||
|
||||
second_turn = await thread.turn(
|
||||
TextInput("Return JSON for a safe feature-flag rollout plan."),
|
||||
approval_policy=APPROVAL_POLICY,
|
||||
approval_mode=APPROVAL_MODE,
|
||||
cwd=str(Path.cwd()),
|
||||
effort=selected_effort,
|
||||
model=selected_model.model,
|
||||
@@ -120,7 +135,12 @@ async def main() -> None:
|
||||
second_persisted_turn = find_turn_by_id(persisted.thread.turns, second.id)
|
||||
|
||||
print("agent.message.params:", assistant_text_from_turn(second_persisted_turn))
|
||||
print("items.params:", 0 if second_persisted_turn is None else len(second_persisted_turn.items or []))
|
||||
print(
|
||||
"items.params:",
|
||||
0
|
||||
if second_persisted_turn is None
|
||||
else len(second_persisted_turn.items or []),
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -5,16 +5,21 @@ _EXAMPLES_ROOT = Path(__file__).resolve().parents[1]
|
||||
if str(_EXAMPLES_ROOT) not in sys.path:
|
||||
sys.path.insert(0, str(_EXAMPLES_ROOT))
|
||||
|
||||
from _bootstrap import assistant_text_from_turn, ensure_local_sdk_src, find_turn_by_id, runtime_config
|
||||
from _bootstrap import (
|
||||
assistant_text_from_turn,
|
||||
ensure_local_sdk_src,
|
||||
find_turn_by_id,
|
||||
runtime_config,
|
||||
)
|
||||
|
||||
ensure_local_sdk_src()
|
||||
|
||||
from openai_codex import (
|
||||
ApprovalMode,
|
||||
Codex,
|
||||
TextInput,
|
||||
)
|
||||
from openai_codex.types import (
|
||||
AskForApproval,
|
||||
Personality,
|
||||
ReasoningEffort,
|
||||
ReasoningSummary,
|
||||
@@ -34,11 +39,16 @@ PREFERRED_MODEL = "gpt-5.4"
|
||||
|
||||
def _pick_highest_model(models):
|
||||
visible = [m for m in models if not m.hidden] or models
|
||||
preferred = next((m for m in visible if m.model == PREFERRED_MODEL or m.id == PREFERRED_MODEL), None)
|
||||
preferred = next(
|
||||
(m for m in visible if m.model == PREFERRED_MODEL or m.id == PREFERRED_MODEL),
|
||||
None,
|
||||
)
|
||||
if preferred is not None:
|
||||
return preferred
|
||||
known_names = {m.id for m in visible} | {m.model for m in visible}
|
||||
top_candidates = [m for m in visible if not (m.upgrade and m.upgrade in known_names)]
|
||||
top_candidates = [
|
||||
m for m in visible if not (m.upgrade and m.upgrade in known_names)
|
||||
]
|
||||
pool = top_candidates or visible
|
||||
return max(pool, key=lambda m: (m.model, m.id))
|
||||
|
||||
@@ -73,7 +83,7 @@ SANDBOX_POLICY = SandboxPolicy.model_validate(
|
||||
"access": {"type": "fullAccess"},
|
||||
}
|
||||
)
|
||||
APPROVAL_POLICY = AskForApproval.never
|
||||
APPROVAL_MODE = ApprovalMode.auto_review
|
||||
|
||||
|
||||
with Codex(config=runtime_config()) as codex:
|
||||
@@ -102,7 +112,7 @@ with Codex(config=runtime_config()) as codex:
|
||||
|
||||
second = thread.turn(
|
||||
TextInput("Return JSON for a safe feature-flag rollout plan."),
|
||||
approval_policy=APPROVAL_POLICY,
|
||||
approval_mode=APPROVAL_MODE,
|
||||
cwd=str(Path.cwd()),
|
||||
effort=selected_effort,
|
||||
model=selected_model.model,
|
||||
|
||||
Reference in New Issue
Block a user