Compare commits

...
Author SHA1 Message Date
bflatastic 54938b9088 layering fixes and circle jumping on resize issue 2025-07-11 15:46:49 +07:00
bflatastic ac7ffcc0db triangle shape bugs fixed 2025-07-11 14:01:22 +07:00
bflatastic 93227aa456 undo, redo, copy, paste, delete keybind fixes for Mac, multi-select move and resizing 2025-07-11 13:52:53 +07:00
bflatastic 586d2acc2b undo redo fix - image working properly 2025-07-11 12:14:48 +07:00
bflatastic 685f8460d8 no center scaling 2025-07-11 12:06:08 +07:00
bflatastic c5ea122cf4 shape free form resizing 2025-07-11 12:03:30 +07:00
bflatastic 82d36ebb16 removed artcraft logo link 2025-07-11 11:57:49 +07:00
bflatastic abcd631038 white bar fixed 2025-07-11 11:53:13 +07:00
bflatastic 42eae1e2c2 white bar fix? 2025-07-11 00:16:58 +07:00
BFlat 01dca1f7b8 fixed image resizing, overflow drawing, show transform controls outside canvas (#710)
- images added to the canvas are now centered and 85% filled on the
canvas
- images resize properly without any restriction
- selected objects now show transform controls when overflowing the
canvas outside
- can now draw freely with the brush and it doesn't stop when you
overflow the edges
- code clean up and unneccessary comments removal
2025-07-10 09:38:50 +07:00
Brandon Thomas bc51b1d22b cleanup sqlx derive macros (#709) 2025-07-09 19:38:19 -04:00
Brandon Thomas fed32ce1fd tokens + enums - make sqlx optional (#708) 2025-07-09 18:21:56 -04:00
bflatastic 138ec975de rearrange button 2025-07-09 22:12:58 +07:00
bflatastic 895159afbf added shape color change 2025-07-09 22:05:51 +07:00
Brandon Thomas d44bd5edd9 fix fal webhook video metadata 2025-07-09 10:54:14 -04:00
Brandon Thomas 5ad15c4a2c background removal command (work in progress) (#707) 2025-07-09 10:02:17 -04:00
bflatastic 7541aa97a5 shape as outline icons instead of solid 2025-07-09 20:46:05 +07:00
BFlat 443648fcf9 bg color button color picker default to white (#706) 2025-07-09 20:32:15 +07:00
BFlat 1d8ed0e7a4 draw shapes by dragging (#705)
- select a shape and drag to draw it
- hold shift to make it proportional
2025-07-09 15:53:28 +07:00
BFlat 8657edf2a1 fix trackpad zoom (#704) 2025-07-09 14:33:24 +07:00
Brandon Thomas 0c283491e4 gpt-image-1 fixes: context images, working on 2d/3d, working with sora/artcraft (#703) 2025-07-07 18:45:48 -04:00
BFlat fa495c65d1 2d bug fixes and improvements (#702)
**fixes:**
- moved canvas fit to screen button to inside prompt box
- canvas fit to screen logic works properly and always centered
- canvas doesnt overflow screen and annoyingly scroll anymore
- brush now has an active state and border and shows brush icon when not
active
- hidden eraser button for now since it doesn't work

TODO:
- missing features
2025-07-07 22:33:39 +07:00
Brandon Thomas e751374192 enqueue 3d using the new endpoint 2025-07-07 01:55:02 -04:00
Brandon Thomas fb3fef02a6 batch get shouldn't require public visibility + error verbosity 2025-07-07 00:36:06 -04:00
Brandon Thomas fff76f8693 handle changes to storyteller API and let tauri inform the frontend (#701) 2025-07-06 21:18:53 -04:00
Brandon Thomas 4f3d23dcc8 Fal + OpenAI gpt-image-1 + preference routing (#700) 2025-07-06 01:14:54 -04:00
Kasisnu b749aa5d6a feat: super basic windows tauri action 2025-07-03 12:50:25 +05:30
Brandon Thomas ef8ccf149f Update documentation 2025-07-01 20:06:05 -04:00
Brandon Thomas 324bfc0964 move api clients crate to top-level of workspace for visibility 2025-07-01 18:50:51 -04:00
BFlat a4ee0eec15 resizable gallery modal and minimum size (#698)
- bug fix for expanded modal conflicting with resized size (not going full screen)
- gallery modal now resizable with set minimum size
- set a minimum size for lightbox resize
2025-07-01 10:19:48 +07:00
Brandon Thomas d8b8b4a037 remove unused imports (#697) 2025-06-30 00:03:52 -04:00
Brandon Thomas a842df3774 fix api docs 2025-06-29 22:22:25 -04:00
Brandon Thomas 4b3df535b5 configure provider priority and dispatch based on it (#696) 2025-06-27 21:15:31 -04:00
ArEnScandCrossPr0duct 3d06f89477 Feature/aspect ratio (#682)
Co-authored-by: CrossPr0duct <alicextensor@gmail.com>
2025-06-26 14:09:52 -04:00
CrossPr0duct d007cb81d5 Fix Drag and Drop Image, I had thought this change was already in. 2025-06-25 16:40:41 -04:00
CrossPr0duct 221d8bb95a the two magic undocumented values that will actually have this work without a bunch of code. 2025-06-25 15:52:33 -04:00
Brandon Thomas 760639d071 video model plumbing (#693) 2025-06-25 14:59:11 -04:00
Brandon Thomas 4e68169faf cookie debugging 2025-06-24 22:28:48 -04:00
Brandon Thomas 8090cdb526 call api endpoints from frontend (#692) 2025-06-24 15:41:02 -04:00
bflatastic ada4c77093 modal closable with esc key (for floating panels that's draggable and background interactable) 2025-06-24 08:22:00 +07:00
bflatastic 7bb14e1b17 topbar minor padding fix 2025-06-24 08:11:25 +07:00
bflatastic f9c8343165 library asset downloaded toast 2025-06-24 08:06:30 +07:00
Tanish Manku 53eb46d2b6 Add 500ms of timeout on tab switching (#691) 2025-06-23 13:03:57 -07:00
bflatastic f5a3ff67b3 small updates to router priority pane 2025-06-23 22:04:55 +07:00
BFlat 6b262d9fcb model router priority drag and drop reordering and login fixes (#690)
Model router priority tab in settings:
- drag and drop to update
- function calls on order change

Login refactor:
- fixed login (opens modal on log out)
- fixes bug where login modal opens even before login state is checked
(annoying)
- uses zustand states now
2025-06-21 02:22:32 +07:00
ArEnScandCrossPr0duct 744df6bcb8 Fixed issue with image background removal and drag and drop. (#689)
Co-authored-by: CrossPr0duct <alicextensor@gmail.com>
2025-06-20 12:55:26 -04:00
bflatastic 9a5ab77d3c don't return profile dropdown 2025-06-20 23:07:18 +07:00
Brandon Thomas 3e42ccc5e5 add artcraft/storyteller host to app info for debugging (#688) 2025-06-20 08:36:47 -04:00
BFlat 81bf7d712c feature/pricing modal (#684)
- pricing modal with tiers: free, plus, pro and max
- set up and ready to integrate with stripe
- small bug fix with the library modal buttons being squished

- upgrade button in TopBar is commented out for now
2025-06-20 12:36:37 +07:00
ArEnScandCrossPr0duct e9fe8bdf0d Fix/login (#686)
Moved Login to top level.
Minor bug fix later., logout after login in and exiting, log out and the button
won't trigger the modal

---------

Co-authored-by: CrossPr0duct <alicextensor@gmail.com>
2025-06-20 01:07:18 -04:00
Brandon Thomas 627fd6beeb roll back Cargo.lock 2025-06-20 00:46:06 -04:00
Brandon Thomas 77788a74e9 fix imports 2025-06-19 21:30:22 -04:00
Brandon Thomas 691f27eb23 misc: refactor client, move endpoints, more models, local testing, etc. etc. (#683) 2025-06-19 20:41:46 -04:00
Brandon Thomas 9008e1f4f4 logging for fal webhook handler 2025-06-19 16:04:38 -04:00
ArEnScandCrossPr0duct c11894692c Background removal race condition fixed. (#681)
Co-authored-by: CrossPr0duct <alicextensor@gmail.com>
2025-06-19 13:22:25 -04:00
Kasisnu e7d8613d09 tweaks to the action 2025-06-19 17:01:19 +05:30
Kasisnu 55832bdfc2 basic tauri/macos action 2025-06-19 16:52:32 +05:30
Brandon Thomas 49f18d7616 remaining tauri -> backend -> fal endpoints (#679) 2025-06-19 06:43:44 -04:00
BFlat 10603f7c86 feature/lightbox drag (#677)
- draggable and resizable lightbox

Bug found: if lightbox is open, gallery modal cannot open the filter
dropdown until lightbox is closed.
2025-06-19 00:41:21 +07:00
5e8c524f84 Feature/login fix (#678)
Co-authored-by: CrossPr0duct <alicextensor@gmail.com>
Co-authored-by: bflatastic <bombayapirat@gmail.com>
2025-06-18 10:59:43 -04:00
Brandon Thomas 05f0fe1216 fal api endpoints in api gateway (#676) 2025-06-17 01:18:36 -04:00
ArEnScandCrossPr0duct 1f44f7e929 Feature/fixes (#675)
Co-authored-by: CrossPr0duct <alicextensor@gmail.com>
2025-06-16 23:58:35 -04:00
876 changed files with 12758 additions and 9075 deletions
+107
View File
@@ -0,0 +1,107 @@
name: "Artcraft Publish"
on:
push:
branches:
- release
- artcraft-release
# This workflow creates Universal macOS builds with signing certificate support.
# On each push to the release branches it will create or update a GitHub release, build your app, and upload the artifacts to the release.
jobs:
publish-artcraft:
permissions:
contents: write
strategy:
fail-fast: false
matrix:
include:
# Universal macOS builds (arm64 and x86_64)
- platform: 'macos-latest'
args: '--target universal-apple-darwin'
# Uncomment below for additional platforms
# - platform: 'ubuntu-22.04'
# args: ''
# - platform: 'windows-latest'
# args: ''
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v4
- name: setup node
uses: actions/setup-node@v4
with:
node-version: lts/*
- name: install Rust stable
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.platform == 'macos-latest' && 'aarch64-apple-darwin,x86_64-apple-darwin' || '' }}
- name: install dependencies (ubuntu only)
if: matrix.platform == 'ubuntu-22.04'
run: |
sudo apt-get update
sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf
- name: install frontend dependencies
run: |
cd ./crates/desktop/artcraft
yarn install
- name: import Apple Developer Certificate
if: matrix.platform == 'macos-latest'
# Prevents keychain from locking automatically for 3600 seconds.
env:
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
run: |
echo $APPLE_CERTIFICATE | base64 --decode > certificate.p12
security create-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
security default-keychain -s build.keychain
security unlock-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
security set-keychain-settings -t 3600 -u build.keychain
security import certificate.p12 -k build.keychain -P "$APPLE_CERTIFICATE_PASSWORD" -T /usr/bin/codesign
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KEYCHAIN_PASSWORD" build.keychain
security find-identity -v -p codesigning build.keychain
- name: verify certificate
if: matrix.platform == 'macos-latest'
run: |
echo "Available certificates:"
security find-identity -v -p codesigning build.keychain
# Try multiple certificate patterns
CERT_INFO=$(security find-identity -v -p codesigning build.keychain | grep -E "(Developer ID Application|Apple Development|Storyteller Developer)" | head -1)
if [ -z "$CERT_INFO" ]; then
echo "No matching certificate found. Using first available certificate."
CERT_INFO=$(security find-identity -v -p codesigning build.keychain | grep -v "Policy" | head -1)
fi
CERT_ID=$(echo "$CERT_INFO" | awk -F'"' '{print $2}')
echo "CERT_ID=$CERT_ID" >> $GITHUB_ENV
echo "Using certificate: $CERT_ID"
- name: build and publish
uses: tauri-apps/tauri-action@v0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
APPLE_SIGNING_IDENTITY: ${{ env.CERT_ID }}
with:
projectPath: ./crates/desktop/artcraft
tagName: artcraft-v__VERSION__ # the action automatically replaces __VERSION__ with the app version.
releaseName: "Artcraft v__VERSION__"
releaseBody: "See the assets to download this version and install."
releaseDraft: true
prerelease: false
args: ${{ matrix.args }}
@@ -0,0 +1,96 @@
name: "Artcraft Windows Publish"
on:
push:
branches:
- release
- artcraft-release
- artcraft-windows # Additional branch for Windows testing
# This workflow creates Windows builds.
# On each push to the release branches it will create or update a GitHub release, build your app, and upload the artifacts to the release.
jobs:
publish-artcraft-windows:
permissions:
contents: write
strategy:
fail-fast: false
matrix:
include:
# Windows builds
- platform: 'windows-latest'
args: ''
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v4
- name: setup node
uses: actions/setup-node@v4
with:
node-version: lts/*
- name: install Rust stable
uses: dtolnay/rust-toolchain@stable
- name: install frontend dependencies
run: |
# Navigate to the frontend directory to install dependencies
cd ./frontend
yarn cache clean
echo "Installing all workspace dependencies..."
yarn install --production=false --frozen-lockfile
echo "=== Version Information ==="
echo "Node version:"
node --version
echo "NPM version:"
npm --version
echo "Yarn version:"
yarn --version
# Remove mismatched @nx packages and reinstall with correct versions
yarn add @nx/remix@20.7.2 @nx/js@20.7.2 @nx/react@20.7.2 @nx/vite@20.7.2 @nx/eslint@20.7.2 --dev -W
echo "=== Nx versions after adjustment ==="
yarn list --pattern "nx" --depth=0
- name: Pre-build frontend to avoid Tauri build issues
run: |
# Build the frontend app from the correct directory
cd ./frontend
# Increase Node.js memory limit for the build
$env:NODE_OPTIONS="--max-old-space-size=8192"
# Use npx to ensure we use the local nx version
echo "Building with npx nx..."
npx nx run editor3d:build
echo "Checking build output..."
dir apps/editor3d/dist/ | Select-Object -First 10
# Update Tauri config to skip the build (since we already built)
cd ${{ github.workspace }}/crates/desktop/artcraft
Copy-Item tauri.conf.json tauri.conf.json.bak
# Set beforeBuildCommand to empty using PowerShell
$config = Get-Content tauri.conf.json | ConvertFrom-Json
$config.build.beforeBuildCommand = ""
$config | ConvertTo-Json -Depth 10 | Set-Content tauri.conf.json
- name: build and publish
uses: tauri-apps/tauri-action@v0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
projectPath: ./crates/desktop/artcraft
tagName: artcraft-windows-v__VERSION__ # the action automatically replaces __VERSION__ with the app version.
releaseName: "Artcraft Windows v__VERSION__"
releaseBody: "Windows build - See the assets to download this version and install."
releaseDraft: true
prerelease: false
args: ${{ matrix.args }}
Generated
+11 -1
View File
@@ -2765,7 +2765,7 @@ dependencies = [
"log",
"mimetypes",
"mysql_queries",
"rand 0.8.5",
"rand 0.9.1",
"sqlx",
"storyteller_root",
"tokens",
@@ -3601,6 +3601,7 @@ dependencies = [
"log",
"reqwest 0.12.15",
"serde",
"test_data",
"testing",
"tokio",
"url",
@@ -10853,6 +10854,15 @@ version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76"
[[package]]
name = "test_data"
version = "0.0.1"
dependencies = [
"anyhow",
"errors",
"workspace-build-acceleration",
]
[[package]]
name = "testing"
version = "0.0.1"
+19 -13
View File
@@ -4,6 +4,18 @@ resolver = "2"
# This block defines the workspace members.
# When we add a library or binary crate, it needs an entry here.
members = [
# API clients for external services
# These might be custom implementations or wrappers over other client libraries.
"crates/api_clients/cloud_storage",
"crates/api_clients/fal_client",
"crates/api_clients/google_drive_common",
"crates/api_clients/google_sign_in",
"crates/api_clients/openai_sora_client",
"crates/api_clients/openart_client",
"crates/api_clients/runwayml_client",
"crates/api_clients/storyteller_client",
"crates/api_clients/thumbnail_generator",
# CLI tools for operations, etc.
"crates/cli/devenv/dev-database-seed",
"crates/cli/devenv/dev-upload-media-file",
@@ -22,15 +34,6 @@ members = [
# Shared libraries
"crates/lib/actix_cors_configs",
"crates/lib/actix_helpers",
"crates/lib/api_clients/cloud_storage",
"crates/lib/api_clients/fal_client",
"crates/lib/api_clients/google_drive_common",
"crates/lib/api_clients/google_sign_in",
"crates/lib/api_clients/openai_sora_client",
"crates/lib/api_clients/openart_client",
"crates/lib/api_clients/runwayml_client",
"crates/lib/api_clients/storyteller_client",
"crates/lib/api_clients/thumbnail_generator",
"crates/lib/bootstrap",
"crates/lib/bucket_paths",
"crates/lib/build_metadata",
@@ -73,6 +76,7 @@ members = [
"crates/lib/sqlx_mysql_helpers",
"crates/lib/storyteller_root",
"crates/lib/subprocess_common",
"crates/lib/test_data",
"crates/lib/testing",
"crates/lib/tts_common",
"crates/lib/url_config",
@@ -210,11 +214,12 @@ url = { version = "2.5.4", features = ["serde"] }
# Internal libs
artcraft_api_defs = { path = "crates/schema/public/api/artcraft_api_defs" }
build_metadata = { path = "crates/lib/build_metadata" }
cloud_storage = { path = "crates/api_clients/cloud_storage" }
crockford = { path = "crates/lib/data/crockford" }
enums = { path = "crates/schema/public/enums" }
enums_public = { path = "crates/schema/public/enums_public" }
errors = { path = "crates/lib/errors" }
fal_client = { path = "crates/lib/api_clients/fal_client" }
fal_client = { path = "crates/api_clients/fal_client" }
file_to_base64_url = { path = "crates/lib/files/file_to_base64_url" }
filesys = { path = "crates/lib/files/filesys" }
idempotency = { path = "crates/lib/idempotency" }
@@ -223,10 +228,11 @@ jwt_light = { path = "crates/lib/jwt_light" }
memory_caching = { path = "crates/lib/caching/memory_caching" }
memory_store = { path = "crates/lib/caching/memory_store" }
mimetypes = { path = "crates/lib/files/mimetypes" }
openai_sora_client = { path = "crates/lib/api_clients/openai_sora_client" }
openai_sora_client = { path = "crates/api_clients/openai_sora_client" }
primitives = { path = "crates/lib/data/primitives" }
shared_service_components = { path = "crates/lib/shared_service_components" }
storyteller_client = { path = "crates/lib/api_clients/storyteller_client" }
storyteller_client = { path = "crates/api_clients/storyteller_client" }
subprocess_common = { path = "crates/lib/subprocess_common" }
test_data = { path = "crates/lib/test_data" }
tokens = { path = "crates/schema/public/tokens" }
web_base64= { path = "crates/lib/data/web_base64" }
web_base64 = { path = "crates/lib/data/web_base64" }
+4 -3
View File
@@ -1,7 +1,7 @@
storyteller-rust
================
Our Rust monorepo, which serves both our API surface area and our downloadable client applications.
Our Artcraft monorepo, which serves our desktop application, backend infrastructure, and more.
```
."-,.__
@@ -45,5 +45,6 @@ Our Rust monorepo, which serves both our API surface area and our downloadable c
/ ,"'"\,' `/ `-.|"
```
- [documentation](./_develop/docs)
- [tools, misc](./_develop)
- [developer documentation](./_docs)
- [server API reference](https://storyteller-docs.netlify.app/)
- [tools, scripts, misc](./script)
+26
View File
@@ -0,0 +1,26 @@
Code Conventions
================
We use three languages at Artcraft: Rust, TypeScript, and Python.
## Rust style guide
We deviate from the official Rust style guide in a few places:
- We use two spaces for indentation instead of four.
- We typically keep one function or type definition per file, though this isn't a steadfast rule.
- We use a Cargo workspace to share code within our monorepo.
### Rust Preferred Libraries
We use the following Rust libraries, though we are open to migrating to alternatives:
- `actix-web` for our HTTP web server
- `reqwest` for HTTP client requests
- `sqlx` for SQL database operations
- `tokio` for asynchronous programming
## TypeScript style guide
- We use two spaces for indentation instead of four.
- We use `nx` for monorepo management
@@ -1,5 +1,5 @@
FakeYou Development Setup (bare metal)
======================================
Local Development Setup Guide (for bare metal)
==============================================
This document describes how to run the FakeYou infrastructure on your development
machine's bare metal (no containers).
@@ -0,0 +1,42 @@
[package]
name = "cloud_storage"
edition = "2018"
version = "0.0.1"
authors = [
"Brandon Thomas <bt@brand.io>",
"Brandon Thomas <echelon@gmail.com>",
]
publish = false
[lib]
name = "cloud_storage"
path = "src/lib.rs"
[package.metadata.cargo-udeps.ignore]
normal = ["workspace-build-acceleration"]
[dependencies]
# Workspace hack (faster compile times)
workspace-build-acceleration.workspace = true
# Internal
bucket_paths = { path = "../../lib/bucket_paths" }
crockford.workspace = true
easyenv = { path = "../../lib/easyenv" }
errors.workspace = true
filesys.workspace = true
hashing = { path = "../../lib/files/hashing" }
mimetypes.workspace = true
storyteller_root = { path = "../../lib/storyteller_root" }
# External
anyhow.workspace = true
log.workspace = true
env_logger = "0.11.3"
async-trait = "0.1.75"
# The "never-encode-slash" feature seems to have been removed:
# https://github.com/durch/rust-s3/pull/254
rust-s3 = { version = "0.36.0-beta.2" }
# Runtime
tokio.workspace = true
@@ -8,27 +8,33 @@ authors = [
]
publish = false
[package.metadata.cargo-udeps.ignore]
normal = ["workspace-build-acceleration"]
[lib]
name = "fakeyou_client"
path = "src/lib.rs"
[dependencies]
# Workspace hack (faster compile times)
workspace-build-acceleration.workspace = true
# Internal
enums = { path = "../../../schema/public/enums" }
errors = { path = "../../errors" }
tokens = { path = "../../../schema/public/tokens" }
enums.workspace = true
errors.workspace = true
tokens.workspace = true
# External
bytes = "1.3"
chrono = { version = "0.4.22", features = ["serde"] }
log = "0.4.14"
once_cell = "1.9.0"
bytes.workspace = true
chrono.workspace = true
log.workspace = true
once_cell.workspace = true
# Serialization
serde = "1.0.125"
serde_derive = "1.0.125"
serde_json = "1.0.64"
toml = "0.5.8"
serde.workspace = true
serde_derive.workspace = true
serde_json.workspace = true
toml.workspace = true
# HTTP
backoff = { version = "0.4.0", features = ["futures"] } # Exponential backoff retry
@@ -12,27 +12,27 @@ name = "fal_client"
path = "src/lib.rs"
[dependencies]
# Workspace hack (faster compile times)
workspace-build-acceleration = { workspace = true }
workspace-build-acceleration.workspace = true
# Internal
file_to_base64_url = { workspace = true }
errors = { workspace = true }
errors.workspace = true
file_to_base64_url.workspace = true
# External
anyhow = { workspace = true }
futures = { workspace = true }
log = { workspace = true }
reqwest = { workspace = true }
serde = { workspace = true }
tokio = { workspace = true }
url = { workspace = true }
anyhow.workspace = true
futures.workspace = true
log.workspace = true
reqwest.workspace = true
serde.workspace = true
tokio.workspace = true
url.workspace = true
# For now, we'll vendor in fal.
# We need to implement webhooks and the upstream doesn't support them yet.
#fal = { version = "0.3.6", features = ["endpoints"] }
fal = { path = "../../../vendor/fal-rs", features = ["endpoints"] }
fal = { path = "../../vendor/fal-rs", features = ["endpoints"] }
[dev-dependencies]
testing = { path = "../../testing" }
testing = { path = "../../lib/testing" }
test_data.workspace = true
@@ -8,6 +8,7 @@ impl FalApiKey {
}
pub fn from_str(api_key: &str) -> Self {
Self(api_key.to_string())
let api_key = api_key.trim().to_string();
Self(api_key)
}
}
@@ -0,0 +1,2 @@
pub mod fal_api_key;
pub mod open_ai_api_key;
@@ -0,0 +1,15 @@
/// This is for Fal's OpenAI "Bring Your Own Key" (BYOK) endpoints.
#[derive(Clone)]
pub struct OpenAiApiKey(pub String);
impl OpenAiApiKey {
pub fn new(api_key: String) -> Self {
Self(api_key)
}
pub fn from_str(api_key: &str) -> Self {
let api_key = api_key.trim().to_string();
Self(api_key)
}
}
@@ -0,0 +1,74 @@
use crate::creds::fal_api_key::FalApiKey;
use crate::error::classify_fal_error::classify_fal_error;
use crate::error::fal_error_plus::FalErrorPlus;
use fal::endpoints::fal_ai::flux_pro::v1_1_ultra::{v1_1_ultra, AspectRatioProperty, FluxProUltraTextToImageInput};
use fal::webhook::WebhookResponse;
use reqwest::IntoUrl;
pub struct FluxPro11UltraArgs<'a, U: IntoUrl> {
pub prompt: &'a str,
pub webhook_url: U,
pub api_key: &'a FalApiKey,
pub aspect_ratio: FluxPro11UltraAspectRatio,
pub num_images: FluxPro11UltraNumImages,
}
#[derive(Copy, Clone, Debug)]
pub enum FluxPro11UltraAspectRatio {
Square, // 1:1
LandscapeFourByThree, // 4:3
LandscapeSixteenByNine, // 16:9
PortraitThreeByFour, // 3:4
PortraitNineBySixteen, // 9:16
//Custom { width: u32, height: u32 }, // TODO
}
#[derive(Copy, Clone, Debug)]
pub enum FluxPro11UltraNumImages {
One, // Default
Two,
Three,
Four,
}
pub async fn enqueue_flux_pro_11_ultra_text_to_image_webhook<U: IntoUrl>(
args: FluxPro11UltraArgs<'_, U>
) -> Result<WebhookResponse, FalErrorPlus> {
let num_images = match args.num_images {
FluxPro11UltraNumImages::One => 1,
FluxPro11UltraNumImages::Two => 2,
FluxPro11UltraNumImages::Three => 3,
FluxPro11UltraNumImages::Four => 4,
};
let aspect_ratio = match args.aspect_ratio {
FluxPro11UltraAspectRatio::Square => AspectRatioProperty::Property_1_1,
FluxPro11UltraAspectRatio::LandscapeFourByThree => AspectRatioProperty::Property_4_3,
FluxPro11UltraAspectRatio::LandscapeSixteenByNine => AspectRatioProperty::Property_16_9,
FluxPro11UltraAspectRatio::PortraitThreeByFour => AspectRatioProperty::Property_3_4,
FluxPro11UltraAspectRatio::PortraitNineBySixteen => AspectRatioProperty::Property_9_16,
};
let request = FluxProUltraTextToImageInput {
prompt: args.prompt.to_string(),
num_images: Some(num_images),
aspect_ratio: Some(aspect_ratio),
// Maybe expose
seed: None,
raw: Some(true), // Generate less processed, more natural-looking images. Default is false.
// Maybe abstract
enable_safety_checker: Some(false),
safety_tolerance: Some("5".to_string()), // 1 is most strict, 5 is most permissive
// Constants
output_format: Some("png".to_string()),
sync_mode: None, // Synchronous / slow
};
let result = v1_1_ultra(request)
.with_api_key(&args.api_key.0)
.queue_webhook(args.webhook_url)
.await;
result.map_err(|err| classify_fal_error(err))
}
@@ -0,0 +1,133 @@
use crate::creds::fal_api_key::FalApiKey;
use crate::creds::open_ai_api_key::OpenAiApiKey;
use crate::error::classify_fal_error::classify_fal_error;
use crate::error::fal_error_plus::FalErrorPlus;
use fal::endpoints::fal_ai::gpt_image_1::edit_image::byok::{gpt_edit_image, GptEditImageRequest};
use fal::webhook::WebhookResponse;
use reqwest::IntoUrl;
pub struct GptEditImageByokArgs<'a, V: IntoUrl> {
// Request
pub image_urls: Vec<String>,
pub prompt: &'a str,
pub image_size: GptEditImageSize,
pub num_images: GptEditImageNumImages,
pub quality: GptEditImageQuality,
// Fulfillment
pub api_key: &'a FalApiKey,
pub openai_api_key: &'a OpenAiApiKey,
pub webhook_url: V,
}
#[derive(Copy, Clone, Debug)]
pub enum GptEditImageSize{
Auto,
Square,
Horizontal,
Vertical,
}
#[derive(Copy, Clone, Debug)]
pub enum GptEditImageQuality {
Auto,
Low,
Medium,
High,
}
#[derive(Copy, Clone, Debug)]
pub enum GptEditImageNumImages{
One,
Two,
Three,
Four,
}
pub async fn enqueue_gpt_image_1_edit_image_webhook<V: IntoUrl>(
args: GptEditImageByokArgs<'_, V>
) -> Result<WebhookResponse, FalErrorPlus> {
// auto, 1024x1024, 1536x1024, 1024x1536
let image_size = match args.image_size {
GptEditImageSize::Auto => "auto",
GptEditImageSize::Square => "1024x1024",
GptEditImageSize::Horizontal => "1536x1024",
GptEditImageSize::Vertical => "1024x1536",
};
let quality = match args.quality {
GptEditImageQuality::Auto => "auto",
GptEditImageQuality::Low => "low",
GptEditImageQuality::Medium => "medium",
GptEditImageQuality::High => "high",
};
let num_images = match args.num_images {
GptEditImageNumImages::One => 1,
GptEditImageNumImages::Two => 2,
GptEditImageNumImages::Three => 3,
GptEditImageNumImages::Four => 4,
};
let request = GptEditImageRequest {
image_urls: args.image_urls,
prompt: args.prompt.to_string(),
image_size: image_size.to_string(),
num_images,
quality: quality.to_string(),
openai_api_key: args.openai_api_key.0.to_string(),
};
let result = gpt_edit_image(request)
.with_api_key(&args.api_key.0)
.queue_webhook(args.webhook_url)
.await;
result.map_err(|err| classify_fal_error(err))
}
#[cfg(test)]
mod tests {
use crate::creds::fal_api_key::FalApiKey;
use crate::creds::open_ai_api_key::OpenAiApiKey;
use crate::requests::webhook::image::enqueue_gpt_image_1_edit_image_webhook::{enqueue_gpt_image_1_edit_image_webhook, GptEditImageByokArgs, GptEditImageNumImages, GptEditImageQuality, GptEditImageSize};
use errors::AnyhowResult;
use std::fs::read_to_string;
use test_data::web::image_urls::{ERNEST_SCARED_STUPID_IMAGE_URL, GHOST_IMAGE_URL, GRASSY_HILL_TRANSPARENT_IMAGE_URL};
#[tokio::test]
#[ignore]
async fn test_gpt_image_1_edit_image() -> AnyhowResult<()> {
let image_url = "https://cdn-2.fakeyou.com/media/3/4/h/f/s/34hfsmt8e38rvne6mwa4pwbxr6292sgy/image_34hfsmt8e38rvne6mwa4pwbxr6292sgy.png";
// XXX: Don't commit secrets!
let secret = read_to_string("/Users/bt/Artcraft/credentials/fal_api_key.txt")?;
let fal_api_key = FalApiKey::from_str(&secret);
let secret = read_to_string("/Users/bt/Artcraft/credentials/openai_api_key.txt")?;
let open_ai_api_key = OpenAiApiKey::from_str(&secret);
let args = GptEditImageByokArgs {
image_urls: vec![
ERNEST_SCARED_STUPID_IMAGE_URL.to_string(),
GHOST_IMAGE_URL.to_string(),
GRASSY_HILL_TRANSPARENT_IMAGE_URL.to_string(),
],
prompt: "put the man and the ghost on the grassy hill. the man is scared of the friendly ghost.",
api_key: &fal_api_key,
openai_api_key: &open_ai_api_key,
webhook_url: "https://example.com/webhook",
image_size: GptEditImageSize::Horizontal,
num_images: GptEditImageNumImages::One,
quality: GptEditImageQuality::High,
};
let result = enqueue_gpt_image_1_edit_image_webhook(args).await?;
Ok(())
}
}
@@ -2,4 +2,5 @@ pub mod enqueue_flux_1_dev_text_to_image_webhook;
pub mod enqueue_flux_1_schnell_text_to_image_webhook;
pub mod enqueue_flux_pro_11_text_to_image_webhook;
pub mod enqueue_flux_pro_11_ultra_text_to_image_webhook;
pub mod remove_background_rembg_webhook;
pub mod enqueue_gpt_image_1_edit_image_webhook;
pub mod remove_background_rembg_webhook;
@@ -37,6 +37,7 @@ mod tests {
use crate::requests::webhook::image::remove_background_rembg_webhook::remove_background_rembg_webhook;
use crate::requests::webhook::image::remove_background_rembg_webhook::RemoveBackgroundRembgWebhookArgs;
use std::fs::read_to_string;
use test_data::web::image_urls::ERNEST_SCARED_STUPID_IMAGE_URL;
#[tokio::test]
#[ignore] // NB: Manually test, don't run in CI!
@@ -46,7 +47,7 @@ mod tests {
let api_key = FalApiKey::from_str(&api_key);
let args = RemoveBackgroundRembgWebhookArgs {
image_url: "https://cdn-2.fakeyou.com/media/b/2/p/b/0/b2pb09bcbxdqjzgbnsapvfs1t897wdnx/image_b2pb09bcbxdqjzgbnsapvfs1t897wdnx.png",
image_url: ERNEST_SCARED_STUPID_IMAGE_URL,
webhook_url: "https://api.storyteller.ai/webhook",
api_key: &api_key,
};
@@ -55,8 +56,6 @@ mod tests {
println!("{:?}", response);
assert_eq!(1, 2);
Ok(())
}
}
@@ -8,25 +8,24 @@ authors = [
]
publish = false
[package.metadata.cargo-udeps.ignore]
normal = ["workspace-build-acceleration"]
[lib]
name = "google_drive_common"
path = "src/lib.rs"
[package.metadata.cargo-udeps.ignore]
normal = ["workspace-build-acceleration"]
[dependencies]
# Workspace hack (faster compile times)
workspace-build-acceleration.workspace = true
# Internal
crockford = { path = "../../data/crockford" }
errors = { path = "../../errors" }
subprocess_common = { path = "../../subprocess_common" }
# Workspace hack (faster compile times)
workspace-build-acceleration = { version = "0.1", path = "../../../workspace-build-acceleration" }
crockford.workspace = true
errors.workspace = true
subprocess_common = { path = "../../lib/subprocess_common" }
# External
anyhow = "1.0.86"
log = "0.4.14"
tempdir = "0.3.7"
anyhow.workspace = true
log.workspace = true
tempdir.workspace = true

Some files were not shown because too many files have changed in this diff Show More