ask-rails 0.7.0 → 0.9.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 +27 -11
- data/README.md +70 -2
- data/lib/ask/actions/backend.rb +66 -0
- data/lib/ask/actions/context.rb +26 -0
- data/lib/ask/actions/result.rb +43 -0
- data/lib/ask/actions.rb +44 -0
- data/lib/ask/rails/version.rb +1 -1
- data/lib/ask/rails.rb +1 -0
- data/lib/generators/ask/action/templates/action.rb +24 -0
- data/lib/generators/ask/action_generator.rb +59 -0
- data/lib/generators/ask/agent/templates/agent.rb +8 -4
- data/lib/generators/ask/agent/templates/instructions.md +9 -0
- data/lib/generators/ask/agent_generator.rb +4 -2
- data/lib/generators/ask/install/templates/application_action.rb +33 -0
- data/lib/generators/ask/install/templates/application_agent.rb +8 -11
- data/lib/generators/ask/install/templates/initializer.rb +10 -0
- data/lib/generators/ask/install_generator.rb +8 -0
- metadata +9 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8c73b254e032db5c313a0eeca47499a4b30152fcf725ed410e575b0ea0a9460f
|
|
4
|
+
data.tar.gz: bfe5f9b1769e4ea4b864b7dee0c6ca3d1a6587186ccac7820a71f65999f8e0f5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9c02cc27c98ea141900339703160ecfad56ed70979292a654b8b1f6c7e6562f02307abc7c02a88e319cc3f8e78b66677cfa37bee8a9fc84430f07ef3b3188ba4
|
|
7
|
+
data.tar.gz: d8f235e67a77dba643184f1f81d71f40c70be04725d24e26f6a4778b41bb6cf658f7e1a73067b9ebd88bb1552e25862dce57fe21b52653cfceb7d1db066fc32a
|
data/CHANGELOG.md
CHANGED
|
@@ -1,26 +1,42 @@
|
|
|
1
|
-
## [0.
|
|
1
|
+
## [0.9.0] — 2026-07-30
|
|
2
2
|
|
|
3
|
-
###
|
|
3
|
+
### Fixed
|
|
4
|
+
|
|
5
|
+
- **Agent generator + templates now follow the ask-agent directory convention.**
|
|
6
|
+
`rails generate ask:agent support_bot` creates `app/agents/support_bot/agent.rb`
|
|
7
|
+
+ `instructions.md` + `tools/` (the discovery layout), not a flat
|
|
8
|
+
`app/agents/support_bot.rb`. Templates no longer reference the removed
|
|
9
|
+
`system_prompt`/`tool` Definition DSL — they use `model`, `provider`,
|
|
10
|
+
`max_turns`, and `tools` (plural), with instructions auto-loaded from
|
|
11
|
+
the sibling `instructions.md`.
|
|
4
12
|
|
|
5
|
-
|
|
13
|
+
## [0.8.0] — 2026-07-31
|
|
6
14
|
|
|
7
|
-
|
|
15
|
+
### Added
|
|
8
16
|
|
|
9
|
-
-
|
|
17
|
+
- **`Ask::Actions` — action convention** — user-facing operations callable from any channel (web, Slack, voice) by name:
|
|
10
18
|
|
|
11
19
|
```ruby
|
|
12
|
-
|
|
13
|
-
Ask::Graph.storage = ASK_STATE
|
|
14
|
-
end
|
|
20
|
+
Ask::Actions.dispatch(action: "chats.create", context: context, params: {})
|
|
15
21
|
```
|
|
16
22
|
|
|
17
|
-
|
|
23
|
+
- `Ask::Actions::Result` — uniform response shape (`ok`/`error`, `message`, `data`, `code`)
|
|
24
|
+
- `Ask::Actions::Context` — per-request context bag; attributes become accessors
|
|
25
|
+
- `Ask::Actions::Backend` — dispatcher with explicit registration (`Ask::Actions.register`) and convention resolution (`"chats.create"` → `Chats::Create` via Zeitwerk). Unknown actions raise `Ask::Actions::Backend::UnknownAction` with guidance.
|
|
26
|
+
|
|
27
|
+
- **`ask:action NAME [NAMESPACE]` generator** — scaffolds actions:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
rails generate ask:action create_workspace # app/actions/create_workspace.rb
|
|
31
|
+
rails generate ask:action chats create # app/actions/chats/create.rb → "chats.create"
|
|
32
|
+
```
|
|
18
33
|
|
|
19
|
-
-
|
|
34
|
+
- **Actions in install generator** — `ask:install` now creates `app/actions/` and `app/actions/application_action.rb` (base class with `call(context:, params:)` → `#call`). The initializer documents the actions block with a registration example.
|
|
20
35
|
|
|
21
36
|
### Tested
|
|
22
37
|
|
|
23
|
-
-
|
|
38
|
+
- 25 new tests (Result, Context, Backend, generators) — 48 runs, 141 assertions, 0 failures.
|
|
39
|
+
- End-to-end smoke test in a fresh Rails 8.1 app: `ask:install` + `ask:action` (namespaced and top-level) run; `Ask::Actions.dispatch` resolves both via convention; unknown actions raise with a helpful message.
|
|
24
40
|
|
|
25
41
|
## [0.7.0] — 2026-07-31
|
|
26
42
|
|
data/README.md
CHANGED
|
@@ -24,6 +24,7 @@ This creates:
|
|
|
24
24
|
|---|---|
|
|
25
25
|
| `config/initializers/ask.rb` | Agent + workflow configuration (graph block only generated when ask-graph is installed) |
|
|
26
26
|
| `app/agents/application_agent.rb` | Base class for your agents |
|
|
27
|
+
| `app/actions/application_action.rb` | Base class for your actions |
|
|
27
28
|
| `app/workflows/application_workflow.rb` | Base class for your workflows (only with ask-graph) |
|
|
28
29
|
| `db/migrate/*_create_ask_state.rb` | Shared key-value state table — workflow checkpoints and agent session indexes |
|
|
29
30
|
| `db/migrate/*_create_ask_audit_logs.rb` | Agent session audit log |
|
|
@@ -40,16 +41,28 @@ Scaffolds a new agent:
|
|
|
40
41
|
rails generate ask:agent support_bot
|
|
41
42
|
```
|
|
42
43
|
|
|
43
|
-
Creates
|
|
44
|
+
Creates the agent directory convention used by ask-agent discovery:
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
app/agents/support_bot/
|
|
48
|
+
├── agent.rb # class Agents::SupportBot < ApplicationAgent
|
|
49
|
+
├── instructions.md # auto-loaded as the system prompt
|
|
50
|
+
└── tools/ # per-agent tools (referenced with `tools :tool_name`)
|
|
51
|
+
```
|
|
44
52
|
|
|
45
53
|
```ruby
|
|
54
|
+
# app/agents/support_bot/agent.rb
|
|
46
55
|
module Agents
|
|
47
56
|
class SupportBot < ApplicationAgent
|
|
48
|
-
|
|
57
|
+
model "gpt-4o"
|
|
58
|
+
# tools :search_knowledge_base
|
|
49
59
|
end
|
|
50
60
|
end
|
|
51
61
|
```
|
|
52
62
|
|
|
63
|
+
Run it with `Ask::Agent.new("support_bot")`. The directory name is the
|
|
64
|
+
agent name — keep the file named `agent.rb`.
|
|
65
|
+
|
|
53
66
|
### `ask:workflow NAME`
|
|
54
67
|
|
|
55
68
|
Scaffolds a new workflow (requires ask-graph):
|
|
@@ -68,6 +81,27 @@ module NotifyCustomer
|
|
|
68
81
|
end
|
|
69
82
|
```
|
|
70
83
|
|
|
84
|
+
### `ask:action NAME [NAMESPACE]`
|
|
85
|
+
|
|
86
|
+
Scaffolds a new action — a user-facing operation callable from any channel (web, Slack, voice) by name:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
rails generate ask:action create_workspace # app/actions/create_workspace.rb
|
|
90
|
+
rails generate ask:action chats create # app/actions/chats/create.rb
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Creates `app/actions/chats/create.rb`:
|
|
94
|
+
|
|
95
|
+
```ruby
|
|
96
|
+
module Chats
|
|
97
|
+
class Create < ApplicationAction
|
|
98
|
+
def call
|
|
99
|
+
Ask::Actions::Result.ok(message: "Chat created", data: { id: record.id })
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
```
|
|
104
|
+
|
|
71
105
|
### Skipping workflow scaffolding
|
|
72
106
|
|
|
73
107
|
If you don't use ask-graph, install with:
|
|
@@ -132,6 +166,40 @@ result.payment
|
|
|
132
166
|
|
|
133
167
|
Checkpoints are saved to the shared `ask_state` table, so workflows resume after crashes.
|
|
134
168
|
|
|
169
|
+
### Actions
|
|
170
|
+
|
|
171
|
+
Actions are the operations your users trigger — booking an appointment, creating a chat, upgrading a plan. They live in `app/actions/` and are dispatched **by name**, so any channel (web controller, Slack handler, voice agent) calls the same operation:
|
|
172
|
+
|
|
173
|
+
```ruby
|
|
174
|
+
# app/actions/chats/create.rb
|
|
175
|
+
module Chats
|
|
176
|
+
class Create < ApplicationAction
|
|
177
|
+
def call
|
|
178
|
+
chat = context.channel.start_new_chat!
|
|
179
|
+
Ask::Actions::Result.ok(message: "Chat created", data: { chat: chat })
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
```ruby
|
|
186
|
+
# From any channel:
|
|
187
|
+
context = Ask::Actions::Context.new(user: current_user, session: session)
|
|
188
|
+
result = Ask::Actions.dispatch(action: "chats.create", context: context, params: { name: "general" })
|
|
189
|
+
result.ok? # => true
|
|
190
|
+
result.message # => "Chat created"
|
|
191
|
+
result.data # => { chat: ... }
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
By convention, `"chats.create"` resolves to `Chats::Create` — no registration needed (Zeitwerk autoloads `app/actions/`). Register explicitly to override the convention or to list actions in `Ask::Actions.available`:
|
|
195
|
+
|
|
196
|
+
```ruby
|
|
197
|
+
# config/initializers/ask.rb
|
|
198
|
+
Ask::Actions.register "chats.create", Chats::Create
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
Unknown action names raise `Ask::Actions::Backend::UnknownAction` with a helpful message.
|
|
202
|
+
|
|
135
203
|
## Configuration
|
|
136
204
|
|
|
137
205
|
API keys are resolved automatically by `Ask::Auth`:
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ask
|
|
4
|
+
module Actions
|
|
5
|
+
# Dispatches named actions to their classes.
|
|
6
|
+
#
|
|
7
|
+
# Resolution order:
|
|
8
|
+
# 1. Explicitly registered actions (Ask::Actions.register)
|
|
9
|
+
# 2. Convention: "chats.create" resolves to Chats::Create
|
|
10
|
+
#
|
|
11
|
+
# Convention resolution works with Zeitwerk — app/actions/chats/create.rb
|
|
12
|
+
# is autoloaded as Chats::Create with no configuration.
|
|
13
|
+
class Backend
|
|
14
|
+
class UnknownAction < StandardError
|
|
15
|
+
def initialize(action)
|
|
16
|
+
super(<<~MSG.strip)
|
|
17
|
+
Unknown action #{action.inspect}. Define it at app/actions/ \
|
|
18
|
+
(e.g. app/actions/chats/create.rb for "chats.create") or register \
|
|
19
|
+
it with Ask::Actions.register.
|
|
20
|
+
MSG
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
CONVENTION = /\A[a-z0-9_.]+\z/
|
|
25
|
+
|
|
26
|
+
class << self
|
|
27
|
+
def register(action, klass)
|
|
28
|
+
registered[action.to_s] = klass
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def registered
|
|
32
|
+
@registered ||= {}
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def reset!
|
|
36
|
+
@registered = {}
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def available
|
|
40
|
+
registered.keys
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def dispatch(action:, context:, params: {})
|
|
44
|
+
resolve(action).call(context: context, params: params)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def resolve(action)
|
|
48
|
+
name = action.to_s
|
|
49
|
+
registered[name] || resolve_by_convention(name) || unknown_action!(name)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
def resolve_by_convention(name)
|
|
55
|
+
return nil unless name.match?(CONVENTION)
|
|
56
|
+
|
|
57
|
+
name.split(".").map { |part| part.split("_").map(&:capitalize).join }.join("::").safe_constantize
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def unknown_action!(name)
|
|
61
|
+
raise UnknownAction, name
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ask
|
|
4
|
+
module Actions
|
|
5
|
+
# A per-request bag of context. Channel adapters construct it with
|
|
6
|
+
# whatever the app needs — user, session, workspace, channel, voice call —
|
|
7
|
+
# and actions read from it. Attributes become accessors on the instance.
|
|
8
|
+
#
|
|
9
|
+
# @example
|
|
10
|
+
# context = Ask::Actions::Context.new(user: user, session: session, workspace: workspace)
|
|
11
|
+
# context.user # => user
|
|
12
|
+
# context.workspace # => workspace
|
|
13
|
+
class Context
|
|
14
|
+
def initialize(**attributes)
|
|
15
|
+
attributes.each { |name, value| define_attribute(name, value) }
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def define_attribute(name, value)
|
|
21
|
+
singleton_class.attr_accessor(name)
|
|
22
|
+
public_send("#{name}=", value)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ask
|
|
4
|
+
module Actions
|
|
5
|
+
# The uniform response shape every action returns. Channel adapters
|
|
6
|
+
# (web controllers, Slack handlers, voice agents) consume the same
|
|
7
|
+
# contract regardless of which surface a request came from.
|
|
8
|
+
#
|
|
9
|
+
# @example
|
|
10
|
+
# Ask::Actions::Result.ok(message: "Booking confirmed", data: { booking: booking })
|
|
11
|
+
# Ask::Actions::Result.error(message: "Slot unavailable", code: :slot_taken)
|
|
12
|
+
class Result
|
|
13
|
+
attr_reader :ok, :message, :data, :code
|
|
14
|
+
|
|
15
|
+
def initialize(ok:, message:, data: {}, code: nil)
|
|
16
|
+
@ok = ok
|
|
17
|
+
@message = message
|
|
18
|
+
@data = data
|
|
19
|
+
@code = code
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def ok?
|
|
23
|
+
ok
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def error?
|
|
27
|
+
!ok
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def to_h
|
|
31
|
+
{ ok: ok, message: message, data: data, code: code }
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def self.ok(message: "", data: {}, code: nil)
|
|
35
|
+
new(ok: true, message: message, data: data, code: code)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def self.error(message: "", data: {}, code: nil)
|
|
39
|
+
new(ok: false, message: message, data: data, code: code)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
data/lib/ask/actions.rb
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "actions/result"
|
|
4
|
+
require_relative "actions/context"
|
|
5
|
+
require_relative "actions/backend"
|
|
6
|
+
|
|
7
|
+
module Ask
|
|
8
|
+
# Actions — a convention for user-facing operations callable from any channel.
|
|
9
|
+
#
|
|
10
|
+
# Every action lives in app/actions/, responds to .call(context:, params:),
|
|
11
|
+
# and returns an Ask::Actions::Result. Dispatch them by name so any channel —
|
|
12
|
+
# web, Slack, voice — can invoke the same operation:
|
|
13
|
+
#
|
|
14
|
+
# Ask::Actions.dispatch(action: "chats.create", context: context, params: {})
|
|
15
|
+
#
|
|
16
|
+
# By convention, "chats.create" resolves to Chats::Create (app/actions/chats/create.rb).
|
|
17
|
+
# Register explicitly to override the convention or to list actions in
|
|
18
|
+
# Ask::Actions.available:
|
|
19
|
+
#
|
|
20
|
+
# Ask::Actions.register "chats.create", Chats::Create
|
|
21
|
+
module Actions
|
|
22
|
+
class << self
|
|
23
|
+
def register(action, klass)
|
|
24
|
+
Backend.register(action, klass)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def dispatch(action:, context:, params: {})
|
|
28
|
+
Backend.dispatch(action: action, context: context, params: params)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def resolve(action)
|
|
32
|
+
Backend.resolve(action)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def available
|
|
36
|
+
Backend.available
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def reset!
|
|
40
|
+
Backend.reset!
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
data/lib/ask/rails/version.rb
CHANGED
data/lib/ask/rails.rb
CHANGED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# <%= dispatch_name %> action.
|
|
4
|
+
#
|
|
5
|
+
# Dispatch it from any channel — web, Slack, voice:
|
|
6
|
+
# Ask::Actions.dispatch(action: "<%= dispatch_name %>", context: context, params: {})
|
|
7
|
+
#
|
|
8
|
+
# Return an Ask::Actions::Result:
|
|
9
|
+
# Ask::Actions::Result.ok(message: "Done", data: { id: record.id })
|
|
10
|
+
<% if namespaced? -%>
|
|
11
|
+
module <%= namespace_class_name %>
|
|
12
|
+
class <%= action_class_name %> < ApplicationAction
|
|
13
|
+
# def call
|
|
14
|
+
# Ask::Actions::Result.ok(message: "Done")
|
|
15
|
+
# end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
<% else -%>
|
|
19
|
+
class <%= action_class_name %> < ApplicationAction
|
|
20
|
+
# def call
|
|
21
|
+
# Ask::Actions::Result.ok(message: "Done")
|
|
22
|
+
# end
|
|
23
|
+
end
|
|
24
|
+
<% end -%>
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators"
|
|
4
|
+
|
|
5
|
+
module Ask
|
|
6
|
+
module Generators
|
|
7
|
+
# Creates an action under app/actions. Actions are operations callable
|
|
8
|
+
# from any channel by name — dispatch them with Ask::Actions.dispatch.
|
|
9
|
+
#
|
|
10
|
+
# rails generate ask:action create_workspace
|
|
11
|
+
# # => app/actions/create_workspace.rb (dispatch name: "create_workspace")
|
|
12
|
+
#
|
|
13
|
+
# rails generate ask:action chats create
|
|
14
|
+
# # => app/actions/chats/create.rb (dispatch name: "chats.create")
|
|
15
|
+
class ActionGenerator < ::Rails::Generators::NamedBase
|
|
16
|
+
source_root File.expand_path("action/templates", __dir__)
|
|
17
|
+
|
|
18
|
+
desc "Creates an action under app/actions — a user-facing operation callable from any channel"
|
|
19
|
+
|
|
20
|
+
def create_action
|
|
21
|
+
template "action.rb", action_path
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def action_path
|
|
27
|
+
if namespaced?
|
|
28
|
+
"app/actions/#{namespace_name}/#{action_name}.rb"
|
|
29
|
+
else
|
|
30
|
+
"app/actions/#{action_name}.rb"
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def namespaced?
|
|
35
|
+
args.any?
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def namespace_name
|
|
39
|
+
file_name
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def action_name
|
|
43
|
+
namespaced? ? args.first.underscore : file_name
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def namespace_class_name
|
|
47
|
+
namespace_name.camelize
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def action_class_name
|
|
51
|
+
action_name.camelize
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def dispatch_name
|
|
55
|
+
namespaced? ? "#{namespace_name}.#{action_name}" : action_name
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -6,13 +6,17 @@
|
|
|
6
6
|
# agent = Ask::Agent.new("<%= file_name %>")
|
|
7
7
|
# response = agent.run("Hello")
|
|
8
8
|
#
|
|
9
|
+
# Instructions load automatically from instructions.md next to this file.
|
|
10
|
+
# Per-agent tools live in tools/ — reference them with `tools :tool_name`.
|
|
11
|
+
#
|
|
9
12
|
module Agents
|
|
10
13
|
class <%= class_name %> < ApplicationAgent
|
|
11
14
|
# model "gpt-4o"
|
|
12
|
-
|
|
15
|
+
# provider :openai
|
|
16
|
+
# max_turns 25
|
|
13
17
|
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
18
|
+
# tools :bash
|
|
19
|
+
# tools :read
|
|
20
|
+
# tools :grep
|
|
17
21
|
end
|
|
18
22
|
end
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# <%= class_name %>
|
|
2
|
+
|
|
3
|
+
You are a helpful assistant. Describe what this agent does and how it
|
|
4
|
+
should behave. This file is auto-loaded as the system prompt.
|
|
5
|
+
|
|
6
|
+
## Guidelines
|
|
7
|
+
|
|
8
|
+
- Be concise and direct.
|
|
9
|
+
- Use the tools available to you when you need information.
|
|
@@ -7,10 +7,12 @@ module Ask
|
|
|
7
7
|
class AgentGenerator < ::Rails::Generators::NamedBase
|
|
8
8
|
source_root File.expand_path("agent/templates", __dir__)
|
|
9
9
|
|
|
10
|
-
desc "Creates an agent under app/agents —
|
|
10
|
+
desc "Creates an agent under app/agents/<name>/ — the ask-agent directory convention with agent.rb, instructions.md, and tools/"
|
|
11
11
|
|
|
12
12
|
def create_agent
|
|
13
|
-
template "agent.rb", "app/agents/#{file_name}.rb"
|
|
13
|
+
template "agent.rb", "app/agents/#{file_name}/agent.rb"
|
|
14
|
+
template "instructions.md", "app/agents/#{file_name}/instructions.md"
|
|
15
|
+
empty_directory "app/agents/#{file_name}/tools"
|
|
14
16
|
end
|
|
15
17
|
end
|
|
16
18
|
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Base class for your application's actions.
|
|
4
|
+
#
|
|
5
|
+
# Subclass this to define actions — operations callable from any channel
|
|
6
|
+
# (web, Slack, voice) by name:
|
|
7
|
+
#
|
|
8
|
+
# module Chats
|
|
9
|
+
# class Create < ApplicationAction
|
|
10
|
+
# def call
|
|
11
|
+
# chat = context.channel.start_new_chat!
|
|
12
|
+
# Ask::Actions::Result.ok(message: "Chat created", data: { chat: chat })
|
|
13
|
+
# end
|
|
14
|
+
# end
|
|
15
|
+
# end
|
|
16
|
+
#
|
|
17
|
+
# Then dispatch from anywhere:
|
|
18
|
+
# Ask::Actions.dispatch(action: "chats.create", context: context, params: {})
|
|
19
|
+
#
|
|
20
|
+
class ApplicationAction
|
|
21
|
+
def self.call(context:, params: {})
|
|
22
|
+
new(context: context, params: params).call
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def initialize(context:, params: {})
|
|
26
|
+
@context = context
|
|
27
|
+
@params = params
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
attr_reader :context, :params
|
|
33
|
+
end
|
|
@@ -4,23 +4,20 @@
|
|
|
4
4
|
#
|
|
5
5
|
# Subclass this to define agents:
|
|
6
6
|
#
|
|
7
|
-
#
|
|
7
|
+
# # app/agents/support_bot/agent.rb
|
|
8
|
+
# class SupportBotAgent < ApplicationAgent
|
|
8
9
|
# model "gpt-4o"
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
# tool :search_knowledge_base
|
|
10
|
+
# tools :search_knowledge_base
|
|
12
11
|
# end
|
|
13
12
|
#
|
|
13
|
+
# # app/agents/support_bot/instructions.md — auto-loaded as the system prompt
|
|
14
|
+
# # app/agents/support_bot/tools/search_knowledge_base.rb — per-agent tools
|
|
15
|
+
#
|
|
14
16
|
# Then run:
|
|
15
17
|
# agent = Ask::Agent.new("support_bot")
|
|
16
18
|
# agent.run("How do I reset my password?")
|
|
17
19
|
#
|
|
18
20
|
class ApplicationAgent < Ask::Agent::Definition
|
|
19
|
-
# Default instructions — override
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
# Uncomment to add built-in tools:
|
|
23
|
-
# tool :bash
|
|
24
|
-
# tool :read
|
|
25
|
-
# tool :grep
|
|
21
|
+
# Default instructions — override by adding an instructions.md file
|
|
22
|
+
# in the agent directory, or with option :system_prompt, "..."
|
|
26
23
|
end
|
|
@@ -31,3 +31,13 @@ if defined?(Ask::Graph)
|
|
|
31
31
|
# Ask::Graph.default_step_timeout 30
|
|
32
32
|
# Ask::Graph.default_workflow_timeout 60
|
|
33
33
|
end
|
|
34
|
+
|
|
35
|
+
# Actions
|
|
36
|
+
#
|
|
37
|
+
# Actions live in app/actions/ and are dispatched by name from any channel:
|
|
38
|
+
# Ask::Actions.dispatch(action: "chats.create", context: context, params: {})
|
|
39
|
+
#
|
|
40
|
+
# By convention, "chats.create" resolves to Chats::Create
|
|
41
|
+
# (app/actions/chats/create.rb). Register explicitly to override the
|
|
42
|
+
# convention or to list the action in Ask::Actions.available:
|
|
43
|
+
# Ask::Actions.register "chats.create", Chats::Create
|
|
@@ -27,6 +27,14 @@ module Ask
|
|
|
27
27
|
template "application_agent.rb", "app/agents/application_agent.rb"
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
+
def create_actions_directory
|
|
31
|
+
empty_directory "app/actions"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def create_application_action
|
|
35
|
+
template "application_action.rb", "app/actions/application_action.rb"
|
|
36
|
+
end
|
|
37
|
+
|
|
30
38
|
def create_workflows_directory
|
|
31
39
|
return if skip_graph?
|
|
32
40
|
empty_directory "app/workflows"
|
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.9.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Kaka Ruto
|
|
@@ -105,12 +105,20 @@ files:
|
|
|
105
105
|
- LICENSE
|
|
106
106
|
- README.md
|
|
107
107
|
- lib/ask-rails.rb
|
|
108
|
+
- lib/ask/actions.rb
|
|
109
|
+
- lib/ask/actions/backend.rb
|
|
110
|
+
- lib/ask/actions/context.rb
|
|
111
|
+
- lib/ask/actions/result.rb
|
|
108
112
|
- lib/ask/rails.rb
|
|
109
113
|
- lib/ask/rails/railtie.rb
|
|
110
114
|
- lib/ask/rails/state.rb
|
|
111
115
|
- lib/ask/rails/version.rb
|
|
116
|
+
- lib/generators/ask/action/templates/action.rb
|
|
117
|
+
- lib/generators/ask/action_generator.rb
|
|
112
118
|
- lib/generators/ask/agent/templates/agent.rb
|
|
119
|
+
- lib/generators/ask/agent/templates/instructions.md
|
|
113
120
|
- lib/generators/ask/agent_generator.rb
|
|
121
|
+
- lib/generators/ask/install/templates/application_action.rb
|
|
114
122
|
- lib/generators/ask/install/templates/application_agent.rb
|
|
115
123
|
- lib/generators/ask/install/templates/application_workflow.rb
|
|
116
124
|
- lib/generators/ask/install/templates/audit_log_migration.rb
|