fix(opencode): reasoning-fallback в вердикте + API-логи в debug-уровень
- verdict(): если в завершённом assistant-сообщении нет text-парта, но есть reasoning — вердикт собирается из reasoning (fallback), а не падает с 'нет text-части в ответе'. Решает сбой dev/reviewer/postmortem на моделях, отвечающих только thinking (например, через прокси tokentool). - Отладочные логи API-вызовов (запрос/ответ) помечены маркером 'debug', чтобы в панели «Логи» они классифицировались как debug, а не info.
This commit is contained in:
@@ -24,9 +24,9 @@ import (
|
||||
// Prompt не блокирует: вердикт собирается поллингом из content[].type=="text"
|
||||
// новых assistant-сообщений (см. Runner.awaitVerdict).
|
||||
type Client struct {
|
||||
BaseURL string // http://host:port (без завершающего слеша)
|
||||
Password string // basic auth (username "opencode")
|
||||
Debug bool // включать отладочные логи API-вызовов (log.level=debug)
|
||||
BaseURL string // http://host:port (без завершающего слеша)
|
||||
Password string // basic auth (username "opencode")
|
||||
Debug bool // включать отладочные логи API-вызовов (log.level=debug)
|
||||
http *http.Client // единый клиент: все операции быстрые (нет блокирующего Send)
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ func (c *Client) do(ctx context.Context, method, path, op string, body []byte) (
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
}
|
||||
if c.Debug {
|
||||
log.Printf("opencode api %s -> %s %s%s", op, method, c.BaseURL, path)
|
||||
log.Printf("opencode api debug: %s -> %s %s%s", op, method, c.BaseURL, path)
|
||||
}
|
||||
resp, err := c.http.Do(req)
|
||||
if err != nil {
|
||||
@@ -76,12 +76,12 @@ func (c *Client) do(ctx context.Context, method, path, op string, body []byte) (
|
||||
}
|
||||
if resp.StatusCode < 200 || resp.StatusCode > 299 {
|
||||
if c.Debug {
|
||||
log.Printf("opencode api %s response: status %d", op, resp.StatusCode)
|
||||
log.Printf("opencode api debug: %s response: status %d", op, resp.StatusCode)
|
||||
}
|
||||
return nil, &ClientErr{Op: op, Err: fmt.Errorf("status %d: %s", resp.StatusCode, truncateStr(string(b), 300))}
|
||||
}
|
||||
if c.Debug {
|
||||
log.Printf("opencode api %s response (%d bytes)", op, len(b))
|
||||
log.Printf("opencode api debug: %s response (%d bytes)", op, len(b))
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
@@ -320,6 +320,26 @@ func assistantText(msgs []v2Message, since int64) []string {
|
||||
return texts
|
||||
}
|
||||
|
||||
// assistantVerdict собирает финальный текст ответа: сначала text-парты, а если
|
||||
// их нет — только reasoning-парты (fallback для моделей, которые на некоторые
|
||||
// запросы отвечают лишь reasoning без text). usedReasoning=true означает, что
|
||||
// text-партов не было вовсе и вердикт собран из reasoning.
|
||||
func assistantVerdict(msgs []v2Message, since int64) (texts []string, usedReasoning bool) {
|
||||
if texts := assistantText(msgs, since); len(texts) > 0 {
|
||||
return texts, false
|
||||
}
|
||||
ass := assistantSince(msgs, since)
|
||||
reasoning := make([]string, 0, len(ass))
|
||||
for i := len(ass) - 1; i >= 0; i-- {
|
||||
for _, p := range ass[i].Content {
|
||||
if p.Type == "reasoning" && p.Text != "" {
|
||||
reasoning = append(reasoning, p.Text)
|
||||
}
|
||||
}
|
||||
}
|
||||
return reasoning, len(reasoning) > 0
|
||||
}
|
||||
|
||||
func truncateStr(s string, n int) string {
|
||||
if len(s) <= n {
|
||||
return s
|
||||
|
||||
Reference in New Issue
Block a user