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 <haijin.chn@gmail.com>
This commit is contained in:
Jin Hai
2026-04-21 21:31:50 +08:00
committed by GitHub
parent 2d05475693
commit 74b44e1aa3
15 changed files with 329 additions and 18 deletions

View File

@@ -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":

View File

@@ -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":

View File

@@ -109,6 +109,7 @@ const (
TokenVector
TokenSize
TokenName // For ALTER PROVIDER <name> NAME <new_name>
TokenBalance
TokenInstance
TokenInstances
TokenDisable

View File

@@ -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 <provider_name> <instance_name>
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> NAME <new_name> FROM PROVIDER <name>
func (c *RAGFlowClient) AlterProviderInstance(cmd *Command) (ResponseIf, error) {

View File

@@ -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 <provider_name> <instance_name>
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> NAME <new_name> FROM PROVIDER <name> command
func (p *Parser) parseAlterInstance() (*Command, error) {
p.nextToken() // consume INSTANCE