jbilcke-hf HF staff commited on
Commit
2f436dd
β€’
1 Parent(s): 026baa0

working on the censorship module

Browse files
.env CHANGED
@@ -11,6 +11,11 @@ REPLICATE_API_TOKEN=
11
  REPLICATE_API_MODEL="lucataco/sdxl-panoramic"
12
  REPLICATE_API_MODEL_VERSION="76acc4075d0633dcb3823c1fed0419de21d42001b65c816c7b5b9beff30ec8cd"
13
 
 
 
 
 
 
14
  # ----------- COMMUNITY SHARING (OPTIONAL, YOU DON'T NEED THIS IN LOCAL) -----------
15
  NEXT_PUBLIC_ENABLE_COMMUNITY_SHARING="false"
16
  # You don't need those community sharing options to run Panoremix
 
11
  REPLICATE_API_MODEL="lucataco/sdxl-panoramic"
12
  REPLICATE_API_MODEL_VERSION="76acc4075d0633dcb3823c1fed0419de21d42001b65c816c7b5b9beff30ec8cd"
13
 
14
+ # ----------- CENSORSHIP -------
15
+ # Due to abuse by users, I've had to add a censorshi/fingerprinting mechanism
16
+ ENABLE_CENSORSHIP="false"
17
+ FINGERPRINT_KEY=
18
+
19
  # ----------- COMMUNITY SHARING (OPTIONAL, YOU DON'T NEED THIS IN LOCAL) -----------
20
  NEXT_PUBLIC_ENABLE_COMMUNITY_SHARING="false"
21
  # You don't need those community sharing options to run Panoremix
src/app/engine/censorship.ts CHANGED
@@ -1,32 +1,150 @@
 
1
  // I don't want to be banned by Replicate because bad actors are asking
2
  // for some naked anime stuff or whatever
3
  // I also want to avoid a PR scandal due to some bad user generated content
4
 
5
- const forbiddenWords = [
6
- // those keywords have been generated by looking at the logs of the AI Comic Factory
7
- // those are real requests some users tried to attempt.. :|
8
- "nazi",
9
- "hitler",
10
- "boob",
11
- "boobs",
12
- "boobies",
13
- "nipple",
14
- "nipples",
15
- "nude",
16
- "nudes",
17
- "naked",
18
- "pee",
19
- "peeing",
20
- "erotic",
21
- "sexy"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
  ]
23
 
24
- // temporary utility to make sure Replicate doesn't ban my account
25
- // because of what users do in their prompt
 
 
 
 
 
 
 
 
 
 
26
  export const filterOutBadWords = (sentence: string) => {
 
 
27
  const words = sentence.split(" ")
28
- return words.filter(word => {
29
- const lowerCase = word.toLocaleLowerCase()
30
- return !forbiddenWords.includes(lowerCase)
31
- }).join(" ")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
  }
 
1
+
2
  // I don't want to be banned by Replicate because bad actors are asking
3
  // for some naked anime stuff or whatever
4
  // I also want to avoid a PR scandal due to some bad user generated content
5
 
6
+ import { computeSecretFingerprint } from "@/lib/computeSecretFingerprint"
7
+
8
+ // those keywords have been generated by looking at the logs of the panorama and the AI Comic Factory
9
+ // those are real requests some users tried to attempt.. :|
10
+
11
+ const chickens = [
12
+ "fcb4dacbd99b21368c50f29c1d47071c87cf2225ab9192282c785460391cd365",
13
+ "68840b60ac27eacaa7afe17e898d3c4a2dc71acff8c74d6782c1bcaafd14963d",
14
+ "67f745224fd6e1a7a3a244514d5807fcc994cbb62ca4ec8fa44cd14244a515ae",
15
+ "681fea565117808c6dbe002520d2cfeeb3e5c67e68630afb4a453449a9da587b",
16
+ "2f3d913b3db9e15a930aac43eb2d6fe8817db8e4bcf37794bf0227b06b718d1b",
17
+ "922a700b807e4994df82eba2b48a6ac131fe8d8d1035d06b3592d622fb232161",
18
+ "cb69ee6774eafcc720adb1f689d28acbb9f47998cbea0299ec66a58dedf91c37"
19
+ ]
20
+
21
+ const ducks = [
22
+ "1c52cb20c0cbc76349fa63232b982bd394cf0850ebc17240dcf33c19fb15a26d",
23
+ "e1d4de9b8d464d7da07c276b63a42c1c9922224f0a6cab6b0826427ce4a7461a",
24
+ "0be3174bfb1a48a65875c2f035b1ae14fbc8f232f55785018de0cfe2132fa952",
25
+ "0f174769641b2e5d2c79b5a83e8ef91e004f6f3e62531cd70cfdff02159268cb",
26
+ "e9fb8ae8ff720acd91025229478a21e43e8e976e30119a76c293201adf572736",
27
+ "f65a0dc0e07b5d084ff24c69dcdb953f7b57101d2ebb716d4dfb5963076ef807",
28
+ "2bf38af1646489c2c086f811d082054cd29e23fa7bb5c525396bec01b3ab688e"
29
+ ]
30
+
31
+ const cats = [
32
+ "fcffc3e997d952007d1b902a9cf40b750ba4a410ac65bfd95475996bf51359e4",
33
+ "3172a5fa159754d703489dfba5af520b8ace107cdf170f4c4cb38a6797aa163f",
34
+ "500012dbff4498a9c4513369d6b9b373fab9330ffd2cb1e622294043cc21b610",
35
+ "84e3a8d34ee7d0c8e7a2926dd1acad46a0b66b9d27725b3a7e5053550f490301"
36
+ ]
37
+
38
+ const roasted = [
39
+ "a2bfbce0046c9a52a0eabf98f73e0f8e09959970431fc892ebdb4e1c97031b50",
40
+ "6eca1adf06851f99e9cdfbb496c27d46ff81106903d11f3346a146e96082b016",
41
+ "49a124c9ed6fbbad4105b3657dc25de369bcafb9d6787f610c08f584cd607d0f",
42
+ "c3afb59420c812cbc7c8f57ad3e8d79407f10106a99f829aa65316c99d0b29c4",
43
+ "2b808858836a5c205080f5b93201ef92e098cff931d8de6d9f20dc722997d077",
44
+ "07bef89d1a7d63c9c5ed64ba0f73d6cff689811847c2e20c8b3fbfb060e1d64e",
45
+ "baeb994922d5473f534aa54322d83effe74c6c4dac807e6b523a677d7acdc17b",
46
+ "ea4735a879edd5cc94ca7db26edd5a970df69a41f0009d3444486647e44175af",
47
+ "f2412249030454cd13ac6f7965871d924c16daacda0123de81892adb19ce49ac",
48
+ "9958c56e12bab8549cf752bcd8bec4ac36cf79c404b1faf5611f057bb71bc0e1",
49
+ "76cdade0b3d4caf0888f60318a5cbca00f830a3b0bf37735fc64fdaeb67c34d3",
50
+ "1bf53c97869e1ea89bda19da64a9173d48fe4ec823e949e2c898f8abb3fbf457",
51
+ "1bf53c97869e1ea89bda19da64a9173d48fe4ec823e949e2c898f8abb3fbf457",
52
+ "3d7f973fab8f4a19c0a3e59efe970ed7bd55a1cb795752d9cbe3c19e8a7d81ec"
53
+ ]
54
+
55
+ const banned = [
56
+ "fb4cabe709b62eea1b4cc0030c76f5e4a43ee677ce19124e8e7bafa86c78ab66",
57
+ "d99c26daee85f7dc81c46c061a5874cff7179ed72d884d2316d664d36ffe7ab5",
58
+ "b93c38af5aa221d76c60ee3eb762efee0cdb0daf29ceb235b7dda6d46c06490d",
59
+ "8cf6c8765dc757319461dd9a785e77c201b8e5a604d36b817cd987c6a5e62500",
60
+ "f4a1cb290745717f86c3cee30fc324c0d80a9945fcbc7bbeb010579f58792f1e",
61
+ "7c87c47c42fc983119551342be9ddd5b32e530c0504ccdbbaa1e12b1d9f1bbcb",
62
+ "d04fad4f21d030da7a1301afbf480ef6246eb7bbf0f26e31865b2e015a25f747",
63
+ "d685ff22fb9da01ee949db212770729603989850864ef7a7085e1f086cfa7deb",
64
+ "533b90588d9ccf7967da54691f575e9fd4926c6e0b5fd94a47b932bcea270bee",
65
+ "9c2d61f28f5bb7f3f1dc9122be64cda8a428b46ce68b70120da4c41dba96ba4c",
66
+ "5d4b1a3eebe64dfa631d0e3b084bd96ee9364c3669269f838ca17a4900276264",
67
+ "d56f56413b9679fc0820a2c0237224ded8554c61fab8959c174123c8b68ba029",
68
+ "323a9ab60739726070d615ff3a05d7ff6bb6e3c4dd9ff16ce24f253ecd7b8851",
69
+ "975c6739de7d4999db15972f707f5f4e95649275f1c0c48e895b8c537e8638ec",
70
+ "67ee26eb9e1c1c7124797321b02bca90a19c18171782917cd4a487b722484dce",
71
+ "6df5aa7b72a4e6e3fb726489ff1437daa5752047507f4da912680b1d6647c7d6",
72
+ "b0864805364359e8c5810c233b1bf2c74dedce9055ae5f7680ba05b4e39db8e2",
73
+ "a8f841472ecffdd6266151148320c8e36847a24ead9d3338e0313b075c16649d",
74
+ "f9b127cd90e85b0ff68dd220361671663f0154b2b827f1f7ea797b020ca0018c",
75
+ "d5c20e9a1ecf01c82da24c514d867498b3e5f522adc1523ce29404a6563641d5",
76
+ "241022b49d7c0aba24a61eea1137a804f36e4bcb47af42950275baac9b4e7aac",
77
+ "fc99a70e17b6c86ef1b537654b0f50353567a7b59912c3ba955f3fca4d1ea696",
78
+ "255306e968009003d295cb2a7256f27bfcdb5d1743bf4d9f2aa4b8adf1a7734d",
79
+ "048c7b709763dd9c43794d241c369f0abcb079d546ddcbbba9968a1ed1da7ed7",
80
+ "520cbfeef3e4c405d79478eedccb97a4d476be585626dd2b1c53292797491bc7",
81
+ "f9f28a7ae7e8b1719b350a04dc087a4b8e33478d109ceeef6ba892b32d1105c9",
82
+ "d177f1bfe603647ef4c1c0e6f1a7172081fb9bbc2ea859705949f2c5aa5d4f22",
83
+ "302feef2c09247fbd23789581f7f5e2219f88ae0a937880954938573c2a52a84",
84
+ "99edd6f57b864873835f16f19c805dd94bed9da8967b84e3a62782f106d9ebcc",
85
+ "e75e5f01dcd8351c9553e89558085bd68e6feb295dee5d8da0c9b43ee303ce36",
86
+ "135e52a026aea9d2e12de358a85e05cf21121a18269269b7c62678c3bc846f5b",
87
+ "28e5b2d3eb5f1ef4cc7b570878b03acf303a6ca4ca95893591e0fb943b0beab0",
88
+ "a26b26340f8d0363633490556d20bcc250726d10e1431eb8c22d6b1ff3f2b14a",
89
+ "27e4ddde96ec6a1dbe1cf12d79448b3e72f144944c15b299629542d1b65fbabf",
90
+ "efd9c0a391ee93251046a58326d1b21b33fe21d71a3fb1855b9048ade53df77c",
91
+ "6d505fcce416c26a606878aab4d249a034ba2a9846cb1f883e0f9e3fb76ba6da",
92
+ "3a37b8a1b72f9bca51233536d50f9c8d33a787434684787871e0049c82347cda",
93
+ "16f9b451184a7c3148344c7d0315f5312ca20553d2271912ecaad91810d977e6",
94
+ "7406537eb74d1885bd05e191228de313b13702a64d90ae1736c6377b25ab579a",
95
+ "7e4d1395ae18980015cab16c85ffa20b4cb90a2db594126e893d0f7ac6eecaa8",
96
+ "ba813ee6c25698f0f68a07121d38bb47c9aa404c1ab0a6e767595cb75e1747b8",
97
+ "6586c93f3ece83e01ecc1eb84a7711e7975826a388d478a009468ea0ed9dc03e",
98
+ "8960174c74d86e03ae88fb6774580170e49952f2286d960be08c556bbd0dda95",
99
+ "4d611454369aa1a4e2b7eed1734fac5d480f08fb86b87a162967e416370f2a8e",
100
+ "59d48440f85eabf565fe8d3bc6b973ba64c70df3b36b0511e0e67ceca91762b3",
101
+ "cd926926e2af74e43d1a6a420a7e1933b78662320477a3c018b2711d8765e339",
102
+ "80e90057df6a59823f51aafac36ed5bc4e5ac26d675d9c1467501590c82f12d4",
103
+ "a9cf28b869b70e258adde5639a048f866ec86f8f3f3d53bfc960b86aa6da9239",
104
+ "cc2adbf8ac0cddeefa304d7b20f14a7e047a4b2299cc5e8f898f5c59660bd964",
105
+ "92a150a46146e9d3f84899cf15e12514af684e7ee18d7add782ddd4f4a15ef18",
106
+ "d9b2e84ef6dc0ce449357d52c9095f69b173a1b848ea2921199d33b0ec10024a",
107
+ "a9329a7e4d367a0135c1ca86c6ce5ecabcc26529235229d71b6bf991f7689e21",
108
+ "8f160c6fd8ccc3fb2a371a4b52748f0bd030766627c4322e2911fe82f6b10497",
109
+ "620e96eae4f3e88cbe0770292b33724c5df3866d83f39df6380441f7271c80e2",
110
+ "cafa3481fa3c45ed1e55cd0129c12b477eeab5aa3d6da20cae6d6292f19b0e6d",
111
+ "be07994e9a83aa3689e79b6e96123676ccc4fa29f523c28c750c6d60505531ee",
112
+ "f6498069768cd3aa79b2b0c91879694f05a259c8ee4a6bb343f0435f74eb1b53",
113
+ "c9b6b26cb3a694eb78fcac0a14ad18d46d50907186a9add41022d31d191b2b65"
114
  ]
115
 
116
+ const getFingerprint = (word: string) => {
117
+ return computeSecretFingerprint(word.toLocaleLowerCase())
118
+ }
119
+
120
+ const encode = (list: string[]) => {
121
+ console.log(JSON.stringify(
122
+ list.sort((a, b) => (b.length - a.length))
123
+ .map(item => getFingerprint(item)), null, 2))
124
+ }
125
+
126
+ // encode([ "badword" ])
127
+
128
  export const filterOutBadWords = (sentence: string) => {
129
+ if (process.env.ENABLE_CENSORSHIP !== "true") { return sentence }
130
+
131
  const words = sentence.split(" ")
132
+ return words.map(word => {
133
+ const fingerprint = getFingerprint(word)
134
+
135
+ let result: string = word
136
+ // some users want to play it smart and bypass our system so let's play too
137
+ if (chickens.includes(fingerprint)) {
138
+ result = "large chicken"
139
+ } else if (ducks.includes(fingerprint)) {
140
+ result = "big duck"
141
+ } else if (cats.includes(fingerprint)) {
142
+ result = "cat"
143
+ } else if (roasted.includes(fingerprint)) {
144
+ result = "roasted chicken"
145
+ } else if (banned.includes(fingerprint)) {
146
+ result = "_BANNED_"
147
+ }
148
+ return result
149
+ }).filter(item => item !== "_BANNED_").join(" ")
150
  }
src/app/engine/community.ts CHANGED
@@ -17,8 +17,13 @@ export async function postToCommunity({
17
  assetUrl: string
18
  }): Promise<Post> {
19
 
 
20
  prompt = filterOutBadWords(prompt)
21
 
 
 
 
 
22
  // if the community API is disabled,
23
  // we don't fail, we just mock
24
  if (!apiUrl) {
 
17
  assetUrl: string
18
  }): Promise<Post> {
19
 
20
+ const before = prompt
21
  prompt = filterOutBadWords(prompt)
22
 
23
+ if (prompt !== before) {
24
+ console.log(`user attempted to use bad words! their original prompt is: ${before}`)
25
+ }
26
+
27
  // if the community API is disabled,
28
  // we don't fail, we just mock
29
  if (!apiUrl) {
src/app/engine/render.ts CHANGED
@@ -29,13 +29,13 @@ export async function newRender({
29
  throw new Error(`cannot call the rendering API without a prompt, aborting..`)
30
  }
31
 
32
- prompt = [
33
  `hdri view`,
34
  `highly detailed`,
35
  `intricate details`,
36
  filterOutBadWords(prompt)
37
- ].join(', ')
38
-
39
  // return await Gorgon.get(cacheKey, async () => {
40
 
41
  let defaulResult: RenderedScene = {
 
29
  throw new Error(`cannot call the rendering API without a prompt, aborting..`)
30
  }
31
 
32
+ prompt = [
33
  `hdri view`,
34
  `highly detailed`,
35
  `intricate details`,
36
  filterOutBadWords(prompt)
37
+ ].join(', ')
38
+
39
  // return await Gorgon.get(cacheKey, async () => {
40
 
41
  let defaulResult: RenderedScene = {
src/app/landing.tsx CHANGED
@@ -23,25 +23,35 @@ export default function Landing() {
23
  return (
24
  <div className={cn(
25
  `light fixed w-full h-full flex flex-col items-center bg-slate-300 text-slate-800`,
26
- `pt-24`,
27
  actionman.className
28
  )}>
29
- <div className="w-full flex flex-col items-center">
30
- <h1 className="text-[100px] text-cyan-700">🌐 Panoremix</h1>
31
- <h2 className="text-3xl mb-12">Generate cool panoramas using AI!</h2>
32
-
33
- <h2 className="text-2xl">Latest locations synthesized:</h2>
 
34
 
35
- <div className="grid grid-col-2 sm:grid-col-3 md:grid-col-4 lg:grid-cols-5 gap-4">
36
  {posts.map(post => (
37
- <div key={post.postId} className="flex flex-col space-y-3">
 
 
 
 
 
 
38
  <div className="w-full h-24">
39
  <img
40
  src={post.assetUrl}
41
- className="w-full h-full rounded-xl overflow-hidden"
 
 
 
42
  />
43
  </div>
44
- <div className="text-base truncate w-full">{post.prompt}</div>
45
  </div>
46
  ))}
47
  </div>
 
23
  return (
24
  <div className={cn(
25
  `light fixed w-full h-full flex flex-col items-center bg-slate-300 text-slate-800`,
26
+ ``,
27
  actionman.className
28
  )}>
29
+ <div className="w-full flex flex-col items-center overflow-y-scroll">
30
+ <div className="flex flex-col space-y-2 pt-18 mb-6">
31
+ <h1 className="text-4xl md:text-6xl lg:text-[70px] xl:text-[100px] text-cyan-700">🌐 Panoremix</h1>
32
+ <h2 className="text-3xl mb-6">Generate cool panoramas using AI!</h2>
33
+ <h2 className="text-2xl">Latest locations synthesized:</h2>
34
+ </div>
35
 
36
+ <div className="w-full grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-x-4 gap-y-6 px-12">
37
  {posts.map(post => (
38
+ <div
39
+ key={post.postId}
40
+ className="flex flex-col space-y-3 cursor-pointer"
41
+ onClick={() => {
42
+ // TODO
43
+ }}
44
+ >
45
  <div className="w-full h-24">
46
  <img
47
  src={post.assetUrl}
48
+ className={cn(
49
+ `w-full h-24 rounded-xl overflow-hidden object-cover`,
50
+ `border border-zinc-900/70`
51
+ )}
52
  />
53
  </div>
54
+ <div className="text-sm text-stone-800/80 truncate w-full">{post.prompt}</div>
55
  </div>
56
  ))}
57
  </div>
src/lib/computeSecretFingerprint.ts ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ import { computeSha256 } from "./computeSha256"
2
+
3
+ const secretFingerprint = `${process.env.FINGERPRINT_KEY || ""}`
4
+
5
+ export function computeSecretFingerprint(input: string) {
6
+ return computeSha256(`${secretFingerprint}_${input}`)
7
+ }
src/lib/computeSha256.ts ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { createHash } from 'node:crypto'
2
+
3
+ /**
4
+ * Returns a SHA256 hash using SHA-3 for the given `content`.
5
+ *
6
+ * @see https://en.wikipedia.org/wiki/SHA-3
7
+ *
8
+ * @param {String} content
9
+ *
10
+ * @returns {String}
11
+ */
12
+ export function computeSha256(strContent: string) {
13
+ return createHash('sha3-256').update(strContent).digest('hex')
14
+ }