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.
@@ -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
+
@@ -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
@@ -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