samagotchi 0.50.0 → 0.52.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 +89 -1
- data/README.md +1 -1
- data/bin/chi +11 -3
- data/docs/architecture.md +1 -1
- data/docs/cli.md +40 -11
- data/docs/configuration.md +63 -21
- data/docs/hooks.md +3 -2
- data/docs/internals/engine.md +4 -2
- data/docs/memory.md +11 -0
- data/docs/plugins.md +2 -1
- data/docs/sessions.md +10 -5
- data/docs/testing.md +6 -1
- data/lib/samagotchi/bootstrap/config_writer.rb +6 -37
- data/lib/samagotchi/bridge/ring_buffer.rb +0 -5
- data/lib/samagotchi/bridge/turn_accumulator.rb +3 -1
- data/lib/samagotchi/bridge.rb +29 -17
- data/lib/samagotchi/bridge_client.rb +3 -1
- data/lib/samagotchi/broadcast/recipients.rb +1 -2
- data/lib/samagotchi/bundles/system/config_modification_protocol.md +1 -1
- data/lib/samagotchi/bundles/system/manifest.yml +2 -2
- data/lib/samagotchi/children_status.rb +1 -1
- data/lib/samagotchi/commands/registry.rb +2 -1
- data/lib/samagotchi/config.rb +50 -182
- data/lib/samagotchi/config_text_edit.rb +8 -32
- data/lib/samagotchi/context_sources.rb +0 -7
- data/lib/samagotchi/context_window.rb +2 -2
- data/lib/samagotchi/continue_offer.rb +5 -4
- data/lib/samagotchi/cut_policy.rb +0 -1
- data/lib/samagotchi/delivery.rb +56 -0
- data/lib/samagotchi/engine.rb +48 -17
- data/lib/samagotchi/engine_peers.rb +1 -0
- data/lib/samagotchi/host_config.rb +183 -0
- data/lib/samagotchi/host_registry.rb +21 -13
- data/lib/samagotchi/kernel_loop.rb +28 -2
- data/lib/samagotchi/last_turn.rb +48 -0
- data/lib/samagotchi/live_versions.rb +0 -6
- data/lib/samagotchi/llm/native_tool_normalizer.rb +2 -7
- data/lib/samagotchi/llm_context_override.rb +3 -22
- data/lib/samagotchi/llm_context_strategy.rb +50 -16
- data/lib/samagotchi/memory_bundle/placeholder.rb +0 -5
- data/lib/samagotchi/model_profile.rb +4 -4
- data/lib/samagotchi/model_settings.rb +46 -0
- data/lib/samagotchi/plugin/context.rb +3 -3
- data/lib/samagotchi/plugin/sessions.rb +2 -2
- data/lib/samagotchi/queued_command.rb +35 -0
- data/lib/samagotchi/reminder_store.rb +0 -27
- data/lib/samagotchi/reply_wait.rb +11 -13
- data/lib/samagotchi/sampling_settings.rb +4 -4
- data/lib/samagotchi/self_report.rb +8 -5
- data/lib/samagotchi/send_command.rb +92 -21
- data/lib/samagotchi/session.rb +45 -24
- data/lib/samagotchi/session_commands.rb +41 -3
- data/lib/samagotchi/session_inbox.rb +36 -7
- data/lib/samagotchi/session_manager.rb +21 -10
- data/lib/samagotchi/session_metrics.rb +25 -6
- data/lib/samagotchi/session_setup.rb +30 -0
- data/lib/samagotchi/sessions_command.rb +1 -1
- data/lib/samagotchi/steer_cut.rb +21 -10
- data/lib/samagotchi/terminal_ui/attach_launcher.rb +10 -4
- data/lib/samagotchi/terminal_ui/attached_loop.rb +48 -1
- data/lib/samagotchi/terminal_ui/formatting.rb +69 -4
- data/lib/samagotchi/terminal_ui.rb +56 -8
- data/lib/samagotchi/thinking.rb +67 -7
- data/lib/samagotchi/thinking_command.rb +72 -0
- data/lib/samagotchi/tool_activity.rb +3 -0
- data/lib/samagotchi/tool_runner.rb +2 -0
- data/lib/samagotchi/tools/builtin_calls.rb +33 -0
- data/lib/samagotchi/tools/builtins.rb +7 -2
- data/lib/samagotchi/tools/peers.rb +3 -2
- data/lib/samagotchi/tools/task_runtime.rb +1 -1
- data/lib/samagotchi/version.rb +1 -1
- data/lib/samagotchi/vision_support.rb +4 -3
- data/lib/samagotchi/web/app.rb +59 -7
- data/lib/samagotchi/web/message_parts.rb +3 -0
- data/lib/samagotchi/web/public/activity.js +6 -0
- data/lib/samagotchi/web/public/app.js +199 -48
- data/lib/samagotchi/web/public/data.js +8 -4
- data/lib/samagotchi/web/public/format.js +8 -4
- data/lib/samagotchi/web/public/index.html +25 -20
- data/lib/samagotchi/web/public/kept_commands.js +52 -0
- data/lib/samagotchi/web/public/model_pick.js +3 -1
- data/lib/samagotchi/web/public/session_title.js +26 -0
- data/lib/samagotchi/web/public/strip.js +4 -6
- data/lib/samagotchi/web/public/thinking_chip.js +169 -0
- data/lib/samagotchi/web/public/timing.js +15 -0
- data/lib/samagotchi/web/public/turn_events.js +26 -2
- data/lib/samagotchi/web/public/turn_html.js +3 -1
- data/lib/samagotchi/web/public/turn_view.js +328 -310
- data/lib/samagotchi/web/public/vendor/lit-html.js +8 -0
- data/lib/samagotchi/web/session_summary.rb +1 -1
- data/lib/samagotchi/worker.rb +44 -43
- data/lib/samagotchi/worker_inbound.rb +10 -7
- data/lib/samagotchi/worker_wakes.rb +0 -2
- data/lib/samagotchi/yaml_lines.rb +72 -0
- data/lib/samagotchi.rb +1 -0
- metadata +13 -1
data/lib/samagotchi/web/app.rb
CHANGED
|
@@ -10,6 +10,7 @@ require "rack"
|
|
|
10
10
|
require "rack/request"
|
|
11
11
|
|
|
12
12
|
require_relative "../client_id"
|
|
13
|
+
require_relative "../delivery"
|
|
13
14
|
require_relative "../answer_tail"
|
|
14
15
|
require_relative "../bridge_client"
|
|
15
16
|
require_relative "../bridge/bounded_queue"
|
|
@@ -23,6 +24,7 @@ require_relative "../host_registry"
|
|
|
23
24
|
require_relative "../llm_context_override"
|
|
24
25
|
require_relative "../model_catalog"
|
|
25
26
|
require_relative "../model_profile"
|
|
27
|
+
require_relative "../thinking"
|
|
26
28
|
require_relative "../sampling_settings"
|
|
27
29
|
require_relative "../served_model"
|
|
28
30
|
require_relative "../project_scope"
|
|
@@ -476,6 +478,7 @@ module Samagotchi
|
|
|
476
478
|
summary = SamplingSettings.summary(target, names: names, models: settings)
|
|
477
479
|
model[:sampling] = summary if summary
|
|
478
480
|
add_llm_context(model, target, names, settings)
|
|
481
|
+
add_thinking(model, target, names, settings)
|
|
479
482
|
end
|
|
480
483
|
rescue StandardError
|
|
481
484
|
nil
|
|
@@ -489,6 +492,14 @@ module Samagotchi
|
|
|
489
492
|
nil
|
|
490
493
|
end
|
|
491
494
|
|
|
495
|
+
# The thinking level +model+ starts at, with where it came from (the
|
|
496
|
+
# start page's think chip; Thinking::Explained#summary, no session).
|
|
497
|
+
def add_thinking(model, target, names, settings)
|
|
498
|
+
model[:thinking] = Thinking.explain(target, names: names, models: settings).summary
|
|
499
|
+
rescue StandardError
|
|
500
|
+
nil
|
|
501
|
+
end
|
|
502
|
+
|
|
492
503
|
# The served model a session's last generation reported (analytics.json,
|
|
493
504
|
# SessionMetrics#persist), for a session no worker runs: shown only
|
|
494
505
|
# while the name it answered is still the session's model, as
|
|
@@ -525,6 +536,19 @@ module Samagotchi
|
|
|
525
536
|
LLMContextStrategy.for_session(session, registry: host_registry)
|
|
526
537
|
end
|
|
527
538
|
|
|
539
|
+
# A session's thinking level with where it came from and its own
|
|
540
|
+
# (Thinking::Explained#summary), as its worker would resolve it; nil
|
|
541
|
+
# when it can't be. Config and the registry only: no network.
|
|
542
|
+
def thinking_for(session)
|
|
543
|
+
registry = host_registry
|
|
544
|
+
name = session.model_name.to_s.strip.empty? ? ModelProfile.required_model_name(nil) : session.model_name
|
|
545
|
+
target = registry.resolve(name)
|
|
546
|
+
names = registry.lookup_names(session.model_typed || name, resolved: name, target: target)
|
|
547
|
+
Thinking.explain(target, names: names, session: session.thinking).summary
|
|
548
|
+
rescue StandardError
|
|
549
|
+
nil
|
|
550
|
+
end
|
|
551
|
+
|
|
528
552
|
# Built again when config.yml's hosts: changed (compared as read: the
|
|
529
553
|
# YAML read is mtime-cached), so the model picker lists a host added
|
|
530
554
|
# while chi web runs. An injected registry (specs) stays.
|
|
@@ -607,6 +631,11 @@ module Samagotchi
|
|
|
607
631
|
llm_context, error = request_llm_context(body["llm_context"])
|
|
608
632
|
return error if error
|
|
609
633
|
|
|
634
|
+
# thinking: the session's own thinking level (the start page's think
|
|
635
|
+
# chip; chi --thinking), checked the same way.
|
|
636
|
+
thinking, error = request_thinking(body["thinking"])
|
|
637
|
+
return error if error
|
|
638
|
+
|
|
610
639
|
folder = dir ? { working_directory: dir } : {}
|
|
611
640
|
# model: the model the session starts on (the start page's picker,
|
|
612
641
|
# spelled as GET /api/models lists it); blank means the default.
|
|
@@ -617,7 +646,8 @@ module Samagotchi
|
|
|
617
646
|
# until that turn is saved.
|
|
618
647
|
preview = idle ? body["preview"].to_s.strip : ""
|
|
619
648
|
folder[:title] = preview unless preview.empty?
|
|
620
|
-
|
|
649
|
+
setup = SessionSetup.new(llm_context: llm_context, thinking: thinking)
|
|
650
|
+
folder[:setup] = setup unless setup.empty?
|
|
621
651
|
begin
|
|
622
652
|
session = @manager.spawn_session(prompt: idle ? nil : prompt.to_s, state_dir: @state_dir, **folder)
|
|
623
653
|
rescue ModelProfile::MissingModel => e
|
|
@@ -635,8 +665,8 @@ module Samagotchi
|
|
|
635
665
|
|
|
636
666
|
# POST /api/sessions with continues: <id, prefix or last:id>: the next
|
|
637
667
|
# link of that session's chain (SessionManager.continue_session), in its
|
|
638
|
-
# folder, on its model and with its llm_context
|
|
639
|
-
# llm_context are refused beside it. 409 continued (with next_id, the
|
|
668
|
+
# folder, on its model and with its llm_context and thinking level, so
|
|
669
|
+
# dir, model, llm_context and thinking are refused beside it. 409 continued (with next_id, the
|
|
640
670
|
# link that continues it already: the page opens that one),
|
|
641
671
|
# open_children (ids), folder_gone, busy, scratch or owned_by_tui.
|
|
642
672
|
def handle_continue(body, prompt, idle)
|
|
@@ -644,9 +674,9 @@ module Samagotchi
|
|
|
644
674
|
unless given.is_a?(String) && !given.strip.empty?
|
|
645
675
|
return error_response(400, "invalid_continues", "continues is a session id")
|
|
646
676
|
end
|
|
647
|
-
if (fields = %w[dir model model_name llm_context].select { |key| body.key?(key) }).any?
|
|
677
|
+
if (fields = %w[dir model model_name llm_context thinking].select { |key| body.key?(key) }).any?
|
|
648
678
|
return error_response(400, "invalid_continues",
|
|
649
|
-
"continues takes the previous session's folder, model and
|
|
679
|
+
"continues takes the previous session's folder, model, llm_context and thinking; " \
|
|
650
680
|
"leave out #{fields.join(", ")}")
|
|
651
681
|
end
|
|
652
682
|
|
|
@@ -701,6 +731,20 @@ module Samagotchi
|
|
|
701
731
|
[nil, error_response(400, "invalid_llm_context", e.message)]
|
|
702
732
|
end
|
|
703
733
|
|
|
734
|
+
# The create request's "thinking" (a level word as /thinking takes it,
|
|
735
|
+
# "default" for none) as the session's own level: [level, nil] (nil
|
|
736
|
+
# when absent or default), or [nil, a 400 response] for anything else.
|
|
737
|
+
def request_thinking(raw)
|
|
738
|
+
return [nil, nil] if raw.nil?
|
|
739
|
+
|
|
740
|
+
word = raw.is_a?(String) ? raw.strip.downcase : nil
|
|
741
|
+
unless word && Thinking::LEVELS.map(&:to_s).include?(word)
|
|
742
|
+
return [nil, error_response(400, "invalid_thinking", "thinking is one of #{Thinking::LEVELS.join(", ")}")]
|
|
743
|
+
end
|
|
744
|
+
|
|
745
|
+
[Thinking.session_level(word), nil]
|
|
746
|
+
end
|
|
747
|
+
|
|
704
748
|
def handle_show(req, id)
|
|
705
749
|
# The page's light re-reads: never the whole conversation.
|
|
706
750
|
return handle_cards_read(id) if req.params["cards"] == "1"
|
|
@@ -750,6 +794,8 @@ module Samagotchi
|
|
|
750
794
|
# The LLM context strategy the next turn runs under (the info bar's
|
|
751
795
|
# chip): the worker's, else worked out here from the session file.
|
|
752
796
|
session_json = session_json.merge(llm_context: (snapshot && snapshot["llm_context"]) || llm_context_for(session))
|
|
797
|
+
# The thinking level the next turn runs at (the think chip), the same way.
|
|
798
|
+
session_json = session_json.merge(thinking: (snapshot && snapshot["thinking"]) || thinking_for(session))
|
|
753
799
|
raw_messages = turn_snapshot ? turn_snapshot["messages"] : session.messages
|
|
754
800
|
timing = timing_payload(id, live_metrics: snapshot && snapshot["metrics"], persisted: persisted)
|
|
755
801
|
json_response(200, {
|
|
@@ -1065,8 +1111,14 @@ module Samagotchi
|
|
|
1065
1111
|
images = turn_images(id, body["images"])
|
|
1066
1112
|
return error_response(400, "bad_images", images) if images.is_a?(String)
|
|
1067
1113
|
|
|
1068
|
-
|
|
1069
|
-
|
|
1114
|
+
delivery = body["delivery"] || body[:delivery]
|
|
1115
|
+
if (refused = Delivery.refusal(delivery))
|
|
1116
|
+
return error_response(400, refused[:error], refused[:detail])
|
|
1117
|
+
end
|
|
1118
|
+
|
|
1119
|
+
result = SessionManager.deliver_turn(id, prompt: prompt.to_s, client_id: client_id, images: images,
|
|
1120
|
+
delivery: delivery, state_dir: @state_dir, manager: @manager,
|
|
1121
|
+
bridge: -> { live_bridge_client(id) })
|
|
1070
1122
|
case result[:status]
|
|
1071
1123
|
when :accepted
|
|
1072
1124
|
record_history(body, prompt)
|
|
@@ -233,6 +233,9 @@ module Samagotchi
|
|
|
233
233
|
part = { tool: name, params: params.to_s }
|
|
234
234
|
title = ToolActivity.tool_title(name, call, cwd: cwd)
|
|
235
235
|
part[:title] = title if title
|
|
236
|
+
# The name the model called it by when that was an alias's (the
|
|
237
|
+
# saved call is rebuilt by BuiltinCalls, which sets it again).
|
|
238
|
+
part[:called_as] = call[:called_as] if call[:called_as]
|
|
236
239
|
# Built from the saved call (never saved itself), so older sessions
|
|
237
240
|
# get it too.
|
|
238
241
|
view = ToolView.for(name, call)&.to_h
|
|
@@ -53,6 +53,7 @@ export function addStarted(model, event = {}, now = null) {
|
|
|
53
53
|
row.label = event.label || row.label;
|
|
54
54
|
row.params = event.params || row.params;
|
|
55
55
|
row.title = event.title || row.title;
|
|
56
|
+
row.called_as = event.called_as || row.called_as;
|
|
56
57
|
row.view = event.view || row.view;
|
|
57
58
|
markStart(row, event, now);
|
|
58
59
|
return { row, created: false };
|
|
@@ -68,6 +69,9 @@ export function addStarted(model, event = {}, now = null) {
|
|
|
68
69
|
// project-relative path, a command without its "cd … &&"); rows show it
|
|
69
70
|
// in place of the params when there is one.
|
|
70
71
|
title: event.title || null,
|
|
72
|
+
// The name the model called the tool by when it was an alias's ("bash"
|
|
73
|
+
// run as execute); the row says so.
|
|
74
|
+
called_as: event.called_as || null,
|
|
71
75
|
// The call's full arguments for the expanded row and the hovers (the
|
|
72
76
|
// server's ToolView: an execute/task_create's command, its cwd).
|
|
73
77
|
view: event.view || null,
|
|
@@ -104,6 +108,7 @@ export function addCompleted(model, event = {}, now = null) {
|
|
|
104
108
|
label: event.label,
|
|
105
109
|
params: event.activity?.params || "",
|
|
106
110
|
title: event.activity?.title,
|
|
111
|
+
called_as: event.activity?.called_as,
|
|
107
112
|
view: event.view,
|
|
108
113
|
});
|
|
109
114
|
row = synthesized.row;
|
|
@@ -113,6 +118,7 @@ export function addCompleted(model, event = {}, now = null) {
|
|
|
113
118
|
row.output_truncated = !!event.output_truncated;
|
|
114
119
|
if (event.activity?.params) row.params = event.activity.params;
|
|
115
120
|
if (event.activity?.title) row.title = event.activity.title;
|
|
121
|
+
if (event.activity?.called_as) row.called_as = event.activity.called_as;
|
|
116
122
|
// The images the tool returned (a reload in the turn view draws them from
|
|
117
123
|
// the saved parts, turn_html.js toolRowHtml).
|
|
118
124
|
if (event.images?.length) row.images = event.images;
|