parameter für Modelle eingeführt, Beschreibung aktualisiert
This commit is contained in:
406
PRP.md
Normal file
406
PRP.md
Normal file
@@ -0,0 +1,406 @@
|
||||
# Project Reconstruction Plan (PRP) for AI StyleGallery
|
||||
|
||||
This document outlines the architecture, functionality, and implementation details of the "AI StyleGallery" web application. It is intended to serve as a comprehensive guide for another AI agent or LLM to understand and potentially reconstruct a similar system.
|
||||
|
||||
## 1. Application Overview
|
||||
|
||||
**Primary Goal:** AI StyleGallery is a web application designed to allow users to transform their uploaded images by applying various AI-generated artistic styles. Users can manage their image gallery, apply styles via external AI services, and save or discard the results.
|
||||
|
||||
**Core Functionality:**
|
||||
- **Image Upload:** Users can upload their own images to the gallery.
|
||||
- **Style Application:** Users can select an image and choose from a predefined set of AI styles to apply.
|
||||
- **AI Processing Integration:** The application interacts with external AI web services (e.g., ComfyUI, RunwareAI) to perform image transformations.
|
||||
- **Gallery Management:** Users can view their original and styled images, and decide to keep (make permanent) or discard (delete) styled images.
|
||||
- **Admin Panel:** An administrative interface for managing AI models, API providers, styles, users, and roles.
|
||||
|
||||
## 2. Core Technologies & Stack
|
||||
|
||||
* **Languages:** PHP 8.1, JavaScript
|
||||
* **Frameworks & Runtimes:** Laravel 12.21.0, Vue.js 3.5.18, Inertia.js 1.3.0, Livewire 3.6.4, Vite 5.4.19
|
||||
* **Databases:** MySQL (default), with configurations for PostgreSQL, SQLite, and SQL Server. Redis is used for caching.
|
||||
* **Key PHP Libraries/Dependencies:**
|
||||
* Filament 3.3.34 (for the admin panel)
|
||||
* Guzzle 7.9.3 (for HTTP requests)
|
||||
* Laravel Sanctum 4.2.0 (for API authentication)
|
||||
* Laravel Breeze 2.3.8 (authentication scaffolding)
|
||||
* Laravel Pint 1.24.0 (code formatter)
|
||||
* Laravel Prompts 0.3.6 (CLI prompts)
|
||||
* Laravel Sail 1.44.0 (Docker development environment)
|
||||
* Laravel Serializable Closure 2.0.4
|
||||
* Laravel Tinker 2.10.1
|
||||
* nesbot/carbon 3.10.2 (date/time library)
|
||||
* predis/predis 3.1.0 (Redis client)
|
||||
* phpunit/php-code-coverage 12.3.2
|
||||
* phpunit/php-file-iterator 6.0.0
|
||||
* phpunit/php-invoker 6.0.0
|
||||
* phpunit/php-text-template 5.0.0
|
||||
* phpunit/php-timer 8.0.0
|
||||
* phpunit/phpunit 12.3.0
|
||||
* ramsey/uuid 4.9.0 (UUID generation)
|
||||
* spatie/laravel-package-tools 1.92.7
|
||||
* tightenco/ziggy 2.5.3 (Laravel routes in JS)
|
||||
* symfony/clock 7.3.0
|
||||
* symfony/console 7.3.2
|
||||
* symfony/css-selector 7.3.0
|
||||
* symfony/deprecation-contracts 3.6.0
|
||||
* symfony/error-handler 7.3.2
|
||||
* symfony/event-dispatcher 7.3.0
|
||||
* symfony/event-dispatcher-contracts 3.6.0
|
||||
* symfony/finder 7.3.2
|
||||
* symfony/html-sanitizer 7.3.2
|
||||
* symfony/http-foundation 7.3.2
|
||||
* symfony/http-kernel 7.3.2
|
||||
* symfony/mailer 7.3.2
|
||||
* symfony/mime 7.3.2
|
||||
* symfony/polyfill-ctype 1.32.0
|
||||
* symfony/polyfill-intl-grapheme 1.32.0
|
||||
* symfony/polyfill-intl-idn 1.32.0
|
||||
* symfony/polyfill-intl-normalizer 1.32.0
|
||||
* symfony/polyfill-mbstring 1.32.0
|
||||
* symfony/polyfill-php80 1.32.0
|
||||
* symfony/polyfill-php83 1.32.0
|
||||
* symfony/polyfill-uuid 1.32.0
|
||||
* symfony/process 7.3.0
|
||||
* symfony/routing 7.3.2
|
||||
* symfony/service-contracts 3.6.0
|
||||
* symfony/string 7.3.2
|
||||
* symfony/translation 7.3.2
|
||||
* symfony/translation-contracts 3.6.0
|
||||
* symfony/uid 7.3.1
|
||||
* symfony/var-dumper 7.3.2
|
||||
* symfony/yaml 7.3.2
|
||||
* **Key JavaScript Libraries/Dependencies:**
|
||||
* axios 1.11.0 (for frontend HTTP requests)
|
||||
* tailwindcss 3.4.17 (for styling)
|
||||
* @inertiajs/vue3 1.3.0
|
||||
* @vitejs/plugin-vue 5.2.4
|
||||
* @tailwindcss/forms 0.5.10
|
||||
* autoprefixer 10.4.21
|
||||
* postcss 8.5.6
|
||||
* laravel-echo 2.1.7 (WebSocket client)
|
||||
* pusher-js 8.4.0 (Pusher client for WebSockets)
|
||||
* @fortawesome/fontawesome-svg-core 7.0.0
|
||||
* @fortawesome/free-solid-svg-icons 7.0.0
|
||||
* @fortawesome/vue-fontawesome 3.1.1
|
||||
* vanilla-lazyload 19.1.3 (lazy loading images)
|
||||
* **Package Manager(s):** Composer for PHP, npm for JavaScript.
|
||||
|
||||
## 2. UI Structure (Frontend / Backend)
|
||||
|
||||
|
||||
### 2.1. Frontend (Vue.js 3 with Inertia.js)
|
||||
|
||||
The frontend is a Single Page Application (SPA) built with Vue.js and Inertia.js, providing a dynamic user experience.
|
||||
|
||||
- **Gallery View (`resources/js/Pages/Home.vue`):**
|
||||
- Displays a grid of images (both original and styled).
|
||||
- Images are fetched from `/api/images`.
|
||||
- Includes pagination for large galleries.
|
||||
- Periodically polls `/api/images` to refresh the gallery and show newly styled images.
|
||||
- **Image Context Menu (`resources/js/Components/ImageContextMenu.vue`):**
|
||||
- Appears when a user taps on an image in the gallery.
|
||||
- Provides options: "Drucken" (Print), "Stil ändern" (Change Style), "Schließen" (Close).
|
||||
- Dynamically adjusts its position based on the tap location.
|
||||
- Layout: Image preview (60% width) and options list (40% width).
|
||||
- **Style Selector (`resources/js/Components/StyleSelector.vue`):**
|
||||
- Displayed when "Stil ändern" is selected from the context menu.
|
||||
- Fetches and lists available AI styles from `/api/styles`.
|
||||
- Allows users to select a style.
|
||||
- Lazy loads style preview images using `IntersectionObserver` for performance.
|
||||
- Includes a "back" arrow to return to the main context menu.
|
||||
- **Styled Image Display (Implicit):**
|
||||
- Styled images appear in the main gallery after processing.
|
||||
- **User Authentication/Profile:**
|
||||
- Login/Logout functionality (handled by Laravel Breeze/Fortify and Inertia.js).
|
||||
- User profile management (via Laravel's built-in features and Filament admin panel).
|
||||
|
||||
### 2.2. Backend (Laravel 12 with Filament Admin Panel)
|
||||
|
||||
The backend is a monolithic Laravel application, with Filament providing a powerful administrative interface.
|
||||
|
||||
- **Filament Admin Panel:**
|
||||
- Accessible via `/admin`.
|
||||
- Provides CRUD (Create, Read, Update, Delete) operations for the following resources:
|
||||
- **AI Models (`app/Filament/Resources/AiModelResource.php`):** Manage AI models, their IDs, types, associated API providers, and specific parameters (JSON).
|
||||
- **API Providers (`app/Filament/Resources/ApiProviderResource.php`):** Configure connections to external AI services (API URL, authentication tokens/credentials, plugin type).
|
||||
- **Styles (`app/Filament/Resources/StyleResource.php`):** Define AI styles, including their title, prompt, description, preview image, parameters (JSON), and associated AI model.
|
||||
- **Images (via direct database interaction and synchronization):** While there's an `ImageResource`, image management is largely automated through synchronization.
|
||||
- **Users (`app/Filament/Resources/UserResource.php`):** Manage user accounts and assign roles.
|
||||
- **Roles (`app/Filament/Resources/RoleResource.php`):** Define user roles and permissions.
|
||||
- **Settings (`app/Filament/Resources/SettingResource.php`):** Manage application-wide settings.
|
||||
|
||||
## 3. Resources/Entities (Models)
|
||||
|
||||
The application's core data structures are represented by Eloquent models:
|
||||
|
||||
- **`App\Models\User`**: Represents a user account.
|
||||
- `id` (PK)
|
||||
- `name`
|
||||
- `email`
|
||||
- `password`
|
||||
- `role_id` (FK to `roles` table)
|
||||
- `two_factor_secret`, `two_factor_recovery_codes`, `two_factor_confirmed_at` (for 2FA)
|
||||
- `settings` (JSON)
|
||||
- **`App\Models\Image`**: Stores metadata for both original and styled images.
|
||||
- `id` (PK)
|
||||
- `path` (string, file path relative to storage disk)
|
||||
- `uuid` (string, unique identifier for tracking)
|
||||
- `original_image_id` (FK to `images` table, for styled images)
|
||||
- `style_id` (FK to `styles` table, for styled images)
|
||||
- `is_temp` (boolean, true for temporary styled images)
|
||||
- `is_public` (boolean, true for publicly visible images)
|
||||
- `comfyui_prompt_id` (string, for tracking ComfyUI jobs)
|
||||
- `created_at`, `updated_at`
|
||||
- **`App\Models\Style`**: Defines an AI style.
|
||||
- `id` (PK)
|
||||
- `title` (string)
|
||||
- `prompt` (text, the base prompt for the AI model)
|
||||
- `description` (text)
|
||||
- `preview_image` (string, path to style preview image)
|
||||
- `parameters` (JSON, additional parameters for the AI plugin, cast to array)
|
||||
- `ai_model_id` (FK to `ai_models` table)
|
||||
- `enabled` (boolean)
|
||||
- `created_at`, `updated_at`
|
||||
- **`App\Models\AiModel`**: Represents a specific AI model (e.g., Stable Diffusion v1.5).
|
||||
- `id` (PK)
|
||||
- `name` (string)
|
||||
- `model_id` (string, identifier used by the AI service)
|
||||
- `model_type` (string, e.g., "Stable Diffusion")
|
||||
- `enabled` (boolean)
|
||||
- `parameters` (JSON, additional parameters for the AI plugin, cast to array)
|
||||
- `created_at`, `updated_at`
|
||||
- **`App\Models\ApiProvider`**: Configures connection details for an external AI service.
|
||||
- `id` (PK)
|
||||
- `name` (string, e.g., "ComfyUI API")
|
||||
- `api_url` (string, base URL of the AI service)
|
||||
- `username` (string, nullable)
|
||||
- `password` (string, nullable)
|
||||
- `token` (string, nullable)
|
||||
- `plugin` (string, identifies the plugin to use, e.g., "comfyui", "runwareai")
|
||||
- `enabled` (boolean)
|
||||
- `created_at`, `updated_at`
|
||||
- **`App\Models\Role`**: Defines user roles for access control.
|
||||
- `id` (PK)
|
||||
- `name` (string, e.g., "admin", "user")
|
||||
- `created_at`, `updated_at`
|
||||
- **`App\Models\Setting`**: Stores application-wide key-value settings.
|
||||
- `id` (PK)
|
||||
- `key` (string)
|
||||
- `value` (text)
|
||||
- `created_at`, `updated_at`
|
||||
|
||||
## 4. Database Structure (Schema)
|
||||
|
||||
Key tables and their relevant columns:
|
||||
|
||||
- **`users`**
|
||||
- `id`: `bigint unsigned auto_increment primary key`
|
||||
- `name`: `varchar(255)`
|
||||
- `email`: `varchar(255) unique`
|
||||
- `password`: `varchar(255)`
|
||||
- `role_id`: `bigint unsigned null` (FK to `roles.id`)
|
||||
- `two_factor_secret`: `text null`
|
||||
- `two_factor_recovery_codes`: `text null`
|
||||
- `two_factor_confirmed_at`: `timestamp null`
|
||||
- `settings`: `json null`
|
||||
- `created_at`: `timestamp null`
|
||||
- `updated_at`: `timestamp null`
|
||||
|
||||
- **`images`**
|
||||
- `id`: `bigint unsigned auto_increment primary key`
|
||||
- `path`: `varchar(255)`
|
||||
- `uuid`: `char(36) unique`
|
||||
- `original_image_id`: `bigint unsigned null` (FK to `images.id`)
|
||||
- `style_id`: `bigint unsigned null` (FK to `styles.id`)
|
||||
- `is_temp`: `tinyint(1) default 0`
|
||||
- `is_public`: `tinyint(1) default 1`
|
||||
- `comfyui_prompt_id`: `varchar(255) null`
|
||||
- `created_at`: `timestamp null`
|
||||
- `updated_at`: `timestamp null`
|
||||
|
||||
- **`styles`**
|
||||
- `id`: `bigint unsigned auto_increment primary key`
|
||||
- `title`: `varchar(255)`
|
||||
- `prompt`: `longtext`
|
||||
- `description`: `longtext`
|
||||
- `preview_image`: `varchar(255)`
|
||||
- `parameters`: `json null`
|
||||
- `ai_model_id`: `bigint unsigned` (FK to `ai_models.id`)
|
||||
- `enabled`: `tinyint(1) default 1`
|
||||
- `created_at`: `timestamp null`
|
||||
- `updated_at`: `timestamp null`
|
||||
|
||||
- **`ai_models`**
|
||||
- `id`: `bigint unsigned auto_increment primary key`
|
||||
- `name`: `varchar(255)`
|
||||
- `model_id`: `varchar(255)`
|
||||
- `model_type`: `varchar(255) null`
|
||||
- `enabled`: `tinyint(1) default 1`
|
||||
- `parameters`: `json null`
|
||||
- `created_at`: `timestamp null`
|
||||
- `updated_at`: `timestamp null`
|
||||
|
||||
- **`api_providers`**
|
||||
- `id`: `bigint unsigned auto_increment primary key`
|
||||
- `name`: `varchar(255)`
|
||||
- `api_url`: `varchar(255)`
|
||||
- `username`: `varchar(255) null`
|
||||
- `password`: `varchar(255) null`
|
||||
- `token`: `varchar(255) null`
|
||||
- `plugin`: `varchar(255)`
|
||||
- `enabled`: `tinyint(1) default 1`
|
||||
- `created_at`: `timestamp null`
|
||||
- `updated_at`: `timestamp null`
|
||||
|
||||
- **`roles`**
|
||||
- `id`: `bigint unsigned auto_increment primary key`
|
||||
- `name`: `varchar(255) unique`
|
||||
- `created_at`: `timestamp null`
|
||||
- `updated_at`: `timestamp null`
|
||||
|
||||
- **`ai_model_api_provider`** (Pivot table for many-to-many relationship)
|
||||
- `ai_model_id`: `bigint unsigned` (FK to `ai_models.id`)
|
||||
- `api_provider_id`: `bigint unsigned` (FK to `api_providers.id`)
|
||||
|
||||
- **`settings`**
|
||||
- `id`: `bigint unsigned auto_increment primary key`
|
||||
- `key`: `varchar(255) unique`
|
||||
- `value`: `longtext`
|
||||
- `created_at`: `timestamp null`
|
||||
- `updated_at`: `timestamp null`
|
||||
|
||||
## 5. User Interactions
|
||||
|
||||
### 5.1. Frontend User Interactions
|
||||
|
||||
- **Image Upload:** Users can upload images via a dedicated interface (not explicitly detailed in provided context, but implied by gallery functionality).
|
||||
- **Image Selection:** Tapping on an image in the gallery opens a context menu.
|
||||
- **Context Menu Actions:**
|
||||
- "Drucken" (Print): Placeholder for printing functionality.
|
||||
- "Stil ändern" (Change Style): Navigates to the style selection interface.
|
||||
- "Schließen" (Close): Closes the context menu.
|
||||
- **Style Selection:**
|
||||
- Users browse a list of available styles.
|
||||
- Selecting a style initiates the AI image transformation process.
|
||||
- "Back" arrow: Returns to the image context menu.
|
||||
- **Gallery Refresh:** The gallery automatically updates to show newly processed images (polling every 5 seconds).
|
||||
- **Authentication:** Users can log in and out.
|
||||
|
||||
### 5.2. Backend (Admin Panel) User Interactions
|
||||
|
||||
- **Resource Management:** Full CRUD operations for AI Models, API Providers, Styles, Users, and Roles.
|
||||
- **Toggle Status:** Enable/disable AI Models, API Providers, and Styles.
|
||||
- **Duplication:** Duplicate existing AI Models and Styles to quickly create new ones.
|
||||
- **Settings Management:** Update application-wide settings.
|
||||
|
||||
## 6. External System Interactions
|
||||
|
||||
### 6.1. AI Web Services
|
||||
|
||||
The application integrates with external AI services for image processing. The specific plugin used is determined by the `plugin` field in the `ApiProvider` model.
|
||||
|
||||
- **ComfyUI (via `App\Api\Plugins\ComfyUi.php`):**
|
||||
- **Image Upload:** Sends image data (base64 encoded) to the ComfyUI server's `/upload/image` endpoint.
|
||||
- **Prompt Queuing:** Sends the constructed workflow JSON to the ComfyUI server's `/prompt` endpoint.
|
||||
- **Result Fetching:** Polls the ComfyUI server's `/history/{prompt_id}` endpoint to check the status and retrieve the base64 encoded styled image.
|
||||
- **RunwareAI (via `App\Api\Plugins\RunwareAi.php`):**
|
||||
- **Image Upload:** Sends image data (base64 encoded) to the RunwareAI API.
|
||||
- **Style Change Request:** Sends a request with prompt, seed image UUID, model ID, and merged parameters to the RunwareAI API for image inference.
|
||||
|
||||
### 6.2. Authentication
|
||||
|
||||
- **Laravel Sanctum:** Used for API authentication, securing routes that require user login (e.g., keeping/deleting images, admin panel access).
|
||||
|
||||
## 7. Call Structure and Access Rights
|
||||
|
||||
### 7.1. API Routes (`routes/api.php`)
|
||||
|
||||
- **Publicly Accessible Routes:**
|
||||
- `GET /api/images`: Retrieves a list of images (public and temporary for unauthenticated users, all for authenticated users). Handled by `ImageController@index`.
|
||||
- `GET /api/styles`: Retrieves a list of available styles. Handled by `StyleController@index`.
|
||||
- `POST /api/images/style-change`: Initiates an AI style change request. Handled by `ImageController@styleChangeRequest`.
|
||||
- `GET /api/comfyui-url`: Retrieves the ComfyUI API URL. Handled by `ImageController@getComfyUiUrl`.
|
||||
- `GET /api/images/fetch-styled/{prompt_id}`: Fetches the result of a styled image request. Handled by `ImageController@fetchStyledImage`.
|
||||
- **Authenticated Routes (requires `auth:sanctum` middleware):**
|
||||
- `GET /api/user`: Retrieves authenticated user details.
|
||||
- `POST /api/admin/navigation-state`: Stores admin navigation state. Handled by `NavigationStateController@store`.
|
||||
- `POST /api/images/keep`: Marks a temporary styled image as permanent. Handled by `ImageController@keepImage`.
|
||||
- `DELETE /api/images/{image}`: Deletes an image. Handled by `ImageController@deleteImage`.
|
||||
- `GET /api/images/status`: Retrieves the status of an image (placeholder for future use). Handled by `ImageController@getStatus`.
|
||||
|
||||
### 7.2. Web Routes (`routes/web.php`)
|
||||
|
||||
- Standard Laravel web routes for serving the Inertia.js frontend. The main application entry point is typically handled by a route that returns an Inertia response (e.g., `Route::get('/', ...)`).
|
||||
|
||||
### 7.3. Access Rights
|
||||
|
||||
- **API Authentication:** Laravel Sanctum is used to protect API routes. Users must be authenticated to access certain functionalities.
|
||||
- **Filament Admin Panel:** Access to the `/admin` panel is controlled by Filament's built-in authentication and authorization system, typically based on user roles.
|
||||
- **Image Visibility:**
|
||||
- `is_public` flag on `Image` model: Controls whether an image is visible to unauthenticated users.
|
||||
- `is_temp` flag on `Image` model: Identifies temporary styled images. Unauthenticated users can see their own temporary images.
|
||||
- **Resource Status:**
|
||||
- `enabled` flag on `AiModel`, `ApiProvider`, and `Style` models: Controls whether these resources are active and usable within the application.
|
||||
|
||||
## 8. Detailed Description of Key Logic/Flows
|
||||
|
||||
### 8.1. Image Styling Process
|
||||
|
||||
1. **User Action (Frontend):** A user selects an image in the gallery and chooses a style from the `StyleSelector` component.
|
||||
2. **Frontend Request:** The `Home.vue` component sends an `axios.post` request to `/api/images/style-change`, including the `image_id` and `style_id`.
|
||||
3. **Backend (ImageController@styleChangeRequest):**
|
||||
- Retrieves the `Image` and `Style` models based on the provided IDs.
|
||||
- Retrieves the associated `AiModel` and `ApiProvider` from the `Style` model.
|
||||
- Loads the appropriate AI plugin (e.g., `ComfyUi`, `RunwareAi`) using `PluginLoader::getPlugin()`.
|
||||
- Calls the `processImageStyleChange()` method on the loaded plugin, passing the `Image` and `Style` models.
|
||||
4. **AI Plugin (`ComfyUi.php` or `RunwareAi.php`):**
|
||||
- **Image Upload:** The plugin first uploads the original image to the external AI service.
|
||||
- **Parameter Merging:**
|
||||
- It retrieves `parameters` from both the `AiModel` and the `Style` models.
|
||||
- These parameters (which are JSON objects) are deeply merged using `array_replace_recursive()`. This ensures that specific parameters from the style can override or extend parameters defined at the model level.
|
||||
- **Prompt Construction:** The plugin constructs the final prompt/workflow for the AI service.
|
||||
- For ComfyUI, it takes the merged parameters (workflow JSON) and performs string replacements for placeholders like `__PROMPT__` (from `Style->prompt`), `__FILENAME__` (uploaded image filename), and `__MODEL_ID__` (from `AiModel->model_id`).
|
||||
- Crucially, values for placeholders are JSON-encoded and then stripped of outer quotes to ensure safe injection into the JSON workflow, preventing syntax errors.
|
||||
- **API Call:** The plugin sends the prepared request (e.g., prompt to ComfyUI, inference request to RunwareAI) to the external AI service.
|
||||
- **Response Handling:** The plugin waits for a response from the AI service (e.g., `prompt_id` from ComfyUI, base64 image data from RunwareAI).
|
||||
5. **Backend (ImageController@styleChangeRequest - continued):**
|
||||
- Receives the initial response from the plugin (e.g., `prompt_id`).
|
||||
- Updates the `Image` record with the `comfyui_prompt_id` and `style_id` for tracking.
|
||||
- Returns a JSON response to the frontend with the `prompt_id` and `image_uuid`.
|
||||
6. **Frontend (Home.vue - Polling for Result):**
|
||||
- Upon receiving the `prompt_id`, the frontend starts polling `/api/images/fetch-styled/{prompt_id}`.
|
||||
7. **Backend (ImageController@fetchStyledImage):**
|
||||
- Retrieves the `Image` record using the `comfyui_prompt_id`.
|
||||
- Loads the relevant AI plugin.
|
||||
- Calls a method on the plugin (e.g., `waitForResult` for ComfyUI) to retrieve the final base64 encoded styled image data.
|
||||
- Decodes the base64 image, saves it to `public/storage/uploads` (e.g., `styled_UUID.png`).
|
||||
- Creates a new `Image` record in the database for the styled image, linking it to the original image and style, and setting `is_temp` to `true`.
|
||||
- Returns a JSON response with the styled image's details.
|
||||
8. **Frontend (Home.vue - Gallery Refresh):**
|
||||
- Upon successful fetching of the styled image, the frontend calls `fetchImages()` to refresh the gallery, which now includes the newly created styled image.
|
||||
|
||||
### 8.2. Image Synchronization
|
||||
|
||||
- The `ImageController@index` method performs a synchronization between the `public/storage/uploads` directory and the `images` table in the database.
|
||||
- It adds new image files found on disk to the database.
|
||||
- It removes database entries for images that no longer exist on disk.
|
||||
- This ensures the gallery always reflects the actual files in storage.
|
||||
|
||||
### 8.3. Parameter Merging Logic
|
||||
|
||||
- The `parameters` fields in `Style` and `AiModel` are stored as JSON in the database and automatically cast to PHP arrays by Eloquent.
|
||||
- In the AI plugins (`ComfyUi.php`, `RunwareAi.php`), when constructing the AI service request:
|
||||
- `$modelParams = $style->aiModel->parameters ?? [];`
|
||||
- `$styleParams = $style->parameters ?? [];`
|
||||
- `$mergedParams = array_replace_recursive($modelParams, $styleParams);`
|
||||
- This `array_replace_recursive` function is crucial for deep merging, allowing style-specific parameters to override or extend model-level parameters.
|
||||
- Placeholders (`__PROMPT__`, `__FILENAME__`, `__MODEL_ID__`) within the merged JSON are replaced with actual values. These values are first JSON-encoded and then trimmed of their outer quotes to ensure they are safely inserted into the JSON structure without breaking it.
|
||||
|
||||
### 8.4. File Storage Configuration
|
||||
|
||||
- The application uses Laravel's filesystem configuration.
|
||||
- The `public` disk is configured in `config/filesystems.php` to point directly to `public_path('storage')`.
|
||||
- Images uploaded via Filament (e.g., style previews) are stored in `public/storage/style_previews`.
|
||||
- The `public/storage` directory is a symbolic link to `storage/app/public` (though the configuration was adjusted to point directly to `public/storage` for the `public` disk).
|
||||
|
||||
This detailed overview should provide a solid foundation for understanding and rebuilding the AI StyleGallery application.
|
||||
46
Styles Flux kontext v1.md
Normal file
46
Styles Flux kontext v1.md
Normal file
@@ -0,0 +1,46 @@
|
||||
Transform the people in the image into a pastel drawing with gentle textures and soft hues.
|
||||
Transform the people in the image into an abstract painting with vibrant swirls and emotional energy.
|
||||
Transform the people in the image into pixel art characters with blocky shapes and a retro video game feel.
|
||||
Transform the people in the image into low-poly 3D models with geometric surfaces and flat shading.
|
||||
Transform the people in the image into chrome sculptures with reflective surfaces and metallic shine.
|
||||
Transform the people in the image into blueprint-style figures with line work and measurement details.
|
||||
Transform the people in the image into characters from a Studio Ghibli film with soft shading and magical warmth.
|
||||
Transform the people in the image into a comic book scene with halftone shading and dynamic poses.
|
||||
Transform the people in the image into characters from a 3D Pixar-style movie with expressive faces and big eyes.
|
||||
Transform the people in the image into characters from The Simpsons with yellow skin and over-the-top expressions.
|
||||
Transform the people in the image into LEGO-style figures with plastic texture and toy-like features.
|
||||
Transform the people in the image into South Park-style cutout characters with flat shapes and goofy faces.
|
||||
Transform the people in the image into medieval knights and nobles in a fantasy setting with ornate costumes. make the image bright
|
||||
Transform the people in the image into elves and wizards from a magical forest with glowing runes
|
||||
Transform the people in the image into steampunk characters with gears, goggles, and Victorian flair.
|
||||
Transform the people in the image into ancient stone statues with cracks and moss growing on them
|
||||
Transform the people in the image into a group of zombies with cartoony decay and goofy expressions. make the image bright.
|
||||
Transform the people in the image into carved wooden figures with natural grain patterns, warm tones, and handmade imperfections.
|
||||
Transform the people in the image into graffiti-style characters on a concrete wall, with spray paint textures, vibrant strokes, and urban flair.
|
||||
Transform the people in the image into 3D-printed sculptures with plastic textures, visible print lines, and geometric precision.
|
||||
Transform the people in the image into painted cardboard cutouts with flat surfaces, hand-drawn outlines, and theater-prop appearance.
|
||||
Transform the people in the image into figures like on an ancient Greek vase, rendered in black-figure ceramic painting with elegant poses.
|
||||
Transform the people in the image into medieval illuminated manuscript figures with gold leaf accents, stylized faces, and vibrant borders.
|
||||
Transform the people in the image into Aztec stone carvings with geometric patterns, mythological motifs, and ancient symbolism.
|
||||
Transform the people in the image into cracked porcelain dolls with aged glaze, fine fractures, and haunting fragility.
|
||||
Transform the people in the image into cardboard paper dolls with tabbed joints, illustrated features, and moveable parts.
|
||||
Transform the people in the image into stylized cut-paper collages, with torn edges, colorful flat textures, and assembled human shapes.
|
||||
Transform the people in the image into 80s disco icons with glitter outfits, exaggerated hair, shiny dance floors, and neon lighting.
|
||||
Transform the people in the image into soap opera villains mid-dramatic twist, with wind machines, sparkles, and over-the-top reactions.
|
||||
Transform the people in the image into enchanted talking teapots, cups, and kitchen objects with faces and arms, ready to sing.
|
||||
Transform the image into a sunny day in front of the Eiffel Tower in Paris, with the people posing like tourists, café tables nearby and pigeons flying around.
|
||||
Transform the image into a night scene in Tokyo’s Shibuya Crossing, with neon lights, animated billboards, and a bustling futuristic city backdrop.
|
||||
remove the background and Transform the image into a street performance in New York’s Times Square, with giant screens, yellow taxis, and amazed onlookers.
|
||||
remove the background and Transform the image into a group photo in front of the Colosseum in Rome, with sunny weather, tourists with cameras, and ancient stone textures. Keep all the face details from the original
|
||||
remove the background and Transform the image into a sunset beach party in Hawaii, with palm trees, ukuleles, tiki torches, and ocean waves.
|
||||
remove the background and Transform the image into a hike through a misty rainforest with lush green foliage, vines, and curious animals peeking from the trees.
|
||||
remove the background and Transform the image into a field of sunflowers stretching to the horizon, with bees buzzing and the people dressed in summer clothes.
|
||||
remove the background and Transform the image into a magical floating island in the sky, with waterfalls cascading into the clouds and glowing plants surrounding the group.
|
||||
remove the background and Transform the image into a dreamlike moonbase picnic with Earth visible above, moon buggies nearby, and silver spacesuits.
|
||||
remove the background and Transform the image into a carnival at night, with colorful tents, cotton candy, blinking lights, and joyful movement.
|
||||
remove the background and Transform the image into a retro 80s arcade hall, with glowing cabinets, pixelated screens, and classic synth lighting.
|
||||
remove the background and Transform the image into a Moroccan market at sunset, with patterned rugs, spices in the air, and hanging lamps lighting the scene.
|
||||
remove the background and Transform the image into a steampunk city plaza with brass towers, flying machines, and the group dressed in goggles and gears.
|
||||
remove the background and Transform the image into a colorful Holi festival in India, with powder paint in the air and everyone covered in bright colors.
|
||||
remove the background and Transform the image into a vintage movie set from the 1920s, with sepia tones, cameras on tracks, and dramatic film star poses.
|
||||
insert into styles (title,prompt,description,preview_image,created_at,ai_model_id,enabled), VALUES(*titel*, *prompt*, *beschreibung*,'uploads/ComfyUI_*laufende Nummer mit 4 führenden Nullen*_.png, NOW(), 8, 1);
|
||||
@@ -11,5 +11,5 @@ interface ApiPluginInterface
|
||||
public function disable(): bool;
|
||||
public function getStatus(string $imageUUID): array;
|
||||
public function getProgress(string $imageUUID): array;
|
||||
public function processImageStyleChange(string $imagePath, string $prompt, string $modelId, ?string $parameters = null): array;
|
||||
public function processImageStyleChange(\App\Models\Image $image, \App\Models\Style $style): array;
|
||||
}
|
||||
@@ -82,16 +82,16 @@ class ComfyUi implements ApiPluginInterface
|
||||
return $response->json();
|
||||
}
|
||||
|
||||
public function processImageStyleChange(string $imagePath, string $prompt, string $modelId, ?string $parameters = null): array
|
||||
public function processImageStyleChange(\App\Models\Image $image, \App\Models\Style $style): array
|
||||
{
|
||||
$this->logInfo('Starting ComfyUI style change process.', ['image_path' => $imagePath]);
|
||||
$this->logInfo('Starting ComfyUI style change process.', ['image_id' => $image->id, 'style_id' => $style->id]);
|
||||
|
||||
// 1. Upload image to ComfyUI
|
||||
$uploadResponse = $this->uploadImage($imagePath);
|
||||
$uploadResponse = $this->uploadImage(public_path('storage/' . $image->path));
|
||||
$filename = $uploadResponse['name'];
|
||||
|
||||
// 2. Construct the prompt
|
||||
$promptData = $this->constructPrompt($prompt, $filename, $modelId, $parameters);
|
||||
$promptData = $this->constructPrompt($style, $filename);
|
||||
|
||||
// 3. Queue the prompt
|
||||
$queueResponse = $this->queuePrompt($promptData);
|
||||
@@ -119,18 +119,39 @@ class ComfyUi implements ApiPluginInterface
|
||||
return $response->json();
|
||||
}
|
||||
|
||||
private function constructPrompt(string $prompt, string $filename, string $modelId, ?string $parameters): array
|
||||
private function constructPrompt(\App\Models\Style $style, string $filename): array
|
||||
{
|
||||
if (empty($parameters)) {
|
||||
$modelParams = $style->aiModel->parameters ?? [];
|
||||
$styleParams = $style->parameters ?? [];
|
||||
|
||||
if (empty($modelParams) && empty($styleParams)) {
|
||||
throw new \Exception('ComfyUI workflow (parameters) is missing.');
|
||||
}
|
||||
|
||||
$workflow = $parameters;
|
||||
$workflow = str_replace('__PROMPT__', $prompt, $workflow);
|
||||
$workflow = str_replace('__FILENAME__', $filename, $workflow);
|
||||
$workflow = str_replace('__MODEL_ID__', $modelId, $workflow);
|
||||
// Use array_replace_recursive for a deep merge
|
||||
$mergedParams = array_replace_recursive($modelParams, $styleParams);
|
||||
$workflow = json_encode($mergedParams);
|
||||
|
||||
return json_decode($workflow, true);
|
||||
// Properly escape the values for JSON injection
|
||||
$prompt = substr(json_encode($style->prompt, JSON_UNESCAPED_SLASHES), 1, -1);
|
||||
$filename_escaped = substr(json_encode($filename, JSON_UNESCAPED_SLASHES), 1, -1);
|
||||
$modelId_escaped = substr(json_encode($style->aiModel->model_id, JSON_UNESCAPED_SLASHES), 1, -1);
|
||||
|
||||
$workflow = str_replace('__PROMPT__', $prompt, $workflow);
|
||||
$workflow = str_replace('__FILENAME__', $filename_escaped, $workflow);
|
||||
$workflow = str_replace('__MODEL_ID__', $modelId_escaped, $workflow);
|
||||
|
||||
$decodedWorkflow = json_decode($workflow, true);
|
||||
|
||||
if (json_last_error() !== JSON_ERROR_NONE) {
|
||||
$this->logError('Failed to decode workflow JSON after placeholder replacement.', [
|
||||
'json_error' => json_last_error_msg(),
|
||||
'workflow_string' => $workflow
|
||||
]);
|
||||
throw new \Exception('Failed to construct valid ComfyUI workflow JSON: ' . json_last_error_msg());
|
||||
}
|
||||
|
||||
return $decodedWorkflow;
|
||||
}
|
||||
|
||||
private function queuePrompt(array $promptData): array
|
||||
|
||||
@@ -71,10 +71,10 @@ class RunwareAi implements ApiPluginInterface
|
||||
return ['progress' => 0];
|
||||
}
|
||||
|
||||
public function processImageStyleChange(string $imagePath, string $prompt, string $modelId, ?string $parameters = null): array
|
||||
public function processImageStyleChange(\App\Models\Image $image, \App\Models\Style $style): array
|
||||
{
|
||||
// Step 1: Upload the original image
|
||||
$uploadResult = $this->upload($imagePath);
|
||||
$uploadResult = $this->upload(public_path('storage/' . $image->path));
|
||||
|
||||
if (!isset($uploadResult['data'][0]['imageUUID'])) {
|
||||
throw new \Exception('Image upload to AI service failed or returned no UUID.');
|
||||
@@ -82,7 +82,7 @@ class RunwareAi implements ApiPluginInterface
|
||||
$seedImageUUID = $uploadResult['data'][0]['imageUUID'];
|
||||
|
||||
// Step 2: Request style change using the uploaded image's UUID
|
||||
$result = $this->styleChangeRequest($prompt, $seedImageUUID, $modelId, $parameters);
|
||||
$result = $this->styleChangeRequest($style, $seedImageUUID);
|
||||
|
||||
if (!isset($result['base64Data'])) {
|
||||
throw new \Exception('AI service did not return base64 image data.');
|
||||
@@ -127,9 +127,9 @@ class RunwareAi implements ApiPluginInterface
|
||||
}
|
||||
}
|
||||
|
||||
private function styleChangeRequest(string $prompt, string $seedImageUUID, string $modelId, ?string $parameters = null): array
|
||||
private function styleChangeRequest(\App\Models\Style $style, string $seedImageUUID): array
|
||||
{
|
||||
$this->logInfo('Attempting style change request to RunwareAI.', ['prompt' => $prompt, 'seed_image_uuid' => $seedImageUUID]);
|
||||
$this->logInfo('Attempting style change request to RunwareAI.', ['style_id' => $style->id, 'seed_image_uuid' => $seedImageUUID]);
|
||||
if (!$this->apiProvider->api_url || !$this->apiProvider->token) {
|
||||
$this->logError('RunwareAI API URL or Token not configured for style change.', ['provider_name' => $this->apiProvider->name]);
|
||||
throw new \Exception('RunwareAI API URL or Token not configured.');
|
||||
@@ -139,17 +139,20 @@ class RunwareAi implements ApiPluginInterface
|
||||
$token = $this->apiProvider->token;
|
||||
$taskUUID = (string) Str::uuid();
|
||||
|
||||
$modelParams = $style->aiModel->parameters ?? [];
|
||||
$styleParams = $style->parameters ?? [];
|
||||
$mergedParams = array_replace_recursive($modelParams, $styleParams);
|
||||
|
||||
$data = [
|
||||
'taskType' => 'imageInference',
|
||||
'taskUUID' => $taskUUID,
|
||||
'positivePrompt' => $prompt,
|
||||
'positivePrompt' => $style->prompt,
|
||||
'seedImage' => $seedImageUUID,
|
||||
'outputType' => 'base64Data',
|
||||
'model' => $modelId,
|
||||
'model' => $style->aiModel->model_id,
|
||||
];
|
||||
|
||||
$decodedParameters = json_decode($parameters, true) ?? [];
|
||||
foreach ($decodedParameters as $key => $value) {
|
||||
foreach ($mergedParams as $key => $value) {
|
||||
$data[$key] = $value;
|
||||
}
|
||||
|
||||
|
||||
@@ -47,6 +47,13 @@ class AiModelResource extends Resource
|
||||
->preload()
|
||||
->searchable(false)
|
||||
->label(__('filament.resource.ai_model.form.api_providers')),
|
||||
Forms\Components\Textarea::make('parameters')
|
||||
->label(__('filament.resource.ai_model.form.parameters'))
|
||||
->nullable()
|
||||
->rows(15)
|
||||
->json()
|
||||
->helperText(__('filament.resource.ai_model.form.parameters_help'))
|
||||
->formatStateUsing(fn (?array $state): ?string => $state ? json_encode($state, JSON_PRETTY_PRINT) : null),
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -56,7 +56,8 @@ class StyleResource extends Resource
|
||||
->nullable()
|
||||
->rows(15)
|
||||
->json()
|
||||
->helperText(__('filament.resource.style.form.parameters_help')),
|
||||
->helperText(__('filament.resource.style.form.parameters_help'))
|
||||
->formatStateUsing(fn (?array $state): ?string => $state ? json_encode($state, JSON_PRETTY_PRINT) : null),
|
||||
Select::make('ai_model_id')
|
||||
->relationship('aiModel', 'name')
|
||||
->label(__('filament.resource.style.form.ai_model'))
|
||||
|
||||
@@ -154,12 +154,7 @@ class ImageController extends Controller
|
||||
}
|
||||
$plugin = PluginLoader::getPlugin($apiProvider->plugin, $apiProvider);
|
||||
|
||||
$result = $plugin->processImageStyleChange(
|
||||
public_path('storage/' . $image->path),
|
||||
$style->prompt,
|
||||
$style->aiModel->model_id,
|
||||
$style->parameters
|
||||
);
|
||||
$result = $plugin->processImageStyleChange($image, $style);
|
||||
|
||||
// Update the image model with the ComfyUI prompt_id and style_id
|
||||
$image->comfyui_prompt_id = $result['prompt_id'];
|
||||
|
||||
@@ -13,6 +13,11 @@ class AiModel extends Model
|
||||
'name',
|
||||
'model_id',
|
||||
'model_type',
|
||||
'parameters',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'parameters' => 'array',
|
||||
];
|
||||
|
||||
public function apiProviders()
|
||||
|
||||
@@ -21,6 +21,7 @@ class Style extends Model
|
||||
|
||||
protected $casts = [
|
||||
'enabled' => 'boolean',
|
||||
'parameters' => 'array',
|
||||
];
|
||||
|
||||
public function aiModel()
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('ai_models', function (Blueprint $table) {
|
||||
$table->json('parameters')->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('ai_models', function (Blueprint $table) {
|
||||
$table->dropColumn('parameters');
|
||||
});
|
||||
}
|
||||
};
|
||||
13
package-lock.json
generated
13
package-lock.json
generated
@@ -9,7 +9,8 @@
|
||||
"@fortawesome/free-solid-svg-icons": "^7.0.0",
|
||||
"@fortawesome/vue-fontawesome": "^3.1.1",
|
||||
"laravel-echo": "^2.1.7",
|
||||
"pusher-js": "^8.4.0"
|
||||
"pusher-js": "^8.4.0",
|
||||
"vanilla-lazyload": "^19.1.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@inertiajs/vue3": "^1.0.0",
|
||||
@@ -3141,6 +3142,16 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/vanilla-lazyload": {
|
||||
"version": "19.1.3",
|
||||
"resolved": "https://registry.npmjs.org/vanilla-lazyload/-/vanilla-lazyload-19.1.3.tgz",
|
||||
"integrity": "sha512-bBMERPu2AFJc35krS+8BOhq++c6dRfL6q368lJPnkS5U92fRQagTR3FsNta69/GukfZzDwDEjD5M3U7VuSiCDw==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "individual",
|
||||
"url": "https://ko-fi.com/verlok"
|
||||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "5.4.19",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.19.tgz",
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
"@fortawesome/free-solid-svg-icons": "^7.0.0",
|
||||
"@fortawesome/vue-fontawesome": "^3.1.1",
|
||||
"laravel-echo": "^2.1.7",
|
||||
"pusher-js": "^8.4.0"
|
||||
"pusher-js": "^8.4.0",
|
||||
"vanilla-lazyload": "^19.1.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
class="style-item"
|
||||
@click="selectStyle(style)"
|
||||
>
|
||||
<img :src="'/storage/' + style.preview_image" :alt="style.title" class="style-thumbnail" />
|
||||
<img :data-src="'/storage/' + style.preview_image" :alt="style.title" class="style-thumbnail" />
|
||||
<div class="style-details">
|
||||
<h4>{{ style.title }}</h4>
|
||||
<p>{{ style.description }}</p>
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
<script setup>
|
||||
import axios from 'axios';
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { ref, onMounted, nextTick } from 'vue';
|
||||
|
||||
const styles = ref([]);
|
||||
|
||||
@@ -44,6 +44,24 @@ const fetchStyles = () => {
|
||||
axios.get('/api/styles')
|
||||
.then(response => {
|
||||
styles.value = response.data;
|
||||
nextTick(() => {
|
||||
const stylesList = document.querySelector('.styles-list');
|
||||
const observer = new IntersectionObserver((entries, observer) => {
|
||||
entries.forEach(entry => {
|
||||
if (entry.isIntersecting) {
|
||||
const img = entry.target;
|
||||
img.src = img.dataset.src;
|
||||
observer.unobserve(img);
|
||||
}
|
||||
});
|
||||
}, {
|
||||
root: stylesList // Observe intersections relative to .styles-list
|
||||
});
|
||||
|
||||
stylesList.querySelectorAll('.style-thumbnail').forEach(img => {
|
||||
observer.observe(img);
|
||||
});
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error fetching styles:', error);
|
||||
|
||||
@@ -8,7 +8,9 @@ return [
|
||||
'model_id' => 'Modell ID',
|
||||
'model_type' => 'Modell Typ',
|
||||
'enabled' => 'Aktiviert',
|
||||
'api_providers' => 'API Anbieter',
|
||||
'api_providers' => 'API Provider',
|
||||
'parameters' => 'Parameter',
|
||||
'parameters_help' => 'Für ComfyUI, fügen Sie hier das Workflow-JSON ein. Verwenden Sie __PROMPT__, __FILENAME__ und __MODEL_ID__ als Platzhalter.',
|
||||
],
|
||||
'table' => [
|
||||
'name' => 'Name',
|
||||
|
||||
@@ -9,6 +9,8 @@ return [
|
||||
'model_type' => 'Model Type',
|
||||
'enabled' => 'Enabled',
|
||||
'api_providers' => 'API Providers',
|
||||
'parameters' => 'Parameters',
|
||||
'parameters_help' => 'For ComfyUI, paste the workflow JSON here. Use __PROMPT__, __FILENAME__, and __MODEL_ID__ as placeholders.',
|
||||
],
|
||||
'table' => [
|
||||
'name' => 'Name',
|
||||
|
||||
Reference in New Issue
Block a user