Refactor model provider and command (#13887)

### What problem does this PR solve?

Introduce 5 new tables, including model groups and provider instance.

### Type of change

- [x] New Feature (non-breaking change which adds functionality)
- [x] Refactoring

---------

Signed-off-by: Jin Hai <haijin.chn@gmail.com>
This commit is contained in:
Jin Hai
2026-04-02 20:20:35 +08:00
committed by GitHub
parent f02f5fa435
commit 6c29128de1
33 changed files with 2986 additions and 100 deletions

View File

@@ -275,30 +275,6 @@ func (p *Parser) parseAdminListDefaultModels() (*Command, error) {
return NewCommand("list_user_default_models"), nil
}
func (p *Parser) parseListModelsOfProvider() (*Command, error) {
if p.curToken.Type != TokenModels {
return nil, fmt.Errorf("expected MODELS")
}
p.nextToken()
if p.curToken.Type != TokenFrom {
return nil, fmt.Errorf("expected FROM")
}
p.nextToken()
providerName, err := p.parseQuotedString()
if err != nil {
return nil, err
}
cmd := NewCommand("list_provider_models")
cmd.Params["provider_name"] = providerName
p.nextToken()
// Semicolon is optional for UNSET TOKEN
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
func (p *Parser) parseCommonListProviders() (*Command, error) {
p.nextToken() // consume AVAILABLE

View File

@@ -332,7 +332,7 @@ func looksLikeSQL(s string) bool {
"LIST ", "SHOW ", "CREATE ", "DROP ", "ALTER ",
"LOGIN ", "REGISTER ", "PING", "GRANT ", "REVOKE ",
"SET ", "UNSET ", "UPDATE ", "DELETE ", "INSERT ",
"SELECT ", "DESCRIBE ", "EXPLAIN ",
"SELECT ", "DESCRIBE ", "EXPLAIN ", "ADD ", "ENABLE ", "DISABLE ", "CHAT ", "USE",
}
for _, prefix := range sqlPrefixes {
if strings.HasPrefix(s, prefix) {
@@ -1008,15 +1008,19 @@ Commands (User Mode):
LIST TOKENS; - List API tokens
LIST PROVIDERS; - List available LLM providers
CREATE TOKEN; - Create new API token
CREATE PROVIDER 'name'; - Create a provider without API key
CREATE PROVIDER 'name' 'api_key'; - Create a provider with API key
ADD PROVIDER 'name'; - Create a provider without API key
ADD PROVIDER 'name' 'api_key'; - Create a provider with API key
DROP TOKEN 'token_value'; - Delete an API token
DROP PROVIDER 'name'; - Delete a provider
DELETE PROVIDER 'name'; - Delete a provider
SET TOKEN 'token_value'; - Set and validate API token
SHOW TOKEN; - Show current API token
SHOW PROVIDER 'name'; - Show provider details
SHOW CURRENT MODEL; - Show current model settings
UNSET TOKEN; - Remove current API token
ALTER PROVIDER 'name' NAME 'new_name'; - Rename a provider
USE MODEL 'provider/instance/model'; - Set current model for chat
CHAT 'message'; - Chat using current model
CHAT 'provider/instance/model' 'message'; - Chat with specified model
Context Engine Commands (no quotes):
ls [path] - List resources

View File

@@ -24,6 +24,13 @@ import (
// PasswordPromptFunc is a function type for password input
type PasswordPromptFunc func(prompt string) (string, error)
// CurrentModel holds the current model configuration
type CurrentModel struct {
Provider string
Instance string
Model string
}
// RAGFlowClient handles API interactions with the RAGFlow server
type RAGFlowClient struct {
HTTPClient *HTTPClient
@@ -31,6 +38,7 @@ type RAGFlowClient struct {
PasswordPrompt PasswordPromptFunc // Function for password input
OutputFormat OutputFormat // Output format: table, plain, json
ContextEngine *ce.Engine // Context Engine for virtual filesystem
CurrentModel *CurrentModel // Current model configuration
}
// NewRAGFlowClient creates a new RAGFlow client
@@ -158,6 +166,8 @@ func (c *RAGFlowClient) ExecuteAdminCommand(cmd *Command) (ResponseIf, error) {
return c.ShowProvider(cmd)
case "list_provider_models":
return c.ListModels(cmd)
case "list_instance_models":
return c.ListInstanceModels(cmd)
case "show_model":
return c.ShowModel(cmd)
// TODO: Implement other commands
@@ -203,21 +213,44 @@ func (c *RAGFlowClient) ExecuteUserCommand(cmd *Command) (ResponseIf, error) {
return c.CreateDocMetaIndex(cmd)
case "drop_doc_meta_index":
return c.DropDocMetaIndex(cmd)
case "list_pool_providers":
case "list_available_providers":
return c.ListAvailableProviders(cmd)
case "show_provider":
return c.ShowProvider(cmd)
case "list_provider_models":
return c.ListModels(cmd)
case "list_instance_models":
return c.ListInstanceModels(cmd)
case "show_model":
return c.ShowModel(cmd)
// Provider commands
case "create_provider":
return c.CreateProvider(cmd)
case "add_provider":
return c.AddProvider(cmd)
case "list_providers":
return c.ListProviders(cmd)
case "drop_provider":
return c.DropProvider(cmd)
case "delete_provider":
return c.DeleteProvider(cmd)
// Provider instance commands
case "create_provider_instance":
return c.CreateProviderInstance(cmd)
case "list_provider_instances":
return c.ListProviderInstances(cmd)
case "show_provider_instance":
return c.ShowProviderInstance(cmd)
case "alter_provider_instance":
return c.AlterProviderInstance(cmd)
case "drop_provider_instance":
return c.DropProviderInstance(cmd)
case "enable_model":
return c.EnableOrDisableModel(cmd, "enable")
case "disable_model":
return c.EnableOrDisableModel(cmd, "disable")
case "chat_to_model":
return c.ChatToModel(cmd)
case "use_model":
return c.UseModel(cmd)
case "show_current_model":
return c.ShowCurrentModel(cmd)
// ContextEngine commands
case "ce_ls":
return c.CEList(cmd)

View File

@@ -181,6 +181,10 @@ func (l *Lexer) lookupIdent(ident string) Token {
return Token{Type: TokenActive, Value: ident}
case "ADMIN":
return Token{Type: TokenAdmin, Value: ident}
case "ADD":
return Token{Type: TokenAdd, Value: ident}
case "DELETE":
return Token{Type: TokenDelete, Value: ident}
case "PASSWORD":
return Token{Type: TokenPassword, Value: ident}
case "DATASET":
@@ -305,14 +309,22 @@ func (l *Lexer) lookupIdent(ident string) Token {
return Token{Type: TokenAvailable, Value: ident}
case "NAME":
return Token{Type: TokenName, Value: ident}
case "POOL":
return Token{Type: TokenPool, Value: ident}
case "INSTANCE":
return Token{Type: TokenInstance, Value: ident}
case "INSTANCES":
return Token{Type: TokenInstances, Value: ident}
case "DISABLE":
return Token{Type: TokenDisable, Value: ident}
case "ENABLE":
return Token{Type: TokenEnable, Value: ident}
case "INSERT":
return Token{Type: TokenInsert, Value: ident}
case "FILE":
return Token{Type: TokenFile, Value: ident}
case "METADATA":
return Token{Type: TokenMetadata, Value: ident}
case "USE":
return Token{Type: TokenUse, Value: ident}
default:
return Token{Type: TokenIdentifier, Value: ident}
}

View File

@@ -150,6 +150,10 @@ func (p *Parser) parseUserCommand() (*Command, error) {
return p.parseCreateCommand()
case TokenDrop:
return p.parseDropCommand()
case TokenAdd:
return p.parseAddCommand()
case TokenDelete:
return p.parseDeleteCommand()
case TokenAlter:
return p.parseAlterCommand()
case TokenGrant:
@@ -182,6 +186,14 @@ func (p *Parser) parseUserCommand() (*Command, error) {
return p.parseShutdownCommand()
case TokenRestart:
return p.parseRestartCommand()
case TokenEnable:
return p.parseEnableCommand()
case TokenDisable:
return p.parseDisableCommand()
case TokenChat:
return p.parseChatCommand()
case TokenUse:
return p.parseUseCommand()
default:
return nil, fmt.Errorf("unknown command: %s", p.curToken.Value)
}

View File

@@ -113,6 +113,34 @@ func (r *SimpleResponse) PrintOut() {
}
}
type MessageResponse struct {
Code int `json:"code"`
Message string `json:"message"`
Duration float64
outputFormat OutputFormat
}
func (r *MessageResponse) Type() string {
return "message"
}
func (r *MessageResponse) TimeCost() float64 {
return r.Duration
}
func (r *MessageResponse) SetOutputFormat(format OutputFormat) {
r.outputFormat = format
}
func (r *MessageResponse) PrintOut() {
if r.Code == 0 {
fmt.Println(r.Message)
} else {
fmt.Println("ERROR")
fmt.Printf("%d, %s\n", r.Code, r.Message)
}
}
type RegisterResponse struct {
Code int `json:"code"`
Message string `json:"message"`

View File

@@ -42,6 +42,8 @@ const (
TokenAlter
TokenActive
TokenAdmin
TokenAdd
TokenDelete
TokenPassword
TokenDataset
TokenDatasets
@@ -104,6 +106,11 @@ const (
TokenVectorSize
TokenDocMeta
TokenName // For ALTER PROVIDER <name> NAME <new_name>
TokenInstance
TokenInstances
TokenDisable
TokenEnable
TokenUse
TokenInsert
TokenFile
TokenMetadata

View File

@@ -751,10 +751,10 @@ func (c *RAGFlowClient) DropDocMetaIndex(cmd *Command) (ResponseIf, error) {
return &result, nil
}
// CreateProvider creates a new model provider
// CREATE PROVIDER <name>
// CREATE PROVIDER <name> <api_key>
func (c *RAGFlowClient) CreateProvider(cmd *Command) (ResponseIf, error) {
// AddProvider creates a new model provider
// ADD PROVIDER <name>
// ADD PROVIDER <name> <api_key>
func (c *RAGFlowClient) AddProvider(cmd *Command) (ResponseIf, error) {
if c.ServerType != "user" {
return nil, fmt.Errorf("this command is only allowed in USER mode")
}
@@ -764,28 +764,23 @@ func (c *RAGFlowClient) CreateProvider(cmd *Command) (ResponseIf, error) {
return nil, fmt.Errorf("provider name not provided")
}
// Get optional api_key
apiKey, _ := cmd.Params["api_key"].(string)
// Build payload
payload := map[string]interface{}{
"llm_factory": providerName,
"api_key": apiKey,
"verify": apiKey != "", // Only verify if api_key is provided
"provider_name": providerName,
}
resp, err := c.HTTPClient.Request("POST", "/llm/set_api_key", true, "web", nil, payload)
resp, err := c.HTTPClient.Request("POST", "/providers", true, "web", nil, payload)
if err != nil {
return nil, fmt.Errorf("failed to create provider: %w", err)
return nil, fmt.Errorf("failed to add provider: %w", err)
}
if resp.StatusCode != 200 {
return nil, fmt.Errorf("failed to create provider: HTTP %d, body: %s", resp.StatusCode, string(resp.Body))
return nil, fmt.Errorf("failed to add provider: HTTP %d, body: %s", resp.StatusCode, string(resp.Body))
}
var result CommonDataResponse
var result SimpleResponse
if err = json.Unmarshal(resp.Body, &result); err != nil {
return nil, fmt.Errorf("create provider failed: invalid JSON (%w)", err)
return nil, fmt.Errorf("add provider failed: invalid JSON (%w)", err)
}
if result.Code != 0 {
@@ -803,7 +798,7 @@ func (c *RAGFlowClient) ListProviders(cmd *Command) (ResponseIf, error) {
return nil, fmt.Errorf("this command is only allowed in USER mode")
}
resp, err := c.HTTPClient.Request("GET", "/llm/factories", true, "web", nil, nil)
resp, err := c.HTTPClient.Request("GET", "/providers", true, "web", nil, nil)
if err != nil {
return nil, fmt.Errorf("failed to list providers: %w", err)
}
@@ -825,9 +820,9 @@ func (c *RAGFlowClient) ListProviders(cmd *Command) (ResponseIf, error) {
return &result, nil
}
// DropProvider deletes a provider
// DROP PROVIDER <name>
func (c *RAGFlowClient) DropProvider(cmd *Command) (ResponseIf, error) {
// DeleteProvider deletes a provider
// DELETE PROVIDER <name>
func (c *RAGFlowClient) DeleteProvider(cmd *Command) (ResponseIf, error) {
if c.ServerType != "user" {
return nil, fmt.Errorf("this command is only allowed in USER mode")
}
@@ -837,23 +832,25 @@ func (c *RAGFlowClient) DropProvider(cmd *Command) (ResponseIf, error) {
return nil, fmt.Errorf("provider name not provided")
}
url := fmt.Sprintf("/providers/%s", providerName)
// Build payload
payload := map[string]interface{}{
"llm_factory": providerName,
}
resp, err := c.HTTPClient.Request("DELETE", "/llm/factory", true, "web", nil, payload)
resp, err := c.HTTPClient.Request("DELETE", url, true, "web", nil, payload)
if err != nil {
return nil, fmt.Errorf("failed to drop provider: %w", err)
return nil, fmt.Errorf("failed to delete provider: %w", err)
}
if resp.StatusCode != 200 {
return nil, fmt.Errorf("failed to drop provider: HTTP %d, body: %s", resp.StatusCode, string(resp.Body))
return nil, fmt.Errorf("failed to delete provider: HTTP %d, body: %s", resp.StatusCode, string(resp.Body))
}
var result SimpleResponse
if err = json.Unmarshal(resp.Body, &result); err != nil {
return nil, fmt.Errorf("drop provider failed: invalid JSON (%w)", err)
return nil, fmt.Errorf("delete provider failed: invalid JSON (%w)", err)
}
if result.Code != 0 {
@@ -864,6 +861,410 @@ func (c *RAGFlowClient) DropProvider(cmd *Command) (ResponseIf, error) {
return &result, nil
}
// CreateProviderInstance creates a new provider instance
// CREATE PROVIDER <name> INSTANCE <instance_name> <api_key>
func (c *RAGFlowClient) CreateProviderInstance(cmd *Command) (ResponseIf, error) {
if c.ServerType != "user" {
return nil, fmt.Errorf("this command is only allowed in USER mode")
}
providerName, ok := cmd.Params["provider_name"].(string)
if !ok {
return nil, fmt.Errorf("provider name not provided")
}
instanceName, ok := cmd.Params["instance_name"].(string)
if !ok {
return nil, fmt.Errorf("instance name not provided")
}
apiKey, ok := cmd.Params["api_key"].(string)
if !ok {
return nil, fmt.Errorf("API key not provided")
}
url := fmt.Sprintf("/providers/%s/instances", providerName)
payload := map[string]interface{}{
"instance_name": instanceName,
"api_key": apiKey,
}
resp, err := c.HTTPClient.Request("POST", url, true, "web", nil, payload)
if err != nil {
return nil, fmt.Errorf("failed to create provider instance: %w", err)
}
if resp.StatusCode != 200 {
return nil, fmt.Errorf("failed to create provider instance: HTTP %d, body: %s", resp.StatusCode, string(resp.Body))
}
var result SimpleResponse
if err = json.Unmarshal(resp.Body, &result); err != nil {
return nil, fmt.Errorf("create provider instance failed: invalid JSON (%w)", err)
}
if result.Code != 0 {
return nil, fmt.Errorf("%s", result.Message)
}
result.Duration = resp.Duration
return &result, nil
}
// ListProviderInstances lists all instances of a provider
// LIST INSTANCES FROM PROVIDER <name>
func (c *RAGFlowClient) ListProviderInstances(cmd *Command) (ResponseIf, error) {
if c.ServerType != "user" {
return nil, fmt.Errorf("this command is only allowed in USER mode")
}
providerName, ok := cmd.Params["provider_name"].(string)
if !ok {
return nil, fmt.Errorf("provider name not provided")
}
url := fmt.Sprintf("/providers/%s/instances", providerName)
resp, err := c.HTTPClient.Request("GET", url, true, "web", nil, nil)
if err != nil {
return nil, fmt.Errorf("failed to list instances: %w", err)
}
if resp.StatusCode != 200 {
return nil, fmt.Errorf("failed to list instances: HTTP %d, body: %s", resp.StatusCode, string(resp.Body))
}
var result CommonResponse
if err = json.Unmarshal(resp.Body, &result); err != nil {
return nil, fmt.Errorf("list instances failed: invalid JSON (%w)", err)
}
if result.Code != 0 {
return nil, fmt.Errorf("%s", result.Message)
}
result.Duration = resp.Duration
return &result, nil
}
// ShowProviderInstance shows details of a specific instance
// SHOW INSTANCE <name> FROM PROVIDER <name>
func (c *RAGFlowClient) ShowProviderInstance(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", 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) {
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")
}
newName, ok := cmd.Params["new_name"].(string)
if !ok {
return nil, fmt.Errorf("new 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", providerName, instanceName)
payload := map[string]interface{}{
"llm_name": newName,
}
resp, err := c.HTTPClient.Request("PUT", url, true, "web", nil, payload)
if err != nil {
return nil, fmt.Errorf("failed to alter instance: %w", err)
}
if resp.StatusCode != 200 {
return nil, fmt.Errorf("failed to alter instance: HTTP %d, body: %s", resp.StatusCode, string(resp.Body))
}
var result SimpleResponse
if err = json.Unmarshal(resp.Body, &result); err != nil {
return nil, fmt.Errorf("alter instance failed: invalid JSON (%w)", err)
}
if result.Code != 0 {
return nil, fmt.Errorf("%s", result.Message)
}
result.Duration = resp.Duration
return &result, nil
}
// DropProviderInstance deletes a provider instance
// DROP INSTANCE <name> FROM PROVIDER <name>
func (c *RAGFlowClient) DropProviderInstance(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", providerName, instanceName)
resp, err := c.HTTPClient.Request("DELETE", url, true, "web", nil, nil)
if err != nil {
return nil, fmt.Errorf("failed to drop instance: %w", err)
}
if resp.StatusCode != 200 {
return nil, fmt.Errorf("failed to drop instance: HTTP %d, body: %s", resp.StatusCode, string(resp.Body))
}
var result SimpleResponse
if err = json.Unmarshal(resp.Body, &result); err != nil {
return nil, fmt.Errorf("drop instance failed: invalid JSON (%w)", err)
}
if result.Code != 0 {
return nil, fmt.Errorf("%s", result.Message)
}
result.Duration = resp.Duration
return &result, nil
}
func (c *RAGFlowClient) ListInstanceModels(cmd *Command) (ResponseIf, error) {
if c.ServerType != "user" {
return nil, fmt.Errorf("this command is only allowed in USER mode")
}
providerName, ok := cmd.Params["provider_name"].(string)
if !ok {
return nil, fmt.Errorf("provider_name not provided")
}
instanceName, ok := cmd.Params["instance_name"].(string)
if !ok {
return nil, fmt.Errorf("instance_name not provided")
}
var endPoint string
endPoint = fmt.Sprintf("/providers/%s/instances/%s/models", providerName, instanceName)
resp, err := c.HTTPClient.Request("GET", endPoint, true, "web", nil, nil)
if err != nil {
return nil, fmt.Errorf("failed to list instance models: %w", err)
}
if resp.StatusCode != 200 {
return nil, fmt.Errorf("failed to list instance models: HTTP %d, body: %s", resp.StatusCode, string(resp.Body))
}
var result CommonResponse
if err = json.Unmarshal(resp.Body, &result); err != nil {
return nil, fmt.Errorf("failed to list instance models: invalid JSON (%w)", err)
}
if result.Code != 0 {
return nil, fmt.Errorf("%s", result.Message)
}
result.Duration = resp.Duration
return &result, nil
}
func (c *RAGFlowClient) EnableOrDisableModel(cmd *Command, status string) (ResponseIf, error) {
if c.ServerType != "user" {
return nil, fmt.Errorf("this command is only allowed in USER mode")
}
modelName, ok := cmd.Params["model_name"].(string)
if !ok {
return nil, fmt.Errorf("model name not provided")
}
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/models/%s", providerName, instanceName, modelName)
payload := map[string]interface{}{
"status": status,
}
resp, err := c.HTTPClient.Request("PUT", url, true, "web", nil, payload)
if err != nil {
return nil, fmt.Errorf("failed to enable/disable model: %w", err)
}
if resp.StatusCode != 200 {
return nil, fmt.Errorf("failed to enable/disable model: HTTP %d, body: %s", resp.StatusCode, string(resp.Body))
}
var result SimpleResponse
if err = json.Unmarshal(resp.Body, &result); err != nil {
return nil, fmt.Errorf("enable/disable model failed: invalid JSON (%w)", err)
}
if result.Code != 0 {
return nil, fmt.Errorf("%s", result.Message)
}
result.Duration = resp.Duration
return &result, nil
}
func (c *RAGFlowClient) ChatToModel(cmd *Command) (ResponseIf, error) {
if c.ServerType != "user" {
return nil, fmt.Errorf("this command is only allowed in USER mode")
}
var providerName, instanceName, modelName string
// Check if model_name is provided in command
if compositeModelName, ok := cmd.Params["model_name"].(string); ok && compositeModelName != "" {
names := strings.Split(compositeModelName, "/")
if len(names) != 3 {
return nil, fmt.Errorf("model name must be in format 'provider/instance/model'")
}
providerName = names[0]
instanceName = names[1]
modelName = names[2]
} else if c.CurrentModel != nil {
// Use current model if set
providerName = c.CurrentModel.Provider
instanceName = c.CurrentModel.Instance
modelName = c.CurrentModel.Model
} else {
return nil, fmt.Errorf("model name not provided and no current model set. Use 'use model' command first")
}
message := cmd.Params["message"].(string)
url := fmt.Sprintf("/providers/%s/instances/%s/models/%s", providerName, instanceName, modelName)
payload := map[string]interface{}{
"message": message,
}
resp, err := c.HTTPClient.Request("POST", url, true, "web", nil, payload)
if err != nil {
return nil, fmt.Errorf("failed to chat model: %w", err)
}
if resp.StatusCode != 200 {
return nil, fmt.Errorf("failed to chat model: HTTP %d, body: %s", resp.StatusCode, string(resp.Body))
}
var result MessageResponse
if err = json.Unmarshal(resp.Body, &result); err != nil {
return nil, fmt.Errorf("chat model failed: invalid JSON (%w)", err)
}
if result.Code != 0 {
return nil, fmt.Errorf("%s", result.Message)
}
result.Duration = resp.Duration
return &result, nil
}
// UseModel sets the current model for chat
func (c *RAGFlowClient) UseModel(cmd *Command) (ResponseIf, error) {
if c.HTTPClient.APIToken == "" && c.HTTPClient.LoginToken == "" {
return nil, fmt.Errorf("API token not set. Please login first")
}
if c.ServerType != "user" {
return nil, fmt.Errorf("this command is only allowed in USER mode")
}
modelIdentifier, ok := cmd.Params["model_identifier"].(string)
if !ok || modelIdentifier == "" {
return nil, fmt.Errorf("model identifier not provided")
}
names := strings.Split(modelIdentifier, "/")
if len(names) != 3 {
return nil, fmt.Errorf("model identifier must be in format 'provider/instance/model'")
}
c.CurrentModel = &CurrentModel{
Provider: names[0],
Instance: names[1],
Model: names[2],
}
var result SimpleResponse
result.Code = 0
result.Message = fmt.Sprintf("Current model set to: %s/%s/%s", c.CurrentModel.Provider, c.CurrentModel.Instance, c.CurrentModel.Model)
return &result, nil
}
// ShowCurrentModel displays the current model configuration
func (c *RAGFlowClient) ShowCurrentModel(cmd *Command) (ResponseIf, error) {
if c.ServerType != "user" {
return nil, fmt.Errorf("this command is only allowed in USER mode")
}
if c.CurrentModel == nil {
return nil, fmt.Errorf("no current model set. Use 'use model' command first")
}
var result CommonResponse
result.Code = 0
result.Data = []map[string]interface{}{
{
"provider": c.CurrentModel.Provider,
"instance": c.CurrentModel.Instance,
"model": c.CurrentModel.Model,
},
}
return &result, nil
}
// Context related commands
// CEList handles the ls command - lists nodes using Context Engine
@@ -947,7 +1348,6 @@ func (c *RAGFlowClient) InsertDatasetFromFile(cmd *Command) (ResponseIf, error)
if c.ServerType != "user" {
return nil, fmt.Errorf("this command is only allowed in USER mode")
}
filePath, ok := cmd.Params["file_path"].(string)
if !ok {
return nil, fmt.Errorf("file_path not provided")

View File

@@ -3,6 +3,7 @@ package cli
import (
"fmt"
"strconv"
"strings"
)
// Command parsers
@@ -170,6 +171,8 @@ func (p *Parser) parseListCommand() (*Command, error) {
return p.parseListModelsOfProvider()
case TokenProviders:
return p.parseListProviders()
case TokenInstances:
return p.parseListInstances()
case TokenDefault:
return p.parseListDefaultModels()
case TokenAvailable:
@@ -348,15 +351,23 @@ func (p *Parser) parseShowCommand() (*Command, error) {
return NewCommand("show_token"), nil
case TokenCurrent:
p.nextToken()
if p.curToken.Type != TokenUser {
return nil, fmt.Errorf("expected USER after CURRENT")
}
p.nextToken()
// Semicolon is optional for SHOW TOKEN
if p.curToken.Type == TokenSemicolon {
if p.curToken.Type == TokenUser {
p.nextToken()
// Semicolon is optional for SHOW CURRENT USER
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return NewCommand("show_current_user"), nil
} else if p.curToken.Type == TokenModel {
p.nextToken()
// Semicolon is optional for SHOW CURRENT MODEL
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return NewCommand("show_current_model"), nil
} else {
return nil, fmt.Errorf("expected USER or MODEL after CURRENT")
}
return NewCommand("show_current_user"), nil
case TokenUser:
return p.parseShowUser()
case TokenRole:
@@ -369,6 +380,8 @@ func (p *Parser) parseShowCommand() (*Command, error) {
return p.parseShowProvider()
case TokenModel:
return p.parseShowModel()
case TokenInstance:
return p.parseShowInstance()
default:
return nil, fmt.Errorf("unknown SHOW target: %s", p.curToken.Value)
}
@@ -524,8 +537,6 @@ func (p *Parser) parseCreateCommand() (*Command, error) {
return p.parseCreateRole()
case TokenModel:
return p.parseCreateModelProvider()
case TokenProvider:
return p.parseCreateProvider()
case TokenDataset:
return p.parseCreateDataset()
case TokenChat:
@@ -534,11 +545,23 @@ func (p *Parser) parseCreateCommand() (*Command, error) {
return p.parseCreateToken()
case TokenIndex:
return p.parseCreateIndex()
case TokenProvider:
return p.parseCreateProviderInstance()
default:
return nil, fmt.Errorf("unknown CREATE target: %s", p.curToken.Value)
}
}
func (p *Parser) parseAddCommand() (*Command, error) {
p.nextToken() // consume ADD
switch p.curToken.Type {
case TokenProvider:
return p.parseAddProvider()
default:
return nil, fmt.Errorf("unknown ADD target: %s", p.curToken.Value)
}
}
func (p *Parser) parseCreateToken() (*Command, error) {
p.nextToken() // consume TOKEN
@@ -689,10 +712,10 @@ func (p *Parser) parseCreateModelProvider() (*Command, error) {
return cmd, nil
}
// parseCreateProvider parses CREATE PROVIDER commands
// CREATE PROVIDER <name>
// CREATE PROVIDER <name> <api_key>
func (p *Parser) parseCreateProvider() (*Command, error) {
// parseAddProvider parses ADD PROVIDER commands
// ADD PROVIDER <name>
// ADD PROVIDER <name> <api_key>
func (p *Parser) parseAddProvider() (*Command, error) {
p.nextToken() // consume PROVIDER
providerName, err := p.parseQuotedString()
@@ -700,7 +723,7 @@ func (p *Parser) parseCreateProvider() (*Command, error) {
return nil, fmt.Errorf("expected provider name: %w", err)
}
cmd := NewCommand("create_provider")
cmd := NewCommand("add_provider")
cmd.Params["provider_name"] = providerName
p.nextToken()
@@ -804,8 +827,6 @@ func (p *Parser) parseDropCommand() (*Command, error) {
return p.parseDropRole()
case TokenModel:
return p.parseDropModelProvider()
case TokenProvider:
return p.parseDropProvider()
case TokenDataset:
return p.parseDropDataset()
case TokenChat:
@@ -814,6 +835,19 @@ func (p *Parser) parseDropCommand() (*Command, error) {
return p.parseDropToken()
case TokenIndex:
return p.parseDropIndex()
case TokenInstance:
return p.parseDropInstance()
default:
return nil, fmt.Errorf("unknown DROP target: %s", p.curToken.Value)
}
}
func (p *Parser) parseDeleteCommand() (*Command, error) {
p.nextToken() // consume DELETE
switch p.curToken.Type {
case TokenProvider:
return p.parseDeleteProvider()
default:
return nil, fmt.Errorf("unknown DROP target: %s", p.curToken.Value)
}
@@ -949,8 +983,8 @@ func (p *Parser) parseDropModelProvider() (*Command, error) {
return cmd, nil
}
// parseDropProvider parses DROP PROVIDER <name> command
func (p *Parser) parseDropProvider() (*Command, error) {
// parseDeleteProvider parses DELETE PROVIDER <name> command
func (p *Parser) parseDeleteProvider() (*Command, error) {
p.nextToken() // consume PROVIDER
providerName, err := p.parseQuotedString()
@@ -958,7 +992,7 @@ func (p *Parser) parseDropProvider() (*Command, error) {
return nil, fmt.Errorf("expected provider name: %w", err)
}
cmd := NewCommand("drop_provider")
cmd := NewCommand("delete_provider")
cmd.Params["provider_name"] = providerName
p.nextToken()
@@ -1015,6 +1049,8 @@ func (p *Parser) parseAlterCommand() (*Command, error) {
return p.parseAlterRole()
case TokenProvider:
return p.parseAlterProvider()
case TokenInstance:
return p.parseAlterInstance()
default:
return nil, fmt.Errorf("unknown ALTER target: %s", p.curToken.Value)
}
@@ -1176,6 +1212,189 @@ func (p *Parser) parseAlterProvider() (*Command, error) {
return cmd, nil
}
// parseCreateProviderInstance parses CREATE PROVIDER <name> INSTANCE <instance_name> <api_key> command
// instance_name cannot be "default"
func (p *Parser) parseCreateProviderInstance() (*Command, error) {
p.nextToken() // consume PROVIDER
providerName, err := p.parseQuotedString()
if err != nil {
return nil, fmt.Errorf("expected provider name: %w", err)
}
p.nextToken()
if p.curToken.Type != TokenInstance {
return nil, fmt.Errorf("expected INSTANCE after provider name")
}
p.nextToken()
instanceName, err := p.parseQuotedString()
if err != nil {
return nil, fmt.Errorf("expected instance name: %w", err)
}
// Check if instance_name is "default"
if instanceName == "default" {
return nil, fmt.Errorf("instance name cannot be 'default'")
}
p.nextToken()
apiKey, err := p.parseQuotedString()
if err != nil {
return nil, fmt.Errorf("expected API key: %w", err)
}
cmd := NewCommand("create_provider_instance")
cmd.Params["provider_name"] = providerName
cmd.Params["instance_name"] = instanceName
cmd.Params["api_key"] = apiKey
p.nextToken()
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
// parseListInstances parses LIST INSTANCES FROM PROVIDER <name> command
func (p *Parser) parseListInstances() (*Command, error) {
p.nextToken() // consume INSTANCES
if p.curToken.Type != TokenFrom {
return nil, fmt.Errorf("expected FROM")
}
p.nextToken()
providerName, err := p.parseQuotedString()
if err != nil {
return nil, fmt.Errorf("expected provider name after FROM PROVIDER: %w", err)
}
cmd := NewCommand("list_provider_instances")
cmd.Params["provider_name"] = providerName
p.nextToken()
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
// parseShowInstance parses SHOW INSTANCE <name> FROM PROVIDER <name> command
func (p *Parser) parseShowInstance() (*Command, error) {
p.nextToken() // consume INSTANCE
instanceName, err := p.parseQuotedString()
if err != nil {
return nil, fmt.Errorf("expected instance name: %w", err)
}
p.nextToken()
if p.curToken.Type != TokenFrom {
return nil, fmt.Errorf("expected FROM")
}
p.nextToken()
providerName, err := p.parseQuotedString()
if err != nil {
return nil, fmt.Errorf("expected provider name after FROM PROVIDER: %w", err)
}
cmd := NewCommand("show_provider_instance")
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
instanceName, err := p.parseQuotedString()
if err != nil {
return nil, fmt.Errorf("expected instance name: %w", err)
}
p.nextToken()
if p.curToken.Type != TokenName {
return nil, fmt.Errorf("expected NAME")
}
p.nextToken()
newName, err := p.parseQuotedString()
if err != nil {
return nil, fmt.Errorf("expected new instance name: %w", err)
}
p.nextToken()
if p.curToken.Type != TokenFrom {
return nil, fmt.Errorf("expected FROM")
}
p.nextToken()
if p.curToken.Type != TokenProvider {
return nil, fmt.Errorf("expected PROVIDER after FROM")
}
p.nextToken()
providerName, err := p.parseQuotedString()
if err != nil {
return nil, fmt.Errorf("expected provider name after FROM PROVIDER: %w", err)
}
cmd := NewCommand("alter_provider_instance")
cmd.Params["instance_name"] = instanceName
cmd.Params["new_name"] = newName
cmd.Params["provider_name"] = providerName
p.nextToken()
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
// parseDropInstance parses DROP INSTANCE <name> FROM PROVIDER <name> command
func (p *Parser) parseDropInstance() (*Command, error) {
p.nextToken() // consume INSTANCE
instanceName, err := p.parseQuotedString()
if err != nil {
return nil, fmt.Errorf("expected instance name: %w", err)
}
p.nextToken()
if p.curToken.Type != TokenFrom {
return nil, fmt.Errorf("expected FROM")
}
p.nextToken()
providerName, err := p.parseQuotedString()
if err != nil {
return nil, fmt.Errorf("expected provider name after FROM PROVIDER: %w", err)
}
cmd := NewCommand("drop_provider_instance")
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
}
func (p *Parser) parseGrantCommand() (*Command, error) {
p.nextToken() // consume GRANT
@@ -1662,6 +1881,215 @@ func (p *Parser) parseSearchCommand() (*Command, error) {
return cmd, nil
}
func (p *Parser) parseListModelsOfProvider() (*Command, error) {
if p.curToken.Type != TokenModels {
return nil, fmt.Errorf("expected MODELS")
}
p.nextToken()
if p.curToken.Type != TokenFrom {
return nil, fmt.Errorf("expected FROM")
}
p.nextToken()
// Parse first quoted string (could be instance_name or provider_name)
firstName, err := p.parseQuotedString()
if err != nil {
return nil, err
}
p.nextToken()
// Check if there's a second quoted string (provider_name)
// If so, format is: LIST MODELS FROM <instance_name> <provider_name>
// If not, format is: LIST MODELS FROM <provider_name>
if p.curToken.Type == TokenQuotedString {
// Two arguments: instance_name and provider_name
instanceName, err := p.parseQuotedString()
if err != nil {
return nil, err
}
cmd := NewCommand("list_instance_models")
cmd.Params["instance_name"] = instanceName
cmd.Params["provider_name"] = firstName
p.nextToken()
// Semicolon is optional for UNSET TOKEN
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
// Only one argument: provider_name
cmd := NewCommand("list_provider_models")
cmd.Params["provider_name"] = firstName
// Semicolon is optional for UNSET TOKEN
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
func (p *Parser) parseEnableCommand() (*Command, error) {
p.nextToken() // consume ENABLE
if p.curToken.Type != TokenModel {
return nil, fmt.Errorf("expected MODEL")
}
p.nextToken()
modelName, err := p.parseQuotedString()
if err != nil {
return nil, err
}
p.nextToken()
if p.curToken.Type != TokenFrom {
return nil, fmt.Errorf("expected FROM")
}
p.nextToken()
modelProvider, err := p.parseQuotedString()
if err != nil {
return nil, err
}
p.nextToken()
modelInstance, err := p.parseQuotedString()
if err != nil {
return nil, err
}
p.nextToken()
// Semicolon is optional for UNSET TOKEN
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
cmd := NewCommand("enable_model")
cmd.Params["model_name"] = modelName
cmd.Params["instance_name"] = modelInstance
cmd.Params["provider_name"] = modelProvider
return cmd, nil
}
func (p *Parser) parseDisableCommand() (*Command, error) {
p.nextToken() // consume DISABLE
if p.curToken.Type != TokenModel {
return nil, fmt.Errorf("expected MODEL")
}
p.nextToken()
modelName, err := p.parseQuotedString()
if err != nil {
return nil, err
}
p.nextToken()
if p.curToken.Type != TokenFrom {
return nil, fmt.Errorf("expected FROM")
}
p.nextToken()
modelProvider, err := p.parseQuotedString()
if err != nil {
return nil, err
}
p.nextToken()
modelInstance, err := p.parseQuotedString()
if err != nil {
return nil, err
}
p.nextToken()
// Semicolon is optional for UNSET TOKEN
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
cmd := NewCommand("disable_model")
cmd.Params["model_name"] = modelName
cmd.Params["instance_name"] = modelInstance
cmd.Params["provider_name"] = modelProvider
return cmd, nil
}
func (p *Parser) parseChatCommand() (*Command, error) {
p.nextToken() // consume CHAT
var modelName string
var message string
// Check if we have a quoted string that looks like a model identifier (contains two slashes)
// Format: 'provider/instance/model' or just 'message'
if p.curToken.Type == TokenQuotedString {
firstArg := p.curToken.Value
// Check if it looks like a model identifier (contains exactly 2 slashes)
slashCount := strings.Count(firstArg, "/")
if slashCount == 2 {
// This is likely a model identifier, expect another quoted string for message
modelName = firstArg
p.nextToken()
// After model name, expect message
if p.curToken.Type != TokenQuotedString {
return nil, fmt.Errorf("expected message after model name")
}
message = p.curToken.Value
p.nextToken()
} else {
// This is just a message, use current model
message = firstArg
p.nextToken()
}
} else if p.curToken.Type == TokenIdentifier {
// Context engine style: chat <message>
message = p.curToken.Value
p.nextToken()
} else {
return nil, fmt.Errorf("expected model name (quoted string) or message")
}
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
cmd := NewCommand("chat_to_model")
if modelName != "" {
cmd.Params["model_name"] = modelName
}
cmd.Params["message"] = message
return cmd, nil
}
func (p *Parser) parseUseCommand() (*Command, error) {
p.nextToken() // consume USE
if p.curToken.Type != TokenModel {
return nil, fmt.Errorf("expected MODEL after USE")
}
p.nextToken() // consume MODEL
// Parse model identifier in format 'provider/instance/model'
modelIdentifier, err := p.parseQuotedString()
if err != nil {
return nil, fmt.Errorf("expected model identifier in format 'provider/instance/model': %w", err)
}
p.nextToken()
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
cmd := NewCommand("use_model")
cmd.Params["model_identifier"] = modelIdentifier
return cmd, nil
}
func (p *Parser) parseParseCommand() (*Command, error) {
p.nextToken() // consume PARSE
@@ -1788,6 +2216,7 @@ func (p *Parser) parseUserStatement() (*Command, error) {
return p.parseInsertCommand()
case TokenSearch:
return p.parseSearchCommand()
default:
return nil, fmt.Errorf("invalid user statement: %s", p.curToken.Value)
}