kagi-api 0.0.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
- checksums.yaml.gz.sig +0 -0
- data/LICENSE.adoc +134 -0
- data/README.adoc +247 -0
- data/kagi-api.gemspec +37 -0
- data/lib/kagi/api/client.rb +33 -0
- data/lib/kagi/api/configuration/content.rb +12 -0
- data/lib/kagi/api/configuration/loader.rb +22 -0
- data/lib/kagi/api/container.rb +37 -0
- data/lib/kagi/api/contracts/error.rb +18 -0
- data/lib/kagi/api/contracts/fast.rb +18 -0
- data/lib/kagi/api/contracts/meta.rb +15 -0
- data/lib/kagi/api/contracts/reference.rb +14 -0
- data/lib/kagi/api/contracts/search.rb +27 -0
- data/lib/kagi/api/contracts/summary.rb +17 -0
- data/lib/kagi/api/dependencies.rb +9 -0
- data/lib/kagi/api/endpoints/container.rb +23 -0
- data/lib/kagi/api/endpoints/dependencies.rb +11 -0
- data/lib/kagi/api/endpoints/enrich/news.rb +55 -0
- data/lib/kagi/api/endpoints/enrich/web.rb +55 -0
- data/lib/kagi/api/endpoints/fast.rb +53 -0
- data/lib/kagi/api/endpoints/search.rb +53 -0
- data/lib/kagi/api/endpoints/summarize.rb +53 -0
- data/lib/kagi/api/models/content/error.rb +22 -0
- data/lib/kagi/api/models/content/fast.rb +16 -0
- data/lib/kagi/api/models/content/meta.rb +20 -0
- data/lib/kagi/api/models/content/reference.rb +12 -0
- data/lib/kagi/api/models/content/search.rb +35 -0
- data/lib/kagi/api/models/content/summary.rb +12 -0
- data/lib/kagi/api/models/content/thumbnail.rb +16 -0
- data/lib/kagi/api/models/error.rb +19 -0
- data/lib/kagi/api/models/fast.rb +19 -0
- data/lib/kagi/api/models/search.rb +19 -0
- data/lib/kagi/api/models/summary.rb +19 -0
- data/lib/kagi/api/requester.rb +31 -0
- data/lib/kagi/api.rb +24 -0
- data.tar.gz.sig +0 -0
- metadata +220 -0
- metadata.gz.sig +0 -0
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "containable"
|
4
|
+
|
5
|
+
module Kagi
|
6
|
+
module API
|
7
|
+
module Endpoints
|
8
|
+
# Registers all endpoints.
|
9
|
+
module Container
|
10
|
+
extend Containable
|
11
|
+
|
12
|
+
namespace :enrich do
|
13
|
+
register(:news) { Enrich::News.new }
|
14
|
+
register(:web) { Enrich::Web.new }
|
15
|
+
end
|
16
|
+
|
17
|
+
register(:fast) { Fast.new }
|
18
|
+
register(:search) { Search.new }
|
19
|
+
register(:summarize) { Summarize.new }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/monads"
|
4
|
+
require "pipeable"
|
5
|
+
|
6
|
+
module Kagi
|
7
|
+
module API
|
8
|
+
module Endpoints
|
9
|
+
module Enrich
|
10
|
+
# Handles news requests.
|
11
|
+
class News
|
12
|
+
include Kagi::API::Dependencies[
|
13
|
+
:requester,
|
14
|
+
contract: "contracts.search",
|
15
|
+
error_contract: "contracts.error",
|
16
|
+
model: "models.search",
|
17
|
+
error_model: "models.error"
|
18
|
+
]
|
19
|
+
|
20
|
+
include Dry::Monads[:result]
|
21
|
+
include Pipeable
|
22
|
+
|
23
|
+
def call(**params)
|
24
|
+
result = requester.get("enrich/news", **params)
|
25
|
+
|
26
|
+
case result
|
27
|
+
in Success then success result
|
28
|
+
in Failure(response) then failure response
|
29
|
+
else Failure "Unable to parse HTTP response."
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def success result
|
36
|
+
pipe result,
|
37
|
+
try(:parse, catch: JSON::ParserError),
|
38
|
+
validate(contract, as: :to_h),
|
39
|
+
to(model, :for)
|
40
|
+
end
|
41
|
+
|
42
|
+
def failure response
|
43
|
+
pipe(
|
44
|
+
response,
|
45
|
+
try(:parse, catch: JSON::ParserError),
|
46
|
+
validate(error_contract, as: :to_h),
|
47
|
+
to(error_model, :for),
|
48
|
+
bind { Failure it }
|
49
|
+
)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/monads"
|
4
|
+
require "pipeable"
|
5
|
+
|
6
|
+
module Kagi
|
7
|
+
module API
|
8
|
+
module Endpoints
|
9
|
+
module Enrich
|
10
|
+
# Handles web requests.
|
11
|
+
class Web
|
12
|
+
include Kagi::API::Dependencies[
|
13
|
+
:requester,
|
14
|
+
contract: "contracts.search",
|
15
|
+
error_contract: "contracts.error",
|
16
|
+
model: "models.search",
|
17
|
+
error_model: "models.error"
|
18
|
+
]
|
19
|
+
|
20
|
+
include Dry::Monads[:result]
|
21
|
+
include Pipeable
|
22
|
+
|
23
|
+
def call(**params)
|
24
|
+
result = requester.get("enrich/web", **params)
|
25
|
+
|
26
|
+
case result
|
27
|
+
in Success then success result
|
28
|
+
in Failure(response) then failure response
|
29
|
+
else Failure "Unable to parse HTTP response."
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def success result
|
36
|
+
pipe result,
|
37
|
+
try(:parse, catch: JSON::ParserError),
|
38
|
+
validate(contract, as: :to_h),
|
39
|
+
to(model, :for)
|
40
|
+
end
|
41
|
+
|
42
|
+
def failure response
|
43
|
+
pipe(
|
44
|
+
response,
|
45
|
+
try(:parse, catch: JSON::ParserError),
|
46
|
+
validate(error_contract, as: :to_h),
|
47
|
+
to(error_model, :for),
|
48
|
+
bind { Failure it }
|
49
|
+
)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/monads"
|
4
|
+
require "pipeable"
|
5
|
+
|
6
|
+
module Kagi
|
7
|
+
module API
|
8
|
+
module Endpoints
|
9
|
+
# Handles Fast GPT requests.
|
10
|
+
class Fast
|
11
|
+
include Kagi::API::Dependencies[
|
12
|
+
:requester,
|
13
|
+
contract: "contracts.fast",
|
14
|
+
error_contract: "contracts.error",
|
15
|
+
model: "models.fast",
|
16
|
+
error_model: "models.error"
|
17
|
+
]
|
18
|
+
|
19
|
+
include Dry::Monads[:result]
|
20
|
+
include Pipeable
|
21
|
+
|
22
|
+
def call(**params)
|
23
|
+
result = requester.post("fastgpt", **params)
|
24
|
+
|
25
|
+
case result
|
26
|
+
in Success then success result
|
27
|
+
in Failure(response) then failure response
|
28
|
+
else Failure "Unable to parse HTTP response."
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def success result
|
35
|
+
pipe result,
|
36
|
+
try(:parse, catch: JSON::ParserError),
|
37
|
+
validate(contract, as: :to_h),
|
38
|
+
to(model, :for)
|
39
|
+
end
|
40
|
+
|
41
|
+
def failure response
|
42
|
+
pipe(
|
43
|
+
response,
|
44
|
+
try(:parse, catch: JSON::ParserError),
|
45
|
+
validate(error_contract, as: :to_h),
|
46
|
+
to(error_model, :for),
|
47
|
+
bind { Failure it }
|
48
|
+
)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/monads"
|
4
|
+
require "pipeable"
|
5
|
+
|
6
|
+
module Kagi
|
7
|
+
module API
|
8
|
+
module Endpoints
|
9
|
+
# Handles search requests.
|
10
|
+
class Search
|
11
|
+
include Kagi::API::Dependencies[
|
12
|
+
:requester,
|
13
|
+
contract: "contracts.search",
|
14
|
+
error_contract: "contracts.error",
|
15
|
+
model: "models.search",
|
16
|
+
error_model: "models.error"
|
17
|
+
]
|
18
|
+
|
19
|
+
include Dry::Monads[:result]
|
20
|
+
include Pipeable
|
21
|
+
|
22
|
+
def call(**params)
|
23
|
+
result = requester.get("search", **params)
|
24
|
+
|
25
|
+
case result
|
26
|
+
in Success then success result
|
27
|
+
in Failure(response) then failure response
|
28
|
+
else Failure "Unable to parse HTTP response."
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def success result
|
35
|
+
pipe result,
|
36
|
+
try(:parse, catch: JSON::ParserError),
|
37
|
+
validate(contract, as: :to_h),
|
38
|
+
to(model, :for)
|
39
|
+
end
|
40
|
+
|
41
|
+
def failure response
|
42
|
+
pipe(
|
43
|
+
response,
|
44
|
+
try(:parse, catch: JSON::ParserError),
|
45
|
+
validate(error_contract, as: :to_h),
|
46
|
+
to(error_model, :for),
|
47
|
+
bind { Failure it }
|
48
|
+
)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/monads"
|
4
|
+
require "pipeable"
|
5
|
+
|
6
|
+
module Kagi
|
7
|
+
module API
|
8
|
+
module Endpoints
|
9
|
+
# Handles summarize requests.
|
10
|
+
class Summarize
|
11
|
+
include Kagi::API::Dependencies[
|
12
|
+
:requester,
|
13
|
+
contract: "contracts.summary",
|
14
|
+
error_contract: "contracts.error",
|
15
|
+
model: "models.summary",
|
16
|
+
error_model: "models.error"
|
17
|
+
]
|
18
|
+
|
19
|
+
include Dry::Monads[:result]
|
20
|
+
include Pipeable
|
21
|
+
|
22
|
+
def call(**params)
|
23
|
+
result = requester.post("summarize", **params)
|
24
|
+
|
25
|
+
case result
|
26
|
+
in Success then success result
|
27
|
+
in Failure(response) then failure response
|
28
|
+
else Failure "Unable to parse HTTP response."
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def success result
|
35
|
+
pipe result,
|
36
|
+
try(:parse, catch: JSON::ParserError),
|
37
|
+
validate(contract, as: :to_h),
|
38
|
+
to(model, :for)
|
39
|
+
end
|
40
|
+
|
41
|
+
def failure response
|
42
|
+
pipe(
|
43
|
+
response,
|
44
|
+
try(:parse, catch: JSON::ParserError),
|
45
|
+
validate(error_contract, as: :to_h),
|
46
|
+
to(error_model, :for),
|
47
|
+
bind { Failure it }
|
48
|
+
)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kagi
|
4
|
+
module API
|
5
|
+
module Models
|
6
|
+
module Content
|
7
|
+
ERROR_MAP = {msg: :message, ref: :reference}.freeze
|
8
|
+
|
9
|
+
# Models error data.
|
10
|
+
Error = Data.define :code, :message, :reference do
|
11
|
+
def self.for(key_map: ERROR_MAP, **attributes)
|
12
|
+
new(**attributes.transform_keys(key_map))
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(reference: nil, **)
|
16
|
+
super
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kagi
|
4
|
+
module API
|
5
|
+
module Models
|
6
|
+
module Content
|
7
|
+
# Models fast data.
|
8
|
+
Fast = Data.define :output, :tokens, :references do
|
9
|
+
def self.for(**attributes)
|
10
|
+
new(**attributes, references: attributes[:references].map { Reference[**it] })
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kagi
|
4
|
+
module API
|
5
|
+
module Models
|
6
|
+
module Content
|
7
|
+
META_MAP = {ms: :duration, api_balance: :balance}.freeze
|
8
|
+
|
9
|
+
# Models meta data.
|
10
|
+
Meta = Data.define :id, :node, :duration, :balance do
|
11
|
+
def self.for(key_map: META_MAP, **attributes) = new(**attributes.transform_keys(key_map))
|
12
|
+
|
13
|
+
def initialize(balance: nil, **)
|
14
|
+
super
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kagi
|
4
|
+
module API
|
5
|
+
module Models
|
6
|
+
module Content
|
7
|
+
SEARCH_MAP = {t: :type, published: :published_at}.freeze
|
8
|
+
|
9
|
+
# Models search data.
|
10
|
+
Search = Data.define :type, :rank, :title, :url, :snippet, :published_at, :thumbnail do
|
11
|
+
def self.for(key_map: SEARCH_MAP, **attributes)
|
12
|
+
new(
|
13
|
+
**attributes.transform_keys(key_map),
|
14
|
+
thumbnail: (Thumbnail[**attributes[:thumbnail]] if attributes.key? :thumbnail)
|
15
|
+
)
|
16
|
+
end
|
17
|
+
|
18
|
+
# rubocop:todo Metrics/ParameterLists
|
19
|
+
def initialize(
|
20
|
+
rank: nil,
|
21
|
+
title: nil,
|
22
|
+
url: nil,
|
23
|
+
snippet: nil,
|
24
|
+
published_at: nil,
|
25
|
+
thumbnail: nil,
|
26
|
+
**attributes
|
27
|
+
)
|
28
|
+
super
|
29
|
+
end
|
30
|
+
# rubocop:enable Metrics/ParameterLists
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kagi
|
4
|
+
module API
|
5
|
+
module Models
|
6
|
+
module Content
|
7
|
+
# Models thumbnail data.
|
8
|
+
Thumbnail = Data.define :url, :width, :height do
|
9
|
+
def initialize url:, width: nil, height: nil
|
10
|
+
super
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kagi
|
4
|
+
module API
|
5
|
+
module Models
|
6
|
+
# Models the API error.
|
7
|
+
Error = Data.define :meta, :error do
|
8
|
+
def self.for(**attributes)
|
9
|
+
new(
|
10
|
+
**attributes.merge!(
|
11
|
+
meta: Content::Meta.for(**attributes[:meta]),
|
12
|
+
error: attributes[:error].map { Content::Error.for(**it) }
|
13
|
+
)
|
14
|
+
)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kagi
|
4
|
+
module API
|
5
|
+
module Models
|
6
|
+
# Models the fast payload.
|
7
|
+
Fast = Data.define :meta, :data do
|
8
|
+
def self.for(**attributes)
|
9
|
+
new(
|
10
|
+
**attributes.merge!(
|
11
|
+
meta: Content::Meta.for(**attributes[:meta]),
|
12
|
+
data: Content::Fast.for(**attributes[:data])
|
13
|
+
)
|
14
|
+
)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kagi
|
4
|
+
module API
|
5
|
+
module Models
|
6
|
+
# Models the search payload.
|
7
|
+
Search = Data.define :meta, :data do
|
8
|
+
def self.for(**attributes)
|
9
|
+
new(
|
10
|
+
**attributes.merge!(
|
11
|
+
meta: Content::Meta.for(**attributes[:meta]),
|
12
|
+
data: attributes[:data].map { Content::Search.for(**it) }
|
13
|
+
)
|
14
|
+
)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kagi
|
4
|
+
module API
|
5
|
+
module Models
|
6
|
+
# Models the API payload.
|
7
|
+
Summary = Data.define :meta, :data do
|
8
|
+
def self.for(**attributes)
|
9
|
+
new(
|
10
|
+
**attributes.merge!(
|
11
|
+
meta: Content::Meta.for(**attributes[:meta]),
|
12
|
+
data: Content::Summary[**attributes[:data]]
|
13
|
+
)
|
14
|
+
)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kagi
|
4
|
+
module API
|
5
|
+
# The low-level object for making basic HTTP requests.
|
6
|
+
class Requester
|
7
|
+
include Dependencies[:settings, :http]
|
8
|
+
include Dry::Monads[:result]
|
9
|
+
|
10
|
+
def initialize(**)
|
11
|
+
super
|
12
|
+
yield settings if block_given?
|
13
|
+
end
|
14
|
+
|
15
|
+
def get(path, **params) = call(__method__, path, params:)
|
16
|
+
|
17
|
+
def post(path, **json) = call(__method__, path, json:)
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
attr_reader :settings
|
22
|
+
|
23
|
+
def call verb, path, **options
|
24
|
+
http.auth("Bot #{settings.token}")
|
25
|
+
.headers(settings.headers)
|
26
|
+
.public_send(verb, "#{settings.uri}/#{path}", options)
|
27
|
+
.then { |response| response.status.success? ? Success(response) : Failure(response) }
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/kagi/api.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/schema"
|
4
|
+
require "zeitwerk"
|
5
|
+
|
6
|
+
Dry::Schema.load_extensions :monads
|
7
|
+
|
8
|
+
Zeitwerk::Loader.new.then do |loader|
|
9
|
+
loader.inflector.inflect "api" => "API"
|
10
|
+
loader.tag = "kagi-api"
|
11
|
+
loader.push_dir "#{__dir__}/.."
|
12
|
+
loader.setup
|
13
|
+
end
|
14
|
+
|
15
|
+
module Kagi
|
16
|
+
# Main namespace.
|
17
|
+
module API
|
18
|
+
def self.loader registry = Zeitwerk::Registry
|
19
|
+
@loader ||= registry.loaders.find { |loader| loader.tag == "kagi-api" }
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.new(&) = Client.new(&)
|
23
|
+
end
|
24
|
+
end
|
data.tar.gz.sig
ADDED
Binary file
|