Class: Insika::DSL::SystemBuilder
- Inherits:
-
Object
- Object
- Insika::DSL::SystemBuilder
- Defined in:
- lib/insika/dsl.rb
Overview
Collects several agents into ONE runtime. A single agent is a Definition;
more than one needs a container, because delegation (subagents) and any
multi-agent pattern only mean something when the children live in the same
graph. It adds no new engine path: each agent is still its own Pack,
imported through the standard PackImporter.
Instance Method Summary collapse
-
#agent(id, &block) ⇒ Object
Declares one agent — the SAME block the standalone
Insika.agenttakes. - #api_base(value) ⇒ Object
- #api_key(value) ⇒ Object
- #build(backend: nil, &block) ⇒ Object
-
#initialize ⇒ SystemBuilder
constructor
A new instance of SystemBuilder.
-
#mcp(name, transport: nil, command: nil, args: nil, url: nil, headers: nil, env: nil, description: nil, enabled: true) ⇒ Object
Declares an MCP server instance: global to the graph, not any one agent — gated per agent through
tools_allow_groupson the groupmcp:<name>, same as any other tool group. -
#provider(name) ⇒ Object
System-wide runtime knobs (NOT part of any pack): they configure the LLM clients for every agent.
-
#workflow(name, description: nil, input: nil, output: nil, &block) ⇒ Object
Declares a WORKFLOW: deterministic Ruby orchestrating agent turns, for the shapes a single tool-loop should not decide on its own — chaining, routing, evaluate-and-retry.
Constructor Details
#initialize ⇒ SystemBuilder
Returns a new instance of SystemBuilder.
47 48 49 50 51 52 |
# File 'lib/insika/dsl.rb', line 47 def initialize @definitions = [] @workflows = [] @mcp_instances = [] @runtime = {} end |
Instance Method Details
#agent(id, &block) ⇒ Object
Declares one agent — the SAME block the standalone Insika.agent takes.
Returns its Definition, so a script can keep a handle if it wants one.
64 65 66 67 68 69 70 71 72 |
# File 'lib/insika/dsl.rb', line 64 def agent(id, &block) definition = Builder.new(id).build(&block) if @definitions.any? { |d| d.id == definition.id } raise ArgumentError, "duplicate agent id in system: #{definition.id}" end @definitions << definition definition end |
#api_base(value) ⇒ Object
105 |
# File 'lib/insika/dsl.rb', line 105 def api_base(value) = @runtime[:api_base] = value.to_s |
#api_key(value) ⇒ Object
104 |
# File 'lib/insika/dsl.rb', line 104 def api_key(value) = @runtime[:api_key] = value.to_s |
#build(backend: nil, &block) ⇒ Object
54 55 56 57 58 59 60 |
# File 'lib/insika/dsl.rb', line 54 def build(backend: nil, &block) instance_eval(&block) if block raise ArgumentError, "Insika.system needs at least one agent" if @definitions.empty? System.new(definitions: @definitions, workflows: @workflows, mcp_instances: @mcp_instances, runtime: @runtime, backend: backend) end |
#mcp(name, transport: nil, command: nil, args: nil, url: nil, headers: nil, env: nil, description: nil, enabled: true) ⇒ Object
Declares an MCP server instance: global to the graph, not
any one agent — gated per agent through tools_allow_groups on the
group mcp:<name>, same as any other tool group. Insika::DSL::Runtime
upserts it into the McpStore at boot; the MOTOR-VS-FORJA rule applies —
code is the TEMPLATE (transport/command/args/url/description always
follow the DSL), but an operator's own enabled/env/headers edit
via Studio/CLI/API, once the instance exists, is never clobbered back.
mcp "tavily", transport: :http, url: "https://mcp.tavily.com/mcp",
headers: { "Authorization" => "Bearer #ENV["TAVILY_KEY"]" }
mcp "filesystem", transport: :stdio, command: "npx",
args: ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"]
A SYSTEM-level declaration (as opposed to inside one member agent { }
block) has no single agent's config to auto-grant — it does NOT by
itself give any agent access. Declare the mcp inside the specific
agent { } block that needs it instead, where Builder#mcp auto-adds
"mcp:tools_allow_groups (below).
124 125 126 127 128 129 130 131 132 133 |
# File 'lib/insika/dsl.rb', line 124 def mcp(name, transport: nil, command: nil, args: nil, url: nil, headers: nil, env: nil, description: nil, enabled: true) n = name.to_s raise ArgumentError, "duplicate mcp instance in system: #{n}" if @mcp_instances.any? { |m| m[:name] == n } @mcp_instances << { name: n, transport: transport&.to_s, command: command, args: args, url: url, headers: headers, env: env, description: description, enabled: enabled } n end |
#provider(name) ⇒ Object
System-wide runtime knobs (NOT part of any pack): they configure the LLM
clients for every agent. A per-agent provider still wins for that agent;
this is the default and the place to put a shared key.
103 |
# File 'lib/insika/dsl.rb', line 103 def provider(name) = @runtime[:provider] = name.to_s |
#workflow(name, description: nil, input: nil, output: nil, &block) ⇒ Object
Declares a WORKFLOW: deterministic Ruby orchestrating agent turns, for the
shapes a single tool-loop should not decide on its own — chaining, routing,
evaluate-and-retry. It is registered in the same WorkflowRegistry a
deployment uses, so it gets a durable run (the run id IS a Task),
:workflow_started/:workflow_completed on the event stream, and — when
served — GET /v1/workflows + POST /v1/workflows/:name.
workflow "draft", input: { type: "object", required: ["topic"], … } do |input, ctx|
draft = ctx.ask("writer", "Write about #{input['topic']}")
ctx.ask("editor", "Tighten this:\n#{draft}")
end
input:/output: take a JSON Schema Hash (validated by the engine's
zero-dep validator) or any dry-schema-compatible #call-able. A bad input
is refused synchronously, with NO run created.
89 90 91 92 93 94 95 96 97 98 |
# File 'lib/insika/dsl.rb', line 89 def workflow(name, description: nil, input: nil, output: nil, &block) raise ArgumentError, "workflow '#{name}' needs a block" if block.nil? name = name.to_s raise ArgumentError, "duplicate workflow in system: #{name}" if @workflows.any? { |w| w[:name] == name } @workflows << { name: name, description: description, input_schema: input, output_schema: output, block: block } name end |