Compare commits

...
Author SHA1 Message Date
arensc 29a137e4fe Adding properties to prompts 2024-02-21 20:40:48 -05:00
Brandon Thomas b8270b0235 fix lints 2024-02-21 17:48:14 -05:00
Brandon Thomas 7dbae4c166 fix build error 2024-02-21 17:30:47 -05:00
Brandon Thomas 170c0afef5 analytics query 2024-02-21 17:24:06 -05:00
saltacc 4af67113b2 Merge pull request #306 from storytold/feature/upload-workflow
Feature/upload workflow
2024-02-21 14:07:22 -08:00
Brandon Thomas c6555b88d5 one or two more terms 2024-02-21 05:14:32 -05:00
Brandon Thomas ce4c9a1d42 doc 2024-02-20 23:36:04 -05:00
Brandon Thomas d85fe3fb3d CLI tool to delete users' files 2024-02-20 23:19:08 -05:00
Brandon Thomas d601164b5b rename two cli apps 2024-02-20 22:12:11 -05:00
Brandon Thomas 73b6b6585e refactor CLI tools 2024-02-20 22:10:18 -05:00
Brandon Thomas 368c216c07 rename CLI binary 2024-02-20 21:57:26 -05:00
Brandon Thomas d31eb74344 named number classifier 2024-02-20 21:46:35 -05:00
Brandon Thomas 2dab930226 additional test cases for italian 2024-02-20 21:40:08 -05:00
Brandon Thomas 9fc6e4adf6 split up tests 2024-02-20 21:28:23 -05:00
Brandon Thomas 77c22336e6 codegen db 2024-02-20 20:27:09 -05:00
Brandon Thomas 4ae7e61b7e additional safety 2024-02-20 19:26:21 -05:00
Brandon Thomas 16a3f74105 "1girl" is a false positive and used frequently in safe contexts 2024-02-20 19:09:03 -05:00
Brandon Thomas 4ad90b31df stop racism too 2024-02-20 18:56:24 -05:00
Brandon Thomas 5cc90d1001 better classifier 2024-02-20 18:49:20 -05:00
Brandon Thomas c6a5f0694f spanish + tests 2024-02-20 18:43:12 -05:00
Brandon Thomas 688efffcb8 improve classifier 2024-02-20 18:37:01 -05:00
Brandon Thomas 767191871d improved filter 2024-02-20 18:28:21 -05:00
Brandon Thomas 7d963deb38 move text classification to own crate (about to build out more) 2024-02-20 16:14:47 -05:00
88 changed files with 1717 additions and 819 deletions
+1
View File
@@ -9,6 +9,7 @@
<w>cheermote</w>
<w>cidrs</w>
<w>crockford</w>
<w>csam</w>
<w>culted</w>
<w>dalle</w>
<w>demuxer</w>
@@ -0,0 +1,12 @@
{
"db_name": "MySQL",
"query": "\nINSERT INTO media_files\nSET\n token = ?,\n\n origin_category = ?,\n origin_product_category = ?,\n maybe_origin_model_type = ?,\n\n media_type = ?,\n maybe_mime_type = ?,\n file_size_bytes = ?,\n\n checksum_sha2 = ?,\n\n public_bucket_directory_hash = ?,\n maybe_public_bucket_prefix = ?,\n maybe_public_bucket_extension = ?,\n\n maybe_creator_user_token = ?,\n maybe_creator_anonymous_visitor_token = ?,\n creator_ip_address = ?,\n\n creator_set_visibility = ?,\n extra_file_modification_info = ?,\n\n maybe_creator_file_synthetic_id = ?,\n maybe_creator_category_synthetic_id = ?,\n\n is_generated_on_prem = ?,\n generated_by_worker = ?,\n generated_by_cluster = ?\n\n ",
"describe": {
"columns": [],
"parameters": {
"Right": 21
},
"nullable": []
},
"hash": "f681f25833875063150b6f881ebbfadc292939745e38754991650651edbe7acb"
}
Generated
+50 -29
View File
@@ -1219,6 +1219,17 @@ version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
[[package]]
name = "classify_text"
version = "0.1.0"
dependencies = [
"errors",
"my-workspace-hack",
"once_cell",
"regex",
"speculoos 0.11.0",
]
[[package]]
name = "cloud_storage"
version = "0.0.1"
@@ -1727,34 +1738,6 @@ version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5"
[[package]]
name = "database-backup"
version = "0.0.1"
dependencies = [
"anyhow",
"container_common",
"dotenv",
"easyenv",
"log",
"my-workspace-hack",
"serde",
"serde_derive",
]
[[package]]
name = "database-cleanup"
version = "0.0.1"
dependencies = [
"anyhow",
"container_common",
"dotenv",
"easyenv",
"log",
"my-workspace-hack",
"serde",
"serde_derive",
]
[[package]]
name = "datetimes"
version = "0.0.1"
@@ -1764,6 +1747,43 @@ dependencies = [
"once_cell",
]
[[package]]
name = "db-backup"
version = "0.0.1"
dependencies = [
"anyhow",
"container_common",
"dotenv",
"easyenv",
"log",
"my-workspace-hack",
"serde",
"serde_derive",
]
[[package]]
name = "db-cleanup"
version = "0.0.1"
dependencies = [
"buckets",
"clap",
"container_common",
"dotenv",
"easyenv",
"enums",
"errors",
"log",
"my-workspace-hack",
"mysql_queries",
"serde",
"serde_derive",
"serde_json",
"sqlx",
"storyteller_root",
"tokens",
"tokio",
]
[[package]]
name = "debug-helper"
version = "0.3.13"
@@ -5530,7 +5550,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "scrape_test"
name = "scrape-test"
version = "0.0.1"
dependencies = [
"clap",
@@ -6339,6 +6359,7 @@ dependencies = [
"bootstrap",
"buckets",
"chrono",
"classify_text",
"cloud_storage",
"collections",
"composite_identifiers",
+11 -10
View File
@@ -11,25 +11,26 @@ members = [
"crates/_types/tokens",
# CLI tools for operations, etc.
"crates/cli/database-backup",
"crates/cli/database-cleanup",
"crates/cli/dev-database-seed",
"crates/cli/dev-upload-media-file",
"crates/cli/elasticsearch-cli",
"crates/cli/kubectl-pod-cleanup",
"crates/cli/lru-delete-models",
"crates/cli/migration-tool",
"crates/cli/scrape_test",
"crates/cli/devenv/dev-database-seed",
"crates/cli/devenv/dev-upload-media-file",
"crates/cli/devenv/scrape-test",
"crates/cli/production/db-backup",
"crates/cli/production/db-cleanup",
"crates/cli/production/elasticsearch-cli",
"crates/cli/production/kubectl-pod-cleanup",
"crates/cli/production/lru-delete-models",
"crates/cli/production/migration-tool",
# Shared libraries
"crates/lib/actix_helpers",
"crates/lib/api_clients/cloud_storage",
"crates/lib/api_clients/google_drive_common",
"crates/lib/api_clients/twitch_common",
"crates/lib/api_clients/thumbnail_generator",
"crates/lib/api_clients/twitch_common",
"crates/lib/bootstrap",
"crates/lib/caching/memory_caching",
"crates/lib/caching/redis_caching",
"crates/lib/classify_text",
"crates/lib/collections",
"crates/lib/concurrency",
"crates/lib/cookies",
@@ -0,0 +1,21 @@
-- noinspection SqlDialectInspectionForFile
-- noinspection SqlNoDataSourceInspectionForFile
-- noinspection SqlResolveForFile
-- This finds the top users of image generation within a given time range.
select
u.username,
count(*) as use_count
from users as u
join
(
select maybe_creator_user_token as user_token
from generic_inference_jobs
where job_type = 'stable_diffusion'
AND created_at > NOW() - INTERVAL 24 HOUR
) as jobs
ON jobs.user_token = u.token
group by u.username
order by use_count desc
-33
View File
@@ -1,33 +0,0 @@
[package]
name = "database-cleanup"
publish = false
edition = "2018"
version = "0.0.1"
authors = [
"Brandon Thomas <bt@brand.io>",
"Brandon Thomas <echelon@gmail.com>",
]
[[bin]]
name = "database-cleanup"
path = "src/main.rs"
[dependencies]
# Internal
container_common = { path = "../../lib/deprecated/container_common" }
easyenv = { path = "../../lib/easyenv" }
# Workspace hack (faster compile times)
my-workspace-hack = { version = "0.1", path = "../../my-workspace-hack" }
# External
anyhow = "1.0.75"
# TODO/NB: Commenting out clap4 as it prevents windows builds
# clap4 = { package = "clap", version = "4.0" } # TODO/NB: We're using an older version of clap elsewhere, hence the naming.
dotenv = "0.15.0"
log = "0.4.14"
# Serialization
serde = "1.0.125"
serde_derive = "1.0.125"
-12
View File
@@ -1,12 +0,0 @@
use easyenv::init_all_with_default_logging;
use log::info;
use container_common::anyhow_result::AnyhowResult;
fn main() -> AnyhowResult<()> {
init_all_with_default_logging(None);
info!("TODO...");
Ok(())
}
-47
View File
@@ -1,47 +0,0 @@
[package]
name = "dev-database-seed"
edition = "2018"
version = "0.0.1"
authors = [
"Brandon Thomas <bt@brand.io>",
"Brandon Thomas <echelon@gmail.com>",
]
publish = false
[[bin]]
name = "dev-database-seed"
path = "src/main.rs"
[dependencies]
# Internal
buckets = { path = "../../_types/buckets" }
cloud_storage = { path = "../../lib/api_clients/cloud_storage" }
config = { path = "../../lib/deprecated/config" }
easyenv = { path = "../../lib/easyenv" }
enums = { path = "../../_types/enums" }
errors = { path = "../../lib/errors" }
filesys = { path = "../../lib/files/filesys" }
hashing = { path = "../../lib/files/hashing" }
idempotency = { path = "../../lib/idempotency" }
mimetypes = { path = "../../lib/files/mimetypes" }
mysql_queries = { path = "../../lib/mysql_queries" }
storyteller_root = { path = "../../lib/storyteller_root" }
tokens = { path = "../../_types/tokens" }
# Workspace hack (faster compile times)
my-workspace-hack = { version = "0.1", path = "../../my-workspace-hack" }
# External
log = "0.4.14"
clap = "4.4.8"
rand = "0.8.5"
# Runtime
# NB: We switched to Tokio from `async-std` for broader ecosystem support.
tokio = { version = "1.34.0", features = ["macros"] }
sqlx = { version = "0.7.2", features = [ "mysql", "runtime-tokio", "tls-rustls", "chrono" ] }
elasticsearch = "8.5.0-alpha.1"
[dev-dependencies]
# None yet
@@ -0,0 +1,47 @@
[package]
name = "dev-database-seed"
edition = "2018"
version = "0.0.1"
authors = [
"Brandon Thomas <bt@brand.io>",
"Brandon Thomas <echelon@gmail.com>",
]
publish = false
[[bin]]
name = "dev-database-seed"
path = "src/main.rs"
[dependencies]
# Internal
buckets = { path = "../../../_types/buckets" }
cloud_storage = { path = "../../../lib/api_clients/cloud_storage" }
config = { path = "../../../lib/deprecated/config" }
easyenv = { path = "../../../lib/easyenv" }
enums = { path = "../../../_types/enums" }
errors = { path = "../../../lib/errors" }
filesys = { path = "../../../lib/files/filesys" }
hashing = { path = "../../../lib/files/hashing" }
idempotency = { path = "../../../lib/idempotency" }
mimetypes = { path = "../../../lib/files/mimetypes" }
mysql_queries = { path = "../../../lib/mysql_queries" }
storyteller_root = { path = "../../../lib/storyteller_root" }
tokens = { path = "../../../_types/tokens" }
# Workspace hack (faster compile times)
my-workspace-hack = { version = "0.1", path = "../../../my-workspace-hack" }
# External
log = "0.4.14"
clap = "4.4.8"
rand = "0.8.5"
# Runtime
# NB: We switched to Tokio from `async-std` for broader ecosystem support.
tokio = { version = "1.34.0", features = ["macros"] }
sqlx = { version = "0.7.2", features = [ "mysql", "runtime-tokio", "tls-rustls", "chrono" ] }
elasticsearch = "8.5.0-alpha.1"
[dev-dependencies]
# None yet
@@ -14,19 +14,19 @@ path = "src/main.rs"
[dependencies]
# Internal
buckets = { path = "../../_types/buckets" }
cloud_storage = { path = "../../lib/api_clients/cloud_storage" }
config = { path = "../../lib/deprecated/config" }
easyenv = { path = "../../lib/easyenv" }
enums = { path = "../../_types/enums" }
errors = { path = "../../lib/errors" }
filesys = { path = "../../lib/files/filesys" }
mimetypes = { path = "../../lib/files/mimetypes" }
mysql_queries = { path = "../../lib/mysql_queries" }
tokens = { path = "../../_types/tokens" }
buckets = { path = "../../../_types/buckets" }
cloud_storage = { path = "../../../lib/api_clients/cloud_storage" }
config = { path = "../../../lib/deprecated/config" }
easyenv = { path = "../../../lib/easyenv" }
enums = { path = "../../../_types/enums" }
errors = { path = "../../../lib/errors" }
filesys = { path = "../../../lib/files/filesys" }
mimetypes = { path = "../../../lib/files/mimetypes" }
mysql_queries = { path = "../../../lib/mysql_queries" }
tokens = { path = "../../../_types/tokens" }
# Workspace hack (faster compile times)
my-workspace-hack = { version = "0.1", path = "../../my-workspace-hack" }
my-workspace-hack = { version = "0.1", path = "../../../my-workspace-hack" }
# External
clap = "4.4.8"
@@ -1,5 +1,5 @@
[package]
name = "scrape_test"
name = "scrape-test"
publish = false
edition = "2021"
version = "0.0.1"
@@ -9,17 +9,17 @@ authors = [
]
[[bin]]
name = "scrape_test"
name = "scrape-test"
path = "src/main.rs"
[dependencies]
# Internal
errors = { path = "../../lib/errors" }
web_scrapers = { path = "../../lib/web_scrapers" }
errors = { path = "../../../lib/errors" }
web_scrapers = { path = "../../../lib/web_scrapers" }
# Workspace hack (faster compile times)
my-workspace-hack = { version = "0.1", path = "../../my-workspace-hack" }
my-workspace-hack = { version = "0.1", path = "../../../my-workspace-hack" }
# External
clap = "4.4.8"
@@ -4,7 +4,7 @@ use errors::AnyhowResult;
use web_scrapers::scrape_supported_webpage::scrape_supported_webpage;
#[derive(Parser, Debug)]
#[command(name = "scrape_test")]
#[command(name = "scrape-test")]
pub struct Args {
#[arg(name="url", short='u', long = "url", help = "URL of the page to scrape", required = true)]
url: String,
@@ -1,5 +1,5 @@
[package]
name = "database-backup"
name = "db-backup"
publish = false
edition = "2018"
version = "0.0.1"
@@ -9,17 +9,17 @@ authors = [
]
[[bin]]
name = "database-backup"
name = "db-backup"
path = "src/main.rs"
[dependencies]
# Internal
container_common = { path = "../../lib/deprecated/container_common" }
easyenv = { path = "../../lib/easyenv" }
container_common = { path = "../../../lib/deprecated/container_common" }
easyenv = { path = "../../../lib/easyenv" }
# Workspace hack (faster compile times)
my-workspace-hack = { version = "0.1", path = "../../my-workspace-hack" }
my-workspace-hack = { version = "0.1", path = "../../../my-workspace-hack" }
# External
anyhow = "1.0.75"
@@ -0,0 +1,44 @@
[package]
name = "db-cleanup"
publish = false
edition = "2018"
version = "0.0.1"
authors = [
"Brandon Thomas <bt@brand.io>",
"Brandon Thomas <echelon@gmail.com>",
]
[[bin]]
name = "db-cleanup"
path = "src/main.rs"
[dependencies]
# Internal
buckets = { path = "../../../_types/buckets" }
container_common = { path = "../../../lib/deprecated/container_common" }
easyenv = { path = "../../../lib/easyenv" }
enums = { path = "../../../_types/enums" }
errors = { path = "../../../lib/errors" }
mysql_queries = { path = "../../../lib/mysql_queries" }
storyteller_root = { path = "../../../lib/storyteller_root" }
tokens = { path = "../../../_types/tokens" }
# Workspace hack (faster compile times)
my-workspace-hack = { version = "0.1", path = "../../../my-workspace-hack" }
# External
clap = "4.4.8"
dotenv = "0.15.0"
log = "0.4.14"
# Bigger pieces
tokio = { version = "1.34.0", features = ["macros"] }
sqlx = { version = "0.7.2", features = [ "mysql", "runtime-tokio", "tls-rustls", "chrono" ] }
# Serialization
serde = "1.0.125"
serde_derive = "1.0.125"
serde_json = "1.0.108"
[dev-dependencies]
# None yet
@@ -0,0 +1,34 @@
use clap::{Parser, ValueEnum};
use serde_derive::Deserialize;
use errors::AnyhowResult;
#[derive(Parser, Debug, Deserialize)]
#[command(name="db-cleanup")]
#[serde(rename_all = "snake_case")]
pub struct Args {
// The primary action to take
#[arg(name="action", long="action", help="the primary action to take", required=true)]
pub action: Action,
// Optional username for some actions
#[arg(name="username", long="username", help="optional username", required=false)]
pub username: Option<String>,
}
#[derive(Clone, Copy, Debug, Deserialize, ValueEnum)]
#[serde(rename_all = "snake_case")]
pub enum Action {
/// Delete files for a particular user.
DeleteUserFiles,
}
pub fn parse_cli_args() -> AnyhowResult<Args> {
let mut args = Args::parse();
if let Some(username) = args.username.as_deref() {
args.username = Some(username.trim().to_string());
}
Ok(args)
}
@@ -0,0 +1,65 @@
use log::info;
use sqlx::{MySql, Pool};
use sqlx::mysql::MySqlPoolOptions;
use container_common::anyhow_result::AnyhowResult;
use easyenv::init_all_with_default_logging;
use crate::cli_args::Action;
use crate::operations::delete_user_files::delete_user_files::delete_user_files;
mod cli_args;
mod operations;
/*
Users that have had files deleted:
- cheesetherat
- endtimes
- expandinggirl
- markiwrench
- regista2
- regista77
TODO:
- juanfra3567
- ceistin
- elfabro17
- sodoka
- fluffernater
*/
#[tokio::main]
async fn main() -> AnyhowResult<()> {
println!("db-cleanup: hard or soft delete database records");
init_all_with_default_logging(None);
// NB: This secrets file differs from the rest because we might want to delete from production.
// (Hopefully this isn't getting out of hand at this point.)
easyenv::from_filename(".env-db-cleanup-secrets")?;
let args = cli_args::parse_cli_args()?;
let mysql = get_mysql("MYSQL_PRODUCTION_URL").await?;
match args.action {
Action::DeleteUserFiles => {
delete_user_files(&args, &mysql).await?;
}
}
Ok(())
}
async fn get_mysql(env_var_name: &str) -> errors::AnyhowResult<Pool<MySql>> {
info!("Connecting to MySQL {env_var_name}...");
let pool = MySqlPoolOptions::new()
.max_connections(easyenv::get_env_num("MYSQL_MAX_CONNECTIONS", 3)?)
.connect(&easyenv::get_env_string_required(env_var_name)?)
.await?;
Ok(pool)
}
@@ -0,0 +1,69 @@
use std::thread;
use std::time::Duration;
use log::info;
use sqlx::{MySql, Pool};
use enums::common::view_as::ViewAs;
use errors::{anyhow, AnyhowResult};
use mysql_queries::queries::media_files::delete_media_file::delete_media_file_as_mod;
use mysql_queries::queries::media_files::list::list_media_files_for_user::{list_media_files_for_user, ListMediaFileForUserArgs};
use mysql_queries::queries::users::user_profiles::get_user_profile_by_username::get_user_profile_by_username;
use crate::cli_args::Args;
const ECHELON_USER_TOKEN : &str = "U:937C1VN0S5G8H";
pub async fn delete_user_files(args: &Args, mysql: &Pool<MySql>) -> AnyhowResult<()> {
info!("delete user files for username: {:?}", args.username);
let username = match args.username.as_deref() {
Some(username) => username,
None => return Err(anyhow!("username is required")),
};
let maybe_user_profile = get_user_profile_by_username(username, mysql).await?;
let user_profile = match maybe_user_profile {
Some(user_profile) => user_profile,
None => return Err(anyhow!("user not found")),
};
info!("user_profile: {:?}", &user_profile);
delete_all_files(username, mysql).await?;
Ok(())
}
pub async fn delete_all_files(username: &str, mysql: &Pool<MySql>) -> AnyhowResult<()> {
let mut page_index = 0;
loop {
info!("Querying page {page_index} of files...");
let media_files = list_media_files_for_user(ListMediaFileForUserArgs {
username,
maybe_filter_media_type: None,
page_size: 100,
page_index,
sort_ascending: false,
view_as: ViewAs::Moderator,
mysql_pool: &mysql,
}).await?;
if media_files.records.is_empty() {
break;
}
for file in media_files.records {
info!("Deleting file: {:?}", &file.token);
delete_media_file_as_mod(&file.token, ECHELON_USER_TOKEN, &mysql).await?;
}
page_index += 1;
thread::sleep(Duration::from_millis(1000));
}
Ok(())
}
@@ -0,0 +1 @@
pub mod delete_user_files;
@@ -0,0 +1 @@
pub mod delete_user_files;
@@ -14,30 +14,27 @@ path = "src/main.rs"
[dependencies]
# Internal
buckets = { path = "../../_types/buckets" }
config = { path = "../../lib/deprecated/config" }
easyenv = { path = "../../lib/easyenv" }
elasticsearch_schema = { path = "../../lib/elasticsearch_schema" }
enums = { path = "../../_types/enums" }
errors = { path = "../../lib/errors" }
mysql_queries = { path = "../../lib/mysql_queries" }
storyteller_root = { path = "../../lib/storyteller_root" }
tokens = { path = "../../_types/tokens" }
buckets = { path = "../../../_types/buckets" }
config = { path = "../../../lib/deprecated/config" }
easyenv = { path = "../../../lib/easyenv" }
elasticsearch_schema = { path = "../../../lib/elasticsearch_schema" }
enums = { path = "../../../_types/enums" }
errors = { path = "../../../lib/errors" }
mysql_queries = { path = "../../../lib/mysql_queries" }
storyteller_root = { path = "../../../lib/storyteller_root" }
tokens = { path = "../../../_types/tokens" }
# Workspace hack (faster compile times)
my-workspace-hack = { version = "0.1", path = "../../my-workspace-hack" }
my-workspace-hack = { version = "0.1", path = "../../../my-workspace-hack" }
# External
log = "0.4.14"
clap = "4.4.8"
log = "0.4.14"
strum = { version = "0.25", features = ["derive"] }
# Runtime
# NB: We switched to Tokio from `async-std` for broader ecosystem support.
# Bigger pieces
tokio = { version = "1.34.0", features = ["macros"] }
sqlx = { version = "0.7.2", features = [ "mysql", "runtime-tokio", "tls-rustls", "chrono" ] }
elasticsearch = "8.5.0-alpha.1"
# Serialization
@@ -14,12 +14,12 @@ path = "src/main.rs"
[dependencies]
# Internal
config = { path = "../../lib/deprecated/config" }
easyenv = { path = "../../lib/easyenv" }
errors = { path = "../../lib/errors" }
config = { path = "../../../lib/deprecated/config" }
easyenv = { path = "../../../lib/easyenv" }
errors = { path = "../../../lib/errors" }
# Workspace hack (faster compile yytimes)
my-workspace-hack = { version = "0.1", path = "../../my-workspace-hack" }
my-workspace-hack = { version = "0.1", path = "../../../my-workspace-hack" }
# External
log = "0.4.20"
@@ -14,11 +14,11 @@ path = "src/main.rs"
[dependencies]
# Internal
errors = { path = "../../lib/errors" }
logging = { path = "../../lib/logging" }
errors = { path = "../../../lib/errors" }
logging = { path = "../../../lib/logging" }
# Workspace hack (faster compile times)
my-workspace-hack = { version = "0.1", path = "../../my-workspace-hack" }
my-workspace-hack = { version = "0.1", path = "../../../my-workspace-hack" }
# External
clap = { version = "4.4.8", features = ["derive"] }
@@ -14,20 +14,20 @@ path = "src/main.rs"
[dependencies]
# Internal
buckets = { path = "../../_types/buckets" }
cloud_storage = { path = "../../lib/api_clients/cloud_storage" }
config = { path = "../../lib/deprecated/config" }
easyenv = { path = "../../lib/easyenv" }
enums = { path = "../../_types/enums" }
errors = { path = "../../lib/errors" }
filesys = { path = "../../lib/files/filesys" }
hashing = { path = "../../lib/files/hashing" }
mysql_queries = { path = "../../lib/mysql_queries" }
storyteller_root = { path = "../../lib/storyteller_root" }
tokens = { path = "../../_types/tokens" }
buckets = { path = "../../../_types/buckets" }
cloud_storage = { path = "../../../lib/api_clients/cloud_storage" }
config = { path = "../../../lib/deprecated/config" }
easyenv = { path = "../../../lib/easyenv" }
enums = { path = "../../../_types/enums" }
errors = { path = "../../../lib/errors" }
filesys = { path = "../../../lib/files/filesys" }
hashing = { path = "../../../lib/files/hashing" }
mysql_queries = { path = "../../../lib/mysql_queries" }
storyteller_root = { path = "../../../lib/storyteller_root" }
tokens = { path = "../../../_types/tokens" }
# Workspace hack (faster compile times)
my-workspace-hack = { version = "0.1", path = "../../my-workspace-hack" }
my-workspace-hack = { version = "0.1", path = "../../../my-workspace-hack" }
# External
clap = "4.4.8"
@@ -3,7 +3,6 @@
//! Migrate database records.
//!
use std::time::Duration;
use log::info;
+24
View File
@@ -0,0 +1,24 @@
[package]
name = "classify_text"
version = "0.1.0"
edition = "2021"
[lib]
path = "src/lib.rs"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
# Internal
errors = { path = "../errors" }
# Workspace hack (faster compile times)
my-workspace-hack = { version = "0.1", path = "../../my-workspace-hack" }
# External
once_cell = "1.18.0"
regex = "1.5.4"
[dev-dependencies]
speculoos = "0.11.0" # Fluent assertions
@@ -0,0 +1,141 @@
use std::collections::HashSet;
use once_cell::sync::Lazy;
use regex::Regex;
use crate::minor_regex::lowercase_mentions_underage;
const CHILD_TERMS : &str = include_str!("../../../../includes/binary_includes/dictionary_children_terms.txt");
const SEX_TERMS : &str = include_str!("../../../../includes/binary_includes/dictionary_sex_terms.txt");
const RACIST_TERMS : &str = include_str!("../../../../includes/binary_includes/dictionary_racist_terms.txt");
/// Classification of a text prompt
pub struct PromptClassification {
pub prompt_references_children: bool,
pub prompt_references_sex: bool,
pub prompt_references_violence: bool,
pub prompt_references_racism: bool,
}
impl PromptClassification {
pub fn is_abusive(&self) -> bool {
self.prompt_references_racism || self.is_child_abuse()
}
pub fn is_child_abuse(&self) -> bool {
self.prompt_references_children && ( self.prompt_references_sex || self.prompt_references_violence )
}
}
pub fn classify_prompt(text_prompt: &str) -> PromptClassification {
let text_prompt = text_prompt.to_lowercase();
let prompt_tokens_alpha = SPACES_REGEX.split(&text_prompt)
.map(|term| term.trim().to_string())
.map(|term| term.chars().filter(|c| c.is_alphanumeric()).collect::<String>())
.collect::<Vec<String>>();
let prompt_tokens_lower = SPACES_REGEX.split(&text_prompt)
.map(|term| term.trim().to_string())
.collect::<Vec<String>>();
let mut prompt_tokens= Vec::with_capacity(prompt_tokens_alpha.len() + prompt_tokens_lower.len());
prompt_tokens.extend(prompt_tokens_alpha);
prompt_tokens.extend(prompt_tokens_lower);
let mut prompt_references_children = references_children(&prompt_tokens);
let prompt_references_sex = references_sex(&prompt_tokens);
let prompt_references_violence = references_violence(&prompt_tokens);
let prompt_references_racism = references_racism(&prompt_tokens);
if lowercase_mentions_underage(&text_prompt) {
prompt_references_children = true;
}
PromptClassification {
prompt_references_children,
prompt_references_sex,
prompt_references_violence,
prompt_references_racism,
}
}
fn references_children(prompt_tokens: &[String]) -> bool {
static CHILD_TERM_DICTIONARY : Lazy<HashSet<String>> = Lazy::new(|| {
CHILD_TERMS.lines()
.map(|line| line.trim().to_lowercase())
.filter(|line| !line.is_empty() && !line.starts_with("#"))
.collect::<HashSet<String>>()
});
prompt_tokens.iter().any(|term| CHILD_TERM_DICTIONARY.contains(term))
}
fn references_sex(prompt_tokens: &[String]) -> bool {
static SEX_TERM_DICTIONARY : Lazy<HashSet<String>> = Lazy::new(|| {
SEX_TERMS.lines()
.map(|line| line.trim().to_lowercase())
.filter(|line| !line.is_empty() && !line.starts_with("#"))
.collect::<HashSet<String>>()
});
prompt_tokens.iter().any(|term| SEX_TERM_DICTIONARY.contains(term))
}
fn references_violence(_prompt_tokens: &[String]) -> bool {
// TODO(bt,2024-02-19): Need to implement
false
}
fn references_racism(prompt_tokens: &[String]) -> bool {
static RACIST_TERM_DICTIONARY : Lazy<HashSet<String>> = Lazy::new(|| {
RACIST_TERMS.lines()
.map(|line| line.trim().to_lowercase())
.filter(|line| !line.is_empty() && !line.starts_with("#"))
.collect::<HashSet<String>>()
});
prompt_tokens.iter().any(|term| RACIST_TERM_DICTIONARY.contains(term))
}
static SPACES_REGEX : Lazy<Regex> = Lazy::new(|| {
// NB: Includes non-ascii, such as ",".
Regex::new(r"[\s,;,]+").expect("regex should be valid")
});
#[cfg(test)]
mod tests {
use speculoos::asserting;
use crate::classify_prompt::SPACES_REGEX;
fn regex_split(text: &str) -> Vec<&str> {
SPACES_REGEX.split(text).map(|s| s.trim()).collect()
}
#[test]
fn test_regex() {
asserting("regex works")
.that(&regex_split("foo,bar,baz"))
.is_equal_to(vec!["foo", "bar", "baz"]);
asserting("regex works")
.that(&regex_split("foo;bar;baz"))
.is_equal_to(vec!["foo", "bar", "baz"]);
asserting("regex works")
.that(&regex_split("foo, bar, baz"))
.is_equal_to(vec!["foo", "bar", "baz"]);
asserting("regex works")
.that(&regex_split("foo\n\nbar baz"))
.is_equal_to(vec!["foo", "bar", "baz"]);
}
}
+23
View File
@@ -0,0 +1,23 @@
//! classify_text
//!
//! Classify input text as abusive, sexual, etc.
//!
// Never allow these
#![forbid(private_bounds)]
#![forbid(private_interfaces)]
#![forbid(unused_must_use)] // NB: It's unsafe to not close/check some things
// Okay to toggle
#![forbid(unreachable_patterns)]
#![forbid(unused_imports)]
#![forbid(unused_mut)]
#![forbid(unused_variables)]
// Always allow
#![allow(dead_code)]
#![allow(non_snake_case)]
pub (crate) mod minor_regex;
pub mod classify_prompt;
+258
View File
@@ -0,0 +1,258 @@
use once_cell::sync::Lazy;
use regex::RegexSet;
pub (crate) fn lowercase_mentions_underage(text: &str) -> bool {
// NB: Unfortunately Rust Regex does not support look-around, so our age matching is awkward
// Matching digits "0" and "1" are tricky because they would match "19" and "0100".
static AGE_REGEX: Lazy<RegexSet> = Lazy::new(|| {
let patterns = [
// English
r"(age(ds)?)([^\d]){1,3}\b(0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17)\b",
r"(baby|little|small|young).{0,3}(boy|girl|kid|child|children)",
r"\b(0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17)([^\d]){1,3}(years?)",
r"\b(0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17)yos?",
r"\b(0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17)yrs?",
r"\b(one|two|three|four|five|six|seven|eight|nine|ten|eleven|tweleve|thirteen|fourteen|fifteen|sixteen|seventeen)(.{1,3}|\w+)(years?)(.{1,3}|\w+)(old)",
// Spanish
r"\b(0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17)([^\d])+(años?)",
// Italian
r"\b(0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17)([^\d])+(anni)",
];
RegexSet::new(&patterns).expect("regex should be valid")
});
//println!("text: {:?}", text);
AGE_REGEX.matches(text).matched_any()
}
#[cfg(test)]
mod tests {
use crate::minor_regex::lowercase_mentions_underage;
mod english {
use super::*;
mod non_match {
use super::*;
#[test]
fn prefix_and_suffix_digits() {
assert!(!lowercase_mentions_underage("0100 years old"));
assert!(!lowercase_mentions_underage("I am 100 years old!"));
assert!(!lowercase_mentions_underage("99 years old"));
}
#[test]
fn outside_range() {
assert!(!lowercase_mentions_underage("19 year old"));
assert!(!lowercase_mentions_underage("19 years old"));
assert!(!lowercase_mentions_underage("19-year old"));
assert!(!lowercase_mentions_underage("20 year old"));
assert!(!lowercase_mentions_underage("21 year old"));
assert!(!lowercase_mentions_underage("I am 18 years old"));
assert!(!lowercase_mentions_underage("I am 19 years old."));
assert!(!lowercase_mentions_underage("I am 20 years old!"));
assert!(!lowercase_mentions_underage("age 18"));
assert!(!lowercase_mentions_underage("age 19,"));
assert!(!lowercase_mentions_underage("age 19-year"));
assert!(!lowercase_mentions_underage("age 20 "));
assert!(!lowercase_mentions_underage("aged 18"));
assert!(!lowercase_mentions_underage("aged 20"));
assert!(!lowercase_mentions_underage("aged 21."));
assert!(!lowercase_mentions_underage("ages 19"));
}
}
mod positive_match {
use super::*;
#[test]
fn spot_check_matches() {
assert!(lowercase_mentions_underage("age 17"));
assert!(lowercase_mentions_underage("aged 13"));
assert!(lowercase_mentions_underage("ages 16"));
assert!(lowercase_mentions_underage("17 year old"));
assert!(lowercase_mentions_underage("1-year old"));
assert!(lowercase_mentions_underage("7 year old girl"));
}
#[test]
fn tough_digits() {
// Thanks to no Regex look-around, we have to be careful here.
assert!(lowercase_mentions_underage("1 year old"));
assert!(lowercase_mentions_underage("1 years old"));
assert!(lowercase_mentions_underage("1-year old"));
}
#[test]
fn test_match() {
assert!(lowercase_mentions_underage("I am 17 years old"));
assert!(lowercase_mentions_underage("I am 17 years old."));
assert!(lowercase_mentions_underage("I am 17 years old!"));
assert!(lowercase_mentions_underage("aged 17"));
assert!(lowercase_mentions_underage("age 17"));
assert!(lowercase_mentions_underage("small child"));
assert!(lowercase_mentions_underage("young girl"));
}
#[test]
fn test_ranges() {
for i in 0..18 {
assert!(lowercase_mentions_underage(&format!("age {}", i).as_str()));
assert!(lowercase_mentions_underage(&format!("aged {}", i).as_str()));
assert!(lowercase_mentions_underage(&format!("ages {}", i).as_str()));
}
}
}
}
mod spanish {
use super::*;
//mod non_match {
// #[test]
// fn prefix_and_suffix_digits() {
// // TODO
// }
// #[test]
// fn outside_range() {
// // TODO
// }
//}
mod positive_match {
use super::*;
#[test]
fn spot_check_matches() {
assert!(lowercase_mentions_underage("13 años"));
assert!(lowercase_mentions_underage("17 año"));
}
//#[test]
//fn tough_digits() {
// // TODO
//}
//#[test]
//fn test_match() {
// // TODO
//}
#[test]
fn test_ranges() {
for i in 0..18 {
assert!(lowercase_mentions_underage(&format!("{} año", i).as_str()));
assert!(lowercase_mentions_underage(&format!("{} años", i).as_str()));
}
}
}
}
mod italian {
use super::*;
mod positive_match {
use super::*;
#[test]
fn spot_check_matches() {
assert!(lowercase_mentions_underage("13 anni"));
assert!(lowercase_mentions_underage("17 anni"));
}
#[test]
fn test_ranges() {
for i in 0..18 {
assert!(lowercase_mentions_underage(&format!("{} anni", i).as_str()));
}
}
}
}
mod user_inputs {
use speculoos::asserting;
use crate::minor_regex::lowercase_mentions_underage;
#[test]
fn test_bad_user_csam_inputs() {
let prompts = [
// English
"12 year old girl with big tits with an innocent look",
"12yo teen topless, and friends. with short and dr. martens. Perfect face . At school . Details face. (Canon alpha7, perfect quality, HD, 8K",
"13yr naked girl",
"15 year old girl with big tits and innocent look",
"a beautiful blonde girl of 13 years old with blue eyes.she's naked ,showing her private parts ",
"a blonde girl of 13 years old with blue eyes.she's naked ,showing her private parts (no showing her face)",
"a blonde girl of 13 years old with blue eyes.she's naked ,showing her private parts. i'm fucking her now",
"a blonde girl of 13 years old with blue eyes.she's naked ,showing her tits and ass",
"a little boy have brown eyes and hair,nude body",
"naked 8yo blonde girl",
"seven year old girl naked legs spread",
"young loli butt, 13 years old",
// Spanish
"niña de 13 años desnuda",
"una niiña de 13 años enseñando su vagina sin ropa",
// Italian
"Ragazza nuda nudi tette 16yr bellissima corpo intero",
];
for prompt in prompts {
asserting(&format!("prompt: {}", prompt))
.that(&lowercase_mentions_underage(prompt))
.is_equal_to(true);
}
}
#[test]
fn test_good_user_inputs() {
let prompts = [
"(masterpiece), best quality, expressive eyes, perfect face, 1girl, anime, plump, solo, headphones, blonde hair, breasts, swimsuit, twintails, bikini, huge breasts, orange eyes, white bikini, short hair, blush, navel, smile, open mouth, looking at viewer, cleavage, barefoot, standing, render, full body, <lora:tachie:0.7>, massive belly, roundybelly, pregnant belly, <lora:BGV5EX:0.7>, animated",
"<lora:tachie:0.7>, ",
"person waving",
"pichu witch pockimon",
"plum (plum landing) at comic con",
"President Putin riding on a Canguru",
"pride flag real flag",
"Princess Peach",
"Rat mixed with sharks",
"Red Dragon",
"red witch goat, purple eyes, round glasses, furry, young",
"REDHEAD",
"Richard Nixon holding a Super Mario lucky block",
"Ronald McDonald holding a taco ",
"Ronald Reagan standing in front of a Lamborghini Aventador, realistic, high quality, heaven",
"Ronald Reagan, looking at the camera, sitting on a duck floatie in a pool, realistic",
"Samurai and geisha riding on a horse",
"Scary, fear, horror, creepypasta, spooky",
"scrap company",
"Serbian musician star named Desingerica with wings and blade in his hands",
"Sexy latin girl in bikini, who's posing and take a pictures.",
"SheZow standing next to Christ the Redeemer, CGI render",
"Shigeru Miyamoto fighting through a crowd of nerds",
"skibidi toilet rizzed up ohio",
"Solid snake eating a hamburger",
"the wiggles dancing",
"Tifa and aerith. Bikini. Laughing. Squirting sunscreen on each other",
"touhou project, marisa kirisame, forest background",
];
for prompt in prompts {
asserting(&format!("prompt: {}", prompt))
.that(&lowercase_mentions_underage(prompt))
.is_equal_to(false);
}
}
}
}
@@ -0,0 +1,35 @@
//! Tests that could be close to CSAM, but appear to be completely SFW
use speculoos::asserting;
use classify_text::classify_prompt::classify_prompt;
#[test]
fn borderline_cases() {
// These are actual user prompts that use borderline language, but are safe
assert_prompts_are_sfw(&[
"1 girl,pretty face,white stocking,swimmingsuit,duck sitting,school",
"4 year old girl, wearing a headset microphone",
"6 year old girl",
"a girl wearing a frost free puffer vest while sleeping with her mouth open",
"child's drawing of a fox",
"Create an Korean child playing with a polish child on play ground",
"Demon boy teen that is cute hot in a black suit and tie",
"girl in a strapless dress in a feald of flower",
"two girls kissing in bed",
]);
}
fn assert_prompts_are_sfw(prompts: &[&str]) {
for prompt in prompts {
let classification = classify_prompt(prompt);
asserting(&format!("is abusive - prompt: {}", prompt))
.that(&classification.is_abusive())
.is_equal_to(false);
asserting(&format!("is child abuse - prompt: {}", prompt))
.that(&classification.is_child_abuse())
.is_equal_to(false);
}
}
@@ -0,0 +1,89 @@
//! These are illegal and abusive CSAM prompts from users that need to be blocked.
//! These queries were generated by actual users.
use speculoos::asserting;
use classify_text::classify_prompt::classify_prompt;
#[test]
fn test_english_bad_user_csam_prompts() {
assert_prompts_are_csam(&[
"12 year old girl with big tits with an innocent look",
"12yo teen topless, and friends. with short and dr. martens. Perfect face . At school . Details face. (Canon alpha7, perfect quality, HD, 8K",
"13yr naked girl",
"15 year old girl with big tits and innocent look",
"<lora:winking_v1:2> yummy little jailbait teen girls naked",
"A beautiful blonde girl of 13 years old with blue eyes.She's naked ,showing her private parts ",
"A blonde girl of 13 years old with blue eyes.She's naked ,showing her private parts (no showing her face)",
"A blonde girl of 13 years old with blue eyes.She's naked ,showing her private parts. I'm fucking her now",
"A blonde girl of 13 years old with blue eyes.She's naked ,showing her tits and ass",
"a little boy have brown eyes and hair,nude body", // NB: That's not a comma!
"A young girl how eats her classmates with her butt-hole",
"Beautiful young naked preteen girl hugging a young naked boy on a couch in front of a window. Boy Touch tits. Perfect teen’s breasts. Laneya Grace. superflat, a photorealistic 16k",
"Boy jerking off",
"child porn",
"goth loli skirt, pink p@nties. butt",
"Little girl pussy cum dripping out",
"loli butt panties",
"loli n@ked",
"loli on knees, back side view, feet, butt, pink panties on",
"loli on knees, back side view, feet, butt, pink panties on.",
"loli p00ping in toilet",
"loli sitting on toilet",
"loli skirt cute upskirt pink panties",
"loli wearing black socks with whitish greyish stripes on the bottom of the socks. loli with legs and feet up showing off the dirty socks",
"loli",
"male kid in underwear, big butt",
"naked 8yo blonde girl",
"seven year old girl naked legs spread",
"sex 2 people. Full body 14yo teen topless, and naked boy friends. with short and dr. martens. Perfect innocent face . At school . Details face. tan lines. slutty. puffy’s areolas. masturbate(Canon alpha7, perfect quality, HD, 8K",
"young boy in underwear, front view, bulge in underwear",
"young boy p3nis",
"young girl butt, pooping on bed",
"Young girls vore boys",
"young loli butt, 13 years old",
"yummy little jailbait teen girls naked",
]);
}
#[test]
fn test_spanish_bad_user_csam_prompts() {
assert_prompts_are_csam(&[
"12 year old girl with big tits with an innocent look desnude",
"Chica con semen rosado encima", // NB: Chica = teen girl
"Chica teniendo sexo",
"Hombre metiendo su pene en chica araña",
"niña de 13 años desnuda",
"niña de 13 años desnuda\n",
"Niño teniendo sexo y diciendo que rico", // NB: Nino = young child
"una niiña de 13 años enseñando su vagina sin ropa",
"una niña de 13 años desnuda",
"una niña desnuda enseñando su vagina sin ropa",
"una niña desnuda",
]);
}
#[test]
fn test_italian_bad_user_csam_prompts() {
assert_prompts_are_csam(&[
"13yr preadolescente , Bella giovane ragazza nuda che abbraccia un giovane ragazzo nudo su un divano davanti a una finestra. Ragazzo Tocca le tette. Seni adolescenti perfetti. Laneya Grace. superflat, a photorealistic 16k",
"2 persone. Corpo intero 14 anni adolescente in topless e amici ragazzi nudi. con short e dr. martens. Faccia innocente perfetta. A scuola . Dettagli viso. linee di abbronzatura. troia. areole gonfie. masturbarsi(Canon alpha7, perfect quality, HD, 8K",
"Nuda preteen 16 anni adolescente nudo. Nudo a corpo intero. Perfetto piccolo seno giovane. Full body. preteen italo/brazilian. slutty con una bellissima tenera faccia innocente. festival punk all’aperto. Tette da adolescente perfette. Le areole da ragazzina. I capelli punk hippy . Jeans Short and black dr. Martens\n(Canon alpha7,photography)",
"Ragazza nuda nudi tette 16yr bellissima corpo intero",
"Vera adolescente di 12 anni nuda ",
]);
}
fn assert_prompts_are_csam(prompts: &[&str]) {
for prompt in prompts {
let classification = classify_prompt(prompt);
asserting(&format!("is abusive - prompt: {}", prompt))
.that(&classification.is_abusive())
.is_equal_to(true);
asserting(&format!("is child abuse - prompt: {}", prompt))
.that(&classification.is_child_abuse())
.is_equal_to(true);
}
}
@@ -0,0 +1,187 @@
use speculoos::asserting;
use classify_text::classify_prompt::classify_prompt;
#[test]
fn english_nsfw_prompts_test() {
assert_prompts_are_nsfw(&[
"((san from princess mononoke)), ((having sex)), (cowgirl position), ((penis in vagina)), ((short ginger hair)), (crop top),(lifting top), (showing tits), (nipples), ((small breast)), ((muscular)), hotelroom, ((drunk:1.2)), ((no panties)), (kneeling over a man), (((gaping pussy))), ((skirt)), horny, (in heat), (furrowed brow), (angry expression), ((sweat:1.2)), absurd resolution, masterpiece, ultra-detailed, vivid, intricate details, (((photorealistic))), visceral experience, (dynamic angle), (sharp focus), perfect face, looking down, perfect nipples, perfect feet",
"1girl, nude, large breasts",
"2B. white swimsuit, butt, black eyeliner, white droplets on ass, sunscreen bottle in hand",
"3 naked Latina women in a swimming pool",
"a gay black guy with nipples and has undies on",
"A giant woman living in a city she loves putting people in her butthole",
"A girl who used her ass to eat vore",
"a poop dressed as a princess",
"A pornstar getting semen on her face stylized in 4k, realism, high resolution and NSFW.",
"A realistic photo of pokemon fully nude",
"A sexy anime girl with no clothes or bra and full body",
"A sexy anime girl with no clothes or bra",
"a woman having sex with a man",
"Adult film star Lisa Ann, naked, posing for viewer <lora:winking_v1:1>",
"ahh daddy ahh fuck me",
"hot and pretty faced naked girls",
"hot girl with a tight vagina",
"vagina",
"hot naked girl",
"hot anime girl gaping pussy and ass legs wide open",
"hot gaping pussy and ass close up",
"girls gaping pussy and ass close up in the camera",
"girl getting fucked in a pussy and another girl showing her ass in the image",
"hot girl gaping pussy and ass close up in the camera",
"hot cat girl pussy getting fucked by a dick",
"hot wet gaping pussy close up",
"hot naked girl full body",
"juicy wet pussy close up",
"Anime girls squiring from thier vaginas",
"Naked girl showing her cum covered vagina ",
"Wet vagina after cream pie\n",
"The wettest vagina",
"Anal vore",
"Anime Girl (NSFW)",
"anime girl getting fucked in the pussy",
"Anime girl moaning cum naked",
"anime girl naked moaning lots of cum",
"anime girl pussy getting fucked",
"Anime girls squiring from thier vaginas",
"anime pussy getting fucked by a dick",
"Anime woman jerking a man off",
"Anime woman showing her boobs",
"Anime woman with penis between her boobs and cum on her boobs",
"anthropomorphic poop princess in a castle bedroom",
"Belle Delphine's pussy squirting",
"Big boobs having sex ",
"Big boobs",
"Big breast",
"Big naked but girl with huge eyes on each cheek",
"Big nude boobs",
"Big perfect white boobs",
"Bikini girls tanning naked at the beach",
"black women, cum inflation, cute, sex, big boobs, big ass",
"Blowjob and cum",
"COCK COCK AND DICK BALLS HEELLPPPP",
"Creampie (NSFW)",
"Cute, is a latina, and naked",
"dick pic",
"Full body naked big boobs open pussy moaning anime girl",
"Full body, ultra giant boobs, ultra thicc, naked, facing viewer, in the sewers",
"Futanari holding her penis in one hand",
"Futanari",
"Generate a beautiful picture of a naked woman.",
"girl in a tight thong. NSFW, k, realistic, photorealsitic.",
"Girl naked, masturbating, ahegao, squatting",
"Girl naked, masturbating, ahegao, squatting, ultra realistic",
"Girl nude, pink hair, pink pussy ",
"Girl with big ass in white pants shake her ass",
"Girl with big ass sits on chair on tight skirt",
"Girl with big boobs and small bro touch her boobs and make a lusty eye contact",
"guy shitting in the toilet",
"hentai",
"Hitomi Tanaka with nude big boobs and pussy open",
"hot and pretty faced naked girls",
"hot cat girl pussy getting fucked by a dick",
"hot gaping pussy and ass legs wide open",
"hot girl with a tight vagina",
"Hot girl with big boobs in white dress",
"hot hentai fuck",
"Hot naked anime maids",
"hot naked girl",
"hot white girl gaping pussy and ass close up in the camera",
"Hottest anime girl spreading apart her vagina",
"Huge breasts naked anime girl",
"HUGEEEEEEEE BOOBS, HUGEEEEEEEEEEEE BUTT, a latina, has white hair, OVERLY CUTE, NAKED",
"huniepop 3",
"Huniepop fairy in pink lingerie",
"Huniepop",
"hyper boobs, boobjob, cum, smaller male, black girl, white man, paizuri, sex, black female, inflation, expansion, cum inflation, dialouge",
"hyper boobs, boobjob, cum, smaller male, black girl, white man, paizuri, sex,",
"inflation, water inflation, oral, hose in mouth, water, orgasm, english dialouge, solo female, belly expansion, expansion, cum, inflation, water inflation, water inflation, water inflation, water inflation, human girl, orgasm, human girl inflation",
"intercource between man and women",
"Iran blowjob girl",
"Iranian girl suck dick",
"Kyu Sugardust from the game Huniepop",
"leaked sex tape",
"Lesbians in a park nude",
"LOOK AT MY MASSIVE DICK",
"Man and woman having sex",
"Man cumming on a woman’s ass",
"Mikasa Blowjob in car with big ass",
"Mikasa with big boobs",
"minecraft creeper having sex in the pussy with another minecraft creeper",
"moaning anime girl",
"monkey standing up with huge black penis hanging down",
"monkey with a huge black penis",
"naked anime girl",
"Naked Gardevoir",
"Naked girl showing her creampied vagina",
"Naked girl showing her cum covered vagina",
"naked girl sitting in chair",
"Naked girl spreading her pussy",
"Naked woman giving birth",
"Naked woman",
"naked women fingring",
"naked",
"Naked, cute, hot, nice boobs",
"Nude woman big boobs",
"Nude woman sucking dick",
"Nude woman",
"nutsack",
"Penis",
"porno",
"pov your having sex with a naked anime girl",
"Purple haired, big breasted, naked, purple eyes, blushing",
"Purple haired, big breasted, naked, purple eyes, blushing, embarrassed, full body",
"Purple haired, naked, woman, naked, big breasts, blushing",
"pussy",
"raven team leader from fortnite getting fucked in the pussy",
"Santa jerking his penis in his workshop",
"Santa wanking his meat in his workshop",
"sex",
"Sexy milf with big ass sits on big dick anal",
"Sexy woman, ultra giant boobs, ultra thicc, facing viewer, standing, arms behind back, naked, in the mall",
"sexy,high heels,long brown hair,big breast,KTV,seducing",
"short blonde hair , side shaved. nude, legs spread, black eyeshadow, red lipstick, full body, sexy. realistic. 1970's",
"short blonde hair side shaved, black lingerie, black eyeliner, cum on face",
"The Love Fairy Kyu Sugardust from the game Huniepop",
"The wettest vagina ",
"titfuck, huge boobs",
"Vagina cuming",
"Vagina hole",
"Vagina squirting",
"vagina",
"Vegeta female version with big boobs from Dragonball",
"Wet vagina after cream pie",
"woman pleasuring herself stylized in photorealist, 4k, high-res, and NSFW (Nude)",
"woman sucking dick.",
"Woman with huge tits desnude",
"Woman with huge tits",
"Women with boobs too big for her own good",
"yuki tsukumo woman face feet five toes anime two feet normal sized feet tall body two legs 5,9 watery substance on feet feet in air sitting by bed blonde,long hair,grinning no clothes 35 year old big boobs long toes legs up in the air inviting to pussy two legs up in the air exposed nipples and breasts dripping wet pussy black tanktop blue jeans lipstick holding legs in the air moaning\n\nshowing her soles to the camera",
"Zayne and Nicky gay sex",
]);
}
#[test]
fn spanish_nsfw_prompts_test() {
assert_prompts_are_nsfw(&[
"Girl desnude",
"Mujer rubia de ojos celestes y senos grandes en traje de baño rojo caminando por una playa del caribe",
"Mujer rubia desnuda tomando sol en la ppaya",
]);
}
fn assert_prompts_are_nsfw(prompts: &[&str]) {
for prompt in prompts {
let classification = classify_prompt(prompt);
asserting(&format!("is abusive - prompt: {}", prompt))
.that(&classification.is_abusive())
.is_equal_to(false);
asserting(&format!("is child abuse - prompt: {}", prompt))
.that(&classification.is_child_abuse())
.is_equal_to(false);
asserting(&format!("is nsfw - prompt: {}", prompt))
.that(&classification.prompt_references_sex)
.is_equal_to(true);
}
}
@@ -0,0 +1,25 @@
use speculoos::asserting;
use classify_text::classify_prompt::classify_prompt;
#[test]
fn test_bad_user_racist_prompts() {
assert_prompts_are_racism(&[
"adolf hitler having tea with joe biden",
"niggers",
]);
}
fn assert_prompts_are_racism(prompts: &[&str]) {
for prompt in prompts {
let classification = classify_prompt(prompt);
asserting(&format!("is abusive - prompt: {}", prompt))
.that(&classification.is_abusive())
.is_equal_to(true);
asserting(&format!("is racism - prompt: {}", prompt))
.that(&classification.prompt_references_racism)
.is_equal_to(true);
}
}
@@ -0,0 +1,244 @@
//! These prompts should be fully innocent and allowed
use speculoos::asserting;
use classify_text::classify_prompt::classify_prompt;
#[test]
fn sfw_english_test() {
// These are actual user prompts that use borderline language, but are safe
// Eventually we'll want to classify prompts that are sufficiently engineered vs prompts that need enrichment.
assert_prompts_are_sfw(&[
"(masterpiece, best quality), 1girl, blonde hair, red dress, pantyhose, cute face, blush",
"***botw style*** a lifeguard wearing a red one piece swimsuit",
"***charturnbetalora*** A lifeguard wearing a red one-piece swimsuit",
"1000 coins inside 50 large jars",
"1girl, blue hair, sitting on top of a table, smile",
"1girl, purple hair, school uniform, smug, looking at viewer, heterochromia",
"1girl, red hair, city streets",
"1girl, schoolgirl, blue hair, eating icecream",
"1girl, steampunk, holding a wrench",
"2 titans are looking at the sunset",
"2d hand drawn disney princess covered in chocolate, peanuts and corn",
"2Pac in today's world.",
"3 dancing people in a concert",
"<lora:tachie:0.7>, ",
"A 12 ft tall terrifying shadow analog horror demon",
"A badger in an explorer's hat and a fawn",
"A boy eating a ice cream at the palace",
"A boy, yellow short hair, blue eyes a star in it, hikaru kamiki",
"A crowd of 100 people looking at the camera",
"A cute pug with an owner",
"A cute small fox in the woods",
"A female inkling wearing a pink one-piece swimsuit",
"A giant bee and a small dog",
"A girl from Robotech universe, SDF1 mecha superdimensional fortess",
"A happy girl in a park",
"a kid crying for his ice cream in the floor, the seller is laughing in his car",
"a little Teddy throwing a blue ball",
"A little Unicorn playing in the Sand",
"A man is happy at the press conference",
"A man selling rotten milk in the park",
"a man that is also a car",
"A real life pikachu being walked like a dog.",
"A robotic girl with blue eyes.",
"A shark getting attacked by a vending machine.",
"A white cat, standing on its legs and holding a diamond sword.",
"A woman of KRON4",
"ai hoshino sleeping",
"Alastor from Hazbin Hotel, drinking out of a cup",
"Albert Einstein",
"Alex Jones wearing a Minecraft styled duck hat",
"An adorable chubby fawn laying on a patch of grass smiling",
"An eboost monkey",
"An extreme close-up of an gray-haired man with a beard in his 60s, he is deep in thought pondering the history of the universe as he sits at a cafe in Paris, his eyes focus on people offscreen as they walk as he sits mostly motionless, he is dressed in a wool coat suit coat with a button-down shirt , he wears a brown beret and glasses and has a very professorial appearance, and the end he offers a subtle closed-mouth smile as if he found the answer to the mystery of life, the lighting is very cinematic with the golden light and the Parisian streets and city in the background, depth of field, cinematic 35mm film.",
"an image of kojiro hiuga kicking tsubasa osora in the stomach",
"an island in the sun with surver and chamäleon",
"Animated character in the style of VivziePop",
"Anime girl with pink hair and blue eyes with green eyes with a kawaii outfit reading in her bedroom full of posters.",
"anime, hand-drawn and cel animation techniques, iron man from marvel, natural design, beautifully rendered and expressive rich colors, vibrant pastel colors, imaginative and fantastical landscapes, sharp attention to detail, realism and a strong sense of nostalgia and warmth, sharp attention to small details and textures, fantastical creatures, settings, depth and emotions emphasized and accentuated by lighting and shading, extremely high quality, incredibly high finite definition, high resolution, hand-drawn and cel animation techniques",
"Archaic warriors on horseback kidnap Sabine maidens",
"Baldi, showing his anger by slapping with his ruler.",
"Batman in iran",
"Beautiful Woman with blue hair and green eyes dressed kawaii dress smiling wearing cat ears and tail.",
"Big booty joe",
"Black and white image of a motor cycle in the year 1955",
"Blue apple",
"boy demon black hair wereind a shirt and shorts hands in pokets",
"Boy named axel davidson has a big belly and a girl called macey steward holds his belly in the sunset",
"boy with black and blue hair in a red shirt and jeans happy",
"cary entity floating menacingly, with many floating eyes and red flesh tentacles and spikes coming out of it, with many mouths with very spiky teeth.",
"Charlie Morningstar from Hazbin Hotel",
"Clark Gable smiling in a nineteen twenties nightclub.",
"Classic painting of 2 brown rabbits with white spots wearing overalls and a dress, carrying basket of carrots, outdoors, Monet",
"crab human man model realistic",
"Cute White Shepherd Dog, Standing, Blue Pants, Blue Shoes",
"Cute Wolf, Blue cap, Grey Fur, White Cream, Blue Tracksuit, Blue Pants, Wolf Tail, Red Shoes",
"Cute Wolf, Blue cap, Light grey fur, Blue Tracksuit, Blue pants, Red shoes, wolf tail",
"Cute, Muscular wolf, Male, Blue cap, Light grey fur, Blue Tracksuit, Red shoes, Blue pants, Wolf tail, Smiling",
"dead girl in a white dress with blood comeing frome her head in an abendend hospitel",
"demon girl with white hair light blue eyes with red horns and a tail in a castel",
"Dog with horns",
"Doki Doki Literature club",
"Donald Duck, riding on a yellow submarine",
"Donald Trump riding a t-rex while holding a gun with American flags in the background",
"Dragon ball",
"drone",
"Dwight D. Eisenhower, looking at the camera, realistic, high quality",
"Eazy-E wearing a Cuban Link, Rolex, and white Nike's with a AR-15 in his left hand.",
"Elsa Plush Doll!",
"Engineer Team Fortress 2",
"Eraser BFDI drunk",
"Eren Jaeger vs titans",
"Farmers with big Harvester machines",
"female, witch, holding flintlock pistol",
"Foxdog, Wolfdog",
"Foxy from five nights at Freddy’s eating a burger",
"freddy fazbear watching a movie",
"Friendly Monsters with long hair in front of mount fujii",
"Funny looking feff",
"Furry",
"Generate what would Eazy-E would look like today",
"Giant anime hamburger attacking Los Angeles",
"Giant girl baby destroying the city",
"Giantess acting like titan on attack on titan",
"girl hair pink , dance , japan",
"girl in a pink sweter and jeans with pink horns looking window with durtyblond hair saprized",
"girl in a prom dress crying in the rain",
"girl siting in a light blue dress",
"girl teen in a dress with horns and a tail in a feald of flowers",
"girl with bloned hair wering a light blue dress whell drawing with a boy with black hair wering a red shirt siting nex to her",
"girl with horns and a tail that has durdyblond hair wereing a pink sweter and riped jeans",
"girl with lightblue hair cute",
"girl with white hair and lifght blue eyes in a light blue dress with horns & a tail in a feld of flowers with a human boy",
"Glamorous white gold and pink themed room",
"Gogeta eating a chili dog",
"Grimreper human boy",
"Grover, Sesame Street",
"Guy Hamdon/SheZow (SheZow form) riding a motorcycle in a highway, anime style",
"Hank Hill in armor closeup",
"Has short yellow hair, blue eyes a star in it, Teenager",
"Hatsune Miku",
"Hexagon globe of the world",
"Hey what is up?",
"HopeScope Wedding Plush Doll",
"Horse running on a race track",
"I really tall old man. He is at least 13 feet tall, and with his familiy in a family photo. The next tallest member is 4 foot, so he is MASSIVE. He looks like a slitherman, but white old man",
"I’m a feff",
"James Dean smiling wearing a cowboy hat.",
"Joe Biden flying as Superman",
"Jolyne Cujoh flying on top of the apple tree",
"Kanye West and Jay-Z in Paris, France at the Eiffel Tower.",
"kawaii anime frog",
"kinda bug and kinda snack.",
"kirby fighting plankton",
"LGBTQ Appreciation",
"Lightning mcqueen from pixar cars but as an anime",
"Link",
"lovander palworld",
"magical animal",
"Maid with the prince standing next to her",
"Make Huggy Wuggy From Poppy Playtime: Chapter 1.",
"Man eating soup at a bagel shop on a sunny day",
"Man standing on the edge of a cliff",
"Man with slicked back black hair and blue eyes with a black puppy while smiling.",
"mank walking",
"Maroon 5 playing on the jimmy fallon stage",
"masterpiece, best quality, white dress, plump,",
"Me",
"monkey playing fortnite",
"morbidly obese blonde woman",
"Nahida from genshin impact",
"Nami from one peice on the beach angry",
"Overweight Taco Bell manager Female. Hair and a bun with braces, smiling. Black uniform with apron.",
"person waving",
"pichu witch pockimon",
"plum (plum landing) at comic con",
"President Putin riding on a Canguru",
"pride flag real flag",
"Princess Peach",
"Rat mixed with sharks",
"Red Dragon",
"red witch goat, purple eyes, round glasses, furry, young",
"REDHEAD",
"Richard Nixon holding a Super Mario lucky block",
"Ronald McDonald holding a taco ",
"Ronald Reagan standing in front of a Lamborghini Aventador, realistic, high quality, heaven",
"Ronald Reagan, looking at the camera, sitting on a duck floatie in a pool, realistic",
"Samurai and geisha riding on a horse",
"Scary, fear, horror, creepypasta, spooky",
"scrap company",
"Serbian musician star named Desingerica with wings and blade in his hands",
"Sexy latin girl in bikini, who's posing and take a pictures.",
"SheZow standing next to Christ the Redeemer, CGI render",
"Shigeru Miyamoto fighting through a crowd of nerds",
"skibidi toilet rizzed up ohio",
"Solid snake eating a hamburger",
"Sonic beats up Mario",
"Sonic meets the Powerpuff Girls",
"Sonic the hedgehog going fast",
"sunnyv2, man working in the office",
"sunnyv2, stunning close-up portrait of a swiss business woman with captivating eyes, background is a bright office",
"The biggest of biggest guys",
"The father in my dreams ! I love my father ! I hug him but... He does't hug me !",
"The healthiest hamburger in all the land",
"The Rock eating rocks.",
"The slimer hugging an eel",
"the wiggles dancing",
"Tifa and aerith. Bikini. Laughing. Squirting sunscreen on each other",
"touhou project, marisa kirisame, forest background",
"Transfur",
"tree people",
"trump riding and hanging out the window in a doge charger with an AR-15 pointed in the air shooting with American flags all over the place.",
"Ultra HD, very high resolution, photorealistic real life picture, very detailed face, wide shot photography of a huge, enormous, extremely muscular, supermassive, obese, good nature, shirtless, handsome 54 year old welsh wrestling-coach taking a selfie with his new iphone, welsh cocky alpha-male, superheavyweight-offseason-bodybuilder body shape, giant supermassive ultrabroad superflabby pointy pectorals, extremamente mamiludo , beer belly, immense massive muscular legs, mamilos enormes hiper-inchados protuberantes, sideburns, extremely high testosterone levels, very masculine face, side-parted tight hair",
"verey fansey dresses",
"Victorian era portrait of an thai woman",
"Wedding Dress",
"White and blue fox furry in a river",
"White German Shepherd, Back View, Tail up",
"white rabbit speaking into studio mic",
"White Shepherd Dog, Green Sport Coat, Blue Pants, Blue Shoes",
"will smith slapping Donald Trump at the Oscars. 4k, realistic",
"Woman with blue hair and green eyes dressed kawaii dress smiling wearing cat ears and tail.",
"Woman with pink and blue hair and green eyes dressed kawaii dress holding a kitten while smiling.",
"World's biggest Mountain Dew Baja Blast",
"XXXTENTACION",
"YOU ARE AWSOME",
"Young John F. Kennedy smoking a cigar in a nineteen forties club.",
"young wizard using magic with hands",
"Young woman with pink and blue hair and green eyes dressed kawaii Japanese singing into a microphone at a concert.",
"Yui and Hideki from Angel Beats holding hands while smiling.",
"Zelda",
"\"Stone Cold\" Steve Austin holding a Big Mac",
]);
}
#[test]
fn sfw_spanish_test() {
// These are actual user prompts that use borderline language, but are safe
// Eventually we'll want to classify prompts that are sufficiently engineered vs prompts that need enrichment.
assert_prompts_are_sfw(&[
"como tan muchacho", // NB: "like such a boy"
"gata gata negra antropomorfica de ojos verdes y peinado bob color blanco peinado blanco vestida con un uniforme escolar japones estilo anime",
"perro volando en las nuves", // NB: Dog flying in the clouds
"rio con personas", // NB: "river with people"
"Uma mulher chorando", // NB: "A woman crying"
"Una mujer con piel de dragon, y cuernos", // NB: "A woman with dragon skin and horns"
"Una mujer japonesa embarazada pero más femenina con cabello blanco, ojos azules, un traje rojo de artes marciales de cuerpo completo, una cicatriz en la mejilla, con un efecto de nieve en el fondo de ciudad, en estilo de dibujo manga",
"Una persona con un brazo arancado caminando", // NB: A person with a torn arm walking
]);
}
fn assert_prompts_are_sfw(prompts: &[&str]) {
for prompt in prompts {
let classification = classify_prompt(prompt);
asserting(&format!("is abusive - prompt: {}", prompt))
.that(&classification.is_abusive())
.is_equal_to(false);
asserting(&format!("is child abuse - prompt: {}", prompt))
.that(&classification.is_child_abuse())
.is_equal_to(false);
}
}
@@ -0,0 +1,37 @@
//! "Spicy" prompts are not necessarily NSFW
//! Currently these get the "NSFW" treatment, but we may wish to engineer the negative prompt
//! to steer the image generation away from NSFW territory.
//!
use speculoos::asserting;
use classify_text::classify_prompt::classify_prompt;
#[test]
fn permissible_sexy_tests() {
// These classify as NSFW now, but should be in a category of their own
assert_prompts_are_spicy(&[
"(masterpiece), best quality, expressive eyes, perfect face, 1girl, anime, plump, solo, headphones, blonde hair, breasts, swimsuit, twintails, bikini, huge breasts, orange eyes, white bikini, short hair, blush, navel, smile, open mouth, looking at viewer, cleavage, barefoot, standing, render, full body, <lora:tachie:0.7>, massive belly, roundybelly, pregnant belly, <lora:BGV5EX:0.7>, animated",
"Beautiful woman, seductive pose, full body visible, leotard, heels",
"sexy,seducing,long hair,high heels,KTV,stockings",
"short blonde hair , side shaved. bunnygirl, corset, bunny ears, black eyeshadow, red lipstick, full body, sexy. realistic. ",
"short green and black hair , side shaved, black lingerie, black eyeshadow, dark lipstick, full body, smirk",
]);
}
fn assert_prompts_are_spicy(prompts: &[&str]) {
for prompt in prompts {
let classification = classify_prompt(prompt);
asserting(&format!("is abusive - prompt: {}", prompt))
.that(&classification.is_abusive())
.is_equal_to(false);
asserting(&format!("is child abuse - prompt: {}", prompt))
.that(&classification.is_child_abuse())
.is_equal_to(false);
// TODO: This test should be made to report `false`
//asserting(&format!("is nsfw - prompt: {}", prompt))
// .that(&classification.prompt_references_sex)
// .is_equal_to(true);
}
}
@@ -1,6 +1,7 @@
use std::collections::HashMap;
use tokens::tokens::media_files::MediaFileToken;
use tokens::tokens::model_weights::ModelWeightToken;
use enums::common::visibility::Visibility;
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(untagged)]
@@ -37,8 +38,25 @@ pub struct WorkflowArgs {
#[serde(skip_serializing_if = "Option::is_none")]
pub maybe_output_path: Option<String>,
// Upload information
// google drive link for uploads
#[serde(rename = "gd")]
#[serde(skip_serializing_if = "Option::is_none")]
pub maybe_google_drive_link: Option<String>
pub maybe_google_drive_link: Option<String>,
#[serde(rename = "ti")]
#[serde(skip_serializing_if = "Option::is_none")]
pub maybe_title: Option<String>,
#[serde(rename = "de")]
#[serde(skip_serializing_if = "Option::is_none")]
pub maybe_description: Option<String>,
#[serde(rename = "ch")]
#[serde(skip_serializing_if = "Option::is_none")]
pub maybe_commit_hash: Option<String>,
#[serde(rename = "cv")]
#[serde(skip_serializing_if = "Option::is_none")]
pub creator_visibility: Option<Visibility>,
}
@@ -11,7 +11,7 @@ use tokens::tokens::users::UserToken;
use crate::helpers::boolean_converters::i8_to_bool;
// TODO: Make non-`Serialize` and make the HTTP endpoints do the work
#[derive(Serialize, Deserialize, Clone)]
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct UserProfileResult {
pub user_token: UserToken,
pub username: String,
@@ -47,7 +47,7 @@ pub struct UserProfileResult {
}
// TODO: Make non-`Serialize` and make the HTTP endpoints do the work
#[derive(Serialize, Deserialize, Clone)]
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct UserProfileModeratorFields {
pub is_banned: bool,
pub maybe_mod_comments: Option<String>,
@@ -2,6 +2,7 @@ use std::time::Instant;
use anyhow::anyhow;
use log::{error, info, warn};
use opentelemetry::KeyValue as OtelAttribute;
use r2d2_redis::redis::Commands;
use enums::by_table::generic_inference_jobs::inference_category::InferenceCategory;
@@ -23,7 +24,6 @@ use crate::job::job_types::vc::process_single_vc_job::process_single_vc_job;
use crate::job::job_types::videofilter::process_single_vf_job::process_single_vf_job;
use crate::job::job_types::workflow::process_single_wf_job::process_single_wf_job;
use crate::job_dependencies::JobDependencies;
use opentelemetry::{global as otel, KeyValue as OtelAttribute, metrics::Unit};
pub async fn process_single_job(
job_dependencies: &JobDependencies,
@@ -338,7 +338,7 @@ pub async fn process_job(args: SadTalkerProcessJobArgs<'_>) -> Result<JobSuccess
.send()
.await;
match (thumbnail_task_result) {
match thumbnail_task_result {
Ok(thumbnail_task) => {
debug!("Thumbnail task created: {:?}", thumbnail_task);
},
@@ -135,7 +135,7 @@ pub async fn process_job(args: ComfyProcessJobArgs<'_>) -> Result<JobSuccessResu
Ok(())
}
let comfy_deps = match args.job_dependencies.job.job_specific_dependencies.maybe_comfy_ui_dependencies {
let _comfy_deps = match args.job_dependencies.job.job_specific_dependencies.maybe_comfy_ui_dependencies {
Some(ref deps) => deps,
None => return Err(ProcessSingleJobError::from(anyhow!("no comfy deps"))),
};
@@ -32,6 +32,35 @@ pub async fn upload_prompt(args: ComfyProcessJobArgs<'_>) -> Result<JobSuccessRe
let wf_args = get_workflow_args_from_job(&args).await?;
let title = match wf_args.maybe_title {
Some(val) => {
val
},
None => { "".to_string() }
};
let description = match wf_args.maybe_description {
Some(val) => {
val
},
None => { "".to_string() }
};
let commit_hash = match wf_args.maybe_commit_hash {
Some(val) => {
val
},
None => { "".to_string() }
};
let visibility = match wf_args.creator_visibility {
Some(val) => {
val
},
None => { Visibility::Public }
};
let file_name = "workflow.json";
let download_script = easyenv::get_env_string_or_default(
@@ -107,19 +136,19 @@ pub async fn upload_prompt(args: ComfyProcessJobArgs<'_>) -> Result<JobSuccessRe
token: &model_weight_token,
weights_type: WeightsType::ComfyUi,
weights_category: WeightsCategory::WorkflowConfig,
title: "".parse().unwrap(),
title: title,
maybe_cover_image_media_file_token: job.maybe_cover_image_media_file_token.clone(),
maybe_description_markdown: Some("".parse().unwrap()),
maybe_description_markdown: Some(description),
maybe_description_rendered_html: None,
creator_user_token: Some(&creator_user_token),
creator_ip_address,
creator_set_visibility: Visibility::Private,
creator_set_visibility:visibility,
maybe_last_update_user_token: None,
original_download_url: Some(download_url),
original_filename: None,
// file_size_bytes: metadata.file_size_bytes, // TODO(bt,2024-02-03): We need to migrate the column to be BIGINT
file_size_bytes: 0,
file_checksum_sha2: metadata.sha256_checksum,
file_checksum_sha2: commit_hash,
public_bucket_hash: bucket_details.object_hash,
maybe_public_bucket_prefix: Some(bucket_details.prefix),
maybe_public_bucket_extension:Some(bucket_details.suffix),
@@ -1,14 +1,13 @@
use errors::anyhow;
use mysql_queries::{payloads::generic_inference_args::{generic_inference_args::PolymorphicInferenceArgs, workflow_payload::WorkflowArgs}, queries::generic_inference::job::list_available_generic_inference_jobs::AvailableInferenceJob};
use mysql_queries::payloads::generic_inference_args::generic_inference_args::GenericInferenceArgs;
use crate::job::job_loop::job_success_result::JobSuccessResult;
use crate::job::job_loop::process_single_job_error::ProcessSingleJobError;
use crate::job::job_types::workflow::comfy_ui;
use crate::job::job_types::workflow::comfy_ui::process_job::ComfyProcessJobArgs;
use crate::job_dependencies::JobDependencies;
use mysql_queries::payloads::generic_inference_args::generic_inference_args::PolymorphicInferenceArgs::Cu;
use errors::{anyhow};
use mysql_queries::payloads::generic_inference_args::generic_inference_args::GenericInferenceArgs;
pub async fn get_workflow_args_from_job(
args: &ComfyProcessJobArgs<'_>
) -> Result<WorkflowArgs, ProcessSingleJobError> {
@@ -50,7 +49,7 @@ pub async fn process_single_wf_job(job_dependencies: &JobDependencies,
// pass link in and download
match workflow_args.maybe_google_drive_link {
Some(link) => {
Some(_link) => {
let job_success_result = comfy_ui::upload_prompt::upload_prompt(
args
).await?;
@@ -41,7 +41,7 @@ pub fn init_otel_metrics_pipeline(
match opentelemetry::global::set_error_handler(|error| {
warn!("OpenTelemetry error: {}", error);
}) {
(err) => {
err => {
warn!("Failed to set OpenTelemetry error handler: {:?}", err);
}
};
@@ -23,6 +23,7 @@ path = "src/api_doc_server.rs"
actix_helpers = { path = "../../../lib/actix_helpers" }
bootstrap = { path = "../../../lib/bootstrap" }
buckets = { path = "../../../_types/buckets" }
classify_text = { path = "../../../lib/classify_text" }
cloud_storage = { path = "../../../lib/api_clients/cloud_storage" }
collections = { path = "../../../lib/collections" }
composite_identifiers = { path = "../../../_types/composite_identifiers" }
@@ -41,7 +41,6 @@ use crate::http_server::endpoints::image_gen::prompt_enrichment::enrich_prompt;
use crate::http_server::web_utils::response_error_helpers::to_simple_json_error;
use crate::server_state::ServerState;
use crate::util::allowed_studio_access::allowed_studio_access;
use crate::util::classify_prompt::classify_prompt;
use crate::validations::validate_idempotency_token_format::validate_idempotency_token_format;
/// This is the number of images (batch size) to generate for each request.
@@ -1,6 +1,7 @@
use crate::http_server::endpoints::image_gen::enqueue_image_generation::{EnqueueImageGenRequest, EnqueueImageGenRequestError};
use classify_text::classify_prompt::classify_prompt;
use crate::http_server::endpoints::image_gen::enqueue_image_generation::EnqueueImageGenRequest;
use crate::http_server::endpoints::image_gen::replacement_prompts::get_replacement_prompt;
use crate::util::classify_prompt::classify_prompt;
pub struct EnrichedPrompts {
pub positive_prompt: String,
@@ -211,7 +211,11 @@ pub async fn enqueue_comfy_ui_handler(
maybe_workflow_config: request.maybe_workflow_config.clone(),
maybe_input_file: request.maybe_input_file.clone(),
maybe_output_path: request.maybe_output_path.clone(),
maybe_google_drive_link: None
maybe_google_drive_link: None,
maybe_title: None,
maybe_commit_hash:None,
maybe_description:None,
creator_visibility:Some(set_visibility)
};
info!("Creating ComfyUI job record...");
@@ -14,11 +14,11 @@ use serde::Deserialize;
use serde::Serialize;
use utoipa::ToSchema;
use web::Data;
use enums::common::visibility::Visibility;
use enums::by_table::generic_inference_jobs::inference_category::InferenceCategory;
use enums::by_table::generic_inference_jobs::inference_job_type::InferenceJobType;
use enums::by_table::generic_inference_jobs::inference_model_type::InferenceModelType;
use enums::common::visibility::Visibility;
use http_server_common::request::get_request_header_optional::get_request_header_optional;
use http_server_common::request::get_request_ip::get_request_ip;
use mysql_queries::payloads::generic_inference_args::generic_inference_args::{
@@ -39,11 +39,8 @@ use tokens::tokens::users::UserToken;
use crate::configs::plans::get_correct_plan_for_session::get_correct_plan_for_session;
use crate::http_server::web_utils::response_error_helpers::to_simple_json_error;
use crate::server_state::ServerState;
use crate::util::allowed_studio_access::allowed_studio_access;
use crate::util::classify_prompt::classify_prompt;
use crate::validations::validate_idempotency_token_format::validate_idempotency_token_format;
/// Debug requests can get routed to special "debug-only" workers, which can
@@ -59,8 +56,12 @@ const ROUTING_TAG_HEADER_NAME: &str = "routing-tag";
pub struct EnqueueWorkFlowRequest {
pub uuid_idempotency_token: String,
pub google_drive_link: String,
pub title: String,
pub description: String,
pub commit_hash: String,
// Optional cover image for workflow.
pub maybe_cover_image_media_file_token: Option<MediaFileToken>,
pub creator_set_visibility:Option<Visibility>
}
#[derive(Serialize, ToSchema)]
@@ -121,8 +122,22 @@ pub async fn enqueue_workflow_upload_request(
return Err(EnqueueWorkFlowRequestError::BadInput("Missing Google Drive Link".to_string()));
}
if request.title.is_empty() {
return Err(EnqueueWorkFlowRequestError::BadInput("Missing Title".to_string()));
}
if request.description.is_empty() {
return Err(EnqueueWorkFlowRequestError::BadInput("Missing Description".to_string()));
}
if request.commit_hash.is_empty() {
return Err(EnqueueWorkFlowRequestError::BadInput("Missing commit hash".to_string()));
}
let mut maybe_user_token: Option<UserToken> = None;
let visbility = enums::common::visibility::Visibility::Private;
let visibility = request.creator_set_visibility
.unwrap_or(Visibility::Private);
let mut mysql_connection = server_state.mysql_pool.acquire().await.map_err(|err| {
warn!("MySql pool error: {:?}", err);
@@ -204,7 +219,10 @@ pub async fn enqueue_workflow_upload_request(
// ==================== INFERENCE ARGS ==================== //
let google_drive_link = request.google_drive_link.clone();
let title = request.title.clone();
let description = request.description.clone();
let commit_hash = request.commit_hash.clone();
let inference_args = WorkflowArgs {
maybe_sd_model: None,
maybe_lora_model: None,
@@ -212,7 +230,11 @@ pub async fn enqueue_workflow_upload_request(
maybe_workflow_config: None,
maybe_input_file: None,
maybe_output_path: None,
maybe_google_drive_link: Some(google_drive_link)
maybe_google_drive_link: Some(google_drive_link),
maybe_title: Some(title),
maybe_description: Some(description),
maybe_commit_hash: Some(commit_hash),
creator_visibility: Some(visibility),
};
// create the inference args here
@@ -1,601 +0,0 @@
use std::collections::HashSet;
use once_cell::sync::Lazy;
use regex::Regex;
const CHILD_TERMS : &str = include_str!("../../../../../../includes/binary_includes/dictionary_children_terms.txt");
const SEX_TERMS : &str = include_str!("../../../../../../includes/binary_includes/dictionary_sex_terms.txt");
/// Classification of a text prompt
pub struct PromptClassification {
pub prompt_references_children: bool,
pub prompt_references_sex: bool,
pub prompt_references_violence: bool,
pub prompt_references_racism: bool,
}
impl PromptClassification {
pub fn is_abusive(&self) -> bool {
self.prompt_references_racism || self.is_child_abuse()
}
pub fn is_child_abuse(&self) -> bool {
self.prompt_references_children && ( self.prompt_references_sex || self.prompt_references_violence )
}
}
pub fn classify_prompt(text_prompt: &str) -> PromptClassification {
let prompt_tokens_alpha = SPACES_REGEX.split(text_prompt)
.map(|term| term.trim().to_lowercase())
.map(|term| term.chars().filter(|c| c.is_alphanumeric()).collect::<String>())
.collect::<Vec<String>>();
let mut prompt_tokens_lower = SPACES_REGEX.split(text_prompt)
.map(|term| term.trim().to_lowercase())
.collect::<Vec<String>>();
let mut prompt_tokens= Vec::with_capacity(prompt_tokens_alpha.len() + prompt_tokens_lower.len());
prompt_tokens.extend(prompt_tokens_alpha);
prompt_tokens.extend(prompt_tokens_lower);
let prompt_references_children = references_children(&prompt_tokens);
let prompt_references_sex = references_sex(&prompt_tokens);
let prompt_references_violence = references_violence(&prompt_tokens);
let prompt_references_racism = references_racism(&prompt_tokens);
PromptClassification {
prompt_references_children,
prompt_references_sex,
prompt_references_violence,
prompt_references_racism,
}
}
fn references_children(prompt_tokens: &[String]) -> bool {
static CHILD_TERM_DICTIONARY : Lazy<HashSet<String>> = Lazy::new(|| {
CHILD_TERMS.lines()
.map(|line| line.trim().to_lowercase())
.filter(|line| !line.is_empty() && !line.starts_with("#"))
.collect::<HashSet<String>>()
});
prompt_tokens.iter().any(|term| CHILD_TERM_DICTIONARY.contains(term))
}
fn references_sex(prompt_tokens: &[String]) -> bool {
static SEX_TERM_DICTIONARY : Lazy<HashSet<String>> = Lazy::new(|| {
SEX_TERMS.lines()
.map(|line| line.trim().to_lowercase())
.filter(|line| !line.is_empty() && !line.starts_with("#"))
.collect::<HashSet<String>>()
});
prompt_tokens.iter().any(|term| SEX_TERM_DICTIONARY.contains(term))
}
fn references_violence(prompt_tokens: &[String]) -> bool {
// TODO(bt,2024-02-19): Need to implement
false
}
fn references_racism(prompt_tokens: &[String]) -> bool {
// TODO(bt,2024-02-19): Need to implement
false
}
static SPACES_REGEX : Lazy<Regex> = Lazy::new(|| {
Regex::new(r"[\s,;]+").expect("regex should be valid")
});
#[cfg(test)]
mod tests {
use crate::util::classify_prompt::{classify_prompt, SPACES_REGEX};
use speculoos::asserting;
fn regex_split(text: &str) -> Vec<&str> {
SPACES_REGEX.split(text).map(|s| s.trim()).collect()
}
#[test]
fn test_regex() {
asserting("regex works")
.that(&regex_split("foo,bar,baz"))
.is_equal_to(vec!["foo", "bar", "baz"]);
asserting("regex works")
.that(&regex_split("foo;bar;baz"))
.is_equal_to(vec!["foo", "bar", "baz"]);
asserting("regex works")
.that(&regex_split("foo, bar, baz"))
.is_equal_to(vec!["foo", "bar", "baz"]);
asserting("regex works")
.that(&regex_split("foo\n\nbar baz"))
.is_equal_to(vec!["foo", "bar", "baz"]);
}
#[test]
fn test_bad_user_csam_prompts() {
// These were actual prompts from several bad actors (usernames "ElFabro17", "markiwrench", et al.)
// These need to be blocked.
let prompts = [
// TODO: Category for hate:
// adolf hitler having tea with joe biden
// niggers
// ben shapiro driving a car on top of multiple children
// TODO: We need to adjust the filter to catch these.
//"A beautiful blonde girl of 13 years old with blue eyes.She's naked ,showing her private parts ",
//"A blonde girl of 13 years old with blue eyes.She's naked ,showing her private parts (no showing her face)",
//"A blonde girl of 13 years old with blue eyes.She's naked ,showing her private parts. I'm fucking her now",
//"A blonde girl of 13 years old with blue eyes.She's naked ,showing her tits and ass",
//"12 year old girl with big tits with an innocent look",
//"15 year old girl with big tits and innocent look",
//"Little girl pussy cum dripping out",
// Is this one illegal? Is teen = 18?
//"Teen girl. Corpo intero. Nuda bellissima. Tette. Italian teen girl.", // NB: Whole body. Beautiful nude. Tits.
//"human grimreper boy hot", // NB: This also seems really bad, but I don't think they meant it to be inappropriate as none of their queries are porn
"A young girl how eats her classmates with her butt-hole",
"Boy jerking off",
"Chica con semen rosado encima", // NB: Chica = teen girl
"child porn",
"goth loli skirt, pink p@nties. butt",
"loli butt panties",
"loli n@ked",
"loli on knees, back side view, feet, butt, pink panties on",
"loli on knees, back side view, feet, butt, pink panties on.",
"loli p00ping in toilet",
"loli sitting on toilet",
"loli skirt cute upskirt pink panties",
"loli wearing black socks with whitish greyish stripes on the bottom of the socks. loli with legs and feet up showing off the dirty socks",
"loli",
"male kid in underwear, big butt",
"Niño teniendo sexo y diciendo que rico", // NB: Nino = young child
"young boy in underwear, front view, bulge in underwear",
"young boy p3nis",
"young girl butt, pooping on bed",
"Young girls vore boys",
"young loli butt, 13 years old",
];
for prompt in prompts {
let classification = classify_prompt(prompt);
asserting(&format!("is abusive - prompt: {}", prompt))
.that(&classification.is_abusive())
.is_equal_to(true);
asserting(&format!("is child abuse - prompt: {}", prompt))
.that(&classification.is_child_abuse())
.is_equal_to(true);
}
}
#[test]
fn borderline_cases() {
// These are actual user prompts that use borderline language, but are safe
let prompts = [
"4 year old girl, wearing a headset microphone",
"6 year old girl",
"a girl wearing a frost free puffer vest while sleeping with her mouth open",
"child's drawing of a fox",
"Create an Korean child playing with a polish child on play ground",
"Demon boy teen that is cute hot in a black suit and tie",
"girl in a strapless dress in a feald of flower",
"two girls kissing in bed",
// What about this? Innocent query, but bad results:
// "1 girl,pretty face,white stocking,swimmingsuit,duck sitting,school",
];
for prompt in prompts {
let classification = classify_prompt(prompt);
asserting(&format!("is abusive - prompt: {}", prompt))
.that(&classification.is_abusive())
.is_equal_to(false);
asserting(&format!("is child abuse - prompt: {}", prompt))
.that(&classification.is_child_abuse())
.is_equal_to(false);
}
}
#[test]
fn permissible_nsfw() {
// These are actual user prompts used as test cases
// We're allowing NSFW for now, but this may change
// These may not be underage or illegal
let prompts = [
// TODO: Is the following abusive?
//"1girl, nude, large breasts",
"((san from princess mononoke)), ((having sex)), (cowgirl position), ((penis in vagina)), ((short ginger hair)), (crop top),(lifting top), (showing tits), (nipples), ((small breast)), ((muscular)), hotelroom, ((drunk:1.2)), ((no panties)), (kneeling over a man), (((gaping pussy))), ((skirt)), horny, (in heat), (furrowed brow), (angry expression), ((sweat:1.2)), absurd resolution, masterpiece, ultra-detailed, vivid, intricate details, (((photorealistic))), visceral experience, (dynamic angle), (sharp focus), perfect face, looking down, perfect nipples, perfect feet",
"2B. white swimsuit, butt, black eyeliner, white droplets on ass, sunscreen bottle in hand",
"3 naked Latina women in a swimming pool",
"a gay black guy with nipples and has undies on",
"A giant woman living in a city she loves putting people in her butthole",
"A girl who used her ass to eat vore",
"a poop dressed as a princess",
"A pornstar getting semen on her face stylized in 4k, realism, high resolution and NSFW.",
"A realistic photo of pokemon fully nude",
"A sexy anime girl with no clothes or bra and full body",
"A sexy anime girl with no clothes or bra",
"a woman having sex with a man",
"ahh daddy ahh fuck me",
"Anal vore",
"Anime Girl (NSFW)",
"anime girl getting fucked in the pussy",
"Anime girl moaning cum naked",
"anime girl naked moaning lots of cum",
"anime girl pussy getting fucked",
"Anime girls squiring from thier vaginas",
"anime pussy getting fucked by a dick",
"Anime woman jerking a man off",
"Anime woman showing her boobs",
"Anime woman with penis between her boobs and cum on her boobs",
"anthropomorphic poop princess in a castle bedroom",
"Beautiful woman, seductive pose, full body visible, leotard, heels",
"Belle Delphine's pussy squirting",
"Big boobs having sex ",
"Big boobs",
"Big breast",
"Big naked but girl with huge eyes on each cheek",
"Big nude boobs",
"Big perfect white boobs",
"Bikini girls tanning naked at the beach",
"black women, cum inflation, cute, sex, big boobs, big ass",
"Blowjob and cum",
"COCK COCK AND DICK BALLS HEELLPPPP",
"Creampie (NSFW)",
"Cute, is a latina, and naked",
"dick pic",
"Full body naked big boobs open pussy moaning anime girl",
"Full body, ultra giant boobs, ultra thicc, naked, facing viewer, in the sewers",
"Futanari holding her penis in one hand",
"Futanari",
"Generate a beautiful picture of a naked woman.",
"Girl desnude",
"girl in a tight thong. NSFW, k, realistic, photorealsitic.",
"Girl naked, masturbating, ahegao, squatting",
"Girl naked, masturbating, ahegao, squatting, ultra realistic",
"Girl nude, pink hair, pink pussy ",
"Girl with big ass in white pants shake her ass",
"Girl with big ass sits on chair on tight skirt",
"Girl with big boobs and small bro touch her boobs and make a lusty eye contact",
"guy shitting in the toilet",
"hentai",
"Hitomi Tanaka with nude big boobs and pussy open",
"hot and pretty faced naked girls",
"hot cat girl pussy getting fucked by a dick",
"hot gaping pussy and ass legs wide open",
"hot girl with a tight vagina",
"Hot girl with big boobs in white dress",
"hot hentai fuck",
"Hot naked anime maids",
"hot naked girl",
"hot white girl gaping pussy and ass close up in the camera",
"Hottest anime girl spreading apart her vagina",
"Huge breasts naked anime girl",
"HUGEEEEEEEE BOOBS, HUGEEEEEEEEEEEE BUTT, a latina, has white hair, OVERLY CUTE, NAKED",
"huniepop 3",
"Huniepop fairy in pink lingerie",
"Huniepop",
"hyper boobs, boobjob, cum, smaller male, black girl, white man, paizuri, sex, black female, inflation, expansion, cum inflation, dialouge",
"hyper boobs, boobjob, cum, smaller male, black girl, white man, paizuri, sex,",
"inflation, water inflation, oral, hose in mouth, water, orgasm, english dialouge, solo female, belly expansion, expansion, cum, inflation, water inflation, water inflation, water inflation, water inflation, human girl, orgasm, human girl inflation",
"intercource between man and women",
"Iran blowjob girl",
"Iranian girl suck dick",
"Kyu Sugardust from the game Huniepop",
"leaked sex tape",
"Lesbians in a park nude",
"LOOK AT MY MASSIVE DICK",
"Man and woman having sex",
"Man cumming on a woman’s ass",
"Mikasa Blowjob in car with big ass",
"Mikasa with big boobs",
"minecraft creeper having sex in the pussy with another minecraft creeper",
"moaning anime girl",
"monkey standing up with huge black penis hanging down",
"monkey with a huge black penis",
"naked anime girl",
"Naked Gardevoir",
"Naked girl showing her creampied vagina",
"Naked girl showing her cum covered vagina",
"naked girl sitting in chair",
"Naked girl spreading her pussy",
"Naked woman giving birth",
"Naked woman",
"naked women fingring",
"naked",
"Naked, cute, hot, nice boobs",
"Nude woman big boobs",
"Nude woman sucking dick",
"Nude woman",
"nutsack",
"Penis",
"porno",
"pov your having sex with a naked anime girl",
"Purple haired, big breasted, naked, purple eyes, blushing",
"Purple haired, big breasted, naked, purple eyes, blushing, embarrassed, full body",
"Purple haired, naked, woman, naked, big breasts, blushing",
"pussy",
"raven team leader from fortnite getting fucked in the pussy",
"Santa jerking his penis in his workshop",
"Santa wanking his meat in his workshop",
"sex",
"Sexy milf with big ass sits on big dick anal",
"Sexy woman, ultra giant boobs, ultra thicc, facing viewer, standing, arms behind back, naked, in the mall",
"sexy,high heels,long brown hair,big breast,KTV,seducing",
"sexy,seducing,long hair,high heels,KTV,stockings",
"The Love Fairy Kyu Sugardust from the game Huniepop",
"The wettest vagina ",
"titfuck, huge boobs",
"Vagina cuming",
"Vagina hole",
"Vagina squirting",
"vagina",
"Vegeta female version with big boobs from Dragonball",
"Wet vagina after cream pie",
"woman pleasuring herself stylized in photorealist, 4k, high-res, and NSFW (Nude)",
"woman sucking dick.",
"Woman with huge tits desnude",
"Woman with huge tits",
"Women with boobs too big for her own good",
"yuki tsukumo woman face feet five toes anime two feet normal sized feet tall body two legs 5,9 watery substance on feet feet in air sitting by bed blonde,long hair,grinning no clothes 35 year old big boobs long toes legs up in the air inviting to pussy two legs up in the air exposed nipples and breasts dripping wet pussy black tanktop blue jeans lipstick holding legs in the air moaning\n\nshowing her soles to the camera",
"Zayne and Nicky gay sex",
// Non-english
"Mujer rubia de ojos celestes y senos grandes en traje de baño rojo caminando por una playa del caribe",
"Mujer rubia desnuda tomando sol en la ppaya",
];
for prompt in prompts {
let classification = classify_prompt(prompt);
asserting(&format!("is abusive - prompt: {}", prompt))
.that(&classification.is_abusive())
.is_equal_to(false);
asserting(&format!("is child abuse - prompt: {}", prompt))
.that(&classification.is_child_abuse())
.is_equal_to(false);
asserting(&format!("is nsfw - prompt: {}", prompt))
.that(&classification.prompt_references_sex)
.is_equal_to(true);
}
}
#[test]
fn okay_cases() {
// These are actual user prompts that use borderline language, but are safe
// Eventually we'll want to classify prompts that are sufficiently engineered vs prompts that need enrichment.
let prompts = [
"(masterpiece, best quality), 1girl, blonde hair, red dress, pantyhose, cute face, blush",
"***botw style*** a lifeguard wearing a red one piece swimsuit",
"***charturnbetalora*** A lifeguard wearing a red one-piece swimsuit",
"1000 coins inside 50 large jars",
"1girl, blue hair, sitting on top of a table, smile",
"1girl, purple hair, school uniform, smug, looking at viewer, heterochromia",
"1girl, red hair, city streets",
"1girl, schoolgirl, blue hair, eating icecream",
"1girl, steampunk, holding a wrench",
"2 titans are looking at the sunset",
"2d hand drawn disney princess covered in chocolate, peanuts and corn",
"2Pac in today's world.",
"3 dancing people in a concert",
"A 12 ft tall terrifying shadow analog horror demon",
"A badger in an explorer's hat and a fawn",
"A boy eating a ice cream at the palace",
"A boy, yellow short hair, blue eyes a star in it, hikaru kamiki",
"A crowd of 100 people looking at the camera",
"A cute pug with an owner",
"A cute small fox in the woods",
"A female inkling wearing a pink one-piece swimsuit",
"A giant bee and a small dog",
"A girl from Robotech universe, SDF1 mecha superdimensional fortess",
"A happy girl in a park",
"a kid crying for his ice cream in the floor, the seller is laughing in his car",
"a little Teddy throwing a blue ball",
"A little Unicorn playing in the Sand",
"A man is happy at the press conference",
"A man selling rotten milk in the park",
"a man that is also a car",
"A real life pikachu being walked like a dog.",
"A robotic girl with blue eyes.",
"A shark getting attacked by a vending machine.",
"A white cat, standing on its legs and holding a diamond sword.",
"A woman of KRON4",
"ai hoshino sleeping",
"Alastor from Hazbin Hotel, drinking out of a cup",
"Albert Einstein",
"Alex Jones wearing a Minecraft styled duck hat",
"An adorable chubby fawn laying on a patch of grass smiling",
"An eboost monkey",
"An extreme close-up of an gray-haired man with a beard in his 60s, he is deep in thought pondering the history of the universe as he sits at a cafe in Paris, his eyes focus on people offscreen as they walk as he sits mostly motionless, he is dressed in a wool coat suit coat with a button-down shirt , he wears a brown beret and glasses and has a very professorial appearance, and the end he offers a subtle closed-mouth smile as if he found the answer to the mystery of life, the lighting is very cinematic with the golden light and the Parisian streets and city in the background, depth of field, cinematic 35mm film.",
"an image of kojiro hiuga kicking tsubasa osora in the stomach",
"an island in the sun with surver and chamäleon",
"Animated character in the style of VivziePop",
"Anime girl with pink hair and blue eyes with green eyes with a kawaii outfit reading in her bedroom full of posters.",
"anime, hand-drawn and cel animation techniques, iron man from marvel, natural design, beautifully rendered and expressive rich colors, vibrant pastel colors, imaginative and fantastical landscapes, sharp attention to detail, realism and a strong sense of nostalgia and warmth, sharp attention to small details and textures, fantastical creatures, settings, depth and emotions emphasized and accentuated by lighting and shading, extremely high quality, incredibly high finite definition, high resolution, hand-drawn and cel animation techniques",
"Archaic warriors on horseback kidnap Sabine maidens",
"Baldi, showing his anger by slapping with his ruler.",
"Batman in iran",
"Beautiful Woman with blue hair and green eyes dressed kawaii dress smiling wearing cat ears and tail.",
"Big booty joe",
"Black and white image of a motor cycle in the year 1955",
"Blue apple",
"boy demon black hair wereind a shirt and shorts hands in pokets",
"Boy named axel davidson has a big belly and a girl called macey steward holds his belly in the sunset",
"boy with black and blue hair in a red shirt and jeans happy",
"cary entity floating menacingly, with many floating eyes and red flesh tentacles and spikes coming out of it, with many mouths with very spiky teeth.",
"Charlie Morningstar from Hazbin Hotel",
"Clark Gable smiling in a nineteen twenties nightclub.",
"Classic painting of 2 brown rabbits with white spots wearing overalls and a dress, carrying basket of carrots, outdoors, Monet",
"crab human man model realistic",
"Cute White Shepherd Dog, Standing, Blue Pants, Blue Shoes",
"Cute Wolf, Blue cap, Grey Fur, White Cream, Blue Tracksuit, Blue Pants, Wolf Tail, Red Shoes",
"Cute Wolf, Blue cap, Light grey fur, Blue Tracksuit, Blue pants, Red shoes, wolf tail",
"Cute, Muscular wolf, Male, Blue cap, Light grey fur, Blue Tracksuit, Red shoes, Blue pants, Wolf tail, Smiling",
"dead girl in a white dress with blood comeing frome her head in an abendend hospitel",
"demon girl with white hair light blue eyes with red horns and a tail in a castel",
"Dog with horns",
"Doki Doki Literature club",
"Donald Duck, riding on a yellow submarine",
"Donald Trump riding a t-rex while holding a gun with American flags in the background",
"Dragon ball",
"drone",
"Dwight D. Eisenhower, looking at the camera, realistic, high quality",
"Eazy-E wearing a Cuban Link, Rolex, and white Nike's with a AR-15 in his left hand.",
"Elsa Plush Doll!",
"Engineer Team Fortress 2",
"Eraser BFDI drunk",
"Eren Jaeger vs titans",
"Farmers with big Harvester machines",
"female, witch, holding flintlock pistol",
"Foxdog, Wolfdog",
"Foxy from five nights at Freddy’s eating a burger",
"freddy fazbear watching a movie",
"Friendly Monsters with long hair in front of mount fujii",
"Funny looking feff",
"Furry",
"Generate what would Eazy-E would look like today",
"Giant anime hamburger attacking Los Angeles",
"Giant girl baby destroying the city",
"Giantess acting like titan on attack on titan",
"girl hair pink , dance , japan",
"girl in a pink sweter and jeans with pink horns looking window with durtyblond hair saprized",
"girl in a prom dress crying in the rain",
"girl siting in a light blue dress",
"girl teen in a dress with horns and a tail in a feald of flowers",
"girl with bloned hair wering a light blue dress whell drawing with a boy with black hair wering a red shirt siting nex to her",
"girl with horns and a tail that has durdyblond hair wereing a pink sweter and riped jeans",
"girl with lightblue hair cute",
"girl with white hair and lifght blue eyes in a light blue dress with horns & a tail in a feld of flowers with a human boy",
"Glamorous white gold and pink themed room",
"Gogeta eating a chili dog",
"Grimreper human boy",
"Grover, Sesame Street",
"Guy Hamdon/SheZow (SheZow form) riding a motorcycle in a highway, anime style",
"Hank Hill in armor closeup",
"Has short yellow hair, blue eyes a star in it, Teenager",
"Hatsune Miku",
"Hexagon globe of the world",
"Hey what is up?",
"HopeScope Wedding Plush Doll",
"Horse running on a race track",
"I really tall old man. He is at least 13 feet tall, and with his familiy in a family photo. The next tallest member is 4 foot, so he is MASSIVE. He looks like a slitherman, but white old man",
"I’m a feff",
"James Dean smiling wearing a cowboy hat.",
"Joe Biden flying as Superman",
"Jolyne Cujoh flying on top of the apple tree",
"Kanye West and Jay-Z in Paris, France at the Eiffel Tower.",
"kawaii anime frog",
"kinda bug and kinda snack.",
"kirby fighting plankton",
"LGBTQ Appreciation",
"Lightning mcqueen from pixar cars but as an anime",
"Link",
"lovander palworld",
"magical animal",
"Maid with the prince standing next to her",
"Make Huggy Wuggy From Poppy Playtime: Chapter 1.",
"Man eating soup at a bagel shop on a sunny day",
"Man standing on the edge of a cliff",
"Man with slicked back black hair and blue eyes with a black puppy while smiling.",
"mank walking",
"Maroon 5 playing on the jimmy fallon stage",
"masterpiece, best quality, white dress, plump,",
"Me",
"monkey playing fortnite",
"morbidly obese blonde woman",
"Nahida from genshin impact",
"Nami from one peice on the beach angry",
"Overweight Taco Bell manager Female. Hair and a bun with braces, smiling. Black uniform with apron.",
"person waving",
"pichu witch pockimon",
"plum (plum landing) at comic con",
"President Putin riding on a Canguru",
"pride flag real flag",
"Princess Peach",
"Rat mixed with sharks",
"Red Dragon",
"red witch goat, purple eyes, round glasses, furry, young",
"REDHEAD",
"Richard Nixon holding a Super Mario lucky block",
"Ronald McDonald holding a taco ",
"Ronald Reagan standing in front of a Lamborghini Aventador, realistic, high quality, heaven",
"Ronald Reagan, looking at the camera, sitting on a duck floatie in a pool, realistic",
"Samurai and geisha riding on a horse",
"Scary, fear, horror, creepypasta, spooky",
"scrap company",
"Serbian musician star named Desingerica with wings and blade in his hands",
"Sexy latin girl in bikini, who's posing and take a pictures.",
"SheZow standing next to Christ the Redeemer, CGI render",
"Shigeru Miyamoto fighting through a crowd of nerds",
"skibidi toilet rizzed up ohio",
"Solid snake eating a hamburger",
"Sonic beats up Mario",
"Sonic meets the Powerpuff Girls",
"Sonic the hedgehog going fast",
"sunnyv2, man working in the office",
"sunnyv2, stunning close-up portrait of a swiss business woman with captivating eyes, background is a bright office",
"The biggest of biggest guys",
"The father in my dreams ! I love my father ! I hug him but... He does't hug me !",
"The healthiest hamburger in all the land",
"The Rock eating rocks.",
"The slimer hugging an eel",
"the wiggles dancing",
"Tifa and aerith. Bikini. Laughing. Squirting sunscreen on each other",
"touhou project, marisa kirisame, forest background",
"Transfur",
"tree people",
"trump riding and hanging out the window in a doge charger with an AR-15 pointed in the air shooting with American flags all over the place.",
"Ultra HD, very high resolution, photorealistic real life picture, very detailed face, wide shot photography of a huge, enormous, extremely muscular, supermassive, obese, good nature, shirtless, handsome 54 year old welsh wrestling-coach taking a selfie with his new iphone, welsh cocky alpha-male, superheavyweight-offseason-bodybuilder body shape, giant supermassive ultrabroad superflabby pointy pectorals, extremamente mamiludo , beer belly, immense massive muscular legs, mamilos enormes hiper-inchados protuberantes, sideburns, extremely high testosterone levels, very masculine face, side-parted tight hair",
"verey fansey dresses",
"Victorian era portrait of an thai woman",
"Wedding Dress",
"White and blue fox furry in a river",
"White German Shepherd, Back View, Tail up",
"white rabbit speaking into studio mic",
"White Shepherd Dog, Green Sport Coat, Blue Pants, Blue Shoes",
"will smith slapping Donald Trump at the Oscars. 4k, realistic",
"Woman with blue hair and green eyes dressed kawaii dress smiling wearing cat ears and tail.",
"Woman with pink and blue hair and green eyes dressed kawaii dress holding a kitten while smiling.",
"World's biggest Mountain Dew Baja Blast",
"XXXTENTACION",
"YOU ARE AWSOME",
"Young John F. Kennedy smoking a cigar in a nineteen forties club.",
"young wizard using magic with hands",
"Young woman with pink and blue hair and green eyes dressed kawaii Japanese singing into a microphone at a concert.",
"Yui and Hideki from Angel Beats holding hands while smiling.",
"Zelda",
"\"Stone Cold\" Steve Austin holding a Big Mac",
// Non-english
"como tan muchacho", // NB: "like such a boy"
"gata gata negra antropomorfica de ojos verdes y peinado bob color blanco peinado blanco vestida con un uniforme escolar japones estilo anime",
"perro volando en las nuves", // NB: Dog flying in the clouds
"rio con personas", // NB: "river with people"
"Uma mulher chorando", // NB: "A woman crying"
"Una mujer con piel de dragon, y cuernos", // NB: "A woman with dragon skin and horns"
"Una mujer japonesa embarazada pero más femenina con cabello blanco, ojos azules, un traje rojo de artes marciales de cuerpo completo, una cicatriz en la mejilla, con un efecto de nieve en el fondo de ciudad, en estilo de dibujo manga",
"Una persona con un brazo arancado caminando", // NB: A person with a torn arm walking
];
for prompt in prompts {
let classification = classify_prompt(prompt);
asserting(&format!("is abusive - prompt: {}", prompt))
.that(&classification.is_abusive())
.is_equal_to(false);
asserting(&format!("is child abuse - prompt: {}", prompt))
.that(&classification.is_child_abuse())
.is_equal_to(false);
}
}
}
@@ -3,4 +3,3 @@ pub mod delete_role_disambiguation;
pub mod encrypted_sort_id;
pub mod placeholder_images;
pub mod troll_user_bans;
pub mod classify_prompt;
@@ -1,5 +1,6 @@
# English
#girl - this seems to be used in non-youth contexts
#1girl - this seems to be used in non-youth contexts
babies
baby
boy
@@ -11,6 +12,7 @@ children
childs
daughter
daughters
jailbait
juvenile
juveniles
kid
@@ -32,6 +34,8 @@ preschool
preschooler
preschoolers
preschools
preteen
preteens
pubescent
pubescents
son
@@ -46,7 +50,6 @@ youngsters
youth
youths
1boy
1girl
1yo
2yo
3yo
@@ -67,4 +70,10 @@ youths
# Spanish
chica
niiña
niña
niño
# Italian
adolescente
giovane
@@ -0,0 +1,129 @@
# These terms should not be allowed for prompts
# A few fake slurs for testing
fakeslur
junglehorse
slurfake
# Nazis
adolf
adolph
hitler
nazi
# Racism
nigger
niggers
# Additional slurs from BibbyBoi 2022-03-04
arabush
aravush
argie
argies
bakra
balija
bamboula
beaner
beaney
bluegum
bogtrotter
boonga
boonie
bootlip
bougnoule
bozgor
buddhahead
bule
bunga
burrhead
carcamano
chankoro
chink
chinks
choc-ice
coon
coonass
dago
dego
dink
dune-coon
dyke
fag
faggot
fags
greaser
groid
guidette
# guido # NB(2022.11.12) - Not a slur for Italian models https://discord.com/channels/963507819011768370/963812062939709471/1041106464867094528
haole
hebe
heeb
hitler
honkey
honkie
hymie
injun
jigaboo
jigarooni
jiggabo
jigger
jijjiboo
kike
kniggers
kyke
maumau
mayomonkey
moolinyan
mulignan
mulignon
neger
negro
negro
negroid
nicc a
nicc er
nicca
niccer
nig
nigar
nigette
niggah
niggar
nigger
niggers
niggertoe
niggress
nigguh
nignig
nignog
nigs
nik-a
nika
nikk-a
nikk-er
ofay
poortagee
rastus
sambo
sheboon
sheeny
sooty
spic
spick
spig
spig
spigotty
spik
tarbaby
whigg-a
whigg-er
whigga
whigger
wigg-er
wigger
wikk-a
wikk-er
wikka
wikker
zigabo
zipperhead
@@ -19,6 +19,8 @@ sexy
squiring
squirting
thong
topless
toplessness
underwear
upskirt
vaginas
@@ -28,10 +30,13 @@ wanking
# Italian
nuda
nudi
nudo
tette
# Spanish
desnuda
pene
desnude
senos
sexo