braintrust 0.1.1 → 0.1.2
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/lib/braintrust/api/functions.rb +11 -0
- data/lib/braintrust/internal/template.rb +91 -0
- data/lib/braintrust/prompt.rb +143 -0
- data/lib/braintrust/vendor/mustache/context.rb +180 -0
- data/lib/braintrust/vendor/mustache/context_miss.rb +22 -0
- data/lib/braintrust/vendor/mustache/enumerable.rb +14 -0
- data/lib/braintrust/vendor/mustache/generator.rb +188 -0
- data/lib/braintrust/vendor/mustache/mustache.rb +260 -0
- data/lib/braintrust/vendor/mustache/parser.rb +364 -0
- data/lib/braintrust/vendor/mustache/settings.rb +252 -0
- data/lib/braintrust/vendor/mustache/template.rb +138 -0
- data/lib/braintrust/vendor/mustache/utils.rb +42 -0
- data/lib/braintrust/vendor/mustache.rb +16 -0
- data/lib/braintrust/version.rb +1 -1
- data/lib/braintrust.rb +1 -0
- metadata +13 -1
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Vendored from mustache gem v1.1.1
|
|
4
|
+
# https://github.com/mustache/mustache
|
|
5
|
+
# License: MIT
|
|
6
|
+
# Modifications: Namespaced under Braintrust::Vendor
|
|
7
|
+
|
|
8
|
+
# Settings which can be configured for all view classes, a single
|
|
9
|
+
# view class, or a single Mustache instance.
|
|
10
|
+
module Braintrust
|
|
11
|
+
module Vendor
|
|
12
|
+
class Mustache
|
|
13
|
+
def initialize_settings
|
|
14
|
+
@template = nil
|
|
15
|
+
@template_path = nil
|
|
16
|
+
@template_extension = nil
|
|
17
|
+
@template_name = nil
|
|
18
|
+
@template_file = nil
|
|
19
|
+
@raise_on_context_miss = nil
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def self.initialize_settings
|
|
23
|
+
@template = nil
|
|
24
|
+
@template_path = nil
|
|
25
|
+
@template_extension = nil
|
|
26
|
+
@template_name = nil
|
|
27
|
+
@template_file = nil
|
|
28
|
+
@raise_on_context_miss = nil
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
initialize_settings
|
|
32
|
+
|
|
33
|
+
def self.inherited(subclass)
|
|
34
|
+
subclass.initialize_settings
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
#
|
|
38
|
+
# Template Path
|
|
39
|
+
#
|
|
40
|
+
|
|
41
|
+
# The template path informs your Mustache view where to look for its
|
|
42
|
+
# corresponding template. By default it's the current directory (".")
|
|
43
|
+
#
|
|
44
|
+
# A class named Stat with a template_path of "app/templates" will look
|
|
45
|
+
# for "app/templates/stat.mustache"
|
|
46
|
+
|
|
47
|
+
def self.template_path
|
|
48
|
+
@template_path ||= inheritable_config_for :template_path, "."
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def self.template_path=(path)
|
|
52
|
+
@template_path = File.expand_path(path)
|
|
53
|
+
@template = nil
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def template_path
|
|
57
|
+
@template_path ||= self.class.template_path
|
|
58
|
+
end
|
|
59
|
+
alias_method :path, :template_path
|
|
60
|
+
|
|
61
|
+
def template_path=(path)
|
|
62
|
+
@template_path = File.expand_path(path)
|
|
63
|
+
@template = nil
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Alias for `template_path`
|
|
67
|
+
def self.path
|
|
68
|
+
template_path
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Alias for `template_path`
|
|
72
|
+
def self.path=(path)
|
|
73
|
+
self.template_path = path
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
#
|
|
77
|
+
# Template Extension
|
|
78
|
+
#
|
|
79
|
+
|
|
80
|
+
# A Mustache template's default extension is 'mustache', but this can be changed.
|
|
81
|
+
|
|
82
|
+
def self.template_extension
|
|
83
|
+
@template_extension ||= inheritable_config_for :template_extension, "mustache"
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def self.template_extension=(template_extension)
|
|
87
|
+
@template_extension = template_extension
|
|
88
|
+
@template = nil
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def template_extension
|
|
92
|
+
@template_extension ||= self.class.template_extension
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def template_extension=(template_extension)
|
|
96
|
+
@template_extension = template_extension
|
|
97
|
+
@template = nil
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
#
|
|
101
|
+
# Template Name
|
|
102
|
+
#
|
|
103
|
+
|
|
104
|
+
# The template name is the Mustache template file without any
|
|
105
|
+
# extension or other information. Defaults to `class_name`.
|
|
106
|
+
#
|
|
107
|
+
# You may want to change this if your class is named Stat but you want
|
|
108
|
+
# to re-use another template.
|
|
109
|
+
#
|
|
110
|
+
# class Stat
|
|
111
|
+
# self.template_name = "graphs" # use graphs.mustache
|
|
112
|
+
# end
|
|
113
|
+
|
|
114
|
+
def self.template_name
|
|
115
|
+
@template_name || underscore
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def self.template_name=(template_name)
|
|
119
|
+
@template_name = template_name
|
|
120
|
+
@template = nil
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def template_name
|
|
124
|
+
@template_name ||= self.class.template_name
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def template_name=(template_name)
|
|
128
|
+
@template_name = template_name
|
|
129
|
+
@template = nil
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
#
|
|
133
|
+
# Template File
|
|
134
|
+
#
|
|
135
|
+
|
|
136
|
+
# The template file is the absolute path of the file Mustache will
|
|
137
|
+
# use as its template. By default it's ./class_name.mustache
|
|
138
|
+
|
|
139
|
+
def self.template_file
|
|
140
|
+
@template_file || "#{path}/#{template_name}.#{template_extension}"
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def self.template_file=(template_file)
|
|
144
|
+
@template_file = template_file
|
|
145
|
+
@template = nil
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# The template file is the absolute path of the file Mustache will
|
|
149
|
+
# use as its template. By default it's ./class_name.mustache
|
|
150
|
+
def template_file
|
|
151
|
+
@template_file || "#{path}/#{template_name}.#{template_extension}"
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def template_file=(template_file)
|
|
155
|
+
@template_file = template_file
|
|
156
|
+
@template = nil
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
#
|
|
160
|
+
# Template
|
|
161
|
+
#
|
|
162
|
+
|
|
163
|
+
# The template is the actual string Mustache uses as its template.
|
|
164
|
+
# There is a bit of magic here: what we get back is actually a
|
|
165
|
+
# Mustache::Template object, but you can still safely use `template=`
|
|
166
|
+
# with a string.
|
|
167
|
+
|
|
168
|
+
def self.template
|
|
169
|
+
@template ||= templateify(File.read(template_file))
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def self.template=(template)
|
|
173
|
+
@template = templateify(template)
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
# The template can be set at the instance level.
|
|
177
|
+
def template
|
|
178
|
+
return @template if @template
|
|
179
|
+
|
|
180
|
+
# If they sent any instance-level options use that instead of the class's.
|
|
181
|
+
if @template_path || @template_extension || @template_name || @template_file
|
|
182
|
+
@template = templateify(File.read(template_file))
|
|
183
|
+
else
|
|
184
|
+
@template = self.class.template
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def template=(template)
|
|
189
|
+
@template = templateify(template)
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
#
|
|
193
|
+
# Raise on context miss
|
|
194
|
+
#
|
|
195
|
+
|
|
196
|
+
# Should an exception be raised when we cannot find a corresponding method
|
|
197
|
+
# or key in the current context? By default this is false to emulate ctemplate's
|
|
198
|
+
# behavior, but it may be useful to enable when debugging or developing.
|
|
199
|
+
#
|
|
200
|
+
# If set to true and there is a context miss, `Mustache::ContextMiss` will
|
|
201
|
+
# be raised.
|
|
202
|
+
|
|
203
|
+
def self.raise_on_context_miss?
|
|
204
|
+
@raise_on_context_miss
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def self.raise_on_context_miss=(boolean)
|
|
208
|
+
@raise_on_context_miss = boolean
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
# Instance level version of `Mustache.raise_on_context_miss?`
|
|
212
|
+
def raise_on_context_miss?
|
|
213
|
+
self.class.raise_on_context_miss? || @raise_on_context_miss
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
def raise_on_context_miss=(boolean)
|
|
217
|
+
@raise_on_context_miss = boolean
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
#
|
|
221
|
+
# View Namespace
|
|
222
|
+
#
|
|
223
|
+
|
|
224
|
+
# The constant under which Mustache will look for views when autoloading.
|
|
225
|
+
# By default the view namespace is `Object`, but it might be nice to set
|
|
226
|
+
# it to something like `Hurl::Views` if your app's main namespace is `Hurl`.
|
|
227
|
+
|
|
228
|
+
def self.view_namespace
|
|
229
|
+
@view_namespace ||= inheritable_config_for(:view_namespace, Object)
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
def self.view_namespace=(namespace)
|
|
233
|
+
@view_namespace = namespace
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
#
|
|
237
|
+
# View Path
|
|
238
|
+
#
|
|
239
|
+
|
|
240
|
+
# Mustache searches the view path for .rb files to require when asked to find a
|
|
241
|
+
# view class. Defaults to "."
|
|
242
|
+
|
|
243
|
+
def self.view_path
|
|
244
|
+
@view_path ||= inheritable_config_for(:view_path, ".")
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
def self.view_path=(path)
|
|
248
|
+
@view_path = path
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
end
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Vendored from mustache gem v1.1.1
|
|
4
|
+
# https://github.com/mustache/mustache
|
|
5
|
+
# License: MIT
|
|
6
|
+
# Modifications: Namespaced under Braintrust::Vendor
|
|
7
|
+
|
|
8
|
+
require "cgi"
|
|
9
|
+
|
|
10
|
+
require_relative "parser"
|
|
11
|
+
require_relative "generator"
|
|
12
|
+
|
|
13
|
+
module Braintrust
|
|
14
|
+
module Vendor
|
|
15
|
+
class Mustache
|
|
16
|
+
# A Template represents a Mustache template. It compiles and caches
|
|
17
|
+
# a raw string template into something usable.
|
|
18
|
+
#
|
|
19
|
+
# The idea is this: when handed a Mustache template, convert it into
|
|
20
|
+
# a Ruby string by transforming Mustache tags into interpolated
|
|
21
|
+
# Ruby.
|
|
22
|
+
#
|
|
23
|
+
# You shouldn't use this class directly, instead:
|
|
24
|
+
#
|
|
25
|
+
# >> Braintrust::Vendor::Mustache.render(template, hash)
|
|
26
|
+
class Template
|
|
27
|
+
attr_reader :source
|
|
28
|
+
|
|
29
|
+
# Expects a Mustache template as a string along with a template
|
|
30
|
+
# path, which it uses to find partials. Options may be passed.
|
|
31
|
+
def initialize(source, options = {})
|
|
32
|
+
@source = source
|
|
33
|
+
@options = options
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Renders the `@source` Mustache template using the given
|
|
37
|
+
# `context`, which should be a simple hash keyed with symbols.
|
|
38
|
+
#
|
|
39
|
+
# The first time a template is rendered, this method is overriden
|
|
40
|
+
# and from then on it is "compiled". Subsequent calls will skip
|
|
41
|
+
# the compilation step and run the Ruby version of the template
|
|
42
|
+
# directly.
|
|
43
|
+
def render(context)
|
|
44
|
+
# Compile our Mustache template into a Ruby string
|
|
45
|
+
compiled = "def render(ctx) #{compile} end"
|
|
46
|
+
|
|
47
|
+
# Here we rewrite ourself with the interpolated Ruby version of
|
|
48
|
+
# our Mustache template so subsequent calls are very fast and
|
|
49
|
+
# can skip the compilation stage.
|
|
50
|
+
instance_eval(compiled, __FILE__, __LINE__ - 1)
|
|
51
|
+
|
|
52
|
+
# Call the newly rewritten version of #render
|
|
53
|
+
render(context)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Does the dirty work of transforming a Mustache template into an
|
|
57
|
+
# interpolation-friendly Ruby string.
|
|
58
|
+
def compile(src = @source)
|
|
59
|
+
Generator.new(@options).compile(tokens(src))
|
|
60
|
+
end
|
|
61
|
+
alias_method :to_s, :compile
|
|
62
|
+
|
|
63
|
+
# Returns an array of tokens for a given template.
|
|
64
|
+
#
|
|
65
|
+
# @return [Array] Array of tokens.
|
|
66
|
+
#
|
|
67
|
+
def tokens(src = @source)
|
|
68
|
+
Parser.new(@options).compile(src)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Returns an array of tags.
|
|
72
|
+
#
|
|
73
|
+
# Tags that belong to sections will be of the form `section1.tag`.
|
|
74
|
+
#
|
|
75
|
+
# @return [Array] Returns an array of tags.
|
|
76
|
+
#
|
|
77
|
+
def tags
|
|
78
|
+
Template.recursor(tokens, []) do |token, section|
|
|
79
|
+
if [:etag, :utag].include?(token[1])
|
|
80
|
+
[new_token = nil, new_section = nil, result = ((section + [token[2][2][0]]).join(".")), stop = true]
|
|
81
|
+
elsif [:section, :inverted_section].include?(token[1])
|
|
82
|
+
[new_token = token[4], new_section = (section + [token[2][2][0]]), result = nil, stop = false]
|
|
83
|
+
else
|
|
84
|
+
[new_token = token, new_section = section, result = nil, stop = false]
|
|
85
|
+
end
|
|
86
|
+
end.flatten.reject(&:nil?).uniq
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Returns an array of sections.
|
|
90
|
+
#
|
|
91
|
+
# Sections that belong to other sections will be of the form `section1.childsection`
|
|
92
|
+
#
|
|
93
|
+
# @return [Array] Returns an array of section.
|
|
94
|
+
#
|
|
95
|
+
def sections
|
|
96
|
+
Template.recursor(tokens, []) do |token, section|
|
|
97
|
+
if [:section, :inverted_section].include?(token[1])
|
|
98
|
+
new_section = (section + [token[2][2][0]])
|
|
99
|
+
[new_token = token[4], new_section, result = new_section.join("."), stop = false]
|
|
100
|
+
else
|
|
101
|
+
[new_token = token, new_section = section, result = nil, stop = false]
|
|
102
|
+
end
|
|
103
|
+
end.flatten.reject(&:nil?).uniq
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Returns an array of partials.
|
|
107
|
+
#
|
|
108
|
+
# Partials that belong to sections are included, but the section name is not preserved
|
|
109
|
+
#
|
|
110
|
+
# @return [Array] Returns an array of partials.
|
|
111
|
+
#
|
|
112
|
+
def partials
|
|
113
|
+
Template.recursor(tokens, []) do |token, section|
|
|
114
|
+
if token[1] == :partial
|
|
115
|
+
[new_token = token, new_section = section, result = token[2], stop = true]
|
|
116
|
+
else
|
|
117
|
+
[new_token = token, new_section = section, result = nil, stop = false]
|
|
118
|
+
end
|
|
119
|
+
end.flatten.reject(&:nil?).uniq
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Simple recursive iterator for tokens
|
|
123
|
+
def self.recursor(toks, section, &block)
|
|
124
|
+
toks.map do |token|
|
|
125
|
+
next unless token.is_a? Array
|
|
126
|
+
|
|
127
|
+
if token.first == :mustache
|
|
128
|
+
new_token, new_section, result, stop = yield(token, section)
|
|
129
|
+
[result] + (stop ? [] : recursor(new_token, new_section, &block))
|
|
130
|
+
else
|
|
131
|
+
recursor(token, section, &block)
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Vendored from mustache gem v1.1.1
|
|
4
|
+
# https://github.com/mustache/mustache
|
|
5
|
+
# License: MIT
|
|
6
|
+
# Modifications: Namespaced under Braintrust::Vendor
|
|
7
|
+
|
|
8
|
+
module Braintrust
|
|
9
|
+
module Vendor
|
|
10
|
+
class Mustache
|
|
11
|
+
module Utils
|
|
12
|
+
class String
|
|
13
|
+
def initialize(string)
|
|
14
|
+
@string = string
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def classify
|
|
18
|
+
@string.split("/").map do |namespace|
|
|
19
|
+
namespace.split(/[-_]/).map do |part|
|
|
20
|
+
part[0] = part.chars.first.upcase
|
|
21
|
+
part
|
|
22
|
+
end.join
|
|
23
|
+
end.join("::")
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def underscore(view_namespace)
|
|
27
|
+
@string
|
|
28
|
+
.dup
|
|
29
|
+
.split("#{view_namespace}::")
|
|
30
|
+
.last
|
|
31
|
+
.split("::")
|
|
32
|
+
.map do |part|
|
|
33
|
+
part[0] = part[0].downcase
|
|
34
|
+
part.gsub(/[A-Z]/) { |s| "_" << s.downcase }
|
|
35
|
+
end
|
|
36
|
+
.join("/")
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Vendored Mustache template engine
|
|
4
|
+
# From mustache gem v1.1.1 - https://github.com/mustache/mustache
|
|
5
|
+
# License: MIT
|
|
6
|
+
#
|
|
7
|
+
# Modifications from original:
|
|
8
|
+
# - Namespaced under Braintrust::Vendor to avoid conflicts
|
|
9
|
+
# - Disabled HTML escaping (LLM prompts don't need HTML entity encoding)
|
|
10
|
+
#
|
|
11
|
+
# This vendored version ensures:
|
|
12
|
+
# - No external dependency required
|
|
13
|
+
# - Consistent behavior across all SDK users
|
|
14
|
+
# - No HTML escaping that would corrupt prompts containing < > & characters
|
|
15
|
+
|
|
16
|
+
require_relative "mustache/mustache"
|
data/lib/braintrust/version.rb
CHANGED
data/lib/braintrust.rb
CHANGED
|
@@ -5,6 +5,7 @@ require_relative "braintrust/config"
|
|
|
5
5
|
require_relative "braintrust/state"
|
|
6
6
|
require_relative "braintrust/trace"
|
|
7
7
|
require_relative "braintrust/api"
|
|
8
|
+
require_relative "braintrust/prompt"
|
|
8
9
|
require_relative "braintrust/internal/experiments"
|
|
9
10
|
require_relative "braintrust/internal/env"
|
|
10
11
|
require_relative "braintrust/eval"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: braintrust
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Braintrust
|
|
@@ -240,15 +240,27 @@ files:
|
|
|
240
240
|
- lib/braintrust/internal/encoding.rb
|
|
241
241
|
- lib/braintrust/internal/env.rb
|
|
242
242
|
- lib/braintrust/internal/experiments.rb
|
|
243
|
+
- lib/braintrust/internal/template.rb
|
|
243
244
|
- lib/braintrust/internal/thread_pool.rb
|
|
244
245
|
- lib/braintrust/internal/time.rb
|
|
245
246
|
- lib/braintrust/logger.rb
|
|
247
|
+
- lib/braintrust/prompt.rb
|
|
246
248
|
- lib/braintrust/setup.rb
|
|
247
249
|
- lib/braintrust/state.rb
|
|
248
250
|
- lib/braintrust/trace.rb
|
|
249
251
|
- lib/braintrust/trace/attachment.rb
|
|
250
252
|
- lib/braintrust/trace/span_filter.rb
|
|
251
253
|
- lib/braintrust/trace/span_processor.rb
|
|
254
|
+
- lib/braintrust/vendor/mustache.rb
|
|
255
|
+
- lib/braintrust/vendor/mustache/context.rb
|
|
256
|
+
- lib/braintrust/vendor/mustache/context_miss.rb
|
|
257
|
+
- lib/braintrust/vendor/mustache/enumerable.rb
|
|
258
|
+
- lib/braintrust/vendor/mustache/generator.rb
|
|
259
|
+
- lib/braintrust/vendor/mustache/mustache.rb
|
|
260
|
+
- lib/braintrust/vendor/mustache/parser.rb
|
|
261
|
+
- lib/braintrust/vendor/mustache/settings.rb
|
|
262
|
+
- lib/braintrust/vendor/mustache/template.rb
|
|
263
|
+
- lib/braintrust/vendor/mustache/utils.rb
|
|
252
264
|
- lib/braintrust/version.rb
|
|
253
265
|
homepage: https://github.com/braintrustdata/braintrust-sdk-ruby
|
|
254
266
|
licenses:
|