mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-07-15 20:27:20 +08:00
Implement chat completions in go (#16491)
### Summary POST /api/v1/chat/completions
This commit is contained in:
@@ -2702,6 +2702,13 @@ func (p *Parser) parseAPIDisable() (*Command, error) {
|
||||
func (p *Parser) parseAPIChat() (*Command, error) {
|
||||
p.nextToken() // consume CHAT
|
||||
|
||||
// Redirect "chat completion[s]" to the standalone chat completions parser.
|
||||
if p.curToken.Type == TokenIdentifier &&
|
||||
(strings.EqualFold(p.curToken.Value, "completion") || strings.EqualFold(p.curToken.Value, "completions")) {
|
||||
p.nextToken() // consume completion/completions
|
||||
return p.parseChatCompletionsBody()
|
||||
}
|
||||
|
||||
var err error
|
||||
var modelNameOrID string = ""
|
||||
var messages []string
|
||||
@@ -3759,6 +3766,150 @@ optionsLoop:
|
||||
return cmd, nil
|
||||
}
|
||||
|
||||
// CHAT COMPLETIONS <question>
|
||||
// [chat_id <string>] [session <string>] [llm <string>]
|
||||
|
||||
|
||||
// parseChatCompletionsBody parses the question and options of a CHAT COMPLETIONS
|
||||
// command. The leading keyword(s) must already have been consumed by the caller.
|
||||
func (p *Parser) parseChatCompletionsBody() (*Command, error) {
|
||||
|
||||
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("chat completions help"), nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("CHAT COMPLETIONS: only -h/--help takes no args; otherwise expected question")
|
||||
}
|
||||
|
||||
cmd := NewCommand("chat completions")
|
||||
|
||||
// Defaults
|
||||
cmd.Params["chat_id"] = ""
|
||||
cmd.Params["temperature"] = 0.0
|
||||
cmd.Params["max_tokens"] = 0
|
||||
cmd.Params["stream"] = false
|
||||
|
||||
// Track which options were explicitly set (distinguishes from defaults).
|
||||
cmd.Params["_set"] = map[string]bool{}
|
||||
|
||||
// Required positional: <question>
|
||||
question, err := p.parseQuotedString()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("CHAT COMPLETIONS: expected question: %w", err)
|
||||
}
|
||||
cmd.Params["question"] = question
|
||||
p.nextToken()
|
||||
|
||||
// Optional named options
|
||||
handleOption := func(name string) error {
|
||||
switch name {
|
||||
case "chat_id", "session", "llm", "system":
|
||||
v, err := p.parseQuotedString()
|
||||
if err != nil {
|
||||
return fmt.Errorf("CHAT COMPLETIONS %s: expected quoted string, got %s", name, p.curToken.Value)
|
||||
}
|
||||
cmd.Params[name] = v
|
||||
p.nextToken()
|
||||
markSet(cmd, name)
|
||||
case "temperature", "top_p", "frequency_penalty", "presence_penalty":
|
||||
v, err := p.parseFloat()
|
||||
if err != nil {
|
||||
return fmt.Errorf("CHAT COMPLETIONS %s: expected number, got %s", name, p.curToken.Value)
|
||||
}
|
||||
cmd.Params[name] = v
|
||||
p.nextToken()
|
||||
markSet(cmd, name)
|
||||
case "max_tokens":
|
||||
v, err := p.parseNumber()
|
||||
if err != nil {
|
||||
return fmt.Errorf("CHAT COMPLETIONS max_tokens: expected integer, got %s", p.curToken.Value)
|
||||
}
|
||||
cmd.Params["max_tokens"] = v
|
||||
p.nextToken()
|
||||
markSet(cmd, "max_tokens")
|
||||
case "stream", "pass_all_history", "legacy":
|
||||
v, err := p.parseBool()
|
||||
if err != nil {
|
||||
return fmt.Errorf("CHAT COMPLETIONS %s: expected true|false, got %s", name, p.curToken.Value)
|
||||
}
|
||||
cmd.Params[name] = v
|
||||
markSet(cmd, name)
|
||||
case "history":
|
||||
raw, err := p.parseQuotedString()
|
||||
if err != nil {
|
||||
return fmt.Errorf("CHAT COMPLETIONS 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("CHAT COMPLETIONS history_delimiter: expected quoted string, got %s", p.curToken.Value)
|
||||
}
|
||||
cmd.Params["history_delimiter"] = v
|
||||
p.nextToken()
|
||||
default:
|
||||
return fmt.Errorf("CHAT COMPLETIONS: unknown option %q (valid: chat_id, session, llm, system, history, history_delimiter, temperature, max_tokens, stream, top_p, frequency_penalty, presence_penalty, pass_all_history, legacy)", 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("CHAT COMPLETIONS: unexpected token %q in option list (valid options: chat_id, session, llm, system, history, history_delimiter, temperature, max_tokens, stream, top_p, frequency_penalty, presence_penalty, pass_all_history, legacy)", p.curToken.Value)
|
||||
}
|
||||
name := p.curToken.Value
|
||||
p.nextToken()
|
||||
if err := handleOption(name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return cmd, nil
|
||||
}
|
||||
|
||||
func markSet(cmd *Command, name string) {
|
||||
if s, ok := cmd.Params["_set"].(map[string]bool); ok {
|
||||
s[name] = true
|
||||
}
|
||||
}
|
||||
|
||||
func isSet(cmd *Command, name string) bool {
|
||||
if s, ok := cmd.Params["_set"].(map[string]bool); ok {
|
||||
return s[name]
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
Reference in New Issue
Block a user