распознавание: большие раздачи размечаются целиком, файлы вне плана видны
- модель адресует файл номером строки нашего списка вместо копии пути: ответ на 180 файлов вместо ~15k токенов стоит ~2.5k, усечение сотней снято, max_files и max_tokens ушли в [recognition], correction-ретрай больше не переприсылает список - негодный элемент ответа отбрасывается поимённой причиной, обрыв генерации и отказ по размеру запроса названы своими причинами, покрытие плана блокирует авто только при непокрытом видеофайле - раскладка показывает все файлы раздачи со строками «не в плане» и полным порядком сортировки; снимок списка файлов лёг рядом с планом (миграция 0012)
This commit is contained in:
+38
-4
@@ -193,8 +193,11 @@ func (c *openAICompat) do(ctx context.Context, body []byte) (Response, bool, err
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
retryable := resp.StatusCode == http.StatusTooManyRequests || resp.StatusCode >= 500
|
||||
return Response{}, retryable, fmt.Errorf("llm: status %d: %s",
|
||||
resp.StatusCode, snippet(raw))
|
||||
err := fmt.Errorf("llm: status %d: %s", resp.StatusCode, snippet(raw))
|
||||
if requestTooLarge(resp.StatusCode, raw) {
|
||||
err = fmt.Errorf("%w: %w", ErrRequestTooLarge, err)
|
||||
}
|
||||
return Response{}, retryable, err
|
||||
}
|
||||
|
||||
var cr chatResponse
|
||||
@@ -209,8 +212,9 @@ func (c *openAICompat) do(ctx context.Context, body []byte) (Response, bool, err
|
||||
}
|
||||
|
||||
return Response{
|
||||
Content: cr.Choices[0].Message.Content,
|
||||
Model: cr.Model,
|
||||
Content: cr.Choices[0].Message.Content,
|
||||
Model: cr.Model,
|
||||
FinishReason: cr.Choices[0].FinishReason,
|
||||
Usage: Usage{
|
||||
PromptTokens: cr.Usage.PromptTokens,
|
||||
CompletionTokens: cr.Usage.CompletionTokens,
|
||||
@@ -236,6 +240,36 @@ func (c *openAICompat) wait(ctx context.Context, attempt int) error {
|
||||
}
|
||||
}
|
||||
|
||||
// tooLargeMarkers — текстовые признаки переполнения контекста в теле отказа.
|
||||
// Единого машинного кода у OpenAI-совместимых шлюзов нет: часть отдаёт 413,
|
||||
// остальные кладут причину в текст при обычном 400.
|
||||
var tooLargeMarkers = []string{
|
||||
"context length", "context_length", "context window", "maximum context",
|
||||
"too many tokens", "prompt is too long", "request too large",
|
||||
"reduce the length",
|
||||
}
|
||||
|
||||
// requestTooLarge — отказал ли провайдер из-за размера запроса. 413 —
|
||||
// безусловно, без разбора текста. 400 — только по маркерам, и смотрим их в
|
||||
// ПОЛНОМ теле: обрезка тела (snippet) выбрасывает причину ровно у тех шлюзов,
|
||||
// которые сперва echo'ят запрос. Ложное срабатывание безопасно: задача уходит
|
||||
// в review, а не в повтор.
|
||||
func requestTooLarge(status int, body []byte) bool {
|
||||
if status == http.StatusRequestEntityTooLarge {
|
||||
return true
|
||||
}
|
||||
if status != http.StatusBadRequest {
|
||||
return false
|
||||
}
|
||||
lower := strings.ToLower(string(body))
|
||||
for _, marker := range tooLargeMarkers {
|
||||
if strings.Contains(lower, marker) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// snippet обрезает тело для сообщения об ошибке.
|
||||
func snippet(b []byte) string {
|
||||
const max = 300
|
||||
|
||||
Reference in New Issue
Block a user