Skip to content

Commit 9bd7446

Browse files
authored
Merge pull request #640 from VipulMascarenhas/ai_hub_stacks
AI Hub Stack Setup
2 parents e2c3b8f + e14a2d8 commit 9bd7446

30 files changed

+2396
-14
lines changed

.github/workflows/stack.yml

Lines changed: 54 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,32 +17,72 @@ jobs:
1717
runs-on: ubuntu-latest
1818
steps:
1919
- name: Checkout
20-
uses: actions/checkout@v3
20+
uses: actions/checkout@v4
2121

2222
- name: Create stacks
2323
id: create_stacks
2424
run: |
25+
set -euo pipefail
26+
RELEASE=$(cat VERSION)
2527
28+
# oci-ods-aqua
2629
STACKNAME=oci-ods-aqua
2730
STACK_FILES="ai-quick-actions/policies/terraform/*"
28-
RELEASE=$(cat VERSION)
29-
ASSETS+="${STACKNAME}.zip"
3031
echo "::group::Processing $STACKNAME"
31-
zip -r ${STACKNAME}-stack.zip $STACK_FILES || { printf '\n⛔ Unable to create %s stack.\n'; exit 1; }
32-
cp ${STACKNAME}-stack.zip ${STACKNAME}.zip || { printf '\n⛔ Unable to create %s stack.\n'; exit 1; }
32+
zip -r ${STACKNAME}-stack.zip $STACK_FILES
33+
cp ${STACKNAME}-stack.zip ${STACKNAME}.zip
34+
echo "::endgroup::"
35+
36+
# ai-document-converter-app
37+
DOC_CONVERTER_STACKNAME=ai-document-converter
38+
DOC_CONVERTER_FILES="ai-hub/ai-document-converter/policies/terraform/*"
39+
echo "::group::Packaging $DOC_CONVERTER_STACKNAME"
40+
zip -r ${DOC_CONVERTER_STACKNAME}-stack.zip $DOC_CONVERTER_FILES
41+
cp ${DOC_CONVERTER_STACKNAME}-stack.zip ${DOC_CONVERTER_STACKNAME}.zip
42+
echo "::endgroup::"
43+
44+
# ai-translation-app
45+
TRANSLATION_STACKNAME=ai-translation
46+
TRANSLATION_FILES="ai-hub/ai-translation/policies/terraform/*"
47+
echo "::group::Packaging $TRANSLATION_STACKNAME"
48+
zip -r ${TRANSLATION_STACKNAME}-stack.zip $TRANSLATION_FILES
49+
cp ${TRANSLATION_STACKNAME}-stack.zip ${TRANSLATION_STACKNAME}.zip
3350
echo "::endgroup::"
34-
echo "::set-output name=assets::$ASSETS"
35-
echo "::set-output name=release::$RELEASE"
36-
echo "::set-output name=prefix::$STACKNAME"
51+
52+
ASSETS="${STACKNAME}.zip ${DOC_CONVERTER_STACKNAME}.zip ${TRANSLATION_STACKNAME}.zip"
53+
54+
{
55+
echo "assets=${ASSETS}"
56+
echo "release=${RELEASE}"
57+
echo "aqua=${STACKNAME}"
58+
echo "ai_document_converter=${DOC_CONVERTER_STACKNAME}"
59+
echo "ai_translation=${TRANSLATION_STACKNAME}"
60+
} >> $GITHUB_OUTPUT
3761
3862
- name: Prepare Release Notes
63+
shell: bash
3964
run: |
40-
#
41-
printf '%s\n' '${{ steps.create_stacks.outputs.prefix }} Stack - v${{ steps.create_stacks.outputs.release }}' >release.md
42-
printf '%s\n' '' '## [![Deploy to Oracle Cloud][magic_button]][magic_stack]' >>release.md
43-
printf '%s\n' '' '' >>release.md
44-
printf '%s\n' '' '[magic_button]: https://oci-resourcemanager-plugin.plugins.oci.oraclecloud.com/latest/deploy-to-oracle-cloud.svg' >>release.md
45-
printf '%s\n' '' '[magic_stack]: https://cloud.oracle.com/resourcemanager/stacks/create?zipUrl=https://github.com/${{ github.repository }}/releases/download/${{ steps.create_stacks.outputs.release }}/${{ steps.create_stacks.outputs.prefix }}.zip' >>release.md
65+
rel="${{ steps.create_stacks.outputs.release }}"
66+
aqua="${{ steps.create_stacks.outputs.aqua }}"
67+
ai_document_converter="${{ steps.create_stacks.outputs.ai_document_converter }}"
68+
ai_translation="${{ steps.create_stacks.outputs.ai_translation }}"
69+
70+
{
71+
printf '# Stacks - v%s\n\n' "$rel"
72+
printf '### %s\n' "$aqua"
73+
printf '## [![Deploy to Oracle Cloud][magic_button]][magic_stack_aqua]\n\n'
74+
75+
printf '### %s\n' "$ai_document_converter"
76+
printf '## [![Deploy to Oracle Cloud][magic_button]][magic_stack_ai_document_converter]\n\n'
77+
78+
printf '### %s\n' "$ai_translation"
79+
printf '## [![Deploy to Oracle Cloud][magic_button]][magic_stack_ai_translation]\n\n'
80+
81+
printf '[magic_button]: https://oci-resourcemanager-plugin.plugins.oci.oraclecloud.com/latest/deploy-to-oracle-cloud.svg\n'
82+
printf '[magic_stack_aqua]: https://cloud.oracle.com/resourcemanager/stacks/create?zipUrl=https://github.com/${{ github.repository }}/releases/download/%s/%s.zip\n' "$rel" "$aqua"
83+
printf '[magic_stack_ai_document_converter]: https://cloud.oracle.com/resourcemanager/stacks/create?zipUrl=https://github.com/${{ github.repository }}/releases/download/%s/%s.zip\n' "$rel" "$ai_document_converter"
84+
printf '[magic_stack_ai_translation]: https://cloud.oracle.com/resourcemanager/stacks/create?zipUrl=https://github.com/${{ github.repository }}/releases/download/%s/%s.zip\n' "$rel" "$ai_translation"
85+
} > release.md
4686
4787
- name: Create Release
4888
run: gh release create ${{ steps.create_stacks.outputs.release }} --generate-notes -F release.md ${{ steps.create_stacks.outputs.assets }}
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
# API Gateway
2+
resource "oci_apigateway_gateway" "ai_application_oci_apigateway_gateway" {
3+
compartment_id = var.compartment_id
4+
display_name = "ai_application_api_gw_${random_string.randomstring.result}"
5+
endpoint_type = "PUBLIC"
6+
response_cache_details {
7+
type = "NONE"
8+
}
9+
subnet_id = local.api_gw_subnet_id
10+
}
11+
12+
# API Gateway Deployments
13+
resource "oci_apigateway_deployment" "ai_application_apigateway_deployment" {
14+
compartment_id = var.compartment_id
15+
display_name = "api-deployment${random_string.randomstring.result}"
16+
gateway_id = oci_apigateway_gateway.ai_application_oci_apigateway_gateway.id
17+
path_prefix = "/"
18+
specification {
19+
logging_policies {
20+
execution_log {
21+
log_level = "INFO"
22+
}
23+
}
24+
request_policies {
25+
authentication {
26+
is_anonymous_access_allowed = "true"
27+
token_auth_scheme = "Bearer"
28+
token_header = "token"
29+
type = "TOKEN_AUTHENTICATION"
30+
validation_failure_policy {
31+
client_details {
32+
type = "VALIDATION_BLOCK"
33+
}
34+
logout_path = "/logout"
35+
response_type = "CODE"
36+
scopes = ["openid"]
37+
source_uri_details {
38+
type = "VALIDATION_BLOCK"
39+
}
40+
type = "OAUTH2"
41+
use_cookies_for_intermediate_steps = "true"
42+
use_cookies_for_session = "true"
43+
max_expiry_duration_in_hours = 1
44+
}
45+
validation_policy {
46+
additional_validation_policy {
47+
issuers = ["https://identity.oraclecloud.com/"]
48+
}
49+
client_details {
50+
client_id = oci_identity_domains_app.ai_application_confidential_app.name
51+
client_secret_id = oci_vault_secret.idcs_app_client_secret.id
52+
client_secret_version_number = "1"
53+
type = "CUSTOM"
54+
}
55+
max_cache_duration_in_hours = "1"
56+
source_uri_details {
57+
type = "DISCOVERY_URI"
58+
uri = "${data.oci_identity_domain.application_identity_domain.url}/.well-known/openid-configuration"
59+
}
60+
type = "REMOTE_DISCOVERY"
61+
}
62+
}
63+
mutual_tls {
64+
is_verified_certificate_required = "false"
65+
}
66+
}
67+
routes {
68+
backend {
69+
type = "OAUTH2_LOGOUT_BACKEND"
70+
}
71+
logging_policies {
72+
}
73+
methods = ["GET"]
74+
path = "/logout"
75+
}
76+
routes {
77+
backend {
78+
status = "200"
79+
type = "STOCK_RESPONSE_BACKEND"
80+
}
81+
logging_policies {
82+
}
83+
methods = ["ANY"]
84+
path = "/token"
85+
response_policies {
86+
header_transformations {
87+
set_headers {
88+
items {
89+
if_exists = "OVERWRITE"
90+
name = "token"
91+
values = ["$${request.auth[id_token]}"]
92+
}
93+
items {
94+
if_exists = "OVERWRITE"
95+
name = "x-csrf-token"
96+
values = ["$${request.auth[apigw_csrf_token]}"]
97+
}
98+
}
99+
}
100+
}
101+
}
102+
routes {
103+
backend {
104+
connect_timeout_in_seconds = "60"
105+
is_ssl_verify_disabled = "false"
106+
read_timeout_in_seconds = "120"
107+
send_timeout_in_seconds = "120"
108+
type = "HTTP_BACKEND"
109+
url = "http://${data.oci_core_vnic.ai_application_container_instance_vnic.private_ip_address}:8080/$${request.path[req]}"
110+
}
111+
logging_policies {
112+
}
113+
methods = ["ANY"]
114+
path = "/{req*}"
115+
request_policies {
116+
header_transformations {
117+
set_headers {
118+
items {
119+
if_exists = "OVERWRITE"
120+
name = "id_token"
121+
values = ["$${request.auth[id_token]}"]
122+
}
123+
items {
124+
if_exists = "OVERWRITE"
125+
name = "Host"
126+
values = ["$${request.host}"]
127+
}
128+
}
129+
}
130+
}
131+
}
132+
}
133+
}
134+
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
resource "oci_container_instances_container_instance" "ai_container_instance" {
2+
#Required
3+
availability_domain = var.availability_domain
4+
compartment_id = var.vcn_compartment_id
5+
containers {
6+
#Required
7+
image_url = local.image
8+
9+
#Optional
10+
environment_variables = {
11+
MULTIMODAL_LLM_PROVIDER = var.multimodal_llm_provider
12+
MULTIMODAL_MODEL_NAME = var.multimodal_model_name
13+
MULTIMODAL_MODEL_ENDPOINT = var.multimodal_model_endpoint
14+
MAX_OUTPUT_TOKEN = var.multimodal_max_output_token
15+
GENAI_COMPARTMENT_OCID = var.genai_compartment_ocid
16+
PROMPT_VERSION = var.prompt_version
17+
BACKEND_MD_URL = oci_datascience_model_deployment.ai_deployment.model_deployment_url
18+
}
19+
}
20+
shape = var.container_shape
21+
shape_config {
22+
#Required
23+
ocpus = var.ocpus
24+
25+
#Optional
26+
memory_in_gbs = var.memory_in_gbs
27+
}
28+
vnics {
29+
#Required
30+
subnet_id = local.app_subnet_id
31+
# private_ip = var.container_instance_private_ip
32+
}
33+
34+
#Optional
35+
display_name = var.container_display_name
36+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
data "oci_core_services" "all_oci_services" {
2+
filter {
3+
name = "name"
4+
values = ["All .* Services In Oracle Services Network"]
5+
regex = true
6+
}
7+
}
8+
9+
data "oci_identity_availability_domains" "get_availability_domains" {
10+
compartment_id = var.compartment_ocid
11+
}
12+
13+
data "oci_identity_domain" "application_identity_domain" {
14+
domain_id = trimspace(var.identity_domain_id)
15+
lifecycle {
16+
precondition {
17+
condition = var.identity_domain_id != null
18+
error_message = "Existing domain id must be provided when using an existing domain."
19+
}
20+
}
21+
}
22+
23+
data "oci_core_vnic" "ai_application_container_instance_vnic" {
24+
vnic_id = oci_container_instances_container_instance.ai_container_instance.vnics[0].vnic_id
25+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
resource "oci_identity_domains_app" "ai_application_confidential_app" {
2+
based_on_template {
3+
value = "CustomWebAppTemplateId"
4+
well_known_id = "CustomWebAppTemplateId"
5+
}
6+
client_type = "confidential"
7+
description = "Confidential Application for AI Translation Application"
8+
display_name = "ai__translation_application_confidential_app_${random_string.randomstring.result}"
9+
schemas = ["urn:ietf:params:scim:schemas:oracle:idcs:App"]
10+
allowed_operations = ["introspect"]
11+
idcs_endpoint = data.oci_identity_domain.application_identity_domain.url
12+
active = true
13+
is_oauth_client = true
14+
bypass_consent = true
15+
allowed_grants = ["authorization_code", "client_credentials", "urn:ietf:params:oauth:grant-type:jwt-bearer", "implicit"]
16+
all_url_schemes_allowed = true
17+
redirect_uris = ["https://${oci_apigateway_gateway.ai_application_oci_apigateway_gateway.hostname}/", "https://${oci_apigateway_gateway.ai_application_oci_apigateway_gateway.hostname}/ui","https://${oci_apigateway_gateway.ai_application_oci_apigateway_gateway.hostname}/ui/", "https://${oci_apigateway_gateway.ai_application_oci_apigateway_gateway.hostname}/ui/gradio", "https://${oci_apigateway_gateway.ai_application_oci_apigateway_gateway.hostname}/ui/playground","https://${oci_apigateway_gateway.ai_application_oci_apigateway_gateway.hostname}/ui/docs", "https://${oci_apigateway_gateway.ai_application_oci_apigateway_gateway.hostname}/callback"]
18+
post_logout_redirect_uris = ["https://${oci_apigateway_gateway.ai_application_oci_apigateway_gateway.hostname}/"]
19+
audience = oci_apigateway_gateway.ai_application_oci_apigateway_gateway.hostname
20+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# dummy model artifact (zip) built from inline files
2+
data "archive_file" "model_zip" {
3+
type = "zip"
4+
output_path = "${path.module}/model-artifact.zip"
5+
6+
# minimal runtime.yaml expected by OCI model artifacts
7+
source {
8+
filename = "runtime.yaml"
9+
content = <<-YAML
10+
kind: generic
11+
type: python
12+
entryPoint: "score.py"
13+
pythonVersion: "3.11"
14+
YAML
15+
}
16+
17+
# Trivial scorer stub
18+
source {
19+
filename = "score.py"
20+
content = <<-PY
21+
def predict(data):
22+
# no-op dummy implementation
23+
return {"ok": True, "echo": data}
24+
PY
25+
}
26+
}
27+
28+
# dummy Model catalog entry with artifact
29+
resource "oci_datascience_model" "ai_model" {
30+
compartment_id = var.data_science_project_compartment_id
31+
project_id = var.project_ocid
32+
display_name = var.model_display_name
33+
description = local.model_desc
34+
35+
# Upload artifact inline (ZIP created above)
36+
model_artifact = data.archive_file.model_zip.output_path
37+
artifact_content_length = data.archive_file.model_zip.output_size
38+
artifact_content_disposition = "attachment; filename=model-artifact.zip"
39+
40+
retention_setting {
41+
archive_after_days = 365
42+
}
43+
}

0 commit comments

Comments
 (0)