Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fe64579cab | ||
|
|
738bce4a03 | ||
|
|
4e8bcdd68e |
No files matched your search
+54
-7
@@ -238,16 +238,50 @@ func NewClient(cfg Config) (*Client, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isReasoningModel 判断是否为推理或带有 Beta 限制的模型(这类模型要求 temperature 固定为 1)
|
||||||
|
func isReasoningModel(model string) bool {
|
||||||
|
m := strings.ToLower(model)
|
||||||
|
return strings.HasPrefix(m, "o1") ||
|
||||||
|
strings.HasPrefix(m, "o3") ||
|
||||||
|
strings.HasPrefix(m, "o4") ||
|
||||||
|
strings.Contains(m, "reasoner") ||
|
||||||
|
strings.Contains(m, "gpt-5") ||
|
||||||
|
strings.Contains(m, "luna") ||
|
||||||
|
strings.Contains(m, "terra") ||
|
||||||
|
strings.Contains(m, "sol")
|
||||||
|
}
|
||||||
|
|
||||||
|
// isTemperatureError 检测 API 报错是否由 temperature 参数限制触发
|
||||||
|
func isTemperatureError(err error) bool {
|
||||||
|
if err == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
msg := strings.ToLower(err.Error())
|
||||||
|
return strings.Contains(msg, "temperature") || strings.Contains(msg, "beta-limitations")
|
||||||
|
}
|
||||||
|
|
||||||
// Review 一次性完整返回代码评审结果
|
// Review 一次性完整返回代码评审结果
|
||||||
func (c *Client) Review(ctx context.Context, systemPrompt, diff string) (string, error) {
|
func (c *Client) Review(ctx context.Context, systemPrompt, diff string) (string, error) {
|
||||||
resp, err := c.client.CreateChatCompletion(ctx, openai.ChatCompletionRequest{
|
temp := float32(0.2)
|
||||||
|
if isReasoningModel(c.model) {
|
||||||
|
temp = 1.0
|
||||||
|
}
|
||||||
|
|
||||||
|
req := openai.ChatCompletionRequest{
|
||||||
Model: c.model,
|
Model: c.model,
|
||||||
Messages: []openai.ChatCompletionMessage{
|
Messages: []openai.ChatCompletionMessage{
|
||||||
{Role: openai.ChatMessageRoleSystem, Content: systemPrompt},
|
{Role: openai.ChatMessageRoleSystem, Content: systemPrompt},
|
||||||
{Role: openai.ChatMessageRoleUser, Content: diff},
|
{Role: openai.ChatMessageRoleUser, Content: diff},
|
||||||
},
|
},
|
||||||
Temperature: 0.2, // 代码评审低随机性
|
Temperature: temp,
|
||||||
})
|
}
|
||||||
|
|
||||||
|
resp, err := c.client.CreateChatCompletion(ctx, req)
|
||||||
|
// 针对限制了 temperature 的模型,且首次非 1.0 时,自动调整为 1.0 重试
|
||||||
|
if err != nil && req.Temperature != 1.0 && isTemperatureError(err) {
|
||||||
|
req.Temperature = 1.0
|
||||||
|
resp, err = c.client.CreateChatCompletion(ctx, req)
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("代码评审调用失败: %w", err)
|
return "", fmt.Errorf("代码评审调用失败: %w", err)
|
||||||
@@ -376,18 +410,31 @@ func (u *Usage) EstimateCost() float64 {
|
|||||||
|
|
||||||
// ReviewStream 流式逐字返回评审结果(打字机效果),同时返回 Token 使用量
|
// ReviewStream 流式逐字返回评审结果(打字机效果),同时返回 Token 使用量
|
||||||
func (c *Client) ReviewStream(ctx context.Context, systemPrompt, diff string, onChunk func(chunk string)) (*Usage, error) {
|
func (c *Client) ReviewStream(ctx context.Context, systemPrompt, diff string, onChunk func(chunk string)) (*Usage, error) {
|
||||||
stream, err := c.client.CreateChatCompletionStream(ctx, openai.ChatCompletionRequest{
|
temp := float32(0.2)
|
||||||
|
if isReasoningModel(c.model) {
|
||||||
|
temp = 1.0
|
||||||
|
}
|
||||||
|
|
||||||
|
req := openai.ChatCompletionRequest{
|
||||||
Model: c.model,
|
Model: c.model,
|
||||||
Messages: []openai.ChatCompletionMessage{
|
Messages: []openai.ChatCompletionMessage{
|
||||||
{Role: openai.ChatMessageRoleSystem, Content: systemPrompt},
|
{Role: openai.ChatMessageRoleSystem, Content: systemPrompt},
|
||||||
{Role: openai.ChatMessageRoleUser, Content: diff},
|
{Role: openai.ChatMessageRoleUser, Content: diff},
|
||||||
},
|
},
|
||||||
Temperature: 0.2,
|
Temperature: temp,
|
||||||
Stream: true,
|
Stream: true,
|
||||||
StreamOptions: &openai.StreamOptions{
|
StreamOptions: &openai.StreamOptions{
|
||||||
IncludeUsage: true,
|
IncludeUsage: true,
|
||||||
},
|
},
|
||||||
})
|
}
|
||||||
|
|
||||||
|
stream, err := c.client.CreateChatCompletionStream(ctx, req)
|
||||||
|
// 针对限制了 temperature 的模型,且首次非 1.0 时,自动调整为 1.0 重试
|
||||||
|
if err != nil && req.Temperature != 1.0 && isTemperatureError(err) {
|
||||||
|
req.Temperature = 1.0
|
||||||
|
stream, err = c.client.CreateChatCompletionStream(ctx, req)
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("创建流式请求失败: %w", err)
|
return nil, fmt.Errorf("创建流式请求失败: %w", err)
|
||||||
}
|
}
|
||||||
@@ -417,7 +464,7 @@ func (c *Client) ReviewStream(ctx context.Context, systemPrompt, diff string, on
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 捕获最后一个 chunk 中携带的 Usage 信息
|
// 捕获最后一个 chunk 中携带的 Usage 信息
|
||||||
if response.Usage.TotalTokens > 0 {
|
if response.Usage != nil && response.Usage.TotalTokens > 0 {
|
||||||
usage.InputTokens = response.Usage.PromptTokens
|
usage.InputTokens = response.Usage.PromptTokens
|
||||||
usage.OutputTokens = response.Usage.CompletionTokens
|
usage.OutputTokens = response.Usage.CompletionTokens
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
package llm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestIsReasoningModel(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
model string
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{"o1", true},
|
||||||
|
{"o1-mini", true},
|
||||||
|
{"o1-preview", true},
|
||||||
|
{"o3-mini", true},
|
||||||
|
{"gpt-5.6-luna", true},
|
||||||
|
{"gpt-5.6-sol", true},
|
||||||
|
{"gpt-5.6-terra", true},
|
||||||
|
{"deepseek-reasoner", true},
|
||||||
|
{"gemini-3.7-flash", false},
|
||||||
|
{"claude-3.7-sonnet", false},
|
||||||
|
{"gpt-4o", false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
got := isReasoningModel(tt.model)
|
||||||
|
if got != tt.expected {
|
||||||
|
t.Errorf("isReasoningModel(%q) = %v, expected %v", tt.model, got, tt.expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIsTemperatureError(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
errStr string
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{"this model has beta-limitations, temperature, top_p and n are fixed at 1", true},
|
||||||
|
{"temperature must be 1.0", true},
|
||||||
|
{"invalid api key", false},
|
||||||
|
{"rate limit exceeded", false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
got := isTemperatureError(fmt.Errorf("%s", tt.errStr))
|
||||||
|
if got != tt.expected {
|
||||||
|
t.Errorf("isTemperatureError(%q) = %v, expected %v", tt.errStr, got, tt.expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,7 +12,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// CurrentVersion 当前编译版本号
|
// CurrentVersion 当前编译版本号
|
||||||
const CurrentVersion = "v1.0.5"
|
const CurrentVersion = "v1.0.6"
|
||||||
|
|
||||||
const (
|
const (
|
||||||
githubAPIURL = "https://api.github.com/repos/unclesam-ly/git-air/releases/latest"
|
githubAPIURL = "https://api.github.com/repos/unclesam-ly/git-air/releases/latest"
|
||||||
|
|||||||
Reference in new issue
Block a user