Lompat ke konten
dokumentasi.id
Commit d59d965
v3
Star763

5

Endpoint POST /llama

Endpoint POST /llama adalah satu-satunya jalur yang menjalankan logika di backend. Handler-nya berada di src/routes/llama/+server.js: sebuah client OpenAI dibuat sekali di scope modul dari OPENAI_API_KEY src/routes/llama/+server.js:1-7, lalu setiap request dibaca body-nya, dicocokkan ke tabel roasts, atau diteruskan ke model gpt-4o-mini dengan stream: false src/routes/llama/+server.js:179-191. Pemanggil tunggalnya adalah handleRoast di UI, yang mengirim Content-Type: application/json dan membaca data.roast src/routes/+page.svelte:39-52.

Kontrak request

Handler langsung memanggil await request.json() tanpa pembungkus try src/routes/llama/+server.js:29-31. Body yang bukan JSON karena itu melempar error di luar kendali handler, dan tidak pernah sampai ke pemeriksaan language.

FieldTipeWajibPeran
usernamestringyaDisisipkan ke path https://api.github.com/users/${username} dan ke teks prompt; bentuknya tidak divalidasi
languagestringyaKunci keanggotaan validLanguages dan nilai kolom roasts.language

Hanya 11 nilai language yang diterima: english, indonesian, indian, chinese, japanese, korean, french, polish, vietnamese, arabic, dan traditionalChinese src/routes/llama/+server.js:15-27. Nilai di luar daftar itu langsung dijawab 400 sebelum satu pun panggilan ke GitHub atau OpenAI terjadi src/routes/llama/+server.js:33-38.

Daftar languages di UI lebih panjang daripada validLanguages: UI juga menawarkan german, italian, finnish, dan portuguese src/routes/+page.svelte:12-28. Keempat nilai itu dijawab 400, meskipun switch prompt di berkas handler memiliki cabangnya. Isi tiap cabang dibahas di Prompt Multibahasa.

Siklus hidup request

Setiap request melewati tiga gerbang berurutan: validasi language, pembacaan cache di tabel roasts, lalu pemanggilan GitHub dan OpenAI. Hanya jalur tanpa baris cache yang menghasilkan baris baru.

stateDiagram-v2
    [*] --> ParseBody
    ParseBody --> InvalidLanguage: language not in validLanguages
    InvalidLanguage --> [*]: "400 invalid language specified, please pass a valid language."
    ParseBody --> CacheLookup: username + language
    CacheLookup --> CacheHit: DB.first() returns a row
    CacheHit --> [*]: "200 { roast, username, language }"
    CacheLookup --> GitHubProfile: no row
    GitHubProfile --> UpstreamTrouble: response.ok is false
    UpstreamTrouble --> [*]: "500 Our roast machine is in trouble"
    GitHubProfile --> Generate: profile + repos + readme
    Generate --> Persist: choices[0].message.content
    Persist --> [*]: "200 { roast }"
    Generate --> GenerateFailed: create() throws
    GenerateFailed --> [*]: "500 Failed to generate roast"
    style ParseBody fill:#f59e0b,color:#000
    style CacheLookup fill:#8b5cf6,color:#fff
    style CacheHit fill:#2d7d46,color:#fff
    style Persist fill:#336791,color:#fff
    style GitHubProfile fill:#f59e0b,color:#000
    style Generate fill:#2d7d46,color:#fff
    style InvalidLanguage fill:#dc2626,color:#fff
    style UpstreamTrouble fill:#dc2626,color:#fff
    style GenerateFailed fill:#dc2626,color:#fff
%% fig: Siklus hidup request POST /llama
%% desc: Request divalidasi, dicek ke tabel roasts, lalu berakhir di cache hit, roast baru, atau salah satu respons error.
Fig. 1: Siklus hidup request POST /llamaRequest divalidasi, dicek ke tabel roasts, lalu berakhir di cache hit, roast baru, atau salah satu respons error.

Kontrak response

Semua respons berbentuk JSON. Perhatikan bahwa dua jalur 200 mengembalikan bentuk body yang berbeda.

StatusBodyKondisiRujukan
200{ roast, username, language }DB.prepare(...).first() menemukan baris untuk pasangan gh_username + languagesrc/routes/llama/+server.js:40-47
200{ roast }completion.choices[0].message.content tersediasrc/routes/llama/+server.js:193-207
400{ error: 'invalid language specified, please pass a valid language.' }language tidak ada di validLanguagessrc/routes/llama/+server.js:33-38
500{ error: 'Our roast machine is in trouble' }fetch profil GitHub selesai dengan response.ok === falsesrc/routes/llama/+server.js:62-66
500{ error: 'Failed to generate roast' }client.chat.completions.create melempar errorsrc/routes/llama/+server.js:209-212

Status 400 dan 500 sama-sama membawa kunci error, bukan roast. handleRoast memeriksa response.ok lebih dulu, sehingga body error tidak pernah ditampilkan ke pembaca; ia jatuh ke pesan cadangan di UI src/routes/+page.svelte:39-52.

Perilaku cache

cache di sini adalah tabel D1, bukan cache HTTP. Sebelum menyentuh GitHub, handler menjalankan SELECT * FROM roasts WHERE gh_username = ? AND language = ? dan mengembalikan kolom response sebagai roast src/routes/llama/+server.js:40-47. Kuncinya pasangan (gh_username, language), jadi satu akun dengan language berbeda menghasilkan baris berbeda.

Tidak ada TTL: kolom created_at tidak pernah dibandingkan dengan waktu sekarang, sehingga baris yang pernah tersimpan menjawab semua request berikutnya dengan pasangan kunci yang sama. Tabel dan index gh_username_idx dideklarasikan di database.sql:2-11. Aksesnya lewat binding DB berupa platform.env.DB, yang dideklarasikan sebagai d1_databases di wrangler.toml:28-31; detail binding ada di Konfigurasi Environment dan Binding.

⚠️ Jalur tulis cache tidak selalu berhasil. Statement INSERT INTO roasts (...) menyebut kolom ip_address src/routes/llama/+server.js:195-207, sementara CREATE TABLE roasts di database.sql:2-9 tidak mendeklarasikan kolom itu. Bila schema tersebut yang berlaku, statement gagal, dan catch {} kosong di baris berikutnya menelannya tanpa jejak. Respons 200 tetap dikirim, tetapi tidak ada baris yang tersimpan, sehingga cache tidak pernah terisi.

⚠️ Nilai kelima yang di-bind adalah hasil sha256(...), padahal sha256 mengembalikan Promise dari crypto.subtle.digest(...), bukan string src/routes/llama/+server.js:215-221. hex yang dipanggil di dalam then hanya muncul satu kali di seluruh repository dan tidak pernah didefinisikan, sehingga Promise itu berakhir sebagai rejection yang tidak ditunggu siapa pun.

Kegagalan yang lolos dari kontrak JSON

request pertama ke profil GitHub dibungkus try: bila response.ok bernilai false, handler menjawab 500 dengan Our roast machine is in trouble src/routes/llama/+server.js:62-66. Cabang catch (err) kemudian mengulang fetch yang sama tanpa pembungkus try src/routes/llama/+server.js:67-71, sehingga kegagalan kedua kali keluar dari handler dan dijawab oleh SvelteKit dengan bentuk error bawaannya, bukan { error: ... }.

Objek headers juga hidup di scope modul src/routes/llama/+server.js:9-13. Authorization ditambahkan di sana ketika GITHUB_API_KEY tersedia src/routes/llama/+server.js:49-51, dan dihapus dengan delete ketika useToken tetap false src/routes/llama/+server.js:73-76. Karena objeknya milik modul, perubahan itu berlaku untuk semua request yang dilayani isolate yang sama.