pangram 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,254 @@
1
+ # API Reference
2
+
3
+ The public API is exposed through `Pangram::Client`. `Pangram.new` is a
4
+ convenience constructor for the same client.
5
+
6
+ ```ruby
7
+ require 'pangram'
8
+
9
+ client = Pangram.new(api_key: ENV.fetch('PANGRAM_API_KEY'))
10
+ # Equivalent to Pangram::Client.new(...)
11
+ ```
12
+
13
+ All successful API responses remain ordinary Ruby `Hash` and `Array` values.
14
+ Hash keys are strings and match Pangram's JSON response fields.
15
+
16
+ ## Authentication
17
+
18
+ ```ruby
19
+ Pangram.new(api_key: 'your-api-key')
20
+ ```
21
+
22
+ When `api_key:` is omitted, the client reads `PANGRAM_API_KEY`. An explicit
23
+ argument takes precedence over the environment. If neither is available,
24
+ construction raises `Pangram::AuthenticationError`.
25
+
26
+ Every request sends the key through the `x-api-key` header.
27
+
28
+ ## Model discovery
29
+
30
+ ### `list_models`
31
+
32
+ ```ruby
33
+ models = client.list_models
34
+ # => ["default", "pangram-4"]
35
+ ```
36
+
37
+ Returns the server-ordered selectors available to the current API key. The
38
+ catalog is entitlement- and availability-aware, so callers should not
39
+ hard-code it.
40
+
41
+ The client validates that the response contains unique, non-blank model names
42
+ and the `default` selector.
43
+
44
+ ## Text prediction
45
+
46
+ ### `predict`
47
+
48
+ ```ruby
49
+ result = client.predict(
50
+ text,
51
+ model: 'pangram-4',
52
+ public_dashboard_link: false,
53
+ timeout: 300,
54
+ poll_interval: 0.5
55
+ )
56
+ ```
57
+
58
+ Submits `POST /task`, polls `GET /task/{task_id}`, and returns when the task
59
+ reaches `STAGE_SUCCESS`.
60
+
61
+ | Keyword | Default | Description |
62
+ | --- | --- | --- |
63
+ | `model` | `nil` | Selector returned by `list_models` |
64
+ | `public_dashboard_link` | `false` | Request a public result link |
65
+ | `timeout` | `300` | Total deadline for submission and polling, in seconds |
66
+ | `poll_interval` | `0.5` | Delay between polls, clamped to at least 0.1 seconds |
67
+
68
+ Omitting `model` currently preserves Pangram's legacy wire payload and emits a
69
+ deprecation warning. New integrations should pass `model: "default"` or
70
+ another selector returned by `list_models`.
71
+
72
+ A successful result can include:
73
+
74
+ - `stage`, `text`, `version`, `headline`, `prediction`, and `prediction_short`
75
+ - `fraction_ai`, `fraction_ai_assisted`, and `fraction_human`
76
+ - segment counters and `dashboard_link` when requested
77
+ - `windows`, including labels, scores, confidence, character offsets, word and
78
+ token counts, and Pangram 4 humanizer fields
79
+
80
+ The SDK returns the server payload without renaming or symbolizing fields.
81
+
82
+ ### `predict_with_dashboard_link`
83
+
84
+ ```ruby
85
+ result = client.predict_with_dashboard_link(
86
+ text,
87
+ model: 'pangram-4',
88
+ timeout: 300,
89
+ poll_interval: 0.5
90
+ )
91
+ ```
92
+
93
+ Equivalent to `predict(..., public_dashboard_link: true)`.
94
+
95
+ ## Bulk jobs
96
+
97
+ ### `submit_bulk`
98
+
99
+ Submit exactly one payload shape:
100
+
101
+ ```ruby
102
+ client.submit_bulk(text: ['first', 'second'], model: 'pangram-4')
103
+
104
+ client.submit_bulk(
105
+ items: [
106
+ { id: 'row-1', text: 'first' },
107
+ { id: 'row-2', text: 'second' }
108
+ ],
109
+ model: 'pangram-4'
110
+ )
111
+ ```
112
+
113
+ Returns the accepted `202` response, including `bulk_id`, initial status,
114
+ accepted items, and immediate validation failures.
115
+
116
+ One model applies to the whole bulk job. Per-item model selectors are not
117
+ supported.
118
+
119
+ ### `get_bulk_status`
120
+
121
+ ```ruby
122
+ status = client.get_bulk_status(bulk_id)
123
+ ```
124
+
125
+ Returns job status, counters, and creation/completion timestamps. Terminal
126
+ statuses are `succeeded`, `failed`, and `partial`.
127
+
128
+ ### `wait_for_bulk`
129
+
130
+ ```ruby
131
+ status = client.wait_for_bulk(
132
+ bulk_id,
133
+ timeout: 3600,
134
+ poll_interval: 0.5
135
+ )
136
+ ```
137
+
138
+ Polls until the job reaches a terminal status. Transient network failures are
139
+ retried within the total deadline.
140
+
141
+ ### `get_bulk_items`
142
+
143
+ ```ruby
144
+ page = client.get_bulk_items(bulk_id, offset: 0, limit: 100)
145
+ ```
146
+
147
+ Returns one page of item metadata.
148
+
149
+ ### `get_bulk_results_page`
150
+
151
+ ```ruby
152
+ page = client.get_bulk_results_page(bulk_id, offset: 0, limit: 100)
153
+ ```
154
+
155
+ Returns one submitted-item page with successful or in-progress items in
156
+ `items` and failures in `failed_items`.
157
+
158
+ ### `get_bulk_results`
159
+
160
+ ```ruby
161
+ results = client.get_bulk_results(bulk_id, page_size: 1000, timeout: 3600)
162
+ ```
163
+
164
+ Fetches every results page and returns a single aggregate containing
165
+ `bulk_id`, `total_items`, `items`, and `failed_items`. `page_size` must be from
166
+ 1 through 1,000. `timeout` is the total deadline in seconds for fetching all
167
+ pages; transient failures are retried until that deadline. The aggregate's
168
+ `total_items` is locked to the first page so in-progress jobs cannot truncate
169
+ the result mid-pagination.
170
+
171
+ This method materializes all pages in memory. Process
172
+ `get_bulk_results_page` incrementally for very large jobs.
173
+
174
+ ## File prediction
175
+
176
+ File prediction uses Pangram's default model and does not accept `model:`.
177
+
178
+ ### `predict_file`
179
+
180
+ ```ruby
181
+ result = client.predict_file(
182
+ 'document.pdf',
183
+ public_dashboard_link: true,
184
+ timeout: 300
185
+ )
186
+ ```
187
+
188
+ Uploads one file and returns its result.
189
+
190
+ ### `predict_files`
191
+
192
+ ```ruby
193
+ results = client.predict_files(
194
+ ['first.docx', 'second.pdf'],
195
+ public_dashboard_link: true,
196
+ timeout: 300
197
+ )
198
+ ```
199
+
200
+ Uploads multiple files in one multipart request. Each path is sent as a
201
+ repeated form field named `files`, not `files[]`. Every file handle is closed
202
+ after success or failure, including partial file-open failures.
203
+
204
+ ## Plagiarism detection
205
+
206
+ ### `check_plagiarism`
207
+
208
+ ```ruby
209
+ result = client.check_plagiarism('Text to check')
210
+ ```
211
+
212
+ Returns Pangram's plagiarism response, including detection status, matched
213
+ content, sentence counts, and plagiarism percentage.
214
+
215
+ ## Deprecated compatibility methods
216
+
217
+ The following methods mirror compatibility behavior still present in the
218
+ Python SDK:
219
+
220
+ - `predict_short(text, model:)` uses the current prediction flow.
221
+ - `batch_predict(texts, model:)` predicts each input sequentially.
222
+
223
+ Use `predict` for one input and `submit_bulk` for many inputs.
224
+
225
+ ## Errors
226
+
227
+ All SDK-defined exceptions inherit from `Pangram::Error`.
228
+
229
+ | Exception | Raised when |
230
+ | --- | --- |
231
+ | `Pangram::AuthenticationError` | No API key is configured |
232
+ | `Pangram::ValidationError` | A local argument is invalid |
233
+ | `Pangram::APIError` | Pangram rejects a request or an async task fails |
234
+ | `Pangram::InvalidResponseError` | A response is not valid JSON or has an invalid top-level contract |
235
+ | `Pangram::NetworkError` | An HTTP request fails at the transport layer |
236
+ | `Pangram::TimeoutError` | Prediction or bulk polling exceeds its total deadline |
237
+
238
+ ```ruby
239
+ begin
240
+ client.predict(text, model: 'default')
241
+ rescue Pangram::TimeoutError => e
242
+ warn e.message
243
+ rescue Pangram::Error => e
244
+ warn "Pangram failed: #{e.message}"
245
+ end
246
+ ```
247
+
248
+ API errors carry the HTTP `status` and raw response `body` when they come from
249
+ a rejected response. `Pangram::InvalidResponseError` is a subclass of
250
+ `Pangram::APIError`, so rescuing `APIError` also catches schema violations.
251
+
252
+ Transport errors and responses with status 408, 429, 500, 502, 503, or 504 are
253
+ retried until the total deadline during prediction polling, bulk polling, and
254
+ bulk results pagination. Other API errors are not retried.
@@ -0,0 +1,113 @@
1
+ # Development Guide
2
+
3
+ ## Repository layout
4
+
5
+ ```text
6
+ lib/pangram.rb Public entry point and Pangram.new
7
+ lib/pangram/client.rb API client, polling, pagination, and uploads
8
+ lib/pangram/errors.rb SDK exception hierarchy
9
+ lib/pangram/version.rb Gem version
10
+ spec/ Offline contract tests
11
+ docs/ Extended project documentation
12
+ pangram.gemspec Package metadata and dependencies
13
+ ```
14
+
15
+ ## Setup
16
+
17
+ Ruby is managed through [mise](https://mise.jdx.dev/):
18
+
19
+ ```bash
20
+ mise trust
21
+ mise install
22
+ bundle install
23
+ ```
24
+
25
+ The gem requires Ruby 3.1 or newer. The lockfile is constrained so all
26
+ development dependencies also remain compatible with Ruby 3.1.
27
+
28
+ ## Make targets
29
+
30
+ Run `make help` to list available commands.
31
+
32
+ | Command | Purpose |
33
+ | --- | --- |
34
+ | `make install` | Install Bundler dependencies |
35
+ | `make test` or `make spec` | Run all RSpec tests |
36
+ | `make coverage` | Run tests and print the coverage report path |
37
+ | `make lint` | Run RuboCop |
38
+ | `make lint-fix` | Run RuboCop auto-correction |
39
+ | `make docs` | Generate YARD documentation under `doc/` |
40
+ | `make build` | Build `pkg/pangram-VERSION.gem` |
41
+ | `make verify` | Run tests, lint, docs, and build |
42
+ | `make console` | Start IRB with `pangram` loaded |
43
+ | `make clean` | Remove generated artifacts |
44
+
45
+ ## Testing
46
+
47
+ ```bash
48
+ make test
49
+ ```
50
+
51
+ The test suite uses WebMock and must not contact Pangram's live services. It
52
+ verifies:
53
+
54
+ - request URLs, headers, JSON payloads, and status codes
55
+ - model catalog validation
56
+ - prediction and bulk polling, retry, failure, and timeout paths
57
+ - bulk result pagination and aggregation
58
+ - exact multipart field names and file-handle cleanup
59
+ - API, network, validation, and invalid-response errors
60
+
61
+ SimpleCov writes its report to `coverage/index.html` and enforces at least 90%
62
+ line coverage.
63
+
64
+ ## Linting
65
+
66
+ ```bash
67
+ make lint
68
+ ```
69
+
70
+ RuboCop targets Ruby 3.1. Prefer correcting the implementation over disabling
71
+ a cop for a local exception.
72
+
73
+ ## API documentation
74
+
75
+ Public methods use YARD-compatible comments. Generate HTML documentation with:
76
+
77
+ ```bash
78
+ make docs
79
+ ```
80
+
81
+ Generated files are written to `doc/` and are excluded from source control.
82
+
83
+ ## Building and inspecting the gem
84
+
85
+ ```bash
86
+ make build
87
+ gem specification pkg/pangram-0.1.0.gem
88
+ gem contents --show-install-dir pangram
89
+ ```
90
+
91
+ For an isolated installation smoke test:
92
+
93
+ ```bash
94
+ tmp_dir="$(mktemp -d)"
95
+ GEM_HOME="$tmp_dir" GEM_PATH="$tmp_dir" \
96
+ gem install pkg/pangram-0.1.0.gem --no-document
97
+ GEM_HOME="$tmp_dir" GEM_PATH="$tmp_dir" \
98
+ ruby -e 'require "pangram"; puts Pangram::VERSION'
99
+ rm -rf "$tmp_dir"
100
+ ```
101
+
102
+ Use the current value from `lib/pangram/version.rb` instead of `0.1.0` when
103
+ building a later release.
104
+
105
+ ## Adding or changing an API method
106
+
107
+ 1. Confirm the current REST contract in the official Pangram documentation.
108
+ 2. Keep HTTP ownership in `Pangram::Client` and reuse existing request helpers.
109
+ 3. Preserve response field names and string keys.
110
+ 4. Add WebMock coverage for the request and relevant failure paths.
111
+ 5. Update `docs/API_REFERENCE.md`, the project README when user-facing, and
112
+ `CHANGELOG.md`.
113
+ 6. Run `make verify`.
data/docs/README.md ADDED
@@ -0,0 +1,25 @@
1
+ # Pangram Ruby SDK Documentation
2
+
3
+ This directory contains the extended documentation for the `pangram` gem.
4
+
5
+ ## Documentation index
6
+
7
+ - [API reference](API_REFERENCE.md) — client construction, method signatures,
8
+ response contracts, polling behavior, and errors.
9
+ - [Development guide](DEVELOPMENT.md) — repository layout, local setup,
10
+ testing, linting, documentation generation, and gem builds.
11
+ - [Release guide](RELEASING.md) — versioning, verification, package inspection,
12
+ and the manual RubyGems publishing checklist.
13
+
14
+ ## Quick links
15
+
16
+ - [Project README](../README.md)
17
+ - [Changelog](../CHANGELOG.md)
18
+ - [Client implementation](../lib/pangram/client.rb)
19
+ - [Contract tests](../spec/pangram/client_spec.rb)
20
+ - [Official Pangram API documentation](https://docs.pangram.com/api-reference/introduction)
21
+
22
+ ## Supported Ruby versions
23
+
24
+ The gem requires Ruby 3.1 or newer. CI covers Ruby 3.1, 3.2, 3.3, 3.4,
25
+ and 4.0.
data/docs/RELEASING.md ADDED
@@ -0,0 +1,72 @@
1
+ # Release Guide
2
+
3
+ Publishing a gem is a shared, irreversible action. Complete the checks below,
4
+ review the built package, and publish only with explicit maintainer approval.
5
+
6
+ ## 1. Update release metadata
7
+
8
+ 1. Change `Pangram::VERSION` in `lib/pangram/version.rb`.
9
+ 2. Move the relevant entries in `CHANGELOG.md` from `Unreleased` into a dated
10
+ version section.
11
+ 3. Confirm README and API reference examples still match the implementation.
12
+
13
+ The version must follow semantic versioning and must not already exist on
14
+ RubyGems.
15
+
16
+ ## 2. Verify the source
17
+
18
+ ```bash
19
+ make verify
20
+ ```
21
+
22
+ This runs the complete test suite, RuboCop, YARD generation, and gem build.
23
+ Do not publish if any command fails.
24
+
25
+ CI also tests the supported Ruby matrix from 3.1 through 4.0.
26
+
27
+ ## 3. Inspect the package
28
+
29
+ ```bash
30
+ gem specification pkg/pangram-VERSION.gem
31
+ gem unpack pkg/pangram-VERSION.gem --target /tmp/pangram-gem-review
32
+ ```
33
+
34
+ Confirm that:
35
+
36
+ - the name is `pangram` and the version is correct
37
+ - `lib/`, README, license, changelog, and docs are present
38
+ - test artifacts, coverage output, and credentials are absent
39
+ - runtime dependencies and Ruby requirements are correct
40
+
41
+ ## 4. Test an isolated install
42
+
43
+ ```bash
44
+ tmp_dir="$(mktemp -d)"
45
+ GEM_HOME="$tmp_dir" GEM_PATH="$tmp_dir" \
46
+ gem install pkg/pangram-VERSION.gem --no-document
47
+ GEM_HOME="$tmp_dir" GEM_PATH="$tmp_dir" \
48
+ ruby -e 'require "pangram"; puts Pangram::VERSION'
49
+ rm -rf "$tmp_dir"
50
+ ```
51
+
52
+ ## 5. Commit and tag
53
+
54
+ After review, commit the version and changelog changes. Create an annotated tag
55
+ using the same version:
56
+
57
+ ```bash
58
+ git tag -a vVERSION -m "Release vVERSION"
59
+ ```
60
+
61
+ Pushing commits or tags requires explicit approval.
62
+
63
+ ## 6. Publish manually
64
+
65
+ With maintainer approval and valid RubyGems credentials:
66
+
67
+ ```bash
68
+ gem push pkg/pangram-VERSION.gem
69
+ ```
70
+
71
+ After publishing, install `pangram` from RubyGems in a clean environment and
72
+ verify its version and `require "pangram"` entry point.