From 74b44e1aa3ecd6687b3aa4ef731d0187720c3cb5 Mon Sep 17 00:00:00 2001 From: Jin Hai Date: Tue, 21 Apr 2026 21:31:50 +0800 Subject: [PATCH] Go: add balance command (#14262) ### What problem does this PR solve? ``` RAGFlow(user)> list supported models from 'moonshot' 'test'; +---------------------------------+ | model_name | +---------------------------------+ | moonshot-v1-32k-vision-preview | | kimi-k2.6 | | moonshot-v1-8k | | moonshot-v1-auto | | moonshot-v1-128k | | moonshot-v1-32k | | kimi-k2.5 | | moonshot-v1-8k-vision-preview | | moonshot-v1-128k-vision-preview | +---------------------------------+ RAGFlow(user)> show balance from 'moonshot' 'test'; +---------+----------+ | balance | currency | +---------+----------+ | 0 | CNY | +---------+----------+ ``` ### Type of change - [x] New Feature (non-breaking change which adds functionality) --------- Signed-off-by: Jin Hai --- internal/cli/client.go | 2 + internal/cli/lexer.go | 2 + internal/cli/types.go | 1 + internal/cli/user_command.go | 41 ++++++++++++++ internal/cli/user_parser.go | 41 ++++++++++++++ internal/entity/models/deepseek.go | 8 +++ internal/entity/models/dummy.go | 8 +++ internal/entity/models/factory.go | 2 +- internal/entity/models/moonshot.go | 90 +++++++++++++++++++++++++----- internal/entity/models/types.go | 4 ++ internal/entity/models/zhipu-ai.go | 16 ++++-- internal/handler/providers.go | 38 +++++++++++++ internal/router/router.go | 1 + internal/service/model_service.go | 70 +++++++++++++++++++++++ uv.lock | 23 ++++++++ 15 files changed, 329 insertions(+), 18 deletions(-) diff --git a/internal/cli/client.go b/internal/cli/client.go index fc9e920ed7..984e1e8ff8 100644 --- a/internal/cli/client.go +++ b/internal/cli/client.go @@ -236,6 +236,8 @@ func (c *RAGFlowClient) ExecuteUserCommand(cmd *Command) (ResponseIf, error) { return c.ListProviderInstances(cmd) case "show_provider_instance": return c.ShowProviderInstance(cmd) + case "show_instance_balance": + return c.ShowInstanceBalance(cmd) case "alter_provider_instance": return c.AlterProviderInstance(cmd) case "drop_provider_instance": diff --git a/internal/cli/lexer.go b/internal/cli/lexer.go index 26d3f647a0..631441626b 100644 --- a/internal/cli/lexer.go +++ b/internal/cli/lexer.go @@ -369,6 +369,8 @@ func (l *Lexer) lookupIdent(ident string) Token { return Token{Type: TokenSupported, Value: ident} case "NAME": return Token{Type: TokenName, Value: ident} + case "BALANCE": + return Token{Type: TokenBalance, Value: ident} case "INSTANCE": return Token{Type: TokenInstance, Value: ident} case "INSTANCES": diff --git a/internal/cli/types.go b/internal/cli/types.go index b8b2115ec9..59130f3107 100644 --- a/internal/cli/types.go +++ b/internal/cli/types.go @@ -109,6 +109,7 @@ const ( TokenVector TokenSize TokenName // For ALTER PROVIDER NAME + TokenBalance TokenInstance TokenInstances TokenDisable diff --git a/internal/cli/user_command.go b/internal/cli/user_command.go index 23d20c8da5..875ab14ac2 100644 --- a/internal/cli/user_command.go +++ b/internal/cli/user_command.go @@ -1234,6 +1234,47 @@ func (c *RAGFlowClient) ShowProviderInstance(cmd *Command) (ResponseIf, error) { return &result, nil } +// ShowInstanceBalance shows balance of a specific instance +// SHOW BALANCE FROM PROVIDER +func (c *RAGFlowClient) ShowInstanceBalance(cmd *Command) (ResponseIf, error) { + if c.ServerType != "user" { + return nil, fmt.Errorf("this command is only allowed in USER mode") + } + + instanceName, ok := cmd.Params["instance_name"].(string) + if !ok { + return nil, fmt.Errorf("instance name not provided") + } + + providerName, ok := cmd.Params["provider_name"].(string) + if !ok { + return nil, fmt.Errorf("provider name not provided") + } + + url := fmt.Sprintf("/providers/%s/instances/%s/balance", providerName, instanceName) + + resp, err := c.HTTPClient.Request("GET", url, true, "web", nil, nil) + if err != nil { + return nil, fmt.Errorf("failed to show instance: %w", err) + } + + if resp.StatusCode != 200 { + return nil, fmt.Errorf("failed to show instance: HTTP %d, body: %s", resp.StatusCode, string(resp.Body)) + } + + var result CommonDataResponse + if err = json.Unmarshal(resp.Body, &result); err != nil { + return nil, fmt.Errorf("show instance failed: invalid JSON (%w)", err) + } + + if result.Code != 0 { + return nil, fmt.Errorf("%s", result.Message) + } + + result.Duration = resp.Duration + return &result, nil +} + // AlterProviderInstance renames a provider instance // ALTER INSTANCE NAME FROM PROVIDER func (c *RAGFlowClient) AlterProviderInstance(cmd *Command) (ResponseIf, error) { diff --git a/internal/cli/user_parser.go b/internal/cli/user_parser.go index ff46c0e378..d9e48ab974 100644 --- a/internal/cli/user_parser.go +++ b/internal/cli/user_parser.go @@ -352,6 +352,8 @@ func (p *Parser) parseShowCommand() (*Command, error) { return p.parseShowModel() case TokenInstance: return p.parseShowInstance() + case TokenBalance: + return p.parseShowBalance() default: return nil, fmt.Errorf("unknown SHOW target: %s", p.curToken.Value) } @@ -1301,6 +1303,45 @@ func (p *Parser) parseShowInstance() (*Command, error) { return cmd, nil } +// parseShowInstance parses SHOW BALANCE FROM +func (p *Parser) parseShowBalance() (*Command, error) { + p.nextToken() // consume INSTANCE + + if p.curToken.Type != TokenFrom { + return nil, fmt.Errorf("expected FROM") + } + p.nextToken() + + if p.curToken.Type != TokenQuotedString { + return nil, fmt.Errorf("expected provider name after FROM PROVIDER") + } + providerName, err := p.parseQuotedString() + if err != nil { + return nil, fmt.Errorf("expected provider name after FROM PROVIDER: %w", err) + } + p.nextToken() + + if p.curToken.Type != TokenQuotedString { + return nil, fmt.Errorf("expected instance name") + } + instanceName, err := p.parseQuotedString() + if err != nil { + return nil, fmt.Errorf("expected instance name: %w", err) + } + p.nextToken() + + cmd := NewCommand("show_instance_balance") + cmd.Params["instance_name"] = instanceName + cmd.Params["provider_name"] = providerName + + p.nextToken() + // Semicolon is optional + if p.curToken.Type == TokenSemicolon { + p.nextToken() + } + return cmd, nil +} + // parseAlterInstance parses ALTER INSTANCE NAME FROM PROVIDER command func (p *Parser) parseAlterInstance() (*Command, error) { p.nextToken() // consume INSTANCE diff --git a/internal/entity/models/deepseek.go b/internal/entity/models/deepseek.go index ef3a81a0f2..6d2945190a 100644 --- a/internal/entity/models/deepseek.go +++ b/internal/entity/models/deepseek.go @@ -49,6 +49,10 @@ func NewDeepSeekModel(baseURL map[string]string, urlSuffix URLSuffix) *DeepSeekM } } +func (z *DeepSeekModel) Name() string { + return "deepseek" +} + // Chat sends a message and returns response func (z *DeepSeekModel) Chat(modelName, message *string, apiConfig *APIConfig, chatModelConfig *ChatConfig) (*ChatResponse, error) { return nil, fmt.Errorf("not implemented") @@ -145,3 +149,7 @@ func (z *DeepSeekModel) ListModels(apiConfig *APIConfig) ([]string, error) { return models, nil } + +func (z *DeepSeekModel) Balance(apiConfig *APIConfig) (map[string]interface{}, error) { + return nil, fmt.Errorf("%s, no such method", z.Name()) +} diff --git a/internal/entity/models/dummy.go b/internal/entity/models/dummy.go index ed07ad6647..4846a45776 100644 --- a/internal/entity/models/dummy.go +++ b/internal/entity/models/dummy.go @@ -34,6 +34,10 @@ func NewDummyModel(baseURL map[string]string, urlSuffix URLSuffix) *DummyModel { } } +func (z *DummyModel) Name() string { + return "dummy" +} + // Chat sends a message and returns response func (z *DummyModel) Chat(modelName, message *string, apiConfig *APIConfig, modelConfig *ChatConfig) (*ChatResponse, error) { return nil, fmt.Errorf("not implemented") @@ -52,3 +56,7 @@ func (z *DummyModel) EncodeToEmbedding(modelName *string, texts []string, apiCon func (z *DummyModel) ListModels(apiConfig *APIConfig) ([]string, error) { return nil, fmt.Errorf("not implemented") } + +func (z *DummyModel) Balance(apiConfig *APIConfig) (map[string]interface{}, error) { + return nil, fmt.Errorf("no such method") +} diff --git a/internal/entity/models/factory.go b/internal/entity/models/factory.go index 1a4ef46138..dd9efc1667 100644 --- a/internal/entity/models/factory.go +++ b/internal/entity/models/factory.go @@ -38,7 +38,7 @@ func (f *ModelFactory) CreateModelDriver(providerName string, baseURL map[string case "deepseek": return NewDeepSeekModel(baseURL, urlSuffix), nil case "moonshot": - return NewMooshotModel(baseURL, urlSuffix), nil + return NewMoonshotModel(baseURL, urlSuffix), nil default: return NewDummyModel(baseURL, urlSuffix), nil } diff --git a/internal/entity/models/moonshot.go b/internal/entity/models/moonshot.go index 85b16a80a1..55058cb41a 100644 --- a/internal/entity/models/moonshot.go +++ b/internal/entity/models/moonshot.go @@ -25,16 +25,16 @@ import ( "time" ) -// MooshotModel implements ModelDriver for Mooshot -type MooshotModel struct { +// MoonshotModel implements ModelDriver for Moonshot +type MoonshotModel struct { BaseURL map[string]string URLSuffix URLSuffix httpClient *http.Client // Reusable HTTP client with connection pool } -// NewMooshotModel creates a new Mooshot model instance -func NewMooshotModel(baseURL map[string]string, urlSuffix URLSuffix) *MooshotModel { - return &MooshotModel{ +// NewMoonshotModel creates a new Moonshot model instance +func NewMoonshotModel(baseURL map[string]string, urlSuffix URLSuffix) *MoonshotModel { + return &MoonshotModel{ BaseURL: baseURL, URLSuffix: urlSuffix, httpClient: &http.Client{ @@ -49,22 +49,26 @@ func NewMooshotModel(baseURL map[string]string, urlSuffix URLSuffix) *MooshotMod } } +func (z *MoonshotModel) Name() string { + return "moonshot" +} + // Chat sends a message and returns response -func (z *MooshotModel) Chat(modelName, message *string, apiConfig *APIConfig, chatModelConfig *ChatConfig) (*ChatResponse, error) { +func (z *MoonshotModel) Chat(modelName, message *string, apiConfig *APIConfig, chatModelConfig *ChatConfig) (*ChatResponse, error) { return nil, fmt.Errorf("not implemented") } // ChatStreamlyWithSender sends a message and streams response via sender function (best performance, no channel) -func (z *MooshotModel) ChatStreamlyWithSender(modelName, message *string, apiConfig *APIConfig, chatModelConfig *ChatConfig, sender func(*string, *string) error) error { +func (z *MoonshotModel) ChatStreamlyWithSender(modelName, message *string, apiConfig *APIConfig, chatModelConfig *ChatConfig, sender func(*string, *string) error) error { return fmt.Errorf("not implemented") } // EncodeToEmbedding encodes a list of texts into embeddings -func (z *MooshotModel) EncodeToEmbedding(modelName *string, texts []string, apiConfig *APIConfig, embeddingConfig *EmbeddingConfig) ([][]float64, error) { +func (z *MoonshotModel) EncodeToEmbedding(modelName *string, texts []string, apiConfig *APIConfig, embeddingConfig *EmbeddingConfig) ([][]float64, error) { return nil, fmt.Errorf("not implemented") } -func (z *MooshotModel) ListModels(apiConfig *APIConfig) ([]string, error) { +func (z *MoonshotModel) ListModels(apiConfig *APIConfig) ([]string, error) { var region = "default" if apiConfig.Region != nil { region = *apiConfig.Region @@ -80,7 +84,7 @@ func (z *MooshotModel) ListModels(apiConfig *APIConfig) ([]string, error) { return nil, fmt.Errorf("failed to marshal request: %w", err) } - req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData)) + req, err := http.NewRequest("GET", url, bytes.NewBuffer(jsonData)) if err != nil { return nil, fmt.Errorf("failed to create request: %w", err) } @@ -109,10 +113,70 @@ func (z *MooshotModel) ListModels(apiConfig *APIConfig) ([]string, error) { return nil, fmt.Errorf("failed to parse response: %w", err) } - models, ok := result["models"].([]string) - if !ok || len(models) == 0 { - return nil, fmt.Errorf("no models in response") + // convert result["data"] to []map[string]interface{} + models := make([]string, 0) + for _, model := range result["data"].([]interface{}) { + modelMap := model.(map[string]interface{}) + modelName := modelMap["id"].(string) + models = append(models, modelName) } return models, nil } + +func (z *MoonshotModel) Balance(apiConfig *APIConfig) (map[string]interface{}, error) { + + var region = "default" + if apiConfig.Region != nil { + region = *apiConfig.Region + } + + url := fmt.Sprintf("%s/%s", z.BaseURL[region], z.URLSuffix.Balance) + + // Build request body + reqBody := map[string]interface{}{} + + jsonData, err := json.Marshal(reqBody) + if err != nil { + return nil, fmt.Errorf("failed to marshal request: %w", err) + } + + req, err := http.NewRequest("GET", url, bytes.NewBuffer(jsonData)) + if err != nil { + return nil, fmt.Errorf("failed to create request: %w", err) + } + + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", *apiConfig.ApiKey)) + + resp, err := z.httpClient.Do(req) + if err != nil { + return nil, fmt.Errorf("failed to send request: %w", err) + } + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("failed to read response: %w", err) + } + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("API request failed with status %d: %s", resp.StatusCode, string(body)) + } + + // Parse response + var result map[string]interface{} + if err = json.Unmarshal(body, &result); err != nil { + return nil, fmt.Errorf("failed to parse response: %w", err) + } + + data := result["data"].(map[string]interface{}) + balance := data["available_balance"].(float64) + + var response = map[string]interface{}{ + "balance": balance, + "currency": "CNY", + } + + return response, nil +} diff --git a/internal/entity/models/types.go b/internal/entity/models/types.go index db005e740e..c316fd60eb 100644 --- a/internal/entity/models/types.go +++ b/internal/entity/models/types.go @@ -2,6 +2,8 @@ package models // EmbeddingModel interface for embedding models type ModelDriver interface { + Name() string + // Chat sends a message and returns response Chat(modelName, message *string, apiConfig *APIConfig, modelConfig *ChatConfig) (*ChatResponse, error) // ChatStreamlyWithSender sends a message and streams response via sender function (best performance, no channel) @@ -10,6 +12,8 @@ type ModelDriver interface { EncodeToEmbedding(modelName *string, texts []string, apiConfig *APIConfig, embeddingConfig *EmbeddingConfig) ([][]float64, error) // List suppported models ListModels(apiConfig *APIConfig) ([]string, error) + + Balance(apiConfig *APIConfig) (map[string]interface{}, error) } type ChatResponse struct { diff --git a/internal/entity/models/zhipu-ai.go b/internal/entity/models/zhipu-ai.go index 502593ea9b..b7c6deb8cd 100644 --- a/internal/entity/models/zhipu-ai.go +++ b/internal/entity/models/zhipu-ai.go @@ -52,6 +52,10 @@ func NewZhipuAIModel(baseURL map[string]string, urlSuffix URLSuffix) *ZhipuAIMod } } +func (z *ZhipuAIModel) Name() string { + return "zhipu" +} + // Chat sends a message and returns response func (z *ZhipuAIModel) Chat(modelName, message *string, apiConfig *APIConfig, chatModelConfig *ChatConfig) (*ChatResponse, error) { if message == nil { @@ -281,7 +285,7 @@ func (z *ZhipuAIModel) ChatStreamlyWithSender(modelName, message *string, apiCon // Parse the JSON event var event map[string]interface{} - if err := json.Unmarshal([]byte(data), &event); err != nil { + if err = json.Unmarshal([]byte(data), &event); err != nil { continue } @@ -322,7 +326,7 @@ func (z *ZhipuAIModel) ChatStreamlyWithSender(modelName, message *string, apiCon // Send [DONE] marker for OpenAI compatibility endOfStream := "[DONE]" - if err := sender(&endOfStream, nil); err != nil { + if err = sender(&endOfStream, nil); err != nil { return err } @@ -377,7 +381,7 @@ func (z *ZhipuAIModel) EncodeToEmbedding(modelName *string, texts []string, apiC // Parse response var result map[string]interface{} - if err := json.Unmarshal(body, &result); err != nil { + if err = json.Unmarshal(body, &result); err != nil { return nil, fmt.Errorf("failed to parse response: %w", err) } @@ -415,5 +419,9 @@ func (z *ZhipuAIModel) EncodeToEmbedding(modelName *string, texts []string, apiC } func (z *ZhipuAIModel) ListModels(apiConfig *APIConfig) ([]string, error) { - return nil, fmt.Errorf("no such method") + return nil, fmt.Errorf("%s, no such method", z.Name()) +} + +func (z *ZhipuAIModel) Balance(apiConfig *APIConfig) (map[string]interface{}, error) { + return nil, fmt.Errorf("%s, no such method", z.Name()) } diff --git a/internal/handler/providers.go b/internal/handler/providers.go index 71ff9c1846..8a493680e4 100644 --- a/internal/handler/providers.go +++ b/internal/handler/providers.go @@ -355,6 +355,44 @@ func (h *ProviderHandler) ShowProviderInstance(c *gin.Context) { }) } +func (h *ProviderHandler) ShowInstanceBalance(c *gin.Context) { + providerName := c.Param("provider_name") + if providerName == "" { + c.JSON(http.StatusBadRequest, gin.H{ + "code": 400, + "message": "Provider name is required", + }) + return + } + + instanceName := c.Param("instance_name") + if instanceName == "" { + c.JSON(http.StatusBadRequest, gin.H{ + "code": 400, + "message": "Instance name is required", + }) + return + } + + userID := c.GetString("user_id") + + // Get tenant ID from user + balance, errorCode, err := h.modelProviderService.ShowInstanceBalance(providerName, instanceName, userID) + if err != nil { + c.JSON(http.StatusOK, gin.H{ + "code": errorCode, + "message": err.Error(), + }) + return + } + + c.JSON(http.StatusOK, gin.H{ + "code": 0, + "message": "success", + "data": balance, + }) +} + type AlterProviderInstanceRequest struct { LLMName string `json:"llm_name" binding:"required"` } diff --git a/internal/router/router.go b/internal/router/router.go index bc979b8b70..b2543d1b0a 100644 --- a/internal/router/router.go +++ b/internal/router/router.go @@ -212,6 +212,7 @@ func (r *Router) Setup(engine *gin.Engine) { provider.POST("/:provider_name/instances", r.providerHandler.CreateProviderInstance) provider.GET("/:provider_name/instances", r.providerHandler.ListProviderInstances) provider.GET("/:provider_name/instances/:instance_name", r.providerHandler.ShowProviderInstance) + provider.GET("/:provider_name/instances/:instance_name/balance", r.providerHandler.ShowInstanceBalance) provider.PUT("/:provider_name/instances/:instance_name", r.providerHandler.AlterProviderInstance) provider.DELETE("/:provider_name/instances", r.providerHandler.DropProviderInstance) provider.GET("/:provider_name/instances/:instance_name/models", r.providerHandler.ListInstanceModels) diff --git a/internal/service/model_service.go b/internal/service/model_service.go index a7aa82d6b8..bb98a9e744 100644 --- a/internal/service/model_service.go +++ b/internal/service/model_service.go @@ -423,6 +423,76 @@ func (m *ModelProviderService) ShowProviderInstance(providerName, instanceName, return result, common.CodeSuccess, nil } +func (m *ModelProviderService) ShowInstanceBalance(providerName, instanceName, userID string) (map[string]interface{}, common.ErrorCode, error) { + + // Get tenant ID from user + tenants, err := m.userTenantDAO.GetByUserIDAndRole(userID, "owner") + if err != nil { + return nil, common.CodeServerError, err + } + + if len(tenants) == 0 { + return nil, common.CodeNotFound, errors.New("user has no tenants") + } + + tenantID := tenants[0].TenantID + + // Check if provider exists + provider, err := m.modelProviderDAO.GetByTenantIDAndProviderName(tenantID, providerName) + if err != nil { + return nil, common.CodeServerError, err + } + + instance, err := m.modelInstanceDAO.GetByProviderIDAndInstanceName(provider.ID, instanceName) + if err != nil { + return nil, common.CodeServerError, err + } + + providerInfo := dao.GetModelProviderManager().FindProvider(providerName) + if providerInfo == nil { + return nil, common.CodeServerError, fmt.Errorf("provider %s not found", providerName) + } + + var extra map[string]string + err = json.Unmarshal([]byte(instance.Extra), &extra) + if err != nil { + return nil, common.CodeServerError, err + } + + apiConfig := &modelModule.APIConfig{ + ApiKey: nil, + Region: nil, + } + + region := extra["region"] + apiConfig.Region = ®ion + apiConfig.ApiKey = &instance.APIKey + + var result map[string]interface{} + result, err = providerInfo.ModelDriver.Balance(apiConfig) + if err != nil { + return nil, common.CodeServerError, err + } + return result, common.CodeSuccess, nil + + // convert instance.Extra (json string) to map + //var extra map[string]string + //err = json.Unmarshal([]byte(instance.Extra), &extra) + //if err != nil { + // return nil, common.CodeServerError, err + //} + // + //result := map[string]interface{}{ + // "id": instance.ID, + // "instanceName": instance.InstanceName, + // "providerID": instance.ProviderID, + // "status": instance.Status, + // "region": extra["region"], + //} + // + //return result, common.CodeSuccess, nil +} + func (m *ModelProviderService) AlterProviderInstance(providerName, instanceName, newInstanceName, apiKey, userID string) (common.ErrorCode, error) { return common.CodeSuccess, nil } diff --git a/uv.lock b/uv.lock index 165fd74ea6..1392286236 100644 --- a/uv.lock +++ b/uv.lock @@ -1745,6 +1745,27 @@ version = "0.8.3" source = { registry = "https://mirrors.aliyun.com/pypi/simple" } sdist = { url = "https://mirrors.aliyun.com/pypi/packages/51/0b/c0f53a14317b304e2e93b29a831b0c83306caae9af7f0e2e037d17c4f63f/datrie-0.8.3.tar.gz", hash = "sha256:ea021ad4c8a8bf14e08a71c7872a622aa399a510f981296825091c7ca0436e80" } +[[package]] +name = "debugpy" +version = "1.8.20" +source = { registry = "https://mirrors.aliyun.com/pypi/simple" } +sdist = { url = "https://mirrors.aliyun.com/pypi/packages/e0/b7/cd8080344452e4874aae67c40d8940e2b4d47b01601a8fd9f44786c757c7/debugpy-1.8.20.tar.gz", hash = "sha256:55bc8701714969f1ab89a6d5f2f3d40c36f91b2cbe2f65d98bf8196f6a6a2c33" } +wheels = [ + { url = "https://mirrors.aliyun.com/pypi/packages/14/57/7f34f4736bfb6e00f2e4c96351b07805d83c9a7b33d28580ae01374430f7/debugpy-1.8.20-cp312-cp312-macosx_15_0_universal2.whl", hash = "sha256:4ae3135e2089905a916909ef31922b2d733d756f66d87345b3e5e52b7a55f13d" }, + { url = "https://mirrors.aliyun.com/pypi/packages/ab/78/b193a3975ca34458f6f0e24aaf5c3e3da72f5401f6054c0dfd004b41726f/debugpy-1.8.20-cp312-cp312-manylinux_2_34_x86_64.whl", hash = "sha256:88f47850a4284b88bd2bfee1f26132147d5d504e4e86c22485dfa44b97e19b4b" }, + { url = "https://mirrors.aliyun.com/pypi/packages/c1/55/f14deb95eaf4f30f07ef4b90a8590fc05d9e04df85ee379712f6fb6736d7/debugpy-1.8.20-cp312-cp312-win32.whl", hash = "sha256:4057ac68f892064e5f98209ab582abfee3b543fb55d2e87610ddc133a954d390" }, + { url = "https://mirrors.aliyun.com/pypi/packages/a1/39/2bef246368bd42f9bd7cba99844542b74b84dacbdbea0833e610f384fee8/debugpy-1.8.20-cp312-cp312-win_amd64.whl", hash = "sha256:a1a8f851e7cf171330679ef6997e9c579ef6dd33c9098458bd9986a0f4ca52e3" }, + { url = "https://mirrors.aliyun.com/pypi/packages/15/e2/fc500524cc6f104a9d049abc85a0a8b3f0d14c0a39b9c140511c61e5b40b/debugpy-1.8.20-cp313-cp313-macosx_15_0_universal2.whl", hash = "sha256:5dff4bb27027821fdfcc9e8f87309a28988231165147c31730128b1c983e282a" }, + { url = "https://mirrors.aliyun.com/pypi/packages/90/83/fb33dcea789ed6018f8da20c5a9bc9d82adc65c0c990faed43f7c955da46/debugpy-1.8.20-cp313-cp313-manylinux_2_34_x86_64.whl", hash = "sha256:84562982dd7cf5ebebfdea667ca20a064e096099997b175fe204e86817f64eaf" }, + { url = "https://mirrors.aliyun.com/pypi/packages/a6/25/b1e4a01bfb824d79a6af24b99ef291e24189080c93576dfd9b1a2815cd0f/debugpy-1.8.20-cp313-cp313-win32.whl", hash = "sha256:da11dea6447b2cadbf8ce2bec59ecea87cc18d2c574980f643f2d2dfe4862393" }, + { url = "https://mirrors.aliyun.com/pypi/packages/13/f7/a0b368ce54ffff9e9028c098bd2d28cfc5b54f9f6c186929083d4c60ba58/debugpy-1.8.20-cp313-cp313-win_amd64.whl", hash = "sha256:eb506e45943cab2efb7c6eafdd65b842f3ae779f020c82221f55aca9de135ed7" }, + { url = "https://mirrors.aliyun.com/pypi/packages/33/2e/f6cb9a8a13f5058f0a20fe09711a7b726232cd5a78c6a7c05b2ec726cff9/debugpy-1.8.20-cp314-cp314-macosx_15_0_universal2.whl", hash = "sha256:9c74df62fc064cd5e5eaca1353a3ef5a5d50da5eb8058fcef63106f7bebe6173" }, + { url = "https://mirrors.aliyun.com/pypi/packages/c5/56/6ddca50b53624e1ca3ce1d1e49ff22db46c47ea5fb4c0cc5c9b90a616364/debugpy-1.8.20-cp314-cp314-manylinux_2_34_x86_64.whl", hash = "sha256:077a7447589ee9bc1ff0cdf443566d0ecf540ac8aa7333b775ebcb8ce9f4ecad" }, + { url = "https://mirrors.aliyun.com/pypi/packages/c5/d9/d64199c14a0d4c476df46c82470a3ce45c8d183a6796cfb5e66533b3663c/debugpy-1.8.20-cp314-cp314-win32.whl", hash = "sha256:352036a99dd35053b37b7803f748efc456076f929c6a895556932eaf2d23b07f" }, + { url = "https://mirrors.aliyun.com/pypi/packages/e0/d9/1f07395b54413432624d61524dfd98c1a7c7827d2abfdb8829ac92638205/debugpy-1.8.20-cp314-cp314-win_amd64.whl", hash = "sha256:a98eec61135465b062846112e5ecf2eebb855305acc1dfbae43b72903b8ab5be" }, + { url = "https://mirrors.aliyun.com/pypi/packages/e0/c3/7f67dea8ccf8fdcb9c99033bbe3e90b9e7395415843accb81428c441be2d/debugpy-1.8.20-py2.py3-none-any.whl", hash = "sha256:5be9bed9ae3be00665a06acaa48f8329d2b9632f15fd09f6a9a8c8d9907e54d7" }, +] + [[package]] name = "decorator" version = "5.2.1" @@ -6549,6 +6570,7 @@ dependencies = [ { name = "cohere" }, { name = "crawl4ai" }, { name = "dashscope" }, + { name = "debugpy" }, { name = "deepl" }, { name = "demjson3" }, { name = "discord-py" }, @@ -6692,6 +6714,7 @@ requires-dist = [ { name = "cohere", specifier = "==5.6.2" }, { name = "crawl4ai", specifier = ">=0.4.0,<1.0.0" }, { name = "dashscope", specifier = "==1.25.11" }, + { name = "debugpy", specifier = ">=1.8.13" }, { name = "deepl", specifier = "==1.18.0" }, { name = "demjson3", specifier = "==3.0.6" }, { name = "discord-py", specifier = "==2.3.2" },