CI / test (push) Has been cancelled
Plan v0.2'nin Faz 0 çıktısı. Yazılan sıraya göre:
- cobra tabanlı CLI iskeleti; server/agent/ctl komutları henüz stub.
- internal/sim: OpenAI uyumlu sahte inference engine. Router'ı GPU olmadan
gerçekçi koşullarda test edebilmek için gerçek bir engine'in router
açısından önemli davranışlarını modeller:
* continuous batching ve KV blok muhasebesi (head-of-line blocking dahil)
* zincirlenmiş blok hash'leri + LRU ile otomatik prefix cache
* batch büyüdükçe artan token gecikmesi
* istemci koptuğunda isteğin düşürülüp blokların bırakılması
* vLLM metrik adlarıyla /metrics (plan §7.6 engine kontratı)
- Testler: prefix hash zincirlemesi, LRU tahliyesi, iptal propagasyonu,
kuyruklama, SSE formatı, usage/cached_tokens, metriklerin gerçek
Prometheus parser'ıyla okunabilirliği.
- Gitea Actions CI: gofmt, vet, race detector.
Modül yolu gitea.mynodes.xyz/ilker/llmgw; `make rename` ile değiştirilebilir.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
78 lines
2.4 KiB
Go
78 lines
2.4 KiB
Go
package sim
|
||
|
||
import "testing"
|
||
|
||
func TestBlockHashesZincirleme(t *testing.T) {
|
||
// Aynı içerik farklı ön ekten sonra geliyorsa blok hash'i farklı olmalı.
|
||
a := BlockHashes(Tokenize("AAAAAAAABBBBBBBB"), 2)
|
||
b := BlockHashes(Tokenize("CCCCCCCCBBBBBBBB"), 2)
|
||
if len(a) != 2 || len(b) != 2 {
|
||
t.Fatalf("beklenen 2 blok, alınan a=%d b=%d", len(a), len(b))
|
||
}
|
||
if a[1] == b[1] {
|
||
t.Error("farklı ön ekten sonra gelen aynı blok aynı hash'i verdi; zincirleme çalışmıyor")
|
||
}
|
||
}
|
||
|
||
func TestBlockHashesOnEkKararli(t *testing.T) {
|
||
// Blok sınırına hizalı bir ön ek, uzun metinde aynı blok hash'lerini vermeli.
|
||
// Hizalama şart: son yarım token farklı olursa onu içeren blok da farklı
|
||
// çıkar — gerçek tokenizer'larda da olan sınır etkisi, bu yüzden prefix
|
||
// cache hit'i her zaman tam blok cinsinden ölçülür.
|
||
const aligned = "merhaba " // 8 bayt = 2 token = blockSize 2 ile 1 tam blok
|
||
short := BlockHashes(Tokenize(aligned), 2)
|
||
long := BlockHashes(Tokenize(aligned+"dunya nasilsin"), 2)
|
||
if len(short) != 1 {
|
||
t.Fatalf("beklenen 1 blok, alınan %d", len(short))
|
||
}
|
||
if short[0] != long[0] {
|
||
t.Fatal("hizalı ön ek blok hash'i değişti; ön ek kararlı değil")
|
||
}
|
||
}
|
||
|
||
func TestBlockHashesYarimBlokAtilir(t *testing.T) {
|
||
// 3 token, blockSize 2 -> 1 tam blok
|
||
if got := len(BlockHashes(make([]Token, 3), 2)); got != 1 {
|
||
t.Errorf("beklenen 1 blok, alınan %d", got)
|
||
}
|
||
if got := len(BlockHashes(make([]Token, 1), 2)); got != 0 {
|
||
t.Errorf("tam blok yokken blok üretildi: %d", got)
|
||
}
|
||
}
|
||
|
||
func TestPrefixCacheKesintisizOnEk(t *testing.T) {
|
||
c := NewPrefixCache(10)
|
||
h := []BlockHash{1, 2, 3, 4}
|
||
c.Insert(h)
|
||
|
||
if n := c.Lookup(h); n != 4 {
|
||
t.Fatalf("tam eşleşme beklendi, alınan %d", n)
|
||
}
|
||
// Ortası eksik: ilk blok hit, sonrası miss olmalı.
|
||
if n := c.Lookup([]BlockHash{1, 99, 3}); n != 1 {
|
||
t.Errorf("kesintisiz ön ek 1 olmalıydı, alınan %d", n)
|
||
}
|
||
}
|
||
|
||
func TestPrefixCacheLRUTahliye(t *testing.T) {
|
||
c := NewPrefixCache(2)
|
||
c.Insert([]BlockHash{1, 2})
|
||
c.Lookup([]BlockHash{1}) // 1'i tazele
|
||
c.Insert([]BlockHash{3}) // kapasite aşıldı, en eski (2) düşmeli
|
||
|
||
if n := c.Lookup([]BlockHash{1}); n != 1 {
|
||
t.Error("taze blok tahliye edildi")
|
||
}
|
||
if n := c.Lookup([]BlockHash{2}); n != 0 {
|
||
t.Error("en eski blok tahliye edilmeliydi")
|
||
}
|
||
}
|
||
|
||
func TestPrefixCacheKapali(t *testing.T) {
|
||
c := NewPrefixCache(0)
|
||
c.Insert([]BlockHash{1, 2})
|
||
if n := c.Lookup([]BlockHash{1}); n != 0 {
|
||
t.Errorf("kapalı cache hit verdi: %d", n)
|
||
}
|
||
}
|