fix(kimi): update base URL and integrate ClaudeExecutor fallback
Browse files- Updated `KimiAPIBaseURL` to remove versioning from the root path.
- Integrated `ClaudeExecutor` fallback in `KimiExecutor` methods for compatibility with Claude requests.
- Simplified token counting by delegating to `ClaudeExecutor`.
internal/auth/kimi/kimi.go
CHANGED
|
@@ -30,7 +30,7 @@ const (
|
|
| 30 |
// kimiTokenURL is the endpoint for exchanging device codes for tokens.
|
| 31 |
kimiTokenURL = kimiOAuthHost + "/api/oauth/token"
|
| 32 |
// KimiAPIBaseURL is the base URL for Kimi API requests.
|
| 33 |
-
KimiAPIBaseURL = "https://api.kimi.com/coding
|
| 34 |
// defaultPollInterval is the default interval for polling token endpoint.
|
| 35 |
defaultPollInterval = 5 * time.Second
|
| 36 |
// maxPollDuration is the maximum time to wait for user authorization.
|
|
|
|
| 30 |
// kimiTokenURL is the endpoint for exchanging device codes for tokens.
|
| 31 |
kimiTokenURL = kimiOAuthHost + "/api/oauth/token"
|
| 32 |
// KimiAPIBaseURL is the base URL for Kimi API requests.
|
| 33 |
+
KimiAPIBaseURL = "https://api.kimi.com/coding"
|
| 34 |
// defaultPollInterval is the default interval for polling token endpoint.
|
| 35 |
defaultPollInterval = 5 * time.Second
|
| 36 |
// maxPollDuration is the maximum time to wait for user authorization.
|
internal/runtime/executor/kimi_executor.go
CHANGED
|
@@ -25,6 +25,7 @@ import (
|
|
| 25 |
|
| 26 |
// KimiExecutor is a stateless executor for Kimi API using OpenAI-compatible chat completions.
|
| 27 |
type KimiExecutor struct {
|
|
|
|
| 28 |
cfg *config.Config
|
| 29 |
}
|
| 30 |
|
|
@@ -64,6 +65,12 @@ func (e *KimiExecutor) HttpRequest(ctx context.Context, auth *cliproxyauth.Auth,
|
|
| 64 |
|
| 65 |
// Execute performs a non-streaming chat completion request to Kimi.
|
| 66 |
func (e *KimiExecutor) Execute(ctx context.Context, auth *cliproxyauth.Auth, req cliproxyexecutor.Request, opts cliproxyexecutor.Options) (resp cliproxyexecutor.Response, err error) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 67 |
baseModel := thinking.ParseSuffix(req.Model).ModelName
|
| 68 |
|
| 69 |
token := kimiCreds(auth)
|
|
@@ -71,7 +78,6 @@ func (e *KimiExecutor) Execute(ctx context.Context, auth *cliproxyauth.Auth, req
|
|
| 71 |
reporter := newUsageReporter(ctx, e.Identifier(), baseModel, auth)
|
| 72 |
defer reporter.trackFailure(ctx, &err)
|
| 73 |
|
| 74 |
-
from := opts.SourceFormat
|
| 75 |
to := sdktranslator.FromString("openai")
|
| 76 |
originalPayload := bytes.Clone(req.Payload)
|
| 77 |
if len(opts.OriginalRequest) > 0 {
|
|
@@ -95,7 +101,7 @@ func (e *KimiExecutor) Execute(ctx context.Context, auth *cliproxyauth.Auth, req
|
|
| 95 |
requestedModel := payloadRequestedModel(opts, req.Model)
|
| 96 |
body = applyPayloadConfigWithRoot(e.cfg, baseModel, to.String(), "", body, originalTranslated, requestedModel)
|
| 97 |
|
| 98 |
-
url := kimiauth.KimiAPIBaseURL + "/chat/completions"
|
| 99 |
httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(body))
|
| 100 |
if err != nil {
|
| 101 |
return resp, err
|
|
@@ -155,14 +161,18 @@ func (e *KimiExecutor) Execute(ctx context.Context, auth *cliproxyauth.Auth, req
|
|
| 155 |
|
| 156 |
// ExecuteStream performs a streaming chat completion request to Kimi.
|
| 157 |
func (e *KimiExecutor) ExecuteStream(ctx context.Context, auth *cliproxyauth.Auth, req cliproxyexecutor.Request, opts cliproxyexecutor.Options) (stream <-chan cliproxyexecutor.StreamChunk, err error) {
|
| 158 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 159 |
|
|
|
|
| 160 |
token := kimiCreds(auth)
|
| 161 |
|
| 162 |
reporter := newUsageReporter(ctx, e.Identifier(), baseModel, auth)
|
| 163 |
defer reporter.trackFailure(ctx, &err)
|
| 164 |
|
| 165 |
-
from := opts.SourceFormat
|
| 166 |
to := sdktranslator.FromString("openai")
|
| 167 |
originalPayload := bytes.Clone(req.Payload)
|
| 168 |
if len(opts.OriginalRequest) > 0 {
|
|
@@ -190,7 +200,7 @@ func (e *KimiExecutor) ExecuteStream(ctx context.Context, auth *cliproxyauth.Aut
|
|
| 190 |
requestedModel := payloadRequestedModel(opts, req.Model)
|
| 191 |
body = applyPayloadConfigWithRoot(e.cfg, baseModel, to.String(), "", body, originalTranslated, requestedModel)
|
| 192 |
|
| 193 |
-
url := kimiauth.KimiAPIBaseURL + "/chat/completions"
|
| 194 |
httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(body))
|
| 195 |
if err != nil {
|
| 196 |
return nil, err
|
|
@@ -269,26 +279,8 @@ func (e *KimiExecutor) ExecuteStream(ctx context.Context, auth *cliproxyauth.Aut
|
|
| 269 |
|
| 270 |
// CountTokens estimates token count for Kimi requests.
|
| 271 |
func (e *KimiExecutor) CountTokens(ctx context.Context, auth *cliproxyauth.Auth, req cliproxyexecutor.Request, opts cliproxyexecutor.Options) (cliproxyexecutor.Response, error) {
|
| 272 |
-
|
| 273 |
-
|
| 274 |
-
from := opts.SourceFormat
|
| 275 |
-
to := sdktranslator.FromString("openai")
|
| 276 |
-
body := sdktranslator.TranslateRequest(from, to, baseModel, bytes.Clone(req.Payload), false)
|
| 277 |
-
|
| 278 |
-
// Use a generic tokenizer for estimation
|
| 279 |
-
enc, err := tokenizerForModel("gpt-4")
|
| 280 |
-
if err != nil {
|
| 281 |
-
return cliproxyexecutor.Response{}, fmt.Errorf("kimi executor: tokenizer init failed: %w", err)
|
| 282 |
-
}
|
| 283 |
-
|
| 284 |
-
count, err := countOpenAIChatTokens(enc, body)
|
| 285 |
-
if err != nil {
|
| 286 |
-
return cliproxyexecutor.Response{}, fmt.Errorf("kimi executor: token counting failed: %w", err)
|
| 287 |
-
}
|
| 288 |
-
|
| 289 |
-
usageJSON := buildOpenAIUsageJSON(count)
|
| 290 |
-
translated := sdktranslator.TranslateTokenCount(ctx, to, from, count, usageJSON)
|
| 291 |
-
return cliproxyexecutor.Response{Payload: []byte(translated)}, nil
|
| 292 |
}
|
| 293 |
|
| 294 |
// Refresh refreshes the Kimi token using the refresh token.
|
|
|
|
| 25 |
|
| 26 |
// KimiExecutor is a stateless executor for Kimi API using OpenAI-compatible chat completions.
|
| 27 |
type KimiExecutor struct {
|
| 28 |
+
ClaudeExecutor
|
| 29 |
cfg *config.Config
|
| 30 |
}
|
| 31 |
|
|
|
|
| 65 |
|
| 66 |
// Execute performs a non-streaming chat completion request to Kimi.
|
| 67 |
func (e *KimiExecutor) Execute(ctx context.Context, auth *cliproxyauth.Auth, req cliproxyexecutor.Request, opts cliproxyexecutor.Options) (resp cliproxyexecutor.Response, err error) {
|
| 68 |
+
from := opts.SourceFormat
|
| 69 |
+
if from.String() == "claude" {
|
| 70 |
+
auth.Attributes["base_url"] = kimiauth.KimiAPIBaseURL
|
| 71 |
+
return e.ClaudeExecutor.Execute(ctx, auth, req, opts)
|
| 72 |
+
}
|
| 73 |
+
|
| 74 |
baseModel := thinking.ParseSuffix(req.Model).ModelName
|
| 75 |
|
| 76 |
token := kimiCreds(auth)
|
|
|
|
| 78 |
reporter := newUsageReporter(ctx, e.Identifier(), baseModel, auth)
|
| 79 |
defer reporter.trackFailure(ctx, &err)
|
| 80 |
|
|
|
|
| 81 |
to := sdktranslator.FromString("openai")
|
| 82 |
originalPayload := bytes.Clone(req.Payload)
|
| 83 |
if len(opts.OriginalRequest) > 0 {
|
|
|
|
| 101 |
requestedModel := payloadRequestedModel(opts, req.Model)
|
| 102 |
body = applyPayloadConfigWithRoot(e.cfg, baseModel, to.String(), "", body, originalTranslated, requestedModel)
|
| 103 |
|
| 104 |
+
url := kimiauth.KimiAPIBaseURL + "/v1/chat/completions"
|
| 105 |
httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(body))
|
| 106 |
if err != nil {
|
| 107 |
return resp, err
|
|
|
|
| 161 |
|
| 162 |
// ExecuteStream performs a streaming chat completion request to Kimi.
|
| 163 |
func (e *KimiExecutor) ExecuteStream(ctx context.Context, auth *cliproxyauth.Auth, req cliproxyexecutor.Request, opts cliproxyexecutor.Options) (stream <-chan cliproxyexecutor.StreamChunk, err error) {
|
| 164 |
+
from := opts.SourceFormat
|
| 165 |
+
if from.String() == "claude" {
|
| 166 |
+
auth.Attributes["base_url"] = kimiauth.KimiAPIBaseURL
|
| 167 |
+
return e.ClaudeExecutor.ExecuteStream(ctx, auth, req, opts)
|
| 168 |
+
}
|
| 169 |
|
| 170 |
+
baseModel := thinking.ParseSuffix(req.Model).ModelName
|
| 171 |
token := kimiCreds(auth)
|
| 172 |
|
| 173 |
reporter := newUsageReporter(ctx, e.Identifier(), baseModel, auth)
|
| 174 |
defer reporter.trackFailure(ctx, &err)
|
| 175 |
|
|
|
|
| 176 |
to := sdktranslator.FromString("openai")
|
| 177 |
originalPayload := bytes.Clone(req.Payload)
|
| 178 |
if len(opts.OriginalRequest) > 0 {
|
|
|
|
| 200 |
requestedModel := payloadRequestedModel(opts, req.Model)
|
| 201 |
body = applyPayloadConfigWithRoot(e.cfg, baseModel, to.String(), "", body, originalTranslated, requestedModel)
|
| 202 |
|
| 203 |
+
url := kimiauth.KimiAPIBaseURL + "/v1/chat/completions"
|
| 204 |
httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(body))
|
| 205 |
if err != nil {
|
| 206 |
return nil, err
|
|
|
|
| 279 |
|
| 280 |
// CountTokens estimates token count for Kimi requests.
|
| 281 |
func (e *KimiExecutor) CountTokens(ctx context.Context, auth *cliproxyauth.Auth, req cliproxyexecutor.Request, opts cliproxyexecutor.Options) (cliproxyexecutor.Response, error) {
|
| 282 |
+
auth.Attributes["base_url"] = kimiauth.KimiAPIBaseURL
|
| 283 |
+
return e.ClaudeExecutor.CountTokens(ctx, auth, req, opts)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 284 |
}
|
| 285 |
|
| 286 |
// Refresh refreshes the Kimi token using the refresh token.
|