FileUploadAPIを呼んでみた！

# はじめに

部署配属後、初めて参画したプロジェクトで一番難しかった実装内容についての記録です。

この記事を読むと
* FileUploadAPIとは/FileUploadAPIをAzure Functionsで呼ぶコード
* Azure FunctionsをHULFT SquareのRESTコネクターで実行する方法

がわかります<(_ _)>

長いですが最後まで読んでいただけたら嬉しいです(*^-^*)

### 実装した処理の目的
トークン数に引っかかるような大容量ファイルをAOAIで分析する。
↑をHULFT Squareで実行させたい！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3878743/56ba6e0b-4d5c-0dac-799f-e6693955bcdc.png)



(AIに直接ファイルをアップロードする(置く)イメージだよ　by先輩)
### 行ったこと
1\. Blobストレージ上にあるファイルをAOAIにアップロードする関数を作る
2\. この関数をAzure開発環境(クライド)にデプロイする
3\. RESTコネクターを用いてHULFT Squareで実行する

の3工程！
かなり大変だったのに文字に起こすと少ない(´；ω；`)


#### 前提知識

私はAzureってなんだ？？レベルで始めたので、必要な前提知識も記録。

* Blobストレージ：エクスプローラの様なもの
 
https://learn.microsoft.com/ja-jp/azure/storage/blobs/storage-blobs-introduction
* AOAI(=Azure OpenAI Service)：Azureで使える生成AI

https://learn.microsoft.com/ja-jp/azure/ai-services/openai/overview

* Azure Functions：

https://learn.microsoft.com/ja-jp/azure/azure-functions/functions-overview?pivots=programming-language-csharp

* Assistants API:

https://learn.microsoft.com/ja-jp/azure/ai-services/openai/concepts/assistants

# FileUploadAPIとは
Assistants APIのひとつです。

### 主な概要
ローカルファイルをAzure(クラウド)にアップロードし、それをAIサービスの入力データとして使用できるようにするためのAPIのこと。ファイルが入力データになるので、**トークン数に引っかかるような大容量ファイル**をAIで分析することができます。

https://learn.microsoft.com/en-us/rest/api/azureopenai/files/upload?view=rest-azureopenai-2024-05-01-preview&tabs=HTTP&preserve-view=true



# Azure Functionsへコードをデプロイする

### デプロイ方法
**1\. 関数をローカルで実行できるか確認**
**2\. Azure環境にデプロイ**
 
の2工程です!


(Azure初心者の為、記録しています<(_ _)>) 


### 作成したAzure Functionsのコード

工夫した点は、アップロードするファイル名を動的に取得できるようにしたことです✨

まずファイル名を固定値で取得する→json型の入力値で動的に取得するという風に作成しました！

```function_app.py
import azure.functions as func
import logging
import requests
from azure.storage.blob import BlobServiceClient
import json


# 設定
OPENAI_API_UPLOAD_URL = "https://your-openai-service-name.openai.azure.com/openai/files?api-version=2024-05-01-preview"  
#↑URL構造：AOAIのエンドポイント/リソースパス？クエリパラメータ

# Azure Blob Storageからファイルをダウンロードする関数
def download_blob(connection_string, container_name, blob_name):
    blob_service_client = BlobServiceClient.from_connection_string(connection_string)
    blob_client = blob_service_client.get_blob_client(container=container_name, blob=blob_name)

    file_data = blob_client.download_blob().readall()
    print(file_data)

    return file_data

# OpenAIにファイルをアップロードする関数
def upload_to_openai(api_key,blob_name,file_data):
    headers = {
        'api-key': f'{api_key}'
    }
    files = {
        'file': (blob_name,file_data)   
    }
    # purposeの指定
    data = {
        'purpose' : 'assistants'
    }
    response = requests.post(OPENAI_API_UPLOAD_URL, headers=headers, files=files,data=data)
    return response.json()

app = func.FunctionApp(http_auth_level=func.AuthLevel.FUNCTION)

@app.route(route="blob_to_aoai_fileupload", methods=[func.HttpMethod.POST])
def blob_to_aoai_fileupload(req: func.HttpRequest) -> func.HttpResponse: #トリガー
    logging.info('Python HTTP trigger function processed a request.')
  
    # name = req.params.get('name') #デフォルトのやつリクエストのbodyを取り出したい
    # if not name:
    #     try:
    #         req_body = req.get_json()
    #     except ValueError:
    #         pass
    #     else:
    #         name = req_body.get('name')

    # if name:
    #     return func.HttpResponse(f"Hello, {name}. This HTTP triggered function executed successfully.")
    # else:
    #     return func.HttpResponse(
    #          "This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response.",
    #          status_code=200
    #     )
    
    try:
        req_body = req.get_json()
        logging.info(f'Request JSON: {req_body}')
    except ValueError as e:
        logging.error(f'Invalid JSON body: {e}')
        return func.HttpResponse(
            "Invalid JSON body",
            status_code=400
        )
    
    #JSONボディから必要な情報を取得
    connection_string = req_body.get('connection_string')
    logging.info('get connection_string successfully')
    container_name = req_body.get('container_name')
    logging.info('get container_name successfully')
    blob_name = req_body.get('blob_name')
    logging.info('get blob_name successfully')
    api_key = req_body.get('api_key')

    # Azure Blob Storageからファイルをダウンロード
    result=download_blob(connection_string, container_name, blob_name)

    logging.info("ok/download")

    # ダウンロードしたファイルをOpenAIにアップロード
    response = upload_to_openai(api_key, blob_name,result)
    logging.info(response)
    logging.info("ok/upload")
    return func.HttpResponse(
        json.dumps({
            "message": "File uploaded successfully to OpenAI.",
            "openai_response": response
        }),
        status_code=200,
        mimetype="application/json"
    )
```


# RESTコネクターで使用するコネクションを作成する
この章は先輩の記事↓を、DataSpiderではなくHULFT SquareのRESTコネクターでやってみたという内容です。この記事とほぼ同じ内容なので、違うところだけを記述させていただきます。

https://dmscube.com/view/post/0/510488

### グローバルリソースの作成をRESTコネクターで行う

上記の記事と違う手順は１つだけ、
> <dt>1 RESTアダプタでAzure Functionsの関数を実行</dt>
> <dd>c DataSpiderでの設定</dd>
><dd>　①グローバルリソースの作成←ここ！！！</dd>

HULFT Squareでは、コネクションで作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3878743/f49e1f10-d6c8-7531-bab7-4e89f6dff40b.png)

Azureで作成した関数アプリのURLを入力します↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3878743/990fa12f-eb18-a147-8af2-00d0f4960979.png)

さらに、コネクションを使用できるよう**APIクライアント**を作成します。

こちらに関しては公式ドキュメントをご参照ください↓

https://www.hulft.com/help/ja-jp/HULFTSquare/Content/TOP/Tutorial/tutorial_job_HTTPREST.htm#SC_04

これ以降は、先輩の記事通りです<(_ _)>

### その他事前準備(RESTコネクターに必要な情報)

* blobの接続文字列
* blobにあるファイル名
* ファイルがあるコンテナ名
* AOAIのAPIキー値

をAzure環境で確認する！


### 作成したデザイナ画面
私が作成した、Azure Functionsを実行するスクリプトのデザイナ画面です！！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3878743/2e22ea12-b43a-ebb4-1da4-fb3634586a83.png)

上記添付の環境変数設定のところで、blobストレージの接続文字列や、キー値(etc..)などを設定しています↓。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3878743/5b5f3a19-7c19-7d8a-0c69-b7d8d6c11497.png)

# 最後に
この経験を通して、Azure FunctionsやそれをHULFT Squareで実行する方法を実践的に学ぶことができました。

同時に、この連携が社内的に初めての試みだったこともあり、他者に伝えるための過程を残すことがとても大切だと知った実装でもあります。

たくさんの方々に大変お世話になりました<(_ _)>
ありがとうございました！！