Compare commits

...
Author SHA1 Message Date
Brandon Thomas c4812e5e3e new table 2025-07-23 16:21:05 -04:00
Brandon Thomas a8ac030d24 migrations 2025-07-23 16:05:49 -04:00
Brandon Thomas 9249a6274d wip prompts migrations 2025-07-23 14:52:42 -04:00
14 changed files with 224 additions and 4 deletions
@@ -0,0 +1,5 @@
-- noinspection SqlDialectInspectionForFile
-- noinspection SqlNoDataSourceInspectionForFile
-- noinspection SqlResolveForFile
ALTER TABLE prompts DROP COLUMN maybe_model_type;
@@ -0,0 +1,6 @@
-- noinspection SqlDialectInspectionForFile
-- noinspection SqlNoDataSourceInspectionForFile
-- noinspection SqlResolveForFile
ALTER TABLE prompts
ADD COLUMN maybe_model_type VARCHAR(32) DEFAULT NULL;
@@ -0,0 +1,5 @@
-- noinspection SqlDialectInspectionForFile
-- noinspection SqlNoDataSourceInspectionForFile
-- noinspection SqlResolveForFile
ALTER TABLE prompts DROP COLUMN maybe_generation_provider;
@@ -0,0 +1,6 @@
-- noinspection SqlDialectInspectionForFile
-- noinspection SqlNoDataSourceInspectionForFile
-- noinspection SqlResolveForFile
ALTER TABLE prompts
ADD COLUMN maybe_generation_provider VARCHAR(16) DEFAULT NULL;
@@ -0,0 +1,5 @@
-- noinspection SqlDialectInspectionForFile
-- noinspection SqlNoDataSourceInspectionForFile
-- noinspection SqlResolveForFile
ALTER TABLE prompts DROP COLUMN maybe_anonymous_visitor_token;
@@ -0,0 +1,6 @@
-- noinspection SqlDialectInspectionForFile
-- noinspection SqlNoDataSourceInspectionForFile
-- noinspection SqlResolveForFile
ALTER TABLE prompts
ADD COLUMN maybe_anonymous_visitor_token VARCHAR(32) DEFAULT NULL;
@@ -0,0 +1,5 @@
-- noinspection SqlDialectInspectionForFile
-- noinspection SqlNoDataSourceInspectionForFile
-- noinspection SqlResolveForFile
DROP TABLE prompt_context_items;
@@ -0,0 +1,30 @@
-- noinspection SqlDialectInspectionForFile
-- noinspection SqlNoDataSourceInspectionForFile
-- noinspection SqlResolveForFile
-- An n:m join table between prompts and media_files.
-- This associates mostly image media files with prompts, though we could conceivably
-- also store 3D sets, etc.
CREATE TABLE prompt_context_items (
-- Not used for anything except replication.
id BIGINT(20) NOT NULL AUTO_INCREMENT,
-- The semantic type of context.
context_semantic_type VARCHAR(16) NOT NULL,
-- Foreign key, many-to-many
-- There is no uniqueness constraint for (prompt_token, media_token) because a user
-- might attach many duplicate images to the same prompt.
prompt_token VARCHAR(32) NOT NULL,
-- Foreign key to the media token
-- There is no uniqueness constraint for (prompt_token, media_token) because a user
-- might attach many duplicate images to the same prompt.
media_token VARCHAR(32) NOT NULL,
-- INDICES --
PRIMARY KEY (id),
KEY index_prompt_token (prompt_token),
KEY index_media_token (media_token)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
+2
View File
@@ -61,6 +61,8 @@ CREATE TABLE media_files (
-- * 'workflow' (DEPRECATED)
origin_product_category VARCHAR(16) NOT NULL DEFAULT "unknown",
-- TODO(bt,2025-07-23): needs maybe_generation_provider.
-- For inference that can be tied back to a model, the type of model.
-- There are multiple indices on this column!
-- DO NOT EXPOSE THIS TO USERS VIA THE API, as we may leak secrets in doing so.
+15 -3
View File
@@ -19,13 +19,25 @@ CREATE TABLE prompts (
-- Original product to which the prompt applied.
-- Prompts may or may not be compatible across products.
-- Possible values:
-- * 'stable_diffusion'
-- * 'comfy_ui'
-- * 'stable_diffusion' (legacy)
-- * 'comfy_ui' (legacy)
-- * 'artcraft_app'
prompt_type VARCHAR(16) NOT NULL,
-- Whomever created the prompt
-- What type of model was used to generate the result.
-- NB: We're conflating prompts with outputs, but in a
-- sense the model is inextricably part of the prompt.
maybe_model_type VARCHAR(32) DEFAULT NULL,
-- What service provided the generation.
maybe_generation_provider VARCHAR(16) NOT NULL,
-- Whoever created the prompt
maybe_creator_user_token VARCHAR(32) DEFAULT NULL,
-- Whoever created the prompt (anonymous user tracking)
maybe_anonymous_visitor_token VARCHAR(32) DEFAULT NULL,
-- ========== PROMPT DETAILS ==========
-- The positive prompt (optional)
@@ -259,7 +259,8 @@ mod tests {
#[test]
fn serialized_length_ok_for_database() {
const MAX_LENGTH : usize = 32;
// NB: The media_files table has allocated width for VARCHAR(32), but let's slim it down to 24.
const MAX_LENGTH : usize = 24;
for variant in MediaFileOriginModelType::all_variants() {
let serialized = variant.to_str();
assert!(serialized.len() > 0, "variant {:?} is too short", variant);
@@ -6,6 +6,8 @@ use strum::EnumCount;
use strum::EnumIter;
use utoipa::ToSchema;
/// NB: This will be used by a variety of tables (MySQL and sqlite)!
/// Keep the max length to 16 characters.
#[cfg_attr(test, derive(EnumIter, EnumCount))]
#[derive(Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd, Serialize, Deserialize, ToSchema)]
#[serde(rename_all = "snake_case")]
@@ -108,5 +110,15 @@ mod tests {
assert_eq!(variant, GenerationProvider::from_str(&format!("{:?}", variant)).unwrap());
}
}
#[test]
fn serialized_length_ok_for_database() {
const MAX_LENGTH : usize = 16;
for variant in GenerationProvider::all_variants() {
let serialized = variant.to_str();
assert!(serialized.len() > 0, "variant {:?} is too short", variant);
assert!(serialized.len() <= MAX_LENGTH, "variant {:?} is too long", variant);
}
}
}
}
@@ -1,6 +1,7 @@
pub mod generation_provider;
pub mod job_status;
pub mod job_status_plus;
pub mod model_type;
pub mod sqlite;
pub mod view_as;
pub mod visibility;
@@ -0,0 +1,124 @@
use std::collections::BTreeSet;
#[cfg(test)]
use strum::EnumCount;
#[cfg(test)]
use strum::EnumIter;
use utoipa::ToSchema;
/// NB: This will be used by a variety of tables (MySQL and sqlite)!
/// Keep the max length to 24 characters.
#[cfg_attr(test, derive(EnumIter, EnumCount))]
#[derive(Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd, Serialize, Deserialize, ToSchema)]
#[serde(rename_all = "snake_case")]
pub enum ModelType {
Artcraft,
Fal,
Sora,
}
impl_enum_display_and_debug_using_to_str!(ModelType);
impl_mysql_enum_coders!(ModelType);
impl_mysql_from_row!(ModelType);
// NB: We can derive `sqlx::Type` instead of using `impl_mysql_enum_coders`
impl ModelType {
pub fn to_str(&self) -> &'static str {
match self {
Self::Artcraft => "artcraft",
Self::Fal => "fal",
Self::Sora => "sora",
}
}
pub fn from_str(job_status: &str) -> Result<Self, String> {
match job_status {
"artcraft" => Ok(Self::Artcraft),
"fal" => Ok(Self::Fal),
"sora" => Ok(Self::Sora),
_ => Err(format!("invalid task_state: {:?}", job_status)),
}
}
pub fn all_variants() -> BTreeSet<Self> {
// NB: BTreeSet is sorted
// NB: BTreeSet::from() isn't const, but not worth using LazyStatic, etc.
BTreeSet::from([
Self::Artcraft,
Self::Fal,
Self::Sora,
])
}
}
#[cfg(test)]
mod tests {
use crate::common::model_type::ModelType;
use crate::test_helpers::assert_serialization;
mod explicit_checks {
use super::*;
#[test]
fn test_serialization() {
assert_serialization(ModelType::Artcraft, "artcraft");
assert_serialization(ModelType::Fal, "fal");
assert_serialization(ModelType::Sora, "sora");
}
#[test]
fn to_str() {
assert_eq!(ModelType::Artcraft.to_str(), "artcraft");
assert_eq!(ModelType::Fal.to_str(), "fal");
assert_eq!(ModelType::Sora.to_str(), "sora");
}
#[test]
fn from_str() {
assert_eq!(ModelType::from_str("artcraft").unwrap(), ModelType::Artcraft);
assert_eq!(ModelType::from_str("fal").unwrap(), ModelType::Fal);
assert_eq!(ModelType::from_str("sora").unwrap(), ModelType::Sora);
}
#[test]
fn all_variants() {
let mut variants = ModelType::all_variants();
assert_eq!(variants.len(), 3);
assert_eq!(variants.pop_first(), Some(ModelType::Artcraft));
assert_eq!(variants.pop_first(), Some(ModelType::Fal));
assert_eq!(variants.pop_first(), Some(ModelType::Sora));
assert_eq!(variants.pop_first(), None);
}
}
mod mechanical_checks {
use super::*;
#[test]
fn variant_length() {
use strum::IntoEnumIterator;
assert_eq!(ModelType::all_variants().len(), ModelType::iter().len());
}
#[test]
fn round_trip() {
for variant in ModelType::all_variants() {
// Test to_str(), from_str(), Display, and Debug.
assert_eq!(variant, ModelType::from_str(variant.to_str()).unwrap());
assert_eq!(variant, ModelType::from_str(&format!("{}", variant)).unwrap());
assert_eq!(variant, ModelType::from_str(&format!("{:?}", variant)).unwrap());
}
}
#[test]
fn serialized_length_ok_for_database() {
const MAX_LENGTH : usize = 24;
for variant in ModelType::all_variants() {
let serialized = variant.to_str();
assert!(serialized.len() > 0, "variant {:?} is too short", variant);
assert!(serialized.len() <= MAX_LENGTH, "variant {:?} is too long", variant);
}
}
}
}