ask-rails 0.6.0 → 0.7.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 +65 -0
- data/README.md +105 -17
- data/lib/ask/rails/railtie.rb +0 -3
- data/lib/ask/rails/state.rb +91 -0
- data/lib/ask/rails/version.rb +1 -1
- data/lib/ask/rails.rb +1 -0
- data/lib/generators/ask/agent/templates/agent.rb +18 -0
- data/lib/generators/ask/agent_generator.rb +17 -0
- data/lib/generators/ask/install/templates/application_workflow.rb +23 -0
- data/lib/generators/ask/install/templates/audit_log_migration.rb +4 -1
- data/lib/generators/ask/install/templates/initializer.rb +18 -4
- data/lib/generators/ask/install/templates/state_migration.rb +6 -2
- data/lib/generators/ask/install_generator.rb +31 -11
- data/lib/generators/ask/workflow/templates/workflow.rb +28 -0
- data/lib/generators/ask/workflow_generator.rb +35 -0
- metadata +22 -6
- data/lib/ask/rails/generators/install/install_generator.rb +0 -45
- data/lib/ask/rails/generators/install/templates/audit_log_migration.rb +0 -19
- data/lib/ask/rails/generators/install/templates/initializer.rb +0 -27
- data/lib/ask/rails/generators/install/templates/state_migration.rb +0 -18
- /data/lib/{ask/rails/generators → generators/ask}/install/templates/application_agent.rb +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 805ea4f5713b8cfacc7f25c54b6229b788199ddba9ab1dc5a50c4719a834f256
|
|
4
|
+
data.tar.gz: da2648a6027373d0c65f37786c347e6fda211d6f43a38378042acb8dae1a7ca8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 02e6b7d1dca1617b4070e9068cb6be162040756119ef1dbdab793ec27b1cb1aaa33a46eb88d7dcc9c9734e3b3c570172f816290f2629be0a09c8cdef041a88e9
|
|
7
|
+
data.tar.gz: bedabfd712c7397baf254aecaf8c2c2316dfb47964303d4b91437e9ef53dcf187a31fccdc406e8b5434de5029d657179845d31044de8e2ee148abff0baef8063
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,68 @@
|
|
|
1
|
+
## [0.7.0] — 2026-07-31
|
|
2
|
+
|
|
3
|
+
### Added
|
|
4
|
+
|
|
5
|
+
- **`ask:workflow NAME` generator** — scaffolds a workflow module under `app/workflows/<name>/` with `workflow.rb` and `steps/` directory. Requires ask-graph; aborts with a helpful message if it's not installed.
|
|
6
|
+
|
|
7
|
+
- **`ask:agent NAME` generator** — scaffolds an individual agent under `app/agents/`.
|
|
8
|
+
|
|
9
|
+
- **ask-graph support in install generator** — `ask:install` now creates `app/workflows/application_workflow.rb` and the workflows directory when ask-graph is installed. Pass `--skip-graph` to skip. The initializer's graph configuration block is only generated when ask-graph is present:
|
|
10
|
+
|
|
11
|
+
```ruby
|
|
12
|
+
if defined?(Ask::Graph)
|
|
13
|
+
Ask::Graph.storage = ASK_STATE
|
|
14
|
+
end
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
- Shared `ask_state` table is explicitly documented as serving both agent sessions and workflow checkpoints.
|
|
20
|
+
|
|
21
|
+
### Tested
|
|
22
|
+
|
|
23
|
+
- 11 generator tests, 51 assertions, 0 failures.
|
|
24
|
+
|
|
25
|
+
## [0.7.0] — 2026-07-31
|
|
26
|
+
|
|
27
|
+
### Added
|
|
28
|
+
|
|
29
|
+
- **`ask:agent NAME` generator** — scaffolds an individual agent under `app/agents/`:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
rails generate ask:agent support_bot
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
- **`ask:workflow NAME` generator** — scaffolds a workflow module under `app/workflows/<name>/` with `workflow.rb` and `steps/` directory. Requires ask-graph; aborts with a helpful message if it's not installed:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
rails generate ask:workflow notify_customer
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
- **`Ask::Rails::State`** — ActiveRecord-backed state adapter for the `ask_state` table. Used by ask-graph for workflow checkpoints. Works with any database adapter (PostgreSQL, MySQL, SQLite). Implements key-value storage plus ordered lists (for agent session indexes).
|
|
42
|
+
|
|
43
|
+
- **ask-graph support in install generator** — `ask:install` now creates `app/workflows/application_workflow.rb` and the workflows directory when ask-graph is installed. Pass `--skip-graph` to skip. The initializer's graph block is only generated when ask-graph is present:
|
|
44
|
+
|
|
45
|
+
```ruby
|
|
46
|
+
if defined?(Ask::Graph)
|
|
47
|
+
Ask::Graph.storage = Ask::Rails::State.new
|
|
48
|
+
end
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Changed
|
|
52
|
+
|
|
53
|
+
- **Generators consolidated under `lib/generators/ask/`** — all three generators (`ask:install`, `ask:agent`, `ask:workflow`) now live at the standard Rails discovery path. The old railtie-registered `ask:rails:*` names and the duplicate `lib/generators/ask/install_generator.rb` were removed. No duplicated work between generators: `ask:install` owns one-time setup, `ask:agent`/`ask:workflow` own per-component scaffolding.
|
|
54
|
+
|
|
55
|
+
- **Initializer fixed to use real APIs** — removed references to non-existent `Ask::State::PostgreSQL` and `config.state`. The agent block now only configures what ask-agent supports (audit log, default model).
|
|
56
|
+
|
|
57
|
+
- **State migration uses `t.json` instead of `t.jsonb`** — works on PostgreSQL, MySQL, and SQLite.
|
|
58
|
+
|
|
59
|
+
- **`migration_version` fix** — generated migrations now produce `ActiveRecord::Migration[8.1]` instead of the broken `Migration[[8.1]]`.
|
|
60
|
+
|
|
61
|
+
### Tested
|
|
62
|
+
|
|
63
|
+
- 24 tests, 67 assertions, 0 failures — including 13 tests for `Ask::Rails::State` against an in-memory SQLite database.
|
|
64
|
+
- End-to-end smoke test in a fresh Rails app: `ask:install`, `ask:agent`, `ask:workflow` all run; migrations execute; a workflow checkpoint survives a second run via `Ask::Rails::State`.
|
|
65
|
+
|
|
1
66
|
## [0.6.0] — 2026-07-29
|
|
2
67
|
|
|
3
68
|
### Added
|
data/README.md
CHANGED
|
@@ -1,36 +1,99 @@
|
|
|
1
1
|
# ask-rails
|
|
2
2
|
|
|
3
|
-
Rails integration for the [ask-rb](https://github.com/ask-rb) ecosystem. Provides generators, file conventions, and railtie for
|
|
3
|
+
Rails integration for the [ask-rb](https://github.com/ask-rb) ecosystem. Provides generators, file conventions, and a railtie for building agents and workflows in your Rails app.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
|
+
Add to your Gemfile:
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
gem "ask-rails"
|
|
11
|
+
gem "ask-graph" # optional — add only if you use workflows
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Run the installer:
|
|
15
|
+
|
|
7
16
|
```bash
|
|
8
|
-
bundle
|
|
17
|
+
bundle install
|
|
9
18
|
rails generate ask:install
|
|
10
19
|
```
|
|
11
20
|
|
|
12
21
|
This creates:
|
|
13
|
-
- `config/initializers/ask.rb` — agent configuration
|
|
14
|
-
- `app/agents/application_agent.rb` — base class for your agents
|
|
15
|
-
- `app/agents/` — directory for agent definitions
|
|
16
22
|
|
|
17
|
-
|
|
23
|
+
| File | Purpose |
|
|
24
|
+
|---|---|
|
|
25
|
+
| `config/initializers/ask.rb` | Agent + workflow configuration (graph block only generated when ask-graph is installed) |
|
|
26
|
+
| `app/agents/application_agent.rb` | Base class for your agents |
|
|
27
|
+
| `app/workflows/application_workflow.rb` | Base class for your workflows (only with ask-graph) |
|
|
28
|
+
| `db/migrate/*_create_ask_state.rb` | Shared key-value state table — workflow checkpoints and agent session indexes |
|
|
29
|
+
| `db/migrate/*_create_ask_audit_logs.rb` | Agent session audit log |
|
|
18
30
|
|
|
19
|
-
|
|
31
|
+
The `ask_state` table is backed by `Ask::Rails::State` — an ActiveRecord adapter that works with any database (PostgreSQL, MySQL, SQLite). Workflow checkpoints and agent session persistence share the same table, keyed by convention.
|
|
32
|
+
|
|
33
|
+
## Generators
|
|
34
|
+
|
|
35
|
+
### `ask:agent NAME`
|
|
36
|
+
|
|
37
|
+
Scaffolds a new agent:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
rails generate ask:agent support_bot
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Creates `app/agents/support_bot.rb`:
|
|
20
44
|
|
|
21
45
|
```ruby
|
|
22
|
-
|
|
23
|
-
class
|
|
24
|
-
|
|
25
|
-
|
|
46
|
+
module Agents
|
|
47
|
+
class SupportBot < ApplicationAgent
|
|
48
|
+
system_prompt "You are a helpful assistant."
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### `ask:workflow NAME`
|
|
54
|
+
|
|
55
|
+
Scaffolds a new workflow (requires ask-graph):
|
|
26
56
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
57
|
+
```bash
|
|
58
|
+
rails generate ask:workflow notify_customer
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Creates `app/workflows/notify_customer/workflow.rb` and `app/workflows/notify_customer/steps/`:
|
|
62
|
+
|
|
63
|
+
```ruby
|
|
64
|
+
module NotifyCustomer
|
|
65
|
+
class Workflow < ApplicationWorkflow
|
|
66
|
+
# step SomeStep
|
|
67
|
+
end
|
|
30
68
|
end
|
|
31
69
|
```
|
|
32
70
|
|
|
33
|
-
|
|
71
|
+
### Skipping workflow scaffolding
|
|
72
|
+
|
|
73
|
+
If you don't use ask-graph, install with:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
rails generate ask:install --skip-graph
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
The initializer still works — the graph block is omitted when ask-graph isn't installed.
|
|
80
|
+
|
|
81
|
+
## Usage
|
|
82
|
+
|
|
83
|
+
### Agents
|
|
84
|
+
|
|
85
|
+
```ruby
|
|
86
|
+
# app/agents/support_bot.rb
|
|
87
|
+
module Agents
|
|
88
|
+
class SupportBot < ApplicationAgent
|
|
89
|
+
model "gpt-4o"
|
|
90
|
+
system_prompt "You help users with support questions."
|
|
91
|
+
|
|
92
|
+
tool :bash
|
|
93
|
+
tool :read
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
```
|
|
34
97
|
|
|
35
98
|
```ruby
|
|
36
99
|
agent = Ask::Agent.new("support_bot")
|
|
@@ -38,12 +101,37 @@ response = agent.run("Find all open issues in the codebase")
|
|
|
38
101
|
puts response
|
|
39
102
|
```
|
|
40
103
|
|
|
41
|
-
|
|
104
|
+
### Workflows
|
|
42
105
|
|
|
43
106
|
```ruby
|
|
44
|
-
|
|
107
|
+
# app/workflows/order_fulfillment/workflow.rb
|
|
108
|
+
module OrderFulfillment
|
|
109
|
+
class Workflow < ApplicationWorkflow
|
|
110
|
+
step ValidatePayment
|
|
111
|
+
step NotifyCustomer
|
|
112
|
+
step ShipOrder
|
|
113
|
+
end
|
|
114
|
+
end
|
|
45
115
|
```
|
|
46
116
|
|
|
117
|
+
```ruby
|
|
118
|
+
# app/workflows/order_fulfillment/steps/validate_payment.rb
|
|
119
|
+
module OrderFulfillment
|
|
120
|
+
class ValidatePayment
|
|
121
|
+
def call(context)
|
|
122
|
+
context.payment = PaymentService.charge(context.input[:order])
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
```ruby
|
|
129
|
+
result = OrderFulfillment::Workflow.call(order: order)
|
|
130
|
+
result.payment
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Checkpoints are saved to the shared `ask_state` table, so workflows resume after crashes.
|
|
134
|
+
|
|
47
135
|
## Configuration
|
|
48
136
|
|
|
49
137
|
API keys are resolved automatically by `Ask::Auth`:
|
data/lib/ask/rails/railtie.rb
CHANGED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_record"
|
|
4
|
+
|
|
5
|
+
module Ask
|
|
6
|
+
module Rails
|
|
7
|
+
# ActiveRecord-backed state adapter for the ask_state table.
|
|
8
|
+
#
|
|
9
|
+
# Used by ask-graph (workflow checkpoints) and any other ask component
|
|
10
|
+
# that needs durable key-value storage in the Rails database. Works with
|
|
11
|
+
# any Rails database adapter — PostgreSQL, MySQL, SQLite — because the
|
|
12
|
+
# +value+ column is a portable JSON type.
|
|
13
|
+
#
|
|
14
|
+
# @example
|
|
15
|
+
# store = Ask::Rails::State.new
|
|
16
|
+
# store.set("checkpoint:1", { completed: true })
|
|
17
|
+
# store.get("checkpoint:1") # => { "completed" => true }
|
|
18
|
+
class State < ::Ask::State::Adapter
|
|
19
|
+
class Record < ::ActiveRecord::Base
|
|
20
|
+
self.table_name = "ask_state"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def get(key)
|
|
24
|
+
record = Record.find_by(key: key)
|
|
25
|
+
return nil unless record
|
|
26
|
+
return nil if record.expires_at && record.expires_at <= Time.current
|
|
27
|
+
|
|
28
|
+
record.value
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def set(key, value, ttl: nil)
|
|
32
|
+
record = Record.find_or_initialize_by(key: key)
|
|
33
|
+
record.value = value
|
|
34
|
+
record.expires_at = ttl ? Time.current + ttl : nil
|
|
35
|
+
record.save!
|
|
36
|
+
value
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def delete(key)
|
|
40
|
+
Record.where(key: key).delete_all
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def clear
|
|
44
|
+
Record.delete_all
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def keys(pattern: nil)
|
|
48
|
+
scope = Record.all
|
|
49
|
+
if pattern
|
|
50
|
+
like = self.class.glob_to_like(pattern)
|
|
51
|
+
scope = scope.where("key LIKE ?", like)
|
|
52
|
+
end
|
|
53
|
+
scope.pluck(:key)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def set_if_not_exists(key, value, ttl: nil)
|
|
57
|
+
return false if exists?(key)
|
|
58
|
+
|
|
59
|
+
set(key, value, ttl: ttl)
|
|
60
|
+
true
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Ordered lists — stored as an array in the value column.
|
|
64
|
+
# Required by Ask::Agent::Persistence::Base for session indexes.
|
|
65
|
+
|
|
66
|
+
def list_append(key, value, max_length: nil)
|
|
67
|
+
entries = list_range(key)
|
|
68
|
+
entries << value
|
|
69
|
+
entries = entries.last(max_length) if max_length
|
|
70
|
+
set(key, entries)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def list_range(key, start = 0, stop = -1)
|
|
74
|
+
entries = get(key)
|
|
75
|
+
return [] unless entries.is_a?(Array)
|
|
76
|
+
|
|
77
|
+
entries[start..stop] || []
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def list_remove(key, value)
|
|
81
|
+
entries = get(key)
|
|
82
|
+
return 0 unless entries.is_a?(Array)
|
|
83
|
+
|
|
84
|
+
before = entries.size
|
|
85
|
+
entries.delete(value)
|
|
86
|
+
set(key, entries)
|
|
87
|
+
before - entries.size
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
data/lib/ask/rails/version.rb
CHANGED
data/lib/ask/rails.rb
CHANGED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# <%= class_name %> agent.
|
|
4
|
+
#
|
|
5
|
+
# Run it:
|
|
6
|
+
# agent = Ask::Agent.new("<%= file_name %>")
|
|
7
|
+
# response = agent.run("Hello")
|
|
8
|
+
#
|
|
9
|
+
module Agents
|
|
10
|
+
class <%= class_name %> < ApplicationAgent
|
|
11
|
+
# model "gpt-4o"
|
|
12
|
+
system_prompt "You are a helpful assistant."
|
|
13
|
+
|
|
14
|
+
# tool :bash
|
|
15
|
+
# tool :read
|
|
16
|
+
# tool :grep
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators"
|
|
4
|
+
|
|
5
|
+
module Ask
|
|
6
|
+
module Generators
|
|
7
|
+
class AgentGenerator < ::Rails::Generators::NamedBase
|
|
8
|
+
source_root File.expand_path("agent/templates", __dir__)
|
|
9
|
+
|
|
10
|
+
desc "Creates an agent under app/agents — subclass ApplicationAgent with a model and system prompt"
|
|
11
|
+
|
|
12
|
+
def create_agent
|
|
13
|
+
template "agent.rb", "app/agents/#{file_name}.rb"
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Base class for your application's workflows.
|
|
4
|
+
#
|
|
5
|
+
# Subclass this to define workflows:
|
|
6
|
+
#
|
|
7
|
+
# module OrderFulfillment
|
|
8
|
+
# class Workflow < ApplicationWorkflow
|
|
9
|
+
# step ValidatePayment
|
|
10
|
+
# step NotifyCustomer
|
|
11
|
+
# step ShipOrder
|
|
12
|
+
# end
|
|
13
|
+
# end
|
|
14
|
+
#
|
|
15
|
+
# Then run:
|
|
16
|
+
# result = OrderFulfillment::Workflow.call({ order: order })
|
|
17
|
+
#
|
|
18
|
+
class ApplicationWorkflow < Ask::Graph
|
|
19
|
+
# Shared defaults — uncomment as needed:
|
|
20
|
+
# step_timeout 30
|
|
21
|
+
# workflow_timeout 60
|
|
22
|
+
# storage PostgresStore.new
|
|
23
|
+
end
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
# Migration for ask-agent's audit log table.
|
|
4
|
+
# This table records session events (tool calls, errors, token usage, etc.)
|
|
5
|
+
# from every agent that has audit_log configured.
|
|
6
|
+
class CreateAskAuditLogs < ActiveRecord::Migration[<%= migration_version %>]
|
|
4
7
|
def change
|
|
5
8
|
create_table :ask_audit_logs, if_not_exists: true do |t|
|
|
6
9
|
t.string :session_id, null: false
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
# Ask
|
|
4
|
-
#
|
|
3
|
+
# Ask Configuration
|
|
4
|
+
# =================
|
|
5
5
|
#
|
|
6
6
|
# API keys are resolved automatically by Ask::Auth:
|
|
7
7
|
# - Environment variables: OPENAI_API_KEY, ANTHROPIC_API_KEY, etc.
|
|
@@ -10,10 +10,24 @@
|
|
|
10
10
|
#
|
|
11
11
|
# See https://github.com/ask-rb/ask-auth for details.
|
|
12
12
|
|
|
13
|
+
# Agents
|
|
13
14
|
Ask::Agent.configure do |config|
|
|
14
|
-
#
|
|
15
|
+
# Audit log for session events (ask_audit_logs table)
|
|
15
16
|
# config.audit_log = { adapter: :active_record }
|
|
16
17
|
|
|
17
|
-
# Optional: set a default model
|
|
18
18
|
# config.default_model = "gpt-4o"
|
|
19
19
|
end
|
|
20
|
+
|
|
21
|
+
# Workflows (ask-graph)
|
|
22
|
+
#
|
|
23
|
+
# The graph block is only generated when ask-graph is installed.
|
|
24
|
+
# Run `bundle add ask-graph` then `rails generate ask:install` to add it.
|
|
25
|
+
if defined?(Ask::Graph)
|
|
26
|
+
# Checkpoint storage for workflow crash recovery — backed by the
|
|
27
|
+
# ask_state table (created by the migration) in your Rails database.
|
|
28
|
+
# Works with any database adapter: PostgreSQL, MySQL, SQLite.
|
|
29
|
+
Ask::Graph.storage = Ask::Rails::State.new
|
|
30
|
+
|
|
31
|
+
# Ask::Graph.default_step_timeout 30
|
|
32
|
+
# Ask::Graph.default_workflow_timeout 60
|
|
33
|
+
end
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
# Migration for ask-rb's shared key-value state table.
|
|
4
|
+
# Used by ask-agent (session persistence) and ask-graph (workflow checkpoints).
|
|
5
|
+
# This single table serves both components — keys are namespaced by convention.
|
|
6
|
+
# The json column type works on PostgreSQL, MySQL, and SQLite.
|
|
7
|
+
class CreateAskState < ActiveRecord::Migration[<%= migration_version %>]
|
|
4
8
|
def change
|
|
5
9
|
create_table :ask_state do |t|
|
|
6
10
|
t.string :key, null: false
|
|
7
|
-
t.
|
|
11
|
+
t.json :value, null: false, default: {}
|
|
8
12
|
t.datetime :expires_at
|
|
9
13
|
t.timestamps
|
|
10
14
|
|
|
@@ -6,34 +6,54 @@ require "rails/generators/active_record"
|
|
|
6
6
|
module Ask
|
|
7
7
|
module Generators
|
|
8
8
|
class InstallGenerator < ::Rails::Generators::Base
|
|
9
|
-
include ::
|
|
9
|
+
include ::ActiveRecord::Generators::Migration
|
|
10
10
|
|
|
11
11
|
source_root File.expand_path("install/templates", __dir__)
|
|
12
12
|
|
|
13
|
-
desc "Sets up ask-rb for Rails — creates initializer,
|
|
13
|
+
desc "Sets up ask-rb for Rails — creates initializer, agents and workflows directories, and shared state migrations"
|
|
14
|
+
|
|
15
|
+
class_option :skip_graph, type: :boolean, default: false,
|
|
16
|
+
desc: "Skip workflow scaffolding even if ask-graph is installed"
|
|
14
17
|
|
|
15
18
|
def create_initializer
|
|
16
19
|
template "initializer.rb", "config/initializers/ask.rb"
|
|
17
20
|
end
|
|
18
21
|
|
|
22
|
+
def create_agents_directory
|
|
23
|
+
empty_directory "app/agents"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def create_application_agent
|
|
27
|
+
template "application_agent.rb", "app/agents/application_agent.rb"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def create_workflows_directory
|
|
31
|
+
return if skip_graph?
|
|
32
|
+
empty_directory "app/workflows"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def create_application_workflow
|
|
36
|
+
return if skip_graph?
|
|
37
|
+
template "application_workflow.rb", "app/workflows/application_workflow.rb"
|
|
38
|
+
end
|
|
39
|
+
|
|
19
40
|
def create_state_migration
|
|
20
|
-
|
|
41
|
+
migration_template "state_migration.rb", "db/migrate/create_ask_state.rb"
|
|
21
42
|
end
|
|
22
43
|
|
|
23
44
|
def create_audit_log_migration
|
|
24
|
-
|
|
45
|
+
migration_template "audit_log_migration.rb", "db/migrate/create_ask_audit_logs.rb"
|
|
25
46
|
end
|
|
26
47
|
|
|
27
48
|
private
|
|
28
49
|
|
|
29
|
-
def
|
|
30
|
-
|
|
31
|
-
@migration_suffix ||= 0
|
|
32
|
-
now = Time.now.utc.strftime("%Y%m%d%H%M")
|
|
33
|
-
"#{now}#{format('%02d', @migration_suffix)}"
|
|
34
|
-
ensure
|
|
35
|
-
@migration_suffix = (@migration_suffix || 0) + 1
|
|
50
|
+
def skip_graph?
|
|
51
|
+
options[:skip_graph] || !defined?(Ask::Graph)
|
|
36
52
|
end
|
|
53
|
+
|
|
54
|
+
def migration_version
|
|
55
|
+
ActiveRecord::Migration.current_version
|
|
56
|
+
end
|
|
37
57
|
end
|
|
38
58
|
end
|
|
39
59
|
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# <%= class_name %> workflow.
|
|
4
|
+
#
|
|
5
|
+
# Define steps as plain Ruby classes in steps/ — each responds to call(context).
|
|
6
|
+
# Compose other workflows via Workflow.call(context) inside a step.
|
|
7
|
+
#
|
|
8
|
+
# module <%= class_name %>
|
|
9
|
+
# class Workflow < ApplicationWorkflow
|
|
10
|
+
# step SomeStep
|
|
11
|
+
# step OtherStep, if: :condition?
|
|
12
|
+
#
|
|
13
|
+
# private
|
|
14
|
+
#
|
|
15
|
+
# def condition?
|
|
16
|
+
# context.some_flag
|
|
17
|
+
# end
|
|
18
|
+
# end
|
|
19
|
+
# end
|
|
20
|
+
#
|
|
21
|
+
# Run it:
|
|
22
|
+
# result = <%= class_name %>::Workflow.call({ input: value })
|
|
23
|
+
#
|
|
24
|
+
module <%= class_name %>
|
|
25
|
+
class Workflow < ApplicationWorkflow
|
|
26
|
+
# step SomeStep
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators"
|
|
4
|
+
|
|
5
|
+
module Ask
|
|
6
|
+
module Generators
|
|
7
|
+
class WorkflowGenerator < ::Rails::Generators::NamedBase
|
|
8
|
+
source_root File.expand_path("workflow/templates", __dir__)
|
|
9
|
+
|
|
10
|
+
desc "Creates a workflow under app/workflows — module directory, Workflow class, and steps directory"
|
|
11
|
+
|
|
12
|
+
def verify_ask_graph
|
|
13
|
+
return if defined?(Ask::Graph)
|
|
14
|
+
|
|
15
|
+
raise ::Thor::Error, <<~MSG.strip
|
|
16
|
+
ask-graph is not installed. Add it to your Gemfile and run `bundle install` first:
|
|
17
|
+
|
|
18
|
+
gem "ask-graph"
|
|
19
|
+
MSG
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def create_workflow_directory
|
|
23
|
+
empty_directory "app/workflows/#{file_name}"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def create_workflow
|
|
27
|
+
template "workflow.rb", "app/workflows/#{file_name}/workflow.rb"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def create_steps_directory
|
|
31
|
+
empty_directory "app/workflows/#{file_name}/steps"
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ask-rails
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.7.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Kaka Ruto
|
|
@@ -79,6 +79,20 @@ dependencies:
|
|
|
79
79
|
- - "~>"
|
|
80
80
|
- !ruby/object:Gem::Version
|
|
81
81
|
version: '13.0'
|
|
82
|
+
- !ruby/object:Gem::Dependency
|
|
83
|
+
name: sqlite3
|
|
84
|
+
requirement: !ruby/object:Gem::Requirement
|
|
85
|
+
requirements:
|
|
86
|
+
- - ">="
|
|
87
|
+
- !ruby/object:Gem::Version
|
|
88
|
+
version: '1.4'
|
|
89
|
+
type: :development
|
|
90
|
+
prerelease: false
|
|
91
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
92
|
+
requirements:
|
|
93
|
+
- - ">="
|
|
94
|
+
- !ruby/object:Gem::Version
|
|
95
|
+
version: '1.4'
|
|
82
96
|
description: Rails generators, file conventions, and railtie for using ask-agent in
|
|
83
97
|
Rails apps.
|
|
84
98
|
email:
|
|
@@ -92,17 +106,19 @@ files:
|
|
|
92
106
|
- README.md
|
|
93
107
|
- lib/ask-rails.rb
|
|
94
108
|
- lib/ask/rails.rb
|
|
95
|
-
- lib/ask/rails/generators/install/install_generator.rb
|
|
96
|
-
- lib/ask/rails/generators/install/templates/application_agent.rb
|
|
97
|
-
- lib/ask/rails/generators/install/templates/audit_log_migration.rb
|
|
98
|
-
- lib/ask/rails/generators/install/templates/initializer.rb
|
|
99
|
-
- lib/ask/rails/generators/install/templates/state_migration.rb
|
|
100
109
|
- lib/ask/rails/railtie.rb
|
|
110
|
+
- lib/ask/rails/state.rb
|
|
101
111
|
- lib/ask/rails/version.rb
|
|
112
|
+
- lib/generators/ask/agent/templates/agent.rb
|
|
113
|
+
- lib/generators/ask/agent_generator.rb
|
|
114
|
+
- lib/generators/ask/install/templates/application_agent.rb
|
|
115
|
+
- lib/generators/ask/install/templates/application_workflow.rb
|
|
102
116
|
- lib/generators/ask/install/templates/audit_log_migration.rb
|
|
103
117
|
- lib/generators/ask/install/templates/initializer.rb
|
|
104
118
|
- lib/generators/ask/install/templates/state_migration.rb
|
|
105
119
|
- lib/generators/ask/install_generator.rb
|
|
120
|
+
- lib/generators/ask/workflow/templates/workflow.rb
|
|
121
|
+
- lib/generators/ask/workflow_generator.rb
|
|
106
122
|
homepage: https://github.com/ask-rb/ask-rails
|
|
107
123
|
licenses:
|
|
108
124
|
- MIT
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "rails/generators"
|
|
4
|
-
require "rails/generators/active_record"
|
|
5
|
-
|
|
6
|
-
module Ask
|
|
7
|
-
module Rails
|
|
8
|
-
module Generators
|
|
9
|
-
class InstallGenerator < ::Rails::Generators::Base
|
|
10
|
-
include ::Rails::Generators::Migration
|
|
11
|
-
|
|
12
|
-
source_root File.expand_path("templates", __dir__)
|
|
13
|
-
|
|
14
|
-
desc "Sets up ask-rb for Rails — creates initializer, agents directory, state and audit log migrations"
|
|
15
|
-
|
|
16
|
-
def create_initializer
|
|
17
|
-
template "initializer.rb", "config/initializers/ask.rb"
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
def create_application_agent
|
|
21
|
-
template "application_agent.rb", "app/agents/application_agent.rb"
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def create_agents_directory
|
|
25
|
-
empty_directory "app/agents"
|
|
26
|
-
create_file "app/agents/.keep", "" unless options[:skip_keep]
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def create_state_migration
|
|
30
|
-
migration_template "state_migration.rb", "db/migrate/create_ask_state.rb"
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
def create_audit_log_migration
|
|
34
|
-
migration_template "audit_log_migration.rb", "db/migrate/create_ask_audit_logs.rb"
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
private
|
|
38
|
-
|
|
39
|
-
def migration_version
|
|
40
|
-
"[#{ActiveRecord::Migration.current_version}]"
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
end
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
# Migration for ask-agent's audit log table.
|
|
4
|
-
# This table records session events (tool calls, errors, token usage, etc.)
|
|
5
|
-
# from every agent that has audit_log configured.
|
|
6
|
-
class CreateAskAuditLogs < ActiveRecord::Migration[<%= migration_version %>]
|
|
7
|
-
def change
|
|
8
|
-
create_table :ask_audit_logs, if_not_exists: true do |t|
|
|
9
|
-
t.string :session_id, null: false
|
|
10
|
-
t.string :event_type, null: false
|
|
11
|
-
t.jsonb :data, default: {}
|
|
12
|
-
t.datetime :timestamp, null: false
|
|
13
|
-
t.timestamps
|
|
14
|
-
|
|
15
|
-
t.index [:session_id, :event_type]
|
|
16
|
-
t.index :timestamp
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
end
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
# Ask Agent Configuration
|
|
4
|
-
# ========================
|
|
5
|
-
#
|
|
6
|
-
# API keys are resolved automatically by Ask::Auth:
|
|
7
|
-
# - Environment variables: OPENAI_API_KEY, ANTHROPIC_API_KEY, etc.
|
|
8
|
-
# - Rails credentials: rails credentials:edit → ask.openai, ask.anthropic
|
|
9
|
-
# - ~/.ask/credentials.yml
|
|
10
|
-
#
|
|
11
|
-
# See https://github.com/ask-rb/ask-auth for details.
|
|
12
|
-
|
|
13
|
-
# Shared key-value state store for agent sessions and graph checkpoints.
|
|
14
|
-
# Uses the ask_state table created by the migration.
|
|
15
|
-
ASK_STATE = Ask::State::PostgreSQL.new(
|
|
16
|
-
table_name: :ask_state
|
|
17
|
-
)
|
|
18
|
-
|
|
19
|
-
Ask::Agent.configure do |config|
|
|
20
|
-
# State persistence for agent sessions
|
|
21
|
-
config.state = ASK_STATE
|
|
22
|
-
|
|
23
|
-
# Audit log for session events
|
|
24
|
-
config.audit_log = { adapter: :active_record }
|
|
25
|
-
|
|
26
|
-
# config.default_model = "gpt-4o"
|
|
27
|
-
end
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
# Migration for ask-rb's shared key-value state table.
|
|
4
|
-
# Used by ask-agent (session persistence) and ask-graph (workflow checkpoints).
|
|
5
|
-
# This single table serves both components — keys are namespaced by convention.
|
|
6
|
-
class CreateAskState < ActiveRecord::Migration[<%= migration_version %>]
|
|
7
|
-
def change
|
|
8
|
-
create_table :ask_state do |t|
|
|
9
|
-
t.string :key, null: false
|
|
10
|
-
t.jsonb :value, null: false, default: {}
|
|
11
|
-
t.datetime :expires_at
|
|
12
|
-
t.timestamps
|
|
13
|
-
|
|
14
|
-
t.index :key, unique: true
|
|
15
|
-
t.index :expires_at
|
|
16
|
-
end
|
|
17
|
-
end
|
|
18
|
-
end
|
|
File without changes
|