diff --git a/LLM Model Serving/README.md b/LLM Model Serving/README.md new file mode 100644 index 0000000..cb89151 --- /dev/null +++ b/LLM Model Serving/README.md @@ -0,0 +1,27 @@ +# Model Serving in Snowpark Container Services + +[Snowflake Documentation](https://docs.snowflake.com/en/developer-guide/snowflake-ml/model-registry/container) +See the [Examples](#examples) section to find end-to-end examples of using Model Serving in SPCS. + +## Setup + +Ensure you have the latest available `snowflake-ml-python` SDK. + +```bash +pip install snowflake-ml-python +``` + +## Getting Started + +### Prerequisites + +1. [Create a Compute Pool](https://docs.snowflake.com/en/developer-guide/snowflake-ml/model-registry/container#create-a-compute-pool) +2. [Create an Image Repository](https://docs.snowflake.com/en/developer-guide/snowflake-ml/model-registry/container#create-an-image-repository) + +## Examples + +### Jupyter Notebooks + +Examples showcasing how Model Serving can be used from a notebook environment like Jupyter or Snowflake Notebook. + +- [Med Gemma Quickstart Notebook](./med_gemma.ipynb) - log a MedGemma model and serve using SPCS. diff --git a/LLM Model Serving/med_gemma.ipynb b/LLM Model Serving/med_gemma.ipynb new file mode 100644 index 0000000..bab48bc --- /dev/null +++ b/LLM Model Serving/med_gemma.ipynb @@ -0,0 +1,1086 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "ea6dce41-4956-447b-84f5-1e5eb6c74454", + "metadata": { + "collapsed": false, + "name": "cell8" + }, + "source": [ + "# Log and serve Huggingface model\n", + "This notebook provides an example of logging and serving a Huggingface transformers model with `text-genration` task.\n", + "\n", + "\n", + "## To read about Model Serving at Snowflake\n", + "Documentation: https://docs.snowflake.com/en/developer-guide/snowflake-ml/model-registry/container" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3775908f-ca36-4846-8f38-5adca39217f2", + "metadata": { + "language": "python", + "name": "cell1" + }, + "outputs": [], + "source": [ + "# Import python packages\n", + "import pandas as pd\n", + "\n", + "# We can also use Snowpark for our analyses!\n", + "from snowflake.snowpark.context import get_active_session\n", + "\n", + "session = get_active_session()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fea0503b-2b9b-4b4c-b57b-3b8f72a7d9f6", + "metadata": { + "language": "python", + "name": "cell2" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "snowflake-ml-python==1.15.0\n" + ] + } + ], + "source": [ + "from snowflake.ml import version\n", + "from snowflake import snowpark\n", + "from snowflake.ml.registry import registry as registry_module\n", + "from snowflake.ml.model import openai_signatures\n", + "\n", + "print(f\"snowflake-ml-python=={version.VERSION}\")\n", + "\n", + "registry = registry_module.Registry(\n", + " session=session,\n", + ")\n", + "\n", + "# make sure to be on `snowflake-ml-python>=1.13.0`" + ] + }, + { + "cell_type": "markdown", + "id": "33a7a225-ad7a-45d4-b901-0058ad174524", + "metadata": { + "collapsed": false, + "name": "cell7" + }, + "source": [ + "### Note: The notebook requires an EAI to download files from Huggingface\n", + "This featuer lets us log a large model without requiring GPU\n", + "\n", + "Keep a lookout for a new API in `HuggingFacePipelineModel.log_model_and_create_service()` (to be released in snowflake-ml-python>=1.15.0)\n", + "which logs the model remotely wihthout having to download the HF model locally. This feature doesn't require EAI to HuggingFace.\n", + "\n", + "For now we are downloading the model weights locally using `download_snapshot=True`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5cbf74c9-6f8d-47db-8ca7-76b8c3e6999a", + "metadata": { + "codeCollapsed": false, + "language": "python", + "name": "cell3" + }, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "2938de9abcc04fd5b9a06720b2fe81c1", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Fetching 15 files: 0%| | 0/15 [00:00" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Note: this requires an EAI to download files from Huggingface\n", + "# This feature lets us log a large model without requiring GPU\n", + "\n", + "# import os\n", + "from snowflake.ml.model.models import huggingface_pipeline\n", + "\n", + "\n", + "model = huggingface_pipeline.HuggingFacePipelineModel(\n", + " model=\"google/medgemma-4b-it\",\n", + " task=\"text-generation\",\n", + " # TODO: provide token if the model is gated\n", + " # token=os.getenv(\"HF_TOKEN\"),\n", + " # token=\"hf_...\",\n", + " download_snapshot=True,\n", + ")\n", + "model" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "45cd4c60-d7a6-411b-b495-0769ce310c1b", + "metadata": { + "codeCollapsed": false, + "language": "python", + "name": "cell4" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[35mREADME.md\u001b[m\u001b[m \u001b[35mmodel.safetensors.index.json\u001b[m\u001b[m\n", + "\u001b[35madded_tokens.json\u001b[m\u001b[m \u001b[35mpreprocessor_config.json\u001b[m\u001b[m\n", + "\u001b[35mchat_template.jinja\u001b[m\u001b[m \u001b[35mprocessor_config.json\u001b[m\u001b[m\n", + "\u001b[35mconfig.json\u001b[m\u001b[m \u001b[35mspecial_tokens_map.json\u001b[m\u001b[m\n", + "\u001b[35mgeneration_config.json\u001b[m\u001b[m \u001b[35mtokenizer.json\u001b[m\u001b[m\n", + "\u001b[35mmodel-00001-of-00002.safetensors\u001b[m\u001b[m \u001b[35mtokenizer.model\u001b[m\u001b[m\n", + "\u001b[35mmodel-00002-of-00002.safetensors\u001b[m\u001b[m \u001b[35mtokenizer_config.json\u001b[m\u001b[m\n" + ] + } + ], + "source": [ + "# list the directory where the model files are downloaded\n", + "! ls {model.repo_snapshot_dir}" + ] + }, + { + "cell_type": "markdown", + "id": "2146c9ad-f99a-4030-bfa4-0bdaabfc2144", + "metadata": { + "collapsed": false, + "name": "cell9" + }, + "source": [ + "Log the Huggingface pipeline to Snowflake" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4cf0908b-d906-4ed2-ad0e-2dcc9588012c", + "metadata": { + "codeCollapsed": false, + "language": "python", + "name": "cell5" + }, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "
\n", + "

\n", + " Model Version Details\n", + "

\n", + " \n", + "
\n", + " \n", + "
Model Name:
\n", + "
MED_GEMMA_4B
\n", + " \n", + "
Version:
\n", + "
EMPTY_RABBIT_4
\n", + " \n", + "
Full Name:
\n", + "
PAVI_DEMO.PUBLIC.MED_GEMMA_4B
\n", + " \n", + "
Description:
\n", + "
None
\n", + " \n", + "
Task:
\n", + "
UNKNOWN
\n", + " \n", + "
\n", + "

Functions

\n", + "
\n", + " \n", + "
\n", + " \n", + " __CALL__\n", + " \n", + "
\n", + " \n", + "
\n", + " Target Method: __call__\n", + "
\n", + "
\n", + " Function Type: FUNCTION\n", + "
\n", + "
\n", + " Partitioned: False\n", + "
\n", + "
\n", + " Signature:\n", + " \n", + "
\n", + "

\n", + " Model Signature\n", + "

\n", + " \n", + "
\n", + " \n", + "
\n", + " \n", + " Inputs\n", + " \n", + "
\n", + " \n", + "
\n", + " \n", + "
\n", + "
\n", + " messages (group)\n", + "
\n", + "
\n", + " \n", + "
\n", + " content: DataType.STRING\n", + "
\n", + " \n", + "
\n", + " name: DataType.STRING\n", + "
\n", + " \n", + "
\n", + " role: DataType.STRING\n", + "
\n", + " \n", + "
\n", + " title: DataType.STRING\n", + "
\n", + " \n", + "
\n", + "
\n", + " \n", + "
\n", + " temperature: DataType.DOUBLE\n", + "
\n", + " \n", + "
\n", + " max_completion_tokens: DataType.INT64\n", + "
\n", + " \n", + "
\n", + " stop: DataType.STRING shape=(-1,)\n", + "
\n", + " \n", + "
\n", + " n: DataType.INT32\n", + "
\n", + " \n", + "
\n", + " stream: DataType.BOOL\n", + "
\n", + " \n", + "
\n", + " top_p: DataType.DOUBLE\n", + "
\n", + " \n", + "
\n", + " frequency_penalty: DataType.DOUBLE\n", + "
\n", + " \n", + "
\n", + " presence_penalty: DataType.DOUBLE\n", + "
\n", + "
\n", + "
\n", + "
\n", + " \n", + " \n", + "
\n", + " \n", + " Outputs\n", + " \n", + "
\n", + " \n", + "
\n", + " \n", + "
\n", + " id: DataType.STRING\n", + "
\n", + " \n", + "
\n", + " object: DataType.STRING\n", + "
\n", + " \n", + "
\n", + " created: DataType.FLOAT\n", + "
\n", + " \n", + "
\n", + " model: DataType.STRING\n", + "
\n", + " \n", + "
\n", + "
\n", + " choices (group)\n", + "
\n", + "
\n", + " \n", + "
\n", + " index: DataType.INT32\n", + "
\n", + " \n", + "
\n", + "
\n", + " message (group)\n", + "
\n", + "
\n", + " \n", + "
\n", + " content: DataType.STRING\n", + "
\n", + " \n", + "
\n", + " name: DataType.STRING\n", + "
\n", + " \n", + "
\n", + " role: DataType.STRING\n", + "
\n", + " \n", + "
\n", + "
\n", + " \n", + "
\n", + " logprobs: DataType.STRING\n", + "
\n", + " \n", + "
\n", + " finish_reason: DataType.STRING\n", + "
\n", + " \n", + "
\n", + "
\n", + " \n", + "
\n", + "
\n", + " usage (group)\n", + "
\n", + "
\n", + " \n", + "
\n", + " completion_tokens: DataType.INT32\n", + "
\n", + " \n", + "
\n", + " prompt_tokens: DataType.INT32\n", + "
\n", + " \n", + "
\n", + " total_tokens: DataType.INT32\n", + "
\n", + " \n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + " \n", + "
\n", + " \n", + "
\n", + " \n", + "
\n", + " \n", + "
\n", + "
\n", + " \n", + "
\n", + "

Metrics

\n", + "
\n", + " No metrics available\n", + "
\n", + " \n", + "
\n", + " " + ], + "text/plain": [ + "ModelVersion(\n", + " name='MED_GEMMA_4B',\n", + " version='EMPTY_RABBIT_4',\n", + ")" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "mv = registry.log_model(\n", + " model=model,\n", + " model_name=\"med_gemma_4b\",\n", + " # provides OpenAI Chat Completions compatible signature (input output format) to interact with the transformers model\n", + " signatures=openai_signatures.OPENAI_CHAT_SIGNATURE,\n", + ")\n", + "mv" + ] + }, + { + "cell_type": "markdown", + "id": "e16a82eb-c8da-40cc-a32e-ee33e3242e4d", + "metadata": { + "collapsed": false, + "name": "cell13" + }, + "source": [ + "Create a service using the logged model. This usually takes few minutes and depends on the node availability in your compute pool.\n", + "Once the service is up and running, the model can be inferred using SQL, Python API and REST Endpoints (if ingress is enabled)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5be6dea2-404d-484c-9992-8f6b7460224d", + "metadata": { + "codeCollapsed": false, + "language": "python", + "name": "cell10" + }, + "outputs": [], + "source": [ + "service_name = \"med_gemma_service\"\n", + "\n", + "mv.create_service(\n", + " service_name=\"med_gemma_service\",\n", + " service_compute_pool=\"\",\n", + " gpu_requests=\"1\",\n", + " # if rest endpoint is required\n", + " ingress_enabled=True,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9af9563f-1573-438f-81b0-e3665da40ac2", + "metadata": { + "codeCollapsed": false, + "language": "python", + "name": "cell12" + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/opt/anaconda3/envs/snowml/lib/python3.10/site-packages/snowflake/ml/model/model_signature.py:671: UserWarning: Null value detected in column stop, model signature inference might not accurate, or your prediction might fail if your model does not support null input. If this is not expected, please check your input dataframe.\n", + " handler.validate(data)\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
idobjectcreatedmodelchoicesusage
0chatcmpl-43a83d69ada04062b709bfeabba377c1chat.completion1758827776.0/shared/model/model/models/MED_GEMMA_4B/model[{'finish_reason': 'stop', 'index': 0, 'logpro...{'completion_tokens': 750, 'prompt_tokens': 27...
\n", + "
" + ], + "text/plain": [ + " id object created \\\n", + "0 chatcmpl-43a83d69ada04062b709bfeabba377c1 chat.completion 1758827776.0 \n", + "\n", + " model \\\n", + "0 /shared/model/model/models/MED_GEMMA_4B/model \n", + "\n", + " choices \\\n", + "0 [{'finish_reason': 'stop', 'index': 0, 'logpro... \n", + "\n", + " usage \n", + "0 {'completion_tokens': 750, 'prompt_tokens': 27... " + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "messages = [\n", + " {\"role\": \"system\", \"content\": \"You are a helpful medical assistant.\"},\n", + " {\n", + " \"role\": \"user\",\n", + " \"content\": \"How do you differentiate bacterial from viral pneumonia?\",\n", + " },\n", + "]\n", + "\n", + "# create a pd.DataFrame with openai.client.chat.completions arguments like below:\n", + "x_df = pd.DataFrame.from_records(\n", + " [\n", + " {\n", + " \"messages\": messages,\n", + " \"max_completion_tokens\": 250,\n", + " \"temperature\": 0.9,\n", + " \"stop\": None,\n", + " \"n\": 3,\n", + " # Note streaming is not supported yet\n", + " \"stream\": False,\n", + " \"top_p\": 1.0,\n", + " \"frequency_penalty\": 0.1,\n", + " \"presence_penalty\": 0.2,\n", + " }\n", + " ],\n", + ")\n", + "\n", + "# To get the model version object\n", + "# mv = registry.get_model(\"\").version(\"\"\")\n", + "# OpenAI Chat Completion compatible output\n", + "output_df = mv.run(X=x_df, service_name=service_name)\n", + "output_df" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "8fc57679-4e6f-40b9-bb3e-34c228870cab", + "metadata": { + "codeCollapsed": false, + "language": "python", + "name": "cell14" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Okay, I can help you understand how a medical assistant can differentiate between bacterial and viral pneumonia.\n", + "\n", + "**How a Medical Assistant can help differentiate between bacterial and viral pneumonia:**\n", + "\n", + "**1. Medical History and Symptoms:**\n", + "\n", + "* **Onset:**\n", + " * **Bacterial pneumonia:** Often has a more sudden onset, can be more sudden and severe.\n", + " * **Viral pneumonia:** Often has a more gradual onset, can be more gradual and more gradual.\n", + "* **Onset:**\n", + " * **Bacterial pneumonia:** Often has a more sudden onset, can be more sudden and severe.\n", + " * **Viral pneumonia:** Often has a more gradual onset, can be more gradual and more gradual.\n", + "* **Symptoms:**\n", + " * **Bacterial pneumonia:**\n", + " * **Symptoms:** Often has a more sudden onset, can be more sudden and severe.\n", + " * **Symptoms:** Often has a more sudden onset, can be more sudden and severe.\n", + " * **Symptoms:** Often has a more sudden onset, can be more sudden and severe.\n", + " * **Symptoms:** Often has a more sudden onset, can be more sudden and severe.\n", + " * \n" + ] + } + ], + "source": [ + "# Print the first choice\n", + "print(output_df[\"choices\"][0][0][\"message\"][\"content\"])" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "57891793-3e6e-4f6d-b9f4-7fc0a60d1dda", + "metadata": { + "language": "python", + "name": "cell16" + }, + "outputs": [], + "source": [ + "! pip install openai -q" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "5dcb94dd-4959-46da-a590-02c97f8de9c4", + "metadata": { + "codeCollapsed": false, + "language": "python", + "name": "cell11" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Okay, I can help you understand how a medical assistant can differentiate between bacterial and viral pneumonia.\\n\\n**How a Medical Assistant can help differentiate between bacterial and viral pneumonia:**\\n\\n**1. Medical History and Symptoms:**\\n\\n* **Onset:**\\n * **Bacterial pneumonia:** Often has a more sudden onset, can be more sudden and severe.\\n * **Viral pneumonia:** Often has a more gradual onset, can be more gradual and more gradual.\\n* **Onset:**\\n * **Bacterial pneumonia:** Often has a more sudden onset, can be more sudden and severe.\\n * **Viral pneumonia:** Often has a more gradual onset, can be more gradual and more gradual.\\n* **Symptoms:**\\n * **Bacterial pneumonia:**\\n * **Symptoms:** Often has a more sudden onset, can be more sudden and severe.\\n * **Symptoms:** Often has a more sudden onset, can be more sudden and severe.\\n * **Symptoms:** Often has a more sudden onset, can be more sudden and severe.\\n * **Symptoms:** Often has a more sudden onset, can be more sudden and severe.\\n * '" + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Optional: To get OpenAI chat completion object\n", + "# Note: requires openai python SDK `pip install openai`\n", + "import openai\n", + "\n", + "\n", + "def convert_to_openai_completion(df):\n", + " completions = []\n", + " for _, row in df.iterrows():\n", + " completions.append(openai.types.chat.ChatCompletion(**row))\n", + "\n", + " return completions\n", + "\n", + "\n", + "completions = convert_to_openai_completion(output_df)\n", + "\n", + "completions[0].choices[0].message.content" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a2d81189", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'bdk7a2g-sfengineering-mlplatformtest.awsuswest2preprod8.pp-snowflakecomputing.app'" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ingress_url = session.sql(f\"SHOW ENDPOINTS IN SERVICE {service_name}\").collect()[0][\n", + " \"ingress_url\"\n", + "]\n", + "ingress_url" + ] + }, + { + "cell_type": "markdown", + "id": "1e5d89df", + "metadata": {}, + "source": [ + "## To consume REST endpoint\n", + "\n", + "Consume the model inference using REST endpoint" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3fc47d9b", + "metadata": {}, + "outputs": [], + "source": [ + "from snowflake.ml.utils import connection_params\n", + "\n", + "conn_cfg = connection_params.SnowflakeLoginOptions(\n", + " connection_name=\"...\", # Optional\n", + ")\n", + "\n", + "pat_token = conn_cfg.get(\"password\")\n", + "headers = {\n", + " \"Authorization\": f'Snowflake Token=\"{pat_token}\"',\n", + " \"Content-Type\": \"application/json\",\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f35a2cdf", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "200" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import requests\n", + "\n", + "URL = f\"https://{ingress_url}/--call--\"\n", + "\n", + "\n", + "def invoke_endpoint(chat_requests, headers):\n", + " data_array = []\n", + " for i, chat_request in enumerate(chat_requests):\n", + " question_row = [i, chat_request]\n", + " data_array.append(question_row)\n", + "\n", + " payload = {\"data\": data_array}\n", + "\n", + " return requests.post(URL, headers=headers, json=payload)\n", + "\n", + "\n", + "messages = [\n", + " {\"role\": \"system\", \"content\": \"You are a helpful medical assistant.\"},\n", + " {\n", + " \"role\": \"user\",\n", + " \"content\": \"How do you differentiate bacterial from viral pneumonia?\",\n", + " },\n", + "]\n", + "\n", + "chat_requests = [\n", + " {\n", + " \"messages\": messages,\n", + " \"max_completion_tokens\": 250,\n", + " \"temperature\": 0.9,\n", + " \"stop\": None,\n", + " \"n\": 3,\n", + " # Note streaming is not supported yet\n", + " \"stream\": False,\n", + " \"top_p\": 1.0,\n", + " \"frequency_penalty\": 0.1,\n", + " \"presence_penalty\": 0.2,\n", + " }\n", + "]\n", + "\n", + "response = invoke_endpoint(chat_requests, headers)\n", + "response.status_code" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "b292ceae", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Okay, I can help you understand how a medical assistant can differentiate between bacterial and viral pneumonia.\n", + "\n", + "**How a Medical Assistant can help differentiate between bacterial and viral pneumonia:**\n", + "\n", + "**1. Medical History and Symptoms:**\n", + "\n", + "* **Onset:**\n", + " * **Bacterial pneumonia:** Often has a more sudden onset, can be more sudden and severe.\n", + " * **Viral pneumonia:** Often has a more gradual onset, can be more gradual and more gradual.\n", + "* **Onset:**\n", + " * **Bacterial pneumonia:** Often has a more sudden onset, can be more sudden and severe.\n", + " * **Viral pneumonia:** Often has a more gradual onset, can be more gradual and more gradual.\n", + "* **Symptoms:**\n", + " * **Bacterial pneumonia:**\n", + " * **Symptoms:** Often has a more sudden onset, can be more sudden and severe.\n", + " * **Symptoms:** Often has a more sudden onset, can be more sudden and severe.\n", + " * **Symptoms:** Often has a more sudden onset, can be more sudden and severe.\n", + " * **Symptoms:** Often has a more sudden onset, can be more sudden and severe.\n", + " * \n" + ] + } + ], + "source": [ + "import openai\n", + "\n", + "\n", + "def convert_responses_to_openai_completion(responses):\n", + " completions = []\n", + " data = responses[\"data\"]\n", + " for item in data:\n", + " response = item[-1]\n", + " completions.append(openai.types.chat.ChatCompletion(**response))\n", + "\n", + " return completions\n", + "\n", + "\n", + "openai_completions = convert_responses_to_openai_completion(response.json())\n", + "print(openai_completions[0].choices[0].message.content)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "daa936c2", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "snowml", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.14" + }, + "lastEditStatus": { + "authorEmail": "p.ramachandran@snowflake.com", + "authorId": "1900264113741", + "authorName": "PRAMACHANDRAN", + "lastEditTime": 1758825796536, + "notebookId": "wcliz4mgunu2knzfdyet", + "sessionId": "8ed1ec23-7aed-4eca-93c5-5221f6e50f0c" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +}