mirror of
https://github.com/lukaszraczylo/mcp-captcharesolver.git
synced 2026-10-18 01:17:53 +00:00
feat(llm): client interface and fake test double
This commit is contained in:
@@ -0,0 +1,36 @@
|
|||||||
|
// Package llm defines a provider-agnostic LLM client for vision and audio
|
||||||
|
// transcription, with concrete HTTP implementations per provider.
|
||||||
|
package llm
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
|
// Image is a decoded image to send to a vision model.
|
||||||
|
type Image struct {
|
||||||
|
MediaType string // e.g. "image/png", "image/jpeg"
|
||||||
|
Data []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// Options are per-call overrides. Zero values mean "use the client default".
|
||||||
|
type Options struct {
|
||||||
|
Model string
|
||||||
|
Language string
|
||||||
|
Temperature float64
|
||||||
|
MaxTokens int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Client is the abstraction every provider implements and every handler depends on.
|
||||||
|
type Client interface {
|
||||||
|
// Vision sends a prompt plus images and returns the model's text answer.
|
||||||
|
Vision(ctx context.Context, prompt string, images []Image, opts Options) (string, error)
|
||||||
|
// Transcribe converts audio bytes to text. filename carries the extension
|
||||||
|
// for providers that infer the audio format from it. Returns ErrUnsupported
|
||||||
|
// if the provider has no audio capability.
|
||||||
|
Transcribe(ctx context.Context, audio []byte, filename string, opts Options) (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrUnsupported is returned by Transcribe on providers without audio support.
|
||||||
|
type ErrUnsupported struct{ Provider string }
|
||||||
|
|
||||||
|
func (e ErrUnsupported) Error() string {
|
||||||
|
return "audio transcription unsupported by provider " + e.Provider
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package llm
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
|
// Fake is a deterministic Client for tests.
|
||||||
|
type Fake struct {
|
||||||
|
VisionResp string
|
||||||
|
TranscribeResp string
|
||||||
|
VisionErr error
|
||||||
|
TranscribeErr error
|
||||||
|
VisionCalls []VisionCall
|
||||||
|
TranscribeCalls []TranscribeCall
|
||||||
|
}
|
||||||
|
|
||||||
|
// VisionCall records a Vision invocation.
|
||||||
|
type VisionCall struct {
|
||||||
|
Prompt string
|
||||||
|
Opts Options
|
||||||
|
Images []Image
|
||||||
|
}
|
||||||
|
|
||||||
|
// TranscribeCall records a Transcribe invocation.
|
||||||
|
type TranscribeCall struct {
|
||||||
|
Filename string
|
||||||
|
Opts Options
|
||||||
|
Audio []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vision implements Client.
|
||||||
|
func (f *Fake) Vision(_ context.Context, prompt string, images []Image, opts Options) (string, error) {
|
||||||
|
f.VisionCalls = append(f.VisionCalls, VisionCall{Prompt: prompt, Images: images, Opts: opts})
|
||||||
|
return f.VisionResp, f.VisionErr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transcribe implements Client.
|
||||||
|
func (f *Fake) Transcribe(_ context.Context, audio []byte, filename string, opts Options) (string, error) {
|
||||||
|
f.TranscribeCalls = append(f.TranscribeCalls, TranscribeCall{Audio: audio, Filename: filename, Opts: opts})
|
||||||
|
return f.TranscribeResp, f.TranscribeErr
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package llm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFakeClient(t *testing.T) {
|
||||||
|
f := &Fake{VisionResp: "hello", TranscribeResp: "spoken"}
|
||||||
|
got, err := f.Vision(context.Background(), "p", []Image{{MediaType: "image/png", Data: []byte{1}}}, Options{})
|
||||||
|
if err != nil || got != "hello" {
|
||||||
|
t.Fatalf("vision got %q err %v", got, err)
|
||||||
|
}
|
||||||
|
if len(f.VisionCalls) != 1 || f.VisionCalls[0].Prompt != "p" {
|
||||||
|
t.Fatalf("vision call not recorded: %+v", f.VisionCalls)
|
||||||
|
}
|
||||||
|
tr, err := f.Transcribe(context.Background(), []byte{1}, "audio.mp3", Options{Language: "en"})
|
||||||
|
if err != nil || tr != "spoken" {
|
||||||
|
t.Fatalf("transcribe got %q err %v", tr, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user