KKiosapi.id

← Dokumentasi

Panduan Ruby

Kiosapi kompatibel OpenAI lewat REST biasa. Contoh di bawah pakai Net::HTTP + json standar (tanpa gem tambahan) — untuk kasus chat sederhana, gem ruby-openai juga kompatibel (lihat contoh singkat di /docs).

Daftar isi

1. Persiapan

Masuk ke dashboard → Kunci API, beri nama, klik Buat key, salin kios_live_… (ditampilkan sekali).

Keamanan: panggil API dari server, bukan dari kode yang jalan di browser. Simpan key sebagai environment variable, jangan hardcode.
export KIOSAPI_API_KEY="kios_live_xxxxxxxxxxxx"

2. Fungsi helper request

require "net/http"
require "json"
require "uri"

BASE = "https://api.kiosapi.id/v1"
API_KEY = ENV.fetch("KIOSAPI_API_KEY")

def kiosapi_post(path, body)
  uri = URI("#{BASE}#{path}")
  req = Net::HTTP::Post.new(uri, "Authorization" => "Bearer #{API_KEY}", "Content-Type" => "application/json")
  req.body = body.to_json
  res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
  JSON.parse(res.body)
end

def kiosapi_get(path)
  uri = URI("#{BASE}#{path}")
  req = Net::HTTP::Get.new(uri, "Authorization" => "Bearer #{API_KEY}")
  res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
  JSON.parse(res.body)
end

Model ID berformat provider/nama-model — lihat daftar lengkap via GET /v1/models atau /pricing.

3. Chat completion dasar

resp = kiosapi_post("/chat/completions", {
  model: "anthropic/claude-sonnet-4-6",
  messages: [
    { role: "system", content: "Kamu asisten yang ringkas dan ramah." },
    { role: "user", content: "Jelaskan apa itu RAG dalam 2 kalimat." },
  ],
})
puts resp.dig("choices", 0, "message", "content")
puts "Token dipakai: #{resp.dig('usage', 'total_tokens')}"

4. Streaming

Tambahkan stream: true dan baca respons per-baris (SSE) pakai request_get/request_post dengan block streaming.

uri = URI("#{BASE}/chat/completions")
req = Net::HTTP::Post.new(uri, "Authorization" => "Bearer #{API_KEY}", "Content-Type" => "application/json")
req.body = {
  model: "deepseek/deepseek-v4-flash",
  messages: [{ role: "user", content: "Tulis puisi pendek tentang hujan di Jakarta." }],
  stream: true,
}.to_json

Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
  http.request(req) do |res|
    res.read_body do |chunk|
      chunk.each_line do |line|
        next unless line.start_with?("data: ")
        data = line.sub("data: ", "").strip
        next if data == "[DONE]"
        json = JSON.parse(data)
        print json.dig("choices", 0, "delta", "content")
      end
    end
  end
end

Model reasoning (🧠) bisa mengirim delta reasoning_content terpisah dari content — cek key itu di JSON delta untuk status "sedang berpikir".

5. Tool calling / function calling

Format sama persis OpenAI — didukung untuk model bertanda 🔧 di katalog, termasuk Claude & Gemini (diterjemahkan otomatis oleh gateway).

tools = [{
  type: "function",
  function: {
    name: "get_weather",
    description: "Ambil cuaca terkini untuk sebuah kota",
    parameters: {
      type: "object",
      properties: { city: { type: "string", description: "Nama kota" } },
      required: ["city"],
    },
  },
}]

messages = [{ role: "user", content: "Cuaca di Bandung sekarang gimana?" }]
resp = kiosapi_post("/chat/completions", { model: "openai/gpt-4o", messages: messages, tools: tools })

msg = resp.dig("choices", 0, "message")
if msg["tool_calls"]
  msg["tool_calls"].each do |call|
    puts "Model minta panggil: #{call.dig('function', 'name')} #{call.dig('function', 'arguments')}"
    messages << msg
    messages << {
      role: "tool", tool_call_id: call["id"],
      content: { suhu_celsius: 27, kondisi: "berawan" }.to_json,
    }
  end
  followup = kiosapi_post("/chat/completions", { model: "openai/gpt-4o", messages: messages })
  puts followup.dig("choices", 0, "message", "content")
end

6. Vision (kirim gambar ke model)

Kirim gambar lewat content berbentuk array (image_url + text) — format persis yang dipakai perintah lihat di Kiosapi CLI.

require "base64"

b64 = Base64.strict_encode64(File.read("foto.jpg"))

resp = kiosapi_post("/chat/completions", {
  model: "google/gemini-2.5-flash", # pilih model yang mendukung vision
  messages: [{
    role: "user",
    content: [
      { type: "text", text: "Ada apa saja di foto ini?" },
      { type: "image_url", image_url: { url: "data:image/jpeg;base64,#{b64}" } },
    ],
  }],
})
puts resp.dig("choices", 0, "message", "content")

Catatan: fitur ini sudah jalan di level API/CLI. Tombol upload gambar langsung di dashboard web belum tersedia.

7. Structured output / JSON mode

response_format diteruskan langsung ke provider upstream — jalan penuh untuk model yang mendukungnya secara native. Belum ada lapisan pemaksaan JSON yang seragam untuk semua model di katalog.

resp = kiosapi_post("/chat/completions", {
  model: "openai/gpt-4o",
  messages: [{ role: "user", content: "Beri saya data profil singkat dalam JSON: nama, umur." }],
  response_format: { type: "json_object" },
})
data = JSON.parse(resp.dig("choices", 0, "message", "content"))
p data

8. Embeddings

resp = kiosapi_post("/embeddings", {
  model: "openai/text-embedding-3-small",
  input: "Selamat datang di Kiosapi!",
})
vector = resp.dig("data", 0, "embedding")
puts "#{vector.length} dimensi"

9. Daftar model & cek saldo

models = kiosapi_get("/models")

# Saldo & kuota gratis harian (endpoint khusus Kiosapi)
saldo = kiosapi_get("/saldo")
p saldo

10. Endpoint khusus Kiosapi

Bentuknya beda dari chat completions standar, tapi tetap lewat helper kiosapi_post/kiosapi_get yang sama.

Gambar — POST /v1/images/generations (sinkron)

data = kiosapi_post("/images/generations", {
  model: "google/imagen-3",
  prompt: "kucing oranye memakai topi koki, fotorealistik",
  option: "standard",
  n: 1,
})
File.binwrite("hasil.png", Base64.decode64(data.dig("data", 0, "b64_json")))
puts "Biaya: #{data.dig('kiosapi', 'cost_rupiah')} rupiah"

Video — POST /v1/videos/generations (asinkron, perlu polling)

submit = kiosapi_post("/videos/generations", {
  model: "alibaba/wan2.7-t2v",
  prompt: "ombak pantai saat matahari terbenam, sinematik",
  option: "standard",
  duration_seconds: 5,
})

job = nil
loop do
  sleep 5
  job = kiosapi_get("/jobs/#{submit['job_id']}")
  break unless job["status"] == "running"
end

if job["status"] == "succeeded"
  puts "Video siap: #{job['video_url']}"
else
  puts "Gagal: #{job['error']}"
end

Image-to-video: sertakan image (base64) + image_mime di body request.

Musik — POST /v1/music/generations

resp = kiosapi_post("/music/generations", {
  model: "minimax/music-2.6",
  prompt: "pop akustik Indonesia yang hangat, vokal wanita",
  lyrics: "[Verse]\nPagi cerah di kota\n[Chorus]\nBersama kita bisa",
})
# poll GET /jobs/#{resp['job_id']} sampai status "succeeded" → field "audio_url"

Text-to-speech — POST /v1/audio/speech

uri = URI("#{BASE}/audio/speech")
req = Net::HTTP::Post.new(uri, "Authorization" => "Bearer #{API_KEY}", "Content-Type" => "application/json")
req.body = {
  model: "minimax/speech-2.8-turbo",
  input: "Selamat datang di Kiosapi!",
  voice: "Indonesian_CalmWoman", # 9 suara asli Indonesia (model MiniMax)
}.to_json
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
File.binwrite("suara.mp3", res.body)

Reranking — POST /v1/rerank (bentuk Cohere, self-hosted)

res = kiosapi_post("/rerank", {
  model: "baai/bge-reranker-base",
  query: "apa itu kucing?",
  documents: [
    "Kucing adalah hewan mamalia berkaki empat.",
    "Mobil listrik semakin populer di Indonesia.",
  ],
  return_documents: true,
})
res["results"].each { |r| puts "#{r['index']}: #{r['relevance_score']}" }

Vector DB — POST /v1/vector-indexes/... (bentuk Pinecone, self-hosted)

# 1) Bikin index (gratis)
kiosapi_post("/vector-indexes", { name: "artikel-saya" })

# 2) Buat embedding lalu upsert (values wajib 1536 angka, cocok text-embedding-3-small)
emb = kiosapi_post("/embeddings", { model: "openai/text-embedding-3-small", input: "Isi artikel di sini" })
vector = emb.dig("data", 0, "embedding")

kiosapi_post("/vector-indexes/artikel-saya/vectors/upsert", {
  vectors: [{ id: "artikel-1", values: vector, metadata: { kategori: "berita" } }],
})

# 3) Cari yang paling relevan
hasil = kiosapi_post("/vector-indexes/artikel-saya/query", {
  vector: vector, top_k: 5, include_metadata: true,
})
p hasil["matches"]

11. Penanganan error

def kiosapi_post_checked(path, body)
  uri = URI("#{BASE}#{path}")
  req = Net::HTTP::Post.new(uri, "Authorization" => "Bearer #{API_KEY}", "Content-Type" => "application/json")
  req.body = body.to_json
  res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
  json = JSON.parse(res.body)
  raise "Kiosapi error #{res.code}: #{json.dig('error', 'message') || res.body}" if res.code.to_i >= 400
  json
end

Kode HTTP umum: 401 API key salah, 400 model tak dikenal/parameter salah, 402 saldo kurang atau batas pengeluaran bulanan tercapai, 422 ditolak moderasi, 429 rate limit, 502/504 provider upstream bermasalah/timeout. Kode error media lengkap ada di /docs.

12. Rate limit & kuota

FreeBerbayar
Rate limit5 req/menit60 req/menit
Kuota harian50 request/hari (reset 07:00 WIB), email terverifikasitanpa batas harian
Output maks~2048 tokensesuai maxOutput model
Input maks~24k karaktersesuai kapasitas model

13. Contoh lengkap — mini pipeline RAG

Menggabungkan embeddings + vector DB + chat dalam satu alur.

def embed(text)
  kiosapi_post("/embeddings", { model: "openai/text-embedding-3-small", input: text }).dig("data", 0, "embedding")
end

# Index dokumen (sekali saja)
kiosapi_post("/vector-indexes", { name: "basis-pengetahuan" })
dokumen = ["Kiosapi adalah AI API gateway Indonesia.", "Kiosapi mendukung 100+ model AI."]
vectors = dokumen.each_with_index.map { |d, i| { id: "doc-#{i}", values: embed(d), metadata: { text: d } } }
kiosapi_post("/vector-indexes/basis-pengetahuan/vectors/upsert", { vectors: vectors })

# Query + jawab pakai konteks yang relevan
pertanyaan = "Apa itu Kiosapi?"
hasil = kiosapi_post("/vector-indexes/basis-pengetahuan/query", {
  vector: embed(pertanyaan), top_k: 2, include_metadata: true,
})
konteks = hasil["matches"].map { |m| m.dig("metadata", "text") }.join("\n")

jawaban = kiosapi_post("/chat/completions", {
  model: "anthropic/claude-sonnet-4-6",
  messages: [
    { role: "system", content: "Jawab berdasar konteks ini:\n#{konteks}" },
    { role: "user", content: pertanyaan },
  ],
})
puts jawaban.dig("choices", 0, "message", "content")

Referensi lain: spesifikasi OpenAPI 3.1 lengkap, harga & daftar model di /pricing, dan dokumentasi utama di /docs.