insika 0.8.0 → 1.0.1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +312 -2
- data/README.md +10 -6
- data/bin/insika +4 -1
- data/bin/insika-router +0 -1
- data/docs/AGENTS.md +142 -18
- data/docs/API.md +73 -0
- data/docs/ARCHITECTURE.md +56 -50
- data/docs/ARTIFACTS.md +33 -3
- data/docs/BENCHMARK.md +50 -7
- data/docs/CHANNELS.md +19 -2
- data/docs/CONTEXT.md +96 -27
- data/docs/CROSS-HARNESS-BENCH.md +157 -0
- data/docs/DEPLOY.md +70 -13
- data/docs/EMBEDDING.md +10 -0
- data/docs/EVALS.md +129 -10
- data/docs/FACTS.md +12 -6
- data/docs/HARVEST.md +8 -3
- data/docs/KNOWLEDGE.md +14 -0
- data/docs/MEDIA.md +6 -2
- data/docs/OBSERVABILITY.md +164 -28
- data/docs/PERFORMANCE.md +110 -0
- data/docs/PLUGINS.md +4 -4
- data/docs/POLICY.md +26 -14
- data/docs/RELEASING.md +9 -0
- data/docs/ROUTER.md +7 -0
- data/docs/RUBYLLM_2_MIGRATION.md +307 -0
- data/docs/SECURITY.md +34 -1
- data/docs/SHARED-CONVERSATIONS.md +40 -0
- data/docs/SKILLS.md +5 -3
- data/docs/STUDIO.md +122 -0
- data/docs/TOOLS.md +238 -42
- data/docs/operate.md +3 -0
- data/docs/prompts/ADD-TOOL.md +12 -2
- data/docs/prompts/DIAGNOSE-TURN.md +3 -0
- data/docs/prompts/GO-LIVE.md +3 -1
- data/lib/insika/agent_defaults.rb +140 -0
- data/lib/insika/agent_file_store.rb +12 -0
- data/lib/insika/agent_profile.rb +99 -17
- data/lib/insika/artifact_signing.rb +11 -0
- data/lib/insika/channels/web/widget.js +33 -0
- data/lib/insika/channels/web.rb +5 -2
- data/lib/insika/chat_builder.rb +124 -28
- data/lib/insika/checkpoint.rb +6 -1
- data/lib/insika/checkpoint_store.rb +59 -7
- data/lib/insika/commands/agent_payload.rb +3 -3
- data/lib/insika/commands/delete_kit.rb +26 -0
- data/lib/insika/commands/gate_harvest.rb +3 -0
- data/lib/insika/commands/review_guardrail_flag.rb +38 -0
- data/lib/insika/commands/run_distillation.rb +5 -8
- data/lib/insika/commands/run_harvest.rb +17 -1
- data/lib/insika/commands/seed_session.rb +128 -0
- data/lib/insika/commands/send_message.rb +9 -3
- data/lib/insika/commands/update_settings.rb +23 -1
- data/lib/insika/commands/write_kit.rb +27 -0
- data/lib/insika/compaction.rb +33 -7
- data/lib/insika/config_store.rb +87 -7
- data/lib/insika/context/builder.rb +29 -9
- data/lib/insika/context/priority.rb +5 -0
- data/lib/insika/context/provider.rb +7 -2
- data/lib/insika/context/providers/briefing.rb +11 -8
- data/lib/insika/context/providers/fence_notice.rb +27 -0
- data/lib/insika/context/providers/knowledge.rb +32 -6
- data/lib/insika/context/providers/memory.rb +61 -8
- data/lib/insika/context/providers/pending_confirmation.rb +52 -0
- data/lib/insika/context/providers/prompt.rb +17 -4
- data/lib/insika/context/providers/session.rb +46 -6
- data/lib/insika/context/reranker.rb +75 -0
- data/lib/insika/distill.rb +22 -13
- data/lib/insika/doctor.rb +142 -4
- data/lib/insika/dsl/runtime.rb +37 -4
- data/lib/insika/dsl/server_boot.rb +4 -12
- data/lib/insika/dsl.rb +19 -5
- data/lib/insika/edge_limiter.rb +27 -16
- data/lib/insika/egress_guard.rb +11 -3
- data/lib/insika/env_schema.rb +33 -73
- data/lib/insika/errors.rb +1 -0
- data/lib/insika/evals/assertions.rb +259 -18
- data/lib/insika/evals/golden.rb +230 -4
- data/lib/insika/evals/judge.rb +19 -10
- data/lib/insika/evals/moderator_compare.rb +47 -0
- data/lib/insika/evals/report.rb +61 -9
- data/lib/insika/evals/runner.rb +64 -4
- data/lib/insika/evals/simulator.rb +21 -3
- data/lib/insika/evals/transport.rb +317 -15
- data/lib/insika/event_stream.rb +63 -7
- data/lib/insika/evidence.rb +105 -17
- data/lib/insika/executor.rb +698 -164
- data/lib/insika/fence.rb +96 -0
- data/lib/insika/golden_store.rb +3 -0
- data/lib/insika/harvest.rb +26 -14
- data/lib/insika/harvest_engine.rb +1 -0
- data/lib/insika/harvest_store.rb +12 -0
- data/lib/insika/http_client.rb +87 -4
- data/lib/insika/kits.rb +54 -0
- data/lib/insika/knowledge.rb +34 -20
- data/lib/insika/llm_configurator.rb +46 -4
- data/lib/insika/llm_provider_store.rb +1 -3
- data/lib/insika/llm_trace_store.rb +54 -0
- data/lib/insika/mcp_client.rb +27 -63
- data/lib/insika/mcp_live_tool.rb +55 -9
- data/lib/insika/mcp_store.rb +37 -3
- data/lib/insika/mcp_tool_registry.rb +76 -26
- data/lib/insika/media.rb +38 -25
- data/lib/insika/memory_store.rb +16 -3
- data/lib/insika/memory_trim.rb +60 -0
- data/lib/insika/middleware.rb +12 -3
- data/lib/insika/model_metrics_store.rb +285 -0
- data/lib/insika/model_registry.rb +110 -0
- data/lib/insika/model_resolver.rb +1 -1
- data/lib/insika/model_selection.rb +14 -21
- data/lib/insika/model_visible.rb +17 -6
- data/lib/insika/onboarding.rb +1 -0
- data/lib/insika/outcome_store.rb +1 -1
- data/lib/insika/overlay_tool_registry.rb +30 -2
- data/lib/insika/pack_importer.rb +54 -9
- data/lib/insika/packaging.rb +14 -5
- data/lib/insika/pending_action_store.rb +45 -18
- data/lib/insika/plugin/loader.rb +4 -2
- data/lib/insika/prefix_fingerprint.rb +32 -27
- data/lib/insika/profile_source.rb +5 -0
- data/lib/insika/provider_error_classifier.rb +18 -3
- data/lib/insika/queue_policy.rb +7 -2
- data/lib/insika/refinement/proposer.rb +42 -17
- data/lib/insika/reliability.rb +12 -3
- data/lib/insika/router/app.rb +14 -5
- data/lib/insika/router/session_key.rb +35 -5
- data/lib/insika/safety/config.rb +20 -3
- data/lib/insika/safety/corpus.rb +10 -3
- data/lib/insika/safety/factory.rb +39 -1
- data/lib/insika/safety/input_guardrail.rb +21 -1
- data/lib/insika/safety/judge_moderator.rb +70 -0
- data/lib/insika/safety/output_validator.rb +29 -5
- data/lib/insika/safety/prompt_echo.rb +89 -0
- data/lib/insika/schema_guard.rb +53 -76
- data/lib/insika/server/app.rb +58 -7
- data/lib/insika/server/rack_app.rb +2 -0
- data/lib/insika/server/responses.rb +50 -5
- data/lib/insika/session_actor.rb +58 -28
- data/lib/insika/session_store.rb +163 -11
- data/lib/insika/settings_store.rb +57 -2
- data/lib/insika/shared_conversations.rb +305 -0
- data/lib/insika/skill_catalog.rb +28 -2
- data/lib/insika/skill_store.rb +6 -0
- data/lib/insika/soak/runner.rb +1 -1
- data/lib/insika/spoken_transcript.rb +31 -0
- data/lib/insika/steer_injector.rb +7 -48
- data/lib/insika/store.rb +29 -1
- data/lib/insika/stores/json_value.rb +37 -0
- data/lib/insika/stores/memory.rb +22 -35
- data/lib/insika/stores/postgres.rb +295 -0
- data/lib/insika/stores/sqlite.rb +257 -43
- data/lib/insika/stores/turn_counter.rb +25 -0
- data/lib/insika/studio/app.rb +1459 -163
- data/lib/insika/studio/assets/dist/application.css +1 -1
- data/lib/insika/studio/assets/dist/application.js +25 -25
- data/lib/insika/studio/assets/dist/fonts/ibm-plex-mono-latin-400-normal.woff2 +0 -0
- data/lib/insika/studio/assets/dist/fonts/ibm-plex-mono-latin-500-normal.woff2 +0 -0
- data/lib/insika/studio/assets/dist/fonts/ibm-plex-mono-latin-600-normal.woff2 +0 -0
- data/lib/insika/studio/assets/dist/fonts/ibm-plex-sans-latin-wght-normal.woff2 +0 -0
- data/lib/insika/studio/forms.rb +189 -12
- data/lib/insika/studio/nav_icons.rb +3 -0
- data/lib/insika/studio/views/_agent_config_retrieval.erb +73 -0
- data/lib/insika/studio/views/_agent_picker.erb +15 -0
- data/lib/insika/studio/views/_agent_tab_config.erb +83 -243
- data/lib/insika/studio/views/_agent_tab_history.erb +56 -1
- data/lib/insika/studio/views/_agent_tab_performance.erb +34 -0
- data/lib/insika/studio/views/_agent_tab_prompts.erb +7 -9
- data/lib/insika/studio/views/_agent_tab_skills.erb +16 -7
- data/lib/insika/studio/views/_agent_tab_tools.erb +90 -0
- data/lib/insika/studio/views/_agents_master.erb +8 -12
- data/lib/insika/studio/views/_cfg_alerts.erb +4 -0
- data/lib/insika/studio/views/_cfg_budget.erb +20 -0
- data/lib/insika/studio/views/_cfg_distill.erb +26 -0
- data/lib/insika/studio/views/_cfg_followup.erb +30 -0
- data/lib/insika/studio/views/_cfg_grounding.erb +21 -0
- data/lib/insika/studio/views/_cfg_guardrails.erb +47 -0
- data/lib/insika/studio/views/_cfg_harvest.erb +35 -0
- data/lib/insika/studio/views/_cfg_reliability.erb +32 -0
- data/lib/insika/studio/views/_chats_master.erb +66 -0
- data/lib/insika/studio/views/_customers_master.erb +37 -0
- data/lib/insika/studio/views/_empty.erb +15 -0
- data/lib/insika/studio/views/_facts_master.erb +42 -0
- data/lib/insika/studio/views/_harvest_candidate_detail.erb +14 -0
- data/lib/insika/studio/views/_identity.erb +16 -0
- data/lib/insika/studio/views/_kpi.erb +17 -0
- data/lib/insika/studio/views/_message.erb +40 -2
- data/lib/insika/studio/views/_model_chart.erb +82 -0
- data/lib/insika/studio/views/_refinement_master.erb +38 -0
- data/lib/insika/studio/views/{tool_edit.erb → _tool_edit.erb} +38 -11
- data/lib/insika/studio/views/_tool_users.erb +17 -0
- data/lib/insika/studio/views/_why.erb +53 -0
- data/lib/insika/studio/views/agent_detail.erb +17 -10
- data/lib/insika/studio/views/agents.erb +28 -16
- data/lib/insika/studio/views/approvals.erb +16 -11
- data/lib/insika/studio/views/artifact.erb +20 -7
- data/lib/insika/studio/views/artifacts.erb +19 -14
- data/lib/insika/studio/views/chats.erb +22 -29
- data/lib/insika/studio/views/customer.erb +88 -43
- data/lib/insika/studio/views/customers.erb +16 -26
- data/lib/insika/studio/views/evals.erb +49 -31
- data/lib/insika/studio/views/fact_detail.erb +114 -0
- data/lib/insika/studio/views/facts.erb +15 -116
- data/lib/insika/studio/views/followups.erb +63 -52
- data/lib/insika/studio/views/funnel.erb +104 -43
- data/lib/insika/studio/views/harvest.erb +85 -59
- data/lib/insika/studio/views/home.erb +138 -23
- data/lib/insika/studio/views/inbox.erb +36 -0
- data/lib/insika/studio/views/kits.erb +176 -0
- data/lib/insika/studio/views/knowledge.erb +92 -80
- data/lib/insika/studio/views/layout.erb +42 -11
- data/lib/insika/studio/views/live.erb +45 -0
- data/lib/insika/studio/views/mcp.erb +43 -13
- data/lib/insika/studio/views/models.erb +187 -0
- data/lib/insika/studio/views/parity.erb +38 -32
- data/lib/insika/studio/views/playground.erb +13 -9
- data/lib/insika/studio/views/refinement.erb +15 -224
- data/lib/insika/studio/views/refinement_detail.erb +243 -0
- data/lib/insika/studio/views/session.erb +153 -118
- data/lib/insika/studio/views/settings.erb +86 -22
- data/lib/insika/studio/views/skills.erb +53 -19
- data/lib/insika/studio/views/system_files.erb +7 -7
- data/lib/insika/studio/views/task.erb +45 -15
- data/lib/insika/studio/views/tasks.erb +35 -18
- data/lib/insika/studio/views/tools.erb +62 -73
- data/lib/insika/task_actor.rb +4 -1
- data/lib/insika/task_store.rb +67 -1
- data/lib/insika/telemetry/pricing.rb +16 -14
- data/lib/insika/telemetry/recorder.rb +115 -9
- data/lib/insika/telemetry/ruby_llm_instrumenter.rb +110 -0
- data/lib/insika/telemetry.rb +12 -2
- data/lib/insika/templates/daily-digest/README.md +13 -0
- data/lib/insika/templates/daily-digest/agent.rb +22 -11
- data/lib/insika/testing/store_contract.rb +65 -0
- data/lib/insika/text_tool_call.rb +66 -0
- data/lib/insika/tick.rb +7 -1
- data/lib/insika/token_store.rb +5 -0
- data/lib/insika/tool_assembly.rb +21 -13
- data/lib/insika/tool_batch.rb +1 -1
- data/lib/insika/tool_catalog.rb +12 -2
- data/lib/insika/tool_definition.rb +82 -13
- data/lib/insika/tool_envelope.rb +266 -29
- data/lib/insika/tool_manifest.rb +1 -0
- data/lib/insika/tool_store.rb +12 -4
- data/lib/insika/tool_trace_store.rb +15 -8
- data/lib/insika/tool_usage_report.rb +14 -4
- data/lib/insika/tools/a2a_remote.rb +1 -1
- data/lib/insika/tools/agent_enum.rb +1 -1
- data/lib/insika/tools/confirm_pending.rb +112 -0
- data/lib/insika/tools/data_defined_tool.rb +24 -15
- data/lib/insika/tools/generate_image.rb +2 -2
- data/lib/insika/tools/load_knowledge.rb +7 -3
- data/lib/insika/tools/load_skill.rb +3 -3
- data/lib/insika/tools/present.rb +115 -0
- data/lib/insika/tools/remember.rb +7 -2
- data/lib/insika/tools/run_persona_eval.rb +9 -6
- data/lib/insika/tools/save_artifact.rb +16 -6
- data/lib/insika/tools/schedule_followup.rb +3 -3
- data/lib/insika/tools/stuck_signal.rb +3 -3
- data/lib/insika/tools/subagent.rb +5 -5
- data/lib/insika/tools/subagents.rb +3 -3
- data/lib/insika/tools/tool_search.rb +11 -7
- data/lib/insika/tools/tts.rb +2 -2
- data/lib/insika/tools/update_briefing.rb +4 -4
- data/lib/insika/turn_output.rb +2 -2
- data/lib/insika/turn_state.rb +31 -1
- data/lib/insika/turn_timing.rb +51 -14
- data/lib/insika/version.rb +1 -1
- data/lib/insika/wiring/graph.rb +88 -16
- data/lib/insika/wiring/graph_chat.rb +4 -0
- data/lib/insika/workflow.rb +25 -64
- data/lib/insika.rb +23 -3
- metadata +74 -13
- data/lib/insika/commands/import_mcp_tools.rb +0 -48
- data/lib/insika/mcp_http_client.rb +0 -67
- data/lib/insika/mcp_tool_ingestor.rb +0 -165
|
@@ -6,13 +6,13 @@ module Insika
|
|
|
6
6
|
module Tools
|
|
7
7
|
# Level 2 of TOOLS progressive disclosure (analog of LoadSkill):
|
|
8
8
|
# searches the deferred catalog and PROMOTES the relevant ones into the live chat via
|
|
9
|
-
# chat.with_tools (
|
|
10
|
-
#
|
|
9
|
+
# chat.with_tools (propagates on the next round of the same `ask`).
|
|
10
|
+
# `require "ruby_llm"` stays in THIS file (it inherits from
|
|
11
11
|
# RubyLLM::Tool) — it does not enter lib/insika.rb; the Executor loads it lazily in
|
|
12
12
|
# configure_chat (like LoadSkill).
|
|
13
13
|
class ToolSearch < RubyLLM::Tool
|
|
14
14
|
description "Searches and enables additional tools by describing the need"
|
|
15
|
-
|
|
15
|
+
parameter :query, description: "What you need to do (e.g.: 'send email', 'generate invoice')"
|
|
16
16
|
|
|
17
17
|
# RubyLLM::Tool#name derives from self.class.name — for a nested class
|
|
18
18
|
# (Insika::Tools::ToolSearch) it produces "insika--tools--tool_search", not
|
|
@@ -21,7 +21,7 @@ module Insika
|
|
|
21
21
|
def name = "tool_search"
|
|
22
22
|
|
|
23
23
|
def initialize(catalog, deferred_allowed, chat, tool_registry:, event_stream:,
|
|
24
|
-
checkpoint_store:, state:)
|
|
24
|
+
checkpoint_store:, state:, trace_recorder: nil)
|
|
25
25
|
@catalog = catalog
|
|
26
26
|
@deferred_allowed = Array(deferred_allowed).map(&:to_s)
|
|
27
27
|
@chat = chat
|
|
@@ -29,6 +29,7 @@ module Insika
|
|
|
29
29
|
@event_stream = event_stream
|
|
30
30
|
@checkpoint_store = checkpoint_store
|
|
31
31
|
@state = state
|
|
32
|
+
@trace_recorder = trace_recorder
|
|
32
33
|
@promoted = [] # names already promoted IN THIS chat — idempotency
|
|
33
34
|
super()
|
|
34
35
|
end
|
|
@@ -59,7 +60,8 @@ module Insika
|
|
|
59
60
|
@promoted << entry.name
|
|
60
61
|
ToolEnvelope.new(tool, state: @state, checkpoint_store: @checkpoint_store,
|
|
61
62
|
tool_registry: @tool_registry, timeout: timeout,
|
|
62
|
-
skip_side_effects: Array(@state.skip_side_effects)
|
|
63
|
+
skip_side_effects: Array(@state.skip_side_effects),
|
|
64
|
+
event_stream: @event_stream, trace_recorder: @trace_recorder)
|
|
63
65
|
rescue Insika::NotFoundError
|
|
64
66
|
nil
|
|
65
67
|
end
|
|
@@ -79,11 +81,13 @@ module Insika
|
|
|
79
81
|
|
|
80
82
|
def describe(entry)
|
|
81
83
|
tool = @tool_registry.resolve(entry.name)
|
|
84
|
+
schema = Coercion.deep_stringify(tool.parameters_schema || {})
|
|
82
85
|
{
|
|
83
86
|
name: entry.name,
|
|
84
87
|
description: entry.description,
|
|
85
|
-
parameters:
|
|
86
|
-
{ type:
|
|
88
|
+
parameters: schema.fetch("properties", {}).to_h do |name, property|
|
|
89
|
+
[name.to_sym, { type: property["type"], description: property["description"],
|
|
90
|
+
required: Array(schema["required"]).include?(name) }]
|
|
87
91
|
end
|
|
88
92
|
}
|
|
89
93
|
rescue Insika::NotFoundError
|
data/lib/insika/tools/tts.rb
CHANGED
|
@@ -20,8 +20,8 @@ module Insika
|
|
|
20
20
|
description "Synthesize speech for the reply — a voice clip travels as an " \
|
|
21
21
|
"output part in the envelope, the channel supports it. Use when the " \
|
|
22
22
|
"customer should HEAR the answer rather than read it."
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
parameter :text, description: "The words to speak"
|
|
24
|
+
parameter :voice, description: "Optional voice override (default from the agent config)",
|
|
25
25
|
required: false
|
|
26
26
|
|
|
27
27
|
def name = "tts"
|
|
@@ -53,8 +53,8 @@ module Insika
|
|
|
53
53
|
names.empty? ? BASE_DESCRIPTION : "#{BASE_DESCRIPTION} field must be one of: #{names.join(', ')}."
|
|
54
54
|
end
|
|
55
55
|
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
parameter :field, description: "The briefing field name (one of the declared list)"
|
|
57
|
+
parameter :value, description: "The value learned. Blank clears the field."
|
|
58
58
|
|
|
59
59
|
def name = "update_briefing"
|
|
60
60
|
|
|
@@ -72,7 +72,7 @@ module Insika
|
|
|
72
72
|
|
|
73
73
|
# The declared names become the `field` enum in the schema the provider
|
|
74
74
|
# sees — per-turn data, like the subagent allowlist (Tools::AgentEnum).
|
|
75
|
-
def
|
|
75
|
+
def parameters_schema
|
|
76
76
|
@field_enum_schema ||= Insika::Tools::AgentEnum.inject(super, @fields, path: %i[field])
|
|
77
77
|
end
|
|
78
78
|
|
|
@@ -99,7 +99,7 @@ module Insika
|
|
|
99
99
|
|
|
100
100
|
description "Records the next step agreed with the customer " \
|
|
101
101
|
"(e.g. 'send the payment link tomorrow at 10'). Blank clears it."
|
|
102
|
-
|
|
102
|
+
parameter :text, description: "The agreed next step, in one sentence"
|
|
103
103
|
|
|
104
104
|
def name = "set_next_step"
|
|
105
105
|
|
data/lib/insika/turn_output.rb
CHANGED
|
@@ -49,12 +49,12 @@ module Insika
|
|
|
49
49
|
# public_intermediate: the agent opted this channel in (`edge_stream`), so the
|
|
50
50
|
# narration is TAGGED and `/v1/responses` gives it its own frame. Default false:
|
|
51
51
|
# an internal event stays internal unless someone said otherwise.
|
|
52
|
-
def initialize(filter:, emit:, public_intermediate: false)
|
|
52
|
+
def initialize(filter:, emit:, public_intermediate: false, halt_text: "")
|
|
53
53
|
@filter = filter
|
|
54
54
|
@emit = emit
|
|
55
55
|
@public_intermediate = public_intermediate
|
|
56
56
|
@pending = +"" # text of the message currently streaming
|
|
57
|
-
@last_intermediate =
|
|
57
|
+
@last_intermediate = halt_text.to_s # already filtered when checkpointed
|
|
58
58
|
@candidate = nil
|
|
59
59
|
end
|
|
60
60
|
|
data/lib/insika/turn_state.rb
CHANGED
|
@@ -6,6 +6,10 @@ module Insika
|
|
|
6
6
|
class TurnState
|
|
7
7
|
attr_reader :task, :profile, :turn # turn identity (1-based)
|
|
8
8
|
attr_accessor :message, # input (Middleware may rewrite)
|
|
9
|
+
:caller_context, # payload `context`: text the caller sends WITH the message
|
|
10
|
+
# (channel rules, what the customer already saw). The model
|
|
11
|
+
# and the transcript get it ahead of the message; guardrails
|
|
12
|
+
# and retrieval read `message` alone. nil = none.
|
|
9
13
|
:context, # ContextPackage from the Builder
|
|
10
14
|
:allowed_tools, # Resolution from the Policy Engine
|
|
11
15
|
:allowed_skills,
|
|
@@ -61,9 +65,16 @@ module Insika
|
|
|
61
65
|
# context via an evidence-declared tool (the envelope
|
|
62
66
|
# appends; the validator/enforcer read it). Built per
|
|
63
67
|
# turn by the Executor; nil = no session/no evidence.
|
|
64
|
-
:evidence_attachments, # [ {type, url, caption} ] hoarded by the
|
|
68
|
+
:evidence_attachments, # [ {type, url, caption, id} ] hoarded by the
|
|
65
69
|
# envelope this turn; read by the Executor at stage 8
|
|
66
70
|
# for the channel delivery. Reset per turn.
|
|
71
|
+
:presentations, # [ {component, title, items: [{id,url,caption}]} ] —
|
|
72
|
+
# what a presentation tool SELECTED this turn, in call
|
|
73
|
+
# order. Non-empty = the channel delivery sends these
|
|
74
|
+
# instead of every hoarded card. Reset per turn.
|
|
75
|
+
:fence_max_chars, # per-leaf cap the ToolEnvelope applies when the
|
|
76
|
+
# agent has `fencing` on — the platform's
|
|
77
|
+
# `fencing.max_chars`, set per turn by the Executor.
|
|
67
78
|
:context_trace_entry # the sanitized trace entry parked at
|
|
68
79
|
# prepare_turn (fingerprints + invalidation_reason);
|
|
69
80
|
# the stage-8 stamp merges the cache-hit fields into
|
|
@@ -104,7 +115,9 @@ module Insika
|
|
|
104
115
|
# to serialize the turn's real exchange — user + assistant(tool_calls) + tool
|
|
105
116
|
# results + final assistant — into the transcript. nil = no chat recorded
|
|
106
117
|
# (workflow/halt) → persist_turn falls back to the {user, assistant} pair.
|
|
118
|
+
attr_accessor :shared_conversations
|
|
107
119
|
attr_accessor :chat_baseline
|
|
120
|
+
attr_accessor :tool_halt
|
|
108
121
|
|
|
109
122
|
# Internal: impl_name(String) -> STABLE name of the capability that
|
|
110
123
|
# resolved it, computed by resolve_capabilities BEFORE the policy_request and
|
|
@@ -134,6 +147,9 @@ module Insika
|
|
|
134
147
|
# Internal (memory): the turn's tenant (from the Command), scope of the write path
|
|
135
148
|
# (`remember` tool). Set in run_pipeline; nil = DEFAULT_TENANT in the MemoryStore.
|
|
136
149
|
attr_accessor :tenant
|
|
150
|
+
# The phrase an injection-flagging InputGuardrail matched (nil = none). The
|
|
151
|
+
# ChatBuilder hands the model a per-turn notice built from it.
|
|
152
|
+
attr_accessor :security_notice
|
|
137
153
|
|
|
138
154
|
# Internal: turn context deposited into the data-tools to
|
|
139
155
|
# resolve {{ctx.*}} (chat_id/agent_id/tenant/store_id) and emit
|
|
@@ -148,6 +164,9 @@ module Insika
|
|
|
148
164
|
# (workflow) or provider without counts.
|
|
149
165
|
attr_accessor :usage
|
|
150
166
|
|
|
167
|
+
# Native rerank usage events, separate from the chat/Responses token totals.
|
|
168
|
+
attr_accessor :rerank_usage
|
|
169
|
+
|
|
151
170
|
# Internal (Tool Search): ids of side-effects already completed in the
|
|
152
171
|
# interrupted turn, propagated to the tools PROMOTED by tool_search (the same `skip`
|
|
153
172
|
# that the eager tools' wrap_tools receives). Set in run_pipeline;
|
|
@@ -159,6 +178,12 @@ module Insika
|
|
|
159
178
|
# creates the PendingAction/suspends/waits; `actor` = the turn's mailbox (used
|
|
160
179
|
# by the coordinator for await(:approval)).
|
|
161
180
|
attr_accessor :requires_approval, :approval_coordinator, :actor
|
|
181
|
+
attr_accessor :native_approvals, :approval_continuation, :turn_output
|
|
182
|
+
|
|
183
|
+
# Customer confirmation: the store a `customer_confirm` call is held in by the
|
|
184
|
+
# ToolEnvelope and resolved from by confirm_pending/cancel_pending. nil = the
|
|
185
|
+
# deployment has no PendingActionStore (a hold then fails loud, like approval).
|
|
186
|
+
attr_accessor :pending_action_store
|
|
162
187
|
|
|
163
188
|
# Internal: the turn's shared in-flight cap for tool calls —
|
|
164
189
|
# ONE Async::Semaphore(tool_concurrency), installed by ToolAssembly#wrap_tools
|
|
@@ -167,6 +192,10 @@ module Insika
|
|
|
167
192
|
# nil = concurrency off: no gate, no overhead, serial execution unchanged.
|
|
168
193
|
attr_accessor :tool_gate
|
|
169
194
|
|
|
195
|
+
# Turns of one session already run serially through SessionActor. This gate
|
|
196
|
+
# prevents overlapping writes within a parallel batch of the current turn.
|
|
197
|
+
attr_accessor :side_effect_gate
|
|
198
|
+
|
|
170
199
|
# parallel tool calls, resolved PER TURN and read by ChatBuilder
|
|
171
200
|
# (whether to hand the gem `concurrency:`) and ToolAssembly (the gate's size).
|
|
172
201
|
#
|
|
@@ -199,6 +228,7 @@ module Insika
|
|
|
199
228
|
@output_parts = []
|
|
200
229
|
@channel_capabilities = []
|
|
201
230
|
@evidence_attachments = []
|
|
231
|
+
@presentations = []
|
|
202
232
|
# Fiber storage is INHERITED by fibers created later, so a turn spawned from
|
|
203
233
|
# inside a tool call (a subagent child) would start out carrying its
|
|
204
234
|
# parent's correlation. Clearing at turn start keeps a child from keying its
|
data/lib/insika/turn_timing.rb
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Insika
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
4
|
+
# Per-turn latency clock. Every turn runs one (a few clock reads); the turn's
|
|
5
|
+
# numbers land in ModelMetricsStore's turn rows (#metrics). INSIKA_TURN_TIMING
|
|
6
|
+
# only decides what the RESPONSE exposes (#to_h) and whether store calls count.
|
|
7
7
|
#
|
|
8
8
|
# Splits a turn into the three windows that answer "is TTFB local or provider?":
|
|
9
9
|
# prep_ms — prep_start -> ask: ALL local work before the provider call
|
|
10
10
|
# (context build, policy, guardrail detectors, chat assembly).
|
|
11
11
|
# ttft_ms — ask -> first_token: the provider round-trip to the 1st token.
|
|
12
12
|
# gen_ms — first_token -> done: streaming the rest of the response.
|
|
13
|
+
# store_calls — store calls this turn made (+ store_calls_by: "op scope" => n).
|
|
13
14
|
#
|
|
14
15
|
# Marks are monotonic; `mark` is first-write-wins so `first_token` records the
|
|
15
16
|
# FIRST content chunk even though it is called on every chunk.
|
|
@@ -26,26 +27,35 @@ module Insika
|
|
|
26
27
|
Insika::EnvSchema.truthy?(Insika::EnvSchema.read("INSIKA_TURN_TIMING", env))
|
|
27
28
|
end
|
|
28
29
|
|
|
29
|
-
#
|
|
30
|
-
#
|
|
31
|
-
|
|
32
|
-
BREAKDOWN_MARKS = %i[prep_start ask first_token done].freeze
|
|
33
|
-
private_constant :BREAKDOWN_MARKS
|
|
30
|
+
# Fiber-storage slot holding the running turn's clock. Child fibers (parallel
|
|
31
|
+
# tool calls, context providers) inherit it, so their store calls count too.
|
|
32
|
+
FIBER_KEY = :insika_turn_timing
|
|
34
33
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
#
|
|
34
|
+
def self.current = Fiber[FIBER_KEY]
|
|
35
|
+
|
|
36
|
+
# "config:<scope>" keeps its config scope; any other scope keeps its first
|
|
37
|
+
# segment, so ids embedded in scope names do not split the breakdown.
|
|
38
|
+
def self.scope_group(scope)
|
|
39
|
+
parts = scope.to_s.split(":")
|
|
40
|
+
parts.first == "config" ? parts.first(2).join(":") : parts.first.to_s
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# breakdown: true (the default) exposes prep/ttft/gen/total in #to_h.
|
|
44
|
+
# false exposes only first_balloon_ms (the flag-off contract); every mark is
|
|
45
|
+
# still taken, so #metrics is complete either way.
|
|
38
46
|
def initialize(breakdown: true)
|
|
39
47
|
@marks = {}
|
|
40
48
|
@breakdown = breakdown
|
|
49
|
+
@tools = []
|
|
41
50
|
end
|
|
42
51
|
|
|
43
52
|
def mark(name)
|
|
44
|
-
return unless @breakdown || !BREAKDOWN_MARKS.include?(name)
|
|
45
|
-
|
|
46
53
|
@marks[name] ||= Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
47
54
|
end
|
|
48
55
|
|
|
56
|
+
# One tool call of this turn (ToolEnvelope#trace): [name, ok, ms].
|
|
57
|
+
def tool(name, ok, ms) = @tools << [name.to_s, ok, ms]
|
|
58
|
+
|
|
49
59
|
# Has this mark fired? Used by `SendMessage` to prove the channel clock was
|
|
50
60
|
# stamped at 202 acceptance (`:inbound` before the debounce window), and by
|
|
51
61
|
# the pipeline to know a threaded clock already started.
|
|
@@ -56,13 +66,40 @@ module Insika
|
|
|
56
66
|
# first_balloon_ms is the inbound -> first outbox flush window.
|
|
57
67
|
# A missing number is never a zero — no mark, no key.
|
|
58
68
|
def to_h
|
|
59
|
-
{
|
|
69
|
+
h = {
|
|
60
70
|
prep_ms: delta(:prep_start, :ask),
|
|
61
71
|
ttft_ms: delta(:ask, :first_token),
|
|
62
72
|
gen_ms: delta(:first_token, :done),
|
|
63
73
|
total_ms: delta(:prep_start, :done),
|
|
64
74
|
first_balloon_ms: delta(:inbound, :first_balloon)
|
|
65
75
|
}.compact
|
|
76
|
+
h = h.slice(:first_balloon_ms) unless @breakdown
|
|
77
|
+
return h unless @store_calls
|
|
78
|
+
|
|
79
|
+
h.merge(store_calls: @store_calls.values.sum,
|
|
80
|
+
store_calls_by: @store_calls.sort_by { |call, n| [-n, call] }.to_h)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# The turn row ModelMetricsStore keeps: every window, whatever the flag.
|
|
84
|
+
# queue_ms is inbound -> pipeline start (debounce + FIFO wait), the part of
|
|
85
|
+
# first_balloon_ms that is not the turn's own work. tools_ms sums the calls,
|
|
86
|
+
# so parallel calls count more than their wall time.
|
|
87
|
+
def metrics
|
|
88
|
+
{
|
|
89
|
+
"prep_ms" => delta(:prep_start, :ask), "ttft_ms" => delta(:ask, :first_token),
|
|
90
|
+
"gen_ms" => delta(:first_token, :done), "total_ms" => delta(:prep_start, :done),
|
|
91
|
+
"first_balloon_ms" => delta(:inbound, :first_balloon), "queue_ms" => delta(:inbound, :prep_start),
|
|
92
|
+
"tools_ms" => (@tools.sum { |_, _, ms| ms.to_i } unless @tools.empty?),
|
|
93
|
+
"tools" => (@tools unless @tools.empty?)
|
|
94
|
+
}.compact
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# One store call made during this turn (see Stores::TurnCounter).
|
|
98
|
+
def count_store(op, scope)
|
|
99
|
+
return unless @breakdown
|
|
100
|
+
|
|
101
|
+
@store_calls ||= Hash.new(0)
|
|
102
|
+
@store_calls["#{op} #{self.class.scope_group(scope)}"] += 1
|
|
66
103
|
end
|
|
67
104
|
|
|
68
105
|
private
|
data/lib/insika/version.rb
CHANGED
data/lib/insika/wiring/graph.rb
CHANGED
|
@@ -24,14 +24,37 @@ module Insika
|
|
|
24
24
|
|
|
25
25
|
# Backend by config: INSIKA_DB set → durable SQLite (survives restart, the
|
|
26
26
|
# prerequisite for Recovery); missing → ephemeral Memory (dev/demo). The same
|
|
27
|
-
# rule lived verbatim in both roots.
|
|
27
|
+
# rule lived verbatim in both roots.
|
|
28
|
+
# LITESTREAM_REPLICA_URL is the same switch deploy/entrypoint.sh uses: set, and
|
|
29
|
+
# Litestream owns the checkpoints.
|
|
30
|
+
# Every backend counts its calls into the running turn (TurnTiming).
|
|
28
31
|
def backend_from_env(env = ENV)
|
|
32
|
+
Insika::Stores::TurnCounter.attach(raw_backend_from_env(env))
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def raw_backend_from_env(env = ENV)
|
|
36
|
+
url = Insika::EnvSchema.read("INSIKA_DATABASE_URL", env)
|
|
37
|
+
if url && !url.empty?
|
|
38
|
+
pool = Insika::EnvSchema.read("INSIKA_DATABASE_POOL", env)
|
|
39
|
+
return Insika::Stores::Postgres.new(url: url, pool: pool && !pool.empty? ? Integer(pool) : 10)
|
|
40
|
+
end
|
|
41
|
+
|
|
29
42
|
db = Insika::EnvSchema.read("INSIKA_DB", env)
|
|
30
|
-
|
|
43
|
+
return Insika::Stores::Memory.new unless db && !db.empty?
|
|
44
|
+
|
|
45
|
+
slow = Insika::EnvSchema.read("INSIKA_SLOW_STORE_MS", env)
|
|
46
|
+
Insika::Stores::SQLite.new(path: db, autocheckpoint: env["LITESTREAM_REPLICA_URL"].to_s.empty?,
|
|
47
|
+
slow_ms: slow && Float(slow))
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# On unless INSIKA_MODEL_VISIBLE_TRACES is set to a false value.
|
|
51
|
+
def model_visible_traces?(env = ENV)
|
|
52
|
+
value = Insika::EnvSchema.read("INSIKA_MODEL_VISIBLE_TRACES", env)
|
|
53
|
+
value.nil? || Insika::EnvSchema.truthy?(value)
|
|
31
54
|
end
|
|
32
55
|
|
|
33
56
|
# an integer tick knob from the env, nil when unset (the caller's
|
|
34
|
-
# default wins).
|
|
57
|
+
# default wins).
|
|
35
58
|
def tick_env(name, env = ENV)
|
|
36
59
|
value = Insika::EnvSchema.read(name, env)
|
|
37
60
|
value&.to_i
|
|
@@ -43,6 +66,8 @@ module Insika
|
|
|
43
66
|
def spine(backend:, extra_policy_builtins: {})
|
|
44
67
|
session_store = Insika::SessionStore.new(store: backend)
|
|
45
68
|
task_store = Insika::TaskStore.new(store: backend)
|
|
69
|
+
llm_trace_store = Insika::LLMTraceStore.new(store: backend)
|
|
70
|
+
model_metrics_store = Insika::ModelMetricsStore.new(store: backend)
|
|
46
71
|
checkpoint_store = Insika::CheckpointStore.new(store: backend)
|
|
47
72
|
pending_action_store = Insika::PendingActionStore.new(store: backend)
|
|
48
73
|
delegation_store = Insika::DelegationStore.new(store: backend)
|
|
@@ -122,7 +147,7 @@ module Insika
|
|
|
122
147
|
|
|
123
148
|
Spine.new(
|
|
124
149
|
backend: backend, event_stream: Insika::EventStream.new,
|
|
125
|
-
session_store: session_store, task_store: task_store,
|
|
150
|
+
session_store: session_store, task_store: task_store, llm_trace_store: llm_trace_store, model_metrics_store: model_metrics_store,
|
|
126
151
|
checkpoint_store: checkpoint_store, pending_action_store: pending_action_store,
|
|
127
152
|
delegation_store: delegation_store,
|
|
128
153
|
memory_store: memory_store, memory_audit_store: memory_audit_store,
|
|
@@ -168,6 +193,12 @@ module Insika
|
|
|
168
193
|
# expose it. The gem's require lives IN the factory block (loaded on
|
|
169
194
|
# the 1st instance, turn time -> wiring-load stays gem-free).
|
|
170
195
|
register_artifact_tool(spine)
|
|
196
|
+
# Turns read profiles with the platform agent defaults applied; the
|
|
197
|
+
# authoring commands the roots register keep the agent's own record.
|
|
198
|
+
if (settings_store = executor_extra[:settings_store])
|
|
199
|
+
profiles = Insika::AgentDefaults::ProfileSource.new(Insika::ProfileSource.coerce(profiles),
|
|
200
|
+
settings_store: settings_store)
|
|
201
|
+
end
|
|
171
202
|
spine.hooks.register(:task, after: guardrails.output_validator)
|
|
172
203
|
middleware = Insika::MiddlewareStack.new([edge_limiter, guardrails.input_guardrail].compact)
|
|
173
204
|
|
|
@@ -192,11 +223,16 @@ module Insika
|
|
|
192
223
|
# the profile's `reliability` data gates it, so the bare wiring is unchanged.
|
|
193
224
|
reliability = Insika::Reliability.new(circuit_store: spine.circuit_state,
|
|
194
225
|
event_stream: spine.event_stream)
|
|
226
|
+
if (url = Insika::Coercion.presence(Insika::EnvSchema.read("INSIKA_CONVERSATIONS_URL")))
|
|
227
|
+
executor_extra = {shared_conversations: Insika::SharedConversations.new(
|
|
228
|
+
url: url, token: Insika::EnvSchema.read("INSIKA_CONVERSATIONS_TOKEN"), store: spine.backend)}.merge(executor_extra)
|
|
229
|
+
end
|
|
195
230
|
executor = Insika::Executor.new(
|
|
196
231
|
context_builder: context_builder, policy_engine: policy_engine,
|
|
197
232
|
middleware: middleware, hooks: spine.hooks,
|
|
198
233
|
tool_registry: tool_registry, skill_catalog: skill_catalog, profiles: profiles,
|
|
199
234
|
session_store: spine.session_store, task_store: spine.task_store,
|
|
235
|
+
llm_trace_store: spine.llm_trace_store, model_metrics_store: spine.model_metrics_store,
|
|
200
236
|
checkpoint_store: spine.checkpoint_store, event_stream: spine.event_stream,
|
|
201
237
|
workflow_registry: spine.workflow_registry, pending_action_store: spine.pending_action_store,
|
|
202
238
|
capability_registry: spine.capability_registry, tool_catalog: tool_catalog,
|
|
@@ -213,11 +249,14 @@ module Insika
|
|
|
213
249
|
# the model-visible trace rides executor_extra like the
|
|
214
250
|
# context trace — a module graph that omits it still builds (nil =
|
|
215
251
|
# parity). The base graph wires it over the same backend the context
|
|
216
|
-
# trace uses.
|
|
217
|
-
|
|
252
|
+
# trace uses. INSIKA_MODEL_VISIBLE_TRACES=0 stops the WRITE only (nothing in
|
|
253
|
+
# the product reads it back; it was ~80% of the bytes a turn wrote): the
|
|
254
|
+
# purge paths below keep their own store, so old records still go.
|
|
255
|
+
model_visible_trace_store: model_visible_traces? ? Insika::ModelVisibleTraceStore.new(store: spine.backend) : nil,
|
|
218
256
|
# the per-turn extraction hook (nil = the loop is off,
|
|
219
257
|
# parity). Gated per-agent by `profile.knowledge`.
|
|
220
258
|
knowledge_store: spine.knowledge_store,
|
|
259
|
+
# llm stays graph-scoped; Executor binds instrumentation on operation copies.
|
|
221
260
|
**executor_extra
|
|
222
261
|
)
|
|
223
262
|
|
|
@@ -228,6 +267,10 @@ module Insika
|
|
|
228
267
|
profiles: profiles, proposal_store: spine.proposal_store,
|
|
229
268
|
session_store: spine.session_store, memory_store: spine.memory_store,
|
|
230
269
|
settings_store: executor_extra[:settings_store],
|
|
270
|
+
distiller_factory: ->(config) {
|
|
271
|
+
Insika::Distill::DistillerFactory.build(config,
|
|
272
|
+
utility_model: executor_extra[:settings_store]&.get&.[]("utility_model"), llm: executor_extra[:llm])
|
|
273
|
+
},
|
|
231
274
|
event_stream: spine.event_stream
|
|
232
275
|
)
|
|
233
276
|
|
|
@@ -297,6 +340,11 @@ module Insika
|
|
|
297
340
|
profiles: profiles, executor: executor, budget_ledger: spine.budget_ledger,
|
|
298
341
|
event_stream: spine.event_stream
|
|
299
342
|
)
|
|
343
|
+
# RubyLLM's model catalog (prices included) in the store, refreshed
|
|
344
|
+
# daily by one worker and reloaded by the rest.
|
|
345
|
+
executor.tick.model_registry = Insika::ModelRegistry.new(
|
|
346
|
+
store: spine.backend, interval: (tick_env("INSIKA_MODEL_REFRESH_HOURS") || 24) * 3600
|
|
347
|
+
)
|
|
300
348
|
# the distillation engine — the tick-duty that finds idle
|
|
301
349
|
# customer sessions and distills them on its own worker fiber (a
|
|
302
350
|
# supervisor child, started in serving mode next to the tick).
|
|
@@ -323,7 +371,11 @@ module Insika
|
|
|
323
371
|
skill_store: skill_catalog.store, # the harvest's dedup reads the authored skills
|
|
324
372
|
tool_trace_store: executor_extra[:tool_trace_store],
|
|
325
373
|
settings_store: executor_extra[:settings_store],
|
|
326
|
-
negative_list: nil,
|
|
374
|
+
negative_list: nil,
|
|
375
|
+
miner_factory: ->(config) {
|
|
376
|
+
Insika::Harvest::MinerFactory.build(config,
|
|
377
|
+
utility_model: executor_extra[:settings_store]&.get&.[]("utility_model"), llm: executor_extra[:llm])
|
|
378
|
+
},
|
|
327
379
|
event_stream: spine.event_stream
|
|
328
380
|
)
|
|
329
381
|
executor.harvest_engine = Insika::HarvestEngine.new(
|
|
@@ -343,6 +395,7 @@ module Insika
|
|
|
343
395
|
Graph::Result.new(
|
|
344
396
|
backend: spine.backend, event_stream: spine.event_stream,
|
|
345
397
|
session_store: spine.session_store, task_store: spine.task_store,
|
|
398
|
+
llm_trace_store: spine.llm_trace_store, model_metrics_store: spine.model_metrics_store,
|
|
346
399
|
checkpoint_store: spine.checkpoint_store, pending_action_store: spine.pending_action_store,
|
|
347
400
|
delegation_store: spine.delegation_store,
|
|
348
401
|
memory_store: spine.memory_store, memory_audit_store: spine.memory_audit_store,
|
|
@@ -367,7 +420,8 @@ module Insika
|
|
|
367
420
|
tool_catalog: tool_catalog, skill_catalog: skill_catalog, prompt_catalog: prompt_catalog,
|
|
368
421
|
hooks: spine.hooks, guardrails: guardrails, middleware: middleware,
|
|
369
422
|
context_providers: context_providers, context_builder: context_builder,
|
|
370
|
-
|
|
423
|
+
# the agent's own records: the Studio and the roots author through them
|
|
424
|
+
policy_engine: policy_engine, profiles: Insika::AgentDefaults.own(profiles), executor: executor, bus: bus
|
|
371
425
|
)
|
|
372
426
|
end
|
|
373
427
|
|
|
@@ -422,6 +476,13 @@ module Insika
|
|
|
422
476
|
bus = Insika::CommandBus.new
|
|
423
477
|
bus.register(:create_session,
|
|
424
478
|
Insika::Commands::CreateSession.new(session_store: spine.session_store, event_stream: spine.event_stream))
|
|
479
|
+
# A snapshot loaded into a conversation before its first turn — what an eval
|
|
480
|
+
# case declares as `state:`. The HTTP route and the in-process eval transport
|
|
481
|
+
# both dispatch this one command.
|
|
482
|
+
bus.register(:seed_session,
|
|
483
|
+
Insika::Commands::SeedSession.new(session_store: spine.session_store,
|
|
484
|
+
memory_store: spine.memory_store,
|
|
485
|
+
event_stream: spine.event_stream))
|
|
425
486
|
bus.register(:cancel_task,
|
|
426
487
|
Insika::Commands::CancelTask.new(task_store: spine.task_store, executor: executor))
|
|
427
488
|
bus.register(:pause_task,
|
|
@@ -529,6 +590,11 @@ module Insika
|
|
|
529
590
|
bus.register(:delete_artifact,
|
|
530
591
|
Insika::Commands::DeleteArtifact.new(artifact_store: spine.artifact_store,
|
|
531
592
|
event_stream: spine.event_stream))
|
|
593
|
+
# a person's verdict on a guardrail flag (false positive / resolved)
|
|
594
|
+
# — the Session viewer's button and the Review inbox.
|
|
595
|
+
bus.register(:review_guardrail_flag,
|
|
596
|
+
Insika::Commands::ReviewGuardrailFlag.new(session_store: spine.session_store,
|
|
597
|
+
event_stream: spine.event_stream))
|
|
532
598
|
bus.register(:revoke_contact,
|
|
533
599
|
Insika::Commands::RevokeContact.new(contact_store: spine.contact_store,
|
|
534
600
|
followup_store: spine.followup_store,
|
|
@@ -554,7 +620,11 @@ module Insika
|
|
|
554
620
|
skill_store: skill_catalog.store,
|
|
555
621
|
tool_trace_store: executor_extra[:tool_trace_store],
|
|
556
622
|
settings_store: executor_extra[:settings_store],
|
|
557
|
-
negative_list: nil,
|
|
623
|
+
negative_list: nil,
|
|
624
|
+
miner_factory: ->(config) {
|
|
625
|
+
Insika::Harvest::MinerFactory.build(config,
|
|
626
|
+
utility_model: executor_extra[:settings_store]&.get&.[]("utility_model"), llm: executor_extra[:llm])
|
|
627
|
+
},
|
|
558
628
|
event_stream: spine.event_stream
|
|
559
629
|
))
|
|
560
630
|
bus.register(:gate_harvest,
|
|
@@ -568,7 +638,7 @@ module Insika
|
|
|
568
638
|
harvest_store: spine.harvest_store,
|
|
569
639
|
skill_store: skill_catalog.store,
|
|
570
640
|
skill_catalog: skill_catalog,
|
|
571
|
-
profile_source: profiles, criterion: nil, conversion_gate: nil,
|
|
641
|
+
profile_source: Insika::AgentDefaults.own(profiles), criterion: nil, conversion_gate: nil,
|
|
572
642
|
event_stream: spine.event_stream
|
|
573
643
|
))
|
|
574
644
|
bus.register(:rollback_harvest,
|
|
@@ -576,7 +646,7 @@ module Insika
|
|
|
576
646
|
harvest_store: spine.harvest_store,
|
|
577
647
|
skill_store: skill_catalog.store,
|
|
578
648
|
skill_catalog: skill_catalog,
|
|
579
|
-
profile_source: profiles, event_stream: spine.event_stream
|
|
649
|
+
profile_source: Insika::AgentDefaults.own(profiles), event_stream: spine.event_stream
|
|
580
650
|
))
|
|
581
651
|
bus.register(:reject_harvest,
|
|
582
652
|
Insika::Commands::RejectHarvest.new(
|
|
@@ -594,7 +664,7 @@ module Insika
|
|
|
594
664
|
# dispatches this bus command; nothing else writes the demo agent.
|
|
595
665
|
config_store = Insika::ConfigStore.new(store: spine.backend)
|
|
596
666
|
demo_seeder = Insika::Demo::Seeder.new(
|
|
597
|
-
profiles: profiles, store: spine.backend, session_store: spine.session_store,
|
|
667
|
+
profiles: Insika::AgentDefaults.own(profiles), store: spine.backend, session_store: spine.session_store,
|
|
598
668
|
task_store: spine.task_store, outcome_store: spine.outcome_store,
|
|
599
669
|
funnel_store: spine.funnel_store, followup_store: spine.followup_store,
|
|
600
670
|
refinement_store: spine.refinement_store, pending_action_store: spine.pending_action_store,
|
|
@@ -611,7 +681,7 @@ module Insika
|
|
|
611
681
|
# Infra spine (phase 1 output). Value object — the roots read these to promote
|
|
612
682
|
# them to their historic public constants (SESSION_STORE, REGISTRY, ...).
|
|
613
683
|
Spine = Struct.new(
|
|
614
|
-
:backend, :event_stream, :session_store, :task_store, :checkpoint_store,
|
|
684
|
+
:backend, :event_stream, :session_store, :task_store, :llm_trace_store, :model_metrics_store, :checkpoint_store,
|
|
615
685
|
:pending_action_store, :delegation_store, :memory_store, :memory_audit_store,
|
|
616
686
|
:refinement_store,
|
|
617
687
|
:harvest_store,
|
|
@@ -629,7 +699,7 @@ module Insika
|
|
|
629
699
|
# deployment's overlay, or the same code registry at the base).
|
|
630
700
|
Result = Struct.new(
|
|
631
701
|
:backend, :event_stream,
|
|
632
|
-
:session_store, :task_store, :checkpoint_store, :pending_action_store, :delegation_store,
|
|
702
|
+
:session_store, :task_store, :llm_trace_store, :model_metrics_store, :checkpoint_store, :pending_action_store, :delegation_store,
|
|
633
703
|
:memory_store, :memory_audit_store, :refinement_store, :outbox_store, :shadow_pair_store,
|
|
634
704
|
:inbound_log, :token_store,
|
|
635
705
|
:budget_ledger, :circuit_state, :outcome_store, :funnel_store,
|
|
@@ -646,7 +716,7 @@ module Insika
|
|
|
646
716
|
:profiles, :executor, :bus,
|
|
647
717
|
keyword_init: true
|
|
648
718
|
) do
|
|
649
|
-
def durable? =
|
|
719
|
+
def durable? = Insika::Stores.durable?(backend)
|
|
650
720
|
end
|
|
651
721
|
|
|
652
722
|
# The save_artifact tool, registered OPTIONAL on the shared code registry
|
|
@@ -678,7 +748,9 @@ module Insika
|
|
|
678
748
|
require "ruby_llm"
|
|
679
749
|
require_relative "../tools/run_persona_eval"
|
|
680
750
|
Insika::Tools::RunPersonaEval.new(
|
|
681
|
-
golden_store: golden_store,
|
|
751
|
+
golden_store: golden_store, tool_registry: graph.tool_registry,
|
|
752
|
+
# the target as its turns see it: kit tools count in the derived swap list
|
|
753
|
+
profiles: Insika::AgentDefaults.for_turns(graph.profiles, settings_store),
|
|
682
754
|
runtime: GraphChat.new(graph: graph), graph: graph, settings_store: settings_store,
|
|
683
755
|
budget_ledger: graph.budget_ledger, event_stream: graph.event_stream, llm: llm
|
|
684
756
|
)
|
|
@@ -18,6 +18,10 @@ module Insika
|
|
|
18
18
|
@graph = graph
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
+
# The graph this seam speaks for — an eval transport reaches its bus and
|
|
22
|
+
# event stream through here.
|
|
23
|
+
attr_reader :graph
|
|
24
|
+
|
|
21
25
|
# One turn, in-process -> the assistant's text. `agent:` is REQUIRED —
|
|
22
26
|
# this seam has no notion of "the default agent" (that is a DSL::Runtime
|
|
23
27
|
# concept, filled in by its own caller before delegating here). Raises
|