1package provider
2
3import (
4 "encoding/json"
5 "testing"
6)
7
8func TestKimiK3DeferredToolsLoadAtToolResult(t *testing.T) {
9 client := NewMoonshot("token", "").(*openaiClient)
10 tools := []Tool{
11 {Name: "search_tools", Schema: json.RawMessage(`{"type":"object"}`)},
12 {Name: "lookup_weather", Schema: json.RawMessage(`{"type":"object"}`), Deferred: true},
13 }
14 wire, err := client.buildRequest(Request{
15 Model: "kimi-k3",
16 Tools: tools,
17 Messages: []Message{
18 {Role: RoleUser, Content: []Content{TextBlock{Text: "weather"}}},
19 {Role: RoleAssistant, Content: []Content{ToolCallBlock{ID: "call-1", Name: "search_tools", Arguments: json.RawMessage(`{}`)}}},
20 {Role: RoleTool, Content: []Content{ToolResultBlock{CallID: "call-1", Content: []Content{TextBlock{Text: "found"}}}}, AddedToolNames: []string{"lookup_weather"}},
21 },
22 })
23 if err != nil {
24 t.Fatal(err)
25 }
26 if len(wire.Tools) != 1 || wire.Tools[0].Function.Name != "search_tools" {
27 t.Fatalf("top-level tools = %+v, want only search_tools", wire.Tools)
28 }
29 var loaded []oaiTool
30 for _, message := range wire.Messages {
31 if message.Role == "system" && len(message.Tools) > 0 {
32 loaded = message.Tools
33 }
34 }
35 if len(loaded) != 1 || loaded[0].Function.Name != "lookup_weather" {
36 t.Fatalf("loaded tools = %+v, want lookup_weather", loaded)
37 }
38}
39
40func TestDeferredToolsFallbackToActiveTopLevelDefinitions(t *testing.T) {
41 client := NewOpenAI("token", "").(*openaiClient)
42 tools := []Tool{
43 {Name: "base", Schema: json.RawMessage(`{"type":"object"}`)},
44 {Name: "late", Schema: json.RawMessage(`{"type":"object"}`), Deferred: true},
45 }
46 wire, err := client.buildRequest(Request{Model: "gpt-5", Tools: tools})
47 if err != nil {
48 t.Fatal(err)
49 }
50 if len(wire.Tools) != 1 || wire.Tools[0].Function.Name != "base" {
51 t.Fatalf("initial tools = %+v", wire.Tools)
52 }
53
54 wire, err = client.buildRequest(Request{
55 Model: "gpt-5",
56 Tools: tools,
57 Messages: []Message{{Role: RoleTool, AddedToolNames: []string{"late"}}},
58 })
59 if err != nil {
60 t.Fatal(err)
61 }
62 if len(wire.Tools) != 2 {
63 t.Fatalf("activated tools = %+v, want both definitions", wire.Tools)
64 }
65}
66
67func TestKimiK3AndGrok45CatalogMetadata(t *testing.T) {
68 for _, tc := range []struct {
69 provider string
70 model string
71 }{
72 {"kimi", "k3"},
73 {"moonshotai", "kimi-k3"},
74 {"moonshotai-cn", "kimi-k3"},
75 {"openrouter", "moonshotai/kimi-k3"},
76 {"vercel-ai-gateway", "moonshotai/kimi-k3"},
77 } {
78 m, err := FindModel(tc.provider, tc.model)
79 if err != nil {
80 t.Fatalf("%s/%s: %v", tc.provider, tc.model, err)
81 }
82 if m.MaxOutput != 131072 || !m.Reasoning {
83 t.Fatalf("%s/%s metadata = %+v", tc.provider, tc.model, m)
84 }
85 }
86 m, err := FindModel("xai", "grok-4.5")
87 if err != nil {
88 t.Fatal(err)
89 }
90 if !m.Reasoning || m.MaxOutput != 30000 || m.API != APIResponses {
91 t.Fatalf("xai/grok-4.5 metadata = %+v", m)
92 }
93}