Class: Pikuri::Agent::ChatTransport
- Inherits:
-
Object
- Object
- Pikuri::Agent::ChatTransport
- Defined in:
- lib/pikuri/agent/chat_transport.rb
Overview
Everything that must travel together for a chat to resolve to the same
model on the same server on every construction: model id, provider
hint, registry-bypass flag, and — when the model lives off the
process-global RubyLLM.config default — that server's base URL and API
key. One value object so a forwarding site (the synthesizer, a sub-agent
spawn, a mid-conversation switch) can't silently drop a field and route
the chat to the wrong server or raise RubyLLM::ModelNotFoundError.
Why api_base / api_key live here
RubyLLM::Chat#with_model swaps only model/provider against the chat's
existing connection, so switching to a model on a different server
needs the connection to travel with the model — else the new id is sent
to the old server's URL with the old key. Pikuri::Agent maps these two generic
fields onto the provider's config slots (+##provider_api_base+ /
#{provider}_api_key) via a per-chat RubyLLM::Context; both nil for a
transport riding the global config.
Pure data carrier — no RubyLLM references here, so the seam stays in
Pikuri::Agent, bin/pikuri-chat, and Tool.
Instance Attribute Summary collapse
-
#api_base ⇒ String?
readonly
This server's base URL (e.g.
http://localhost:8080/v1);nilrides the global config. -
#api_key ⇒ String?
readonly
API key for this server;
nilrides the global config. -
#assume_model_exists ⇒ Boolean
readonly
Forwarded to
RubyLLM.chat;trueskips the registry lookup and trusts the model id. -
#context_window ⇒ Integer?
readonly
Explicit context-window cap for this model-on-server, or
nilto defer to ContextWindowDetector. -
#faraday_adapter ⇒ Class, ...
readonly
A custom Faraday adapter to install on this agent's per-chat
RubyLLM::Context(viaconfig.faraday_adapter=), ornilfor ruby_llm's default (+:net_http+). -
#model ⇒ String?
readonly
LLM id;
nildefers toRubyLLM.config.default_modelat Pikuri::Agent construction. -
#provider ⇒ Symbol?
readonly
Forwarded to
RubyLLM.chat.
Class Method Summary collapse
-
.from_openai_server(server:, model:, api_key: 'not-needed', context_window: nil) ⇒ ChatTransport
Build an +:openai+-provider transport for an OpenAI-compatible server (local llama.cpp, cloud endpoint, ...), carrying its connection so the agent rides a per-chat
RubyLLM::Contextinstead of the global config.
Instance Method Summary collapse
-
#chat_kwargs ⇒ Hash{Symbol => String, Symbol, Boolean, nil}
Model-resolution kwargs to spread into
RubyLLM.chat/RubyLLM::Context#chat. -
#connection_overrides? ⇒ Boolean
Whether this transport overrides the process-global connection (and so needs a dedicated
RubyLLM::Context). -
#initialize(model:, provider: nil, assume_model_exists: false, api_base: nil, api_key: nil, context_window: nil, faraday_adapter: nil) ⇒ ChatTransport
constructor
A new instance of ChatTransport.
-
#inspect ⇒ String
(also: #to_s)
Default
Data#inspectwould printapi_keyverbatim into any log line or backtrace.
Constructor Details
#initialize(model:, provider: nil, assume_model_exists: false, api_base: nil, api_key: nil, context_window: nil, faraday_adapter: nil) ⇒ ChatTransport
Returns a new instance of ChatTransport.
107 108 109 110 111 112 113 114 |
# File 'lib/pikuri/agent/chat_transport.rb', line 107 def initialize(model:, provider: nil, assume_model_exists: false, api_base: nil, api_key: nil, context_window: nil, faraday_adapter: nil) if (api_base || api_key) && provider.nil? raise ArgumentError, "api_base/api_key require a provider, got #{provider.inspect}" end super end |
Instance Attribute Details
#api_base ⇒ String? (readonly)
Returns this server's base URL (e.g.
http://localhost:8080/v1); nil rides the global config. Mapped to
+##provider_api_base+ by Pikuri::Agent.
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 |
# File 'lib/pikuri/agent/chat_transport.rb', line 64 class ChatTransport < Data.define(:model, :provider, :assume_model_exists, :api_base, :api_key, :context_window, :faraday_adapter) # Build an +:openai+-provider transport for an OpenAI-compatible server # (local llama.cpp, cloud endpoint, ...), carrying its connection so the # agent rides a per-chat +RubyLLM::Context+ instead of the global config. # The +bin/pikuri-*+ host-boot factory in place of +RubyLLM.configure+ — # one isolated connection per agent. # # A trailing +/v1+ on +server+ is stripped and re-appended exactly once, # so +https://api.x.ai+, +.../v1+, and +.../v1/+ all normalize to the same # +.../v1+ base — without which a +/v1+-terminated value would double to # +/v1/v1+ and 404. # # @param server [String] server origin, with or without a trailing +/v1+ # @param model [String] model id served there, trusted verbatim # @param api_key [String] API key; the +"not-needed"+ placeholder for a # keyless local server # @param context_window [Integer, nil] explicit cap, or +nil+ to defer to # {ContextWindowDetector}'s +/props+ probe (right for local llama.cpp; # an override for a cloud server the probe can't reach) # @return [ChatTransport] def self.from_openai_server(server:, model:, api_key: 'not-needed', context_window: nil) base = server.to_s.strip.chomp('/').delete_suffix('/v1') new( model: model, provider: :openai, assume_model_exists: true, api_base: "#{base}/v1", api_key: api_key, context_window: context_window ) end # @param model [String, nil] # @param provider [Symbol, nil] # @param assume_model_exists [Boolean] # @param api_base [String, nil] # @param api_key [String, nil] # @param context_window [Integer, nil] # @param faraday_adapter [Class, Symbol, nil] # @raise [ArgumentError] if +api_base+ or +api_key+ is set without # a +provider+ (the provider names the config slots the # connection overrides map onto) def initialize(model:, provider: nil, assume_model_exists: false, api_base: nil, api_key: nil, context_window: nil, faraday_adapter: nil) if (api_base || api_key) && provider.nil? raise ArgumentError, "api_base/api_key require a provider, got #{provider.inspect}" end super end # Model-resolution kwargs to spread into +RubyLLM.chat+ / # +RubyLLM::Context#chat+. Excludes the connection fields (those configure # the +Context+, not the +chat+ call). # # @return [Hash{Symbol => String, Symbol, Boolean, nil}] def chat_kwargs { model: model, provider: provider, assume_model_exists: assume_model_exists } end # Whether this transport overrides the process-global connection # (and so needs a dedicated +RubyLLM::Context+). # # @return [Boolean] def connection_overrides? !api_base.nil? || !api_key.nil? end # Default +Data#inspect+ would print +api_key+ verbatim into any log line # or backtrace. Redact it. # # @return [String] def inspect "#<#{self.class} model=#{model.inspect} provider=#{provider.inspect} " \ "assume_model_exists=#{assume_model_exists} api_base=#{api_base.inspect} " \ "api_key=#{api_key.nil? ? 'nil' : '[REDACTED]'} context_window=#{context_window.inspect} " \ "faraday_adapter=#{faraday_adapter.inspect}>" end alias to_s inspect end |
#api_key ⇒ String? (readonly)
Returns API key for this server; nil rides the global
config. Mapped to +##provider_api_key+ by Pikuri::Agent. Redacted in
#inspect.
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 |
# File 'lib/pikuri/agent/chat_transport.rb', line 64 class ChatTransport < Data.define(:model, :provider, :assume_model_exists, :api_base, :api_key, :context_window, :faraday_adapter) # Build an +:openai+-provider transport for an OpenAI-compatible server # (local llama.cpp, cloud endpoint, ...), carrying its connection so the # agent rides a per-chat +RubyLLM::Context+ instead of the global config. # The +bin/pikuri-*+ host-boot factory in place of +RubyLLM.configure+ — # one isolated connection per agent. # # A trailing +/v1+ on +server+ is stripped and re-appended exactly once, # so +https://api.x.ai+, +.../v1+, and +.../v1/+ all normalize to the same # +.../v1+ base — without which a +/v1+-terminated value would double to # +/v1/v1+ and 404. # # @param server [String] server origin, with or without a trailing +/v1+ # @param model [String] model id served there, trusted verbatim # @param api_key [String] API key; the +"not-needed"+ placeholder for a # keyless local server # @param context_window [Integer, nil] explicit cap, or +nil+ to defer to # {ContextWindowDetector}'s +/props+ probe (right for local llama.cpp; # an override for a cloud server the probe can't reach) # @return [ChatTransport] def self.from_openai_server(server:, model:, api_key: 'not-needed', context_window: nil) base = server.to_s.strip.chomp('/').delete_suffix('/v1') new( model: model, provider: :openai, assume_model_exists: true, api_base: "#{base}/v1", api_key: api_key, context_window: context_window ) end # @param model [String, nil] # @param provider [Symbol, nil] # @param assume_model_exists [Boolean] # @param api_base [String, nil] # @param api_key [String, nil] # @param context_window [Integer, nil] # @param faraday_adapter [Class, Symbol, nil] # @raise [ArgumentError] if +api_base+ or +api_key+ is set without # a +provider+ (the provider names the config slots the # connection overrides map onto) def initialize(model:, provider: nil, assume_model_exists: false, api_base: nil, api_key: nil, context_window: nil, faraday_adapter: nil) if (api_base || api_key) && provider.nil? raise ArgumentError, "api_base/api_key require a provider, got #{provider.inspect}" end super end # Model-resolution kwargs to spread into +RubyLLM.chat+ / # +RubyLLM::Context#chat+. Excludes the connection fields (those configure # the +Context+, not the +chat+ call). # # @return [Hash{Symbol => String, Symbol, Boolean, nil}] def chat_kwargs { model: model, provider: provider, assume_model_exists: assume_model_exists } end # Whether this transport overrides the process-global connection # (and so needs a dedicated +RubyLLM::Context+). # # @return [Boolean] def connection_overrides? !api_base.nil? || !api_key.nil? end # Default +Data#inspect+ would print +api_key+ verbatim into any log line # or backtrace. Redact it. # # @return [String] def inspect "#<#{self.class} model=#{model.inspect} provider=#{provider.inspect} " \ "assume_model_exists=#{assume_model_exists} api_base=#{api_base.inspect} " \ "api_key=#{api_key.nil? ? 'nil' : '[REDACTED]'} context_window=#{context_window.inspect} " \ "faraday_adapter=#{faraday_adapter.inspect}>" end alias to_s inspect end |
#assume_model_exists ⇒ Boolean (readonly)
Returns forwarded to RubyLLM.chat; true skips the registry
lookup and trusts the model id. Requires provider.
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 |
# File 'lib/pikuri/agent/chat_transport.rb', line 64 class ChatTransport < Data.define(:model, :provider, :assume_model_exists, :api_base, :api_key, :context_window, :faraday_adapter) # Build an +:openai+-provider transport for an OpenAI-compatible server # (local llama.cpp, cloud endpoint, ...), carrying its connection so the # agent rides a per-chat +RubyLLM::Context+ instead of the global config. # The +bin/pikuri-*+ host-boot factory in place of +RubyLLM.configure+ — # one isolated connection per agent. # # A trailing +/v1+ on +server+ is stripped and re-appended exactly once, # so +https://api.x.ai+, +.../v1+, and +.../v1/+ all normalize to the same # +.../v1+ base — without which a +/v1+-terminated value would double to # +/v1/v1+ and 404. # # @param server [String] server origin, with or without a trailing +/v1+ # @param model [String] model id served there, trusted verbatim # @param api_key [String] API key; the +"not-needed"+ placeholder for a # keyless local server # @param context_window [Integer, nil] explicit cap, or +nil+ to defer to # {ContextWindowDetector}'s +/props+ probe (right for local llama.cpp; # an override for a cloud server the probe can't reach) # @return [ChatTransport] def self.from_openai_server(server:, model:, api_key: 'not-needed', context_window: nil) base = server.to_s.strip.chomp('/').delete_suffix('/v1') new( model: model, provider: :openai, assume_model_exists: true, api_base: "#{base}/v1", api_key: api_key, context_window: context_window ) end # @param model [String, nil] # @param provider [Symbol, nil] # @param assume_model_exists [Boolean] # @param api_base [String, nil] # @param api_key [String, nil] # @param context_window [Integer, nil] # @param faraday_adapter [Class, Symbol, nil] # @raise [ArgumentError] if +api_base+ or +api_key+ is set without # a +provider+ (the provider names the config slots the # connection overrides map onto) def initialize(model:, provider: nil, assume_model_exists: false, api_base: nil, api_key: nil, context_window: nil, faraday_adapter: nil) if (api_base || api_key) && provider.nil? raise ArgumentError, "api_base/api_key require a provider, got #{provider.inspect}" end super end # Model-resolution kwargs to spread into +RubyLLM.chat+ / # +RubyLLM::Context#chat+. Excludes the connection fields (those configure # the +Context+, not the +chat+ call). # # @return [Hash{Symbol => String, Symbol, Boolean, nil}] def chat_kwargs { model: model, provider: provider, assume_model_exists: assume_model_exists } end # Whether this transport overrides the process-global connection # (and so needs a dedicated +RubyLLM::Context+). # # @return [Boolean] def connection_overrides? !api_base.nil? || !api_key.nil? end # Default +Data#inspect+ would print +api_key+ verbatim into any log line # or backtrace. Redact it. # # @return [String] def inspect "#<#{self.class} model=#{model.inspect} provider=#{provider.inspect} " \ "assume_model_exists=#{assume_model_exists} api_base=#{api_base.inspect} " \ "api_key=#{api_key.nil? ? 'nil' : '[REDACTED]'} context_window=#{context_window.inspect} " \ "faraday_adapter=#{faraday_adapter.inspect}>" end alias to_s inspect end |
#context_window ⇒ Integer? (readonly)
Returns explicit context-window cap for this
model-on-server, or nil to defer to Pikuri::Agent::ContextWindowDetector. Travels
with the model because the cap is per-model-per-server. Never sent to
ruby_llm — pure pikuri metadata read by
Pikuri::Agent#detect_and_emit_context_cap!, and the cap-inheritance channel:
a spawned agent gets +parent.transport.with(context_window:
parent.context_window_cap)+ so the resolved cap rides along without a
re-probe.
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 |
# File 'lib/pikuri/agent/chat_transport.rb', line 64 class ChatTransport < Data.define(:model, :provider, :assume_model_exists, :api_base, :api_key, :context_window, :faraday_adapter) # Build an +:openai+-provider transport for an OpenAI-compatible server # (local llama.cpp, cloud endpoint, ...), carrying its connection so the # agent rides a per-chat +RubyLLM::Context+ instead of the global config. # The +bin/pikuri-*+ host-boot factory in place of +RubyLLM.configure+ — # one isolated connection per agent. # # A trailing +/v1+ on +server+ is stripped and re-appended exactly once, # so +https://api.x.ai+, +.../v1+, and +.../v1/+ all normalize to the same # +.../v1+ base — without which a +/v1+-terminated value would double to # +/v1/v1+ and 404. # # @param server [String] server origin, with or without a trailing +/v1+ # @param model [String] model id served there, trusted verbatim # @param api_key [String] API key; the +"not-needed"+ placeholder for a # keyless local server # @param context_window [Integer, nil] explicit cap, or +nil+ to defer to # {ContextWindowDetector}'s +/props+ probe (right for local llama.cpp; # an override for a cloud server the probe can't reach) # @return [ChatTransport] def self.from_openai_server(server:, model:, api_key: 'not-needed', context_window: nil) base = server.to_s.strip.chomp('/').delete_suffix('/v1') new( model: model, provider: :openai, assume_model_exists: true, api_base: "#{base}/v1", api_key: api_key, context_window: context_window ) end # @param model [String, nil] # @param provider [Symbol, nil] # @param assume_model_exists [Boolean] # @param api_base [String, nil] # @param api_key [String, nil] # @param context_window [Integer, nil] # @param faraday_adapter [Class, Symbol, nil] # @raise [ArgumentError] if +api_base+ or +api_key+ is set without # a +provider+ (the provider names the config slots the # connection overrides map onto) def initialize(model:, provider: nil, assume_model_exists: false, api_base: nil, api_key: nil, context_window: nil, faraday_adapter: nil) if (api_base || api_key) && provider.nil? raise ArgumentError, "api_base/api_key require a provider, got #{provider.inspect}" end super end # Model-resolution kwargs to spread into +RubyLLM.chat+ / # +RubyLLM::Context#chat+. Excludes the connection fields (those configure # the +Context+, not the +chat+ call). # # @return [Hash{Symbol => String, Symbol, Boolean, nil}] def chat_kwargs { model: model, provider: provider, assume_model_exists: assume_model_exists } end # Whether this transport overrides the process-global connection # (and so needs a dedicated +RubyLLM::Context+). # # @return [Boolean] def connection_overrides? !api_base.nil? || !api_key.nil? end # Default +Data#inspect+ would print +api_key+ verbatim into any log line # or backtrace. Redact it. # # @return [String] def inspect "#<#{self.class} model=#{model.inspect} provider=#{provider.inspect} " \ "assume_model_exists=#{assume_model_exists} api_base=#{api_base.inspect} " \ "api_key=#{api_key.nil? ? 'nil' : '[REDACTED]'} context_window=#{context_window.inspect} " \ "faraday_adapter=#{faraday_adapter.inspect}>" end alias to_s inspect end |
#faraday_adapter ⇒ Class, ... (readonly)
Returns a custom Faraday adapter to install on this
agent's per-chat RubyLLM::Context (via config.faraday_adapter=),
or nil for ruby_llm's default (+:net_http+). A generic HTTP seam —
inject a VCR / logging / scripted-fake adapter without touching the
global RubyLLM.config. Its worked use is
Testing.fake_transport, which bakes an anonymous scripted
adapter here so the real ruby_llm loop runs against canned HTTP. Only
honoured on the connection-override path (#connection_overrides?),
which any adapter-carrying transport is on (it also sets api_base).
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 |
# File 'lib/pikuri/agent/chat_transport.rb', line 64 class ChatTransport < Data.define(:model, :provider, :assume_model_exists, :api_base, :api_key, :context_window, :faraday_adapter) # Build an +:openai+-provider transport for an OpenAI-compatible server # (local llama.cpp, cloud endpoint, ...), carrying its connection so the # agent rides a per-chat +RubyLLM::Context+ instead of the global config. # The +bin/pikuri-*+ host-boot factory in place of +RubyLLM.configure+ — # one isolated connection per agent. # # A trailing +/v1+ on +server+ is stripped and re-appended exactly once, # so +https://api.x.ai+, +.../v1+, and +.../v1/+ all normalize to the same # +.../v1+ base — without which a +/v1+-terminated value would double to # +/v1/v1+ and 404. # # @param server [String] server origin, with or without a trailing +/v1+ # @param model [String] model id served there, trusted verbatim # @param api_key [String] API key; the +"not-needed"+ placeholder for a # keyless local server # @param context_window [Integer, nil] explicit cap, or +nil+ to defer to # {ContextWindowDetector}'s +/props+ probe (right for local llama.cpp; # an override for a cloud server the probe can't reach) # @return [ChatTransport] def self.from_openai_server(server:, model:, api_key: 'not-needed', context_window: nil) base = server.to_s.strip.chomp('/').delete_suffix('/v1') new( model: model, provider: :openai, assume_model_exists: true, api_base: "#{base}/v1", api_key: api_key, context_window: context_window ) end # @param model [String, nil] # @param provider [Symbol, nil] # @param assume_model_exists [Boolean] # @param api_base [String, nil] # @param api_key [String, nil] # @param context_window [Integer, nil] # @param faraday_adapter [Class, Symbol, nil] # @raise [ArgumentError] if +api_base+ or +api_key+ is set without # a +provider+ (the provider names the config slots the # connection overrides map onto) def initialize(model:, provider: nil, assume_model_exists: false, api_base: nil, api_key: nil, context_window: nil, faraday_adapter: nil) if (api_base || api_key) && provider.nil? raise ArgumentError, "api_base/api_key require a provider, got #{provider.inspect}" end super end # Model-resolution kwargs to spread into +RubyLLM.chat+ / # +RubyLLM::Context#chat+. Excludes the connection fields (those configure # the +Context+, not the +chat+ call). # # @return [Hash{Symbol => String, Symbol, Boolean, nil}] def chat_kwargs { model: model, provider: provider, assume_model_exists: assume_model_exists } end # Whether this transport overrides the process-global connection # (and so needs a dedicated +RubyLLM::Context+). # # @return [Boolean] def connection_overrides? !api_base.nil? || !api_key.nil? end # Default +Data#inspect+ would print +api_key+ verbatim into any log line # or backtrace. Redact it. # # @return [String] def inspect "#<#{self.class} model=#{model.inspect} provider=#{provider.inspect} " \ "assume_model_exists=#{assume_model_exists} api_base=#{api_base.inspect} " \ "api_key=#{api_key.nil? ? 'nil' : '[REDACTED]'} context_window=#{context_window.inspect} " \ "faraday_adapter=#{faraday_adapter.inspect}>" end alias to_s inspect end |
#model ⇒ String? (readonly)
Returns LLM id; nil defers to
RubyLLM.config.default_model at Pikuri::Agent construction.
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 |
# File 'lib/pikuri/agent/chat_transport.rb', line 64 class ChatTransport < Data.define(:model, :provider, :assume_model_exists, :api_base, :api_key, :context_window, :faraday_adapter) # Build an +:openai+-provider transport for an OpenAI-compatible server # (local llama.cpp, cloud endpoint, ...), carrying its connection so the # agent rides a per-chat +RubyLLM::Context+ instead of the global config. # The +bin/pikuri-*+ host-boot factory in place of +RubyLLM.configure+ — # one isolated connection per agent. # # A trailing +/v1+ on +server+ is stripped and re-appended exactly once, # so +https://api.x.ai+, +.../v1+, and +.../v1/+ all normalize to the same # +.../v1+ base — without which a +/v1+-terminated value would double to # +/v1/v1+ and 404. # # @param server [String] server origin, with or without a trailing +/v1+ # @param model [String] model id served there, trusted verbatim # @param api_key [String] API key; the +"not-needed"+ placeholder for a # keyless local server # @param context_window [Integer, nil] explicit cap, or +nil+ to defer to # {ContextWindowDetector}'s +/props+ probe (right for local llama.cpp; # an override for a cloud server the probe can't reach) # @return [ChatTransport] def self.from_openai_server(server:, model:, api_key: 'not-needed', context_window: nil) base = server.to_s.strip.chomp('/').delete_suffix('/v1') new( model: model, provider: :openai, assume_model_exists: true, api_base: "#{base}/v1", api_key: api_key, context_window: context_window ) end # @param model [String, nil] # @param provider [Symbol, nil] # @param assume_model_exists [Boolean] # @param api_base [String, nil] # @param api_key [String, nil] # @param context_window [Integer, nil] # @param faraday_adapter [Class, Symbol, nil] # @raise [ArgumentError] if +api_base+ or +api_key+ is set without # a +provider+ (the provider names the config slots the # connection overrides map onto) def initialize(model:, provider: nil, assume_model_exists: false, api_base: nil, api_key: nil, context_window: nil, faraday_adapter: nil) if (api_base || api_key) && provider.nil? raise ArgumentError, "api_base/api_key require a provider, got #{provider.inspect}" end super end # Model-resolution kwargs to spread into +RubyLLM.chat+ / # +RubyLLM::Context#chat+. Excludes the connection fields (those configure # the +Context+, not the +chat+ call). # # @return [Hash{Symbol => String, Symbol, Boolean, nil}] def chat_kwargs { model: model, provider: provider, assume_model_exists: assume_model_exists } end # Whether this transport overrides the process-global connection # (and so needs a dedicated +RubyLLM::Context+). # # @return [Boolean] def connection_overrides? !api_base.nil? || !api_key.nil? end # Default +Data#inspect+ would print +api_key+ verbatim into any log line # or backtrace. Redact it. # # @return [String] def inspect "#<#{self.class} model=#{model.inspect} provider=#{provider.inspect} " \ "assume_model_exists=#{assume_model_exists} api_base=#{api_base.inspect} " \ "api_key=#{api_key.nil? ? 'nil' : '[REDACTED]'} context_window=#{context_window.inspect} " \ "faraday_adapter=#{faraday_adapter.inspect}>" end alias to_s inspect end |
#provider ⇒ Symbol? (readonly)
Returns forwarded to RubyLLM.chat. Required with
assume_model_exists for a local OpenAI-compatible server whose ids
aren't in ruby_llm's registry, and whenever +api_base+/+api_key+ is
set (it names the config slots).
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 |
# File 'lib/pikuri/agent/chat_transport.rb', line 64 class ChatTransport < Data.define(:model, :provider, :assume_model_exists, :api_base, :api_key, :context_window, :faraday_adapter) # Build an +:openai+-provider transport for an OpenAI-compatible server # (local llama.cpp, cloud endpoint, ...), carrying its connection so the # agent rides a per-chat +RubyLLM::Context+ instead of the global config. # The +bin/pikuri-*+ host-boot factory in place of +RubyLLM.configure+ — # one isolated connection per agent. # # A trailing +/v1+ on +server+ is stripped and re-appended exactly once, # so +https://api.x.ai+, +.../v1+, and +.../v1/+ all normalize to the same # +.../v1+ base — without which a +/v1+-terminated value would double to # +/v1/v1+ and 404. # # @param server [String] server origin, with or without a trailing +/v1+ # @param model [String] model id served there, trusted verbatim # @param api_key [String] API key; the +"not-needed"+ placeholder for a # keyless local server # @param context_window [Integer, nil] explicit cap, or +nil+ to defer to # {ContextWindowDetector}'s +/props+ probe (right for local llama.cpp; # an override for a cloud server the probe can't reach) # @return [ChatTransport] def self.from_openai_server(server:, model:, api_key: 'not-needed', context_window: nil) base = server.to_s.strip.chomp('/').delete_suffix('/v1') new( model: model, provider: :openai, assume_model_exists: true, api_base: "#{base}/v1", api_key: api_key, context_window: context_window ) end # @param model [String, nil] # @param provider [Symbol, nil] # @param assume_model_exists [Boolean] # @param api_base [String, nil] # @param api_key [String, nil] # @param context_window [Integer, nil] # @param faraday_adapter [Class, Symbol, nil] # @raise [ArgumentError] if +api_base+ or +api_key+ is set without # a +provider+ (the provider names the config slots the # connection overrides map onto) def initialize(model:, provider: nil, assume_model_exists: false, api_base: nil, api_key: nil, context_window: nil, faraday_adapter: nil) if (api_base || api_key) && provider.nil? raise ArgumentError, "api_base/api_key require a provider, got #{provider.inspect}" end super end # Model-resolution kwargs to spread into +RubyLLM.chat+ / # +RubyLLM::Context#chat+. Excludes the connection fields (those configure # the +Context+, not the +chat+ call). # # @return [Hash{Symbol => String, Symbol, Boolean, nil}] def chat_kwargs { model: model, provider: provider, assume_model_exists: assume_model_exists } end # Whether this transport overrides the process-global connection # (and so needs a dedicated +RubyLLM::Context+). # # @return [Boolean] def connection_overrides? !api_base.nil? || !api_key.nil? end # Default +Data#inspect+ would print +api_key+ verbatim into any log line # or backtrace. Redact it. # # @return [String] def inspect "#<#{self.class} model=#{model.inspect} provider=#{provider.inspect} " \ "assume_model_exists=#{assume_model_exists} api_base=#{api_base.inspect} " \ "api_key=#{api_key.nil? ? 'nil' : '[REDACTED]'} context_window=#{context_window.inspect} " \ "faraday_adapter=#{faraday_adapter.inspect}>" end alias to_s inspect end |
Class Method Details
.from_openai_server(server:, model:, api_key: 'not-needed', context_window: nil) ⇒ ChatTransport
Build an +:openai+-provider transport for an OpenAI-compatible server
(local llama.cpp, cloud endpoint, ...), carrying its connection so the
agent rides a per-chat RubyLLM::Context instead of the global config.
The bin/pikuri-* host-boot factory in place of RubyLLM.configure —
one isolated connection per agent.
A trailing /v1 on server is stripped and re-appended exactly once,
so https://api.x.ai, .../v1, and .../v1/ all normalize to the same
.../v1 base — without which a +/v1+-terminated value would double to
/v1/v1 and 404.
85 86 87 88 89 90 91 92 93 94 95 |
# File 'lib/pikuri/agent/chat_transport.rb', line 85 def self.from_openai_server(server:, model:, api_key: 'not-needed', context_window: nil) base = server.to_s.strip.chomp('/').delete_suffix('/v1') new( model: model, provider: :openai, assume_model_exists: true, api_base: "#{base}/v1", api_key: api_key, context_window: context_window ) end |
Instance Method Details
#chat_kwargs ⇒ Hash{Symbol => String, Symbol, Boolean, nil}
Model-resolution kwargs to spread into RubyLLM.chat /
RubyLLM::Context#chat. Excludes the connection fields (those configure
the Context, not the chat call).
121 122 123 |
# File 'lib/pikuri/agent/chat_transport.rb', line 121 def chat_kwargs { model: model, provider: provider, assume_model_exists: assume_model_exists } end |
#connection_overrides? ⇒ Boolean
Whether this transport overrides the process-global connection
(and so needs a dedicated RubyLLM::Context).
129 130 131 |
# File 'lib/pikuri/agent/chat_transport.rb', line 129 def connection_overrides? !api_base.nil? || !api_key.nil? end |
#inspect ⇒ String Also known as: to_s
Default Data#inspect would print api_key verbatim into any log line
or backtrace. Redact it.
137 138 139 140 141 142 |
# File 'lib/pikuri/agent/chat_transport.rb', line 137 def inspect "#<#{self.class} model=#{model.inspect} provider=#{provider.inspect} " \ "assume_model_exists=#{assume_model_exists} api_base=#{api_base.inspect} " \ "api_key=#{api_key.nil? ? 'nil' : '[REDACTED]'} context_window=#{context_window.inspect} " \ "faraday_adapter=#{faraday_adapter.inspect}>" end |