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.
- checksums.yaml +7 -0
- data/.claude/agents/project-environment-setup.md +149 -0
- data/.claude/commands/soba/implement.md +93 -0
- data/.claude/commands/soba/plan.md +98 -0
- data/.claude/commands/soba/review.md +91 -0
- data/.claude/commands/soba/revise.md +79 -0
- data/.devcontainer/LICENSE +21 -0
- data/.devcontainer/README.md +85 -0
- data/.devcontainer/bin/devcontainer-common.sh +36 -0
- data/.devcontainer/bin/down +55 -0
- data/.devcontainer/bin/init +159 -0
- data/.devcontainer/bin/rebuild +10 -0
- data/.devcontainer/bin/up +11 -0
- data/.devcontainer/compose.yaml +12 -0
- data/.devcontainer/compose.yaml.template +8 -0
- data/.devcontainer/devcontainer.json +30 -0
- data/.devcontainer/devcontainer.local.json +3 -0
- data/.devcontainer/scripts/setup.sh +12 -0
- data/.lefthook/rubocop-autofix.sh +51 -0
- data/.soba/config.yml +78 -0
- data/CHANGELOG.md +23 -0
- data/CLAUDE.md +20 -0
- data/LICENSE +21 -0
- data/README.md +310 -0
- data/Rakefile +12 -0
- data/docs/business/INDEX.md +3 -0
- data/docs/business/overview.md +35 -0
- data/docs/development/INDEX.md +3 -0
- data/docs/development/technical-design.md +77 -0
- data/docs/document_system.md +58 -0
- data/lefthook.yml +8 -0
- data/lib/kie/client.rb +36 -0
- data/lib/kie/errors.rb +43 -0
- data/lib/kie/general_engine.rb +56 -0
- data/lib/kie/general_task.rb +106 -0
- data/lib/kie/middleware/raise_error.rb +46 -0
- data/lib/kie/model_definition.rb +10 -0
- data/lib/kie/model_registry.rb +36 -0
- data/lib/kie/models/nano_banana_pro.rb +17 -0
- data/lib/kie/models/suno_v4.rb +14 -0
- data/lib/kie/models/suno_v4_5.rb +14 -0
- data/lib/kie/models/suno_v4_5_all.rb +14 -0
- data/lib/kie/models/suno_v4_5_plus.rb +14 -0
- data/lib/kie/models/suno_v5.rb +14 -0
- data/lib/kie/suno_engine.rb +129 -0
- data/lib/kie/suno_task.rb +99 -0
- data/lib/kie/task.rb +69 -0
- data/lib/kie/version.rb +5 -0
- data/lib/kie.rb +26 -0
- data/sig/kie.rbs +6 -0
- metadata +110 -0
data/README.md
ADDED
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
# kie-ruby
|
|
2
|
+
|
|
3
|
+
[](https://github.com/douhashi/kie-ruby/actions/workflows/ci.yml)
|
|
4
|
+
[](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,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,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
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
|