Google Vertex AI
Fine-tuned to conduct natural conversation. Using Google Vertex Ai (PaLM API for Chat). The current integration of Google Vertex Ai is focusing only on the Generative AI model (PaLM) using the Chat prediction. Later, this plugin will be updated to include the following: - Model Creation - Model Fine Tuning - PaLM Text.
Data Enrichment & Threat Intelligence · Google Vertex AI
Details
| ID | Google Vertex AI |
|---|---|
| Provider | |
| Category | Data Enrichment & Threat Intelligence |
| From Version | 5.0.0 |
| Docker Image | demisto/googleapi-python3:1.0.0.115085 |
| Supported Modules | Agentix XSIAM |
README
Google Vertex AI
Integration Author: Sameh El-Hakim
Fine-tuned to conduct natural conversation. Using Google Vertex Ai (PaLM API for Chat)
The current integration of Google Vertex Ai is focusing only on the Generative AI model (PaLM) using the Chat prediction.
Later, this plugin will be updated to include the following:
PaLM for Text (Once the New API is released to public from Google will be modified to support quick integration)
Access to Model Garden through Playbooks
Model Development
Once the New API for (PaLM for Chat & Text) is released to the public from Google, then this integration will be modified to support quick integration. This integration is using an early version of Generative AI API from Google. So, you are expected to face some challenges.
***
The setup steps as following
- Create a new project on Google Cloud (Recommended instead of using existing project)
- Enable Vertex AI API
- Configure Consent Page
- Create OAuth Client ID
- Generate Authentication Code (OAuth Code)
- Setup XSOAR Instance
- Testing Command
Last Section will be Troubleshooting; the test button is not working with OAuth2 Method
***
If you have a knowledge of Google Cloud Administration, you can configure the project & API and skip directly to step 2.
Step 1: Create a new project on Google Cloud
In this step, you will need to have permission to create a new project in your GCP console
-
Login to GCP Console:
https://console.cloud.google.com/ -
Click on Create Project

- Fill project Name: XSOAR_VertexAI or any name, then click on Create

- Select the new created project
- Go to marketplace

- Search & Select Vertex AI API

- Click Enable

Step 2: Configure Consent Page
- Click on Configure Consent Screen

- Select Internal as User Type and Click on Create; It is recommended to limit the access to your project scope to Internal users in your organization as later as planned you can build your own Model and fine tune in a confidential environment that is shared publicly

- Fill the App information (Fill only the mandatory fields as below, rest are optional) - Click on Save and Continue

- Click Add or Remove Scopes; We will add Vertex AI API as part of the project scope; NOTE: Don’t add unnecessary scope as this might reveal other data in the project using the created credential

- In current version of this integration, it is only require read only permission in the Scope; Then Click Update

After added, it will looks like this screenshot

- Click Save and Continue; Now Step 3
Step 3: Create OAuth Client ID
- Go to APIs & Services > Credentials

- Click Create Credentials

- Fill your Credential Information as following
Application Type: Web application
Name: XSOAR-VertexAI
In Authorized redirect URIs: https://oproxy.demisto.ninja/authcode
This one will be easy as a user experience to generate the auth code; Please see Step 4

- Copy Client ID & Client secret, we will use them during XSOAR’s instance configurations; Then Click OK

Step 4: Generate Authentication Code (OAuth Code)
In this step, we will use the created client ID & secret to generate OAuth Code so, the integration can generate access token for authentication & authorization to Google APIs. For more information about Tokens: please check the following URL from Google:
https://developers.google.com/identity/protocols/oauth2
https://cloud.google.com/docs/authentication/token-types
- There are two ways to generate the required URL, you can create an instance of the integration and add all information except for auth code as still we don’t have it
First get the project id by clicking on the project name from top left then it will looks as following:

- Fill the instance information on XSOAR as following:

- In XSOAR’s CLI, execute the following command:
!google-vertex-ai-generate-auth-url
- Copy the generated authorization url to your browser and go to step 6

- You can skip previous configuration and use the following URL after filling the required parameters
URL Format:
https://accounts.google.com/o/oauth2/auth/oauthchooseaccount?scope=https://www.googleapis.com/auth/cloud-platform&access_type=offline&prompt=consent&response_type=code&state=state_parameter_passthrough_value&redirect_uri={REDIRECT_URI}&client_id={CLIENT_ID}
{REDIRECT_URI) replace it with: https://oproxy.demisto.ninja/authcode
{CLIENT_ID} replace it with: You Client ID that is generated in step 3
So, final URL should like that:
https://accounts.google.com/o/oauth2/auth/oauthchooseaccount?scope=https://www.googleapis.com/auth/cloud-platform&access_type=offline&prompt=consent&response_type=code&state=state_parameter_passthrough_value&redirect_uri=https://oproxy.demisto.ninja/authcode&client_id=223432736531-aqebta31ip0t35vr07gldb4qj9egj2na.apps.googleusercontent.com
- Choose your account or Sign in

- Click on Allow

- It will redirect you to the REDIRECT_URI domain
https://oproxy.demisto.ninja/authcode
The beauty of using the OProxy is to make it easier for users to copy paste the code instead of using manual way from Browser Address bar in case of using localhost as a redirect uri.
For more information about OProxy from Palo Alto Networks; check the following link:
https://xsoar.pan.dev/docs/reference/articles/o-proxy
- Copy the auth code to your configured XSOAR instance; the final look for XSOAR Instance should look like Step 5
Step 5: Setup XSOAR Instance
This is the final look for how your XSOAR instance will looks like

Step 7: Testing (Instance Test button doesn’t work with OAuth2 method)
Now it is time to put the integration in test.
- Execute the following command:
!google-vertex-PaLM-chat prompt=”Any message”

Troubleshooting
In case of any failure it will be related to authentication code expired or reset somehow. In that case, you will need to repeat steps of generating a new auth code and adding it to XSOAR. BUT before that most important to reset the cache to the integration as following:
- In the instance, click reset integration cache
- Save & Exit (Important)

- Repeat from step 4 to 7 to generate a new authentication code and configure your instance then test
Configuration parameters
proxy— Use system proxy settingsinsecure— Trust any certificate (not secure)ID— Client ID (required)Secret— Client Secret (required)Authentication_Code— Authentication Code (OAuth2) - View documentation to generate the Authentication CodeProjectID— Project ID (required)
Commands (2)
-
google-vertex-PaLM-chatSend Text to Google Vertex Ai (PaLM for Chat) and receive a generative ai response
-
google-vertex-ai-generate-auth-urlThis command will generate the authentication url required to generate auth code
import demistomock as demisto # noqa: F401 from CommonServerPython import * # noqa: F401 """ IMPORTS """ import json import urllib3 # Disable insecure warnings urllib3.disable_warnings() """ GLOBAL Variables """ DISABLE_SSL = demisto.params().get("insecure", False) PROXY = demisto.params().get("proxy") PROMPT = demisto.params().get("prompt") SERVICE_SCOPES = "https://www.googleapis.com/auth/cloud-platform" REDIRECT_URI = "https://oproxy.demisto.ninja/authcode" AUTH_URL = "https://accounts.google.com/o/oauth2/auth" ACCESS_TOKEN_URL = "https://oauth2.googleapis.com/token" PROJECT_ID = demisto.params().get("ProjectID") URL = "https://us-central1-aiplatform.googleapis.com/v1/projects/" AI_Model = "chat-bison:predict" TOKEN = demisto.params().get("token") CLIENT_ID = demisto.params().get("ID") CLIENT_SECRET = demisto.params().get("Secret") AUTH_CODE = demisto.params().get("Authentication_Code") ERROR_MSG = ( "ERROR: The authentication code has been reset" "Please reset integration cache for Vetex AI Instance" "in XSOAR and regenerate the 'Authorization code'" ) """ CLIENT CLASS """ class Client(BaseClient): """ Client class to interact with Google Vertex AI API """ def __init__(self, token_str: str, base_url: str, proxy: bool, verify: bool): super().__init__(base_url=URL, proxy=PROXY, verify=verify) self.token_str = token_str self.base_url = base_url self.proxy = proxy self.headers = {"Authorization": f"Bearer {self.token_str}", "Content-Type": "application/json"} def PaLMModel(self, prompt: str): options = {"instances": [{"messages": [{"content": prompt}]}]} return self._http_request( method="POST", url_suffix=f"{PROJECT_ID}/locations/us-central1/publishers/google/models/{AI_Model}", json_data=options, headers=self.headers, ) """ MAIN FUNCTIONS """ def createAuthorizationURL(): # The client ID and access scopes are required. partOne = f"{AUTH_URL}/oauthchooseaccount?scope={SERVICE_SCOPES}&access_type=offline&prompt=consent" partTwo = f"&response_type=code&state=state_parameter_passthrough_value&redirect_uri={REDIRECT_URI}&client_id={CLIENT_ID}" authorization_url = partOne + partTwo return authorization_url def check_access_token_validation(): """ Access tokens expires in 1 hour, then using refresh_access_token function we will request for a new access token """ demisto.debug("Start Token Validation") integration_context: dict = get_integration_context() access_token: str = integration_context.get("access_token", "") valid_until: int = integration_context.get("valid_until", int) time_now = epoch_seconds() if access_token and (time_now < valid_until): demisto.debug("Access Token still valid") return access_token elif access_token and (time_now > valid_until): demisto.debug("Access Token is expired, using refresh token") access_token = refresh_access_token() return access_token else: access_token = get_access_token() return access_token def get_access_token(): """ Generate a new Access Token using ClientID, ClientSecret and configured Authentication Code """ demisto.debug("Generate a new access token") integration_context: dict = get_integration_context() data: dict = { "code": AUTH_CODE, "client_id": CLIENT_ID, "client_secret": CLIENT_SECRET, "redirect_uri": REDIRECT_URI, "grant_type": "authorization_code", } response: requests.Response = requests.post(ACCESS_TOKEN_URL, data=data, verify=DISABLE_SSL) if not response.ok: error = error_parser(response) raise ValueError(f"Failed to get access token [{response.status_code}] - {error}") response_json: dict = response.json() access_token = response_json.get("access_token", "") expires_in: int = response_json.get("expires_in", 3595) refresh_token = response_json.get("refresh_token", "") time_now: int = epoch_seconds() time_buffer = 5 # seconds by which to shorten the validity period if expires_in - time_buffer > 0: expires_in -= time_buffer integration_context["refresh_token"] = refresh_token integration_context["access_token"] = access_token integration_context["valid_until"] = time_now + expires_in set_integration_context(integration_context) return access_token def refresh_access_token(): """ A refresh token might stop working for one of these reasons: The user has revoked your app's access. The refresh token has not been used for six months https://developers.google.com/identity/protocols/oauth2#:~:text=Refresh%20token%20expiration, -You%20must%20write&text=A%20refresh%20token%20might%20stop,been%20used%20for%20six%20months. """ demisto.debug("Refresh Access token using refresh_token from integration_context") integration_context: dict = get_integration_context() refresh_token: str = integration_context.get("refresh_token", "") data: dict = { "refresh_token": refresh_token, "client_id": CLIENT_ID, "client_secret": CLIENT_SECRET, "grant_type": "refresh_token", } response: requests.Response = requests.post(ACCESS_TOKEN_URL, data=data, verify=DISABLE_SSL) if not response.ok: error = error_parser(response) raise ValueError(f"Failed to get refresh token [{response.status_code}] - {error}") response_json: dict = response.json() access_token = response_json.get("access_token", "") expires_in: int = response_json.get("expires_in", 3595) time_now: int = epoch_seconds() time_buffer = 5 # seconds by which to shorten the validity period if expires_in - time_buffer > 0: expires_in -= time_buffer integration_context["refresh_token"] = refresh_token integration_context["access_token"] = access_token integration_context["valid_until"] = time_now + expires_in set_integration_context(integration_context) return access_token def epoch_seconds(d: datetime = None) -> int: """ Return the number of seconds for given date. If no date, return current. :param d: timestamp datetime object :return: timestamp in epoch """ if not d: d = datetime.utcnow() return int((d - datetime.utcfromtimestamp(0)).total_seconds()) def resetIntegrationContext(): """ In case of error related to authentication or authorization, the cached context will be reseted """ demisto.debug(ERROR_MSG) integration_context: dict = get_integration_context() integration_context["refresh_token"] = "" integration_context["access_token"] = "" integration_context["valid_until"] = "" set_integration_context(integration_context) return True def error_parser(resp_err: requests.Response) -> str: """ Parse Error """ try: response: dict = resp_err.json() if "Unauthorized" in response.get("error_description", ""): resetIntegrationContext() raise ValueError(ERROR_MSG) elif "invalid authentication credentials" in response.get("error_description", ""): resetIntegrationContext() raise ValueError(ERROR_MSG) elif "Bad" in response.get("error_description", ""): resetIntegrationContext() raise ValueError(ERROR_MSG) else: error = response.get("error", {}) err_str = ( f"{error.get('code', '')}: {error.get('message', '')}" if isinstance(error, dict) else response.get("error_description", "") ) if err_str: demisto.debug(err_str) return err_str # If no error message raise ValueError except ValueError: return resp_err.text def test_module(client: Client): """ This is the call made when pressing the integration test button. """ promptText = "Hi, what is your name" status = "" try: response = client.PaLMModel(promptText) rep = json.dumps(response) repJSON = json.loads(rep) PaLMResp = repJSON.get("predictions", [])[0].get("candidates", [])[0].get("content", "") if PaLMResp: status = "ok" return status else: status = ( "There is an error in communciating with Google Vertex AI API- Please regenerate the Authentication Code again" ) except Exception as e: exception_text = str(e).lower() if "Bad Request" in exception_text or "invalid_grant" in exception_text: status = ERROR_MSG return status else: raise e return status def send_prompts_PaLM_command(client: Client, prompt: str) -> CommandResults: """ Send Text to Bard """ PaLM_response = client.PaLMModel(prompt) return PaLM_output(PaLM_response) def PaLM_output(response) -> CommandResults: """ Convert response from ChatGPT to a human readable format in markdown table :return: CommandResults return output of ChatGPT response :rtype: ``CommandResults`` """ if response and isinstance(response, dict): rep = json.dumps(response) repJSON = json.loads(rep) PaLMResp = repJSON.get("predictions", [])[0].get("candidates", [])[0].get("content", "") context = [{"PaLM Model Response": PaLMResp}] markdown = tableToMarkdown( "Google Vertex AI API Response", context, ) results = CommandResults( readable_output=markdown, outputs_prefix="GoogleVertexAIResponse", outputs_key_field="predictions", outputs=context ) return results else: raise DemistoException("Error in results") """ MAIN FUNCTION """ def main(): """ Main function, runs command functions """ params = demisto.params() args = demisto.args() command = demisto.command() verify = not params.get("insecure", False) try: if command == "test-module": access_token = check_access_token_validation() client = Client(token_str=access_token, base_url=URL, verify=verify, proxy=PROXY) return_results(test_module(client)) elif command == "google-vertex-PaLM-chat": access_token = check_access_token_validation() client = Client(token_str=access_token, base_url=URL, verify=verify, proxy=PROXY) return_results(send_prompts_PaLM_command(client, **args)) elif command == "google-vertex-ai-generate-auth-url": return_results(createAuthorizationURL()) except Exception as e: if "Quota exceeded for quota metric" in str(e): return_error("Quota for Google Vertex API exceeded") else: return_error(str(e)) # python2 uses __builtin__ python3 uses builtins if __name__ == "__builtin__" or __name__ == "builtins": main()