Class: Insika::DSL::Builder
- Inherits:
-
Object
- Object
- Insika::DSL::Builder
- Defined in:
- lib/insika/dsl.rb
Overview
Collects the declarations and emits a Insika::Pack. Declarations map 1:1 to the pack manifest (AgentProfile.build attrs) + the pack's files/skills/tools — so what you write is exactly the data the engine stores.
Instance Method Summary collapse
-
#alerts(hash) ⇒ Object
Operator alert delivery (WS6): POST this agent's budget_warning / breaker_open / delivery_failed events to the webhook, as JSON.
- #api_base(value) ⇒ Object
-
#api_key(value) ⇒ Object
--- runtime (LLM provider) config — NOT part of the pack ------------ Configures RubyLLM at chat/serve time.
-
#budget(hash) ⇒ Object
Spend caps per calendar window (WS2): daily/monthly token budgets for this agent, per (tenant, agent) when multi-tenant.
- #build(&block) ⇒ Object
-
#data_tool(defn) ⇒ Object
A DATA-DEFINED (declarative HTTP) tool — pure config-over-code.
-
#declares(*names) ⇒ Object
Facts about THIS deployment that are not tools, so an eval case can declare what it needs and be skipped where it is absent instead of failing for the wrong reason.
- #deny_tools(*names) ⇒ Object
-
#edge_stream(hash) ⇒ Object
Which internal channels may cross to the CUSTOMER.
-
#guardrails(hash) ⇒ Object
Content-safety guardrails — opt-in and configurable per agent.
-
#initialize(id) ⇒ Builder
constructor
A new instance of Builder.
- #instructions(text) ⇒ Object (also: #prompt)
-
#limit(key, value) ⇒ Object
Per-agent limits (timeouts/budgets).
- #limits(hash) ⇒ Object
- #max_tokens(value) ⇒ Object
-
#memory(on = true) ⇒ Object
--- knobs -----------------------------------------------------------.
- #metadata(hash) ⇒ Object
-
#model(name) ⇒ Object
--- identity & model ------------------------------------------------.
-
#param(key, value) ⇒ Object
LLM generation params.
- #params(hash) ⇒ Object
- #policies(*names) ⇒ Object
-
#prompt_file(name, content) ⇒ Object
An extra prompt FILE (identity fragment).
-
#provider(name) ⇒ Object
Provider for both the profile AND the RubyLLM configuration at run time.
-
#refine(hash) ⇒ Object
Refinement — how the agent's own instruction files may be improved from real traffic.
-
#reliability(hash) ⇒ Object
Provider-interaction reliability, as DATA (WS3): retries + exponential backoff on transient failures, a fallback model chain (mid-turn rotation), and a circuit breaker per (tenant, provider/model) that fail-fasts once the window trips.
-
#skill(name, content = nil, description: nil, instructions: nil) ⇒ Object
--- skills ---------------------------------------------------------- skill "escalate", "
" — or — skill "escalate", description: "…", instructions: "…" The name is auto-added to the agent's skill allowlist. -
#skills_eager(*names) ⇒ Object
skills_eager — turns progressive disclosure off for THIS agent, wholly or in part.
-
#stuck_signal(on = true) ⇒ Object
The agent may signal it cannot proceed (WS5): when on, the model can call
signal_stuck, which ends the turn withoutcome: :stuck+ a final message + a:turn_stuckevent. -
#subagents(*ids) ⇒ Object
--- delegation ------------------------------------------------------ subagents "security", "performance" → the child agents this one MAY spawn.
- #temperature(value) ⇒ Object
-
#to_pack ⇒ Object
The generated portable artifact — the heart of "generates the data".
-
#tool_output_compression(on = true) ⇒ Object
Mechanical tool-result dedupe in the replayed history (no-LLM compaction, apt for bloated transcripts).
-
#tools(*names) ⇒ Object
--- tools ----------------------------------------------------------- tools "a", "b" → allowlist [names].
Constructor Details
#initialize(id) ⇒ Builder
Returns a new instance of Builder.
110 111 112 113 114 115 116 117 118 119 120 |
# File 'lib/insika/dsl.rb', line 110 def initialize(id) @id = id.to_s @config = {} @files = {} @skills = {} @tools = [] # Auto-enable the allowlist policies: harmless when the allowlist is nil=all, # correct once you restrict tools/skills. Visible in #to_pack — no hidden magic. @config[:policies] = %i[tool_allowlist skill_allowlist] @runtime = {} # non-pack knobs (llm provider/key/base) consumed by the runtime end |
Instance Method Details
#alerts(hash) ⇒ Object
Operator alert delivery (WS6): POST this agent's budget_warning / breaker_open / delivery_failed events to the webhook, as JSON. alerts webhook: "https://ops.example.com/insika-alerts"
232 |
# File 'lib/insika/dsl.rb', line 232 def alerts(hash) = (@config[:alerts] ||= {}).merge!(hash.transform_keys(&:to_s)) |
#api_base(value) ⇒ Object
294 |
# File 'lib/insika/dsl.rb', line 294 def api_base(value) = @runtime[:api_base] = value.to_s |
#api_key(value) ⇒ Object
--- runtime (LLM provider) config — NOT part of the pack ------------ Configures RubyLLM at chat/serve time. Defaults: provider = the agent's provider; key = ENV.
293 |
# File 'lib/insika/dsl.rb', line 293 def api_key(value) = @runtime[:api_key] = value.to_s |
#budget(hash) ⇒ Object
Spend caps per calendar window (WS2): daily/monthly token budgets for
this agent, per (tenant, agent) when multi-tenant. HARD is the default:
absent soft: (or soft: false) turns the cap into a hard wall (the
turn fails with budget_exceeded + retry_after); soft: true warns once
per window and keeps running.
budget daily: 100_000, monthly: 2_000_000, soft: false
216 |
# File 'lib/insika/dsl.rb', line 216 def budget(hash) = (@config[:budget] ||= {}).merge!(hash.transform_keys(&:to_s)) |
#build(&block) ⇒ Object
122 123 124 125 |
# File 'lib/insika/dsl.rb', line 122 def build(&block) instance_eval(&block) if block Definition.new(pack: to_pack, runtime: @runtime) end |
#data_tool(defn) ⇒ Object
A DATA-DEFINED (declarative HTTP) tool — pure config-over-code. defn is a
ToolDefinition hash (name/description/parameters/binding…). Its name is
auto-added to the allowlist so the agent can call its own tool.
157 158 159 160 161 162 163 |
# File 'lib/insika/dsl.rb', line 157 def data_tool(defn) h = defn.transform_keys(&:to_s) @tools << h name = h["name"].to_s (@config[:tools_allow] ||= []) << name unless name.empty? || Array(@config[:tools_allow]).include?(name) h end |
#declares(*names) ⇒ Object
Facts about THIS deployment that are not tools, so an eval case can declare what it needs and be skipped where it is absent instead of failing for the wrong reason. declares "promotions", "human_handoff"
265 266 267 |
# File 'lib/insika/dsl.rb', line 265 def declares(*names) (@config[:capabilities_declared] ||= []).concat(names.flatten.map(&:to_s)) end |
#deny_tools(*names) ⇒ Object
150 151 152 |
# File 'lib/insika/dsl.rb', line 150 def deny_tools(*names) @config[:tools_deny] = names.flatten.map(&:to_s) end |
#edge_stream(hash) ⇒ Object
Which internal channels may cross to the CUSTOMER. Both off by default: the
answer is the answer, and the provider's reasoning (thinking) or the model
narrating its tool loop (intermediate) is for the Studio and the trace.
Each opted-in channel gets its OWN frame type at /v1/responses — never the
answer's — so a consumer that only reads the answer is unaffected either way.
edge_stream thinking: true, intermediate: false
275 |
# File 'lib/insika/dsl.rb', line 275 def edge_stream(hash) = (@config[:edge_stream] ||= {}).merge!(hash.transform_keys(&:to_s)) |
#guardrails(hash) ⇒ Object
Content-safety guardrails — opt-in and configurable per agent. Pure config-over-code: the hash is stored on the profile and consumed by Safety::Config.from_profile. Merges, so repeated calls accumulate. guardrails input: true, output: true, strictness: "medium", moderator: "deepseek/deepseek-v4-flash", responses: { "injection" => "I can't help with that." }
251 |
# File 'lib/insika/dsl.rb', line 251 def guardrails(hash) = (@config[:guardrails] ||= {}).merge!(hash.transform_keys(&:to_s)) |
#instructions(text) ⇒ Object Also known as: prompt
136 |
# File 'lib/insika/dsl.rb', line 136 def instructions(text) = @config[:base_prompt] = text.to_s |
#limit(key, value) ⇒ Object
Per-agent limits (timeouts/budgets). limit :turn_timeout, 120 or limits(...).
284 |
# File 'lib/insika/dsl.rb', line 284 def limit(key, value) = (@config[:limits] ||= {})[key.to_sym] = value |
#limits(hash) ⇒ Object
285 |
# File 'lib/insika/dsl.rb', line 285 def limits(hash) = (@config[:limits] ||= {}).merge!(hash.transform_keys(&:to_sym)) |
#max_tokens(value) ⇒ Object
281 |
# File 'lib/insika/dsl.rb', line 281 def max_tokens(value) = param(:max_tokens, value) |
#memory(on = true) ⇒ Object
--- knobs -----------------------------------------------------------
208 |
# File 'lib/insika/dsl.rb', line 208 def memory(on = true) = @config[:memory] = on |
#metadata(hash) ⇒ Object
288 |
# File 'lib/insika/dsl.rb', line 288 def (hash) = (@config[:metadata] ||= {}).merge!(hash.transform_keys(&:to_s)) |
#model(name) ⇒ Object
--- identity & model ------------------------------------------------
128 |
# File 'lib/insika/dsl.rb', line 128 def model(name) = @config[:model] = name.to_s |
#param(key, value) ⇒ Object
LLM generation params. param:temperature, 0.2 or params(...).
278 |
# File 'lib/insika/dsl.rb', line 278 def param(key, value) = (@config[:params] ||= {})[key.to_sym] = value |
#params(hash) ⇒ Object
279 |
# File 'lib/insika/dsl.rb', line 279 def params(hash) = (@config[:params] ||= {}).merge!(hash.transform_keys(&:to_sym)) |
#policies(*names) ⇒ Object
287 |
# File 'lib/insika/dsl.rb', line 287 def policies(*names) = @config[:policies] = names.flatten.map(&:to_sym) |
#prompt_file(name, content) ⇒ Object
An extra prompt FILE (identity fragment). Name = the file name (e.g. "SOUL.md").
140 141 142 |
# File 'lib/insika/dsl.rb', line 140 def prompt_file(name, content) @files[name.to_s] = content.to_s end |
#provider(name) ⇒ Object
Provider for both the profile AND the RubyLLM configuration at run time.
131 132 133 134 |
# File 'lib/insika/dsl.rb', line 131 def provider(name) @config[:provider] = name.to_s @runtime[:provider] ||= name.to_s end |
#refine(hash) ⇒ Object
Refinement — how the agent's own instruction files may be
improved from real traffic. Same config-over-code shape as guardrails;
omitting it entirely leaves the agent report-only (writes nothing).
refine mode: "propose", window: { last_sessions: 200 }, files: %w,
proposers: ["deepseek/deepseek-v4-flash", "gpt-5-mini"],
budget: { tokens: 200_000 }
259 |
# File 'lib/insika/dsl.rb', line 259 def refine(hash) = (@config[:refinement] ||= {}).merge!(hash.transform_keys(&:to_s)) |
#reliability(hash) ⇒ Object
Provider-interaction reliability, as DATA (WS3): retries + exponential
backoff on transient failures, a fallback model chain (mid-turn
rotation), and a circuit breaker per (tenant, provider/model) that
fail-fasts once the window trips. fallback/circuit_breaker entries
are "provider/model" refs or plain model ids.
reliability retries: 3, backoff: "exponential",
fallback: ["gpt-4o-mini"], circuit_breaker: { after: 10, within: 60, cooldown: 300 }
225 226 227 |
# File 'lib/insika/dsl.rb', line 225 def reliability(hash) (@config[:reliability] ||= {}).merge!(hash.transform_keys(&:to_s)) end |
#skill(name, content = nil, description: nil, instructions: nil) ⇒ Object
--- skills ----------------------------------------------------------
skill "escalate", "
169 170 171 172 173 174 |
# File 'lib/insika/dsl.rb', line 169 def skill(name, content = nil, description: nil, instructions: nil) n = name.to_s @skills[n] = normalize_skill(n, content, description, instructions) (@config[:skills] ||= []) << n unless @config.fetch(:skills, []).include?(n) n end |
#skills_eager(*names) ⇒ Object
skills_eager — turns progressive disclosure off for THIS agent, wholly or in
part. The body of an eager skill is in the prompt on every turn, so its
activation is not a decision and cannot be missed; it is paid for on every
turn, so measure the bodies against context_budget first (a stable position
makes them a cacheable prefix).
skills_eager # every allowed skill
skills_eager "formato", "markers" # exactly these
skills_eager false # none (the default)
A LIST and not a per-skill flag because skills are shared: escalation-to-human
sits in several allowlists, and one flag on the skill would force one decision
onto every agent holding it.
189 190 191 192 193 194 195 196 |
# File 'lib/insika/dsl.rb', line 189 def skills_eager(*names) flat = names.flatten @config[:skills_eager] = if flat.empty? then true elsif flat == [true] || flat == [false] then flat.first else flat.map(&:to_s) end end |
#stuck_signal(on = true) ⇒ Object
The agent may signal it cannot proceed (WS5): when on, the model
can call signal_stuck, which ends the turn with outcome: :stuck + a final
message + a :turn_stuck event. What "stuck" means is the consumer's call.
stuck_signal true
238 |
# File 'lib/insika/dsl.rb', line 238 def stuck_signal(on = true) = @config[:stuck_signal] = on |
#subagents(*ids) ⇒ Object
--- delegation ------------------------------------------------------
subagents "security", "performance" → the child agents this one MAY
spawn. CAPACITY field: opt-in, never inherited, and the ids
must be agents of the same system (Insika.system { … }) or already in
the store. Present ⇒ the engine wires spawn_subagent/spawn_subagents.
203 204 205 |
# File 'lib/insika/dsl.rb', line 203 def subagents(*ids) @config[:subagents] = ids.flatten.map(&:to_s) end |
#temperature(value) ⇒ Object
280 |
# File 'lib/insika/dsl.rb', line 280 def temperature(value) = param(:temperature, value) |
#to_pack ⇒ Object
The generated portable artifact — the heart of "generates the data".
297 298 299 300 301 302 |
# File 'lib/insika/dsl.rb', line 297 def to_pack Insika::Pack.from_h( config: @config.merge(id: @id), files: @files, skills: @skills, tools: @tools ) end |
#tool_output_compression(on = true) ⇒ Object
Mechanical tool-result dedupe in the replayed history (no-LLM compaction, apt for bloated transcripts). CHANGES WHAT THE MODEL SEES: repeated identical tool results collapse to a back-reference.
243 |
# File 'lib/insika/dsl.rb', line 243 def tool_output_compression(on = true) = @config[:tool_output_compression] = on |
#tools(*names) ⇒ Object
--- tools ----------------------------------------------------------- tools "a", "b" → allowlist [names]. Not called → nil = all (parity).
146 147 148 |
# File 'lib/insika/dsl.rb', line 146 def tools(*names) @config[:tools_allow] = names.flatten.map(&:to_s) end |