relay.app 0.5.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 +43 -0
- data/README.md +52 -53
- data/app/concerns/attachment.rb +2 -0
- data/app/concerns/context.rb +5 -12
- data/app/hooks/require_user.rb +1 -1
- data/app/init/database.rb +1 -1
- data/app/init/router.rb +0 -2
- data/app/init.rb +17 -7
- data/app/models/context.rb +13 -4
- data/app/pages/base.rb +0 -1
- data/app/routes/base.rb +0 -1
- data/app/routes/mcp/base.rb +1 -1
- data/app/routes/websocket/connection.rb +14 -14
- data/app/tools/juke_box.rb +1 -1
- data/app/tools/relay_knowledge.rb +14 -13
- data/app/views/fragments/_input.erb +3 -0
- data/app/views/fragments/_providers.erb +1 -2
- data/bin/relay +9 -17
- data/config.ru +1 -1
- data/lib/relay/jukebox.rb +2 -1
- data/lib/relay/version.rb +1 -1
- data/lib/relay.rb +26 -13
- data/libexec/relay/bootstrap +2 -1
- data/libexec/relay/configure +51 -15
- data/libexec/relay/console +8 -0
- data/libexec/relay/download-models +1 -1
- data/libexec/relay/migrate +2 -1
- data/libexec/relay/setup +1 -1
- data/libexec/relay/start +3 -2
- data/public/js/relay.js +81 -11
- data/public/js/relay.js.map +1 -1
- metadata +24 -11
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5bfc3dab37e4f9472c9bc2fc6e384059cd1d75a772407feaccf874d87606ee37
|
|
4
|
+
data.tar.gz: dcafd2c06b26dfbbe84e5f9b700a88cdac835621bedc3a8ffc3b01477028af2d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0601e7bc7927a746fd219c63f1f048ce6ae284cda78dda87acf0fae8fe2850375b53ea5b98d5ea6c2fa817b4ea36e26e6787c45f8b518d09d24d9b5c4e27be5d
|
|
7
|
+
data.tar.gz: 7cf9df0755a86c7e932ddd9cfac5dc22632ac875ab82350a8205414a7527cde5f1feb45293a0c0ae5e655526b76d3debc2df97a13af9522c442c202042bb7d4e
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,49 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## v0.6.0
|
|
6
|
+
|
|
7
|
+
Changes since v0.5.0.
|
|
8
|
+
|
|
9
|
+
### Change
|
|
10
|
+
|
|
11
|
+
* **Move the default Relay home to `~/.config/relay`** <br>
|
|
12
|
+
Change Relay's default writable home directory from `~/.relay` to
|
|
13
|
+
`~/.config/relay`, and update the README examples for user-installed
|
|
14
|
+
tools to match the new location.
|
|
15
|
+
|
|
16
|
+
* **Add `relay console`** <br>
|
|
17
|
+
Add a console command that boots Relay and starts IRB in the loaded
|
|
18
|
+
application process for interactive debugging.
|
|
19
|
+
|
|
20
|
+
* **Add AWS Bedrock provider support** <br>
|
|
21
|
+
Add Bedrock to Relay's provider registry and persisted context
|
|
22
|
+
initialization, and extend `relay configure` to prompt for AWS access
|
|
23
|
+
key credentials.
|
|
24
|
+
|
|
25
|
+
* **Prepopulate provider API key prompts from environment** <br>
|
|
26
|
+
Let `relay configure` reuse existing provider secrets from process
|
|
27
|
+
environment variables such as `OPENAI_API_KEY` and
|
|
28
|
+
`DEEPSEEK_API_KEY`, while still writing Relay's canonical `*_SECRET`
|
|
29
|
+
keys to `~/.relay/env`.
|
|
30
|
+
|
|
31
|
+
### Fix
|
|
32
|
+
|
|
33
|
+
* **Launch Falcon with the current Ruby interpreter** <br>
|
|
34
|
+
Change `relay start` to exec Falcon through `RbConfig.ruby -S` so the
|
|
35
|
+
server process uses the same Ruby and gem environment as Relay instead
|
|
36
|
+
of whichever `falcon` binary appears first on `PATH`.
|
|
37
|
+
|
|
38
|
+
* **Load user-installed tools through Zeitwerk** <br>
|
|
39
|
+
Replace manual loading of `~/.config/relay/tools/*.rb` with a dedicated
|
|
40
|
+
Zeitwerk loader so development reloads unload and recreate user tools
|
|
41
|
+
instead of reopening existing classes.
|
|
42
|
+
|
|
43
|
+
* **Activate the `llm.rb` gem before `require "llm"`** <br>
|
|
44
|
+
Avoid RubyGems loading the unrelated `llm` gem when both gems are
|
|
45
|
+
installed by explicitly activating `llm.rb` before requiring its
|
|
46
|
+
`llm` entrypoint.
|
|
47
|
+
|
|
5
48
|
## v0.5.0
|
|
6
49
|
|
|
7
50
|
Model catalog workflow release.
|
data/README.md
CHANGED
|
@@ -1,68 +1,66 @@
|
|
|
1
1
|
## About
|
|
2
2
|
|
|
3
|
-
Relay is a self-hostable LLM environment
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
Relay is a self-hostable, hackable LLM web environment that can be extended
|
|
4
|
+
with your own tools and skills that live in your `${HOME}` directory.
|
|
5
|
+
It is for programmers, AI engineers, hackers, and anyone who wants
|
|
6
|
+
their own AI environment with the option to extend it with code.
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
## Setup
|
|
10
|
+
|
|
11
|
+
It is simple to setup and get started. The application is
|
|
12
|
+
distributed as a RubyGem. It has a minimal set of dependencies -
|
|
13
|
+
built on Roda, Sequel, Falcon, [llm.rb](https://github.com/llmrb/llm.rb),
|
|
7
14
|
HTMX and web sockets.
|
|
8
15
|
|
|
9
|
-
|
|
10
|
-
add your own tools to `~/.relay/tools` which is a neat way to extend the environment
|
|
11
|
-
with your own functionality. The database uses SQLite3 to keep things simple - the
|
|
12
|
-
goal is to have something you can setup in under two minutes.
|
|
16
|
+

|
|
13
17
|
|
|
14
|
-
##
|
|
18
|
+
## Appearance
|
|
15
19
|
|
|
16
|
-
####
|
|
20
|
+
#### Sign-in
|
|
17
21
|
|
|
18
|
-
|
|
22
|
+

|
|
19
23
|
|
|
20
|
-
|
|
21
|
-
gem install relay.app
|
|
22
|
-
```
|
|
24
|
+
#### Chat
|
|
23
25
|
|
|
24
|
-
|
|
25
|
-
http://localhost:9292.
|
|
26
|
+

|
|
26
27
|
|
|
27
|
-
|
|
28
|
-
relay setup
|
|
29
|
-
relay start
|
|
30
|
-
```
|
|
28
|
+
#### MCP
|
|
31
29
|
|
|
32
|
-
|
|
30
|
+

|
|
33
31
|
|
|
34
|
-
* Install and setup in 2 minutes
|
|
35
|
-
* Localize your chats and mcp settings to your user account
|
|
36
|
-
* Connect to multiple providers (OpenAI, xAI, Anthropic, Google, DeepSeek, zAI)
|
|
37
|
-
* Connect to MCP servers
|
|
38
|
-
* Cancel in-flight requests and tool execution cleanly
|
|
39
|
-
* Run tools concurrently
|
|
40
|
-
* Make it yours: extend and customize with your own tools and system prompt
|
|
41
|
-
* Lightweight architecture
|
|
42
32
|
|
|
43
|
-
##
|
|
33
|
+
## Getting started
|
|
44
34
|
|
|
45
|
-
|
|
35
|
+
#### Install
|
|
46
36
|
|
|
47
|
-
|
|
37
|
+
Install the gem
|
|
48
38
|
|
|
49
|
-
|
|
39
|
+
```sh
|
|
40
|
+
gem install relay.app
|
|
41
|
+
```
|
|
50
42
|
|
|
51
|
-
|
|
43
|
+
#### Configure
|
|
52
44
|
|
|
53
|
-
|
|
45
|
+
Interactive setup
|
|
54
46
|
|
|
55
|
-
|
|
47
|
+
```sh
|
|
48
|
+
relay setup
|
|
49
|
+
```
|
|
56
50
|
|
|
57
|
-
|
|
51
|
+
#### Serve
|
|
58
52
|
|
|
59
|
-
|
|
53
|
+
Start the server, and visit http://localhost:9292
|
|
60
54
|
|
|
61
|
-
|
|
55
|
+
```sh
|
|
56
|
+
relay start
|
|
57
|
+
```
|
|
62
58
|
|
|
63
|
-
##
|
|
59
|
+
## Tools
|
|
64
60
|
|
|
65
|
-
|
|
61
|
+
#### How do I add my own tool?
|
|
62
|
+
|
|
63
|
+
Before running `relay start` you should add `~/.config/relay/tools/<yourtool>.rb`.
|
|
66
64
|
The tool will be automatically made available to the LLM. This is how a tool
|
|
67
65
|
might look - it is not very useful because it does not emit command output
|
|
68
66
|
but it serves as a simple example that you can modify and change to meet
|
|
@@ -82,18 +80,18 @@ class Shell < LLM::Tool
|
|
|
82
80
|
end
|
|
83
81
|
```
|
|
84
82
|
|
|
85
|
-
|
|
83
|
+
#### Wait, what is a tool?
|
|
86
84
|
|
|
87
85
|
A tool contains a name, a description, and optional parameters. It is attached
|
|
88
86
|
to a method, and that method that can be called. The model or LLM decides when
|
|
89
|
-
and how to call a tool. A tool can
|
|
90
|
-
|
|
91
|
-
you can think of. They're a powerful way to extend the capabilities of an LLM.
|
|
87
|
+
and how to call a tool. A tool can extend the abilities of the LLM with
|
|
88
|
+
your own code that could can search the web, read documentation, etc.
|
|
92
89
|
|
|
93
90
|
An MCP server can also expose pre-packaged tools, and those can be especially
|
|
94
|
-
|
|
91
|
+
useful for talking to GitHub, your own Forgejo instance or any other
|
|
92
|
+
kind of MCP server.
|
|
95
93
|
|
|
96
|
-
|
|
94
|
+
#### What are the default tools?
|
|
97
95
|
|
|
98
96
|
The `relay-knowledge` tool returns documentation for both Relay
|
|
99
97
|
and [llm.rb](https://github.com/llmrb/llm.rb) - ask about either
|
|
@@ -106,14 +104,15 @@ can be played inline in the chat, and you can also add your own
|
|
|
106
104
|
songs or remove existing ones through the same tools. The only
|
|
107
105
|
requirement is that it is a YouTube URL.
|
|
108
106
|
|
|
109
|
-
##
|
|
107
|
+
## Costs
|
|
108
|
+
|
|
109
|
+
#### What provider is the best value?
|
|
110
110
|
|
|
111
|
-
DeepSeek.
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
that a 1M context window costs 14 cents or so.
|
|
111
|
+
DeepSeek. <br>
|
|
112
|
+
Hard to beat it on price. <br>
|
|
113
|
+
Recent models have a context window of 1M.
|
|
115
114
|
|
|
116
|
-
|
|
115
|
+
#### What about self-hosting with Ollama ?
|
|
117
116
|
|
|
118
117
|
[llm.rb](https://github.com/llmrb/llm.rb#readme) provides support ollama, llama.cpp,
|
|
119
118
|
and any OpenAI-compatible endpoint. But Relay does not surface it as a feature. I haven't
|
data/app/concerns/attachment.rb
CHANGED
data/app/concerns/context.rb
CHANGED
|
@@ -55,9 +55,9 @@ module Relay::Concerns
|
|
|
55
55
|
# @return [Array<Relay::Models::MCP>]
|
|
56
56
|
# Saved MCP servers for the current user, newest first.
|
|
57
57
|
def mcps
|
|
58
|
-
@mcps ||= user ? Relay::Models::MCP.summary_dataset(user.mcps_dataset)
|
|
59
|
-
reverse_order(:created_at)
|
|
60
|
-
all : []
|
|
58
|
+
@mcps ||= user ? Relay::Models::MCP.summary_dataset(user.mcps_dataset)
|
|
59
|
+
.reverse_order(:created_at)
|
|
60
|
+
.all : []
|
|
61
61
|
end
|
|
62
62
|
|
|
63
63
|
##
|
|
@@ -94,13 +94,7 @@ module Relay::Concerns
|
|
|
94
94
|
# @return [Hash<String, LLM::Provider>]
|
|
95
95
|
# A map of initialized LLM providers.
|
|
96
96
|
def llms
|
|
97
|
-
|
|
98
|
-
"openai" => LLM.openai(key: ENV["OPENAI_SECRET"]),
|
|
99
|
-
"google" => LLM.google(key: ENV["GOOGLE_SECRET"]),
|
|
100
|
-
"anthropic" => LLM.anthropic(key: ENV["ANTHROPIC_SECRET"]),
|
|
101
|
-
"deepseek" => LLM.deepseek(key: ENV["DEEPSEEK_SECRET"]),
|
|
102
|
-
"xai" => LLM.xai(key: ENV["XAI_SECRET"])
|
|
103
|
-
}.transform_values(&:persist!)
|
|
97
|
+
Relay.providers
|
|
104
98
|
end
|
|
105
99
|
|
|
106
100
|
##
|
|
@@ -114,7 +108,7 @@ module Relay::Concerns
|
|
|
114
108
|
# @return [String]
|
|
115
109
|
# Returns the default chat model for the current provider.
|
|
116
110
|
def default_model
|
|
117
|
-
case (provider = llms
|
|
111
|
+
case (provider = llms[self.provider]).name
|
|
118
112
|
when :deepseek then "deepseek-v4-flash"
|
|
119
113
|
when :openai then "gpt-5.4"
|
|
120
114
|
when :xai then "grok-3"
|
|
@@ -142,6 +136,5 @@ module Relay::Concerns
|
|
|
142
136
|
def user
|
|
143
137
|
@user ||= Relay::Models::User[session["user_id"]] if session["user_id"]
|
|
144
138
|
end
|
|
145
|
-
|
|
146
139
|
end
|
|
147
140
|
end
|
data/app/hooks/require_user.rb
CHANGED
data/app/init/database.rb
CHANGED
|
@@ -8,7 +8,7 @@ module Relay::Database
|
|
|
8
8
|
# @param [String] env
|
|
9
9
|
# @return [Hash]
|
|
10
10
|
def load(env:)
|
|
11
|
-
erb = ERB.new(File.read(File.join(
|
|
11
|
+
erb = ERB.new(File.read(File.join(Relay.home, "db", "config.yml")))
|
|
12
12
|
config = YAML.safe_load(erb.result, aliases: true)
|
|
13
13
|
config.fetch(env)
|
|
14
14
|
end
|
data/app/init/router.rb
CHANGED
data/app/init.rb
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Relay
|
|
4
|
+
require_relative "../lib/relay"
|
|
5
|
+
|
|
4
6
|
require "async"
|
|
5
7
|
require "async/websocket"
|
|
6
8
|
require "async/websocket/adapters/rack"
|
|
@@ -12,7 +14,6 @@ module Relay
|
|
|
12
14
|
require "zeitwerk"
|
|
13
15
|
require "sequel"
|
|
14
16
|
require "yaml"
|
|
15
|
-
require "relay"
|
|
16
17
|
|
|
17
18
|
loader = Zeitwerk::Loader.new
|
|
18
19
|
loader.inflector.inflect(
|
|
@@ -25,7 +26,6 @@ module Relay
|
|
|
25
26
|
File.join(__dir__, "init")
|
|
26
27
|
)
|
|
27
28
|
loader.push_dir(__dir__, namespace: self)
|
|
28
|
-
|
|
29
29
|
loader.enable_reloading if development?
|
|
30
30
|
loader.setup
|
|
31
31
|
|
|
@@ -37,14 +37,24 @@ module Relay
|
|
|
37
37
|
end
|
|
38
38
|
@loader = loader
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
Relay.bootstrap!
|
|
41
|
+
user_tools_dir = File.join(home, "tools")
|
|
42
|
+
user_loader = Zeitwerk::Loader.new
|
|
43
|
+
user_loader.tag = "relay-user-tools"
|
|
44
|
+
user_loader.push_dir(user_tools_dir)
|
|
45
|
+
user_loader.enable_reloading if development?
|
|
46
|
+
user_loader.setup
|
|
47
|
+
|
|
48
|
+
##
|
|
49
|
+
# Returns the Zeitwerk loader used for user-installed tools
|
|
50
|
+
# @return [Zeitwerk::Loader]
|
|
51
|
+
def self.user_loader
|
|
52
|
+
@user_loader
|
|
53
|
+
end
|
|
54
|
+
@user_loader = user_loader
|
|
44
55
|
|
|
45
56
|
require_relative "init/env"
|
|
46
57
|
require_relative "init/database"
|
|
47
58
|
require_relative "init/router"
|
|
48
|
-
|
|
49
59
|
Relay.reload
|
|
50
60
|
end
|
data/app/models/context.rb
CHANGED
|
@@ -44,13 +44,13 @@ module Relay::Models
|
|
|
44
44
|
|
|
45
45
|
##
|
|
46
46
|
# @note
|
|
47
|
-
# This method excludes tool calls and system messages.
|
|
47
|
+
# This method excludes tool calls, tool returns, and system messages.
|
|
48
48
|
# It is safe to render in the UI.
|
|
49
49
|
# @return [Array<Hash>]
|
|
50
50
|
# Returns persisted user and assistant messages
|
|
51
51
|
def messages
|
|
52
52
|
ctx.messages.filter_map do |message|
|
|
53
|
-
next if message.tool_call? || message.compaction?
|
|
53
|
+
next if message.tool_call? || message.tool_return? || message.compaction?
|
|
54
54
|
next unless message.user? || message.assistant?
|
|
55
55
|
{role: message.role.to_sym, content: message.content.to_s}
|
|
56
56
|
end
|
|
@@ -67,11 +67,20 @@ module Relay::Models
|
|
|
67
67
|
private
|
|
68
68
|
|
|
69
69
|
def set_provider
|
|
70
|
-
|
|
70
|
+
case provider
|
|
71
|
+
when "bedrock"
|
|
72
|
+
LLM.bedrock(
|
|
73
|
+
access_key_id: ENV["AWS_ACCESS_KEY_ID"],
|
|
74
|
+
secret_access_key: ENV["AWS_SECRET_ACCESS_KEY"],
|
|
75
|
+
persistent: true
|
|
76
|
+
)
|
|
77
|
+
else
|
|
78
|
+
LLM.method(provider).call(key: ENV["#{provider.upcase}_SECRET"], persistent: true)
|
|
79
|
+
end
|
|
71
80
|
end
|
|
72
81
|
|
|
73
82
|
def set_context
|
|
74
|
-
{
|
|
83
|
+
{model: self[:model], compactor: {retention_window: 8, token_threshold: "95%"}}
|
|
75
84
|
end
|
|
76
85
|
|
|
77
86
|
def set_tracer
|
data/app/pages/base.rb
CHANGED
data/app/routes/base.rb
CHANGED
data/app/routes/mcp/base.rb
CHANGED
|
@@ -8,7 +8,7 @@ module Relay::Routes
|
|
|
8
8
|
Relay::Models::MCP.where(id:, user_id: user.id).first || raise(Sequel::NoMatchingRow)
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
-
def workspace(selected_id: nil
|
|
11
|
+
def workspace(form:, selected_id: nil)
|
|
12
12
|
partial("fragments/mcp/workspace", locals: {mcps:, selected_id:, form:}) +
|
|
13
13
|
partial("fragments/mcp_settings", locals: {servers: mcps, show_label: false, swap_oob: true})
|
|
14
14
|
end
|
|
@@ -37,9 +37,9 @@ class Relay::Routes::Websocket
|
|
|
37
37
|
# The mutable request params for the current turn
|
|
38
38
|
# @return [void]
|
|
39
39
|
def dispatch(conn, ctx, payload, params)
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
if interrupt?(payload)
|
|
41
|
+
interrupt!(conn, ctx)
|
|
42
|
+
elsif request_in_flight?
|
|
43
43
|
write(conn, fragment(:status, status_bar(status: "Busy", ctx:)))
|
|
44
44
|
else
|
|
45
45
|
@task = Async { on_message(conn, ctx, payload, params) }
|
|
@@ -73,14 +73,14 @@ class Relay::Routes::Websocket
|
|
|
73
73
|
file = attachment_from_payload(payload) || attachment.consume
|
|
74
74
|
prompt = build_prompt(ctx, payload["message"], file)
|
|
75
75
|
return if prompt.empty?
|
|
76
|
-
vars[:messages].concat [{role: :user, content: prompt}, {role: :assistant, content: +""}]
|
|
77
|
-
write(conn, fragment(:status, status_bar(status: "Thinking...", ctx:)))
|
|
78
|
-
write(conn, fragment(:remove_empty_state)) if vars[:messages].length == 2
|
|
79
|
-
write(conn, fragment(:append_message, message: vars[:messages][-2]))
|
|
80
|
-
write(conn, fragment(:append_message, message: vars[:messages][-1]))
|
|
81
|
-
write(conn, fragment(:input))
|
|
82
76
|
yield_tools(ctx) do |tools|
|
|
83
77
|
params[:tools] = tools
|
|
78
|
+
vars[:messages].concat [{role: :user, content: prompt}, {role: :assistant, content: +""}]
|
|
79
|
+
write(conn, fragment(:status, status_bar(status: "Thinking...", ctx:)))
|
|
80
|
+
write(conn, fragment(:remove_empty_state)) if vars[:messages].length == 2
|
|
81
|
+
write(conn, fragment(:append_message, message: vars[:messages][-2]))
|
|
82
|
+
write(conn, fragment(:append_message, message: vars[:messages][-1]))
|
|
83
|
+
write(conn, fragment(:input))
|
|
84
84
|
wait_with_heartbeat(conn, proc { talk(ctx, prompt, params) })
|
|
85
85
|
resolve_functions(ctx, conn, params)
|
|
86
86
|
end
|
|
@@ -121,11 +121,11 @@ class Relay::Routes::Websocket
|
|
|
121
121
|
# The WebSocket connection object
|
|
122
122
|
# @return [void]
|
|
123
123
|
def resolve_functions(ctx, conn, params)
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
124
|
+
while ctx.functions?
|
|
125
|
+
returns = wait_with_heartbeat(conn, proc { ctx.wait(:task) })
|
|
126
|
+
break if returns.empty?
|
|
127
|
+
write(conn, fragment(:status, status_bar(status: tool_status(ctx.functions), ctx:))) if ctx.functions?
|
|
128
|
+
wait_with_heartbeat(conn, proc { ctx.talk(returns, params) })
|
|
129
129
|
end
|
|
130
130
|
end
|
|
131
131
|
|
data/app/tools/juke_box.rb
CHANGED
|
@@ -10,24 +10,26 @@ module Relay::Tools
|
|
|
10
10
|
include Relay::Tool
|
|
11
11
|
|
|
12
12
|
name "relay-knowledge"
|
|
13
|
-
description "Returns Relay
|
|
14
|
-
|
|
13
|
+
description "Returns Relay, llm.rb or mruby-llm documentation"
|
|
14
|
+
parameter :topic, Enum["relay", "llm.rb", "mruby-llm"], "The knowledge topic"
|
|
15
|
+
required %i[topic]
|
|
15
16
|
|
|
16
17
|
##
|
|
17
18
|
# Provides the Relay documentation
|
|
18
19
|
# @return [Hash]
|
|
19
20
|
def call(topic:)
|
|
20
21
|
case topic
|
|
21
|
-
when "relay" then {directions:, documentation:
|
|
22
|
-
when "llm.rb" then {directions:, documentation:
|
|
22
|
+
when "relay" then {directions:, documentation: fetch(relay_resources)}
|
|
23
|
+
when "llm.rb" then {directions:, documentation: fetch(llmrb_resources)}
|
|
24
|
+
when "mruby-llm" then {directions:, documentation: fetch(mruby_llm_resources)}
|
|
23
25
|
else {error: "unknown topic: #{topic}"}
|
|
24
26
|
end
|
|
25
27
|
end
|
|
26
28
|
|
|
27
29
|
private
|
|
28
30
|
|
|
29
|
-
def
|
|
30
|
-
|
|
31
|
+
def fetch(resources)
|
|
32
|
+
resources.each_with_object({}) do |(key, url), h|
|
|
31
33
|
res = Net::HTTP.get_response URI.parse(url)
|
|
32
34
|
h[key] = res.body
|
|
33
35
|
end
|
|
@@ -37,13 +39,6 @@ module Relay::Tools
|
|
|
37
39
|
{"readme" => "https://raw.githubusercontent.com/llmrb/relay/refs/heads/main/README.md"}
|
|
38
40
|
end
|
|
39
41
|
|
|
40
|
-
def llmrb_documentation
|
|
41
|
-
llmrb_resources.each_with_object({}) do |(key, url), h|
|
|
42
|
-
res = Net::HTTP.get_response URI.parse(url)
|
|
43
|
-
h[key] = res.body
|
|
44
|
-
end
|
|
45
|
-
end
|
|
46
|
-
|
|
47
42
|
def llmrb_resources
|
|
48
43
|
{
|
|
49
44
|
"readme" => "https://raw.githubusercontent.com/llmrb/llm.rb/refs/heads/main/README.md",
|
|
@@ -52,6 +47,12 @@ module Relay::Tools
|
|
|
52
47
|
}
|
|
53
48
|
end
|
|
54
49
|
|
|
50
|
+
def mruby_llm_resources
|
|
51
|
+
{
|
|
52
|
+
"readme" => "https://raw.githubusercontent.com/llmrb/mruby-llm/refs/heads/main/README.md"
|
|
53
|
+
}
|
|
54
|
+
end
|
|
55
|
+
|
|
55
56
|
def directions
|
|
56
57
|
"Reference links from the associated document in your response"
|
|
57
58
|
end
|
|
@@ -12,8 +12,7 @@
|
|
|
12
12
|
hx-swap="outerHTML"
|
|
13
13
|
hx-trigger="change"
|
|
14
14
|
>
|
|
15
|
-
<% Relay.providers.each do |_,
|
|
16
|
-
<% llm = builder.call %>
|
|
15
|
+
<% Relay.providers.each do |_, llm| %>
|
|
17
16
|
<option
|
|
18
17
|
<%= "selected" if llm.name.to_s == provider.to_s %>
|
|
19
18
|
value="<%= llm.name %>">
|
data/bin/relay
CHANGED
|
@@ -13,27 +13,19 @@ end
|
|
|
13
13
|
|
|
14
14
|
def main(argv)
|
|
15
15
|
case argv[0]
|
|
16
|
-
when "setup"
|
|
17
|
-
Process.spawn File.join(libexec,
|
|
18
|
-
Process.wait
|
|
19
|
-
when "download-models"
|
|
20
|
-
Process.spawn File.join(libexec, "download-models"), *argv[1..]
|
|
21
|
-
Process.wait
|
|
22
|
-
when "migrate"
|
|
23
|
-
Process.spawn File.join(libexec, "migrate"), *argv[1..]
|
|
24
|
-
Process.wait
|
|
25
|
-
when "start"
|
|
26
|
-
Process.spawn File.join(libexec, "start"), *argv[1..]
|
|
16
|
+
when "start", "setup", "download-models", "migrate", "bootstrap", "console"
|
|
17
|
+
Process.spawn File.join(libexec, argv[0]), *argv[1..]
|
|
27
18
|
Process.wait
|
|
28
19
|
else
|
|
29
|
-
|
|
20
|
+
require_relative "../lib/relay"
|
|
30
21
|
warn Relay.banner
|
|
31
22
|
warn "Usage: relay [COMMAND] [OPTIONS]\n\n" \
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
23
|
+
"Commands:\n" \
|
|
24
|
+
" setup Setup Relay for the first time\n" \
|
|
25
|
+
" download-models Download provider model catalogs\n" \
|
|
26
|
+
" start Start Relay\n" \
|
|
27
|
+
" migrate Run database migrations\n" \
|
|
28
|
+
" console Start an IRB console with Relay booted\n"
|
|
37
29
|
end
|
|
38
30
|
rescue Interrupt
|
|
39
31
|
wait
|
data/config.ru
CHANGED
|
@@ -4,7 +4,7 @@ ENV["RACK_MULTIPART_BUFFERED_UPLOAD_BYTESIZE_LIMIT"] ||= (64 * 1024 * 1024).to_s
|
|
|
4
4
|
|
|
5
5
|
require_relative "app/init"
|
|
6
6
|
|
|
7
|
-
use Rack::Static, urls: ["/g"], root: Relay.
|
|
7
|
+
use Rack::Static, urls: ["/g"], root: Relay.public_dir
|
|
8
8
|
use Rack::Static, urls: ["/images", "/stylesheets", "/js"], root: Relay.public_dir
|
|
9
9
|
case Relay.environment
|
|
10
10
|
when "development"
|
data/lib/relay/jukebox.rb
CHANGED
|
@@ -63,7 +63,8 @@ module Relay
|
|
|
63
63
|
def extract_youtube_id(uri)
|
|
64
64
|
path = uri.path.to_s
|
|
65
65
|
return path.split("/").reject(&:empty?).last if path.start_with?("/embed/", "/shorts/")
|
|
66
|
-
|
|
66
|
+
form = URI.decode_www_form(uri.query.to_s)
|
|
67
|
+
(form.to_h["v"] || []).first
|
|
67
68
|
end
|
|
68
69
|
|
|
69
70
|
def songs
|
data/lib/relay/version.rb
CHANGED