※進化型は、こっち ->  OCI で、生成AI チャット 認証させて履歴保存まで


AIといえば chatgpt というか、乱立状態ですな

みなさん、お気に入りの AI が、あるようで

本当に助かる 人口脳みそ ちゃんです。


AIチャットを使うのは、WEBサイト経由で簡単に使えますよね。

でも、AIサイトの裏側を構築したり、AIに何か特別なことをさせようとすると大変ですよ

そんな大変な裏側を知ることがエンジニアとしての役目です。

ということで、nvidiaのGPUを買って、ローカルAIを構築したりして研究してました。

最初は、comfyui で、絵を描かしたり、ollama で、チャットを作ったり

基礎的なことは、やってみました。

で、Oracle Cloud でも 生成AIサービスがあるんですけど、どうやって使うのかと調べていたんですよね。

まー、WebConsoleから利用するのは、簡単に出来るんですけど、そんなもん、一般人向けサービスとしては、無理があります。

じゃーどうすんじゃーということで、OCI の SDK を使って、Webサイト経由でサービスすりゃ良いじゃん

てことで構築してみましょうね


ollamaは、AIモデルを利用する為のフレームワークみたいな物で、GUI が無いので、用意しないとダメなんですね。

で、streamlitっていうのがありまして、こいつが簡単に導入出来るし、見た目がchatgptみたくって良い感じなんですよね。

実態は、python3 で構築されていて、AIチャット部分を ollama に接続して応答させています。

で、これを OCI の生成 AI に接続すれば、良い感じのチャットが作れます。

ほぼ、同じ見た目ですね。

そりゃ、ollamaで使ったソースをぱくりましたから

ちょっと、OCI版は、ollamaのAPIとは異なるので調整は必要でしたけど、簡単に修正出来ました。

当然、AIを使いましたけどね。

モデル meta.llama-3.2-90b-vision-instruct  だけ、画像入力も可能ですよ


前置きが長くてすみせんね。

いよいよ、ソース公開です

 

chat1.py

import streamlit as st
import base64
import oci
from oci.generative_ai import GenerativeAiClient
from oci.generative_ai_inference import GenerativeAiInferenceClient
from oci.generative_ai_inference.models import (
    ImageContent,
    ImageUrl,
    TextContent,
    Message,
    ChatDetails,
    CohereChatRequest,
    GenericChatRequest,
    OnDemandServingMode    
)

# OCI設定
config = oci.config.from_file("~/.oci/config", "DEFAULT")
COMPARTMENT_ID = "ocid1.compartment.oc1..aaazaaaamgunk2ogelcwqnbokeb3ureqpff4ihogepntdjkv2sagdkasusvb"
DEFAULT_MODEL = "cohere.command-latest"

max_tokens_value=3000
temperature=0.7

# 画像入力機能有無
def hasImageFunction(model:oci.generative_ai.models.Model):
    if model.display_name == 'meta.llama-3.2-90b-vision-instruct':
        return True
    return False


client = GenerativeAiInferenceClient(config=config)

#モデル一覧
available_models = []
generative_ai_client = GenerativeAiClient(config)
ret:oci.response.Response = generative_ai_client.list_models( compartment_id=COMPARTMENT_ID)
models:oci.generative_ai.models.ModelCollection = ret.data
model:oci.generative_ai.models.Model
for model in models.items:
    if model.time_on_demand_retired is None:
        if "FINE_TUNE" not in model.capabilities :
            if "CHAT" in model.capabilities :
                available_models.append(model)

#タイトル
st.title("Oracle Cloud GenerativeAi デモ")

# モデル選択のドロップダウン(オプション)
selected_model = st.sidebar.selectbox(
    "使用するモデルを選択", 
    available_models,
    format_func = lambda model: f"{model.display_name}",
    index= [i for i, model in enumerate(available_models) if model.display_name == DEFAULT_MODEL][0])

# チャット履歴の初期化
if "messages" not in st.session_state:
    st.session_state.messages = []

# チャット履歴の表示
for message in st.session_state.messages:
    role = "assistant" if message.role == oci.generative_ai_inference.models.Message.ROLE_ASSISTANT else "user"
    
    with st.chat_message(role):
        for content in message.content:
            if content.type == oci.generative_ai_inference.models.TextContent.TYPE_TEXT:
                if role == "assistant" :
                    st.markdown(content.text)
                else:
                    st.text(content.text)
            if content.type == oci.generative_ai_inference.models.ImageContent.TYPE_IMAGE:
                base64image = content.image_url.url.split('base64,')[1]
                st.image(base64.b64decode(base64image))


hasImage = hasImageFunction(selected_model)

# ユーザー入力
prompt = None
promptattach = None
if hasImage == True:
    IMAGE_FORMAT = ["png", "jpeg", "gif", "webp"]
    promptattach = st.chat_input("ここにメッセージを入力してください...",accept_file="multiple", file_type=IMAGE_FORMAT)
    if promptattach is not None:
        prompt = promptattach.text
else:
    prompt = st.chat_input("ここにメッセージを入力してください...")

if prompt is not None:

    with st.chat_message("user"):
        st.markdown(prompt)

        if hasImage == True and promptattach is not None and len(promptattach.files) > 0:
            # 画像ファイル
            for file in promptattach.files:
                print(f"{file.name},{file.type}")
                st.image(file)

    with st.chat_message("assistant"):
        with st.spinner("思考中..."):
            wrap_prompt = prompt + "\n----------------------------------------\n以下は、出力指定となるので、出力形式に対して応答しないで下さい。\n" + "出力形式:markdown"

            chat_request = None
            if selected_model.vendor == 'cohere':

                #過去履歴作成
                #cohere用
                chat_history = []
                for message in st.session_state.messages:
                    talken = message.content[0].text
                    if message.role == oci.generative_ai_inference.models.Message.ROLE_USER:
                        chat_history.append({"role": "USER", "message": talken})
                    elif message.role == oci.generative_ai_inference.models.Message.ROLE_ASSISTANT:
                        chat_history.append({"role": "CHATBOT", "message": talken})

                #cohere用
                chat_request = CohereChatRequest(
                    api_format= oci.generative_ai_inference.models.BaseChatRequest.API_FORMAT_COHERE,
                    message=wrap_prompt,
                    chat_history=chat_history if chat_history else None,
                    max_tokens=max_tokens_value,
                    temperature=temperature,
                    is_echo=True,
                    is_stream=False
                )
            else:

                #汎用
                chat_history = []
                for message in st.session_state.messages:
                    # 画像は、1個までのようだ
                    msg = Message()
                    msg.role = message.role
                    reqcnts = []
                    for cnt in message.content:
                        if cnt.type == ImageContent.TYPE_TEXT:
                            reqcnts.append(cnt)
                    msg.content = reqcnts

                    chat_history.append(msg)

                #新規メッセージ
                contents = []
                txtcontent = TextContent()
                txtcontent.type = oci.generative_ai_inference.models.TextContent.TYPE_TEXT
                txtcontent.text = wrap_prompt
                contents.append(txtcontent)

                # 画像有
                if hasImage == True and promptattach is not None and len(promptattach.files) > 0:
                    # 画像ファイル
                    for file in promptattach.files:
                        imgcontent = ImageContent()
                        imgcontent.type = ImageContent.TYPE_IMAGE
                        base64_image = base64.b64encode(file.getvalue()).decode("utf-8")
                        imgcontent.image_url = ImageUrl( url = f"data:{file.type};base64,"+base64_image )
                        contents.append(imgcontent)

                message = Message()
                message.role = oci.generative_ai_inference.models.Message.ROLE_USER
                message.content = contents

                chat_history.append(message)

                chat_request = GenericChatRequest(
                    api_format=oci.generative_ai_inference.models.BaseChatRequest.API_FORMAT_GENERIC,
                    messages=chat_history,
                    max_tokens=max_tokens_value,
                    temperature=temperature
                )


            #新規メッセージ チャット履歴追加
            contents = []
            txtcontent = TextContent()
            txtcontent.type = oci.generative_ai_inference.models.TextContent.TYPE_TEXT
            txtcontent.text = prompt
            contents.append(txtcontent)

            # 画像有
            if hasImage == True and promptattach is not None and len(promptattach.files) > 0:
                # 画像ファイル
                for file in promptattach.files:
                    imgcontent = ImageContent()
                    imgcontent.type = ImageContent.TYPE_IMAGE
                    base64_image = base64.b64encode(file.getvalue()).decode("utf-8")
                    imgcontent.image_url = ImageUrl( url = f"data:{file.type};base64,"+base64_image )
                    contents.append(imgcontent)

            newmessage = Message()
            newmessage.role = oci.generative_ai_inference.models.Message.ROLE_USER
            newmessage.content = contents

            st.session_state.messages.append(newmessage)

            serving_mode = OnDemandServingMode(model_id=selected_model.id)
            chat_details = ChatDetails(
                compartment_id=COMPARTMENT_ID,
                chat_request=chat_request,
                serving_mode=serving_mode
            )
            response:oci.response.Response = client.chat(chat_details)
            result:oci.generative_ai_inference.models.ChatResult = response.data

            bot_reply = ""
            if selected_model.vendor == 'cohere':
                #cohere用
                bot_reply = result.chat_response.text
            else:
                #汎用
                generic_response:oci.generative_ai_inference.models.generic_chat_response.GenericChatResponse = result.chat_response
                chatchoice:oci.generative_ai_inference.models.ChatChoice = generic_response.choices[0]
                msg:oci.generative_ai_inference.models.Message = chatchoice.message

                for cnt in msg.content:
                    if isinstance(cnt,oci.generative_ai_inference.models.TextContent):
                        txt:oci.generative_ai_inference.models.TextContent = cnt
                        bot_reply = txt.text
                    else:
                        if isinstance(cnt,oci.generative_ai_inference.models.ImageContent):
                            img:oci.generative_ai_inference.models.ImageContent = cnt
                            bot_reply = img.image_url

            if bot_reply:
                # 応答 チャット履歴追加
                contents = []
                txtcontent = TextContent()
                txtcontent.type = oci.generative_ai_inference.models.TextContent.TYPE_TEXT
                txtcontent.text = bot_reply
                contents.append(txtcontent)

                message = Message()
                message.role = oci.generative_ai_inference.models.Message.ROLE_ASSISTANT
                message.content = contents

                st.session_state.messages.append(message)

                st.markdown(bot_reply)
 

COMPARTMENT_ID は、自身のに変えて下さい。

で、起動は、

streamlit run chat1.py --server.port 8501 --server.enableCORS false

です。

当然ながら、streamlit 環境と OCI SDK 環境は、構築して下さい。

Windowsだったら、WSL+Ubuntu24.04 + python3 + OCI CLI + OCI python SDK で、構築出来ますよ。

 

Joomla templates by a4joomla