Package: ai-core-sdk
SAP AI Core SDKThe SAP AI Core SDK is a Python-based SDK that lets you access SAP AI Core using Python methods and data structures. It provides tools that help you to manage your scenarios and workflows in SAP AI Core.The SAP AI Core SDK can be used to interact with SAP AI Core. It provides access to all public lifecycle and administration APIs.For example:You can execute pipelines as a batch job to preprocess or train your models, or perform batch inference.You can deploy а trained machine learning model as a web service to serve inference requests with high performance.You can register your own Docker registry, synchronize your AI content from your own git repository, and register your own object store for training data and trained models.You can log metrics within a workflow execution using the SDK. You can use the same code for tracking metrics in both your local environment and in the workflow execution (production).NoteNote that executing online inference is not part of SAP AI Core SDK.Metrics persistence is not currently available in your local environment using the SDK. However, it is available in your productive workflow execution.Example UsageThe SDK can, for instance, be used in a Jupyter notebook for convenient interaction with SAP AI Core in a test or development context.Here are a few examples how to use the SDK. For details on the methods, please refer to the html documentation provided in the/docsfolder of the wheel file.Import Definitionsfromai_core_sdk.ai_core_v2_clientimportAICoreV2ClientCreate ClientThe SDK requires credentials from your tenant's subaccount Service Key:client=AICoreV2Client(base_url=AI_API_BASE,auth_url=AUTH_URL,client_id=CLIENT_ID,client_secret=CLIENT_SECRET,resource_group=resource_group_id)(For persistent client configuration see below.)Create New Resource Groupresource_group_create=client.resource_groups.create(resource_group_id=resource_group_id)print(resource_group_create.resource_group_id)resource_group_details=client.resource_groups.get(resource_group_id=resource_group_id)print(f"{resource_group_details.status_message}\n{resource_group_details.resource_group_id}")Create Object Store Secret# access key and secret are assumed to reside in environment variables OSS_KEY and OSS_SECRETobject_store_secret_create=client.object_store_secrets.create(name="default",type="S3",bucket="<your S3 bucket>",endpoint="<your S3 host>",path_prefix="<your path prefix in S3>",region="<your S3 region>",data={"AWS_ACCESS_KEY_ID":os.environ.get("OSS_KEY"),"AWS_SECRET_ACCESS_KEY":os.environ.get("OSS_SECRET")})secret_get=client.object_store_secrets.get(name="default")print(f"{secret_get.metadata}")List Scenariosscenarios=client.scenario.query()forscenarioinscenarios.resources:print(f"{scenario.name}{scenario.id}")Client ConfigurationThere are different options to persist the client credentials
(in this order of precedence):in code via keyword arguments (see above),environment variables,profile configuration file.Aprofileis a json file residing in a config directory,
which can be set via environment variableAICORE_HOME(the default being~/.aicore/config.json).The commandai-core-sdk configure --helpcan be used to generate a profile.With profile names one can switch easily between profiles e.g., for different (sub)accounts.
The profile name can be passed also as a keyword. If no profile is specified, the default profile is used.AICore Content Packagesai-core-sdk provides a command-line utility as well as a python library to use AICore content packages.InstallationThe content functionality should be installed as an optional dependency of ai-core-sdk:pipinstallai-core-sdk[aicore-content]Content packages use Metaflow's Argo Workflows plugin to generate templates for AICore and hence Metaflow should be also configured.
Run a configuration wizzard:metaflowconfigurekubernetesor create a simple configuration file~/.metaflowconfig/config.jsonwith a link to the configured in AICore object store:{"METAFLOW_DEFAULT_DATASTORE":"s3","METAFLOW_DATASTORE_SYSROOT_S3":"s3://<bucket>/<prefix>"}SeeConfiguring Metaflowfor more details.Discover Content Packages and their ContentCLIEcho descriptions of all packages from all registires:aicore-contentlistTo add custom content package spec the environment variableAI_CORE_CONTENT_SPECS(.envfile is also supported ) or the-c <path to content spec py/yaml>option can be used:aicore-content-cai_core_content_spec.pylistexportAI_CORE_CONTENT_SPECS=$PWD/ai_core_content_spec.py&&aicore-contentshowecho"AI_CORE_CONTENT_SPECS=$PWD/ai_core_content_spec.py">>.env&&aicore-contentshowPythonAll packages:fromai_core_sdk.contentimportget_content_packages# function to fetch a list of all packages from all registriesforcontent_pkginget_content_packages().values():content_pkg.print()# print more details for each packageThe content specs can also be used directly:fromcontent_package.ai_core_content_specimportpackage_spec_objpackage_spec_obj.print()A content packages consists of multipleworkflows.Workflowscan be AI CoreExecutionsorDeployments.CLIList of all workflows available:aicore-contentlist<nameofthepackage>Details of a specific workflow:aicore-contentshow<nameofthepackage><nameoftheworkflow>PythonAll packages:fromai_core_sdk.contentimportget_content_packages# function to fetch a list of all packages from all registriespackage=[*get_content_packages().values()][0]# Get a ContentPackage objectworkflows=package.workflows# get all workflows from a packageforflowinworkflows:flow.print(compact=True)# Print a compact description of the workflowforflowinworkflows:flow.print(compact=False)# Print a detailed description of the workflow.When usingpythona package can also directly used from theContentPackagefile without registering:# Load the object from the submodulefromcontent_package.ai_core_content_specimportpackage_spec_obj[*package_spec_obj.workflows.values()][0].print(compact=True)# create the object from content package spec yamlfromai_core_sdk.contentimportContentPackagepackage_spec_obj=ContentPackage.from_yaml('<path to package spec yaml>')[*package_spec_obj.workflows.values()][0].print(compact=True)# load content package specs from .py filefromai_core_sdk.contentimportget_content_packages_from_py_filelist_of_package_specs=get_content_packages_from_py_file('<path to package spec py>')# a .py file can contain multiple package specsforpackage_spec_objinlist_of_package_specs:[*package_spec_obj.workflows.values()][0].print(compact=True)User's workflow configurationEvery AICore template has a section with a user-specific information, i.e. scenario, docker repo, secret names, etc.  To generate a template from a content package with user settings, user should create aworkflow configuration.  This is a yaml file with a following structure:.contentPackage:my-package
.workflow:my-package-workflow
.dockerType:gpu

name:"my-executable-id"labels:scenarios.ai.sap.com/id:"my-scenario-id"ai.sap.com/version:"0.0.1"annotations:scenarios.ai.sap.com/name:"my-scenario-name"executables.ai.sap.com/description:"My description"executables.ai.sap.com/name:"my-executable-name"image:"my-docker-image"imagePullSecret:"my-docker-registry-secret"objectStoreSecret:"default-object-store-secret"In this config the target content package and workflow can be referenced using the keys.packageand.workflow. If provided those references are used to create the image and template. If not provided the package and the workflow have to be specified with--packageand--workflowoptions (see following sections for details).Create Docker imagesTo run an execution or start a deployment, at least one docker image is needed. Required docker images could be generated through the CLI/Python calls. Both run adocker buildinternally.CLITo create docker images:aicore-contentcreate-image[--package=<packagename>][--workflow=<workflowname>]<workflowconfig>The command-line options--packageand--workflowoverwrite valudes from the worfklow config.The building process has to be followed by adocker push -t <generated-image>to push the container to the registry.PythonThe workflow objects got a function.create_image(...):workflow=content_package_spec_obj['batch_processing']# fetch the workflow object using its namedocker_tag='my-very-own-docker.repo/sap-cv-batch-processing:0.0.1'workflow_config='my-config.yaml'withopen(workflow_config)asstreamworkflow_config=yaml.load(stream)cmd=workflow.create_image(workflow_config,return_cmd=True)# perform a dry run and return the cmdprint(cmd)workflow.create_image(workflow_config)# actually build the docker containeros.system(f'docker push{workflow_config["image"]}')# push the containerCreate AI Core TemplatesThe template creation is similar for Execution and Deployment templates.Create Execution TemplatesTo create execution templates themetaflow argo pluginis used.CLIWorkflow templates need a workflow config to be provided to thecreate-templatesubcommand.aicore-contentcreate-template[--package=<packagename>][--workflow=<workflowname>]<workflowconfig>-o<pathtooutputtemplate.json>PythonThe workflow config for execution templates has to be provided to the workflows's.create_template(...)function. The output file can be specified through the keyword parametertarget_file:workflow.create_template('my-workflow-config.yaml',target_file='workflow-template.json')Additonal OptionsAdditional arguments can be defined in the workflow config under the keyadditionalOptions....additionalOptions:workflow:# list of strings passed as workflow options...metaflow:# list of strings passed as metaflow options---package-suffixes=.py,.shStrings in theworkflow/metaflowpasted into these positions:python-mflow[metaflow]argocreate[workflow]To check the resulting call an--dry-run(CLI)/return_cmd=True(Python) option is availble. Alternativly the subcommandaicore-content <package name> metaflow-cli <workflow name>orworkflow.raw_metaflow_cli(...). Every input after the command is directly passed to the underlyingpython -m <flow>call.Create Serving TemplatesCLIServing templates also need aworkflow configto be passed to thecreate-templatesubcommand.aicore-contentcreate-template[--package=<packagename>][--workflow=<workflowname>]<workflowconfig>-o<pathtooutputtemplate.yaml>PythonThe workflow config has to be provided to the workflows's.create_template(...)function. The output file can be specified through the keyword parametertarget_file:workflow.create_template('my-workflow-config.yaml',target_file='serving-template.yaml')Content Package CreationA content package needs two additional files: theContentPackagefile and a workflows yaml.ContentPackageContent package are defined in aContentPackageinstance. This instance can either be loaded from a.pyfile or can be created from a yaml. A typical.pyfile looks like this:importpathlibimportyamlfromai_core_sdk.contentimportContentPackageHERE=pathlib.Path(__file__).parentworkflow_yaml=HERE/'workflows.yaml'withworkflow_yaml.open()asstream:workflows=yaml.safe_load(stream)spec=ContentPackage(name='my-package name',# name of the package and the aicore-content cli subcommandworkflows_base_path=workflow_yaml.parent,# Base paths for all relative paths in the workflow dictworkflows=workflows,description='This is an epic content package.',# Package descriptionexamples=HERE/'examples',# Folder to package related examples [optional]version='0.0.1'# Version of the package)If the package is to be distributed as a python module via Nexus or PyPI and the content package spec python file should be included in the package asai_core_content_spec.py. This allows the CLI to find the package without additional configuration efforts and creates a subcommand using the name from theContentPackage.nameattribute.ExamplesExamples for the content package can copy by the consumer to a directory using the commandaicore-content examples <package name> <target folder>. This command creates the target folder and copies all files from the paths set in theContentPackage. If no path is set or the path does not exists theexamplessubcommand is not created.VersionCurrently the version in theContentPackageis passed to the docker build call as--build-arg pkg_version==={version}. In theDockerfilethis build argument can be used to build the docker container using the local version of the package. This is useful for content packages distributed as module through Nexus or PyPI:FROMpytorch/pytorchARGpkg_version=ENVpkg_version=$pkg_version...RUNpython-mpipinstallsap-computer-vision-package${pkg_version}Workflows FileThe second mandatory file a workflows yaml. This yaml is used to define workflows and is structed like a dictionary:
Entries of the dict look like this:name-of-the-workflow:description:>Description text [optional]dockerfile:...type:ExecutionMetaflow/DeploymentYAML[... more type specific fields]It is important that all paths in the workflow yaml are specified as paths relative to the workflow yaml`s path. This also means that all files must be located in the same folder or in subfolders.DockerfilesThe dockerfile entry can either be a single:dockerfile:train_workflow/Dockerfileor a dictionary of paths:dockerfile:cpu:train_workflow/Dockerfilegpu:train_workflow/DockerfileThis allows to define different Docker container for different node types or multiple containers for cases where different steps use different containers. The different dockerfile can be selected using the option/argumentdocker_typewhen running the build docker command/function.TypesCurrently two types of workflows are supported:ExecutionMetaflow: exections defined as metaflow flowspecsDeploymentYaml: deployment defined as yamlsExecutionMetaflowAdditional fields for aExecutionMetaflowentry arepy: path to the python file containing themetaflow.FlowSpecclassclassName: name of themetaflow.FlowSpecclassadditionalOptions(optional): The section is identical to theadditionalOptionsfrom the workflow configs.train-workflow:description:>Description text [optional]dockerfile:cpu:train/Dockerfilecpu:train/DockerfileGPUtype:ExecutionMetaflowpy:train/flow.pyclassName:TrainingFlowadditionalOptions:annotations:artifacts.ai.sap.com/datain.kind:datasetartifacts.ai.sap.com/trainedmodel.kind:modellabels:...workflow:# list of strings passed as workflow options...metaflow:# list of strings passed as metaflow options (only valid for ExecutionMetaflow)---package-suffixes=.py,.shDeploymentYamlAdditional fields:yaml: contains a path to a Deployment Template.my_deployment:type:DeploymentYamlyaml:deploy/template.yamldockerfile:deploy/DockerfileA Deployment Template is a regular Serving Template.  Data fromname,annotationsandlabelsfields from a user-defined
workflow config would replace correspondent attributes in the template.
In addition, variables$imageand$imagePullSecretwill be replaced with the values from the workflow config.apiVersion:ai.sap.com/v1alpha1kind:ServingTemplatemetadata:name:my-servingannotations:executables.ai.sap.com/name:my-servingexecutables.ai.sap.com/description:"Mymodelserving"labels:ai.sap.com/version:"0.0.1"spec:template:apiVersion:serving.kserve.io/v1beta1metadata:labels:|ai.sap.com/resourcePlan: "infer.s"spec:|predictor:imagePullSecrets:- name: $imagePullSecretcontainers:- name: kserve-containerimage: $imageContentPackagefrom yamlThe specification of a content package and of the workflows can also be merged into a single yaml file:name:test-spec-yamlexamples:examplesdescription:|this is a test.show_files:test:SHOW_FILEworkflows:test_execution:type:ExecutionMetaflowclassName:TestPipelinepy:train/flow.pydockerfile:cpu:train/Dockerfilegpu:train/DockerfileGPUannotations:artifacts.ai.sap.com/datain.kind:datasetartifacts.ai.sap.com/trainedmodel.kind:modelmetaflow:---package-suffixes=.py,.sCurrently there is no discovery mechanism for yaml specs.They either have to be consumed in python:fromai_core_sdk.contentimportContentPackagecontent_package=ContentPackage.from_yaml(spec_yaml)or made available to the CLI through theaicore-content -c <path to the spec yaml> ...option or through theAI_CORE_CONTENT_SPECSenvironment variable.Embedded code packagesMetaflow supports the Data Scientist during the experimentation phase, such that it packs all python files into an archive called "code package" and stores it in the configured S3 bucket. Later, during steps execution, this code package is downloaded into the docker container, unpacked there and the respective step function from the Metaflow python file is executed. This approach helps to iterate faster since there is no need to rebuild the docker image after every code change.In production, however the ML code will not change often. Therefore, it could be preferable to pack the code into the docker image instead of injecting it for every run. In other words, we want to embed the ML code into the docker image.When runningaicore-content create-image ...,	a code package is always generated and placed next to the corresponding Dockerfile under the namecodepkg.tgz.  This file could be used in the Dockerfile with:ADD codepkg.tgz .In addition, it is necessary to to tell sap-ai-core-metaflow to use the embedded coded package instead of downloading it.
Add this to the workflow's config file:additionalOptions:
    workflow:
      - --embeddedTrackingThe tracking module of the SAP AI Core SDK can be used to log metrics in both your local environment, and productive workflow executions. Metrics persistence is currently available in your productive environment.Here are a few code samples demonstrating how to use the SDK for metrics tracking.Modify Metricsfrom ai_core_sdk.tracking import Tracking

from ai_core_sdk.models import Metric, MetricTag, MetricCustomInfo

tracking_client = Tracking()

tracking_client.modify(
   tags = [
       # list
       MetricTag(name="Our Team Tag", value="Tutorial Team"),
       MetricTag(name="Stage", value="Development")
   ],
   metrics = [
       Metric(
           name="Training Loss",
           value=np.finfo(np.float64).max,
           timestamp= datetime.now().utcnow(),
           step = 1, # denotes epoch 1
           labels = []
       )
   ],
   custom_info = [
       # list of Custom Information
        MetricCustomInfo(
            name = "My Classification Report",
            # you may convert anything to string and store it
            value = str('''{
                "Cats": {
                    "Precision": 75,
                    "Recall": 74
                },
                "Dogs": {
                    "Precision": 85,
                    "Recall": 84
                }
            }''')
       )
   ]
)Log Metricstracking_client.log_metrics(
   metrics = [
       Metric(
           name="Training Loss",
           value=float(86.99),
           timestamp= datetime.now().utcnow(),
           step = 1, # denotes epoch 1
           labels = []
       ),
   ],
)Set Tagstracking_client.set_tags(
   tags = [
       # list
       MetricTag(name="Our Team Tag", value="Tutorial Team"),
       MetricTag(name="Stage", value="Development")
   ]
)Set Custom Infotracking_client.set_custom_info(
   custom_info = [
       # list of Custom Information
        MetricCustomInfo(
            name = "My Classification Report",
            # you may convert anything to string and store it
            value = str('''
            {
                "Cats": {
                    "Precision": 75,
                    "Recall": 74
                },
                "Dogs": {
                    "Precision": 85,
                    "Recall": 84
                }
            }
            '''
            )
       ),
   ]
)Query Metricsmetrics_response = tracking_client.query(execution_ids = [
   "test_execution_id"    # Change this with the training execution id
])Delete Metricsmetrics_response = tracking_client.delete(execution_id = "test_execution_id") # Change this with the actual execution id