mirror of
https://github.com/openai/codex.git
synced 2026-10-30 19:18:49 +00:00
Merge 4de61af3fb into sapling-pr-archive-bolinfest
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
if [[ "$#" -lt 1 ]]; then
|
||||
echo "usage: $0 <target> [<target> ...]"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
process_target() {
|
||||
local target="$1"
|
||||
local release_dir="target/${target}/release"
|
||||
local dest="dist/${target}"
|
||||
local repo_root
|
||||
repo_root="$(pwd)"
|
||||
|
||||
ls -lh "${release_dir}/codex.exe"
|
||||
ls -lh "${release_dir}/codex-responses-api-proxy.exe"
|
||||
ls -lh "${release_dir}/codex-windows-sandbox-setup.exe"
|
||||
ls -lh "${release_dir}/codex-command-runner.exe"
|
||||
|
||||
mkdir -p "$dest"
|
||||
cp "${release_dir}/codex.exe" "$dest/codex-${target}.exe"
|
||||
cp "${release_dir}/codex-responses-api-proxy.exe" "$dest/codex-responses-api-proxy-${target}.exe"
|
||||
cp "${release_dir}/codex-windows-sandbox-setup.exe" "$dest/codex-windows-sandbox-setup-${target}.exe"
|
||||
cp "${release_dir}/codex-command-runner.exe" "$dest/codex-command-runner-${target}.exe"
|
||||
|
||||
for f in "$dest"/*; do
|
||||
local base
|
||||
base="$(basename "$f")"
|
||||
|
||||
if [[ "$base" == *.tar.gz || "$base" == *.zip || "$base" == *.dmg ]]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
if [[ "$base" == *.sigstore ]]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
tar -C "$dest" -czf "$dest/${base}.tar.gz" "$base"
|
||||
|
||||
if [[ "$base" == "codex-${target}.exe" ]]; then
|
||||
local bundle_dir
|
||||
local runner_src
|
||||
local setup_src
|
||||
bundle_dir="$(mktemp -d)"
|
||||
runner_src="$dest/codex-command-runner-${target}.exe"
|
||||
setup_src="$dest/codex-windows-sandbox-setup-${target}.exe"
|
||||
|
||||
if [[ -f "$runner_src" && -f "$setup_src" ]]; then
|
||||
cp "$dest/$base" "$bundle_dir/$base"
|
||||
cp "$runner_src" "$bundle_dir/codex-command-runner.exe"
|
||||
cp "$setup_src" "$bundle_dir/codex-windows-sandbox-setup.exe"
|
||||
(cd "$bundle_dir" && 7z a "$repo_root/$dest/${base}.zip" .)
|
||||
else
|
||||
echo "warning: missing sandbox binaries; falling back to single-binary zip"
|
||||
echo "warning: expected $runner_src and $setup_src"
|
||||
(cd "$dest" && 7z a "${base}.zip" "$base")
|
||||
fi
|
||||
|
||||
rm -rf "$bundle_dir"
|
||||
else
|
||||
(cd "$dest" && 7z a "${base}.zip" "$base")
|
||||
fi
|
||||
|
||||
"${GITHUB_WORKSPACE}/.github/workflows/zstd" -T0 -19 "$dest/$base"
|
||||
done
|
||||
}
|
||||
|
||||
for target in "$@"; do
|
||||
process_target "$target"
|
||||
done
|
||||
@@ -121,8 +121,8 @@ jobs:
|
||||
build-windows:
|
||||
needs:
|
||||
- build-windows-binaries
|
||||
name: Build - ${{ matrix.runner }} - ${{ matrix.target }}
|
||||
runs-on: ${{ matrix.runs_on }}
|
||||
name: Build - windows-x64 - ${{ matrix.target }}
|
||||
runs-on: windows-2022
|
||||
timeout-minutes: 60
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -135,16 +135,8 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- runner: windows-x64
|
||||
target: x86_64-pc-windows-msvc
|
||||
runs_on:
|
||||
group: codex-runners
|
||||
labels: codex-windows-x64
|
||||
- runner: windows-arm64
|
||||
target: aarch64-pc-windows-msvc
|
||||
runs_on:
|
||||
group: codex-runners
|
||||
labels: codex-windows-arm64
|
||||
- target: x86_64-pc-windows-msvc
|
||||
- target: aarch64-pc-windows-msvc
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
@@ -161,14 +153,8 @@ jobs:
|
||||
name: windows-binaries-${{ matrix.target }}-helpers
|
||||
path: codex-rs/target/${{ matrix.target }}/release
|
||||
|
||||
- name: Verify binaries
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
ls -lh target/${{ matrix.target }}/release/codex.exe
|
||||
ls -lh target/${{ matrix.target }}/release/codex-responses-api-proxy.exe
|
||||
ls -lh target/${{ matrix.target }}/release/codex-windows-sandbox-setup.exe
|
||||
ls -lh target/${{ matrix.target }}/release/codex-command-runner.exe
|
||||
- name: Install DotSlash
|
||||
uses: facebook/install-dotslash@v2
|
||||
|
||||
- name: Sign Windows binaries with Azure Trusted Signing
|
||||
uses: ./.github/actions/windows-code-sign
|
||||
@@ -181,81 +167,11 @@ jobs:
|
||||
account-name: ${{ secrets.AZURE_TRUSTED_SIGNING_ACCOUNT_NAME }}
|
||||
certificate-profile-name: ${{ secrets.AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE_NAME }}
|
||||
|
||||
- name: Stage artifacts
|
||||
- name: Stage and compress Windows release artifacts
|
||||
shell: bash
|
||||
run: |
|
||||
dest="dist/${{ matrix.target }}"
|
||||
mkdir -p "$dest"
|
||||
|
||||
cp target/${{ matrix.target }}/release/codex.exe "$dest/codex-${{ matrix.target }}.exe"
|
||||
cp target/${{ matrix.target }}/release/codex-responses-api-proxy.exe "$dest/codex-responses-api-proxy-${{ matrix.target }}.exe"
|
||||
cp target/${{ matrix.target }}/release/codex-windows-sandbox-setup.exe "$dest/codex-windows-sandbox-setup-${{ matrix.target }}.exe"
|
||||
cp target/${{ matrix.target }}/release/codex-command-runner.exe "$dest/codex-command-runner-${{ matrix.target }}.exe"
|
||||
|
||||
- name: Install DotSlash
|
||||
uses: facebook/install-dotslash@v2
|
||||
|
||||
- name: Compress artifacts
|
||||
shell: bash
|
||||
run: |
|
||||
# Path that contains the uncompressed binaries for the current
|
||||
# ${{ matrix.target }}
|
||||
dest="dist/${{ matrix.target }}"
|
||||
repo_root=$PWD
|
||||
|
||||
# For compatibility with environments that lack the `zstd` tool we
|
||||
# additionally create a `.tar.gz` and `.zip` for every Windows binary.
|
||||
# The end result is:
|
||||
# codex-<target>.zst
|
||||
# codex-<target>.tar.gz
|
||||
# codex-<target>.zip
|
||||
for f in "$dest"/*; do
|
||||
base="$(basename "$f")"
|
||||
# Skip files that are already archives (shouldn't happen, but be
|
||||
# safe).
|
||||
if [[ "$base" == *.tar.gz || "$base" == *.zip || "$base" == *.dmg ]]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
# Don't try to compress signature bundles.
|
||||
if [[ "$base" == *.sigstore ]]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
# Create per-binary tar.gz
|
||||
tar -C "$dest" -czf "$dest/${base}.tar.gz" "$base"
|
||||
|
||||
# Create zip archive for Windows binaries.
|
||||
# Must run from inside the dest dir so 7z won't embed the
|
||||
# directory path inside the zip.
|
||||
if [[ "$base" == "codex-${{ matrix.target }}.exe" ]]; then
|
||||
# Bundle the sandbox helper binaries into the main codex zip so
|
||||
# WinGet installs include the required helpers next to codex.exe.
|
||||
# Fall back to the single-binary zip if the helpers are missing
|
||||
# to avoid breaking releases.
|
||||
bundle_dir="$(mktemp -d)"
|
||||
runner_src="$dest/codex-command-runner-${{ matrix.target }}.exe"
|
||||
setup_src="$dest/codex-windows-sandbox-setup-${{ matrix.target }}.exe"
|
||||
if [[ -f "$runner_src" && -f "$setup_src" ]]; then
|
||||
cp "$dest/$base" "$bundle_dir/$base"
|
||||
cp "$runner_src" "$bundle_dir/codex-command-runner.exe"
|
||||
cp "$setup_src" "$bundle_dir/codex-windows-sandbox-setup.exe"
|
||||
# Use an absolute path so bundle zips land in the real dist
|
||||
# dir even when 7z runs from a temp directory.
|
||||
(cd "$bundle_dir" && 7z a "$repo_root/$dest/${base}.zip" .)
|
||||
else
|
||||
echo "warning: missing sandbox binaries; falling back to single-binary zip"
|
||||
echo "warning: expected $runner_src and $setup_src"
|
||||
(cd "$dest" && 7z a "${base}.zip" "$base")
|
||||
fi
|
||||
rm -rf "$bundle_dir"
|
||||
else
|
||||
(cd "$dest" && 7z a "${base}.zip" "$base")
|
||||
fi
|
||||
|
||||
# Keep raw executables and produce .zst alongside them.
|
||||
"${GITHUB_WORKSPACE}/.github/workflows/zstd" -T0 -19 "$dest/$base"
|
||||
done
|
||||
bash "$GITHUB_WORKSPACE/.github/scripts/stage-and-compress-windows-release-artifacts.sh" \
|
||||
${{ matrix.target }}
|
||||
|
||||
- uses: actions/upload-artifact@v6
|
||||
with:
|
||||
|
||||
@@ -254,6 +254,17 @@ pub fn process_responses_event(
|
||||
"response.failed event received".into(),
|
||||
)));
|
||||
}
|
||||
"response.incomplete" => {
|
||||
let reason = event.response.as_ref().and_then(|response| {
|
||||
response
|
||||
.get("incomplete_details")
|
||||
.and_then(|details| details.get("reason"))
|
||||
.and_then(Value::as_str)
|
||||
});
|
||||
let reason = reason.unwrap_or("unknown");
|
||||
let message = format!("Incomplete response returned, reason: {reason}");
|
||||
return Err(ResponsesEventError::Api(ApiError::Stream(message)));
|
||||
}
|
||||
"response.completed" => {
|
||||
if let Some(resp_val) = event.response {
|
||||
match serde_json::from_value::<ResponseCompleted>(resp_val) {
|
||||
|
||||
@@ -177,20 +177,15 @@ impl SessionState {
|
||||
}
|
||||
}
|
||||
|
||||
// Merge partial rate-limit updates: new fields overwrite existing values;
|
||||
// missing fields retain prior values. If `limit_id` is absent everywhere,
|
||||
// default it to `"codex"`.
|
||||
// Sometimes new snapshots don't include credits or plan information.
|
||||
// Preserve those from the previous snapshot when missing. For `limit_id`, treat
|
||||
// missing values as the default `"codex"` bucket.
|
||||
fn merge_rate_limit_fields(
|
||||
previous: Option<&RateLimitSnapshot>,
|
||||
mut snapshot: RateLimitSnapshot,
|
||||
) -> RateLimitSnapshot {
|
||||
if snapshot.limit_id.is_none() {
|
||||
snapshot.limit_id = previous
|
||||
.and_then(|prior| prior.limit_id.clone())
|
||||
.or_else(|| Some("codex".to_string()));
|
||||
}
|
||||
if snapshot.limit_name.is_none() {
|
||||
snapshot.limit_name = previous.and_then(|prior| prior.limit_name.clone());
|
||||
snapshot.limit_id = Some("codex".to_string());
|
||||
}
|
||||
if snapshot.credits.is_none() {
|
||||
snapshot.credits = previous.and_then(|prior| prior.credits.clone());
|
||||
@@ -305,7 +300,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn set_rate_limits_preserves_previous_limit_id_when_missing() {
|
||||
async fn set_rate_limits_defaults_to_codex_when_limit_id_missing_after_other_bucket() {
|
||||
let session_configuration = make_session_configuration_for_tests().await;
|
||||
let mut state = SessionState::new(session_configuration);
|
||||
|
||||
@@ -339,12 +334,12 @@ mod tests {
|
||||
.latest_rate_limits
|
||||
.as_ref()
|
||||
.and_then(|v| v.limit_id.clone()),
|
||||
Some("codex_other".to_string())
|
||||
Some("codex".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn set_rate_limits_accepts_new_limit_id_bucket() {
|
||||
async fn set_rate_limits_carries_credits_and_plan_type_from_codex_to_codex_other() {
|
||||
let session_configuration = make_session_configuration_for_tests().await;
|
||||
let mut state = SessionState::new(session_configuration);
|
||||
|
||||
@@ -367,7 +362,7 @@ mod tests {
|
||||
|
||||
state.set_rate_limits(RateLimitSnapshot {
|
||||
limit_id: Some("codex_other".to_string()),
|
||||
limit_name: Some("codex_other".to_string()),
|
||||
limit_name: None,
|
||||
primary: Some(RateLimitWindow {
|
||||
used_percent: 30.0,
|
||||
window_minutes: Some(120),
|
||||
@@ -382,7 +377,7 @@ mod tests {
|
||||
state.latest_rate_limits,
|
||||
Some(RateLimitSnapshot {
|
||||
limit_id: Some("codex_other".to_string()),
|
||||
limit_name: Some("codex_other".to_string()),
|
||||
limit_name: None,
|
||||
primary: Some(RateLimitWindow {
|
||||
used_percent: 30.0,
|
||||
window_minutes: Some(120),
|
||||
|
||||
@@ -37,6 +37,8 @@ use codex_protocol::user_input::UserInput;
|
||||
use core_test_support::load_default_config_for_test;
|
||||
use core_test_support::responses::ev_completed;
|
||||
use core_test_support::responses::ev_completed_with_tokens;
|
||||
use core_test_support::responses::ev_message_item_added;
|
||||
use core_test_support::responses::ev_output_text_delta;
|
||||
use core_test_support::responses::ev_response_created;
|
||||
use core_test_support::responses::mount_sse_once;
|
||||
use core_test_support::responses::mount_sse_once_match;
|
||||
@@ -1787,6 +1789,64 @@ async fn context_window_error_sets_total_tokens_to_model_window() -> anyhow::Res
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn incomplete_response_emits_content_filter_error_message() -> anyhow::Result<()> {
|
||||
skip_if_no_network!(Ok(()));
|
||||
let server = MockServer::start().await;
|
||||
|
||||
let incomplete_response = sse(vec![
|
||||
ev_response_created("resp_incomplete"),
|
||||
ev_message_item_added("msg_incomplete", "partial content"),
|
||||
ev_output_text_delta("continued chunk"),
|
||||
json!({
|
||||
"type": "response.incomplete",
|
||||
"response": {
|
||||
"id": "resp_incomplete",
|
||||
"object": "response",
|
||||
"status": "incomplete",
|
||||
"error": null,
|
||||
"incomplete_details": {
|
||||
"reason": "content_filter"
|
||||
}
|
||||
}
|
||||
}),
|
||||
]);
|
||||
|
||||
let responses_mock = mount_sse_once(&server, incomplete_response).await;
|
||||
|
||||
let TestCodex { codex, .. } = test_codex()
|
||||
.with_config(|config| {
|
||||
config.model_provider.stream_max_retries = Some(0);
|
||||
})
|
||||
.build(&server)
|
||||
.await?;
|
||||
codex
|
||||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "trigger incomplete".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
})
|
||||
.await?;
|
||||
|
||||
let error_event = wait_for_event(&codex, |ev| matches!(ev, EventMsg::Error(_))).await;
|
||||
assert!(
|
||||
matches!(
|
||||
error_event,
|
||||
EventMsg::Error(ref err)
|
||||
if err.message
|
||||
== "stream disconnected before completion: Incomplete response returned, reason: content_filter"
|
||||
),
|
||||
"expected incomplete content filter error; got {error_event:?}"
|
||||
);
|
||||
|
||||
assert_eq!(responses_mock.requests().len(), 1);
|
||||
|
||||
wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn azure_overrides_assign_properties_used_for_responses_url() {
|
||||
skip_if_no_network!();
|
||||
|
||||
Reference in New Issue
Block a user