auris_tools 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.
Potentially problematic release.
This version of auris_tools might be problematic. Click here for more details.
- auris_tools-0.0.1/PKG-INFO +76 -0
- auris_tools-0.0.1/README.md +56 -0
- auris_tools-0.0.1/auris_tools/__init__.py +0 -0
- auris_tools-0.0.1/auris_tools/configuration.py +81 -0
- auris_tools-0.0.1/auris_tools/databaseHandlers.py +132 -0
- auris_tools-0.0.1/auris_tools/geminiHandler.py +245 -0
- auris_tools-0.0.1/auris_tools/officeWordHandler.py +271 -0
- auris_tools-0.0.1/auris_tools/storageHandler.py +195 -0
- auris_tools-0.0.1/auris_tools/textractHandler.py +169 -0
- auris_tools-0.0.1/auris_tools/utils.py +120 -0
- auris_tools-0.0.1/pyproject.toml +54 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: auris_tools
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: The swiss knife tools to coordinates cloud frameworks with an easy for Auris platforms
|
|
5
|
+
Author: Antonio Senra
|
|
6
|
+
Author-email: acsenrafilho@gmail.com
|
|
7
|
+
Requires-Python: >=3.10,<4.0
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
13
|
+
Requires-Dist: boto3 (>=1.40.29,<2.0.0)
|
|
14
|
+
Requires-Dist: dotenv (>=0.9.9,<0.10.0)
|
|
15
|
+
Requires-Dist: google-generativeai (>=0.8.5,<0.9.0)
|
|
16
|
+
Requires-Dist: python-docx (>=1.2.0,<2.0.0)
|
|
17
|
+
Requires-Dist: rich (>=14.1.0,<15.0.0)
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
|
|
20
|
+
# auris-tools
|
|
21
|
+
|
|
22
|
+
The swiss knife tools to coordinates cloud frameworks with an easy for Auris platforms
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
This project requires **Python 3.10** and uses [Poetry](https://python-poetry.org/) for dependency management.
|
|
27
|
+
|
|
28
|
+
1. **Clone the repository:**
|
|
29
|
+
```bash
|
|
30
|
+
git clone https://github.com/AurisAASI/auris-tools.git
|
|
31
|
+
cd auris-tools
|
|
32
|
+
```
|
|
33
|
+
2. **Install Poetry (if not already installed):**
|
|
34
|
+
```bash
|
|
35
|
+
pip install poetry
|
|
36
|
+
```
|
|
37
|
+
3. **Install dependencies:**
|
|
38
|
+
```bash
|
|
39
|
+
poetry install
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Project Structure
|
|
45
|
+
|
|
46
|
+
The main classes and modules are organized as follows:
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
/auris_tools
|
|
50
|
+
├── __init__.py
|
|
51
|
+
├── configuration.py # AWS configuration utilities
|
|
52
|
+
├── databaseHandlers.py # DynamoDB handler class
|
|
53
|
+
├── officeWordHandler.py # Office Word document handler
|
|
54
|
+
├── storageHandler.py # AWS S3 storage handler
|
|
55
|
+
├── textractHandler.py # AWS Textract handler
|
|
56
|
+
├── utils.py # Utility functions
|
|
57
|
+
├── geminiHandler.py # Google Gemini AI handler
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## Testing & Linting
|
|
63
|
+
|
|
64
|
+
- **Run all tests:**
|
|
65
|
+
```bash
|
|
66
|
+
task test
|
|
67
|
+
```
|
|
68
|
+
- **Run linter (ruff):**
|
|
69
|
+
```bash
|
|
70
|
+
task lint
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Test coverage and linting are enforced in CI. Make sure all tests pass and code is linted before submitting a PR.
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# auris-tools
|
|
2
|
+
|
|
3
|
+
The swiss knife tools to coordinates cloud frameworks with an easy for Auris platforms
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
This project requires **Python 3.10** and uses [Poetry](https://python-poetry.org/) for dependency management.
|
|
8
|
+
|
|
9
|
+
1. **Clone the repository:**
|
|
10
|
+
```bash
|
|
11
|
+
git clone https://github.com/AurisAASI/auris-tools.git
|
|
12
|
+
cd auris-tools
|
|
13
|
+
```
|
|
14
|
+
2. **Install Poetry (if not already installed):**
|
|
15
|
+
```bash
|
|
16
|
+
pip install poetry
|
|
17
|
+
```
|
|
18
|
+
3. **Install dependencies:**
|
|
19
|
+
```bash
|
|
20
|
+
poetry install
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Project Structure
|
|
26
|
+
|
|
27
|
+
The main classes and modules are organized as follows:
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
/auris_tools
|
|
31
|
+
├── __init__.py
|
|
32
|
+
├── configuration.py # AWS configuration utilities
|
|
33
|
+
├── databaseHandlers.py # DynamoDB handler class
|
|
34
|
+
├── officeWordHandler.py # Office Word document handler
|
|
35
|
+
├── storageHandler.py # AWS S3 storage handler
|
|
36
|
+
├── textractHandler.py # AWS Textract handler
|
|
37
|
+
├── utils.py # Utility functions
|
|
38
|
+
├── geminiHandler.py # Google Gemini AI handler
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Testing & Linting
|
|
44
|
+
|
|
45
|
+
- **Run all tests:**
|
|
46
|
+
```bash
|
|
47
|
+
task test
|
|
48
|
+
```
|
|
49
|
+
- **Run linter (ruff):**
|
|
50
|
+
```bash
|
|
51
|
+
task lint
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Test coverage and linting are enforced in CI. Make sure all tests pass and code is linted before submitting a PR.
|
|
55
|
+
|
|
56
|
+
---
|
|
File without changes
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import os
|
|
3
|
+
|
|
4
|
+
from dotenv import load_dotenv
|
|
5
|
+
|
|
6
|
+
# Load environment variables from .env file
|
|
7
|
+
load_dotenv()
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class AWSConfiguration:
|
|
11
|
+
"""
|
|
12
|
+
AWS Configuration class that handles credentials and region settings.
|
|
13
|
+
Prioritizes environment variables over constructor parameters.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def __init__(
|
|
17
|
+
self,
|
|
18
|
+
access_key: str = None,
|
|
19
|
+
secret_key: str = None,
|
|
20
|
+
region: str = None,
|
|
21
|
+
profile: str = None,
|
|
22
|
+
endpoint_url: str = None,
|
|
23
|
+
):
|
|
24
|
+
# Try to get credentials from environment variables first
|
|
25
|
+
self.access_key = (
|
|
26
|
+
access_key if access_key else os.environ.get('AWS_ACCESS_KEY_ID')
|
|
27
|
+
)
|
|
28
|
+
self.secret_key = (
|
|
29
|
+
secret_key
|
|
30
|
+
if secret_key
|
|
31
|
+
else os.environ.get('AWS_SECRET_ACCESS_KEY')
|
|
32
|
+
)
|
|
33
|
+
self.region = (
|
|
34
|
+
region
|
|
35
|
+
if region
|
|
36
|
+
else os.environ.get('AWS_DEFAULT_REGION') or 'us-east-1'
|
|
37
|
+
)
|
|
38
|
+
self.profile = profile if profile else os.environ.get('AWS_PROFILE')
|
|
39
|
+
self.endpoint_url = (
|
|
40
|
+
endpoint_url
|
|
41
|
+
if endpoint_url
|
|
42
|
+
else os.environ.get('AWS_ENDPOINT_URL')
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
# Validate configuration
|
|
46
|
+
self._validate_config()
|
|
47
|
+
|
|
48
|
+
def _validate_config(self):
|
|
49
|
+
"""Validate that we have enough configuration to proceed."""
|
|
50
|
+
if not ((self.access_key and self.secret_key) or self.profile):
|
|
51
|
+
logging.warning(
|
|
52
|
+
'No AWS credentials provided via environment variables or constructor. '
|
|
53
|
+
'AWS operations may fail unless credentials are configured via '
|
|
54
|
+
'~/.aws/credentials, IAM roles, or other AWS credential providers.'
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
def get_boto3_session_args(self):
|
|
58
|
+
"""
|
|
59
|
+
Return a dictionary of arguments that can be passed to boto3.session.Session()
|
|
60
|
+
"""
|
|
61
|
+
session_args = {'region_name': self.region}
|
|
62
|
+
|
|
63
|
+
if self.access_key and self.secret_key:
|
|
64
|
+
session_args['aws_access_key_id'] = self.access_key
|
|
65
|
+
session_args['aws_secret_access_key'] = self.secret_key
|
|
66
|
+
|
|
67
|
+
if self.profile:
|
|
68
|
+
session_args['profile_name'] = self.profile
|
|
69
|
+
|
|
70
|
+
return session_args
|
|
71
|
+
|
|
72
|
+
def get_client_args(self):
|
|
73
|
+
"""
|
|
74
|
+
Return a dictionary of arguments that can be passed to boto3 client creation
|
|
75
|
+
"""
|
|
76
|
+
client_args = {}
|
|
77
|
+
|
|
78
|
+
if self.endpoint_url:
|
|
79
|
+
client_args['endpoint_url'] = self.endpoint_url
|
|
80
|
+
|
|
81
|
+
return client_args
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
import boto3
|
|
4
|
+
from boto3.dynamodb.types import TypeDeserializer, TypeSerializer
|
|
5
|
+
|
|
6
|
+
from auris_tools.configuration import AWSConfiguration
|
|
7
|
+
from auris_tools.utils import generate_uuid
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class DatabaseHandler:
|
|
11
|
+
def __init__(self, table_name, config=None):
|
|
12
|
+
"""
|
|
13
|
+
Initialize the database handler.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
table_name: Name of the DynamoDB table.
|
|
17
|
+
config: An AWSConfiguration object, or None to use environment variables.
|
|
18
|
+
"""
|
|
19
|
+
self.table_name = table_name
|
|
20
|
+
if config is None:
|
|
21
|
+
config = AWSConfiguration()
|
|
22
|
+
|
|
23
|
+
# Create a boto3 session with the configuration
|
|
24
|
+
session = boto3.session.Session(**config.get_boto3_session_args())
|
|
25
|
+
|
|
26
|
+
# Create a DynamoDB client with additional configuration if needed
|
|
27
|
+
self.client = session.client('dynamodb', **config.get_client_args())
|
|
28
|
+
|
|
29
|
+
if not self._check_table_exists(table_name):
|
|
30
|
+
raise Exception(f'Table does not exist: {table_name}')
|
|
31
|
+
|
|
32
|
+
logging.info(f'Initialized DynamoDB client in region {config.region}')
|
|
33
|
+
|
|
34
|
+
def insert_item(self, item, primary_key: str = 'id'):
|
|
35
|
+
"""Insert an item with automatic type conversion"""
|
|
36
|
+
if not isinstance(item, dict):
|
|
37
|
+
raise TypeError('Item must be a dictionary')
|
|
38
|
+
|
|
39
|
+
if primary_key not in item:
|
|
40
|
+
item[primary_key] = generate_uuid()
|
|
41
|
+
|
|
42
|
+
dynamo_item = self._serialize_item(item)
|
|
43
|
+
response = self.client.put_item(
|
|
44
|
+
TableName=self.table_name, Item=dynamo_item
|
|
45
|
+
)
|
|
46
|
+
return response
|
|
47
|
+
|
|
48
|
+
def get_item(self, key):
|
|
49
|
+
"""
|
|
50
|
+
Retrieve an item from a DynamoDB table.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
key: A dictionary representing the key of the item to retrieve.
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
The retrieved item, or None if not found.
|
|
57
|
+
"""
|
|
58
|
+
if not isinstance(key, dict):
|
|
59
|
+
raise TypeError('Key must be a dictionary')
|
|
60
|
+
|
|
61
|
+
# Check if the key is in DynamoDB format (i.e., values are dicts with type keys)
|
|
62
|
+
if not all(isinstance(v, dict) and len(v) == 1 for v in key.values()):
|
|
63
|
+
# Convert to DynamoDB format
|
|
64
|
+
key = self._serialize_item(key)
|
|
65
|
+
|
|
66
|
+
try:
|
|
67
|
+
response = self.client.get_item(TableName=self.table_name, Key=key)
|
|
68
|
+
return response.get('Item')
|
|
69
|
+
except Exception as e:
|
|
70
|
+
logging.error(
|
|
71
|
+
f'Error retrieving item from {self.table_name}: {str(e)}'
|
|
72
|
+
)
|
|
73
|
+
return None
|
|
74
|
+
|
|
75
|
+
def delete_item(self, key, primary_key='id'):
|
|
76
|
+
"""
|
|
77
|
+
Delete an item from a DynamoDB table.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
key (str or dict): Either a string identifier for the primary key,
|
|
81
|
+
or a dictionary containing the complete key structure.
|
|
82
|
+
primary_key (str, optional): Name of the primary key field. Defaults to 'id'.
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
bool: True if deletion was successful, False otherwise.
|
|
86
|
+
"""
|
|
87
|
+
# Convert string key to a dictionary with the primary key
|
|
88
|
+
if isinstance(key, str):
|
|
89
|
+
key = {primary_key: key}
|
|
90
|
+
elif not isinstance(key, dict):
|
|
91
|
+
raise TypeError('Key must be a string identifier or a dictionary')
|
|
92
|
+
|
|
93
|
+
# Check if the key is in DynamoDB format
|
|
94
|
+
if not self.item_is_serialized(key):
|
|
95
|
+
key = self._serialize_item(key)
|
|
96
|
+
|
|
97
|
+
try:
|
|
98
|
+
self.client.delete_item(
|
|
99
|
+
TableName=self.table_name,
|
|
100
|
+
Key=key,
|
|
101
|
+
ReturnValues='ALL_OLD', # Return the deleted item
|
|
102
|
+
)
|
|
103
|
+
logging.info(f'Deleted item from {self.table_name} with key {key}')
|
|
104
|
+
return True
|
|
105
|
+
except Exception as e:
|
|
106
|
+
logging.error(
|
|
107
|
+
f'Error deleting item from {self.table_name}: {str(e)}'
|
|
108
|
+
)
|
|
109
|
+
return False
|
|
110
|
+
|
|
111
|
+
def item_is_serialized(self, item):
|
|
112
|
+
"""Check if an item is in DynamoDB serialized format"""
|
|
113
|
+
return all(isinstance(v, dict) and len(v) == 1 for v in item.values())
|
|
114
|
+
|
|
115
|
+
def _serialize_item(self, item):
|
|
116
|
+
"""Convert Python types to DynamoDB format"""
|
|
117
|
+
serializer = TypeSerializer()
|
|
118
|
+
return {k: serializer.serialize(v) for k, v in item.items()}
|
|
119
|
+
|
|
120
|
+
def _deserialize_item(self, item):
|
|
121
|
+
"""Convert DynamoDB format back to Python types"""
|
|
122
|
+
deserializer = TypeDeserializer()
|
|
123
|
+
return {k: deserializer.deserialize(v) for k, v in item.items()}
|
|
124
|
+
|
|
125
|
+
def _check_table_exists(self, table_name):
|
|
126
|
+
"""Check if a DynamoDB table exists"""
|
|
127
|
+
try:
|
|
128
|
+
existing_tables = self.client.list_tables().get('TableNames', [])
|
|
129
|
+
return table_name in existing_tables
|
|
130
|
+
except Exception as e:
|
|
131
|
+
logging.error(f'Error checking table existence: {str(e)}')
|
|
132
|
+
return False
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import os
|
|
3
|
+
|
|
4
|
+
import google.generativeai as genai
|
|
5
|
+
from dotenv import load_dotenv
|
|
6
|
+
|
|
7
|
+
# Load environment variables from .env file
|
|
8
|
+
load_dotenv()
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class GoogleGeminiHandler:
|
|
14
|
+
"""A handler class for interacting with Google's Gemini AI models.
|
|
15
|
+
|
|
16
|
+
This class provides a convenient interface for generating content using Google's
|
|
17
|
+
Gemini generative AI models. It handles authentication, model configuration,
|
|
18
|
+
and content generation with automatic error handling and logging.
|
|
19
|
+
|
|
20
|
+
Attributes:
|
|
21
|
+
api_key (str): The Google AI API key used for authentication.
|
|
22
|
+
model_name (str): The name of the Gemini model to use.
|
|
23
|
+
temperature (float): Controls randomness in generation (0.0 to 1.0).
|
|
24
|
+
response_schema (dict): Optional schema for structured responses.
|
|
25
|
+
response_mime_type (str): MIME type for response format.
|
|
26
|
+
generation_config (genai.types.GenerationConfig): Configuration for content generation.
|
|
27
|
+
model (genai.GenerativeModel): The configured Gemini model instance.
|
|
28
|
+
|
|
29
|
+
Example:
|
|
30
|
+
Basic usage with environment variable API key:
|
|
31
|
+
|
|
32
|
+
>>> handler = GoogleGeminiHandler()
|
|
33
|
+
>>> response = handler.generate_output("What is artificial intelligence?")
|
|
34
|
+
>>> text = handler.get_text(response)
|
|
35
|
+
|
|
36
|
+
Usage with custom parameters:
|
|
37
|
+
|
|
38
|
+
>>> handler = GoogleGeminiHandler(
|
|
39
|
+
... api_key="your-api-key",
|
|
40
|
+
... model="gemini-2.0-flash-exp",
|
|
41
|
+
... temperature=0.7,
|
|
42
|
+
... response_mime_type="text/plain"
|
|
43
|
+
... )
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
def __init__(
|
|
47
|
+
self, api_key: str = None, model: str = 'gemini-2.5-flash', **kwargs
|
|
48
|
+
):
|
|
49
|
+
"""Initialize the Google Gemini handler.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
api_key (str, optional): Google AI API key. If not provided, will attempt
|
|
53
|
+
to load from GEMINI_API_KEY environment variable. Defaults to None.
|
|
54
|
+
model (str, optional): Name of the Gemini model to use.
|
|
55
|
+
Defaults to 'gemini-2.5-flash'.
|
|
56
|
+
**kwargs: Additional configuration parameters:
|
|
57
|
+
- temperature (float): Controls randomness (0.0-1.0). Defaults to 0.5.
|
|
58
|
+
- response_schema (dict): Schema for structured responses. Defaults to None.
|
|
59
|
+
- response_mime_type (str): Response MIME type. Defaults to 'application/json'.
|
|
60
|
+
|
|
61
|
+
Raises:
|
|
62
|
+
TypeError: If the specified model is not available.
|
|
63
|
+
|
|
64
|
+
Example:
|
|
65
|
+
>>> handler = GoogleGeminiHandler(
|
|
66
|
+
... api_key="your-api-key",
|
|
67
|
+
... model="gemini-2.0-flash-exp",
|
|
68
|
+
... temperature=0.7
|
|
69
|
+
... )
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
self.api_key = api_key if api_key else os.getenv('GEMINI_API_KEY')
|
|
73
|
+
if self.api_key is None:
|
|
74
|
+
logger.error(
|
|
75
|
+
'Gemini API key not configured. Please, define the GEMINI_API_KEY environment variable or enter your key directly in the code.'
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
self.model_name = model
|
|
79
|
+
self._check_model_availability()
|
|
80
|
+
|
|
81
|
+
# More configuration from input parameters
|
|
82
|
+
self.temperature = kwargs.get('temperature', 0.5)
|
|
83
|
+
self.response_schema = kwargs.get('response_schema', None)
|
|
84
|
+
self.response_mime_type = kwargs.get(
|
|
85
|
+
'response_mime_type', 'application/json'
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
self.generation_config = genai.types.GenerationConfig(
|
|
89
|
+
temperature=self.temperature,
|
|
90
|
+
response_schema=self.response_schema,
|
|
91
|
+
response_mime_type=self.response_mime_type,
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
self.model = genai.GenerativeModel(
|
|
95
|
+
generation_config=self.generation_config,
|
|
96
|
+
model_name=self.model_name,
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
def generate_output(
|
|
100
|
+
self, prompt: str, input_data: str = None, input_mime_type: str = None
|
|
101
|
+
):
|
|
102
|
+
"""Generate content using the configured Gemini model.
|
|
103
|
+
|
|
104
|
+
This method sends a prompt to the Gemini model and returns the generated response.
|
|
105
|
+
It supports both text-only prompts and multimodal inputs with additional data.
|
|
106
|
+
|
|
107
|
+
Args:
|
|
108
|
+
prompt (str): The text prompt to send to the model. This is the main
|
|
109
|
+
instruction or question for the AI to respond to.
|
|
110
|
+
input_data (str, optional): Additional input data to include with the prompt.
|
|
111
|
+
This could be text content, encoded media, or other data. Requires
|
|
112
|
+
input_mime_type to be specified. Defaults to None.
|
|
113
|
+
input_mime_type (str, optional): MIME type of the input_data. Required if
|
|
114
|
+
input_data is provided. Examples: 'text/plain', 'image/jpeg',
|
|
115
|
+
'application/pdf'. Defaults to None.
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
genai.types.GenerateContentResponse or str: The response from the Gemini model
|
|
119
|
+
if successful, or an empty string if an error occurred.
|
|
120
|
+
|
|
121
|
+
Raises:
|
|
122
|
+
ValueError: If input_data is provided without input_mime_type or vice versa.
|
|
123
|
+
|
|
124
|
+
Example:
|
|
125
|
+
Text-only generation:
|
|
126
|
+
|
|
127
|
+
>>> response = handler.generate_output("Explain quantum computing")
|
|
128
|
+
|
|
129
|
+
Multimodal generation with additional data:
|
|
130
|
+
|
|
131
|
+
>>> response = handler.generate_output(
|
|
132
|
+
... prompt="Describe this image",
|
|
133
|
+
... input_data=base64_encoded_image,
|
|
134
|
+
... input_mime_type="image/jpeg"
|
|
135
|
+
... )
|
|
136
|
+
"""
|
|
137
|
+
if (input_data is not None and input_mime_type is None) or (
|
|
138
|
+
input_data is None and input_mime_type is not None
|
|
139
|
+
):
|
|
140
|
+
raise ValueError(
|
|
141
|
+
'input_mime_type must be provided if input_data is given, or otherwise both must be None.'
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
if input_data and input_mime_type: # Add input data if provided
|
|
145
|
+
prompt = [
|
|
146
|
+
prompt,
|
|
147
|
+
{'mime_type': input_mime_type, 'content': input_data},
|
|
148
|
+
]
|
|
149
|
+
|
|
150
|
+
try:
|
|
151
|
+
response = self.model.generate_content(prompt)
|
|
152
|
+
return response
|
|
153
|
+
except Exception as e:
|
|
154
|
+
logger.error(f'Error generating LLM output: {str(e)}')
|
|
155
|
+
return ''
|
|
156
|
+
|
|
157
|
+
def get_text(self, response) -> str:
|
|
158
|
+
"""Extract text content from a Gemini model response.
|
|
159
|
+
|
|
160
|
+
This method parses the response object returned by the Gemini model and
|
|
161
|
+
extracts the generated text content. It handles the response structure
|
|
162
|
+
safely and provides fallbacks for various response formats.
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
response (genai.types.GenerateContentResponse or dict): The response object
|
|
166
|
+
returned from the generate_output method. This can be either a
|
|
167
|
+
GenerateContentResponse object or a dictionary representation.
|
|
168
|
+
|
|
169
|
+
Returns:
|
|
170
|
+
str: The extracted text content from the response. Returns an empty
|
|
171
|
+
string if no content is found or if an error occurs during extraction.
|
|
172
|
+
|
|
173
|
+
Example:
|
|
174
|
+
>>> response = handler.generate_output("What is AI?")
|
|
175
|
+
>>> text_content = handler.get_text(response)
|
|
176
|
+
>>> print(text_content)
|
|
177
|
+
"Artificial Intelligence (AI) refers to..."
|
|
178
|
+
|
|
179
|
+
>>> # Handle case with no candidates
|
|
180
|
+
>>> empty_response = {'candidates': []}
|
|
181
|
+
>>> text = handler.get_text(empty_response)
|
|
182
|
+
>>> print(text) # Returns empty string
|
|
183
|
+
""
|
|
184
|
+
"""
|
|
185
|
+
try:
|
|
186
|
+
if 'candidates' in response and len(response['candidates']) > 0:
|
|
187
|
+
return response['candidates'][0]['content']
|
|
188
|
+
else:
|
|
189
|
+
logger.warning('No candidates found in the response.')
|
|
190
|
+
return ''
|
|
191
|
+
except Exception as e:
|
|
192
|
+
logger.error(f'Error extracting text from response: {str(e)}')
|
|
193
|
+
return ''
|
|
194
|
+
|
|
195
|
+
def _check_model_availability(self):
|
|
196
|
+
"""Check if the specified Gemini model is available.
|
|
197
|
+
|
|
198
|
+
This private method validates that the requested model name exists in the
|
|
199
|
+
list of available Google Gemini models. It queries the Google AI API to
|
|
200
|
+
get the current list of available models and compares against the requested
|
|
201
|
+
model name.
|
|
202
|
+
|
|
203
|
+
Raises:
|
|
204
|
+
TypeError: If the specified model is not found in the list of available
|
|
205
|
+
models from the Google AI API.
|
|
206
|
+
|
|
207
|
+
Note:
|
|
208
|
+
This method is called automatically during initialization and will
|
|
209
|
+
prevent the handler from being created if an invalid model is specified.
|
|
210
|
+
It also logs the availability check results for debugging purposes.
|
|
211
|
+
|
|
212
|
+
Example:
|
|
213
|
+
This method is called internally during initialization:
|
|
214
|
+
|
|
215
|
+
>>> # This will call _check_model_availability internally
|
|
216
|
+
>>> handler = GoogleGeminiHandler(model="gemini-2.5-flash") # Success
|
|
217
|
+
>>> handler = GoogleGeminiHandler(model="invalid-model") # Raises TypeError
|
|
218
|
+
"""
|
|
219
|
+
try:
|
|
220
|
+
available_models = genai.list_models()
|
|
221
|
+
# Extract model names and handle the 'models/' prefix
|
|
222
|
+
available_model_names = []
|
|
223
|
+
for model in available_models:
|
|
224
|
+
model_name = model.name
|
|
225
|
+
# Remove 'models/' prefix if present
|
|
226
|
+
if model_name.startswith('models/'):
|
|
227
|
+
model_name = model_name[7:] # Remove 'models/' prefix
|
|
228
|
+
available_model_names.append(model_name)
|
|
229
|
+
|
|
230
|
+
if self.model_name not in available_model_names:
|
|
231
|
+
logger.error(
|
|
232
|
+
f'Model {self.model_name} is not available. Please check the model name.'
|
|
233
|
+
)
|
|
234
|
+
logger.info(
|
|
235
|
+
f'Available models: {", ".join(available_model_names)}'
|
|
236
|
+
)
|
|
237
|
+
raise TypeError(f'Invalid model name: {self.model_name}')
|
|
238
|
+
else:
|
|
239
|
+
logger.info(f'Model {self.model_name} is available.')
|
|
240
|
+
except Exception as e:
|
|
241
|
+
if 'Invalid model name' in str(e):
|
|
242
|
+
raise # Re-raise our custom error
|
|
243
|
+
else:
|
|
244
|
+
logger.error(f'Error checking model availability: {str(e)}')
|
|
245
|
+
# Don't raise error for API connectivity issues, just log
|