2025/08/22

DXL Step-by-Step:#62)アクションボタン

前回はアクションバーについてまとめましたが、スタイルの部分だけとなっていました。今回は、アクションバーに配置されているアクションボタンについてまとめます。


アクションボタンと DXL

アクションボタンの DXL は actionbar ノードのスタイル表示の後ろに action ノードとしてまとまっています。action ノードは配置されているボタンの数だけ出力されます。

具体的に、アクションボタンを DXL に変換した例を示します。


アクションボタンのプロパティが action ノードの属性に設定されています。

ボタンクリック時に実行する式が code ノードに出力されています。また、ボタンのラベル式も code ノードとして出力されています。このように code ノードは状況に応じて複数することになります。あたりの仕組みは、ビューの選択式やイベントにプログラムを記述した場合と同様ですね。


シンプルアクション

アクションボタンには@式や LotusScript  以外にシンプルアクションを設定できます。シンプルアクションを設定したボタンの DXL は少し構造が変わるので注意が必要です。

code ノードの属性である event が 'action' となり、simpleaction というサブノードが配置されます。また、この DXL では simpleaction が 2 つ出力されています。シンプルアクションは一つのボタンに複数のアクションを設定できます。これに対応するため、配列のような構造になっているということですね。

simpleaction ノードの属性にシンプルアクションで実行する処理が記述されます(上図はエージェントの実行の場合)。シンプルアクションには、さまざまな種類があり、その種類ごとに設定できる項目が変化します。そして、DXL もその種類ごとに変化します(詳細は別の機会にまとめます)。


前回 DXL Step-by-Step 次回


2025/08/21

DXL Step-by-Step:#61)アクションバー

前回はビューの code ノードを確認しました。今回は actionbar ノードについてまとめます。


actionbar ノードの構成

まずは、Domino Designer を使って DXL の構造を確認します。actionbar ノードを開くと、色やスタイル、フォントなどのアクションバーの設定があり、そのあとにアクションボタンである action ノードが配置されています。


アクションバーのスタイル

前述の通り、アクションバーのスタイルは、actionbar の属性といくつかのサブノードから構成されています。ざっくりいうと、プロパティのタブとノードは対になっています。いかに記載がないタブと一部に例外が actionbar の属性に配置されています。


◇ フォント

アクションバーのプロパティの[ボタンのフォント]タブの設定は、font ノードに集約されます。


フォント名(name)やサイズ(size)などが含まれます。この構造はリッチテキストの文字の装飾と同じですね(DXL Step-by-Step:#22)文字の装飾)。


◇ ボタンのプロパティ

アクションボタンのサイズや色などを設定する[ボタンのプロパティ]タブは、actionbuttonstyle ノードに出力されます。



◇ アクションバー境界

アクションバーに影をつけたりできる境界の設定は border ノードに出力されます。


境界線の幅は width 属性で設定でき、上/右/下/左の順でピクセル指定します。この指定方法は表のセルの境界線 borderwidth と同じですね(DXL Step-by-Step:#38)セルの設定 - 表の罫線)。

また、境界を表す border ノードは、表やインラインイメージの境界としても利用されています。


◇ アクションバー背景

プロパティの[アクションバー背景]タブの設定は、actionbarstyle ノードに出力されます。


イメージリソースを利用すると imageref ノードが登場します。これは、リッチテキストにインラインイメージを表示する場合と同じです(DXL Step-by-Step:#26)イメージリソースの DXL)。


まとめ

今回はアクションバーに関してまとめました。

font や border、imageref のようにリッチテキスト内で利用したノードが出てきました。また、今回紹介した actionbar ノードはフォームやサブフォームにも登場します。このように DXL は部品として再利用しやすいよう構造化されています。この点を理解しておくと DXL の理解が早くなります。

積み木やレゴのように組み合わせてモノを作る感じが面白いですね。


前回 DXL Step-by-Step 次回


2025/08/20

DXL Step-by-Step:#60)ビューの選択式と Code

前回はビューの DXL の全体構成を確認しました。今回からサブノードの構造を確認します。最初は、code ノードです。


選択式

code ノードを開いて詳細を確認します。

同じ部分を VS Code で整形して表示すると次の通りです。

見ただけでわかりますね。この部分はビューの選択式を表しています。formula サブノードには選択式が記述されていることが確認できます。


event 属性

code ノードの event 属性には ’selection’ と設定されています。これが配下のコードは選択式であることを示しています。たとえば、このビューにフォームの式と文書の貼り付け禁止を設定します。

このビューを DXL で確認すると次のように code ノードが3つ出力されています。

フォームの式と貼り付け禁止については event 属性が form、querypaste となっています。このように code ノードはデザイナーのオブジェクトごとに作成され、event 属性でどのオブジェクトかを表しています。


コードと DXL

ビューの設計内に記述したプログラムも DXL 内に出力されます。式言語の場合は formula ノード、LotusScript では lotusscript ノードが作成され、その配下のテキストノードにプログラムが出力されます。

このように記述した言語に応じたノード名で出力される仕様となっています。


前回 DXL Step-by-Step 次回


2025/08/19

DXL Step-by-Step:#59)ビューの DXL

最近、設計要素の DXL を調査する機会があったので、その結果をまとめます。今回は、ビューに関してです。


全体の構成

Domino Designer でビューの右クリックメニューより[DXL で編集]を選択し、DXL を表示します。画面下部の[設計]タブを開くと、DXL の構造が階層化されて表示されます。全体の構成を確認するにはこの画面が便利ですね。

まず、ビュー全体を表すノードは view となっています。これまで操作してきた文書では documentでした。この連載当初に紹介したエージェントでは agent でした。単純でわかりやすいですね。

view ノードの配下には、緑のアイコンで属性が表示されます。name にビューの名称、showinmenu には[表示]メニューに表示するの設定があります。このようにビューのプロパティで設定できる項目が列挙されています。

属性の下には、アクションボタンや列などを表すサブノードが列挙されています。属性とこれらをまとめて、ビュー全体を表すということですね。

各サブノードの詳細については、次回以降順にまとめます。


主な属性

ここでは、view ノードの属性についてまとめます。抜粋すると次の通りです。

属性 設定値 補足
name '下書き' など ビューの名称
alias 'vDraft' など ビューの別名
showinmenu 'true' or 'false' [表示]メニューに表示する
default 'true'
デフォルトビューでない場合、属性自体がない
bgcolor 'white' など
'#ffffea' など
背景色
altrowcolor 交互の色
rowlinecount '5' など 行の高さ(行数)
headerlinecount '1' など ヘッダーの高さ(行数)
rowspacing '1’, '1.25' など 行間

このようにビューのプロパティにある項目が属性として出力されます。機能やデザインなど多種多様な項目があるので、view ノードには多数の属性が出力されます。


前回 DXL Step-by-Step 次回


2025/08/01

ID Vault の有効期限切れにご注意 ! 最初の被害者は DJX かも !?

先日来、DJX に関する投稿が続いていますが、今回も DJX に関連したした投稿です。先日、ユーザ登録において突然『未登録のエラーです [ 4005]Notes エラー: Notes ID ボールトへの ID のアップロードが失敗しました。』とエラーが発生するようになりました。

このエラー過去に経験がありました。DJX 経由でユーザを削除した場合、ID Vault 内のエントリが削除されない問題(仕様?)があり、ユーザを再登録するとこのエラーが発生します。ところが今回の事例では、完全に新規登録のユーザで、ID Vault 内にそのユーザの ID は存在していませんでした。


原因は DJX ではない

何度登録しても同じ結果がでます。ID Vault の DB に対して修復コマンドを実行しても効果なしでした。行き詰ったのでテクニカルサポートに問い合わせたところ、ID Vault の認証期限を確認するよう、即答がありました。

ID ボールトの認証情報の更新について

この手順に従い確認したところ、見事に期限が切れていました。そして、手順に従い、認証期限を更新したところ、DJX のユーザ登録でエラーはなくなり、ID Vault へのアップロードも正常に行われました!

今回のトラブルもサポートの迅速な対応のおかげで、瞬殺できました。いつも、ありがとうございます!


ID Vault の有効期限に注意

ID Vault のセットアップは外部の業者がセットアップしたこともあり、設定は詳しくありません。ドミノディレクトリの[設定]-[セキュリティ]-[認証]に相互認証などと同列に ID Vault の設定があることは知っていたので、今思えば有効期限があることを意識しておくべきでした...。

ちなみに有効期限は、ボールトの信頼認証文書を開いて[Notes 認証の調査]ボタンをクリックすると簡単に確認できます。


導入時期が早い環境は要注意

上記記事によると、この有効期限なのですが導入した Domino(Administrator クライアント)のバージョンにより期間が変わるようです。10.0.1 FP2 / 11.0 以降の場合は 25 年、それ以前は 10 年となっています。

導入時期が早い環境では、要注意ですので、期限を確認して延長をしておきましょう。


パスワードリセット認証も注意

ID Vault にはパスワードリセット機能があります。この機能を利用するためにも認証があり、ID Vault の有効期限とは別になっています。パスワードリセットの導入時期によっては、ずれている可能性があります。この機会に確認して揃えておきましょう。

X デーは一つでも少ないほうがシステム管理が楽ですから...


2025/07/31

標準アプリは Notes/Domino ユーザのお手本であれ!

昨日投稿した『有効期限 68 年の DJX ユーザ登録には要注意 !?』に関連して、気になることがあったので追記します。

TimeDifference の戻り値に関する問題により、下記の If 文が正常に判定できず、範囲内の 68 年を指定しても、『認証の有効期限が、設定の範囲(現在時刻~68年後)を超えています』とエラーとなる症状でした。


判定の仕様

この If 文の条件は、現在時刻 ~ 68年後の範囲に含まれないことを判定をしているのですが、私が気になったのは、68 年後を表す MAXADJUSTSEC 定数です。

   If diffs& < 0 Or diffs& > MAXADJUSTSEC Then

この定数の定義は、GeneralConstant ライブラリの Declarations に定義されていました。

   Const MAXADJUSTSEC = 2145916800 ' 68 years in seconds

「なるほど、68 年を秒に変換した値が入っているのか」っと思いつつこの値を検算してみました。その結果、1 年の日数を 365.25 日として計算していることがわかりました。

   68 年 * 365.25 日 * 24 時間 * 60 分 * 60 秒 = 2145916800 秒

この 0.25 は 4 年に一度のうるう年に配慮しているようですね。ただ、うるう年は 100 で割り切れる年は平年、400で割り切れる年は再びうるう年になり、そんなに単純ではありません。正確には、約365.2422日だそうで、それでも ”約” がついています。

よって、この If 文の判定はあまり正確ではないと言えますね。


判定仕様は揃えよう

DJX 管理ツールの有効期間の設定は、年月日のそれぞれを数値で入力する仕様です。 

そして、内部的な日付計算は NotesDatatime クラスの機能を活用して作成されています。ここでも、年月日それぞれの数値を使って算出しています。

このように、入力や演算は日付を基準とした処理で行われていることがわかります。にもかかわらず、上記の If 文では秒数を使って判定が行われていました。つまり、DJX 管理ツールには、こよみ上の「日付」と、時間間隔を表す「秒」という、異なる2種類の基準が混在していることになります。

しかし、「日数」と「その間の秒数」は常に一意に対応するわけではありません。このような設計は、想定外の動作やバグを引き起こす原因になると感じました。


標準アプリは、お手本であれ!

Notes/Domino は、標準テンプレートとして多くのサンプルアプリも用意されています。また、管理用ツールがアプリとして提供されていることが多いです。ユーザはこれらを参考にすることで、アプリ開発の手法を学んだり、自社のアプリ設計のヒントを得たりすることができます。こうした点は、Notes/Domino の面白さであり、大きな魅力のひとつだと感じています。

しかし今回紹介したように、必ずしも参考にすべきでない点も存在します。また、最近の標準テンプレートは機能が過剰で複雑になりすぎており、内容を理解するのが非常に困難になってきています。

標準アプリはすべての Notes/Domino ユーザにとっての「良き手本」であるべきです。誰が見てもエレガントで「イケてる」と感じられる、シンプルかつ洗練された仕様を目指してほしいものです。


2025/07/30

有効期限 68 年の DJX ユーザ登録には要注意 !?

本日は、ノーツコンソーシアム の Domino Lounge(旧 研究会)でネタにしたお話です。今年 5 月に、Domino 11 から 14 にアップデートした時に発生した現象についてご紹介します。


発生した現象

DJX の管理ツールからのユーザ登録は、有効期限を設定できる最大値の 68 年で運用していました。

14.0 にアップデート後、ユーザを登録したところ、『認証の有効期限が、設定の範囲(現在時刻~68年後)を超えています』とエラーが発生しました。

アップデート前は正常に登録できていましたし、設定の変更は行っていません。そもそもエラーメッセージに表示されている日付は、ちょうど 68 年後なので、何がエラーなのかさっぱりわかりません。


エラーの発生個所

原因調査のため、DJX 管理ツールの設計をのぞいてみたところ、ExtReadLib スクリプトライブラリの CreateReqUserCSV 関数の中でエラーを出力していることがわかりました。

このエラーを出力するためには、その直前の If 文でエラーだと判定される必要があります。ということはその値を演算している TimeDifference メソッドに起因する現象ということがわかります。


エラーの原因

このままでは検証しづらいので、この部分だけを抜き出して確認します。変数 dt には 68 年後の日付、dtnow には SetNow メソッドで現在の時刻がセットされています。この部分だけのサンプルプログラムを作成します。

Sub Initialize
   Dim ns As New NotesSession
   Dim dt As New NotesDateTime("2093/05/26 12:57:54")
   Dim dtnow As New NotesDateTime("2025/05/26 12:57:54")

   diffs& = dt.TimeDifference(dtnow)

   MsgBox CStr(diffs&), 0, ns.Notesversion
End Sub

このプログラムを移行前の環境である Notes 11 と 移行後の環境である Notes 14 で実行します。すると、結果が違ってました!

誤差(?)は 1,566,847 秒でした(換算すると18 日 と 3 時間 14 分 7 秒)。68 年間にあるうるう年の回数に近いです(68 / 4 = 17)。うるう年計算を忘れているなんてバカなことはないと思いながら、おそるおそるテクニカルサポートに問い合わせてみました。


誤差の原因はバグ

返答は次の通りでした。

サンプルコードを実行すると、Notes 14 では Long 型の最大値 2,147,483,647 が戻り値となっており、オーバーフローしている。
本来、TimeDifference の値は戻り値が 68 年と 20 日程度を超えると Long 値の最大となるが、Notes 14 ではそれよりも短い期間でオーバーフローしてしまっている。
この問題は、問題報告番号 JPKRDEVSGW として報告され、Notes 14.5、14.0FP5、12.0.2FP7 にて修正が予定されている。

オーバーフローって桁あふれが原因の現象と理解しているのですが、今回の現象は、Notes 14 の方が大きな値が出力されています。どんな演算を行うと ”早く桁があふれる” のか気になりながらも、バグなら仕方がないと飲み込みました。

ちなみにこの問題、記載通り Notes 14.5 では修正されていることを確認しました。

また、サポートの方からは TimeDifferenceDouble というメソッドを使用すると改善するとの提案もいただきました。NotesDatetime クラスは使用したことがないのですが、さまざまな機能がありそうですねぇ...


DJX の対応

ユーザ登録時の意味不明のエラー原因は明確になりました。また、14.5 でも治っていることは確認したのですが、これだけを理由にバージョンアップをするわけにはいかないですね。

仕方がないので、有効期限の設定を 67 年と 11 ヶ月に変更して運用を継続しています。

Notes/Domino 11 のサポート終了に伴い、バージョンアップを実施される環境は多いかと思います。14 を選択される場合、DJX + 68 年のユーザ登録にはご注意ください。レアケースかもしれませんが...


2025/07/20

つないでみよう:#28)画像生成 AI DALL-E3 - Base64 文字列を画像ファイルに変換

OpenAI 社の DALL-E3 を API で利用して画像を生成するシリーズの最終回です。今回はレスポンスの JSON に含まれる画像をファイルとして抽出する部分を作成します。


メインルーチンの修正

画像ファイルを取得するには大きく分けて2つの処理が必要です。

まず、前回紹介したレスポンスの中から画像データの文字列だけを取得します。2つ目の処理では。画像データを画像ファイルに変換します。

この流れに沿ってメインルーチンを構築します。

Sub Initialize
         ・・・(省略)・・・
   'API をコール
   Set jnavResponce = xAskGPT(sJSON_Post)
   Call xSetRT(nd, "JSON_Responce", jnavResponce.Stringify())


   '生成画像を Base64 文字列で取得
   sBase64 = xGetImage_Base64(jnavResponce)

   'Base64 画像をファイルとして保存
   Call xDownloadImage(sBase64, "c:\Temp\DALL-E3#28.jpg")

   Call nd.save(True, False)
End Sub


画像データの取得

レスポンスの JSON はこのような構造でした。

ここから base64_json の値を取得する関数 xGetImage_Base64 を作成します。引数はレスポンスの JSON 全体で、戻り値が画像データです。

関数内の処理では、data ノードが配列になっている点に注意が必要です。

Function xGetImage_Base64(vjnavResponce As NotesJSONNavigator) As String
   Dim je As NotesJSONElement
   Dim ja As NotesJSONArray
   Dim jeData As NotesJSONElement
   Dim jobj As NotesJSONObject
   Dim jeBase64 As NotesJSONElement
   Dim sResponce As String

   ' data を検索
   Set je = vjnavResponce.GetElementByName("data")

   ' data は配列なので NotesJSONArray で受ける
   Set ja = je.Value

   If ja.Size > 0 Then
      '配列の 1 件目を取得
      Set jeData = ja.GetFirstElement()
      Set jobj = jeData.Value

      ' b64_json を検索し値( = 画像)を取得
      Set jeBase64 = jobj.GetElementByName("b64_json")
      xGetImage_Base64 = jeBase64.Value
   End If
End Function


画像データの変換と保存

メインルーチンがコールする2つ目の関数です。Base64 画像をデコードする部分と画像ファイルとして保存する処理に分かれます。それぞれの処理はサブ関数化されています。

Function xDownloadImage(ByVal vsBase64 As String, ByVal vsFP As String) As Boolean
   Dim nst As NotesStream

   'Base64 画像をファイルとして保存
   Set nst = xns.CreateStream()
   Call Base64ToBinary(vsBase64, nst)
   Call StreamToImageFile(nst, vsFP)
End Function


サブ関数は別の連載記事『DXL Step-by-Step:#9)DXL 内の画像のダウンロード』で紹介したもので、そのまま再利用します。今回は関数のみ転載しますので、詳細を知りたい方は、上記リンクをご確認ください。


◇ Base64 のデコード

%REM
   Sub Base64ToBinary
   Description: Given a string of base64-encoded data, write into a binary stream we are passed.
      This is done rather than creating the stream here and returning it, so that you can
   stream directly into a file if you choose.
%END REM

Sub Base64ToBinary(strBase64$, streamOut As NotesStream)
   ' Given a string of base64 encoded data, this routine decodes and writes the original binary data into a NotesStream
   Dim doc As NotesDocument
   Dim mime As NotesMIMEEntity
   Dim streamIn As NotesStream
   Dim db As NotesDatabase
   Dim session As New NotesSession

   Set db = session.CurrentDatabase
   Set doc = db.CreateDocument
   Set mime = doc.CreateMIMEEntity("Body") ' the mime classes already know how to do this conversion,
   Set streamIn = session.CreateStream
   Call streamIn.WriteText(strBase64)
   streamIn.Position = 0
   Call mime.SetContentFromText(streamIn, "binary", ENC_BASE64)
   Call mime.GetContentAsBytes(streamOut, True) ' decode as you stream out the data.
End Sub

※ OpenNTF の LotusScript Gold Collection プロジェクトから拝借


◇ 画像をファイルとして保存

Sub StreamToImageFile(vnstImage As NotesStream, ByVal vsFP As String)
   Dim nstOut As NotesStream

   On Error Resume Next
   Kill vsFP '存在してたら削除

   Set nstOut = xns.CreateStream()
   Call nstOut.Open(vsFP)

   vnstImage.Position = 0
   Do Until vnstImage.Position >= vnstImage.Bytes
      Call nstOut.Write(vnstImage.Read(16000))
   Loop
End Sub


テスト実行

エージェントが完成したらテスト実行します。前回のテストと同様に『上司と部下が会議室で本気で議論しているシーンを少年漫画のバトルシーンのようなタッチで描いてください。』と指示を出すと以下のような画像が作成されました。


まとめ

今回は画像生成 AI の OpenAI 社の DALL-E3 を API で利用する方法を紹介しました。画像1枚あたりの生成コストは $0.04(約 6 円、今回の設定の場合)と比較的安価です。この金額で、上記のようなクオリティの画像が生成できるのですから、十分に利用価値があるといえるでしょう。

今後はプロンプトエンジニアリングの技術を磨き、より理想に近い画像を生成できるようにしていきたいですね。


前回 連載:つないでみよう


2025/07/19

つないでみよう:#27)画像生成 AI DALL-E3 - テスト実行とレスポンスの確認

前回は API をコールしてレスポンスを取得するまでのエージェントを作成しました。実際に実行し、結果を確認しましょう。


テスト実行 

フォームを開き、生成する画像の説明を入力し、文書を保存します。今回は『上司と部下が会議室で本気で議論しているシーンを少年漫画のバトルシーンのようなタッチで描いてください。』と入力してみました。

作成した文書を選択し、エージェントを実行します。実行後の文書を開くと、送受信した JSON が実行結果に表示されます(数分はかかるのでご注意ください)。


受信した JSON

今回のリクエストでは、responce_format に "b64_json" を指定しているので、受信した JSON 内に画像データが含まれます。大量の文字のほとんどは画像データとなり、見通しが悪く、わかりずらいです。VSCode で整形して、構造を確認します。

整形すると一目瞭然ですね。data ノードの下に画像データである b64_json ノードが存在します。Create Image API では1回のリクエストで複数枚の画像を作成する機能があります。これに対応するため data ノードは配列となっている点がポイントとなります。


リクエストは英語の方がいい?

先ほど紹介した受信した JSON を見て気になった点があります。画像データの次の項目に revised_prompt というノードがあります。ノード名から生成画像の指示だとわかりますが、値は次の通りとなっていました。

Imagine a scene where a male boss of Middle-Eastern descent and a female employee of Black descent are passionately debating in a conference room. The artistic style should evoke the feeling of a battle scene taken from a classic boy's comic, emphasizing dramatic angles and exaggerated expressions, featuring in bold, ink-heavy shading resembling manga styles from the 19th century. Elements such as onomatopoeic words or speed lines may also be included to intensify the scene.

ChatGPT で日本語に翻訳するといろいろと尾ひれがついていることがわかります。

中東系の男性上司と、黒人系の女性社員が会議室で激しく議論している場面を想像してください。 芸術的なスタイルは、まるで少年漫画のバトルシーンのような雰囲気を醸し出し、ドラマチックなアングルや誇張された表情を強調します。 表現には、19世紀の漫画スタイルを思わせる、太くインクの乗った重厚な陰影が使われます。 さらに、擬音語やスピード線といった要素が加わり、シーンの迫力を一層引き立てます。

また、同じ文章でもう一度リクエストしてみたところ、revised_prompt は違うものになっていました。

Create an image of an intense discussion between a supervisor and an employee in a conference room. Depict the scene with the energetic and intense characteristics commonly found in pre-1912 battle scenes from boys' comics. The supervisor is a Middle-Eastern man and the employee is a South Asian woman. She is standing up confidently asserting her points, while the supervisor listens with full attention suggesting his curiosity and openness. The conference room is modern with a large oval table, ergonomic chairs and a wall filled with whiteboards and charts.

会議室で上司と部下が激しい議論を交わしている場面の画像を作成してください。 このシーンは、1912年以前の少年漫画に見られる戦闘シーンのような、エネルギッシュで緊張感のある表現で描写してください。 上司は中東系の男性、部下は南アジア系の女性です。彼女は自信を持って立ち上がり、自分の主張を力強く述べています。一方、上司は興味と受容の姿勢を示しながら、真剣に彼女の話に耳を傾けています。 会議室はモダンなデザインで、大きな楕円形のテーブル、エルゴノミクスチェア、そして壁一面にはホワイトボードとグラフが設置されています。

初回はスピード線など作画に偏った指示でしたが、2回目は会議室の什器について詳細に記述されています。実行回によってこだわるポイントが違うようです。

この結果より、Create Image API では、リクエストを英語で送った方がよりダイレクトに指示が出せそうです。ChatGPT などを使用して、自身の希望をできる限り反映した英文を作成してから送信する方がよさそうですね。


まとめ

今回は、作成したエージェントのテストと Create Image API のレスポンスの JSON を確認しました。次回は、JSON 内の Base64 画像データから画像ファイルを抽出する部分を作成します。


前回 連載:つないでみよう 次回


2025/07/18

つないでみよう:#26)画像生成 AI DALL-E3 - API のコール

OpenAI 社の DALL-E3 を API で利用して画像を生成するシリーズの2回目です。前回は API の仕様を紹介しましたので、今回は具体的なコーディングを行います。

基本的な流れは、これまでこの連載で紹介してきた OpenAI 社の API 利用と同じです。ただ、しばらく期間があいているので、LotusScript のコードはすべてを掲載いたします。


フォームの作成

まずは、どのような画像を作成するのか指示を入力するためのフォームを作成します。

API コールで使用するのは「リクエスト内容」の部分だけです。実行結果は、送受信される JSON を記録する確認用のリッチテキストフィールドとなります。


エージェントの作成

新規で LotusScript のエージェントを作成ます。ビューで選択した文書に対して実行するので、対象に[すべての選択文書]を設定します。

まず、エージェントのメインルーチンは次の通りです。

Option Declare

Private xns As NotesSession
Private Const xcsBearer = "xxxxx" 'APIキーをここにセット

Sub Initialize
   Dim ndb As NotesDatabase
   Dim ndc As NotesDocumentCollection
   Dim nd As NotesDocument

   Dim sBase64 As String

   Dim sJSON_Post As String
   Dim sJSON_Responce As String
   Dim jnavResponce As NotesJSONNavigator

   Set xns = New NotesSession

   'ビューの選択文書の1つ目を取得
   Set ndb = xns.CurrentDatabase
   Set ndc = ndb.UnprocessedDocuments
   If ndc.Count = 0 Then
      MsgBox "文書を選択してください。", 16, ndb.Title
      Exit Sub
   End If
   Set nd = ndc.GetFirstDocument()

   '送信(POST)する JSON を作成
   sJSON_Post = xGetJSON(nd)
   Call xSetRT(nd, "JSON_Send", sJSON_Post)

   'API をコール
   Set jnavResponce = xAskGPT(sJSON_Post)
   Call xSetRT(nd, "JSON_Responce", jnavResponce.Stringify())   '必要なときのみ有効化

   Call nd.save(True, False)
End Sub

まず、このエージェントでは、ビューの選択文書の1つ目を取得して処理するようになっています。送信する JSON の作成と API をコールする2段階に分かれていて、それぞれ関数化しています。


送信する JSON の作成

送信する JSON は次のような構成でした。今回は単純化するために、prompt に文書に入力した「生成する画像の説明」をセットする以外は、固定の値とします。

{
   "model": "dall-e-3",
   "size": "1024x1024",
   "quality": "standard",
   "response_format": "b64_json",
   "prompt": "(ここに生成する画像の説明をセット)"
}

この JSON を作成する関数は次の通りです。引数 vnd が処理する文書で、戻り値は作成した JSON です。

Function xGetJSON(vnd As NotesDocument) As String
   Dim jnav As NotesJSONNavigator
   Dim s As String

   'RequestBody(JSON) の準備
   Set jnav = xns.CreateJSONNavigator("")

   '1.model
   Call jnav.AppendElement("dall-e-3","model")
   '1.size
   Call jnav.AppendElement("1024x1024","size")
   '1.quality
   Call jnav.AppendElement("standard","quality")
   '1.response_format
   Call jnav.AppendElement("b64_json","response_format")
   '1.prompt
   Call jnav.AppendElement(vnd.Prompt(0),"prompt")

   xGetJSON = jnav.Stringify
End Function


API をコール

続いて、API をコールする関数です。コメントに記載している通り、HTTP リクエストの準備して、それにヘッダの設定を行い、エンドポイントに対して POST しています。

ヘッダの Authorization には API キーを 設定しています(キーの取得については #1 を参照)。また、Post メソッドの2つ目の引数に先ほどの xGetJSON 関数で作成した JSON をセットしています。

Function xAskGPT(ByVal vsJSON_Post As String) As NotesJSONNavigator
   Dim sURL As String
   Dim http As NotesHTTPRequest
   Dim jnav As NotesJSONNavigator
   Dim ja As NotesJSONArray
   Dim jobj As NotesJSONObject

   'HTTP リクエストの準備
   Set http = xns.CreateHTTPRequest()

   'HTTP ヘッダーの設定
   Call http.SetHeaderField("Content-Type", "application/json")
   Call http.SetHeaderField("Authorization", "Bearer " & xcsBearer)

   'API 実行
   http.PreferJSONNavigator = True
   sURL = "https://api.openai.com/v1/images/generations"     'エンドポイント
   Set jnav = http.Post(sURL, vsJSON_Post)

   'Responceをセット
   Set xAskGPT = jnav
End Function

関数の戻り値は、API の返答の JSON で、NotesJSONNavigator のオブジェクトとして返します。


JSON の記録

メインルーチンで作成したJSON や API のレスポンスの JSON をリッチテキストフィールドに保存するのが xSetRT 関数です。

Function xSetRT(vnd As NotesDocument, ByVal vsFld As String, ByVal vsVal As String)
   Dim nrti As NotesRichTextItem

   Call vnd.RemoveItem(vsFld)
   Set nrti = vnd.CreateRichTextItem(vsFld)
   Call nrti.AppendText(vsVal)
End Function

上記メインルーチンでは有効化していますが、JSON を確認したい場合に実行し、それ以外はコメントアウトしてください。

今回は、画像生成ですので、レスポンスの JSON が大きくなります。文書内に JSON を記録すると文書を開くのが非常に遅くなります(私の環境では 5 分程度)。ご注意ください。


テスト実行は次回

ここまでで API をコールして、結果を JSONで取得するとことまでができました。次回はこれを実行して結果を確認します。


前回 連載:つないでみよう 次回