package api
// These tests pin the wire format of LLMFunc. CanSeeImages is the
// pre-existing capability flag (do not rename — production func
// definitions at oscarkilo.com/klex/funcs already use it).
// CanSeePDFs is the new capability flag for PDF attachments. The
// two are independent because some providers support images but
// not PDFs (Fireworks, Ollama, xAI-via-v1-of-our-adapter).
import "encoding/json"
import "strings"
import "testing"
func TestLLMFuncMarshalImagesPDFs(t *testing.T) {
f := LLMFunc{
Provider: "anthropic",
Model: "claude-sonnet-4-6",
CanSeeImages: true,
CanSeePDFs: true,
CanStream: true,
}
buf, err := json.Marshal(f)
if err != nil {
t.Fatalf("marshal: %v", err)
}
s := string(buf)
if !strings.Contains(s, `"can_see_images":true`) {
t.Errorf("missing can_see_images in %s", s)
}
if !strings.Contains(s, `"can_see_pdfs":true`) {
t.Errorf("missing can_see_pdfs in %s", s)
}
}
func TestLLMFuncUnmarshalLegacyData(t *testing.T) {
// Existing production func versions are stored without a
// can_see_pdfs key. They must unmarshal to CanSeePDFs=false
// without error.
in := []byte(
`{"provider":"anthropic","model":"x","can_see_images":true}`)
var f LLMFunc
if err := json.Unmarshal(in, &f); err != nil {
t.Fatalf("unmarshal: %v", err)
}
if !f.CanSeeImages {
t.Errorf("CanSeeImages = false, want true")
}
if f.CanSeePDFs {
t.Errorf("CanSeePDFs = true, want false (key absent)")
}
}
func TestLLMFuncUnmarshalPDFsOnly(t *testing.T) {
// A model that accepts PDFs but not images (rare but possible
// if a future provider goes that way).
in := []byte(
`{"provider":"x","model":"y","can_see_pdfs":true}`)
var f LLMFunc
if err := json.Unmarshal(in, &f); err != nil {
t.Fatalf("unmarshal: %v", err)
}
if f.CanSeeImages {
t.Errorf("CanSeeImages = true, want false")
}
if !f.CanSeePDFs {
t.Errorf("CanSeePDFs = false, want true")
}
}
func TestLLMFuncUnmarshalBoth(t *testing.T) {
in := []byte(
`{"provider":"openai","model":"gpt-4o",` +
`"can_see_images":true,"can_see_pdfs":true}`)
var f LLMFunc
if err := json.Unmarshal(in, &f); err != nil {
t.Fatalf("unmarshal: %v", err)
}
if !f.CanSeeImages || !f.CanSeePDFs {
t.Errorf("expected both flags true; got %+v", f)
}
}
func TestLLMFuncUnmarshalNeither(t *testing.T) {
in := []byte(`{"provider":"x","model":"y"}`)
var f LLMFunc
if err := json.Unmarshal(in, &f); err != nil {
t.Fatalf("unmarshal: %v", err)
}
if f.CanSeeImages || f.CanSeePDFs {
t.Errorf("expected both flags false; got %+v", f)
}
}
func TestLLMFuncRoundTrip(t *testing.T) {
orig := LLMFunc{
Provider: "openai",
Model: "gpt-4o",
CanSeeImages: true,
CanSeePDFs: true,
CanUseTools: true,
CanStream: true,
}
buf, err := json.Marshal(orig)
if err != nil {
t.Fatalf("marshal: %v", err)
}
var got LLMFunc
if err := json.Unmarshal(buf, &got); err != nil {
t.Fatalf("unmarshal: %v", err)
}
if got != orig {
t.Errorf("round-trip diff\nwant: %+v\n got: %+v", orig, got)
}
}