Files
wehub-resource-sync a06f331eb8
CI / benchmark (push) Has been skipped
install-script / posix-syntax (push) Successful in 6m1s
CI / build-onnx (push) Failing after 6m43s
init-smoke / dry-run (push) Failing after 15m57s
security / govulncheck (push) Has been cancelled
security / trivy-fs (push) Has been cancelled
CI / test (1.26, ubuntu-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
CI / test (1.26, macos-latest) (push) Has been cancelled
CI / build-windows (push) Has been cancelled
CI / lint (push) Has been cancelled
install-script / powershell-syntax (push) Has been cancelled
install-script / install (macos-14) (push) Has been cancelled
install-script / install (ubuntu-latest) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:33:42 +08:00

85 lines
2.3 KiB
Go

//go:build llama
// llmtest: minimal end-to-end smoke test of the internal/llm CGO
// wrapper. Loads a GGUF, runs greedy decoding from a prompt, streams
// the output to stdout.
//
// go build -tags llama -o /tmp/llmtest ./internal/llm/cmd/llmtest
// /tmp/llmtest -model ~/models/qwen2.5-0.5b-instruct-q4_k_m.gguf \
// -prompt 'List three Go web frameworks.'
package main
import (
"flag"
"fmt"
"os"
"time"
llm "github.com/zzet/gortex/internal/llm"
)
// qwenChat wraps a user message in Qwen2.5's chat template. Hardcoded
// here so we don't need llama_chat_apply_template bindings yet.
func qwenChat(system, user string) string {
if system == "" {
system = "You are a helpful assistant."
}
return "<|im_start|>system\n" + system + "<|im_end|>\n" +
"<|im_start|>user\n" + user + "<|im_end|>\n" +
"<|im_start|>assistant\n"
}
func main() {
modelPath := flag.String("model", "", "path to .gguf model (required)")
prompt := flag.String("prompt", "Say hello in one short sentence.", "user prompt")
system := flag.String("system", "", "system prompt (optional)")
maxTok := flag.Int("max", 256, "max tokens to generate")
nCtx := flag.Int("ctx", 2048, "context size")
gpu := flag.Int("gpu", 999, "number of layers to offload to GPU (Metal); 0 = CPU only")
threads := flag.Int("threads", 0, "CPU threads (0 = llama.cpp default)")
raw := flag.Bool("raw", false, "use -prompt as raw text (no Qwen chat template)")
flag.Parse()
if *modelPath == "" {
fmt.Fprintln(os.Stderr, "error: -model is required")
flag.Usage()
os.Exit(2)
}
tLoad := time.Now()
m, err := llm.LoadModel(*modelPath, *gpu)
if err != nil {
fmt.Fprintf(os.Stderr, "load: %v\n", err)
os.Exit(1)
}
defer m.Close()
fmt.Fprintf(os.Stderr, "[loaded model in %s]\n", time.Since(tLoad).Round(time.Millisecond))
ctx, err := m.NewContext(*nCtx, *threads)
if err != nil {
fmt.Fprintf(os.Stderr, "context: %v\n", err)
os.Exit(1)
}
defer ctx.Close()
text := *prompt
if !*raw {
text = qwenChat(*system, *prompt)
}
tGen := time.Now()
n, err := ctx.Generate(text, *maxTok, func(piece string) bool {
fmt.Print(piece)
return true
})
fmt.Println()
if err != nil {
fmt.Fprintf(os.Stderr, "generate: %v\n", err)
os.Exit(1)
}
dt := time.Since(tGen)
tps := float64(n) / dt.Seconds()
fmt.Fprintf(os.Stderr, "[%d tokens in %s, %.1f tok/s]\n",
n, dt.Round(time.Millisecond), tps)
}