llm_gateway 0.6.0 → 0.8.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.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +20 -0
  3. data/README.md +255 -1
  4. data/docs/migration_guide_0.7.0.md +193 -0
  5. data/lib/llm_gateway/adapters/adapter.rb +30 -12
  6. data/lib/llm_gateway/adapters/anthropic/input_mapper.rb +24 -0
  7. data/lib/llm_gateway/adapters/anthropic/stream_mapper.rb +31 -8
  8. data/lib/llm_gateway/adapters/anthropic_option_mapper.rb +1 -1
  9. data/lib/llm_gateway/adapters/groq/option_mapper.rb +1 -1
  10. data/lib/llm_gateway/adapters/input_message_sanitizer.rb +98 -7
  11. data/lib/llm_gateway/adapters/normalized_stream_accumulator.rb +48 -16
  12. data/lib/llm_gateway/adapters/openai/chat_completions/option_mapper.rb +1 -1
  13. data/lib/llm_gateway/adapters/openai/responses/input_mapper.rb +47 -31
  14. data/lib/llm_gateway/adapters/openai/responses/option_mapper.rb +1 -1
  15. data/lib/llm_gateway/adapters/openai/responses/stream_mapper.rb +131 -3
  16. data/lib/llm_gateway/adapters/structs.rb +45 -10
  17. data/lib/llm_gateway/agents/event.rb +105 -0
  18. data/lib/llm_gateway/agents/file_session_manager.rb +100 -0
  19. data/lib/llm_gateway/agents/harness.rb +176 -0
  20. data/lib/llm_gateway/agents/in_memory_session_manager.rb +222 -0
  21. data/lib/llm_gateway/agents/tools/bash_tool.rb +132 -0
  22. data/lib/llm_gateway/agents/tools/edit_tool.rb +215 -0
  23. data/lib/llm_gateway/agents/tools/read_tool.rb +143 -0
  24. data/lib/llm_gateway/agents/tools/tool_utils.rb +164 -0
  25. data/lib/llm_gateway/agents/tools/write_tool.rb +34 -0
  26. data/lib/llm_gateway/base_client.rb +3 -3
  27. data/lib/llm_gateway/clients/anthropic.rb +5 -5
  28. data/lib/llm_gateway/clients/claude_code/oauth_flow.rb +2 -2
  29. data/lib/llm_gateway/clients/openai.rb +2 -2
  30. data/lib/llm_gateway/clients/openai_codex/oauth_flow.rb +4 -4
  31. data/lib/llm_gateway/prompt.rb +105 -68
  32. data/lib/llm_gateway/proxy/adapter.rb +48 -0
  33. data/lib/llm_gateway/proxy/client.rb +85 -0
  34. data/lib/llm_gateway/proxy/server.rb +65 -0
  35. data/lib/llm_gateway/utils.rb +116 -13
  36. data/lib/llm_gateway/version.rb +1 -1
  37. data/lib/llm_gateway.rb +12 -1
  38. metadata +15 -2
@@ -2,112 +2,149 @@
2
2
 
3
3
  module LlmGateway
4
4
  class Prompt
5
- UNSET = Object.new.freeze
5
+ class_attribute :provider, :model, :reasoning
6
+ class_attribute :before_execute_callbacks, :after_execute_callbacks, instance_accessor: false, default: []
7
+ attr_accessor :cache_key, :cache_retention
6
8
 
7
- attr_reader :provider, :model
9
+ def self.before_execute(*methods, &block)
10
+ self.before_execute_callbacks += methods
11
+ self.before_execute_callbacks += [ block ] if block_given?
12
+ end
8
13
 
9
- class << self
10
- def provider(value = UNSET)
11
- @provider = value unless value.equal?(UNSET)
12
- @provider
13
- end
14
+ def self.after_execute(*methods, &block)
15
+ self.after_execute_callbacks += methods
16
+ self.after_execute_callbacks += [ block ] if block_given?
17
+ end
14
18
 
15
- def provider=(value)
16
- @provider = value
17
- end
19
+ def initialize(provider: nil, model: nil, reasoning: nil, cache_key: nil, cache_retention: nil)
20
+ @provider = provider || self.class.provider
21
+ @model = model || self.class.model
22
+ @reasoning = reasoning || self.class.reasoning
23
+ @cache_key = cache_key
24
+ @cache_retention = cache_retention
25
+ end
18
26
 
19
- def model(value = UNSET)
20
- @model = value unless value.equal?(UNSET)
21
- @model
22
- end
27
+ def run(provider: nil, model: nil, reasoning: nil, **options, &block)
28
+ # Resolve the prompt once so dynamic or expensive prompt builders are not
29
+ # evaluated multiple times during a single run.
30
+ input = prompt
23
31
 
24
- def model=(value)
25
- @model = value
26
- end
27
- end
32
+ run_callbacks(:before_execute, input)
28
33
 
29
- def self.before_execute(*methods, &block)
30
- before_execute_callbacks.concat(methods)
31
- before_execute_callbacks << block if block_given?
34
+ response = run_tool_loop(input, provider: resolved_provider(provider), model: model, reasoning: reasoning, **options, &block)
35
+
36
+ run_callbacks(:after_execute, response)
37
+
38
+ response
32
39
  end
33
40
 
34
- def self.after_execute(*methods, &block)
35
- after_execute_callbacks.concat(methods)
36
- after_execute_callbacks << block if block_given?
41
+ def stream(input = prompt, provider: nil, model: nil, reasoning: nil, **options, &block)
42
+ stream_provider = resolved_provider(provider)
43
+ stream_options = default_stream_options(model: model, reasoning: reasoning).merge(options)
44
+
45
+ stream_provider.stream(input, **stream_options, &block)
37
46
  end
38
47
 
39
- def self.before_execute_callbacks
40
- @before_execute_callbacks ||= []
48
+ def self.tools
49
+ const_defined?(:TOOLS, false) ? self::TOOLS : []
41
50
  end
42
51
 
43
- def self.after_execute_callbacks
44
- @after_execute_callbacks ||= []
52
+ def self.find_tool(name)
53
+ tools.find { |tool| tool.tool_name == name }
45
54
  end
46
55
 
47
- def self.inherited(subclass)
48
- super
49
- subclass.instance_variable_set(:@before_execute_callbacks, before_execute_callbacks.dup)
50
- subclass.instance_variable_set(:@after_execute_callbacks, after_execute_callbacks.dup)
51
- subclass.provider = provider
52
- subclass.model = model
56
+ def tools
57
+ self.class.tools.map(&:definition)
53
58
  end
54
59
 
55
- def initialize(provider = nil, model = nil)
56
- @provider = provider || self.class.provider
57
- @model = model || self.class.model
60
+ def system_prompt
61
+ nil
58
62
  end
59
63
 
60
- def run
61
- run_callbacks(:before_execute, prompt)
64
+ private
62
65
 
63
- response = stream
66
+ def find_and_execute_tool(tool_request)
67
+ tool_name = tool_request.name
68
+ tool_input = tool_request.input
69
+ tool_class = self.class.find_tool(tool_name)
64
70
 
65
- response_content = if respond_to?(:extract_response)
66
- extract_response(response)
67
- else
68
- response[:choices][0][:content]
71
+ result = begin
72
+ if tool_class
73
+ execute_tool(tool_class, tool_input)
74
+ else
75
+ "Unknown tool: #{tool_name}"
76
+ end
77
+ rescue StandardError => e
78
+ "Error executing tool: #{e.message}"
69
79
  end
80
+ ToolResult.new(
81
+ type: "tool_result",
82
+ tool_use_id: tool_request.id,
83
+ content: result,
84
+ )
85
+ end
70
86
 
71
- result = if respond_to?(:parse_response)
72
- parse_response(response_content)
73
- else
74
- response_content
75
- end
87
+ def execute_tool(tool_class, tool_input)
88
+ tool_class.new.execute(tool_input)
89
+ end
90
+
91
+ def run_tool_loop(input, provider: nil, model: nil, reasoning: nil, **options, &block)
92
+ response = stream(input, provider: provider, model: model, reasoning: reasoning, **options, &block)
76
93
 
77
- run_callbacks(:after_execute, response, response_content)
94
+ while tool_requests(response).any?
95
+ input = prompt_with_tool_results(input, response, tool_requests(response))
96
+ response = stream(input, provider: provider, model: model, reasoning: reasoning, **options, &block)
97
+ end
78
98
 
79
- result
99
+ response
80
100
  end
81
101
 
82
- def stream(provider: nil, model: nil, **options)
83
- stream_provider = provider || self.provider
84
- stream_model = model || self.model
85
- options[:model] = stream_model if stream_model
102
+ def tool_requests(response)
103
+ return [] unless response.respond_to?(:content)
86
104
 
87
- stream_provider.stream(prompt, tools: tools, system: system_prompt, **options)
105
+ response.content.select { |content| content.respond_to?(:type) && content.type == "tool_use" }
88
106
  end
89
107
 
90
- def tools
91
- nil
108
+ def prompt_with_tool_results(input, response, requests)
109
+ messages = input.is_a?(Array) ? input.dup : [ { role: "user", content: input } ]
110
+ messages << response.to_h
111
+ messages << {
112
+ role: "user",
113
+ content: requests.map { |request| find_and_execute_tool(request).to_h }
114
+ }
115
+ messages
92
116
  end
93
117
 
94
- def self.find_tool(tool_name)
95
- tools.find { |tool| tool.tool_name == tool_name }
118
+ def default_stream_options(model: nil, reasoning: nil)
119
+ {
120
+ tools: tools,
121
+ system: system_prompt,
122
+ model: resolved_model(model),
123
+ reasoning: resolved_reasoning(reasoning),
124
+ cache_key: cache_key,
125
+ cache_retention: cache_retention
126
+ }.compact
96
127
  end
97
128
 
98
- def system_prompt
99
- nil
129
+ def resolved_provider(provider)
130
+ provider || self.provider
100
131
  end
101
132
 
102
- private
133
+ def resolved_model(model)
134
+ model || self.model
135
+ end
136
+
137
+ def resolved_reasoning(reasoning)
138
+ reasoning || self.reasoning
139
+ end
103
140
 
104
141
  def run_callbacks(callback_type, *args)
105
- callbacks = self.class.send("#{callback_type}_callbacks")
106
- callbacks.each do |callback|
107
- if callback.is_a?(Proc)
142
+ self.class.public_send("#{callback_type}_callbacks").each do |callback|
143
+ case callback
144
+ when Proc
108
145
  instance_exec(*args, &callback)
109
- elsif callback.is_a?(Symbol) || callback.is_a?(String)
110
- send(callback, *args)
146
+ when Symbol, String
147
+ public_send(callback, *args)
111
148
  end
112
149
  end
113
150
  end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LlmGateway
4
+ module Proxy
5
+ class Adapter
6
+ attr_reader :client
7
+
8
+ def initialize(client)
9
+ @client = client
10
+ end
11
+
12
+ def stream(message, tools: nil, system: nil, **options, &block)
13
+ target_adapter = LlmGateway.build_provider(client.target_config.merge(provider: client.target_provider))
14
+ mapper_class = target_adapter.stream_mapper_class
15
+ raise LlmGateway::Errors::MissingMapperForProvider, "No stream_mapper configured" unless mapper_class
16
+
17
+ mapper = mapper_class.new(
18
+ provider: LlmGateway::Client.provider_id_from_client(target_adapter.client),
19
+ api: target_adapter.stream_api_name
20
+ )
21
+
22
+ client.stream(normalize_messages(message), tools: tools, system: normalize_system(system), **options) do |chunk|
23
+ mapper.map(chunk, &block)
24
+ end
25
+
26
+ mapper.result
27
+ end
28
+
29
+ private
30
+
31
+ def normalize_system(system)
32
+ if system.nil?
33
+ []
34
+ elsif system.is_a?(String)
35
+ [ { role: "system", content: system } ]
36
+ elsif system.is_a?(Array)
37
+ system
38
+ else
39
+ raise ArgumentError, "System parameter must be a string or array, got #{system.class}"
40
+ end
41
+ end
42
+
43
+ def normalize_messages(message)
44
+ message.is_a?(String) ? [ { role: "user", content: message } ] : message
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "net/http"
5
+ require "uri"
6
+
7
+ module LlmGateway
8
+ module Proxy
9
+ class Client
10
+ attr_reader :url, :target_provider, :target_config, :path
11
+
12
+ def initialize(url:, target_provider:, target_config: {}, api_key: nil, path: "/agent/llm_proxy", **_options)
13
+ @url = url.to_s.sub(%r{/+\z}, "")
14
+ @target_provider = target_provider.to_s
15
+ @target_config = (target_config || {}).transform_keys(&:to_sym)
16
+ @api_key = api_key
17
+ @path = path.to_s.start_with?("/") ? path.to_s : "/#{path}"
18
+ end
19
+
20
+ def stream(messages, tools: nil, system: nil, **options, &block)
21
+ uri = URI("#{url}#{path}")
22
+ http = Net::HTTP.new(uri.host, uri.port)
23
+ http.use_ssl = uri.scheme == "https"
24
+ http.read_timeout = 480
25
+ http.open_timeout = 10
26
+
27
+ request = Net::HTTP::Post.new(uri)
28
+ request["content-type"] = "application/json"
29
+ request["accept"] = "text/event-stream"
30
+ request["accept-encoding"] = "identity"
31
+ request["authorization"] = "Bearer #{@api_key}" if @api_key
32
+ request.body = {
33
+ provider: target_provider,
34
+ config: target_config,
35
+ messages: messages,
36
+ system: system,
37
+ tools: tools,
38
+ options: options
39
+ }.to_json
40
+
41
+ http.request(request) do |response|
42
+ unless response.code.to_i == 200
43
+ body = +""
44
+ response.read_body { |chunk| body << chunk }
45
+ raise Errors::APIStatusError.new("Proxy request failed with status #{response.code}: #{body}", nil)
46
+ end
47
+
48
+ parse_sse_stream(response, &block)
49
+ end
50
+ end
51
+
52
+ private
53
+
54
+ def parse_sse_stream(response)
55
+ buffer = +""
56
+ response.read_body do |chunk|
57
+ buffer << chunk
58
+ while (idx = buffer.index("\n\n"))
59
+ raw_event = buffer.slice!(0, idx + 2)
60
+ event_type = nil
61
+ data_lines = []
62
+
63
+ raw_event.each_line do |line|
64
+ line = line.chomp
65
+ event_type = line.sub(/^event:\s*/, "") if line.start_with?("event:")
66
+ data_lines << line.sub(/^data:\s*/, "") if line.start_with?("data:")
67
+ end
68
+
69
+ next if data_lines.empty?
70
+
71
+ data_str = data_lines.join("\n")
72
+ next if data_str == "[DONE]"
73
+
74
+ data = begin
75
+ JSON.parse(data_str).deep_symbolize_keys
76
+ rescue JSON::ParserError
77
+ { raw: data_str }
78
+ end
79
+ yield({ event: event_type, data: data })
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module LlmGateway
6
+ module Proxy
7
+ class Server
8
+ PATH = "/agent/llm_proxy"
9
+
10
+ def call(env)
11
+ return not_found unless env["REQUEST_METHOD"] == "POST" && env["PATH_INFO"] == PATH
12
+
13
+ request = JSON.parse(env["rack.input"].read).deep_symbolize_keys
14
+ options = request[:options] || {}
15
+ options = options.merge(model: request[:model]) if request.key?(:model)
16
+ adapter = build_adapter(request)
17
+
18
+ body = Enumerator.new do |yielder|
19
+ adapter.raw_stream(
20
+ request[:messages],
21
+ system: request[:system],
22
+ tools: request[:tools],
23
+ **options
24
+ ) do |chunk|
25
+ yielder << encode_sse(chunk)
26
+ end
27
+ end
28
+
29
+ [ 200, { "content-type" => "text/event-stream", "cache-control" => "no-cache" }, body ]
30
+ rescue KeyError, JSON::ParserError, ArgumentError => e
31
+ json_error(400, e.message)
32
+ rescue Errors::UnsupportedProvider => e
33
+ json_error(404, e.message)
34
+ rescue StandardError => e
35
+ json_error(500, e.message)
36
+ end
37
+
38
+ private
39
+
40
+ def build_adapter(request)
41
+ provider = request.fetch(:provider)
42
+ config = (request[:config] || {}).merge(provider: provider)
43
+
44
+ LlmGateway.build_provider(config)
45
+ end
46
+
47
+ def encode_sse(chunk)
48
+ event = chunk[:event]
49
+ data = chunk[:data]
50
+ out = +""
51
+ out << "event: #{event}\n" if event
52
+ JSON.generate(data).each_line { |line| out << "data: #{line.chomp}\n" }
53
+ out << "\n"
54
+ end
55
+
56
+ def json_error(status, message)
57
+ [ status, { "content-type" => "application/json" }, [ { error: message }.to_json ] ]
58
+ end
59
+
60
+ def not_found
61
+ [ 404, { "content-type" => "application/json" }, [ { error: "Not found" }.to_json ] ]
62
+ end
63
+ end
64
+ end
65
+ end
@@ -4,16 +4,20 @@ module LlmGateway
4
4
  module Utils
5
5
  module_function
6
6
 
7
- def deep_symbolize_keys(hash)
8
- case hash
7
+ def symbolize_keys(hash)
8
+ hash.to_h.transform_keys { |key| key.respond_to?(:to_sym) ? key.to_sym : key }
9
+ end
10
+
11
+ def deep_symbolize_keys(value)
12
+ case value
9
13
  when Hash
10
- hash.each_with_object({}) do |(key, value), result|
11
- result[key.to_sym] = deep_symbolize_keys(value)
14
+ value.each_with_object({}) do |(key, nested_value), result|
15
+ result[symbolize_key(key)] = deep_symbolize_keys(nested_value)
12
16
  end
13
17
  when Array
14
- hash.map { |item| deep_symbolize_keys(item) }
18
+ value.map { |item| deep_symbolize_keys(item) }
15
19
  else
16
- hash
20
+ value
17
21
  end
18
22
  end
19
23
 
@@ -21,19 +25,118 @@ module LlmGateway
21
25
  !blank?(value)
22
26
  end
23
27
 
28
+ def presence(value)
29
+ present?(value) ? value : nil
30
+ end
31
+
24
32
  def blank?(value)
25
33
  case value
26
- when nil
34
+ when nil, false
27
35
  true
28
- when String
29
- value.strip.empty?
30
- when Array, Hash
31
- value.empty?
32
- when Numeric
36
+ when true, Numeric
33
37
  false
38
+ when String
39
+ value.match?(/\A[[:space:]]*\z/)
34
40
  else
35
- value.respond_to?(:empty?) ? value.empty? : false
41
+ value.respond_to?(:empty?) ? !!value.empty? : false
36
42
  end
37
43
  end
44
+
45
+ def symbolize_key(key)
46
+ key.respond_to?(:to_sym) ? key.to_sym : key
47
+ rescue StandardError
48
+ key
49
+ end
50
+ end
51
+ end
52
+
53
+ unless Class.method_defined?(:class_attribute)
54
+ class Class
55
+ def class_attribute(*names, instance_accessor: true, instance_reader: instance_accessor, instance_writer: instance_accessor, instance_predicate: true, default: nil)
56
+ names.each do |name|
57
+ ivar = :"@#{name}"
58
+ instance_variable_set(ivar, default)
59
+
60
+ unset = Object.new
61
+
62
+ define_singleton_method(name) do |value = unset|
63
+ unless value.equal?(unset)
64
+ instance_variable_set(ivar, value)
65
+ next value
66
+ end
67
+
68
+ if instance_variable_defined?(ivar)
69
+ instance_variable_get(ivar)
70
+ elsif superclass.respond_to?(name)
71
+ superclass.public_send(name)
72
+ end
73
+ end
74
+
75
+ define_singleton_method("#{name}=") do |value|
76
+ instance_variable_set(ivar, value)
77
+ end
78
+
79
+ define_singleton_method("#{name}?") { !!public_send(name) } if instance_predicate
80
+
81
+ if instance_reader
82
+ define_method(name) do
83
+ if instance_variable_defined?(ivar)
84
+ instance_variable_get(ivar)
85
+ else
86
+ self.class.public_send(name)
87
+ end
88
+ end
89
+ end
90
+
91
+ define_method("#{name}=") { |value| instance_variable_set(ivar, value) } if instance_writer
92
+
93
+ define_method("#{name}?") { !!public_send(name) } if instance_predicate
94
+ end
95
+ end
96
+ end
97
+ end
98
+
99
+ class Object
100
+ def blank?
101
+ LlmGateway::Utils.blank?(self)
102
+ end
103
+
104
+ def present?
105
+ LlmGateway::Utils.present?(self)
106
+ end
107
+
108
+ def presence
109
+ LlmGateway::Utils.presence(self)
110
+ end
111
+ end
112
+
113
+ class Hash
114
+ def symbolize_keys
115
+ transform_keys { |key| LlmGateway::Utils.symbolize_key(key) }
116
+ end
117
+
118
+ def symbolize_keys!
119
+ replace(symbolize_keys)
120
+ end
121
+
122
+ def deep_symbolize_keys
123
+ LlmGateway::Utils.deep_symbolize_keys(self)
124
+ end
125
+
126
+ def deep_symbolize_keys!
127
+ replace(deep_symbolize_keys)
128
+ end
129
+
130
+ unless method_defined?(:except)
131
+ def except(*keys)
132
+ reject { |key, _| keys.include?(key) }
133
+ end
134
+ end
135
+
136
+ unless method_defined?(:except!)
137
+ def except!(*keys)
138
+ keys.each { |key| delete(key) }
139
+ self
140
+ end
38
141
  end
39
142
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module LlmGateway
4
- VERSION = "0.6.0"
4
+ VERSION = "0.8.0"
5
5
  end
data/lib/llm_gateway.rb CHANGED
@@ -7,6 +7,10 @@ require_relative "llm_gateway/base_client"
7
7
  require_relative "llm_gateway/client"
8
8
  require_relative "llm_gateway/prompt"
9
9
  require_relative "llm_gateway/tool"
10
+ require_relative "llm_gateway/agents/event"
11
+ require_relative "llm_gateway/agents/in_memory_session_manager"
12
+ require_relative "llm_gateway/agents/file_session_manager"
13
+ require_relative "llm_gateway/agents/harness"
10
14
 
11
15
  # Load clients - order matters for inheritance
12
16
  require_relative "llm_gateway/clients/anthropic"
@@ -44,6 +48,9 @@ require_relative "llm_gateway/adapters/groq/chat_completions_adapter"
44
48
 
45
49
  # Load provider registry
46
50
  require_relative "llm_gateway/provider_registry"
51
+ require_relative "llm_gateway/proxy/client"
52
+ require_relative "llm_gateway/proxy/adapter"
53
+ require_relative "llm_gateway/proxy/server"
47
54
 
48
55
  module LlmGateway
49
56
  class Error < StandardError; end
@@ -106,7 +113,7 @@ module LlmGateway
106
113
  entry = ProviderRegistry.resolve(provider_name)
107
114
 
108
115
  client = entry[:client].new(**config)
109
- entry[:adapter].new(client)
116
+ entry[:adapter].new(client, provider_key: provider_name)
110
117
  end
111
118
 
112
119
  def self.configure(configs)
@@ -156,4 +163,8 @@ module LlmGateway
156
163
  ProviderRegistry.register("openai_codex",
157
164
  client: Clients::OpenAI,
158
165
  adapter: Adapters::OpenAICodex::ResponsesAdapter)
166
+
167
+ ProviderRegistry.register("proxy",
168
+ client: Proxy::Client,
169
+ adapter: Proxy::Adapter)
159
170
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: llm_gateway
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - billybonks
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-05-27 00:00:00.000000000 Z
11
+ date: 2026-06-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dry-struct
@@ -43,6 +43,7 @@ files:
43
43
  - Rakefile
44
44
  - docs/migration-guide.md
45
45
  - docs/migration_guide_0.6.0.md
46
+ - docs/migration_guide_0.7.0.md
46
47
  - lib/llm_gateway.rb
47
48
  - lib/llm_gateway/adapters/adapter.rb
48
49
  - lib/llm_gateway/adapters/anthropic/acts_like_messages.rb
@@ -75,6 +76,15 @@ files:
75
76
  - lib/llm_gateway/adapters/option_mapper.rb
76
77
  - lib/llm_gateway/adapters/stream_mapper.rb
77
78
  - lib/llm_gateway/adapters/structs.rb
79
+ - lib/llm_gateway/agents/event.rb
80
+ - lib/llm_gateway/agents/file_session_manager.rb
81
+ - lib/llm_gateway/agents/harness.rb
82
+ - lib/llm_gateway/agents/in_memory_session_manager.rb
83
+ - lib/llm_gateway/agents/tools/bash_tool.rb
84
+ - lib/llm_gateway/agents/tools/edit_tool.rb
85
+ - lib/llm_gateway/agents/tools/read_tool.rb
86
+ - lib/llm_gateway/agents/tools/tool_utils.rb
87
+ - lib/llm_gateway/agents/tools/write_tool.rb
78
88
  - lib/llm_gateway/base_client.rb
79
89
  - lib/llm_gateway/client.rb
80
90
  - lib/llm_gateway/clients/anthropic.rb
@@ -87,6 +97,9 @@ files:
87
97
  - lib/llm_gateway/errors.rb
88
98
  - lib/llm_gateway/prompt.rb
89
99
  - lib/llm_gateway/provider_registry.rb
100
+ - lib/llm_gateway/proxy/adapter.rb
101
+ - lib/llm_gateway/proxy/client.rb
102
+ - lib/llm_gateway/proxy/server.rb
90
103
  - lib/llm_gateway/tool.rb
91
104
  - lib/llm_gateway/utils.rb
92
105
  - lib/llm_gateway/version.rb