ruby_llm-ag_ui 0.1.0 → 0.2.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/CONTRIBUTING.md +27 -3
- data/README.md +39 -115
- data/docs/_advanced/client-example.md +96 -0
- data/docs/_advanced/security-and-deployment.md +1 -1
- data/docs/_core_features/reconnects.md +6 -0
- data/docs/_core_features/streaming-runs.md +1 -0
- data/docs/_core_features/tool-progress.md +90 -0
- data/docs/_getting_started/configuration.md +2 -1
- data/docs/_getting_started/whats-new-in-0-2.md +53 -0
- data/docs/_reference/compatibility.md +39 -13
- data/docs/_reference/protocol-coverage.md +6 -5
- data/docs/_reference/release-history.md +3 -2
- data/docs/_reference/releasing.md +15 -0
- data/docs/_reference/upgrading.md +20 -15
- data/docs/{_getting_started → _reference}/whats-new-in-0-1.md +2 -1
- data/lib/ruby_llm/ag_ui/configuration.rb +7 -1
- data/lib/ruby_llm/ag_ui/coverage.rb +2 -2
- data/lib/ruby_llm/ag_ui/mapper.rb +20 -1
- data/lib/ruby_llm/ag_ui/redaction.rb +1 -0
- data/lib/ruby_llm/ag_ui/resume.rb +6 -2
- data/lib/ruby_llm/ag_ui/run.rb +22 -11
- data/lib/ruby_llm/ag_ui/store/base.rb +1 -1
- data/lib/ruby_llm/ag_ui/version.rb +1 -1
- metadata +6 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d8529d394d1ef5d52bc73952e047ee5303634af7aa7738cc894acc8dde378f8b
|
|
4
|
+
data.tar.gz: ed676da00af762d17fbf6d1a743b995f03d1e7744aba9afb985b95c20bedf93f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 34df865e3f9dd71cbf573ad7a89d19259d16931685cb153df6aab6e989d92be68407f6207e654fc44f615b625a840a90a44e6c4e725e5781e976e253d6744457
|
|
7
|
+
data.tar.gz: 40c5c7fd607e2ce6ccd54e53e6e6e8c2482bf7645d3155e4cdf0eedb95c58f3337bb481650a44c902863629a52bc70cd03dd4224ecaad2ab0546a947f633a0e1
|
data/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,25 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.2.0] - 2026-10-19
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- Opt-in tool progress as redacted `ACTIVITY_SNAPSHOT` events, with stable
|
|
14
|
+
per-call identity and per-run or global configuration.
|
|
15
|
+
- Installed-artifact host checks for generated migrations, RubyLLM model setup,
|
|
16
|
+
actor-scoped chat persistence, and Ruby/Rails run paths.
|
|
17
|
+
- A pinned official AG-UI client demo for Ruby and Rails, with browser workflow
|
|
18
|
+
tests, mobile screenshots, and schema validation of consumed events.
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- Reconnect an exact completed resume request without redeeming its approval
|
|
23
|
+
again. Changed input still conflicts, and a new run cannot reuse an answered
|
|
24
|
+
interrupt.
|
|
25
|
+
- Supply the required actor scope in the opt-in live-provider check and exercise
|
|
26
|
+
its run setup offline, before provider verification.
|
|
27
|
+
|
|
9
28
|
## [0.1.0] - 2026-10-18
|
|
10
29
|
|
|
11
30
|
### Added
|
data/CONTRIBUTING.md
CHANGED
|
@@ -7,8 +7,7 @@ with tests are the easiest to accept.
|
|
|
7
7
|
|
|
8
8
|
```bash
|
|
9
9
|
bundle install
|
|
10
|
-
|
|
11
|
-
bundle exec rubocop
|
|
10
|
+
bin/check
|
|
12
11
|
```
|
|
13
12
|
|
|
14
13
|
## Ground rules
|
|
@@ -33,15 +32,40 @@ bundle exec rubocop
|
|
|
33
32
|
|
|
34
33
|
## Tests
|
|
35
34
|
|
|
35
|
+
Run focused specs first, then `bin/check`. For client or demo changes, also run
|
|
36
|
+
`bin/check-browser` and inspect its screenshots. The browser suite uses synthetic
|
|
37
|
+
fixtures, a real AG-UI client, and a temporary local server. Keep artifacts in
|
|
38
|
+
ignored `tmp/`. Use `bin/check --package --browser` for the full local check,
|
|
39
|
+
or `bin/check-browser --grep "rails: approval"` for one browser workflow.
|
|
40
|
+
The package probe installs the artifact outside the checkout using available
|
|
41
|
+
development dependencies; `bin/verify-package` separately tests clean dependency
|
|
42
|
+
resolution. Shut down all task-owned processes when the check finishes.
|
|
43
|
+
|
|
36
44
|
Every behavior change needs a test, and lifecycle, identity and security changes
|
|
37
45
|
need their negative case too. The acceptance IDs (FR-01..FR-08) are referenced
|
|
38
46
|
in spec descriptions. Offline CI uses fakes; live provider tests are opt-in and
|
|
39
47
|
never required:
|
|
40
48
|
|
|
41
49
|
```bash
|
|
42
|
-
RUBY_LLM_LIVE=1
|
|
50
|
+
RUBY_LLM_LIVE=1 bundle exec rspec spec/live
|
|
43
51
|
```
|
|
44
52
|
|
|
53
|
+
Load provider credentials through your environment or an untracked env file,
|
|
54
|
+
never through command-line arguments. See the
|
|
55
|
+
[compatibility guide](https://rubyllm-ag-ui.wangyaodi.com/compatibility/) for
|
|
56
|
+
what each check establishes.
|
|
57
|
+
|
|
58
|
+
## Documentation
|
|
59
|
+
|
|
60
|
+
- Keep README to the project introduction, installation, one example, and links.
|
|
61
|
+
- Keep usage, configuration, compatibility, and API details in `docs/`, the
|
|
62
|
+
source of the documentation site. Link to a guide instead of copying it.
|
|
63
|
+
- Keep contributor rules here, shipped changes in [CHANGELOG.md](CHANGELOG.md),
|
|
64
|
+
and proposed work in [ROADMAP.md](ROADMAP.md).
|
|
65
|
+
- Change a public API's guide in the same commit. Follow [AGENTS.md](AGENTS.md)
|
|
66
|
+
and build with `docs/bin/build.sh` before submitting documentation changes.
|
|
67
|
+
- Keep example READMEs specific to running or adapting that example.
|
|
68
|
+
|
|
45
69
|
## Commit style
|
|
46
70
|
|
|
47
71
|
Conventional Commits (`feat:`, `fix:`, `docs:`, `test:`, `chore:`, `refactor:`),
|
data/README.md
CHANGED
|
@@ -2,145 +2,69 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://github.com/yorzi/ruby_llm-ag-ui/actions/workflows/ci.yml)
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
schema-valid AG-UI events, turns `requires_approval` pauses into interrupt
|
|
10
|
-
outcomes with host-authorized resume, reports `Chat#cancel` as the cancelled
|
|
11
|
-
outcome, and writes the stream as Server-Sent Events. A pure-Ruby core comes
|
|
12
|
-
with an optional Rails adapter.
|
|
13
|
-
|
|
14
|
-
> **Unofficial community project.** This gem is not affiliated with, endorsed
|
|
15
|
-
> by, or maintained by the RubyLLM maintainers or the AG-UI protocol
|
|
16
|
-
> maintainers. `RubyLLM::` and "AG-UI" appear here because this gem extends
|
|
17
|
-
> those projects, not because it speaks for them.
|
|
18
|
-
|
|
19
|
-
## Why
|
|
20
|
-
|
|
21
|
-
If your agent already runs on RubyLLM and you want an AG-UI client (CopilotKit,
|
|
22
|
-
`@ag-ui/client`, or another AG-UI consumer) in front of it, you currently have
|
|
23
|
-
to hand-map RubyLLM callbacks, approval pauses and cancellation onto the AG-UI
|
|
24
|
-
run lifecycle, and you have to parse and authorize `RunAgentInput` yourself:
|
|
25
|
-
the community Ruby SDK builds outbound events but has no inbound parser. The
|
|
26
|
-
usual results are wrong terminal events, duplicate runs on retry, approvals that
|
|
27
|
-
are not bound to the call they approve, and cancellations reported as success.
|
|
28
|
-
This gem removes that mapping work.
|
|
29
|
-
|
|
30
|
-
## What it does and does not do
|
|
31
|
-
|
|
32
|
-
It owns the RubyLLM → AG-UI mapping, input parsing, event ordering, run identity
|
|
33
|
-
and terminal correctness. It is **not** an AG-UI core SDK, a schema fork, a chat
|
|
34
|
-
UI, a CopilotKit runtime clone, or a second agent framework. RubyLLM owns the
|
|
35
|
-
agent loop, tool execution, approval pauses and cancellation checkpoints. Your
|
|
36
|
-
application owns identity, authorization, which tools and model a run may use,
|
|
37
|
-
and persistence.
|
|
38
|
-
|
|
39
|
-
## Status
|
|
40
|
-
|
|
41
|
-
Version `0.1.0` is the first release. The pure-Ruby core, run store, approval/resume, cancel
|
|
42
|
-
handling and the Rails adapter are implemented and covered by an offline test
|
|
43
|
-
suite. Compatibility claims for real AG-UI clients and live providers are
|
|
44
|
-
tracked in the [compatibility guide](https://rubyllm-ag-ui.wangyaodi.com/compatibility/) and are
|
|
45
|
-
**not** asserted until recorded there.
|
|
46
|
-
|
|
47
|
-
Read the [documentation site](https://rubyllm-ag-ui.wangyaodi.com/) for the
|
|
48
|
-
guides, protocol coverage, compatibility matrix and security model. Its source
|
|
49
|
-
is in [docs/](https://github.com/yorzi/ruby_llm-ag-ui/tree/main/docs).
|
|
50
|
-
|
|
51
|
-
## Installation
|
|
5
|
+
Connect [RubyLLM](https://rubyllm.com) chats to [AG-UI](https://docs.ag-ui.com)
|
|
6
|
+
clients over Server-Sent Events. The gem handles streaming text, tool calls, approval and resume,
|
|
7
|
+
cancellation outcomes, and run identity. Use it from plain Ruby or with the
|
|
8
|
+
optional Rails adapter.
|
|
52
9
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
```
|
|
10
|
+
Your application provides authentication, tool permissions, chat history, and
|
|
11
|
+
the model. This is an unofficial community project, independent of the
|
|
12
|
+
RubyLLM and AG-UI maintainers.
|
|
57
13
|
|
|
58
|
-
|
|
59
|
-
|
|
14
|
+
**[Documentation](https://rubyllm-ag-ui.wangyaodi.com/)** ·
|
|
15
|
+
[Getting started](https://rubyllm-ag-ui.wangyaodi.com/getting-started/) ·
|
|
16
|
+
[Rails guide](https://rubyllm-ag-ui.wangyaodi.com/rails-integration/) ·
|
|
17
|
+
[Compatibility](https://rubyllm-ag-ui.wangyaodi.com/compatibility/)
|
|
18
|
+
|
|
19
|
+
## Install
|
|
20
|
+
|
|
21
|
+
```ruby
|
|
22
|
+
gem 'ruby_llm-ag_ui', '~> 0.2.0'
|
|
60
23
|
```
|
|
61
24
|
|
|
62
|
-
Requires Ruby >= 3.2, RubyLLM >= 2.1 and `ag-ui-protocol`
|
|
63
|
-
|
|
25
|
+
Requires Ruby >= 3.2, RubyLLM >= 2.1 and < 3.0, and `ag-ui-protocol` 0.2.x.
|
|
26
|
+
Run `bundle install` after adding the gem.
|
|
64
27
|
|
|
65
|
-
##
|
|
28
|
+
## Stream a run
|
|
66
29
|
|
|
67
|
-
|
|
68
|
-
stream
|
|
30
|
+
Pass a chat authorized for the current actor, a shared run store, and an
|
|
31
|
+
IO-like response stream from your application:
|
|
69
32
|
|
|
70
33
|
```ruby
|
|
71
|
-
require 'ruby_llm'
|
|
72
34
|
require 'ruby_llm/ag_ui'
|
|
73
35
|
|
|
74
|
-
input = RubyLLM::AgUI::Input.parse(request_body)
|
|
75
|
-
chat
|
|
36
|
+
input = RubyLLM::AgUI::Input.parse(request_body)
|
|
37
|
+
chat = authorized_chat_for(current_user, input.thread_id)
|
|
76
38
|
|
|
77
39
|
RubyLLM::AgUI::Run.new(
|
|
78
40
|
chat: chat,
|
|
79
41
|
input: input,
|
|
80
|
-
scope: "
|
|
42
|
+
scope: "actor:#{current_user.id}",
|
|
81
43
|
store: run_store
|
|
82
|
-
).stream(to:
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
`scope` is what the run is reserved under: it must be a non-empty String the host
|
|
86
|
-
builds from authenticated identity, never anything from the request body.
|
|
87
|
-
|
|
88
|
-
`examples/pure_ruby/app.rb` is a runnable Rack endpoint that streams a complete
|
|
89
|
-
run (a tool call, an approval pause and the run that answers it) with no
|
|
90
|
-
provider and no credentials (`rake example`, or
|
|
91
|
-
`rackup examples/pure_ruby/config.ru`). Set `RUBY_LLM_AG_UI_EXAMPLE_MODEL` to
|
|
92
|
-
drive a real `RubyLLM::Chat` through the same code.
|
|
93
|
-
|
|
94
|
-
## Rails
|
|
95
|
-
|
|
96
|
-
The optional adapter provides a controller concern, a durable run store keyed
|
|
97
|
-
by the actor's scope, and reconnect snapshots. It loads only when
|
|
98
|
-
`Rails::Railtie` is already defined.
|
|
99
|
-
|
|
100
|
-
```ruby
|
|
101
|
-
class AgentRunsController < ApplicationController
|
|
102
|
-
include RubyLLM::AgUI::Rails::Streaming
|
|
103
|
-
|
|
104
|
-
def create
|
|
105
|
-
stream_ag_ui_run { |input| current_user.chats.find_or_create_by!(ag_ui_thread_id: input.thread_id) }
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
def ag_ui_scope
|
|
109
|
-
"actor:#{current_user.id}"
|
|
110
|
-
end
|
|
111
|
-
end
|
|
44
|
+
).stream(to: response_stream)
|
|
112
45
|
```
|
|
113
46
|
|
|
114
|
-
|
|
47
|
+
Build `scope` from authenticated identity. See the
|
|
48
|
+
[streaming guide](https://rubyllm-ag-ui.wangyaodi.com/streaming-runs/) for setup
|
|
49
|
+
and the [examples](examples/) for integration code.
|
|
115
50
|
|
|
116
51
|
## Development
|
|
117
52
|
|
|
118
|
-
```
|
|
53
|
+
```sh
|
|
119
54
|
bundle install
|
|
120
55
|
bundle exec rspec
|
|
121
56
|
bundle exec rubocop
|
|
122
|
-
rake example
|
|
57
|
+
bundle exec rake example # offline approval and resume demo, no API key needed
|
|
123
58
|
```
|
|
124
59
|
|
|
125
|
-
The suite is offline
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
## Documentation
|
|
133
|
-
|
|
134
|
-
- [Architecture](https://rubyllm-ag-ui.wangyaodi.com/overview/) - the components, run, and invariants.
|
|
135
|
-
- [Protocol coverage](https://rubyllm-ag-ui.wangyaodi.com/protocol-coverage/) - all 31 AG-UI 1.0 event types
|
|
136
|
-
and what this gem does with each.
|
|
137
|
-
- [Compatibility](https://rubyllm-ag-ui.wangyaodi.com/compatibility/) - what this repository verifies.
|
|
138
|
-
- [Rails](https://rubyllm-ag-ui.wangyaodi.com/rails-integration/) - the controller concern, store, and transport.
|
|
139
|
-
- [Security](https://rubyllm-ag-ui.wangyaodi.com/security-and-deployment/) - the threat model and deployment checklist.
|
|
140
|
-
|
|
141
|
-
## License
|
|
60
|
+
The default suite is offline. Real client and provider verification is tracked
|
|
61
|
+
in the [compatibility guide](https://rubyllm-ag-ui.wangyaodi.com/compatibility/).
|
|
62
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for development and documentation rules,
|
|
63
|
+
[ROADMAP.md](ROADMAP.md) for the proposed path to 1.0, and
|
|
64
|
+
[CHANGELOG.md](CHANGELOG.md) for shipped changes.
|
|
142
65
|
|
|
143
|
-
|
|
66
|
+
## License and security
|
|
144
67
|
|
|
145
|
-
|
|
146
|
-
|
|
68
|
+
[MIT](LICENSE.txt). See the
|
|
69
|
+
[third-party notices](https://rubyllm-ag-ui.wangyaodi.com/third-party-notices/)
|
|
70
|
+
and report vulnerabilities through [SECURITY.md](SECURITY.md).
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: default
|
|
3
|
+
title: Client Example
|
|
4
|
+
nav_order: 3
|
|
5
|
+
description: Run a real AG-UI browser client against offline Ruby and Rails endpoints.
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# {{ page.title }}
|
|
9
|
+
|
|
10
|
+
{{ page.description }}
|
|
11
|
+
{: .fs-6 .fw-300 }
|
|
12
|
+
|
|
13
|
+
After reading this guide, you will know:
|
|
14
|
+
|
|
15
|
+
* How to run the maintained offline client example.
|
|
16
|
+
* Which interactions it verifies with the official client.
|
|
17
|
+
* Which responsibilities remain with a real host application.
|
|
18
|
+
|
|
19
|
+
## Run the Example
|
|
20
|
+
|
|
21
|
+
From the repository, install the development dependencies and build the client:
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
bundle install
|
|
25
|
+
npm ci
|
|
26
|
+
npm run build:demo
|
|
27
|
+
bundle exec ruby examples/client/server.rb
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Open `http://127.0.0.1:3199`. Stop the server with `Ctrl-C` when finished.
|
|
31
|
+
The host binds loopback and serves only the demo HTML and its locally bundled
|
|
32
|
+
JavaScript. It creates and removes a temporary SQLite database on each run.
|
|
33
|
+
No CDN, provider account, or API key is needed.
|
|
34
|
+
|
|
35
|
+
Choose **Plain Ruby** or **Rails + SQLite**, then run a turn. The official
|
|
36
|
+
`@ag-ui/client` package consumes the SSE stream. The scripted host retains one
|
|
37
|
+
chat per fixture actor and thread, so approval resumes the original tool call
|
|
38
|
+
and later turns use the same conversation.
|
|
39
|
+
|
|
40
|
+
## Try the Interactions
|
|
41
|
+
|
|
42
|
+
The tool scenario pauses for approval. Approve it to run a synthetic order
|
|
43
|
+
cancellation once, or deny it to leave the order unchanged. Tool results stay
|
|
44
|
+
redacted by default. Approved tools also publish synthetic progress counts;
|
|
45
|
+
the host policy withholds progress messages. The text, sanitized failure, and cancelled scenarios show
|
|
46
|
+
the other run outcomes.
|
|
47
|
+
|
|
48
|
+
Use **Reconnect completed run** after a successful turn. The client sends the
|
|
49
|
+
original input and receives the recorded outcome and snapshot; no new model
|
|
50
|
+
turn or text delta is produced.
|
|
51
|
+
|
|
52
|
+
## Check in a Browser
|
|
53
|
+
|
|
54
|
+
```sh
|
|
55
|
+
bin/check-browser
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
This runs Playwright with Chrome or its installed Chromium. On Linux, install
|
|
59
|
+
the browser first with `npx playwright install --with-deps chromium`. Set
|
|
60
|
+
`CHROME_BIN` to select another local Chrome executable.
|
|
61
|
+
|
|
62
|
+
The check starts its own server, runs desktop and 390px mobile scenarios, saves
|
|
63
|
+
screenshots and consumed events under ignored `tmp/browser/`, and stops the
|
|
64
|
+
server and browser. It validates the captured events against the pinned JSON
|
|
65
|
+
schema. Failures retain traces and screenshots. Inspect those artifacts before
|
|
66
|
+
retrying; a screenshot alone is not an assertion.
|
|
67
|
+
|
|
68
|
+
## Verify an Installed Gem
|
|
69
|
+
|
|
70
|
+
```sh
|
|
71
|
+
bin/check --package
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
The package probe builds a gem, installs that artifact outside the checkout,
|
|
75
|
+
and checks both run paths, generated migration, approval, progress, and
|
|
76
|
+
reconnect. It also generates RubyLLM's models and tables, applies the Rails
|
|
77
|
+
example migrations in order, and checks actor-scoped chat persistence. It uses
|
|
78
|
+
available development dependencies. Use `bin/verify-package --rails --audit`
|
|
79
|
+
after `bundle exec rake build` to resolve dependencies in a clean gem home.
|
|
80
|
+
|
|
81
|
+
## Adapt to a Real Host
|
|
82
|
+
|
|
83
|
+
The example's actor header is a synthetic identity selector. It does not
|
|
84
|
+
provide authentication, CSRF protection, or durable chat history. The run store
|
|
85
|
+
uses SQLite, but the scripted chat objects live in one process and disappear
|
|
86
|
+
when that process stops. A real host authorizes the thread and the next user
|
|
87
|
+
turn, persists its RubyLLM chat, and rechecks tool permission before execution.
|
|
88
|
+
|
|
89
|
+
The demo and browser suite make no live provider call. Read
|
|
90
|
+
[Compatibility]({% link _reference/compatibility.md %}) for the exact versions
|
|
91
|
+
and evidence boundaries.
|
|
92
|
+
|
|
93
|
+
## Next Steps
|
|
94
|
+
|
|
95
|
+
* [Rails Integration]({% link _advanced/rails-integration.md %}) - install the adapter in your application.
|
|
96
|
+
* [Approval and Resume]({% link _core_features/approval-and-resume.md %}) - inspect the resume contract.
|
|
@@ -52,7 +52,7 @@ responsibility.
|
|
|
52
52
|
- **Redeclare a run id.** The same identity with a *different* input is refused
|
|
53
53
|
as `RunConflictError` (409 in the Rails adapter) rather than executed
|
|
54
54
|
("refuses the same identity with a changed input as a conflict").
|
|
55
|
-
- **Run the same tool twice.** A resume is recorded once; a
|
|
55
|
+
- **Run the same tool twice.** A resume is recorded once; a new run that answers the
|
|
56
56
|
same interrupt is refused without executing the tool ("refuses a decision
|
|
57
57
|
another resume already recorded, without executing the tool").
|
|
58
58
|
- **Widen what an approval covers.** An interrupt pins the tool call and the
|
|
@@ -29,6 +29,12 @@ The store reserves a run before any provider work. It keys the record by authent
|
|
|
29
29
|
|
|
30
30
|
Reconnect does not replay text or tool deltas. Replaying deltas could display text twice or repeat a side effect. The host can publish a state snapshot for a finished run, but the gem does not reconstruct conversation history.
|
|
31
31
|
|
|
32
|
+
The same rule applies to a completed approval-resume run. Resend its exact
|
|
33
|
+
input, including `resume`, with the original run id to receive the recorded
|
|
34
|
+
outcome without applying the decision again. A new run id that tries to answer
|
|
35
|
+
an already answered interrupt is refused. Changing the input under an existing
|
|
36
|
+
run id is a conflict.
|
|
37
|
+
|
|
32
38
|
## Choose a Store
|
|
33
39
|
|
|
34
40
|
`MemoryStore` is suitable for tests and a single process. Its records disappear on restart and are not shared between workers. The Rails adapter's `ActiveRecordStore` persists records and redeems interrupts under a database lock.
|
|
@@ -45,5 +45,6 @@ The run record reaches a terminal state before the terminal event is written. If
|
|
|
45
45
|
## Next Steps
|
|
46
46
|
|
|
47
47
|
* [Validating Input]({% link _core_features/input-validation.md %}) - parse untrusted request data.
|
|
48
|
+
* [Tool Progress]({% link _core_features/tool-progress.md %}) - publish local tool updates.
|
|
48
49
|
* [Approval and Resume]({% link _core_features/approval-and-resume.md %}) - answer an interrupt on a new run.
|
|
49
50
|
* [Reconnects and Run Identity]({% link _core_features/reconnects.md %}) - serve a completed run again.
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: default
|
|
3
|
+
title: Tool Progress
|
|
4
|
+
nav_order: 5
|
|
5
|
+
description: Publish local tool progress as redacted AG-UI activity snapshots.
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# {{ page.title }}
|
|
9
|
+
|
|
10
|
+
{{ page.description }}
|
|
11
|
+
{: .fs-6 .fw-300 }
|
|
12
|
+
|
|
13
|
+
After reading this guide, you will know:
|
|
14
|
+
|
|
15
|
+
* How to enable tool progress globally or for one run.
|
|
16
|
+
* How progress snapshots identify a tool call and replace earlier values.
|
|
17
|
+
* How to choose which progress values reach the client.
|
|
18
|
+
|
|
19
|
+
## Enable Progress
|
|
20
|
+
|
|
21
|
+
Long tools can report their work before the result arrives. Enable progress
|
|
22
|
+
when your client handles AG-UI activity messages:
|
|
23
|
+
|
|
24
|
+
```ruby
|
|
25
|
+
RubyLLM::AgUI.configure do |config|
|
|
26
|
+
config.emit_tool_progress = true
|
|
27
|
+
end
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
The default is `false`. You can override it on one run with
|
|
31
|
+
`Run.new(chat: chat, input: input, scope: scope, store: store,
|
|
32
|
+
emit_tool_progress: true)`. Set the per-run option to `false` to disable a global
|
|
33
|
+
opt-in. The Rails concern uses the global setting.
|
|
34
|
+
|
|
35
|
+
The gem listens to RubyLLM's `after_tool_progress(tool_call, progress)` callback.
|
|
36
|
+
Your RubyLLM tool reports progress through its own `progress` method. The gem
|
|
37
|
+
publishes only values that the tool reports; it does not estimate a percentage
|
|
38
|
+
or make up a total.
|
|
39
|
+
|
|
40
|
+
## Choose a Redaction Policy
|
|
41
|
+
|
|
42
|
+
Progress can include private messages. Its values pass through the configured
|
|
43
|
+
redactor with kind `:tool_progress`. The default replaces each reported value
|
|
44
|
+
with `[redacted]`, including numeric values. The tool call identifier remains
|
|
45
|
+
available so the client can associate the activity with the existing call.
|
|
46
|
+
|
|
47
|
+
For example, a host can publish counts while withholding messages:
|
|
48
|
+
|
|
49
|
+
```ruby
|
|
50
|
+
RubyLLM::AgUI.configure do |config|
|
|
51
|
+
config.emit_tool_progress = true
|
|
52
|
+
config.redact = lambda do |kind, payload|
|
|
53
|
+
if kind == :tool_progress
|
|
54
|
+
payload.slice('value', 'total')
|
|
55
|
+
else
|
|
56
|
+
RubyLLM::AgUI::Redaction.default(kind, payload)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Use `Redaction.method(:none)` only for host-approved public data. The same
|
|
63
|
+
policy still protects tool results with kind `:tool_result`.
|
|
64
|
+
|
|
65
|
+
## Read an Activity Snapshot
|
|
66
|
+
|
|
67
|
+
With counts approved by the host, a tool update can produce:
|
|
68
|
+
|
|
69
|
+
```json
|
|
70
|
+
{"type":"ACTIVITY_SNAPSHOT","messageId":"run-1:progress:call-1","activityType":"TOOL_PROGRESS","content":{"toolCallId":"call-1","progress":{"value":1,"total":3}},"replace":true}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Each tool call has one activity id per run. Later updates replace that
|
|
74
|
+
activity's content. Unknown fields are absent: a message-only update has no
|
|
75
|
+
`value` or `total`. Empty progress updates produce no event.
|
|
76
|
+
|
|
77
|
+
Snapshots do not open another tool call, change an approval, or determine the
|
|
78
|
+
run outcome. Updates for a tool whose result has arrived are ignored, as are
|
|
79
|
+
callbacks after the run closes. Concurrent callbacks and run finalization use
|
|
80
|
+
the same lock so progress cannot be written after the terminal event.
|
|
81
|
+
|
|
82
|
+
Progress is not retained for reconnect. A completed-run reconnect serves its
|
|
83
|
+
recorded outcome and optional state snapshot, without replaying activity,
|
|
84
|
+
text, or tool deltas. No `ACTIVITY_DELTA` events are produced.
|
|
85
|
+
|
|
86
|
+
## Next Steps
|
|
87
|
+
|
|
88
|
+
* [Client Example]({% link _advanced/client-example.md %}) - try a real client with offline tools.
|
|
89
|
+
* [Configuration]({% link _getting_started/configuration.md %}) - choose run defaults.
|
|
90
|
+
* [Protocol Coverage]({% link _reference/protocol-coverage.md %}) - inspect supported events.
|
|
@@ -32,12 +32,13 @@ p RubyLLM::AgUI.config.to_h[:redact]
|
|
|
32
32
|
|
|
33
33
|
## Settings
|
|
34
34
|
|
|
35
|
-
The global configuration supplies defaults. `Run.new` can override the store, logger, redaction policy, clock,
|
|
35
|
+
The global configuration supplies defaults. `Run.new` can override the store, logger, redaction policy, clock, interrupt expiry, and tool progress for one run. `MemoryStore` is process-local, so use a durable store when runs can move between workers.
|
|
36
36
|
|
|
37
37
|
| Setting | Default | Purpose |
|
|
38
38
|
| --- | --- | --- |
|
|
39
39
|
| `run_store` | `nil` | Store run records and interrupts. A run needs a store, supplied here or to `Run.new`. |
|
|
40
40
|
| `redact` | `Redaction.default` | Transform tool results before they reach the stream. |
|
|
41
|
+
| `emit_tool_progress` | `false` | Publish tool progress as redacted activity snapshots. |
|
|
41
42
|
| `logger` | `nil` | Receive host-side diagnostics without prompts or raw tool payloads. |
|
|
42
43
|
| `protocol_version` | `"1.0"` | Value reported by `RUN_STARTED`. |
|
|
43
44
|
| `max_input_bytes` | `256_000` | Maximum request body size. |
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: default
|
|
3
|
+
title: "What's New in 0.2"
|
|
4
|
+
nav_order: 0
|
|
5
|
+
description: Verify real AG-UI client workflows and opt in to redacted tool progress.
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# {{ page.title }}
|
|
9
|
+
|
|
10
|
+
{{ page.description }}
|
|
11
|
+
{: .fs-6 .fw-300 }
|
|
12
|
+
|
|
13
|
+
After reading this guide, you will know:
|
|
14
|
+
|
|
15
|
+
* How to publish tool progress without exposing private messages.
|
|
16
|
+
* How to run the maintained browser client example.
|
|
17
|
+
* Which client checks run offline and which provider checks remain separate.
|
|
18
|
+
|
|
19
|
+
## Publish Tool Progress
|
|
20
|
+
|
|
21
|
+
Enable activity snapshots when your client supports them:
|
|
22
|
+
|
|
23
|
+
```ruby
|
|
24
|
+
RubyLLM::AgUI.configure do |config|
|
|
25
|
+
config.emit_tool_progress = true
|
|
26
|
+
end
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Progress is off by default. Each tool call has a stable activity id and later
|
|
30
|
+
snapshots replace earlier values. Values pass through the host redaction policy;
|
|
31
|
+
late callbacks cannot write after the run closes. See
|
|
32
|
+
[Tool Progress]({% link _core_features/tool-progress.md %}).
|
|
33
|
+
|
|
34
|
+
## Run the Official Client Locally
|
|
35
|
+
|
|
36
|
+
```sh
|
|
37
|
+
bundle install
|
|
38
|
+
npm ci
|
|
39
|
+
bin/check-browser
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The maintained example connects a pinned official `@ag-ui/client` to plain Ruby
|
|
43
|
+
and Rails endpoints. It covers text, a second turn, approval/denial, progress,
|
|
44
|
+
completed-run reconnect, sanitized failure, and cancellation outcomes. The Rails
|
|
45
|
+
endpoint uses a generated migration and temporary SQLite database. See
|
|
46
|
+
[Client Example]({% link _advanced/client-example.md %}).
|
|
47
|
+
|
|
48
|
+
Browser screenshots, event traces, and schema validation are local fixture
|
|
49
|
+
evidence. They do not establish live provider behavior, all-client compatibility,
|
|
50
|
+
or remote CI success. The opt-in live spec now shares an actor-scoped setup
|
|
51
|
+
with an offline harness check. See
|
|
52
|
+
[Compatibility]({% link _reference/compatibility.md %}) and
|
|
53
|
+
[Upgrading]({% link _reference/upgrading.md %}) before updating your application.
|
|
@@ -21,15 +21,16 @@ dependency does.
|
|
|
21
21
|
|
|
22
22
|
## Supported versions
|
|
23
23
|
|
|
24
|
-
| Component | Range |
|
|
24
|
+
| Component | Range | Evidence |
|
|
25
25
|
|---|---|---|
|
|
26
|
-
| Ruby | `>= 3.2` | 3.2, 3.3, 3.4
|
|
26
|
+
| Ruby | `>= 3.2` | CI passes on 3.2.11, 3.3.12, and 3.4.11; 3.3.5 checked locally |
|
|
27
27
|
| RubyLLM | `>= 2.1`, `< 3.0` | 2.1.1 (callback, approval and cancellation behavior read from its source) |
|
|
28
28
|
| `ag-ui-protocol` | `~> 0.2.0` | 0.2.0 |
|
|
29
|
-
| Rails (adapter only) | `railties`, `actionpack`, `activerecord` `>= 8.0` | 8.0.
|
|
29
|
+
| Rails (adapter only) | `railties`, `actionpack`, `activerecord` `>= 8.0` | CI passes on 8.0.5.1 and 8.1.4 with Ruby 3.3/3.4; 8.1.4 checked locally |
|
|
30
30
|
| AG-UI schema | pinned commit `89cedd3fd549feeee41e50e1d7c6d373e549241a` | vendored, SHA-256 recorded in [AG-UI Protocol Coverage]({% link _reference/protocol-coverage.md %}) |
|
|
31
31
|
|
|
32
|
-
The pure-Ruby core
|
|
32
|
+
The pure-Ruby core uses `ruby_llm` and `ag-ui-protocol`, plus the patched
|
|
33
|
+
standard-library gems required by the gemspec. Rails,
|
|
33
34
|
`ToolViews`, `OpenUI` and `OddlyDock` are not runtime dependencies, and the Rails
|
|
34
35
|
adapter is only loaded when `Rails::Railtie` is already defined.
|
|
35
36
|
|
|
@@ -52,15 +53,40 @@ adapter is only loaded when `Rails::Railtie` is already defined.
|
|
|
52
53
|
|
|
53
54
|
The RSpec suite runs offline with fakes and no provider credentials. Package verification uses the network only to install dependencies, then runs its core and Rails checks without a provider. Its advisory check includes the actual default-library gem versions activated by the isolated core install.
|
|
54
55
|
|
|
56
|
+
## Recorded Client Verification
|
|
57
|
+
|
|
58
|
+
On 2026-10-19, the local 0.2.0 candidate passes the maintained browser workflows
|
|
59
|
+
with `@ag-ui/client` 1.0.3, Playwright 1.64.1, and Chrome 155.0.8059.82 on macOS.
|
|
60
|
+
Both the plain Ruby and Rails endpoints use scripted chats. Checks cover text,
|
|
61
|
+
a second turn, approval/denial, incremental progress before completion, default
|
|
62
|
+
tool-result redaction, sanitized failure, cancellation, and exact completed-run
|
|
63
|
+
reconnect, including a completed resume run. Captured events also validate
|
|
64
|
+
against the pinned schema. The progress demo publishes synthetic counts through
|
|
65
|
+
a host policy and withholds messages; unit tests check default progress redaction.
|
|
66
|
+
|
|
67
|
+
Run `bin/check-browser` to reproduce and inspect screenshots and events under
|
|
68
|
+
`tmp/browser/`. `bin/check --package` separately installs the built artifact
|
|
69
|
+
outside the checkout, checks both endpoints, and generates RubyLLM models and
|
|
70
|
+
ordered example migrations to verify scoped chat persistence. This package
|
|
71
|
+
probe uses available development dependencies; `bin/verify-package` checks a
|
|
72
|
+
clean gem home with freshly resolved dependencies.
|
|
73
|
+
|
|
74
|
+
The [2026-10-19 CI run](https://github.com/yorzi/ruby_llm-ag-ui/actions/runs/39179834370)
|
|
75
|
+
passes all ten jobs at `4aba0dc`: the Ruby and Rails matrices above, lint,
|
|
76
|
+
installed-host verification, and the browser suite on Ubuntu 24.04.
|
|
77
|
+
The browser job uses Playwright's Chromium 156.0.8078.4 and passes all 12
|
|
78
|
+
scenarios, with 154 consumed events validated against the schema. Its desktop
|
|
79
|
+
progress and mobile denial screenshots were also inspected. These remain
|
|
80
|
+
synthetic checks; no live provider session is recorded.
|
|
81
|
+
|
|
55
82
|
## What is not asserted
|
|
56
83
|
|
|
57
|
-
- **
|
|
58
|
-
client
|
|
59
|
-
|
|
60
|
-
renders this correctly" is a separate claim, and it is not made here.
|
|
84
|
+
- **Other clients or versions.** CopilotKit and client versions other than the
|
|
85
|
+
recorded `@ag-ui/client` version have not been verified. Schema conformance
|
|
86
|
+
does not establish that every UI renders the events correctly.
|
|
61
87
|
- **Live providers.** `spec/live/live_run_spec.rb` is the opt-in check
|
|
62
|
-
(`RUBY_LLM_LIVE=1`, plus a provider key);
|
|
63
|
-
Provider behavior in 2.1.1 is derived from its source, not from a recorded
|
|
88
|
+
(`RUBY_LLM_LIVE=1`, plus a provider key); its provider context is skipped in the default suite. The same run setup
|
|
89
|
+
is exercised separately with an offline chat. Provider behavior in 2.1.1 is derived from its source, not from a recorded
|
|
64
90
|
session.
|
|
65
91
|
- **Every RubyLLM patch release in the `>= 2.1, < 3.0` range.** The upper bound
|
|
66
92
|
is enforced by the gemspec; the verified version is 2.1.1.
|
|
@@ -83,9 +109,9 @@ These are choices, not gaps, and each has a reason:
|
|
|
83
109
|
repeating it would duplicate the call in the client's conversation.
|
|
84
110
|
- **`MESSAGES_SNAPSHOT` is never produced.** History is the host's, and the gem
|
|
85
111
|
will not re-id it. `STATE_SNAPSHOT` is produced, on a reconnect.
|
|
86
|
-
- **
|
|
87
|
-
|
|
88
|
-
|
|
112
|
+
- **Tool progress is opt-in.** `ACTIVITY_SNAPSHOT` carries local tool progress
|
|
113
|
+
when enabled. `ACTIVITY_DELTA`, `REASONING_*`, `RAW` and `SUBAGENT_*` remain
|
|
114
|
+
unproduced. A thinking-only chunk produces nothing.
|
|
89
115
|
- **Message ids are the gem's** (`run-<id>:a<n>`), scoped to the run, because
|
|
90
116
|
RubyLLM streaming chunks carry no message id.
|
|
91
117
|
- **Steps are per phase** (`completion`, `tool`), named `step-<n>` per run.
|
|
@@ -66,11 +66,12 @@ Legend:
|
|
|
66
66
|
| `TOOL_CALL_RESULT` | tool | The tool result, after the host redaction policy. |
|
|
67
67
|
| `STATE_SNAPSHOT` | state | On a reconnect: the state the host gave the run. |
|
|
68
68
|
|
|
69
|
-
### Optional (
|
|
69
|
+
### Optional (2)
|
|
70
70
|
|
|
71
71
|
| Event type | Family | When |
|
|
72
72
|
|---|---|---|
|
|
73
73
|
| `CUSTOM` | raw/custom | Only safe, documented host extensions. Nothing in the gem emits one today. |
|
|
74
|
+
| `ACTIVITY_SNAPSHOT` | activity | Local tool progress when `emit_tool_progress` is enabled, after host redaction. |
|
|
74
75
|
|
|
75
76
|
### Host-owned, not produced (2)
|
|
76
77
|
|
|
@@ -79,14 +80,13 @@ Legend:
|
|
|
79
80
|
| `MESSAGES_SNAPSHOT` | state | The conversation belongs to the host. Producing one would mean re-synthesizing message ids the gem did not mint, which a client would then render as new messages. |
|
|
80
81
|
| `STATE_DELTA` | state | Deltas describe a change the host made. The gem never invents a patch, and it has no API that publishes one. |
|
|
81
82
|
|
|
82
|
-
### Not produced (
|
|
83
|
+
### Not produced (14)
|
|
83
84
|
|
|
84
85
|
| Event type | Family | Why |
|
|
85
86
|
|---|---|---|
|
|
86
87
|
| `TEXT_MESSAGE_CHUNK` | text | Convenience form of the explicit triad; the gem emits `START`/`CONTENT`/`END`. |
|
|
87
88
|
| `TOOL_CALL_CHUNK` | tool | Convenience form; the gem emits the explicit triad. |
|
|
88
|
-
| `
|
|
89
|
-
| `ACTIVITY_DELTA` | activity | Same: no progress projection, and no invented patches. |
|
|
89
|
+
| `ACTIVITY_DELTA` | activity | Progress uses full snapshots; no patches are produced. |
|
|
90
90
|
| `RAW` | raw/custom | The gem never passes provider payloads through. |
|
|
91
91
|
| `REASONING_START` | reasoning | Private reasoning is not emitted until a host opts in, and there is no opt-in yet. |
|
|
92
92
|
| `REASONING_MESSAGE_START` | reasoning | A chunk that carries thinking is ignored. |
|
|
@@ -99,7 +99,7 @@ Legend:
|
|
|
99
99
|
| `SUBAGENT_FINISHED` | subagents | Same. |
|
|
100
100
|
| `SUBAGENT_ERROR` | subagents | Same. |
|
|
101
101
|
|
|
102
|
-
Totals: 13 produced +
|
|
102
|
+
Totals: 13 produced + 2 optional + 2 host-owned + 14 not produced = **31**.
|
|
103
103
|
|
|
104
104
|
## Terminal events
|
|
105
105
|
|
|
@@ -172,4 +172,5 @@ must produce no event at all.
|
|
|
172
172
|
## Next Steps
|
|
173
173
|
|
|
174
174
|
* [Streaming Runs]({% link _core_features/streaming-runs.md %}) - follow event order and outcomes.
|
|
175
|
+
* [Tool Progress]({% link _core_features/tool-progress.md %}) - opt in to activity snapshots.
|
|
175
176
|
* [Compatibility]({% link _reference/compatibility.md %}) - see what this repository verifies.
|
|
@@ -11,12 +11,13 @@ description: Review RubyLLM AG-UI releases, newest first, with highlights and up
|
|
|
11
11
|
{{ page.description }}
|
|
12
12
|
{: .fs-6 .fw-300 }
|
|
13
13
|
|
|
14
|
-
{% if site.docs_unreleased %}This build follows `main
|
|
14
|
+
{% if site.docs_unreleased %}This build follows `main` for version `{{ site.version }}` development. Select Stable in the version menu for the published release.
|
|
15
15
|
{% else %}This documentation describes version `{{ site.version }}`. Use its tag and release notes to identify the exact source.
|
|
16
16
|
{% endif %}
|
|
17
17
|
|
|
18
18
|
| Release | Date | Highlights |
|
|
19
19
|
| --- | --- | --- |
|
|
20
|
-
| [0.
|
|
20
|
+
| [0.2.0](https://github.com/yorzi/ruby_llm-ag-ui/releases/tag/v0.2.0) | 2026-10-19 | [Client workflows and tool progress]({% link _getting_started/whats-new-in-0-2.md %}): opt-in redacted progress, a real-client demo, and completed resume reconnect |
|
|
21
|
+
| [0.1.0](https://github.com/yorzi/ruby_llm-ag-ui/releases/tag/v0.1.0) | 2026-10-18 | [First release]({% link _reference/whats-new-in-0-1.md %}): AG-UI event mapping, approval and resume, cancellation, reconnect, and the optional Rails adapter |
|
|
21
22
|
|
|
22
23
|
To move an application from one release to the next, follow [Upgrading]({% link _reference/upgrading.md %}).
|
|
@@ -20,16 +20,31 @@ After reading this guide, you will know:
|
|
|
20
20
|
|
|
21
21
|
```bash
|
|
22
22
|
bundle exec rake
|
|
23
|
+
npm ci
|
|
24
|
+
bin/check-browser
|
|
23
25
|
bundle exec bundle-audit check --update
|
|
24
26
|
bundle exec rake build
|
|
27
|
+
bundle exec ruby scripts/check_package_host.rb --built
|
|
25
28
|
bundle exec ruby bin/verify-package --rails --audit
|
|
26
29
|
docs/bin/build.sh
|
|
30
|
+
python3 scripts/check_docs_links.py
|
|
27
31
|
```
|
|
28
32
|
|
|
29
33
|
The package check installs dependencies in a fresh temporary gem home and requires the installed artifact from outside the checkout. It verifies the core entrypoint, shipped templates and guides, and input parsing. With `--rails`, it invokes the documented generator and migrates its table up and down in SQLite. With `--audit`, it checks the actually activated core dependencies against the advisory database.
|
|
30
34
|
|
|
31
35
|
These checks make no live provider call. Keep provider and client interoperability evidence in [Compatibility]({% link _reference/compatibility.md %}).
|
|
32
36
|
|
|
37
|
+
Install the documentation bundle before building the site: run `bundle install`
|
|
38
|
+
from `docs/`. API HTML and Markdown use the RDoc version locked in that bundle,
|
|
39
|
+
so local and CI builds use the same navigation and parser.
|
|
40
|
+
|
|
41
|
+
## Review Release Scope
|
|
42
|
+
|
|
43
|
+
Use the repository's [roadmap](https://github.com/yorzi/ruby_llm-ag-ui/blob/main/ROADMAP.md)
|
|
44
|
+
for proposed iterations and the 1.0 release conditions. Guides describe available
|
|
45
|
+
behavior; the roadmap describes planned work. Check the release's conditions
|
|
46
|
+
before preparing its tag.
|
|
47
|
+
|
|
33
48
|
## Configure Trusted Publishing
|
|
34
49
|
|
|
35
50
|
Before the first tag, register a pending trusted publisher on RubyGems with gem name `ruby_llm-ag_ui`, GitHub owner `yorzi`, repository `ruby_llm-ag-ui`, workflow `release.yml`, and environment `release`. The workflow uses a short-lived GitHub OIDC identity instead of a stored RubyGems API key.
|
|
@@ -15,28 +15,33 @@ After reading this guide, you will know:
|
|
|
15
15
|
* How to move between versioned releases.
|
|
16
16
|
* Where to find changes that need code updates.
|
|
17
17
|
|
|
18
|
-
##
|
|
18
|
+
## Move from 0.1 to 0.2
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
## Install Version 0.1
|
|
20
|
+
Change your Gemfile and update the dependency:
|
|
23
21
|
|
|
24
22
|
```ruby
|
|
25
|
-
gem 'ruby_llm-ag_ui', '~> 0.
|
|
23
|
+
gem 'ruby_llm-ag_ui', '~> 0.2.0'
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
```sh
|
|
27
|
+
bundle update ruby_llm-ag_ui
|
|
26
28
|
```
|
|
27
29
|
|
|
28
|
-
|
|
30
|
+
The 0.2 release keeps the 0.1 run, store, and Rails APIs. It does not require
|
|
31
|
+
another migration or installer run.
|
|
29
32
|
|
|
30
|
-
|
|
33
|
+
Tool progress is off by default. Set `config.emit_tool_progress = true` only
|
|
34
|
+
when your client handles activity snapshots, and review its redaction policy.
|
|
35
|
+
See [Tool Progress]({% link _core_features/tool-progress.md %}).
|
|
31
36
|
|
|
32
|
-
|
|
37
|
+
## Completed Resume Retries in 0.2
|
|
33
38
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
39
|
+
An exact retry of a completed resume run now returns its recorded outcome.
|
|
40
|
+
Keep the original run id and full input, including `resume`. A new run id that
|
|
41
|
+
tries to answer an already answered interrupt is still refused, and changed
|
|
42
|
+
input under the original id is a conflict. No approval or tool is executed again.
|
|
37
43
|
|
|
38
|
-
|
|
39
|
-
bin/rails generate ruby_llm:ag_ui:install
|
|
40
|
-
```
|
|
44
|
+
## Older Development Checkouts
|
|
41
45
|
|
|
42
|
-
|
|
46
|
+
For the initial release's generator namespace correction, read the
|
|
47
|
+
[0.1 upgrading guide](https://github.com/yorzi/ruby_llm-ag-ui/blob/v0.1.0/docs/_reference/upgrading.md).
|
|
@@ -28,6 +28,10 @@ module RubyLLM
|
|
|
28
28
|
# RubyLLM::AgUI::Redaction.method(:none), or replace it with your policy.
|
|
29
29
|
attr_writer :redact
|
|
30
30
|
|
|
31
|
+
# Whether runs publish local tool progress as activity snapshots. Off
|
|
32
|
+
# unless the host opts in; progress payloads still pass through redaction.
|
|
33
|
+
attr_accessor :emit_tool_progress
|
|
34
|
+
|
|
31
35
|
# A Logger used for host-side diagnostics. Never receives prompts, raw
|
|
32
36
|
# tool payloads or credentials.
|
|
33
37
|
attr_accessor :logger
|
|
@@ -71,6 +75,7 @@ module RubyLLM
|
|
|
71
75
|
@interrupt_ttl = 900
|
|
72
76
|
@clock = -> { Time.now.utc }
|
|
73
77
|
@allow_client_history = false
|
|
78
|
+
@emit_tool_progress = false
|
|
74
79
|
end
|
|
75
80
|
|
|
76
81
|
# The redaction policy in force. Never nil: an unset or nil policy falls
|
|
@@ -105,7 +110,8 @@ module RubyLLM
|
|
|
105
110
|
max_tools: max_tools,
|
|
106
111
|
max_context: max_context,
|
|
107
112
|
interrupt_ttl: interrupt_ttl,
|
|
108
|
-
allow_client_history: allow_client_history
|
|
113
|
+
allow_client_history: allow_client_history,
|
|
114
|
+
emit_tool_progress: emit_tool_progress
|
|
109
115
|
}
|
|
110
116
|
end
|
|
111
117
|
end
|
|
@@ -66,8 +66,8 @@ module RubyLLM
|
|
|
66
66
|
'MESSAGES_SNAPSHOT' => [:host_owned, 'Not produced: history is the host\'s; ids are never re-synthesized.'],
|
|
67
67
|
'STATE_SNAPSHOT' => [:produced, 'Reconnect: the state the host gave the run, never replayed deltas.'],
|
|
68
68
|
'STATE_DELTA' => [:host_owned, 'Not produced: patches are the host\'s to describe; the gem invents none.'],
|
|
69
|
-
'ACTIVITY_SNAPSHOT' => [:
|
|
70
|
-
'ACTIVITY_DELTA' => [:not_produced, '
|
|
69
|
+
'ACTIVITY_SNAPSHOT' => [:optional, 'Opt-in local tool progress, after host redaction.'],
|
|
70
|
+
'ACTIVITY_DELTA' => [:not_produced, 'Progress uses full snapshots; the gem does not invent patches.'],
|
|
71
71
|
'REASONING_START' => [:not_produced, 'Private reasoning is not emitted until a host opts in.'],
|
|
72
72
|
'REASONING_MESSAGE_START' => [:not_produced, 'Never emitted; a chunk that carries thinking is ignored.'],
|
|
73
73
|
'REASONING_MESSAGE_CONTENT' => [:not_produced, 'Never emitted; a chunk that carries thinking is ignored.'],
|
|
@@ -53,6 +53,7 @@ module RubyLLM
|
|
|
53
53
|
@last_message_id = nil
|
|
54
54
|
@text_emitted = false
|
|
55
55
|
@calls = {}
|
|
56
|
+
@completed_tools = {}
|
|
56
57
|
@open_step = nil
|
|
57
58
|
@step_kind = nil
|
|
58
59
|
@warned_redaction = false
|
|
@@ -95,6 +96,23 @@ module RubyLLM
|
|
|
95
96
|
events
|
|
96
97
|
end
|
|
97
98
|
|
|
99
|
+
# Reports a tool's actual progress as a replacing activity snapshot.
|
|
100
|
+
# Each call has one run-scoped activity id. Values pass through the host's
|
|
101
|
+
# :tool_progress redaction policy; the tool call id remains structural.
|
|
102
|
+
# Empty updates and callbacks for finished tools or runs produce nothing.
|
|
103
|
+
def tool_progress(tool_call, progress)
|
|
104
|
+
return [] if @closed || @completed_tools[tool_call.id]
|
|
105
|
+
|
|
106
|
+
payload = { 'value' => progress.value, 'total' => progress.total, 'message' => progress.message }.compact
|
|
107
|
+
return [] if payload.empty?
|
|
108
|
+
|
|
109
|
+
[::AgUiProtocol::Core::Events::ActivitySnapshotEvent.new(
|
|
110
|
+
message_id: "#{@run_id}:progress:#{tool_call.id}", activity_type: 'TOOL_PROGRESS',
|
|
111
|
+
content: { 'toolCallId' => tool_call.id, 'progress' => redact(Redaction::TOOL_PROGRESS, payload) },
|
|
112
|
+
replace: true
|
|
113
|
+
)]
|
|
114
|
+
end
|
|
115
|
+
|
|
98
116
|
# Closes any open text message, tool call and step, and closes the mapper.
|
|
99
117
|
#
|
|
100
118
|
# The caller writes these before the terminal event: AG-UI has no way to
|
|
@@ -261,6 +279,7 @@ module RubyLLM
|
|
|
261
279
|
# --- tool results --------------------------------------------------------
|
|
262
280
|
|
|
263
281
|
def tool_result_events(message)
|
|
282
|
+
@completed_tools[message.tool_call_id] = true
|
|
264
283
|
events = close_text_events
|
|
265
284
|
events.concat(close_tool_calls)
|
|
266
285
|
events.concat(ensure_step(STEP_TOOL))
|
|
@@ -320,7 +339,7 @@ module RubyLLM
|
|
|
320
339
|
return if @warned_redaction
|
|
321
340
|
|
|
322
341
|
@warned_redaction = true
|
|
323
|
-
@logger&.warn('[ruby_llm_ag_ui] tool results are redacted by default; ' \
|
|
342
|
+
@logger&.warn('[ruby_llm_ag_ui] tool results and progress are redacted by default; ' \
|
|
324
343
|
'configure RubyLLM::AgUI.config.redact to publish them')
|
|
325
344
|
end
|
|
326
345
|
|
|
@@ -32,9 +32,13 @@ module RubyLLM
|
|
|
32
32
|
@input.resume?
|
|
33
33
|
end
|
|
34
34
|
|
|
35
|
-
# Checks
|
|
36
|
-
#
|
|
35
|
+
# Checks a new resume and builds its plan before the run opens a stream.
|
|
36
|
+
# An existing identity is left to reservation and reconnect: an exact
|
|
37
|
+
# retry reports its recorded outcome, never redeems the decision twice.
|
|
37
38
|
def plan(now:)
|
|
39
|
+
existing = @store.find(scope: @scope, thread_id: @input.thread_id, run_id: @input.run_id)
|
|
40
|
+
return self if existing
|
|
41
|
+
|
|
38
42
|
source = interrupted_run
|
|
39
43
|
@plan = @input.resume.map { |entry| plan_entry(source, entry, now) }
|
|
40
44
|
self
|
data/lib/ruby_llm/ag_ui/run.rb
CHANGED
|
@@ -25,15 +25,17 @@ module RubyLLM
|
|
|
25
25
|
|
|
26
26
|
# rubocop:disable-next Metrics/ParameterLists -- one keyword per host-supplied or overridable value
|
|
27
27
|
def initialize(chat:, input:, scope:, store: nil, logger: nil, redact: nil, clock: nil, expires_in: nil,
|
|
28
|
-
state: nil)
|
|
28
|
+
state: nil, emit_tool_progress: RubyLLM::AgUI.config.emit_tool_progress)
|
|
29
|
+
defaults = RubyLLM::AgUI.config
|
|
29
30
|
@chat = chat
|
|
30
31
|
@input = input
|
|
31
32
|
@scope = scope
|
|
32
|
-
@store = store ||
|
|
33
|
-
@logger = logger ||
|
|
33
|
+
@store = store || defaults.run_store!
|
|
34
|
+
@logger = logger || defaults.logger
|
|
34
35
|
@redact = redact
|
|
35
|
-
@clock = clock ||
|
|
36
|
-
@expires_in = expires_in ||
|
|
36
|
+
@clock = clock || defaults.clock
|
|
37
|
+
@expires_in = expires_in || defaults.interrupt_ttl
|
|
38
|
+
@emit_tool_progress = emit_tool_progress
|
|
37
39
|
@mutex = Mutex.new
|
|
38
40
|
validate!
|
|
39
41
|
@state = state.nil? ? @input.state : state
|
|
@@ -84,16 +86,20 @@ module RubyLLM
|
|
|
84
86
|
end
|
|
85
87
|
|
|
86
88
|
def execute(record, to)
|
|
87
|
-
|
|
88
|
-
@emitter = build_emitter(record, to, mapper: @mapper)
|
|
89
|
-
@emitter.start(run_started_event(record), state: @state)
|
|
89
|
+
start_execution(record, to)
|
|
90
90
|
resume.apply(now: @clock.call) if resume.needed?
|
|
91
91
|
drive
|
|
92
|
-
@record = finish
|
|
92
|
+
@record = finalize { finish }
|
|
93
93
|
rescue RubyLLM::CancelledError
|
|
94
|
-
@record = @emitter.cancel
|
|
94
|
+
@record = finalize { @emitter.cancel }
|
|
95
95
|
rescue StandardError => e
|
|
96
|
-
@record = @emitter.fail(e)
|
|
96
|
+
@record = finalize { @emitter.fail(e) }
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def start_execution(record, to)
|
|
100
|
+
@mapper = build_mapper(record)
|
|
101
|
+
@emitter = build_emitter(record, to, mapper: @mapper)
|
|
102
|
+
@emitter.start(run_started_event(record), state: @state)
|
|
97
103
|
end
|
|
98
104
|
|
|
99
105
|
def build_emitter(record, to, mapper: build_mapper(record))
|
|
@@ -113,9 +119,14 @@ module RubyLLM
|
|
|
113
119
|
def drive
|
|
114
120
|
@chat.before_tool_call { |tool_call| project { @mapper.before_tool_call(tool_call) } }
|
|
115
121
|
@chat.after_message { |message| project { @mapper.message(message) } }
|
|
122
|
+
if @emit_tool_progress == true
|
|
123
|
+
@chat.after_tool_progress { |call, progress| project { @mapper.tool_progress(call, progress) } }
|
|
124
|
+
end
|
|
116
125
|
@chat.complete { |chunk| project { @mapper.chunk(chunk) } }
|
|
117
126
|
end
|
|
118
127
|
|
|
128
|
+
def finalize(&) = @mutex.synchronize(&)
|
|
129
|
+
|
|
119
130
|
def project
|
|
120
131
|
@mutex.synchronize { @emitter.write(yield) }
|
|
121
132
|
end
|
|
@@ -9,7 +9,7 @@ module RubyLLM
|
|
|
9
9
|
# already in progress instead of executing twice, and it keeps the
|
|
10
10
|
# interrupts that a later resume has to answer.
|
|
11
11
|
#
|
|
12
|
-
# Two stores ship with the gem: Store::MemoryStore
|
|
12
|
+
# Two stores ship with the gem: Store::MemoryStore for
|
|
13
13
|
# a single process, and the optional Rails adapter's ActiveRecord store. A
|
|
14
14
|
# host may implement this interface instead; every method must be safe to
|
|
15
15
|
# call from concurrent threads.
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ruby_llm-ag_ui
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.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: ag-ui-protocol
|
|
@@ -142,16 +142,18 @@ files:
|
|
|
142
142
|
- LICENSE.txt
|
|
143
143
|
- README.md
|
|
144
144
|
- SECURITY.md
|
|
145
|
+
- docs/_advanced/client-example.md
|
|
145
146
|
- docs/_advanced/rails-integration.md
|
|
146
147
|
- docs/_advanced/security-and-deployment.md
|
|
147
148
|
- docs/_core_features/approval-and-resume.md
|
|
148
149
|
- docs/_core_features/input-validation.md
|
|
149
150
|
- docs/_core_features/reconnects.md
|
|
150
151
|
- docs/_core_features/streaming-runs.md
|
|
152
|
+
- docs/_core_features/tool-progress.md
|
|
151
153
|
- docs/_getting_started/configuration.md
|
|
152
154
|
- docs/_getting_started/getting-started.md
|
|
153
155
|
- docs/_getting_started/overview.md
|
|
154
|
-
- docs/_getting_started/whats-new-in-0-
|
|
156
|
+
- docs/_getting_started/whats-new-in-0-2.md
|
|
155
157
|
- docs/_reference/api-reference.md
|
|
156
158
|
- docs/_reference/compatibility.md
|
|
157
159
|
- docs/_reference/protocol-coverage.md
|
|
@@ -159,6 +161,7 @@ files:
|
|
|
159
161
|
- docs/_reference/releasing.md
|
|
160
162
|
- docs/_reference/third-party-notices.md
|
|
161
163
|
- docs/_reference/upgrading.md
|
|
164
|
+
- docs/_reference/whats-new-in-0-1.md
|
|
162
165
|
- docs/index.md
|
|
163
166
|
- lib/generators/ruby_llm/ag_ui/install_generator.rb
|
|
164
167
|
- lib/generators/ruby_llm/ag_ui/templates/create_ruby_llm_ag_ui_runs.rb.tt
|