Skip to content
5 changes: 3 additions & 2 deletions .github/workflows/codex_audit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,9 @@ on:
description: "Audit provider"
required: false
type: choice
default: "auto"
default: "task_default"
options:
- task_default
- auto
- api
- anthropic
Expand Down Expand Up @@ -68,7 +69,7 @@ jobs:
ISSUE_NUMBER: ${{ github.event.client_payload.issue_number || inputs.issue_number }}
SOURCE_REF: ${{ github.event.client_payload.source_ref || inputs.source_ref || 'main' }}
CODEX_AUDIT_MODE: ${{ github.event.client_payload.mode || inputs.mode || 'review_and_fix' }}
CODEX_AUDIT_PROVIDER: ${{ github.event.client_payload.provider || inputs.provider || 'auto' }}
CODEX_AUDIT_PROVIDER: ${{ github.event.client_payload.provider || inputs.provider || 'task_default' }}
CODEX_AUDIT_CODEX_BACKEND: service
CODEX_AUDIT_TASK: ${{ github.event.client_payload.task || inputs.task || 'monthly_snapshot_audit' }}
CODEX_AUDIT_AUTO_MERGE: ${{ github.event.client_payload.auto_merge || inputs.auto_merge || 'false' }}
Expand Down
12 changes: 12 additions & 0 deletions .github/workflows/codex_pr_review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,16 @@ on:
required: false
type: boolean
default: false
api_fallback_enabled:
description: "Optional true/false override for direct API fallback after a recoverable Codex service failure. Empty defers to repository variables."
required: false
type: string
default: ""
direct_api_primary_enabled:
description: "Optional true/false override for API-only PR review when CODEX_AUDIT_SERVICE_URL is not configured. Empty defers to repository variables."
required: false
type: string
default: ""
secrets:
CODEX_AUDIT_REUSABLE_WORKFLOW_TOKEN:
description: "Token that can read QuantStrategyLab/AIAuditBridge when this workflow is called from another private repo."
Expand Down Expand Up @@ -81,6 +91,8 @@ jobs:
CODEX_AUDIT_SERVICE_AUDIENCE: ${{ vars.CODEX_AUDIT_SERVICE_AUDIENCE || 'quant-codex-audit' }}
CODEX_PR_REVIEW_REPO_ROOT: ${{ github.workspace }}/source
CODEX_PR_REVIEW_ALLOW_UNCONFIGURED_BACKEND: ${{ inputs.allow_unconfigured_backend || 'false' }}
CODEX_PR_REVIEW_API_FALLBACK_ENABLED: ${{ github.event_name == 'workflow_call' && inputs.api_fallback_enabled != '' && inputs.api_fallback_enabled || vars.CODEX_PR_REVIEW_API_FALLBACK_ENABLED || 'true' }}
CODEX_PR_REVIEW_DIRECT_API_PRIMARY_ENABLED: ${{ github.event_name == 'workflow_call' && inputs.direct_api_primary_enabled != '' && inputs.direct_api_primary_enabled || vars.CODEX_PR_REVIEW_DIRECT_API_PRIMARY_ENABLED || 'true' }}
working-directory: source
run: |
set -euo pipefail
Expand Down
17 changes: 15 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,23 @@ Configure these values in `QuantStrategyLab/AIAuditBridge`:
`openai,anthropic`.
- Repository variable `OPENAI_MODEL` for OpenAI API fallback.
- Repository variable `ANTHROPIC_MODEL` for Anthropic API fallback.
- Monthly audits default to `provider=auto` for `monthly_snapshot_audit` and
`provider=codex` for `long_horizon_signal_shadow`; override with
`CODEX_AUDIT_PROVIDER` when you need a specific provider. Workflow dispatch
uses `task_default` to defer provider selection to the task policy.
- Monthly audits with `CODEX_AUDIT_PROVIDER=auto` fall back to the configured
API reviewers when the Codex service hits quota/capacity failures.
- PR review workflows fall back to direct API review on recoverable Codex
service failures.
- PR review workflows can fall back to direct API review on recoverable Codex
service failures through `CODEX_PR_REVIEW_API_FALLBACK_ENABLED=true` or the
reusable workflow input `api_fallback_enabled`. The reusable workflow input
accepts string values `true`/`false`; when omitted it defers to repository
variables and then defaults to `true` for compatibility. Codex-only callers
should pass `api_fallback_enabled: "false"`. API-only PR review when no
service URL is configured is controlled separately by
`CODEX_PR_REVIEW_DIRECT_API_PRIMARY_ENABLED` or reusable workflow input
`direct_api_primary_enabled`; this uses the same `true`/`false`, variable,
and compatibility default rules and should be set to `"false"` for Codex-only
callers.
- Repository variable `CODEX_AUDIT_SERVICE_MODEL` for the VPS Codex service primary
path; `VPS Codex Service Ops` deploy writes it into the systemd unit.
- Optional repository variable `CODEX_AUDIT_SERVICE_REASONING_EFFORT` for a
Expand Down
13 changes: 13 additions & 0 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,21 @@ AIAuditBridge 只使用 service backend。workflow 运行在 `ubuntu-latest`,
`openai,anthropic`。
- repository variable `OPENAI_MODEL`,API fallback 使用的 OpenAI 模型。
- repository variable `ANTHROPIC_MODEL`,API fallback 使用的 Anthropic 模型。
- monthly audit 的默认 provider 随 task 而变:`monthly_snapshot_audit`
默认 `auto`,`long_horizon_signal_shadow` 默认 `codex`;如需固定 provider,
请显式设置 `CODEX_AUDIT_PROVIDER`。workflow dispatch 使用 `task_default`
把 provider 选择交给 task policy。
- repository variable `CODEX_AUDIT_SERVICE_MODEL`,VPS Codex service 主路径模型;
`VPS Codex Service Ops` deploy 会写入 systemd unit。
- PR review 可以通过 repository variable
`CODEX_PR_REVIEW_API_FALLBACK_ENABLED=true` 或 reusable workflow input
`api_fallback_enabled` 在可恢复的 Codex service 失败后启用 direct API
fallback。reusable workflow input 使用字符串 `true`/`false`;省略时先使用
repository variable,再为兼容旧调用方默认 `true`。只走 Codex 的调用方应传入
`api_fallback_enabled: "false"`。当未配置 service URL 时,是否允许 API-only
PR review 由 `CODEX_PR_REVIEW_DIRECT_API_PRIMARY_ENABLED` 或 reusable
workflow input `direct_api_primary_enabled` 单独控制;该项使用同样的变量和
兼容默认规则,Codex-only 调用方应设为 `"false"`。
- workflow 已配置 `id-token: write`,用于向 service 提供 GitHub Actions OIDC token。

service host 启动示例:
Expand Down
19 changes: 15 additions & 4 deletions scripts/run_codex_pr_review.py
Original file line number Diff line number Diff line change
Expand Up @@ -492,15 +492,21 @@ def _allow_unconfigured_backend() -> bool:
return parse_bool(env_value("CODEX_PR_REVIEW_ALLOW_UNCONFIGURED_BACKEND"))


def _api_fallback_enabled() -> bool:
return parse_bool(env_value("CODEX_PR_REVIEW_API_FALLBACK_ENABLED", "true"))


def _direct_api_primary_enabled() -> bool:
return parse_bool(env_value("CODEX_PR_REVIEW_DIRECT_API_PRIMARY_ENABLED", "true"))


def run_codex_review_with_fallback(
prompt: str,
timeout_minutes: int,
complexity: str = "",
changed_file_count: int = 0,
changed_line_count: int = 0,
) -> str:
# env_value() returns "" when CODEX_AUDIT_SERVICE_URL is unset, so this
# guard keeps the direct-API path intact without special error handling.
service_url = env_value("CODEX_AUDIT_SERVICE_URL")
service_failure: Exception | None = None
if service_url:
Expand All @@ -517,10 +523,15 @@ def run_codex_review_with_fallback(
if not _service_review_should_fallback(exc):
raise
service_failure = exc
print(f"::warning::Codex service review failed; falling back to direct API: {exc}")
print(f"::warning::Codex service review failed: {exc}")
except (json.JSONDecodeError, OSError, urllib.error.URLError) as exc:
service_failure = exc
print(f"::error::Codex service review failed; falling back to direct API: {exc}")
print(f"::error::Codex service review failed: {exc}")

if service_failure is not None and not _api_fallback_enabled():
raise ReviewError(f"Codex service review failed and direct API fallback is disabled: {service_failure}")
if not service_url and not _direct_api_primary_enabled():
raise ReviewError(NO_REVIEW_BACKEND_CONFIGURED)

print("Running Codex review via direct API")
try:
Expand Down
17 changes: 14 additions & 3 deletions scripts/run_monthly_codex_audit.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@
DEFAULT_TASK = "monthly_snapshot_audit"
DEFAULT_MODE = "review_and_fix"
DEFAULT_PROVIDER = "auto"
TASK_DEFAULT_PROVIDER = "task_default"
TASK_DEFAULT_PROVIDERS = {
"monthly_snapshot_audit": "auto",
"long_horizon_signal_shadow": "codex",
}
API_PATCH_SYSTEM_PROMPT = (
"You are AIAuditBridge's API fallback patch provider. "
"Return exactly one JSON object that matches the service patch contract. "
Expand Down Expand Up @@ -384,13 +389,19 @@ def validate_task(task: str, source_repo: str) -> str:
return normalized


def validate_provider(provider: str) -> str:
normalized = (provider or DEFAULT_PROVIDER).strip().lower()
def validate_provider(provider: str, task: str = DEFAULT_TASK) -> str:
normalized = (provider or TASK_DEFAULT_PROVIDER).strip().lower()
if normalized == TASK_DEFAULT_PROVIDER:
return default_provider_for_task(task)
if normalized not in SUPPORTED_PROVIDERS:
raise BridgeError(f"Unsupported CODEX_AUDIT_PROVIDER: {provider!r}")
return normalized


def default_provider_for_task(task: str) -> str:
return TASK_DEFAULT_PROVIDERS.get(task, DEFAULT_PROVIDER)


def api_fallback_allowed_source_repos() -> frozenset[str]:
configured = env_value("CODEX_AUDIT_API_FALLBACK_ALLOWED_SOURCE_REPOSITORIES")
if not configured:
Expand Down Expand Up @@ -2772,7 +2783,7 @@ def main() -> int:
mode = env_value("CODEX_AUDIT_MODE", DEFAULT_MODE)
if mode not in {"review_only", "review_and_fix"}:
raise BridgeError(f"Unsupported CODEX_AUDIT_MODE: {mode}")
provider = validate_provider(env_value("CODEX_AUDIT_PROVIDER", DEFAULT_PROVIDER))
provider = validate_provider(env_value("CODEX_AUDIT_PROVIDER", TASK_DEFAULT_PROVIDER), task=task)
codex_backend = validate_codex_backend(env_value("CODEX_AUDIT_CODEX_BACKEND", DEFAULT_CODEX_BACKEND))
issue_number_raw = env_value("ISSUE_NUMBER")
if not issue_number_raw.isdigit():
Expand Down
Loading
Loading