llm.rb 13.0.0 → 14.0.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 +505 -14
- data/README.md +484 -50
- data/bin/llm.rb +148 -0
- data/data/anthropic.json +206 -263
- data/data/bedrock.json +2138 -1860
- data/data/deepinfra.json +1003 -624
- data/data/deepseek.json +38 -34
- data/data/google.json +1079 -371
- data/data/mistral.json +448 -368
- data/data/moonshot.json +384 -0
- data/data/openai.json +974 -1343
- data/data/xai.json +154 -126
- data/data/zai.json +191 -191
- data/lib/llm/agent.rb +123 -20
- data/lib/llm/context.rb +71 -88
- data/lib/llm/cost.rb +23 -17
- data/lib/llm/error.rb +0 -8
- data/lib/llm/function/array.rb +3 -3
- data/lib/llm/function/async/task.rb +2 -0
- data/lib/llm/function/fiber/task.rb +2 -0
- data/lib/llm/function/fork/task.rb +2 -0
- data/lib/llm/function/ractor/task.rb +2 -0
- data/lib/llm/function/sequential/group.rb +4 -1
- data/lib/llm/function/sequential/task.rb +1 -1
- data/lib/llm/function/task.rb +4 -0
- data/lib/llm/function/thread/task.rb +2 -0
- data/lib/llm/function.rb +33 -6
- data/lib/llm/guard/loop.rb +89 -0
- data/lib/llm/guard/null.rb +19 -0
- data/lib/llm/guard.rb +61 -0
- data/lib/llm/provider.rb +36 -0
- data/lib/llm/providers/anthropic/stream_parser.rb +1 -0
- data/lib/llm/providers/anthropic.rb +2 -9
- data/lib/llm/providers/bedrock/request_adapter.rb +1 -1
- data/lib/llm/providers/bedrock/stream_parser.rb +1 -0
- data/lib/llm/providers/bedrock.rb +1 -8
- data/lib/llm/providers/google/stream_parser.rb +1 -0
- data/lib/llm/providers/google.rb +1 -8
- data/lib/llm/providers/mistral.rb +1 -1
- data/lib/llm/providers/moonshot.rb +76 -0
- data/lib/llm/providers/ollama.rb +2 -9
- data/lib/llm/providers/openai/responses/stream_parser.rb +1 -0
- data/lib/llm/providers/openai/responses.rb +7 -9
- data/lib/llm/providers/openai/stream_parser.rb +1 -0
- data/lib/llm/providers/openai.rb +4 -11
- data/lib/llm/repl/bar.rb +4 -3
- data/lib/llm/repl/{transcript.rb → buffer.rb} +69 -29
- data/lib/llm/repl/color.rb +78 -0
- data/lib/llm/repl/command.rb +12 -5
- data/lib/llm/repl/commands/compact.rb +2 -2
- data/lib/llm/repl/commands/help.rb +3 -5
- data/lib/llm/repl/input/char.rb +46 -0
- data/lib/llm/repl/input/row.rb +39 -0
- data/lib/llm/repl/input.rb +251 -66
- data/lib/llm/repl/markdown/table.rb +11 -3
- data/lib/llm/repl/markdown.rb +34 -8
- data/lib/llm/repl/node.rb +37 -0
- data/lib/llm/repl/status.rb +42 -7
- data/lib/llm/repl/stream.rb +18 -6
- data/lib/llm/repl/walker.rb +3 -2
- data/lib/llm/repl/window.rb +54 -35
- data/lib/llm/repl.rb +74 -32
- data/lib/llm/skill.rb +20 -4
- data/lib/llm/stream.rb +8 -7
- data/lib/llm/tool.rb +29 -0
- data/lib/llm/tools/{swap_text.rb → edit-file.rb} +3 -3
- data/lib/llm/tools/git.rb +3 -0
- data/lib/llm/tools/mkdir.rb +3 -0
- data/lib/llm/tools/rg.rb +3 -0
- data/lib/llm/tools/ruby.rb +46 -0
- data/lib/llm/tools/shell.rb +3 -0
- data/lib/llm/tracer/pretty_logger.rb +127 -0
- data/lib/llm/tracer.rb +1 -0
- data/lib/llm/transformer/null.rb +21 -0
- data/lib/llm/transformer.rb +55 -0
- data/lib/llm/version.rb +1 -1
- data/lib/llm.rb +12 -2
- data/llm.gemspec +9 -2
- data/resources/deepdive/advanced/cancellation.md +74 -0
- data/resources/deepdive/advanced/compaction.md +83 -0
- data/resources/deepdive/advanced/context.md +267 -0
- data/resources/deepdive/advanced/guard.md +371 -0
- data/resources/deepdive/advanced/tracer.md +180 -0
- data/resources/deepdive/advanced/transformer.md +67 -0
- data/resources/deepdive/advanced/transports.md +45 -0
- data/resources/deepdive/everything_else/audio.md +122 -0
- data/resources/deepdive/everything_else/cost.md +99 -0
- data/resources/deepdive/everything_else/images.md +89 -0
- data/resources/deepdive/everything_else/object.md +108 -0
- data/resources/deepdive/everything_else/ocr.md +48 -0
- data/resources/deepdive/fundamentals/agents.md +202 -0
- data/resources/deepdive/fundamentals/builtin_tools.md +191 -0
- data/resources/deepdive/fundamentals/concurrency.md +104 -0
- data/resources/deepdive/fundamentals/database.md +449 -0
- data/resources/deepdive/fundamentals/embeddings.md +157 -0
- data/resources/deepdive/fundamentals/repl.md +87 -0
- data/resources/deepdive/fundamentals/schema.md +61 -0
- data/resources/deepdive/fundamentals/skills.md +106 -0
- data/resources/deepdive/fundamentals/stream.md +110 -0
- data/resources/deepdive/fundamentals/tools.md +265 -0
- data/resources/deepdive/protocols/a2a.md +106 -0
- data/resources/deepdive/protocols/mcp.md +111 -0
- data/resources/deepdive.md +58 -1792
- metadata +51 -7
- data/lib/llm/loop_guard.rb +0 -107
data/bin/llm.rb
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require "llm"
|
|
4
|
+
require "json"
|
|
5
|
+
require "fileutils"
|
|
6
|
+
require "securerandom"
|
|
7
|
+
|
|
8
|
+
##
|
|
9
|
+
# utils
|
|
10
|
+
|
|
11
|
+
def keys
|
|
12
|
+
@keys ||= Dir[File.join(__dir__, "..", "lib", "llm", "providers", "*")]
|
|
13
|
+
.select { File.file?(_1) }
|
|
14
|
+
.map { File.basename(_1, ".rb") }
|
|
15
|
+
.sort_by { _1 == "deepseek" ? 0 : 1 }
|
|
16
|
+
.map { "#{_1.upcase}_API_KEY" } - ["BEDROCK_API_KEY"]
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def help
|
|
20
|
+
prog = File.basename($PROGRAM_NAME)
|
|
21
|
+
warn ""
|
|
22
|
+
warn "Usage: #{prog} [options]"
|
|
23
|
+
warn ""
|
|
24
|
+
warn "Options:"
|
|
25
|
+
warn " -p PROVIDER Choose a provider"
|
|
26
|
+
warn " -t Temporary session that doesn't persist to disk"
|
|
27
|
+
warn " -h Show this help"
|
|
28
|
+
warn ""
|
|
29
|
+
warn "Examples:"
|
|
30
|
+
warn " #{prog} # auto-detect provider from $PROVIDER_API_KEY"
|
|
31
|
+
warn " #{prog} -p openai # use OpenAI"
|
|
32
|
+
warn " #{prog} -h # this help"
|
|
33
|
+
warn ""
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def loaderror(ex)
|
|
37
|
+
gem, = ex.message.split(" is an optional runtime dependency")
|
|
38
|
+
warn ""
|
|
39
|
+
warn " ── llm.rb ──────────────────────────────────────────────"
|
|
40
|
+
warn ""
|
|
41
|
+
warn " ✖ Missing dependency: #{gem}"
|
|
42
|
+
warn ""
|
|
43
|
+
warn " The repl needs this gem, but it's not installed."
|
|
44
|
+
warn ""
|
|
45
|
+
warn " Fix: gem install #{gem}"
|
|
46
|
+
warn " Or: bundle add #{gem}"
|
|
47
|
+
warn ""
|
|
48
|
+
warn " Tip: If you don't need the repl, you can use the"
|
|
49
|
+
warn " library directly with: require \"llm\""
|
|
50
|
+
warn ""
|
|
51
|
+
warn " ───────────────────────────────────────────────────────"
|
|
52
|
+
warn ""
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
##
|
|
56
|
+
# main
|
|
57
|
+
|
|
58
|
+
def main(argv)
|
|
59
|
+
##
|
|
60
|
+
# Make sure the dependencies are satisified first
|
|
61
|
+
begin
|
|
62
|
+
require "llm/tools"
|
|
63
|
+
require "llm/repl"
|
|
64
|
+
rescue LLM::LoadError => ex
|
|
65
|
+
loaderror(ex)
|
|
66
|
+
exit 1
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
##
|
|
70
|
+
# C-Style option parser
|
|
71
|
+
# No external dep
|
|
72
|
+
while option = argv.shift
|
|
73
|
+
case option
|
|
74
|
+
when '-h'
|
|
75
|
+
help
|
|
76
|
+
exit 0
|
|
77
|
+
when '-t'
|
|
78
|
+
temp = true
|
|
79
|
+
when '-p'
|
|
80
|
+
provider = argv.shift
|
|
81
|
+
if provider.nil?
|
|
82
|
+
warn "llm.rb: -p switch requires an argument"
|
|
83
|
+
help
|
|
84
|
+
exit 1
|
|
85
|
+
end
|
|
86
|
+
else
|
|
87
|
+
warn "llm.rb: unknown option #{option}"
|
|
88
|
+
help
|
|
89
|
+
exit 1
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
##
|
|
94
|
+
# No provider has been given.
|
|
95
|
+
# Try to infer one.
|
|
96
|
+
if provider.nil?
|
|
97
|
+
key = keys.find { ENV[_1] }
|
|
98
|
+
if key.nil?
|
|
99
|
+
warn "llm.rb: provide a provider with the -p switch"
|
|
100
|
+
exit 1
|
|
101
|
+
else
|
|
102
|
+
provider, = key.split("_")
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
provider = provider.downcase
|
|
106
|
+
|
|
107
|
+
##
|
|
108
|
+
# Setup the filesystem where <provider>.json maps
|
|
109
|
+
# the current working directory to a session file,
|
|
110
|
+
# and where the session file is stored in
|
|
111
|
+
# `~/.llm.rb/<provider>/<uuid>.json`.
|
|
112
|
+
# This can be skipped with the `-t` option.
|
|
113
|
+
if temp.nil?
|
|
114
|
+
home = File.join(Dir.home, ".llm.rb")
|
|
115
|
+
file = File.join(home, "#{provider}.json")
|
|
116
|
+
parent = File.join(home, provider)
|
|
117
|
+
|
|
118
|
+
FileUtils.mkdir_p(parent)
|
|
119
|
+
FileUtils.touch(file)
|
|
120
|
+
|
|
121
|
+
if File.size(file).zero?
|
|
122
|
+
data = LLM::Object.from({})
|
|
123
|
+
File.binwrite file, JSON.pretty_generate(data)
|
|
124
|
+
else
|
|
125
|
+
data = LLM::Object.from JSON.parse(File.read(file))
|
|
126
|
+
end
|
|
127
|
+
data[Dir.getwd] ||= File.join(parent, "#{SecureRandom.uuid}.json")
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
##
|
|
131
|
+
# We're ready to start the REPL
|
|
132
|
+
# This should always succeed unless -p gave garbage
|
|
133
|
+
if LLM.respond_to?(provider)
|
|
134
|
+
key ||= "#{provider.upcase}_API_KEY"
|
|
135
|
+
if ENV[key].nil? || ENV[key].to_s.empty?
|
|
136
|
+
warn "llm.rb: set #{key} to use #{provider}"
|
|
137
|
+
exit 1
|
|
138
|
+
end
|
|
139
|
+
llm = LLM.method(provider).call(key: ENV[key])
|
|
140
|
+
path = temp ? nil : data[Dir.getwd]
|
|
141
|
+
agent = LLM::Agent.new(llm, path:, tools: LLM::Tool.subclasses)
|
|
142
|
+
agent.repl
|
|
143
|
+
else
|
|
144
|
+
warn "llm.rb: #{provider} was not recognized"
|
|
145
|
+
exit 1
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
main(ARGV)
|