terret-morph 0.1.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 +7 -0
- data/lib/terret/morph/summarizer.rb +114 -0
- data/lib/terret/morph.rb +9 -0
- metadata +62 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 6dc795139de48f9e15f00647df387d36f94aba7592661b3a070209ac6a9847e2
|
|
4
|
+
data.tar.gz: dfa94e68912825fb939d5843dce02fe305ec04aac71afc24a5df869c74bdafb3
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 1bd98fc5d45df8d52985553ad15b62c0ae01879184755f5f0af6553f160b93f640c6fc2ab4fb2a43d3b73de1f0fcac36f5150e96bdfff6349ff81de1c8f32433
|
|
7
|
+
data.tar.gz: d93c9e77f8a0bf16e511c7679875d3d939c6657c83bfe7840df30d7734ea00d2fc20fd56354ef9818f5eb4f88204a1ca51bf293ea4797e6da9733871ad2ea3de
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require "net/http"
|
|
5
|
+
require "uri"
|
|
6
|
+
|
|
7
|
+
module Terret
|
|
8
|
+
module Morph
|
|
9
|
+
# ctx[:summarizer] backed by Morph's Compact API (POST /v1/compact):
|
|
10
|
+
# extractive line-level compression, surviving lines byte-identical. Wire
|
|
11
|
+
# shape mirrors the deployed agora integration (Morph::CompactClient).
|
|
12
|
+
# Every failure declines to nil with a warn — compaction is an
|
|
13
|
+
# optimization, and the Compactor skips the boundary when declined. All
|
|
14
|
+
# knobs are read per call, so reconfigure is live by construction.
|
|
15
|
+
class Summarizer < Hames::Service
|
|
16
|
+
service_key :summarizer
|
|
17
|
+
# transport: is an injectable seam (tests pass a callable), not YAML config.
|
|
18
|
+
config_schema compression_ratio: { type: Numeric, default: 0.4,
|
|
19
|
+
doc: "target fraction of the original token count" },
|
|
20
|
+
api_key: { type: String,
|
|
21
|
+
doc: "Morph key; falls back to ENV MORPH_API_KEY when unset" },
|
|
22
|
+
api_base: { type: String, default: "https://api.morphllm.com/v1",
|
|
23
|
+
doc: "Morph Compact API base URL" },
|
|
24
|
+
timeout: { type: Numeric, default: 30.0,
|
|
25
|
+
doc: "seconds a compaction request may run" }
|
|
26
|
+
|
|
27
|
+
DEFAULT_BASE = "https://api.morphllm.com/v1"
|
|
28
|
+
DEFAULT_TIMEOUT = 30.0
|
|
29
|
+
DEFAULT_RATIO = 0.4
|
|
30
|
+
|
|
31
|
+
def start(_ctx); end
|
|
32
|
+
|
|
33
|
+
def reconfigure(_config); end # knobs are read per call
|
|
34
|
+
|
|
35
|
+
def summarize(history)
|
|
36
|
+
key = api_key
|
|
37
|
+
return decline("MORPH_API_KEY not configured") if key.nil? || key.empty?
|
|
38
|
+
|
|
39
|
+
body = JSON.generate({ input: render(history),
|
|
40
|
+
compression_ratio: config[:compression_ratio] || DEFAULT_RATIO,
|
|
41
|
+
preserve_recent: 0 })
|
|
42
|
+
status, response = transport.call("#{api_base}/compact",
|
|
43
|
+
{ "Authorization" => "Bearer #{key}",
|
|
44
|
+
"Content-Type" => "application/json" },
|
|
45
|
+
body)
|
|
46
|
+
return decline("HTTP #{status}") unless (200..299).cover?(status)
|
|
47
|
+
|
|
48
|
+
parsed = JSON.parse(response.to_s)
|
|
49
|
+
return decline("unexpected response shape: #{parsed.class}") unless parsed.is_a?(Hash)
|
|
50
|
+
|
|
51
|
+
output = parsed["output"]
|
|
52
|
+
return decline("non-string output: #{output.class}") unless output.is_a?(String)
|
|
53
|
+
|
|
54
|
+
output.empty? ? decline("empty output") : output
|
|
55
|
+
rescue JSON::ParserError => e
|
|
56
|
+
decline("invalid JSON: #{e.message}")
|
|
57
|
+
rescue StandardError => e
|
|
58
|
+
decline("#{e.class}: #{e.message}")
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
private
|
|
62
|
+
|
|
63
|
+
def api_key = config[:api_key] || ENV["MORPH_API_KEY"]
|
|
64
|
+
def api_base = config[:api_base] || ENV["MORPH_API_BASE"] || DEFAULT_BASE
|
|
65
|
+
|
|
66
|
+
# timeout=0 must not mean "no timeout" (the agora/Faraday lesson):
|
|
67
|
+
# floor anything non-positive back to the default.
|
|
68
|
+
def timeout
|
|
69
|
+
configured = (config[:timeout] || ENV["MORPH_COMPACT_TIMEOUT"]).to_f
|
|
70
|
+
configured.positive? ? configured : DEFAULT_TIMEOUT
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# The transcript Morph compresses: role-tagged lines, one per message
|
|
74
|
+
# part so line-level compression can keep or drop each on its merit.
|
|
75
|
+
# Extractive compression keeps surviving lines byte-identical, so the
|
|
76
|
+
# compacted history the model sees is a strict subset of what it already
|
|
77
|
+
# saw. Tool calls and results carry their own line rather than rendering
|
|
78
|
+
# blank: a session's deploy ids, arguments, and errors live there, and a
|
|
79
|
+
# summary that dropped them would be worse than no summary at all.
|
|
80
|
+
def render(history)
|
|
81
|
+
history.flat_map { |m| m.parts.map { |part| "#{m.role}: #{line_for(part)}" } }.join("\n")
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def line_for(part)
|
|
85
|
+
case part
|
|
86
|
+
when LLM::Text then part.text
|
|
87
|
+
when LLM::ToolCall then "[tool_call #{part.name} #{JSON.generate(part.args)}]"
|
|
88
|
+
when LLM::ToolResult then "[tool_result #{part.error || part.content}]"
|
|
89
|
+
else part.to_s
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def transport
|
|
94
|
+
config[:transport] || method(:http_post)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def http_post(url, headers, body)
|
|
98
|
+
uri = URI(url)
|
|
99
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
100
|
+
http.use_ssl = uri.scheme == "https"
|
|
101
|
+
http.open_timeout = timeout
|
|
102
|
+
http.read_timeout = timeout
|
|
103
|
+
http.write_timeout = timeout
|
|
104
|
+
response = http.post(uri.request_uri, body, headers)
|
|
105
|
+
[response.code.to_i, response.body]
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def decline(message)
|
|
109
|
+
warn "terret-morph: compact declined: #{message}"
|
|
110
|
+
nil
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
data/lib/terret/morph.rb
ADDED
metadata
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: terret-morph
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Obie Fernandez
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: terret-core
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '0.1'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '0.1'
|
|
26
|
+
description: 'ctx[:summarizer] backed by Morph''s Compact API: extractive line-level
|
|
27
|
+
compression (surviving lines byte-identical), wire mirrored from the deployed agora
|
|
28
|
+
integration. Zero runtime dependencies beyond stdlib net/http.'
|
|
29
|
+
email:
|
|
30
|
+
- obiefernandez@gmail.com
|
|
31
|
+
executables: []
|
|
32
|
+
extensions: []
|
|
33
|
+
extra_rdoc_files: []
|
|
34
|
+
files:
|
|
35
|
+
- lib/terret/morph.rb
|
|
36
|
+
- lib/terret/morph/summarizer.rb
|
|
37
|
+
homepage: https://terret.org
|
|
38
|
+
licenses:
|
|
39
|
+
- MIT
|
|
40
|
+
metadata:
|
|
41
|
+
homepage_uri: https://terret.org
|
|
42
|
+
source_code_uri: https://github.com/terret-org/terret
|
|
43
|
+
bug_tracker_uri: https://github.com/terret-org/terret/issues
|
|
44
|
+
rubygems_mfa_required: 'true'
|
|
45
|
+
rdoc_options: []
|
|
46
|
+
require_paths:
|
|
47
|
+
- lib
|
|
48
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
49
|
+
requirements:
|
|
50
|
+
- - ">="
|
|
51
|
+
- !ruby/object:Gem::Version
|
|
52
|
+
version: '4.0'
|
|
53
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
54
|
+
requirements:
|
|
55
|
+
- - ">="
|
|
56
|
+
- !ruby/object:Gem::Version
|
|
57
|
+
version: '0'
|
|
58
|
+
requirements: []
|
|
59
|
+
rubygems_version: 4.0.16
|
|
60
|
+
specification_version: 4
|
|
61
|
+
summary: Morph-backed context compaction for Terret
|
|
62
|
+
test_files: []
|