mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-08-04 23:00:30 +08:00
Implement InsertDataset and InsertMetadata in GO (#13883)
### What problem does this PR solve? Implement InsertDataset and InsertMetadata in GO new internal cli for go: INSERT DATASET FROM FILE "file_name" INSERT METADATA FROM FILE "file_name" ### Type of change - [x] Refactoring
This commit is contained in:
@@ -223,6 +223,10 @@ func (c *RAGFlowClient) ExecuteUserCommand(cmd *Command) (ResponseIf, error) {
|
||||
return c.CEList(cmd)
|
||||
case "ce_search":
|
||||
return c.CESearch(cmd)
|
||||
case "insert_dataset_from_file":
|
||||
return c.InsertDatasetFromFile(cmd)
|
||||
case "insert_metadata_from_file":
|
||||
return c.InsertMetadataFromFile(cmd)
|
||||
// TODO: Implement other commands
|
||||
default:
|
||||
return nil, fmt.Errorf("command '%s' would be executed with API", cmd.Type)
|
||||
|
||||
@@ -305,6 +305,14 @@ func (l *Lexer) lookupIdent(ident string) Token {
|
||||
return Token{Type: TokenAvailable, Value: ident}
|
||||
case "NAME":
|
||||
return Token{Type: TokenName, Value: ident}
|
||||
case "POOL":
|
||||
return Token{Type: TokenPool, Value: ident}
|
||||
case "INSERT":
|
||||
return Token{Type: TokenInsert, Value: ident}
|
||||
case "FILE":
|
||||
return Token{Type: TokenFile, Value: ident}
|
||||
case "METADATA":
|
||||
return Token{Type: TokenMetadata, Value: ident}
|
||||
default:
|
||||
return Token{Type: TokenIdentifier, Value: ident}
|
||||
}
|
||||
|
||||
@@ -166,6 +166,8 @@ func (p *Parser) parseUserCommand() (*Command, error) {
|
||||
return p.parseGenerateCommand()
|
||||
case TokenImport:
|
||||
return p.parseImportCommand()
|
||||
case TokenInsert:
|
||||
return p.parseInsertCommand()
|
||||
case TokenSearch:
|
||||
return p.parseSearchCommand()
|
||||
case TokenParse:
|
||||
@@ -217,7 +219,7 @@ func (p *Parser) expectSemicolon() error {
|
||||
}
|
||||
|
||||
func isKeyword(tokenType int) bool {
|
||||
return tokenType >= TokenLogin && tokenType <= TokenDocMeta
|
||||
return tokenType >= TokenLogin && tokenType <= TokenMetadata
|
||||
}
|
||||
|
||||
// isCECommand checks if the given string is a ContextEngine command
|
||||
|
||||
@@ -104,6 +104,9 @@ const (
|
||||
TokenVectorSize
|
||||
TokenDocMeta
|
||||
TokenName // For ALTER PROVIDER <name> NAME <new_name>
|
||||
TokenInsert
|
||||
TokenFile
|
||||
TokenMetadata
|
||||
|
||||
// Literals
|
||||
TokenIdentifier
|
||||
|
||||
@@ -941,3 +941,93 @@ func (c *RAGFlowClient) CESearch(cmd *Command) (ResponseIf, error) {
|
||||
|
||||
return &response, nil
|
||||
}
|
||||
|
||||
// InsertDatasetFromFile inserts dataset chunks from a JSON file
|
||||
func (c *RAGFlowClient) InsertDatasetFromFile(cmd *Command) (ResponseIf, error) {
|
||||
if c.ServerType != "user" {
|
||||
return nil, fmt.Errorf("this command is only allowed in USER mode")
|
||||
}
|
||||
|
||||
filePath, ok := cmd.Params["file_path"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("file_path not provided")
|
||||
}
|
||||
|
||||
payload := map[string]interface{}{
|
||||
"file_path": filePath,
|
||||
}
|
||||
|
||||
resp, err := c.HTTPClient.Request("POST", "/kb/insert_from_file", false, "web", nil, payload)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to insert dataset from file: %w", err)
|
||||
}
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
return nil, fmt.Errorf("failed to insert dataset from file: HTTP %d, body: %s", resp.StatusCode, string(resp.Body))
|
||||
}
|
||||
|
||||
resJSON, err := resp.JSON()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid JSON response: %w", err)
|
||||
}
|
||||
|
||||
code, ok := resJSON["code"].(float64)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid response format: code is not a number")
|
||||
}
|
||||
|
||||
var result SimpleResponse
|
||||
result.Code = int(code)
|
||||
if result.Code == 0 {
|
||||
result.Message = fmt.Sprintf("Success to insert dataset from file: %s", filePath)
|
||||
} else {
|
||||
result.Message = fmt.Sprintf("Failed to insert dataset from file: %v", resJSON)
|
||||
}
|
||||
result.Duration = 0
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// InsertMetadataFromFile inserts metadata from a JSON file
|
||||
func (c *RAGFlowClient) InsertMetadataFromFile(cmd *Command) (ResponseIf, error) {
|
||||
if c.ServerType != "user" {
|
||||
return nil, fmt.Errorf("this command is only allowed in USER mode")
|
||||
}
|
||||
|
||||
filePath, ok := cmd.Params["file_path"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("file_path not provided")
|
||||
}
|
||||
|
||||
payload := map[string]interface{}{
|
||||
"file_path": filePath,
|
||||
}
|
||||
|
||||
resp, err := c.HTTPClient.Request("POST", "/tenant/insert_metadata_from_file", false, "web", nil, payload)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to insert metadata from file: %w", err)
|
||||
}
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
return nil, fmt.Errorf("failed to insert metadata from file: HTTP %d, body: %s", resp.StatusCode, string(resp.Body))
|
||||
}
|
||||
|
||||
resJSON, err := resp.JSON()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid JSON response: %w", err)
|
||||
}
|
||||
|
||||
code, ok := resJSON["code"].(float64)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid response format: code is not a number")
|
||||
}
|
||||
|
||||
var result SimpleResponse
|
||||
result.Code = int(code)
|
||||
if result.Code == 0 {
|
||||
result.Message = fmt.Sprintf("Success to insert metadata from file: %s", filePath)
|
||||
} else {
|
||||
result.Message = fmt.Sprintf("Failed to insert metadata from file: %v", resJSON)
|
||||
}
|
||||
result.Duration = 0
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
@@ -32,6 +32,21 @@ func (p *Parser) parseLoginUser() (*Command, error) {
|
||||
cmd.Params["email"] = email
|
||||
|
||||
p.nextToken()
|
||||
// Optional: WITH PASSWORD 'password'
|
||||
if p.curToken.Type == TokenWith {
|
||||
p.nextToken()
|
||||
if p.curToken.Type != TokenPassword {
|
||||
return nil, fmt.Errorf("expected PASSWORD after WITH")
|
||||
}
|
||||
p.nextToken()
|
||||
password, err := p.parseQuotedString()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cmd.Params["password"] = password
|
||||
p.nextToken()
|
||||
}
|
||||
|
||||
// Semicolon is optional for UNSET TOKEN
|
||||
if p.curToken.Type == TokenSemicolon {
|
||||
p.nextToken()
|
||||
@@ -1531,6 +1546,88 @@ func (p *Parser) parseImportCommand() (*Command, error) {
|
||||
return cmd, nil
|
||||
}
|
||||
|
||||
// parseInsertCommand parses INSERT command and dispatches to specific handler
|
||||
func (p *Parser) parseInsertCommand() (*Command, error) {
|
||||
p.nextToken() // consume INSERT
|
||||
|
||||
// Expect DATASET or METADATA
|
||||
if p.curToken.Type == TokenDataset {
|
||||
return p.parseInsertDatasetFromFile()
|
||||
}
|
||||
if p.curToken.Type == TokenMetadata {
|
||||
return p.parseInsertMetadataFromFile()
|
||||
}
|
||||
return nil, fmt.Errorf("expected DATASET or METADATA after INSERT, got %s", p.curToken.Value)
|
||||
}
|
||||
|
||||
// Internal CLI for GO
|
||||
// parseInsertDatasetFromFile parses: INSERT DATASET FROM FILE "file_path"
|
||||
func (p *Parser) parseInsertDatasetFromFile() (*Command, error) {
|
||||
p.nextToken() // consume DATASET
|
||||
|
||||
// Expect FROM
|
||||
if p.curToken.Type != TokenFrom {
|
||||
return nil, fmt.Errorf("expected FROM, got %s", p.curToken.Value)
|
||||
}
|
||||
p.nextToken()
|
||||
|
||||
// Expect FILE
|
||||
if p.curToken.Type != TokenFile {
|
||||
return nil, fmt.Errorf("expected FILE, got %s", p.curToken.Value)
|
||||
}
|
||||
p.nextToken()
|
||||
|
||||
// Get file path (quoted string)
|
||||
filePath, err := p.parseQuotedString()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cmd := NewCommand("insert_dataset_from_file")
|
||||
cmd.Params["file_path"] = filePath
|
||||
|
||||
p.nextToken()
|
||||
// Semicolon is optional
|
||||
if p.curToken.Type == TokenSemicolon {
|
||||
p.nextToken()
|
||||
}
|
||||
return cmd, nil
|
||||
}
|
||||
|
||||
// Internal CLI for GO
|
||||
// parseInsertMetadataFromFile parses: INSERT INTO METADATA FROM FILE "file_path"
|
||||
func (p *Parser) parseInsertMetadataFromFile() (*Command, error) {
|
||||
p.nextToken() // consume METADATA
|
||||
|
||||
// Expect FROM
|
||||
if p.curToken.Type != TokenFrom {
|
||||
return nil, fmt.Errorf("expected FROM, got %s", p.curToken.Value)
|
||||
}
|
||||
p.nextToken()
|
||||
|
||||
// Expect FILE
|
||||
if p.curToken.Type != TokenFile {
|
||||
return nil, fmt.Errorf("expected FILE, got %s", p.curToken.Value)
|
||||
}
|
||||
p.nextToken()
|
||||
|
||||
// Get file path (quoted string)
|
||||
filePath, err := p.parseQuotedString()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cmd := NewCommand("insert_metadata_from_file")
|
||||
cmd.Params["file_path"] = filePath
|
||||
|
||||
p.nextToken()
|
||||
// Semicolon is optional
|
||||
if p.curToken.Type == TokenSemicolon {
|
||||
p.nextToken()
|
||||
}
|
||||
return cmd, nil
|
||||
}
|
||||
|
||||
func (p *Parser) parseSearchCommand() (*Command, error) {
|
||||
p.nextToken() // consume SEARCH
|
||||
question, err := p.parseQuotedString()
|
||||
@@ -1687,6 +1784,8 @@ func (p *Parser) parseUserStatement() (*Command, error) {
|
||||
return p.parseParseCommand()
|
||||
case TokenImport:
|
||||
return p.parseImportCommand()
|
||||
case TokenInsert:
|
||||
return p.parseInsertCommand()
|
||||
case TokenSearch:
|
||||
return p.parseSearchCommand()
|
||||
default:
|
||||
|
||||
Reference in New Issue
Block a user