feat: add Kimi Coding compatibility and model preset
This commit is contained in:
parent
7cbfa89a96
commit
2a2797f99d
3 changed files with 105 additions and 0 deletions
|
|
@ -177,6 +177,13 @@ func DefaultConfig() *Config {
|
||||||
APIBase: "https://api.moonshot.cn/v1",
|
APIBase: "https://api.moonshot.cn/v1",
|
||||||
APIKey: "",
|
APIKey: "",
|
||||||
},
|
},
|
||||||
|
// Kimi Plan/Coding - https://www.kimi.com/code/docs
|
||||||
|
{
|
||||||
|
ModelName: "kimi-plan-coding",
|
||||||
|
Model: "moonshot/kimi-k2-thinking",
|
||||||
|
APIBase: "https://api.kimi.com/coding/v1",
|
||||||
|
APIKey: "",
|
||||||
|
},
|
||||||
|
|
||||||
// Groq - https://console.groq.com/keys
|
// Groq - https://console.groq.com/keys
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -125,6 +125,9 @@ func (p *Provider) Chat(
|
||||||
if p.apiKey != "" {
|
if p.apiKey != "" {
|
||||||
req.Header.Set("Authorization", "Bearer "+p.apiKey)
|
req.Header.Set("Authorization", "Bearer "+p.apiKey)
|
||||||
}
|
}
|
||||||
|
if ua := selectUserAgent(p.apiBase); ua != "" {
|
||||||
|
req.Header.Set("User-Agent", ua)
|
||||||
|
}
|
||||||
|
|
||||||
resp, err := p.httpClient.Do(req)
|
resp, err := p.httpClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -247,6 +250,15 @@ func normalizeModel(model, apiBase string) string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func selectUserAgent(apiBase string) string {
|
||||||
|
base := strings.ToLower(apiBase)
|
||||||
|
if strings.Contains(base, "api.kimi.com/coding") {
|
||||||
|
// Kimi For Coding currently restricts access to recognized coding-agent clients.
|
||||||
|
return "KimiCLI/1.3"
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
func asInt(v any) (int, bool) {
|
func asInt(v any) (int, bool) {
|
||||||
switch val := v.(type) {
|
switch val := v.(type) {
|
||||||
case int:
|
case int:
|
||||||
|
|
|
||||||
|
|
@ -232,6 +232,92 @@ func TestProvider_ProxyConfigured(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestProviderChat_UsesKimiUserAgentOnCodingEndpoint(t *testing.T) {
|
||||||
|
var gotUA string
|
||||||
|
|
||||||
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
gotUA = r.Header.Get("User-Agent")
|
||||||
|
resp := map[string]any{
|
||||||
|
"choices": []map[string]any{
|
||||||
|
{
|
||||||
|
"message": map[string]any{"content": "ok"},
|
||||||
|
"finish_reason": "stop",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
_ = json.NewEncoder(w).Encode(resp)
|
||||||
|
}))
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
kimiBase := server.URL + "/api.kimi.com/coding/v1"
|
||||||
|
p := NewProvider("key", kimiBase, "")
|
||||||
|
_, err := p.Chat(t.Context(), []Message{{Role: "user", Content: "hi"}}, nil, "kimi-for-coding", nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Chat() error = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if gotUA != "KimiCLI/1.3" {
|
||||||
|
t.Fatalf("User-Agent = %q, want %q", gotUA, "KimiCLI/1.3")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestProviderChat_KimiCodingModelAliases(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
model string
|
||||||
|
}{
|
||||||
|
{name: "k2.5", model: "kimi-k2.5"},
|
||||||
|
{name: "k2-thinking", model: "kimi-k2-thinking"},
|
||||||
|
{name: "for-coding", model: "kimi-for-coding"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
var gotUA string
|
||||||
|
var gotModel string
|
||||||
|
|
||||||
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
gotUA = r.Header.Get("User-Agent")
|
||||||
|
var body map[string]any
|
||||||
|
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if m, ok := body["model"].(string); ok {
|
||||||
|
gotModel = m
|
||||||
|
}
|
||||||
|
|
||||||
|
resp := map[string]any{
|
||||||
|
"choices": []map[string]any{
|
||||||
|
{
|
||||||
|
"message": map[string]any{"content": "ok"},
|
||||||
|
"finish_reason": "stop",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
_ = json.NewEncoder(w).Encode(resp)
|
||||||
|
}))
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
kimiBase := server.URL + "/api.kimi.com/coding/v1"
|
||||||
|
p := NewProvider("key", kimiBase, "")
|
||||||
|
_, err := p.Chat(t.Context(), []Message{{Role: "user", Content: "hi"}}, nil, tt.model, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Chat() error = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if gotUA != "KimiCLI/1.3" {
|
||||||
|
t.Fatalf("User-Agent = %q, want %q", gotUA, "KimiCLI/1.3")
|
||||||
|
}
|
||||||
|
if gotModel != tt.model {
|
||||||
|
t.Fatalf("model = %q, want %q", gotModel, tt.model)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestProviderChat_AcceptsNumericOptionTypes(t *testing.T) {
|
func TestProviderChat_AcceptsNumericOptionTypes(t *testing.T) {
|
||||||
var requestBody map[string]any
|
var requestBody map[string]any
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue