Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion invokeai/app/services/model_records/model_records_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from pathlib import Path
from typing import List, Optional, Set, Union

from pydantic import BaseModel, Field
from pydantic import BaseModel, Field, field_validator

from invokeai.app.services.shared.pagination import PaginatedResults
from invokeai.app.util.model_exclude_null import BaseModelExcludeNull
Expand Down Expand Up @@ -77,6 +77,16 @@ class ModelRecordChanges(BaseModelExcludeNull):
source: Optional[str] = Field(description="original source of the model", default=None)
source_type: Optional[ModelSourceType] = Field(description="type of model source", default=None)
source_api_response: Optional[str] = Field(description="metadata from remote source", default=None)
source_url: Optional[str] = Field(description="Optional URL for the model (e.g. download page)", default=None)

@field_validator("source_url", mode="before")
@classmethod
def validate_source_url(cls, v: Optional[str]) -> Optional[str]:
if v is not None and v != "":
if not v.startswith(("https://", "http://")):
raise ValueError("source_url must be an http or https URL")
return v or None

name: Optional[str] = Field(description="Name of the model.", default=None)
path: Optional[str] = Field(description="Path to the model.", default=None)
description: Optional[str] = Field(description="Model description", default=None)
Expand Down
15 changes: 14 additions & 1 deletion invokeai/backend/model_manager/configs/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
Type,
)

from pydantic import BaseModel, ConfigDict, Field, Tag
from pydantic import BaseModel, ConfigDict, Field, Tag, field_validator
from pydantic_core import PydanticUndefined

from invokeai.app.util.misc import uuid_string
Expand Down Expand Up @@ -77,6 +77,19 @@ class Config_Base(ABC, BaseModel):
default=None,
description="The original API response from the source, as stringified JSON.",
)
source_url: str | None = Field(
default=None,
description="Optional URL for the model (e.g. download page or model page).",
)

@field_validator("source_url", mode="before")
@classmethod
def validate_source_url(cls, v: str | None) -> str | None:
if v is not None and v != "":
if not v.startswith(("https://", "http://")):
raise ValueError("source_url must be an http or https URL")
return v or None

cover_image: str | None = Field(
default=None,
description="Url for image to preview model",
Expand Down
1 change: 1 addition & 0 deletions invokeai/frontend/web/public/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1192,6 +1192,7 @@
"settings": "Settings",
"simpleModelPlaceholder": "URL or path to a local file or diffusers folder",
"source": "Source",
"sourceUrl": "Source URL",
"sigLip": "SigLIP",
"spandrelImageToImage": "Image to Image (Spandrel)",
"starterBundles": "Starter Bundles",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,12 @@ export const ModelEdit = memo(({ modelConfig }: Props) => {
<Textarea {...form.register('description')} minH={32} />
</FormControl>
</Flex>
<Flex gap="4" alignItems="center">
<FormControl flexDir="column" alignItems="flex-start" gap={1}>
<FormLabel>{t('modelManager.sourceUrl')}</FormLabel>
<Input {...form.register('source_url')} size="md" placeholder="https://" />
</FormControl>
</Flex>
<Heading as="h3" fontSize="md" mt="4">
{t('modelManager.modelSettings')}
</Heading>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import { Flex, Heading, Spacer, Text } from '@invoke-ai/ui-library';
import { Flex, Heading, Link, Spacer, Text } from '@invoke-ai/ui-library';
import { useIsModelManagerEnabled } from 'features/modelManagerV2/hooks/useIsModelManagerEnabled';
import ModelImageUpload from 'features/modelManagerV2/subpanels/ModelPanel/Fields/ModelImageUpload';
import type { PropsWithChildren } from 'react';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import type { AnyModelConfig } from 'services/api/types';

const isSafeUrl = (url: string): boolean => {
return url.startsWith('https://') || url.startsWith('http://');
};

type Props = PropsWithChildren<{
modelConfig: AnyModelConfig;
}>;
Expand All @@ -30,6 +34,14 @@ export const ModelHeader = memo(({ modelConfig, children }: Props) => {
{t('modelManager.source')}: {modelConfig.source}
</Text>
)}
{'source_url' in modelConfig && modelConfig.source_url && isSafeUrl(modelConfig.source_url) && (
<Text variant="subtext" noOfLines={1} wordBreak="break-all">
{t('modelManager.sourceUrl')}:{' '}
<Link href={modelConfig.source_url} isExternal color="invokeBlue.300">
{modelConfig.source_url}
</Link>
</Text>
)}
<Text noOfLines={3}>{modelConfig.description}</Text>
</Flex>
</Flex>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -657,6 +657,7 @@ describe('Graph', () => {
source: '/home/bat/invokeai-4.0.0/models/sdxl/main/stable-diffusion-xl-1.0-inpainting-0.1',
source_type: 'path',
source_api_response: null,
source_url: null,
cover_image: null,
type: 'main',
trigger_phrases: null,
Expand Down
Loading
Loading