iop/apps/node/internal/adapters/mock/mock.go
toki 0e02958684 feat(node): inference provider availability probing을 추가한다
Adapter에 ProviderProber 인터페이스를 도입하여 provider endpoint와
target model의 가용성을 확인한다.

- runtime에 ProviderStatus, ProviderProbeResult 타입을 추가한다
- ollama/vllm/mock adapter에 ProbeProvider를 구현한다
- node handleCapabilitiesCommand에서 Prober 인터페이스를 호출하여
  target-aware provider status를 응답한다
- 각 adapter 및 node 테스트를 추가한다
- 로드맵 문서를 갱신한다
2026-06-14 16:39:49 +09:00

106 lines
2.4 KiB
Go

// Package mock provides a mock Adapter that echoes input with streaming deltas.
// Useful for testing transport and pipeline correctness without real models.
package mock
import (
"context"
"fmt"
"strings"
"time"
"go.uber.org/zap"
"iop/apps/node/internal/runtime"
)
const Name = "mock"
type Mock struct {
logger *zap.Logger
}
func New(logger *zap.Logger) *Mock {
return &Mock{logger: logger}
}
func (m *Mock) Name() string { return Name }
func (m *Mock) Capabilities(_ context.Context) (runtime.Capabilities, error) {
return runtime.Capabilities{
AdapterName: Name,
Targets: []string{"mock-echo", "mock-stream"},
MaxConcurrency: 16,
ProviderStatus: runtime.ProviderStatusAvailable,
}, nil
}
func (m *Mock) ProbeProvider(ctx context.Context, target string) (runtime.ProviderProbeResult, error) {
targets := []string{"mock-echo", "mock-stream"}
return runtime.ProviderProbeResult{
AdapterName: Name,
Target: target,
Targets: targets,
Status: runtime.ProviderStatusAvailable,
}, nil
}
func (m *Mock) Execute(ctx context.Context, spec runtime.ExecutionSpec, sink runtime.EventSink) error {
m.logger.Info("mock adapter executing", zap.String("run_id", spec.RunID))
if err := sink.Emit(ctx, runtime.RuntimeEvent{
RunID: spec.RunID,
Type: runtime.EventTypeStart,
Timestamp: time.Now(),
}); err != nil {
return err
}
prompt := extractPrompt(spec.Input)
words := strings.Fields(fmt.Sprintf("echo: %s", prompt))
if len(words) == 0 {
words = []string{"(empty", "input)"}
}
for _, word := range words {
select {
case <-ctx.Done():
return ctx.Err()
default:
}
if err := sink.Emit(ctx, runtime.RuntimeEvent{
RunID: spec.RunID,
Type: runtime.EventTypeDelta,
Delta: word + " ",
Timestamp: time.Now(),
}); err != nil {
return err
}
time.Sleep(30 * time.Millisecond)
}
return sink.Emit(ctx, runtime.RuntimeEvent{
RunID: spec.RunID,
Type: runtime.EventTypeComplete,
Message: "mock execution complete",
Usage: &runtime.UsageStats{
InputTokens: len(strings.Fields(prompt)),
OutputTokens: len(words),
},
Timestamp: time.Now(),
})
}
func extractPrompt(input map[string]any) string {
if input == nil {
return ""
}
if v, ok := input["prompt"]; ok {
if s, ok := v.(string); ok {
return s
}
}
if v, ok := input["messages"]; ok {
return fmt.Sprintf("%v", v)
}
return fmt.Sprintf("%v", input)
}