kie-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.
Files changed (51) hide show
  1. checksums.yaml +7 -0
  2. data/.claude/agents/project-environment-setup.md +149 -0
  3. data/.claude/commands/soba/implement.md +93 -0
  4. data/.claude/commands/soba/plan.md +98 -0
  5. data/.claude/commands/soba/review.md +91 -0
  6. data/.claude/commands/soba/revise.md +79 -0
  7. data/.devcontainer/LICENSE +21 -0
  8. data/.devcontainer/README.md +85 -0
  9. data/.devcontainer/bin/devcontainer-common.sh +36 -0
  10. data/.devcontainer/bin/down +55 -0
  11. data/.devcontainer/bin/init +159 -0
  12. data/.devcontainer/bin/rebuild +10 -0
  13. data/.devcontainer/bin/up +11 -0
  14. data/.devcontainer/compose.yaml +12 -0
  15. data/.devcontainer/compose.yaml.template +8 -0
  16. data/.devcontainer/devcontainer.json +30 -0
  17. data/.devcontainer/devcontainer.local.json +3 -0
  18. data/.devcontainer/scripts/setup.sh +12 -0
  19. data/.lefthook/rubocop-autofix.sh +51 -0
  20. data/.soba/config.yml +78 -0
  21. data/CHANGELOG.md +23 -0
  22. data/CLAUDE.md +20 -0
  23. data/LICENSE +21 -0
  24. data/README.md +310 -0
  25. data/Rakefile +12 -0
  26. data/docs/business/INDEX.md +3 -0
  27. data/docs/business/overview.md +35 -0
  28. data/docs/development/INDEX.md +3 -0
  29. data/docs/development/technical-design.md +77 -0
  30. data/docs/document_system.md +58 -0
  31. data/lefthook.yml +8 -0
  32. data/lib/kie/client.rb +36 -0
  33. data/lib/kie/errors.rb +43 -0
  34. data/lib/kie/general_engine.rb +56 -0
  35. data/lib/kie/general_task.rb +106 -0
  36. data/lib/kie/middleware/raise_error.rb +46 -0
  37. data/lib/kie/model_definition.rb +10 -0
  38. data/lib/kie/model_registry.rb +36 -0
  39. data/lib/kie/models/nano_banana_pro.rb +17 -0
  40. data/lib/kie/models/suno_v4.rb +14 -0
  41. data/lib/kie/models/suno_v4_5.rb +14 -0
  42. data/lib/kie/models/suno_v4_5_all.rb +14 -0
  43. data/lib/kie/models/suno_v4_5_plus.rb +14 -0
  44. data/lib/kie/models/suno_v5.rb +14 -0
  45. data/lib/kie/suno_engine.rb +129 -0
  46. data/lib/kie/suno_task.rb +99 -0
  47. data/lib/kie/task.rb +69 -0
  48. data/lib/kie/version.rb +5 -0
  49. data/lib/kie.rb +26 -0
  50. data/sig/kie.rbs +6 -0
  51. metadata +110 -0
data/README.md ADDED
@@ -0,0 +1,310 @@
1
+ # kie-ruby
2
+
3
+ [![CI](https://github.com/douhashi/kie-ruby/actions/workflows/ci.yml/badge.svg)](https://github.com/douhashi/kie-ruby/actions/workflows/ci.yml)
4
+ [![Gem Version](https://badge.fury.io/rb/kie-ruby.svg)](https://badge.fury.io/rb/kie-ruby)
5
+
6
+ A Ruby client library for interacting with the [Kie.ai](https://kie.ai) API. Supports both General models for image generation and Suno models for music generation.
7
+
8
+ ## Requirements
9
+
10
+ - Ruby 3.0 or higher
11
+
12
+ ## Installation
13
+
14
+ Add this line to your application's Gemfile:
15
+
16
+ ```ruby
17
+ gem "kie-ruby"
18
+ ```
19
+
20
+ And then execute:
21
+
22
+ ```bash
23
+ bundle install
24
+ ```
25
+
26
+ Or install it yourself as:
27
+
28
+ ```bash
29
+ gem install kie-ruby
30
+ ```
31
+
32
+ ## Quick Start
33
+
34
+ ### Setting up your API Key
35
+
36
+ Get your API key from [Kie.ai](https://kie.ai) and configure it:
37
+
38
+ **Option 1: Environment variable (Recommended)**
39
+
40
+ ```bash
41
+ export KIE_API_KEY=your_api_key_here
42
+ ```
43
+
44
+ ```ruby
45
+ require "kie"
46
+
47
+ client = Kie::Client.new
48
+ ```
49
+
50
+ **Option 2: Direct configuration**
51
+
52
+ ```ruby
53
+ require "kie"
54
+
55
+ client = Kie::Client.new(api_key: "your_api_key_here")
56
+ ```
57
+
58
+ ## Usage Examples
59
+
60
+ ### Image Generation (General API)
61
+
62
+ Generate images using the `nano-banana-pro` model:
63
+
64
+ ```ruby
65
+ require "kie"
66
+
67
+ client = Kie::Client.new
68
+
69
+ # Create an image generation task
70
+ task = client.general.generate(
71
+ model: "nano-banana-pro",
72
+ input: { prompt: "A serene Japanese garden with cherry blossoms" },
73
+ aspect_ratio: "16:9",
74
+ resolution: "2K",
75
+ output_format: "png"
76
+ )
77
+
78
+ # Wait for completion (polls automatically)
79
+ task.wait
80
+
81
+ # Get the generated image URLs
82
+ task.urls.each do |url|
83
+ puts url
84
+ end
85
+ ```
86
+
87
+ ### Music Generation (Suno API)
88
+
89
+ Generate music using the Suno V5 model:
90
+
91
+ **Simple mode (describe the song):**
92
+
93
+ ```ruby
94
+ require "kie"
95
+
96
+ client = Kie::Client.new
97
+
98
+ # Create a music generation task
99
+ task = client.suno.generate(
100
+ model: "V5",
101
+ input: {
102
+ prompt: "An upbeat electronic dance track with energetic synths",
103
+ callback_url: "https://your-server.com/webhook" # Optional webhook URL
104
+ }
105
+ )
106
+
107
+ # Wait for completion with progress monitoring
108
+ task.wait(timeout: 600, interval: 5)
109
+
110
+ # Get the generated audio and cover image URLs
111
+ puts "Audio files:"
112
+ task.audio_urls.each { |url| puts " #{url}" }
113
+
114
+ puts "Cover images:"
115
+ task.image_urls.each { |url| puts " #{url}" }
116
+ ```
117
+
118
+ **Custom mode (specify lyrics and style):**
119
+
120
+ ```ruby
121
+ task = client.suno.generate(
122
+ model: "V5",
123
+ input: {
124
+ custom_mode: true,
125
+ prompt: "[Verse]\nWalking through the city lights\n[Chorus]\nWe are alive tonight",
126
+ style: "Pop, Electronic, Upbeat",
127
+ title: "City Lights",
128
+ callback_url: "https://your-server.com/webhook"
129
+ }
130
+ )
131
+
132
+ task.wait(timeout: 600)
133
+
134
+ task.audio_urls.each { |url| puts url }
135
+ ```
136
+
137
+ ### Monitoring Task Progress
138
+
139
+ You can check task status manually instead of using `wait`:
140
+
141
+ ```ruby
142
+ task = client.general.generate(model: "nano-banana-pro", input: { prompt: "A sunset" })
143
+
144
+ loop do
145
+ task.refresh!
146
+
147
+ case task.status
148
+ when :processing
149
+ puts "Still processing..."
150
+ when :success
151
+ puts "Done! URLs: #{task.urls}"
152
+ break
153
+ when :failed
154
+ puts "Failed: #{task.error_message}"
155
+ break
156
+ end
157
+
158
+ sleep 3
159
+ end
160
+ ```
161
+
162
+ ## Error Handling
163
+
164
+ The library provides structured exception classes for different error scenarios:
165
+
166
+ ```ruby
167
+ require "kie"
168
+
169
+ client = Kie::Client.new
170
+
171
+ begin
172
+ task = client.general.generate(
173
+ model: "nano-banana-pro",
174
+ input: { prompt: "A beautiful landscape" }
175
+ )
176
+ task.wait
177
+
178
+ puts task.urls
179
+ rescue Kie::AuthenticationError => e
180
+ # Invalid or missing API key (HTTP 401)
181
+ puts "Authentication failed: #{e.message}"
182
+
183
+ rescue Kie::InsufficientCreditsError => e
184
+ # Not enough credits (HTTP 402)
185
+ puts "Insufficient credits. Please top up your account."
186
+
187
+ rescue Kie::RateLimitError => e
188
+ # Too many requests (HTTP 429)
189
+ puts "Rate limited. Please wait and retry."
190
+ sleep 10
191
+ retry
192
+
193
+ rescue Kie::ContentPolicyError => e
194
+ # Content violates policy (e.g., SENSITIVE_WORD_ERROR)
195
+ puts "Content policy violation: #{e.message}"
196
+
197
+ rescue Kie::TimeoutError => e
198
+ # Task did not complete within the timeout period
199
+ puts "Task timed out"
200
+
201
+ rescue Kie::TaskFailedError => e
202
+ # Task failed during processing
203
+ puts "Task failed: #{e.message}"
204
+ puts "Fail code: #{e.fail_code}"
205
+
206
+ rescue Kie::ApiError => e
207
+ # Other API errors
208
+ puts "API error (#{e.status_code}): #{e.message}"
209
+ end
210
+ ```
211
+
212
+ ### Handling Task Failures Without Exceptions
213
+
214
+ If you prefer to handle failures without exceptions:
215
+
216
+ ```ruby
217
+ task = client.suno.generate(
218
+ model: "V5",
219
+ input: { prompt: "A jazz song", callback_url: "https://your-server.com/webhook" }
220
+ )
221
+ task.wait(raise_on_failure: false)
222
+
223
+ if task.failed?
224
+ puts "Task failed: #{task.error_message}"
225
+ else
226
+ puts "Success! #{task.urls}"
227
+ end
228
+ ```
229
+
230
+ ## Supported Models
231
+
232
+ ### Suno Models (Music Generation)
233
+
234
+ | Model | Description |
235
+ |:------|:------------|
236
+ | `V4` | Suno V4 |
237
+ | `V4_5` | Suno V4.5 |
238
+ | `V4_5PLUS` | Suno V4.5+ |
239
+ | `V4_5ALL` | Suno V4.5 ALL |
240
+ | `V5` | Suno V5 (Latest) |
241
+
242
+ **Suno model input parameters:**
243
+
244
+ | Parameter | Type | Description |
245
+ |:----------|:-----|:------------|
246
+ | `prompt` | String | Song description or lyrics (required) |
247
+ | `callback_url` | String | Webhook URL for notifications (optional) |
248
+ | `custom_mode` | Boolean | Enable custom mode for lyrics input |
249
+ | `style` | String | Music style (custom mode only) |
250
+ | `title` | String | Song title (custom mode only) |
251
+ | `instrumental` | Boolean | Generate instrumental only |
252
+
253
+ ### General Models (Image Generation)
254
+
255
+ | Model | Description |
256
+ |:------|:------------|
257
+ | `nano-banana-pro` | High-quality image generation |
258
+
259
+ **General model parameters:**
260
+
261
+ | Parameter | Values | Default |
262
+ |:----------|:-------|:--------|
263
+ | `aspect_ratio` | `1:1`, `2:3`, `3:2`, `3:4`, `4:3`, `4:5`, `5:4`, `9:16`, `16:9`, `21:9`, `auto` | `1:1` |
264
+ | `resolution` | `1K`, `2K`, `4K` | `1K` |
265
+ | `output_format` | `png`, `jpg` | `png` |
266
+
267
+ ## Platform Limits
268
+
269
+ | Item | Limit |
270
+ |:-----|:------|
271
+ | Rate limit | 20 requests per 10 seconds |
272
+ | Concurrent tasks | 100 |
273
+ | Polling rate | 3 requests per second per task |
274
+ | File retention | 14 days |
275
+
276
+ ## Development
277
+
278
+ ### Setup
279
+
280
+ After checking out the repo, run the setup script to install dependencies:
281
+
282
+ ```bash
283
+ bin/setup
284
+ ```
285
+
286
+ ### Running Tests
287
+
288
+ ```bash
289
+ bundle exec rspec
290
+ ```
291
+
292
+ ### Linting
293
+
294
+ ```bash
295
+ bundle exec rubocop
296
+ ```
297
+
298
+ ### Interactive Console
299
+
300
+ ```bash
301
+ bin/console
302
+ ```
303
+
304
+ ## Contributing
305
+
306
+ Bug reports and pull requests are welcome on GitHub at https://github.com/douhashi/kie-ruby.
307
+
308
+ ## License
309
+
310
+ This gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ require "rubocop/rake_task"
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ task default: %i[spec rubocop]
@@ -0,0 +1,3 @@
1
+ # Business Documents
2
+
3
+ - `overview.md`: プロジェクト概要、設計思想、プラットフォーム仕様
@@ -0,0 +1,35 @@
1
+ # Kie.ai Ruby Client: プロジェクト概要
2
+
3
+ ## 背景
4
+ Kie.ai は、Suno(音楽生成)、Grok Imagine(動画生成)、Nano Banana Pro(画像生成)など、多様な AI モデルを統合提供するプラットフォームです。API の進化過程で生じた「Suno(音楽生成用)」と「General(画像/動画生成用)」の2つのフォーマットが混在しており、本 Gem はその差異を抽象化し、Ruby らしい統一的なインターフェースを提供します。
5
+
6
+ ## 設計思想
7
+
8
+ ### 🎯 ミニマリスト原則
9
+ - **Zero Bloat**: Zeitwerk や ActiveSupport 等の大型ライブラリを排除
10
+ - **Explicit Over Implicit**: 依存関係を明示的に定義、副作用の最小化
11
+ - **Lean & Robust**: 実行時依存は Faraday のみ
12
+
13
+ ### 💎 Ruby らしさの追求
14
+ - `task.wait { |status| ... }` のようなブロック構文での進捗監視
15
+ - DSL 的な記述を可能にする設計
16
+ - 慣習に従った命名規則とメソッドチェーン
17
+
18
+ ### 🔍 透明性の確保
19
+ - API エラーの詳細を構造化された例外として報告
20
+ - デバッグしやすい透明性の高い実装
21
+ - データ駆動型設計で新モデル追加を容易に
22
+
23
+ ## プラットフォーム仕様
24
+
25
+ ### 制約事項
26
+ | 項目 | 仕様 |
27
+ |:---|:---|
28
+ | **データ保持** | 生成ファイル: 14日間 / ログ: 2ヶ月間 |
29
+ | **レート制限** | 10秒間に20リクエスト / 並行100タスク |
30
+ | **ポーリング** | 同一タスク: 1秒間に3リクエスト |
31
+ | **処理方式** | 全タスク非同期(task_id による追跡) |
32
+
33
+ ### モデル分類
34
+ - **Suno (音楽生成)**: V4, V4_5, V4_5ALL, V4_5PLUS, V5
35
+ - **General (画像/動画生成)**: nano-banana-pro, grok-imagine/text-to-video 等
@@ -0,0 +1,3 @@
1
+ # Development Documents
2
+
3
+ - `technical-design.md`: アーキテクチャ、技術設計、開発環境
@@ -0,0 +1,77 @@
1
+ # Kie.ai Ruby Client: 技術設計
2
+
3
+ ## アーキテクチャ
4
+
5
+ ### デュアルエンジン構造
6
+ Suno と General の2つの API フォーマットをアダプター層で吸収:
7
+
8
+ ```
9
+ User Code
10
+
11
+ Kie::Client (統一インターフェース)
12
+
13
+ ModelRegistry (ルーティング判定)
14
+ / \
15
+ Suno API General API
16
+ (音楽) (画像等)
17
+ ```
18
+
19
+ ### 主要コンポーネント
20
+
21
+ #### 1. Kie::ModelRegistry
22
+ 各モデルの API 系統を管理:
23
+ - Suno: `/api/v1/generate`
24
+ - General: `/api/v1/jobs/createTask`
25
+
26
+ #### 2. Kie::Task
27
+ レスポンス構造の正規化:
28
+ - Suno: `data.response.sunoData[].audioUrl`
29
+ - General: `data.resultJson` → parse → `resultUrls[]`
30
+
31
+ #### 3. Kie::Poller
32
+ 指数バックオフによるポーリング:
33
+ - 0-30秒: 2-3秒間隔
34
+ - 30秒以降: 5-10秒間隔
35
+
36
+ ## エラーハンドリング
37
+
38
+ ### HTTP ステータス
39
+ | コード | 意味 | 対処 |
40
+ |:---|:---|:---|
41
+ | 401 | 認証エラー | API キー確認 |
42
+ | 402 | クレジット不足 | チャージ必要 |
43
+ | 429 | レート制限 | リトライ |
44
+ | 455 | メンテナンス | 待機 |
45
+
46
+ ### タスクエラー
47
+ - Suno: `msg` フィールド
48
+ - General: `failMsg` フィールド
49
+ - 著作権/ポリシー違反は専用例外クラス
50
+
51
+ ## 開発環境
52
+
53
+ ### 依存関係
54
+ **実行時:**
55
+ - Faraday (HTTP 通信のみ)
56
+
57
+ **開発時:**
58
+ - RSpec (テスト)
59
+ - WebMock (API モック)
60
+ - RuboCop (コード品質)
61
+
62
+ ### CI/CD
63
+ GitHub Actions による自動化:
64
+ - Ruby 3.0〜3.3 での互換性テスト
65
+ - RuboCop による静的解析
66
+ - Bundler Audit によるセキュリティチェック
67
+ - SimpleCov によるカバレッジ測定
68
+
69
+ ### 拡張方法
70
+ 新モデル追加は定義ファイルの編集のみ:
71
+ ```yaml
72
+ model_name: "new-model"
73
+ format_type: "Standard"
74
+ validations:
75
+ input_length: 1000
76
+ aspect_ratios: ["16:9", "1:1"]
77
+ ```
@@ -0,0 +1,58 @@
1
+ You are part of a documentation-aware AI system.
2
+
3
+ This project uses a structured **Document System** under the `docs/` directory to manage business and technical documents in a hierarchical and searchable format.
4
+
5
+ ## Folder Structure Overview
6
+
7
+ ```
8
+ docs/
9
+ ├── business/ # Business documents
10
+ │ ├── INDEX.md # Index file for this folder
11
+ │ ├── overview.md # Project overview
12
+ │ └── model.md # Business model description
13
+
14
+ ├── development/ # Development-related documents
15
+ │ ├── INDEX.md # Index file for this folder
16
+ │ ├── guideline.md # Development guide
17
+ │ └── coding-rule.md # Coding standards
18
+
19
+ ├── operations/ # (Planned) Operational documents
20
+ │ ├── INDEX.md # Index file for this folder
21
+ │ ├── server.md # Server operations
22
+ │ └── monitoring.md # Monitoring and incident response
23
+ ```
24
+
25
+ ## Document Characteristics
26
+
27
+ - All documents are written in Markdown format.
28
+ - Each directory contains an `INDEX.md` file listing:
29
+ - The filenames in the same directory
30
+ - A brief description for each
31
+
32
+ Example:
33
+ ```markdown
34
+ # Development Documents
35
+
36
+ - `guideline.md`: A guide to the development workflow.
37
+ - `coding-rule.md`: Coding standards for this project.
38
+ ```
39
+
40
+ ## Integration with CLAUDE.md
41
+
42
+ The AI system does **not directly reference documents**. Instead, it recognizes document availability via `CLAUDE.md`, where paths are listed using the format:
43
+
44
+ ```
45
+ @docs/development/INDEX.md
46
+ ```
47
+
48
+ This tells the AI:
49
+ - The document exists
50
+ - It may be referenced when needed
51
+ - But the AI should **autonomously decide** which document to consult
52
+
53
+ ## Purpose
54
+
55
+ The Document System allows AI agents to:
56
+ - Navigate structured, maintainable documentation
57
+ - Understand the project context through index files
58
+ - Autonomously choose and reference relevant documents during tasks
data/lefthook.yml ADDED
@@ -0,0 +1,8 @@
1
+ # Lefthook configuration
2
+ # https://github.com/evilmartians/lefthook
3
+
4
+ pre-commit:
5
+ commands:
6
+ rubocop:
7
+ glob: "*.rb"
8
+ run: .lefthook/rubocop-autofix.sh {staged_files}
data/lib/kie/client.rb ADDED
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "faraday"
4
+
5
+ module Kie
6
+ # HTTP client for Kie.ai API
7
+ class Client
8
+ BASE_URL = "https://api.kie.ai"
9
+
10
+ attr_reader :api_key
11
+
12
+ def initialize(api_key: nil)
13
+ @api_key = api_key || ENV.fetch("KIE_API_KEY", nil)
14
+
15
+ return unless @api_key.nil? || @api_key.empty?
16
+
17
+ raise ConfigurationError, "API key is required. Pass it as an argument or set KIE_API_KEY environment variable."
18
+ end
19
+
20
+ def connection
21
+ @connection ||= Faraday.new(url: BASE_URL) do |conn|
22
+ conn.headers["Authorization"] = "Bearer #{api_key}"
23
+ conn.headers["Content-Type"] = "application/json"
24
+ conn.use Middleware::RaiseError
25
+ end
26
+ end
27
+
28
+ def general
29
+ @general ||= GeneralEngine.new(client: self)
30
+ end
31
+
32
+ def suno
33
+ @suno ||= SunoEngine.new(client: self)
34
+ end
35
+ end
36
+ end
data/lib/kie/errors.rb ADDED
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kie
4
+ class ConfigurationError < Error; end
5
+ class UnknownModelError < Error; end
6
+ class ValidationError < Error; end
7
+
8
+ # Base class for API-related errors with HTTP status and response details
9
+ class ApiError < Error
10
+ attr_reader :status_code, :response_body
11
+
12
+ def initialize(message = nil, status_code: nil, response_body: nil)
13
+ super(message)
14
+ @status_code = status_code
15
+ @response_body = response_body
16
+ end
17
+ end
18
+
19
+ class TaskCreationError < ApiError; end
20
+ class NotFoundError < ApiError; end
21
+ class InsufficientCreditsError < ApiError; end
22
+ class RateLimitError < ApiError; end
23
+ class InvalidParametersError < ApiError; end
24
+ class AuthenticationError < ApiError; end
25
+ class ServerError < ApiError; end
26
+ class ServiceUnavailableError < ApiError; end
27
+
28
+ # Raised when an asynchronous task fails during execution
29
+ class TaskFailedError < ApiError
30
+ attr_reader :fail_code
31
+
32
+ def initialize(message = nil, fail_code: nil, status_code: nil, response_body: nil)
33
+ super(message, status_code: status_code, response_body: response_body)
34
+ @fail_code = fail_code
35
+ end
36
+ end
37
+
38
+ # Raised when content violates policy (SENSITIVE_WORD_ERROR, CONTENT_POLICY, etc.)
39
+ class ContentPolicyError < TaskFailedError; end
40
+
41
+ # Raised when a task wait operation times out
42
+ class TimeoutError < Error; end
43
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module Kie
6
+ # Engine for General API endpoints (/api/v1/jobs/*)
7
+ class GeneralEngine
8
+ CREATE_TASK_ENDPOINT = "/api/v1/jobs/createTask"
9
+
10
+ def initialize(client:)
11
+ @client = client
12
+ end
13
+
14
+ def generate(model:, input:, **parameters)
15
+ definition = ModelRegistry.find(model)
16
+ response = @client.connection.post(CREATE_TASK_ENDPOINT) do |req|
17
+ req.body = build_request_body(definition, input, parameters)
18
+ end
19
+
20
+ handle_response(response)
21
+ end
22
+
23
+ private
24
+
25
+ def build_request_body(definition, input, parameters)
26
+ merged_input = input.merge(parameters)
27
+ merged_input[:callBackUrl] ||= "https://example.invalid/callback"
28
+
29
+ {
30
+ model: definition.name,
31
+ input: merged_input
32
+ }.to_json
33
+ end
34
+
35
+ def handle_response(response)
36
+ data = JSON.parse(response.body)
37
+ handle_api_error(data) unless data["code"] == 200
38
+
39
+ GeneralTask.new(client: @client, task_id: data["data"]["taskId"])
40
+ end
41
+
42
+ def handle_api_error(data)
43
+ error_class = error_class_for_code(data["code"])
44
+ raise error_class.new(data["msg"], status_code: data["code"], response_body: data.to_json)
45
+ end
46
+
47
+ def error_class_for_code(code)
48
+ case code
49
+ when 400 then InvalidParametersError
50
+ when 402 then InsufficientCreditsError
51
+ when 429 then RateLimitError
52
+ else ApiError
53
+ end
54
+ end
55
+ end
56
+ end