Module: SmartPrompt::HTTPClient

Included in:
SenseNovaAdapter, SiliconFlowAdapter, ZhipuAIAdapter
Defined in:
lib/smart_prompt/concerns/http_client.rb

Overview

Shared Net::HTTP plumbing for Net::HTTP-style adapters (ZhipuAI, SenseNova, SiliconFlow). Each previously carried its own copy of post/get/binary/multipart + SSE stream helpers, differing only in the provider label sprinkled through log/exception messages — which the ‘provider_label` hook now supplies.

http_post_multipart takes the general 5-arg shape (file_field + mime); Zhipu’s ASR call site uses a 3-arg shim defined on the adapter itself.

Instance Method Summary collapse

Instance Method Details

#http_get_json(url) ⇒ Object



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/smart_prompt/concerns/http_client.rb', line 34

def http_get_json(url)
  uri = URI.parse(url)
  http = Net::HTTP.new(uri.host, uri.port)
  http.use_ssl = (uri.scheme == "https")
  http.open_timeout = 30
  http.read_timeout = 60
  req = Net::HTTP::Get.new(uri.request_uri)
  req["Authorization"] = "Bearer #{@api_key}"
  SmartPrompt.logger.debug "#{provider_label} GET #{uri}"
  resp = http.request(req)
  if resp.is_a?(Net::HTTPSuccess)
    resp.body.to_s.empty? ? {} : JSON.parse(resp.body)
  else
    raise LLMAPIError, "#{provider_label} API error: #{resp.code} - #{resp.body}"
  end
end

#http_post_binary(url, body) ⇒ Object

Returns the raw response body bytes (for binary payloads like TTS audio).



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/smart_prompt/concerns/http_client.rb', line 52

def http_post_binary(url, body)
  uri = URI.parse(url)
  http = Net::HTTP.new(uri.host, uri.port)
  http.use_ssl = (uri.scheme == "https")
  http.open_timeout = 30
  http.read_timeout = 120
  req = Net::HTTP::Post.new(uri.request_uri)
  req["Content-Type"]  = "application/json"
  req["Authorization"] = "Bearer #{@api_key}"
  req.body = body.to_json
  resp = http.request(req)
  if resp.is_a?(Net::HTTPSuccess)
    resp.body
  else
    raise LLMAPIError, "#{provider_label} TTS API error: #{resp.code} - #{resp.body}"
  end
end

#http_post_json(url, body) ⇒ Object



14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/smart_prompt/concerns/http_client.rb', line 14

def http_post_json(url, body)
  uri = URI.parse(url)
  http = Net::HTTP.new(uri.host, uri.port)
  http.use_ssl = (uri.scheme == "https")
  http.open_timeout = 30
  http.read_timeout = 240
  req = Net::HTTP::Post.new(uri.request_uri)
  req["Content-Type"]  = "application/json"
  req["Authorization"] = "Bearer #{@api_key}"
  req.body = body.to_json
  SmartPrompt.logger.debug "#{provider_label} POST #{uri} body=#{body.to_json}"
  resp = http.request(req)
  if resp.is_a?(Net::HTTPSuccess)
    resp.body.to_s.empty? ? {} : JSON.parse(resp.body)
  else
    SmartPrompt.logger.error "#{provider_label} API error: #{resp.code} - #{resp.body}"
    raise LLMAPIError, "#{provider_label} API error: #{resp.code} - #{resp.body}"
  end
end

#http_post_multipart(url, form, file_field, file_path, mime) ⇒ Object

multipart/form-data POST with a file upload (ASR, voice upload). Returns parsed JSON.



71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/smart_prompt/concerns/http_client.rb', line 71

def http_post_multipart(url, form, file_field, file_path, mime)
  uri = URI.parse(url)
  http = Net::HTTP.new(uri.host, uri.port)
  http.use_ssl = (uri.scheme == "https")
  http.open_timeout = 30
  http.read_timeout = 180

  boundary = "----SmartPrompt#{object_id}"
  body = +""
  form.each do |k, v|
    body << "--#{boundary}\r\n"
    body << "Content-Disposition: form-data; name=\"#{k}\"\r\n\r\n"
    body << "#{v}\r\n"
  end
  File.open(file_path, "rb") do |f|
    body << "--#{boundary}\r\n"
    body << "Content-Disposition: form-data; name=\"#{file_field}\"; filename=\"#{File.basename(file_path)}\"\r\n"
    body << "Content-Type: #{mime}\r\n\r\n"
    body << f.read
    body << "\r\n"
  end
  body << "--#{boundary}--\r\n"

  req = Net::HTTP::Post.new(uri.request_uri)
  req["Content-Type"]  = "multipart/form-data; boundary=#{boundary}"
  req["Authorization"] = "Bearer #{@api_key}"
  req.body = body
  resp = http.request(req)
  if resp.is_a?(Net::HTTPSuccess)
    resp.body.to_s.empty? ? {} : JSON.parse(resp.body)
  else
    raise LLMAPIError, "#{provider_label} multipart API error: #{resp.code} - #{resp.body}"
  end
end

#stream_chat(url, body) ⇒ Object

POST with stream:true and yield each parsed SSE ‘data:` payload to the block.



107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/smart_prompt/concerns/http_client.rb', line 107

def stream_chat(url, body)
  uri = URI.parse(url)
  http = Net::HTTP.new(uri.host, uri.port)
  http.use_ssl = (uri.scheme == "https")
  http.open_timeout = 30
  http.read_timeout = 300

  req = Net::HTTP::Post.new(uri.request_uri)
  req["Content-Type"]  = "application/json"
  req["Authorization"] = "Bearer #{@api_key}"
  req["Accept"]        = "text/event-stream"
  req.body = body.to_json

  buffer = +""
  done = false
  http.request(req) do |response|
    unless response.is_a?(Net::HTTPSuccess)
      raise LLMAPIError, "#{provider_label} stream error: #{response.code} - #{response.body}"
    end
    response.read_body do |segment|
      break if done
      buffer << segment
      while (idx = buffer.index("\n"))
        line = buffer.slice!(0, idx + 1).strip
        next if line.empty? || !line.start_with?("data:")
        payload = line.sub(/\Adata:\s*/, "")
        if payload == "[DONE]"
          done = true
          break
        end
        begin
          yield JSON.parse(payload)
        rescue JSON::ParserError
          next
        end
      end
    end
  end
end