ruby_llm-browser 0.2.0 → 0.3.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +19 -0
- data/README.md +2 -2
- data/docs/README.md +1 -0
- data/docs/RELEASING.md +11 -6
- data/docs/_advanced/development-and-testing.md +7 -0
- data/docs/_advanced/rails-integration.md +7 -3
- data/docs/_advanced/result-delivery.md +119 -0
- data/docs/_config.yml +2 -2
- data/docs/_data/navigation.yml +1 -1
- data/docs/_getting_started/overview.md +3 -0
- data/docs/_getting_started/whats-new-in-0-3.md +48 -0
- data/docs/_reference/api-and-configuration.md +15 -0
- data/docs/_reference/release-history.md +2 -1
- data/docs/_reference/roadmap.md +14 -6
- data/docs/_reference/upgrading.md +30 -27
- data/docs/{_getting_started → _reference}/whats-new-in-0-2.md +2 -3
- data/examples/rails_results.rb +141 -0
- data/lib/ruby_llm/browser/rails/operation_job.rb +16 -4
- data/lib/ruby_llm/browser/rails/result_delivery_error.rb +22 -0
- data/lib/ruby_llm/browser/rails.rb +16 -0
- data/lib/ruby_llm/browser/result.rb +3 -3
- data/lib/ruby_llm/browser/version.rb +1 -1
- metadata +7 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6c1e32269fa76e278df596a6e63e30d28506a9e7e3ada269703bad06ba870fef
|
|
4
|
+
data.tar.gz: d103e32333352508883d3794dd6e37a4aa3058d4f2e5842ccd174c6b2b43b11e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 39769537e3c05f25231657f2b56ab72bda7690ac2b7e144149cac85a8d2d7de7b29e73f45a4dd41fe8e6718aa0f1535d6c2d77d5f4203469474b4a64e018dc4d
|
|
7
|
+
data.tar.gz: 1ff61fb1a7fe9017be814634d6763938e6a6652814df76386b43ca90217f2ffad2f51429a5edec1855a431300802182c8d2ad7c9a9eb00890a6612ba9ccd7da4
|
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,25 @@ All notable changes to this project are documented here. The format is based on
|
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project aims to
|
|
5
5
|
follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [0.3.0] - 2026-10-19
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- Opt-in `Rails.result_delivery` receives a sanitized `Result` and its operation
|
|
12
|
+
after execution and cleanup. Hosts own persistence, authorization and recovery.
|
|
13
|
+
- `Rails::ResultDeliveryError` reports failed delivery with retained in-process
|
|
14
|
+
operation/result objects. Callback errors do not leak through exception causes.
|
|
15
|
+
- A runnable host-owned SQLite result store and separate-process Active Job example,
|
|
16
|
+
with scoped reads, idempotent writes, failure recovery and worker-crash tests.
|
|
17
|
+
- Upgrade coverage against the frozen 0.2.0 migration with existing records retained.
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- Copy and freeze `Result` identity and error strings so constructor-input mutation
|
|
22
|
+
cannot alter a retained result. Mutating these result attributes raises `FrozenError`.
|
|
23
|
+
- Suppress result delivery from cancelled or fenced workers. A delivery failure
|
|
24
|
+
preserves session state; duplicate jobs never repeat browser work or delivery.
|
|
25
|
+
|
|
7
26
|
## [0.2.0] - 2026-10-19
|
|
8
27
|
|
|
9
28
|
### Added
|
data/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
An unofficial [RubyLLM](https://rubyllm.com) extension for read-only browser inspection
|
|
4
4
|
and schema-checked extraction, with an optional Rails 8 adapter.
|
|
5
5
|
|
|
6
|
-
**0.
|
|
6
|
+
**0.3.0 supports offline use only.** `FakeBackend` runs without credentials or network
|
|
7
7
|
requests. The experimental Stagehand adapter refuses live operations; `Act` is an
|
|
8
8
|
offline simulation. This project is not maintained or endorsed by RubyLLM or Browserbase.
|
|
9
9
|
|
|
@@ -16,7 +16,7 @@ offline simulation. This project is not maintained or endorsed by RubyLLM or Bro
|
|
|
16
16
|
Use Ruby 3.2+ and RubyLLM 2.x. Add to your Gemfile, then run `bundle install`:
|
|
17
17
|
|
|
18
18
|
```ruby
|
|
19
|
-
gem "ruby_llm-browser", "~> 0.
|
|
19
|
+
gem "ruby_llm-browser", "~> 0.3.0"
|
|
20
20
|
```
|
|
21
21
|
|
|
22
22
|
## Try it offline
|
data/docs/README.md
CHANGED
|
@@ -8,6 +8,7 @@ the gem.
|
|
|
8
8
|
- [Getting started](_getting_started/getting-started.md): installation and an offline example.
|
|
9
9
|
- [Configuration](_getting_started/configuration.md): host inputs and process defaults.
|
|
10
10
|
- [Inspect and Extract](_core_features/inspect-extract.md): tools and result handling.
|
|
11
|
+
- [Rails result delivery](_advanced/result-delivery.md): host storage, access checks, and delivery recovery.
|
|
11
12
|
- [Rails jobs and recovery](_advanced/rails-integration.md): installation, workers, and cleanup.
|
|
12
13
|
- [API and configuration reference](_reference/api-and-configuration.md): parameters and errors.
|
|
13
14
|
- [Security model](SECURITY_MODEL.md): guarantees and limits.
|
data/docs/RELEASING.md
CHANGED
|
@@ -17,6 +17,10 @@ After reading this guide, you will know:
|
|
|
17
17
|
|
|
18
18
|
## Compatibility evidence
|
|
19
19
|
|
|
20
|
+
The source repository's `docs/evidence/V0_3_VALIDATION.md` records local 0.3.0 checks.
|
|
21
|
+
`docs/evidence/V0_3_RELEASE.md` records the exact remote CI, published artifact and
|
|
22
|
+
hosted documentation results. The earlier audit below is historical evidence.
|
|
23
|
+
|
|
20
24
|
The 2026-10-18 audit runs on Ruby **3.3.5**, arm64 macOS, with Stagehand **3.21.0**,
|
|
21
25
|
JSONSchemer **2.5.0**, and sqlite3 **2.10.0**. Full offline suites cover:
|
|
22
26
|
|
|
@@ -40,6 +44,7 @@ bundle exec rake
|
|
|
40
44
|
bundle exec bundle-audit check --update
|
|
41
45
|
bundle exec ruby examples/plain_ruby.rb
|
|
42
46
|
bundle exec ruby examples/rails_adapter.rb
|
|
47
|
+
bundle exec ruby examples/rails_results.rb
|
|
43
48
|
|
|
44
49
|
BUNDLE_GEMFILE=gemfiles/ruby_llm_2_0.gemfile bundle install
|
|
45
50
|
BUNDLE_GEMFILE=gemfiles/ruby_llm_2_0.gemfile bundle exec rspec
|
|
@@ -77,10 +82,10 @@ Verify all of the following:
|
|
|
77
82
|
- Core require loads no Rails, Active Record, Active Job, or Stagehand client.
|
|
78
83
|
- The optional Rails require loads on demand.
|
|
79
84
|
- The generated migration and all README-linked guides are shipped.
|
|
80
|
-
-
|
|
85
|
+
- All shipped examples run using the installed artifact.
|
|
81
86
|
- The artifact contains no keys, cookies, dumps, databases, lockfiles, or test fixtures.
|
|
82
87
|
- Dependency/license inventory and known-advisory check match the resolved versions.
|
|
83
|
-
- Record the artifact SHA-256, exact versions, commands and date in the
|
|
88
|
+
- Record the artifact SHA-256, exact versions, commands and date in the candidate validation note.
|
|
84
89
|
|
|
85
90
|
## Release boundaries
|
|
86
91
|
|
|
@@ -114,12 +119,12 @@ section and run `bin/verify-release` without that flag. The tag workflow require
|
|
|
114
119
|
released metadata; local candidate verification cannot enable publication.
|
|
115
120
|
|
|
116
121
|
Run the checks above, review the package contents and push the completed commit to
|
|
117
|
-
`main`. Then create and push a matching tag, for example `
|
|
118
|
-
`
|
|
122
|
+
`main`. Then create and push a matching tag, for example `v{{ site.version }}` for gem version
|
|
123
|
+
`{{ site.version }}`:
|
|
119
124
|
|
|
120
125
|
```bash
|
|
121
|
-
git tag -a
|
|
122
|
-
git push origin
|
|
126
|
+
git tag -a v{{ site.version }} -m "Release {{ site.version }}"
|
|
127
|
+
git push origin v{{ site.version }}
|
|
123
128
|
```
|
|
124
129
|
|
|
125
130
|
The `Release Gem` workflow rejects a tag that does not match both the gem and docs
|
|
@@ -28,6 +28,13 @@ the RubyLLM 2.0 and Rails 8.0 alternate bundles, and the plain-Ruby and Rails ex
|
|
|
28
28
|
Install each alternate bundle first if needed. Database suites run sequentially.
|
|
29
29
|
RuboCop uses the writable `tmp/rubocop` cache. Default tests block unmocked HTTP.
|
|
30
30
|
|
|
31
|
+
The Rails result suite executes serialized jobs in separate processes against temporary
|
|
32
|
+
SQLite files. It verifies reads after worker exit, actor/run isolation, conflicting
|
|
33
|
+
delivery rejection, persistence failures before and after commit, and the crash gap
|
|
34
|
+
between browser execution and host storage. HTTP stays blocked in those subprocesses.
|
|
35
|
+
A frozen 0.2.0 migration verifies preservation of existing rows and indexes. These
|
|
36
|
+
checks exercise host result delivery without adding a second browser fixture.
|
|
37
|
+
|
|
31
38
|
Commit each completed change with its docs and checks. Keep the next work item in the
|
|
32
39
|
[roadmap]({% link _reference/roadmap.md %}); do not create a second task ledger.
|
|
33
40
|
|
|
@@ -15,6 +15,7 @@ After reading this guide, you will know:
|
|
|
15
15
|
|
|
16
16
|
- How to install the adapter's tables.
|
|
17
17
|
- How to build a freshly authorized job context with matching budgets.
|
|
18
|
+
- How to deliver results to host storage.
|
|
18
19
|
- How to inspect status, cancel work, and recover orphaned sessions.
|
|
19
20
|
|
|
20
21
|
## Installing the adapter
|
|
@@ -103,9 +104,12 @@ summary = operation.summary
|
|
|
103
104
|
|
|
104
105
|
`summary` omits provider session IDs and budget internals. It reports state, steps,
|
|
105
106
|
bytes, fence, timestamps, error code and cancellation flag. It is coarse progress,
|
|
106
|
-
not a percentage-complete promise.
|
|
107
|
-
|
|
108
|
-
|
|
107
|
+
not a percentage-complete promise. To persist a result body, opt in to
|
|
108
|
+
`RubyLLM::Browser::Rails.result_delivery`. The callback receives `operation:` and
|
|
109
|
+
`result:` after execution and cleanup. Your application owns storage and authorized
|
|
110
|
+
retrieval. Read [Rails result delivery]({% link _advanced/result-delivery.md %}) for
|
|
111
|
+
failure handling and recovery. Do not rely on an Active Job return value after
|
|
112
|
+
asynchronous execution.
|
|
109
113
|
|
|
110
114
|
The stores are trusted server APIs, not tenant-safe controllers. They expose unscoped
|
|
111
115
|
fetch/sweep methods for workers; never expose them directly to model arguments or HTTP.
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: default
|
|
3
|
+
title: Rails Result Delivery
|
|
4
|
+
nav_order: 2
|
|
5
|
+
description: Persist sanitized background results in your application and recover delivery separately from browser cleanup.
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# {{ page.title }}
|
|
9
|
+
|
|
10
|
+
{{ page.description }}
|
|
11
|
+
|
|
12
|
+
After reading this guide, you will know:
|
|
13
|
+
|
|
14
|
+
- How to receive a background operation's bounded result.
|
|
15
|
+
- How to store and authorize results in your application.
|
|
16
|
+
- How to recover delivery without repeating browser work.
|
|
17
|
+
|
|
18
|
+
## Persist a Result
|
|
19
|
+
|
|
20
|
+
Configure the optional Rails adapter and its context factory first. See
|
|
21
|
+
[Rails jobs and recovery]({% link _advanced/rails-integration.md %}). Register a trusted
|
|
22
|
+
callback in your initializer:
|
|
23
|
+
|
|
24
|
+
```ruby
|
|
25
|
+
RubyLLM::Browser::Rails.result_delivery = lambda do |operation:, result:|
|
|
26
|
+
# Your host service persists result.to_h with operation.id, actor_id and run_id.
|
|
27
|
+
BrowserResults.persist(operation: operation, payload: result.to_h)
|
|
28
|
+
end
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
`BrowserResults` is your application service. It is not a gem class. The callback
|
|
32
|
+
receives an immutable `Operation` snapshot and the core's sanitized, bounded `Result`.
|
|
33
|
+
Persist `result.to_h` or `result.to_json`, rather than the operation object, which
|
|
34
|
+
contains a provider session ID for trusted bookkeeping.
|
|
35
|
+
|
|
36
|
+
The callback runs synchronously in the worker after the engine attempts cleanup and
|
|
37
|
+
before the job returns. It receives successful, truncated, error, unknown, and
|
|
38
|
+
cleanup-pending results. Preflight exceptions have no `Result` and do not invoke it.
|
|
39
|
+
Cancellation or a changed fence detected after execution suppresses delivery. A
|
|
40
|
+
missing, already started, or terminal operation does not invoke it either.
|
|
41
|
+
|
|
42
|
+
The default is `nil`, which preserves the existing job return behavior. Setting the
|
|
43
|
+
hook to a non-callable value raises `InvalidInput`. Callback return values are ignored;
|
|
44
|
+
raise an exception if persistence fails.
|
|
45
|
+
|
|
46
|
+
## Keep Storage and Access in the Host
|
|
47
|
+
|
|
48
|
+
Run the disposable, offline host example:
|
|
49
|
+
|
|
50
|
+
```sh
|
|
51
|
+
bundle exec ruby examples/rails_results.rb
|
|
52
|
+
# RESULT_AFTER_WORKER_EXIT_PASS
|
|
53
|
+
# SCOPED_READS_PASS
|
|
54
|
+
# IDEMPOTENT_WRITE_PASS
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
It writes a serialized Active Job to a temporary file, executes it in a separate
|
|
58
|
+
worker, waits for exit, and reads the schema-checked extraction from SQLite. Repeating
|
|
59
|
+
the job in another worker starts no browser session. The example's host table accepts
|
|
60
|
+
identical repeated writes and rejects conflicting bodies or actor/run identities.
|
|
61
|
+
Its file queue holds one job and is for demonstration; use a durable queue in your
|
|
62
|
+
application. The temporary database and queue file are removed on completion. Read
|
|
63
|
+
[the example source](https://github.com/yorzi/ruby_llm-browser/blob/main/examples/rails_results.rb).
|
|
64
|
+
|
|
65
|
+
Use a unique operation ID for idempotent writes. Repeating a write of the same result
|
|
66
|
+
should succeed; reject a conflicting body or actor/run identity. Retrieve with the
|
|
67
|
+
authenticated actor and run in the database query. Never use model-supplied actor IDs
|
|
68
|
+
as authorization. Recheck host revocation and retention rules when serving a result.
|
|
69
|
+
|
|
70
|
+
The gem creates no result table and adds no migrations. Keep results in your host's
|
|
71
|
+
database or durable storage. Do not rely on an Active Job return value or in-memory
|
|
72
|
+
callback state after a worker exits.
|
|
73
|
+
|
|
74
|
+
An `ok` extraction passed the pinned schema before redaction and bounding. A truncated
|
|
75
|
+
result can contain a preview instead of the original schema shape. Treat all page
|
|
76
|
+
content as untrusted. Store failures too, without interpreting `cleanup_pending` or
|
|
77
|
+
`unknown` as success. A stored result describes that attempt; a later reaper changes
|
|
78
|
+
session state, not the historical result body.
|
|
79
|
+
|
|
80
|
+
## Recover Delivery Separately
|
|
81
|
+
|
|
82
|
+
If the callback raises, the job raises `RubyLLM::Browser::Rails::ResultDeliveryError`
|
|
83
|
+
with code `RESULT_DELIVERY_FAILED`. The original callback exception is not chained,
|
|
84
|
+
so automatic job logs cannot print its potentially sensitive message. The error
|
|
85
|
+
exposes `operation` and `result` to trusted, in-process host recovery code:
|
|
86
|
+
|
|
87
|
+
```ruby
|
|
88
|
+
# Inside host code that catches ResultDeliveryError while these objects still exist:
|
|
89
|
+
RubyLLM::Browser::Rails.result_delivery.call(
|
|
90
|
+
operation: error.operation, result: error.result
|
|
91
|
+
)
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
This retries only your callback. Reauthorize recovery in the host. Keep external side
|
|
95
|
+
effects idempotent and persist the result before sending notifications. For durable
|
|
96
|
+
notification retries, write the result and a host outbox entry in one transaction.
|
|
97
|
+
An exception object does not survive worker exit as durable result storage.
|
|
98
|
+
|
|
99
|
+
A failed callback does not change the operation's session state or error code. Queue
|
|
100
|
+
redelivery of `OperationJob` does not repeat a started browser operation and does not
|
|
101
|
+
redeliver its result. Keep delivery failures visible in your job monitoring; retrying
|
|
102
|
+
the browser job is not a result-recovery mechanism.
|
|
103
|
+
|
|
104
|
+
There is no atomic transaction across provider execution, operation bookkeeping, and
|
|
105
|
+
host persistence. A process crash after execution but before persistence can lose the
|
|
106
|
+
result. The hook offers one automatic delivery attempt, not guaranteed or exactly-once
|
|
107
|
+
delivery. If host storage is unavailable, a recovery handler must save the retained
|
|
108
|
+
result to a separate durable destination before it exits. Never recover a lost result
|
|
109
|
+
by replaying an uncertain browser operation automatically.
|
|
110
|
+
|
|
111
|
+
Cancellation can race after the worker's final check. If your host forbids storing a
|
|
112
|
+
result after revocation, enforce that rule in the persistence transaction as well as
|
|
113
|
+
at read time. A callback failure must not clear cancellation, pending cleanup, or quota.
|
|
114
|
+
|
|
115
|
+
## Next Steps
|
|
116
|
+
|
|
117
|
+
Read [the API reference]({% link _reference/api-and-configuration.md %}) for result
|
|
118
|
+
fields and [Rails jobs and recovery]({% link _advanced/rails-integration.md %}) for
|
|
119
|
+
cancellation and reaper behavior.
|
data/docs/_config.yml
CHANGED
|
@@ -108,6 +108,6 @@ code_language: ruby
|
|
|
108
108
|
# and {{ site.examples.NAME }}. Candidates remain unreleased; the docs builder
|
|
109
109
|
# reads stable docs from the latest release tag and main under /next/.
|
|
110
110
|
released: true
|
|
111
|
-
version: "0.
|
|
112
|
-
stable_version: "0.
|
|
111
|
+
version: "0.3.0"
|
|
112
|
+
stable_version: "0.3.0"
|
|
113
113
|
examples: {}
|
data/docs/_data/navigation.yml
CHANGED
|
@@ -50,3 +50,6 @@ does not click or submit forms.
|
|
|
50
50
|
* [Getting Started]({% link _getting_started/getting-started.md %}) - install and run the offline example.
|
|
51
51
|
* [Configuration]({% link _getting_started/configuration.md %}) - choose host-owned defaults.
|
|
52
52
|
* [API and configuration reference]({% link _reference/api-reference.md %}) - see options, statuses, and result fields.
|
|
53
|
+
|
|
54
|
+
Read [Rails Result Delivery]({% link _advanced/result-delivery.md %}) to persist a
|
|
55
|
+
background result in host storage and recover delivery separately from session cleanup.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: default
|
|
3
|
+
title: What's New in 0.3
|
|
4
|
+
redirect_from:
|
|
5
|
+
- /whats-new/
|
|
6
|
+
nav_order: 4
|
|
7
|
+
description: Deliver Rails background results to host storage without repeating browser work on delivery failure.
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# {{ page.title }}
|
|
11
|
+
|
|
12
|
+
{{ page.description }}
|
|
13
|
+
|
|
14
|
+
After reading this guide, you will know:
|
|
15
|
+
|
|
16
|
+
- How to persist the result of a background operation.
|
|
17
|
+
- Which delivery failures your application must recover.
|
|
18
|
+
- Which capabilities remain unsupported.
|
|
19
|
+
|
|
20
|
+
## Rails Result Delivery
|
|
21
|
+
|
|
22
|
+
Register a host callback to receive each eligible job's sanitized `Result` after
|
|
23
|
+
execution and cleanup:
|
|
24
|
+
|
|
25
|
+
```ruby
|
|
26
|
+
RubyLLM::Browser::Rails.result_delivery = lambda do |operation:, result:|
|
|
27
|
+
BrowserResults.persist(operation: operation, payload: result.to_h)
|
|
28
|
+
end
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Your application supplies `BrowserResults`, storage, access checks, retention, and
|
|
32
|
+
idempotent recovery. The gem adds no database tables or runtime dependencies. Read
|
|
33
|
+
[Rails Result Delivery]({% link _advanced/result-delivery.md %}) for the contract.
|
|
34
|
+
|
|
35
|
+
Run `bundle exec ruby examples/rails_results.rb` for a disposable SQLite host example.
|
|
36
|
+
It executes a serialized job in a separate process, waits for the worker to exit,
|
|
37
|
+
then verifies result retrieval, actor/run isolation, and idempotent persistence.
|
|
38
|
+
|
|
39
|
+
## Delivery Failure Is Separate from Cleanup
|
|
40
|
+
|
|
41
|
+
A callback failure raises `Rails::ResultDeliveryError` with the retained operation and
|
|
42
|
+
result. It does not change session state. A duplicate browser job does not rerun the
|
|
43
|
+
operation or redeliver the result. Recover host delivery using the retained result;
|
|
44
|
+
do not enqueue a new browser operation to recover persistence.
|
|
45
|
+
|
|
46
|
+
The hook cannot guarantee delivery across worker crashes. Stagehand and live Act remain
|
|
47
|
+
unsupported. Review [the roadmap]({% link _reference/roadmap.md %}) for live-backend
|
|
48
|
+
qualification and the remaining 1.0 requirements.
|
|
@@ -95,6 +95,10 @@ or redacted field need not retain the original schema's type. Always check the s
|
|
|
95
95
|
|
|
96
96
|
## Results and errors
|
|
97
97
|
|
|
98
|
+
`Result` copies and freezes its strings and nested data. This includes operation ID,
|
|
99
|
+
error code and error message; changes to constructor inputs cannot alter a retained
|
|
100
|
+
result. Duplicate a returned string if host code needs to edit it.
|
|
101
|
+
|
|
98
102
|
`Result#to_h` contains symbol keys: `status`, `operation_id`, `final_url`,
|
|
99
103
|
`captured_at` (UTC ISO 8601), `locator`, `digest`, `truncated`, `data`, and optional
|
|
100
104
|
`error: { code:, message: }`. Locator/data objects use string keys. JSON encodes all
|
|
@@ -182,6 +186,17 @@ is deferred to a later version. The Engine refuses all non-fake Act calls before
|
|
|
182
186
|
a session. Selector/description keyword filters are defense in depth, not semantic proof
|
|
183
187
|
that a click is harmless. The Stagehand adapter does not support Act in this release.
|
|
184
188
|
|
|
189
|
+
## Rails Result Delivery
|
|
190
|
+
|
|
191
|
+
After `require "ruby_llm/browser/rails"`, set `Rails.result_delivery` to a callable
|
|
192
|
+
accepting `operation:` and `result:`, or `nil` to disable it. `Rails.reset!` clears the
|
|
193
|
+
hook. The callback runs synchronously after execution and cleanup. Return values are
|
|
194
|
+
ignored; exceptions become `Rails::ResultDeliveryError` (`RESULT_DELIVERY_FAILED`),
|
|
195
|
+
which exposes `operation` and `result` to trusted recovery code. These objects are
|
|
196
|
+
not automatically persisted. Duplicate browser jobs do not retry delivery. Read
|
|
197
|
+
[Rails Result Delivery]({% link _advanced/result-delivery.md %}) for storage, access,
|
|
198
|
+
cancellation, and crash boundaries.
|
|
199
|
+
|
|
185
200
|
## Next steps
|
|
186
201
|
|
|
187
202
|
- Read [Rails integration]({% link _advanced/rails-integration.md %}) for durable workers and recovery.
|
|
@@ -18,7 +18,8 @@ release notes to identify the exact source for this version.
|
|
|
18
18
|
|
|
19
19
|
| Release | Date | Highlights |
|
|
20
20
|
| --- | --- | --- |
|
|
21
|
-
| [0.
|
|
21
|
+
| [0.3.0]({% link _getting_started/whats-new-in-0-3.md %}) | 2026-10-19 | Opt-in Rails result delivery, durable host example, recovery and upgrade verification |
|
|
22
|
+
| [0.2.0]({% link _reference/whats-new-in-0-2.md %}) | 2026-10-19 | Read-only tool factory, backend-aware Doctor, and agent development/browser verification |
|
|
22
23
|
| [0.1.0](https://github.com/yorzi/ruby_llm-browser/releases/tag/v0.1.0) | 2026-10-18 | Offline read-only Inspect and Extract, Rails adapter, and Act simulation; live Stagehand and live Act are unsupported |
|
|
23
24
|
|
|
24
25
|
For changes to existing applications, follow [Upgrading]({% link _reference/upgrading.md %}).
|
data/docs/_reference/roadmap.md
CHANGED
|
@@ -24,8 +24,9 @@ Ruby and through the optional Rails adapter.
|
|
|
24
24
|
|
|
25
25
|
Version 0.1.0 introduced the offline foundation: Inspect, Extract, URL policy, bounded
|
|
26
26
|
results, operation bookkeeping, Rails jobs, and Act simulation. It does not provide a
|
|
27
|
-
supported live backend. The current Stagehand adapter refuses live operations.
|
|
28
|
-
|
|
27
|
+
supported live backend. The current Stagehand adapter refuses live operations. The
|
|
28
|
+
0.3.0 release adds opt-in Rails result delivery to host-owned storage;
|
|
29
|
+
it does not add a gem-managed results database or guaranteed delivery.
|
|
29
30
|
|
|
30
31
|
**This is a proposed development sequence, not a list of available features or release
|
|
31
32
|
dates.** Future API names below are proposals. Keep each release small; use patch
|
|
@@ -48,8 +49,8 @@ feedback to settle the public API.
|
|
|
48
49
|
|
|
49
50
|
### 0.2.0: Two Small Features
|
|
50
51
|
|
|
51
|
-
Version `0.2.0` adds both features below.
|
|
52
|
-
[What's New]({% link
|
|
52
|
+
Version `0.2.0` adds both features below. Milestones after 0.3 remain proposed scope. See
|
|
53
|
+
[What's New]({% link _reference/whats-new-in-0-2.md %}) for usage and
|
|
53
54
|
[development and testing]({% link _advanced/development-and-testing.md %}) for validation.
|
|
54
55
|
|
|
55
56
|
1. Add the read-only tool factory `RubyLLM::Browser.tools(context:, schema:)`.
|
|
@@ -72,8 +73,15 @@ validation precede the release tag and publication.
|
|
|
72
73
|
|
|
73
74
|
### 0.3.0: Complete the Rails Result Path
|
|
74
75
|
|
|
75
|
-
|
|
76
|
-
|
|
76
|
+
Version `0.3.0` implements `Rails.result_delivery`, typed
|
|
77
|
+
host-delivery failures, and a runnable SQLite host example with separate workers.
|
|
78
|
+
Read [What's New in 0.3]({% link _getting_started/whats-new-in-0-3.md %}) and
|
|
79
|
+
[Rails Result Delivery]({% link _advanced/result-delivery.md %}) for the current API.
|
|
80
|
+
The source repository's `docs/evidence/V0_3_VALIDATION.md` records local candidate
|
|
81
|
+
checks. `docs/evidence/V0_3_RELEASE.md` records remote verification and publication.
|
|
82
|
+
|
|
83
|
+
The result-delivery hook lets the host persist the sanitized `Result`
|
|
84
|
+
against its operation ID. Its runnable host example stores and retrieves
|
|
77
85
|
the result after an asynchronous job finishes. The host owns access checks, retention,
|
|
78
86
|
and storage; the gem does not need an admin interface or a general results database.
|
|
79
87
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
layout: default
|
|
3
3
|
title: Upgrading
|
|
4
4
|
nav_order: 2
|
|
5
|
-
description: Upgrade from 0.
|
|
5
|
+
description: Upgrade from 0.2.0 while preserving operation data and configuring optional Rails result delivery.
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# {{ page.title }}
|
|
@@ -11,11 +11,11 @@ description: Upgrade from 0.1.0 while preserving tool registration, diagnostics,
|
|
|
11
11
|
|
|
12
12
|
After reading this guide, you will know:
|
|
13
13
|
|
|
14
|
-
- How to update an application from 0.
|
|
15
|
-
-
|
|
16
|
-
-
|
|
14
|
+
- How to update an application from 0.2.0.
|
|
15
|
+
- How to preserve existing data and enable result delivery.
|
|
16
|
+
- Which result and worker behaviors change.
|
|
17
17
|
|
|
18
|
-
## Upgrade from 0.
|
|
18
|
+
## Upgrade from 0.2.0
|
|
19
19
|
|
|
20
20
|
Update your Gemfile and bundle:
|
|
21
21
|
|
|
@@ -27,38 +27,41 @@ gem "ruby_llm-browser", "~> {{ site.version }}"
|
|
|
27
27
|
bundle update ruby_llm-browser
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
approval
|
|
32
|
-
|
|
30
|
+
This release adds no gem-managed database migration or runtime dependency. Preserve all
|
|
31
|
+
operation, approval, and actor-lock rows. Do not rerun the install generator on an
|
|
32
|
+
existing installation. Result storage belongs to your application.
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
chat.with_tools(*RubyLLM::Browser.tools(context: context, schema: schema))
|
|
36
|
-
```
|
|
34
|
+
## Enable Result Delivery When Needed
|
|
37
35
|
|
|
38
|
-
|
|
36
|
+
`RubyLLM::Browser::Rails.result_delivery` defaults to `nil`. Existing job return values
|
|
37
|
+
remain supported. To retain results after a worker exits, follow
|
|
38
|
+
[Rails Result Delivery]({% link _advanced/result-delivery.md %}). Configure persistence,
|
|
39
|
+
authorized reads, idempotent writes, and recovery before enabling the callback.
|
|
39
40
|
|
|
40
|
-
|
|
41
|
+
A callback failure raises `Rails::ResultDeliveryError`. Queue retries do not redeliver
|
|
42
|
+
a result. The error retains the result only in process; it is not durable storage.
|
|
43
|
+
Recover delivery separately from browser execution. A crash before host persistence
|
|
44
|
+
can still lose the result, so do not treat this hook as guaranteed delivery.
|
|
41
45
|
|
|
42
|
-
|
|
43
|
-
on the backend. Do not require Stagehand or credential checks in every report, and do
|
|
44
|
-
not rely on check order. FakeBackend diagnostics skip provider environment and SDK
|
|
45
|
-
checks. Stagehand still reports a failure; `mode: :live` does not enable it.
|
|
46
|
+
## Account for Immutable Results and Fenced Workers
|
|
46
47
|
|
|
47
|
-
|
|
48
|
-
|
|
48
|
+
`Result` now also copies and freezes operation ID, error code and error message strings,
|
|
49
|
+
consistent with its other fields. Mutating these attributes raises `FrozenError`;
|
|
50
|
+
duplicate a string before changing it in host code. Caller-owned inputs stay mutable.
|
|
49
51
|
|
|
50
|
-
|
|
52
|
+
A worker fenced during browser execution now returns `:fenced_out` and cannot deliver
|
|
53
|
+
its stale result. Cancelled work never invokes the delivery hook, including while
|
|
54
|
+
cleanup remains pending. Delivery failures do not change the session state or free
|
|
55
|
+
quota. Stagehand and live Act remain unsupported.
|
|
51
56
|
|
|
52
|
-
|
|
53
|
-
the additive migration instructions in the
|
|
54
|
-
[Rails guide]({% link _advanced/rails-integration.md %}). Drain workers during migration;
|
|
55
|
-
do not reuse idempotency keys for legacy rows without request digests.
|
|
57
|
+
## Earlier Versions
|
|
56
58
|
|
|
57
|
-
For
|
|
58
|
-
[0.
|
|
59
|
+
For upgrades from 0.1.0 and earlier development snapshots, first follow the
|
|
60
|
+
[0.2.0 upgrading guide](https://github.com/yorzi/ruby_llm-browser/blob/v0.2.0/docs/_reference/upgrading.md).
|
|
61
|
+
Preserve operation data and use additive migrations where required.
|
|
59
62
|
|
|
60
63
|
## Next Steps
|
|
61
64
|
|
|
62
|
-
- Read [What's New in 0.
|
|
65
|
+
- Read [What's New in 0.3]({% link _getting_started/whats-new-in-0-3.md %}).
|
|
63
66
|
- Review [release history]({% link _reference/release-history.md %}) and
|
|
64
67
|
[live-backend qualification]({% link _advanced/live-backend-qualification.md %}).
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# A host-owned result table and a single-job file queue, using only FakeBackend.
|
|
4
|
+
# This disposable demonstration is not a production queue, controller or migration.
|
|
5
|
+
require "ruby_llm/browser/rails"
|
|
6
|
+
require "logger"
|
|
7
|
+
require "open3"
|
|
8
|
+
require "tmpdir"
|
|
9
|
+
|
|
10
|
+
module BrowserResultsExample
|
|
11
|
+
SCHEMA = { "type" => "object", "properties" => { "title" => { "type" => "string" } },
|
|
12
|
+
"required" => [ "title" ], "additionalProperties" => false }.freeze
|
|
13
|
+
RESOLVER = Object.new
|
|
14
|
+
def RESOLVER.getaddresses(_host) = [ "93.184.216.34" ]
|
|
15
|
+
|
|
16
|
+
class ResultRecord < ActiveRecord::Base
|
|
17
|
+
self.table_name = "example_browser_results"
|
|
18
|
+
self.primary_key = "operation_id"
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
class FileQueue
|
|
22
|
+
def initialize(path) = @path = path
|
|
23
|
+
def enqueue(job) = File.write(@path, JSON.generate(job.serialize))
|
|
24
|
+
def enqueue_at(*) = raise(ArgumentError, "demo queue supports immediate jobs only")
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
module_function
|
|
28
|
+
|
|
29
|
+
def connect(database)
|
|
30
|
+
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: database, timeout: 5000)
|
|
31
|
+
ActiveRecord::Base.logger = Logger.new(IO::NULL)
|
|
32
|
+
ActiveJob::Base.logger = Logger.new(IO::NULL)
|
|
33
|
+
RubyLLM::Browser::Rails::Schema.create!
|
|
34
|
+
connection = ActiveRecord::Base.connection
|
|
35
|
+
return if connection.table_exists?(ResultRecord.table_name)
|
|
36
|
+
|
|
37
|
+
connection.create_table(ResultRecord.table_name, id: false) do |table|
|
|
38
|
+
table.string :operation_id, primary_key: true, null: false
|
|
39
|
+
table.string :actor_id, null: false
|
|
40
|
+
table.string :run_id, null: false
|
|
41
|
+
table.text :body, null: false
|
|
42
|
+
table.datetime :created_at, null: false
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def configure(backend: RubyLLM::Browser::FakeBackend.new)
|
|
47
|
+
RubyLLM::Browser::Rails.context_factory = lambda do |actor_id:, run_id:|
|
|
48
|
+
RubyLLM::Browser::Context.new(
|
|
49
|
+
actor_id: actor_id, run_id: run_id, backend: backend,
|
|
50
|
+
source_policy: RubyLLM::Browser::Policy.new(allowed_hosts: [ "docs.example.com" ], resolver: RESOLVER),
|
|
51
|
+
operation_store: RubyLLM::Browser::Rails.operation_store
|
|
52
|
+
)
|
|
53
|
+
end
|
|
54
|
+
RubyLLM::Browser::Rails.result_delivery = lambda do |operation:, result:|
|
|
55
|
+
store_result(operation: operation, payload: result.to_h)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Trusted server API. The worker supplies identity, never a model or request body.
|
|
60
|
+
def store_result(operation:, payload:)
|
|
61
|
+
body = RubyLLM::Browser::Redactor.canonical_json(payload)
|
|
62
|
+
ResultRecord.transaction do
|
|
63
|
+
record = ResultRecord.create_or_find_by!(operation_id: operation.id) do |row|
|
|
64
|
+
row.actor_id = operation.actor_id
|
|
65
|
+
row.run_id = operation.run_id
|
|
66
|
+
row.body = body
|
|
67
|
+
end
|
|
68
|
+
unless [ record.actor_id, record.run_id, record.body ] == [ operation.actor_id, operation.run_id, body ]
|
|
69
|
+
raise RubyLLM::Browser::InvalidInput, "conflicting result delivery"
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# In an HTTP host, derive actor/run from authenticated records and recheck access.
|
|
75
|
+
# Use the same response for a missing result and an inaccessible result.
|
|
76
|
+
def read_result(operation_id:, actor_id:, run_id:)
|
|
77
|
+
record = ResultRecord.find_by(operation_id: operation_id, actor_id: actor_id, run_id: run_id)
|
|
78
|
+
raise RubyLLM::Browser::NotAuthorized, "result unavailable" unless record
|
|
79
|
+
|
|
80
|
+
JSON.parse(record.body)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def worker(database, queue_file)
|
|
84
|
+
connect(database)
|
|
85
|
+
backend = RubyLLM::Browser::FakeBackend.new
|
|
86
|
+
configure(backend: backend)
|
|
87
|
+
ActiveJob::Base.execute(JSON.parse(File.read(queue_file)))
|
|
88
|
+
puts JSON.generate(starts: backend.call_count(:start), closes: backend.call_count(:end_session))
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def run_demo
|
|
92
|
+
Dir.mktmpdir("browser-results-") do |directory|
|
|
93
|
+
database = File.join(directory, "host.sqlite3")
|
|
94
|
+
queue_file = File.join(directory, "job.json")
|
|
95
|
+
connect(database)
|
|
96
|
+
ActiveJob::Base.queue_adapter = FileQueue.new(queue_file)
|
|
97
|
+
operation = RubyLLM::Browser::Rails::OperationJob.enqueue(
|
|
98
|
+
actor_id: "demo-actor", run_id: "demo-run", kind: :extract,
|
|
99
|
+
url: "https://docs.example.com/", schema: SCHEMA,
|
|
100
|
+
budget: RubyLLM::Browser::Budget.new, idempotency_key: "demo-result"
|
|
101
|
+
)
|
|
102
|
+
# capture3 waits for exit. Execute the serialized job twice in separate workers.
|
|
103
|
+
[ 1, 0 ].each do |expected_starts|
|
|
104
|
+
stdout, stderr, status = Open3.capture3(RbConfig.ruby, "-I", File.expand_path("../lib", __dir__),
|
|
105
|
+
__FILE__, "worker", database, queue_file)
|
|
106
|
+
raise "worker failed: #{stderr}" unless status.success?
|
|
107
|
+
unless JSON.parse(stdout) == { "starts" => expected_starts, "closes" => expected_starts }
|
|
108
|
+
raise "unexpected browser dispatch"
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
payload = read_result(operation_id: operation.id, actor_id: "demo-actor", run_id: "demo-run")
|
|
113
|
+
raise "lost extraction" unless payload.fetch("data") == { "title" => "Example" } && payload.fetch("status") == "ok"
|
|
114
|
+
raise "lost provenance" unless payload.fetch("locator").fetch("schema_digest") == RubyLLM::Browser::Redactor.digest(SCHEMA)
|
|
115
|
+
puts "RESULT_AFTER_WORKER_EXIT_PASS"
|
|
116
|
+
|
|
117
|
+
[ [ "other-actor", "demo-run" ], [ "demo-actor", "other-run" ] ].each do |actor, run|
|
|
118
|
+
begin
|
|
119
|
+
read_result(operation_id: operation.id, actor_id: actor, run_id: run)
|
|
120
|
+
raise "cross-scope result leak"
|
|
121
|
+
rescue RubyLLM::Browser::NotAuthorized
|
|
122
|
+
# Expected refusal, including after the worker has exited.
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
puts "SCOPED_READS_PASS"
|
|
126
|
+
2.times { store_result(operation: operation, payload: payload) }
|
|
127
|
+
raise "duplicate result rows" unless ResultRecord.count == 1
|
|
128
|
+
puts "IDEMPOTENT_WRITE_PASS"
|
|
129
|
+
ensure
|
|
130
|
+
ActiveRecord::Base.connection_pool.disconnect!
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
if $PROGRAM_NAME == __FILE__
|
|
136
|
+
if ARGV.first == "worker"
|
|
137
|
+
BrowserResultsExample.worker(*ARGV.drop(1))
|
|
138
|
+
else
|
|
139
|
+
BrowserResultsExample.run_demo
|
|
140
|
+
end
|
|
141
|
+
end
|
|
@@ -91,8 +91,14 @@ module RubyLLM
|
|
|
91
91
|
max_bytes: max_bytes)
|
|
92
92
|
|
|
93
93
|
# Keep unknown action/start results, even when cleanup later closes a session.
|
|
94
|
-
|
|
94
|
+
completed = store.fetch!(operation_id)
|
|
95
|
+
final = settled(completed)
|
|
95
96
|
return :cancelled if final == :cancelled
|
|
97
|
+
# Cancellation can still await cleanup. A reaper or newer worker can also
|
|
98
|
+
# fence this job while the browser call is in flight.
|
|
99
|
+
return :fenced_out unless completed.cancel_requested? || completed.fence == leased.fence
|
|
100
|
+
|
|
101
|
+
deliver(completed, result) unless completed.cancel_requested?
|
|
96
102
|
|
|
97
103
|
result.ok? ? (final || result.to_h) : result.to_h
|
|
98
104
|
rescue InvalidInput => e
|
|
@@ -106,15 +112,21 @@ module RubyLLM
|
|
|
106
112
|
# Reports the durable state if the host cancelled or another worker fenced this
|
|
107
113
|
# one while the operation was running; otherwise returns nil so the engine's own
|
|
108
114
|
# result is used.
|
|
109
|
-
def settled(
|
|
110
|
-
|
|
111
|
-
case final&.state
|
|
115
|
+
def settled(operation)
|
|
116
|
+
case operation.state
|
|
112
117
|
when :cancelled then :cancelled
|
|
113
118
|
when :cleanup_pending then :cleanup_pending
|
|
114
119
|
when :unknown then :unknown
|
|
115
120
|
end
|
|
116
121
|
end
|
|
117
122
|
|
|
123
|
+
def deliver(operation, result)
|
|
124
|
+
RubyLLM::Browser::Rails.result_delivery&.call(operation: operation, result: result)
|
|
125
|
+
rescue StandardError
|
|
126
|
+
# Queue logs may print chained exceptions containing host credentials.
|
|
127
|
+
raise ResultDeliveryError.new(operation: operation, result: result), cause: nil
|
|
128
|
+
end
|
|
129
|
+
|
|
118
130
|
def run(kind, operation, url:, instruction:, schema:, max_bytes:)
|
|
119
131
|
context = build_context(operation)
|
|
120
132
|
unless context.is_a?(Context) && context.operation_store.equal?(store)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyLLM
|
|
4
|
+
module Browser
|
|
5
|
+
module Rails
|
|
6
|
+
# The host callback failed after browser execution and cleanup were attempted.
|
|
7
|
+
# Retain these values in host storage to retry delivery without rerunning a job.
|
|
8
|
+
# These are in-process objects, not a durable queue or automatic retry policy.
|
|
9
|
+
class ResultDeliveryError < Error
|
|
10
|
+
CODE = "RESULT_DELIVERY_FAILED"
|
|
11
|
+
|
|
12
|
+
attr_reader :operation, :result
|
|
13
|
+
|
|
14
|
+
def initialize(operation:, result:)
|
|
15
|
+
@operation = operation
|
|
16
|
+
@result = result
|
|
17
|
+
super("host result delivery failed; retry delivery without repeating browser work")
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -27,6 +27,7 @@ require_relative "rails/operation_record"
|
|
|
27
27
|
require_relative "rails/approval_record"
|
|
28
28
|
require_relative "rails/active_record_operation_store"
|
|
29
29
|
require_relative "rails/active_record_approval_store"
|
|
30
|
+
require_relative "rails/result_delivery_error"
|
|
30
31
|
require_relative "rails/operation_job"
|
|
31
32
|
require_relative "rails/reaper"
|
|
32
33
|
|
|
@@ -41,6 +42,20 @@ module RubyLLM
|
|
|
41
42
|
# ->(actor_id:, run_id:) { RubyLLM::Browser::Context }, supplied by the host.
|
|
42
43
|
attr_accessor :context_factory
|
|
43
44
|
|
|
45
|
+
# Optional host callback: ->(operation:, result:) { persist_result }.
|
|
46
|
+
# Runs synchronously after execution and cleanup, before the job returns.
|
|
47
|
+
# Raise to report failure; return values are ignored. Duplicate jobs do not
|
|
48
|
+
# redeliver. The host owns persistence, idempotency, access and recovery.
|
|
49
|
+
attr_reader :result_delivery
|
|
50
|
+
|
|
51
|
+
def result_delivery=(callback)
|
|
52
|
+
unless callback.nil? || callback.respond_to?(:call)
|
|
53
|
+
raise InvalidInput, "result_delivery must be callable or nil"
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
@result_delivery = callback
|
|
57
|
+
end
|
|
58
|
+
|
|
44
59
|
# Returns the current UTC time used by the adapter.
|
|
45
60
|
def clock
|
|
46
61
|
@clock ||= -> { Time.now.utc }
|
|
@@ -68,6 +83,7 @@ module RubyLLM
|
|
|
68
83
|
# Restores process defaults. Primarily a test helper.
|
|
69
84
|
def reset!
|
|
70
85
|
@context_factory = nil
|
|
86
|
+
@result_delivery = nil
|
|
71
87
|
@clock = nil
|
|
72
88
|
@job_queue = nil
|
|
73
89
|
@operation_store = nil
|
|
@@ -27,15 +27,15 @@ module RubyLLM
|
|
|
27
27
|
def initialize(status:, operation_id: nil, final_url: nil, captured_at: nil, locator: nil,
|
|
28
28
|
digest: nil, data: nil, truncated: false, error_code: nil, error_message: nil)
|
|
29
29
|
@status = normalize_status(status)
|
|
30
|
-
@operation_id = operation_id
|
|
30
|
+
@operation_id = Immutable.copy(operation_id)
|
|
31
31
|
@final_url = Immutable.copy(final_url)
|
|
32
32
|
@captured_at = Immutable.copy(captured_at)
|
|
33
33
|
@locator = Immutable.copy(locator)
|
|
34
34
|
@digest = Immutable.copy(digest)
|
|
35
35
|
@data = Immutable.copy(data)
|
|
36
36
|
@truncated = truncated ? true : false
|
|
37
|
-
@error_code = error_code
|
|
38
|
-
@error_message = error_message
|
|
37
|
+
@error_code = Immutable.copy(error_code)
|
|
38
|
+
@error_message = Immutable.copy(error_message)
|
|
39
39
|
freeze
|
|
40
40
|
end
|
|
41
41
|
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ruby_llm-browser
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Andy Wang
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-10-
|
|
11
|
+
date: 2026-10-19 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: ruby_llm
|
|
@@ -174,6 +174,7 @@ files:
|
|
|
174
174
|
- docs/_advanced/development-and-testing.md
|
|
175
175
|
- docs/_advanced/live-backend-qualification.md
|
|
176
176
|
- docs/_advanced/rails-integration.md
|
|
177
|
+
- docs/_advanced/result-delivery.md
|
|
177
178
|
- docs/_config.yml
|
|
178
179
|
- docs/_core_features/diagnostics.md
|
|
179
180
|
- docs/_core_features/inspect-extract.md
|
|
@@ -186,13 +187,14 @@ files:
|
|
|
186
187
|
- docs/_getting_started/configuration.md
|
|
187
188
|
- docs/_getting_started/getting-started.md
|
|
188
189
|
- docs/_getting_started/overview.md
|
|
189
|
-
- docs/_getting_started/whats-new-in-0-
|
|
190
|
+
- docs/_getting_started/whats-new-in-0-3.md
|
|
190
191
|
- docs/_plugins/versioned_docs.rb
|
|
191
192
|
- docs/_reference/api-and-configuration.md
|
|
192
193
|
- docs/_reference/api-reference.md
|
|
193
194
|
- docs/_reference/release-history.md
|
|
194
195
|
- docs/_reference/roadmap.md
|
|
195
196
|
- docs/_reference/upgrading.md
|
|
197
|
+
- docs/_reference/whats-new-in-0-2.md
|
|
196
198
|
- docs/bin/build-api.sh
|
|
197
199
|
- docs/bin/build.sh
|
|
198
200
|
- docs/bin/export-api-markdown.rb
|
|
@@ -204,6 +206,7 @@ files:
|
|
|
204
206
|
- docs/index.md
|
|
205
207
|
- examples/plain_ruby.rb
|
|
206
208
|
- examples/rails_adapter.rb
|
|
209
|
+
- examples/rails_results.rb
|
|
207
210
|
- lib/generators/ruby_llm/browser/install_generator.rb
|
|
208
211
|
- lib/generators/ruby_llm/browser/templates/create_ruby_llm_browser_tables.rb.tt
|
|
209
212
|
- lib/ruby_llm/browser.rb
|
|
@@ -232,6 +235,7 @@ files:
|
|
|
232
235
|
- lib/ruby_llm/browser/rails/operation_job.rb
|
|
233
236
|
- lib/ruby_llm/browser/rails/operation_record.rb
|
|
234
237
|
- lib/ruby_llm/browser/rails/reaper.rb
|
|
238
|
+
- lib/ruby_llm/browser/rails/result_delivery_error.rb
|
|
235
239
|
- lib/ruby_llm/browser/rails/schema.rb
|
|
236
240
|
- lib/ruby_llm/browser/redactor.rb
|
|
237
241
|
- lib/ruby_llm/browser/result.rb
|