Skip to content

Commit dd1377f

Browse files
feat: add OpenAI-compatible error boundary gem
0 parents  commit dd1377f

28 files changed

Lines changed: 2116 additions & 0 deletions

‎.github/workflows/ci.yml‎

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
name: Ruby CI
2+
3+
on:
4+
pull_request:
5+
push:
6+
branches:
7+
- main
8+
9+
permissions:
10+
contents: read
11+
12+
jobs:
13+
test:
14+
name: Ruby ${{ matrix.ruby }}
15+
runs-on: ubuntu-latest
16+
timeout-minutes: 10
17+
strategy:
18+
fail-fast: false
19+
matrix:
20+
ruby:
21+
- "3.0"
22+
- "3.1"
23+
- "3.2"
24+
- "3.3"
25+
- "3.4"
26+
steps:
27+
- uses: actions/checkout@v5
28+
with:
29+
persist-credentials: false
30+
- uses: ruby/setup-ruby@v1
31+
with:
32+
ruby-version: ${{ matrix.ruby }}
33+
bundler-cache: true
34+
- run: bundle exec rake test
35+
- run: gem build openai-compatible-errors.gemspec
36+
- name: Install the built gem without network access
37+
run: |
38+
isolated_gem_home="${RUNNER_TEMP}/gem-home"
39+
mkdir -p "${isolated_gem_home}"
40+
GEM_HOME="${isolated_gem_home}" GEM_PATH="${isolated_gem_home}" \
41+
gem install --local openai-compatible-errors-*.gem --no-document
42+
GEM_HOME="${isolated_gem_home}" GEM_PATH="${isolated_gem_home}" \
43+
ruby -ropenai_compatible_errors -e \
44+
'abort "smoke failed" unless OpenAICompatibleErrors.normalize_error({ status: 429 }).category == :rate_limit'

‎.github/workflows/release.yml‎

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
name: Push gem
2+
3+
on:
4+
push:
5+
tags:
6+
- "v*"
7+
8+
jobs:
9+
push:
10+
name: Build, verify and publish
11+
runs-on: ubuntu-latest
12+
timeout-minutes: 15
13+
permissions:
14+
contents: write
15+
id-token: write
16+
environment:
17+
name: release
18+
url: https://rubygems.org/gems/openai-compatible-errors
19+
steps:
20+
- uses: actions/checkout@v5
21+
with:
22+
persist-credentials: false
23+
- name: Set up Ruby
24+
uses: ruby/setup-ruby@v1
25+
with:
26+
ruby-version: "3.3"
27+
bundler-cache: true
28+
- name: Verify tag matches gem version
29+
env:
30+
RELEASE_TAG: ${{ github.ref_name }}
31+
run: |
32+
bundle exec ruby -Ilib -e '
33+
expected = "v#{OpenAICompatibleErrors::VERSION}"
34+
abort "release tag must be #{expected}" unless ENV.fetch("RELEASE_TAG") == expected
35+
'
36+
- run: bundle exec rake test
37+
- uses: rubygems/release-gem@v1

‎.gitignore‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/.bundle/
2+
/pkg/
3+
/checksums/
4+
/.ruby-version
5+
*.gem

‎CHANGELOG.md‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Changelog
2+
3+
All notable changes to this gem are documented here.
4+
5+
## [0.1.0] - 2026-08-09
6+
7+
Initial public release.
8+
9+
- Normalize HTTP-like hashes, response objects and common SDK exception shapes.
10+
- Classify authentication, permission, rate-limit, quota, validation, conflict,
11+
transport, upstream, server, schema and stream failures.
12+
- Parse bounded Retry-After and millisecond retry hints.
13+
- Return immutable, safe-by-default error snapshots with optional redacted
14+
provider text.
15+
- Plan retries only when the caller supplies replay-safety and stream-phase
16+
evidence.
17+
- Inspect Chat Completions and Responses Server-Sent Events incrementally
18+
without retaining generated output.
19+
- Provide bounded recursive log sanitization with cycle detection.
20+
21+
[0.1.0]: https://github.com/airouter-dev/openai-compatible-errors-ruby/releases/tag/v0.1.0

‎CONTRIBUTING.md‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Contributing
2+
3+
## Local validation
4+
5+
This gem intentionally has no runtime dependencies. With Ruby 3.0 or newer:
6+
7+
bundle install
8+
bundle exec rake test
9+
gem build openai-compatible-errors.gemspec
10+
11+
The tests never call an API, open a network connection, sleep, or use real
12+
credentials. Keep fixtures synthetic and bounded.
13+
14+
## Design boundaries
15+
16+
- normalize_error returns a snapshot; it does not raise a provider exception
17+
again or retain a raw body.
18+
- decide_retry returns a plan; the caller owns waiting, cancellation,
19+
idempotency and replay.
20+
- SSEInspector retains replay-boundary state only, never generated text.
21+
- Redaction reduces logging risk but is not a substitute for a data-retention
22+
policy.
23+
24+
Please add a regression test for every new provider shape and document whether
25+
the shape is observed in HTTP headers, a structured body, an SDK exception, or
26+
an SSE event.

‎Gemfile‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# frozen_string_literal: true
2+
3+
source "https://rubygems.org"
4+
5+
gemspec

‎Gemfile.lock‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
PATH
2+
remote: .
3+
specs:
4+
openai-compatible-errors (0.1.0)
5+
6+
GEM
7+
remote: https://rubygems.org/
8+
specs:
9+
minitest (5.14.2)
10+
rake (13.0.6)
11+
12+
PLATFORMS
13+
x86_64-linux
14+
15+
DEPENDENCIES
16+
minitest (~> 5.14)
17+
openai-compatible-errors!
18+
rake (~> 13.0)
19+
20+
BUNDLED WITH
21+
2.3.5

‎LICENSE‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2026 AI ROUTER contributors
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

0 commit comments

Comments
 (0)