rusticai-vertexai 0.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- rusticai_vertexai-0.0.1/PKG-INFO +58 -0
- rusticai_vertexai-0.0.1/README.md +38 -0
- rusticai_vertexai-0.0.1/pyproject.toml +72 -0
- rusticai_vertexai-0.0.1/src/rustic_ai/vertexai/__init__.py +0 -0
- rusticai_vertexai-0.0.1/src/rustic_ai/vertexai/agent_ext/__init__.py +0 -0
- rusticai_vertexai-0.0.1/src/rustic_ai/vertexai/agent_ext/embeddings.py +88 -0
- rusticai_vertexai-0.0.1/src/rustic_ai/vertexai/agents/__init__.py +0 -0
- rusticai_vertexai-0.0.1/src/rustic_ai/vertexai/agents/image_generation.py +81 -0
- rusticai_vertexai-0.0.1/src/rustic_ai/vertexai/client.py +63 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: rusticai-vertexai
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Rustic AI module with Vertex AI integrations
|
|
5
|
+
License: Apache-2.0
|
|
6
|
+
Author: Dragonscale Industries Inc.
|
|
7
|
+
Author-email: dev@dragonscale.ai
|
|
8
|
+
Requires-Python: >=3.12,<3.13
|
|
9
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
12
|
+
Provides-Extra: test
|
|
13
|
+
Requires-Dist: google-cloud-aiplatform (>=1.91.0,<2.0.0)
|
|
14
|
+
Requires-Dist: rusticai-core (>=0.0.8,<0.1.0)
|
|
15
|
+
Project-URL: Homepage, https://www.rustic.ai/
|
|
16
|
+
Project-URL: Repository, https://github.com/rustic-ai/rustic-ai
|
|
17
|
+
Project-URL: Rustic AI Core, https://pypi.org/project/rusticai-core/
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
|
|
20
|
+
# Rustic AI VertexAI
|
|
21
|
+
|
|
22
|
+
Rustic AI module supporting integration with [Vertex AI](https://cloud.google.com/vertex-ai)
|
|
23
|
+
|
|
24
|
+
## Prerequisites
|
|
25
|
+
1. Using this module requires a Google Cloud Account project with Vertex AI API enabled. The account must have the role `Vertex AI User`.
|
|
26
|
+
2. `gcloud CLI` for local development.
|
|
27
|
+
|
|
28
|
+
## Installing
|
|
29
|
+
|
|
30
|
+
```shell
|
|
31
|
+
pip install rusticai-vertexai
|
|
32
|
+
```
|
|
33
|
+
**Note:** It depends on [rusticai-core](https://pypi.org/project/rusticai-core/)
|
|
34
|
+
|
|
35
|
+
## Building from Source
|
|
36
|
+
|
|
37
|
+
```shell
|
|
38
|
+
poetry install --with dev
|
|
39
|
+
poetry build
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Usage
|
|
43
|
+
The library also supports using environment variables `VERTEXAI_PROJECT` and `VERTEXAI_LOCATION` in addition to the ones supported by [Google Cloud AI Platform SDK](https://cloud.google.com/python/docs/reference/aiplatform/latest)
|
|
44
|
+
```shell
|
|
45
|
+
export VERTEXAI_PROJECT=<project-id>
|
|
46
|
+
export VERTEXAI_LOCATION=<location>
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
For local development/use, ensure the account you are using has the roles/permissions of `Vertex AI User` and `Service Usage Consumer` at the minimum.
|
|
50
|
+
Login using `gcloud CLI` in a terminal
|
|
51
|
+
```shell
|
|
52
|
+
gcloud auth application-default set-quota-project <project-id>
|
|
53
|
+
gcloud auth application-default login
|
|
54
|
+
```
|
|
55
|
+
For more options/details, [refer Google Cloud docs](https://cloud.google.com/docs/authentication/set-up-adc-local-dev-environment).
|
|
56
|
+
|
|
57
|
+
On Cloud, It's recommended to use Google Workload Identity.
|
|
58
|
+
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Rustic AI VertexAI
|
|
2
|
+
|
|
3
|
+
Rustic AI module supporting integration with [Vertex AI](https://cloud.google.com/vertex-ai)
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
1. Using this module requires a Google Cloud Account project with Vertex AI API enabled. The account must have the role `Vertex AI User`.
|
|
7
|
+
2. `gcloud CLI` for local development.
|
|
8
|
+
|
|
9
|
+
## Installing
|
|
10
|
+
|
|
11
|
+
```shell
|
|
12
|
+
pip install rusticai-vertexai
|
|
13
|
+
```
|
|
14
|
+
**Note:** It depends on [rusticai-core](https://pypi.org/project/rusticai-core/)
|
|
15
|
+
|
|
16
|
+
## Building from Source
|
|
17
|
+
|
|
18
|
+
```shell
|
|
19
|
+
poetry install --with dev
|
|
20
|
+
poetry build
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
The library also supports using environment variables `VERTEXAI_PROJECT` and `VERTEXAI_LOCATION` in addition to the ones supported by [Google Cloud AI Platform SDK](https://cloud.google.com/python/docs/reference/aiplatform/latest)
|
|
25
|
+
```shell
|
|
26
|
+
export VERTEXAI_PROJECT=<project-id>
|
|
27
|
+
export VERTEXAI_LOCATION=<location>
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
For local development/use, ensure the account you are using has the roles/permissions of `Vertex AI User` and `Service Usage Consumer` at the minimum.
|
|
31
|
+
Login using `gcloud CLI` in a terminal
|
|
32
|
+
```shell
|
|
33
|
+
gcloud auth application-default set-quota-project <project-id>
|
|
34
|
+
gcloud auth application-default login
|
|
35
|
+
```
|
|
36
|
+
For more options/details, [refer Google Cloud docs](https://cloud.google.com/docs/authentication/set-up-adc-local-dev-environment).
|
|
37
|
+
|
|
38
|
+
On Cloud, It's recommended to use Google Workload Identity.
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["poetry-core"]
|
|
3
|
+
build-backend = "poetry.core.masonry.api"
|
|
4
|
+
|
|
5
|
+
[tool.poetry]
|
|
6
|
+
name = "rusticai-vertexai"
|
|
7
|
+
version = "0.0.1"
|
|
8
|
+
description = "Rustic AI module with Vertex AI integrations"
|
|
9
|
+
authors = ["Dragonscale Industries Inc. <dev@dragonscale.ai>"]
|
|
10
|
+
license = "Apache-2.0"
|
|
11
|
+
readme = "README.md"
|
|
12
|
+
homepage = "https://www.rustic.ai/"
|
|
13
|
+
repository = "https://github.com/rustic-ai/rustic-ai"
|
|
14
|
+
packages = [{ include = "rustic_ai", from = "src" }]
|
|
15
|
+
|
|
16
|
+
[tool.poetry.urls]
|
|
17
|
+
"Rustic AI Core" = "https://pypi.org/project/rusticai-core/"
|
|
18
|
+
|
|
19
|
+
[tool.poetry.dependencies]
|
|
20
|
+
python = ">=3.12,<3.13"
|
|
21
|
+
rusticai-core = { version = "0.0.8"}
|
|
22
|
+
google-cloud-aiplatform = "^1.91.0"
|
|
23
|
+
|
|
24
|
+
[tool.poetry-monorepo.deps]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
[tool.poetry.group.dev]
|
|
28
|
+
optional = true
|
|
29
|
+
|
|
30
|
+
[tool.poetry.group.dev.dependencies]
|
|
31
|
+
pytest = "^8.3.4"
|
|
32
|
+
black = { version = "^24.2.0", extras = ["jupyter"] }
|
|
33
|
+
flake8 = "^7.1.2"
|
|
34
|
+
tox = "^4.24.1"
|
|
35
|
+
isort = "^6.0.0"
|
|
36
|
+
mypy = "^1.15.0"
|
|
37
|
+
|
|
38
|
+
[tool.poetry.extras]
|
|
39
|
+
test = ["pytest"]
|
|
40
|
+
|
|
41
|
+
[tool.black]
|
|
42
|
+
line-length = 120
|
|
43
|
+
target-version = ['py312']
|
|
44
|
+
include = '\.pyi?$'
|
|
45
|
+
exclude = '''
|
|
46
|
+
/(
|
|
47
|
+
\.git
|
|
48
|
+
| \.mypy_cache
|
|
49
|
+
| \.tox
|
|
50
|
+
| \.venv
|
|
51
|
+
| _build
|
|
52
|
+
| buck-out
|
|
53
|
+
| build
|
|
54
|
+
| dist
|
|
55
|
+
)/
|
|
56
|
+
'''
|
|
57
|
+
|
|
58
|
+
[tool.mypy]
|
|
59
|
+
python_version = "3.12"
|
|
60
|
+
ignore_missing_imports = true
|
|
61
|
+
check_untyped_defs = true
|
|
62
|
+
plugins = "pydantic.mypy"
|
|
63
|
+
explicit_package_bases = true
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
[tool.isort]
|
|
67
|
+
profile = "black"
|
|
68
|
+
force_sort_within_sections = true # For the sorting order issues
|
|
69
|
+
lines_between_sections = 1 # For the newline issue
|
|
70
|
+
known_third_party = ["pydantic"]
|
|
71
|
+
known_first_party = ["rustic_ai.core", "rustic_ai.vertexai"]
|
|
72
|
+
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
from typing import List, Optional
|
|
2
|
+
|
|
3
|
+
from pydantic.config import JsonDict
|
|
4
|
+
from vertexai.language_models import TextEmbeddingInput, TextEmbeddingModel
|
|
5
|
+
|
|
6
|
+
from rustic_ai.core.guild.agent_ext.depends import DependencyResolver
|
|
7
|
+
from rustic_ai.core.guild.agent_ext.depends.embeddings import Embeddings
|
|
8
|
+
from rustic_ai.vertexai.client import VertexAIBase, VertexAIConf
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class VertexAIEmbeddingConf(VertexAIConf):
|
|
12
|
+
auto_truncate: bool = False
|
|
13
|
+
task_type: Optional[str] = None
|
|
14
|
+
output_dimensionality: Optional[int] = None
|
|
15
|
+
tokens_per_entry: int = 500
|
|
16
|
+
max_tokens_per_batch: int = 20000
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
DEFAULT_MODEL: str = "text-embedding-005"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class VertexAIEmbeddings(Embeddings, VertexAIBase):
|
|
23
|
+
def __init__(self, model: str, conf: VertexAIEmbeddingConf):
|
|
24
|
+
VertexAIBase.__init__(self, conf.project_id, conf.location)
|
|
25
|
+
self.conf = conf
|
|
26
|
+
self.model = TextEmbeddingModel.from_pretrained(model)
|
|
27
|
+
|
|
28
|
+
def _create_embedding_batches(self, text: list[str], tokens_per_entry: int) -> list[list[TextEmbeddingInput]]:
|
|
29
|
+
if not text:
|
|
30
|
+
return []
|
|
31
|
+
|
|
32
|
+
if tokens_per_entry <= 0:
|
|
33
|
+
raise ValueError("Tokens per entry must be greater than 0")
|
|
34
|
+
|
|
35
|
+
# Create batches
|
|
36
|
+
batches = []
|
|
37
|
+
current_batch = []
|
|
38
|
+
current_batch_tokens = 0
|
|
39
|
+
|
|
40
|
+
for t in text:
|
|
41
|
+
embedding_input = TextEmbeddingInput(text=t, task_type=self.conf.task_type)
|
|
42
|
+
|
|
43
|
+
# If adding this would exceed our token limit, start a new batch
|
|
44
|
+
if current_batch_tokens + tokens_per_entry < self.conf.max_tokens_per_batch:
|
|
45
|
+
# Add to current batch
|
|
46
|
+
current_batch.append(embedding_input)
|
|
47
|
+
current_batch_tokens += tokens_per_entry
|
|
48
|
+
else:
|
|
49
|
+
batches.append(current_batch)
|
|
50
|
+
current_batch = [embedding_input]
|
|
51
|
+
current_batch_tokens = tokens_per_entry
|
|
52
|
+
|
|
53
|
+
# Add the last batch if it's not empty
|
|
54
|
+
if current_batch:
|
|
55
|
+
batches.append(current_batch)
|
|
56
|
+
|
|
57
|
+
return batches
|
|
58
|
+
|
|
59
|
+
def embed(self, text: List[str]) -> List[List[float]]:
|
|
60
|
+
input_batches = self._create_embedding_batches(text, self.conf.tokens_per_entry)
|
|
61
|
+
result: List[List[float]] = []
|
|
62
|
+
for batch in input_batches:
|
|
63
|
+
embeddings = self.model.get_embeddings(
|
|
64
|
+
batch, auto_truncate=self.conf.auto_truncate, output_dimensionality=self.conf.output_dimensionality
|
|
65
|
+
)
|
|
66
|
+
for embedding in embeddings:
|
|
67
|
+
result.append(embedding.values)
|
|
68
|
+
return result
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class VertexAIEmbeddingsResolver(DependencyResolver[Embeddings]):
|
|
72
|
+
memoize_resolution: bool = False
|
|
73
|
+
|
|
74
|
+
def __init__(
|
|
75
|
+
self,
|
|
76
|
+
model_name: str = DEFAULT_MODEL,
|
|
77
|
+
conf: JsonDict = {},
|
|
78
|
+
):
|
|
79
|
+
super().__init__()
|
|
80
|
+
embedding_conf = VertexAIEmbeddingConf.model_validate(conf)
|
|
81
|
+
|
|
82
|
+
self.embedding = VertexAIEmbeddings(
|
|
83
|
+
model=model_name,
|
|
84
|
+
conf=embedding_conf,
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
def resolve(self, guild_id: str, agent_id: str) -> Embeddings:
|
|
88
|
+
return self.embedding
|
|
File without changes
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import mimetypes
|
|
2
|
+
from typing import Literal, Optional
|
|
3
|
+
import uuid
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel
|
|
6
|
+
from vertexai.vision_models import ImageGenerationModel
|
|
7
|
+
|
|
8
|
+
from rustic_ai.core import Agent, AgentMode, AgentSpec, AgentType
|
|
9
|
+
from rustic_ai.core.agents.commons.image_generation import ImageGenerationResponse
|
|
10
|
+
from rustic_ai.core.agents.commons.media import MediaLink
|
|
11
|
+
from rustic_ai.core.guild import agent
|
|
12
|
+
from rustic_ai.core.guild.agent_ext.depends.filesystem import FileSystem
|
|
13
|
+
from rustic_ai.core.guild.dsl import BaseAgentProps
|
|
14
|
+
from rustic_ai.vertexai.client import VertexAIBase, VertexAIConf
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class VertexAiImagenAgentProps(BaseAgentProps, VertexAIConf):
|
|
18
|
+
model_id: str = "imagen-3.0-fast-generate-001"
|
|
19
|
+
add_watermark: Optional[bool] = True
|
|
20
|
+
safety_filter_level: Optional[Literal["block_most", "block_some", "block_few", "block_fewest"]] = None
|
|
21
|
+
person_generation: Optional[Literal["dont_allow", "allow_adult", "allow_all"]] = None
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class VertexAiImageGenerationRequest(BaseModel):
|
|
25
|
+
prompt: str
|
|
26
|
+
negative_prompt: Optional[str] = None
|
|
27
|
+
number_of_images: int = 1
|
|
28
|
+
aspect_ratio: Optional[Literal["1:1", "9:16", "16:9", "4:3", "3:4"]] = None
|
|
29
|
+
guidance_scale: Optional[float] = None
|
|
30
|
+
language: Optional[str] = None
|
|
31
|
+
seed: Optional[int] = None
|
|
32
|
+
image_format: Optional[str] = "png"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class VertexAiImagenAgent(Agent[VertexAiImagenAgentProps], VertexAIBase):
|
|
36
|
+
def __init__(self, agent_spec: AgentSpec[VertexAiImagenAgentProps]):
|
|
37
|
+
if agent_spec.properties is None:
|
|
38
|
+
agent_spec.properties = VertexAiImagenAgentProps()
|
|
39
|
+
Agent.__init__(
|
|
40
|
+
self,
|
|
41
|
+
agent_spec=agent_spec,
|
|
42
|
+
agent_type=AgentType.BOT,
|
|
43
|
+
agent_mode=AgentMode.REMOTE,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
self.conf = agent_spec.properties
|
|
47
|
+
VertexAIBase.__init__(self, self.conf.project_id, self.conf.location)
|
|
48
|
+
print("initialized vertexai")
|
|
49
|
+
self.model = ImageGenerationModel.from_pretrained(self.conf.model_id)
|
|
50
|
+
|
|
51
|
+
@agent.processor(VertexAiImageGenerationRequest, depends_on=["filesystem:guild_fs:True"])
|
|
52
|
+
def generate_image(self, ctx: agent.ProcessContext[VertexAiImageGenerationRequest], guild_fs: FileSystem) -> None:
|
|
53
|
+
image_gen_request = ctx.payload
|
|
54
|
+
|
|
55
|
+
output_images = self.model.generate_images(
|
|
56
|
+
prompt=image_gen_request.prompt,
|
|
57
|
+
negative_prompt=image_gen_request.negative_prompt,
|
|
58
|
+
number_of_images=image_gen_request.number_of_images,
|
|
59
|
+
aspect_ratio=image_gen_request.aspect_ratio,
|
|
60
|
+
guidance_scale=image_gen_request.guidance_scale,
|
|
61
|
+
language=image_gen_request.language,
|
|
62
|
+
seed=image_gen_request.seed,
|
|
63
|
+
add_watermark=self.conf.add_watermark,
|
|
64
|
+
safety_filter_level=self.conf.safety_filter_level,
|
|
65
|
+
person_generation=self.conf.person_generation,
|
|
66
|
+
).images
|
|
67
|
+
|
|
68
|
+
result = ImageGenerationResponse(files=[], errors=[], request=image_gen_request.model_dump_json())
|
|
69
|
+
for i, image in enumerate(output_images):
|
|
70
|
+
filename = f"{uuid.uuid4()}.{image_gen_request.image_format}"
|
|
71
|
+
try:
|
|
72
|
+
with guild_fs.open(filename, "wb") as f:
|
|
73
|
+
image.save(f)
|
|
74
|
+
# Create a MediaLink object for the image
|
|
75
|
+
media_link = MediaLink(
|
|
76
|
+
url=filename, name=filename, mimetype=mimetypes.guess_type(filename)[0], on_filesystem=True
|
|
77
|
+
)
|
|
78
|
+
result.files.append(media_link)
|
|
79
|
+
except Exception as e:
|
|
80
|
+
result.errors.append(f"Failed to write image file {filename}:{e}")
|
|
81
|
+
ctx.send(result)
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
from google.cloud import aiplatform
|
|
5
|
+
from pydantic import BaseModel, ConfigDict
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class VertexAIConf(BaseModel):
|
|
9
|
+
"""Configuration class for Vertex AI.
|
|
10
|
+
|
|
11
|
+
This class is used to encapsulate configuration details required for
|
|
12
|
+
interacting with Vertex AI.
|
|
13
|
+
|
|
14
|
+
Attributes:
|
|
15
|
+
project_id (Optional[str]): The Google Cloud project ID associated with
|
|
16
|
+
Vertex AI.
|
|
17
|
+
location (Optional[str]): The region or location where the Vertex AI
|
|
18
|
+
resources are hosted.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
project_id: Optional[str] = None
|
|
22
|
+
location: Optional[str] = None
|
|
23
|
+
|
|
24
|
+
model_config = ConfigDict(arbitrary_types_allowed=True)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class VertexAIBase:
|
|
28
|
+
"""Base class for Google Cloud Vertex AI services.
|
|
29
|
+
|
|
30
|
+
This class handles the common initialization pattern for the Vertex AI SDK,
|
|
31
|
+
ensuring that it's only initialized once per runtime. Other Vertex AI
|
|
32
|
+
service implementations can inherit from this class to reuse this logic.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
_is_vertexai_initialized = False
|
|
36
|
+
|
|
37
|
+
def __init__(
|
|
38
|
+
self,
|
|
39
|
+
project_id: Optional[str] = None,
|
|
40
|
+
location: Optional[str] = None,
|
|
41
|
+
):
|
|
42
|
+
"""Initialize Vertex AI SDK if not already initialized.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
project_id (Optional[str]): The Google Cloud project ID. If None,
|
|
46
|
+
attempts to use the VERTEXAI_PROJECT environment variable.
|
|
47
|
+
location (Optional[str]): The region or location where the Vertex AI
|
|
48
|
+
resources are hosted. If None, attempts to use the VERTEXAI_LOCATION
|
|
49
|
+
environment variable.
|
|
50
|
+
|
|
51
|
+
Raises:
|
|
52
|
+
ValueError: If project_id or location cannot be determined from
|
|
53
|
+
parameters or environment variables.
|
|
54
|
+
"""
|
|
55
|
+
if not self._is_vertexai_initialized:
|
|
56
|
+
if project_id is None:
|
|
57
|
+
project_id = os.environ.get("VERTEXAI_PROJECT")
|
|
58
|
+
|
|
59
|
+
if location is None:
|
|
60
|
+
location = os.environ.get("VERTEXAI_LOCATION")
|
|
61
|
+
|
|
62
|
+
aiplatform.init(project=project_id, location=location)
|
|
63
|
+
self._is_vertexai_initialized = True
|