Named Functions

TurboWarp拡張機能 · 利用者ガイド

一度書けば、名前で呼べる。

Named Functionsは、スクリプトを「名前・説明・引数のJSON Schema」を持つ関数に変えます。どのスプライトからでも呼び出して値を受け取れます。結果を待つことも、複数の呼び出しを先に始めて後でまとめて受け取ることもできます。関数をツールとして公開すれば、AIのツール呼び出しなど他の拡張機能からも使えます。

何に役立つか

ブロック定義はスプライトの外に出られない

ブロック定義(カスタムブロック)は1つのスプライトに属し、値を返せません。メッセージはすべてのスプライトに届きますが、引数も結果も運べません。Named Functionsはその隙間を埋め、どのスクリプトからでも呼べて、入力が検査され、本当に値を返す処理の単位を提供します。

スプライト間でロジックを共有

得点計算、持ち物、会話の規則を1か所にまとめます。スクリプトを複製せず、すべてのスプライトとステージから同じ関数を呼び出します。

構造化された結果を受け取る

JSONを渡してJSONを受け取ります。数値・文字列・リスト・オブジェクトを返せ、引数はスクリプト開始前に検査されます。

AIにツールを提供

ツールとして公開した関数はOpenAIのfunction calling形式で一覧化され、AI拡張機能がプロジェクトにできることを知り、呼び出せます。

はじめに

4ステップで準備完了

関数はdefine functionハットを読み取って見つけるため、起動時の登録は不要です。ハットを置き、本体を書き、呼び出すだけです。

  1. 拡張機能を読み込む

    サンドボックスなしで実行を有効にして、カスタム拡張機能として読み込みます。関数スクリプトの開始にVMランタイムを使うためです。

  2. defineハットを置く

    任意のスプライトにdefine functionを置きます。名前・説明・args schemaはハットに直接入力します。これらはリテラル文字列である必要があります。

  3. 本体を書く

    function argumentで入力を読み、returnで終えます。returnせずに終わったスクリプトはnullを返します。

  4. どこからでも呼ぶ

    どのスプライトからでも、JSON引数を付けてcall functionを使います。ブロックは関数の終了を待ち、戻り値を返します。

1回の呼び出しで起きること

引数が不正ならスクリプトは開始されず、last function errorが一致しなかった項目を示します。

拡張機能のURL

バージョン固定のビルドを使ってください:@kubohiroya/turbowarp-named-functions@0.1.2。信頼できる配布元の拡張機能だけを読み込んでください。

使用例

関数を呼ぶ3つの方法

1つの結果を待つ、複数の呼び出しを始めてまとめて受け取る、どの関数を呼ぶかを他のプログラムに任せる——の3通りです。

↩ 呼んで待つ

ダメージ値を計算する

define function damage args schema {"power":number,"armor":number}
return (power) − (armor)
hpを 0 − (call function damage with {"power":12,"armor":4}) ずつ変える

関数は独立したスクリプトとして動くため、call functionは最低1フレームかかります。

⇉ 始めて、後で待つ

呼び出しを並べて実行する

p1を start function add with {"a":1,"b":2} にする
p2を start function add with {"a":3,"b":4} にする
await all [p1, p2] と言う

start functionは{"$promise":"p_1"}のようなプロミス参照を返します。await allは[3,7]を返し、失敗した呼び出しは{"$error": メッセージ}になります。

◇ ツールとして公開

AIに得点を読ませる

define function get_score description Reports the player’s score. export as tool
return (score)

ツールには説明が必要です。モデルが読むのはこの説明です。引数がスキーマに一致していても、ツールの引数は信頼できない入力として扱ってください。

スキーマ

入るものと出るものを宣言する

args schemaはJSON Schemaのオブジェクトで、関数の実行前に小さなインタープリターが検査します(コード生成はしません)。returns schemaを入力すると戻り値も検査し、空欄なら戻り値は検査しません。

args schemaの例

{
  "type": "object",
  "properties": {
    "name":  {"type": "string", "minLength": 1},
    "score": {"type": "number", "minimum": 0},
    "tags":  {"type": "array", "items": {"type": "string"}}
  },
  "required": ["name", "score"],
  "additionalProperties": false
}

対応キーワード

type、properties、required、additionalProperties(trueまたはfalse)、items、enum、const、minimum、maximum、minLength、maxLength、minItems、maxItems。

  • その他のキーワードは書けますが、検査は追加されません。
  • args schemaは"type": "object"である必要があります。
  • 不正なスキーマの関数は定義されず、function [NAME] defined?はfalseを返します。
  • 検査に失敗するとJSONパスが示されます(例:$.score must be >= 0)。

動作

タイミング・上限・エラー

関数はブロック定義ではなくスクリプトです。そのため共有でき、非同期に動きますが、使う前に知っておきたい規則もあります。

状況動作
呼び出しごと最低1フレームかかります。細かいループにはブロック定義を使ってください。
同じ関数への複数の呼び出し順番に1つずつ実行されます。異なる関数は並行して動きます。
関数が直接または間接に自分を呼ぶ永久に待つ代わりにReentrant call: a -> b -> aで失敗します。再帰にはブロック定義を使ってください。
関数が30秒を超えて動くタイムアウトで失敗します。
同時に開始した呼び出しが8件を超える以降のstart functionは空きが出るまで待ちます。
プロジェクトを停止実行中の呼び出しは失敗し、プロミス参照は消去されます。
同じ名前のハットが2つある、またはハットの入力がリテラルでない関数は定義されず、last function errorが理由を示します。

アプリケーション統合

自作の拡張機能に同じ関数機能を組み込む

Composition APIを使うと、単体版のブロックを登録せずに、同じレジストリとディスパッチャーを別の拡張機能で利用できます。利用側は自分のdefine functionハットのopcodeを持ち、3つのブロックからAPIへ処理を渡します。

作成と呼び出し

import {createNamedFunctions} from
  '@kubohiroya/turbowarp-named-functions/composition';

const functions = createNamedFunctions({
  runtime: Scratch.vm.runtime,
  functionHatOpcode: 'myextension_defineFunction'
});

const tools = functions.tools();
const score = await functions.call(
  'get_score', {}, {exportedOnly: true});

functions.release();

利用側が渡すもの

  • define functionハットからmatchHat(NAME, thread)。
  • 引数レポーターからargumentsFor(thread)。
  • returnブロックからreturnFrom(thread, value)。
  • tools()は公開された関数の一覧を返し、call(…, {exportedOnly: true})は公開されていない関数を拒否します。
  • cancelAll(reason)は待機中の呼び出しを失敗させ、release()はランタイムのリスナーも解除します。

ブロック一覧

3グループ・11ブロック

すべてのブロック・引数・既定値は、英語版の完全なブロックリファレンスに載っています。

定義

  • define function … 名前・説明・args schema・任意のreturns schema・公開方法を宣言します。
  • function argument [PATH] items.0.nameのように引数を1つ読みます。
  • function arguments JSON すべての引数をJSON文字列で返します。
  • return [VALUE] 値を返して関数を終えます。

呼び出し

  • call function [NAME] with [ARGS] 終了を待って結果を返します。
  • start function [NAME] with [ARGS] すぐにプロミス参照を返します。
  • await [PROMISE] 開始した呼び出しを1つ待ちます。
  • await all [PROMISES] プロミス参照のJSON配列をまとめて待ちます。

確認

  • [PROMISE] settled? 待たずに、開始した呼び出しが終わったかを確認します。
  • function [NAME] defined? 有効なハットがあるかを確認します。
  • last function error 直近の失敗の理由を返します。

トラブルシューティング

呼び出しが失敗するとき

まずlast function errorを確認してください。各項目を開くと、よくある原因と最短の対処が分かります。

function [NAME] defined? がfalseになる

NAME・DESCRIPTION・SCHEMA、および空でないRETURNSは、レポーターで組み立てず、ハットに直接入力してください。名前は1〜64文字の英数字・_・-、スキーマは"type": "object"を持つ正しいJSONである必要があり、同じ名前のハットが他にあってはいけません。

関数が始まる前に呼び出しが失敗する

ARGSはargs schemaに一致するJSON文字列である必要があります。{"a":"1"}は数値ではなく文字列を渡しています。JSONブロックや、正しいJSONになるjoinでARGSを組み立ててください。

自分を呼ぶ関数が「Reentrant call」になる

内側の呼び出しが外側の終了を永久に待つことになるため、名前付き関数を通した再帰は意図的に拒否されます。再帰する処理はブロック定義に書き、関数からそれを呼んでください。

結果が空になる

関数のスクリプトがreturnに到達せずに終わったため、結果はnullです。すべての分岐を確認してください。RETURNSを設定している場合、returnがないとnullとして検査され、失敗することがあります。

関数を呼ぶループが遅い

呼び出しごとに最低1フレームかかります。内側の処理をブロック定義に移すか、start functionで呼び出しを始めてawait allでまとめて受け取ってください。

AI拡張機能から関数が見えない

export asをtoolにし、DESCRIPTIONを入力してください。説明のないツールは定義されません。