#author("2024-12-02T14:54:40+00:00","default:iseki","iseki")
#author("2024-12-04T09:04:31+00:00","default:iseki","iseki")
** v0.9 
*** Note Card
**** ChatGPT_API_Key
 sk-##########################################
**** ChatGPT_Model
 gpt-4-turbo
- or gpt-3.5-turbo



**** ChatGPT_Charactor
<pre>
貴方は美術館の説明員です.
美術の質問以外については答えてはいけません.
答えは日本語で答えます.
</pre>


**** Appearance
- [[ProNama_Red_Appearance>./ProNama_Red_Appearance]]


*** Code
**** NSL ChatGPT for OpenSimulator
<pre>
// NPC and NSL_ChatGPT Rezzer v1.1.3
//   support NSL_ChatCPT is v1.3.1
// NSL ChatGPT for OpenSimulator v1.3.1
//

string  fname = "プロ生";
string  lname = "ちゃん";
string  appear_name = "ProNama_Red_Appearance";
integer command_channel = 0;
integer listen_hdl = 0;
integer say_bufsize = 320; // < 1023バイト以下での UTF-8(3Byte) の文字数

float   gap_time = 4.0;
integer time_count = 0;

// ChatGPT API のエンドポイント
string  api_url = "https://api.openai.com/v1/chat/completions";

// ChatGPT API の変数
string  api_key = "";
string  api_model = "";
string  api_chara = "";

// Note Card
string  chara_notecard_name  = "ChatGPT_Charactor";
string  model_notecard_name  = "ChatGPT_Model";
string  gptkey_notecard_name = "ChatGPT_API_Key";

key     chara_notecard_key  = NULL_KEY;
key     model_notecard_key  = NULL_KEY;
key     gptkey_notecard_key = NULL_KEY;

integer chara_notecard_line  = 0;
integer model_notecard_line  = 0;
integer gptkey_notecard_line = 0;

string  chara_notecard  = "";
string  model_notecard  = "";
string  gptkey_notecard = "";


// ChatGPT API の JSON データのテンプレート.記載されていないパラメータはデフォルト値 
string  json_templ = "{
  \"model\": \"\",
  \"user\": \"\",
  \"messages\":[
     {
       \"role\": \"system\",
       \"content\": \"\"
     },
    {
       \"role\": \"user\",
       \"content\": \"\"
     }
  ],
  \"stream\": false 
}";


//
// Listen
integer DEFAULT_CHANNEL = 683;
integer listen_hdl = 0;
key    user_key  = NULL_KEY;
key    npc_key   = NULL_KEY;
string user_name = "";

// NPC List
integer list_stride = 3;
list npc_list = []; // [user_key, npc_key, channel] の繰り返し


// 指定したユーザがリストにあるかどうかをチェックする.
//   ユーザが存在する場合は,npc_list中の位置を返す.
//   ユーザが存在しない場合は -1 を返す
integer check_user_list(key _user_key)
// ノートカードから最初の一行を読み込む
key read_notecard_first(string notecard_name)
{
    integer _indx = 0;
    integer _len = llGetListLength(npc_list);
    
    for(_indx=0; _indx<_len; _indx+=list_stride) {
        if (_user_key == (key)npc_list[_indx]) {
            return _indx;
        }
    } 
    return -1;
    if (llGetInventoryType(notecard_name)==INVENTORY_NOTECARD) {
        key notecard_key = llGetNotecardLine(notecard_name, 0);
        return notecard_key;
    }
    return NULL_KEY;
}


// 指定したNPCがリストにあるかどうかをチェックする.
//   NPCが存在する場合は,npc_list中の位置を返す.
//   NPCが存在しない場合は -1 を返す
integer check_npc_list(key _npc_key)
// 全てのノートカードを読み込む
read_notecards()
{
    integer _indx = 0;
    integer _len = llGetListLength(npc_list);
    chara_notecard  = "";   
    model_notecard  = "";
    gptkey_notecard = "";
    
    for(_indx=0; _indx<_len; _indx+=list_stride) {
        if (_npc_key == (key)npc_list[_indx + 1]) {
            return _indx;
        }
    } 
    return -1;
    chara_notecard_key  = read_notecard_first(chara_notecard_name);
    gptkey_notecard_key = read_notecard_first(gptkey_notecard_name);
    model_notecard_key  = read_notecard_first(model_notecard_name);
}


// 指定したチャンネル番号がリストにあるかどうかをチェックする.
//   チャンネル番号が存在する場合は,npc_list中の位置を返す.
//   チャンネル番号が存在しない場合は -1 を返す
integer check_channel_list(integer _channel)
// ChatGPTの APIに _messageを送信する.
request_gpt_api(string _message)
{
    integer _indx = 0;
    integer _len = llGetListLength(npc_list);
    string _json_body = "";
    _json_body = llJsonSetValue(json_templ, ["model"], api_model);
    _json_body = llJsonSetValue(_json_body, ["user"],  user_key);
    _json_body = llJsonSetValue(_json_body, ["messages", 0, "content"], api_chara);
    _json_body = llJsonSetValue(_json_body, ["messages", 1, "content"], _message);
    //llSay(0, _json_body);
    
    for(_indx=0; _indx<_len; _indx+=list_stride) {
        if (_channel == (integer)npc_list[_indx + 2]) {
            return _indx;
        }
    } 
    return -1;
    llHTTPRequest(api_url, 
        [
            HTTP_MIMETYPE, "application/json",
            HTTP_METHOD, "POST",
            HTTP_BODY_MAXLENGTH, 16384,
            //HTTP_ACCEPT, "application/json",
            HTTP_CUSTOM_HEADER, "Authorization", "Bearer " + api_key
        ],
        _json_body
    );
}


integer  get_valid_channel()
start_conversation(string _name)
{
    integer _channel = DEFAULT_CHANNEL + 1;
    
    integer _indx = check_channel_list(_channel);
    while (_indx != -1) {
        _channel++;
        _indx = check_channel_list(_channel);
    }   
    return _channel;
    //llSay(0, "Start Conversation");
    request_gpt_api("こんにちは.私は " + _name + " です");
    llListen(0, "", NULL_KEY, "");
}


// NPC の作成と NSL ChatGPT の Rez
key create_npc(key _user_key, vector _user_pos)
short_conversation(string _message)
{
    vector _obj_pos = llGetPos();
    vector _move_to = _user_pos - llVecNorm(_user_pos - _obj_pos)*1.5; // 1.5m 手前
    float  _dist = llVecDist(_obj_pos, _move_to);
    //llRegionSayTo(_user_key, 0, "DIST = " + _dist);

    key _npc_key = NULL_KEY;
    if (_dist < 10.0) {
        // NPC 作成
        _npc_key = osNpcCreate(fname, lname, _obj_pos, appear_name, OS_NPC_SENSE_AS_AGENT);
        osNpcMoveTo(_npc_key, _move_to);
        
        // NSL_ChatGPT の Rez
        integer _channel = get_valid_channel();
        llRezObject("NSL_ChatGPT", _move_to, <0.0, 0.0, 0.0>, <0.0, 0.0, 0.0, 1.0>, _channel);
        //
        npc_list = npc_list + [_user_key, _npc_key, _channel];
    if (npc_key == NULL_KEY) {
        llRegionSayTo(user_key, 0, _message);
    }
    else {
        llRegionSayTo(_user_key, 0, "オブジェクトから離れすぎています.10m 以内に近づいてください.");
        osNpcSayTo(npc_key, user_key, 0, _message);
    }
    //llOwnerSay("NPC Key = " + _npc_key + ", Channel = " + _channel);
    
    return _npc_key;
}


delete_all_npc()
stop_conversation()
{
    integer _len = llGetListLength(npc_list);
    integer _indx = 0;
    for (_indx=0; _indx<_len; _indx += list_stride) {
        key _npc_key = (key)npc_list[_indx + 1];
        osNpcRemove(_npc_key);
    }
    npc_list = [];
    string _message = "バイバイ.";
    short_conversation(_message);
}


init_script()
init_state()
{
    if (listen_hdl!=0) llListenRemove(listen_hdl);
    listen_hdl = llListen(DEFAULT_CHANNEL, "", NULL_KEY, "");
 
    delete_all_npc();
    //llSay( 0, "NSL ChatGPT Running");
    llSetTimerEvent(0);
    llResetTime();
    
    read_notecards();
    
    if (command_channel != 0) {
        if (listen_hdl!=0) llListenRemove(listen_hdl);
        listen_hdl = llListen(command_channel, "", NULL_KEY, "");
        //llSay(0, "init_state: listen Channel = " + command_channel);
    }
}


////////////////////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////////////////////////////
default
{
    state_entry()
    on_rez(integer _param)
    {
        //llSay(0, "Script running");
        init_script();
        command_channel = _param;
        if (command_channel != 0) {
            if (listen_hdl!=0) llListenRemove(listen_hdl);
            listen_hdl = llListen(command_channel, "", NULL_KEY, "");
            //llSay(0, "on_rez: listen Channel = " + command_channel);
        }
    }
    
    
    touch_start(integer _number)
    state_entry()
    {
        key    _user_key  = llDetectedKey(0);
        string _user_name = llDetectedName(0);
        vector _user_pos  = llDetectedPos(0);
        
        integer _indx = check_user_list(_user_key);
        
        // NPC と ChatGPT を起動
        if (_indx==-1) {
            key _npc_key = create_npc(_user_key, _user_pos);
            llSleep(1.0);
            integer _num = check_npc_list(_npc_key);
            if (_num>=0) {
                integer _channel = (integer)npc_list[_num + 2];
                string  _npc_name = fname + lname;
                string  _command = "start " + _user_key + " " + _npc_key + " " + _user_name;
                llRegionSay(_channel, _command);
                //llSay(0, "Send Data to " + _channel + ": " + _command);
        init_state();
    }
  
   
    touch_start(integer _num_detected)
    {
        user_key  = llDetectedKey(0);
        user_name = llDetectedName(0);
        npc_key   = NULL_KEY;
        start_conversation(user_name);
    }


    listen(integer _ch, string _name, key _id, string _message)
    {
        //llSay(0, "Receive Data from " + _ch + ": " + _id + ": " + _message); 
        // from Controller
        //      /### start user_key npc_key user_first_name user_last_name 
        if (_ch != 0 && _ch == command_channel) {
            list   _items = llParseString2List(_message, [" "], []);
            string _cmd   = llList2String(_items, 0);

            if (llToLower(_cmd) == "start") {
                integer _len = llGetListLength(_items);
                if (_len > 3) {
                    user_key  = llList2String(_items, 1);
                    npc_key   = llList2String(_items, 2);
                    user_name = llList2String(_items, 3);
                    if (_len > 4) user_name += " " + llList2String(_items, 4); 
                    start_conversation(user_name);
                }
            }
            else if (llToLower(_cmd) == "stop") {
                stop_conversation();
                llDie();
            }
        }
        // 既にNPCを起動している場合は,ChatGPTとNPCを削除
        else if (_indx>=0) {
            key     _npc_key = (key)npc_list[_indx + 1];
            integer _channel = (integer)npc_list[_indx + 2];
            string  _command = "stop";
            llRegionSay(_channel, _command);
            //llSay(0, "Send Data to " + _channel + ": " + _command);
            llSleep(1.0);
            osNpcRemove(_npc_key);
            npc_list = llListReplaceList(npc_list, [], _indx, _indx + list_stride - 1);
        // from User
        else if (_id == user_key) {
            //llSay(0, _message);
            request_gpt_api(_message);
            //
            llSetTimerEvent(gap_time);
            llResetTime();
            time_count = 0;
        }
        else {
            llSay(0, "NOING");
        }
    } 


    changed(integer _change)
    {
        if (_change & CHANGED_INVENTORY) {
            //llSay(0, "Reread NoteCards");
            read_notecards();
        }
    }
    
    
    listen(integer _ch, string _name, key _id, string _msg) 


    // ノートカードが一行読まれる度に発生するイベント
    dataserver(key _requested_key, string _data)
    {
        //llSay(0, "Recived Message: " + _msg);
        
        list _items = llParseString2List(_msg, ["=", ",", " ", "\n"], []);       
        string _cmd = llList2String(_items, 0);
        string _opr = llList2String(_items, 1);
        // GPT Charactor
        if (_requested_key == chara_notecard_key) {
            if (_data != EOF){
                chara_notecard += _data + "\n";
                chara_notecard_line++;
                chara_notecard_key = llGetNotecardLine(chara_notecard_name, chara_notecard_line);
            }
            else {
                api_chara = chara_notecard;
            }
        }
        // GPT API キー
        else if (_requested_key == gptkey_notecard_key) {
            if (_data != EOF){
                gptkey_notecard += _data;
                gptkey_notecard_line++;
                gptkey_notecard_key = llGetNotecardLine(gptkey_notecard_name, gptkey_notecard_line);
            }
            else {
                api_key = gptkey_notecard;
            }
        }
        // GPT API Model
        else if (_requested_key == model_notecard_key ) {
            if (_data != EOF){
                model_notecard += _data;
                model_notecard_line++;
                model_notecard_key = llGetNotecardLine(model_notecard_name, model_notecard_line);
            }
            else {
                api_model = model_notecard;
            }
        }
    }

        if  (_cmd == "" || _cmd == " ") {
            // NOP
        }
        else if (llToLower(_cmd)=="delete" || llToLower(_cmd)=="del") {
            if (_id == llGetOwner()) {
                if (llToLower(_opr) == "all") {
                    delete_all_npc();

    http_response(key _request_id, integer _status, list _metadata, string _body)
    {
        llSetTimerEvent(0);
        time_count = 0;
        //
        if (_status == 200) {
            string _content = llJsonGetValue(_body, ["choices", 0, "message", "content"]);
            //llSay(0, "BODY SIZE = " + llStringLength(_body));
            //llSay(0, "CONTENT SIZE = " + llStringLength(_content));
            //llSay(0, "Data = " + _body);
            
            if (npc_key == NULL_KEY) {
                for (integer p=0; p<llStringLength(_content); p+=say_bufsize) {
                    llRegionSayTo(user_key, 0, llGetSubString(_content, p, p+say_bufsize-1));
                }
                else {
                    key _npc_key = (key)_opr;
                    osNpcRemove(_npc_key);
                    integer _indx = check_npc_list(_npc_key);
                    if (_indx>=0) npc_list = llListReplaceList(npc_list, [], _indx, _indx + list_stride - 1);
            }
            else {
                for (integer p=0; p<llStringLength(_content); p+=say_bufsize) {
                    osNpcSayTo(npc_key, user_key, 0, llGetSubString(_content, p, p+say_bufsize-1));
                }
            }
        }
        // エラー応答
        else {
            if (npc_key == NULL_KEY) {
                llRegionSayTo(user_key, 0, "リクエスト失敗: " + (string)_status + ", 応答内容: " + _body);
            }
            else {
                osNpcSayTo(npc_key, user_key, 0, "リクエスト失敗: " + (string)_status + ", 応答内容: " + _body);
            }
        }
    }
    

    //
    on_rez(integer _start_param) 
    
    timer()
    {
        init_script();
        llResetScript();
        if      (time_count==0) short_conversation("少々おまちください.");
        else if (time_count==2) short_conversation("もうしばらくおまちください.");
        else if (time_count==5) short_conversation("時間が掛かっています.");
        time_count++;
    }
   
    
    changed(integer _change)
    {
        //地域が再起動された場合
        if (_change & CHANGED_REGION_START) {
            init_script();
            llResetScript();
        }
        else if (_change & CHANGED_INVENTORY) {
            init_script();
            llResetScript();
        }   
    }                
}

</pre>



**** NPC and NSL_ChatGPT Rezzer 
<pre>
// NPC and NSL_ChatGPT Rezzer v1.1.3
//   support NSL_ChatCPT is v1.3.1
//

string  fname = "プロ生";
string  lname = "ちゃん";
string  appear_name = "ProNama_Red_Appearance";

//
// Listen
integer DEFAULT_CHANNEL = 683;
integer listen_hdl = 0;

// NPC List
integer list_stride = 3;
list npc_list = []; // [user_key, npc_key, channel] の繰り返し


// 指定したユーザがリストにあるかどうかをチェックする.
//   ユーザが存在する場合は,npc_list中の位置を返す.
//   ユーザが存在しない場合は -1 を返す
integer check_user_list(key _user_key)
{
    integer _indx = 0;
    integer _len = llGetListLength(npc_list);
    
    for(_indx=0; _indx<_len; _indx+=list_stride) {
        if (_user_key == (key)npc_list[_indx]) {
            return _indx;
        }
    } 
    return -1;
}


// 指定したNPCがリストにあるかどうかをチェックする.
//   NPCが存在する場合は,npc_list中の位置を返す.
//   NPCが存在しない場合は -1 を返す
integer check_npc_list(key _npc_key)
{
    integer _indx = 0;
    integer _len = llGetListLength(npc_list);
    
    for(_indx=0; _indx<_len; _indx+=list_stride) {
        if (_npc_key == (key)npc_list[_indx + 1]) {
            return _indx;
        }
    } 
    return -1;
}


// 指定したチャンネル番号がリストにあるかどうかをチェックする.
//   チャンネル番号が存在する場合は,npc_list中の位置を返す.
//   チャンネル番号が存在しない場合は -1 を返す
integer check_channel_list(integer _channel)
{
    integer _indx = 0;
    integer _len = llGetListLength(npc_list);
    
    for(_indx=0; _indx<_len; _indx+=list_stride) {
        if (_channel == (integer)npc_list[_indx + 2]) {
            return _indx;
        }
    } 
    return -1;
}


integer  get_valid_channel()
{
    integer _channel = DEFAULT_CHANNEL + 1;
    
    integer _indx = check_channel_list(_channel);
    while (_indx != -1) {
        _channel++;
        _indx = check_channel_list(_channel);
    }   
    return _channel;
}


// NPC の作成と NSL ChatGPT の Rez
key create_npc(key _user_key, vector _user_pos)
{
    vector _obj_pos = llGetPos();
    vector _move_to = _user_pos - llVecNorm(_user_pos - _obj_pos)*1.5; // 1.5m 手前
    float  _dist = llVecDist(_obj_pos, _move_to);
    //llRegionSayTo(_user_key, 0, "DIST = " + _dist);

    key _npc_key = NULL_KEY;
    if (_dist < 10.0) {
        // NPC 作成
        _npc_key = osNpcCreate(fname, lname, _obj_pos, appear_name, OS_NPC_SENSE_AS_AGENT);
        osNpcMoveTo(_npc_key, _move_to);
        
        // NSL_ChatGPT の Rez
        integer _channel = get_valid_channel();
        llRezObject("NSL_ChatGPT", _move_to, <0.0, 0.0, 0.0>, <0.0, 0.0, 0.0, 1.0>, _channel);
        //
        npc_list = npc_list + [_user_key, _npc_key, _channel];
    }
    else {
        llRegionSayTo(_user_key, 0, "オブジェクトから離れすぎています.10m 以内に近づいてください.");
    }
    //llOwnerSay("NPC Key = " + _npc_key + ", Channel = " + _channel);
    
    return _npc_key;
}


delete_all_npc()
{
    integer _len = llGetListLength(npc_list);
    integer _indx = 0;
    for (_indx=0; _indx<_len; _indx += list_stride) {
        key _npc_key = (key)npc_list[_indx + 1];
        osNpcRemove(_npc_key);
    }
    npc_list = [];
}


init_script()
{
    if (listen_hdl!=0) llListenRemove(listen_hdl);
    listen_hdl = llListen(DEFAULT_CHANNEL, "", NULL_KEY, "");
 
    delete_all_npc();
}


////////////////////////////////////////////////////////////////////////////////////////////

default
{
    state_entry()
    {
        //llSay(0, "Script running");
        init_script();
    }
    
    
    touch_start(integer _number)
    {
        key    _user_key  = llDetectedKey(0);
        string _user_name = llDetectedName(0);
        vector _user_pos  = llDetectedPos(0);
        
        integer _indx = check_user_list(_user_key);
        
        // NPC と ChatGPT を起動
        if (_indx==-1) {
            key _npc_key = create_npc(_user_key, _user_pos);
            llSleep(1.0);
            integer _num = check_npc_list(_npc_key);
            if (_num>=0) {
                integer _channel = (integer)npc_list[_num + 2];
                string  _npc_name = fname + lname;
                string  _command = "start " + _user_key + " " + _npc_key + " " + _user_name;
                llRegionSay(_channel, _command);
                //llSay(0, "Send Data to " + _channel + ": " + _command);
            }
        }
        // 既にNPCを起動している場合は,ChatGPTとNPCを削除
        else if (_indx>=0) {
            key     _npc_key = (key)npc_list[_indx + 1];
            integer _channel = (integer)npc_list[_indx + 2];
            string  _command = "stop";
            llRegionSay(_channel, _command);
            //llSay(0, "Send Data to " + _channel + ": " + _command);
            llSleep(1.0);
            osNpcRemove(_npc_key);
            npc_list = llListReplaceList(npc_list, [], _indx, _indx + list_stride - 1);
        }
    }
    
    
    listen(integer _ch, string _name, key _id, string _msg) 
    {
        //llSay(0, "Recived Message: " + _msg);
        
        list _items = llParseString2List(_msg, ["=", ",", " ", "\n"], []);       
        string _cmd = llList2String(_items, 0);
        string _opr = llList2String(_items, 1);

        if  (_cmd == "" || _cmd == " ") {
            // NOP
        }
        else if (llToLower(_cmd)=="delete" || llToLower(_cmd)=="del") {
            if (_id == llGetOwner()) {
                if (llToLower(_opr) == "all") {
                    delete_all_npc();
                }
                else {
                    key _npc_key = (key)_opr;
                    osNpcRemove(_npc_key);
                    integer _indx = check_npc_list(_npc_key);
                    if (_indx>=0) npc_list = llListReplaceList(npc_list, [], _indx, _indx + list_stride - 1);
                }
            }
        }
    }
    

    //
    on_rez(integer _start_param) 
    {
        init_script();
        llResetScript();
    }
   
    
    changed(integer _change)
    {
        //地域が再起動された場合
        if (_change & CHANGED_REGION_START) {
            init_script();
            llResetScript();
        }
        else if (_change & CHANGED_INVENTORY) {
            init_script();
            llResetScript();
        }   
    }                
}

</pre>

トップ   編集 差分 バックアップ 添付 複製 名前変更 リロード   新規 ページ一覧 検索 最終更新   ヘルプ   最終更新のRSS