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-71, 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-1912. Pemanggil tunggalnya adalah handleRoast di UI, yang mengirim Content-Type: application/json dan membaca data.roast src/routes/+page.svelte:39-523.
Kontrak request
Handler langsung memanggil await request.json() tanpa pembungkus try src/routes/llama/+server.js:29-314. Body yang bukan JSON karena itu melempar error di luar kendali handler, dan tidak pernah sampai ke pemeriksaan language.
| Field | Tipe | Wajib | Peran |
|---|---|---|---|
username | string | ya | Disisipkan ke path https://api.github.com/users/${username} dan ke teks prompt; bentuknya tidak divalidasi |
language | string | ya | Kunci 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-275. Nilai di luar daftar itu langsung dijawab 400 sebelum satu pun panggilan ke GitHub atau OpenAI terjadi src/routes/llama/+server.js:33-386.
Daftar languages di UI lebih panjang daripada validLanguages: UI juga menawarkan german, italian, finnish, dan portuguese src/routes/+page.svelte:12-287. 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.Kontrak response
Semua respons berbentuk JSON. Perhatikan bahwa dua jalur 200 mengembalikan bentuk body yang berbeda.
| Status | Body | Kondisi | Rujukan |
|---|---|---|---|
| 200 | { roast, username, language } | DB.prepare(...).first() menemukan baris untuk pasangan gh_username + language | src/routes/llama/+server.js:40-478 |
| 200 | { roast } | completion.choices[0].message.content tersedia | src/routes/llama/+server.js:193-2079 |
| 400 | { error: 'invalid language specified, please pass a valid language.' } | language tidak ada di validLanguages | src/routes/llama/+server.js:33-386 |
| 500 | { error: 'Our roast machine is in trouble' } | fetch profil GitHub selesai dengan response.ok === false | src/routes/llama/+server.js:62-6610 |
| 500 | { error: 'Failed to generate roast' } | client.chat.completions.create melempar error | src/routes/llama/+server.js:209-21211 |
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-523.
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-478. 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-1112. Aksesnya lewat binding DB berupa platform.env.DB, yang dideklarasikan sebagai d1_databases di wrangler.toml:28-3113; detail binding ada di Konfigurasi Environment dan Binding.
⚠️ Jalur tulis
cachetidak selalu berhasil. StatementINSERT INTO roasts (...)menyebut kolomip_addresssrc/routes/llama/+server.js:195-20714, sementaraCREATE TABLE roastsdi database.sql:2-915 tidak mendeklarasikan kolom itu. Bilaschematersebut yang berlaku, statement gagal, dancatch {}kosong di baris berikutnya menelannya tanpa jejak. Respons200tetap dikirim, tetapi tidak ada baris yang tersimpan, sehinggacachetidak pernah terisi.
⚠️ Nilai kelima yang di-
bindadalah hasilsha256(...), padahalsha256mengembalikanPromisedaricrypto.subtle.digest(...), bukanstringsrc/routes/llama/+server.js:215-22116.hexyang dipanggil di dalamthenhanya muncul satu kali di seluruh repository dan tidak pernah didefinisikan, sehinggaPromiseitu 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-6610. Cabang catch (err) kemudian mengulang fetch yang sama tanpa pembungkus try src/routes/llama/+server.js:67-7117, 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-1318. Authorization ditambahkan di sana ketika GITHUB_API_KEY tersedia src/routes/llama/+server.js:49-5119, dan dihapus dengan delete ketika useToken tetap false src/routes/llama/+server.js:73-7620. Karena objeknya milik modul, perubahan itu berlaku untuk semua request yang dilayani isolate yang sama.