Skip to content

Commit c9f432a

Browse files
aegeigergithub-actions[bot]cdoern
authored
chore(ci): change CI to use ogx_open_client (#6016)
# What does this PR do? Fixes multiple integration test failures when running with the OpenAPI-generated SDK (`ogx_open_client`) instead of the Stainless SDK (`ogx_client`). This PR switches the integration test suite to use the new SDK and resolves all compatibility issues discovered during the transition. **Changes by commit:** 1. **fix(ci):** Fix the OpenAPI SDK publish workflow to use OIDC trusted publishing instead of unconfigured secret-based auth. 2. **chore(client-sdks):** Switch all integration tests and the library client from `ogx_client` to `ogx_open_client`. Add fallback import chains for backward compatibility. Update model class references in `test_rerank.py` and adapt `test_openai_vector_store_file_contents_with_extra_query` for the new SDK's direct query parameter convention (vs. the OpenAI SDK's `extra_query`). 3. **fix(client-sdks):** Fix dict serialization in multipart form requests. The generated SDK was sending dicts as JSON strings, but the OGX server expects bracket-notation (`key[subkey]=value`). Affects `expires_after` and similar nested fields on file upload. 4. **fix(client-sdks):** Add a `ChoiceDeltaToolCall` streaming delta variant in the OpenAPI spec. The spec previously used the same `ChatCompletionMessageToolCall` schema (all fields required, no `index`) for both complete responses and streaming deltas, but streaming deltas on the wire include `index` and omit `id`/`type`/`function` on continuation chunks. This formalizes the de-facto protocol without changing anything on the wire. 5. **fix(client-sdks):** Fix enum deserialization in anyOf validators. The generated `actual_instance_must_validate_anyof` used `isinstance()` to check variant types, which failed for `str,Enum` types (e.g., `OpenAIResponseInputToolChoiceMode`) because a bare string like `"auto"` is not an `isinstance` of the enum class. Add a fallback that tries constructing the type from `str`/`int` values. 6. **fix(tests):** Fix four bugs in vision inference integration tests: guard against `None` in streaming `delta.content`, fix `multi_image_data` fixture reference (was using the function instead of the resolved value), fix wrong streaming API call (`chunk.event.delta.text` → `chunk.choices[0].delta.content`), and update stale GitHub image URL. ## Test Plan Integration tests were run against a live OpenAI backend with the `ci-tests` distribution: ```bash # Ensure the generated SDK is installed with all template fixes cp -r client-sdks/openapi/sdks/python/ogx_open_client/* \ .venv/lib64/python3.12/site-packages/ogx_open_client/ # Run the full base test suite uv run --no-sync ./scripts/integration-tests.sh \ --stack-config server:ci-tests --setup gpt --suite base --inference-mode live ``` **Results: 22 failures (down from 29 baseline), 0 regressions.** All 22 remaining failures are pre-existing and unrelated: - 16x ranker/hybrid search returning empty results (keyword indexing issue across backends) - 6x legacy `/v1/completions` endpoint 404 (not served by the `ci-tests` distribution with `gpt` setup — not a real CI configuration) **Tests fixed by this PR (7 resolved):** | Test | Root cause | Fix | |------|-----------|-----| | `test_openai_vector_store_file_contents_with_extra_query[client_with_models-*]` (x6 backends) | Generated SDK rejects `extra_query` kwarg | `_get_file_content` helper adapts params per client type | | `test_inference_store_tool_calls[client_with_models-True]` | Streaming tool call chunks deserialized as raw dicts | `ChoiceDeltaToolCall` schema with relaxed fields | | `test_list_response_input_items[client_with_models]` | `tool_choice: "auto"` fails anyOf enum validation | Try enum construction for str/int in validator | | `test_image_chat_completion_multiple_images[True]` | `None` delta.content on streaming chunks | `or ""` guard | | `test_image_chat_completion_multiple_images[False]` | `multi_image_data` fixture function used as value | Changed to `multi_image_url` | | `test_image_chat_completion_streaming` | `.lower()` called on `None` delta.content | `if delta_content:` guard | | `test_openai_chat_completion_image_url` | Stale GitHub URL (`meta-llama/ogx`) | Updated to `ogx-ai/ogx` | --------- Signed-off-by: E Geiger <egeiger@redhat.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Charlie Doern <cdoern@redhat.com>
1 parent 15a9769 commit c9f432a

51 files changed

Lines changed: 2019 additions & 470 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/actions/install-ogx-client/action.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ runs:
3030
id: configure
3131
shell: bash
3232
run: |
33-
# If sdk_install_url is provided (e.g., from Stainless preview), use it directly
33+
# If sdk_install_url is provided, use it directly
3434
if [ -n "${{ inputs.sdk_install_url }}" ]; then
3535
echo "Using provided sdk_install_url: ${{ inputs.sdk_install_url }}"
3636
echo "install-after-sync=true" >> $GITHUB_OUTPUT

.github/workflows/README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ OGX uses GitHub Actions for Continuous Integration (CI). Below is a table detail
3333
| Integration Tests (Record) | [record-integration-tests.yml](record-integration-tests.yml) | Auto-record missing test recordings for PR |
3434
| Release Branch Scheduled CI | [release-branch-scheduled-ci.yml](release-branch-scheduled-ci.yml) | Scheduled CI checks for active release branches |
3535
| Check semantic PR titles | [semantic-pr.yml](semantic-pr.yml) | Ensure that PR titles follow the conventional commit spec |
36-
| Stainless SDK Builds | [stainless-builds.yml](stainless-builds.yml) | Build Stainless SDK from OpenAPI spec changes |
3736
| Close stale issues and PRs | [stale_bot.yml](stale_bot.yml) | Run the Stale Bot action |
3837
| Test External Providers Installed via Module | [test-external-provider-module.yml](test-external-provider-module.yml) | Test External Provider installation via Python module |
3938
| Test External API and Providers | [test-external.yml](test-external.yml) | Test the External API and Provider mechanisms |

.github/workflows/integration-tests.yml

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ on:
5151
required: false
5252
type: string
5353
default: 'default'
54-
description: 'Matrix configuration key from ci_matrix.json (e.g., "default", "stainless")'
54+
description: 'Matrix configuration key from ci_matrix.json (e.g., "default")'
5555
matrix_json:
5656
required: false
5757
type: string
@@ -87,8 +87,7 @@ concurrency:
8787

8888
# Limit permissions of the GITHUB_TOKEN to the minimum required.
8989
# Default mode is 'replay' which only needs read access.
90-
# When called via workflow_call (e.g., from stainless-builds.yml with record-if-missing),
91-
# the caller's permissions apply.
90+
# When called via workflow_call, the caller's permissions apply.
9291
permissions:
9392
contents: read
9493

@@ -204,7 +203,7 @@ jobs:
204203
WATSONX_PROJECT_ID: replay-mode-dummy-project
205204
VERTEX_AI_PROJECT: ${{ matrix.config.setup == 'vertexai' && 'replay-mode-dummy-project' || '' }}
206205
VERTEX_AI_LOCATION: ${{ matrix.config.setup == 'vertexai' && 'global' || '' }}
207-
AWS_BEARER_TOKEN_BEDROCK: replay-mode-dummy-key
206+
AWS_BEDROCK_BEARER_TOKEN: replay-mode-dummy-key
208207
AWS_DEFAULT_REGION: us-west-2
209208
GEMINI_API_KEY: replay-mode-dummy-key
210209
TAVILY_SEARCH_API_KEY: ${{ secrets.TAVILY_SEARCH_API_KEY || 'replay-mode-dummy-key' }}

.github/workflows/openapi-generator-validation.yml

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -185,9 +185,9 @@ jobs:
185185
echo "OpenAPI spec generated successfully"
186186
ls -lh openapi.yml
187187
188-
- name: Generate Python SDK (for CI testing with ogx_client name)
188+
- name: Generate Python SDK
189189
working-directory: client-sdks/openapi
190-
run: make sdk OPEN=0
190+
run: make sdk OPEN=1
191191

192192
- name: Validate generated SDK
193193
working-directory: client-sdks/openapi
@@ -217,16 +217,14 @@ jobs:
217217
218218
- name: Install generated SDK
219219
run: |
220-
echo "Installing OpenAPI-generated SDK (ogx_client)..."
221-
# Uninstall existing client (note: uv pip uninstall doesn't support -y flag)
222-
uv pip uninstall ogx-client || true
220+
echo "Reinstalling OpenAPI-generated SDK (ogx_open_client)..."
221+
uv pip uninstall ogx-open-client || true
223222
224-
# Install SDK using uv pip - this ensures proper editable install
223+
# Install SDK using uv pip
225224
uv pip install -e client-sdks/openapi/sdks/python
226225
227226
echo "Verifying installation..."
228-
# Use 'uv run python' to ensure we use the correct Python in the venv
229-
uv run python -c "import ogx_client; print(f'Installed: {ogx_client.__name__}')"
227+
uv run python -c "import ogx_open_client; print(f'Installed: {ogx_open_client.__name__}')"
230228
231229
- name: Setup Ollama (for integration tests)
232230
if: runner.os == 'Linux'
@@ -244,7 +242,7 @@ jobs:
244242
|| echo "::warning::Some integration tests failed - this may indicate SDK compatibility issues"
245243
246244
# Show which SDK is actually being used
247-
uv run python -c "import ogx_client; import inspect; print(f'SDK location: {inspect.getfile(ogx_client)}')"
245+
uv run python -c "import ogx_open_client; import inspect; print(f'SDK location: {inspect.getfile(ogx_open_client)}')"
248246
249247
- name: Summary
250248
if: runner.os == 'Linux'
@@ -257,5 +255,5 @@ jobs:
257255
echo "✅ Integration tests executed (check logs for results)"
258256
echo ""
259257
echo "**Platform**: ${{ matrix.os }}"
260-
echo "**Package**: ogx_client (OpenAPI-generated)"
258+
echo "**Package**: ogx_open_client (OpenAPI-generated)"
261259
} >> "$GITHUB_STEP_SUMMARY"

.github/workflows/publish-openapi-sdk.yml

Lines changed: 61 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,10 @@ concurrency:
2727

2828
permissions:
2929
contents: read
30-
id-token: write # Required for trusted publishing to PyPI
3130

3231
jobs:
33-
publish-sdk:
32+
build-sdk:
3433
runs-on: ubuntu-latest
35-
environment:
36-
name: ${{ github.event.inputs.publish_to == 'pypi' && 'pypi-production' || 'testpypi' }}
3734

3835
steps:
3936
- name: Checkout repository
@@ -100,65 +97,80 @@ jobs:
10097
echo "Built distribution files:"
10198
ls -lh dist/
10299
103-
- name: Publish to TestPyPI (dry-run or manual)
104-
if: |
105-
(github.event_name == 'workflow_dispatch' && inputs.publish_to == 'testpypi' && inputs.dry_run == false) ||
106-
(github.event_name == 'push' && startsWith(github.ref, 'refs/tags/openapi-sdk-v') && !contains(github.ref, '-rc') && !contains(github.ref, '-alpha') && !contains(github.ref, '-beta'))
107-
working-directory: client-sdks/openapi/sdks/python
108-
run: |
109-
echo "Publishing to TestPyPI..."
110-
uv publish --publish-url https://test.pypi.org/legacy/ dist/*
111-
env:
112-
UV_PUBLISH_TOKEN: ${{ secrets.TEST_PYPI_API_TOKEN }}
113-
114-
- name: Publish to PyPI (production)
115-
if: |
116-
github.event_name == 'workflow_dispatch' &&
117-
inputs.publish_to == 'pypi' &&
118-
inputs.dry_run == false
119-
working-directory: client-sdks/openapi/sdks/python
120-
run: |
121-
echo "Publishing to PyPI..."
122-
uv publish dist/*
123-
env:
124-
UV_PUBLISH_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
125-
126100
- name: Upload build artifacts
127101
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v4.6.0
128102
with:
129103
name: python-sdk-dist
130104
path: client-sdks/openapi/sdks/python/dist/
131105
retention-days: 30
132106

107+
publish-testpypi:
108+
if: |
109+
(github.event_name == 'workflow_dispatch' && inputs.publish_to == 'testpypi' && inputs.dry_run == false) ||
110+
(github.event_name == 'push' && startsWith(github.ref, 'refs/tags/openapi-sdk-v') && !contains(github.ref, '-rc') && !contains(github.ref, '-alpha') && !contains(github.ref, '-beta'))
111+
needs: build-sdk
112+
runs-on: ubuntu-latest
113+
environment: testpypi
114+
permissions:
115+
id-token: write
116+
steps:
117+
- name: Download build artifacts
118+
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
119+
with:
120+
name: python-sdk-dist
121+
path: dist
122+
123+
- name: Publish to TestPyPI
124+
uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0
125+
with:
126+
repository-url: https://test.pypi.org/legacy/
127+
packages-dir: dist/
128+
verbose: true
129+
133130
- name: Summary
134131
run: |
135132
{
136133
echo "## SDK Publishing Summary"
137134
echo ""
138135
echo "- **Event**: ${{ github.event_name }}"
139-
echo "- **Dry Run**: ${{ inputs.dry_run || 'false' }}"
140-
echo "- **Target**: ${{ inputs.publish_to || 'testpypi (default)' }}"
136+
echo "- **Target**: TestPyPI"
141137
echo "- **Package**: ogx-open-client"
142138
echo ""
139+
echo "✅ Package published to TestPyPI"
140+
echo "Install with: \`pip install --index-url https://test.pypi.org/simple/ ogx-open-client\`"
141+
} >> "$GITHUB_STEP_SUMMARY"
142+
143+
publish-pypi:
144+
if: |
145+
github.event_name == 'workflow_dispatch' &&
146+
inputs.publish_to == 'pypi' &&
147+
inputs.dry_run == false
148+
needs: build-sdk
149+
runs-on: ubuntu-latest
150+
permissions:
151+
id-token: write
152+
steps:
153+
- name: Download build artifacts
154+
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
155+
with:
156+
name: python-sdk-dist
157+
path: dist
143158

144-
if [ "${{ inputs.dry_run }}" = "true" ]; then
145-
echo "✅ Package built successfully (dry-run, not published)"
146-
elif [ "${{ inputs.publish_to }}" = "testpypi" ]; then
147-
echo "✅ Package published to TestPyPI"
148-
echo "Install with: \`pip install --index-url https://test.pypi.org/simple/ ogx-open-client\`"
149-
elif [ "${{ github.event_name }}" = "push" ]; then
150-
# Check for pre-release tags (same logic as publish step)
151-
case "${{ github.ref }}" in
152-
*"-rc"*|*"-alpha"*|*"-beta"*)
153-
echo "✅ Package built successfully (pre-release tag, not published)"
154-
;;
155-
*)
156-
echo "✅ Package published to TestPyPI"
157-
echo "Install with: \`pip install --index-url https://test.pypi.org/simple/ ogx-open-client\`"
158-
;;
159-
esac
160-
else
161-
echo "✅ Package published to PyPI"
162-
echo "Install with: \`pip install ogx-open-client\`"
163-
fi
159+
- name: Publish to PyPI
160+
uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0
161+
with:
162+
packages-dir: dist/
163+
verbose: true
164+
165+
- name: Summary
166+
run: |
167+
{
168+
echo "## SDK Publishing Summary"
169+
echo ""
170+
echo "- **Event**: ${{ github.event_name }}"
171+
echo "- **Target**: PyPI (production)"
172+
echo "- **Package**: ogx-open-client"
173+
echo ""
174+
echo "✅ Package published to PyPI"
175+
echo "Install with: \`pip install ogx-open-client\`"
164176
} >> "$GITHUB_STEP_SUMMARY"

.github/workflows/record-integration-tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@ jobs:
273273
GOOGLE_APPLICATION_CREDENTIALS: ${{ matrix.provider.setup == 'vertexai' && secrets.GOOGLE_APPLICATION_CREDENTIALS || '' }}
274274
GEMINI_API_KEY: ${{ matrix.provider.setup == 'gemini' && secrets.GEMINI_API_KEY || '' }}
275275
TAVILY_SEARCH_API_KEY: ${{ contains(fromJSON('["gpt","azure","vertexai"]'), matrix.provider.setup) && secrets.TAVILY_SEARCH_API_KEY || '' }}
276-
AWS_BEARER_TOKEN_BEDROCK: ${{ matrix.provider.setup == 'bedrock' && secrets.AWS_BEARER_TOKEN_BEDROCK || '' }}
276+
AWS_BEDROCK_BEARER_TOKEN: ${{ matrix.provider.setup == 'bedrock' && secrets.AWS_BEARER_TOKEN_BEDROCK || '' }}
277277
AWS_DEFAULT_REGION: ${{ matrix.provider.setup == 'bedrock' && 'us-west-2' || '' }}
278278
with:
279279
stack-config: 'server:ci-tests'

0 commit comments

Comments
 (0)