支持流式输出思考过程与思考模式配置
This commit is contained in:
+45
-5
@@ -2,10 +2,12 @@ package bot
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/openai/openai-go"
|
||||
"github.com/openai/openai-go/option"
|
||||
"github.com/openai/openai-go/shared"
|
||||
"myaibot/internal/config"
|
||||
)
|
||||
|
||||
@@ -61,7 +63,27 @@ func (b *Bot) SwitchModel(id string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Bot) Chat(ctx context.Context, userMsg string) (string, error) {
|
||||
func (b *Bot) SetThinking(v string) error {
|
||||
if v != "enabled" && v != "disabled" {
|
||||
return fmt.Errorf("无效值: %s(可选 enabled/disabled)", v)
|
||||
}
|
||||
b.provider.Thinking = v
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Bot) SetEffort(v string) error {
|
||||
if v != "low" && v != "high" && v != "max" {
|
||||
return fmt.Errorf("无效值: %s(可选 low/high/max)", v)
|
||||
}
|
||||
b.provider.ReasoningEffort = v
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Bot) ThinkingConfig() (string, string) {
|
||||
return b.provider.Thinking, b.provider.ReasoningEffort
|
||||
}
|
||||
|
||||
func (b *Bot) Chat(ctx context.Context, userMsg string, onReasoning, onContent func(string)) (string, error) {
|
||||
if b.provider.APIKey == "" {
|
||||
return "", fmt.Errorf("供应商 %s 未配置 api_key,请编辑 data/config.yaml", b.provider.Name)
|
||||
}
|
||||
@@ -70,14 +92,32 @@ func (b *Bot) Chat(ctx context.Context, userMsg string) (string, error) {
|
||||
history = append(history, b.history...)
|
||||
history = append(history, openai.UserMessage(userMsg))
|
||||
|
||||
stream := b.client().Chat.Completions.NewStreaming(ctx, openai.ChatCompletionNewParams{
|
||||
params := openai.ChatCompletionNewParams{
|
||||
Model: b.model,
|
||||
Messages: history,
|
||||
})
|
||||
}
|
||||
if p := b.provider; p.ReasoningEffort != "" && p.Thinking != "disabled" {
|
||||
params.ReasoningEffort = shared.ReasoningEffort(p.ReasoningEffort)
|
||||
}
|
||||
if b.provider.Thinking != "" {
|
||||
params.SetExtraFields(map[string]any{
|
||||
"thinking": map[string]string{"type": b.provider.Thinking},
|
||||
})
|
||||
}
|
||||
stream := b.client().Chat.Completions.NewStreaming(ctx, params)
|
||||
answer := ""
|
||||
for stream.Next() {
|
||||
for _, delta := range stream.Current().Choices {
|
||||
answer += delta.Delta.Content
|
||||
for _, choice := range stream.Current().Choices {
|
||||
if rc, ok := choice.Delta.JSON.ExtraFields["reasoning_content"]; ok && rc.Valid() {
|
||||
var s string
|
||||
if json.Unmarshal([]byte(rc.Raw()), &s) == nil && s != "" {
|
||||
onReasoning(s)
|
||||
}
|
||||
}
|
||||
if choice.Delta.Content != "" {
|
||||
answer += choice.Delta.Content
|
||||
onContent(choice.Delta.Content)
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := stream.Err(); err != nil {
|
||||
|
||||
@@ -16,10 +16,12 @@ const (
|
||||
)
|
||||
|
||||
type Provider struct {
|
||||
Name string `yaml:"name"`
|
||||
APIKey string `yaml:"api_key"`
|
||||
BaseURL string `yaml:"base_url"`
|
||||
Models []string `yaml:"models"`
|
||||
Name string `yaml:"name"`
|
||||
APIKey string `yaml:"api_key"`
|
||||
BaseURL string `yaml:"base_url"`
|
||||
Models []string `yaml:"models"`
|
||||
Thinking string `yaml:"thinking"`
|
||||
ReasoningEffort string `yaml:"reasoning_effort"`
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
@@ -146,6 +148,12 @@ func validate(c *Config) error {
|
||||
return fmt.Errorf("供应商 %s 包含空模型名", p.Name)
|
||||
}
|
||||
}
|
||||
if p.Thinking != "" && !contains([]string{"enabled", "disabled"}, p.Thinking) {
|
||||
return fmt.Errorf("供应商 %s 的 thinking 无效: %q(可选 enabled/disabled)", p.Name, p.Thinking)
|
||||
}
|
||||
if p.ReasoningEffort != "" && !contains([]string{"low", "high", "max"}, p.ReasoningEffort) {
|
||||
return fmt.Errorf("供应商 %s 的 reasoning_effort 无效: %q(可选 low/high/max)", p.Name, p.ReasoningEffort)
|
||||
}
|
||||
}
|
||||
if _, ok := names[c.DefaultProvider]; !ok {
|
||||
return fmt.Errorf("default_provider %q 不存在", c.DefaultProvider)
|
||||
|
||||
Reference in New Issue
Block a user