Files
ragflow/internal/cli/user_parser.go

3850 lines
90 KiB
Go
Raw Permalink Normal View History

package cli
import (
"encoding/json"
"fmt"
"ragflow/internal/common"
"regexp"
"strings"
)
func tokenTypeDescription(t int, tok Token) string {
if tok.Type == t && tok.Value != "" {
return fmt.Sprintf("%s %q", tokenTypeToString(t), tok.Value)
}
return tokenTypeToString(t)
}
// Command parsers
func (p *Parser) parseAPILoginUser() (*Command, error) {
cmd := NewCommand("api_login_user")
p.nextToken() // consume LOGIN
if p.curToken.Type != TokenUser {
return nil, fmt.Errorf("expected USER after LOGIN")
}
p.nextToken()
email, err := p.parseQuotedString()
if err != nil {
return nil, err
}
cmd.Params["email"] = email
p.nextToken()
// Optional: PASSWORD 'password'
if p.curToken.Type == TokenPassword {
p.nextToken()
var password string
password, err = p.parseQuotedString()
if err != nil {
return nil, err
}
cmd.Params["password"] = password
p.nextToken()
}
2026-06-25 20:36:50 +08:00
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
func (p *Parser) parseAPILogout() (*Command, error) {
cmd := NewCommand("api_logout")
p.nextToken()
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
func (p *Parser) parseAPIPingServer() (*Command, error) {
cmd := NewCommand("api_ping_server")
p.nextToken()
2026-06-25 20:36:50 +08:00
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
func (p *Parser) parseAPIRegister() (*Command, error) {
cmd := NewCommand("api_register_user")
if err := p.expectPeek(TokenUser); err != nil {
return nil, err
}
p.nextToken() // consume USER
userName, err := p.parseQuotedString()
if err != nil {
return nil, err
}
cmd.Params["user_name"] = userName
p.nextToken() // consume Email
if p.curToken.Type != TokenAs {
return nil, fmt.Errorf("expected AS")
}
p.nextToken() // consume AS
nickname, err := p.parseQuotedString()
if err != nil {
return nil, err
}
cmd.Params["nickname"] = nickname
p.nextToken() // consume nickname
if p.curToken.Type != TokenPassword {
return nil, fmt.Errorf("expected PASSWORD")
}
p.nextToken() // consume PASSWORD
password, err := p.parseQuotedString()
if err != nil {
return nil, err
}
cmd.Params["password"] = password
p.nextToken() // consume 'password'
2026-06-25 20:36:50 +08:00
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
// region LIST commands
// LIST CONFIGS;
// LIST PROVIDER 'provider_name' MODELS;
// LIST PROVIDER 'provider_name' INSTANCE 'instance_name' MODELS
// LIST MODELS;
func (p *Parser) parseAPIListCommands() (*Command, error) {
p.nextToken() // consume LIST
switch p.curToken.Type {
case TokenConfigs:
return p.parseAPIListConfigs()
case TokenDatasets:
return p.parseAPIListDatasets()
case TokenDataset:
Go CLI: list provider instance models, sync and list provider (#16311) ### What problem does this PR solve? ``` RAGFlow(api/default)> list provider 'zhipu-ai' instance 'test' models sync; +------------+---------------+------------+-------------+------------------+---------------------------------------------+ | dimensions | max_dimension | max_tokens | model_types | name | thinking | +------------+---------------+------------+-------------+------------------+---------------------------------------------+ | | | 128000 | [chat] | glm-4.5@z-ai | map[clear_thinking:true default_value:true] | | | | 128000 | [chat] | glm-4.5-air@z-ai | map[clear_thinking:true default_value:true] | | | | 202752 | [chat] | glm-4.6@z-ai | map[clear_thinking:true default_value:true] | | | | 202752 | [chat] | glm-4.7@z-ai | map[clear_thinking:true default_value:true] | | | | 202752 | [chat] | glm-5@z-ai | map[clear_thinking:true default_value:true] | | | | 200000 | [chat] | glm-5-turbo@z-ai | map[clear_thinking:true default_value:true] | | | | 202752 | [chat] | glm-5.1@z-ai | map[clear_thinking:true default_value:true] | | | | | [chat] | glm-5.2@z-ai | | +------------+---------------+------------+-------------+------------------+---------------------------------------------+ RAGFlow(api/default)> list provider 'zhipu-ai' instance 'test' models; RAGFlow(api/default)> list dataset 'aaa' ingestion tasks; RAGFlow(api/default)> list dataset '0abe79f9423311f1ad8d38a74640adcc' documents; ``` --------- Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-06-25 10:01:21 +08:00
return p.parseAPIListDatasetCommands()
case TokenAgents:
return p.parseAPIListAgents()
case TokenChats:
return p.parseAPIListChats()
case TokenSearches:
return p.parseAPIListSearches()
case TokenMemories:
return p.parseAPIListMemories()
case TokenKeys:
return p.parseAPIListAPIKeys()
case TokenProviders:
return p.parseAPIListProviders()
case TokenProvider:
return p.parseAPIListProviderCommands()
case TokenModels:
return p.parseAPIListAllModels()
Go: use NATS as the message queue (#15327) ### What problem does this PR solve? ``` RAGFlow(admin)> mq publish 'msg2'; SUCCESS RAGFlow(admin)> mq publish 'msg3'; SUCCESS RAGFlow(admin)> mq list; +---------+---------------+ | message | subject | +---------+---------------+ | msg1 | tasks.RAGFLOW | | msg2 | tasks.RAGFLOW | | msg3 | tasks.RAGFLOW | +---------+---------------+ RAGFlow(admin)> mq pull 2; +---------+---------------+ | message | subject | +---------+---------------+ | msg1 | tasks.RAGFLOW | | msg2 | tasks.RAGFLOW | +---------+---------------+ RAGFlow(admin)> mq pull noack; +---------+---------------+ | message | subject | +---------+---------------+ | abc | tasks.RAGFLOW | +---------+---------------+ RAGFlow(admin)> mq show +-------------------+----------------+--------+---------------+---------------+-------------------+---------------+ | ack_pending_count | consumer_count | memory | message_count | pending_count | redelivered_count | waiting_count | +-------------------+----------------+--------+---------------+---------------+-------------------+---------------+ | 2 | 1 | 0 | 2 | 0 | 1 | 0 | +-------------------+----------------+--------+---------------+---------------+-------------------+---------------+ RAGFlow(admin)> list ingestors; +--------------+-------------------------------------------+--------+ | host | name | status | +--------------+-------------------------------------------+--------+ | 192.168.1.38 | ingestor-8f0e4bd5650a4ac58b0151969fbf6935 | alive | +--------------+-------------------------------------------+--------+ RAGFlow(admin)> list ingestion tasks; +----------------------------------+----------------------------------+-----------+------+-------------+----------------------------------+ | document_id | id | status | step | user | user_id | +----------------------------------+----------------------------------+-----------+------+-------------+----------------------------------+ | ffe64fae423411f1a2d938a74640adcc | 90d3d0f6528941c1ac8eb0360effccc4 | COMPLETED | 5 | aaa@aaa.com | 2ba4881420fa11f19e9c38a74640adcc | +----------------------------------+----------------------------------+-----------+------+-------------+----------------------------------+ RAGFlow(admin)> remove ingestion tasks '90d3d0f6528941c1ac8eb0360effccc4'; +---------+----------------------------------+ | delete | task_id | +---------+----------------------------------+ | success | 90d3d0f6528941c1ac8eb0360effccc4 | +---------+----------------------------------+ RAGFlow(admin)> stop ingestion tasks 'e89e20d9a25848a1b79bd9345ddbfe1d'; +----------+----------------------------------+ | status | task_id | +----------+----------------------------------+ | STOPPING | e89e20d9a25848a1b79bd9345ddbfe1d | +----------+----------------------------------+ # Publish a message RAGFlow(admin)> mq publish 'cdd'; SUCCESS # List current tasks in the message queue RAGFlow(admin)> mq list +----------------------------------+---------------+ | message | subject | +----------------------------------+---------------+ | 7ce392a3c1624cd2be4b5276e8825059 | tasks.RAGFLOW | +----------------------------------+---------------+ # Consume a task from the message queue RAGFlow(admin)> mq pull +------+-----+----------------+ | ack | id | type | +------+-----+----------------+ | true | cdd | ingestion_test | +------+-----+----------------+ # User mode # List ingestion tasks, followed by dataset id RAGFlow(user)> list ingestion tasks from '0abe79f9423311f1ad8d38a74640adcc'; +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | create_date | create_time | dataset_id | document_id | id | schema | status | update_date | update_time | user_id | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | 2026-05-30T20:21:06+08:00 | 1780143666289 | 0abe79f9423311f1ad8d38a74640adcc | ffe64fae423411f1a2d938a74640adcc | 8d758cd14a8b4ba8ab505003fb52017d | | COMPLETED | 2026-05-30T20:21:26+08:00 | 1780143686431 | 2ba4881420fa11f19e9c38a74640adcc | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ RAGFlow(user)> list ingestion tasks; +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | create_date | create_time | dataset_id | document_id | id | schema | status | update_date | update_time | user_id | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | 2026-06-02T19:02:31+08:00 | 1780398151417 | 0abe79f9423311f1ad8d38a74640adcc | ffe64fae423411f1a2d938a74640adcc | e89e20d9a25848a1b79bd9345ddbfe1d | | COMPLETED | 2026-06-02T19:02:52+08:00 | 1780398172208 | 2ba4881420fa11f19e9c38a74640adcc | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ # Create an ingestion task # First argument is document id, second argument is dataset id RAGFlow(user)> start ingestion 'ffe64fae423411f1a2d938a74640adcc' from '0abe79f9423311f1ad8d38a74640adcc'; +----------------------------------+-------------------------------------------+ | document_id | result | +----------------------------------+-------------------------------------------+ | ffe64fae423411f1a2d938a74640adcc | task_id: 8d758cd14a8b4ba8ab505003fb52017d | +----------------------------------+-------------------------------------------+ # Pause an ingestion task, first argument is ingestion id RAGFlow(user)> stop ingestion '8d758cd14a8b4ba8ab505003fb52017d'; +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | create_date | create_time | dataset_id | document_id | id | schema | status | update_date | update_time | user_id | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | 2026-05-30T20:21:06+08:00 | 1780143666289 | 0abe79f9423311f1ad8d38a74640adcc | ffe64fae423411f1a2d938a74640adcc | 8d758cd14a8b4ba8ab505003fb52017d | | COMPLETED | 2026-05-30T20:21:26+08:00 | 1780143686431 | 2ba4881420fa11f19e9c38a74640adcc | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ # Delete an ingestion task RAGFlow(api/default)> remove ingestion tasks 'f366450a27d54677aec1c7090add30f0'; +---------+----------------------------------+ | remove | task_id | +---------+----------------------------------+ | success | f366450a27d54677aec1c7090add30f0 | +---------+----------------------------------+ ``` ### Type of change - [x] New Feature (non-breaking change which adds functionality) --------- Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-06-12 14:56:44 +08:00
case TokenIngestion:
Go CLI: list provider instance models, sync and list provider (#16311) ### What problem does this PR solve? ``` RAGFlow(api/default)> list provider 'zhipu-ai' instance 'test' models sync; +------------+---------------+------------+-------------+------------------+---------------------------------------------+ | dimensions | max_dimension | max_tokens | model_types | name | thinking | +------------+---------------+------------+-------------+------------------+---------------------------------------------+ | | | 128000 | [chat] | glm-4.5@z-ai | map[clear_thinking:true default_value:true] | | | | 128000 | [chat] | glm-4.5-air@z-ai | map[clear_thinking:true default_value:true] | | | | 202752 | [chat] | glm-4.6@z-ai | map[clear_thinking:true default_value:true] | | | | 202752 | [chat] | glm-4.7@z-ai | map[clear_thinking:true default_value:true] | | | | 202752 | [chat] | glm-5@z-ai | map[clear_thinking:true default_value:true] | | | | 200000 | [chat] | glm-5-turbo@z-ai | map[clear_thinking:true default_value:true] | | | | 202752 | [chat] | glm-5.1@z-ai | map[clear_thinking:true default_value:true] | | | | | [chat] | glm-5.2@z-ai | | +------------+---------------+------------+-------------+------------------+---------------------------------------------+ RAGFlow(api/default)> list provider 'zhipu-ai' instance 'test' models; RAGFlow(api/default)> list dataset 'aaa' ingestion tasks; RAGFlow(api/default)> list dataset '0abe79f9423311f1ad8d38a74640adcc' documents; ``` --------- Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-06-25 10:01:21 +08:00
return p.parseAPIListIngestionTasks()
case TokenDefault:
Go CLI: list provider instance models, sync and list provider (#16311) ### What problem does this PR solve? ``` RAGFlow(api/default)> list provider 'zhipu-ai' instance 'test' models sync; +------------+---------------+------------+-------------+------------------+---------------------------------------------+ | dimensions | max_dimension | max_tokens | model_types | name | thinking | +------------+---------------+------------+-------------+------------------+---------------------------------------------+ | | | 128000 | [chat] | glm-4.5@z-ai | map[clear_thinking:true default_value:true] | | | | 128000 | [chat] | glm-4.5-air@z-ai | map[clear_thinking:true default_value:true] | | | | 202752 | [chat] | glm-4.6@z-ai | map[clear_thinking:true default_value:true] | | | | 202752 | [chat] | glm-4.7@z-ai | map[clear_thinking:true default_value:true] | | | | 202752 | [chat] | glm-5@z-ai | map[clear_thinking:true default_value:true] | | | | 200000 | [chat] | glm-5-turbo@z-ai | map[clear_thinking:true default_value:true] | | | | 202752 | [chat] | glm-5.1@z-ai | map[clear_thinking:true default_value:true] | | | | | [chat] | glm-5.2@z-ai | | +------------+---------------+------------+-------------+------------------+---------------------------------------------+ RAGFlow(api/default)> list provider 'zhipu-ai' instance 'test' models; RAGFlow(api/default)> list dataset 'aaa' ingestion tasks; RAGFlow(api/default)> list dataset '0abe79f9423311f1ad8d38a74640adcc' documents; ``` --------- Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-06-25 10:01:21 +08:00
return p.parseAPIListDefaultModels()
case TokenAvailable:
return p.parseAPIListAvailableProviders()
Go: refactor CLI (#15728) ### What problem does this PR solve? ``` RAGFlow(user)> add api server 'ccc' host '127.0.0.1:9980'; SUCCESS RAGFlow(user)> list api server; +------------+---------------+-----------------+---------+-------------+---------------+ | api_server | api_server_ip | api_server_port | auth | user_name | user_password | +------------+---------------+-----------------+---------+-------------+---------------+ | ccc | 127.0.0.1 | 9980 | no auth | | | | default | 127.0.0.1 | 9384 | login | aaa@aaa.com | *** | +------------+---------------+-----------------+---------+-------------+---------------+ RAGFlow(user)> delete api server 'ccc'; SUCCESS RAGFlow(user)> list api server; +------------+---------------+-----------------+---------+ | api_server | api_server_ip | api_server_port | auth | +------------+---------------+-----------------+---------+ | default | 127.0.0.1 | 9384 | no auth | +------------+---------------+-----------------+---------+ RAGFlow(user)> show admin server; +--------------+-------+ | field | value | +--------------+-------+ | admin_server | N/A | +--------------+-------+ RAGFlow(user)> add admin server host '127.0.0.1:9880'; SUCCESS RAGFlow(user)> show admin server; +-------------------+-----------+ | field | value | +-------------------+-----------+ | admin_server_ip | 127.0.0.1 | | admin_server_port | 9880 | | auth | no auth | +-------------------+-----------+ RAGFlow(user)> delete admin server; SUCCESS RAGFlow(user)> show admin server; +--------------+-------+ | field | value | +--------------+-------+ | admin_server | N/A | +--------------+-------+ RAGFlow(user)> show current +-----------------+-------------+ | field | value | +-----------------+-------------+ | api_server_port | 9384 | | user_name | aaa@aaa.com | | user_password | *** | | mode | api | | verbose | false | | api_server | default | | api_server_ip | 127.0.0.1 | | auth | login | | output | table | | interactive | true | +-----------------+-------------+ ``` ### Type of change - [x] Refactoring --------- Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-06-09 15:22:50 +08:00
case TokenAPI:
return p.parseAPIListAPIServers()
2026-06-25 20:36:50 +08:00
case TokenEnvs:
return p.parseAPIListEnvironments()
case TokenVars:
return p.parseAPIListVariables()
default:
return nil, fmt.Errorf("unknown LIST target: %s", p.curToken.Value)
}
}
// LIST CONFIGS;
func (p *Parser) parseAPIListConfigs() (*Command, error) {
p.nextToken()
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return NewCommand("api_list_configs"), nil
}
func (p *Parser) parseAPIListDatasets() (*Command, error) {
cmd := NewCommand("api_list_datasets")
p.nextToken() // consume DATASETS
2026-06-25 20:36:50 +08:00
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
// LIST DATASET 'dataset_name' DOCUMENTS;
Go CLI: list provider instance models, sync and list provider (#16311) ### What problem does this PR solve? ``` RAGFlow(api/default)> list provider 'zhipu-ai' instance 'test' models sync; +------------+---------------+------------+-------------+------------------+---------------------------------------------+ | dimensions | max_dimension | max_tokens | model_types | name | thinking | +------------+---------------+------------+-------------+------------------+---------------------------------------------+ | | | 128000 | [chat] | glm-4.5@z-ai | map[clear_thinking:true default_value:true] | | | | 128000 | [chat] | glm-4.5-air@z-ai | map[clear_thinking:true default_value:true] | | | | 202752 | [chat] | glm-4.6@z-ai | map[clear_thinking:true default_value:true] | | | | 202752 | [chat] | glm-4.7@z-ai | map[clear_thinking:true default_value:true] | | | | 202752 | [chat] | glm-5@z-ai | map[clear_thinking:true default_value:true] | | | | 200000 | [chat] | glm-5-turbo@z-ai | map[clear_thinking:true default_value:true] | | | | 202752 | [chat] | glm-5.1@z-ai | map[clear_thinking:true default_value:true] | | | | | [chat] | glm-5.2@z-ai | | +------------+---------------+------------+-------------+------------------+---------------------------------------------+ RAGFlow(api/default)> list provider 'zhipu-ai' instance 'test' models; RAGFlow(api/default)> list dataset 'aaa' ingestion tasks; RAGFlow(api/default)> list dataset '0abe79f9423311f1ad8d38a74640adcc' documents; ``` --------- Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-06-25 10:01:21 +08:00
func (p *Parser) parseAPIListDatasetCommands() (*Command, error) {
p.nextToken() // consume DATASET
datasetID, err := p.parseQuotedString()
if err != nil {
return nil, err
}
p.nextToken()
Go CLI: list provider instance models, sync and list provider (#16311) ### What problem does this PR solve? ``` RAGFlow(api/default)> list provider 'zhipu-ai' instance 'test' models sync; +------------+---------------+------------+-------------+------------------+---------------------------------------------+ | dimensions | max_dimension | max_tokens | model_types | name | thinking | +------------+---------------+------------+-------------+------------------+---------------------------------------------+ | | | 128000 | [chat] | glm-4.5@z-ai | map[clear_thinking:true default_value:true] | | | | 128000 | [chat] | glm-4.5-air@z-ai | map[clear_thinking:true default_value:true] | | | | 202752 | [chat] | glm-4.6@z-ai | map[clear_thinking:true default_value:true] | | | | 202752 | [chat] | glm-4.7@z-ai | map[clear_thinking:true default_value:true] | | | | 202752 | [chat] | glm-5@z-ai | map[clear_thinking:true default_value:true] | | | | 200000 | [chat] | glm-5-turbo@z-ai | map[clear_thinking:true default_value:true] | | | | 202752 | [chat] | glm-5.1@z-ai | map[clear_thinking:true default_value:true] | | | | | [chat] | glm-5.2@z-ai | | +------------+---------------+------------+-------------+------------------+---------------------------------------------+ RAGFlow(api/default)> list provider 'zhipu-ai' instance 'test' models; RAGFlow(api/default)> list dataset 'aaa' ingestion tasks; RAGFlow(api/default)> list dataset '0abe79f9423311f1ad8d38a74640adcc' documents; ``` --------- Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-06-25 10:01:21 +08:00
switch p.curToken.Type {
case TokenDocuments:
return p.parseAPIListDatasetDocuments(datasetID)
case TokenFiles:
return p.parseAPIListDatasetFiles(datasetID)
case TokenIngestion:
return p.parseAPIListDatasetIngestionTasks(datasetID)
default:
return nil, fmt.Errorf("unknown LIST target: %s", p.curToken.Value)
}
}
func (p *Parser) parseAPIListDatasetDocuments(datasetID string) (*Command, error) {
p.nextToken() // consume DOCUMENTS
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
cmd := NewCommand("api_list_dataset_documents")
cmd.Params["dataset_id"] = datasetID
Go CLI: list provider instance models, sync and list provider (#16311) ### What problem does this PR solve? ``` RAGFlow(api/default)> list provider 'zhipu-ai' instance 'test' models sync; +------------+---------------+------------+-------------+------------------+---------------------------------------------+ | dimensions | max_dimension | max_tokens | model_types | name | thinking | +------------+---------------+------------+-------------+------------------+---------------------------------------------+ | | | 128000 | [chat] | glm-4.5@z-ai | map[clear_thinking:true default_value:true] | | | | 128000 | [chat] | glm-4.5-air@z-ai | map[clear_thinking:true default_value:true] | | | | 202752 | [chat] | glm-4.6@z-ai | map[clear_thinking:true default_value:true] | | | | 202752 | [chat] | glm-4.7@z-ai | map[clear_thinking:true default_value:true] | | | | 202752 | [chat] | glm-5@z-ai | map[clear_thinking:true default_value:true] | | | | 200000 | [chat] | glm-5-turbo@z-ai | map[clear_thinking:true default_value:true] | | | | 202752 | [chat] | glm-5.1@z-ai | map[clear_thinking:true default_value:true] | | | | | [chat] | glm-5.2@z-ai | | +------------+---------------+------------+-------------+------------------+---------------------------------------------+ RAGFlow(api/default)> list provider 'zhipu-ai' instance 'test' models; RAGFlow(api/default)> list dataset 'aaa' ingestion tasks; RAGFlow(api/default)> list dataset '0abe79f9423311f1ad8d38a74640adcc' documents; ``` --------- Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-06-25 10:01:21 +08:00
return cmd, nil
}
Go CLI: list provider instance models, sync and list provider (#16311) ### What problem does this PR solve? ``` RAGFlow(api/default)> list provider 'zhipu-ai' instance 'test' models sync; +------------+---------------+------------+-------------+------------------+---------------------------------------------+ | dimensions | max_dimension | max_tokens | model_types | name | thinking | +------------+---------------+------------+-------------+------------------+---------------------------------------------+ | | | 128000 | [chat] | glm-4.5@z-ai | map[clear_thinking:true default_value:true] | | | | 128000 | [chat] | glm-4.5-air@z-ai | map[clear_thinking:true default_value:true] | | | | 202752 | [chat] | glm-4.6@z-ai | map[clear_thinking:true default_value:true] | | | | 202752 | [chat] | glm-4.7@z-ai | map[clear_thinking:true default_value:true] | | | | 202752 | [chat] | glm-5@z-ai | map[clear_thinking:true default_value:true] | | | | 200000 | [chat] | glm-5-turbo@z-ai | map[clear_thinking:true default_value:true] | | | | 202752 | [chat] | glm-5.1@z-ai | map[clear_thinking:true default_value:true] | | | | | [chat] | glm-5.2@z-ai | | +------------+---------------+------------+-------------+------------------+---------------------------------------------+ RAGFlow(api/default)> list provider 'zhipu-ai' instance 'test' models; RAGFlow(api/default)> list dataset 'aaa' ingestion tasks; RAGFlow(api/default)> list dataset '0abe79f9423311f1ad8d38a74640adcc' documents; ``` --------- Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-06-25 10:01:21 +08:00
func (p *Parser) parseAPIListDatasetFiles(datasetName string) (*Command, error) {
p.nextToken() // consume FILES
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
cmd := NewCommand("api_list_dataset_files")
cmd.Params["dataset_name"] = datasetName
return cmd, nil
}
func (p *Parser) parseAPIListDatasetIngestionTasks(datasetName string) (*Command, error) {
p.nextToken() // consume INGESTION
if p.curToken.Type != TokenTasks {
return nil, fmt.Errorf("expected TASKS")
}
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
Go CLI: list provider instance models, sync and list provider (#16311) ### What problem does this PR solve? ``` RAGFlow(api/default)> list provider 'zhipu-ai' instance 'test' models sync; +------------+---------------+------------+-------------+------------------+---------------------------------------------+ | dimensions | max_dimension | max_tokens | model_types | name | thinking | +------------+---------------+------------+-------------+------------------+---------------------------------------------+ | | | 128000 | [chat] | glm-4.5@z-ai | map[clear_thinking:true default_value:true] | | | | 128000 | [chat] | glm-4.5-air@z-ai | map[clear_thinking:true default_value:true] | | | | 202752 | [chat] | glm-4.6@z-ai | map[clear_thinking:true default_value:true] | | | | 202752 | [chat] | glm-4.7@z-ai | map[clear_thinking:true default_value:true] | | | | 202752 | [chat] | glm-5@z-ai | map[clear_thinking:true default_value:true] | | | | 200000 | [chat] | glm-5-turbo@z-ai | map[clear_thinking:true default_value:true] | | | | 202752 | [chat] | glm-5.1@z-ai | map[clear_thinking:true default_value:true] | | | | | [chat] | glm-5.2@z-ai | | +------------+---------------+------------+-------------+------------------+---------------------------------------------+ RAGFlow(api/default)> list provider 'zhipu-ai' instance 'test' models; RAGFlow(api/default)> list dataset 'aaa' ingestion tasks; RAGFlow(api/default)> list dataset '0abe79f9423311f1ad8d38a74640adcc' documents; ``` --------- Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-06-25 10:01:21 +08:00
cmd := NewCommand("api_list_ingestion_tasks")
cmd.Params["dataset_name"] = datasetName
return cmd, nil
}
func (p *Parser) parseAPIListAgents() (*Command, error) {
p.nextToken() // consume AGENTS
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return NewCommand("api_list_agents"), nil
}
func (p *Parser) parseAPIListChats() (*Command, error) {
p.nextToken() // consume CHATS
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return NewCommand("api_list_chats"), nil
}
func (p *Parser) parseAPIListSearches() (*Command, error) {
p.nextToken() // consume SEARCHES
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return NewCommand("api_list_searches"), nil
}
func (p *Parser) parseAPIListMemories() (*Command, error) {
p.nextToken() // consume MEMORIES
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return NewCommand("api_list_memories"), nil
}
func (p *Parser) parseAPIListAPIKeys() (*Command, error) {
p.nextToken() // consume KEYS
cmd := NewCommand("api_list_api_keys")
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
// LIST PROVIDERS
func (p *Parser) parseAPIListProviders() (*Command, error) {
p.nextToken() // consume PROVIDERS
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
Go CLI: list provider instance models, sync and list provider (#16311) ### What problem does this PR solve? ``` RAGFlow(api/default)> list provider 'zhipu-ai' instance 'test' models sync; +------------+---------------+------------+-------------+------------------+---------------------------------------------+ | dimensions | max_dimension | max_tokens | model_types | name | thinking | +------------+---------------+------------+-------------+------------------+---------------------------------------------+ | | | 128000 | [chat] | glm-4.5@z-ai | map[clear_thinking:true default_value:true] | | | | 128000 | [chat] | glm-4.5-air@z-ai | map[clear_thinking:true default_value:true] | | | | 202752 | [chat] | glm-4.6@z-ai | map[clear_thinking:true default_value:true] | | | | 202752 | [chat] | glm-4.7@z-ai | map[clear_thinking:true default_value:true] | | | | 202752 | [chat] | glm-5@z-ai | map[clear_thinking:true default_value:true] | | | | 200000 | [chat] | glm-5-turbo@z-ai | map[clear_thinking:true default_value:true] | | | | 202752 | [chat] | glm-5.1@z-ai | map[clear_thinking:true default_value:true] | | | | | [chat] | glm-5.2@z-ai | | +------------+---------------+------------+-------------+------------------+---------------------------------------------+ RAGFlow(api/default)> list provider 'zhipu-ai' instance 'test' models; RAGFlow(api/default)> list dataset 'aaa' ingestion tasks; RAGFlow(api/default)> list dataset '0abe79f9423311f1ad8d38a74640adcc' documents; ``` --------- Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-06-25 10:01:21 +08:00
return NewCommand("api_list_providers"), nil
}
// LIST PROVIDER 'provider_name' INSTANCES
func (p *Parser) parseAPIListProviderCommands() (*Command, error) {
p.nextToken() // consume PROVIDER
providerName, err := p.parseQuotedString()
if err != nil {
return nil, err
}
p.nextToken()
switch p.curToken.Type {
case TokenInstances:
return p.parseListProviderInstances(providerName)
case TokenInstance:
return p.parseListProviderInstanceCommands(providerName)
case TokenModels:
return p.parseListProviderModels(providerName)
default:
return nil, fmt.Errorf("unknown LIST target: %s", p.curToken.Value)
}
}
// LIST PROVIDER 'provider_name' INSTANCES
func (p *Parser) parseListProviderInstances(providerName string) (*Command, error) {
p.nextToken() // consume INSTANCES
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
cmd := NewCommand("api_list_provider_instances")
cmd.Params["provider_name"] = providerName
return cmd, nil
}
// LIST PROVIDER 'provider_name' INSTANCE 'instance_name' MODELS
Go CLI: list provider instance models, sync and list provider (#16311) ### What problem does this PR solve? ``` RAGFlow(api/default)> list provider 'zhipu-ai' instance 'test' models sync; +------------+---------------+------------+-------------+------------------+---------------------------------------------+ | dimensions | max_dimension | max_tokens | model_types | name | thinking | +------------+---------------+------------+-------------+------------------+---------------------------------------------+ | | | 128000 | [chat] | glm-4.5@z-ai | map[clear_thinking:true default_value:true] | | | | 128000 | [chat] | glm-4.5-air@z-ai | map[clear_thinking:true default_value:true] | | | | 202752 | [chat] | glm-4.6@z-ai | map[clear_thinking:true default_value:true] | | | | 202752 | [chat] | glm-4.7@z-ai | map[clear_thinking:true default_value:true] | | | | 202752 | [chat] | glm-5@z-ai | map[clear_thinking:true default_value:true] | | | | 200000 | [chat] | glm-5-turbo@z-ai | map[clear_thinking:true default_value:true] | | | | 202752 | [chat] | glm-5.1@z-ai | map[clear_thinking:true default_value:true] | | | | | [chat] | glm-5.2@z-ai | | +------------+---------------+------------+-------------+------------------+---------------------------------------------+ RAGFlow(api/default)> list provider 'zhipu-ai' instance 'test' models; RAGFlow(api/default)> list dataset 'aaa' ingestion tasks; RAGFlow(api/default)> list dataset '0abe79f9423311f1ad8d38a74640adcc' documents; ``` --------- Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-06-25 10:01:21 +08:00
// LIST PROVIDER 'provider_name' INSTANCE 'instance_name' MODELS SYNC, get model list by API from remote server
func (p *Parser) parseListProviderInstanceCommands(providerName string) (*Command, error) {
p.nextToken() // consume INSTANCE
instanceName, err := p.parseQuotedString()
if err != nil {
return nil, err
}
p.nextToken()
switch p.curToken.Type {
case TokenModels:
return p.parseListProviderInstanceModels(providerName, instanceName)
case TokenTasks:
return p.parseListProviderInstanceTasks(providerName, instanceName)
default:
return nil, fmt.Errorf("unknown LIST target: %s", p.curToken.Value)
}
}
func (p *Parser) parseListProviderInstanceModels(providerName, instanceName string) (*Command, error) {
p.nextToken() // consume MODELS
Go CLI: list provider instance models, sync and list provider (#16311) ### What problem does this PR solve? ``` RAGFlow(api/default)> list provider 'zhipu-ai' instance 'test' models sync; +------------+---------------+------------+-------------+------------------+---------------------------------------------+ | dimensions | max_dimension | max_tokens | model_types | name | thinking | +------------+---------------+------------+-------------+------------------+---------------------------------------------+ | | | 128000 | [chat] | glm-4.5@z-ai | map[clear_thinking:true default_value:true] | | | | 128000 | [chat] | glm-4.5-air@z-ai | map[clear_thinking:true default_value:true] | | | | 202752 | [chat] | glm-4.6@z-ai | map[clear_thinking:true default_value:true] | | | | 202752 | [chat] | glm-4.7@z-ai | map[clear_thinking:true default_value:true] | | | | 202752 | [chat] | glm-5@z-ai | map[clear_thinking:true default_value:true] | | | | 200000 | [chat] | glm-5-turbo@z-ai | map[clear_thinking:true default_value:true] | | | | 202752 | [chat] | glm-5.1@z-ai | map[clear_thinking:true default_value:true] | | | | | [chat] | glm-5.2@z-ai | | +------------+---------------+------------+-------------+------------------+---------------------------------------------+ RAGFlow(api/default)> list provider 'zhipu-ai' instance 'test' models; RAGFlow(api/default)> list dataset 'aaa' ingestion tasks; RAGFlow(api/default)> list dataset '0abe79f9423311f1ad8d38a74640adcc' documents; ``` --------- Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-06-25 10:01:21 +08:00
cmd := NewCommand("api_list_provider_instance_models")
if p.curToken.Type == TokenSync {
cmd = NewCommand("api_list_provider_instance_models_sync")
}
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
cmd.Params["provider_name"] = providerName
cmd.Params["instance_name"] = instanceName
return cmd, nil
}
func (p *Parser) parseListProviderInstanceTasks(providerName, instanceName string) (*Command, error) {
p.nextToken() // consume TASKS
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
cmd := NewCommand("api_list_provider_instance_tasks")
cmd.Params["provider_name"] = providerName
cmd.Params["instance_name"] = instanceName
return cmd, nil
}
func (p *Parser) parseListProviderModels(providerName string) (*Command, error) {
p.nextToken() // consume MODELS
cmd := NewCommand("api_list_provider_models")
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
cmd.Params["provider_name"] = providerName
return cmd, nil
}
// LIST MODELS
func (p *Parser) parseAPIListAllModels() (*Command, error) {
p.nextToken() // consume models
cmd := NewCommand("api_list_all_models")
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
func (p *Parser) parseAPIListIngestionTasks() (*Command, error) {
p.nextToken() // consume Ingestion
if p.curToken.Type != TokenTasks {
return nil, fmt.Errorf("expected TASKS")
}
p.nextToken() // consume TASKS
cmd := NewCommand("api_list_ingestion_tasks")
if p.curToken.Type == TokenFrom {
p.nextToken()
datasetID, err := p.parseQuotedString()
if err != nil {
return nil, err
}
cmd.Params["dataset_id"] = datasetID
}
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
Go CLI: list provider instance models, sync and list provider (#16311) ### What problem does this PR solve? ``` RAGFlow(api/default)> list provider 'zhipu-ai' instance 'test' models sync; +------------+---------------+------------+-------------+------------------+---------------------------------------------+ | dimensions | max_dimension | max_tokens | model_types | name | thinking | +------------+---------------+------------+-------------+------------------+---------------------------------------------+ | | | 128000 | [chat] | glm-4.5@z-ai | map[clear_thinking:true default_value:true] | | | | 128000 | [chat] | glm-4.5-air@z-ai | map[clear_thinking:true default_value:true] | | | | 202752 | [chat] | glm-4.6@z-ai | map[clear_thinking:true default_value:true] | | | | 202752 | [chat] | glm-4.7@z-ai | map[clear_thinking:true default_value:true] | | | | 202752 | [chat] | glm-5@z-ai | map[clear_thinking:true default_value:true] | | | | 200000 | [chat] | glm-5-turbo@z-ai | map[clear_thinking:true default_value:true] | | | | 202752 | [chat] | glm-5.1@z-ai | map[clear_thinking:true default_value:true] | | | | | [chat] | glm-5.2@z-ai | | +------------+---------------+------------+-------------+------------------+---------------------------------------------+ RAGFlow(api/default)> list provider 'zhipu-ai' instance 'test' models; RAGFlow(api/default)> list dataset 'aaa' ingestion tasks; RAGFlow(api/default)> list dataset '0abe79f9423311f1ad8d38a74640adcc' documents; ``` --------- Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-06-25 10:01:21 +08:00
func (p *Parser) parseAPIListDefaultModels() (*Command, error) {
p.nextToken() // consume DEFAULT
if p.curToken.Type != TokenModels {
return nil, fmt.Errorf("expected MODELS")
}
p.nextToken()
2026-06-25 20:36:50 +08:00
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
Go CLI: list provider instance models, sync and list provider (#16311) ### What problem does this PR solve? ``` RAGFlow(api/default)> list provider 'zhipu-ai' instance 'test' models sync; +------------+---------------+------------+-------------+------------------+---------------------------------------------+ | dimensions | max_dimension | max_tokens | model_types | name | thinking | +------------+---------------+------------+-------------+------------------+---------------------------------------------+ | | | 128000 | [chat] | glm-4.5@z-ai | map[clear_thinking:true default_value:true] | | | | 128000 | [chat] | glm-4.5-air@z-ai | map[clear_thinking:true default_value:true] | | | | 202752 | [chat] | glm-4.6@z-ai | map[clear_thinking:true default_value:true] | | | | 202752 | [chat] | glm-4.7@z-ai | map[clear_thinking:true default_value:true] | | | | 202752 | [chat] | glm-5@z-ai | map[clear_thinking:true default_value:true] | | | | 200000 | [chat] | glm-5-turbo@z-ai | map[clear_thinking:true default_value:true] | | | | 202752 | [chat] | glm-5.1@z-ai | map[clear_thinking:true default_value:true] | | | | | [chat] | glm-5.2@z-ai | | +------------+---------------+------------+-------------+------------------+---------------------------------------------+ RAGFlow(api/default)> list provider 'zhipu-ai' instance 'test' models; RAGFlow(api/default)> list dataset 'aaa' ingestion tasks; RAGFlow(api/default)> list dataset '0abe79f9423311f1ad8d38a74640adcc' documents; ``` --------- Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-06-25 10:01:21 +08:00
return NewCommand("api_list_default_models"), nil
}
func (p *Parser) parseAPIListAvailableProviders() (*Command, error) {
p.nextToken() // consume AVAILABLE
if p.curToken.Type != TokenProviders {
return nil, fmt.Errorf("expected PROVIDERS")
}
return NewCommand("api_list_available_providers"), nil
}
func (p *Parser) parseAPIListAPIServers() (*Command, error) {
p.nextToken() // consume API
var cmd *Command
switch p.curToken.Type {
case TokenServer:
p.nextToken()
cmd = NewCommand("api_list_api_servers")
default:
return nil, fmt.Errorf("expected SERVER after API")
}
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
func (p *Parser) parseAPIListEnvironments() (*Command, error) {
p.nextToken() // consume Envs
cmd := NewCommand("api_list_environments")
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
func (p *Parser) parseAPIListVariables() (*Command, error) {
p.nextToken() // consume Variables
cmd := NewCommand("api_list_variables")
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
// endregion LIST commands
// region SHOW commands
func (p *Parser) parseAPIShowCommands() (*Command, error) {
p.nextToken() // consume SHOW
switch p.curToken.Type {
case TokenVersion:
return p.parseAPIShowVersion()
case TokenKey:
return p.parseAPIShowKey()
case TokenCurrent:
2026-06-25 20:36:50 +08:00
return p.parseAPIShowCurrent()
case TokenVar:
return p.parseAPIShowVariable()
case TokenProvider:
return p.parseAPIShowProviderCommands()
case TokenModel:
return p.parseAPIShowModel()
Go: refactor CLI (#15728) ### What problem does this PR solve? ``` RAGFlow(user)> add api server 'ccc' host '127.0.0.1:9980'; SUCCESS RAGFlow(user)> list api server; +------------+---------------+-----------------+---------+-------------+---------------+ | api_server | api_server_ip | api_server_port | auth | user_name | user_password | +------------+---------------+-----------------+---------+-------------+---------------+ | ccc | 127.0.0.1 | 9980 | no auth | | | | default | 127.0.0.1 | 9384 | login | aaa@aaa.com | *** | +------------+---------------+-----------------+---------+-------------+---------------+ RAGFlow(user)> delete api server 'ccc'; SUCCESS RAGFlow(user)> list api server; +------------+---------------+-----------------+---------+ | api_server | api_server_ip | api_server_port | auth | +------------+---------------+-----------------+---------+ | default | 127.0.0.1 | 9384 | no auth | +------------+---------------+-----------------+---------+ RAGFlow(user)> show admin server; +--------------+-------+ | field | value | +--------------+-------+ | admin_server | N/A | +--------------+-------+ RAGFlow(user)> add admin server host '127.0.0.1:9880'; SUCCESS RAGFlow(user)> show admin server; +-------------------+-----------+ | field | value | +-------------------+-----------+ | admin_server_ip | 127.0.0.1 | | admin_server_port | 9880 | | auth | no auth | +-------------------+-----------+ RAGFlow(user)> delete admin server; SUCCESS RAGFlow(user)> show admin server; +--------------+-------+ | field | value | +--------------+-------+ | admin_server | N/A | +--------------+-------+ RAGFlow(user)> show current +-----------------+-------------+ | field | value | +-----------------+-------------+ | api_server_port | 9384 | | user_name | aaa@aaa.com | | user_password | *** | | mode | api | | verbose | false | | api_server | default | | api_server_ip | 127.0.0.1 | | auth | login | | output | table | | interactive | true | +-----------------+-------------+ ``` ### Type of change - [x] Refactoring --------- Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-06-09 15:22:50 +08:00
case TokenAdmin:
return p.parseAPIShowAdmin()
Go: refactor CLI (#15728) ### What problem does this PR solve? ``` RAGFlow(user)> add api server 'ccc' host '127.0.0.1:9980'; SUCCESS RAGFlow(user)> list api server; +------------+---------------+-----------------+---------+-------------+---------------+ | api_server | api_server_ip | api_server_port | auth | user_name | user_password | +------------+---------------+-----------------+---------+-------------+---------------+ | ccc | 127.0.0.1 | 9980 | no auth | | | | default | 127.0.0.1 | 9384 | login | aaa@aaa.com | *** | +------------+---------------+-----------------+---------+-------------+---------------+ RAGFlow(user)> delete api server 'ccc'; SUCCESS RAGFlow(user)> list api server; +------------+---------------+-----------------+---------+ | api_server | api_server_ip | api_server_port | auth | +------------+---------------+-----------------+---------+ | default | 127.0.0.1 | 9384 | no auth | +------------+---------------+-----------------+---------+ RAGFlow(user)> show admin server; +--------------+-------+ | field | value | +--------------+-------+ | admin_server | N/A | +--------------+-------+ RAGFlow(user)> add admin server host '127.0.0.1:9880'; SUCCESS RAGFlow(user)> show admin server; +-------------------+-----------+ | field | value | +-------------------+-----------+ | admin_server_ip | 127.0.0.1 | | admin_server_port | 9880 | | auth | no auth | +-------------------+-----------+ RAGFlow(user)> delete admin server; SUCCESS RAGFlow(user)> show admin server; +--------------+-------+ | field | value | +--------------+-------+ | admin_server | N/A | +--------------+-------+ RAGFlow(user)> show current +-----------------+-------------+ | field | value | +-----------------+-------------+ | api_server_port | 9384 | | user_name | aaa@aaa.com | | user_password | *** | | mode | api | | verbose | false | | api_server | default | | api_server_ip | 127.0.0.1 | | auth | login | | output | table | | interactive | true | +-----------------+-------------+ ``` ### Type of change - [x] Refactoring --------- Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-06-09 15:22:50 +08:00
case TokenAPI:
return p.parseAPIShowAPI()
2026-06-25 20:36:50 +08:00
case TokenLog:
return p.parseAPIShowLogCommands()
default:
return nil, fmt.Errorf("unknown SHOW target: %s", p.curToken.Value)
}
}
func (p *Parser) parseAPIShowVersion() (*Command, error) {
p.nextToken() // consume VERSION
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return NewCommand("api_show_version"), nil
}
func (p *Parser) parseAPIShowKey() (*Command, error) {
p.nextToken() // consume KEY
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return NewCommand("api_show_api_key"), nil
}
2026-06-25 20:36:50 +08:00
func (p *Parser) parseAPIShowCurrent() (*Command, error) {
p.nextToken()
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return NewCommand("api_show_current"), nil
}
func (p *Parser) parseAPIShowVariable() (*Command, error) {
p.nextToken() // consume VAR
varName, err := p.parseQuotedString()
if err != nil {
return nil, err
}
p.nextToken()
cmd := NewCommand("api_show_variable")
cmd.Params["var_name"] = varName
2026-06-25 20:36:50 +08:00
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
// SHOW PROVIDER <name>;
// SHOW PROVIDER <name> INSTANCE <instance_name>;
// SHOW PROVIDER <name> INSTANCE <instance_name> BALANCE;
// SHOW PROVIDER <name> INSTANCE <instance_name> TASK <task_id>;
// SHOW PROVIDER 'provider_name' MODEL 'model_name';
func (p *Parser) parseAPIShowProviderCommands() (*Command, error) {
p.nextToken() // consume PROVIDER
providerName, err := p.parseQuotedString()
if err != nil {
return nil, fmt.Errorf("expected provider name: %w", err)
}
p.nextToken() // consume provider_name
switch p.curToken.Type {
case TokenInstance:
return p.parseAPIShowProviderInstance(providerName)
case TokenModel:
return p.parseAPIShowProviderModel(providerName)
case TokenSemicolon, TokenEOF:
p.nextToken()
default:
return nil, fmt.Errorf("unknown SHOW target: %s", p.curToken.Value)
}
cmd := NewCommand("api_show_provider")
cmd.Params["provider_name"] = providerName
return cmd, nil
}
// SHOW PROVIDER <name> INSTANCE <instance_name>;
func (p *Parser) parseAPIShowProviderInstance(providerName string) (*Command, error) {
p.nextToken() // consume INSTANCE
instanceName, err := p.parseQuotedString()
if err != nil {
return nil, fmt.Errorf("expected instance name: %w", err)
}
p.nextToken() // consume instance_name
switch p.curToken.Type {
case TokenBalance:
return p.parseAPIShowProviderInstanceBalance(providerName, instanceName)
case TokenTask:
return p.parseAPIShowProviderInstanceTask(providerName, instanceName)
case TokenSemicolon, TokenEOF:
p.nextToken()
default:
return nil, fmt.Errorf("unknown SHOW target: %s", p.curToken.Value)
}
cmd := NewCommand("api_show_provider_instance")
cmd.Params["instance_name"] = instanceName
cmd.Params["provider_name"] = providerName
return cmd, nil
}
// SHOW PROVIDER <name> INSTANCE <instance_name> BALANCE
func (p *Parser) parseAPIShowProviderInstanceBalance(providerName, instanceName string) (*Command, error) {
p.nextToken() // consume BALANCE
cmd := NewCommand("api_show_provider_instance_balance")
cmd.Params["instance_name"] = instanceName
cmd.Params["provider_name"] = providerName
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
// SHOW PROVIDER <name> INSTANCE <instance_name> TASK <task_id>
func (p *Parser) parseAPIShowProviderInstanceTask(providerName, instanceName string) (*Command, error) {
p.nextToken() // consume TASK
taskID, err := p.parseQuotedString()
if err != nil {
return nil, fmt.Errorf("expected task id: %w", err)
}
p.nextToken() // consume task_id
cmd := NewCommand("api_show_provider_instance_task")
cmd.Params["instance_name"] = instanceName
cmd.Params["provider_name"] = providerName
cmd.Params["task_id"] = taskID
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
// SHOW PROVIDER <name> MODEL <model_name>
func (p *Parser) parseAPIShowProviderModel(providerName string) (*Command, error) {
p.nextToken() // consume MODEL
modelName, err := p.parseQuotedString()
if err != nil {
return nil, fmt.Errorf("expected model name: %w", err)
}
p.nextToken() // consume model_name
cmd := NewCommand("api_show_provider_model")
cmd.Params["model_name"] = modelName
cmd.Params["provider_name"] = providerName
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
// SHOW MODEL 'model_name';
func (p *Parser) parseAPIShowModel() (*Command, error) {
p.nextToken() // consume MODEL
Go: new CLI command, list all models and show model (#15786) ### What problem does this PR solve? ``` RAGFlow(user)> list models; +---------------------------+------------+-------------+--------------------+---------------------------------------------+ | alias | max_tokens | model_types | name | thinking | +---------------------------+------------+-------------+--------------------+---------------------------------------------+ | | 1048576 | [chat] | deepseek-v4-flash | map[clear_thinking:true default_value:true] | | | 1048576 | [chat] | deepseek-v4-pro | map[clear_thinking:true default_value:true] | | | 1024000 | [chat] | minimax-m3 | map[clear_thinking:true default_value:true] | | | 64000 | [vision] | glm-4.5v | map[clear_thinking:true default_value:true] | | [baai/bge-m3] | 8192 | [embedding] | bge-m3 | | | [baai/bge-reranker-v2-m3] | 1024 | [rerank] | bge-reranker-v2-m3 | | | | | [tts] | step-audio-tts-3b | | | [qwen/qwen3-asr-1.7b] | | [asr] | qwen3-asr-1.7b | | | [paddleocr-vl-1.5] | | [ocr] | paddleocr-vl-0.9b | | +---------------------------+------------+-------------+--------------------+---------------------------------------------+ RAGFlow(user)> show model 'minimax-m3'; +--------------+---------------------------------------------+ | field | value | +--------------+---------------------------------------------+ | name | minimax-m3 | | max_tokens | 1024000 | | model_types | [chat] | | thinking | map[clear_thinking:true default_value:true] | | class | | | alias | | | ModelTypeMap | | +--------------+---------------------------------------------+ RAGFlow(user)> show model 'baai/bge-m3'; +--------------+---------------+ | field | value | +--------------+---------------+ | model_types | [embedding] | | thinking | | | class | | | alias | [baai/bge-m3] | | ModelTypeMap | | | name | bge-m3 | | max_tokens | 8192 | +--------------+---------------+ ``` --------- Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-06-08 21:38:15 +08:00
modelName, err := p.parseQuotedString()
if err != nil {
return nil, fmt.Errorf("expected model name: %w", err)
}
p.nextToken() // consume model_name
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
cmd := NewCommand("api_show_model")
cmd.Params["model_name"] = modelName
return cmd, nil
}
// SHOW ADMIN SERVER
func (p *Parser) parseAPIShowAdmin() (*Command, error) {
p.nextToken() // consume ADMIN
var cmd *Command
switch p.curToken.Type {
case TokenServer:
p.nextToken()
cmd = NewCommand("api_show_admin_server")
default:
return nil, fmt.Errorf("expected SERVER after ADMIN")
}
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
// SHOW API SERVER <server_name>
func (p *Parser) parseAPIShowAPI() (*Command, error) {
p.nextToken() // consume API
var cmd *Command
switch p.curToken.Type {
case TokenServer:
p.nextToken()
cmd = NewCommand("api_show_api_server")
serverName, err := p.parseQuotedString()
if err != nil {
return nil, fmt.Errorf("expected API server name: %w", err)
}
cmd.Params["api_server_name"] = serverName
p.nextToken()
default:
return nil, fmt.Errorf("expected SERVER after API")
}
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
Go: new CLI command, list all models and show model (#15786) ### What problem does this PR solve? ``` RAGFlow(user)> list models; +---------------------------+------------+-------------+--------------------+---------------------------------------------+ | alias | max_tokens | model_types | name | thinking | +---------------------------+------------+-------------+--------------------+---------------------------------------------+ | | 1048576 | [chat] | deepseek-v4-flash | map[clear_thinking:true default_value:true] | | | 1048576 | [chat] | deepseek-v4-pro | map[clear_thinking:true default_value:true] | | | 1024000 | [chat] | minimax-m3 | map[clear_thinking:true default_value:true] | | | 64000 | [vision] | glm-4.5v | map[clear_thinking:true default_value:true] | | [baai/bge-m3] | 8192 | [embedding] | bge-m3 | | | [baai/bge-reranker-v2-m3] | 1024 | [rerank] | bge-reranker-v2-m3 | | | | | [tts] | step-audio-tts-3b | | | [qwen/qwen3-asr-1.7b] | | [asr] | qwen3-asr-1.7b | | | [paddleocr-vl-1.5] | | [ocr] | paddleocr-vl-0.9b | | +---------------------------+------------+-------------+--------------------+---------------------------------------------+ RAGFlow(user)> show model 'minimax-m3'; +--------------+---------------------------------------------+ | field | value | +--------------+---------------------------------------------+ | name | minimax-m3 | | max_tokens | 1024000 | | model_types | [chat] | | thinking | map[clear_thinking:true default_value:true] | | class | | | alias | | | ModelTypeMap | | +--------------+---------------------------------------------+ RAGFlow(user)> show model 'baai/bge-m3'; +--------------+---------------+ | field | value | +--------------+---------------+ | model_types | [embedding] | | thinking | | | class | | | alias | [baai/bge-m3] | | ModelTypeMap | | | name | bge-m3 | | max_tokens | 8192 | +--------------+---------------+ ``` --------- Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-06-08 21:38:15 +08:00
func (p *Parser) parseAPIShowLogCommands() (*Command, error) {
p.nextToken() // consume LOG
switch p.curToken.Type {
case TokenLevel:
return p.parseShowLogLevel()
default:
return nil, fmt.Errorf("expected LEVEL after LOG")
}
}
func (p *Parser) parseShowLogLevel() (*Command, error) {
p.nextToken() // consume LEVEL
cmd := NewCommand("api_show_log_level")
// Semicolon is optional
Go: new CLI command, list all models and show model (#15786) ### What problem does this PR solve? ``` RAGFlow(user)> list models; +---------------------------+------------+-------------+--------------------+---------------------------------------------+ | alias | max_tokens | model_types | name | thinking | +---------------------------+------------+-------------+--------------------+---------------------------------------------+ | | 1048576 | [chat] | deepseek-v4-flash | map[clear_thinking:true default_value:true] | | | 1048576 | [chat] | deepseek-v4-pro | map[clear_thinking:true default_value:true] | | | 1024000 | [chat] | minimax-m3 | map[clear_thinking:true default_value:true] | | | 64000 | [vision] | glm-4.5v | map[clear_thinking:true default_value:true] | | [baai/bge-m3] | 8192 | [embedding] | bge-m3 | | | [baai/bge-reranker-v2-m3] | 1024 | [rerank] | bge-reranker-v2-m3 | | | | | [tts] | step-audio-tts-3b | | | [qwen/qwen3-asr-1.7b] | | [asr] | qwen3-asr-1.7b | | | [paddleocr-vl-1.5] | | [ocr] | paddleocr-vl-0.9b | | +---------------------------+------------+-------------+--------------------+---------------------------------------------+ RAGFlow(user)> show model 'minimax-m3'; +--------------+---------------------------------------------+ | field | value | +--------------+---------------------------------------------+ | name | minimax-m3 | | max_tokens | 1024000 | | model_types | [chat] | | thinking | map[clear_thinking:true default_value:true] | | class | | | alias | | | ModelTypeMap | | +--------------+---------------------------------------------+ RAGFlow(user)> show model 'baai/bge-m3'; +--------------+---------------+ | field | value | +--------------+---------------+ | model_types | [embedding] | | thinking | | | class | | | alias | [baai/bge-m3] | | ModelTypeMap | | | name | bge-m3 | | max_tokens | 8192 | +--------------+---------------+ ``` --------- Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-06-08 21:38:15 +08:00
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
Go: new CLI command, list all models and show model (#15786) ### What problem does this PR solve? ``` RAGFlow(user)> list models; +---------------------------+------------+-------------+--------------------+---------------------------------------------+ | alias | max_tokens | model_types | name | thinking | +---------------------------+------------+-------------+--------------------+---------------------------------------------+ | | 1048576 | [chat] | deepseek-v4-flash | map[clear_thinking:true default_value:true] | | | 1048576 | [chat] | deepseek-v4-pro | map[clear_thinking:true default_value:true] | | | 1024000 | [chat] | minimax-m3 | map[clear_thinking:true default_value:true] | | | 64000 | [vision] | glm-4.5v | map[clear_thinking:true default_value:true] | | [baai/bge-m3] | 8192 | [embedding] | bge-m3 | | | [baai/bge-reranker-v2-m3] | 1024 | [rerank] | bge-reranker-v2-m3 | | | | | [tts] | step-audio-tts-3b | | | [qwen/qwen3-asr-1.7b] | | [asr] | qwen3-asr-1.7b | | | [paddleocr-vl-1.5] | | [ocr] | paddleocr-vl-0.9b | | +---------------------------+------------+-------------+--------------------+---------------------------------------------+ RAGFlow(user)> show model 'minimax-m3'; +--------------+---------------------------------------------+ | field | value | +--------------+---------------------------------------------+ | name | minimax-m3 | | max_tokens | 1024000 | | model_types | [chat] | | thinking | map[clear_thinking:true default_value:true] | | class | | | alias | | | ModelTypeMap | | +--------------+---------------------------------------------+ RAGFlow(user)> show model 'baai/bge-m3'; +--------------+---------------+ | field | value | +--------------+---------------+ | model_types | [embedding] | | thinking | | | class | | | alias | [baai/bge-m3] | | ModelTypeMap | | | name | bge-m3 | | max_tokens | 8192 | +--------------+---------------+ ``` --------- Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-06-08 21:38:15 +08:00
return cmd, nil
}
// endregion SHOW commands
// region CREATE commands
func (p *Parser) parseAPICreateCommands() (*Command, error) {
p.nextToken() // consume CREATE
switch p.curToken.Type {
case TokenDataset:
return p.parseAPICreateDataset()
case TokenChat:
return p.parseAPICreateChat()
case TokenSearch:
return p.parseAPICreateSearch()
case TokenAgent:
return p.parseAPICreateAgent()
case TokenMemory:
return p.parseAPICreateMemory()
case TokenKey:
return p.parseAPICreateKey()
case TokenChunkStore:
return p.parseDevCreateChunkStore()
case TokenMetadata:
return p.parseDevCreateMetadataStore()
case TokenProvider:
return p.parseAPICreateProviderInstance()
default:
return nil, fmt.Errorf("unknown CREATE target: %s", p.curToken.Value)
}
}
// CREATE DATASET 'abc';
func (p *Parser) parseAPICreateDataset() (*Command, error) {
p.nextToken() // consume DATASET
datasetName, err := p.parseQuotedString()
if err != nil {
return nil, err
}
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
cmd := NewCommand("api_create_dataset")
cmd.Params["dataset_name"] = datasetName
return cmd, nil
}
// CREATE CHAT 'chat_name'
func (p *Parser) parseAPICreateChat() (*Command, error) {
p.nextToken() // consume CHAT
chatName, err := p.parseQuotedString()
if err != nil {
return nil, err
}
p.nextToken()
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
cmd := NewCommand("api_create_chat")
cmd.Params["chat_name"] = chatName
return cmd, nil
}
// CREATE SEARCH 'search_name'
func (p *Parser) parseAPICreateSearch() (*Command, error) {
p.nextToken() // consume SEARCH
searchName, err := p.parseQuotedString()
if err != nil {
return nil, err
}
p.nextToken()
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
cmd := NewCommand("api_create_search")
cmd.Params["search_name"] = searchName
return cmd, nil
}
// CREATE AGENT 'agent_name'
func (p *Parser) parseAPICreateAgent() (*Command, error) {
p.nextToken() // consume AGENT
agentName, err := p.parseQuotedString()
if err != nil {
return nil, err
}
p.nextToken()
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
cmd := NewCommand("api_create_agent")
cmd.Params["agent_name"] = agentName
return cmd, nil
}
// CREATE MEMORY 'memory_name'
func (p *Parser) parseAPICreateMemory() (*Command, error) {
p.nextToken() // consume MEMORY
memoryName, err := p.parseQuotedString()
if err != nil {
return nil, err
}
p.nextToken()
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
cmd := NewCommand("api_create_memory")
cmd.Params["memory_name"] = memoryName
return cmd, nil
}
// CREATE KEY 'key_name'
func (p *Parser) parseAPICreateKey() (*Command, error) {
p.nextToken() // consume KEY
// Semicolon is optional for UNSET KEY
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return NewCommand("api_create_api_key"), nil
}
// CREATE PROVIDER <name> INSTANCE <instance_name> KEY <api_key> URL <base_url> REGION <region>
func (p *Parser) parseAPICreateProviderInstance() (*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)
}
p.nextToken()
if p.curToken.Type != TokenKey {
return nil, fmt.Errorf("expected KEY after instance name")
}
p.nextToken()
apiKey, err := p.parseQuotedString()
if err != nil {
return nil, fmt.Errorf("expected API key: %w", err)
}
p.nextToken()
baseURL := ""
region := ""
optionsLoop:
for {
switch p.curToken.Type {
case TokenRegion:
p.nextToken()
region, err = p.parseQuotedString()
if err != nil {
return nil, fmt.Errorf("expected region: %w", err)
}
p.nextToken()
case TokenURL:
p.nextToken()
baseURL, err = p.parseQuotedString()
if err != nil {
return nil, fmt.Errorf("expected base URL: %w", err)
}
p.nextToken()
default:
break optionsLoop
}
}
cmd := NewCommand("api_create_provider_instance")
cmd.Params["provider_name"] = providerName
cmd.Params["instance_name"] = instanceName
cmd.Params["api_key"] = apiKey
if baseURL != "" {
// Only local model provider need to set URL
cmd.Params["base_url"] = baseURL
}
if region != "" {
cmd.Params["region"] = region
}
p.nextToken()
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
// endregion CREATE commands
feat(go-cli): support batch model add/remove and optional embedding dimension (#15631) ## Summary This PR improves the Go CLI in two areas: 1. It adds batch model management support, allowing multiple models to be added or removed in a single command. 2. It makes the `dimension` argument optional for the `embed text` command. These changes keep the existing single-model and explicit-dimension behaviors compatible while making the CLI more convenient for common workflows. ## What Changed ### 1. Batch model add/remove support The CLI now supports operating on multiple model names provided in a single quoted string. Supported commands include: ``` add model 'x1 x2 x3' to provider 'vllm' instance 'test' with tokens 1024 chat think vision, token 2048 chat, token 1024 think vision; drop model 'x1 x2 x3' from 'vllm' 'test'; remove model 'x1 x2 x3' from 'vllm' 'test'; ``` For add model, each config segment after with is matched to the corresponding model name by position. Example mapping: - x1 -> tokens 1024, chat + vision, thinking=true - x2 -> tokens 2048, chat - x3 -> tokens 1024, vision, thinking=true The existing single-model syntax remains supported. ### 2. Optional embedding dimension Previously, the Go CLI required dimension to be explicitly provided for embed text. Before: embed text 'what is rag' 'who are you' with 'model@test@provider' dimension 8192; Now both forms are supported: embed text 'what is rag' 'who are you' with 'model@test@provider' dimension 8192; embed text 'what is rag' 'who are you' with 'model@test@provider'; When omitted, the CLI leaves dimension unset and relies on provider/backend behavior. ## Tests Added parser tests covering: - Multiple models with multiple config segments - Model type deduplication - Model/config count mismatch - Drop/remove multiple models - Optional embedding dimension parsing
2026-06-05 19:31:06 +08:00
// region DROP commands
func (p *Parser) parseAPIDropCommands() (*Command, error) {
p.nextToken() // consume DROP
feat(go-cli): support batch model add/remove and optional embedding dimension (#15631) ## Summary This PR improves the Go CLI in two areas: 1. It adds batch model management support, allowing multiple models to be added or removed in a single command. 2. It makes the `dimension` argument optional for the `embed text` command. These changes keep the existing single-model and explicit-dimension behaviors compatible while making the CLI more convenient for common workflows. ## What Changed ### 1. Batch model add/remove support The CLI now supports operating on multiple model names provided in a single quoted string. Supported commands include: ``` add model 'x1 x2 x3' to provider 'vllm' instance 'test' with tokens 1024 chat think vision, token 2048 chat, token 1024 think vision; drop model 'x1 x2 x3' from 'vllm' 'test'; remove model 'x1 x2 x3' from 'vllm' 'test'; ``` For add model, each config segment after with is matched to the corresponding model name by position. Example mapping: - x1 -> tokens 1024, chat + vision, thinking=true - x2 -> tokens 2048, chat - x3 -> tokens 1024, vision, thinking=true The existing single-model syntax remains supported. ### 2. Optional embedding dimension Previously, the Go CLI required dimension to be explicitly provided for embed text. Before: embed text 'what is rag' 'who are you' with 'model@test@provider' dimension 8192; Now both forms are supported: embed text 'what is rag' 'who are you' with 'model@test@provider' dimension 8192; embed text 'what is rag' 'who are you' with 'model@test@provider'; When omitted, the CLI leaves dimension unset and relies on provider/backend behavior. ## Tests Added parser tests covering: - Multiple models with multiple config segments - Model type deduplication - Model/config count mismatch - Drop/remove multiple models - Optional embedding dimension parsing
2026-06-05 19:31:06 +08:00
switch p.curToken.Type {
case TokenDataset:
return p.parseAPIDropDataset()
case TokenChat:
return p.parseAPIDropChat()
case TokenSearch:
return p.parseAPIDropSearch()
case TokenMemory:
return p.parseAPIDropMemory()
case TokenAgent:
return p.parseAPIDropAgent()
case TokenKey:
return p.parseAPIDropAPIKey()
case TokenChunkStore:
return p.parseDevDropChunkStore()
case TokenMetadata:
return p.parseDevDropMetadataStore()
default:
return nil, fmt.Errorf("unknown DROP target: %s", p.curToken.Value)
feat(go-cli): support batch model add/remove and optional embedding dimension (#15631) ## Summary This PR improves the Go CLI in two areas: 1. It adds batch model management support, allowing multiple models to be added or removed in a single command. 2. It makes the `dimension` argument optional for the `embed text` command. These changes keep the existing single-model and explicit-dimension behaviors compatible while making the CLI more convenient for common workflows. ## What Changed ### 1. Batch model add/remove support The CLI now supports operating on multiple model names provided in a single quoted string. Supported commands include: ``` add model 'x1 x2 x3' to provider 'vllm' instance 'test' with tokens 1024 chat think vision, token 2048 chat, token 1024 think vision; drop model 'x1 x2 x3' from 'vllm' 'test'; remove model 'x1 x2 x3' from 'vllm' 'test'; ``` For add model, each config segment after with is matched to the corresponding model name by position. Example mapping: - x1 -> tokens 1024, chat + vision, thinking=true - x2 -> tokens 2048, chat - x3 -> tokens 1024, vision, thinking=true The existing single-model syntax remains supported. ### 2. Optional embedding dimension Previously, the Go CLI required dimension to be explicitly provided for embed text. Before: embed text 'what is rag' 'who are you' with 'model@test@provider' dimension 8192; Now both forms are supported: embed text 'what is rag' 'who are you' with 'model@test@provider' dimension 8192; embed text 'what is rag' 'who are you' with 'model@test@provider'; When omitted, the CLI leaves dimension unset and relies on provider/backend behavior. ## Tests Added parser tests covering: - Multiple models with multiple config segments - Model type deduplication - Model/config count mismatch - Drop/remove multiple models - Optional embedding dimension parsing
2026-06-05 19:31:06 +08:00
}
}
func (p *Parser) parseAPIDropDataset() (*Command, error) {
p.nextToken() // consume DATASET
datasetName, err := p.parseQuotedString()
feat(go-cli): support batch model add/remove and optional embedding dimension (#15631) ## Summary This PR improves the Go CLI in two areas: 1. It adds batch model management support, allowing multiple models to be added or removed in a single command. 2. It makes the `dimension` argument optional for the `embed text` command. These changes keep the existing single-model and explicit-dimension behaviors compatible while making the CLI more convenient for common workflows. ## What Changed ### 1. Batch model add/remove support The CLI now supports operating on multiple model names provided in a single quoted string. Supported commands include: ``` add model 'x1 x2 x3' to provider 'vllm' instance 'test' with tokens 1024 chat think vision, token 2048 chat, token 1024 think vision; drop model 'x1 x2 x3' from 'vllm' 'test'; remove model 'x1 x2 x3' from 'vllm' 'test'; ``` For add model, each config segment after with is matched to the corresponding model name by position. Example mapping: - x1 -> tokens 1024, chat + vision, thinking=true - x2 -> tokens 2048, chat - x3 -> tokens 1024, vision, thinking=true The existing single-model syntax remains supported. ### 2. Optional embedding dimension Previously, the Go CLI required dimension to be explicitly provided for embed text. Before: embed text 'what is rag' 'who are you' with 'model@test@provider' dimension 8192; Now both forms are supported: embed text 'what is rag' 'who are you' with 'model@test@provider' dimension 8192; embed text 'what is rag' 'who are you' with 'model@test@provider'; When omitted, the CLI leaves dimension unset and relies on provider/backend behavior. ## Tests Added parser tests covering: - Multiple models with multiple config segments - Model type deduplication - Model/config count mismatch - Drop/remove multiple models - Optional embedding dimension parsing
2026-06-05 19:31:06 +08:00
if err != nil {
return nil, err
}
p.nextToken()
cmd := NewCommand("api_drop_dataset")
cmd.Params["dataset_name"] = datasetName
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
func (p *Parser) parseAPIDropChat() (*Command, error) {
p.nextToken() // consume CHAT
chatName, err := p.parseQuotedString()
if err != nil {
return nil, err
}
cmd := NewCommand("api_drop_chat")
cmd.Params["chat_name"] = chatName
p.nextToken()
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
func (p *Parser) parseAPIDropSearch() (*Command, error) {
p.nextToken() // consume SEARCH
searchName, err := p.parseQuotedString()
if err != nil {
return nil, err
}
p.nextToken()
cmd := NewCommand("api_drop_search")
cmd.Params["search_name"] = searchName
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
func (p *Parser) parseAPIDropMemory() (*Command, error) {
p.nextToken() // consume MEMORY
memoryName, err := p.parseQuotedString()
if err != nil {
return nil, err
}
cmd := NewCommand("api_drop_memory")
cmd.Params["memory_name"] = memoryName
p.nextToken()
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
func (p *Parser) parseAPIDropAgent() (*Command, error) {
p.nextToken() // consume AGENT
agentName, err := p.parseQuotedString()
if err != nil {
return nil, err
}
cmd := NewCommand("api_drop_agent")
cmd.Params["agent_name"] = agentName
p.nextToken()
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
func (p *Parser) parseAPIDropAPIKey() (*Command, error) {
p.nextToken() // consume KEY
apiKey, err := p.parseQuotedString()
if err != nil {
return nil, err
}
p.nextToken()
cmd := NewCommand("api_delete_api_key")
cmd.Params["api_key"] = apiKey
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
// endregion DROP commands
// region ADD commands
func (p *Parser) parseAPIAddCommands() (*Command, error) {
p.nextToken() // consume ADD
switch p.curToken.Type {
case TokenProvider:
return p.parseAPIAddProvider()
case TokenModel:
return p.parseAPIAddModel()
case TokenAPI:
return p.parseAddAPIServer()
case TokenAdmin:
return p.parseAddAdminServer()
default:
return nil, fmt.Errorf("unknown ADD target: %s", p.curToken.Value)
}
}
// parseAPIAddProvider parses ADD PROVIDER commands
// ADD PROVIDER <name>
// ADD PROVIDER <name> INSTANCE <instance> KEY <key> [URL <url> | REGION <region>]
func (p *Parser) parseAPIAddProvider() (*Command, error) {
p.nextToken() // consume PROVIDER
providerName, err := p.parseQuotedString()
if err != nil {
return nil, fmt.Errorf("expected provider name: %w", err)
}
p.nextToken()
switch p.curToken.Type {
case TokenInstance:
return p.parseAddProviderInstance(providerName)
case TokenEOF, TokenSemicolon:
p.nextToken()
default:
return nil, fmt.Errorf("unknown ADD PROVIDER target: %s", p.curToken.Value)
}
cmd := NewCommand("api_add_provider")
cmd.Params["provider_name"] = providerName
p.nextToken()
return cmd, nil
}
// ADD PROVIDER <name> INSTANCE <instance>
func (p *Parser) parseAddProviderInstance(providerName string) (*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 != TokenKey {
return nil, fmt.Errorf("expected KEY after instance name")
}
p.nextToken()
apiKey, err := p.parseQuotedString()
if err != nil {
return nil, fmt.Errorf("expected API key: %w", err)
}
p.nextToken()
baseURL := ""
region := ""
optionsLoop:
for {
switch p.curToken.Type {
case TokenRegion:
p.nextToken()
region, err = p.parseQuotedString()
if err != nil {
return nil, fmt.Errorf("expected region: %w", err)
}
p.nextToken()
case TokenURL:
p.nextToken()
baseURL, err = p.parseQuotedString()
if err != nil {
return nil, fmt.Errorf("expected base URL: %w", err)
}
p.nextToken()
default:
break optionsLoop
}
}
cmd := NewCommand("api_add_provider_instance")
cmd.Params["provider_name"] = providerName
cmd.Params["instance_name"] = instanceName
cmd.Params["api_key"] = apiKey
if baseURL != "" {
// Only local model provider need to set URL
cmd.Params["base_url"] = baseURL
}
if region != "" {
cmd.Params["region"] = region
}
p.nextToken()
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
func (p *Parser) parseModelNames(raw string) ([]string, error) {
modelNames := strings.Fields(raw)
if len(modelNames) == 0 {
return nil, fmt.Errorf("model name is required")
}
seen := make(map[string]struct{}, len(modelNames))
for _, modelName := range modelNames {
if _, ok := seen[modelName]; ok {
return nil, fmt.Errorf("duplicate model name: %s", modelName)
}
seen[modelName] = struct{}{}
}
return modelNames, nil
}
// ADD MODEL 'xxx' TO PROVIDER 'vllm' INSTANCE 'test' TOKENS 1024 CHAT THINK VISION;
func (p *Parser) parseAPIAddModel() (*Command, error) {
p.nextToken() // consume MODEL
if p.curToken.Type != TokenQuotedString {
return nil, fmt.Errorf("expected model name")
}
rawModelNames, err := p.parseQuotedString()
if err != nil {
return nil, err
}
modelNames, err := p.parseModelNames(rawModelNames)
if err != nil {
return nil, err
}
p.nextToken() // consume model name
if p.curToken.Type != TokenTo {
return nil, fmt.Errorf("expected TO")
}
p.nextToken()
if p.curToken.Type != TokenProvider {
return nil, fmt.Errorf("expected PROVIDER")
}
p.nextToken()
// provider name
if p.curToken.Type != TokenQuotedString {
return nil, fmt.Errorf("expected provider name")
}
providerName, err := p.parseQuotedString()
if err != nil {
return nil, err
}
p.nextToken()
if p.curToken.Type != TokenInstance {
return nil, fmt.Errorf("expected INSTANCE")
}
p.nextToken()
// instance name
if p.curToken.Type != TokenQuotedString {
return nil, fmt.Errorf("expected provider name")
}
instanceName, err := p.parseQuotedString()
if err != nil {
return nil, err
}
p.nextToken()
modelIndex := 0
var modelTypes []string
var supportThink *bool = nil
maxTokens := 0
var maxDimension *int = nil
var dimensions []int = nil
feat(go-cli): support batch model add/remove and optional embedding dimension (#15631) ## Summary This PR improves the Go CLI in two areas: 1. It adds batch model management support, allowing multiple models to be added or removed in a single command. 2. It makes the `dimension` argument optional for the `embed text` command. These changes keep the existing single-model and explicit-dimension behaviors compatible while making the CLI more convenient for common workflows. ## What Changed ### 1. Batch model add/remove support The CLI now supports operating on multiple model names provided in a single quoted string. Supported commands include: ``` add model 'x1 x2 x3' to provider 'vllm' instance 'test' with tokens 1024 chat think vision, token 2048 chat, token 1024 think vision; drop model 'x1 x2 x3' from 'vllm' 'test'; remove model 'x1 x2 x3' from 'vllm' 'test'; ``` For add model, each config segment after with is matched to the corresponding model name by position. Example mapping: - x1 -> tokens 1024, chat + vision, thinking=true - x2 -> tokens 2048, chat - x3 -> tokens 1024, vision, thinking=true The existing single-model syntax remains supported. ### 2. Optional embedding dimension Previously, the Go CLI required dimension to be explicitly provided for embed text. Before: embed text 'what is rag' 'who are you' with 'model@test@provider' dimension 8192; Now both forms are supported: embed text 'what is rag' 'who are you' with 'model@test@provider' dimension 8192; embed text 'what is rag' 'who are you' with 'model@test@provider'; When omitted, the CLI leaves dimension unset and relies on provider/backend behavior. ## Tests Added parser tests covering: - Multiple models with multiple config segments - Model type deduplication - Model/config count mismatch - Drop/remove multiple models - Optional embedding dimension parsing
2026-06-05 19:31:06 +08:00
models := make([]map[string]any, 0, len(modelNames))
optionsLoop:
feat(go-cli): support batch model add/remove and optional embedding dimension (#15631) ## Summary This PR improves the Go CLI in two areas: 1. It adds batch model management support, allowing multiple models to be added or removed in a single command. 2. It makes the `dimension` argument optional for the `embed text` command. These changes keep the existing single-model and explicit-dimension behaviors compatible while making the CLI more convenient for common workflows. ## What Changed ### 1. Batch model add/remove support The CLI now supports operating on multiple model names provided in a single quoted string. Supported commands include: ``` add model 'x1 x2 x3' to provider 'vllm' instance 'test' with tokens 1024 chat think vision, token 2048 chat, token 1024 think vision; drop model 'x1 x2 x3' from 'vllm' 'test'; remove model 'x1 x2 x3' from 'vllm' 'test'; ``` For add model, each config segment after with is matched to the corresponding model name by position. Example mapping: - x1 -> tokens 1024, chat + vision, thinking=true - x2 -> tokens 2048, chat - x3 -> tokens 1024, vision, thinking=true The existing single-model syntax remains supported. ### 2. Optional embedding dimension Previously, the Go CLI required dimension to be explicitly provided for embed text. Before: embed text 'what is rag' 'who are you' with 'model@test@provider' dimension 8192; Now both forms are supported: embed text 'what is rag' 'who are you' with 'model@test@provider' dimension 8192; embed text 'what is rag' 'who are you' with 'model@test@provider'; When omitted, the CLI leaves dimension unset and relies on provider/backend behavior. ## Tests Added parser tests covering: - Multiple models with multiple config segments - Model type deduplication - Model/config count mismatch - Drop/remove multiple models - Optional embedding dimension parsing
2026-06-05 19:31:06 +08:00
for {
if modelIndex >= len(modelNames) {
feat(go-cli): support batch model add/remove and optional embedding dimension (#15631) ## Summary This PR improves the Go CLI in two areas: 1. It adds batch model management support, allowing multiple models to be added or removed in a single command. 2. It makes the `dimension` argument optional for the `embed text` command. These changes keep the existing single-model and explicit-dimension behaviors compatible while making the CLI more convenient for common workflows. ## What Changed ### 1. Batch model add/remove support The CLI now supports operating on multiple model names provided in a single quoted string. Supported commands include: ``` add model 'x1 x2 x3' to provider 'vllm' instance 'test' with tokens 1024 chat think vision, token 2048 chat, token 1024 think vision; drop model 'x1 x2 x3' from 'vllm' 'test'; remove model 'x1 x2 x3' from 'vllm' 'test'; ``` For add model, each config segment after with is matched to the corresponding model name by position. Example mapping: - x1 -> tokens 1024, chat + vision, thinking=true - x2 -> tokens 2048, chat - x3 -> tokens 1024, vision, thinking=true The existing single-model syntax remains supported. ### 2. Optional embedding dimension Previously, the Go CLI required dimension to be explicitly provided for embed text. Before: embed text 'what is rag' 'who are you' with 'model@test@provider' dimension 8192; Now both forms are supported: embed text 'what is rag' 'who are you' with 'model@test@provider' dimension 8192; embed text 'what is rag' 'who are you' with 'model@test@provider'; When omitted, the CLI leaves dimension unset and relies on provider/backend behavior. ## Tests Added parser tests covering: - Multiple models with multiple config segments - Model type deduplication - Model/config count mismatch - Drop/remove multiple models - Optional embedding dimension parsing
2026-06-05 19:31:06 +08:00
return nil, fmt.Errorf("too many model configs: got more configs than model names")
}
currentModelName := modelNames[modelIndex]
feat(go-cli): support batch model add/remove and optional embedding dimension (#15631) ## Summary This PR improves the Go CLI in two areas: 1. It adds batch model management support, allowing multiple models to be added or removed in a single command. 2. It makes the `dimension` argument optional for the `embed text` command. These changes keep the existing single-model and explicit-dimension behaviors compatible while making the CLI more convenient for common workflows. ## What Changed ### 1. Batch model add/remove support The CLI now supports operating on multiple model names provided in a single quoted string. Supported commands include: ``` add model 'x1 x2 x3' to provider 'vllm' instance 'test' with tokens 1024 chat think vision, token 2048 chat, token 1024 think vision; drop model 'x1 x2 x3' from 'vllm' 'test'; remove model 'x1 x2 x3' from 'vllm' 'test'; ``` For add model, each config segment after with is matched to the corresponding model name by position. Example mapping: - x1 -> tokens 1024, chat + vision, thinking=true - x2 -> tokens 2048, chat - x3 -> tokens 1024, vision, thinking=true The existing single-model syntax remains supported. ### 2. Optional embedding dimension Previously, the Go CLI required dimension to be explicitly provided for embed text. Before: embed text 'what is rag' 'who are you' with 'model@test@provider' dimension 8192; Now both forms are supported: embed text 'what is rag' 'who are you' with 'model@test@provider' dimension 8192; embed text 'what is rag' 'who are you' with 'model@test@provider'; When omitted, the CLI leaves dimension unset and relies on provider/backend behavior. ## Tests Added parser tests covering: - Multiple models with multiple config segments - Model type deduplication - Model/config count mismatch - Drop/remove multiple models - Optional embedding dimension parsing
2026-06-05 19:31:06 +08:00
switch p.curToken.Type {
case TokenThink:
if supportThink != nil {
return nil, fmt.Errorf("think model is already set for model %s", currentModelName)
feat(go-cli): support batch model add/remove and optional embedding dimension (#15631) ## Summary This PR improves the Go CLI in two areas: 1. It adds batch model management support, allowing multiple models to be added or removed in a single command. 2. It makes the `dimension` argument optional for the `embed text` command. These changes keep the existing single-model and explicit-dimension behaviors compatible while making the CLI more convenient for common workflows. ## What Changed ### 1. Batch model add/remove support The CLI now supports operating on multiple model names provided in a single quoted string. Supported commands include: ``` add model 'x1 x2 x3' to provider 'vllm' instance 'test' with tokens 1024 chat think vision, token 2048 chat, token 1024 think vision; drop model 'x1 x2 x3' from 'vllm' 'test'; remove model 'x1 x2 x3' from 'vllm' 'test'; ``` For add model, each config segment after with is matched to the corresponding model name by position. Example mapping: - x1 -> tokens 1024, chat + vision, thinking=true - x2 -> tokens 2048, chat - x3 -> tokens 1024, vision, thinking=true The existing single-model syntax remains supported. ### 2. Optional embedding dimension Previously, the Go CLI required dimension to be explicitly provided for embed text. Before: embed text 'what is rag' 'who are you' with 'model@test@provider' dimension 8192; Now both forms are supported: embed text 'what is rag' 'who are you' with 'model@test@provider' dimension 8192; embed text 'what is rag' 'who are you' with 'model@test@provider'; When omitted, the CLI leaves dimension unset and relies on provider/backend behavior. ## Tests Added parser tests covering: - Multiple models with multiple config segments - Model type deduplication - Model/config count mismatch - Drop/remove multiple models - Optional embedding dimension parsing
2026-06-05 19:31:06 +08:00
}
value := true
supportThink = &value
p.nextToken()
case TokenVision:
modelTypes = append(modelTypes, "vision")
p.nextToken()
case TokenChat:
modelTypes = append(modelTypes, "chat")
p.nextToken()
case TokenEmbedding:
modelTypes = append(modelTypes, "embedding")
p.nextToken()
if p.curToken.Type == TokenInteger {
val, err := p.parseNumber()
if err != nil {
return nil, err
}
maxDimension = &val
p.nextToken()
dimensions = make([]int, 0)
for p.curToken.Type == TokenInteger {
dim, err := p.parseNumber()
if err != nil {
return nil, err
}
dimensions = append(dimensions, int(dim))
p.nextToken()
}
}
feat(go-cli): support batch model add/remove and optional embedding dimension (#15631) ## Summary This PR improves the Go CLI in two areas: 1. It adds batch model management support, allowing multiple models to be added or removed in a single command. 2. It makes the `dimension` argument optional for the `embed text` command. These changes keep the existing single-model and explicit-dimension behaviors compatible while making the CLI more convenient for common workflows. ## What Changed ### 1. Batch model add/remove support The CLI now supports operating on multiple model names provided in a single quoted string. Supported commands include: ``` add model 'x1 x2 x3' to provider 'vllm' instance 'test' with tokens 1024 chat think vision, token 2048 chat, token 1024 think vision; drop model 'x1 x2 x3' from 'vllm' 'test'; remove model 'x1 x2 x3' from 'vllm' 'test'; ``` For add model, each config segment after with is matched to the corresponding model name by position. Example mapping: - x1 -> tokens 1024, chat + vision, thinking=true - x2 -> tokens 2048, chat - x3 -> tokens 1024, vision, thinking=true The existing single-model syntax remains supported. ### 2. Optional embedding dimension Previously, the Go CLI required dimension to be explicitly provided for embed text. Before: embed text 'what is rag' 'who are you' with 'model@test@provider' dimension 8192; Now both forms are supported: embed text 'what is rag' 'who are you' with 'model@test@provider' dimension 8192; embed text 'what is rag' 'who are you' with 'model@test@provider'; When omitted, the CLI leaves dimension unset and relies on provider/backend behavior. ## Tests Added parser tests covering: - Multiple models with multiple config segments - Model type deduplication - Model/config count mismatch - Drop/remove multiple models - Optional embedding dimension parsing
2026-06-05 19:31:06 +08:00
case TokenRerank:
modelTypes = append(modelTypes, "rerank")
p.nextToken()
case TokenOCR:
modelTypes = append(modelTypes, "ocr")
p.nextToken()
case TokenDocParse:
modelTypes = append(modelTypes, "doc_parse")
p.nextToken()
case TokenTTS:
modelTypes = append(modelTypes, "tts")
p.nextToken()
case TokenASR:
modelTypes = append(modelTypes, "asr")
p.nextToken()
case TokenToken, TokenTokens:
p.nextToken()
if maxTokens != 0 {
return nil, fmt.Errorf("max tokens is already given %d for model %s", maxTokens, currentModelName)
feat(go-cli): support batch model add/remove and optional embedding dimension (#15631) ## Summary This PR improves the Go CLI in two areas: 1. It adds batch model management support, allowing multiple models to be added or removed in a single command. 2. It makes the `dimension` argument optional for the `embed text` command. These changes keep the existing single-model and explicit-dimension behaviors compatible while making the CLI more convenient for common workflows. ## What Changed ### 1. Batch model add/remove support The CLI now supports operating on multiple model names provided in a single quoted string. Supported commands include: ``` add model 'x1 x2 x3' to provider 'vllm' instance 'test' with tokens 1024 chat think vision, token 2048 chat, token 1024 think vision; drop model 'x1 x2 x3' from 'vllm' 'test'; remove model 'x1 x2 x3' from 'vllm' 'test'; ``` For add model, each config segment after with is matched to the corresponding model name by position. Example mapping: - x1 -> tokens 1024, chat + vision, thinking=true - x2 -> tokens 2048, chat - x3 -> tokens 1024, vision, thinking=true The existing single-model syntax remains supported. ### 2. Optional embedding dimension Previously, the Go CLI required dimension to be explicitly provided for embed text. Before: embed text 'what is rag' 'who are you' with 'model@test@provider' dimension 8192; Now both forms are supported: embed text 'what is rag' 'who are you' with 'model@test@provider' dimension 8192; embed text 'what is rag' 'who are you' with 'model@test@provider'; When omitted, the CLI leaves dimension unset and relies on provider/backend behavior. ## Tests Added parser tests covering: - Multiple models with multiple config segments - Model type deduplication - Model/config count mismatch - Drop/remove multiple models - Optional embedding dimension parsing
2026-06-05 19:31:06 +08:00
}
if p.curToken.Type != TokenInteger {
return nil, fmt.Errorf("expected integer")
}
maxTokens, err = p.parseNumber()
if err != nil {
return nil, err
}
p.nextToken() // consume number
case TokenComma, TokenSemicolon, TokenEOF:
if len(modelTypes) == 0 {
return nil, fmt.Errorf("model type is required for model %s", currentModelName)
feat(go-cli): support batch model add/remove and optional embedding dimension (#15631) ## Summary This PR improves the Go CLI in two areas: 1. It adds batch model management support, allowing multiple models to be added or removed in a single command. 2. It makes the `dimension` argument optional for the `embed text` command. These changes keep the existing single-model and explicit-dimension behaviors compatible while making the CLI more convenient for common workflows. ## What Changed ### 1. Batch model add/remove support The CLI now supports operating on multiple model names provided in a single quoted string. Supported commands include: ``` add model 'x1 x2 x3' to provider 'vllm' instance 'test' with tokens 1024 chat think vision, token 2048 chat, token 1024 think vision; drop model 'x1 x2 x3' from 'vllm' 'test'; remove model 'x1 x2 x3' from 'vllm' 'test'; ``` For add model, each config segment after with is matched to the corresponding model name by position. Example mapping: - x1 -> tokens 1024, chat + vision, thinking=true - x2 -> tokens 2048, chat - x3 -> tokens 1024, vision, thinking=true The existing single-model syntax remains supported. ### 2. Optional embedding dimension Previously, the Go CLI required dimension to be explicitly provided for embed text. Before: embed text 'what is rag' 'who are you' with 'model@test@provider' dimension 8192; Now both forms are supported: embed text 'what is rag' 'who are you' with 'model@test@provider' dimension 8192; embed text 'what is rag' 'who are you' with 'model@test@provider'; When omitted, the CLI leaves dimension unset and relies on provider/backend behavior. ## Tests Added parser tests covering: - Multiple models with multiple config segments - Model type deduplication - Model/config count mismatch - Drop/remove multiple models - Optional embedding dimension parsing
2026-06-05 19:31:06 +08:00
}
seenTypes := make(map[string]struct{}, len(modelTypes))
dedupedModelTypes := make([]string, 0, len(modelTypes))
for _, modelType := range modelTypes {
modelType = strings.TrimSpace(modelType)
if modelType == "" {
continue
}
feat(go-cli): support batch model add/remove and optional embedding dimension (#15631) ## Summary This PR improves the Go CLI in two areas: 1. It adds batch model management support, allowing multiple models to be added or removed in a single command. 2. It makes the `dimension` argument optional for the `embed text` command. These changes keep the existing single-model and explicit-dimension behaviors compatible while making the CLI more convenient for common workflows. ## What Changed ### 1. Batch model add/remove support The CLI now supports operating on multiple model names provided in a single quoted string. Supported commands include: ``` add model 'x1 x2 x3' to provider 'vllm' instance 'test' with tokens 1024 chat think vision, token 2048 chat, token 1024 think vision; drop model 'x1 x2 x3' from 'vllm' 'test'; remove model 'x1 x2 x3' from 'vllm' 'test'; ``` For add model, each config segment after with is matched to the corresponding model name by position. Example mapping: - x1 -> tokens 1024, chat + vision, thinking=true - x2 -> tokens 2048, chat - x3 -> tokens 1024, vision, thinking=true The existing single-model syntax remains supported. ### 2. Optional embedding dimension Previously, the Go CLI required dimension to be explicitly provided for embed text. Before: embed text 'what is rag' 'who are you' with 'model@test@provider' dimension 8192; Now both forms are supported: embed text 'what is rag' 'who are you' with 'model@test@provider' dimension 8192; embed text 'what is rag' 'who are you' with 'model@test@provider'; When omitted, the CLI leaves dimension unset and relies on provider/backend behavior. ## Tests Added parser tests covering: - Multiple models with multiple config segments - Model type deduplication - Model/config count mismatch - Drop/remove multiple models - Optional embedding dimension parsing
2026-06-05 19:31:06 +08:00
if _, ok := seenTypes[modelType]; ok {
continue
}
feat(go-cli): support batch model add/remove and optional embedding dimension (#15631) ## Summary This PR improves the Go CLI in two areas: 1. It adds batch model management support, allowing multiple models to be added or removed in a single command. 2. It makes the `dimension` argument optional for the `embed text` command. These changes keep the existing single-model and explicit-dimension behaviors compatible while making the CLI more convenient for common workflows. ## What Changed ### 1. Batch model add/remove support The CLI now supports operating on multiple model names provided in a single quoted string. Supported commands include: ``` add model 'x1 x2 x3' to provider 'vllm' instance 'test' with tokens 1024 chat think vision, token 2048 chat, token 1024 think vision; drop model 'x1 x2 x3' from 'vllm' 'test'; remove model 'x1 x2 x3' from 'vllm' 'test'; ``` For add model, each config segment after with is matched to the corresponding model name by position. Example mapping: - x1 -> tokens 1024, chat + vision, thinking=true - x2 -> tokens 2048, chat - x3 -> tokens 1024, vision, thinking=true The existing single-model syntax remains supported. ### 2. Optional embedding dimension Previously, the Go CLI required dimension to be explicitly provided for embed text. Before: embed text 'what is rag' 'who are you' with 'model@test@provider' dimension 8192; Now both forms are supported: embed text 'what is rag' 'who are you' with 'model@test@provider' dimension 8192; embed text 'what is rag' 'who are you' with 'model@test@provider'; When omitted, the CLI leaves dimension unset and relies on provider/backend behavior. ## Tests Added parser tests covering: - Multiple models with multiple config segments - Model type deduplication - Model/config count mismatch - Drop/remove multiple models - Optional embedding dimension parsing
2026-06-05 19:31:06 +08:00
seenTypes[modelType] = struct{}{}
dedupedModelTypes = append(dedupedModelTypes, modelType)
}
modelTypes = dedupedModelTypes
if len(modelTypes) == 0 {
return nil, fmt.Errorf("model type is required for model %s", currentModelName)
feat(go-cli): support batch model add/remove and optional embedding dimension (#15631) ## Summary This PR improves the Go CLI in two areas: 1. It adds batch model management support, allowing multiple models to be added or removed in a single command. 2. It makes the `dimension` argument optional for the `embed text` command. These changes keep the existing single-model and explicit-dimension behaviors compatible while making the CLI more convenient for common workflows. ## What Changed ### 1. Batch model add/remove support The CLI now supports operating on multiple model names provided in a single quoted string. Supported commands include: ``` add model 'x1 x2 x3' to provider 'vllm' instance 'test' with tokens 1024 chat think vision, token 2048 chat, token 1024 think vision; drop model 'x1 x2 x3' from 'vllm' 'test'; remove model 'x1 x2 x3' from 'vllm' 'test'; ``` For add model, each config segment after with is matched to the corresponding model name by position. Example mapping: - x1 -> tokens 1024, chat + vision, thinking=true - x2 -> tokens 2048, chat - x3 -> tokens 1024, vision, thinking=true The existing single-model syntax remains supported. ### 2. Optional embedding dimension Previously, the Go CLI required dimension to be explicitly provided for embed text. Before: embed text 'what is rag' 'who are you' with 'model@test@provider' dimension 8192; Now both forms are supported: embed text 'what is rag' 'who are you' with 'model@test@provider' dimension 8192; embed text 'what is rag' 'who are you' with 'model@test@provider'; When omitted, the CLI leaves dimension unset and relies on provider/backend behavior. ## Tests Added parser tests covering: - Multiple models with multiple config segments - Model type deduplication - Model/config count mismatch - Drop/remove multiple models - Optional embedding dimension parsing
2026-06-05 19:31:06 +08:00
}
model := map[string]any{
"model_name": currentModelName,
feat(go-cli): support batch model add/remove and optional embedding dimension (#15631) ## Summary This PR improves the Go CLI in two areas: 1. It adds batch model management support, allowing multiple models to be added or removed in a single command. 2. It makes the `dimension` argument optional for the `embed text` command. These changes keep the existing single-model and explicit-dimension behaviors compatible while making the CLI more convenient for common workflows. ## What Changed ### 1. Batch model add/remove support The CLI now supports operating on multiple model names provided in a single quoted string. Supported commands include: ``` add model 'x1 x2 x3' to provider 'vllm' instance 'test' with tokens 1024 chat think vision, token 2048 chat, token 1024 think vision; drop model 'x1 x2 x3' from 'vllm' 'test'; remove model 'x1 x2 x3' from 'vllm' 'test'; ``` For add model, each config segment after with is matched to the corresponding model name by position. Example mapping: - x1 -> tokens 1024, chat + vision, thinking=true - x2 -> tokens 2048, chat - x3 -> tokens 1024, vision, thinking=true The existing single-model syntax remains supported. ### 2. Optional embedding dimension Previously, the Go CLI required dimension to be explicitly provided for embed text. Before: embed text 'what is rag' 'who are you' with 'model@test@provider' dimension 8192; Now both forms are supported: embed text 'what is rag' 'who are you' with 'model@test@provider' dimension 8192; embed text 'what is rag' 'who are you' with 'model@test@provider'; When omitted, the CLI leaves dimension unset and relies on provider/backend behavior. ## Tests Added parser tests covering: - Multiple models with multiple config segments - Model type deduplication - Model/config count mismatch - Drop/remove multiple models - Optional embedding dimension parsing
2026-06-05 19:31:06 +08:00
"model_types": modelTypes,
"max_tokens": maxTokens,
}
if supportThink != nil {
model["thinking"] = *supportThink
}
if maxDimension != nil {
model["max_dimension"] = *maxDimension
model["dimensions"] = dimensions
}
feat(go-cli): support batch model add/remove and optional embedding dimension (#15631) ## Summary This PR improves the Go CLI in two areas: 1. It adds batch model management support, allowing multiple models to be added or removed in a single command. 2. It makes the `dimension` argument optional for the `embed text` command. These changes keep the existing single-model and explicit-dimension behaviors compatible while making the CLI more convenient for common workflows. ## What Changed ### 1. Batch model add/remove support The CLI now supports operating on multiple model names provided in a single quoted string. Supported commands include: ``` add model 'x1 x2 x3' to provider 'vllm' instance 'test' with tokens 1024 chat think vision, token 2048 chat, token 1024 think vision; drop model 'x1 x2 x3' from 'vllm' 'test'; remove model 'x1 x2 x3' from 'vllm' 'test'; ``` For add model, each config segment after with is matched to the corresponding model name by position. Example mapping: - x1 -> tokens 1024, chat + vision, thinking=true - x2 -> tokens 2048, chat - x3 -> tokens 1024, vision, thinking=true The existing single-model syntax remains supported. ### 2. Optional embedding dimension Previously, the Go CLI required dimension to be explicitly provided for embed text. Before: embed text 'what is rag' 'who are you' with 'model@test@provider' dimension 8192; Now both forms are supported: embed text 'what is rag' 'who are you' with 'model@test@provider' dimension 8192; embed text 'what is rag' 'who are you' with 'model@test@provider'; When omitted, the CLI leaves dimension unset and relies on provider/backend behavior. ## Tests Added parser tests covering: - Multiple models with multiple config segments - Model type deduplication - Model/config count mismatch - Drop/remove multiple models - Optional embedding dimension parsing
2026-06-05 19:31:06 +08:00
models = append(models, model)
modelIndex++
feat(go-cli): support batch model add/remove and optional embedding dimension (#15631) ## Summary This PR improves the Go CLI in two areas: 1. It adds batch model management support, allowing multiple models to be added or removed in a single command. 2. It makes the `dimension` argument optional for the `embed text` command. These changes keep the existing single-model and explicit-dimension behaviors compatible while making the CLI more convenient for common workflows. ## What Changed ### 1. Batch model add/remove support The CLI now supports operating on multiple model names provided in a single quoted string. Supported commands include: ``` add model 'x1 x2 x3' to provider 'vllm' instance 'test' with tokens 1024 chat think vision, token 2048 chat, token 1024 think vision; drop model 'x1 x2 x3' from 'vllm' 'test'; remove model 'x1 x2 x3' from 'vllm' 'test'; ``` For add model, each config segment after with is matched to the corresponding model name by position. Example mapping: - x1 -> tokens 1024, chat + vision, thinking=true - x2 -> tokens 2048, chat - x3 -> tokens 1024, vision, thinking=true The existing single-model syntax remains supported. ### 2. Optional embedding dimension Previously, the Go CLI required dimension to be explicitly provided for embed text. Before: embed text 'what is rag' 'who are you' with 'model@test@provider' dimension 8192; Now both forms are supported: embed text 'what is rag' 'who are you' with 'model@test@provider' dimension 8192; embed text 'what is rag' 'who are you' with 'model@test@provider'; When omitted, the CLI leaves dimension unset and relies on provider/backend behavior. ## Tests Added parser tests covering: - Multiple models with multiple config segments - Model type deduplication - Model/config count mismatch - Drop/remove multiple models - Optional embedding dimension parsing
2026-06-05 19:31:06 +08:00
modelTypes = nil
supportThink = nil
maxTokens = 0
maxDimension = nil
dimensions = nil
feat(go-cli): support batch model add/remove and optional embedding dimension (#15631) ## Summary This PR improves the Go CLI in two areas: 1. It adds batch model management support, allowing multiple models to be added or removed in a single command. 2. It makes the `dimension` argument optional for the `embed text` command. These changes keep the existing single-model and explicit-dimension behaviors compatible while making the CLI more convenient for common workflows. ## What Changed ### 1. Batch model add/remove support The CLI now supports operating on multiple model names provided in a single quoted string. Supported commands include: ``` add model 'x1 x2 x3' to provider 'vllm' instance 'test' with tokens 1024 chat think vision, token 2048 chat, token 1024 think vision; drop model 'x1 x2 x3' from 'vllm' 'test'; remove model 'x1 x2 x3' from 'vllm' 'test'; ``` For add model, each config segment after with is matched to the corresponding model name by position. Example mapping: - x1 -> tokens 1024, chat + vision, thinking=true - x2 -> tokens 2048, chat - x3 -> tokens 1024, vision, thinking=true The existing single-model syntax remains supported. ### 2. Optional embedding dimension Previously, the Go CLI required dimension to be explicitly provided for embed text. Before: embed text 'what is rag' 'who are you' with 'model@test@provider' dimension 8192; Now both forms are supported: embed text 'what is rag' 'who are you' with 'model@test@provider' dimension 8192; embed text 'what is rag' 'who are you' with 'model@test@provider'; When omitted, the CLI leaves dimension unset and relies on provider/backend behavior. ## Tests Added parser tests covering: - Multiple models with multiple config segments - Model type deduplication - Model/config count mismatch - Drop/remove multiple models - Optional embedding dimension parsing
2026-06-05 19:31:06 +08:00
if p.curToken.Type == TokenComma {
p.nextToken()
continue
}
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
break optionsLoop
feat(go-cli): support batch model add/remove and optional embedding dimension (#15631) ## Summary This PR improves the Go CLI in two areas: 1. It adds batch model management support, allowing multiple models to be added or removed in a single command. 2. It makes the `dimension` argument optional for the `embed text` command. These changes keep the existing single-model and explicit-dimension behaviors compatible while making the CLI more convenient for common workflows. ## What Changed ### 1. Batch model add/remove support The CLI now supports operating on multiple model names provided in a single quoted string. Supported commands include: ``` add model 'x1 x2 x3' to provider 'vllm' instance 'test' with tokens 1024 chat think vision, token 2048 chat, token 1024 think vision; drop model 'x1 x2 x3' from 'vllm' 'test'; remove model 'x1 x2 x3' from 'vllm' 'test'; ``` For add model, each config segment after with is matched to the corresponding model name by position. Example mapping: - x1 -> tokens 1024, chat + vision, thinking=true - x2 -> tokens 2048, chat - x3 -> tokens 1024, vision, thinking=true The existing single-model syntax remains supported. ### 2. Optional embedding dimension Previously, the Go CLI required dimension to be explicitly provided for embed text. Before: embed text 'what is rag' 'who are you' with 'model@test@provider' dimension 8192; Now both forms are supported: embed text 'what is rag' 'who are you' with 'model@test@provider' dimension 8192; embed text 'what is rag' 'who are you' with 'model@test@provider'; When omitted, the CLI leaves dimension unset and relies on provider/backend behavior. ## Tests Added parser tests covering: - Multiple models with multiple config segments - Model type deduplication - Model/config count mismatch - Drop/remove multiple models - Optional embedding dimension parsing
2026-06-05 19:31:06 +08:00
default:
return nil, fmt.Errorf("unexpected token type: %s", p.curToken.Value)
}
feat(go-cli): support batch model add/remove and optional embedding dimension (#15631) ## Summary This PR improves the Go CLI in two areas: 1. It adds batch model management support, allowing multiple models to be added or removed in a single command. 2. It makes the `dimension` argument optional for the `embed text` command. These changes keep the existing single-model and explicit-dimension behaviors compatible while making the CLI more convenient for common workflows. ## What Changed ### 1. Batch model add/remove support The CLI now supports operating on multiple model names provided in a single quoted string. Supported commands include: ``` add model 'x1 x2 x3' to provider 'vllm' instance 'test' with tokens 1024 chat think vision, token 2048 chat, token 1024 think vision; drop model 'x1 x2 x3' from 'vllm' 'test'; remove model 'x1 x2 x3' from 'vllm' 'test'; ``` For add model, each config segment after with is matched to the corresponding model name by position. Example mapping: - x1 -> tokens 1024, chat + vision, thinking=true - x2 -> tokens 2048, chat - x3 -> tokens 1024, vision, thinking=true The existing single-model syntax remains supported. ### 2. Optional embedding dimension Previously, the Go CLI required dimension to be explicitly provided for embed text. Before: embed text 'what is rag' 'who are you' with 'model@test@provider' dimension 8192; Now both forms are supported: embed text 'what is rag' 'who are you' with 'model@test@provider' dimension 8192; embed text 'what is rag' 'who are you' with 'model@test@provider'; When omitted, the CLI leaves dimension unset and relies on provider/backend behavior. ## Tests Added parser tests covering: - Multiple models with multiple config segments - Model type deduplication - Model/config count mismatch - Drop/remove multiple models - Optional embedding dimension parsing
2026-06-05 19:31:06 +08:00
}
if len(models) != len(modelNames) {
return nil, fmt.Errorf("model config count %d does not match model name count %d", len(models), len(modelNames))
}
cmd := NewCommand("api_add_custom_model")
cmd.Params["provider_name"] = providerName
cmd.Params["instance_name"] = instanceName
feat(go-cli): support batch model add/remove and optional embedding dimension (#15631) ## Summary This PR improves the Go CLI in two areas: 1. It adds batch model management support, allowing multiple models to be added or removed in a single command. 2. It makes the `dimension` argument optional for the `embed text` command. These changes keep the existing single-model and explicit-dimension behaviors compatible while making the CLI more convenient for common workflows. ## What Changed ### 1. Batch model add/remove support The CLI now supports operating on multiple model names provided in a single quoted string. Supported commands include: ``` add model 'x1 x2 x3' to provider 'vllm' instance 'test' with tokens 1024 chat think vision, token 2048 chat, token 1024 think vision; drop model 'x1 x2 x3' from 'vllm' 'test'; remove model 'x1 x2 x3' from 'vllm' 'test'; ``` For add model, each config segment after with is matched to the corresponding model name by position. Example mapping: - x1 -> tokens 1024, chat + vision, thinking=true - x2 -> tokens 2048, chat - x3 -> tokens 1024, vision, thinking=true The existing single-model syntax remains supported. ### 2. Optional embedding dimension Previously, the Go CLI required dimension to be explicitly provided for embed text. Before: embed text 'what is rag' 'who are you' with 'model@test@provider' dimension 8192; Now both forms are supported: embed text 'what is rag' 'who are you' with 'model@test@provider' dimension 8192; embed text 'what is rag' 'who are you' with 'model@test@provider'; When omitted, the CLI leaves dimension unset and relies on provider/backend behavior. ## Tests Added parser tests covering: - Multiple models with multiple config segments - Model type deduplication - Model/config count mismatch - Drop/remove multiple models - Optional embedding dimension parsing
2026-06-05 19:31:06 +08:00
cmd.Params["models"] = models
return cmd, nil
}
// syntax: ADD API 'abc' HOST '127.0.0.1:9333';
func (p *Parser) parseAddAPIServer() (*Command, error) {
p.nextToken() // consume API
serverName, err := p.parseQuotedString()
if err != nil {
return nil, err
}
p.nextToken() // consume model name
Go: refactor CLI (#15728) ### What problem does this PR solve? ``` RAGFlow(user)> add api server 'ccc' host '127.0.0.1:9980'; SUCCESS RAGFlow(user)> list api server; +------------+---------------+-----------------+---------+-------------+---------------+ | api_server | api_server_ip | api_server_port | auth | user_name | user_password | +------------+---------------+-----------------+---------+-------------+---------------+ | ccc | 127.0.0.1 | 9980 | no auth | | | | default | 127.0.0.1 | 9384 | login | aaa@aaa.com | *** | +------------+---------------+-----------------+---------+-------------+---------------+ RAGFlow(user)> delete api server 'ccc'; SUCCESS RAGFlow(user)> list api server; +------------+---------------+-----------------+---------+ | api_server | api_server_ip | api_server_port | auth | +------------+---------------+-----------------+---------+ | default | 127.0.0.1 | 9384 | no auth | +------------+---------------+-----------------+---------+ RAGFlow(user)> show admin server; +--------------+-------+ | field | value | +--------------+-------+ | admin_server | N/A | +--------------+-------+ RAGFlow(user)> add admin server host '127.0.0.1:9880'; SUCCESS RAGFlow(user)> show admin server; +-------------------+-----------+ | field | value | +-------------------+-----------+ | admin_server_ip | 127.0.0.1 | | admin_server_port | 9880 | | auth | no auth | +-------------------+-----------+ RAGFlow(user)> delete admin server; SUCCESS RAGFlow(user)> show admin server; +--------------+-------+ | field | value | +--------------+-------+ | admin_server | N/A | +--------------+-------+ RAGFlow(user)> show current +-----------------+-------------+ | field | value | +-----------------+-------------+ | api_server_port | 9384 | | user_name | aaa@aaa.com | | user_password | *** | | mode | api | | verbose | false | | api_server | default | | api_server_ip | 127.0.0.1 | | auth | login | | output | table | | interactive | true | +-----------------+-------------+ ``` ### Type of change - [x] Refactoring --------- Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-06-09 15:22:50 +08:00
if p.curToken.Type != TokenHost {
return nil, fmt.Errorf("expected HOST")
}
p.nextToken()
host, err := p.parseQuotedString()
if err != nil {
return nil, err
}
p.nextToken()
ip, port, err := parseHostPort(host)
if err != nil {
return nil, err
}
cmd := NewCommand("api_add_api_server")
cmd.Params["server_name"] = serverName
Go: refactor CLI (#15728) ### What problem does this PR solve? ``` RAGFlow(user)> add api server 'ccc' host '127.0.0.1:9980'; SUCCESS RAGFlow(user)> list api server; +------------+---------------+-----------------+---------+-------------+---------------+ | api_server | api_server_ip | api_server_port | auth | user_name | user_password | +------------+---------------+-----------------+---------+-------------+---------------+ | ccc | 127.0.0.1 | 9980 | no auth | | | | default | 127.0.0.1 | 9384 | login | aaa@aaa.com | *** | +------------+---------------+-----------------+---------+-------------+---------------+ RAGFlow(user)> delete api server 'ccc'; SUCCESS RAGFlow(user)> list api server; +------------+---------------+-----------------+---------+ | api_server | api_server_ip | api_server_port | auth | +------------+---------------+-----------------+---------+ | default | 127.0.0.1 | 9384 | no auth | +------------+---------------+-----------------+---------+ RAGFlow(user)> show admin server; +--------------+-------+ | field | value | +--------------+-------+ | admin_server | N/A | +--------------+-------+ RAGFlow(user)> add admin server host '127.0.0.1:9880'; SUCCESS RAGFlow(user)> show admin server; +-------------------+-----------+ | field | value | +-------------------+-----------+ | admin_server_ip | 127.0.0.1 | | admin_server_port | 9880 | | auth | no auth | +-------------------+-----------+ RAGFlow(user)> delete admin server; SUCCESS RAGFlow(user)> show admin server; +--------------+-------+ | field | value | +--------------+-------+ | admin_server | N/A | +--------------+-------+ RAGFlow(user)> show current +-----------------+-------------+ | field | value | +-----------------+-------------+ | api_server_port | 9384 | | user_name | aaa@aaa.com | | user_password | *** | | mode | api | | verbose | false | | api_server | default | | api_server_ip | 127.0.0.1 | | auth | login | | output | table | | interactive | true | +-----------------+-------------+ ``` ### Type of change - [x] Refactoring --------- Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-06-09 15:22:50 +08:00
cmd.Params["server_ip"] = ip
cmd.Params["server_port"] = port
return cmd, nil
}
// syntax: add admin host '127.0.0.1:9333' user 'ccc' password 'ppp'
func (p *Parser) parseAddAdminServer() (*Command, error) {
p.nextToken() // consume ADMIN
Go: refactor CLI (#15728) ### What problem does this PR solve? ``` RAGFlow(user)> add api server 'ccc' host '127.0.0.1:9980'; SUCCESS RAGFlow(user)> list api server; +------------+---------------+-----------------+---------+-------------+---------------+ | api_server | api_server_ip | api_server_port | auth | user_name | user_password | +------------+---------------+-----------------+---------+-------------+---------------+ | ccc | 127.0.0.1 | 9980 | no auth | | | | default | 127.0.0.1 | 9384 | login | aaa@aaa.com | *** | +------------+---------------+-----------------+---------+-------------+---------------+ RAGFlow(user)> delete api server 'ccc'; SUCCESS RAGFlow(user)> list api server; +------------+---------------+-----------------+---------+ | api_server | api_server_ip | api_server_port | auth | +------------+---------------+-----------------+---------+ | default | 127.0.0.1 | 9384 | no auth | +------------+---------------+-----------------+---------+ RAGFlow(user)> show admin server; +--------------+-------+ | field | value | +--------------+-------+ | admin_server | N/A | +--------------+-------+ RAGFlow(user)> add admin server host '127.0.0.1:9880'; SUCCESS RAGFlow(user)> show admin server; +-------------------+-----------+ | field | value | +-------------------+-----------+ | admin_server_ip | 127.0.0.1 | | admin_server_port | 9880 | | auth | no auth | +-------------------+-----------+ RAGFlow(user)> delete admin server; SUCCESS RAGFlow(user)> show admin server; +--------------+-------+ | field | value | +--------------+-------+ | admin_server | N/A | +--------------+-------+ RAGFlow(user)> show current +-----------------+-------------+ | field | value | +-----------------+-------------+ | api_server_port | 9384 | | user_name | aaa@aaa.com | | user_password | *** | | mode | api | | verbose | false | | api_server | default | | api_server_ip | 127.0.0.1 | | auth | login | | output | table | | interactive | true | +-----------------+-------------+ ``` ### Type of change - [x] Refactoring --------- Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-06-09 15:22:50 +08:00
if p.curToken.Type != TokenHost {
return nil, fmt.Errorf("expected HOST")
Go: refactor CLI (#15728) ### What problem does this PR solve? ``` RAGFlow(user)> add api server 'ccc' host '127.0.0.1:9980'; SUCCESS RAGFlow(user)> list api server; +------------+---------------+-----------------+---------+-------------+---------------+ | api_server | api_server_ip | api_server_port | auth | user_name | user_password | +------------+---------------+-----------------+---------+-------------+---------------+ | ccc | 127.0.0.1 | 9980 | no auth | | | | default | 127.0.0.1 | 9384 | login | aaa@aaa.com | *** | +------------+---------------+-----------------+---------+-------------+---------------+ RAGFlow(user)> delete api server 'ccc'; SUCCESS RAGFlow(user)> list api server; +------------+---------------+-----------------+---------+ | api_server | api_server_ip | api_server_port | auth | +------------+---------------+-----------------+---------+ | default | 127.0.0.1 | 9384 | no auth | +------------+---------------+-----------------+---------+ RAGFlow(user)> show admin server; +--------------+-------+ | field | value | +--------------+-------+ | admin_server | N/A | +--------------+-------+ RAGFlow(user)> add admin server host '127.0.0.1:9880'; SUCCESS RAGFlow(user)> show admin server; +-------------------+-----------+ | field | value | +-------------------+-----------+ | admin_server_ip | 127.0.0.1 | | admin_server_port | 9880 | | auth | no auth | +-------------------+-----------+ RAGFlow(user)> delete admin server; SUCCESS RAGFlow(user)> show admin server; +--------------+-------+ | field | value | +--------------+-------+ | admin_server | N/A | +--------------+-------+ RAGFlow(user)> show current +-----------------+-------------+ | field | value | +-----------------+-------------+ | api_server_port | 9384 | | user_name | aaa@aaa.com | | user_password | *** | | mode | api | | verbose | false | | api_server | default | | api_server_ip | 127.0.0.1 | | auth | login | | output | table | | interactive | true | +-----------------+-------------+ ``` ### Type of change - [x] Refactoring --------- Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-06-09 15:22:50 +08:00
}
p.nextToken()
host, err := p.parseQuotedString()
if err != nil {
return nil, err
}
p.nextToken()
ip, port, err := parseHostPort(host)
if err != nil {
return nil, err
}
cmd := NewCommand("api_add_admin_server")
Go: refactor CLI (#15728) ### What problem does this PR solve? ``` RAGFlow(user)> add api server 'ccc' host '127.0.0.1:9980'; SUCCESS RAGFlow(user)> list api server; +------------+---------------+-----------------+---------+-------------+---------------+ | api_server | api_server_ip | api_server_port | auth | user_name | user_password | +------------+---------------+-----------------+---------+-------------+---------------+ | ccc | 127.0.0.1 | 9980 | no auth | | | | default | 127.0.0.1 | 9384 | login | aaa@aaa.com | *** | +------------+---------------+-----------------+---------+-------------+---------------+ RAGFlow(user)> delete api server 'ccc'; SUCCESS RAGFlow(user)> list api server; +------------+---------------+-----------------+---------+ | api_server | api_server_ip | api_server_port | auth | +------------+---------------+-----------------+---------+ | default | 127.0.0.1 | 9384 | no auth | +------------+---------------+-----------------+---------+ RAGFlow(user)> show admin server; +--------------+-------+ | field | value | +--------------+-------+ | admin_server | N/A | +--------------+-------+ RAGFlow(user)> add admin server host '127.0.0.1:9880'; SUCCESS RAGFlow(user)> show admin server; +-------------------+-----------+ | field | value | +-------------------+-----------+ | admin_server_ip | 127.0.0.1 | | admin_server_port | 9880 | | auth | no auth | +-------------------+-----------+ RAGFlow(user)> delete admin server; SUCCESS RAGFlow(user)> show admin server; +--------------+-------+ | field | value | +--------------+-------+ | admin_server | N/A | +--------------+-------+ RAGFlow(user)> show current +-----------------+-------------+ | field | value | +-----------------+-------------+ | api_server_port | 9384 | | user_name | aaa@aaa.com | | user_password | *** | | mode | api | | verbose | false | | api_server | default | | api_server_ip | 127.0.0.1 | | auth | login | | output | table | | interactive | true | +-----------------+-------------+ ``` ### Type of change - [x] Refactoring --------- Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-06-09 15:22:50 +08:00
cmd.Params["server_ip"] = ip
cmd.Params["server_port"] = port
return cmd, nil
}
// endregion ADD commands
Go: refactor CLI (#15728) ### What problem does this PR solve? ``` RAGFlow(user)> add api server 'ccc' host '127.0.0.1:9980'; SUCCESS RAGFlow(user)> list api server; +------------+---------------+-----------------+---------+-------------+---------------+ | api_server | api_server_ip | api_server_port | auth | user_name | user_password | +------------+---------------+-----------------+---------+-------------+---------------+ | ccc | 127.0.0.1 | 9980 | no auth | | | | default | 127.0.0.1 | 9384 | login | aaa@aaa.com | *** | +------------+---------------+-----------------+---------+-------------+---------------+ RAGFlow(user)> delete api server 'ccc'; SUCCESS RAGFlow(user)> list api server; +------------+---------------+-----------------+---------+ | api_server | api_server_ip | api_server_port | auth | +------------+---------------+-----------------+---------+ | default | 127.0.0.1 | 9384 | no auth | +------------+---------------+-----------------+---------+ RAGFlow(user)> show admin server; +--------------+-------+ | field | value | +--------------+-------+ | admin_server | N/A | +--------------+-------+ RAGFlow(user)> add admin server host '127.0.0.1:9880'; SUCCESS RAGFlow(user)> show admin server; +-------------------+-----------+ | field | value | +-------------------+-----------+ | admin_server_ip | 127.0.0.1 | | admin_server_port | 9880 | | auth | no auth | +-------------------+-----------+ RAGFlow(user)> delete admin server; SUCCESS RAGFlow(user)> show admin server; +--------------+-------+ | field | value | +--------------+-------+ | admin_server | N/A | +--------------+-------+ RAGFlow(user)> show current +-----------------+-------------+ | field | value | +-----------------+-------------+ | api_server_port | 9384 | | user_name | aaa@aaa.com | | user_password | *** | | mode | api | | verbose | false | | api_server | default | | api_server_ip | 127.0.0.1 | | auth | login | | output | table | | interactive | true | +-----------------+-------------+ ``` ### Type of change - [x] Refactoring --------- Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-06-09 15:22:50 +08:00
// region DELETE commands
Go: refactor CLI (#15728) ### What problem does this PR solve? ``` RAGFlow(user)> add api server 'ccc' host '127.0.0.1:9980'; SUCCESS RAGFlow(user)> list api server; +------------+---------------+-----------------+---------+-------------+---------------+ | api_server | api_server_ip | api_server_port | auth | user_name | user_password | +------------+---------------+-----------------+---------+-------------+---------------+ | ccc | 127.0.0.1 | 9980 | no auth | | | | default | 127.0.0.1 | 9384 | login | aaa@aaa.com | *** | +------------+---------------+-----------------+---------+-------------+---------------+ RAGFlow(user)> delete api server 'ccc'; SUCCESS RAGFlow(user)> list api server; +------------+---------------+-----------------+---------+ | api_server | api_server_ip | api_server_port | auth | +------------+---------------+-----------------+---------+ | default | 127.0.0.1 | 9384 | no auth | +------------+---------------+-----------------+---------+ RAGFlow(user)> show admin server; +--------------+-------+ | field | value | +--------------+-------+ | admin_server | N/A | +--------------+-------+ RAGFlow(user)> add admin server host '127.0.0.1:9880'; SUCCESS RAGFlow(user)> show admin server; +-------------------+-----------+ | field | value | +-------------------+-----------+ | admin_server_ip | 127.0.0.1 | | admin_server_port | 9880 | | auth | no auth | +-------------------+-----------+ RAGFlow(user)> delete admin server; SUCCESS RAGFlow(user)> show admin server; +--------------+-------+ | field | value | +--------------+-------+ | admin_server | N/A | +--------------+-------+ RAGFlow(user)> show current +-----------------+-------------+ | field | value | +-----------------+-------------+ | api_server_port | 9384 | | user_name | aaa@aaa.com | | user_password | *** | | mode | api | | verbose | false | | api_server | default | | api_server_ip | 127.0.0.1 | | auth | login | | output | table | | interactive | true | +-----------------+-------------+ ``` ### Type of change - [x] Refactoring --------- Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-06-09 15:22:50 +08:00
func (p *Parser) parseAPIDeleteCommands() (*Command, error) {
p.nextToken() // consume DELETE
Go: refactor CLI (#15728) ### What problem does this PR solve? ``` RAGFlow(user)> add api server 'ccc' host '127.0.0.1:9980'; SUCCESS RAGFlow(user)> list api server; +------------+---------------+-----------------+---------+-------------+---------------+ | api_server | api_server_ip | api_server_port | auth | user_name | user_password | +------------+---------------+-----------------+---------+-------------+---------------+ | ccc | 127.0.0.1 | 9980 | no auth | | | | default | 127.0.0.1 | 9384 | login | aaa@aaa.com | *** | +------------+---------------+-----------------+---------+-------------+---------------+ RAGFlow(user)> delete api server 'ccc'; SUCCESS RAGFlow(user)> list api server; +------------+---------------+-----------------+---------+ | api_server | api_server_ip | api_server_port | auth | +------------+---------------+-----------------+---------+ | default | 127.0.0.1 | 9384 | no auth | +------------+---------------+-----------------+---------+ RAGFlow(user)> show admin server; +--------------+-------+ | field | value | +--------------+-------+ | admin_server | N/A | +--------------+-------+ RAGFlow(user)> add admin server host '127.0.0.1:9880'; SUCCESS RAGFlow(user)> show admin server; +-------------------+-----------+ | field | value | +-------------------+-----------+ | admin_server_ip | 127.0.0.1 | | admin_server_port | 9880 | | auth | no auth | +-------------------+-----------+ RAGFlow(user)> delete admin server; SUCCESS RAGFlow(user)> show admin server; +--------------+-------+ | field | value | +--------------+-------+ | admin_server | N/A | +--------------+-------+ RAGFlow(user)> show current +-----------------+-------------+ | field | value | +-----------------+-------------+ | api_server_port | 9384 | | user_name | aaa@aaa.com | | user_password | *** | | mode | api | | verbose | false | | api_server | default | | api_server_ip | 127.0.0.1 | | auth | login | | output | table | | interactive | true | +-----------------+-------------+ ``` ### Type of change - [x] Refactoring --------- Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-06-09 15:22:50 +08:00
switch p.curToken.Type {
case TokenProvider:
return p.parseAPIDeleteProvider()
case TokenMetadata:
return p.parseDevDeleteMeta()
case TokenAdmin:
return p.parseAPIDeleteAdminServer()
case TokenAPI:
return p.parseAPIDeleteAPIServer()
default:
return nil, fmt.Errorf("unknown DELETE target: %s", p.curToken.Value)
Go: refactor CLI (#15728) ### What problem does this PR solve? ``` RAGFlow(user)> add api server 'ccc' host '127.0.0.1:9980'; SUCCESS RAGFlow(user)> list api server; +------------+---------------+-----------------+---------+-------------+---------------+ | api_server | api_server_ip | api_server_port | auth | user_name | user_password | +------------+---------------+-----------------+---------+-------------+---------------+ | ccc | 127.0.0.1 | 9980 | no auth | | | | default | 127.0.0.1 | 9384 | login | aaa@aaa.com | *** | +------------+---------------+-----------------+---------+-------------+---------------+ RAGFlow(user)> delete api server 'ccc'; SUCCESS RAGFlow(user)> list api server; +------------+---------------+-----------------+---------+ | api_server | api_server_ip | api_server_port | auth | +------------+---------------+-----------------+---------+ | default | 127.0.0.1 | 9384 | no auth | +------------+---------------+-----------------+---------+ RAGFlow(user)> show admin server; +--------------+-------+ | field | value | +--------------+-------+ | admin_server | N/A | +--------------+-------+ RAGFlow(user)> add admin server host '127.0.0.1:9880'; SUCCESS RAGFlow(user)> show admin server; +-------------------+-----------+ | field | value | +-------------------+-----------+ | admin_server_ip | 127.0.0.1 | | admin_server_port | 9880 | | auth | no auth | +-------------------+-----------+ RAGFlow(user)> delete admin server; SUCCESS RAGFlow(user)> show admin server; +--------------+-------+ | field | value | +--------------+-------+ | admin_server | N/A | +--------------+-------+ RAGFlow(user)> show current +-----------------+-------------+ | field | value | +-----------------+-------------+ | api_server_port | 9384 | | user_name | aaa@aaa.com | | user_password | *** | | mode | api | | verbose | false | | api_server | default | | api_server_ip | 127.0.0.1 | | auth | login | | output | table | | interactive | true | +-----------------+-------------+ ``` ### Type of change - [x] Refactoring --------- Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-06-09 15:22:50 +08:00
}
}
// DELETE PROVIDER <name>
// DELETE PROVIDER <name> INSTANCE <instance_name>
func (p *Parser) parseAPIDeleteProvider() (*Command, error) {
p.nextToken() // consume PROVIDER
providerName, err := p.parseQuotedString()
if err != nil {
return nil, fmt.Errorf("expected provider name: %w", err)
}
p.nextToken()
switch p.curToken.Type {
case TokenInstance:
return p.parseAPIDeleteProviderInstance(providerName)
case TokenEOF, TokenSemicolon:
p.nextToken()
default:
return nil, fmt.Errorf("unknown DELETE target: %s", p.curToken.Value)
}
cmd := NewCommand("api_delete_provider")
cmd.Params["provider_name"] = providerName
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
func (p *Parser) parseAPIDeleteProviderInstance(providerName string) (*Command, error) {
p.nextToken() // consume INSTANCE
instanceName, err := p.parseQuotedString()
if err != nil {
return nil, fmt.Errorf("expected instance name: %w", err)
}
p.nextToken()
cmd := NewCommand("api_delete_provider_instance")
cmd.Params["provider_name"] = providerName
cmd.Params["instance_name"] = instanceName
switch p.curToken.Type {
case TokenModels:
return p.parseAPIDeleteProviderInstanceModels(providerName, instanceName)
case TokenEOF, TokenSemicolon:
p.nextToken()
default:
return nil, fmt.Errorf("unknown DELETE target: %s", p.curToken.Value)
}
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
// DELETE PROVIDER <name> INSTANCE <instance_name> MODELS <name1 name2 name3>
// Only works for local deployed model
func (p *Parser) parseAPIDeleteProviderInstanceModels(providerName, instanceName string) (*Command, error) {
p.nextToken() // consume MODEL
rawModels, err := p.parseQuotedString()
if err != nil {
return nil, fmt.Errorf("expected model name: %w", err)
}
p.nextToken()
modelNames := strings.Split(rawModels, " ")
cmd := NewCommand("api_delete_provider_instance_model")
cmd.Params["instance_name"] = instanceName
cmd.Params["provider_name"] = providerName
cmd.Params["model_names"] = modelNames
p.nextToken()
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
// syntax: delete api 'abc'
func (p *Parser) parseAPIDeleteAPIServer() (*Command, error) {
p.nextToken() // consume API
serverName, err := p.parseQuotedString()
if err != nil {
return nil, err
}
p.nextToken() // consume model name
cmd := NewCommand("api_delete_api_server")
cmd.Params["server_name"] = serverName
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
// syntax: delete admin server 'abc'
func (p *Parser) parseAPIDeleteAdminServer() (*Command, error) {
p.nextToken() // consume ADMIN
cmd := NewCommand("api_delete_admin_server")
2026-06-25 20:36:50 +08:00
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
// endregion ADD commands
// region ALTER commands
func (p *Parser) parseAPIAlterCommands() (*Command, error) {
p.nextToken() // consume ALTER
switch p.curToken.Type {
case TokenProvider:
return p.parseAPIAlterInstance()
default:
return nil, fmt.Errorf("unknown ALTER target: %s", p.curToken.Value)
}
}
// ALTER PROVIDER <name> INSTANCE <name> [NAME <new_name> | KEY <new_key>]
func (p *Parser) parseAPIAlterInstance() (*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)
}
p.nextToken()
2026-06-23 16:57:05 +08:00
newInstanceName := ""
2026-06-23 16:57:05 +08:00
newAPIKey := ""
optionsLoop:
for {
switch p.curToken.Type {
case TokenName:
p.nextToken()
newInstanceName, err = p.parseQuotedString()
2026-06-23 16:57:05 +08:00
if err != nil {
return nil, fmt.Errorf("expected model name: %w", err)
}
p.nextToken()
case TokenKey:
p.nextToken()
newAPIKey, err = p.parseQuotedString()
if err != nil {
return nil, fmt.Errorf("expected API key: %w", err)
}
p.nextToken()
default:
break optionsLoop
}
}
if newInstanceName == "" && newAPIKey == "" {
2026-06-23 16:57:05 +08:00
return nil, fmt.Errorf("expected NAME or KEY after INSTANCE")
}
cmd := NewCommand("api_alter_provider_instance")
cmd.Params["provider_name"] = providerName
2026-06-23 16:57:05 +08:00
cmd.Params["instance_name"] = instanceName
cmd.Params["new_instance_name"] = newInstanceName
2026-06-23 16:57:05 +08:00
cmd.Params["new_api_key"] = newAPIKey
p.nextToken()
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
func (p *Parser) parseIdentifierList() ([]string, error) {
var list []string
ident, err := p.parseIdentifier()
if err != nil {
return nil, err
}
list = append(list, ident)
p.nextToken()
for p.curToken.Type == TokenComma {
p.nextToken()
ident, err = p.parseIdentifier()
if err != nil {
return nil, err
}
list = append(list, ident)
p.nextToken()
}
return list, nil
}
// endregion ALTER commands
// region SET commands
func (p *Parser) parseAPISetCommands() (*Command, error) {
p.nextToken() // consume SET
switch p.curToken.Type {
case TokenVar:
2026-06-25 20:36:50 +08:00
return p.parseAPISetVariable()
case TokenDefault:
return p.parseAPISetDefault()
case TokenKey:
return p.parseAPISetAPIKey()
case TokenLog:
2026-06-25 20:36:50 +08:00
return p.parseAPISetLog()
case TokenMetadata:
return p.parseDevSetMeta()
default:
return nil, fmt.Errorf("unknown SET target: %s", p.curToken.Value)
}
}
2026-06-25 20:36:50 +08:00
func (p *Parser) parseAPISetVariable() (*Command, error) {
p.nextToken() // consume VAR
2026-06-25 20:36:50 +08:00
varName, err := p.parseQuotedString()
if err != nil {
return nil, err
}
p.nextToken()
2026-06-25 20:36:50 +08:00
varValue, err := p.parseVariableValue()
if err != nil {
return nil, err
}
2026-06-25 20:36:50 +08:00
p.nextToken()
2026-06-25 20:36:50 +08:00
cmd := NewCommand("api_set_variable")
cmd.Params["var_name"] = varName
cmd.Params["var_value"] = varValue
2026-06-25 20:36:50 +08:00
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
func (p *Parser) parseAPISetDefault() (*Command, error) {
p.nextToken() // consume DEFAULT
var modelType, modelNameOrID string
var err error
switch p.curToken.Type {
case TokenChat:
modelType = "chat"
case TokenVision:
modelType = "vision"
case TokenEmbedding:
modelType = "embedding"
case TokenRerank:
modelType = "rerank"
case TokenASR:
modelType = "asr"
case TokenTTS:
modelType = "tts"
case TokenOCR:
modelType = "ocr"
default:
return nil, fmt.Errorf("unknown model type: %s", p.curToken.Value)
}
p.nextToken() // pass model type
if p.curToken.Type != TokenModel {
return nil, fmt.Errorf("expected MODEL")
}
p.nextToken() // pass MODEL
// Format: 'model@instance@provider' or just 'message'
if p.curToken.Type != TokenQuotedString {
return nil, fmt.Errorf("expected quoted string with format model@instance@provider")
}
modelNameOrID, err = p.parseQuotedString()
if err != nil {
return nil, err
}
p.nextToken()
cmd := NewCommand("api_set_default_model")
cmd.Params["model_type"] = modelType
if common.IsCompositeModelName(modelNameOrID) {
cmd.Params["composite_model_name"] = modelNameOrID
} else if common.IsUUID(modelNameOrID) {
cmd.Params["model_id"] = modelNameOrID
} else {
return nil, fmt.Errorf("invalid format of model name or ID: %s", modelNameOrID)
}
p.nextToken()
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
func (p *Parser) parseAPISetAPIKey() (*Command, error) {
p.nextToken() // consume KEY
apiKey, err := p.parseQuotedString()
if err != nil {
return nil, err
}
p.nextToken()
cmd := NewCommand("api_set_api_key")
cmd.Params["api_key"] = apiKey
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
2026-06-25 20:36:50 +08:00
func (p *Parser) parseAPISetLog() (*Command, error) {
p.nextToken() // consume LOG
switch p.curToken.Type {
case TokenLevel:
2026-06-25 20:36:50 +08:00
return p.parseAPISetLogLevel()
default:
return nil, fmt.Errorf("unknown log target: %s", p.curToken.Value)
}
}
2026-06-25 20:36:50 +08:00
func (p *Parser) parseAPISetLogLevel() (*Command, error) {
p.nextToken() // consume LEVEL
2026-06-25 20:36:50 +08:00
cmd := NewCommand("api_set_log_level")
switch p.curToken.Type {
case TokenDebug:
cmd.Params["level"] = "debug"
case TokenInfo:
cmd.Params["level"] = "info"
case TokenWarn:
cmd.Params["level"] = "warn"
case TokenError:
cmd.Params["level"] = "error"
case TokenFatal:
cmd.Params["level"] = "fatal"
case TokenPanic:
cmd.Params["level"] = "panic"
default:
2026-06-25 20:36:50 +08:00
return nil, fmt.Errorf("unknown log level: %s", p.curToken.Value)
}
p.nextToken()
2026-06-25 20:36:50 +08:00
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
// endregion SET commands
// region RESET commands
2026-06-25 20:36:50 +08:00
func (p *Parser) parseAPIResetCommands() (*Command, error) {
p.nextToken() // consume RESET
2026-06-25 20:36:50 +08:00
switch p.curToken.Type {
case TokenDefault:
return p.parseAPIResetDefaultModel()
case TokenKey:
return p.parseAPIResetKey()
default:
return nil, fmt.Errorf("unknown RESET target: %s", p.curToken.Value)
}
2026-06-25 20:36:50 +08:00
}
func (p *Parser) parseAPIResetDefaultModel() (*Command, error) {
p.nextToken() // consume DEFAULT
var modelType string
switch p.curToken.Type {
case TokenChat:
modelType = "chat"
case TokenVision:
modelType = "vision"
case TokenEmbedding:
modelType = "embedding"
case TokenRerank:
modelType = "rerank"
case TokenASR:
modelType = "asr"
case TokenTTS:
modelType = "tts"
case TokenOCR:
modelType = "ocr"
default:
return nil, fmt.Errorf("unknown model type: %s", p.curToken.Value)
}
cmd := NewCommand("reset_default_model")
cmd.Params["model_type"] = modelType
p.nextToken()
if p.curToken.Type != TokenModel {
return nil, fmt.Errorf("expected MODEL")
}
p.nextToken() // pass MODEL
2026-06-25 20:36:50 +08:00
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
2026-06-25 20:36:50 +08:00
func (p *Parser) parseAPIResetKey() (*Command, error) {
p.nextToken()
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return NewCommand("api_unset_api_key"), nil
}
// endregion RESET commands
func (p *Parser) parseAPIImport() (*Command, error) {
p.nextToken() // consume IMPORT
documentPaths, err := p.parseQuotedString()
if err != nil {
return nil, err
}
p.nextToken()
if p.curToken.Type != TokenInto {
return nil, fmt.Errorf("expected INTO")
}
p.nextToken()
if p.curToken.Type != TokenDataset {
return nil, fmt.Errorf("expected DATASET")
}
p.nextToken()
datasetName, err := p.parseQuotedString()
if err != nil {
return nil, err
}
cmd := NewCommand("import_docs_into_dataset")
cmd.Params["document_paths"] = documentPaths
cmd.Params["dataset_name"] = datasetName
p.nextToken()
2026-06-25 20:36:50 +08:00
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
func (p *Parser) parseAPIRetrieve() (*Command, error) {
p.nextToken() // consume SEARCH
// Handle help flag: -h / --help. The lexer tokenizes each leading
// `-` as a separate `TokenDash` and then the rest of the flag name
// (e.g. "help") as a `TokenIdentifier`. We collect any leading
// dashes before checking the identifier value. Short-circuit with
// a dedicated command type so the dispatcher can print the search
// usage instead of erroring out on the missing question.
if p.curToken.Type == TokenDash {
dashCount := 0
for p.curToken.Type == TokenDash {
dashCount++
p.nextToken()
}
if dashCount > 0 && p.curToken.Type == TokenIdentifier {
switch strings.ToLower(p.curToken.Value) {
case "h", "help":
return NewCommand("search_help"), nil
}
}
return nil, fmt.Errorf("expected quoted string or identifier")
}
var err error
var question string
if p.curToken.Type == TokenQuotedString {
question, err = p.parseQuotedString()
if err != nil {
return nil, err
}
} else if p.curToken.Type == TokenIdentifier {
question, err = p.parseIdentifier()
if err != nil {
return nil, err
}
} else {
return nil, fmt.Errorf("expected quoted string or identifier")
}
p.nextToken()
if p.curToken.Type == TokenOn {
p.nextToken() // skip on
if p.curToken.Type != TokenDatasets {
return nil, fmt.Errorf("expected DATASETS")
}
p.nextToken()
datasets, err := p.parseQuotedString()
if err != nil {
return nil, err
}
cmd := NewCommand("search_on_datasets")
cmd.Params["question"] = question
cmd.Params["datasets"] = datasets
p.nextToken()
// Parse optional WITH clause for additional parameters
if p.curToken.Type == TokenWith || (p.curToken.Type == TokenIdentifier && strings.ToLower(p.curToken.Value) == "with") {
if p.curToken.Type == TokenWith {
p.nextToken()
} else {
p.nextToken() // skip "with" identifier
}
for p.curToken.Type != TokenEOF && p.curToken.Type != TokenSemicolon {
if p.curToken.Type == TokenComma {
return nil, fmt.Errorf("syntax error: WITH options must be space-separated, not comma-separated")
}
// Parse parameter name
if p.curToken.Type != TokenIdentifier {
break
}
paramName := strings.ToLower(p.curToken.Value)
p.nextToken()
// Parse parameter value
var paramValue interface{}
valueToken := p.curToken.Type
var valueErr error
switch p.curToken.Type {
case TokenInteger:
paramValue, valueErr = p.parseNumber()
if valueErr != nil {
return nil, valueErr
}
p.nextToken() // step past the integer
case TokenFloat:
paramValue, valueErr = p.parseFloat()
if valueErr != nil {
return nil, valueErr
}
p.nextToken() // step past the float
case TokenQuotedString:
paramValue, valueErr = p.parseQuotedString()
if valueErr != nil {
return nil, valueErr
}
p.nextToken() // step past the closing quote
case TokenIdentifier:
// Bare identifiers are only meaningful for the
// boolean keys (keyword / use_kg = true|false);
// everything else rejects them.
paramValue = p.curToken.Value
p.nextToken()
case TokenLBracket:
// List value: parsed inside the switch below by
// cross_languages / doc_ids. No value is captured here.
paramValue = nil
default:
// EOF, ';', or any other non-value token: the option
// is missing a value, which is a hard error rather
// than a silent drop.
return nil, fmt.Errorf("WITH option %q is missing a value", paramName)
}
switch paramName {
case "top_k", "page_size", "page":
if valueToken != TokenInteger {
return nil, fmt.Errorf("WITH option %q must be an integer, got %s", paramName, tokenTypeDescription(valueToken, p.curToken))
}
cmd.Params[paramName] = paramValue
case "similarity_threshold", "vector_similarity_weight":
switch n := paramValue.(type) {
case int:
cmd.Params[paramName] = float64(n)
case float64:
cmd.Params[paramName] = n
default:
return nil, fmt.Errorf("WITH option %q must be a number, got %s", paramName, tokenTypeDescription(valueToken, p.curToken))
}
case "keyword", "use_kg":
s, ok := paramValue.(string)
if !ok {
return nil, fmt.Errorf("WITH option %q must be true or false, got %s", paramName, tokenTypeDescription(valueToken, p.curToken))
}
switch strings.ToLower(s) {
case "true":
cmd.Params[paramName] = true
case "false":
cmd.Params[paramName] = false
default:
return nil, fmt.Errorf("WITH option %q must be true or false, got %q", paramName, s)
}
case "rerank_id", "tenant_rerank_id", "search_id", "meta_data_filter":
if valueToken != TokenQuotedString {
return nil, fmt.Errorf("WITH option %q must be a quoted string, got %s", paramName, tokenTypeDescription(valueToken, p.curToken))
}
// meta_data_filter JSON string is decoded into a map in
// the SearchOnDatasets handler; parser stores the raw
// string so the handler can surface a clean error on
// invalid JSON.
cmd.Params[paramName] = paramValue
case "cross_languages", "doc_ids":
if p.curToken.Type != TokenLBracket {
return nil, fmt.Errorf("WITH option %q must be a list, e.g. %q ['a', 'b']", paramName, paramName)
}
list, err := p.parseQuotedStringList()
if err != nil {
return nil, err
}
cmd.Params[paramName] = list
default:
return nil, fmt.Errorf("unknown WITH option %q", paramName)
}
}
}
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
cmd := NewCommand("ce_search")
cmd.Params["query"] = question
if p.curToken.Type == TokenEOF {
cmd.Params["path"] = "."
return cmd, nil
}
for p.curToken.Type != TokenEOF {
if p.curToken.Type == TokenDash {
p.nextToken() // skip dash
if p.curToken.Type != TokenIdentifier {
return nil, fmt.Errorf("expect identifier")
}
if strings.ToLower(p.curToken.Value) == "n" {
p.nextToken()
var err error
if p.curToken.Type != TokenInteger {
return nil, fmt.Errorf("expect number")
}
cmd.Params["number"], err = p.parseNumber()
if err != nil {
return nil, err
}
p.nextToken()
continue
}
//if strings.ToLower(p.curToken.Value) == "t" {
// p.nextToken()
// var err error
// if p.curToken.Type != TokenInteger {
// return nil, fmt.Errorf("expect number")
// }
// cmd.Params["threshold"], err = p.parseFloat()
// if err != nil {
// return nil, err
// }
// p.nextToken()
// continue
//}
return nil, fmt.Errorf("unknow parameter: %s", p.curToken.Value)
} else if p.curToken.Type == TokenIdentifier {
if cmd.Params["path"] == nil {
cmd.Params["path"] = p.curToken.Value
} else {
cmd.Params["path"] = fmt.Sprintf("%s%s", cmd.Params["path"], p.curToken.Value)
}
p.nextToken() // skip path
continue
} else if p.curToken.Type == TokenSlash {
if cmd.Params["path"] == nil {
cmd.Params["path"] = "/"
} else {
cmd.Params["path"] = fmt.Sprintf("%s/", cmd.Params["path"])
}
p.nextToken() // skip slash
if p.curToken.Type == TokenIdentifier {
cmd.Params["path"] = fmt.Sprintf("%s%s", cmd.Params["path"], p.curToken.Value)
p.nextToken()
}
continue
} else if p.curToken.Type == TokenIllegal && p.curToken.Value == "." {
if cmd.Params["path"] == nil {
cmd.Params["path"] = "."
} else {
cmd.Params["path"] = fmt.Sprintf("%s.", cmd.Params["path"])
}
p.nextToken()
continue
} else {
return nil, fmt.Errorf("unexpected token %q in search path", p.curToken.Value)
}
}
return cmd, nil
}
// region ParseCommands
func (p *Parser) parseParseCommands() (*Command, error) {
p.nextToken() // consume PARSE
switch p.curToken.Type {
case TokenDataset:
return p.parseAPIParseDataset()
case TokenWith:
return p.parseAPIModelParse()
case TokenDocument:
return p.parseAPIParseDocs()
case TokenFile:
return p.parseAPIParseLocalFile()
default:
return nil, fmt.Errorf("expected DATASET, WITH, or DOCUMENT")
}
}
func (p *Parser) parseAPIParseDataset() (*Command, error) {
p.nextToken() // consume DATASET
datasetName, err := p.parseQuotedString()
if err != nil {
return nil, err
}
p.nextToken()
var method string
if p.curToken.Type == TokenSync {
method = "sync"
} else if p.curToken.Type == TokenAsync {
method = "async"
} else {
return nil, fmt.Errorf("expected SYNC or ASYNC")
}
p.nextToken()
cmd := NewCommand("parse_dataset")
cmd.Params["dataset_name"] = datasetName
cmd.Params["method"] = method
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
func (p *Parser) parseAPIModelParse() (*Command, error) {
p.nextToken() // consume WITH
modelNameOrID, err := p.parseQuotedString()
if err != nil {
return nil, err
}
p.nextToken()
cmd := NewCommand("api_model_parse_file")
switch p.curToken.Type {
case TokenFile:
p.nextToken()
var file string
file, err = p.parseQuotedString()
if err != nil {
return nil, err
}
cmd.Params["file"] = file
p.nextToken()
case TokenURL:
p.nextToken()
var url string
url, err = p.parseQuotedString()
if err != nil {
return nil, err
}
cmd.Params["url"] = url
p.nextToken()
default:
return nil, fmt.Errorf("expected FILE or URL")
}
if common.IsCompositeModelName(modelNameOrID) {
cmd.Params["composite_model_name"] = modelNameOrID
} else if common.IsUUID(modelNameOrID) {
cmd.Params["model_id"] = modelNameOrID
} else {
return nil, fmt.Errorf("invalid format of model name or ID: %s", modelNameOrID)
}
2026-06-25 20:36:50 +08:00
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
func (p *Parser) parseAPIParseDocs() (*Command, error) {
p.nextToken() // consume document
documentsStr, err := p.parseQuotedString()
if err != nil {
return nil, err
}
p.nextToken()
if p.curToken.Type != TokenFrom {
return nil, fmt.Errorf("expected FROM")
}
p.nextToken()
datasetID, err := p.parseQuotedString()
if err != nil {
return nil, err
}
p.nextToken()
cmd := NewCommand("api_parse_documents")
documents := strings.Split(documentsStr, " ")
cmd.Params["documents"] = documents
cmd.Params["dataset_id"] = datasetID
2026-06-25 20:36:50 +08:00
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
func (p *Parser) parseAPIParseLocalFile() (*Command, error) {
p.nextToken() // consume FILE
filename, err := p.parseQuotedString()
if err != nil {
return nil, err
}
p.nextToken()
cmd := NewCommand("user_parse_local_file_command")
cmd.Params["filename"] = filename
optionsLoop:
for {
switch p.curToken.Type {
case TokenVision:
p.nextToken()
var visionModel string
visionModel, err = p.parseQuotedString()
if err != nil {
return nil, err
}
cmd.Params["vision_model"] = visionModel
p.nextToken()
case TokenASR:
p.nextToken()
var asrModel string
asrModel, err = p.parseQuotedString()
if err != nil {
return nil, err
}
cmd.Params["asr_model"] = asrModel
p.nextToken()
case TokenOCR:
p.nextToken()
var ocrModel string
ocrModel, err = p.parseQuotedString()
if err != nil {
return nil, err
}
cmd.Params["ocr_model"] = ocrModel
p.nextToken()
case TokenChat:
p.nextToken()
var chatModel string
chatModel, err = p.parseQuotedString()
if err != nil {
return nil, err
}
cmd.Params["chat_model"] = chatModel
p.nextToken()
case TokenEmbed:
p.nextToken()
var embedModel string
embedModel, err = p.parseQuotedString()
if err != nil {
return nil, err
}
cmd.Params["embedding_model"] = embedModel
p.nextToken()
case TokenDocParse:
p.nextToken()
var docParseModel string
docParseModel, err = p.parseQuotedString()
if err != nil {
return nil, err
}
cmd.Params["doc_parse_model"] = docParseModel
p.nextToken()
case TokenSemicolon:
p.nextToken()
break optionsLoop // done
default:
// No more options to process
break optionsLoop
}
}
return cmd, nil
}
// endregion PARSE commands
func (p *Parser) parseBenchmarkCommand() (*Command, error) {
cmd := NewCommand("benchmark")
p.nextToken() // consume BENCHMARK
concurrency, err := p.parseNumber()
if err != nil {
return nil, err
}
cmd.Params["concurrency"] = concurrency
p.nextToken()
iterations, err := p.parseNumber()
if err != nil {
return nil, err
}
cmd.Params["iterations"] = iterations
p.nextToken()
// Parse user_statement
nestedCmd, err := p.parseUserStatement() // Not only user statement
if err != nil {
return nil, err
}
cmd.Params["command"] = nestedCmd
return cmd, nil
}
func (p *Parser) parseUserStatement() (*Command, error) {
switch p.curToken.Type {
case TokenPing:
return p.parseAPIPingServer()
case TokenShow:
return p.parseAPIShowCommands()
case TokenList:
return p.parseAPIListCommands()
case TokenImport:
return p.parseAPIImport()
case TokenInsert:
return p.parseDevInsertCommand()
case TokenRetrieve:
return p.parseAPIRetrieve()
default:
return nil, fmt.Errorf("invalid user statement: %s", p.curToken.Value)
}
}
func (p *Parser) parseAPIEnable() (*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
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) parseAPIDisable() (*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
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
}
// region MODEL commands
// CHAT 'model@instance@provider' 'hello world'
// CHAT WITH 'model@instance@provider' MESSAGE 'hello world' 'who are you' IMAGE 'url1' 'file0' VIDEO "url2.mov" "file1" FILE "url" "path file2" AUDIO "file.wav"
func (p *Parser) parseAPIChat() (*Command, error) {
p.nextToken() // consume CHAT
var err error
var modelNameOrID string = ""
Go: CLI chat with text, image, video (#14573) ### What problem does this PR solve? ``` RAGFlow(user)> chat with 'glm-4.6v-flash@test@zhipu-ai' message 'What are the pics talk about?' image 'https://cdn.bigmodel.cn/static/logo/register.png' 'https://cdn.bigmodel.cn/static/logo/api-key.png' Answer: The first picture shows a login/register modal with options for phone number login, account login, and WeChat QR code login, along with a prompt for new users to get a 20 million tokens experience package. The second picture displays the API keys management page of a platform, including a warning about API key security and a table listing existing API keys with details like creation time and usage history. Time: 31.600545 RAGFlow(user)> chat with 'glm-4.6v-flash@test@zhipu-ai' message 'What are the video talk about?' video 'https://cdn.bigmodel.cn/agent-demos/lark/113123.mov' Answer: Based on the sequence of frames provided, the video is a demonstration of a web search and navigation process. 1. The video starts with a blank Google search page. 2. The user types "智谱" (which is the Chinese name for the company Zhipu AI) into the search box. 3. The search is initiated and the page shows "About 0 results". 4. The search results load, showing information about Zhipu AI, including its website. 5. The user clicks on the main website link (www.zhipuai.cn). 6. The video ends by showing the homepage of Zhipu AI's website, titled "Z.ai GLM Large Model Open Platform". In summary, the video is about searching for the company "智谱" (Zhipu AI) on Google and then navigating to its official website. Time: 76.582520 ``` ### Type of change - [x] New Feature (non-breaking change which adds functionality) Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-05-05 18:14:39 +08:00
var messages []string
var images []string
var videos []string
var audios []string
var files []string
effort := "default"
verbosity := "low"
Go: CLI chat with text, image, video (#14573) ### What problem does this PR solve? ``` RAGFlow(user)> chat with 'glm-4.6v-flash@test@zhipu-ai' message 'What are the pics talk about?' image 'https://cdn.bigmodel.cn/static/logo/register.png' 'https://cdn.bigmodel.cn/static/logo/api-key.png' Answer: The first picture shows a login/register modal with options for phone number login, account login, and WeChat QR code login, along with a prompt for new users to get a 20 million tokens experience package. The second picture displays the API keys management page of a platform, including a warning about API key security and a table listing existing API keys with details like creation time and usage history. Time: 31.600545 RAGFlow(user)> chat with 'glm-4.6v-flash@test@zhipu-ai' message 'What are the video talk about?' video 'https://cdn.bigmodel.cn/agent-demos/lark/113123.mov' Answer: Based on the sequence of frames provided, the video is a demonstration of a web search and navigation process. 1. The video starts with a blank Google search page. 2. The user types "智谱" (which is the Chinese name for the company Zhipu AI) into the search box. 3. The search is initiated and the page shows "About 0 results". 4. The search results load, showing information about Zhipu AI, including its website. 5. The user clicks on the main website link (www.zhipuai.cn). 6. The video ends by showing the homepage of Zhipu AI's website, titled "Z.ai GLM Large Model Open Platform". In summary, the video is about searching for the company "智谱" (Zhipu AI) on Google and then navigating to its official website. Time: 76.582520 ``` ### Type of change - [x] New Feature (non-breaking change which adds functionality) Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-05-05 18:14:39 +08:00
optionsLoop:
for {
switch p.curToken.Type {
case TokenWith:
p.nextToken()
// 'model@instance@provider' or model ID
if modelNameOrID != "" {
return nil, fmt.Errorf("model name or ID is already set")
Go: CLI chat with text, image, video (#14573) ### What problem does this PR solve? ``` RAGFlow(user)> chat with 'glm-4.6v-flash@test@zhipu-ai' message 'What are the pics talk about?' image 'https://cdn.bigmodel.cn/static/logo/register.png' 'https://cdn.bigmodel.cn/static/logo/api-key.png' Answer: The first picture shows a login/register modal with options for phone number login, account login, and WeChat QR code login, along with a prompt for new users to get a 20 million tokens experience package. The second picture displays the API keys management page of a platform, including a warning about API key security and a table listing existing API keys with details like creation time and usage history. Time: 31.600545 RAGFlow(user)> chat with 'glm-4.6v-flash@test@zhipu-ai' message 'What are the video talk about?' video 'https://cdn.bigmodel.cn/agent-demos/lark/113123.mov' Answer: Based on the sequence of frames provided, the video is a demonstration of a web search and navigation process. 1. The video starts with a blank Google search page. 2. The user types "智谱" (which is the Chinese name for the company Zhipu AI) into the search box. 3. The search is initiated and the page shows "About 0 results". 4. The search results load, showing information about Zhipu AI, including its website. 5. The user clicks on the main website link (www.zhipuai.cn). 6. The video ends by showing the homepage of Zhipu AI's website, titled "Z.ai GLM Large Model Open Platform". In summary, the video is about searching for the company "智谱" (Zhipu AI) on Google and then navigating to its official website. Time: 76.582520 ``` ### Type of change - [x] New Feature (non-breaking change which adds functionality) Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-05-05 18:14:39 +08:00
}
modelNameOrID, err = p.parseQuotedString()
Go: CLI chat with text, image, video (#14573) ### What problem does this PR solve? ``` RAGFlow(user)> chat with 'glm-4.6v-flash@test@zhipu-ai' message 'What are the pics talk about?' image 'https://cdn.bigmodel.cn/static/logo/register.png' 'https://cdn.bigmodel.cn/static/logo/api-key.png' Answer: The first picture shows a login/register modal with options for phone number login, account login, and WeChat QR code login, along with a prompt for new users to get a 20 million tokens experience package. The second picture displays the API keys management page of a platform, including a warning about API key security and a table listing existing API keys with details like creation time and usage history. Time: 31.600545 RAGFlow(user)> chat with 'glm-4.6v-flash@test@zhipu-ai' message 'What are the video talk about?' video 'https://cdn.bigmodel.cn/agent-demos/lark/113123.mov' Answer: Based on the sequence of frames provided, the video is a demonstration of a web search and navigation process. 1. The video starts with a blank Google search page. 2. The user types "智谱" (which is the Chinese name for the company Zhipu AI) into the search box. 3. The search is initiated and the page shows "About 0 results". 4. The search results load, showing information about Zhipu AI, including its website. 5. The user clicks on the main website link (www.zhipuai.cn). 6. The video ends by showing the homepage of Zhipu AI's website, titled "Z.ai GLM Large Model Open Platform". In summary, the video is about searching for the company "智谱" (Zhipu AI) on Google and then navigating to its official website. Time: 76.582520 ``` ### Type of change - [x] New Feature (non-breaking change which adds functionality) Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-05-05 18:14:39 +08:00
if err != nil {
return nil, err
}
p.nextToken()
Go: CLI chat with text, image, video (#14573) ### What problem does this PR solve? ``` RAGFlow(user)> chat with 'glm-4.6v-flash@test@zhipu-ai' message 'What are the pics talk about?' image 'https://cdn.bigmodel.cn/static/logo/register.png' 'https://cdn.bigmodel.cn/static/logo/api-key.png' Answer: The first picture shows a login/register modal with options for phone number login, account login, and WeChat QR code login, along with a prompt for new users to get a 20 million tokens experience package. The second picture displays the API keys management page of a platform, including a warning about API key security and a table listing existing API keys with details like creation time and usage history. Time: 31.600545 RAGFlow(user)> chat with 'glm-4.6v-flash@test@zhipu-ai' message 'What are the video talk about?' video 'https://cdn.bigmodel.cn/agent-demos/lark/113123.mov' Answer: Based on the sequence of frames provided, the video is a demonstration of a web search and navigation process. 1. The video starts with a blank Google search page. 2. The user types "智谱" (which is the Chinese name for the company Zhipu AI) into the search box. 3. The search is initiated and the page shows "About 0 results". 4. The search results load, showing information about Zhipu AI, including its website. 5. The user clicks on the main website link (www.zhipuai.cn). 6. The video ends by showing the homepage of Zhipu AI's website, titled "Z.ai GLM Large Model Open Platform". In summary, the video is about searching for the company "智谱" (Zhipu AI) on Google and then navigating to its official website. Time: 76.582520 ``` ### Type of change - [x] New Feature (non-breaking change which adds functionality) Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-05-05 18:14:39 +08:00
case TokenMessage:
p.nextToken()
Go: CLI chat with text, image, video (#14573) ### What problem does this PR solve? ``` RAGFlow(user)> chat with 'glm-4.6v-flash@test@zhipu-ai' message 'What are the pics talk about?' image 'https://cdn.bigmodel.cn/static/logo/register.png' 'https://cdn.bigmodel.cn/static/logo/api-key.png' Answer: The first picture shows a login/register modal with options for phone number login, account login, and WeChat QR code login, along with a prompt for new users to get a 20 million tokens experience package. The second picture displays the API keys management page of a platform, including a warning about API key security and a table listing existing API keys with details like creation time and usage history. Time: 31.600545 RAGFlow(user)> chat with 'glm-4.6v-flash@test@zhipu-ai' message 'What are the video talk about?' video 'https://cdn.bigmodel.cn/agent-demos/lark/113123.mov' Answer: Based on the sequence of frames provided, the video is a demonstration of a web search and navigation process. 1. The video starts with a blank Google search page. 2. The user types "智谱" (which is the Chinese name for the company Zhipu AI) into the search box. 3. The search is initiated and the page shows "About 0 results". 4. The search results load, showing information about Zhipu AI, including its website. 5. The user clicks on the main website link (www.zhipuai.cn). 6. The video ends by showing the homepage of Zhipu AI's website, titled "Z.ai GLM Large Model Open Platform". In summary, the video is about searching for the company "智谱" (Zhipu AI) on Google and then navigating to its official website. Time: 76.582520 ``` ### Type of change - [x] New Feature (non-breaking change which adds functionality) Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-05-05 18:14:39 +08:00
if len(messages) != 0 {
return nil, fmt.Errorf("message is already set")
}
messageLoop:
for {
if p.curToken.Type != TokenQuotedString {
break messageLoop
}
Go: CLI chat with text, image, video (#14573) ### What problem does this PR solve? ``` RAGFlow(user)> chat with 'glm-4.6v-flash@test@zhipu-ai' message 'What are the pics talk about?' image 'https://cdn.bigmodel.cn/static/logo/register.png' 'https://cdn.bigmodel.cn/static/logo/api-key.png' Answer: The first picture shows a login/register modal with options for phone number login, account login, and WeChat QR code login, along with a prompt for new users to get a 20 million tokens experience package. The second picture displays the API keys management page of a platform, including a warning about API key security and a table listing existing API keys with details like creation time and usage history. Time: 31.600545 RAGFlow(user)> chat with 'glm-4.6v-flash@test@zhipu-ai' message 'What are the video talk about?' video 'https://cdn.bigmodel.cn/agent-demos/lark/113123.mov' Answer: Based on the sequence of frames provided, the video is a demonstration of a web search and navigation process. 1. The video starts with a blank Google search page. 2. The user types "智谱" (which is the Chinese name for the company Zhipu AI) into the search box. 3. The search is initiated and the page shows "About 0 results". 4. The search results load, showing information about Zhipu AI, including its website. 5. The user clicks on the main website link (www.zhipuai.cn). 6. The video ends by showing the homepage of Zhipu AI's website, titled "Z.ai GLM Large Model Open Platform". In summary, the video is about searching for the company "智谱" (Zhipu AI) on Google and then navigating to its official website. Time: 76.582520 ``` ### Type of change - [x] New Feature (non-breaking change which adds functionality) Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-05-05 18:14:39 +08:00
var message string
message, err = p.parseQuotedString()
if err != nil {
return nil, err
}
message = strings.TrimSpace(message)
messages = append(messages, message)
p.nextToken()
}
Go: CLI chat with text, image, video (#14573) ### What problem does this PR solve? ``` RAGFlow(user)> chat with 'glm-4.6v-flash@test@zhipu-ai' message 'What are the pics talk about?' image 'https://cdn.bigmodel.cn/static/logo/register.png' 'https://cdn.bigmodel.cn/static/logo/api-key.png' Answer: The first picture shows a login/register modal with options for phone number login, account login, and WeChat QR code login, along with a prompt for new users to get a 20 million tokens experience package. The second picture displays the API keys management page of a platform, including a warning about API key security and a table listing existing API keys with details like creation time and usage history. Time: 31.600545 RAGFlow(user)> chat with 'glm-4.6v-flash@test@zhipu-ai' message 'What are the video talk about?' video 'https://cdn.bigmodel.cn/agent-demos/lark/113123.mov' Answer: Based on the sequence of frames provided, the video is a demonstration of a web search and navigation process. 1. The video starts with a blank Google search page. 2. The user types "智谱" (which is the Chinese name for the company Zhipu AI) into the search box. 3. The search is initiated and the page shows "About 0 results". 4. The search results load, showing information about Zhipu AI, including its website. 5. The user clicks on the main website link (www.zhipuai.cn). 6. The video ends by showing the homepage of Zhipu AI's website, titled "Z.ai GLM Large Model Open Platform". In summary, the video is about searching for the company "智谱" (Zhipu AI) on Google and then navigating to its official website. Time: 76.582520 ``` ### Type of change - [x] New Feature (non-breaking change which adds functionality) Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-05-05 18:14:39 +08:00
case TokenImage:
p.nextToken()
if len(images) != 0 {
return nil, fmt.Errorf("image is already set")
}
imageLoop:
for {
if p.curToken.Type != TokenQuotedString {
break imageLoop
}
var image string
image, err = p.parseQuotedString()
if err != nil {
return nil, err
}
images = append(images, image)
p.nextToken()
}
case TokenVideo:
p.nextToken()
if len(videos) != 0 {
return nil, fmt.Errorf("video is already set")
}
videoLoop:
for {
if p.curToken.Type != TokenQuotedString {
break videoLoop
}
Go: CLI chat with text, image, video (#14573) ### What problem does this PR solve? ``` RAGFlow(user)> chat with 'glm-4.6v-flash@test@zhipu-ai' message 'What are the pics talk about?' image 'https://cdn.bigmodel.cn/static/logo/register.png' 'https://cdn.bigmodel.cn/static/logo/api-key.png' Answer: The first picture shows a login/register modal with options for phone number login, account login, and WeChat QR code login, along with a prompt for new users to get a 20 million tokens experience package. The second picture displays the API keys management page of a platform, including a warning about API key security and a table listing existing API keys with details like creation time and usage history. Time: 31.600545 RAGFlow(user)> chat with 'glm-4.6v-flash@test@zhipu-ai' message 'What are the video talk about?' video 'https://cdn.bigmodel.cn/agent-demos/lark/113123.mov' Answer: Based on the sequence of frames provided, the video is a demonstration of a web search and navigation process. 1. The video starts with a blank Google search page. 2. The user types "智谱" (which is the Chinese name for the company Zhipu AI) into the search box. 3. The search is initiated and the page shows "About 0 results". 4. The search results load, showing information about Zhipu AI, including its website. 5. The user clicks on the main website link (www.zhipuai.cn). 6. The video ends by showing the homepage of Zhipu AI's website, titled "Z.ai GLM Large Model Open Platform". In summary, the video is about searching for the company "智谱" (Zhipu AI) on Google and then navigating to its official website. Time: 76.582520 ``` ### Type of change - [x] New Feature (non-breaking change which adds functionality) Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-05-05 18:14:39 +08:00
var video string
video, err = p.parseQuotedString()
if err != nil {
return nil, err
}
videos = append(videos, video)
p.nextToken()
}
case TokenAudio:
p.nextToken()
if len(audios) != 0 {
return nil, fmt.Errorf("video is already set")
}
audioLoop:
for {
if p.curToken.Type != TokenQuotedString {
break audioLoop
}
var audio string
audio, err = p.parseQuotedString()
if err != nil {
return nil, err
}
audios = append(audios, audio)
p.nextToken()
}
case TokenFile:
p.nextToken()
if len(files) != 0 {
return nil, fmt.Errorf("video is already set")
}
fileLoop:
for {
if p.curToken.Type != TokenQuotedString {
break fileLoop
}
var file string
file, err = p.parseQuotedString()
if err != nil {
return nil, err
}
files = append(files, file)
p.nextToken()
}
Go: CLI chat with text, image, video (#14573) ### What problem does this PR solve? ``` RAGFlow(user)> chat with 'glm-4.6v-flash@test@zhipu-ai' message 'What are the pics talk about?' image 'https://cdn.bigmodel.cn/static/logo/register.png' 'https://cdn.bigmodel.cn/static/logo/api-key.png' Answer: The first picture shows a login/register modal with options for phone number login, account login, and WeChat QR code login, along with a prompt for new users to get a 20 million tokens experience package. The second picture displays the API keys management page of a platform, including a warning about API key security and a table listing existing API keys with details like creation time and usage history. Time: 31.600545 RAGFlow(user)> chat with 'glm-4.6v-flash@test@zhipu-ai' message 'What are the video talk about?' video 'https://cdn.bigmodel.cn/agent-demos/lark/113123.mov' Answer: Based on the sequence of frames provided, the video is a demonstration of a web search and navigation process. 1. The video starts with a blank Google search page. 2. The user types "智谱" (which is the Chinese name for the company Zhipu AI) into the search box. 3. The search is initiated and the page shows "About 0 results". 4. The search results load, showing information about Zhipu AI, including its website. 5. The user clicks on the main website link (www.zhipuai.cn). 6. The video ends by showing the homepage of Zhipu AI's website, titled "Z.ai GLM Large Model Open Platform". In summary, the video is about searching for the company "智谱" (Zhipu AI) on Google and then navigating to its official website. Time: 76.582520 ``` ### Type of change - [x] New Feature (non-breaking change which adds functionality) Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-05-05 18:14:39 +08:00
case TokenEffort:
p.nextToken() // pass Effort
switch p.curToken.Type {
case TokenNone:
effort = "none"
case TokenMinimal:
effort = "minimal"
case TokenLow:
effort = "low"
case TokenMedium:
effort = "medium"
case TokenHigh:
effort = "high"
case TokenMax:
effort = "max"
default:
return nil, fmt.Errorf("invalid effort level")
}
p.nextToken()
break optionsLoop
case TokenVerbosity:
p.nextToken() // pass VERBOSITY
switch p.curToken.Type {
case TokenLow:
verbosity = "low"
case TokenMedium:
verbosity = "median"
case TokenHigh:
verbosity = "high"
default:
return nil, fmt.Errorf("invalid verbosity level")
}
p.nextToken()
break optionsLoop
case TokenSemicolon:
p.nextToken()
break optionsLoop // done
default:
Go: CLI chat with text, image, video (#14573) ### What problem does this PR solve? ``` RAGFlow(user)> chat with 'glm-4.6v-flash@test@zhipu-ai' message 'What are the pics talk about?' image 'https://cdn.bigmodel.cn/static/logo/register.png' 'https://cdn.bigmodel.cn/static/logo/api-key.png' Answer: The first picture shows a login/register modal with options for phone number login, account login, and WeChat QR code login, along with a prompt for new users to get a 20 million tokens experience package. The second picture displays the API keys management page of a platform, including a warning about API key security and a table listing existing API keys with details like creation time and usage history. Time: 31.600545 RAGFlow(user)> chat with 'glm-4.6v-flash@test@zhipu-ai' message 'What are the video talk about?' video 'https://cdn.bigmodel.cn/agent-demos/lark/113123.mov' Answer: Based on the sequence of frames provided, the video is a demonstration of a web search and navigation process. 1. The video starts with a blank Google search page. 2. The user types "智谱" (which is the Chinese name for the company Zhipu AI) into the search box. 3. The search is initiated and the page shows "About 0 results". 4. The search results load, showing information about Zhipu AI, including its website. 5. The user clicks on the main website link (www.zhipuai.cn). 6. The video ends by showing the homepage of Zhipu AI's website, titled "Z.ai GLM Large Model Open Platform". In summary, the video is about searching for the company "智谱" (Zhipu AI) on Google and then navigating to its official website. Time: 76.582520 ``` ### Type of change - [x] New Feature (non-breaking change which adds functionality) Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-05-05 18:14:39 +08:00
// No more options to process
break optionsLoop
}
}
cmd := NewCommand("api_chat_to_model")
if modelNameOrID != "" {
if common.IsCompositeModelName(modelNameOrID) {
cmd.Params["composite_model_name"] = modelNameOrID
} else if common.IsUUID(modelNameOrID) {
cmd.Params["model_id"] = modelNameOrID
} else {
return nil, fmt.Errorf("invalid format of model name or ID: %s", modelNameOrID)
}
}
Go: CLI chat with text, image, video (#14573) ### What problem does this PR solve? ``` RAGFlow(user)> chat with 'glm-4.6v-flash@test@zhipu-ai' message 'What are the pics talk about?' image 'https://cdn.bigmodel.cn/static/logo/register.png' 'https://cdn.bigmodel.cn/static/logo/api-key.png' Answer: The first picture shows a login/register modal with options for phone number login, account login, and WeChat QR code login, along with a prompt for new users to get a 20 million tokens experience package. The second picture displays the API keys management page of a platform, including a warning about API key security and a table listing existing API keys with details like creation time and usage history. Time: 31.600545 RAGFlow(user)> chat with 'glm-4.6v-flash@test@zhipu-ai' message 'What are the video talk about?' video 'https://cdn.bigmodel.cn/agent-demos/lark/113123.mov' Answer: Based on the sequence of frames provided, the video is a demonstration of a web search and navigation process. 1. The video starts with a blank Google search page. 2. The user types "智谱" (which is the Chinese name for the company Zhipu AI) into the search box. 3. The search is initiated and the page shows "About 0 results". 4. The search results load, showing information about Zhipu AI, including its website. 5. The user clicks on the main website link (www.zhipuai.cn). 6. The video ends by showing the homepage of Zhipu AI's website, titled "Z.ai GLM Large Model Open Platform". In summary, the video is about searching for the company "智谱" (Zhipu AI) on Google and then navigating to its official website. Time: 76.582520 ``` ### Type of change - [x] New Feature (non-breaking change which adds functionality) Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-05-05 18:14:39 +08:00
cmd.Params["messages"] = messages
cmd.Params["images"] = images
cmd.Params["videos"] = videos
cmd.Params["audios"] = audios
cmd.Params["files"] = files
cmd.Params["thinking"] = false
cmd.Params["stream"] = false
cmd.Params["effort"] = effort
cmd.Params["verbosity"] = verbosity
return cmd, nil
}
func (p *Parser) parseAPIThink() (*Command, error) {
p.nextToken() // consume THINK
if p.curToken.Type != TokenChat {
return nil, fmt.Errorf("expected CHAT after THINK")
}
command, err := p.parseAPIChat()
if err != nil {
return nil, err
}
command.Params["thinking"] = true
return command, nil
}
func (p *Parser) parseAPIStream() (*Command, error) {
p.nextToken() // consume STREAM
var command *Command
var err error
switch p.curToken.Type {
case TokenChat:
command, err = p.parseAPIChat()
if err != nil {
return nil, err
}
case TokenThink:
command, err = p.parseAPIThink()
if err != nil {
return nil, err
}
case TokenASR:
command, err = p.parseAPIASR()
if err != nil {
return nil, err
}
case TokenTTS:
command, err = p.parseAPITTS()
if err != nil {
return nil, err
}
default:
return nil, fmt.Errorf("expected CHAT, THINK, ASR, or TTS after STREAM")
}
command.Params["stream"] = true
return command, nil
}
func (p *Parser) parseAPIEmbed() (*Command, error) {
p.nextToken() // consume EMBED
if p.curToken.Type != TokenText {
return nil, fmt.Errorf("expected WITH after EMBED")
}
p.nextToken() // consume TEXT
var texts []string
textLoop:
for {
if p.curToken.Type != TokenQuotedString {
break textLoop
}
text, err := p.parseQuotedString()
if err != nil {
return nil, err
}
text = strings.TrimSpace(text)
texts = append(texts, text)
p.nextToken()
}
if p.curToken.Type != TokenWith {
return nil, fmt.Errorf("expected WITH after EMBED")
}
p.nextToken() // consume WITH
modelNameOrID, err := p.parseQuotedString()
if err != nil {
return nil, err
}
p.nextToken()
feat(go-cli): support batch model add/remove and optional embedding dimension (#15631) ## Summary This PR improves the Go CLI in two areas: 1. It adds batch model management support, allowing multiple models to be added or removed in a single command. 2. It makes the `dimension` argument optional for the `embed text` command. These changes keep the existing single-model and explicit-dimension behaviors compatible while making the CLI more convenient for common workflows. ## What Changed ### 1. Batch model add/remove support The CLI now supports operating on multiple model names provided in a single quoted string. Supported commands include: ``` add model 'x1 x2 x3' to provider 'vllm' instance 'test' with tokens 1024 chat think vision, token 2048 chat, token 1024 think vision; drop model 'x1 x2 x3' from 'vllm' 'test'; remove model 'x1 x2 x3' from 'vllm' 'test'; ``` For add model, each config segment after with is matched to the corresponding model name by position. Example mapping: - x1 -> tokens 1024, chat + vision, thinking=true - x2 -> tokens 2048, chat - x3 -> tokens 1024, vision, thinking=true The existing single-model syntax remains supported. ### 2. Optional embedding dimension Previously, the Go CLI required dimension to be explicitly provided for embed text. Before: embed text 'what is rag' 'who are you' with 'model@test@provider' dimension 8192; Now both forms are supported: embed text 'what is rag' 'who are you' with 'model@test@provider' dimension 8192; embed text 'what is rag' 'who are you' with 'model@test@provider'; When omitted, the CLI leaves dimension unset and relies on provider/backend behavior. ## Tests Added parser tests covering: - Multiple models with multiple config segments - Model type deduplication - Model/config count mismatch - Drop/remove multiple models - Optional embedding dimension parsing
2026-06-05 19:31:06 +08:00
dimension := 0
if p.curToken.Type == TokenDimension {
p.nextToken() // consume DIMENSION
if p.curToken.Type != TokenInteger {
return nil, fmt.Errorf("expected integer after DIMENSION")
}
var err error
dimension, err = p.parseNumber()
if err != nil {
return nil, err
}
p.nextToken()
}
feat(go-cli): support batch model add/remove and optional embedding dimension (#15631) ## Summary This PR improves the Go CLI in two areas: 1. It adds batch model management support, allowing multiple models to be added or removed in a single command. 2. It makes the `dimension` argument optional for the `embed text` command. These changes keep the existing single-model and explicit-dimension behaviors compatible while making the CLI more convenient for common workflows. ## What Changed ### 1. Batch model add/remove support The CLI now supports operating on multiple model names provided in a single quoted string. Supported commands include: ``` add model 'x1 x2 x3' to provider 'vllm' instance 'test' with tokens 1024 chat think vision, token 2048 chat, token 1024 think vision; drop model 'x1 x2 x3' from 'vllm' 'test'; remove model 'x1 x2 x3' from 'vllm' 'test'; ``` For add model, each config segment after with is matched to the corresponding model name by position. Example mapping: - x1 -> tokens 1024, chat + vision, thinking=true - x2 -> tokens 2048, chat - x3 -> tokens 1024, vision, thinking=true The existing single-model syntax remains supported. ### 2. Optional embedding dimension Previously, the Go CLI required dimension to be explicitly provided for embed text. Before: embed text 'what is rag' 'who are you' with 'model@test@provider' dimension 8192; Now both forms are supported: embed text 'what is rag' 'who are you' with 'model@test@provider' dimension 8192; embed text 'what is rag' 'who are you' with 'model@test@provider'; When omitted, the CLI leaves dimension unset and relies on provider/backend behavior. ## Tests Added parser tests covering: - Multiple models with multiple config segments - Model type deduplication - Model/config count mismatch - Drop/remove multiple models - Optional embedding dimension parsing
2026-06-05 19:31:06 +08:00
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
if p.curToken.Type != TokenEOF {
return nil, fmt.Errorf("unexpected token after embed command: %s", p.curToken.Value)
}
cmd := NewCommand("api_embed_user_text")
if common.IsCompositeModelName(modelNameOrID) {
cmd.Params["composite_model_name"] = modelNameOrID
} else if common.IsUUID(modelNameOrID) {
cmd.Params["model_id"] = modelNameOrID
} else {
return nil, fmt.Errorf("invalid format of model name or ID: %s", modelNameOrID)
}
cmd.Params["texts"] = texts
feat(go-cli): support batch model add/remove and optional embedding dimension (#15631) ## Summary This PR improves the Go CLI in two areas: 1. It adds batch model management support, allowing multiple models to be added or removed in a single command. 2. It makes the `dimension` argument optional for the `embed text` command. These changes keep the existing single-model and explicit-dimension behaviors compatible while making the CLI more convenient for common workflows. ## What Changed ### 1. Batch model add/remove support The CLI now supports operating on multiple model names provided in a single quoted string. Supported commands include: ``` add model 'x1 x2 x3' to provider 'vllm' instance 'test' with tokens 1024 chat think vision, token 2048 chat, token 1024 think vision; drop model 'x1 x2 x3' from 'vllm' 'test'; remove model 'x1 x2 x3' from 'vllm' 'test'; ``` For add model, each config segment after with is matched to the corresponding model name by position. Example mapping: - x1 -> tokens 1024, chat + vision, thinking=true - x2 -> tokens 2048, chat - x3 -> tokens 1024, vision, thinking=true The existing single-model syntax remains supported. ### 2. Optional embedding dimension Previously, the Go CLI required dimension to be explicitly provided for embed text. Before: embed text 'what is rag' 'who are you' with 'model@test@provider' dimension 8192; Now both forms are supported: embed text 'what is rag' 'who are you' with 'model@test@provider' dimension 8192; embed text 'what is rag' 'who are you' with 'model@test@provider'; When omitted, the CLI leaves dimension unset and relies on provider/backend behavior. ## Tests Added parser tests covering: - Multiple models with multiple config segments - Model type deduplication - Model/config count mismatch - Drop/remove multiple models - Optional embedding dimension parsing
2026-06-05 19:31:06 +08:00
if dimension > 0 {
cmd.Params["dimension"] = dimension
}
return cmd, nil
}
func (p *Parser) parseAPIRerank() (*Command, error) {
p.nextToken() // consume RERANK
if p.curToken.Type != TokenQuery {
return nil, fmt.Errorf("expected WITH after EMBED")
}
p.nextToken() // consume QUERY
query, err := p.parseQuotedString()
if err != nil {
return nil, err
}
query = strings.TrimSpace(query)
p.nextToken() // consume query
if p.curToken.Type != TokenDocument {
return nil, fmt.Errorf("expected DOCUMENT after query")
}
p.nextToken() // consume DOCUMENT
var documents []string
documentLoop:
for {
if p.curToken.Type != TokenQuotedString {
break documentLoop
}
var document string
document, err = p.parseQuotedString()
if err != nil {
return nil, err
}
document = strings.TrimSpace(document)
documents = append(documents, document)
p.nextToken()
}
if p.curToken.Type != TokenWith {
return nil, fmt.Errorf("expected WITH after EMBED")
}
p.nextToken() // consume WITH
modelNameOrID, err := p.parseQuotedString()
if err != nil {
return nil, err
}
p.nextToken()
if p.curToken.Type != TokenTop {
return nil, fmt.Errorf("expected TOP after model")
}
p.nextToken()
topN, err := p.parseNumber()
if err != nil {
return nil, err
}
p.nextToken()
cmd := NewCommand("api_rarank_user_document")
if common.IsCompositeModelName(modelNameOrID) {
cmd.Params["composite_model_name"] = modelNameOrID
} else if common.IsUUID(modelNameOrID) {
cmd.Params["model_id"] = modelNameOrID
} else {
return nil, fmt.Errorf("invalid format of model name or ID: %s", modelNameOrID)
}
cmd.Params["query"] = query
cmd.Params["documents"] = documents
cmd.Params["top_n"] = topN
return cmd, nil
}
func (p *Parser) parseAPIASR() (*Command, error) {
p.nextToken() // consume ASR
if p.curToken.Type != TokenWith {
return nil, fmt.Errorf("expected WITH after ASR")
}
p.nextToken() // consume WITH
modelNameOrID, err := p.parseQuotedString()
if err != nil {
return nil, err
}
p.nextToken()
if p.curToken.Type != TokenAudio {
return nil, fmt.Errorf("expected AUDIO to ASR")
}
Go: implement TTS for fishaudio, openrouter and asr for fishaudio (#14926) ### What problem does this PR solve? This PR implement TTS for FishAudio and MiniMax provider and ASR for FishAudio **The following functionalities are now supported:** **FishAudio:** - [x] Text To Speech - [x] Stream Text To Speech - [x] Audio To Text **OpenRouter:** - [x] Text To Speech **Verified examples from the CLI:** ```plaintext **FishAudio** RAGFlow(user)> tts with 's1@test@fishaudio' text 'He who desires but acts not, breeds pestilence.' play format 'wav' save './internal' param '{"reference_id": "90e65eaaf50e4470b8e6d43ee6afd7d5", "temperature": 0.7, "top_p": 0.7, "prosody": {"speed": 1, "volume": 0, "normalize_loudness": true}, "chunk_length": 300, "normalize": true, "sample_rate": 44100, "mp3_bitrate": 128, "latency": "normal", "max_new_tokens": 1024, "repetition_penalty": 1.2, "min_chunk_length": 50, "condition_on_previous_chunks": true, "early_stop_threshold": 1}' Saved to directory: /home/infiniflow/Documents/development/ragflow/internal/s1_output.wav SUCCESS RAGFlow(user)> stream tts with 's1@test@fishaudio' text 'He who desires but acts not, breeds pestilence.' play format 'wav' save './internal' param '{"reference_id": "90e65eaaf50e4470b8e6d43ee6afd7d5", "temperature": 0.7, "top_p": 0.7, "prosody": {"speed": 1, "volume": 0, "normalize_loudness": true}, "chunk_length": 300, "normalize": true, "sample_rate": 44100, "mp3_bitrate": 128, "latency": "normal", "max_new_tokens": 1024, "repetition_penalty": 1.2, "min_chunk_length": 50, "condition_on_previous_chunks": true, "early_stop_threshold": 1}' Saved to directory: /home/infiniflow/Documents/development/ragflow/internal/s1_output.wav SUCCESS RAGFlow(user)> asr with 'transcribe-1@test@fishaudio' audio './internal/test.wav' param '{"language": "en", "ignore_timestamps": true}' +----------------------------------------------------------------------------------------------------------------------+ | text | +----------------------------------------------------------------------------------------------------------------------+ | The examination and testimony of the experts enabled the commission to conclude that five shots may have been fired. | +----------------------------------------------------------------------------------------------------------------------+ ``` ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) - [x] New Feature (non-breaking change which adds functionality) - [x] Refactoring
2026-05-14 18:58:00 +08:00
p.nextToken() // consume AUDIO
audioFile, err := p.parseQuotedString()
if err != nil {
return nil, err
}
p.nextToken()
Go: implement TTS for fishaudio, openrouter and asr for fishaudio (#14926) ### What problem does this PR solve? This PR implement TTS for FishAudio and MiniMax provider and ASR for FishAudio **The following functionalities are now supported:** **FishAudio:** - [x] Text To Speech - [x] Stream Text To Speech - [x] Audio To Text **OpenRouter:** - [x] Text To Speech **Verified examples from the CLI:** ```plaintext **FishAudio** RAGFlow(user)> tts with 's1@test@fishaudio' text 'He who desires but acts not, breeds pestilence.' play format 'wav' save './internal' param '{"reference_id": "90e65eaaf50e4470b8e6d43ee6afd7d5", "temperature": 0.7, "top_p": 0.7, "prosody": {"speed": 1, "volume": 0, "normalize_loudness": true}, "chunk_length": 300, "normalize": true, "sample_rate": 44100, "mp3_bitrate": 128, "latency": "normal", "max_new_tokens": 1024, "repetition_penalty": 1.2, "min_chunk_length": 50, "condition_on_previous_chunks": true, "early_stop_threshold": 1}' Saved to directory: /home/infiniflow/Documents/development/ragflow/internal/s1_output.wav SUCCESS RAGFlow(user)> stream tts with 's1@test@fishaudio' text 'He who desires but acts not, breeds pestilence.' play format 'wav' save './internal' param '{"reference_id": "90e65eaaf50e4470b8e6d43ee6afd7d5", "temperature": 0.7, "top_p": 0.7, "prosody": {"speed": 1, "volume": 0, "normalize_loudness": true}, "chunk_length": 300, "normalize": true, "sample_rate": 44100, "mp3_bitrate": 128, "latency": "normal", "max_new_tokens": 1024, "repetition_penalty": 1.2, "min_chunk_length": 50, "condition_on_previous_chunks": true, "early_stop_threshold": 1}' Saved to directory: /home/infiniflow/Documents/development/ragflow/internal/s1_output.wav SUCCESS RAGFlow(user)> asr with 'transcribe-1@test@fishaudio' audio './internal/test.wav' param '{"language": "en", "ignore_timestamps": true}' +----------------------------------------------------------------------------------------------------------------------+ | text | +----------------------------------------------------------------------------------------------------------------------+ | The examination and testimony of the experts enabled the commission to conclude that five shots may have been fired. | +----------------------------------------------------------------------------------------------------------------------+ ``` ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) - [x] New Feature (non-breaking change which adds functionality) - [x] Refactoring
2026-05-14 18:58:00 +08:00
cmd := NewCommand("asr_user_command")
if common.IsCompositeModelName(modelNameOrID) {
cmd.Params["composite_model_name"] = modelNameOrID
} else if common.IsUUID(modelNameOrID) {
cmd.Params["model_id"] = modelNameOrID
} else {
return nil, fmt.Errorf("invalid format of model name or ID: %s", modelNameOrID)
}
Go: implement TTS for fishaudio, openrouter and asr for fishaudio (#14926) ### What problem does this PR solve? This PR implement TTS for FishAudio and MiniMax provider and ASR for FishAudio **The following functionalities are now supported:** **FishAudio:** - [x] Text To Speech - [x] Stream Text To Speech - [x] Audio To Text **OpenRouter:** - [x] Text To Speech **Verified examples from the CLI:** ```plaintext **FishAudio** RAGFlow(user)> tts with 's1@test@fishaudio' text 'He who desires but acts not, breeds pestilence.' play format 'wav' save './internal' param '{"reference_id": "90e65eaaf50e4470b8e6d43ee6afd7d5", "temperature": 0.7, "top_p": 0.7, "prosody": {"speed": 1, "volume": 0, "normalize_loudness": true}, "chunk_length": 300, "normalize": true, "sample_rate": 44100, "mp3_bitrate": 128, "latency": "normal", "max_new_tokens": 1024, "repetition_penalty": 1.2, "min_chunk_length": 50, "condition_on_previous_chunks": true, "early_stop_threshold": 1}' Saved to directory: /home/infiniflow/Documents/development/ragflow/internal/s1_output.wav SUCCESS RAGFlow(user)> stream tts with 's1@test@fishaudio' text 'He who desires but acts not, breeds pestilence.' play format 'wav' save './internal' param '{"reference_id": "90e65eaaf50e4470b8e6d43ee6afd7d5", "temperature": 0.7, "top_p": 0.7, "prosody": {"speed": 1, "volume": 0, "normalize_loudness": true}, "chunk_length": 300, "normalize": true, "sample_rate": 44100, "mp3_bitrate": 128, "latency": "normal", "max_new_tokens": 1024, "repetition_penalty": 1.2, "min_chunk_length": 50, "condition_on_previous_chunks": true, "early_stop_threshold": 1}' Saved to directory: /home/infiniflow/Documents/development/ragflow/internal/s1_output.wav SUCCESS RAGFlow(user)> asr with 'transcribe-1@test@fishaudio' audio './internal/test.wav' param '{"language": "en", "ignore_timestamps": true}' +----------------------------------------------------------------------------------------------------------------------+ | text | +----------------------------------------------------------------------------------------------------------------------+ | The examination and testimony of the experts enabled the commission to conclude that five shots may have been fired. | +----------------------------------------------------------------------------------------------------------------------+ ``` ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) - [x] New Feature (non-breaking change which adds functionality) - [x] Refactoring
2026-05-14 18:58:00 +08:00
cmd.Params["audio_file"] = audioFile
for p.curToken.Type != TokenEOF && p.curToken.Type != TokenSemicolon {
switch p.curToken.Type {
case TokenParam:
p.nextToken()
if p.curToken.Type != TokenQuotedString {
return nil, fmt.Errorf("expect quoted string after 'param'")
}
cmd.Params["param_str"] = strings.Trim(p.curToken.Value, "\"'")
p.nextToken()
default:
return nil, fmt.Errorf("unexpected token in asr command: %s", p.curToken.Value)
}
}
2026-06-25 20:36:50 +08:00
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
func (p *Parser) parseAPITTS() (*Command, error) {
Go: implement TTS for MiniMax provider and CLI testing for TTS (#14911) ### What problem does this PR solve? This PR implement TTS for MiniMax provider and CLI testing for TTS **The following functionalities are now supported:** **MiniMax:** - [x] Chat / Stream Chat - [x] Embedding - [x] Rerank - [x] Model listing - [x] Provider connection checking - [x] Text To Speech - [ ] OCRFile - [ ] ~~Audio To Text~~ - [ ] ~~Balance~~ **Verified examples from the CLI:** ```plaintext RAGFlow(user)> tts with 'speech-2.8-hd@test@minimax' text 'He who desires but acts not, breeds pestilence.' play format 'wav' save './internal' param '{"voice_setting": {"voice_id": "English_radiant_girl", "speed": 1, "vol": 1, "pitch": 0}, "audio_setting": {"sample_rate": 32000, "bitrate": 128000, "format": "wav", "channel": 1}, "output_format": "hex"}' Saved to directory: /home/infiniflow/Documents/development/ragflow/internal/speech-2.8-hd_output.wav SUCCESS RAGFlow(user)> stream tts with 'speech-2.8-hd@test@minimax' text 'He who desires but acts not, breeds pestilence.' play format 'wav' save './internal' param '{"voice_setting": {"voice_id": "English_radiant_girl", "speed": 1, "vol": 1, "pitch": 0}, "audio_setting": {"sample_rate": 32000, "bitrate": 128000, "format": "wav", "channel": 1}, "output_format": "hex"}' Saved to directory: /home/infiniflow/Documents/development/ragflow/internal/speech-2.8-hd_output.wav SUCCESS ``` Set `Play` to play audio in CLI Set `Save` `PATH_TO_SAVE` to save file Set `format` to save file in wav or mp3 Set `Param` align with official request body ### Type of change - [x] New Feature (non-breaking change which adds functionality)
2026-05-14 13:19:31 +08:00
p.nextToken()
cmd := NewCommand("tts_user_command")
if p.curToken.Type != TokenWith {
Go: implement TTS for MiniMax provider and CLI testing for TTS (#14911) ### What problem does this PR solve? This PR implement TTS for MiniMax provider and CLI testing for TTS **The following functionalities are now supported:** **MiniMax:** - [x] Chat / Stream Chat - [x] Embedding - [x] Rerank - [x] Model listing - [x] Provider connection checking - [x] Text To Speech - [ ] OCRFile - [ ] ~~Audio To Text~~ - [ ] ~~Balance~~ **Verified examples from the CLI:** ```plaintext RAGFlow(user)> tts with 'speech-2.8-hd@test@minimax' text 'He who desires but acts not, breeds pestilence.' play format 'wav' save './internal' param '{"voice_setting": {"voice_id": "English_radiant_girl", "speed": 1, "vol": 1, "pitch": 0}, "audio_setting": {"sample_rate": 32000, "bitrate": 128000, "format": "wav", "channel": 1}, "output_format": "hex"}' Saved to directory: /home/infiniflow/Documents/development/ragflow/internal/speech-2.8-hd_output.wav SUCCESS RAGFlow(user)> stream tts with 'speech-2.8-hd@test@minimax' text 'He who desires but acts not, breeds pestilence.' play format 'wav' save './internal' param '{"voice_setting": {"voice_id": "English_radiant_girl", "speed": 1, "vol": 1, "pitch": 0}, "audio_setting": {"sample_rate": 32000, "bitrate": 128000, "format": "wav", "channel": 1}, "output_format": "hex"}' Saved to directory: /home/infiniflow/Documents/development/ragflow/internal/speech-2.8-hd_output.wav SUCCESS ``` Set `Play` to play audio in CLI Set `Save` `PATH_TO_SAVE` to save file Set `format` to save file in wav or mp3 Set `Param` align with official request body ### Type of change - [x] New Feature (non-breaking change which adds functionality)
2026-05-14 13:19:31 +08:00
return nil, fmt.Errorf("expect 'with' after tts")
}
Go: implement TTS for MiniMax provider and CLI testing for TTS (#14911) ### What problem does this PR solve? This PR implement TTS for MiniMax provider and CLI testing for TTS **The following functionalities are now supported:** **MiniMax:** - [x] Chat / Stream Chat - [x] Embedding - [x] Rerank - [x] Model listing - [x] Provider connection checking - [x] Text To Speech - [ ] OCRFile - [ ] ~~Audio To Text~~ - [ ] ~~Balance~~ **Verified examples from the CLI:** ```plaintext RAGFlow(user)> tts with 'speech-2.8-hd@test@minimax' text 'He who desires but acts not, breeds pestilence.' play format 'wav' save './internal' param '{"voice_setting": {"voice_id": "English_radiant_girl", "speed": 1, "vol": 1, "pitch": 0}, "audio_setting": {"sample_rate": 32000, "bitrate": 128000, "format": "wav", "channel": 1}, "output_format": "hex"}' Saved to directory: /home/infiniflow/Documents/development/ragflow/internal/speech-2.8-hd_output.wav SUCCESS RAGFlow(user)> stream tts with 'speech-2.8-hd@test@minimax' text 'He who desires but acts not, breeds pestilence.' play format 'wav' save './internal' param '{"voice_setting": {"voice_id": "English_radiant_girl", "speed": 1, "vol": 1, "pitch": 0}, "audio_setting": {"sample_rate": 32000, "bitrate": 128000, "format": "wav", "channel": 1}, "output_format": "hex"}' Saved to directory: /home/infiniflow/Documents/development/ragflow/internal/speech-2.8-hd_output.wav SUCCESS ``` Set `Play` to play audio in CLI Set `Save` `PATH_TO_SAVE` to save file Set `format` to save file in wav or mp3 Set `Param` align with official request body ### Type of change - [x] New Feature (non-breaking change which adds functionality)
2026-05-14 13:19:31 +08:00
p.nextToken()
Go: implement TTS for MiniMax provider and CLI testing for TTS (#14911) ### What problem does this PR solve? This PR implement TTS for MiniMax provider and CLI testing for TTS **The following functionalities are now supported:** **MiniMax:** - [x] Chat / Stream Chat - [x] Embedding - [x] Rerank - [x] Model listing - [x] Provider connection checking - [x] Text To Speech - [ ] OCRFile - [ ] ~~Audio To Text~~ - [ ] ~~Balance~~ **Verified examples from the CLI:** ```plaintext RAGFlow(user)> tts with 'speech-2.8-hd@test@minimax' text 'He who desires but acts not, breeds pestilence.' play format 'wav' save './internal' param '{"voice_setting": {"voice_id": "English_radiant_girl", "speed": 1, "vol": 1, "pitch": 0}, "audio_setting": {"sample_rate": 32000, "bitrate": 128000, "format": "wav", "channel": 1}, "output_format": "hex"}' Saved to directory: /home/infiniflow/Documents/development/ragflow/internal/speech-2.8-hd_output.wav SUCCESS RAGFlow(user)> stream tts with 'speech-2.8-hd@test@minimax' text 'He who desires but acts not, breeds pestilence.' play format 'wav' save './internal' param '{"voice_setting": {"voice_id": "English_radiant_girl", "speed": 1, "vol": 1, "pitch": 0}, "audio_setting": {"sample_rate": 32000, "bitrate": 128000, "format": "wav", "channel": 1}, "output_format": "hex"}' Saved to directory: /home/infiniflow/Documents/development/ragflow/internal/speech-2.8-hd_output.wav SUCCESS ``` Set `Play` to play audio in CLI Set `Save` `PATH_TO_SAVE` to save file Set `format` to save file in wav or mp3 Set `Param` align with official request body ### Type of change - [x] New Feature (non-breaking change which adds functionality)
2026-05-14 13:19:31 +08:00
if p.curToken.Type != TokenQuotedString && p.curToken.Type != TokenIdentifier {
return nil, fmt.Errorf("expect model name after 'with'")
}
modelNameOrID := strings.Trim(p.curToken.Value, "\"'")
p.nextToken()
if common.IsCompositeModelName(modelNameOrID) {
cmd.Params["composite_model_name"] = modelNameOrID
} else if common.IsUUID(modelNameOrID) {
cmd.Params["model_id"] = modelNameOrID
} else {
return nil, fmt.Errorf("invalid format of model name or ID: %s", modelNameOrID)
}
if p.curToken.Type != TokenText {
Go: implement TTS for MiniMax provider and CLI testing for TTS (#14911) ### What problem does this PR solve? This PR implement TTS for MiniMax provider and CLI testing for TTS **The following functionalities are now supported:** **MiniMax:** - [x] Chat / Stream Chat - [x] Embedding - [x] Rerank - [x] Model listing - [x] Provider connection checking - [x] Text To Speech - [ ] OCRFile - [ ] ~~Audio To Text~~ - [ ] ~~Balance~~ **Verified examples from the CLI:** ```plaintext RAGFlow(user)> tts with 'speech-2.8-hd@test@minimax' text 'He who desires but acts not, breeds pestilence.' play format 'wav' save './internal' param '{"voice_setting": {"voice_id": "English_radiant_girl", "speed": 1, "vol": 1, "pitch": 0}, "audio_setting": {"sample_rate": 32000, "bitrate": 128000, "format": "wav", "channel": 1}, "output_format": "hex"}' Saved to directory: /home/infiniflow/Documents/development/ragflow/internal/speech-2.8-hd_output.wav SUCCESS RAGFlow(user)> stream tts with 'speech-2.8-hd@test@minimax' text 'He who desires but acts not, breeds pestilence.' play format 'wav' save './internal' param '{"voice_setting": {"voice_id": "English_radiant_girl", "speed": 1, "vol": 1, "pitch": 0}, "audio_setting": {"sample_rate": 32000, "bitrate": 128000, "format": "wav", "channel": 1}, "output_format": "hex"}' Saved to directory: /home/infiniflow/Documents/development/ragflow/internal/speech-2.8-hd_output.wav SUCCESS ``` Set `Play` to play audio in CLI Set `Save` `PATH_TO_SAVE` to save file Set `format` to save file in wav or mp3 Set `Param` align with official request body ### Type of change - [x] New Feature (non-breaking change which adds functionality)
2026-05-14 13:19:31 +08:00
return nil, fmt.Errorf("expect 'text' parameter")
}
Go: implement TTS for MiniMax provider and CLI testing for TTS (#14911) ### What problem does this PR solve? This PR implement TTS for MiniMax provider and CLI testing for TTS **The following functionalities are now supported:** **MiniMax:** - [x] Chat / Stream Chat - [x] Embedding - [x] Rerank - [x] Model listing - [x] Provider connection checking - [x] Text To Speech - [ ] OCRFile - [ ] ~~Audio To Text~~ - [ ] ~~Balance~~ **Verified examples from the CLI:** ```plaintext RAGFlow(user)> tts with 'speech-2.8-hd@test@minimax' text 'He who desires but acts not, breeds pestilence.' play format 'wav' save './internal' param '{"voice_setting": {"voice_id": "English_radiant_girl", "speed": 1, "vol": 1, "pitch": 0}, "audio_setting": {"sample_rate": 32000, "bitrate": 128000, "format": "wav", "channel": 1}, "output_format": "hex"}' Saved to directory: /home/infiniflow/Documents/development/ragflow/internal/speech-2.8-hd_output.wav SUCCESS RAGFlow(user)> stream tts with 'speech-2.8-hd@test@minimax' text 'He who desires but acts not, breeds pestilence.' play format 'wav' save './internal' param '{"voice_setting": {"voice_id": "English_radiant_girl", "speed": 1, "vol": 1, "pitch": 0}, "audio_setting": {"sample_rate": 32000, "bitrate": 128000, "format": "wav", "channel": 1}, "output_format": "hex"}' Saved to directory: /home/infiniflow/Documents/development/ragflow/internal/speech-2.8-hd_output.wav SUCCESS ``` Set `Play` to play audio in CLI Set `Save` `PATH_TO_SAVE` to save file Set `format` to save file in wav or mp3 Set `Param` align with official request body ### Type of change - [x] New Feature (non-breaking change which adds functionality)
2026-05-14 13:19:31 +08:00
p.nextToken()
Go: implement TTS for MiniMax provider and CLI testing for TTS (#14911) ### What problem does this PR solve? This PR implement TTS for MiniMax provider and CLI testing for TTS **The following functionalities are now supported:** **MiniMax:** - [x] Chat / Stream Chat - [x] Embedding - [x] Rerank - [x] Model listing - [x] Provider connection checking - [x] Text To Speech - [ ] OCRFile - [ ] ~~Audio To Text~~ - [ ] ~~Balance~~ **Verified examples from the CLI:** ```plaintext RAGFlow(user)> tts with 'speech-2.8-hd@test@minimax' text 'He who desires but acts not, breeds pestilence.' play format 'wav' save './internal' param '{"voice_setting": {"voice_id": "English_radiant_girl", "speed": 1, "vol": 1, "pitch": 0}, "audio_setting": {"sample_rate": 32000, "bitrate": 128000, "format": "wav", "channel": 1}, "output_format": "hex"}' Saved to directory: /home/infiniflow/Documents/development/ragflow/internal/speech-2.8-hd_output.wav SUCCESS RAGFlow(user)> stream tts with 'speech-2.8-hd@test@minimax' text 'He who desires but acts not, breeds pestilence.' play format 'wav' save './internal' param '{"voice_setting": {"voice_id": "English_radiant_girl", "speed": 1, "vol": 1, "pitch": 0}, "audio_setting": {"sample_rate": 32000, "bitrate": 128000, "format": "wav", "channel": 1}, "output_format": "hex"}' Saved to directory: /home/infiniflow/Documents/development/ragflow/internal/speech-2.8-hd_output.wav SUCCESS ``` Set `Play` to play audio in CLI Set `Save` `PATH_TO_SAVE` to save file Set `format` to save file in wav or mp3 Set `Param` align with official request body ### Type of change - [x] New Feature (non-breaking change which adds functionality)
2026-05-14 13:19:31 +08:00
if p.curToken.Type != TokenQuotedString {
return nil, fmt.Errorf("expect quoted string after 'text'")
}
Go: implement TTS for MiniMax provider and CLI testing for TTS (#14911) ### What problem does this PR solve? This PR implement TTS for MiniMax provider and CLI testing for TTS **The following functionalities are now supported:** **MiniMax:** - [x] Chat / Stream Chat - [x] Embedding - [x] Rerank - [x] Model listing - [x] Provider connection checking - [x] Text To Speech - [ ] OCRFile - [ ] ~~Audio To Text~~ - [ ] ~~Balance~~ **Verified examples from the CLI:** ```plaintext RAGFlow(user)> tts with 'speech-2.8-hd@test@minimax' text 'He who desires but acts not, breeds pestilence.' play format 'wav' save './internal' param '{"voice_setting": {"voice_id": "English_radiant_girl", "speed": 1, "vol": 1, "pitch": 0}, "audio_setting": {"sample_rate": 32000, "bitrate": 128000, "format": "wav", "channel": 1}, "output_format": "hex"}' Saved to directory: /home/infiniflow/Documents/development/ragflow/internal/speech-2.8-hd_output.wav SUCCESS RAGFlow(user)> stream tts with 'speech-2.8-hd@test@minimax' text 'He who desires but acts not, breeds pestilence.' play format 'wav' save './internal' param '{"voice_setting": {"voice_id": "English_radiant_girl", "speed": 1, "vol": 1, "pitch": 0}, "audio_setting": {"sample_rate": 32000, "bitrate": 128000, "format": "wav", "channel": 1}, "output_format": "hex"}' Saved to directory: /home/infiniflow/Documents/development/ragflow/internal/speech-2.8-hd_output.wav SUCCESS ``` Set `Play` to play audio in CLI Set `Save` `PATH_TO_SAVE` to save file Set `format` to save file in wav or mp3 Set `Param` align with official request body ### Type of change - [x] New Feature (non-breaking change which adds functionality)
2026-05-14 13:19:31 +08:00
cmd.Params["text"] = strings.Trim(p.curToken.Value, "\"'")
p.nextToken()
Go: implement TTS for MiniMax provider and CLI testing for TTS (#14911) ### What problem does this PR solve? This PR implement TTS for MiniMax provider and CLI testing for TTS **The following functionalities are now supported:** **MiniMax:** - [x] Chat / Stream Chat - [x] Embedding - [x] Rerank - [x] Model listing - [x] Provider connection checking - [x] Text To Speech - [ ] OCRFile - [ ] ~~Audio To Text~~ - [ ] ~~Balance~~ **Verified examples from the CLI:** ```plaintext RAGFlow(user)> tts with 'speech-2.8-hd@test@minimax' text 'He who desires but acts not, breeds pestilence.' play format 'wav' save './internal' param '{"voice_setting": {"voice_id": "English_radiant_girl", "speed": 1, "vol": 1, "pitch": 0}, "audio_setting": {"sample_rate": 32000, "bitrate": 128000, "format": "wav", "channel": 1}, "output_format": "hex"}' Saved to directory: /home/infiniflow/Documents/development/ragflow/internal/speech-2.8-hd_output.wav SUCCESS RAGFlow(user)> stream tts with 'speech-2.8-hd@test@minimax' text 'He who desires but acts not, breeds pestilence.' play format 'wav' save './internal' param '{"voice_setting": {"voice_id": "English_radiant_girl", "speed": 1, "vol": 1, "pitch": 0}, "audio_setting": {"sample_rate": 32000, "bitrate": 128000, "format": "wav", "channel": 1}, "output_format": "hex"}' Saved to directory: /home/infiniflow/Documents/development/ragflow/internal/speech-2.8-hd_output.wav SUCCESS ``` Set `Play` to play audio in CLI Set `Save` `PATH_TO_SAVE` to save file Set `format` to save file in wav or mp3 Set `Param` align with official request body ### Type of change - [x] New Feature (non-breaking change which adds functionality)
2026-05-14 13:19:31 +08:00
for p.curToken.Type != TokenEOF && p.curToken.Type != TokenSemicolon {
switch p.curToken.Type {
case TokenPlay:
p.nextToken()
cmd.Params["play"] = true
case TokenParam:
p.nextToken()
if p.curToken.Type != TokenQuotedString {
return nil, fmt.Errorf("expect quoted string after 'param'")
}
cmd.Params["param_str"] = strings.Trim(p.curToken.Value, "\"'")
p.nextToken()
p.nextToken()
case TokenSave:
p.nextToken()
if p.curToken.Type != TokenQuotedString && p.curToken.Type != TokenIdentifier {
return nil, fmt.Errorf("expect directory path after 'save'")
}
cmd.Params["save"] = true
cmd.Params["save_path"] = strings.Trim(p.curToken.Value, "\"'")
p.nextToken()
case TokenFormat:
p.nextToken()
if p.curToken.Type != TokenQuotedString && p.curToken.Type != TokenIdentifier {
return nil, fmt.Errorf("expect format string (e.g. 'wav') after 'format'")
}
cmd.Params["format"] = strings.Trim(p.curToken.Value, "\"'")
p.nextToken()
default:
return nil, fmt.Errorf("unexpected token: %s", p.curToken.Value)
}
}
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
func (p *Parser) parseAPIOCR() (*Command, error) {
p.nextToken() // consume OCR
if p.curToken.Type != TokenWith {
return nil, fmt.Errorf("expected WITH after OCR")
}
p.nextToken() // consume WITH
modelNameOrID, err := p.parseQuotedString()
if err != nil {
return nil, err
}
p.nextToken()
cmd := NewCommand("ocr_user_command")
switch p.curToken.Type {
case TokenFile:
p.nextToken()
var file string
file, err = p.parseQuotedString()
if err != nil {
return nil, err
}
cmd.Params["file"] = file
p.nextToken()
case TokenURL:
p.nextToken()
var url string
url, err = p.parseQuotedString()
if err != nil {
return nil, err
}
cmd.Params["url"] = url
p.nextToken()
default:
return nil, fmt.Errorf("expected FILE or URL")
}
if common.IsCompositeModelName(modelNameOrID) {
cmd.Params["composite_model_name"] = modelNameOrID
} else if common.IsUUID(modelNameOrID) {
cmd.Params["model_id"] = modelNameOrID
} else {
return nil, fmt.Errorf("invalid format of model name or ID: %s", modelNameOrID)
}
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
// endregion MODEL commands
func (p *Parser) parseAPICheck() (*Command, error) {
p.nextToken() // consume CHECK
switch p.curToken.Type {
case TokenInstance:
return p.parseCheckInstanceCommand()
case TokenProvider:
return p.parseCheckProviderByKeyCommand()
default:
return nil, fmt.Errorf("expected INSTANCE or PROVIDER after CHECK")
}
}
Go: use NATS as the message queue (#15327) ### What problem does this PR solve? ``` RAGFlow(admin)> mq publish 'msg2'; SUCCESS RAGFlow(admin)> mq publish 'msg3'; SUCCESS RAGFlow(admin)> mq list; +---------+---------------+ | message | subject | +---------+---------------+ | msg1 | tasks.RAGFLOW | | msg2 | tasks.RAGFLOW | | msg3 | tasks.RAGFLOW | +---------+---------------+ RAGFlow(admin)> mq pull 2; +---------+---------------+ | message | subject | +---------+---------------+ | msg1 | tasks.RAGFLOW | | msg2 | tasks.RAGFLOW | +---------+---------------+ RAGFlow(admin)> mq pull noack; +---------+---------------+ | message | subject | +---------+---------------+ | abc | tasks.RAGFLOW | +---------+---------------+ RAGFlow(admin)> mq show +-------------------+----------------+--------+---------------+---------------+-------------------+---------------+ | ack_pending_count | consumer_count | memory | message_count | pending_count | redelivered_count | waiting_count | +-------------------+----------------+--------+---------------+---------------+-------------------+---------------+ | 2 | 1 | 0 | 2 | 0 | 1 | 0 | +-------------------+----------------+--------+---------------+---------------+-------------------+---------------+ RAGFlow(admin)> list ingestors; +--------------+-------------------------------------------+--------+ | host | name | status | +--------------+-------------------------------------------+--------+ | 192.168.1.38 | ingestor-8f0e4bd5650a4ac58b0151969fbf6935 | alive | +--------------+-------------------------------------------+--------+ RAGFlow(admin)> list ingestion tasks; +----------------------------------+----------------------------------+-----------+------+-------------+----------------------------------+ | document_id | id | status | step | user | user_id | +----------------------------------+----------------------------------+-----------+------+-------------+----------------------------------+ | ffe64fae423411f1a2d938a74640adcc | 90d3d0f6528941c1ac8eb0360effccc4 | COMPLETED | 5 | aaa@aaa.com | 2ba4881420fa11f19e9c38a74640adcc | +----------------------------------+----------------------------------+-----------+------+-------------+----------------------------------+ RAGFlow(admin)> remove ingestion tasks '90d3d0f6528941c1ac8eb0360effccc4'; +---------+----------------------------------+ | delete | task_id | +---------+----------------------------------+ | success | 90d3d0f6528941c1ac8eb0360effccc4 | +---------+----------------------------------+ RAGFlow(admin)> stop ingestion tasks 'e89e20d9a25848a1b79bd9345ddbfe1d'; +----------+----------------------------------+ | status | task_id | +----------+----------------------------------+ | STOPPING | e89e20d9a25848a1b79bd9345ddbfe1d | +----------+----------------------------------+ # Publish a message RAGFlow(admin)> mq publish 'cdd'; SUCCESS # List current tasks in the message queue RAGFlow(admin)> mq list +----------------------------------+---------------+ | message | subject | +----------------------------------+---------------+ | 7ce392a3c1624cd2be4b5276e8825059 | tasks.RAGFLOW | +----------------------------------+---------------+ # Consume a task from the message queue RAGFlow(admin)> mq pull +------+-----+----------------+ | ack | id | type | +------+-----+----------------+ | true | cdd | ingestion_test | +------+-----+----------------+ # User mode # List ingestion tasks, followed by dataset id RAGFlow(user)> list ingestion tasks from '0abe79f9423311f1ad8d38a74640adcc'; +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | create_date | create_time | dataset_id | document_id | id | schema | status | update_date | update_time | user_id | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | 2026-05-30T20:21:06+08:00 | 1780143666289 | 0abe79f9423311f1ad8d38a74640adcc | ffe64fae423411f1a2d938a74640adcc | 8d758cd14a8b4ba8ab505003fb52017d | | COMPLETED | 2026-05-30T20:21:26+08:00 | 1780143686431 | 2ba4881420fa11f19e9c38a74640adcc | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ RAGFlow(user)> list ingestion tasks; +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | create_date | create_time | dataset_id | document_id | id | schema | status | update_date | update_time | user_id | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | 2026-06-02T19:02:31+08:00 | 1780398151417 | 0abe79f9423311f1ad8d38a74640adcc | ffe64fae423411f1a2d938a74640adcc | e89e20d9a25848a1b79bd9345ddbfe1d | | COMPLETED | 2026-06-02T19:02:52+08:00 | 1780398172208 | 2ba4881420fa11f19e9c38a74640adcc | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ # Create an ingestion task # First argument is document id, second argument is dataset id RAGFlow(user)> start ingestion 'ffe64fae423411f1a2d938a74640adcc' from '0abe79f9423311f1ad8d38a74640adcc'; +----------------------------------+-------------------------------------------+ | document_id | result | +----------------------------------+-------------------------------------------+ | ffe64fae423411f1a2d938a74640adcc | task_id: 8d758cd14a8b4ba8ab505003fb52017d | +----------------------------------+-------------------------------------------+ # Pause an ingestion task, first argument is ingestion id RAGFlow(user)> stop ingestion '8d758cd14a8b4ba8ab505003fb52017d'; +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | create_date | create_time | dataset_id | document_id | id | schema | status | update_date | update_time | user_id | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | 2026-05-30T20:21:06+08:00 | 1780143666289 | 0abe79f9423311f1ad8d38a74640adcc | ffe64fae423411f1a2d938a74640adcc | 8d758cd14a8b4ba8ab505003fb52017d | | COMPLETED | 2026-05-30T20:21:26+08:00 | 1780143686431 | 2ba4881420fa11f19e9c38a74640adcc | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ # Delete an ingestion task RAGFlow(api/default)> remove ingestion tasks 'f366450a27d54677aec1c7090add30f0'; +---------+----------------------------------+ | remove | task_id | +---------+----------------------------------+ | success | f366450a27d54677aec1c7090add30f0 | +---------+----------------------------------+ ``` ### Type of change - [x] New Feature (non-breaking change which adds functionality) --------- Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-06-12 14:56:44 +08:00
func (p *Parser) parseUserStartIngestion() (*Command, error) {
p.nextToken() // consume Start
if p.curToken.Type != TokenIngestion {
return nil, fmt.Errorf("expect INGESTION")
}
p.nextToken() // consume Ingestion
documentID, err := p.parseQuotedString()
if err != nil {
return nil, err
}
p.nextToken()
if p.curToken.Type != TokenFrom {
return nil, fmt.Errorf("expect FROM")
}
p.nextToken() // consume FROM
datasetID, err := p.parseQuotedString()
if err != nil {
return nil, err
}
cmd := NewCommand("api_start_ingestion")
Go: use NATS as the message queue (#15327) ### What problem does this PR solve? ``` RAGFlow(admin)> mq publish 'msg2'; SUCCESS RAGFlow(admin)> mq publish 'msg3'; SUCCESS RAGFlow(admin)> mq list; +---------+---------------+ | message | subject | +---------+---------------+ | msg1 | tasks.RAGFLOW | | msg2 | tasks.RAGFLOW | | msg3 | tasks.RAGFLOW | +---------+---------------+ RAGFlow(admin)> mq pull 2; +---------+---------------+ | message | subject | +---------+---------------+ | msg1 | tasks.RAGFLOW | | msg2 | tasks.RAGFLOW | +---------+---------------+ RAGFlow(admin)> mq pull noack; +---------+---------------+ | message | subject | +---------+---------------+ | abc | tasks.RAGFLOW | +---------+---------------+ RAGFlow(admin)> mq show +-------------------+----------------+--------+---------------+---------------+-------------------+---------------+ | ack_pending_count | consumer_count | memory | message_count | pending_count | redelivered_count | waiting_count | +-------------------+----------------+--------+---------------+---------------+-------------------+---------------+ | 2 | 1 | 0 | 2 | 0 | 1 | 0 | +-------------------+----------------+--------+---------------+---------------+-------------------+---------------+ RAGFlow(admin)> list ingestors; +--------------+-------------------------------------------+--------+ | host | name | status | +--------------+-------------------------------------------+--------+ | 192.168.1.38 | ingestor-8f0e4bd5650a4ac58b0151969fbf6935 | alive | +--------------+-------------------------------------------+--------+ RAGFlow(admin)> list ingestion tasks; +----------------------------------+----------------------------------+-----------+------+-------------+----------------------------------+ | document_id | id | status | step | user | user_id | +----------------------------------+----------------------------------+-----------+------+-------------+----------------------------------+ | ffe64fae423411f1a2d938a74640adcc | 90d3d0f6528941c1ac8eb0360effccc4 | COMPLETED | 5 | aaa@aaa.com | 2ba4881420fa11f19e9c38a74640adcc | +----------------------------------+----------------------------------+-----------+------+-------------+----------------------------------+ RAGFlow(admin)> remove ingestion tasks '90d3d0f6528941c1ac8eb0360effccc4'; +---------+----------------------------------+ | delete | task_id | +---------+----------------------------------+ | success | 90d3d0f6528941c1ac8eb0360effccc4 | +---------+----------------------------------+ RAGFlow(admin)> stop ingestion tasks 'e89e20d9a25848a1b79bd9345ddbfe1d'; +----------+----------------------------------+ | status | task_id | +----------+----------------------------------+ | STOPPING | e89e20d9a25848a1b79bd9345ddbfe1d | +----------+----------------------------------+ # Publish a message RAGFlow(admin)> mq publish 'cdd'; SUCCESS # List current tasks in the message queue RAGFlow(admin)> mq list +----------------------------------+---------------+ | message | subject | +----------------------------------+---------------+ | 7ce392a3c1624cd2be4b5276e8825059 | tasks.RAGFLOW | +----------------------------------+---------------+ # Consume a task from the message queue RAGFlow(admin)> mq pull +------+-----+----------------+ | ack | id | type | +------+-----+----------------+ | true | cdd | ingestion_test | +------+-----+----------------+ # User mode # List ingestion tasks, followed by dataset id RAGFlow(user)> list ingestion tasks from '0abe79f9423311f1ad8d38a74640adcc'; +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | create_date | create_time | dataset_id | document_id | id | schema | status | update_date | update_time | user_id | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | 2026-05-30T20:21:06+08:00 | 1780143666289 | 0abe79f9423311f1ad8d38a74640adcc | ffe64fae423411f1a2d938a74640adcc | 8d758cd14a8b4ba8ab505003fb52017d | | COMPLETED | 2026-05-30T20:21:26+08:00 | 1780143686431 | 2ba4881420fa11f19e9c38a74640adcc | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ RAGFlow(user)> list ingestion tasks; +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | create_date | create_time | dataset_id | document_id | id | schema | status | update_date | update_time | user_id | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | 2026-06-02T19:02:31+08:00 | 1780398151417 | 0abe79f9423311f1ad8d38a74640adcc | ffe64fae423411f1a2d938a74640adcc | e89e20d9a25848a1b79bd9345ddbfe1d | | COMPLETED | 2026-06-02T19:02:52+08:00 | 1780398172208 | 2ba4881420fa11f19e9c38a74640adcc | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ # Create an ingestion task # First argument is document id, second argument is dataset id RAGFlow(user)> start ingestion 'ffe64fae423411f1a2d938a74640adcc' from '0abe79f9423311f1ad8d38a74640adcc'; +----------------------------------+-------------------------------------------+ | document_id | result | +----------------------------------+-------------------------------------------+ | ffe64fae423411f1a2d938a74640adcc | task_id: 8d758cd14a8b4ba8ab505003fb52017d | +----------------------------------+-------------------------------------------+ # Pause an ingestion task, first argument is ingestion id RAGFlow(user)> stop ingestion '8d758cd14a8b4ba8ab505003fb52017d'; +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | create_date | create_time | dataset_id | document_id | id | schema | status | update_date | update_time | user_id | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | 2026-05-30T20:21:06+08:00 | 1780143666289 | 0abe79f9423311f1ad8d38a74640adcc | ffe64fae423411f1a2d938a74640adcc | 8d758cd14a8b4ba8ab505003fb52017d | | COMPLETED | 2026-05-30T20:21:26+08:00 | 1780143686431 | 2ba4881420fa11f19e9c38a74640adcc | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ # Delete an ingestion task RAGFlow(api/default)> remove ingestion tasks 'f366450a27d54677aec1c7090add30f0'; +---------+----------------------------------+ | remove | task_id | +---------+----------------------------------+ | success | f366450a27d54677aec1c7090add30f0 | +---------+----------------------------------+ ``` ### Type of change - [x] New Feature (non-breaking change which adds functionality) --------- Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-06-12 14:56:44 +08:00
cmd.Params["document_id"] = documentID
cmd.Params["dataset_id"] = datasetID
p.nextToken()
2026-06-25 20:36:50 +08:00
// Semicolon is optional
Go: use NATS as the message queue (#15327) ### What problem does this PR solve? ``` RAGFlow(admin)> mq publish 'msg2'; SUCCESS RAGFlow(admin)> mq publish 'msg3'; SUCCESS RAGFlow(admin)> mq list; +---------+---------------+ | message | subject | +---------+---------------+ | msg1 | tasks.RAGFLOW | | msg2 | tasks.RAGFLOW | | msg3 | tasks.RAGFLOW | +---------+---------------+ RAGFlow(admin)> mq pull 2; +---------+---------------+ | message | subject | +---------+---------------+ | msg1 | tasks.RAGFLOW | | msg2 | tasks.RAGFLOW | +---------+---------------+ RAGFlow(admin)> mq pull noack; +---------+---------------+ | message | subject | +---------+---------------+ | abc | tasks.RAGFLOW | +---------+---------------+ RAGFlow(admin)> mq show +-------------------+----------------+--------+---------------+---------------+-------------------+---------------+ | ack_pending_count | consumer_count | memory | message_count | pending_count | redelivered_count | waiting_count | +-------------------+----------------+--------+---------------+---------------+-------------------+---------------+ | 2 | 1 | 0 | 2 | 0 | 1 | 0 | +-------------------+----------------+--------+---------------+---------------+-------------------+---------------+ RAGFlow(admin)> list ingestors; +--------------+-------------------------------------------+--------+ | host | name | status | +--------------+-------------------------------------------+--------+ | 192.168.1.38 | ingestor-8f0e4bd5650a4ac58b0151969fbf6935 | alive | +--------------+-------------------------------------------+--------+ RAGFlow(admin)> list ingestion tasks; +----------------------------------+----------------------------------+-----------+------+-------------+----------------------------------+ | document_id | id | status | step | user | user_id | +----------------------------------+----------------------------------+-----------+------+-------------+----------------------------------+ | ffe64fae423411f1a2d938a74640adcc | 90d3d0f6528941c1ac8eb0360effccc4 | COMPLETED | 5 | aaa@aaa.com | 2ba4881420fa11f19e9c38a74640adcc | +----------------------------------+----------------------------------+-----------+------+-------------+----------------------------------+ RAGFlow(admin)> remove ingestion tasks '90d3d0f6528941c1ac8eb0360effccc4'; +---------+----------------------------------+ | delete | task_id | +---------+----------------------------------+ | success | 90d3d0f6528941c1ac8eb0360effccc4 | +---------+----------------------------------+ RAGFlow(admin)> stop ingestion tasks 'e89e20d9a25848a1b79bd9345ddbfe1d'; +----------+----------------------------------+ | status | task_id | +----------+----------------------------------+ | STOPPING | e89e20d9a25848a1b79bd9345ddbfe1d | +----------+----------------------------------+ # Publish a message RAGFlow(admin)> mq publish 'cdd'; SUCCESS # List current tasks in the message queue RAGFlow(admin)> mq list +----------------------------------+---------------+ | message | subject | +----------------------------------+---------------+ | 7ce392a3c1624cd2be4b5276e8825059 | tasks.RAGFLOW | +----------------------------------+---------------+ # Consume a task from the message queue RAGFlow(admin)> mq pull +------+-----+----------------+ | ack | id | type | +------+-----+----------------+ | true | cdd | ingestion_test | +------+-----+----------------+ # User mode # List ingestion tasks, followed by dataset id RAGFlow(user)> list ingestion tasks from '0abe79f9423311f1ad8d38a74640adcc'; +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | create_date | create_time | dataset_id | document_id | id | schema | status | update_date | update_time | user_id | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | 2026-05-30T20:21:06+08:00 | 1780143666289 | 0abe79f9423311f1ad8d38a74640adcc | ffe64fae423411f1a2d938a74640adcc | 8d758cd14a8b4ba8ab505003fb52017d | | COMPLETED | 2026-05-30T20:21:26+08:00 | 1780143686431 | 2ba4881420fa11f19e9c38a74640adcc | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ RAGFlow(user)> list ingestion tasks; +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | create_date | create_time | dataset_id | document_id | id | schema | status | update_date | update_time | user_id | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | 2026-06-02T19:02:31+08:00 | 1780398151417 | 0abe79f9423311f1ad8d38a74640adcc | ffe64fae423411f1a2d938a74640adcc | e89e20d9a25848a1b79bd9345ddbfe1d | | COMPLETED | 2026-06-02T19:02:52+08:00 | 1780398172208 | 2ba4881420fa11f19e9c38a74640adcc | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ # Create an ingestion task # First argument is document id, second argument is dataset id RAGFlow(user)> start ingestion 'ffe64fae423411f1a2d938a74640adcc' from '0abe79f9423311f1ad8d38a74640adcc'; +----------------------------------+-------------------------------------------+ | document_id | result | +----------------------------------+-------------------------------------------+ | ffe64fae423411f1a2d938a74640adcc | task_id: 8d758cd14a8b4ba8ab505003fb52017d | +----------------------------------+-------------------------------------------+ # Pause an ingestion task, first argument is ingestion id RAGFlow(user)> stop ingestion '8d758cd14a8b4ba8ab505003fb52017d'; +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | create_date | create_time | dataset_id | document_id | id | schema | status | update_date | update_time | user_id | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | 2026-05-30T20:21:06+08:00 | 1780143666289 | 0abe79f9423311f1ad8d38a74640adcc | ffe64fae423411f1a2d938a74640adcc | 8d758cd14a8b4ba8ab505003fb52017d | | COMPLETED | 2026-05-30T20:21:26+08:00 | 1780143686431 | 2ba4881420fa11f19e9c38a74640adcc | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ # Delete an ingestion task RAGFlow(api/default)> remove ingestion tasks 'f366450a27d54677aec1c7090add30f0'; +---------+----------------------------------+ | remove | task_id | +---------+----------------------------------+ | success | f366450a27d54677aec1c7090add30f0 | +---------+----------------------------------+ ``` ### Type of change - [x] New Feature (non-breaking change which adds functionality) --------- Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-06-12 14:56:44 +08:00
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
func (p *Parser) parseUserStopIngestion() (*Command, error) {
p.nextToken() // consume Stop
if p.curToken.Type != TokenIngestion {
return nil, fmt.Errorf("expect INGESTION")
}
p.nextToken() // consume Ingestion
if p.curToken.Type != TokenTasks {
return nil, fmt.Errorf("expect TASKS")
}
p.nextToken()
taskStr, err := p.parseQuotedString()
if err != nil {
return nil, err
}
tasks := strings.Split(taskStr, " ")
cmd := NewCommand("api_stop_ingestion")
Go: use NATS as the message queue (#15327) ### What problem does this PR solve? ``` RAGFlow(admin)> mq publish 'msg2'; SUCCESS RAGFlow(admin)> mq publish 'msg3'; SUCCESS RAGFlow(admin)> mq list; +---------+---------------+ | message | subject | +---------+---------------+ | msg1 | tasks.RAGFLOW | | msg2 | tasks.RAGFLOW | | msg3 | tasks.RAGFLOW | +---------+---------------+ RAGFlow(admin)> mq pull 2; +---------+---------------+ | message | subject | +---------+---------------+ | msg1 | tasks.RAGFLOW | | msg2 | tasks.RAGFLOW | +---------+---------------+ RAGFlow(admin)> mq pull noack; +---------+---------------+ | message | subject | +---------+---------------+ | abc | tasks.RAGFLOW | +---------+---------------+ RAGFlow(admin)> mq show +-------------------+----------------+--------+---------------+---------------+-------------------+---------------+ | ack_pending_count | consumer_count | memory | message_count | pending_count | redelivered_count | waiting_count | +-------------------+----------------+--------+---------------+---------------+-------------------+---------------+ | 2 | 1 | 0 | 2 | 0 | 1 | 0 | +-------------------+----------------+--------+---------------+---------------+-------------------+---------------+ RAGFlow(admin)> list ingestors; +--------------+-------------------------------------------+--------+ | host | name | status | +--------------+-------------------------------------------+--------+ | 192.168.1.38 | ingestor-8f0e4bd5650a4ac58b0151969fbf6935 | alive | +--------------+-------------------------------------------+--------+ RAGFlow(admin)> list ingestion tasks; +----------------------------------+----------------------------------+-----------+------+-------------+----------------------------------+ | document_id | id | status | step | user | user_id | +----------------------------------+----------------------------------+-----------+------+-------------+----------------------------------+ | ffe64fae423411f1a2d938a74640adcc | 90d3d0f6528941c1ac8eb0360effccc4 | COMPLETED | 5 | aaa@aaa.com | 2ba4881420fa11f19e9c38a74640adcc | +----------------------------------+----------------------------------+-----------+------+-------------+----------------------------------+ RAGFlow(admin)> remove ingestion tasks '90d3d0f6528941c1ac8eb0360effccc4'; +---------+----------------------------------+ | delete | task_id | +---------+----------------------------------+ | success | 90d3d0f6528941c1ac8eb0360effccc4 | +---------+----------------------------------+ RAGFlow(admin)> stop ingestion tasks 'e89e20d9a25848a1b79bd9345ddbfe1d'; +----------+----------------------------------+ | status | task_id | +----------+----------------------------------+ | STOPPING | e89e20d9a25848a1b79bd9345ddbfe1d | +----------+----------------------------------+ # Publish a message RAGFlow(admin)> mq publish 'cdd'; SUCCESS # List current tasks in the message queue RAGFlow(admin)> mq list +----------------------------------+---------------+ | message | subject | +----------------------------------+---------------+ | 7ce392a3c1624cd2be4b5276e8825059 | tasks.RAGFLOW | +----------------------------------+---------------+ # Consume a task from the message queue RAGFlow(admin)> mq pull +------+-----+----------------+ | ack | id | type | +------+-----+----------------+ | true | cdd | ingestion_test | +------+-----+----------------+ # User mode # List ingestion tasks, followed by dataset id RAGFlow(user)> list ingestion tasks from '0abe79f9423311f1ad8d38a74640adcc'; +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | create_date | create_time | dataset_id | document_id | id | schema | status | update_date | update_time | user_id | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | 2026-05-30T20:21:06+08:00 | 1780143666289 | 0abe79f9423311f1ad8d38a74640adcc | ffe64fae423411f1a2d938a74640adcc | 8d758cd14a8b4ba8ab505003fb52017d | | COMPLETED | 2026-05-30T20:21:26+08:00 | 1780143686431 | 2ba4881420fa11f19e9c38a74640adcc | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ RAGFlow(user)> list ingestion tasks; +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | create_date | create_time | dataset_id | document_id | id | schema | status | update_date | update_time | user_id | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | 2026-06-02T19:02:31+08:00 | 1780398151417 | 0abe79f9423311f1ad8d38a74640adcc | ffe64fae423411f1a2d938a74640adcc | e89e20d9a25848a1b79bd9345ddbfe1d | | COMPLETED | 2026-06-02T19:02:52+08:00 | 1780398172208 | 2ba4881420fa11f19e9c38a74640adcc | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ # Create an ingestion task # First argument is document id, second argument is dataset id RAGFlow(user)> start ingestion 'ffe64fae423411f1a2d938a74640adcc' from '0abe79f9423311f1ad8d38a74640adcc'; +----------------------------------+-------------------------------------------+ | document_id | result | +----------------------------------+-------------------------------------------+ | ffe64fae423411f1a2d938a74640adcc | task_id: 8d758cd14a8b4ba8ab505003fb52017d | +----------------------------------+-------------------------------------------+ # Pause an ingestion task, first argument is ingestion id RAGFlow(user)> stop ingestion '8d758cd14a8b4ba8ab505003fb52017d'; +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | create_date | create_time | dataset_id | document_id | id | schema | status | update_date | update_time | user_id | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | 2026-05-30T20:21:06+08:00 | 1780143666289 | 0abe79f9423311f1ad8d38a74640adcc | ffe64fae423411f1a2d938a74640adcc | 8d758cd14a8b4ba8ab505003fb52017d | | COMPLETED | 2026-05-30T20:21:26+08:00 | 1780143686431 | 2ba4881420fa11f19e9c38a74640adcc | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ # Delete an ingestion task RAGFlow(api/default)> remove ingestion tasks 'f366450a27d54677aec1c7090add30f0'; +---------+----------------------------------+ | remove | task_id | +---------+----------------------------------+ | success | f366450a27d54677aec1c7090add30f0 | +---------+----------------------------------+ ``` ### Type of change - [x] New Feature (non-breaking change which adds functionality) --------- Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-06-12 14:56:44 +08:00
cmd.Params["tasks"] = tasks
p.nextToken()
2026-06-25 20:36:50 +08:00
// Semicolon is optional
Go: use NATS as the message queue (#15327) ### What problem does this PR solve? ``` RAGFlow(admin)> mq publish 'msg2'; SUCCESS RAGFlow(admin)> mq publish 'msg3'; SUCCESS RAGFlow(admin)> mq list; +---------+---------------+ | message | subject | +---------+---------------+ | msg1 | tasks.RAGFLOW | | msg2 | tasks.RAGFLOW | | msg3 | tasks.RAGFLOW | +---------+---------------+ RAGFlow(admin)> mq pull 2; +---------+---------------+ | message | subject | +---------+---------------+ | msg1 | tasks.RAGFLOW | | msg2 | tasks.RAGFLOW | +---------+---------------+ RAGFlow(admin)> mq pull noack; +---------+---------------+ | message | subject | +---------+---------------+ | abc | tasks.RAGFLOW | +---------+---------------+ RAGFlow(admin)> mq show +-------------------+----------------+--------+---------------+---------------+-------------------+---------------+ | ack_pending_count | consumer_count | memory | message_count | pending_count | redelivered_count | waiting_count | +-------------------+----------------+--------+---------------+---------------+-------------------+---------------+ | 2 | 1 | 0 | 2 | 0 | 1 | 0 | +-------------------+----------------+--------+---------------+---------------+-------------------+---------------+ RAGFlow(admin)> list ingestors; +--------------+-------------------------------------------+--------+ | host | name | status | +--------------+-------------------------------------------+--------+ | 192.168.1.38 | ingestor-8f0e4bd5650a4ac58b0151969fbf6935 | alive | +--------------+-------------------------------------------+--------+ RAGFlow(admin)> list ingestion tasks; +----------------------------------+----------------------------------+-----------+------+-------------+----------------------------------+ | document_id | id | status | step | user | user_id | +----------------------------------+----------------------------------+-----------+------+-------------+----------------------------------+ | ffe64fae423411f1a2d938a74640adcc | 90d3d0f6528941c1ac8eb0360effccc4 | COMPLETED | 5 | aaa@aaa.com | 2ba4881420fa11f19e9c38a74640adcc | +----------------------------------+----------------------------------+-----------+------+-------------+----------------------------------+ RAGFlow(admin)> remove ingestion tasks '90d3d0f6528941c1ac8eb0360effccc4'; +---------+----------------------------------+ | delete | task_id | +---------+----------------------------------+ | success | 90d3d0f6528941c1ac8eb0360effccc4 | +---------+----------------------------------+ RAGFlow(admin)> stop ingestion tasks 'e89e20d9a25848a1b79bd9345ddbfe1d'; +----------+----------------------------------+ | status | task_id | +----------+----------------------------------+ | STOPPING | e89e20d9a25848a1b79bd9345ddbfe1d | +----------+----------------------------------+ # Publish a message RAGFlow(admin)> mq publish 'cdd'; SUCCESS # List current tasks in the message queue RAGFlow(admin)> mq list +----------------------------------+---------------+ | message | subject | +----------------------------------+---------------+ | 7ce392a3c1624cd2be4b5276e8825059 | tasks.RAGFLOW | +----------------------------------+---------------+ # Consume a task from the message queue RAGFlow(admin)> mq pull +------+-----+----------------+ | ack | id | type | +------+-----+----------------+ | true | cdd | ingestion_test | +------+-----+----------------+ # User mode # List ingestion tasks, followed by dataset id RAGFlow(user)> list ingestion tasks from '0abe79f9423311f1ad8d38a74640adcc'; +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | create_date | create_time | dataset_id | document_id | id | schema | status | update_date | update_time | user_id | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | 2026-05-30T20:21:06+08:00 | 1780143666289 | 0abe79f9423311f1ad8d38a74640adcc | ffe64fae423411f1a2d938a74640adcc | 8d758cd14a8b4ba8ab505003fb52017d | | COMPLETED | 2026-05-30T20:21:26+08:00 | 1780143686431 | 2ba4881420fa11f19e9c38a74640adcc | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ RAGFlow(user)> list ingestion tasks; +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | create_date | create_time | dataset_id | document_id | id | schema | status | update_date | update_time | user_id | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | 2026-06-02T19:02:31+08:00 | 1780398151417 | 0abe79f9423311f1ad8d38a74640adcc | ffe64fae423411f1a2d938a74640adcc | e89e20d9a25848a1b79bd9345ddbfe1d | | COMPLETED | 2026-06-02T19:02:52+08:00 | 1780398172208 | 2ba4881420fa11f19e9c38a74640adcc | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ # Create an ingestion task # First argument is document id, second argument is dataset id RAGFlow(user)> start ingestion 'ffe64fae423411f1a2d938a74640adcc' from '0abe79f9423311f1ad8d38a74640adcc'; +----------------------------------+-------------------------------------------+ | document_id | result | +----------------------------------+-------------------------------------------+ | ffe64fae423411f1a2d938a74640adcc | task_id: 8d758cd14a8b4ba8ab505003fb52017d | +----------------------------------+-------------------------------------------+ # Pause an ingestion task, first argument is ingestion id RAGFlow(user)> stop ingestion '8d758cd14a8b4ba8ab505003fb52017d'; +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | create_date | create_time | dataset_id | document_id | id | schema | status | update_date | update_time | user_id | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | 2026-05-30T20:21:06+08:00 | 1780143666289 | 0abe79f9423311f1ad8d38a74640adcc | ffe64fae423411f1a2d938a74640adcc | 8d758cd14a8b4ba8ab505003fb52017d | | COMPLETED | 2026-05-30T20:21:26+08:00 | 1780143686431 | 2ba4881420fa11f19e9c38a74640adcc | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ # Delete an ingestion task RAGFlow(api/default)> remove ingestion tasks 'f366450a27d54677aec1c7090add30f0'; +---------+----------------------------------+ | remove | task_id | +---------+----------------------------------+ | success | f366450a27d54677aec1c7090add30f0 | +---------+----------------------------------+ ``` ### Type of change - [x] New Feature (non-breaking change which adds functionality) --------- Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-06-12 14:56:44 +08:00
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
func (p *Parser) parseCheckInstanceCommand() (*Command, error) {
if p.curToken.Type != TokenInstance {
return nil, fmt.Errorf("expected INSTANCE after CHECK")
}
p.nextToken()
Go: use NATS as the message queue (#15327) ### What problem does this PR solve? ``` RAGFlow(admin)> mq publish 'msg2'; SUCCESS RAGFlow(admin)> mq publish 'msg3'; SUCCESS RAGFlow(admin)> mq list; +---------+---------------+ | message | subject | +---------+---------------+ | msg1 | tasks.RAGFLOW | | msg2 | tasks.RAGFLOW | | msg3 | tasks.RAGFLOW | +---------+---------------+ RAGFlow(admin)> mq pull 2; +---------+---------------+ | message | subject | +---------+---------------+ | msg1 | tasks.RAGFLOW | | msg2 | tasks.RAGFLOW | +---------+---------------+ RAGFlow(admin)> mq pull noack; +---------+---------------+ | message | subject | +---------+---------------+ | abc | tasks.RAGFLOW | +---------+---------------+ RAGFlow(admin)> mq show +-------------------+----------------+--------+---------------+---------------+-------------------+---------------+ | ack_pending_count | consumer_count | memory | message_count | pending_count | redelivered_count | waiting_count | +-------------------+----------------+--------+---------------+---------------+-------------------+---------------+ | 2 | 1 | 0 | 2 | 0 | 1 | 0 | +-------------------+----------------+--------+---------------+---------------+-------------------+---------------+ RAGFlow(admin)> list ingestors; +--------------+-------------------------------------------+--------+ | host | name | status | +--------------+-------------------------------------------+--------+ | 192.168.1.38 | ingestor-8f0e4bd5650a4ac58b0151969fbf6935 | alive | +--------------+-------------------------------------------+--------+ RAGFlow(admin)> list ingestion tasks; +----------------------------------+----------------------------------+-----------+------+-------------+----------------------------------+ | document_id | id | status | step | user | user_id | +----------------------------------+----------------------------------+-----------+------+-------------+----------------------------------+ | ffe64fae423411f1a2d938a74640adcc | 90d3d0f6528941c1ac8eb0360effccc4 | COMPLETED | 5 | aaa@aaa.com | 2ba4881420fa11f19e9c38a74640adcc | +----------------------------------+----------------------------------+-----------+------+-------------+----------------------------------+ RAGFlow(admin)> remove ingestion tasks '90d3d0f6528941c1ac8eb0360effccc4'; +---------+----------------------------------+ | delete | task_id | +---------+----------------------------------+ | success | 90d3d0f6528941c1ac8eb0360effccc4 | +---------+----------------------------------+ RAGFlow(admin)> stop ingestion tasks 'e89e20d9a25848a1b79bd9345ddbfe1d'; +----------+----------------------------------+ | status | task_id | +----------+----------------------------------+ | STOPPING | e89e20d9a25848a1b79bd9345ddbfe1d | +----------+----------------------------------+ # Publish a message RAGFlow(admin)> mq publish 'cdd'; SUCCESS # List current tasks in the message queue RAGFlow(admin)> mq list +----------------------------------+---------------+ | message | subject | +----------------------------------+---------------+ | 7ce392a3c1624cd2be4b5276e8825059 | tasks.RAGFLOW | +----------------------------------+---------------+ # Consume a task from the message queue RAGFlow(admin)> mq pull +------+-----+----------------+ | ack | id | type | +------+-----+----------------+ | true | cdd | ingestion_test | +------+-----+----------------+ # User mode # List ingestion tasks, followed by dataset id RAGFlow(user)> list ingestion tasks from '0abe79f9423311f1ad8d38a74640adcc'; +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | create_date | create_time | dataset_id | document_id | id | schema | status | update_date | update_time | user_id | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | 2026-05-30T20:21:06+08:00 | 1780143666289 | 0abe79f9423311f1ad8d38a74640adcc | ffe64fae423411f1a2d938a74640adcc | 8d758cd14a8b4ba8ab505003fb52017d | | COMPLETED | 2026-05-30T20:21:26+08:00 | 1780143686431 | 2ba4881420fa11f19e9c38a74640adcc | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ RAGFlow(user)> list ingestion tasks; +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | create_date | create_time | dataset_id | document_id | id | schema | status | update_date | update_time | user_id | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | 2026-06-02T19:02:31+08:00 | 1780398151417 | 0abe79f9423311f1ad8d38a74640adcc | ffe64fae423411f1a2d938a74640adcc | e89e20d9a25848a1b79bd9345ddbfe1d | | COMPLETED | 2026-06-02T19:02:52+08:00 | 1780398172208 | 2ba4881420fa11f19e9c38a74640adcc | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ # Create an ingestion task # First argument is document id, second argument is dataset id RAGFlow(user)> start ingestion 'ffe64fae423411f1a2d938a74640adcc' from '0abe79f9423311f1ad8d38a74640adcc'; +----------------------------------+-------------------------------------------+ | document_id | result | +----------------------------------+-------------------------------------------+ | ffe64fae423411f1a2d938a74640adcc | task_id: 8d758cd14a8b4ba8ab505003fb52017d | +----------------------------------+-------------------------------------------+ # Pause an ingestion task, first argument is ingestion id RAGFlow(user)> stop ingestion '8d758cd14a8b4ba8ab505003fb52017d'; +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | create_date | create_time | dataset_id | document_id | id | schema | status | update_date | update_time | user_id | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | 2026-05-30T20:21:06+08:00 | 1780143666289 | 0abe79f9423311f1ad8d38a74640adcc | ffe64fae423411f1a2d938a74640adcc | 8d758cd14a8b4ba8ab505003fb52017d | | COMPLETED | 2026-05-30T20:21:26+08:00 | 1780143686431 | 2ba4881420fa11f19e9c38a74640adcc | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ # Delete an ingestion task RAGFlow(api/default)> remove ingestion tasks 'f366450a27d54677aec1c7090add30f0'; +---------+----------------------------------+ | remove | task_id | +---------+----------------------------------+ | success | f366450a27d54677aec1c7090add30f0 | +---------+----------------------------------+ ``` ### Type of change - [x] New Feature (non-breaking change which adds functionality) --------- Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-06-12 14:56:44 +08:00
if p.curToken.Type != TokenQuotedString {
return nil, fmt.Errorf("expected instance name after INSTANCE")
Go: use NATS as the message queue (#15327) ### What problem does this PR solve? ``` RAGFlow(admin)> mq publish 'msg2'; SUCCESS RAGFlow(admin)> mq publish 'msg3'; SUCCESS RAGFlow(admin)> mq list; +---------+---------------+ | message | subject | +---------+---------------+ | msg1 | tasks.RAGFLOW | | msg2 | tasks.RAGFLOW | | msg3 | tasks.RAGFLOW | +---------+---------------+ RAGFlow(admin)> mq pull 2; +---------+---------------+ | message | subject | +---------+---------------+ | msg1 | tasks.RAGFLOW | | msg2 | tasks.RAGFLOW | +---------+---------------+ RAGFlow(admin)> mq pull noack; +---------+---------------+ | message | subject | +---------+---------------+ | abc | tasks.RAGFLOW | +---------+---------------+ RAGFlow(admin)> mq show +-------------------+----------------+--------+---------------+---------------+-------------------+---------------+ | ack_pending_count | consumer_count | memory | message_count | pending_count | redelivered_count | waiting_count | +-------------------+----------------+--------+---------------+---------------+-------------------+---------------+ | 2 | 1 | 0 | 2 | 0 | 1 | 0 | +-------------------+----------------+--------+---------------+---------------+-------------------+---------------+ RAGFlow(admin)> list ingestors; +--------------+-------------------------------------------+--------+ | host | name | status | +--------------+-------------------------------------------+--------+ | 192.168.1.38 | ingestor-8f0e4bd5650a4ac58b0151969fbf6935 | alive | +--------------+-------------------------------------------+--------+ RAGFlow(admin)> list ingestion tasks; +----------------------------------+----------------------------------+-----------+------+-------------+----------------------------------+ | document_id | id | status | step | user | user_id | +----------------------------------+----------------------------------+-----------+------+-------------+----------------------------------+ | ffe64fae423411f1a2d938a74640adcc | 90d3d0f6528941c1ac8eb0360effccc4 | COMPLETED | 5 | aaa@aaa.com | 2ba4881420fa11f19e9c38a74640adcc | +----------------------------------+----------------------------------+-----------+------+-------------+----------------------------------+ RAGFlow(admin)> remove ingestion tasks '90d3d0f6528941c1ac8eb0360effccc4'; +---------+----------------------------------+ | delete | task_id | +---------+----------------------------------+ | success | 90d3d0f6528941c1ac8eb0360effccc4 | +---------+----------------------------------+ RAGFlow(admin)> stop ingestion tasks 'e89e20d9a25848a1b79bd9345ddbfe1d'; +----------+----------------------------------+ | status | task_id | +----------+----------------------------------+ | STOPPING | e89e20d9a25848a1b79bd9345ddbfe1d | +----------+----------------------------------+ # Publish a message RAGFlow(admin)> mq publish 'cdd'; SUCCESS # List current tasks in the message queue RAGFlow(admin)> mq list +----------------------------------+---------------+ | message | subject | +----------------------------------+---------------+ | 7ce392a3c1624cd2be4b5276e8825059 | tasks.RAGFLOW | +----------------------------------+---------------+ # Consume a task from the message queue RAGFlow(admin)> mq pull +------+-----+----------------+ | ack | id | type | +------+-----+----------------+ | true | cdd | ingestion_test | +------+-----+----------------+ # User mode # List ingestion tasks, followed by dataset id RAGFlow(user)> list ingestion tasks from '0abe79f9423311f1ad8d38a74640adcc'; +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | create_date | create_time | dataset_id | document_id | id | schema | status | update_date | update_time | user_id | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | 2026-05-30T20:21:06+08:00 | 1780143666289 | 0abe79f9423311f1ad8d38a74640adcc | ffe64fae423411f1a2d938a74640adcc | 8d758cd14a8b4ba8ab505003fb52017d | | COMPLETED | 2026-05-30T20:21:26+08:00 | 1780143686431 | 2ba4881420fa11f19e9c38a74640adcc | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ RAGFlow(user)> list ingestion tasks; +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | create_date | create_time | dataset_id | document_id | id | schema | status | update_date | update_time | user_id | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | 2026-06-02T19:02:31+08:00 | 1780398151417 | 0abe79f9423311f1ad8d38a74640adcc | ffe64fae423411f1a2d938a74640adcc | e89e20d9a25848a1b79bd9345ddbfe1d | | COMPLETED | 2026-06-02T19:02:52+08:00 | 1780398172208 | 2ba4881420fa11f19e9c38a74640adcc | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ # Create an ingestion task # First argument is document id, second argument is dataset id RAGFlow(user)> start ingestion 'ffe64fae423411f1a2d938a74640adcc' from '0abe79f9423311f1ad8d38a74640adcc'; +----------------------------------+-------------------------------------------+ | document_id | result | +----------------------------------+-------------------------------------------+ | ffe64fae423411f1a2d938a74640adcc | task_id: 8d758cd14a8b4ba8ab505003fb52017d | +----------------------------------+-------------------------------------------+ # Pause an ingestion task, first argument is ingestion id RAGFlow(user)> stop ingestion '8d758cd14a8b4ba8ab505003fb52017d'; +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | create_date | create_time | dataset_id | document_id | id | schema | status | update_date | update_time | user_id | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | 2026-05-30T20:21:06+08:00 | 1780143666289 | 0abe79f9423311f1ad8d38a74640adcc | ffe64fae423411f1a2d938a74640adcc | 8d758cd14a8b4ba8ab505003fb52017d | | COMPLETED | 2026-05-30T20:21:26+08:00 | 1780143686431 | 2ba4881420fa11f19e9c38a74640adcc | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ # Delete an ingestion task RAGFlow(api/default)> remove ingestion tasks 'f366450a27d54677aec1c7090add30f0'; +---------+----------------------------------+ | remove | task_id | +---------+----------------------------------+ | success | f366450a27d54677aec1c7090add30f0 | +---------+----------------------------------+ ``` ### Type of change - [x] New Feature (non-breaking change which adds functionality) --------- Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-06-12 14:56:44 +08:00
}
instanceName := p.curToken.Value
p.nextToken()
Go: use NATS as the message queue (#15327) ### What problem does this PR solve? ``` RAGFlow(admin)> mq publish 'msg2'; SUCCESS RAGFlow(admin)> mq publish 'msg3'; SUCCESS RAGFlow(admin)> mq list; +---------+---------------+ | message | subject | +---------+---------------+ | msg1 | tasks.RAGFLOW | | msg2 | tasks.RAGFLOW | | msg3 | tasks.RAGFLOW | +---------+---------------+ RAGFlow(admin)> mq pull 2; +---------+---------------+ | message | subject | +---------+---------------+ | msg1 | tasks.RAGFLOW | | msg2 | tasks.RAGFLOW | +---------+---------------+ RAGFlow(admin)> mq pull noack; +---------+---------------+ | message | subject | +---------+---------------+ | abc | tasks.RAGFLOW | +---------+---------------+ RAGFlow(admin)> mq show +-------------------+----------------+--------+---------------+---------------+-------------------+---------------+ | ack_pending_count | consumer_count | memory | message_count | pending_count | redelivered_count | waiting_count | +-------------------+----------------+--------+---------------+---------------+-------------------+---------------+ | 2 | 1 | 0 | 2 | 0 | 1 | 0 | +-------------------+----------------+--------+---------------+---------------+-------------------+---------------+ RAGFlow(admin)> list ingestors; +--------------+-------------------------------------------+--------+ | host | name | status | +--------------+-------------------------------------------+--------+ | 192.168.1.38 | ingestor-8f0e4bd5650a4ac58b0151969fbf6935 | alive | +--------------+-------------------------------------------+--------+ RAGFlow(admin)> list ingestion tasks; +----------------------------------+----------------------------------+-----------+------+-------------+----------------------------------+ | document_id | id | status | step | user | user_id | +----------------------------------+----------------------------------+-----------+------+-------------+----------------------------------+ | ffe64fae423411f1a2d938a74640adcc | 90d3d0f6528941c1ac8eb0360effccc4 | COMPLETED | 5 | aaa@aaa.com | 2ba4881420fa11f19e9c38a74640adcc | +----------------------------------+----------------------------------+-----------+------+-------------+----------------------------------+ RAGFlow(admin)> remove ingestion tasks '90d3d0f6528941c1ac8eb0360effccc4'; +---------+----------------------------------+ | delete | task_id | +---------+----------------------------------+ | success | 90d3d0f6528941c1ac8eb0360effccc4 | +---------+----------------------------------+ RAGFlow(admin)> stop ingestion tasks 'e89e20d9a25848a1b79bd9345ddbfe1d'; +----------+----------------------------------+ | status | task_id | +----------+----------------------------------+ | STOPPING | e89e20d9a25848a1b79bd9345ddbfe1d | +----------+----------------------------------+ # Publish a message RAGFlow(admin)> mq publish 'cdd'; SUCCESS # List current tasks in the message queue RAGFlow(admin)> mq list +----------------------------------+---------------+ | message | subject | +----------------------------------+---------------+ | 7ce392a3c1624cd2be4b5276e8825059 | tasks.RAGFLOW | +----------------------------------+---------------+ # Consume a task from the message queue RAGFlow(admin)> mq pull +------+-----+----------------+ | ack | id | type | +------+-----+----------------+ | true | cdd | ingestion_test | +------+-----+----------------+ # User mode # List ingestion tasks, followed by dataset id RAGFlow(user)> list ingestion tasks from '0abe79f9423311f1ad8d38a74640adcc'; +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | create_date | create_time | dataset_id | document_id | id | schema | status | update_date | update_time | user_id | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | 2026-05-30T20:21:06+08:00 | 1780143666289 | 0abe79f9423311f1ad8d38a74640adcc | ffe64fae423411f1a2d938a74640adcc | 8d758cd14a8b4ba8ab505003fb52017d | | COMPLETED | 2026-05-30T20:21:26+08:00 | 1780143686431 | 2ba4881420fa11f19e9c38a74640adcc | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ RAGFlow(user)> list ingestion tasks; +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | create_date | create_time | dataset_id | document_id | id | schema | status | update_date | update_time | user_id | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | 2026-06-02T19:02:31+08:00 | 1780398151417 | 0abe79f9423311f1ad8d38a74640adcc | ffe64fae423411f1a2d938a74640adcc | e89e20d9a25848a1b79bd9345ddbfe1d | | COMPLETED | 2026-06-02T19:02:52+08:00 | 1780398172208 | 2ba4881420fa11f19e9c38a74640adcc | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ # Create an ingestion task # First argument is document id, second argument is dataset id RAGFlow(user)> start ingestion 'ffe64fae423411f1a2d938a74640adcc' from '0abe79f9423311f1ad8d38a74640adcc'; +----------------------------------+-------------------------------------------+ | document_id | result | +----------------------------------+-------------------------------------------+ | ffe64fae423411f1a2d938a74640adcc | task_id: 8d758cd14a8b4ba8ab505003fb52017d | +----------------------------------+-------------------------------------------+ # Pause an ingestion task, first argument is ingestion id RAGFlow(user)> stop ingestion '8d758cd14a8b4ba8ab505003fb52017d'; +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | create_date | create_time | dataset_id | document_id | id | schema | status | update_date | update_time | user_id | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | 2026-05-30T20:21:06+08:00 | 1780143666289 | 0abe79f9423311f1ad8d38a74640adcc | ffe64fae423411f1a2d938a74640adcc | 8d758cd14a8b4ba8ab505003fb52017d | | COMPLETED | 2026-05-30T20:21:26+08:00 | 1780143686431 | 2ba4881420fa11f19e9c38a74640adcc | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ # Delete an ingestion task RAGFlow(api/default)> remove ingestion tasks 'f366450a27d54677aec1c7090add30f0'; +---------+----------------------------------+ | remove | task_id | +---------+----------------------------------+ | success | f366450a27d54677aec1c7090add30f0 | +---------+----------------------------------+ ``` ### Type of change - [x] New Feature (non-breaking change which adds functionality) --------- Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-06-12 14:56:44 +08:00
if p.curToken.Type != TokenFrom {
return nil, fmt.Errorf("expected FROM after instance name")
}
p.nextToken()
Go: use NATS as the message queue (#15327) ### What problem does this PR solve? ``` RAGFlow(admin)> mq publish 'msg2'; SUCCESS RAGFlow(admin)> mq publish 'msg3'; SUCCESS RAGFlow(admin)> mq list; +---------+---------------+ | message | subject | +---------+---------------+ | msg1 | tasks.RAGFLOW | | msg2 | tasks.RAGFLOW | | msg3 | tasks.RAGFLOW | +---------+---------------+ RAGFlow(admin)> mq pull 2; +---------+---------------+ | message | subject | +---------+---------------+ | msg1 | tasks.RAGFLOW | | msg2 | tasks.RAGFLOW | +---------+---------------+ RAGFlow(admin)> mq pull noack; +---------+---------------+ | message | subject | +---------+---------------+ | abc | tasks.RAGFLOW | +---------+---------------+ RAGFlow(admin)> mq show +-------------------+----------------+--------+---------------+---------------+-------------------+---------------+ | ack_pending_count | consumer_count | memory | message_count | pending_count | redelivered_count | waiting_count | +-------------------+----------------+--------+---------------+---------------+-------------------+---------------+ | 2 | 1 | 0 | 2 | 0 | 1 | 0 | +-------------------+----------------+--------+---------------+---------------+-------------------+---------------+ RAGFlow(admin)> list ingestors; +--------------+-------------------------------------------+--------+ | host | name | status | +--------------+-------------------------------------------+--------+ | 192.168.1.38 | ingestor-8f0e4bd5650a4ac58b0151969fbf6935 | alive | +--------------+-------------------------------------------+--------+ RAGFlow(admin)> list ingestion tasks; +----------------------------------+----------------------------------+-----------+------+-------------+----------------------------------+ | document_id | id | status | step | user | user_id | +----------------------------------+----------------------------------+-----------+------+-------------+----------------------------------+ | ffe64fae423411f1a2d938a74640adcc | 90d3d0f6528941c1ac8eb0360effccc4 | COMPLETED | 5 | aaa@aaa.com | 2ba4881420fa11f19e9c38a74640adcc | +----------------------------------+----------------------------------+-----------+------+-------------+----------------------------------+ RAGFlow(admin)> remove ingestion tasks '90d3d0f6528941c1ac8eb0360effccc4'; +---------+----------------------------------+ | delete | task_id | +---------+----------------------------------+ | success | 90d3d0f6528941c1ac8eb0360effccc4 | +---------+----------------------------------+ RAGFlow(admin)> stop ingestion tasks 'e89e20d9a25848a1b79bd9345ddbfe1d'; +----------+----------------------------------+ | status | task_id | +----------+----------------------------------+ | STOPPING | e89e20d9a25848a1b79bd9345ddbfe1d | +----------+----------------------------------+ # Publish a message RAGFlow(admin)> mq publish 'cdd'; SUCCESS # List current tasks in the message queue RAGFlow(admin)> mq list +----------------------------------+---------------+ | message | subject | +----------------------------------+---------------+ | 7ce392a3c1624cd2be4b5276e8825059 | tasks.RAGFLOW | +----------------------------------+---------------+ # Consume a task from the message queue RAGFlow(admin)> mq pull +------+-----+----------------+ | ack | id | type | +------+-----+----------------+ | true | cdd | ingestion_test | +------+-----+----------------+ # User mode # List ingestion tasks, followed by dataset id RAGFlow(user)> list ingestion tasks from '0abe79f9423311f1ad8d38a74640adcc'; +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | create_date | create_time | dataset_id | document_id | id | schema | status | update_date | update_time | user_id | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | 2026-05-30T20:21:06+08:00 | 1780143666289 | 0abe79f9423311f1ad8d38a74640adcc | ffe64fae423411f1a2d938a74640adcc | 8d758cd14a8b4ba8ab505003fb52017d | | COMPLETED | 2026-05-30T20:21:26+08:00 | 1780143686431 | 2ba4881420fa11f19e9c38a74640adcc | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ RAGFlow(user)> list ingestion tasks; +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | create_date | create_time | dataset_id | document_id | id | schema | status | update_date | update_time | user_id | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | 2026-06-02T19:02:31+08:00 | 1780398151417 | 0abe79f9423311f1ad8d38a74640adcc | ffe64fae423411f1a2d938a74640adcc | e89e20d9a25848a1b79bd9345ddbfe1d | | COMPLETED | 2026-06-02T19:02:52+08:00 | 1780398172208 | 2ba4881420fa11f19e9c38a74640adcc | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ # Create an ingestion task # First argument is document id, second argument is dataset id RAGFlow(user)> start ingestion 'ffe64fae423411f1a2d938a74640adcc' from '0abe79f9423311f1ad8d38a74640adcc'; +----------------------------------+-------------------------------------------+ | document_id | result | +----------------------------------+-------------------------------------------+ | ffe64fae423411f1a2d938a74640adcc | task_id: 8d758cd14a8b4ba8ab505003fb52017d | +----------------------------------+-------------------------------------------+ # Pause an ingestion task, first argument is ingestion id RAGFlow(user)> stop ingestion '8d758cd14a8b4ba8ab505003fb52017d'; +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | create_date | create_time | dataset_id | document_id | id | schema | status | update_date | update_time | user_id | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | 2026-05-30T20:21:06+08:00 | 1780143666289 | 0abe79f9423311f1ad8d38a74640adcc | ffe64fae423411f1a2d938a74640adcc | 8d758cd14a8b4ba8ab505003fb52017d | | COMPLETED | 2026-05-30T20:21:26+08:00 | 1780143686431 | 2ba4881420fa11f19e9c38a74640adcc | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ # Delete an ingestion task RAGFlow(api/default)> remove ingestion tasks 'f366450a27d54677aec1c7090add30f0'; +---------+----------------------------------+ | remove | task_id | +---------+----------------------------------+ | success | f366450a27d54677aec1c7090add30f0 | +---------+----------------------------------+ ``` ### Type of change - [x] New Feature (non-breaking change which adds functionality) --------- Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-06-12 14:56:44 +08:00
if p.curToken.Type != TokenQuotedString {
return nil, fmt.Errorf("expected provider name after FROM")
Go: use NATS as the message queue (#15327) ### What problem does this PR solve? ``` RAGFlow(admin)> mq publish 'msg2'; SUCCESS RAGFlow(admin)> mq publish 'msg3'; SUCCESS RAGFlow(admin)> mq list; +---------+---------------+ | message | subject | +---------+---------------+ | msg1 | tasks.RAGFLOW | | msg2 | tasks.RAGFLOW | | msg3 | tasks.RAGFLOW | +---------+---------------+ RAGFlow(admin)> mq pull 2; +---------+---------------+ | message | subject | +---------+---------------+ | msg1 | tasks.RAGFLOW | | msg2 | tasks.RAGFLOW | +---------+---------------+ RAGFlow(admin)> mq pull noack; +---------+---------------+ | message | subject | +---------+---------------+ | abc | tasks.RAGFLOW | +---------+---------------+ RAGFlow(admin)> mq show +-------------------+----------------+--------+---------------+---------------+-------------------+---------------+ | ack_pending_count | consumer_count | memory | message_count | pending_count | redelivered_count | waiting_count | +-------------------+----------------+--------+---------------+---------------+-------------------+---------------+ | 2 | 1 | 0 | 2 | 0 | 1 | 0 | +-------------------+----------------+--------+---------------+---------------+-------------------+---------------+ RAGFlow(admin)> list ingestors; +--------------+-------------------------------------------+--------+ | host | name | status | +--------------+-------------------------------------------+--------+ | 192.168.1.38 | ingestor-8f0e4bd5650a4ac58b0151969fbf6935 | alive | +--------------+-------------------------------------------+--------+ RAGFlow(admin)> list ingestion tasks; +----------------------------------+----------------------------------+-----------+------+-------------+----------------------------------+ | document_id | id | status | step | user | user_id | +----------------------------------+----------------------------------+-----------+------+-------------+----------------------------------+ | ffe64fae423411f1a2d938a74640adcc | 90d3d0f6528941c1ac8eb0360effccc4 | COMPLETED | 5 | aaa@aaa.com | 2ba4881420fa11f19e9c38a74640adcc | +----------------------------------+----------------------------------+-----------+------+-------------+----------------------------------+ RAGFlow(admin)> remove ingestion tasks '90d3d0f6528941c1ac8eb0360effccc4'; +---------+----------------------------------+ | delete | task_id | +---------+----------------------------------+ | success | 90d3d0f6528941c1ac8eb0360effccc4 | +---------+----------------------------------+ RAGFlow(admin)> stop ingestion tasks 'e89e20d9a25848a1b79bd9345ddbfe1d'; +----------+----------------------------------+ | status | task_id | +----------+----------------------------------+ | STOPPING | e89e20d9a25848a1b79bd9345ddbfe1d | +----------+----------------------------------+ # Publish a message RAGFlow(admin)> mq publish 'cdd'; SUCCESS # List current tasks in the message queue RAGFlow(admin)> mq list +----------------------------------+---------------+ | message | subject | +----------------------------------+---------------+ | 7ce392a3c1624cd2be4b5276e8825059 | tasks.RAGFLOW | +----------------------------------+---------------+ # Consume a task from the message queue RAGFlow(admin)> mq pull +------+-----+----------------+ | ack | id | type | +------+-----+----------------+ | true | cdd | ingestion_test | +------+-----+----------------+ # User mode # List ingestion tasks, followed by dataset id RAGFlow(user)> list ingestion tasks from '0abe79f9423311f1ad8d38a74640adcc'; +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | create_date | create_time | dataset_id | document_id | id | schema | status | update_date | update_time | user_id | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | 2026-05-30T20:21:06+08:00 | 1780143666289 | 0abe79f9423311f1ad8d38a74640adcc | ffe64fae423411f1a2d938a74640adcc | 8d758cd14a8b4ba8ab505003fb52017d | | COMPLETED | 2026-05-30T20:21:26+08:00 | 1780143686431 | 2ba4881420fa11f19e9c38a74640adcc | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ RAGFlow(user)> list ingestion tasks; +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | create_date | create_time | dataset_id | document_id | id | schema | status | update_date | update_time | user_id | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | 2026-06-02T19:02:31+08:00 | 1780398151417 | 0abe79f9423311f1ad8d38a74640adcc | ffe64fae423411f1a2d938a74640adcc | e89e20d9a25848a1b79bd9345ddbfe1d | | COMPLETED | 2026-06-02T19:02:52+08:00 | 1780398172208 | 2ba4881420fa11f19e9c38a74640adcc | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ # Create an ingestion task # First argument is document id, second argument is dataset id RAGFlow(user)> start ingestion 'ffe64fae423411f1a2d938a74640adcc' from '0abe79f9423311f1ad8d38a74640adcc'; +----------------------------------+-------------------------------------------+ | document_id | result | +----------------------------------+-------------------------------------------+ | ffe64fae423411f1a2d938a74640adcc | task_id: 8d758cd14a8b4ba8ab505003fb52017d | +----------------------------------+-------------------------------------------+ # Pause an ingestion task, first argument is ingestion id RAGFlow(user)> stop ingestion '8d758cd14a8b4ba8ab505003fb52017d'; +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | create_date | create_time | dataset_id | document_id | id | schema | status | update_date | update_time | user_id | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | 2026-05-30T20:21:06+08:00 | 1780143666289 | 0abe79f9423311f1ad8d38a74640adcc | ffe64fae423411f1a2d938a74640adcc | 8d758cd14a8b4ba8ab505003fb52017d | | COMPLETED | 2026-05-30T20:21:26+08:00 | 1780143686431 | 2ba4881420fa11f19e9c38a74640adcc | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ # Delete an ingestion task RAGFlow(api/default)> remove ingestion tasks 'f366450a27d54677aec1c7090add30f0'; +---------+----------------------------------+ | remove | task_id | +---------+----------------------------------+ | success | f366450a27d54677aec1c7090add30f0 | +---------+----------------------------------+ ``` ### Type of change - [x] New Feature (non-breaking change which adds functionality) --------- Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-06-12 14:56:44 +08:00
}
providerName := p.curToken.Value
p.nextToken()
Go: use NATS as the message queue (#15327) ### What problem does this PR solve? ``` RAGFlow(admin)> mq publish 'msg2'; SUCCESS RAGFlow(admin)> mq publish 'msg3'; SUCCESS RAGFlow(admin)> mq list; +---------+---------------+ | message | subject | +---------+---------------+ | msg1 | tasks.RAGFLOW | | msg2 | tasks.RAGFLOW | | msg3 | tasks.RAGFLOW | +---------+---------------+ RAGFlow(admin)> mq pull 2; +---------+---------------+ | message | subject | +---------+---------------+ | msg1 | tasks.RAGFLOW | | msg2 | tasks.RAGFLOW | +---------+---------------+ RAGFlow(admin)> mq pull noack; +---------+---------------+ | message | subject | +---------+---------------+ | abc | tasks.RAGFLOW | +---------+---------------+ RAGFlow(admin)> mq show +-------------------+----------------+--------+---------------+---------------+-------------------+---------------+ | ack_pending_count | consumer_count | memory | message_count | pending_count | redelivered_count | waiting_count | +-------------------+----------------+--------+---------------+---------------+-------------------+---------------+ | 2 | 1 | 0 | 2 | 0 | 1 | 0 | +-------------------+----------------+--------+---------------+---------------+-------------------+---------------+ RAGFlow(admin)> list ingestors; +--------------+-------------------------------------------+--------+ | host | name | status | +--------------+-------------------------------------------+--------+ | 192.168.1.38 | ingestor-8f0e4bd5650a4ac58b0151969fbf6935 | alive | +--------------+-------------------------------------------+--------+ RAGFlow(admin)> list ingestion tasks; +----------------------------------+----------------------------------+-----------+------+-------------+----------------------------------+ | document_id | id | status | step | user | user_id | +----------------------------------+----------------------------------+-----------+------+-------------+----------------------------------+ | ffe64fae423411f1a2d938a74640adcc | 90d3d0f6528941c1ac8eb0360effccc4 | COMPLETED | 5 | aaa@aaa.com | 2ba4881420fa11f19e9c38a74640adcc | +----------------------------------+----------------------------------+-----------+------+-------------+----------------------------------+ RAGFlow(admin)> remove ingestion tasks '90d3d0f6528941c1ac8eb0360effccc4'; +---------+----------------------------------+ | delete | task_id | +---------+----------------------------------+ | success | 90d3d0f6528941c1ac8eb0360effccc4 | +---------+----------------------------------+ RAGFlow(admin)> stop ingestion tasks 'e89e20d9a25848a1b79bd9345ddbfe1d'; +----------+----------------------------------+ | status | task_id | +----------+----------------------------------+ | STOPPING | e89e20d9a25848a1b79bd9345ddbfe1d | +----------+----------------------------------+ # Publish a message RAGFlow(admin)> mq publish 'cdd'; SUCCESS # List current tasks in the message queue RAGFlow(admin)> mq list +----------------------------------+---------------+ | message | subject | +----------------------------------+---------------+ | 7ce392a3c1624cd2be4b5276e8825059 | tasks.RAGFLOW | +----------------------------------+---------------+ # Consume a task from the message queue RAGFlow(admin)> mq pull +------+-----+----------------+ | ack | id | type | +------+-----+----------------+ | true | cdd | ingestion_test | +------+-----+----------------+ # User mode # List ingestion tasks, followed by dataset id RAGFlow(user)> list ingestion tasks from '0abe79f9423311f1ad8d38a74640adcc'; +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | create_date | create_time | dataset_id | document_id | id | schema | status | update_date | update_time | user_id | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | 2026-05-30T20:21:06+08:00 | 1780143666289 | 0abe79f9423311f1ad8d38a74640adcc | ffe64fae423411f1a2d938a74640adcc | 8d758cd14a8b4ba8ab505003fb52017d | | COMPLETED | 2026-05-30T20:21:26+08:00 | 1780143686431 | 2ba4881420fa11f19e9c38a74640adcc | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ RAGFlow(user)> list ingestion tasks; +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | create_date | create_time | dataset_id | document_id | id | schema | status | update_date | update_time | user_id | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | 2026-06-02T19:02:31+08:00 | 1780398151417 | 0abe79f9423311f1ad8d38a74640adcc | ffe64fae423411f1a2d938a74640adcc | e89e20d9a25848a1b79bd9345ddbfe1d | | COMPLETED | 2026-06-02T19:02:52+08:00 | 1780398172208 | 2ba4881420fa11f19e9c38a74640adcc | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ # Create an ingestion task # First argument is document id, second argument is dataset id RAGFlow(user)> start ingestion 'ffe64fae423411f1a2d938a74640adcc' from '0abe79f9423311f1ad8d38a74640adcc'; +----------------------------------+-------------------------------------------+ | document_id | result | +----------------------------------+-------------------------------------------+ | ffe64fae423411f1a2d938a74640adcc | task_id: 8d758cd14a8b4ba8ab505003fb52017d | +----------------------------------+-------------------------------------------+ # Pause an ingestion task, first argument is ingestion id RAGFlow(user)> stop ingestion '8d758cd14a8b4ba8ab505003fb52017d'; +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | create_date | create_time | dataset_id | document_id | id | schema | status | update_date | update_time | user_id | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | 2026-05-30T20:21:06+08:00 | 1780143666289 | 0abe79f9423311f1ad8d38a74640adcc | ffe64fae423411f1a2d938a74640adcc | 8d758cd14a8b4ba8ab505003fb52017d | | COMPLETED | 2026-05-30T20:21:26+08:00 | 1780143686431 | 2ba4881420fa11f19e9c38a74640adcc | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ # Delete an ingestion task RAGFlow(api/default)> remove ingestion tasks 'f366450a27d54677aec1c7090add30f0'; +---------+----------------------------------+ | remove | task_id | +---------+----------------------------------+ | success | f366450a27d54677aec1c7090add30f0 | +---------+----------------------------------+ ``` ### Type of change - [x] New Feature (non-breaking change which adds functionality) --------- Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-06-12 14:56:44 +08:00
2026-06-25 20:36:50 +08:00
// Semicolon is optional
Go: use NATS as the message queue (#15327) ### What problem does this PR solve? ``` RAGFlow(admin)> mq publish 'msg2'; SUCCESS RAGFlow(admin)> mq publish 'msg3'; SUCCESS RAGFlow(admin)> mq list; +---------+---------------+ | message | subject | +---------+---------------+ | msg1 | tasks.RAGFLOW | | msg2 | tasks.RAGFLOW | | msg3 | tasks.RAGFLOW | +---------+---------------+ RAGFlow(admin)> mq pull 2; +---------+---------------+ | message | subject | +---------+---------------+ | msg1 | tasks.RAGFLOW | | msg2 | tasks.RAGFLOW | +---------+---------------+ RAGFlow(admin)> mq pull noack; +---------+---------------+ | message | subject | +---------+---------------+ | abc | tasks.RAGFLOW | +---------+---------------+ RAGFlow(admin)> mq show +-------------------+----------------+--------+---------------+---------------+-------------------+---------------+ | ack_pending_count | consumer_count | memory | message_count | pending_count | redelivered_count | waiting_count | +-------------------+----------------+--------+---------------+---------------+-------------------+---------------+ | 2 | 1 | 0 | 2 | 0 | 1 | 0 | +-------------------+----------------+--------+---------------+---------------+-------------------+---------------+ RAGFlow(admin)> list ingestors; +--------------+-------------------------------------------+--------+ | host | name | status | +--------------+-------------------------------------------+--------+ | 192.168.1.38 | ingestor-8f0e4bd5650a4ac58b0151969fbf6935 | alive | +--------------+-------------------------------------------+--------+ RAGFlow(admin)> list ingestion tasks; +----------------------------------+----------------------------------+-----------+------+-------------+----------------------------------+ | document_id | id | status | step | user | user_id | +----------------------------------+----------------------------------+-----------+------+-------------+----------------------------------+ | ffe64fae423411f1a2d938a74640adcc | 90d3d0f6528941c1ac8eb0360effccc4 | COMPLETED | 5 | aaa@aaa.com | 2ba4881420fa11f19e9c38a74640adcc | +----------------------------------+----------------------------------+-----------+------+-------------+----------------------------------+ RAGFlow(admin)> remove ingestion tasks '90d3d0f6528941c1ac8eb0360effccc4'; +---------+----------------------------------+ | delete | task_id | +---------+----------------------------------+ | success | 90d3d0f6528941c1ac8eb0360effccc4 | +---------+----------------------------------+ RAGFlow(admin)> stop ingestion tasks 'e89e20d9a25848a1b79bd9345ddbfe1d'; +----------+----------------------------------+ | status | task_id | +----------+----------------------------------+ | STOPPING | e89e20d9a25848a1b79bd9345ddbfe1d | +----------+----------------------------------+ # Publish a message RAGFlow(admin)> mq publish 'cdd'; SUCCESS # List current tasks in the message queue RAGFlow(admin)> mq list +----------------------------------+---------------+ | message | subject | +----------------------------------+---------------+ | 7ce392a3c1624cd2be4b5276e8825059 | tasks.RAGFLOW | +----------------------------------+---------------+ # Consume a task from the message queue RAGFlow(admin)> mq pull +------+-----+----------------+ | ack | id | type | +------+-----+----------------+ | true | cdd | ingestion_test | +------+-----+----------------+ # User mode # List ingestion tasks, followed by dataset id RAGFlow(user)> list ingestion tasks from '0abe79f9423311f1ad8d38a74640adcc'; +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | create_date | create_time | dataset_id | document_id | id | schema | status | update_date | update_time | user_id | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | 2026-05-30T20:21:06+08:00 | 1780143666289 | 0abe79f9423311f1ad8d38a74640adcc | ffe64fae423411f1a2d938a74640adcc | 8d758cd14a8b4ba8ab505003fb52017d | | COMPLETED | 2026-05-30T20:21:26+08:00 | 1780143686431 | 2ba4881420fa11f19e9c38a74640adcc | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ RAGFlow(user)> list ingestion tasks; +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | create_date | create_time | dataset_id | document_id | id | schema | status | update_date | update_time | user_id | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | 2026-06-02T19:02:31+08:00 | 1780398151417 | 0abe79f9423311f1ad8d38a74640adcc | ffe64fae423411f1a2d938a74640adcc | e89e20d9a25848a1b79bd9345ddbfe1d | | COMPLETED | 2026-06-02T19:02:52+08:00 | 1780398172208 | 2ba4881420fa11f19e9c38a74640adcc | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ # Create an ingestion task # First argument is document id, second argument is dataset id RAGFlow(user)> start ingestion 'ffe64fae423411f1a2d938a74640adcc' from '0abe79f9423311f1ad8d38a74640adcc'; +----------------------------------+-------------------------------------------+ | document_id | result | +----------------------------------+-------------------------------------------+ | ffe64fae423411f1a2d938a74640adcc | task_id: 8d758cd14a8b4ba8ab505003fb52017d | +----------------------------------+-------------------------------------------+ # Pause an ingestion task, first argument is ingestion id RAGFlow(user)> stop ingestion '8d758cd14a8b4ba8ab505003fb52017d'; +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | create_date | create_time | dataset_id | document_id | id | schema | status | update_date | update_time | user_id | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | 2026-05-30T20:21:06+08:00 | 1780143666289 | 0abe79f9423311f1ad8d38a74640adcc | ffe64fae423411f1a2d938a74640adcc | 8d758cd14a8b4ba8ab505003fb52017d | | COMPLETED | 2026-05-30T20:21:26+08:00 | 1780143686431 | 2ba4881420fa11f19e9c38a74640adcc | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ # Delete an ingestion task RAGFlow(api/default)> remove ingestion tasks 'f366450a27d54677aec1c7090add30f0'; +---------+----------------------------------+ | remove | task_id | +---------+----------------------------------+ | success | f366450a27d54677aec1c7090add30f0 | +---------+----------------------------------+ ``` ### Type of change - [x] New Feature (non-breaking change which adds functionality) --------- Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-06-12 14:56:44 +08:00
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
cmd := NewCommand("check_provider_connection")
cmd.Params["provider_name"] = providerName
cmd.Params["instance_name"] = instanceName
Go: use NATS as the message queue (#15327) ### What problem does this PR solve? ``` RAGFlow(admin)> mq publish 'msg2'; SUCCESS RAGFlow(admin)> mq publish 'msg3'; SUCCESS RAGFlow(admin)> mq list; +---------+---------------+ | message | subject | +---------+---------------+ | msg1 | tasks.RAGFLOW | | msg2 | tasks.RAGFLOW | | msg3 | tasks.RAGFLOW | +---------+---------------+ RAGFlow(admin)> mq pull 2; +---------+---------------+ | message | subject | +---------+---------------+ | msg1 | tasks.RAGFLOW | | msg2 | tasks.RAGFLOW | +---------+---------------+ RAGFlow(admin)> mq pull noack; +---------+---------------+ | message | subject | +---------+---------------+ | abc | tasks.RAGFLOW | +---------+---------------+ RAGFlow(admin)> mq show +-------------------+----------------+--------+---------------+---------------+-------------------+---------------+ | ack_pending_count | consumer_count | memory | message_count | pending_count | redelivered_count | waiting_count | +-------------------+----------------+--------+---------------+---------------+-------------------+---------------+ | 2 | 1 | 0 | 2 | 0 | 1 | 0 | +-------------------+----------------+--------+---------------+---------------+-------------------+---------------+ RAGFlow(admin)> list ingestors; +--------------+-------------------------------------------+--------+ | host | name | status | +--------------+-------------------------------------------+--------+ | 192.168.1.38 | ingestor-8f0e4bd5650a4ac58b0151969fbf6935 | alive | +--------------+-------------------------------------------+--------+ RAGFlow(admin)> list ingestion tasks; +----------------------------------+----------------------------------+-----------+------+-------------+----------------------------------+ | document_id | id | status | step | user | user_id | +----------------------------------+----------------------------------+-----------+------+-------------+----------------------------------+ | ffe64fae423411f1a2d938a74640adcc | 90d3d0f6528941c1ac8eb0360effccc4 | COMPLETED | 5 | aaa@aaa.com | 2ba4881420fa11f19e9c38a74640adcc | +----------------------------------+----------------------------------+-----------+------+-------------+----------------------------------+ RAGFlow(admin)> remove ingestion tasks '90d3d0f6528941c1ac8eb0360effccc4'; +---------+----------------------------------+ | delete | task_id | +---------+----------------------------------+ | success | 90d3d0f6528941c1ac8eb0360effccc4 | +---------+----------------------------------+ RAGFlow(admin)> stop ingestion tasks 'e89e20d9a25848a1b79bd9345ddbfe1d'; +----------+----------------------------------+ | status | task_id | +----------+----------------------------------+ | STOPPING | e89e20d9a25848a1b79bd9345ddbfe1d | +----------+----------------------------------+ # Publish a message RAGFlow(admin)> mq publish 'cdd'; SUCCESS # List current tasks in the message queue RAGFlow(admin)> mq list +----------------------------------+---------------+ | message | subject | +----------------------------------+---------------+ | 7ce392a3c1624cd2be4b5276e8825059 | tasks.RAGFLOW | +----------------------------------+---------------+ # Consume a task from the message queue RAGFlow(admin)> mq pull +------+-----+----------------+ | ack | id | type | +------+-----+----------------+ | true | cdd | ingestion_test | +------+-----+----------------+ # User mode # List ingestion tasks, followed by dataset id RAGFlow(user)> list ingestion tasks from '0abe79f9423311f1ad8d38a74640adcc'; +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | create_date | create_time | dataset_id | document_id | id | schema | status | update_date | update_time | user_id | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | 2026-05-30T20:21:06+08:00 | 1780143666289 | 0abe79f9423311f1ad8d38a74640adcc | ffe64fae423411f1a2d938a74640adcc | 8d758cd14a8b4ba8ab505003fb52017d | | COMPLETED | 2026-05-30T20:21:26+08:00 | 1780143686431 | 2ba4881420fa11f19e9c38a74640adcc | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ RAGFlow(user)> list ingestion tasks; +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | create_date | create_time | dataset_id | document_id | id | schema | status | update_date | update_time | user_id | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | 2026-06-02T19:02:31+08:00 | 1780398151417 | 0abe79f9423311f1ad8d38a74640adcc | ffe64fae423411f1a2d938a74640adcc | e89e20d9a25848a1b79bd9345ddbfe1d | | COMPLETED | 2026-06-02T19:02:52+08:00 | 1780398172208 | 2ba4881420fa11f19e9c38a74640adcc | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ # Create an ingestion task # First argument is document id, second argument is dataset id RAGFlow(user)> start ingestion 'ffe64fae423411f1a2d938a74640adcc' from '0abe79f9423311f1ad8d38a74640adcc'; +----------------------------------+-------------------------------------------+ | document_id | result | +----------------------------------+-------------------------------------------+ | ffe64fae423411f1a2d938a74640adcc | task_id: 8d758cd14a8b4ba8ab505003fb52017d | +----------------------------------+-------------------------------------------+ # Pause an ingestion task, first argument is ingestion id RAGFlow(user)> stop ingestion '8d758cd14a8b4ba8ab505003fb52017d'; +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | create_date | create_time | dataset_id | document_id | id | schema | status | update_date | update_time | user_id | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | 2026-05-30T20:21:06+08:00 | 1780143666289 | 0abe79f9423311f1ad8d38a74640adcc | ffe64fae423411f1a2d938a74640adcc | 8d758cd14a8b4ba8ab505003fb52017d | | COMPLETED | 2026-05-30T20:21:26+08:00 | 1780143686431 | 2ba4881420fa11f19e9c38a74640adcc | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ # Delete an ingestion task RAGFlow(api/default)> remove ingestion tasks 'f366450a27d54677aec1c7090add30f0'; +---------+----------------------------------+ | remove | task_id | +---------+----------------------------------+ | success | f366450a27d54677aec1c7090add30f0 | +---------+----------------------------------+ ``` ### Type of change - [x] New Feature (non-breaking change which adds functionality) --------- Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-06-12 14:56:44 +08:00
return cmd, nil
}
func (p *Parser) parseCheckProviderByKeyCommand() (*Command, error) {
if p.curToken.Type != TokenProvider {
return nil, fmt.Errorf("expected PROVIDER after CHECK")
Go: use NATS as the message queue (#15327) ### What problem does this PR solve? ``` RAGFlow(admin)> mq publish 'msg2'; SUCCESS RAGFlow(admin)> mq publish 'msg3'; SUCCESS RAGFlow(admin)> mq list; +---------+---------------+ | message | subject | +---------+---------------+ | msg1 | tasks.RAGFLOW | | msg2 | tasks.RAGFLOW | | msg3 | tasks.RAGFLOW | +---------+---------------+ RAGFlow(admin)> mq pull 2; +---------+---------------+ | message | subject | +---------+---------------+ | msg1 | tasks.RAGFLOW | | msg2 | tasks.RAGFLOW | +---------+---------------+ RAGFlow(admin)> mq pull noack; +---------+---------------+ | message | subject | +---------+---------------+ | abc | tasks.RAGFLOW | +---------+---------------+ RAGFlow(admin)> mq show +-------------------+----------------+--------+---------------+---------------+-------------------+---------------+ | ack_pending_count | consumer_count | memory | message_count | pending_count | redelivered_count | waiting_count | +-------------------+----------------+--------+---------------+---------------+-------------------+---------------+ | 2 | 1 | 0 | 2 | 0 | 1 | 0 | +-------------------+----------------+--------+---------------+---------------+-------------------+---------------+ RAGFlow(admin)> list ingestors; +--------------+-------------------------------------------+--------+ | host | name | status | +--------------+-------------------------------------------+--------+ | 192.168.1.38 | ingestor-8f0e4bd5650a4ac58b0151969fbf6935 | alive | +--------------+-------------------------------------------+--------+ RAGFlow(admin)> list ingestion tasks; +----------------------------------+----------------------------------+-----------+------+-------------+----------------------------------+ | document_id | id | status | step | user | user_id | +----------------------------------+----------------------------------+-----------+------+-------------+----------------------------------+ | ffe64fae423411f1a2d938a74640adcc | 90d3d0f6528941c1ac8eb0360effccc4 | COMPLETED | 5 | aaa@aaa.com | 2ba4881420fa11f19e9c38a74640adcc | +----------------------------------+----------------------------------+-----------+------+-------------+----------------------------------+ RAGFlow(admin)> remove ingestion tasks '90d3d0f6528941c1ac8eb0360effccc4'; +---------+----------------------------------+ | delete | task_id | +---------+----------------------------------+ | success | 90d3d0f6528941c1ac8eb0360effccc4 | +---------+----------------------------------+ RAGFlow(admin)> stop ingestion tasks 'e89e20d9a25848a1b79bd9345ddbfe1d'; +----------+----------------------------------+ | status | task_id | +----------+----------------------------------+ | STOPPING | e89e20d9a25848a1b79bd9345ddbfe1d | +----------+----------------------------------+ # Publish a message RAGFlow(admin)> mq publish 'cdd'; SUCCESS # List current tasks in the message queue RAGFlow(admin)> mq list +----------------------------------+---------------+ | message | subject | +----------------------------------+---------------+ | 7ce392a3c1624cd2be4b5276e8825059 | tasks.RAGFLOW | +----------------------------------+---------------+ # Consume a task from the message queue RAGFlow(admin)> mq pull +------+-----+----------------+ | ack | id | type | +------+-----+----------------+ | true | cdd | ingestion_test | +------+-----+----------------+ # User mode # List ingestion tasks, followed by dataset id RAGFlow(user)> list ingestion tasks from '0abe79f9423311f1ad8d38a74640adcc'; +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | create_date | create_time | dataset_id | document_id | id | schema | status | update_date | update_time | user_id | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | 2026-05-30T20:21:06+08:00 | 1780143666289 | 0abe79f9423311f1ad8d38a74640adcc | ffe64fae423411f1a2d938a74640adcc | 8d758cd14a8b4ba8ab505003fb52017d | | COMPLETED | 2026-05-30T20:21:26+08:00 | 1780143686431 | 2ba4881420fa11f19e9c38a74640adcc | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ RAGFlow(user)> list ingestion tasks; +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | create_date | create_time | dataset_id | document_id | id | schema | status | update_date | update_time | user_id | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | 2026-06-02T19:02:31+08:00 | 1780398151417 | 0abe79f9423311f1ad8d38a74640adcc | ffe64fae423411f1a2d938a74640adcc | e89e20d9a25848a1b79bd9345ddbfe1d | | COMPLETED | 2026-06-02T19:02:52+08:00 | 1780398172208 | 2ba4881420fa11f19e9c38a74640adcc | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ # Create an ingestion task # First argument is document id, second argument is dataset id RAGFlow(user)> start ingestion 'ffe64fae423411f1a2d938a74640adcc' from '0abe79f9423311f1ad8d38a74640adcc'; +----------------------------------+-------------------------------------------+ | document_id | result | +----------------------------------+-------------------------------------------+ | ffe64fae423411f1a2d938a74640adcc | task_id: 8d758cd14a8b4ba8ab505003fb52017d | +----------------------------------+-------------------------------------------+ # Pause an ingestion task, first argument is ingestion id RAGFlow(user)> stop ingestion '8d758cd14a8b4ba8ab505003fb52017d'; +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | create_date | create_time | dataset_id | document_id | id | schema | status | update_date | update_time | user_id | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | 2026-05-30T20:21:06+08:00 | 1780143666289 | 0abe79f9423311f1ad8d38a74640adcc | ffe64fae423411f1a2d938a74640adcc | 8d758cd14a8b4ba8ab505003fb52017d | | COMPLETED | 2026-05-30T20:21:26+08:00 | 1780143686431 | 2ba4881420fa11f19e9c38a74640adcc | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ # Delete an ingestion task RAGFlow(api/default)> remove ingestion tasks 'f366450a27d54677aec1c7090add30f0'; +---------+----------------------------------+ | remove | task_id | +---------+----------------------------------+ | success | f366450a27d54677aec1c7090add30f0 | +---------+----------------------------------+ ``` ### Type of change - [x] New Feature (non-breaking change which adds functionality) --------- Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-06-12 14:56:44 +08:00
}
p.nextToken()
Go: use NATS as the message queue (#15327) ### What problem does this PR solve? ``` RAGFlow(admin)> mq publish 'msg2'; SUCCESS RAGFlow(admin)> mq publish 'msg3'; SUCCESS RAGFlow(admin)> mq list; +---------+---------------+ | message | subject | +---------+---------------+ | msg1 | tasks.RAGFLOW | | msg2 | tasks.RAGFLOW | | msg3 | tasks.RAGFLOW | +---------+---------------+ RAGFlow(admin)> mq pull 2; +---------+---------------+ | message | subject | +---------+---------------+ | msg1 | tasks.RAGFLOW | | msg2 | tasks.RAGFLOW | +---------+---------------+ RAGFlow(admin)> mq pull noack; +---------+---------------+ | message | subject | +---------+---------------+ | abc | tasks.RAGFLOW | +---------+---------------+ RAGFlow(admin)> mq show +-------------------+----------------+--------+---------------+---------------+-------------------+---------------+ | ack_pending_count | consumer_count | memory | message_count | pending_count | redelivered_count | waiting_count | +-------------------+----------------+--------+---------------+---------------+-------------------+---------------+ | 2 | 1 | 0 | 2 | 0 | 1 | 0 | +-------------------+----------------+--------+---------------+---------------+-------------------+---------------+ RAGFlow(admin)> list ingestors; +--------------+-------------------------------------------+--------+ | host | name | status | +--------------+-------------------------------------------+--------+ | 192.168.1.38 | ingestor-8f0e4bd5650a4ac58b0151969fbf6935 | alive | +--------------+-------------------------------------------+--------+ RAGFlow(admin)> list ingestion tasks; +----------------------------------+----------------------------------+-----------+------+-------------+----------------------------------+ | document_id | id | status | step | user | user_id | +----------------------------------+----------------------------------+-----------+------+-------------+----------------------------------+ | ffe64fae423411f1a2d938a74640adcc | 90d3d0f6528941c1ac8eb0360effccc4 | COMPLETED | 5 | aaa@aaa.com | 2ba4881420fa11f19e9c38a74640adcc | +----------------------------------+----------------------------------+-----------+------+-------------+----------------------------------+ RAGFlow(admin)> remove ingestion tasks '90d3d0f6528941c1ac8eb0360effccc4'; +---------+----------------------------------+ | delete | task_id | +---------+----------------------------------+ | success | 90d3d0f6528941c1ac8eb0360effccc4 | +---------+----------------------------------+ RAGFlow(admin)> stop ingestion tasks 'e89e20d9a25848a1b79bd9345ddbfe1d'; +----------+----------------------------------+ | status | task_id | +----------+----------------------------------+ | STOPPING | e89e20d9a25848a1b79bd9345ddbfe1d | +----------+----------------------------------+ # Publish a message RAGFlow(admin)> mq publish 'cdd'; SUCCESS # List current tasks in the message queue RAGFlow(admin)> mq list +----------------------------------+---------------+ | message | subject | +----------------------------------+---------------+ | 7ce392a3c1624cd2be4b5276e8825059 | tasks.RAGFLOW | +----------------------------------+---------------+ # Consume a task from the message queue RAGFlow(admin)> mq pull +------+-----+----------------+ | ack | id | type | +------+-----+----------------+ | true | cdd | ingestion_test | +------+-----+----------------+ # User mode # List ingestion tasks, followed by dataset id RAGFlow(user)> list ingestion tasks from '0abe79f9423311f1ad8d38a74640adcc'; +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | create_date | create_time | dataset_id | document_id | id | schema | status | update_date | update_time | user_id | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | 2026-05-30T20:21:06+08:00 | 1780143666289 | 0abe79f9423311f1ad8d38a74640adcc | ffe64fae423411f1a2d938a74640adcc | 8d758cd14a8b4ba8ab505003fb52017d | | COMPLETED | 2026-05-30T20:21:26+08:00 | 1780143686431 | 2ba4881420fa11f19e9c38a74640adcc | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ RAGFlow(user)> list ingestion tasks; +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | create_date | create_time | dataset_id | document_id | id | schema | status | update_date | update_time | user_id | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | 2026-06-02T19:02:31+08:00 | 1780398151417 | 0abe79f9423311f1ad8d38a74640adcc | ffe64fae423411f1a2d938a74640adcc | e89e20d9a25848a1b79bd9345ddbfe1d | | COMPLETED | 2026-06-02T19:02:52+08:00 | 1780398172208 | 2ba4881420fa11f19e9c38a74640adcc | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ # Create an ingestion task # First argument is document id, second argument is dataset id RAGFlow(user)> start ingestion 'ffe64fae423411f1a2d938a74640adcc' from '0abe79f9423311f1ad8d38a74640adcc'; +----------------------------------+-------------------------------------------+ | document_id | result | +----------------------------------+-------------------------------------------+ | ffe64fae423411f1a2d938a74640adcc | task_id: 8d758cd14a8b4ba8ab505003fb52017d | +----------------------------------+-------------------------------------------+ # Pause an ingestion task, first argument is ingestion id RAGFlow(user)> stop ingestion '8d758cd14a8b4ba8ab505003fb52017d'; +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | create_date | create_time | dataset_id | document_id | id | schema | status | update_date | update_time | user_id | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | 2026-05-30T20:21:06+08:00 | 1780143666289 | 0abe79f9423311f1ad8d38a74640adcc | ffe64fae423411f1a2d938a74640adcc | 8d758cd14a8b4ba8ab505003fb52017d | | COMPLETED | 2026-05-30T20:21:26+08:00 | 1780143686431 | 2ba4881420fa11f19e9c38a74640adcc | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ # Delete an ingestion task RAGFlow(api/default)> remove ingestion tasks 'f366450a27d54677aec1c7090add30f0'; +---------+----------------------------------+ | remove | task_id | +---------+----------------------------------+ | success | f366450a27d54677aec1c7090add30f0 | +---------+----------------------------------+ ``` ### Type of change - [x] New Feature (non-breaking change which adds functionality) --------- Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-06-12 14:56:44 +08:00
if p.curToken.Type != TokenQuotedString {
return nil, fmt.Errorf("expected provider name after PROVIDER")
Go: use NATS as the message queue (#15327) ### What problem does this PR solve? ``` RAGFlow(admin)> mq publish 'msg2'; SUCCESS RAGFlow(admin)> mq publish 'msg3'; SUCCESS RAGFlow(admin)> mq list; +---------+---------------+ | message | subject | +---------+---------------+ | msg1 | tasks.RAGFLOW | | msg2 | tasks.RAGFLOW | | msg3 | tasks.RAGFLOW | +---------+---------------+ RAGFlow(admin)> mq pull 2; +---------+---------------+ | message | subject | +---------+---------------+ | msg1 | tasks.RAGFLOW | | msg2 | tasks.RAGFLOW | +---------+---------------+ RAGFlow(admin)> mq pull noack; +---------+---------------+ | message | subject | +---------+---------------+ | abc | tasks.RAGFLOW | +---------+---------------+ RAGFlow(admin)> mq show +-------------------+----------------+--------+---------------+---------------+-------------------+---------------+ | ack_pending_count | consumer_count | memory | message_count | pending_count | redelivered_count | waiting_count | +-------------------+----------------+--------+---------------+---------------+-------------------+---------------+ | 2 | 1 | 0 | 2 | 0 | 1 | 0 | +-------------------+----------------+--------+---------------+---------------+-------------------+---------------+ RAGFlow(admin)> list ingestors; +--------------+-------------------------------------------+--------+ | host | name | status | +--------------+-------------------------------------------+--------+ | 192.168.1.38 | ingestor-8f0e4bd5650a4ac58b0151969fbf6935 | alive | +--------------+-------------------------------------------+--------+ RAGFlow(admin)> list ingestion tasks; +----------------------------------+----------------------------------+-----------+------+-------------+----------------------------------+ | document_id | id | status | step | user | user_id | +----------------------------------+----------------------------------+-----------+------+-------------+----------------------------------+ | ffe64fae423411f1a2d938a74640adcc | 90d3d0f6528941c1ac8eb0360effccc4 | COMPLETED | 5 | aaa@aaa.com | 2ba4881420fa11f19e9c38a74640adcc | +----------------------------------+----------------------------------+-----------+------+-------------+----------------------------------+ RAGFlow(admin)> remove ingestion tasks '90d3d0f6528941c1ac8eb0360effccc4'; +---------+----------------------------------+ | delete | task_id | +---------+----------------------------------+ | success | 90d3d0f6528941c1ac8eb0360effccc4 | +---------+----------------------------------+ RAGFlow(admin)> stop ingestion tasks 'e89e20d9a25848a1b79bd9345ddbfe1d'; +----------+----------------------------------+ | status | task_id | +----------+----------------------------------+ | STOPPING | e89e20d9a25848a1b79bd9345ddbfe1d | +----------+----------------------------------+ # Publish a message RAGFlow(admin)> mq publish 'cdd'; SUCCESS # List current tasks in the message queue RAGFlow(admin)> mq list +----------------------------------+---------------+ | message | subject | +----------------------------------+---------------+ | 7ce392a3c1624cd2be4b5276e8825059 | tasks.RAGFLOW | +----------------------------------+---------------+ # Consume a task from the message queue RAGFlow(admin)> mq pull +------+-----+----------------+ | ack | id | type | +------+-----+----------------+ | true | cdd | ingestion_test | +------+-----+----------------+ # User mode # List ingestion tasks, followed by dataset id RAGFlow(user)> list ingestion tasks from '0abe79f9423311f1ad8d38a74640adcc'; +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | create_date | create_time | dataset_id | document_id | id | schema | status | update_date | update_time | user_id | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | 2026-05-30T20:21:06+08:00 | 1780143666289 | 0abe79f9423311f1ad8d38a74640adcc | ffe64fae423411f1a2d938a74640adcc | 8d758cd14a8b4ba8ab505003fb52017d | | COMPLETED | 2026-05-30T20:21:26+08:00 | 1780143686431 | 2ba4881420fa11f19e9c38a74640adcc | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ RAGFlow(user)> list ingestion tasks; +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | create_date | create_time | dataset_id | document_id | id | schema | status | update_date | update_time | user_id | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | 2026-06-02T19:02:31+08:00 | 1780398151417 | 0abe79f9423311f1ad8d38a74640adcc | ffe64fae423411f1a2d938a74640adcc | e89e20d9a25848a1b79bd9345ddbfe1d | | COMPLETED | 2026-06-02T19:02:52+08:00 | 1780398172208 | 2ba4881420fa11f19e9c38a74640adcc | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ # Create an ingestion task # First argument is document id, second argument is dataset id RAGFlow(user)> start ingestion 'ffe64fae423411f1a2d938a74640adcc' from '0abe79f9423311f1ad8d38a74640adcc'; +----------------------------------+-------------------------------------------+ | document_id | result | +----------------------------------+-------------------------------------------+ | ffe64fae423411f1a2d938a74640adcc | task_id: 8d758cd14a8b4ba8ab505003fb52017d | +----------------------------------+-------------------------------------------+ # Pause an ingestion task, first argument is ingestion id RAGFlow(user)> stop ingestion '8d758cd14a8b4ba8ab505003fb52017d'; +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | create_date | create_time | dataset_id | document_id | id | schema | status | update_date | update_time | user_id | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | 2026-05-30T20:21:06+08:00 | 1780143666289 | 0abe79f9423311f1ad8d38a74640adcc | ffe64fae423411f1a2d938a74640adcc | 8d758cd14a8b4ba8ab505003fb52017d | | COMPLETED | 2026-05-30T20:21:26+08:00 | 1780143686431 | 2ba4881420fa11f19e9c38a74640adcc | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ # Delete an ingestion task RAGFlow(api/default)> remove ingestion tasks 'f366450a27d54677aec1c7090add30f0'; +---------+----------------------------------+ | remove | task_id | +---------+----------------------------------+ | success | f366450a27d54677aec1c7090add30f0 | +---------+----------------------------------+ ``` ### Type of change - [x] New Feature (non-breaking change which adds functionality) --------- Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-06-12 14:56:44 +08:00
}
providerName := p.curToken.Value
p.nextToken()
Go: use NATS as the message queue (#15327) ### What problem does this PR solve? ``` RAGFlow(admin)> mq publish 'msg2'; SUCCESS RAGFlow(admin)> mq publish 'msg3'; SUCCESS RAGFlow(admin)> mq list; +---------+---------------+ | message | subject | +---------+---------------+ | msg1 | tasks.RAGFLOW | | msg2 | tasks.RAGFLOW | | msg3 | tasks.RAGFLOW | +---------+---------------+ RAGFlow(admin)> mq pull 2; +---------+---------------+ | message | subject | +---------+---------------+ | msg1 | tasks.RAGFLOW | | msg2 | tasks.RAGFLOW | +---------+---------------+ RAGFlow(admin)> mq pull noack; +---------+---------------+ | message | subject | +---------+---------------+ | abc | tasks.RAGFLOW | +---------+---------------+ RAGFlow(admin)> mq show +-------------------+----------------+--------+---------------+---------------+-------------------+---------------+ | ack_pending_count | consumer_count | memory | message_count | pending_count | redelivered_count | waiting_count | +-------------------+----------------+--------+---------------+---------------+-------------------+---------------+ | 2 | 1 | 0 | 2 | 0 | 1 | 0 | +-------------------+----------------+--------+---------------+---------------+-------------------+---------------+ RAGFlow(admin)> list ingestors; +--------------+-------------------------------------------+--------+ | host | name | status | +--------------+-------------------------------------------+--------+ | 192.168.1.38 | ingestor-8f0e4bd5650a4ac58b0151969fbf6935 | alive | +--------------+-------------------------------------------+--------+ RAGFlow(admin)> list ingestion tasks; +----------------------------------+----------------------------------+-----------+------+-------------+----------------------------------+ | document_id | id | status | step | user | user_id | +----------------------------------+----------------------------------+-----------+------+-------------+----------------------------------+ | ffe64fae423411f1a2d938a74640adcc | 90d3d0f6528941c1ac8eb0360effccc4 | COMPLETED | 5 | aaa@aaa.com | 2ba4881420fa11f19e9c38a74640adcc | +----------------------------------+----------------------------------+-----------+------+-------------+----------------------------------+ RAGFlow(admin)> remove ingestion tasks '90d3d0f6528941c1ac8eb0360effccc4'; +---------+----------------------------------+ | delete | task_id | +---------+----------------------------------+ | success | 90d3d0f6528941c1ac8eb0360effccc4 | +---------+----------------------------------+ RAGFlow(admin)> stop ingestion tasks 'e89e20d9a25848a1b79bd9345ddbfe1d'; +----------+----------------------------------+ | status | task_id | +----------+----------------------------------+ | STOPPING | e89e20d9a25848a1b79bd9345ddbfe1d | +----------+----------------------------------+ # Publish a message RAGFlow(admin)> mq publish 'cdd'; SUCCESS # List current tasks in the message queue RAGFlow(admin)> mq list +----------------------------------+---------------+ | message | subject | +----------------------------------+---------------+ | 7ce392a3c1624cd2be4b5276e8825059 | tasks.RAGFLOW | +----------------------------------+---------------+ # Consume a task from the message queue RAGFlow(admin)> mq pull +------+-----+----------------+ | ack | id | type | +------+-----+----------------+ | true | cdd | ingestion_test | +------+-----+----------------+ # User mode # List ingestion tasks, followed by dataset id RAGFlow(user)> list ingestion tasks from '0abe79f9423311f1ad8d38a74640adcc'; +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | create_date | create_time | dataset_id | document_id | id | schema | status | update_date | update_time | user_id | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | 2026-05-30T20:21:06+08:00 | 1780143666289 | 0abe79f9423311f1ad8d38a74640adcc | ffe64fae423411f1a2d938a74640adcc | 8d758cd14a8b4ba8ab505003fb52017d | | COMPLETED | 2026-05-30T20:21:26+08:00 | 1780143686431 | 2ba4881420fa11f19e9c38a74640adcc | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ RAGFlow(user)> list ingestion tasks; +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | create_date | create_time | dataset_id | document_id | id | schema | status | update_date | update_time | user_id | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | 2026-06-02T19:02:31+08:00 | 1780398151417 | 0abe79f9423311f1ad8d38a74640adcc | ffe64fae423411f1a2d938a74640adcc | e89e20d9a25848a1b79bd9345ddbfe1d | | COMPLETED | 2026-06-02T19:02:52+08:00 | 1780398172208 | 2ba4881420fa11f19e9c38a74640adcc | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ # Create an ingestion task # First argument is document id, second argument is dataset id RAGFlow(user)> start ingestion 'ffe64fae423411f1a2d938a74640adcc' from '0abe79f9423311f1ad8d38a74640adcc'; +----------------------------------+-------------------------------------------+ | document_id | result | +----------------------------------+-------------------------------------------+ | ffe64fae423411f1a2d938a74640adcc | task_id: 8d758cd14a8b4ba8ab505003fb52017d | +----------------------------------+-------------------------------------------+ # Pause an ingestion task, first argument is ingestion id RAGFlow(user)> stop ingestion '8d758cd14a8b4ba8ab505003fb52017d'; +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | create_date | create_time | dataset_id | document_id | id | schema | status | update_date | update_time | user_id | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | 2026-05-30T20:21:06+08:00 | 1780143666289 | 0abe79f9423311f1ad8d38a74640adcc | ffe64fae423411f1a2d938a74640adcc | 8d758cd14a8b4ba8ab505003fb52017d | | COMPLETED | 2026-05-30T20:21:26+08:00 | 1780143686431 | 2ba4881420fa11f19e9c38a74640adcc | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ # Delete an ingestion task RAGFlow(api/default)> remove ingestion tasks 'f366450a27d54677aec1c7090add30f0'; +---------+----------------------------------+ | remove | task_id | +---------+----------------------------------+ | success | f366450a27d54677aec1c7090add30f0 | +---------+----------------------------------+ ``` ### Type of change - [x] New Feature (non-breaking change which adds functionality) --------- Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-06-12 14:56:44 +08:00
if p.curToken.Type != TokenRegion {
return nil, fmt.Errorf("expected REGION after provider name")
}
p.nextToken()
Go: use NATS as the message queue (#15327) ### What problem does this PR solve? ``` RAGFlow(admin)> mq publish 'msg2'; SUCCESS RAGFlow(admin)> mq publish 'msg3'; SUCCESS RAGFlow(admin)> mq list; +---------+---------------+ | message | subject | +---------+---------------+ | msg1 | tasks.RAGFLOW | | msg2 | tasks.RAGFLOW | | msg3 | tasks.RAGFLOW | +---------+---------------+ RAGFlow(admin)> mq pull 2; +---------+---------------+ | message | subject | +---------+---------------+ | msg1 | tasks.RAGFLOW | | msg2 | tasks.RAGFLOW | +---------+---------------+ RAGFlow(admin)> mq pull noack; +---------+---------------+ | message | subject | +---------+---------------+ | abc | tasks.RAGFLOW | +---------+---------------+ RAGFlow(admin)> mq show +-------------------+----------------+--------+---------------+---------------+-------------------+---------------+ | ack_pending_count | consumer_count | memory | message_count | pending_count | redelivered_count | waiting_count | +-------------------+----------------+--------+---------------+---------------+-------------------+---------------+ | 2 | 1 | 0 | 2 | 0 | 1 | 0 | +-------------------+----------------+--------+---------------+---------------+-------------------+---------------+ RAGFlow(admin)> list ingestors; +--------------+-------------------------------------------+--------+ | host | name | status | +--------------+-------------------------------------------+--------+ | 192.168.1.38 | ingestor-8f0e4bd5650a4ac58b0151969fbf6935 | alive | +--------------+-------------------------------------------+--------+ RAGFlow(admin)> list ingestion tasks; +----------------------------------+----------------------------------+-----------+------+-------------+----------------------------------+ | document_id | id | status | step | user | user_id | +----------------------------------+----------------------------------+-----------+------+-------------+----------------------------------+ | ffe64fae423411f1a2d938a74640adcc | 90d3d0f6528941c1ac8eb0360effccc4 | COMPLETED | 5 | aaa@aaa.com | 2ba4881420fa11f19e9c38a74640adcc | +----------------------------------+----------------------------------+-----------+------+-------------+----------------------------------+ RAGFlow(admin)> remove ingestion tasks '90d3d0f6528941c1ac8eb0360effccc4'; +---------+----------------------------------+ | delete | task_id | +---------+----------------------------------+ | success | 90d3d0f6528941c1ac8eb0360effccc4 | +---------+----------------------------------+ RAGFlow(admin)> stop ingestion tasks 'e89e20d9a25848a1b79bd9345ddbfe1d'; +----------+----------------------------------+ | status | task_id | +----------+----------------------------------+ | STOPPING | e89e20d9a25848a1b79bd9345ddbfe1d | +----------+----------------------------------+ # Publish a message RAGFlow(admin)> mq publish 'cdd'; SUCCESS # List current tasks in the message queue RAGFlow(admin)> mq list +----------------------------------+---------------+ | message | subject | +----------------------------------+---------------+ | 7ce392a3c1624cd2be4b5276e8825059 | tasks.RAGFLOW | +----------------------------------+---------------+ # Consume a task from the message queue RAGFlow(admin)> mq pull +------+-----+----------------+ | ack | id | type | +------+-----+----------------+ | true | cdd | ingestion_test | +------+-----+----------------+ # User mode # List ingestion tasks, followed by dataset id RAGFlow(user)> list ingestion tasks from '0abe79f9423311f1ad8d38a74640adcc'; +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | create_date | create_time | dataset_id | document_id | id | schema | status | update_date | update_time | user_id | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | 2026-05-30T20:21:06+08:00 | 1780143666289 | 0abe79f9423311f1ad8d38a74640adcc | ffe64fae423411f1a2d938a74640adcc | 8d758cd14a8b4ba8ab505003fb52017d | | COMPLETED | 2026-05-30T20:21:26+08:00 | 1780143686431 | 2ba4881420fa11f19e9c38a74640adcc | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ RAGFlow(user)> list ingestion tasks; +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | create_date | create_time | dataset_id | document_id | id | schema | status | update_date | update_time | user_id | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | 2026-06-02T19:02:31+08:00 | 1780398151417 | 0abe79f9423311f1ad8d38a74640adcc | ffe64fae423411f1a2d938a74640adcc | e89e20d9a25848a1b79bd9345ddbfe1d | | COMPLETED | 2026-06-02T19:02:52+08:00 | 1780398172208 | 2ba4881420fa11f19e9c38a74640adcc | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ # Create an ingestion task # First argument is document id, second argument is dataset id RAGFlow(user)> start ingestion 'ffe64fae423411f1a2d938a74640adcc' from '0abe79f9423311f1ad8d38a74640adcc'; +----------------------------------+-------------------------------------------+ | document_id | result | +----------------------------------+-------------------------------------------+ | ffe64fae423411f1a2d938a74640adcc | task_id: 8d758cd14a8b4ba8ab505003fb52017d | +----------------------------------+-------------------------------------------+ # Pause an ingestion task, first argument is ingestion id RAGFlow(user)> stop ingestion '8d758cd14a8b4ba8ab505003fb52017d'; +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | create_date | create_time | dataset_id | document_id | id | schema | status | update_date | update_time | user_id | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | 2026-05-30T20:21:06+08:00 | 1780143666289 | 0abe79f9423311f1ad8d38a74640adcc | ffe64fae423411f1a2d938a74640adcc | 8d758cd14a8b4ba8ab505003fb52017d | | COMPLETED | 2026-05-30T20:21:26+08:00 | 1780143686431 | 2ba4881420fa11f19e9c38a74640adcc | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ # Delete an ingestion task RAGFlow(api/default)> remove ingestion tasks 'f366450a27d54677aec1c7090add30f0'; +---------+----------------------------------+ | remove | task_id | +---------+----------------------------------+ | success | f366450a27d54677aec1c7090add30f0 | +---------+----------------------------------+ ``` ### Type of change - [x] New Feature (non-breaking change which adds functionality) --------- Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-06-12 14:56:44 +08:00
if p.curToken.Type != TokenQuotedString {
return nil, fmt.Errorf("expected region name after REGION")
Go: use NATS as the message queue (#15327) ### What problem does this PR solve? ``` RAGFlow(admin)> mq publish 'msg2'; SUCCESS RAGFlow(admin)> mq publish 'msg3'; SUCCESS RAGFlow(admin)> mq list; +---------+---------------+ | message | subject | +---------+---------------+ | msg1 | tasks.RAGFLOW | | msg2 | tasks.RAGFLOW | | msg3 | tasks.RAGFLOW | +---------+---------------+ RAGFlow(admin)> mq pull 2; +---------+---------------+ | message | subject | +---------+---------------+ | msg1 | tasks.RAGFLOW | | msg2 | tasks.RAGFLOW | +---------+---------------+ RAGFlow(admin)> mq pull noack; +---------+---------------+ | message | subject | +---------+---------------+ | abc | tasks.RAGFLOW | +---------+---------------+ RAGFlow(admin)> mq show +-------------------+----------------+--------+---------------+---------------+-------------------+---------------+ | ack_pending_count | consumer_count | memory | message_count | pending_count | redelivered_count | waiting_count | +-------------------+----------------+--------+---------------+---------------+-------------------+---------------+ | 2 | 1 | 0 | 2 | 0 | 1 | 0 | +-------------------+----------------+--------+---------------+---------------+-------------------+---------------+ RAGFlow(admin)> list ingestors; +--------------+-------------------------------------------+--------+ | host | name | status | +--------------+-------------------------------------------+--------+ | 192.168.1.38 | ingestor-8f0e4bd5650a4ac58b0151969fbf6935 | alive | +--------------+-------------------------------------------+--------+ RAGFlow(admin)> list ingestion tasks; +----------------------------------+----------------------------------+-----------+------+-------------+----------------------------------+ | document_id | id | status | step | user | user_id | +----------------------------------+----------------------------------+-----------+------+-------------+----------------------------------+ | ffe64fae423411f1a2d938a74640adcc | 90d3d0f6528941c1ac8eb0360effccc4 | COMPLETED | 5 | aaa@aaa.com | 2ba4881420fa11f19e9c38a74640adcc | +----------------------------------+----------------------------------+-----------+------+-------------+----------------------------------+ RAGFlow(admin)> remove ingestion tasks '90d3d0f6528941c1ac8eb0360effccc4'; +---------+----------------------------------+ | delete | task_id | +---------+----------------------------------+ | success | 90d3d0f6528941c1ac8eb0360effccc4 | +---------+----------------------------------+ RAGFlow(admin)> stop ingestion tasks 'e89e20d9a25848a1b79bd9345ddbfe1d'; +----------+----------------------------------+ | status | task_id | +----------+----------------------------------+ | STOPPING | e89e20d9a25848a1b79bd9345ddbfe1d | +----------+----------------------------------+ # Publish a message RAGFlow(admin)> mq publish 'cdd'; SUCCESS # List current tasks in the message queue RAGFlow(admin)> mq list +----------------------------------+---------------+ | message | subject | +----------------------------------+---------------+ | 7ce392a3c1624cd2be4b5276e8825059 | tasks.RAGFLOW | +----------------------------------+---------------+ # Consume a task from the message queue RAGFlow(admin)> mq pull +------+-----+----------------+ | ack | id | type | +------+-----+----------------+ | true | cdd | ingestion_test | +------+-----+----------------+ # User mode # List ingestion tasks, followed by dataset id RAGFlow(user)> list ingestion tasks from '0abe79f9423311f1ad8d38a74640adcc'; +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | create_date | create_time | dataset_id | document_id | id | schema | status | update_date | update_time | user_id | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | 2026-05-30T20:21:06+08:00 | 1780143666289 | 0abe79f9423311f1ad8d38a74640adcc | ffe64fae423411f1a2d938a74640adcc | 8d758cd14a8b4ba8ab505003fb52017d | | COMPLETED | 2026-05-30T20:21:26+08:00 | 1780143686431 | 2ba4881420fa11f19e9c38a74640adcc | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ RAGFlow(user)> list ingestion tasks; +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | create_date | create_time | dataset_id | document_id | id | schema | status | update_date | update_time | user_id | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | 2026-06-02T19:02:31+08:00 | 1780398151417 | 0abe79f9423311f1ad8d38a74640adcc | ffe64fae423411f1a2d938a74640adcc | e89e20d9a25848a1b79bd9345ddbfe1d | | COMPLETED | 2026-06-02T19:02:52+08:00 | 1780398172208 | 2ba4881420fa11f19e9c38a74640adcc | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ # Create an ingestion task # First argument is document id, second argument is dataset id RAGFlow(user)> start ingestion 'ffe64fae423411f1a2d938a74640adcc' from '0abe79f9423311f1ad8d38a74640adcc'; +----------------------------------+-------------------------------------------+ | document_id | result | +----------------------------------+-------------------------------------------+ | ffe64fae423411f1a2d938a74640adcc | task_id: 8d758cd14a8b4ba8ab505003fb52017d | +----------------------------------+-------------------------------------------+ # Pause an ingestion task, first argument is ingestion id RAGFlow(user)> stop ingestion '8d758cd14a8b4ba8ab505003fb52017d'; +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | create_date | create_time | dataset_id | document_id | id | schema | status | update_date | update_time | user_id | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | 2026-05-30T20:21:06+08:00 | 1780143666289 | 0abe79f9423311f1ad8d38a74640adcc | ffe64fae423411f1a2d938a74640adcc | 8d758cd14a8b4ba8ab505003fb52017d | | COMPLETED | 2026-05-30T20:21:26+08:00 | 1780143686431 | 2ba4881420fa11f19e9c38a74640adcc | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ # Delete an ingestion task RAGFlow(api/default)> remove ingestion tasks 'f366450a27d54677aec1c7090add30f0'; +---------+----------------------------------+ | remove | task_id | +---------+----------------------------------+ | success | f366450a27d54677aec1c7090add30f0 | +---------+----------------------------------+ ``` ### Type of change - [x] New Feature (non-breaking change which adds functionality) --------- Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-06-12 14:56:44 +08:00
}
regionName := p.curToken.Value
p.nextToken()
Go: use NATS as the message queue (#15327) ### What problem does this PR solve? ``` RAGFlow(admin)> mq publish 'msg2'; SUCCESS RAGFlow(admin)> mq publish 'msg3'; SUCCESS RAGFlow(admin)> mq list; +---------+---------------+ | message | subject | +---------+---------------+ | msg1 | tasks.RAGFLOW | | msg2 | tasks.RAGFLOW | | msg3 | tasks.RAGFLOW | +---------+---------------+ RAGFlow(admin)> mq pull 2; +---------+---------------+ | message | subject | +---------+---------------+ | msg1 | tasks.RAGFLOW | | msg2 | tasks.RAGFLOW | +---------+---------------+ RAGFlow(admin)> mq pull noack; +---------+---------------+ | message | subject | +---------+---------------+ | abc | tasks.RAGFLOW | +---------+---------------+ RAGFlow(admin)> mq show +-------------------+----------------+--------+---------------+---------------+-------------------+---------------+ | ack_pending_count | consumer_count | memory | message_count | pending_count | redelivered_count | waiting_count | +-------------------+----------------+--------+---------------+---------------+-------------------+---------------+ | 2 | 1 | 0 | 2 | 0 | 1 | 0 | +-------------------+----------------+--------+---------------+---------------+-------------------+---------------+ RAGFlow(admin)> list ingestors; +--------------+-------------------------------------------+--------+ | host | name | status | +--------------+-------------------------------------------+--------+ | 192.168.1.38 | ingestor-8f0e4bd5650a4ac58b0151969fbf6935 | alive | +--------------+-------------------------------------------+--------+ RAGFlow(admin)> list ingestion tasks; +----------------------------------+----------------------------------+-----------+------+-------------+----------------------------------+ | document_id | id | status | step | user | user_id | +----------------------------------+----------------------------------+-----------+------+-------------+----------------------------------+ | ffe64fae423411f1a2d938a74640adcc | 90d3d0f6528941c1ac8eb0360effccc4 | COMPLETED | 5 | aaa@aaa.com | 2ba4881420fa11f19e9c38a74640adcc | +----------------------------------+----------------------------------+-----------+------+-------------+----------------------------------+ RAGFlow(admin)> remove ingestion tasks '90d3d0f6528941c1ac8eb0360effccc4'; +---------+----------------------------------+ | delete | task_id | +---------+----------------------------------+ | success | 90d3d0f6528941c1ac8eb0360effccc4 | +---------+----------------------------------+ RAGFlow(admin)> stop ingestion tasks 'e89e20d9a25848a1b79bd9345ddbfe1d'; +----------+----------------------------------+ | status | task_id | +----------+----------------------------------+ | STOPPING | e89e20d9a25848a1b79bd9345ddbfe1d | +----------+----------------------------------+ # Publish a message RAGFlow(admin)> mq publish 'cdd'; SUCCESS # List current tasks in the message queue RAGFlow(admin)> mq list +----------------------------------+---------------+ | message | subject | +----------------------------------+---------------+ | 7ce392a3c1624cd2be4b5276e8825059 | tasks.RAGFLOW | +----------------------------------+---------------+ # Consume a task from the message queue RAGFlow(admin)> mq pull +------+-----+----------------+ | ack | id | type | +------+-----+----------------+ | true | cdd | ingestion_test | +------+-----+----------------+ # User mode # List ingestion tasks, followed by dataset id RAGFlow(user)> list ingestion tasks from '0abe79f9423311f1ad8d38a74640adcc'; +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | create_date | create_time | dataset_id | document_id | id | schema | status | update_date | update_time | user_id | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | 2026-05-30T20:21:06+08:00 | 1780143666289 | 0abe79f9423311f1ad8d38a74640adcc | ffe64fae423411f1a2d938a74640adcc | 8d758cd14a8b4ba8ab505003fb52017d | | COMPLETED | 2026-05-30T20:21:26+08:00 | 1780143686431 | 2ba4881420fa11f19e9c38a74640adcc | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ RAGFlow(user)> list ingestion tasks; +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | create_date | create_time | dataset_id | document_id | id | schema | status | update_date | update_time | user_id | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | 2026-06-02T19:02:31+08:00 | 1780398151417 | 0abe79f9423311f1ad8d38a74640adcc | ffe64fae423411f1a2d938a74640adcc | e89e20d9a25848a1b79bd9345ddbfe1d | | COMPLETED | 2026-06-02T19:02:52+08:00 | 1780398172208 | 2ba4881420fa11f19e9c38a74640adcc | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ # Create an ingestion task # First argument is document id, second argument is dataset id RAGFlow(user)> start ingestion 'ffe64fae423411f1a2d938a74640adcc' from '0abe79f9423311f1ad8d38a74640adcc'; +----------------------------------+-------------------------------------------+ | document_id | result | +----------------------------------+-------------------------------------------+ | ffe64fae423411f1a2d938a74640adcc | task_id: 8d758cd14a8b4ba8ab505003fb52017d | +----------------------------------+-------------------------------------------+ # Pause an ingestion task, first argument is ingestion id RAGFlow(user)> stop ingestion '8d758cd14a8b4ba8ab505003fb52017d'; +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | create_date | create_time | dataset_id | document_id | id | schema | status | update_date | update_time | user_id | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | 2026-05-30T20:21:06+08:00 | 1780143666289 | 0abe79f9423311f1ad8d38a74640adcc | ffe64fae423411f1a2d938a74640adcc | 8d758cd14a8b4ba8ab505003fb52017d | | COMPLETED | 2026-05-30T20:21:26+08:00 | 1780143686431 | 2ba4881420fa11f19e9c38a74640adcc | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ # Delete an ingestion task RAGFlow(api/default)> remove ingestion tasks 'f366450a27d54677aec1c7090add30f0'; +---------+----------------------------------+ | remove | task_id | +---------+----------------------------------+ | success | f366450a27d54677aec1c7090add30f0 | +---------+----------------------------------+ ``` ### Type of change - [x] New Feature (non-breaking change which adds functionality) --------- Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-06-12 14:56:44 +08:00
if p.curToken.Type != TokenKey {
return nil, fmt.Errorf("expected KEY after region name")
}
p.nextToken()
Go: use NATS as the message queue (#15327) ### What problem does this PR solve? ``` RAGFlow(admin)> mq publish 'msg2'; SUCCESS RAGFlow(admin)> mq publish 'msg3'; SUCCESS RAGFlow(admin)> mq list; +---------+---------------+ | message | subject | +---------+---------------+ | msg1 | tasks.RAGFLOW | | msg2 | tasks.RAGFLOW | | msg3 | tasks.RAGFLOW | +---------+---------------+ RAGFlow(admin)> mq pull 2; +---------+---------------+ | message | subject | +---------+---------------+ | msg1 | tasks.RAGFLOW | | msg2 | tasks.RAGFLOW | +---------+---------------+ RAGFlow(admin)> mq pull noack; +---------+---------------+ | message | subject | +---------+---------------+ | abc | tasks.RAGFLOW | +---------+---------------+ RAGFlow(admin)> mq show +-------------------+----------------+--------+---------------+---------------+-------------------+---------------+ | ack_pending_count | consumer_count | memory | message_count | pending_count | redelivered_count | waiting_count | +-------------------+----------------+--------+---------------+---------------+-------------------+---------------+ | 2 | 1 | 0 | 2 | 0 | 1 | 0 | +-------------------+----------------+--------+---------------+---------------+-------------------+---------------+ RAGFlow(admin)> list ingestors; +--------------+-------------------------------------------+--------+ | host | name | status | +--------------+-------------------------------------------+--------+ | 192.168.1.38 | ingestor-8f0e4bd5650a4ac58b0151969fbf6935 | alive | +--------------+-------------------------------------------+--------+ RAGFlow(admin)> list ingestion tasks; +----------------------------------+----------------------------------+-----------+------+-------------+----------------------------------+ | document_id | id | status | step | user | user_id | +----------------------------------+----------------------------------+-----------+------+-------------+----------------------------------+ | ffe64fae423411f1a2d938a74640adcc | 90d3d0f6528941c1ac8eb0360effccc4 | COMPLETED | 5 | aaa@aaa.com | 2ba4881420fa11f19e9c38a74640adcc | +----------------------------------+----------------------------------+-----------+------+-------------+----------------------------------+ RAGFlow(admin)> remove ingestion tasks '90d3d0f6528941c1ac8eb0360effccc4'; +---------+----------------------------------+ | delete | task_id | +---------+----------------------------------+ | success | 90d3d0f6528941c1ac8eb0360effccc4 | +---------+----------------------------------+ RAGFlow(admin)> stop ingestion tasks 'e89e20d9a25848a1b79bd9345ddbfe1d'; +----------+----------------------------------+ | status | task_id | +----------+----------------------------------+ | STOPPING | e89e20d9a25848a1b79bd9345ddbfe1d | +----------+----------------------------------+ # Publish a message RAGFlow(admin)> mq publish 'cdd'; SUCCESS # List current tasks in the message queue RAGFlow(admin)> mq list +----------------------------------+---------------+ | message | subject | +----------------------------------+---------------+ | 7ce392a3c1624cd2be4b5276e8825059 | tasks.RAGFLOW | +----------------------------------+---------------+ # Consume a task from the message queue RAGFlow(admin)> mq pull +------+-----+----------------+ | ack | id | type | +------+-----+----------------+ | true | cdd | ingestion_test | +------+-----+----------------+ # User mode # List ingestion tasks, followed by dataset id RAGFlow(user)> list ingestion tasks from '0abe79f9423311f1ad8d38a74640adcc'; +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | create_date | create_time | dataset_id | document_id | id | schema | status | update_date | update_time | user_id | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | 2026-05-30T20:21:06+08:00 | 1780143666289 | 0abe79f9423311f1ad8d38a74640adcc | ffe64fae423411f1a2d938a74640adcc | 8d758cd14a8b4ba8ab505003fb52017d | | COMPLETED | 2026-05-30T20:21:26+08:00 | 1780143686431 | 2ba4881420fa11f19e9c38a74640adcc | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ RAGFlow(user)> list ingestion tasks; +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | create_date | create_time | dataset_id | document_id | id | schema | status | update_date | update_time | user_id | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | 2026-06-02T19:02:31+08:00 | 1780398151417 | 0abe79f9423311f1ad8d38a74640adcc | ffe64fae423411f1a2d938a74640adcc | e89e20d9a25848a1b79bd9345ddbfe1d | | COMPLETED | 2026-06-02T19:02:52+08:00 | 1780398172208 | 2ba4881420fa11f19e9c38a74640adcc | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ # Create an ingestion task # First argument is document id, second argument is dataset id RAGFlow(user)> start ingestion 'ffe64fae423411f1a2d938a74640adcc' from '0abe79f9423311f1ad8d38a74640adcc'; +----------------------------------+-------------------------------------------+ | document_id | result | +----------------------------------+-------------------------------------------+ | ffe64fae423411f1a2d938a74640adcc | task_id: 8d758cd14a8b4ba8ab505003fb52017d | +----------------------------------+-------------------------------------------+ # Pause an ingestion task, first argument is ingestion id RAGFlow(user)> stop ingestion '8d758cd14a8b4ba8ab505003fb52017d'; +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | create_date | create_time | dataset_id | document_id | id | schema | status | update_date | update_time | user_id | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ | 2026-05-30T20:21:06+08:00 | 1780143666289 | 0abe79f9423311f1ad8d38a74640adcc | ffe64fae423411f1a2d938a74640adcc | 8d758cd14a8b4ba8ab505003fb52017d | | COMPLETED | 2026-05-30T20:21:26+08:00 | 1780143686431 | 2ba4881420fa11f19e9c38a74640adcc | +---------------------------+---------------+----------------------------------+----------------------------------+----------------------------------+--------+-----------+---------------------------+---------------+----------------------------------+ # Delete an ingestion task RAGFlow(api/default)> remove ingestion tasks 'f366450a27d54677aec1c7090add30f0'; +---------+----------------------------------+ | remove | task_id | +---------+----------------------------------+ | success | f366450a27d54677aec1c7090add30f0 | +---------+----------------------------------+ ``` ### Type of change - [x] New Feature (non-breaking change which adds functionality) --------- Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-06-12 14:56:44 +08:00
if p.curToken.Type != TokenQuotedString {
return nil, fmt.Errorf("expected API key after KEY")
}
apiKey := p.curToken.Value
p.nextToken()
Go: refactor CLI (#15728) ### What problem does this PR solve? ``` RAGFlow(user)> add api server 'ccc' host '127.0.0.1:9980'; SUCCESS RAGFlow(user)> list api server; +------------+---------------+-----------------+---------+-------------+---------------+ | api_server | api_server_ip | api_server_port | auth | user_name | user_password | +------------+---------------+-----------------+---------+-------------+---------------+ | ccc | 127.0.0.1 | 9980 | no auth | | | | default | 127.0.0.1 | 9384 | login | aaa@aaa.com | *** | +------------+---------------+-----------------+---------+-------------+---------------+ RAGFlow(user)> delete api server 'ccc'; SUCCESS RAGFlow(user)> list api server; +------------+---------------+-----------------+---------+ | api_server | api_server_ip | api_server_port | auth | +------------+---------------+-----------------+---------+ | default | 127.0.0.1 | 9384 | no auth | +------------+---------------+-----------------+---------+ RAGFlow(user)> show admin server; +--------------+-------+ | field | value | +--------------+-------+ | admin_server | N/A | +--------------+-------+ RAGFlow(user)> add admin server host '127.0.0.1:9880'; SUCCESS RAGFlow(user)> show admin server; +-------------------+-----------+ | field | value | +-------------------+-----------+ | admin_server_ip | 127.0.0.1 | | admin_server_port | 9880 | | auth | no auth | +-------------------+-----------+ RAGFlow(user)> delete admin server; SUCCESS RAGFlow(user)> show admin server; +--------------+-------+ | field | value | +--------------+-------+ | admin_server | N/A | +--------------+-------+ RAGFlow(user)> show current +-----------------+-------------+ | field | value | +-----------------+-------------+ | api_server_port | 9384 | | user_name | aaa@aaa.com | | user_password | *** | | mode | api | | verbose | false | | api_server | default | | api_server_ip | 127.0.0.1 | | auth | login | | output | table | | interactive | true | +-----------------+-------------+ ``` ### Type of change - [x] Refactoring --------- Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-06-09 15:22:50 +08:00
baseURL := ""
if p.curToken.Type == TokenURL {
Go: refactor CLI (#15728) ### What problem does this PR solve? ``` RAGFlow(user)> add api server 'ccc' host '127.0.0.1:9980'; SUCCESS RAGFlow(user)> list api server; +------------+---------------+-----------------+---------+-------------+---------------+ | api_server | api_server_ip | api_server_port | auth | user_name | user_password | +------------+---------------+-----------------+---------+-------------+---------------+ | ccc | 127.0.0.1 | 9980 | no auth | | | | default | 127.0.0.1 | 9384 | login | aaa@aaa.com | *** | +------------+---------------+-----------------+---------+-------------+---------------+ RAGFlow(user)> delete api server 'ccc'; SUCCESS RAGFlow(user)> list api server; +------------+---------------+-----------------+---------+ | api_server | api_server_ip | api_server_port | auth | +------------+---------------+-----------------+---------+ | default | 127.0.0.1 | 9384 | no auth | +------------+---------------+-----------------+---------+ RAGFlow(user)> show admin server; +--------------+-------+ | field | value | +--------------+-------+ | admin_server | N/A | +--------------+-------+ RAGFlow(user)> add admin server host '127.0.0.1:9880'; SUCCESS RAGFlow(user)> show admin server; +-------------------+-----------+ | field | value | +-------------------+-----------+ | admin_server_ip | 127.0.0.1 | | admin_server_port | 9880 | | auth | no auth | +-------------------+-----------+ RAGFlow(user)> delete admin server; SUCCESS RAGFlow(user)> show admin server; +--------------+-------+ | field | value | +--------------+-------+ | admin_server | N/A | +--------------+-------+ RAGFlow(user)> show current +-----------------+-------------+ | field | value | +-----------------+-------------+ | api_server_port | 9384 | | user_name | aaa@aaa.com | | user_password | *** | | mode | api | | verbose | false | | api_server | default | | api_server_ip | 127.0.0.1 | | auth | login | | output | table | | interactive | true | +-----------------+-------------+ ``` ### Type of change - [x] Refactoring --------- Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-06-09 15:22:50 +08:00
p.nextToken()
if p.curToken.Type != TokenQuotedString {
return nil, fmt.Errorf("expected base URL after URL")
Go: refactor CLI (#15728) ### What problem does this PR solve? ``` RAGFlow(user)> add api server 'ccc' host '127.0.0.1:9980'; SUCCESS RAGFlow(user)> list api server; +------------+---------------+-----------------+---------+-------------+---------------+ | api_server | api_server_ip | api_server_port | auth | user_name | user_password | +------------+---------------+-----------------+---------+-------------+---------------+ | ccc | 127.0.0.1 | 9980 | no auth | | | | default | 127.0.0.1 | 9384 | login | aaa@aaa.com | *** | +------------+---------------+-----------------+---------+-------------+---------------+ RAGFlow(user)> delete api server 'ccc'; SUCCESS RAGFlow(user)> list api server; +------------+---------------+-----------------+---------+ | api_server | api_server_ip | api_server_port | auth | +------------+---------------+-----------------+---------+ | default | 127.0.0.1 | 9384 | no auth | +------------+---------------+-----------------+---------+ RAGFlow(user)> show admin server; +--------------+-------+ | field | value | +--------------+-------+ | admin_server | N/A | +--------------+-------+ RAGFlow(user)> add admin server host '127.0.0.1:9880'; SUCCESS RAGFlow(user)> show admin server; +-------------------+-----------+ | field | value | +-------------------+-----------+ | admin_server_ip | 127.0.0.1 | | admin_server_port | 9880 | | auth | no auth | +-------------------+-----------+ RAGFlow(user)> delete admin server; SUCCESS RAGFlow(user)> show admin server; +--------------+-------+ | field | value | +--------------+-------+ | admin_server | N/A | +--------------+-------+ RAGFlow(user)> show current +-----------------+-------------+ | field | value | +-----------------+-------------+ | api_server_port | 9384 | | user_name | aaa@aaa.com | | user_password | *** | | mode | api | | verbose | false | | api_server | default | | api_server_ip | 127.0.0.1 | | auth | login | | output | table | | interactive | true | +-----------------+-------------+ ``` ### Type of change - [x] Refactoring --------- Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-06-09 15:22:50 +08:00
}
baseURL = p.curToken.Value
Go: refactor CLI (#15728) ### What problem does this PR solve? ``` RAGFlow(user)> add api server 'ccc' host '127.0.0.1:9980'; SUCCESS RAGFlow(user)> list api server; +------------+---------------+-----------------+---------+-------------+---------------+ | api_server | api_server_ip | api_server_port | auth | user_name | user_password | +------------+---------------+-----------------+---------+-------------+---------------+ | ccc | 127.0.0.1 | 9980 | no auth | | | | default | 127.0.0.1 | 9384 | login | aaa@aaa.com | *** | +------------+---------------+-----------------+---------+-------------+---------------+ RAGFlow(user)> delete api server 'ccc'; SUCCESS RAGFlow(user)> list api server; +------------+---------------+-----------------+---------+ | api_server | api_server_ip | api_server_port | auth | +------------+---------------+-----------------+---------+ | default | 127.0.0.1 | 9384 | no auth | +------------+---------------+-----------------+---------+ RAGFlow(user)> show admin server; +--------------+-------+ | field | value | +--------------+-------+ | admin_server | N/A | +--------------+-------+ RAGFlow(user)> add admin server host '127.0.0.1:9880'; SUCCESS RAGFlow(user)> show admin server; +-------------------+-----------+ | field | value | +-------------------+-----------+ | admin_server_ip | 127.0.0.1 | | admin_server_port | 9880 | | auth | no auth | +-------------------+-----------+ RAGFlow(user)> delete admin server; SUCCESS RAGFlow(user)> show admin server; +--------------+-------+ | field | value | +--------------+-------+ | admin_server | N/A | +--------------+-------+ RAGFlow(user)> show current +-----------------+-------------+ | field | value | +-----------------+-------------+ | api_server_port | 9384 | | user_name | aaa@aaa.com | | user_password | *** | | mode | api | | verbose | false | | api_server | default | | api_server_ip | 127.0.0.1 | | auth | login | | output | table | | interactive | true | +-----------------+-------------+ ``` ### Type of change - [x] Refactoring --------- Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-06-09 15:22:50 +08:00
p.nextToken()
}
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
if p.curToken.Type != TokenEOF {
return nil, fmt.Errorf("unexpected token: %s", p.curToken.Value)
}
cmd := NewCommand("check_provider_with_key")
cmd.Params["provider_name"] = providerName
cmd.Params["region"] = regionName
cmd.Params["api_key"] = apiKey
if baseURL != "" {
cmd.Params["base_url"] = baseURL
}
Go: refactor CLI (#15728) ### What problem does this PR solve? ``` RAGFlow(user)> add api server 'ccc' host '127.0.0.1:9980'; SUCCESS RAGFlow(user)> list api server; +------------+---------------+-----------------+---------+-------------+---------------+ | api_server | api_server_ip | api_server_port | auth | user_name | user_password | +------------+---------------+-----------------+---------+-------------+---------------+ | ccc | 127.0.0.1 | 9980 | no auth | | | | default | 127.0.0.1 | 9384 | login | aaa@aaa.com | *** | +------------+---------------+-----------------+---------+-------------+---------------+ RAGFlow(user)> delete api server 'ccc'; SUCCESS RAGFlow(user)> list api server; +------------+---------------+-----------------+---------+ | api_server | api_server_ip | api_server_port | auth | +------------+---------------+-----------------+---------+ | default | 127.0.0.1 | 9384 | no auth | +------------+---------------+-----------------+---------+ RAGFlow(user)> show admin server; +--------------+-------+ | field | value | +--------------+-------+ | admin_server | N/A | +--------------+-------+ RAGFlow(user)> add admin server host '127.0.0.1:9880'; SUCCESS RAGFlow(user)> show admin server; +-------------------+-----------+ | field | value | +-------------------+-----------+ | admin_server_ip | 127.0.0.1 | | admin_server_port | 9880 | | auth | no auth | +-------------------+-----------+ RAGFlow(user)> delete admin server; SUCCESS RAGFlow(user)> show admin server; +--------------+-------+ | field | value | +--------------+-------+ | admin_server | N/A | +--------------+-------+ RAGFlow(user)> show current +-----------------+-------------+ | field | value | +-----------------+-------------+ | api_server_port | 9384 | | user_name | aaa@aaa.com | | user_password | *** | | mode | api | | verbose | false | | api_server | default | | api_server_ip | 127.0.0.1 | | auth | login | | output | table | | interactive | true | +-----------------+-------------+ ``` ### Type of change - [x] Refactoring --------- Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-06-09 15:22:50 +08:00
return cmd, nil
}
func (p *Parser) parseAPISave() (*Command, error) {
p.nextToken() // consume SAVE
2026-06-25 20:36:50 +08:00
switch p.curToken.Type {
case TokenConfig:
return p.parseAPISaveConfig()
2026-06-25 20:36:50 +08:00
default:
return nil, fmt.Errorf("unknown ADD target: %s", p.curToken.Value)
2026-06-25 20:36:50 +08:00
}
}
// SAVE CONFIG AS 'path'
func (p *Parser) parseAPISaveConfig() (*Command, error) {
p.nextToken() // consume CONFIG
2026-06-25 20:36:50 +08:00
if p.curToken.Type != TokenAs {
return nil, fmt.Errorf("expected AS after CONFIG")
}
p.nextToken() // consume AS
path, err := p.parseQuotedString()
if err != nil {
return nil, err
}
cmd := NewCommand("api_save_config_command")
cmd.Params["path"] = path
2026-06-25 20:36:50 +08:00
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
func (p *Parser) parseAPIUseCommands() (*Command, error) {
p.nextToken() // consume USE
Go: refactor CLI (#15728) ### What problem does this PR solve? ``` RAGFlow(user)> add api server 'ccc' host '127.0.0.1:9980'; SUCCESS RAGFlow(user)> list api server; +------------+---------------+-----------------+---------+-------------+---------------+ | api_server | api_server_ip | api_server_port | auth | user_name | user_password | +------------+---------------+-----------------+---------+-------------+---------------+ | ccc | 127.0.0.1 | 9980 | no auth | | | | default | 127.0.0.1 | 9384 | login | aaa@aaa.com | *** | +------------+---------------+-----------------+---------+-------------+---------------+ RAGFlow(user)> delete api server 'ccc'; SUCCESS RAGFlow(user)> list api server; +------------+---------------+-----------------+---------+ | api_server | api_server_ip | api_server_port | auth | +------------+---------------+-----------------+---------+ | default | 127.0.0.1 | 9384 | no auth | +------------+---------------+-----------------+---------+ RAGFlow(user)> show admin server; +--------------+-------+ | field | value | +--------------+-------+ | admin_server | N/A | +--------------+-------+ RAGFlow(user)> add admin server host '127.0.0.1:9880'; SUCCESS RAGFlow(user)> show admin server; +-------------------+-----------+ | field | value | +-------------------+-----------+ | admin_server_ip | 127.0.0.1 | | admin_server_port | 9880 | | auth | no auth | +-------------------+-----------+ RAGFlow(user)> delete admin server; SUCCESS RAGFlow(user)> show admin server; +--------------+-------+ | field | value | +--------------+-------+ | admin_server | N/A | +--------------+-------+ RAGFlow(user)> show current +-----------------+-------------+ | field | value | +-----------------+-------------+ | api_server_port | 9384 | | user_name | aaa@aaa.com | | user_password | *** | | mode | api | | verbose | false | | api_server | default | | api_server_ip | 127.0.0.1 | | auth | login | | output | table | | interactive | true | +-----------------+-------------+ ``` ### Type of change - [x] Refactoring --------- Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-06-09 15:22:50 +08:00
switch p.curToken.Type {
case TokenModel:
return p.parseAPIUseModel()
case TokenAPI:
return p.parseAPIUseAPIServer()
case TokenAdmin:
return p.parseAPIUseAdminServer()
Go: refactor CLI (#15728) ### What problem does this PR solve? ``` RAGFlow(user)> add api server 'ccc' host '127.0.0.1:9980'; SUCCESS RAGFlow(user)> list api server; +------------+---------------+-----------------+---------+-------------+---------------+ | api_server | api_server_ip | api_server_port | auth | user_name | user_password | +------------+---------------+-----------------+---------+-------------+---------------+ | ccc | 127.0.0.1 | 9980 | no auth | | | | default | 127.0.0.1 | 9384 | login | aaa@aaa.com | *** | +------------+---------------+-----------------+---------+-------------+---------------+ RAGFlow(user)> delete api server 'ccc'; SUCCESS RAGFlow(user)> list api server; +------------+---------------+-----------------+---------+ | api_server | api_server_ip | api_server_port | auth | +------------+---------------+-----------------+---------+ | default | 127.0.0.1 | 9384 | no auth | +------------+---------------+-----------------+---------+ RAGFlow(user)> show admin server; +--------------+-------+ | field | value | +--------------+-------+ | admin_server | N/A | +--------------+-------+ RAGFlow(user)> add admin server host '127.0.0.1:9880'; SUCCESS RAGFlow(user)> show admin server; +-------------------+-----------+ | field | value | +-------------------+-----------+ | admin_server_ip | 127.0.0.1 | | admin_server_port | 9880 | | auth | no auth | +-------------------+-----------+ RAGFlow(user)> delete admin server; SUCCESS RAGFlow(user)> show admin server; +--------------+-------+ | field | value | +--------------+-------+ | admin_server | N/A | +--------------+-------+ RAGFlow(user)> show current +-----------------+-------------+ | field | value | +-----------------+-------------+ | api_server_port | 9384 | | user_name | aaa@aaa.com | | user_password | *** | | mode | api | | verbose | false | | api_server | default | | api_server_ip | 127.0.0.1 | | auth | login | | output | table | | interactive | true | +-----------------+-------------+ ``` ### Type of change - [x] Refactoring --------- Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-06-09 15:22:50 +08:00
default:
return nil, fmt.Errorf("expected MODEL or SKILL after USE")
}
}
func (p *Parser) parseAPIUseModel() (*Command, error) {
p.nextToken() // consume MODEL
modelNameOrID, err := p.parseQuotedString()
if err != nil {
return nil, fmt.Errorf("expected model identifier in format 'model@instance@provider': %w", err)
Go: refactor CLI (#15728) ### What problem does this PR solve? ``` RAGFlow(user)> add api server 'ccc' host '127.0.0.1:9980'; SUCCESS RAGFlow(user)> list api server; +------------+---------------+-----------------+---------+-------------+---------------+ | api_server | api_server_ip | api_server_port | auth | user_name | user_password | +------------+---------------+-----------------+---------+-------------+---------------+ | ccc | 127.0.0.1 | 9980 | no auth | | | | default | 127.0.0.1 | 9384 | login | aaa@aaa.com | *** | +------------+---------------+-----------------+---------+-------------+---------------+ RAGFlow(user)> delete api server 'ccc'; SUCCESS RAGFlow(user)> list api server; +------------+---------------+-----------------+---------+ | api_server | api_server_ip | api_server_port | auth | +------------+---------------+-----------------+---------+ | default | 127.0.0.1 | 9384 | no auth | +------------+---------------+-----------------+---------+ RAGFlow(user)> show admin server; +--------------+-------+ | field | value | +--------------+-------+ | admin_server | N/A | +--------------+-------+ RAGFlow(user)> add admin server host '127.0.0.1:9880'; SUCCESS RAGFlow(user)> show admin server; +-------------------+-----------+ | field | value | +-------------------+-----------+ | admin_server_ip | 127.0.0.1 | | admin_server_port | 9880 | | auth | no auth | +-------------------+-----------+ RAGFlow(user)> delete admin server; SUCCESS RAGFlow(user)> show admin server; +--------------+-------+ | field | value | +--------------+-------+ | admin_server | N/A | +--------------+-------+ RAGFlow(user)> show current +-----------------+-------------+ | field | value | +-----------------+-------------+ | api_server_port | 9384 | | user_name | aaa@aaa.com | | user_password | *** | | mode | api | | verbose | false | | api_server | default | | api_server_ip | 127.0.0.1 | | auth | login | | output | table | | interactive | true | +-----------------+-------------+ ``` ### Type of change - [x] Refactoring --------- Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-06-09 15:22:50 +08:00
}
p.nextToken()
Go: refactor CLI (#15728) ### What problem does this PR solve? ``` RAGFlow(user)> add api server 'ccc' host '127.0.0.1:9980'; SUCCESS RAGFlow(user)> list api server; +------------+---------------+-----------------+---------+-------------+---------------+ | api_server | api_server_ip | api_server_port | auth | user_name | user_password | +------------+---------------+-----------------+---------+-------------+---------------+ | ccc | 127.0.0.1 | 9980 | no auth | | | | default | 127.0.0.1 | 9384 | login | aaa@aaa.com | *** | +------------+---------------+-----------------+---------+-------------+---------------+ RAGFlow(user)> delete api server 'ccc'; SUCCESS RAGFlow(user)> list api server; +------------+---------------+-----------------+---------+ | api_server | api_server_ip | api_server_port | auth | +------------+---------------+-----------------+---------+ | default | 127.0.0.1 | 9384 | no auth | +------------+---------------+-----------------+---------+ RAGFlow(user)> show admin server; +--------------+-------+ | field | value | +--------------+-------+ | admin_server | N/A | +--------------+-------+ RAGFlow(user)> add admin server host '127.0.0.1:9880'; SUCCESS RAGFlow(user)> show admin server; +-------------------+-----------+ | field | value | +-------------------+-----------+ | admin_server_ip | 127.0.0.1 | | admin_server_port | 9880 | | auth | no auth | +-------------------+-----------+ RAGFlow(user)> delete admin server; SUCCESS RAGFlow(user)> show admin server; +--------------+-------+ | field | value | +--------------+-------+ | admin_server | N/A | +--------------+-------+ RAGFlow(user)> show current +-----------------+-------------+ | field | value | +-----------------+-------------+ | api_server_port | 9384 | | user_name | aaa@aaa.com | | user_password | *** | | mode | api | | verbose | false | | api_server | default | | api_server_ip | 127.0.0.1 | | auth | login | | output | table | | interactive | true | +-----------------+-------------+ ``` ### Type of change - [x] Refactoring --------- Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-06-09 15:22:50 +08:00
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
cmd := NewCommand("api_use_model")
if common.IsCompositeModelName(modelNameOrID) {
cmd.Params["composite_model_name"] = modelNameOrID
} else if common.IsUUID(modelNameOrID) {
cmd.Params["model_id"] = modelNameOrID
} else {
return nil, fmt.Errorf("invalid format of model name or ID: %s", modelNameOrID)
}
2026-06-25 20:36:50 +08:00
return cmd, nil
}
func (p *Parser) parseAPIUseAPIServer() (*Command, error) {
p.nextToken() // consume API
2026-06-25 20:36:50 +08:00
serverName, err := p.parseQuotedString()
if err != nil {
return nil, err
}
p.nextToken()
cmd := NewCommand("api_use_api_server")
cmd.Params["server_name"] = serverName
2026-06-25 20:36:50 +08:00
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
func (p *Parser) parseAPIUseAdminServer() (*Command, error) {
p.nextToken() // consume ADMIN
2026-06-25 20:36:50 +08:00
cmd := NewCommand("api_use_admin_server")
2026-06-25 20:36:50 +08:00
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
Go: refactor CLI (#15728) ### What problem does this PR solve? ``` RAGFlow(user)> add api server 'ccc' host '127.0.0.1:9980'; SUCCESS RAGFlow(user)> list api server; +------------+---------------+-----------------+---------+-------------+---------------+ | api_server | api_server_ip | api_server_port | auth | user_name | user_password | +------------+---------------+-----------------+---------+-------------+---------------+ | ccc | 127.0.0.1 | 9980 | no auth | | | | default | 127.0.0.1 | 9384 | login | aaa@aaa.com | *** | +------------+---------------+-----------------+---------+-------------+---------------+ RAGFlow(user)> delete api server 'ccc'; SUCCESS RAGFlow(user)> list api server; +------------+---------------+-----------------+---------+ | api_server | api_server_ip | api_server_port | auth | +------------+---------------+-----------------+---------+ | default | 127.0.0.1 | 9384 | no auth | +------------+---------------+-----------------+---------+ RAGFlow(user)> show admin server; +--------------+-------+ | field | value | +--------------+-------+ | admin_server | N/A | +--------------+-------+ RAGFlow(user)> add admin server host '127.0.0.1:9880'; SUCCESS RAGFlow(user)> show admin server; +-------------------+-----------+ | field | value | +-------------------+-----------+ | admin_server_ip | 127.0.0.1 | | admin_server_port | 9880 | | auth | no auth | +-------------------+-----------+ RAGFlow(user)> delete admin server; SUCCESS RAGFlow(user)> show admin server; +--------------+-------+ | field | value | +--------------+-------+ | admin_server | N/A | +--------------+-------+ RAGFlow(user)> show current +-----------------+-------------+ | field | value | +-----------------+-------------+ | api_server_port | 9384 | | user_name | aaa@aaa.com | | user_password | *** | | mode | api | | verbose | false | | api_server | default | | api_server_ip | 127.0.0.1 | | auth | login | | output | table | | interactive | true | +-----------------+-------------+ ``` ### Type of change - [x] Refactoring --------- Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2026-06-09 15:22:50 +08:00
return cmd, nil
}
func (p *Parser) parseAPIRemove() (*Command, error) {
p.nextToken() // consume REMOVE
switch p.curToken.Type {
// API commands
case TokenIngestion:
return p.parseAPIRemoveTask()
// Dev commands
case TokenTag:
return p.parseDevRemoveTags()
case TokenChunks, TokenAll:
return p.parseDevRemoveChunk()
default:
return nil, fmt.Errorf("unknown REMOVE target: %s", p.curToken.Value)
}
}
func (p *Parser) parseAPIRemoveTask() (*Command, error) {
p.nextToken() // consume Ingestion
if p.curToken.Type != TokenTasks {
return nil, fmt.Errorf("expected TASKS")
}
p.nextToken() // consume TASKS
taskStr, err := p.parseQuotedString()
if err != nil {
return nil, err
}
cmd := NewCommand("api_remove_task")
tasks := strings.Split(taskStr, " ")
cmd.Params["tasks"] = tasks
// Semicolon is optional
if p.curToken.Type == TokenSemicolon {
p.nextToken()
}
return cmd, nil
}
// parseOpenaiChat parses:
//
// OPENAI_CHAT <chat_id> <message>
// [model <string>] [system <string>]
// [history <string>] [history_delimiter <string>]
// [temperature <float>] [max_tokens <int>] [stream <bool>]
// [top_p <float>] [frequency_penalty <float>] [presence_penalty <float>]
// [extra_body <json>]
// ;
//
// Named options can appear in any order. The chat_id and message are
// required positional args; everything else is optional with a default.
//
// `history` is captured as a single string in cmd.Params["history_raw"]
// and is split into turns by cmd.Params["history_delimiter"] (default
// ";") later in buildOpenaiChatRequestBody — this two-step split lets
// `history_delimiter` and `history` appear in either order on the
// command line. The chosen delimiter must not appear inside any
// message body.
//
// `extra_body` is well-formed JSON. The accepted keys are:
//
// reference bool
// reference_metadata { include?: bool, fields?: string[] }
// metadata_condition { logic?: "and"|"or", conditions?: [{key, operator, value}] }
// (See user_command.go:allowedExtraBodyKeys for the authoritative set)
func (p *Parser) parseOpenaiChat() (*Command, error) {
p.nextToken() // consume OPENAI_CHAT
if p.curToken.Type == TokenDash {
dashCount := 0
for p.curToken.Type == TokenDash {
dashCount++
p.nextToken()
}
if dashCount > 0 && p.curToken.Type == TokenIdentifier {
switch strings.ToLower(p.curToken.Value) {
case "h", "help":
return NewCommand("openai_chat_help"), nil
}
}
return nil, fmt.Errorf("OPENAI_CHAT: only -h/--help takes no args; otherwise expected chat_id and message")
}
cmd := NewCommand("api_openai_chat")
// Defaults — match the OpenAI spec / RAGFlow server behavior.
cmd.Params["model"] = "model" // placeholder; server resolves to dialog.llm_id
cmd.Params["temperature"] = 0.0
cmd.Params["max_tokens"] = 0
cmd.Params["stream"] = false
// Required positional: <chat_id> <message>
chatID, err := p.parseQuotedString()
if err != nil {
return nil, fmt.Errorf("OPENAI_CHAT: expected chat_id as first argument: %w", err)
}
cmd.Params["chat_id"] = chatID
p.nextToken()
message, err := p.parseQuotedString()
if err != nil {
return nil, fmt.Errorf("OPENAI_CHAT: expected message as second argument: %w", err)
}
cmd.Params["message"] = message
p.nextToken()
// Optional
handleOption := func(name string) error {
switch name {
case "model", "system":
v, err := p.parseQuotedString()
if err != nil {
return fmt.Errorf("OPENAI_CHAT %s: expected quoted string, got %s", name, p.curToken.Value)
}
cmd.Params[name] = v
p.nextToken()
case "temperature", "top_p", "frequency_penalty", "presence_penalty":
v, err := p.parseFloat()
if err != nil {
return fmt.Errorf("OPENAI_CHAT %s: expected number, got %s", name, p.curToken.Value)
}
cmd.Params[name] = v
p.nextToken()
case "max_tokens":
v, err := p.parseNumber()
if err != nil {
return fmt.Errorf("OPENAI_CHAT max_tokens: expected integer, got %s", p.curToken.Value)
}
cmd.Params["max_tokens"] = v
p.nextToken()
case "stream":
v, err := p.parseBool()
if err != nil {
return fmt.Errorf("OPENAI_CHAT %s: expected true|false, got %s", name, p.curToken.Value)
}
cmd.Params[name] = v
// parseBool already advances the cursor.
case "extra_body":
raw, err := p.parseJSONLiteral()
if err != nil {
return fmt.Errorf("OPENAI_CHAT %s: %w", name, err)
}
cmd.Params[name] = raw
p.nextToken()
case "history":
raw, err := p.parseQuotedString()
if err != nil {
return fmt.Errorf("OPENAI_CHAT history: expected quoted string, got %s", p.curToken.Value)
}
cmd.Params["history_raw"] = raw
p.nextToken()
case "history_delimiter":
v, err := p.parseQuotedString()
if err != nil {
return fmt.Errorf("OPENAI_CHAT history_delimiter: expected quoted string, got %s", p.curToken.Value)
}
cmd.Params["history_delimiter"] = v
p.nextToken()
default:
return fmt.Errorf("OPENAI_CHAT: unknown option %q (valid: model, system, history, history_delimiter, temperature, max_tokens, stream, top_p, frequency_penalty, presence_penalty, extra_body)", name)
}
return nil
}
// Named options, any order, until ';'.
optionsLoop:
for {
switch p.curToken.Type {
case TokenSemicolon:
p.nextToken()
break optionsLoop
case TokenEOF:
break optionsLoop
case TokenIdentifier, TokenQuotedString:
name := p.curToken.Value
if p.curToken.Type == TokenQuotedString {
name = strings.Trim(name, "'\"")
}
p.nextToken()
if err := handleOption(name); err != nil {
return nil, err
}
default:
if !isKeyword(p.curToken.Type) {
return nil, fmt.Errorf("OPENAI_CHAT: unexpected token %q in option list (valid options: model, system, history, history_delimiter, temperature, max_tokens, stream, top_p, frequency_penalty, presence_penalty, extra_body)", p.curToken.Value)
}
name := p.curToken.Value
p.nextToken()
if err := handleOption(name); err != nil {
return nil, err
}
}
}
return cmd, nil
}
// parseJSONLiteral consumes a TokenQuotedString whose payload is a JSON
// value (object, array, string, number, or boolean) and returns it as
// the original raw string (NOT decoded — the caller decides whether to
// embed it into a larger JSON object or pass it through as-is).
func (p *Parser) parseJSONLiteral() (string, error) {
if p.curToken.Type != TokenQuotedString {
return "", fmt.Errorf("expected JSON literal in single/double quotes, got %s", p.curToken.Value)
}
raw := p.curToken.Value
// Validate it actually parses as JSON so we fail fast on
// typos like `'{}' extra comma'` or `'not json'`.
var probe interface{}
if err := json.Unmarshal([]byte(raw), &probe); err != nil {
return "", fmt.Errorf("invalid JSON literal %q: %w", raw, err)
}
return raw, nil
}
// parseBool accepts a TokenIdentifier "true"/"false"
func (p *Parser) parseBool() (bool, error) {
switch strings.ToLower(p.curToken.Value) {
case "true":
p.nextToken()
return true, nil
case "false":
p.nextToken()
return false, nil
}
return false, fmt.Errorf("expected true or false, got %q", p.curToken.Value)
}
// historyRoleRegex matches the role prefix on a turn. The captured
// alternation is the role; the colon is required so we don't
// accidentally split on a word like "user:foo" appearing inside
// other content.
var historyRoleRegex = regexp.MustCompile(`(?i)^(user|assistant):`)
// defaultHistoryDelimiter is the turn separator used when the
// caller does not pass the `history_delimiter` option.
const defaultHistoryDelimiter = ";"
// parseHistory splits the history literal into a slice of
// {"role": ..., "content": ...} maps. Format:
//
// "user:question one;assistant:answer one;user:question two"
//
// Turns are separated by `history_delimiter` (default `;`). Each
// segment must start with the role prefix `user:` or `assistant:`
// (case-insensitive).
func parseHistory(literal, delimiter string) ([]map[string]string, error) {
if delimiter == "" {
delimiter = defaultHistoryDelimiter
}
// Trim a single pair of surrounding quotes if present.
s := strings.TrimSpace(literal)
if len(s) >= 2 {
first, last := s[0], s[len(s)-1]
if (first == '"' && last == '"') || (first == '\'' && last == '\'') {
s = s[1 : len(s)-1]
}
}
raw := strings.Split(s, delimiter)
turns := make([]map[string]string, 0, len(raw))
for _, segment := range raw {
segment = strings.TrimSpace(segment)
if segment == "" {
continue
}
m := historyRoleRegex.FindStringSubmatch(segment)
if m == nil {
return nil, fmt.Errorf("history segment %q must start with 'user:' or 'assistant:'", segment)
}
role := strings.ToLower(m[1])
// Drop the "<role>:" prefix (m[0] is the whole match, e.g.
// "user:"; we want the content AFTER the colon).
content := strings.TrimPrefix(segment, m[0])
turns = append(turns, map[string]string{
"role": role,
"content": content,
})
}
if len(turns) == 0 {
return nil, fmt.Errorf("history is empty or unparseable: %q", literal)
}
return turns, nil
}