Class: Sudhanva::Client

Inherits:
Object
  • Object
show all
Defined in:
lib/sudhanva/client.rb

Constant Summary collapse

DEFAULT_BASE_URL =
"https://sudhanva.me/api/v1"
USER_AGENT =
"sudhanva-ruby/#{VERSION}"

Instance Method Summary collapse

Constructor Details

#initialize(base_url: DEFAULT_BASE_URL, timeout: 10, transport: nil) ⇒ Client

Returns a new instance of Client.

Raises:

  • (ArgumentError)


25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/sudhanva/client.rb', line 25

def initialize(base_url: DEFAULT_BASE_URL, timeout: 10, transport: nil)
  raise ArgumentError, "timeout must be positive" unless timeout.positive?

  @base_url = base_url.sub(%r{/+\z}, "")
  parsed = URI.parse(@base_url)
  unless %w[http https].include?(parsed.scheme) && parsed.host
    raise ArgumentError, "base_url must be an absolute HTTP(S) URL"
  end

  @site_url = "#{parsed.scheme}://#{parsed.host}#{":#{parsed.port}" unless parsed.default_port == parsed.port}"
  @timeout = timeout
  @transport = transport || method(:default_transport)
end

Instance Method Details

#ask(text, limit: 10, mode: "list") ⇒ Object

Raises:

  • (ArgumentError)


97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/sudhanva/client.rb', line 97

def ask(text, limit: 10, mode: "list")
  raise ArgumentError, "text is required" if text.to_s.empty?
  raise ArgumentError, "limit must be between 1 and 20" unless (1..20).cover?(limit)

  request(
    "POST",
    "/ask",
    base: @site_url,
    json: {
      query: { text: text, site: @site_url, limit: limit },
      prefer: { streaming: false, response_format: "conversational_search", mode: mode },
      meta: { version: "0.55" }
    }
  )
end

#batch(operations) ⇒ Object

Raises:

  • (ArgumentError)


55
56
57
58
59
# File 'lib/sudhanva/client.rb', line 55

def batch(operations)
  raise ArgumentError, "operations must contain between 1 and 20 items" unless (1..20).cover?(operations.length)

  request("POST", "/batch", json: { operations: operations })
end

#create_profile_insight(audience:, idempotency_key:, focus: nil) ⇒ Object

Raises:

  • (ArgumentError)


61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/sudhanva/client.rb', line 61

def create_profile_insight(audience:, idempotency_key:, focus: nil)
  raise ArgumentError, "idempotency_key is required" if idempotency_key.to_s.empty?

  payload = { audience: audience }
  payload[:focus] = focus if focus
  request(
    "POST",
    "/profile-insights",
    headers: { "Idempotency-Key" => idempotency_key },
    json: payload
  )
end

#post(slug) ⇒ Object

Raises:

  • (ArgumentError)


49
50
51
52
53
# File 'lib/sudhanva/client.rb', line 49

def post(slug)
  raise ArgumentError, "slug is required" if slug.to_s.empty?

  request("GET", "/posts/#{URI.encode_www_form_component(slug)}")
end

#posts(limit: 20, tag: nil, cursor: nil) ⇒ Object

Raises:

  • (ArgumentError)


43
44
45
46
47
# File 'lib/sudhanva/client.rb', line 43

def posts(limit: 20, tag: nil, cursor: nil)
  raise ArgumentError, "limit must be between 1 and 100" unless (1..100).cover?(limit)

  request("GET", "/posts", query: { limit: limit, tag: tag, cursor: cursor })
end

#profile(locale: "en") ⇒ Object



39
40
41
# File 'lib/sudhanva/client.rb', line 39

def profile(locale: "en")
  request("GET", "/profile", query: { locale: locale })
end

#profile_insight(job_id) ⇒ Object

Raises:

  • (ArgumentError)


74
75
76
77
78
# File 'lib/sudhanva/client.rb', line 74

def profile_insight(job_id)
  raise ArgumentError, "job_id is required" if job_id.to_s.empty?

  request("GET", "/profile-insights/#{URI.encode_www_form_component(job_id)}")
end

#wait_for_profile_insight(job_id, timeout: 30, poll_interval: 1) ⇒ Object



80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/sudhanva/client.rb', line 80

def wait_for_profile_insight(job_id, timeout: 30, poll_interval: 1)
  unless timeout.positive? && poll_interval >= 0
    raise ArgumentError, "timeout must be positive and poll_interval cannot be negative"
  end

  deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + timeout
  loop do
    job = profile_insight(job_id)
    return job if %w[succeeded failed].include?(job["status"])
    if Process.clock_gettime(Process::CLOCK_MONOTONIC) >= deadline
      raise Timeout::Error, "profile insight #{job_id} did not finish within #{timeout}s"
    end

    sleep(poll_interval)
  end
end