2024年2月14日水曜日

OllamaでCode Llamaを実行しAPEXアプリケーションからコードを生成させる

Oracle APEX界隈で著名なPlamen Muskovさんが、X(旧Twitter)にてOllamaを使ってLLaVAを呼び出すAPEXアプリケーションを作ったと投稿していました。


Ollamaであれば簡単にCode Llamaを動かすことができそうなので、以前に作成した環境で試してみました。Ollamallama_cpp.serverと同様に、APIサーバーとして動作します。

llama_cpp.serverをAmpere A1のインスタンス上で動かしてみる
https://apexugj.blogspot.com/2023/07/llamacppserver-on-ap.html

以下はその作業の記録です。

OllamaのGitHubのページに、Ollamaのインストール手順が記載されています。

Always FreeのAmpere A1のコンピュート・インスタンスが作成済みとします。OSはUbuntuです。接続ユーザーはubuntu(Oracle Linuxのインスタンスでのopcに当たります)になります。

ユーザーubuntuで接続し、Linux & WSL2のインストール手順として記載されている以下のコマンドを実行します。

ubuntu@mywhisper2:~$ curl -fsSL https://ollama.com/install.sh | sh

>>> Downloading ollama...

######################################################################## 100.0%######################################################################### 100.0%

>>> Installing ollama to /usr/local/bin...

>>> Creating ollama user...

>>> Adding ollama user to render group...

>>> Adding current user to ollama group...

>>> Creating ollama systemd service...

>>> Enabling and starting ollama service...

Created symlink /etc/systemd/system/default.target.wants/ollama.service → /etc/systemd/system/ollama.service.

>>> The Ollama API is now available at 0.0.0.0:11434.

>>> Install complete. Run "ollama" from the command line.

WARNING: No NVIDIA GPU detected. Ollama will run in CPU-only mode.

ubuntu@mywhisper2:~$


インストールを実行しているユーザーがroot権限を持っているか、sudoでroot権限を取れることが前提のようです。スクリプトの実行が完了すると、OSのサービスとしてollamaがインストールされます。また/usr/local/bin以下にollamaが作成されます。

インストール直後にollamaがサービスとして起動します。サービスの起動と終了にはsystemctlコマンドを使用します。

モデルcodellamaを呼び出せるように、ダウンロードします。

ubuntu@mywhisper2:~$ ollama run codellama

pulling manifest 

pulling 3a43f93b78ec... 100% ▕████████████████▏ 3.8 GB                         

pulling 8c17c2ebb0ea... 100% ▕████████████████▏ 7.0 KB                         

pulling 590d74a5569b... 100% ▕████████████████▏ 4.8 KB                         

pulling 2e0493f67d0c... 100% ▕████████████████▏   59 B                         

pulling 7f6a57943a88... 100% ▕████████████████▏  120 B                         

pulling 316526ac7323... 100% ▕████████████████▏  529 B                         

verifying sha256 digest 

writing manifest 

removing any unused layers 

success 

>>> /?

Available Commands:

  /set            Set session variables

  /show           Show model information

  /load <model>   Load a session or model

  /save <model>   Save your current session

  /bye            Exit

  /?, /help       Help for a command

  /? shortcuts    Help for keyboard shortcuts


Use """ to begin a multi-line message.


>>> /bye

ubuntu@mywhisper2:~$


以上でCode Llamaを呼び出す準備ができました。

OllamaはデフォルトでTCPポート11434を開けて、REST APIの要求を待ち受けます。

firewalldに対して、TCPポート11434への接続を許可します。

ubuntu@mywhisper2:~$ sudo firewall-cmd --add-port=11434/tcp

You're performing an operation over default zone ('public'),

but your connections/interfaces are in zone 'docker' (see --get-active-zones)

You most likely need to use --zone=docker option.


success

ubuntu@mywhisper2:~$ 


変更を永続化します。

ubuntu@mywhisper2:~$ sudo firewall-cmd --runtime-to-permanent

success

ubuntu@mywhisper2:~$ 


リクエストをHTTPSで受け付けてHTTPでllama_cpp.serverを呼び出していたNginxの設定を変更します。/etc/nginx/conf.d以下のserver.confに含まれるproxy_passの行のポート番号を8000から11434に変更します。

proxy_pass http://localhost:11434/;

以上の変更を行なった後にNginxを再起動すると、Ollamaを呼び出せる状態になります。

OllamaとCode Llamaを呼び出すAPEXアプリケーションについて、簡単に紹介します。

OllamaはOpenAIのchat completions互換APIをサポートしているとのことですが、今回はCode Llamaを呼び出すので、単純にGenerate a completionを呼び出すことにしました。

ページ・アイテムP1_MODELモデル名を設定します。今回はcodellamaの指定だけを想定していますが、一応変更できます。P1_MESSAGEプロンプトとなる文章を入力します。Generation APIの呼び出しに与える値は以上です。

ページ・アイテムP1_RESPONSEにCode Llamaの出力を表示します。マークダウンが返されるため、アイテム・タイプMarkdownエディタを選択しています。ページ・アイテムP1_STATSにAPI呼び出しに関する統計情報を表示します。

Ollama/Code Llamaの呼び出しは、ボタンSEND_MESSAGEに実装した動的アクションにより実行します。TRUEアクションサーバー側のコードを実行PL/SQLコードとして以下を記述します。Ollamaのレスポンスはデフォルトでストリーミングで、ストリーミング出力にしないとNginxがタイムアウトするため、APEX側でCode Llamaのレスポンスをつなげ合わせています。

declare
l_request json_object_t;
l_request_clob clob;
l_response clob;
l_generated_message clob;
e_call_api_failed exception;
/* 改行ごとにJSONを読む際に使用する */
l_pos integer;
l_offset integer;
l_amount integer;
l_line varchar2(32767);
l_json json_object_t;
l_is_done boolean;
begin
/*
* OllamaのGenerate a completionを呼び出す。
* https://github.com/ollama/ollama/blob/main/docs/api.md
*/
l_request := json_object_t();
l_request.put('model', :P1_MODEL);
l_request.put('prompt', :P1_MESSAGE);
l_request_clob := l_request.to_clob();
/*
* REST APIの呼び出し。Always FreeのAmpere A1インスタンスは遅いので
* p_transfer_timeoutの設定は必須。
*/
apex_web_service.clear_request_headers();
apex_web_service.set_request_headers('Content-Type', 'application/json', p_reset => false);
l_response := apex_web_service.make_rest_request(
p_url => :G_ENDPOINT
,p_http_method => 'POST'
,p_body => l_request_clob
,p_transfer_timeout => :G_TRANSFER_TIMEOUT
);
if apex_web_service.g_status_code <> 200 then
raise e_call_api_failed;
end if;
/*
* 出力はデフォルトでストリーミングで、APEXの場合、Ollamaが分割で返しているレスポンスが
* ひとつのレスポンスとして返される。そのため、レスポンスを改行ごとに分割して、それぞれを
* JSONとして解釈する必要がある。
*/
l_generated_message := '';
l_offset := 1;
while true
loop
/*
* LFの位置を探す。
* 返される位置はoffsetからの相対位置ではなく、CLOBの中での絶対位置が返される。
*/
l_pos := dbms_lob.instr(
lob_loc => l_response
,pattern => CHR(10)
,offset => l_offset
);
/* LFがなければ終了 */
exit when ( l_pos = 0 );
l_amount := l_pos - l_offset;
/* 1行取り出す */
l_line := dbms_lob.substr(
lob_loc => l_response
,amount => l_amount
,offset => l_offset
);
l_json := json_object_t(l_line);
l_generated_message := l_generated_message || l_json.get_string('response');
l_is_done := l_json.get_boolean('done');
if l_is_done then
/* context以外をSTATSとして印刷 */
l_json.remove('context');
:P1_STATS := l_json.to_string();
end if;
/* 次の行を取り出す */
l_offset := l_pos + 1;
end loop;
:P1_RESPONSE := l_generated_message;
end;


アプリケーション定義置換に置換文字列G_ENDPOINTとして、OllamaのAPI呼び出しのエンドポイント(https://ホスト名/api/generate)、G_TRANSFER_TIMEOUTにAPEX側のAPEX_WEB_SERVICE.MAKE_REST_REQUESTのp_transfer_timeoutに与える秒数を指定します。

以下、プロンプトとして以下を与えた結果です。

「フィボナッチ数列の12番目の数を表示するJavaScriptのコードを教えてください。」

レスポンスが返されるまで2分はかかるので実用性はありませんが、Code Llamaが日本語を受け付けてレスポンスも日本語で返してきたのには、少し驚きました。


今回作成したAPEXアプリケーションのエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/ollama-codellama-app.zip

Oracle APEXのアプリケーション作成の参考になれば幸いです。