mirror of
https://github.com/luxfi/ai.git
synced 2026-10-30 17:02:43 +00:00
Close three `// TODO: Integrate with actual ...` stubs in pkg/miner/miner.go (runInference, runChat, runEmbedding) by introducing a small pluggable backend interface. The miner now dispatches through backend.InferenceBackend rather than inlining placeholder output. Two reference backends ship in-tree: - pkg/miner/backend/noop — deterministic mock, byte-for-byte identical to the pre-refactor inline stubs. Default. Zero config, zero deps. - pkg/miner/backend/openai — OpenAI-compatible HTTP adapter built on net/http. Covers llama.cpp, vllm, ollama, LocalAI, and api.openai.com via OPENAI_API_BASE. No new go.mod dependencies. Config gains Backend / OpenAIBase / OpenAIAPIKey / OpenAIModel / OpenAIEmbeddingModel fields. Unknown backend values fall back to noop so operator typos don't crash-loop the miner. Existing miner_test.go passes unchanged; DefaultConfig() behaviour is preserved. Also wire a GPUStatsProvider hook on Miner so GetStats can surface best-effort GPU telemetry (GPUUtilization, MemoryUsed) without pulling NVML/MLX bindings into the main binary. Default is nil = no-op, matching current behaviour; a follow-up PR will wire a real provider. Closes luxfi/ai#1. Contributed by kcolbchain (https://kcolbchain.com) · https://abhishekkrishna.com
73 lines
2.0 KiB
Go
73 lines
2.0 KiB
Go
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package backend_test
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"github.com/luxfi/ai/pkg/miner/backend"
|
|
"github.com/luxfi/ai/pkg/miner/backend/noop"
|
|
)
|
|
|
|
// TestInterfaceContract ensures the canonical in-tree backends implement
|
|
// backend.InferenceBackend. If this test stops compiling, a backend has
|
|
// drifted from the interface.
|
|
func TestInterfaceContract(t *testing.T) {
|
|
var _ backend.InferenceBackend = noop.New()
|
|
}
|
|
|
|
// TestCapabilitiesShape exercises the Capabilities reporting path end-to-end
|
|
// for a concrete backend. The point is to lock in the JSON-visible shape;
|
|
// any rename breaks the wire contract for API consumers.
|
|
func TestCapabilitiesShape(t *testing.T) {
|
|
caps := noop.New().Capabilities()
|
|
if !caps.Chat || !caps.Inference || !caps.Embedding {
|
|
t.Fatalf("noop backend should advertise all three capabilities: %+v", caps)
|
|
}
|
|
if caps.EmbeddingDims == 0 {
|
|
t.Fatal("noop backend should advertise a fixed embedding dimensionality")
|
|
}
|
|
}
|
|
|
|
// TestRequestResponseRoundTrip sanity-checks that every request/response pair
|
|
// travels through the interface with no surprise mutations.
|
|
func TestRequestResponseRoundTrip(t *testing.T) {
|
|
ctx := context.Background()
|
|
b := noop.New()
|
|
|
|
chat, err := b.Chat(ctx, backend.ChatRequest{
|
|
Model: "test-model",
|
|
Messages: []backend.Message{{Role: "user", Content: "hi"}},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Chat: %v", err)
|
|
}
|
|
if chat.Model != "test-model" {
|
|
t.Errorf("Chat: model not preserved, got %q", chat.Model)
|
|
}
|
|
|
|
inf, err := b.Inference(ctx, backend.InferenceRequest{
|
|
Model: "inf-model",
|
|
Prompt: "hello",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Inference: %v", err)
|
|
}
|
|
if inf.Model != "inf-model" {
|
|
t.Errorf("Inference: model not preserved, got %q", inf.Model)
|
|
}
|
|
|
|
emb, err := b.Embed(ctx, backend.EmbedRequest{
|
|
Model: "emb-model",
|
|
Text: "some text",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Embed: %v", err)
|
|
}
|
|
if emb.Model != "emb-model" {
|
|
t.Errorf("Embed: model not preserved, got %q", emb.Model)
|
|
}
|
|
}
|