Module: Insika::Refinement::ProposerFactory
- Defined in:
- lib/insika/refinement/proposer.rb
Overview
Resolves WHICH model(s) write the candidate, and builds the ask.
refinement.proposers on the agent (a PANEL)
-> refinement.proposer ("deepseek/deepseek-v4-flash" | "deepseek-v4-flash")
-> the platform utility_model
-> nothing, and the caller refuses. There is no default model here on purpose:
guessing one spends an operator's provider budget without being asked.
Class Method Summary collapse
-
.build(config, utility_model: nil, ask_factory: nil, llm: nil) ⇒ Object
config: the agent's
refinementhash. - .normalize_ref(entry) ⇒ Object
-
.panel(config, utility_model: nil, ask_factory: nil, max: nil, llm: nil) ⇒ Object
-> [Proposer], in configured order, DEDUPED by model ref and capped at the fan-out (says the panel reuses it).
-
.refs_for(config, utility_model) ⇒ Object
proposersaccepts either syntax — a bare ref ("deepseek/deepseek-v4-flash") or the RFC's{ "model" =>, "provider"? => }— because the two already coexist in this config (proposeris a bare ref,judgesare hashes) and refusing one of them would only teach operators which page they were reading. -
.ruby_llm_ask(model, provider, llm: nil) ⇒ Object
Temperature 0: a proposal an operator rejects should be re-proposable, and a gate result should be attributable to the edit rather than to a sampling seed.
-
.split_ref(ref) ⇒ Object
"provider/model" -> [provider, model]; "model" -> [nil, model].
Class Method Details
.build(config, utility_model: nil, ask_factory: nil, llm: nil) ⇒ Object
config: the agent's refinement hash. -> Proposer | nil (the FIRST of the
panel — single-proposer entry point, kept because a deployment that
never configured a panel is a panel of one).
194 195 196 |
# File 'lib/insika/refinement/proposer.rb', line 194 def build(config, utility_model: nil, ask_factory: nil, llm: nil) panel(config, utility_model: utility_model, ask_factory: ask_factory, llm: llm).first end |
.normalize_ref(entry) ⇒ Object
227 228 229 230 231 232 233 234 235 |
# File 'lib/insika/refinement/proposer.rb', line 227 def normalize_ref(entry) return Coercion.presence(entry) unless entry.is_a?(Hash) e = Coercion.deep_stringify(entry) model = Coercion.presence(e["model"]) return nil if model.nil? (provider = Coercion.presence(e["provider"])) ? "#{provider}/#{model}" : model end |
.panel(config, utility_model: nil, ask_factory: nil, max: nil, llm: nil) ⇒ Object
-> [Proposer], in configured order, DEDUPED by model ref and capped at the
fan-out (says the panel reuses it). Two entries naming the same
model are one proposer: asking the same model twice at temperature 0 measures
its variance, which is exactly what rejected for the judges.
llm: the graph's own RubyLLM context; nil = the global
constant. Today only the deployment root builds a panel, and a deployment
is one graph per process — the seam exists so an embedded graph that ever
gains the refinement commands proposes on its own credentials.
206 207 208 209 210 211 212 213 214 |
# File 'lib/insika/refinement/proposer.rb', line 206 def panel(config, utility_model: nil, ask_factory: nil, max: nil, llm: nil) refs = refs_for(Coercion.deep_stringify(config || {}), utility_model) cap = max || Insika::SubagentGraph.fan_out_cap factory = ask_factory || ->(model, provider) { ruby_llm_ask(model, provider, llm: llm) } refs.first(cap).map do |ref| provider, model = split_ref(ref) Proposer.new(ask: factory.call(model, provider), model: ref) end end |
.refs_for(config, utility_model) ⇒ Object
proposers accepts either syntax — a bare ref ("deepseek/deepseek-v4-flash") or
the RFC's { "model" =>, "provider"? => } — because the two already coexist in
this config (proposer is a bare ref, judges are hashes) and refusing one of
them would only teach operators which page they were reading.
220 221 222 223 224 225 |
# File 'lib/insika/refinement/proposer.rb', line 220 def refs_for(config, utility_model) listed = Array(config["proposers"]).filter_map { |entry| normalize_ref(entry) } return listed.uniq unless listed.empty? [Coercion.presence(config["proposer"]) || Coercion.presence(utility_model)].compact end |
.ruby_llm_ask(model, provider, llm: nil) ⇒ Object
Temperature 0: a proposal an operator rejects should be re-proposable, and a
gate result should be attributable to the edit rather than to a sampling seed.
ruby_llm is required lazily so nothing loads a provider gem until a proposer
is actually configured.
Returns the MESSAGE, not .content: the token counts ride on it and the
budget is what spends them. Proposer reads either shape.
252 253 254 255 256 257 258 259 |
# File 'lib/insika/refinement/proposer.rb', line 252 def ruby_llm_ask(model, provider, llm: nil) require "ruby_llm" llm ||= RubyLLM lambda do |prompt| llm.chat(model: model, provider: provider, assume_model_exists: true) .with_temperature(0).ask(prompt) end end |
.split_ref(ref) ⇒ Object
"provider/model" -> [provider, model]; "model" -> [nil, model]. Same reading
Safety::Factory uses for the moderator — one syntax for "which model", not
one per feature.
240 241 242 243 |
# File 'lib/insika/refinement/proposer.rb', line 240 def split_ref(ref) prov, name = ref.to_s.split("/", 2) name ? [prov, name] : [nil, prov] end |