Skip to content

Commit 067729a

Browse files
Generate default avatar fallbacks and label images for Gemini alignment (#278)
1 parent 73fdc4c commit 067729a

2 files changed

Lines changed: 43 additions & 10 deletions

File tree

src/yetibot/core/util/image_input.clj

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,25 @@
66
(def ^:private image-url-pattern
77
#"(https?://\S+\.(?:jpg|jpeg|png|gif|webp)(?:[?\#]\S*)?|https?://(?:cdn\.discordapp\.com|media\.discordapp\.net|i\.imgur\.com)/\S+)")
88

9+
(defn- parse-id [id]
10+
(try
11+
(BigInteger. (str id))
12+
(catch Exception _
13+
BigInteger/ZERO)))
14+
15+
(defn- default-avatar-url [id]
16+
(let [id-val (parse-id id)
17+
idx (if (pos? id-val)
18+
(mod (.shiftRight id-val 22) 6)
19+
0)]
20+
(format "https://cdn.discordapp.com/embed/avatars/%d.png" (int idx))))
21+
922
(defn- discord-avatar-url [{:keys [id avatar]}]
1023
(cond
1124
(= id "269292446041636866") "https://i.imgflip.com/4/9omh8s.jpg"
1225
(and id avatar) (let [ext (if (str/starts-with? avatar "a_") "gif" "png")]
1326
(format "https://cdn.discordapp.com/avatars/%s/%s.%s?size=256" id avatar ext))
14-
:else nil))
27+
:else (default-avatar-url id)))
1528

1629
(defn- entity-index [prompt entity]
1730
(if (:is-me? entity)
@@ -48,12 +61,22 @@
4861
(str/replace (str "<@" id ">") (str "@" username))))))
4962
prompt entities))
5063

64+
(defn- build-labels-prefix [entities]
65+
(if (seq entities)
66+
(let [labels (map-indexed (fn [idx entity]
67+
(format "Image %d is @%s" (inc idx) (:username entity)))
68+
entities)]
69+
(str "(" (str/join ". " labels) ") "))
70+
""))
71+
5172
(defn- extract-mention-avatars [prompt raw-event]
5273
(let [entities (get-entities prompt raw-event)
5374
sorted-entities (sort-by #(entity-index prompt %) entities)]
5475
(if (seq sorted-entities)
55-
{:urls (vec (keep discord-avatar-url sorted-entities))
56-
:prompt (replace-entities prompt sorted-entities)}
76+
(let [prefix (build-labels-prefix sorted-entities)
77+
replaced-prompt (replace-entities prompt sorted-entities)]
78+
{:urls (vec (keep discord-avatar-url sorted-entities))
79+
:prompt (str prefix replaced-prompt)})
5780
{:urls [] :prompt prompt})))
5881

5982
(defn- image-attachment? [{:keys [content-type content_type filename]}]

test/yetibot/core/test/util/image_input.clj

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,24 @@
1010
(img-input/extract-images
1111
"<@123456789>"
1212
{:raw-event {:mentions [{:id "123456789" :avatar "avatar123" :username "alice"}]}})
13-
=> {:prompt "@alice" :image-urls ["https://cdn.discordapp.com/avatars/123456789/avatar123.png?size=256"]})
13+
=> {:prompt "(Image 1 is @alice) @alice"
14+
:image-urls ["https://cdn.discordapp.com/avatars/123456789/avatar123.png?size=256"]})
1415

1516
(fact
1617
"a hardcoded user mention extracts the custom mario death meme avatar"
1718
(img-input/extract-images
1819
"<@269292446041636866>"
1920
{:raw-event {:mentions [{:id "269292446041636866" :avatar "someavatar" :username "bob"}]}})
20-
=> {:prompt "@bob" :image-urls ["https://i.imgflip.com/4/9omh8s.jpg"]})
21+
=> {:prompt "(Image 1 is @bob) @bob"
22+
:image-urls ["https://i.imgflip.com/4/9omh8s.jpg"]})
2123

2224
(fact
2325
"user mentions are extracted and sorted in typing order, not snowflake order"
2426
(img-input/extract-images
2527
"draw <@333333333> and <@111111111>"
2628
{:raw-event {:mentions [{:id "111111111" :avatar "av1" :username "alice"}
2729
{:id "333333333" :avatar "av3" :username "charlie"}]}})
28-
=> {:prompt "draw @charlie and @alice"
30+
=> {:prompt "(Image 1 is @charlie. Image 2 is @alice) draw @charlie and @alice"
2931
:image-urls ["https://cdn.discordapp.com/avatars/333333333/av3.png?size=256"
3032
"https://cdn.discordapp.com/avatars/111111111/av1.png?size=256"]})
3133

@@ -34,15 +36,15 @@
3436
(img-input/extract-images
3537
"draw me as a wizard"
3638
{:raw-event {:author {:id "222222222" :avatar "av2" :username "bob"}}})
37-
=> {:prompt "draw @bob as a wizard"
39+
=> {:prompt "(Image 1 is @bob) draw @bob as a wizard"
3840
:image-urls ["https://cdn.discordapp.com/avatars/222222222/av2.png?size=256"]})
3941

4042
(fact
4143
"extracting speaking user's avatar is case-insensitive for 'me'"
4244
(img-input/extract-images
4345
"draw ME flying"
4446
{:raw-event {:author {:id "222222222" :avatar "av2" :username "bob"}}})
45-
=> {:prompt "draw @bob flying"
47+
=> {:prompt "(Image 1 is @bob) draw @bob flying"
4648
:image-urls ["https://cdn.discordapp.com/avatars/222222222/av2.png?size=256"]})
4749

4850
(fact
@@ -59,6 +61,14 @@
5961
"draw me and <@111111111>"
6062
{:raw-event {:author {:id "222222222" :avatar "av2" :username "bob"}
6163
:mentions [{:id "111111111" :avatar "av1" :username "alice"}]}})
62-
=> {:prompt "draw @bob and @alice"
64+
=> {:prompt "(Image 1 is @bob. Image 2 is @alice) draw @bob and @alice"
6365
:image-urls ["https://cdn.discordapp.com/avatars/222222222/av2.png?size=256"
64-
"https://cdn.discordapp.com/avatars/111111111/av1.png?size=256"]}))
66+
"https://cdn.discordapp.com/avatars/111111111/av1.png?size=256"]})
67+
68+
(fact
69+
"a user without an avatar gets the default discord avatar URL"
70+
(img-input/extract-images
71+
"<@123456789>"
72+
{:raw-event {:mentions [{:id "123456789" :username "alice"}]}})
73+
=> {:prompt "(Image 1 is @alice) @alice"
74+
:image-urls ["https://cdn.discordapp.com/embed/avatars/5.png"]}))

0 commit comments

Comments
 (0)