[C++] 365 ChatAIアプリの製作 その46 画像エンコードデータ送信 claude-3 

[Mac M2 Pro 12CPU, Sonoma 14.3.1, clang++ 15.0.0]
対応LLM:gpt-4, gpt-4-vision, claude-3

claude-3でも画像のエンコードデータを送れるようにしました。JSONの形式がgpt-4と異なるため、かなり手を入れました。

画像のあるなしに関係なくclaude-3の方がレスポンスに時間が掛かります。

if (model.find("gpt") != string::npos){
    // gpt-4
    requestData = "{\"model\":\"" + model + "\", \"messages\":[{\"role\":\"system\",\"content\":\"" + systemStr + "\"},{\"role\":\"user\",\"content\":\"" + question + "\"}], \"temperature\":0.0}";
} else {
    if (urls == ""){
        // claude-3 画像なし
        requestData = "{\"model\":\"" + model + "\", \"messages\":[{\"role\":\"user\",\"content\":\"" + systemStr + question + "\"}], \"temperature\":0.0, \"max_tokens\":4096}";
    } else {
        // claude-3 画像あり
        string requestData_1 = "{\"model\":\"" + model + "\", \"messages\":[{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"text\":\"" + systemStr + question + "\"},";
        vector<string> urlList = splitString(urls, '\n');
        cout << "urlListの要素数: " << to_string(urlList.size()) << endl;

        std::stringstream imageStream;
        for (size_t i = 0; i < urlList.size(); ++i) {
            // ローカル画像はBase64形式にエンコードする
            if (urlList[i].find("https:") != string::npos){
                cout << "claude3への画像URL送信には未対応" << endl;
                return "";
            } else {
                string base64_image = file_to_base64(urlList[i]);
                string file_extension = get_file_extension(urlList[i]);
                string url = "\"media_type\":\"image/" + file_extension + "\",\"type\":\"base64\", \"data\": \"" + base64_image + "\"";
                imageStream << "{\"type\": \"image\", \"source\": {" << url << "}}";
            }
            
            if (i < urlList.size() - 1) {
                imageStream << ",";
            }
        }

        std::string requestData_2 = imageStream.str() + "]}], \"max_tokens\": 4096,\"temperature\": 0.0}";
        cout << "requestData_2: \n" << requestData_2.c_str() << endl;

        requestData = requestData_1 + requestData_2;
    }
}

※ file_to_base64関数他については前回の記事参照

[C++] 364 ChatAIアプリの製作 その45 画像URL or エンコードデータ送信 gpt-4-vision 

[Mac M2 Pro 12CPU, Sonoma 14.3.1, clang++ 15.0.0]
対応LLM:gpt-4, gpt-4-vision, claude-3

これまでは自分のブログの非公開記事に画像を貼り付けて、そのURLを送信していましたが、ローカル画像をBase64形式にエンコードして送れるようにもしました。

string requestData_1 = "{\"model\":\"" + model + "\", \"messages\":[{\"role\":\"system\",\"content\":\"" + systemStr + "\"},{\"role\":\"user\",\"content\":[{\"type\": \"text\", \"text\":\"" + question + "\"},";

cout << "requestData_1: \n" << requestData_1.c_str() << endl;

vector<string> urlList = splitString(urls, '\n');
cout << "urlListの要素数: " << to_string(urlList.size()) << endl;

std::stringstream imageStream;
for (size_t i = 0; i < urlList.size(); ++i) {
    // URLの場合はそのまま使用し、ローカルの場合はBase64形式にエンコードする
    if (urlList[i].find("https:") != string::npos){
        imageStream << "{\"type\": \"image_url\", \"image_url\": {\"url\":\"" << urlList[i] << "\"}}";
    } else {
        string base64_image = file_to_base64(urlList[i]);
        string file_extension = get_file_extension(urlList[i]);
        string url = "data:image/" + file_extension + ";base64," + base64_image;
        imageStream << "{\"type\": \"image_url\", \"image_url\": {\"url\":\"" << url << "\"}}";
    }
    
    if (i < urlList.size() - 1) {
        imageStream << ",";
    }
}

std::string requestData_2 = imageStream.str() + "]}], \"max_tokens\": 4096,\"temperature\": 0.0}";
cout << "requestData_2: \n" << requestData_2.c_str() << endl;

requestData = requestData_1 + requestData_2;
#include <boost/archive/iterators/base64_from_binary.hpp>
#include <boost/archive/iterators/transform_width.hpp>
#include <boost/archive/iterators/ostream_iterator.hpp>
#include <fstream>
#include <sstream>
#include <string>

std::string file_to_base64(const std::string& file_path) {
    using namespace boost::archive::iterators;
    using Base64EncodeIterator = base64_from_binary<transform_width<std::string::const_iterator, 6, 8>>;
    
    std::ifstream file(file_path, std::ios::binary);
    std::string file_contents((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
    
    std::stringstream os;
    std::copy(Base64EncodeIterator(file_contents.begin()), Base64EncodeIterator(file_contents.end()), ostream_iterator<char>(os));
    size_t num = (3 - file_contents.length() % 3) % 3;
    for (size_t i = 0; i < num; i++) {
        os.put('=');
    }
    return os.str();
}

std::string get_file_extension(const std::string& file_path) {
    size_t dot_pos = file_path.rfind('.');
    if (dot_pos == std::string::npos) return "";
    return file_path.substr(dot_pos + 1);
}

[C++] 363 ChatAIアプリの製作 その44 role:systemの扱い claude-3 

[Mac M2 Pro 12CPU, Sonoma 14.3.1, clang++ 15.0.0]
対応LLM:gpt-4, gpt-4-vision, claude-3

claude-3ではgpt-4のようにrole:systemの設定ができないため、最初のプロンプトでuserとして指示します。

if (model.find("gpt") != string::npos){
   requestData = "{\"model\":\"" + model + "\", \"messages\":[{\"role\":\"system\",\"content\":\"" + systemStr + "\"},{\"role\":\"user\",\"content\":\"" + question + "\"}], \"temperature\":0.0}";
} else {
   requestData = "{\"model\":\"" + model + "\", \"messages\":[{\"role\":\"user\",\"content\":\"" + systemStr + question + "\"}], \"temperature\":0.0, \"max_tokens\":1024}";
}