envoy_ai 0.0.1 → 0.0.2
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/README.md +181 -2
- data/app/assets/stylesheets/envoy/chat.css +209 -0
- data/app/assets/stylesheets/envoy/console.css +50 -176
- data/app/controllers/envoy/conversations_controller.rb +1 -4
- data/app/controllers/envoy/panels_controller.rb +30 -0
- data/app/javascript/envoy/controllers/autoscroll_controller.js +26 -32
- data/app/jobs/envoy/run_job.rb +25 -4
- data/app/models/envoy/conversation.rb +33 -6
- data/app/views/envoy/conversations/_composer_input.html.erb +1 -1
- data/app/views/envoy/conversations/index.html.erb +0 -2
- data/app/views/envoy/conversations/new.html.erb +1 -7
- data/app/views/envoy/conversations/show.html.erb +1 -17
- data/app/views/envoy/messages/_error.html.erb +6 -0
- data/app/views/envoy/messages/_streaming.html.erb +1 -1
- data/app/views/envoy/messages/create.turbo_stream.erb +5 -5
- data/app/views/envoy/panels/_panel.html.erb +3 -0
- data/app/views/envoy/panels/_transcript.html.erb +21 -0
- data/config/routes.rb +1 -1
- data/db/migrate/20260716000001_drop_envoy_system_prompts.rb +12 -0
- data/db/migrate/20260716000002_add_surface_to_envoy_conversations.rb +15 -0
- data/lib/envoy/engine.rb +6 -0
- data/lib/envoy/errors.rb +4 -0
- data/lib/envoy/guard.rb +13 -1
- data/lib/envoy/library.rb +82 -0
- data/lib/envoy/llm.rb +1 -1
- data/lib/envoy/page_surface.rb +34 -0
- data/lib/envoy/prompt.rb +55 -0
- data/lib/envoy/runner.rb +17 -3
- data/lib/envoy/surface.rb +90 -0
- data/lib/envoy/tool_definition.rb +12 -2
- data/lib/envoy/version.rb +1 -1
- data/lib/envoy.rb +59 -0
- metadata +12 -12
- data/app/controllers/envoy/system_prompts_controller.rb +0 -52
- data/app/models/envoy/system_prompt.rb +0 -24
- data/app/models/envoy/system_prompt_version.rb +0 -14
- data/app/views/envoy/system_prompts/_form.html.erb +0 -14
- data/app/views/envoy/system_prompts/edit.html.erb +0 -2
- data/app/views/envoy/system_prompts/index.html.erb +0 -14
- data/app/views/envoy/system_prompts/new.html.erb +0 -2
- data/app/views/envoy/system_prompts/show.html.erb +0 -15
- data/db/migrate/20260711000001_create_envoy_system_prompts.rb +0 -10
- data/db/migrate/20260711000002_create_envoy_system_prompt_versions.rb +0 -13
- data/db/migrate/20260711000003_add_system_prompt_version_to_envoy_conversations.rb +0 -6
data/lib/envoy/runner.rb
CHANGED
|
@@ -22,7 +22,7 @@ module Envoy
|
|
|
22
22
|
|
|
23
23
|
def toolset = @conversation.toolset
|
|
24
24
|
|
|
25
|
-
def read_only? = @conversation.
|
|
25
|
+
def read_only? = @conversation.read_only?
|
|
26
26
|
|
|
27
27
|
def compiled_tools
|
|
28
28
|
toolset.tools_for(read_only: read_only?).map do |definition|
|
|
@@ -30,9 +30,23 @@ module Envoy
|
|
|
30
30
|
end
|
|
31
31
|
end
|
|
32
32
|
|
|
33
|
+
# Ordered stable-prefix-first: the leading segments are identical across
|
|
34
|
+
# turns and so are cacheable, while context is re-resolved every turn and
|
|
35
|
+
# never is. A resumed conversation outlives the page state it started with,
|
|
36
|
+
# which is exactly why context cannot be a snapshot.
|
|
33
37
|
def instructions
|
|
34
|
-
[ Envoy.config.system_preamble,
|
|
35
|
-
|
|
38
|
+
[ Envoy.config.system_preamble,
|
|
39
|
+
toolset.description,
|
|
40
|
+
ERROR_CONVENTION,
|
|
41
|
+
@conversation.prompt_body,
|
|
42
|
+
surface_context,
|
|
43
|
+
@conversation.effective_system_prompt ].compact_blank.join("\n\n")
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def surface_context
|
|
47
|
+
surface = @conversation.surface
|
|
48
|
+
return nil unless surface
|
|
49
|
+
surface.context_for(actor: @conversation.actor, key: @conversation.context_key)
|
|
36
50
|
end
|
|
37
51
|
|
|
38
52
|
def backfill_statuses!
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
module Envoy
|
|
2
|
+
# Binds a page to an agent: which tools, which prompt, which model, and how to
|
|
3
|
+
# describe what the user is looking at.
|
|
4
|
+
#
|
|
5
|
+
# A surface is the source of truth for a conversation opened against it —
|
|
6
|
+
# toolset, prompt and model are read here on every turn, so editing a surface
|
|
7
|
+
# reaches conversations that already exist.
|
|
8
|
+
#
|
|
9
|
+
# References are stored as keys and resolved lazily (Envoy.validate! checks
|
|
10
|
+
# them in one pass at boot), so definition load order does not matter.
|
|
11
|
+
class Surface
|
|
12
|
+
attr_reader :key
|
|
13
|
+
|
|
14
|
+
def initialize(key)
|
|
15
|
+
@key = key.to_s
|
|
16
|
+
@toolset_key = nil
|
|
17
|
+
@model_id = nil
|
|
18
|
+
@read_only = false
|
|
19
|
+
@prompt = nil
|
|
20
|
+
@context_block = nil
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# --- DSL ---
|
|
24
|
+
def toolset(name = nil)
|
|
25
|
+
return @toolset_key if name.nil?
|
|
26
|
+
@toolset_key = name.to_s
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def model(id = nil)
|
|
30
|
+
return @model_id if id.nil?
|
|
31
|
+
@model_id = id
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def read_only(value = nil)
|
|
35
|
+
return @read_only if value.nil?
|
|
36
|
+
@read_only = value
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# A registry key (Symbol) or a literal String.
|
|
40
|
+
def system_prompt(ref = nil)
|
|
41
|
+
return @prompt if ref.nil?
|
|
42
|
+
@prompt = ref
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Runs on every turn, in the job — so it gets actor and key and nothing else.
|
|
46
|
+
# No request, no params, no session. This is also where a host enforces that
|
|
47
|
+
# this actor may see this subject: raise Envoy::Forbidden and Guard handles it.
|
|
48
|
+
def context(&block)
|
|
49
|
+
@context_block = block
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# --- introspection ---
|
|
53
|
+
def toolset_key = @toolset_key
|
|
54
|
+
def model_id = @model_id
|
|
55
|
+
def read_only? = !!@read_only
|
|
56
|
+
|
|
57
|
+
def prompt_body
|
|
58
|
+
case @prompt
|
|
59
|
+
when nil then nil
|
|
60
|
+
when String then @prompt
|
|
61
|
+
else Envoy.prompt(@prompt).full_body
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def context_for(actor:, key:)
|
|
66
|
+
return nil unless @context_block
|
|
67
|
+
@context_block.call(actor: actor, key: key)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Resolve every lazy reference, naming this surface in any failure.
|
|
71
|
+
def validate!
|
|
72
|
+
raise Envoy::Error, "surface #{key.inspect} has no toolset" if @toolset_key.blank?
|
|
73
|
+
|
|
74
|
+
begin
|
|
75
|
+
# tools_for walks the whole `use` graph, not just this toolset's own
|
|
76
|
+
# tools — so a typo in a composed toolset's `use` list fails here,
|
|
77
|
+
# at boot, instead of surfacing mid-conversation inside RunJob.
|
|
78
|
+
Envoy.toolset(@toolset_key).tools_for
|
|
79
|
+
rescue Envoy::UnknownToolset, Envoy::ToolsetCycle => e
|
|
80
|
+
raise e.class, "surface #{key.inspect}: #{e.message}"
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
begin
|
|
84
|
+
prompt_body
|
|
85
|
+
rescue Envoy::UnknownPrompt => e
|
|
86
|
+
raise Envoy::UnknownPrompt, "surface #{key.inspect}: #{e.message}"
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
@@ -23,8 +23,18 @@ module Envoy
|
|
|
23
23
|
@access = level
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
# enum declares a closed vocabulary for a param. RubyLLM::Parameter has no
|
|
27
|
+
# enum support (its initializer would raise on the keyword), so the values
|
|
28
|
+
# are folded into the description the model reads, and Guard enforces them —
|
|
29
|
+
# yielding the same {"error":..,"type":"invalid"} shape as any other bad argument.
|
|
30
|
+
def param(name, desc, type: :string, required: true, enum: nil)
|
|
31
|
+
enum = enum&.map(&:to_s)
|
|
32
|
+
desc = "#{desc} One of: #{enum.join(', ')}." if enum
|
|
33
|
+
@params << { name: name.to_sym, desc: desc, type: type, required: required, enum: enum }
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def enum_for(param_name)
|
|
37
|
+
@params.find { |p| p[:name] == param_name.to_sym }&.fetch(:enum)
|
|
28
38
|
end
|
|
29
39
|
|
|
30
40
|
def perform(&block)
|
data/lib/envoy/version.rb
CHANGED
data/lib/envoy.rb
CHANGED
|
@@ -8,6 +8,10 @@ require "envoy/engine"
|
|
|
8
8
|
require "envoy/errors"
|
|
9
9
|
require "envoy/tool_definition"
|
|
10
10
|
require "envoy/toolset"
|
|
11
|
+
require "envoy/prompt"
|
|
12
|
+
require "envoy/library"
|
|
13
|
+
require "envoy/surface"
|
|
14
|
+
require "envoy/page_surface"
|
|
11
15
|
require "envoy/guard"
|
|
12
16
|
require "envoy/compiled_tool"
|
|
13
17
|
require "envoy/llm"
|
|
@@ -41,5 +45,60 @@ module Envoy
|
|
|
41
45
|
def toolset(key)
|
|
42
46
|
toolsets.fetch(key.to_s) { raise UnknownToolset, "no toolset #{key.inspect}" }
|
|
43
47
|
end
|
|
48
|
+
|
|
49
|
+
def prompts
|
|
50
|
+
@prompts ||= {}
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def define_prompt(key, &block)
|
|
54
|
+
prompt = Prompt.new(key)
|
|
55
|
+
prompt.instance_eval(&block)
|
|
56
|
+
prompts[prompt.key] = prompt
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def prompt(key)
|
|
60
|
+
prompts.fetch(key.to_s) { raise UnknownPrompt, "no prompt #{key.inspect}" }
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def libraries
|
|
64
|
+
@libraries ||= {}
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Registers both the Library (for introspection) and its compiled Toolset
|
|
68
|
+
# under the same key, so `use :principles` works like any other toolset.
|
|
69
|
+
def define_library(key, &block)
|
|
70
|
+
library = Library.new(key)
|
|
71
|
+
library.instance_eval(&block)
|
|
72
|
+
libraries[library.key] = library
|
|
73
|
+
toolsets[library.key] = library.to_toolset
|
|
74
|
+
library
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def library(key)
|
|
78
|
+
libraries.fetch(key.to_s) { raise UnknownLibrary, "no library #{key.inspect}" }
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def surfaces
|
|
82
|
+
@surfaces ||= {}
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def define_surface(key, &block)
|
|
86
|
+
surface = Surface.new(key)
|
|
87
|
+
surface.instance_eval(&block)
|
|
88
|
+
surfaces[surface.key] = surface
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def surface(key)
|
|
92
|
+
surfaces.fetch(key.to_s) { raise UnknownSurface, "no surface #{key.inspect}" }
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Resolve every surface's lazy references in one pass. Hosts call this at the
|
|
96
|
+
# end of the to_prepare block that loads their definitions, so a typo fails
|
|
97
|
+
# on boot rather than inside a job — where the failure is invisible and the
|
|
98
|
+
# user watches "working…" forever.
|
|
99
|
+
def validate!
|
|
100
|
+
surfaces.each_value(&:validate!)
|
|
101
|
+
true
|
|
102
|
+
end
|
|
44
103
|
end
|
|
45
104
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: envoy_ai
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0.
|
|
4
|
+
version: 0.0.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Travis Petticrew
|
|
@@ -62,11 +62,12 @@ extra_rdoc_files: []
|
|
|
62
62
|
files:
|
|
63
63
|
- MIT-LICENSE
|
|
64
64
|
- README.md
|
|
65
|
+
- app/assets/stylesheets/envoy/chat.css
|
|
65
66
|
- app/assets/stylesheets/envoy/console.css
|
|
66
67
|
- app/controllers/envoy/application_controller.rb
|
|
67
68
|
- app/controllers/envoy/conversations_controller.rb
|
|
68
69
|
- app/controllers/envoy/messages_controller.rb
|
|
69
|
-
- app/controllers/envoy/
|
|
70
|
+
- app/controllers/envoy/panels_controller.rb
|
|
70
71
|
- app/javascript/envoy/controllers/autoscroll_controller.js
|
|
71
72
|
- app/javascript/envoy/controllers/composer_controller.js
|
|
72
73
|
- app/jobs/envoy/application_job.rb
|
|
@@ -74,30 +75,25 @@ files:
|
|
|
74
75
|
- app/models/envoy/application_record.rb
|
|
75
76
|
- app/models/envoy/conversation.rb
|
|
76
77
|
- app/models/envoy/message.rb
|
|
77
|
-
- app/models/envoy/system_prompt.rb
|
|
78
|
-
- app/models/envoy/system_prompt_version.rb
|
|
79
78
|
- app/models/envoy/tool_call.rb
|
|
80
79
|
- app/views/envoy/conversations/_composer_input.html.erb
|
|
81
80
|
- app/views/envoy/conversations/index.html.erb
|
|
82
81
|
- app/views/envoy/conversations/new.html.erb
|
|
83
82
|
- app/views/envoy/conversations/show.html.erb
|
|
83
|
+
- app/views/envoy/messages/_error.html.erb
|
|
84
84
|
- app/views/envoy/messages/_message.html.erb
|
|
85
85
|
- app/views/envoy/messages/_streaming.html.erb
|
|
86
86
|
- app/views/envoy/messages/_tool_call.html.erb
|
|
87
87
|
- app/views/envoy/messages/create.turbo_stream.erb
|
|
88
|
-
- app/views/envoy/
|
|
89
|
-
- app/views/envoy/
|
|
90
|
-
- app/views/envoy/system_prompts/index.html.erb
|
|
91
|
-
- app/views/envoy/system_prompts/new.html.erb
|
|
92
|
-
- app/views/envoy/system_prompts/show.html.erb
|
|
88
|
+
- app/views/envoy/panels/_panel.html.erb
|
|
89
|
+
- app/views/envoy/panels/_transcript.html.erb
|
|
93
90
|
- app/views/layouts/envoy/application.html.erb
|
|
94
91
|
- config/routes.rb
|
|
95
92
|
- db/migrate/20260710000001_create_envoy_conversations.rb
|
|
96
93
|
- db/migrate/20260710000002_create_envoy_messages.rb
|
|
97
94
|
- db/migrate/20260710000003_create_envoy_tool_calls.rb
|
|
98
|
-
- db/migrate/
|
|
99
|
-
- db/migrate/
|
|
100
|
-
- db/migrate/20260711000003_add_system_prompt_version_to_envoy_conversations.rb
|
|
95
|
+
- db/migrate/20260716000001_drop_envoy_system_prompts.rb
|
|
96
|
+
- db/migrate/20260716000002_add_surface_to_envoy_conversations.rb
|
|
101
97
|
- lib/envoy.rb
|
|
102
98
|
- lib/envoy/badges.rb
|
|
103
99
|
- lib/envoy/compiled_tool.rb
|
|
@@ -105,9 +101,13 @@ files:
|
|
|
105
101
|
- lib/envoy/engine.rb
|
|
106
102
|
- lib/envoy/errors.rb
|
|
107
103
|
- lib/envoy/guard.rb
|
|
104
|
+
- lib/envoy/library.rb
|
|
108
105
|
- lib/envoy/llm.rb
|
|
109
106
|
- lib/envoy/markdown.rb
|
|
107
|
+
- lib/envoy/page_surface.rb
|
|
108
|
+
- lib/envoy/prompt.rb
|
|
110
109
|
- lib/envoy/runner.rb
|
|
110
|
+
- lib/envoy/surface.rb
|
|
111
111
|
- lib/envoy/testing/fake_llm.rb
|
|
112
112
|
- lib/envoy/tool_definition.rb
|
|
113
113
|
- lib/envoy/toolset.rb
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
module Envoy
|
|
2
|
-
class SystemPromptsController < ApplicationController
|
|
3
|
-
def index
|
|
4
|
-
@system_prompts = SystemPrompt.order(:name)
|
|
5
|
-
end
|
|
6
|
-
|
|
7
|
-
def show
|
|
8
|
-
@system_prompt = SystemPrompt.find(params[:id])
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
def new
|
|
12
|
-
@system_prompt = SystemPrompt.new
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def create
|
|
16
|
-
@system_prompt = SystemPrompt.new(prompt_params.except(:body))
|
|
17
|
-
body = prompt_params[:body].to_s
|
|
18
|
-
if body.blank?
|
|
19
|
-
@system_prompt.errors.add(:base, "Body can't be blank")
|
|
20
|
-
return render :new, status: :unprocessable_entity
|
|
21
|
-
end
|
|
22
|
-
SystemPrompt.transaction do
|
|
23
|
-
@system_prompt.save!
|
|
24
|
-
@system_prompt.add_version!(body)
|
|
25
|
-
end
|
|
26
|
-
redirect_to system_prompt_path(@system_prompt), notice: "Prompt created."
|
|
27
|
-
rescue ActiveRecord::RecordInvalid
|
|
28
|
-
render :new, status: :unprocessable_entity
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
def edit
|
|
32
|
-
@system_prompt = SystemPrompt.find(params[:id])
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
def update
|
|
36
|
-
@system_prompt = SystemPrompt.find(params[:id])
|
|
37
|
-
if @system_prompt.update(prompt_params.except(:body))
|
|
38
|
-
body = prompt_params[:body].to_s
|
|
39
|
-
@system_prompt.add_version!(body) if body.present? && body != @system_prompt.current_body
|
|
40
|
-
redirect_to system_prompt_path(@system_prompt), notice: "Prompt updated."
|
|
41
|
-
else
|
|
42
|
-
render :edit, status: :unprocessable_entity
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
private
|
|
47
|
-
|
|
48
|
-
def prompt_params
|
|
49
|
-
params.require(:system_prompt).permit(:name, :description, :body)
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
end
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
module Envoy
|
|
2
|
-
class SystemPrompt < ApplicationRecord
|
|
3
|
-
has_many :versions, -> { order(:version_number) },
|
|
4
|
-
class_name: "Envoy::SystemPromptVersion",
|
|
5
|
-
foreign_key: "system_prompt_id",
|
|
6
|
-
inverse_of: :system_prompt,
|
|
7
|
-
dependent: :destroy
|
|
8
|
-
|
|
9
|
-
validates :name, presence: true, uniqueness: true
|
|
10
|
-
|
|
11
|
-
def latest_version
|
|
12
|
-
versions.order(:version_number).last
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def current_body
|
|
16
|
-
latest_version&.body
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def add_version!(body)
|
|
20
|
-
next_number = (versions.maximum(:version_number) || 0) + 1
|
|
21
|
-
versions.create!(version_number: next_number, body: body)
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
end
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
module Envoy
|
|
2
|
-
class SystemPromptVersion < ApplicationRecord
|
|
3
|
-
belongs_to :system_prompt, class_name: "Envoy::SystemPrompt", inverse_of: :versions
|
|
4
|
-
|
|
5
|
-
validates :version_number, presence: true, uniqueness: { scope: :system_prompt_id }
|
|
6
|
-
validates :body, presence: true
|
|
7
|
-
|
|
8
|
-
attr_readonly :body, :version_number, :system_prompt_id
|
|
9
|
-
|
|
10
|
-
before_update do
|
|
11
|
-
raise ActiveRecord::ReadOnlyRecord, "Envoy::SystemPromptVersion is immutable"
|
|
12
|
-
end
|
|
13
|
-
end
|
|
14
|
-
end
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
<%= form_with model: system_prompt, url: url do |f| %>
|
|
2
|
-
<% if system_prompt.errors.any? %>
|
|
3
|
-
<div class="envoy-error">
|
|
4
|
-
<% system_prompt.errors.full_messages.each do |m| %><div><%= m %></div><% end %>
|
|
5
|
-
</div>
|
|
6
|
-
<% end %>
|
|
7
|
-
<div class="envoy-form">
|
|
8
|
-
<%= f.text_field :name, placeholder: "Name", class: "envoy-field" %>
|
|
9
|
-
<%= f.text_field :description, placeholder: "Description (optional)", class: "envoy-field" %>
|
|
10
|
-
<%= text_area_tag "system_prompt[body]", system_prompt.current_body, rows: 12,
|
|
11
|
-
placeholder: "System prompt body", class: "envoy-field envoy-field--mono" %>
|
|
12
|
-
<%= f.submit submit_label, class: "envoy-btn" %>
|
|
13
|
-
</div>
|
|
14
|
-
<% end %>
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
<div class="envoy-header-row envoy-mb-4">
|
|
2
|
-
<h1 class="envoy-title">System prompts</h1>
|
|
3
|
-
<%= link_to "New prompt", new_system_prompt_path, class: "envoy-btn" %>
|
|
4
|
-
</div>
|
|
5
|
-
<ul class="envoy-list">
|
|
6
|
-
<% @system_prompts.each do |p| %>
|
|
7
|
-
<li class="envoy-list__item">
|
|
8
|
-
<%= link_to p.name, system_prompt_path(p), class: "envoy-link" %>
|
|
9
|
-
<span class="envoy-muted">· v<%= p.latest_version&.version_number || 0 %></span>
|
|
10
|
-
<% if p.description.present? %><div class="envoy-muted"><%= p.description %></div><% end %>
|
|
11
|
-
</li>
|
|
12
|
-
<% end %>
|
|
13
|
-
</ul>
|
|
14
|
-
<div class="envoy-mt-4"><%= link_to "← Chats", conversations_path, class: "envoy-link" %></div>
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
<div class="envoy-header-row envoy-mb-3">
|
|
2
|
-
<h1 class="envoy-title"><%= @system_prompt.name %></h1>
|
|
3
|
-
<%= link_to "Edit", edit_system_prompt_path(@system_prompt), class: "envoy-btn" %>
|
|
4
|
-
</div>
|
|
5
|
-
<% if @system_prompt.description.present? %>
|
|
6
|
-
<p class="envoy-muted envoy-mb-3"><%= @system_prompt.description %></p>
|
|
7
|
-
<% end %>
|
|
8
|
-
<pre class="envoy-prose-box envoy-mb-4"><%= @system_prompt.current_body %></pre>
|
|
9
|
-
<h2 class="envoy-subtitle">Versions</h2>
|
|
10
|
-
<ul class="envoy-list">
|
|
11
|
-
<% @system_prompt.versions.order(version_number: :desc).each do |v| %>
|
|
12
|
-
<li class="envoy-list__item envoy-muted">v<%= v.version_number %> · <%= v.created_at.strftime("%Y-%m-%d %H:%M") %></li>
|
|
13
|
-
<% end %>
|
|
14
|
-
</ul>
|
|
15
|
-
<div class="envoy-mt-4"><%= link_to "← System prompts", system_prompts_path, class: "envoy-link" %></div>
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
class CreateEnvoySystemPromptVersions < ActiveRecord::Migration[8.1]
|
|
2
|
-
def change
|
|
3
|
-
create_table :envoy_system_prompt_versions do |t|
|
|
4
|
-
t.references :system_prompt, null: false, index: true,
|
|
5
|
-
foreign_key: { to_table: :envoy_system_prompts }
|
|
6
|
-
t.integer :version_number, null: false
|
|
7
|
-
t.text :body, null: false
|
|
8
|
-
t.timestamps
|
|
9
|
-
end
|
|
10
|
-
add_index :envoy_system_prompt_versions, %i[system_prompt_id version_number],
|
|
11
|
-
unique: true, name: "index_envoy_prompt_versions_on_prompt_and_number"
|
|
12
|
-
end
|
|
13
|
-
end
|