Class: Terret::Sessions
- Inherits:
-
Hames::Service
- Object
- Hames::Service
- Terret::Sessions
- Defined in:
- lib/terret/sessions.rb
Overview
ctx.sessions — the append-only session log. The single source of the context the model sees: derive_messages projects model history from it, and a digest invariant asserts that what goes out to an adapter is exactly what replaying the log yields ("model-visible means logged").
Defined Under Namespace
Classes: Session
Instance Method Summary collapse
- #append(session_id, type, payload = {}) ⇒ Object
-
#assert_log_invariant!(session_id, outbound_messages) ⇒ Object
The enforcement point for "model-visible means logged": the loop calls this with the message list it is about to send; a mismatch against the log projection raises in dev/test.
- #create(id: SecureRandom.hex(6), parent_id: nil) ⇒ Object
-
#derive_messages(session_id, upto: nil) ⇒ Object
Project provider-neutral model history from the durable log.
- #fetch(id) ⇒ Object
- #fork(source_id, boundary: nil, child_id: SecureRandom.hex(6)) ⇒ Object
- #start(ctx) ⇒ Object
Instance Method Details
#append(session_id, type, payload = {}) ⇒ Object
36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
# File 'lib/terret/sessions.rb', line 36 def append(session_id, type, payload = {}) decl = Hames.event(type) raise Hames::ContractError, "#{type} is not a durable event" unless decl.durable s = fetch(session_id) ev = SessionEvent.new( id: SecureRandom.hex(8), session_id:, seq: s.events.length, at: Time.now.utc, type: type.to_s, payload: payload ) s.events << ev persist(ev) @ctx.emit("session/event", ev) ev end |
#assert_log_invariant!(session_id, outbound_messages) ⇒ Object
The enforcement point for "model-visible means logged": the loop calls this with the message list it is about to send; a mismatch against the log projection raises in dev/test.
73 74 75 76 77 78 79 |
# File 'lib/terret/sessions.rb', line 73 def assert_log_invariant!(session_id, ) derived = (session_id) return if digest(derived) == digest() raise LogInvariantViolation, "outbound request diverges from session log projection for #{session_id}" end |
#create(id: SecureRandom.hex(6), parent_id: nil) ⇒ Object
27 28 29 30 31 32 |
# File 'lib/terret/sessions.rb', line 27 def create(id: SecureRandom.hex(6), parent_id: nil) s = Session.new(id:, events: [], parent_id:) @store[id] = s append(id, "session/created", { parent_id: }) s end |
#derive_messages(session_id, upto: nil) ⇒ Object
Project provider-neutral model history from the durable log.
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
# File 'lib/terret/sessions.rb', line 52 def (session_id, upto: nil) events = fetch(session_id).events events = events.take(upto) if upto events.filter_map do |ev| case ev.type when "user/message", "context/injected" LLM::Message.new(role: :user, parts: [LLM::Text.new(text: ev.payload[:text])]) when "assistant/message" LLM::Message.new(role: :assistant, parts: ev.payload[:parts]) when "tool/result" LLM::Message.new(role: :tool, parts: [ LLM::ToolResult.new(id: ev.payload[:id], content: ev.payload[:content], error: ev.payload[:error]) ]) end end end |
#fetch(id) ⇒ Object
34 |
# File 'lib/terret/sessions.rb', line 34 def fetch(id) = @store.fetch(id) |
#fork(source_id, boundary: nil, child_id: SecureRandom.hex(6)) ⇒ Object
81 82 83 84 85 86 87 88 89 |
# File 'lib/terret/sessions.rb', line 81 def fork(source_id, boundary: nil, child_id: SecureRandom.hex(6)) src = fetch(source_id) child = Session.new(id: child_id, events: [], parent_id: source_id) @store[child_id] = child events = boundary ? src.events.take(boundary) : src.events.dup events.each { |ev| child.events << ev.with(session_id: child_id) } append(child_id, "session/forked", { from: source_id, boundary: boundary }) child end |
#start(ctx) ⇒ Object
21 22 23 24 25 |
# File 'lib/terret/sessions.rb', line 21 def start(ctx) @ctx = ctx @store = {} @dir = config[:jsonl_dir] # optional persistence end |