106 lines
2.8 KiB
Go
106 lines
2.8 KiB
Go
package backend
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"net/url"
|
|
"testing"
|
|
)
|
|
|
|
// These tests lock in the "no tools loaded" compatibility path: an agent with
|
|
// an empty tools set (and no /tool_load) produces an empty tool slice,
|
|
// and every backend must then emit a request with NO "tools" field. This keeps
|
|
// Ollie compatible with models that do not support tool/function calling — the
|
|
// answer for such a model is simply to use a tool-free agent.
|
|
|
|
// captureBody spins up an httptest server, records the raw request body, drains
|
|
// the (empty) stream, and returns the decoded top-level JSON object.
|
|
func captureBody(t *testing.T, build func(baseURL string) (Backend, error)) map[string]json.RawMessage {
|
|
t.Helper()
|
|
var rawBody []byte
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
rawBody, _ = io.ReadAll(r.Body)
|
|
w.Header().Set("Content-Type", "text/event-stream")
|
|
fmt.Fprint(w, "data: [DONE]\n\n")
|
|
}))
|
|
defer srv.Close()
|
|
|
|
b, err := build(srv.URL)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
msgs := []Message{{Role: "user", Content: "hi"}}
|
|
// No tools: the empty slice must yield a request without a tools field.
|
|
ch, err := b.ChatStream(context.Background(), msgs, nil, GenerationParams{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for range ch { //nolint:revive
|
|
}
|
|
|
|
var top map[string]json.RawMessage
|
|
json.Unmarshal(rawBody, &top) //nolint:errcheck
|
|
return top
|
|
}
|
|
|
|
func assertNoToolsField(t *testing.T, top map[string]json.RawMessage) {
|
|
t.Helper()
|
|
if raw, ok := top["tools"]; ok {
|
|
t.Errorf("request must omit \"tools\" when no tools are loaded; got tools=%s", raw)
|
|
}
|
|
if raw, ok := top["tool_choice"]; ok {
|
|
t.Errorf("request must omit \"tool_choice\" when no tools are loaded; got tool_choice=%s", raw)
|
|
}
|
|
}
|
|
|
|
func TestNoTools_OpenAIOmitsToolsField(t *testing.T) {
|
|
top := captureBody(t, func(baseURL string) (Backend, error) {
|
|
b, err := NewOpenAI("openai", baseURL, "key")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
b.SetModel("gpt-4o")
|
|
return b, nil
|
|
})
|
|
assertNoToolsField(t, top)
|
|
}
|
|
|
|
func TestNoTools_OpenRouterOmitsToolsField(t *testing.T) {
|
|
top := captureBody(t, func(baseURL string) (Backend, error) {
|
|
b, err := NewOpenAI("openrouter", baseURL, "key")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
b.SetModel("mistral-7b")
|
|
return b, nil
|
|
})
|
|
assertNoToolsField(t, top)
|
|
}
|
|
|
|
func TestNoTools_OllamaOmitsToolsField(t *testing.T) {
|
|
top := captureBody(t, func(baseURL string) (Backend, error) {
|
|
return NewOllama(baseURL)
|
|
})
|
|
assertNoToolsField(t, top)
|
|
}
|
|
|
|
func TestNoTools_AnthropicOmitsToolsField(t *testing.T) {
|
|
top := captureBody(t, func(baseURL string) (Backend, error) {
|
|
b, err := NewAnthropic("key")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
u, err := url.Parse(baseURL)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
b.baseURL = u
|
|
return b, nil
|
|
})
|
|
assertNoToolsField(t, top)
|
|
}
|