hindsight-ruby 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/LICENSE.txt +21 -0
- data/README.md +325 -0
- data/hindsight-ruby.gemspec +37 -0
- data/lib/hindsight/bank.rb +204 -0
- data/lib/hindsight/client.rb +296 -0
- data/lib/hindsight/errors.rb +36 -0
- data/lib/hindsight/option_validation.rb +255 -0
- data/lib/hindsight/resources/banks.rb +129 -0
- data/lib/hindsight/resources/base.rb +118 -0
- data/lib/hindsight/resources/chunks.rb +14 -0
- data/lib/hindsight/resources/config.rb +21 -0
- data/lib/hindsight/resources/directives.rb +58 -0
- data/lib/hindsight/resources/documents.rb +30 -0
- data/lib/hindsight/resources/entities.rb +27 -0
- data/lib/hindsight/resources/graph.rb +21 -0
- data/lib/hindsight/resources/memories.rb +30 -0
- data/lib/hindsight/resources/mental_models.rb +65 -0
- data/lib/hindsight/resources/observations.rb +16 -0
- data/lib/hindsight/resources/operations.rb +92 -0
- data/lib/hindsight/resources/tags.rb +18 -0
- data/lib/hindsight/types/fact.rb +76 -0
- data/lib/hindsight/types/operation_receipt.rb +63 -0
- data/lib/hindsight/types/operation_status.rb +57 -0
- data/lib/hindsight/types/payload.rb +33 -0
- data/lib/hindsight/types/recall_result.rb +63 -0
- data/lib/hindsight/types/reflection.rb +49 -0
- data/lib/hindsight/upload_normalizer.rb +142 -0
- data/lib/hindsight/version.rb +5 -0
- data/lib/hindsight-ruby.rb +3 -0
- data/lib/hindsight.rb +30 -0
- metadata +141 -0
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'base'
|
|
4
|
+
|
|
5
|
+
module Hindsight
|
|
6
|
+
module Resources
|
|
7
|
+
class Banks < Base
|
|
8
|
+
BANK_PAYLOAD_KEYS = %i[
|
|
9
|
+
name
|
|
10
|
+
mission
|
|
11
|
+
reflect_mission
|
|
12
|
+
disposition
|
|
13
|
+
disposition_skepticism
|
|
14
|
+
disposition_literalism
|
|
15
|
+
disposition_empathy
|
|
16
|
+
skepticism
|
|
17
|
+
literalism
|
|
18
|
+
empathy
|
|
19
|
+
enable_observations
|
|
20
|
+
observations_mission
|
|
21
|
+
retain_mission
|
|
22
|
+
retain_extraction_mode
|
|
23
|
+
retain_custom_instructions
|
|
24
|
+
retain_chunk_size
|
|
25
|
+
].freeze
|
|
26
|
+
|
|
27
|
+
endpoint :list, method: :get, path: '' do |params|
|
|
28
|
+
{
|
|
29
|
+
query: {
|
|
30
|
+
**extract_pagination!(params)
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def get(bank_id)
|
|
36
|
+
id = normalize_required_id(bank_id, key: :bank_id)
|
|
37
|
+
request_json(:get, '/%{bank_id}/profile', path_params: { bank_id: id })
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def stats(bank_id)
|
|
41
|
+
id = normalize_required_id(bank_id, key: :bank_id)
|
|
42
|
+
request_json(:get, '/%{bank_id}/stats', path_params: { bank_id: id })
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
endpoint :create, method: :put, path: '/%{bank_id}' do |params|
|
|
46
|
+
bank_id = normalize_required_id(params.delete(:bank_id), key: :bank_id)
|
|
47
|
+
body = normalize_bank_payload(extract_bank_payload!(params))
|
|
48
|
+
{ path_params: { bank_id: bank_id }, body: body }
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
endpoint :update, method: :patch, path: '/%{bank_id}' do |params|
|
|
52
|
+
bank_id = normalize_required_id(params.delete(:bank_id), key: :bank_id)
|
|
53
|
+
body = normalize_bank_payload(extract_bank_payload!(params))
|
|
54
|
+
{ path_params: { bank_id: bank_id }, body: body }
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def delete(bank_id)
|
|
58
|
+
id = normalize_required_id(bank_id, key: :bank_id)
|
|
59
|
+
request_json(:delete, '/%{bank_id}', path_params: { bank_id: id })
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
private
|
|
63
|
+
|
|
64
|
+
def extract_bank_payload!(params)
|
|
65
|
+
BANK_PAYLOAD_KEYS.each_with_object({}) do |key, attributes|
|
|
66
|
+
next unless params.key?(key)
|
|
67
|
+
|
|
68
|
+
attributes[key] = params.delete(key)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def normalize_bank_payload(attributes)
|
|
73
|
+
payload = Types::Payload.stringify_keys(attributes || {})
|
|
74
|
+
normalize_disposition!(payload)
|
|
75
|
+
normalize_disposition_traits!(payload)
|
|
76
|
+
normalize_observations_flag!(payload)
|
|
77
|
+
normalize_extraction_mode!(payload)
|
|
78
|
+
normalize_chunk_size!(payload)
|
|
79
|
+
|
|
80
|
+
payload.compact
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def normalize_disposition!(payload)
|
|
84
|
+
return unless payload.key?('disposition')
|
|
85
|
+
|
|
86
|
+
payload['disposition'] = OptionValidation.normalize_disposition(payload['disposition'], key: :disposition)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def normalize_disposition_traits!(payload)
|
|
90
|
+
%w[skepticism literalism empathy].each do |trait|
|
|
91
|
+
api_key = "disposition_#{trait}"
|
|
92
|
+
if payload.key?(trait) && payload.key?(api_key)
|
|
93
|
+
raise ValidationError,
|
|
94
|
+
"Conflicting disposition keys: provide either #{trait} or #{api_key}, not both"
|
|
95
|
+
end
|
|
96
|
+
payload[api_key] = payload.delete(trait) if payload.key?(trait) && !payload.key?(api_key)
|
|
97
|
+
payload.delete(trait)
|
|
98
|
+
next unless payload.key?(api_key) && !payload[api_key].nil?
|
|
99
|
+
|
|
100
|
+
payload[api_key] = OptionValidation.normalize_integer_in_range(
|
|
101
|
+
payload[api_key], key: api_key.to_sym, minimum: 1, maximum: 5
|
|
102
|
+
)
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def normalize_observations_flag!(payload)
|
|
107
|
+
return unless payload.key?('enable_observations')
|
|
108
|
+
|
|
109
|
+
payload['enable_observations'] =
|
|
110
|
+
OptionValidation.normalize_boolean(payload['enable_observations'], key: :enable_observations)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def normalize_extraction_mode!(payload)
|
|
114
|
+
return unless payload.key?('retain_extraction_mode') && !payload['retain_extraction_mode'].nil?
|
|
115
|
+
|
|
116
|
+
payload['retain_extraction_mode'] =
|
|
117
|
+
OptionValidation.normalize_extraction_mode(payload['retain_extraction_mode']).to_s
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def normalize_chunk_size!(payload)
|
|
121
|
+
return unless payload.key?('retain_chunk_size') && !payload['retain_chunk_size'].nil?
|
|
122
|
+
|
|
123
|
+
payload['retain_chunk_size'] =
|
|
124
|
+
OptionValidation.normalize_positive_integer(payload['retain_chunk_size'], key: :retain_chunk_size)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../errors'
|
|
4
|
+
require_relative '../option_validation'
|
|
5
|
+
|
|
6
|
+
module Hindsight
|
|
7
|
+
module Resources
|
|
8
|
+
class Base
|
|
9
|
+
def self.endpoint(name, method:, path:, &builder)
|
|
10
|
+
path_keys = path.scan(/%\{([a-zA-Z0-9_]+)\}/).flatten.map(&:to_sym).uniq.freeze
|
|
11
|
+
|
|
12
|
+
define_method(name) do |*args, **kwargs|
|
|
13
|
+
params = normalize_endpoint_params(name, args, kwargs, path_keys)
|
|
14
|
+
specification = builder ? instance_exec(params, &builder) : {}
|
|
15
|
+
specification = {} if specification.nil?
|
|
16
|
+
unless specification.is_a?(Hash)
|
|
17
|
+
raise ValidationError, "Invalid endpoint definition for #{self.class}##{name}: expected Hash"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
ensure_no_unknown_kwargs!(params)
|
|
21
|
+
|
|
22
|
+
request_json(
|
|
23
|
+
method,
|
|
24
|
+
path,
|
|
25
|
+
path_params: specification[:path_params] || {},
|
|
26
|
+
query: specification[:query] || {},
|
|
27
|
+
body: specification[:body]
|
|
28
|
+
)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
attr_reader :client, :base_path
|
|
33
|
+
|
|
34
|
+
def initialize(client:, base_path:)
|
|
35
|
+
@client = client
|
|
36
|
+
@base_path = base_path
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
def request_json(method, path_template, path_params: nil, query: nil, body: nil)
|
|
42
|
+
path = interpolate_path(path_template, path_params || {})
|
|
43
|
+
normalized_query = normalize_query_hash(query || {})
|
|
44
|
+
client.request_json(method, "#{base_path}#{path}", body, query: normalized_query)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def interpolate_path(template, values)
|
|
48
|
+
template.gsub(/%\{([a-zA-Z0-9_]+)\}/) do
|
|
49
|
+
key = Regexp.last_match(1).to_sym
|
|
50
|
+
raise ValidationError, "Missing required path parameter: #{key}" unless values.key?(key)
|
|
51
|
+
|
|
52
|
+
client.escape(values.fetch(key))
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def normalize_query_hash(params)
|
|
57
|
+
params.each_with_object({}) do |(key, value), normalized|
|
|
58
|
+
next if value.nil?
|
|
59
|
+
|
|
60
|
+
normalized[key.to_s] = normalize_query_value(value)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def normalize_query_value(value)
|
|
65
|
+
if value.is_a?(Array)
|
|
66
|
+
value.compact.map(&:to_s)
|
|
67
|
+
else
|
|
68
|
+
value.to_s
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def ensure_no_unknown_kwargs!(kwargs)
|
|
73
|
+
return if kwargs.empty?
|
|
74
|
+
|
|
75
|
+
raise ValidationError, "Unknown keyword arguments: #{kwargs.keys.join(', ')}"
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def normalize_endpoint_params(name, args, kwargs, path_keys)
|
|
79
|
+
raise ValidationError, "Invalid arguments for #{self.class}##{name}: expected at most one positional argument" if args.length > 1
|
|
80
|
+
return kwargs.dup if args.empty?
|
|
81
|
+
unless kwargs.empty?
|
|
82
|
+
raise ValidationError,
|
|
83
|
+
"Invalid arguments for #{self.class}##{name}: cannot mix positional and keyword arguments"
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
arg = args.first
|
|
87
|
+
if arg.is_a?(Hash)
|
|
88
|
+
return arg.each_with_object({}) do |(key, value), params|
|
|
89
|
+
params[key.to_sym] = value
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
return { path_keys.first => arg } if path_keys.length == 1
|
|
94
|
+
|
|
95
|
+
raise ValidationError, "Invalid arguments for #{self.class}##{name}: expected keyword arguments"
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def normalize_required_id(value, key:)
|
|
99
|
+
OptionValidation.normalize_non_empty_string(value, key: key)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def normalize_fact_type_filter(value)
|
|
103
|
+
OptionValidation.normalize_fact_type(value, key: :type)&.to_s
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def normalize_tags_match(value)
|
|
107
|
+
OptionValidation.normalize_tags_match(value, key: :tags_match)&.to_s
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def extract_pagination!(params)
|
|
111
|
+
{
|
|
112
|
+
limit: OptionValidation.normalize_optional_positive_integer(params.delete(:limit), key: :limit),
|
|
113
|
+
offset: OptionValidation.normalize_optional_non_negative_integer(params.delete(:offset), key: :offset)
|
|
114
|
+
}
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
|
|
5
|
+
module Hindsight
|
|
6
|
+
module Resources
|
|
7
|
+
class Chunks < Base
|
|
8
|
+
def get(chunk_id)
|
|
9
|
+
id = normalize_required_id(chunk_id, key: :chunk_id)
|
|
10
|
+
request_json(:get, '/chunks/%{chunk_id}', path_params: { chunk_id: id })
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'base'
|
|
4
|
+
|
|
5
|
+
module Hindsight
|
|
6
|
+
module Resources
|
|
7
|
+
class Config < Base
|
|
8
|
+
endpoint :get, method: :get, path: '/config'
|
|
9
|
+
|
|
10
|
+
endpoint :patch, method: :patch, path: '/config' do |params|
|
|
11
|
+
{
|
|
12
|
+
body: {
|
|
13
|
+
updates: OptionValidation.normalize_required_hash(params.delete(:updates), key: :updates)
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
endpoint :reset, method: :delete, path: '/config'
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'base'
|
|
4
|
+
|
|
5
|
+
module Hindsight
|
|
6
|
+
module Resources
|
|
7
|
+
class Directives < Base
|
|
8
|
+
endpoint :list, method: :get, path: '/directives' do |params|
|
|
9
|
+
active_only = params.delete(:active_only)
|
|
10
|
+
{
|
|
11
|
+
query: {
|
|
12
|
+
tags: OptionValidation.normalize_tags(params.delete(:tags)),
|
|
13
|
+
tags_match: normalize_tags_match(params.delete(:tags_match)),
|
|
14
|
+
active_only: OptionValidation.normalize_optional_boolean(active_only, key: :active_only),
|
|
15
|
+
**extract_pagination!(params)
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
endpoint :create, method: :post, path: '/directives' do |params|
|
|
21
|
+
active = params.delete(:is_active)
|
|
22
|
+
{
|
|
23
|
+
body: {
|
|
24
|
+
name: OptionValidation.normalize_non_empty_string(params.delete(:name), key: :name),
|
|
25
|
+
content: OptionValidation.normalize_non_empty_string(params.delete(:content), key: :content),
|
|
26
|
+
priority: OptionValidation.normalize_optional_integer(params.delete(:priority), key: :priority),
|
|
27
|
+
is_active: OptionValidation.normalize_optional_boolean(active, key: :is_active),
|
|
28
|
+
tags: OptionValidation.normalize_tags(params.delete(:tags))
|
|
29
|
+
}.compact
|
|
30
|
+
}
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def get(directive_id)
|
|
34
|
+
id = normalize_required_id(directive_id, key: :directive_id)
|
|
35
|
+
request_json(:get, '/directives/%{directive_id}', path_params: { directive_id: id })
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
endpoint :update, method: :patch, path: '/directives/%{directive_id}' do |params|
|
|
39
|
+
active = params.delete(:is_active)
|
|
40
|
+
{
|
|
41
|
+
path_params: { directive_id: normalize_required_id(params.delete(:directive_id), key: :directive_id) },
|
|
42
|
+
body: {
|
|
43
|
+
name: OptionValidation.normalize_optional_non_empty_string(params.delete(:name), key: :name),
|
|
44
|
+
content: OptionValidation.normalize_optional_non_empty_string(params.delete(:content), key: :content),
|
|
45
|
+
priority: OptionValidation.normalize_optional_integer(params.delete(:priority), key: :priority),
|
|
46
|
+
is_active: OptionValidation.normalize_optional_boolean(active, key: :is_active),
|
|
47
|
+
tags: OptionValidation.normalize_tags(params.delete(:tags))
|
|
48
|
+
}.compact
|
|
49
|
+
}
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def delete(directive_id)
|
|
53
|
+
id = normalize_required_id(directive_id, key: :directive_id)
|
|
54
|
+
request_json(:delete, '/directives/%{directive_id}', path_params: { directive_id: id })
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'base'
|
|
4
|
+
|
|
5
|
+
module Hindsight
|
|
6
|
+
module Resources
|
|
7
|
+
class Documents < Base
|
|
8
|
+
endpoint :list, method: :get, path: '/documents' do |params|
|
|
9
|
+
{
|
|
10
|
+
query: {
|
|
11
|
+
q: OptionValidation.normalize_optional_non_empty_string(params.delete(:q), key: :q),
|
|
12
|
+
tags: OptionValidation.normalize_tags(params.delete(:tags)),
|
|
13
|
+
tags_match: normalize_tags_match(params.delete(:tags_match)),
|
|
14
|
+
**extract_pagination!(params)
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def get(document_id)
|
|
20
|
+
id = normalize_required_id(document_id, key: :document_id)
|
|
21
|
+
request_json(:get, '/documents/%{document_id}', path_params: { document_id: id })
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def delete(document_id)
|
|
25
|
+
id = normalize_required_id(document_id, key: :document_id)
|
|
26
|
+
request_json(:delete, '/documents/%{document_id}', path_params: { document_id: id })
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'base'
|
|
4
|
+
|
|
5
|
+
module Hindsight
|
|
6
|
+
module Resources
|
|
7
|
+
class Entities < Base
|
|
8
|
+
endpoint :list, method: :get, path: '/entities' do |params|
|
|
9
|
+
{
|
|
10
|
+
query: {
|
|
11
|
+
**extract_pagination!(params)
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def get(entity_id)
|
|
17
|
+
id = normalize_required_id(entity_id, key: :entity_id)
|
|
18
|
+
request_json(:get, '/entities/%{entity_id}', path_params: { entity_id: id })
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def regenerate(entity_id)
|
|
22
|
+
id = normalize_required_id(entity_id, key: :entity_id)
|
|
23
|
+
request_json(:post, '/entities/%{entity_id}/regenerate', path_params: { entity_id: id })
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'base'
|
|
4
|
+
|
|
5
|
+
module Hindsight
|
|
6
|
+
module Resources
|
|
7
|
+
class Graph < Base
|
|
8
|
+
endpoint :get, method: :get, path: '/graph' do |params|
|
|
9
|
+
{
|
|
10
|
+
query: {
|
|
11
|
+
type: OptionValidation.normalize_optional_non_empty_string(params.delete(:type), key: :type),
|
|
12
|
+
q: OptionValidation.normalize_optional_non_empty_string(params.delete(:q), key: :q),
|
|
13
|
+
tags: OptionValidation.normalize_tags(params.delete(:tags)),
|
|
14
|
+
tags_match: normalize_tags_match(params.delete(:tags_match)),
|
|
15
|
+
limit: OptionValidation.normalize_optional_positive_integer(params.delete(:limit), key: :limit)
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'base'
|
|
4
|
+
|
|
5
|
+
module Hindsight
|
|
6
|
+
module Resources
|
|
7
|
+
class Memories < Base
|
|
8
|
+
endpoint :list, method: :get, path: '/memories/list' do |params|
|
|
9
|
+
{
|
|
10
|
+
query: {
|
|
11
|
+
type: normalize_fact_type_filter(params.delete(:type)),
|
|
12
|
+
q: OptionValidation.normalize_optional_non_empty_string(params.delete(:q), key: :q),
|
|
13
|
+
**extract_pagination!(params)
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def get(memory_id)
|
|
19
|
+
id = normalize_required_id(memory_id, key: :memory_id)
|
|
20
|
+
request_json(:get, '/memories/%{memory_id}', path_params: { memory_id: id })
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
endpoint :delete, method: :delete, path: '/memories' do |params|
|
|
24
|
+
{
|
|
25
|
+
query: { type: normalize_fact_type_filter(params.delete(:type)) }
|
|
26
|
+
}
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'base'
|
|
4
|
+
|
|
5
|
+
module Hindsight
|
|
6
|
+
module Resources
|
|
7
|
+
class MentalModels < Base
|
|
8
|
+
endpoint :list, method: :get, path: '/mental-models' do |params|
|
|
9
|
+
{
|
|
10
|
+
query: {
|
|
11
|
+
tags: OptionValidation.normalize_tags(params.delete(:tags)),
|
|
12
|
+
tags_match: normalize_tags_match(params.delete(:tags_match)),
|
|
13
|
+
**extract_pagination!(params)
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
endpoint :create, method: :post, path: '/mental-models' do |params|
|
|
19
|
+
{
|
|
20
|
+
body: {
|
|
21
|
+
id: OptionValidation.normalize_optional_non_empty_string(params.delete(:id), key: :id),
|
|
22
|
+
name: OptionValidation.normalize_non_empty_string(params.delete(:name), key: :name),
|
|
23
|
+
source_query: OptionValidation.normalize_non_empty_string(params.delete(:source_query), key: :source_query),
|
|
24
|
+
tags: OptionValidation.normalize_tags(params.delete(:tags)),
|
|
25
|
+
max_tokens: OptionValidation.normalize_optional_positive_integer(params.delete(:max_tokens),
|
|
26
|
+
key: :max_tokens),
|
|
27
|
+
trigger: OptionValidation.normalize_hash(params.delete(:trigger), key: :trigger)
|
|
28
|
+
}.compact
|
|
29
|
+
}
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def get(mental_model_id)
|
|
33
|
+
id = normalize_required_id(mental_model_id, key: :mental_model_id)
|
|
34
|
+
request_json(:get, '/mental-models/%{mental_model_id}', path_params: { mental_model_id: id })
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
endpoint :update, method: :patch, path: '/mental-models/%{mental_model_id}' do |params|
|
|
38
|
+
{
|
|
39
|
+
path_params: {
|
|
40
|
+
mental_model_id: normalize_required_id(params.delete(:mental_model_id), key: :mental_model_id)
|
|
41
|
+
},
|
|
42
|
+
body: {
|
|
43
|
+
name: OptionValidation.normalize_optional_non_empty_string(params.delete(:name), key: :name),
|
|
44
|
+
source_query: OptionValidation.normalize_optional_non_empty_string(params.delete(:source_query),
|
|
45
|
+
key: :source_query),
|
|
46
|
+
tags: OptionValidation.normalize_tags(params.delete(:tags)),
|
|
47
|
+
max_tokens: OptionValidation.normalize_optional_positive_integer(params.delete(:max_tokens),
|
|
48
|
+
key: :max_tokens),
|
|
49
|
+
trigger: OptionValidation.normalize_hash(params.delete(:trigger), key: :trigger)
|
|
50
|
+
}.compact
|
|
51
|
+
}
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def delete(mental_model_id)
|
|
55
|
+
id = normalize_required_id(mental_model_id, key: :mental_model_id)
|
|
56
|
+
request_json(:delete, '/mental-models/%{mental_model_id}', path_params: { mental_model_id: id })
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def refresh(mental_model_id)
|
|
60
|
+
id = normalize_required_id(mental_model_id, key: :mental_model_id)
|
|
61
|
+
request_json(:post, '/mental-models/%{mental_model_id}/refresh', path_params: { mental_model_id: id })
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'base'
|
|
4
|
+
|
|
5
|
+
module Hindsight
|
|
6
|
+
module Resources
|
|
7
|
+
class Observations < Base
|
|
8
|
+
endpoint :delete, method: :delete, path: '/observations'
|
|
9
|
+
|
|
10
|
+
def delete_for_memory(memory_id)
|
|
11
|
+
id = normalize_required_id(memory_id, key: :memory_id)
|
|
12
|
+
request_json(:delete, '/memories/%{memory_id}/observations', path_params: { memory_id: id })
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'base'
|
|
4
|
+
require_relative '../types/operation_status'
|
|
5
|
+
|
|
6
|
+
module Hindsight
|
|
7
|
+
module Resources
|
|
8
|
+
class Operations < Base
|
|
9
|
+
VALID_BACKOFF_MODES = %i[fixed exponential].freeze
|
|
10
|
+
|
|
11
|
+
endpoint :list, method: :get, path: '/operations' do |params|
|
|
12
|
+
{
|
|
13
|
+
query: {
|
|
14
|
+
status: OptionValidation.normalize_optional_non_empty_string(params.delete(:status), key: :status),
|
|
15
|
+
**extract_pagination!(params)
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def get(operation_id)
|
|
21
|
+
id = normalize_required_id(operation_id, key: :operation_id)
|
|
22
|
+
body = request_json(:get, '/operations/%{operation_id}', path_params: { operation_id: id })
|
|
23
|
+
Types::OperationStatus.from_api(body, operation_id: id)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def wait(operation_ids, interval: 1.0, timeout: 120.0, backoff: :exponential, max_interval: 30.0)
|
|
27
|
+
ids = normalize_operation_ids(operation_ids)
|
|
28
|
+
interval_seconds = OptionValidation.normalize_positive_number(interval, key: :interval)
|
|
29
|
+
timeout_seconds = OptionValidation.normalize_positive_number(timeout, key: :timeout)
|
|
30
|
+
max_interval_seconds = OptionValidation.normalize_positive_number(max_interval, key: :max_interval)
|
|
31
|
+
backoff_mode = OptionValidation.normalize_enum(backoff, key: :backoff, valid: VALID_BACKOFF_MODES)
|
|
32
|
+
deadline = monotonic_now + timeout_seconds
|
|
33
|
+
|
|
34
|
+
completed = {}
|
|
35
|
+
pending_ids = ids.dup
|
|
36
|
+
attempts = 0
|
|
37
|
+
|
|
38
|
+
loop do
|
|
39
|
+
cycle_start = monotonic_now
|
|
40
|
+
statuses = pending_ids.map { |id| get(id) }
|
|
41
|
+
statuses.each do |status|
|
|
42
|
+
completed[status.id] = status if status.terminal?
|
|
43
|
+
end
|
|
44
|
+
pending_ids = pending_ids.reject { |id| completed.key?(id) }
|
|
45
|
+
return ids.map { |id| completed[id] } if pending_ids.empty?
|
|
46
|
+
|
|
47
|
+
now = monotonic_now
|
|
48
|
+
raise TimeoutError, "Timed out waiting for operations: #{pending_ids.join(', ')}" if now >= deadline
|
|
49
|
+
|
|
50
|
+
elapsed = now - cycle_start
|
|
51
|
+
cycle_interval = next_interval_seconds(
|
|
52
|
+
attempts: attempts,
|
|
53
|
+
interval_seconds: interval_seconds,
|
|
54
|
+
max_interval_seconds: max_interval_seconds,
|
|
55
|
+
backoff_mode: backoff_mode
|
|
56
|
+
)
|
|
57
|
+
remaining_interval = cycle_interval - elapsed
|
|
58
|
+
remaining_timeout = deadline - now
|
|
59
|
+
sleep_seconds = [remaining_interval, remaining_timeout].min
|
|
60
|
+
sleep(sleep_seconds) if sleep_seconds.positive?
|
|
61
|
+
attempts += 1
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def cancel(operation_id)
|
|
66
|
+
id = normalize_required_id(operation_id, key: :operation_id)
|
|
67
|
+
request_json(:delete, '/operations/%{operation_id}', path_params: { operation_id: id })
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
private
|
|
71
|
+
|
|
72
|
+
def normalize_operation_ids(operation_ids)
|
|
73
|
+
ids = Array(operation_ids).flatten.compact.map do |operation_id|
|
|
74
|
+
normalize_required_id(operation_id, key: :operation_id)
|
|
75
|
+
end
|
|
76
|
+
return ids unless ids.empty?
|
|
77
|
+
|
|
78
|
+
raise ValidationError, 'operation_ids must contain at least one entry'
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def monotonic_now
|
|
82
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def next_interval_seconds(attempts:, interval_seconds:, max_interval_seconds:, backoff_mode:)
|
|
86
|
+
return interval_seconds if backoff_mode == :fixed
|
|
87
|
+
|
|
88
|
+
[interval_seconds * (2**attempts), max_interval_seconds].min
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'base'
|
|
4
|
+
|
|
5
|
+
module Hindsight
|
|
6
|
+
module Resources
|
|
7
|
+
class Tags < Base
|
|
8
|
+
endpoint :list, method: :get, path: '/tags' do |params|
|
|
9
|
+
{
|
|
10
|
+
query: {
|
|
11
|
+
q: OptionValidation.normalize_optional_non_empty_string(params.delete(:q), key: :q),
|
|
12
|
+
**extract_pagination!(params)
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|