585 lines
17 KiB
Go
585 lines
17 KiB
Go
package adapters_test
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
|
|
"go.uber.org/zap"
|
|
|
|
"iop/apps/node/internal/adapters"
|
|
noderuntime "iop/apps/node/internal/runtime"
|
|
iop "iop/proto/gen/iop"
|
|
)
|
|
|
|
type testSink struct {
|
|
mu sync.Mutex
|
|
events []noderuntime.RuntimeEvent
|
|
}
|
|
|
|
func (s *testSink) Emit(_ context.Context, event noderuntime.RuntimeEvent) error {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
s.events = append(s.events, event)
|
|
return nil
|
|
}
|
|
|
|
// --- BuildFromPayload tests ---
|
|
|
|
func TestBuildFromPayload_EmptyPayloadRegistersNoAdapters(t *testing.T) {
|
|
reg, err := adapters.BuildFromPayload(&iop.NodeConfigPayload{}, zap.NewNop())
|
|
if err != nil {
|
|
t.Fatalf("build from payload: %v", err)
|
|
}
|
|
if got := len(reg.All()); got != 0 {
|
|
t.Fatalf("expected no adapters, got %d", got)
|
|
}
|
|
if _, ok := reg.Get("mock"); ok {
|
|
t.Fatal("mock adapter must not be registered implicitly")
|
|
}
|
|
}
|
|
|
|
func TestBuildFromPayload_ExplicitMockEnabled(t *testing.T) {
|
|
reg, err := adapters.BuildFromPayload(&iop.NodeConfigPayload{
|
|
Adapters: []*iop.AdapterConfig{
|
|
{Type: "mock", Enabled: true},
|
|
},
|
|
}, zap.NewNop())
|
|
if err != nil {
|
|
t.Fatalf("build from payload: %v", err)
|
|
}
|
|
if _, ok := reg.Get("mock"); !ok {
|
|
t.Fatal("expected explicit mock adapter to be registered")
|
|
}
|
|
}
|
|
|
|
func TestBuildFromPayload_OllamaEnabled(t *testing.T) {
|
|
reg, err := adapters.BuildFromPayload(&iop.NodeConfigPayload{
|
|
Adapters: []*iop.AdapterConfig{
|
|
{
|
|
Type: "ollama",
|
|
Enabled: true,
|
|
Config: &iop.AdapterConfig_Ollama{Ollama: &iop.OllamaAdapterConfig{BaseUrl: "http://localhost:11434"}},
|
|
},
|
|
},
|
|
}, zap.NewNop())
|
|
if err != nil {
|
|
t.Fatalf("build from payload: %v", err)
|
|
}
|
|
if _, ok := reg.Get("ollama"); !ok {
|
|
t.Fatal("expected ollama adapter to be registered")
|
|
}
|
|
}
|
|
|
|
func TestBuildFromPayload_MultipleAdapters(t *testing.T) {
|
|
reg, err := adapters.BuildFromPayload(&iop.NodeConfigPayload{
|
|
Adapters: []*iop.AdapterConfig{
|
|
{Type: "ollama", Enabled: true, Config: &iop.AdapterConfig_Ollama{Ollama: &iop.OllamaAdapterConfig{BaseUrl: "x"}}},
|
|
{Type: "cli", Enabled: true, Config: &iop.AdapterConfig_Cli{Cli: &iop.CLIAdapterConfig{
|
|
Profiles: map[string]*iop.CLIProfileConfig{
|
|
"codex": {Command: "codex", Persistent: true, ResponseIdleTimeoutMs: 1500, StartupIdleTimeoutMs: 300},
|
|
},
|
|
}}},
|
|
},
|
|
}, zap.NewNop())
|
|
if err != nil {
|
|
t.Fatalf("build from payload: %v", err)
|
|
}
|
|
for _, name := range []string{"ollama", "cli"} {
|
|
if _, ok := reg.Get(name); !ok {
|
|
t.Fatalf("expected %s adapter to be registered", name)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestBuildFromPayload_VllmEnabled(t *testing.T) {
|
|
reg, err := adapters.BuildFromPayload(&iop.NodeConfigPayload{
|
|
Adapters: []*iop.AdapterConfig{
|
|
{Type: "vllm", Enabled: true, Config: &iop.AdapterConfig_Vllm{Vllm: &iop.VllmAdapterConfig{Endpoint: "http://localhost:8000"}}},
|
|
},
|
|
}, zap.NewNop())
|
|
if err != nil {
|
|
t.Fatalf("build from payload: %v", err)
|
|
}
|
|
if _, ok := reg.Get("vllm"); !ok {
|
|
t.Fatal("expected vllm adapter to be registered")
|
|
}
|
|
}
|
|
|
|
func TestBuildFromPayload_UnknownType(t *testing.T) {
|
|
_, err := adapters.BuildFromPayload(&iop.NodeConfigPayload{
|
|
Adapters: []*iop.AdapterConfig{
|
|
{Type: "unknown", Enabled: true},
|
|
},
|
|
}, zap.NewNop())
|
|
if err == nil {
|
|
t.Fatal("expected unknown adapter type error")
|
|
}
|
|
}
|
|
|
|
// --- Registry lifecycle tests ---
|
|
|
|
type lifecycleAdapter struct {
|
|
name string
|
|
log *[]string
|
|
}
|
|
|
|
func (a *lifecycleAdapter) Name() string { return a.name }
|
|
func (a *lifecycleAdapter) Capabilities(_ context.Context) (noderuntime.Capabilities, error) {
|
|
return noderuntime.Capabilities{AdapterName: a.name}, nil
|
|
}
|
|
func (a *lifecycleAdapter) Execute(_ context.Context, _ noderuntime.ExecutionSpec, _ noderuntime.EventSink) error {
|
|
return nil
|
|
}
|
|
func (a *lifecycleAdapter) Start(_ context.Context) error {
|
|
*a.log = append(*a.log, "start:"+a.name)
|
|
return nil
|
|
}
|
|
func (a *lifecycleAdapter) Stop(_ context.Context) error {
|
|
*a.log = append(*a.log, "stop:"+a.name)
|
|
return nil
|
|
}
|
|
|
|
type failingLifecycleAdapter struct {
|
|
name string
|
|
log *[]string
|
|
}
|
|
|
|
func (a *failingLifecycleAdapter) Name() string { return a.name }
|
|
func (a *failingLifecycleAdapter) Capabilities(_ context.Context) (noderuntime.Capabilities, error) {
|
|
return noderuntime.Capabilities{AdapterName: a.name}, nil
|
|
}
|
|
func (a *failingLifecycleAdapter) Execute(_ context.Context, _ noderuntime.ExecutionSpec, _ noderuntime.EventSink) error {
|
|
return nil
|
|
}
|
|
func (a *failingLifecycleAdapter) Start(_ context.Context) error {
|
|
*a.log = append(*a.log, "start:"+a.name)
|
|
return fmt.Errorf("start failed: %s", a.name)
|
|
}
|
|
func (a *failingLifecycleAdapter) Stop(_ context.Context) error {
|
|
*a.log = append(*a.log, "stop:"+a.name)
|
|
return nil
|
|
}
|
|
|
|
type failingStopAdapter struct {
|
|
name string
|
|
log *[]string
|
|
stopErr error
|
|
}
|
|
|
|
func (a *failingStopAdapter) Name() string { return a.name }
|
|
func (a *failingStopAdapter) Capabilities(_ context.Context) (noderuntime.Capabilities, error) {
|
|
return noderuntime.Capabilities{AdapterName: a.name}, nil
|
|
}
|
|
func (a *failingStopAdapter) Execute(_ context.Context, _ noderuntime.ExecutionSpec, _ noderuntime.EventSink) error {
|
|
return nil
|
|
}
|
|
func (a *failingStopAdapter) Start(_ context.Context) error {
|
|
*a.log = append(*a.log, "start:"+a.name)
|
|
return nil
|
|
}
|
|
func (a *failingStopAdapter) Stop(_ context.Context) error {
|
|
*a.log = append(*a.log, "stop:"+a.name)
|
|
return a.stopErr
|
|
}
|
|
|
|
func TestRegistryLifecycle_StartStopOrder(t *testing.T) {
|
|
log := []string{}
|
|
reg := adapters.NewRegistry()
|
|
reg.Register(&lifecycleAdapter{name: "first", log: &log})
|
|
reg.Register(&lifecycleAdapter{name: "second", log: &log})
|
|
|
|
ctx := context.Background()
|
|
if err := reg.Start(ctx); err != nil {
|
|
t.Fatalf("Start: %v", err)
|
|
}
|
|
if err := reg.Stop(ctx); err != nil {
|
|
t.Fatalf("Stop: %v", err)
|
|
}
|
|
|
|
want := []string{"start:first", "start:second", "stop:second", "stop:first"}
|
|
if len(log) != len(want) {
|
|
t.Fatalf("expected %v, got %v", want, log)
|
|
}
|
|
for i, got := range log {
|
|
if got != want[i] {
|
|
t.Fatalf("log[%d]: want %q, got %q", i, want[i], got)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestRegistryLifecycle_StartFailureStopsStartedAdapters(t *testing.T) {
|
|
log := []string{}
|
|
reg := adapters.NewRegistry()
|
|
reg.Register(&lifecycleAdapter{name: "first", log: &log})
|
|
reg.Register(&failingLifecycleAdapter{name: "second", log: &log})
|
|
|
|
ctx := context.Background()
|
|
if err := reg.Start(ctx); err == nil {
|
|
t.Fatal("expected Start to return error")
|
|
}
|
|
|
|
want := []string{"start:first", "start:second", "stop:first"}
|
|
if len(log) != len(want) {
|
|
t.Fatalf("expected log %v, got %v", want, log)
|
|
}
|
|
for i, got := range log {
|
|
if got != want[i] {
|
|
t.Fatalf("log[%d]: want %q, got %q", i, want[i], got)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestRegistryLifecycle_StopContinuesOnFailingAdapter(t *testing.T) {
|
|
log := []string{}
|
|
reg := adapters.NewRegistry()
|
|
reg.Register(&lifecycleAdapter{name: "first", log: &log})
|
|
reg.Register(&failingStopAdapter{name: "second", log: &log, stopErr: fmt.Errorf("stop failed: second")})
|
|
|
|
ctx := context.Background()
|
|
if err := reg.Start(ctx); err != nil {
|
|
t.Fatalf("Start: %v", err)
|
|
}
|
|
if err := reg.Stop(ctx); err == nil {
|
|
t.Fatal("expected non-nil error from Stop")
|
|
}
|
|
|
|
want := []string{"start:first", "start:second", "stop:second", "stop:first"}
|
|
if len(log) != len(want) {
|
|
t.Fatalf("expected log %v, got %v", want, log)
|
|
}
|
|
for i, got := range log {
|
|
if got != want[i] {
|
|
t.Fatalf("log[%d]: want %q, got %q", i, want[i], got)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestRegistryLifecycle_NonLifecycleAdapterSkipped(t *testing.T) {
|
|
reg, err := adapters.BuildFromPayload(&iop.NodeConfigPayload{
|
|
Adapters: []*iop.AdapterConfig{
|
|
{Type: "mock", Enabled: true},
|
|
},
|
|
}, zap.NewNop())
|
|
if err != nil {
|
|
t.Fatalf("build: %v", err)
|
|
}
|
|
ctx := context.Background()
|
|
if err := reg.Start(ctx); err != nil {
|
|
t.Fatalf("Start: %v", err)
|
|
}
|
|
if err := reg.Stop(ctx); err != nil {
|
|
t.Fatalf("Stop: %v", err)
|
|
}
|
|
}
|
|
|
|
// --- Registry namespace tests ---
|
|
|
|
func TestRegistry_MultiInstanceSameType(t *testing.T) {
|
|
log := []string{}
|
|
reg := adapters.NewRegistry()
|
|
reg.RegisterKeyed("ollama@local", "ollama", &lifecycleAdapter{name: "ollama@local", log: &log})
|
|
reg.RegisterKeyed("ollama@dgx", "ollama", &lifecycleAdapter{name: "ollama@dgx", log: &log})
|
|
|
|
a1, ok1 := reg.Get("ollama@local")
|
|
a2, ok2 := reg.Get("ollama@dgx")
|
|
if !ok1 || !ok2 {
|
|
t.Fatalf("expected both instances to be retrievable by key; ok1=%v ok2=%v", ok1, ok2)
|
|
}
|
|
if a1 == a2 {
|
|
t.Fatal("expected distinct adapter instances")
|
|
}
|
|
if a1.Name() != "ollama@local" || a2.Name() != "ollama@dgx" {
|
|
t.Errorf("adapter names: got %q and %q", a1.Name(), a2.Name())
|
|
}
|
|
|
|
all := reg.All()
|
|
if len(all) != 2 {
|
|
t.Fatalf("expected 2 adapters in All(), got %d", len(all))
|
|
}
|
|
}
|
|
|
|
func TestRegistry_LegacyLookupSingleInstance(t *testing.T) {
|
|
reg := adapters.NewRegistry()
|
|
reg.RegisterKeyed("ollama@prod", "ollama", &lifecycleAdapter{name: "ollama@prod", log: nil})
|
|
|
|
// Exact key lookup must work.
|
|
a, err := reg.Lookup("ollama@prod")
|
|
if err != nil {
|
|
t.Fatalf("Lookup by instance key: %v", err)
|
|
}
|
|
if a.Name() != "ollama@prod" {
|
|
t.Errorf("expected ollama@prod, got %q", a.Name())
|
|
}
|
|
|
|
// Legacy type-name lookup works when there is exactly one instance.
|
|
a2, err := reg.Lookup("ollama")
|
|
if err != nil {
|
|
t.Fatalf("legacy Lookup by type name: %v", err)
|
|
}
|
|
if a2.Name() != "ollama@prod" {
|
|
t.Errorf("expected ollama@prod via legacy lookup, got %q", a2.Name())
|
|
}
|
|
}
|
|
|
|
func TestRegistry_AmbiguousLegacyLookup(t *testing.T) {
|
|
log := []string{}
|
|
reg := adapters.NewRegistry()
|
|
reg.RegisterKeyed("ollama@local", "ollama", &lifecycleAdapter{name: "ollama@local", log: &log})
|
|
reg.RegisterKeyed("ollama@dgx", "ollama", &lifecycleAdapter{name: "ollama@dgx", log: &log})
|
|
|
|
_, err := reg.Lookup("ollama")
|
|
if err == nil {
|
|
t.Fatal("expected ambiguous error for type-name lookup with multiple instances")
|
|
}
|
|
if !strings.Contains(err.Error(), "ambiguous") {
|
|
t.Errorf("expected 'ambiguous' in error, got %v", err)
|
|
}
|
|
if !strings.Contains(err.Error(), "instance key") {
|
|
t.Errorf("expected guidance to use instance key, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestRegistry_LifecycleOrderMultiInstance(t *testing.T) {
|
|
log := []string{}
|
|
reg := adapters.NewRegistry()
|
|
reg.RegisterKeyed("ollama@local", "ollama", &lifecycleAdapter{name: "ollama@local", log: &log})
|
|
reg.RegisterKeyed("ollama@dgx", "ollama", &lifecycleAdapter{name: "ollama@dgx", log: &log})
|
|
reg.RegisterKeyed("cli", "cli", &lifecycleAdapter{name: "cli", log: &log})
|
|
|
|
ctx := context.Background()
|
|
if err := reg.Start(ctx); err != nil {
|
|
t.Fatalf("Start: %v", err)
|
|
}
|
|
if err := reg.Stop(ctx); err != nil {
|
|
t.Fatalf("Stop: %v", err)
|
|
}
|
|
|
|
want := []string{
|
|
"start:ollama@local", "start:ollama@dgx", "start:cli",
|
|
"stop:cli", "stop:ollama@dgx", "stop:ollama@local",
|
|
}
|
|
if len(log) != len(want) {
|
|
t.Fatalf("expected log %v, got %v", want, log)
|
|
}
|
|
for i, got := range log {
|
|
if got != want[i] {
|
|
t.Fatalf("log[%d]: want %q, got %q", i, want[i], got)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestBuildFromPayload_MultiInstanceSameType(t *testing.T) {
|
|
reg, err := adapters.BuildFromPayload(&iop.NodeConfigPayload{
|
|
Adapters: []*iop.AdapterConfig{
|
|
{
|
|
Type: "ollama",
|
|
Name: "ollama@local",
|
|
Enabled: true,
|
|
Config: &iop.AdapterConfig_Ollama{Ollama: &iop.OllamaAdapterConfig{BaseUrl: "http://localhost:11434"}},
|
|
},
|
|
{
|
|
Type: "ollama",
|
|
Name: "ollama@dgx",
|
|
Enabled: true,
|
|
Config: &iop.AdapterConfig_Ollama{Ollama: &iop.OllamaAdapterConfig{BaseUrl: "http://192.168.1.10:11434"}},
|
|
},
|
|
},
|
|
}, zap.NewNop())
|
|
if err != nil {
|
|
t.Fatalf("build from payload: %v", err)
|
|
}
|
|
|
|
if len(reg.All()) != 2 {
|
|
t.Fatalf("expected 2 adapters, got %d", len(reg.All()))
|
|
}
|
|
if _, ok := reg.Get("ollama@local"); !ok {
|
|
t.Error("expected ollama@local to be registered")
|
|
}
|
|
if _, ok := reg.Get("ollama@dgx"); !ok {
|
|
t.Error("expected ollama@dgx to be registered")
|
|
}
|
|
// Legacy type-name lookup must fail with ambiguous error.
|
|
if _, err := reg.Lookup("ollama"); err == nil {
|
|
t.Error("expected ambiguous error for legacy type-name lookup")
|
|
}
|
|
// Exact key lookup must succeed.
|
|
if _, err := reg.Lookup("ollama@local"); err != nil {
|
|
t.Errorf("Lookup ollama@local: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestBuildFromPayload_OllamaMultiInstanceExecution(t *testing.T) {
|
|
var (
|
|
gotModelA string
|
|
gotNumCtxA int
|
|
gotModelB string
|
|
gotNumCtxB int
|
|
)
|
|
|
|
makeOllamaServer := func(gotModel *string, gotNumCtx *int, reply string) *httptest.Server {
|
|
return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.URL.Path == "/api/tags" {
|
|
_, _ = w.Write([]byte(`{"models":[]}`))
|
|
return
|
|
}
|
|
if r.URL.Path != "/api/chat" {
|
|
return
|
|
}
|
|
var req struct {
|
|
Model string `json:"model"`
|
|
Options map[string]any `json:"options"`
|
|
}
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
t.Errorf("decode: %v", err)
|
|
return
|
|
}
|
|
*gotModel = req.Model
|
|
if v, ok := req.Options["num_ctx"].(float64); ok {
|
|
*gotNumCtx = int(v)
|
|
}
|
|
w.Header().Set("Content-Type", "application/x-ndjson")
|
|
_, _ = fmt.Fprintf(w, `{"message":{"role":"assistant","content":"%s"},"done":false}`+"\n", reply)
|
|
_, _ = w.Write([]byte(`{"done":true}` + "\n"))
|
|
}))
|
|
}
|
|
|
|
serverA := makeOllamaServer(&gotModelA, &gotNumCtxA, "a")
|
|
defer serverA.Close()
|
|
serverB := makeOllamaServer(&gotModelB, &gotNumCtxB, "b")
|
|
defer serverB.Close()
|
|
|
|
reg, err := adapters.BuildFromPayload(&iop.NodeConfigPayload{
|
|
Adapters: []*iop.AdapterConfig{
|
|
{
|
|
Type: "ollama",
|
|
Name: "ollama@local",
|
|
Enabled: true,
|
|
Config: &iop.AdapterConfig_Ollama{Ollama: &iop.OllamaAdapterConfig{BaseUrl: serverA.URL, ContextSize: 4096}},
|
|
},
|
|
{
|
|
Type: "ollama",
|
|
Name: "ollama@dgx",
|
|
Enabled: true,
|
|
Config: &iop.AdapterConfig_Ollama{Ollama: &iop.OllamaAdapterConfig{BaseUrl: serverB.URL, ContextSize: 8192}},
|
|
},
|
|
},
|
|
}, zap.NewNop())
|
|
if err != nil {
|
|
t.Fatalf("build: %v", err)
|
|
}
|
|
|
|
adapterA, ok := reg.Get("ollama@local")
|
|
if !ok {
|
|
t.Fatal("expected ollama@local")
|
|
}
|
|
adapterB, ok := reg.Get("ollama@dgx")
|
|
if !ok {
|
|
t.Fatal("expected ollama@dgx")
|
|
}
|
|
|
|
if err := adapterA.Execute(context.Background(), noderuntime.ExecutionSpec{
|
|
RunID: "run-a", Target: "model-a", Input: map[string]any{"prompt": "test"},
|
|
}, &testSink{}); err != nil {
|
|
t.Fatalf("A execute: %v", err)
|
|
}
|
|
if err := adapterB.Execute(context.Background(), noderuntime.ExecutionSpec{
|
|
RunID: "run-b", Target: "model-b", Input: map[string]any{"prompt": "test"},
|
|
}, &testSink{}); err != nil {
|
|
t.Fatalf("B execute: %v", err)
|
|
}
|
|
|
|
if gotModelA != "model-a" {
|
|
t.Errorf("A model: got %q want model-a", gotModelA)
|
|
}
|
|
if gotNumCtxA != 4096 {
|
|
t.Errorf("A num_ctx: got %d want 4096", gotNumCtxA)
|
|
}
|
|
if gotModelB != "model-b" {
|
|
t.Errorf("B model: got %q want model-b", gotModelB)
|
|
}
|
|
if gotNumCtxB != 8192 {
|
|
t.Errorf("B num_ctx: got %d want 8192", gotNumCtxB)
|
|
}
|
|
|
|
capsA, _ := adapterA.Capabilities(context.Background())
|
|
if capsA.InstanceKey != "ollama@local" {
|
|
t.Errorf("A InstanceKey: got %q want ollama@local", capsA.InstanceKey)
|
|
}
|
|
capsB, _ := adapterB.Capabilities(context.Background())
|
|
if capsB.InstanceKey != "ollama@dgx" {
|
|
t.Errorf("B InstanceKey: got %q want ollama@dgx", capsB.InstanceKey)
|
|
}
|
|
}
|
|
|
|
func TestBuildFromPayload_VllmExecution(t *testing.T) {
|
|
var gotModel string
|
|
var gotStream bool
|
|
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.URL.Path == "/v1/models" {
|
|
_, _ = w.Write([]byte(`{"object":"list","data":[]}`))
|
|
return
|
|
}
|
|
if r.URL.Path != "/v1/chat/completions" {
|
|
t.Errorf("unexpected path: %s", r.URL.Path)
|
|
return
|
|
}
|
|
var req struct {
|
|
Model string `json:"model"`
|
|
Stream bool `json:"stream"`
|
|
}
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
t.Errorf("decode: %v", err)
|
|
return
|
|
}
|
|
gotModel = req.Model
|
|
gotStream = req.Stream
|
|
w.Header().Set("Content-Type", "text/event-stream")
|
|
_, _ = fmt.Fprintf(w, "data: %s\n\n", `{"choices":[{"delta":{"content":"ok"}}]}`)
|
|
_, _ = fmt.Fprintf(w, "data: [DONE]\n\n")
|
|
}))
|
|
defer server.Close()
|
|
|
|
reg, err := adapters.BuildFromPayload(&iop.NodeConfigPayload{
|
|
Adapters: []*iop.AdapterConfig{
|
|
{
|
|
Type: "vllm",
|
|
Name: "vllm@gpu",
|
|
Enabled: true,
|
|
Config: &iop.AdapterConfig_Vllm{Vllm: &iop.VllmAdapterConfig{Endpoint: server.URL}},
|
|
},
|
|
},
|
|
}, zap.NewNop())
|
|
if err != nil {
|
|
t.Fatalf("build: %v", err)
|
|
}
|
|
|
|
a, ok := reg.Get("vllm@gpu")
|
|
if !ok {
|
|
t.Fatal("expected vllm@gpu")
|
|
}
|
|
|
|
if err := a.Execute(context.Background(), noderuntime.ExecutionSpec{
|
|
RunID: "run-vllm", Target: "llama-3", Input: map[string]any{"prompt": "hi"},
|
|
}, &testSink{}); err != nil {
|
|
t.Fatalf("Execute: %v", err)
|
|
}
|
|
|
|
if gotModel != "llama-3" {
|
|
t.Errorf("model: got %q want llama-3", gotModel)
|
|
}
|
|
if !gotStream {
|
|
t.Error("expected stream=true")
|
|
}
|
|
|
|
caps, _ := a.Capabilities(context.Background())
|
|
if caps.InstanceKey != "vllm@gpu" {
|
|
t.Errorf("InstanceKey: got %q want vllm@gpu", caps.InstanceKey)
|
|
}
|
|
}
|