v0.2.27 9b298e6

anthropic_image.go RawHistoryFolder

1package provider
2
3import (
4	"bytes"
5	"image"
6	"image/jpeg"
7	"image/png"
8	"strings"
9
10	xdraw "golang.org/x/image/draw"
11
12	// Register decoders for the formats we want to accept on input.
13	_ "image/gif"
14	_ "image/jpeg"
15	_ "image/png"
16)
17
18// anthMaxImageSide is Anthropic's per-image dimension cap when a
19// request contains more than one image. Single-image requests are
20// allowed up to 8000 px, but zot conversations routinely include
21// multiple images (a screenshot here, a tool result image there)
22// so we always normalise down to the stricter limit. 2000 px on the
23// longest side is well below their cap and still readable for the
24// kinds of screenshots / charts the model usually consumes.
25const anthMaxImageSide = 2000
26
27// anthShrinkImageBytesIfTooBig returns data unchanged when the image
28// already fits within Anthropic's per-image dimension cap. When it
29// doesn't, the image is decoded, resampled with Catmull-Rom (a good
30// balance of quality and speed for downscaling), and re-encoded in
31// the same format. The MIME type may be rewritten when re-encoding
32// requires a format change (e.g. an image originally tagged image/gif
33// is shipped as image/png after resize, since stdlib only encodes
34// gif at the package's own narrow API which is awkward for a single
35// frame).
36//
37// Errors during decode/encode return the original bytes untouched so
38// the caller's existing flow continues to work; Anthropic will then
39// emit its own clearer error than we could synthesise. We log no
40// failures here because this runs on every outbound request and is
41// best-effort.
42func anthShrinkImageBytesIfTooBig(data []byte, mime string) ([]byte, string) {
43	if len(data) == 0 {
44		return data, mime
45	}
46	cfg, format, err := image.DecodeConfig(bytes.NewReader(data))
47	if err != nil {
48		return data, mime
49	}
50	if cfg.Width <= anthMaxImageSide && cfg.Height <= anthMaxImageSide {
51		return data, mime
52	}
53	// Compute target dimensions preserving aspect ratio, longest side
54	// clamped to anthMaxImageSide.
55	tw, th := cfg.Width, cfg.Height
56	if tw >= th {
57		th = th * anthMaxImageSide / tw
58		tw = anthMaxImageSide
59	} else {
60		tw = tw * anthMaxImageSide / th
61		th = anthMaxImageSide
62	}
63	if tw < 1 {
64		tw = 1
65	}
66	if th < 1 {
67		th = 1
68	}
69
70	src, _, err := image.Decode(bytes.NewReader(data))
71	if err != nil {
72		return data, mime
73	}
74	dst := image.NewRGBA(image.Rect(0, 0, tw, th))
75	xdraw.CatmullRom.Scale(dst, dst.Bounds(), src, src.Bounds(), xdraw.Over, nil)
76
77	var buf bytes.Buffer
78	switch strings.ToLower(format) {
79	case "jpeg":
80		if err := jpeg.Encode(&buf, dst, &jpeg.Options{Quality: 85}); err != nil {
81			return data, mime
82		}
83		return buf.Bytes(), "image/jpeg"
84	case "png", "gif":
85		// gifs are re-encoded as png so the call site stays simple
86		// (avoiding a single-frame Encode helper). Anthropic accepts
87		// image/png happily; the visual result is the same for the
88		// model's vision pipeline.
89		if err := png.Encode(&buf, dst); err != nil {
90			return data, mime
91		}
92		nextMime := mime
93		if strings.ToLower(format) == "gif" {
94			nextMime = "image/png"
95		}
96		return buf.Bytes(), nextMime
97	default:
98		return data, mime
99	}
100}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 476

↑↓ moveenter openesc close