samagotchi 0.156.0 → 0.158.0
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 +70 -1
- data/README.md +1 -1
- data/bin/chi +4 -1
- data/docs/architecture.md +18 -12
- data/docs/bundle-authoring.md +5 -5
- data/docs/bundles.md +4 -1
- data/docs/cli.md +29 -24
- data/docs/configuration.md +43 -9
- data/docs/context.md +5 -3
- data/docs/guardrails.md +12 -7
- data/docs/hooks.md +53 -83
- data/docs/internals/bridge.md +84 -7
- data/docs/internals/bundles.md +69 -46
- data/docs/internals/context-telemetry.md +4 -3
- data/docs/internals/engine.md +23 -7
- data/docs/internals/participants.md +3 -2
- data/docs/internals/remote-sessions.md +5 -2
- data/docs/internals/web-frontend.md +3 -2
- data/docs/internals/working-set.md +1 -1
- data/docs/memory.md +51 -10
- data/docs/plugins.md +48 -11
- data/docs/releasing.md +6 -5
- data/docs/sessions.md +13 -13
- data/docs/sub-agent.md +17 -2
- data/docs/testing.md +56 -5
- data/lib/samagotchi/answer_command.rb +2 -1
- data/lib/samagotchi/archive_store.rb +4 -3
- data/lib/samagotchi/bootstrap_command.rb +3 -2
- data/lib/samagotchi/bridge/card_places.rb +5 -7
- data/lib/samagotchi/bridge/card_store.rb +3 -0
- data/lib/samagotchi/bridge/turn_accumulator.rb +113 -10
- data/lib/samagotchi/bridge/turn_drawings.rb +271 -0
- data/lib/samagotchi/bridge.rb +31 -6
- data/lib/samagotchi/broadcast/recipients.rb +8 -8
- data/lib/samagotchi/broadcast/triage_model.rb +1 -1
- data/lib/samagotchi/bundle_command/check.rb +41 -10
- data/lib/samagotchi/bundle_command/installed_check.rb +13 -2
- data/lib/samagotchi/bundle_command/try/run.rb +4 -1
- data/lib/samagotchi/bundle_command/try/sandbox.rb +1 -1
- data/lib/samagotchi/bundle_command/try/trace.rb +1 -1
- data/lib/samagotchi/bundle_command.rb +20 -45
- data/lib/samagotchi/bundles/known-names/manifest.yml +4 -7
- data/lib/samagotchi/bundles/known-names/{hooks/known_names.rb → plugin.rb} +17 -0
- data/lib/samagotchi/bundles/source-links/manifest.yml +4 -7
- data/lib/samagotchi/bundles/source-links/{hooks/source_links.rb → plugin.rb} +23 -4
- data/lib/samagotchi/bundles/system/config_modification_protocol.md +1 -1
- data/lib/samagotchi/bundles/system/manifest.yml +4 -4
- data/lib/samagotchi/bundles/system/memory_guide.md +7 -7
- data/lib/samagotchi/bundles/system/self_map.md +1 -1
- data/lib/samagotchi/child_move.rb +6 -6
- data/lib/samagotchi/child_reports.rb +2 -2
- data/lib/samagotchi/children_status.rb +15 -21
- data/lib/samagotchi/cli/parent_wait.rb +8 -22
- data/lib/samagotchi/config.rb +3 -0
- data/lib/samagotchi/context_command.rb +7 -5
- data/lib/samagotchi/context_sources.rb +9 -6
- data/lib/samagotchi/context_window.rb +9 -12
- data/lib/samagotchi/effective_model.rb +57 -22
- data/lib/samagotchi/engine/plugins.rb +1 -1
- data/lib/samagotchi/engine/recap.rb +8 -4
- data/lib/samagotchi/engine.rb +30 -9
- data/lib/samagotchi/events.rb +3 -2
- data/lib/samagotchi/extension_load.rb +26 -44
- data/lib/samagotchi/guardrails/load_failures.rb +3 -2
- data/lib/samagotchi/guardrails/path_resolve.rb +11 -0
- data/lib/samagotchi/guardrails/review_paths.rb +1 -5
- data/lib/samagotchi/guardrails_command.rb +1 -1
- data/lib/samagotchi/hook_routes.rb +2 -2
- data/lib/samagotchi/hooks.rb +2 -3
- data/lib/samagotchi/host_address.rb +52 -0
- data/lib/samagotchi/host_registry.rb +57 -45
- data/lib/samagotchi/kernel_loop.rb +7 -3
- data/lib/samagotchi/llm/chat_loop.rb +6 -3
- data/lib/samagotchi/llm_context_strategy.rb +27 -63
- data/lib/samagotchi/memory_bundle/asset_installer.rb +92 -177
- data/lib/samagotchi/memory_bundle/builder.rb +12 -35
- data/lib/samagotchi/memory_bundle/bundle_hook.rb +6 -31
- data/lib/samagotchi/memory_bundle/file_actions.rb +193 -0
- data/lib/samagotchi/memory_bundle/file_digest.rb +13 -0
- data/lib/samagotchi/memory_bundle/file_state.rb +152 -0
- data/lib/samagotchi/memory_bundle/installed_bundle.rb +1 -1
- data/lib/samagotchi/memory_bundle/installed_copy.rb +13 -19
- data/lib/samagotchi/memory_bundle/installed_manifest.rb +10 -17
- data/lib/samagotchi/memory_bundle/installer.rb +212 -178
- data/lib/samagotchi/memory_bundle/manifest.rb +51 -58
- data/lib/samagotchi/memory_bundle/merger.rb +69 -65
- data/lib/samagotchi/memory_bundle/plugin_ref.rb +6 -5
- data/lib/samagotchi/memory_bundle/profile.rb +5 -5
- data/lib/samagotchi/memory_bundle/provenance.rb +26 -52
- data/lib/samagotchi/memory_bundle/remerge.rb +31 -14
- data/lib/samagotchi/memory_bundle/shas.rb +5 -8
- data/lib/samagotchi/memory_bundle/shipped_update.rb +51 -11
- data/lib/samagotchi/memory_bundle/status.rb +26 -30
- data/lib/samagotchi/memory_bundle/system_bundle.rb +39 -10
- data/lib/samagotchi/memory_bundle/trash.rb +28 -18
- data/lib/samagotchi/memory_bundle/uninstaller.rb +16 -11
- data/lib/samagotchi/memory_orphans.rb +4 -4
- data/lib/samagotchi/memory_paths.rb +102 -13
- data/lib/samagotchi/memory_usage.rb +1 -1
- data/lib/samagotchi/model_lookup.rb +75 -0
- data/lib/samagotchi/model_profile.rb +1 -0
- data/lib/samagotchi/models_command.rb +1 -1
- data/lib/samagotchi/note_command.rb +1 -1
- data/lib/samagotchi/participant_mode.rb +2 -1
- data/lib/samagotchi/plugin/api.rb +42 -10
- data/lib/samagotchi/plugin/loader.rb +75 -15
- data/lib/samagotchi/plugin/sessions.rb +1 -1
- data/lib/samagotchi/project_command.rb +167 -0
- data/lib/samagotchi/project_init.rb +69 -0
- data/lib/samagotchi/project_scope.rb +6 -4
- data/lib/samagotchi/prompt_files.rb +10 -7
- data/lib/samagotchi/reasoning_history.rb +14 -20
- data/lib/samagotchi/recap_place.rb +26 -0
- data/lib/samagotchi/reply_wait/liveness.rb +38 -0
- data/lib/samagotchi/reply_wait/poll.rb +44 -15
- data/lib/samagotchi/reply_wait/target.rb +113 -0
- data/lib/samagotchi/reply_wait.rb +22 -2
- data/lib/samagotchi/run_options.rb +1 -0
- data/lib/samagotchi/sampling_settings.rb +9 -16
- data/lib/samagotchi/self_report.rb +34 -35
- data/lib/samagotchi/send_command.rb +17 -23
- data/lib/samagotchi/session.rb +349 -441
- data/lib/samagotchi/session_access/contract.rb +1 -1
- data/lib/samagotchi/session_access/local.rb +5 -6
- data/lib/samagotchi/session_access/remote.rb +5 -3
- data/lib/samagotchi/session_field.rb +134 -0
- data/lib/samagotchi/session_inbox.rb +49 -1
- data/lib/samagotchi/session_manager/emptiness.rb +2 -1
- data/lib/samagotchi/session_manager/listing.rb +29 -100
- data/lib/samagotchi/session_manager.rb +2 -1
- data/lib/samagotchi/session_metrics.rb +7 -4
- data/lib/samagotchi/session_paths.rb +57 -0
- data/lib/samagotchi/session_retention.rb +2 -2
- data/lib/samagotchi/session_row.rb +200 -0
- data/lib/samagotchi/session_setup.rb +15 -12
- data/lib/samagotchi/sessions_command.rb +11 -129
- data/lib/samagotchi/steer.rb +10 -0
- data/lib/samagotchi/steer_cut_after.rb +7 -14
- data/lib/samagotchi/system_prompt.rb +20 -11
- data/lib/samagotchi/terminal_ui/attached_loop.rb +6 -26
- data/lib/samagotchi/terminal_ui/join_replay.rb +91 -8
- data/lib/samagotchi/terminal_ui/local_link.rb +2 -2
- data/lib/samagotchi/terminal_ui.rb +3 -1
- data/lib/samagotchi/testing/bundle.rb +5 -12
- data/lib/samagotchi/testing/install.rb +5 -4
- data/lib/samagotchi/testing/recorder.rb +1 -1
- data/lib/samagotchi/testing.rb +1 -1
- data/lib/samagotchi/thinking.rb +16 -21
- data/lib/samagotchi/tool_declarations.rb +1 -1
- data/lib/samagotchi/tool_response.rb +5 -3
- data/lib/samagotchi/tool_runner.rb +18 -1
- data/lib/samagotchi/toolkit_binding.rb +1 -4
- data/lib/samagotchi/toolkit_command.rb +1 -4
- data/lib/samagotchi/tools/delegate.rb +60 -51
- data/lib/samagotchi/tools/delegate_relay.rb +5 -5
- data/lib/samagotchi/tools/delegate_result.rb +1 -1
- data/lib/samagotchi/tools/delegate_wait.rb +41 -17
- data/lib/samagotchi/tools/list_sessions.rb +7 -7
- data/lib/samagotchi/update_command.rb +11 -7
- data/lib/samagotchi/verify_fresh.rb +7 -11
- data/lib/samagotchi/version.rb +1 -1
- data/lib/samagotchi/vision_support.rb +11 -14
- data/lib/samagotchi/web/app.rb +66 -21
- data/lib/samagotchi/web/message_display.rb +57 -7
- data/lib/samagotchi/web/participant_access.rb +1 -1
- data/lib/samagotchi/web/project_list.rb +3 -2
- data/lib/samagotchi/web/public/activity.js +16 -7
- data/lib/samagotchi/web/public/app.js +29 -8
- data/lib/samagotchi/web/public/bubbles.js +8 -3
- data/lib/samagotchi/web/public/cards_place.js +15 -0
- data/lib/samagotchi/web/public/commands.js +5 -1
- data/lib/samagotchi/web/public/data.js +4 -2
- data/lib/samagotchi/web/public/history_shape.js +220 -0
- data/lib/samagotchi/web/public/index.html +6 -1
- data/lib/samagotchi/web/public/project_picker.js +86 -4
- data/lib/samagotchi/web/public/recap_stamp.js +17 -0
- data/lib/samagotchi/web/public/reload_check.js +71 -0
- data/lib/samagotchi/web/public/stage_model.js +7 -5
- data/lib/samagotchi/web/public/stage_view.js +7 -3
- data/lib/samagotchi/web/public/stream_handlers.js +8 -2
- data/lib/samagotchi/web/public/timing.js +43 -2
- data/lib/samagotchi/web/public/turn_block.js +215 -30
- data/lib/samagotchi/web/public/turn_drawing.js +43 -0
- data/lib/samagotchi/web/public/turn_events.js +10 -1
- data/lib/samagotchi/web/public/turn_pieces.js +29 -0
- data/lib/samagotchi/web/reload_check_route.rb +95 -0
- data/lib/samagotchi/web/server.rb +4 -2
- data/lib/samagotchi/web/session_access_routes.rb +3 -3
- data/lib/samagotchi/web/session_control.rb +1 -1
- data/lib/samagotchi/web/session_hub.rb +9 -17
- data/lib/samagotchi/web/session_show.rb +64 -2
- data/lib/samagotchi/web/session_summary.rb +36 -24
- data/lib/samagotchi/worker.rb +28 -5
- data/lib/samagotchi/worker_link.rb +3 -0
- metadata +22 -5
- data/lib/samagotchi/hooks/bundle_loader.rb +0 -191
- data/lib/samagotchi/send_command/wait_target.rb +0 -85
data/lib/samagotchi/hooks.rb
CHANGED
|
@@ -2,12 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
require_relative "hooks/registry"
|
|
4
4
|
require_relative "hooks/loader"
|
|
5
|
-
require_relative "hooks/bundle_loader"
|
|
6
5
|
require_relative "hooks/stream_watch"
|
|
7
6
|
|
|
8
7
|
module Samagotchi
|
|
9
|
-
# Thin wrapper that exposes the Hooks::Registry
|
|
10
|
-
#
|
|
8
|
+
# Thin wrapper that exposes the Hooks::Registry and Hooks::Loader
|
|
9
|
+
# (config.yml's hooks; a bundle's hook through its plugin, Plugin::Api#on).
|
|
11
10
|
#
|
|
12
11
|
# The Engine holds an instance of Hooks::Registry and provides:
|
|
13
12
|
# - #register_hook(name, &block) — register a turn-scoped hook
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "ipaddr"
|
|
4
|
+
require_relative "config"
|
|
5
|
+
|
|
6
|
+
module Samagotchi
|
|
7
|
+
# What a host's address says about it, without asking it: whether it is
|
|
8
|
+
# remote (a provider on the network rather than a local server), and the
|
|
9
|
+
# configured first-token limit. A leaf: HostRegistry::HostEntry and
|
|
10
|
+
# BootstrapCommand read it, it reads only Config.
|
|
11
|
+
module HostAddress
|
|
12
|
+
# Loopback, private (RFC 1918, IPv6 unique local) and link-local nets.
|
|
13
|
+
LOCAL_NETS = %w[127.0.0.0/8 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 169.254.0.0/16 100.64.0.0/10
|
|
14
|
+
::1/128 fc00::/7 fe80::/10].map { |net| IPAddr.new(net) }.freeze
|
|
15
|
+
|
|
16
|
+
# Name suffixes that stay on a home or office network: mDNS, the
|
|
17
|
+
# special-use and customary private names, router defaults (fritz.box)
|
|
18
|
+
# and Tailscale's MagicDNS. Compared on whole labels.
|
|
19
|
+
LOCAL_NAME_SUFFIXES = %w[localhost local lan home home.arpa internal intranet localdomain private corp test
|
|
20
|
+
box ts.net].freeze
|
|
21
|
+
|
|
22
|
+
# Whether an address is remote: https, an http IP address outside the
|
|
23
|
+
# local nets (LOCAL_NETS), or an http name with a dot whose suffix isn't
|
|
24
|
+
# a local one (LOCAL_NAME_SUFFIXES): `gpu.example.com` is remote, while
|
|
25
|
+
# `box`, `mac.local`, `gpu.lan` and `pc.tail1234.ts.net` are local. chi
|
|
26
|
+
# doesn't look names up; hosts.<name>.remote overrides either way.
|
|
27
|
+
def self.remote_address?(scheme, host)
|
|
28
|
+
return true if scheme.to_s == "https"
|
|
29
|
+
|
|
30
|
+
address = IPAddr.new(host.to_s.delete_prefix("[").delete_suffix("]"))
|
|
31
|
+
address = address.native if address.ipv4_mapped?
|
|
32
|
+
LOCAL_NETS.none? { |net| net.family == address.family && net.include?(address) }
|
|
33
|
+
rescue IPAddr::Error
|
|
34
|
+
remote_name?(host)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def self.remote_name?(host)
|
|
38
|
+
name = host.to_s.downcase.delete_suffix(".")
|
|
39
|
+
return false unless name.include?(".")
|
|
40
|
+
|
|
41
|
+
LOCAL_NAME_SUFFIXES.none? { |suffix| name == suffix || name.end_with?(".#{suffix}") }
|
|
42
|
+
end
|
|
43
|
+
private_class_method :remote_name?
|
|
44
|
+
|
|
45
|
+
# server.first_token_timeout, or nil when unset or unreadable.
|
|
46
|
+
def self.configured_first_token_timeout
|
|
47
|
+
Config.get("server.first_token_timeout")
|
|
48
|
+
rescue StandardError # degrade-ok: no configured limit; the host's own or the remote default applies
|
|
49
|
+
nil
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "ipaddr"
|
|
4
3
|
require_relative "config"
|
|
4
|
+
require_relative "host_address"
|
|
5
5
|
require_relative "log"
|
|
6
6
|
require_relative "model_ref"
|
|
7
7
|
require_relative "client"
|
|
@@ -88,11 +88,11 @@ module Samagotchi
|
|
|
88
88
|
def dialect_profile = LLM::Dialect.for(dialect)
|
|
89
89
|
|
|
90
90
|
# A provider on the network rather than a local server: hosts.<name>.remote
|
|
91
|
-
# when set, else by its address (
|
|
91
|
+
# when set, else by its address (HostAddress.remote_address?). An API
|
|
92
92
|
# key doesn't decide: a llama.cpp on the LAN can have one. A remote
|
|
93
93
|
# host's model list is cached longer, it gets a first-token limit, and
|
|
94
94
|
# it isn't asked for llama.cpp's /props.
|
|
95
|
-
def remote? = remote.nil? ?
|
|
95
|
+
def remote? = remote.nil? ? HostAddress.remote_address?(scheme, host) : remote
|
|
96
96
|
|
|
97
97
|
def models_ttl = remote? ? REMOTE_CACHE_TTL_SECONDS : CACHE_TTL_SECONDS
|
|
98
98
|
|
|
@@ -102,7 +102,7 @@ module Samagotchi
|
|
|
102
102
|
# and read_timeout catches a dead one). 0 turns it off.
|
|
103
103
|
def first_token_limit
|
|
104
104
|
seconds = first_token_timeout
|
|
105
|
-
seconds =
|
|
105
|
+
seconds = HostAddress.configured_first_token_timeout if seconds.nil?
|
|
106
106
|
seconds = remote? ? REMOTE_FIRST_TOKEN_TIMEOUT : nil if seconds.nil?
|
|
107
107
|
seconds&.positive? ? seconds : nil
|
|
108
108
|
end
|
|
@@ -115,13 +115,61 @@ module Samagotchi
|
|
|
115
115
|
def openai_base_url = entry.openai_base_url
|
|
116
116
|
end
|
|
117
117
|
|
|
118
|
+
# Raised when a client override is set on the shared registry (.current):
|
|
119
|
+
# every command and chi web's routes would then send through it.
|
|
120
|
+
class Shared < StandardError
|
|
121
|
+
def initialize(msg = "HostRegistry.current is shared: set a client override on a registry of your own") = super
|
|
122
|
+
end
|
|
123
|
+
|
|
118
124
|
# A client that every target uses instead of its host's own (specs inject
|
|
119
125
|
# a stub this way; the Engine/TUI `client:` keyword sets it).
|
|
120
|
-
|
|
126
|
+
attr_reader :client_override
|
|
127
|
+
|
|
128
|
+
# @raise [Shared] on the shared registry (.current)
|
|
129
|
+
def client_override=(client)
|
|
130
|
+
raise Shared if @shared
|
|
131
|
+
|
|
132
|
+
@client_override = client
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
@current_mutex = Mutex.new
|
|
136
|
+
|
|
137
|
+
# The registry this process's commands and chi web share: one per
|
|
138
|
+
# process, built again when config.yml's hosts: no longer equal the ones
|
|
139
|
+
# it was built from (HostConfig.all, compared as read: the YAML read is
|
|
140
|
+
# mtime-cached), so a host added while chi web runs is listed on the next
|
|
141
|
+
# read, and its model lists, index and adapters are kept while hosts:
|
|
142
|
+
# stay. It doesn't rebuild on server.first_token_timeout or the
|
|
143
|
+
# server.host/port fallback entry: those are read at construction. It
|
|
144
|
+
# refuses a client override (Shared). An Engine and the TerminalUI take
|
|
145
|
+
# it once, at their start: a running worker keeps the hosts it started
|
|
146
|
+
# with.
|
|
147
|
+
# For any env but the process ENV (chi self's spec envs): a fresh
|
|
148
|
+
# registry on that env, never kept.
|
|
149
|
+
# @return [HostRegistry]
|
|
150
|
+
def self.current(env: ENV)
|
|
151
|
+
return new(env: env) unless env.equal?(ENV)
|
|
152
|
+
|
|
153
|
+
@current_mutex.synchronize do
|
|
154
|
+
hosts = HostConfig.all
|
|
155
|
+
if @current.nil? || hosts != @current_hosts
|
|
156
|
+
@current = new(hosts_config: hosts, shared: true)
|
|
157
|
+
@current_hosts = hosts
|
|
158
|
+
end
|
|
159
|
+
@current
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
# Forget the shared registry (specs: one per example).
|
|
164
|
+
def self.reset_current!
|
|
165
|
+
@current_mutex.synchronize { @current = @current_hosts = nil }
|
|
166
|
+
end
|
|
121
167
|
|
|
122
168
|
# @param clock [#call, nil] monotonic seconds (specs)
|
|
123
|
-
|
|
169
|
+
# @param shared [Boolean] .current's own: refuses a client override
|
|
170
|
+
def initialize(hosts_config: nil, env: ENV, client_override: nil, clock: nil, shared: false)
|
|
124
171
|
@client_override = client_override
|
|
172
|
+
@shared = shared
|
|
125
173
|
@env = env
|
|
126
174
|
@clock = clock || -> { Process.clock_gettime(Process::CLOCK_MONOTONIC) }
|
|
127
175
|
@adapters = {}
|
|
@@ -155,45 +203,9 @@ module Samagotchi
|
|
|
155
203
|
|
|
156
204
|
attr_reader :entries
|
|
157
205
|
|
|
158
|
-
#
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
# Name suffixes that stay on a home or office network: mDNS, the
|
|
163
|
-
# special-use and customary private names, router defaults (fritz.box)
|
|
164
|
-
# and Tailscale's MagicDNS. Compared on whole labels.
|
|
165
|
-
LOCAL_NAME_SUFFIXES = %w[localhost local lan home home.arpa internal intranet localdomain private corp test
|
|
166
|
-
box ts.net].freeze
|
|
167
|
-
|
|
168
|
-
# Whether an address is remote: https, an http IP address outside the
|
|
169
|
-
# local nets (LOCAL_NETS), or an http name with a dot whose suffix isn't
|
|
170
|
-
# a local one (LOCAL_NAME_SUFFIXES): `gpu.example.com` is remote, while
|
|
171
|
-
# `box`, `mac.local`, `gpu.lan` and `pc.tail1234.ts.net` are local. chi
|
|
172
|
-
# doesn't look names up; hosts.<name>.remote overrides either way.
|
|
173
|
-
def self.remote_address?(scheme, host)
|
|
174
|
-
return true if scheme.to_s == "https"
|
|
175
|
-
|
|
176
|
-
address = IPAddr.new(host.to_s.delete_prefix("[").delete_suffix("]"))
|
|
177
|
-
address = address.native if address.ipv4_mapped?
|
|
178
|
-
LOCAL_NETS.none? { |net| net.family == address.family && net.include?(address) }
|
|
179
|
-
rescue IPAddr::Error
|
|
180
|
-
remote_name?(host)
|
|
181
|
-
end
|
|
182
|
-
|
|
183
|
-
def self.remote_name?(host)
|
|
184
|
-
name = host.to_s.downcase.delete_suffix(".")
|
|
185
|
-
return false unless name.include?(".")
|
|
186
|
-
|
|
187
|
-
LOCAL_NAME_SUFFIXES.none? { |suffix| name == suffix || name.end_with?(".#{suffix}") }
|
|
188
|
-
end
|
|
189
|
-
private_class_method :remote_name?
|
|
190
|
-
|
|
191
|
-
# server.first_token_timeout, or nil when unset or unreadable.
|
|
192
|
-
def self.configured_first_token_timeout
|
|
193
|
-
Config.get("server.first_token_timeout")
|
|
194
|
-
rescue StandardError
|
|
195
|
-
nil
|
|
196
|
-
end
|
|
206
|
+
# The env its hosts, aliases and saved lists are read with (ENV, or chi
|
|
207
|
+
# self's): what an EffectiveModel's ModelLookup reads every layer with.
|
|
208
|
+
attr_reader :env
|
|
197
209
|
|
|
198
210
|
def default_entry
|
|
199
211
|
@entries["default"] || @entries.values.first
|
|
@@ -792,20 +792,24 @@ module Samagotchi
|
|
|
792
792
|
turn.qwen_attempts = 0
|
|
793
793
|
turn.qwen_partial = nil
|
|
794
794
|
runs = []
|
|
795
|
+
# The batch's ids before it runs (each run's events carry its own):
|
|
796
|
+
# nothing appends to the conversation during a batch, so they are the
|
|
797
|
+
# ids the entry would get after it (spec/tool_ids_pin_spec.rb).
|
|
798
|
+
ids = ToolIds.next_ids(turn.conversation, calls.size)
|
|
795
799
|
begin
|
|
796
800
|
ToolResponse.run_batch(tool_runner, calls, context: @context, iteration: turn.iteration, emit: turn.emit,
|
|
797
|
-
on_stream_event: turn.on_stream_event, cap: turn.cap) { |run, _| runs << run }
|
|
801
|
+
on_stream_event: turn.on_stream_event, cap: turn.cap, ids: ids) { |run, _| runs << run }
|
|
798
802
|
rescue Interrupt
|
|
799
803
|
# A Ctrl-C mid-batch: the entry still answers every call (the
|
|
800
804
|
# finished runs, then run_batch's interrupted ones), for #run to
|
|
801
805
|
# hand on with the conversation.
|
|
802
|
-
turn.conversation << ToolResponse.joined(runs, ids:
|
|
806
|
+
turn.conversation << ToolResponse.joined(runs, ids: ids.first(runs.size))
|
|
803
807
|
raise
|
|
804
808
|
end
|
|
805
809
|
turn.tool_activity.concat(ToolResponse.activities(runs))
|
|
806
810
|
# One entry for the batch: the capped outputs joined (ToolResponse.joined),
|
|
807
811
|
# an id per run (ToolIds).
|
|
808
|
-
turn.conversation << ToolResponse.joined(runs, ids:
|
|
812
|
+
turn.conversation << ToolResponse.joined(runs, ids: ids.first(runs.size))
|
|
809
813
|
turn.pending_tool_calls = true
|
|
810
814
|
:next
|
|
811
815
|
end
|
|
@@ -1132,10 +1132,13 @@ module Samagotchi
|
|
|
1132
1132
|
def dispatch(tool_calls, iteration, cap)
|
|
1133
1133
|
log_direct_deferred(tool_calls)
|
|
1134
1134
|
calls = tool_calls.map { |tool_call| NativeToolNormalizer.normalize(tool_call) }
|
|
1135
|
+
# The batch's ids before it runs (each run's events carry its own):
|
|
1136
|
+
# the same as one next id per result as it lands
|
|
1137
|
+
# (spec/tool_ids_pin_spec.rb).
|
|
1138
|
+
ids = ToolIds.next_ids(@conversation, calls.size)
|
|
1135
1139
|
runs = ToolResponse.run_batch(@loop.tool_runner, calls, context: @turn_context, iteration: iteration, emit: method(:emit),
|
|
1136
|
-
on_stream_event: @on_stream_event, cap: cap) do |run, index|
|
|
1137
|
-
@conversation << ToolResponse.single(run, tool_call_id: tool_calls[index].id,
|
|
1138
|
-
ids: ToolIds.next_ids(@conversation, 1))
|
|
1140
|
+
on_stream_event: @on_stream_event, cap: cap, ids: ids) do |run, index|
|
|
1141
|
+
@conversation << ToolResponse.single(run, tool_call_id: tool_calls[index].id, ids: [ids[index]])
|
|
1139
1142
|
end
|
|
1140
1143
|
@tool_activity.concat(ToolResponse.activities(runs))
|
|
1141
1144
|
end
|
|
@@ -148,10 +148,10 @@ module Samagotchi
|
|
|
148
148
|
end
|
|
149
149
|
|
|
150
150
|
# Where one value came from: :session, :model_setting, :host_setting
|
|
151
|
-
# or :config, and the place as
|
|
152
|
-
# "models: deepseek", "hosts
|
|
151
|
+
# or :config, and the place, named as the other settings name theirs
|
|
152
|
+
# ("session", "models: deepseek", "hosts.box", "llm_context.apply").
|
|
153
153
|
Origin = Data.define(:source, :where)
|
|
154
|
-
SESSION_ORIGIN = Origin.new(source: :session, where: "
|
|
154
|
+
SESSION_ORIGIN = Origin.new(source: :session, where: "session")
|
|
155
155
|
|
|
156
156
|
# What a turn's strategy, apply rule and budget are, each with its
|
|
157
157
|
# Origin (/llm-context, /stats, the web's info bar).
|
|
@@ -169,26 +169,22 @@ module Samagotchi
|
|
|
169
169
|
end
|
|
170
170
|
end
|
|
171
171
|
|
|
172
|
-
# The strategy for +
|
|
173
|
-
# @param
|
|
174
|
-
# @param names [Array<String>] HostRegistry#lookup_names
|
|
175
|
-
# @param models [Hash{String => ModelSettings}, nil] ModelSettings.all (specs)
|
|
172
|
+
# The strategy for +lookup+'s model's turn.
|
|
173
|
+
# @param lookup [ModelLookup] the model's layers (EffectiveModel#lookup)
|
|
176
174
|
# @param session [Array<Symbol>, nil] the session's own layers ([]: none)
|
|
177
175
|
# @param session_apply [Symbol, nil] the session's own apply rule
|
|
178
176
|
# @param session_budget [Integer, nil] the session's own budget (0: off)
|
|
179
177
|
# @return [Resolved]
|
|
180
|
-
def resolve(
|
|
181
|
-
explain(
|
|
182
|
-
session_budget: session_budget).resolved
|
|
178
|
+
def resolve(lookup, session: nil, session_apply: nil, session_budget: nil)
|
|
179
|
+
explain(lookup, session: session, session_apply: session_apply, session_budget: session_budget).resolved
|
|
183
180
|
end
|
|
184
181
|
|
|
185
182
|
# #resolve, with where each value came from.
|
|
186
183
|
# @return [Explained]
|
|
187
|
-
def explain(
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
budget, budget_origin = budget_for(target, names, models, session_budget)
|
|
184
|
+
def explain(lookup, session: nil, session_apply: nil, session_budget: nil)
|
|
185
|
+
layers, source, where = layers_for(lookup, session)
|
|
186
|
+
apply, apply_origin = apply_for(lookup, session_apply)
|
|
187
|
+
budget, budget_origin = budget_for(lookup, session_budget)
|
|
192
188
|
resolved = resolved(layers, source, where).with(apply: apply, protect_steps: protect_steps,
|
|
193
189
|
stale_edits: Config.get(STALE_EDITS_SETTING) == true,
|
|
194
190
|
budget_tokens: budget)
|
|
@@ -196,79 +192,47 @@ module Samagotchi
|
|
|
196
192
|
budget_tokens: budget_origin)
|
|
197
193
|
end
|
|
198
194
|
|
|
199
|
-
# A session's LLM context strategy, apply rule and budget, each with
|
|
200
|
-
# where it came from (Explained#summary), as its worker would resolve
|
|
201
|
-
# them; nil when they can't be (an unreadable config, a model that
|
|
202
|
-
# resolves nowhere). Resolves from config and the registry only: no
|
|
203
|
-
# network, no model call. What a session page, a session list's ctx %
|
|
204
|
-
# and a spawn-time check all read.
|
|
205
|
-
# @param session [Session, nil]
|
|
206
|
-
# @param registry [HostRegistry, nil] the hosts to resolve against;
|
|
207
|
-
# HostRegistry.new by default (what the caller usually has)
|
|
208
|
-
# @return [Hash, nil] Explained#summary, symbol keys
|
|
209
|
-
def for_session(session, registry: nil)
|
|
210
|
-
return nil if session.nil?
|
|
211
|
-
|
|
212
|
-
# Here, not at the top: config.rb requires this file before ConfigFile
|
|
213
|
-
# exists, and effective_model.rb requires config.rb (via host_registry.rb).
|
|
214
|
-
require_relative "effective_model"
|
|
215
|
-
registry ||= HostRegistry.new
|
|
216
|
-
own = session.llm_context
|
|
217
|
-
EffectiveModel.of_session(session, registry: registry).llm_context_explained(own&.resolve_args || {})&.summary(own)
|
|
218
|
-
rescue StandardError
|
|
219
|
-
nil
|
|
220
|
-
end
|
|
221
|
-
|
|
222
|
-
# The budget a session's turns run under (for_session's :budget_tokens),
|
|
223
|
-
# nil when it is off or can't be resolved. What a session list's ctx %
|
|
224
|
-
# counts against, so a saved session's fill reads as the live meter.
|
|
225
|
-
# @param session [Session, nil]
|
|
226
|
-
# @param registry [HostRegistry, nil] see .for_session
|
|
227
|
-
# @return [Integer, nil]
|
|
228
|
-
def session_budget(session, registry: nil)
|
|
229
|
-
for_session(session, registry: registry)&.fetch(:budget_tokens, nil)
|
|
230
|
-
end
|
|
231
|
-
|
|
232
195
|
# [the budget, its Origin]: the session's own first (0 there is off,
|
|
233
196
|
# whatever the model says).
|
|
234
|
-
def budget_for(
|
|
197
|
+
def budget_for(lookup, session_budget)
|
|
235
198
|
return [session_budget.positive? ? session_budget : nil, SESSION_ORIGIN] unless session_budget.nil?
|
|
236
199
|
|
|
237
|
-
if (setting =
|
|
200
|
+
if (setting = lookup.model(:llm_context_budget_tokens))
|
|
238
201
|
return [setting.value, Origin.new(source: :model_setting, where: "models: #{setting.key}")]
|
|
239
202
|
end
|
|
240
203
|
|
|
241
|
-
budget =
|
|
242
|
-
return [budget, Origin.new(source: :host_setting, where: "hosts
|
|
204
|
+
budget = lookup.host(:llm_context_budget_tokens)
|
|
205
|
+
return [budget, Origin.new(source: :host_setting, where: "hosts.#{lookup.host_name}")] if budget
|
|
243
206
|
|
|
244
|
-
[parse_budget(
|
|
207
|
+
[parse_budget(lookup.global(BUDGET_SETTING).first, BUDGET_SETTING), Origin.new(source: :config, where: BUDGET_SETTING)]
|
|
245
208
|
end
|
|
246
209
|
|
|
247
|
-
def layers_for(
|
|
210
|
+
def layers_for(lookup, session)
|
|
248
211
|
return [session, :session, SESSION_ORIGIN.where] if session
|
|
249
212
|
|
|
250
|
-
if (setting =
|
|
213
|
+
if (setting = lookup.model(:llm_context_strategy))
|
|
251
214
|
return [setting.value, :model_setting, "models: #{setting.key}"]
|
|
252
215
|
end
|
|
253
216
|
|
|
254
|
-
layers =
|
|
255
|
-
return [layers, :host_setting, "hosts
|
|
217
|
+
layers = lookup.host(:llm_context_strategy)
|
|
218
|
+
return [layers, :host_setting, "hosts.#{lookup.host_name}"] if layers
|
|
256
219
|
|
|
257
|
-
[parse(
|
|
220
|
+
[parse(lookup.global(SETTING).first, SETTING) || [], :config, SETTING]
|
|
258
221
|
end
|
|
259
222
|
|
|
260
223
|
# [the apply rule, its Origin].
|
|
261
|
-
def apply_for(
|
|
224
|
+
def apply_for(lookup, session_apply)
|
|
262
225
|
return [session_apply, SESSION_ORIGIN] if session_apply
|
|
263
226
|
|
|
264
|
-
if (setting =
|
|
227
|
+
if (setting = lookup.model(:llm_context_apply))
|
|
265
228
|
return [setting.value, Origin.new(source: :model_setting, where: "models: #{setting.key}")]
|
|
266
229
|
end
|
|
267
230
|
|
|
268
|
-
apply =
|
|
269
|
-
return [apply, Origin.new(source: :host_setting, where: "hosts
|
|
231
|
+
apply = lookup.host(:llm_context_apply)
|
|
232
|
+
return [apply, Origin.new(source: :host_setting, where: "hosts.#{lookup.host_name}")] if apply
|
|
270
233
|
|
|
271
|
-
[parse_apply(
|
|
234
|
+
[parse_apply(lookup.global(APPLY_SETTING).first, APPLY_SETTING) || DEFAULT_APPLY,
|
|
235
|
+
Origin.new(source: :config, where: APPLY_SETTING)]
|
|
272
236
|
end
|
|
273
237
|
|
|
274
238
|
# llm_context.protect_steps: 0 or more (a negative one is the default).
|