From 1520852238ebd70a0658248db6497619afcbae74 Mon Sep 17 00:00:00 2001 From: Hare Date: Tue, 8 Sep 2026 12:11:52 +0900 Subject: [PATCH] docs: define operation IDL and entry discovery --- 1-concept.md | 6 +- 2.1-object-tree-schema.md | 16 +-- 2.2-operation.md | 22 ++- 3-core-protocol.md | 10 +- 4-client.md | 29 ++-- 4.1-ai-tools-example.md | 16 ++- draft/unresolved-issues.md | 27 ++-- draft/wip-idl.md | 281 +++++++++++++++++++++++++++++++++++++ draft/wip-over-https.md | 73 ++++++++++ 9 files changed, 435 insertions(+), 45 deletions(-) create mode 100644 draft/wip-idl.md diff --git a/1-concept.md b/1-concept.md index 2ea010b..72e64ba 100644 --- a/1-concept.md +++ b/1-concept.md @@ -18,7 +18,7 @@ Objectは、短いdescription、利用可能なOperation、子Object、必要に 一般的なTool Callingでは、モデルは提示されたTool一覧から操作を選ぶ。 -WIPでは、モデルは既知のWorldspaceを辿り、目的に関連する対象を見つけ、公開されたOperationを確認して実行する。Operationの結果から未知のエントリやLinkを発見し、既知の世界へ追加できる。 +WIPでは、モデルは既知のWorldspaceを辿り、目的に関連する対象を見つけ、公開されたOperationを確認して実行する。Operationの結果から未知のエントリを発見し、既知の世界へ追加できる。 中心となる問題を**Tool SelectionからWorld Explorationへ移す**ことがWIPの狙いである。 @@ -26,7 +26,7 @@ WIPでは、モデルは既知のWorldspaceを辿り、目的に関連する対 Pathはidentityではなく、現在のWorldspace上の配置位置である。同一Objectが複数のエントリとして現れてもよい。 -親子関係は所有・内包を表す。所有関係にない意味的な関連はLinkとして扱い、Treeへ無理に押し込まない。 +親子関係は所有・内包を表す。所有関係にない意味的な関連は、それを表す名前付きOperationから関連エントリのpathを返し、Treeへ無理に押し込まない。 Worldspace全体を事前にmaterializeする必要はない。Hostはlazy / virtualなTreeとして、必要な範囲だけを動的に公開できる。 @@ -40,7 +40,7 @@ Operationには、名前や型に加えて、用途を判断するための短 ## 発見と既知世界 -Clientは、これまでに発見したエントリを既知のWorldspaceとして保持できる。既知のエントリはClient側で自由に辿り、Operation結果やLinkから得たエントリを追加して、さらに探索できる。 +Clientは、これまでに発見したエントリを既知のWorldspaceとして保持できる。既知のエントリはClient側で自由に辿り、Operation結果から得たエントリを追加して、さらに探索できる。 機械的な広域探索の対象は、Hostがindexableとして公開した範囲に限定できる。これにより、大量の検索結果や動的Objectが自動的に探索indexへ流入することを防ぐ。 diff --git a/2.1-object-tree-schema.md b/2.1-object-tree-schema.md index 0652907..c5dcaf1 100644 --- a/2.1-object-tree-schema.md +++ b/2.1-object-tree-schema.md @@ -38,9 +38,9 @@ Worldspaceは、Objectが親子関係によって配置されたTreeとして観 Tree上のObjectをエントリとして呼び、その位置をpathで表す。 -子エントリは所有・内包関係を表す。非内包の意味的関係はLinkとして扱う。 +子エントリは所有・内包関係を表す。非内包の意味的関係は、それを表すOperationから関連エントリのpathを返す。 -例えばRepository配下の`issues`は子エントリとして自然だが、Issueから関連Pull Requestへの参照はLinkとして表現する。 +例えばRepository配下の`issues`は子エントリとして自然だが、Issueから関連Pull Requestを得る場合は`get_related_pull_requests`のようなOperationで表現する。 Treeはmaterializedな全世界ではなく、HostがそのClientに対して公開する**lazy / virtual tree**である。Hostは全Objectを事前保持する必要はなく、`fetch`や`fetch_tree`に応じて動的に解決してよい。 @@ -54,15 +54,9 @@ indexableな子エントリは、Worldspaceの構造理解に必要で、通常 一方、大量のIssue、検索結果、一時的なObject群などはindexable childとして露出しない。必要なエントリはCollection Objectの`query` / `list`等のOperation結果として返す。 -## 2.1.6 Link +Operation結果として返されることは、実行対象との親子関係を意味しない。Collectionの要素を返す場合だけでなく、親、兄弟、関連Objectなど、Worldspace内の別の位置にあるエントリを発見するためにもOperationを利用できる。 -LinkはTree上の所有・内包ではない意味的関係を表す。 - -Linkによって未知のエントリが発見された場合、ClientはそのエントリをKnown Spaceへ追加できる。ただし、それだけではDiscoverable Spaceには追加しない。 - -そのエントリを起点に`fetch_tree`を行い、indexableなTreeを取得した場合にのみ、その範囲をdiscover対象として扱える。 - -## 2.1.7 Core Protocolとの対応 +## 2.1.6 Core Protocolとの対応 `fetch(entry)`は、指定されたTree上の位置にあるObjectの公開情報を取得する。 @@ -70,4 +64,4 @@ Linkによって未知のエントリが発見された場合、Clientはその 探索が`depth`または`limit`により打ち切られた場合は、レスポンスで`truncated`を必ず明示する。 -Operationを実行した結果として、新しいエントリ、エントリツリー、Linkが返る場合がある。Clientはそれらを構造化された発見結果としてKnown Spaceへ統合する。 \ No newline at end of file +Operationを実行した結果には、WIP IDLの`entry`型として新しいエントリのpathが含まれる場合がある。Clientはそれらを一時的な発見結果として扱い、後続の参照に必要なものだけを取得してKnown Spaceへ統合できる。 diff --git a/2.2-operation.md b/2.2-operation.md index 6d09095..2525bf8 100644 --- a/2.2-operation.md +++ b/2.2-operation.md @@ -16,13 +16,17 @@ Operationは少なくとも次の情報を持つ。 Operation名や型だけに意味を担わせず、`description`をinterface metadataの一部として扱う。長文documentationを埋め込むことは想定しない。 -## 2.2.3 入出力型 +## 2.2.3 入出力型とWIP IDL -Operationのinput / outputは、Wire Protocol上で機械的に検証・解釈できる構造化された型として定義する。 +Operationのinput / outputは、機械的に検証・解釈できる構造化された型として[WIP IDL](draft/wip-idl.md)で定義する。WIP IDLのsourceをschemaのcanonicalな交換形式とし、JSON SchemaやIDLを変換したJSON ASTはCore Protocol上の交換形式にしない。 -ただしWIPは任意のドメインデータをObjectのpropertiesとしてモデル化するものではない。ここでの型はOperation境界の値を記述するためのものである。 +Hostは公開するinterfaceについて、IDLのlanguage identifier、sourceまたはsourceへの参照、およびcacheや同一性確認に利用できるdigestをClientへ提供する。ClientはIDLをparseして内部表現を構築する。AI ToolやGUIへ投影する際にJSON Schema等へ変換することはClient実装の責務であり、その変換形式をWIP interfaceとしてHostへ要求しない。 -具体的な型システム、primitive type、record / list / optional / union、ObjectやLinkを結果として返す場合の表現はこのページで定める。 +WIP IDLは、Operation境界の値を記述するための小さな型システムとする。Primitive、record、list、optional field、enum、unionに加え、Worldspace内のエントリpathを示す`entry`型を表現する。任意のドメインデータをObjectのpropertiesとしてモデル化するための型システムではない。 + +Operationのinputは常に名前付きfieldを持つrecordとする。引数を持たないOperationのinputは空のrecordであり、位置引数は用いない。これにより、呼び出しの可読性を保ち、既存fieldの意味を変えずにoptional fieldを追加できる。 + +Outputは任意のIDL型を返してよい。ただし、将来の拡張や付加情報の追加が想定されるOperationではrecordを基本とする。 ## 2.2.4 可視性 @@ -42,6 +46,12 @@ Guardの具体的schemaは未解決事項として管理する。 ## 2.2.6 Operation Result -Operationの結果には通常の値だけでなく、新しいObject / エントリやLinkの発見が含まれ得る。 +Operationの結果には、通常のdomain valueに加えて、WIP IDLの`entry`型として新しいエントリのpathが含まれ得る。 -ClientがそれらをKnown Spaceへ統合できるよう、発見されたWorldspace要素は構造化された結果として表現する。 \ No newline at end of file +`entry`は同じWorldspace内のcanonical absolute pathであり、wire上では文字列として表現する。Clientはoutput schemaに従って`entry`型の位置を認識し、返されたpathを一時的な発見結果として扱う。通常の`string`や任意JSON値に含まれるpathらしい文字列を、エントリとして推測してはならない。 + +Clientは発見したpathを必要に応じて`fetch`または`fetch_tree`する。複数pathのbatch取得や、invoke responseへのエントリ公開情報の同梱は、Operationのoutput型とは独立したprotocol上の最適化として扱う。 + +Operationの実行対象と、結果として発見されたエントリのTree上の位置は独立している。発見されたエントリは実行対象の子である必要はなく、親、兄弟、またはWorldspace内の任意の位置にあるエントリであってよい。 + +Operationがエントリpathを返したこと自体から、実行対象との間に親子関係や意味的関係を推論してはならない。親子関係はTree上の配置で表し、非内包の意味的関係はOperation名、outputのfield名、またはdomain固有のrelation型で表現する。 diff --git a/3-core-protocol.md b/3-core-protocol.md index 85bf660..6c1d820 100644 --- a/3-core-protocol.md +++ b/3-core-protocol.md @@ -38,6 +38,8 @@ ClientとHostの境界は通信境界であると同時に、ある主体へど - `fetch_tree(entry, depth, limit)` — 指定エントリを起点に、indexableな子エントリを幅優先探索してエントリツリーとして取得する。 - `call_operation(target, operation, input)` — Objectが公開するOperationを実行する。 +Operationのinterfaceは[WIP IDL](draft/wip-idl.md)で定義する。`call_operation`の`input`はOperationが宣言したinput recordに従う名前付きの型付き値であり、引数がない場合も空のrecordを渡す。Core Protocolはこの値をJSONに限定せず、transport bindingが具体的なencodingを定める。 + ## 3.4 `fetch_tree` `fetch_tree`は、Clientが機械的探索のために多数の`fetch`を連打することを避けるための操作である。 @@ -50,9 +52,13 @@ ClientとHostの境界は通信境界であると同時に、ある主体へど ## 3.5 Operation実行と発見 -`call_operation`の結果は単なる値に限らない。新しいエントリ、エントリツリー、Linkなどを返し得る。 +`call_operation`の結果はOperationのWIP IDL output型に従う型付き値である。Output schema上で`entry`として宣言された位置には、同じWorldspace内のエントリを指すcanonical absolute pathを返せる。 -ClientはOperation結果として発見したエントリを既知の空間へ統合し、そこからさらにWorldspaceを探索できる。 +Clientはoutput schemaに従って`entry`型の位置を認識する。通常の`string`や任意JSONに含まれるpathらしい文字列を、エントリとして推測してはならない。 + +返されたエントリpathのTree上の位置はOperationの実行対象から独立しており、実行対象の子に限定されない。Operationの実行対象と返されたエントリの間に、暗黙の親子関係や意味的関係を作ってはならない。 + +ClientはOperation結果として発見したpathを一時的に扱い、必要なものを`fetch`または`fetch_tree`して既知の空間へ統合できる。複数pathに対する`fetch`は意味上それぞれ独立しており、Transport Bindingは同じ意味を保ったまま一つのrequestへbatchしてよい。 ## 3.6 Transportとの分離 diff --git a/4-client.md b/4-client.md index 1b2f1b5..712dc39 100644 --- a/4-client.md +++ b/4-client.md @@ -4,27 +4,40 @@ Clientは、Hostが提供するWorldspaceをユーザーへ提示する実装主体である。WIPのCore Protocolを直接利用しつつ、用途に応じたViewやAPIへ投影する。 -ClientはHostから得た情報をそのまま一時表示するだけでなく、これまでに発見したエントリを保持し、ユーザーから見た**既知の空間**を構成する。 +ClientはHostから得た情報をそのまま一時表示するだけでなく、rootからindexable edgeを辿って構成したエントリツリーと、必要に応じて選択したエントリを保持し、ユーザーから見た**既知の空間**を構成する。 ## Known Space -Clientは、これまでに発見したエントリをKnown Spaceとして保持・キャッシュしてよい。 +Known Spaceは、Clientが**現在保持しているエントリの集合**である。過去に発見したすべてのエントリを単調に蓄積する集合ではない。 -Known Spaceには、次のような経路で得られたエントリが含まれる。 +次のような経路で得られたエントリをKnown Spaceへ追加できる。 - `fetch`または`fetch_tree`で取得したエントリ -- Operationの結果として返されたエントリまたはエントリツリー -- Operation結果に含まれるLinkから発見したエントリ +- Operation結果の`entry`型pathを`fetch`して取得したエントリ + +Operationを通して発見されたpathは、必ずしもKnown Spaceへ追加する必要はない。Clientは現在の結果として一時的に扱うことも、取得後に必要なエントリだけを選択して保持することもできる。 既知エントリ間の移動や参照はClient側で処理できる。既知の空間を辿るたびにHostへ問い合わせる必要はない。 ## Discoverable Space -Clientは、Hostから`fetch_tree`で取得したindexableなエントリツリーをDiscoverable Spaceとして保持してよい。 +Clientは、Hostから`fetch_tree`で取得したindexableなエントリツリーをDiscoverable Spaceとして保持する。Discoverable Spaceとして扱う間、Clientはそのtreeの構造を保持する必要がある。 -Discoverable SpaceはKnown Spaceの部分集合であり、AI向け`discover`などの機械的探索に利用する。 +Discoverable SpaceはKnown Spaceの部分集合であり、AI向け`discover`などの機械的探索に利用する。Worldspace rootまたはClientが選択した起点からindexable edgeで到達できる、materialize済みの範囲によって構成される。 -OperationやLinkを通して偶発的に発見されたエントリはKnown Spaceには追加されるが、それだけではDiscoverable Spaceには追加しない。そのエントリを起点として`fetch_tree`を取得した場合に、そのindexableな範囲をDiscoverable Spaceへ追加できる。 +Treeの再取得やHostからの更新によって親のindexable edgeが削除された場合、その子エントリはDiscoverable Spaceから除外する。その結果、保持しているどの起点からも到達不能になったsubtreeは、その場で破棄できる。ただし、別の経路から到達できるエントリ、Clientがpinしたエントリ、実行中のOperationが参照しているエントリは引き続き保持してよい。 + +Operationを通して発見されたpathは、それだけではDiscoverable Spaceへ追加しない。そのエントリを起点として`fetch_tree`を取得した場合に、そのindexableな範囲をDiscoverable Spaceへ追加できる。 + +## 一時的な発見とpin + +Clientは、Operation Resultの`entry`型として発見したpathを現在の結果として一時的に扱える。標準的なClientはoutput schemaからentry pathを抽出して重複を除去し、必要なpathの公開情報を取得する。Transportがbatch fetchやinvoke responseへの同梱を提供する場合は、それを利用してround tripを削減してよい。 + +一時的に取得したエントリは、結果の利用が終わった時点で破棄してよい。 + +後続のInteractionでも必要なエントリは、Client側でpinしてKnown Spaceへ保持できる。PinはClient内部の保持方針であり、Host上のObject lifetimeやCore Protocol上の状態を変更しない。 + +単純なClientは発見したエントリをすべて保持してもよいが、WIPはそれを必須としない。具体的な容量制限やeviction algorithmはClient実装に委ねる。 ## Tree取得 diff --git a/4.1-ai-tools-example.md b/4.1-ai-tools-example.md index cc8c9c2..52f1f49 100644 --- a/4.1-ai-tools-example.md +++ b/4.1-ai-tools-example.md @@ -10,7 +10,7 @@ AI用Toolsは、WIP ClientがAIへ提供する想定インターフェースで `discover(scope, query)` は、Clientが保持するDiscoverable Spaceから目的に関連するエントリを探す。 -検索対象は、Hostから`fetch_tree`によって取得されたindexableなエントリ公開情報に限定する。OperationやLinkによって新たに発見されたエントリはKnown Spaceには追加されるが、それだけでは`discover`の検索対象にはならない。 +検索対象は、Hostから`fetch_tree`によって取得されたindexableなエントリ公開情報に限定する。Operationによって新たに発見されたentry pathは、一時的な結果として扱うか、取得してKnown Spaceへ保持できるが、それだけでは`discover`の検索対象にはならない。 `discover`はドメインデータを検索する操作ではない。Issue本文やRAG corpusなどを検索する場合は、その能力を持つObjectのOperationを`invoke`する。 @@ -23,14 +23,17 @@ Objectのドメインデータをpropertiesとして直接公開することは 例えばIssueでは、次のような操作が示され得る。 - `read_body()` — 本文を読む。 -- `get_metadata()` — 期日、担当者、ラベル、関連ObjectへのLinkなどを取得する。 +- `get_metadata()` — 期日、担当者、ラベルなどを取得する。 +- `get_related_items()` — 関連Objectのentry pathを取得する。 - `close(reason: string)` — 理由を添えてIssueをクローズする。 Operationはシグネチャだけでなく、AIが用途を判断できる短いdescriptionを持つ。長文ドキュメントを埋め込むことは想定しない。 +Operationのinput / output schemaは[WIP IDL](draft/wip-idl.md)として取得する。ClientはIDLをparseし、利用するAI providerが要求するTool schemaへ変換する。JSON Schema等への変換はAI向けClient adapterの責務であり、HostがWIP interfaceとして提供する必要はない。 + ## `invoke` -`invoke(target, operation, input)` は、`inspect`によって発見したOperationを実行する。 +`invoke(target, operation, input)` は、`inspect`によって発見したOperationを実行する。`input`はOperationのWIP IDLで定義された名前付きfieldを持つrecordであり、位置引数は用いない。引数を持たないOperationには空のrecordを渡す。 PTC / REPL bindingでは、通常のメソッド呼び出しへ投影してよい。 @@ -46,13 +49,12 @@ AIが新しいエントリを知る経路は複数ある。 - `discover`によってDiscoverable Spaceから見つける。 - Known Space上の既知エントリを辿る。 -- Operationの結果としてエントリまたはエントリツリーを受け取る。 -- Operation結果に含まれるLinkから別エントリを知る。 +- Operation Resultの`entry`型としてpathを受け取る。 -新たに発見されたエントリの保持、Treeへの統合、indexableな範囲の管理はClientの責務とし、このページではAI向けToolの意味論だけを扱う。 +ClientはOperation Resultからentry pathを抽出し、必要なpathを`fetch`または`fetch_tree`する。新たに取得したエントリの保持、Treeへの統合、indexableな範囲の管理はClientの責務とし、このページではAI向けToolの意味論だけを扱う。 ## ドメイン検索 全文検索、semantic retrieval、外部API検索などはWIP固有のToolにはしない。 -検索能力を持つObjectが`query`や`search`などのOperationを公開し、AIはそれを`invoke`する。結果としてエントリやエントリツリーが返された場合、それらはClientを通して新たなKnown Spaceとして利用可能になる。 \ No newline at end of file +検索能力を持つObjectが`query`や`search`などのOperationを公開し、AIはそれを`invoke`する。結果として`entry`型のpathが返された場合、Clientは必要なpathを取得し、一時的な発見結果として利用するかKnown Spaceへ保持できる。 \ No newline at end of file diff --git a/draft/unresolved-issues.md b/draft/unresolved-issues.md index d75f00d..9518909 100644 --- a/draft/unresolved-issues.md +++ b/draft/unresolved-issues.md @@ -8,17 +8,28 @@ - Object / エントリ公開情報の具体的なwire schema。 - `name`とpath segmentの関係。 +- entry pathのcanonicalization規則。 - `description`の長さや必須性をprotocol上でどこまで規定するか。 -- Linkの標準表現。 -- Operation Result内でエントリ / エントリツリー / Linkをどう統一的に表現するか。 -## 2.2. Operation +## 2.2. Operation / WIP IDL -対象: [2.2. Operation](../2.2-operation.md) +対象: [2.2. Operation](../2.2-operation.md)、[WIP IDL](wip-idl.md) -- input / outputに使う型システム。 +- WIP IDL sourceのcanonicalization、digest algorithm、参照方法。 +- WIP IDLのversion間における互換性規則。 +- Sourceにdescriptionやdocumentation commentを含めるか。 +- String length、numeric range、pattern等のconstraintを導入するか。 +- Recursive named typeを許可するか。 +- `i64`、`u64`、`bigint`、`decimal`等を追加するか。 - `description`の必須性と長さ。 -- optional / union / enum / record等の表現。 -- Object / Linkを値として返す場合の標準表現。 - Guardの具体的schema。 -- error型をOperation outputに含めるか、Protocol errorとして分離するか。 \ No newline at end of file +- error型をOperation outputに含めるか、Protocol errorとして分離するか。 + +## WIP over HTTPS + +対象: [WIP over HTTPS](wip-over-https.md) + +- 複数の`fetch`をまとめるbatch request / responseのwire schema。 +- Invoke時に`included`を要求する方法と、Hostが自動同梱できる条件。 +- `included`のresponse size上限とtruncation表現。 +- `included`と個別の`fetch`で観測整合性情報をどう共有するか。 \ No newline at end of file diff --git a/draft/wip-idl.md b/draft/wip-idl.md new file mode 100644 index 0000000..4c33991 --- /dev/null +++ b/draft/wip-idl.md @@ -0,0 +1,281 @@ +# WIP IDL + +WIP IDLは、Operationのinput / outputを記述するための小さなinterface definition languageである。 + +WIP IDLのsourceそのものをschemaのcanonicalな交換形式とする。HostとClientの間でJSON SchemaやIDLを変換したJSON ASTを交換することは前提としない。Clientはsourceをparseして内部表現を構築し、必要に応じてAI Tool schemaやGUI formへ投影する。 + +この文書では、WIP IDLの初期文法と型を定義する。 + +## 設計方針 + +- Operation境界の型だけを表現する。 +- 文法を小さく保ち、一般的なschema validation languageにはしない。 +- Operationのinputは名前付きfieldを持つrecordとする。 +- Wire上の値表現はTransport Bindingが定める。 +- JSON SchemaはWIPの交換形式にせず、必要なClientが生成する。 +- Worldspaceのエントリは、そのpathを表す`entry`型として扱う。 + +## Source + +WIP IDL sourceはUTF-8 textとする。ASCII space、tab、LFによる空白は、tokenを分離する場合を除いて意味を持たない。 + +`//`から行末まではcommentとし、parserは無視する。 + +IdentifierはASCIIの英字または`_`で始まり、以降にASCIIの英数字または`_`を含められる。 + +```text +identifier = (ALPHA | "_") { ALPHA | DIGIT | "_" }; +``` + +Keyword、primitive type名、Worldspace固有型名はidentifierとして使用できない。 + +Named typeにはUpperCamelCase、Operation、field、enum case、union caseにはsnake_caseを使用する。 + +## Grammar + +以下のEBNFは空白とcommentを省略して示す。 + +```ebnf +document = { declaration } ; + +declaration = type-declaration | operation-declaration ; + +type-declaration = "type", type-name, "=", type-definition, ";" ; + +type-definition = type-expression | enum-type | union-type ; + +operation-declaration = + "operation", identifier, "{", + "input", ":", type-expression, ";", + "output", ":", type-expression, ";", + "}" ; + +type-expression = + primitive-type + | worldspace-type + | type-name + | record-type + | list-type ; + +record-type = + "{", [ record-field, { ",", record-field }, [ "," ] ], "}" ; + +record-field = identifier, [ "?" ], ":", type-expression ; + +list-type = "[", type-expression, "]" ; + +enum-type = + "enum", "{", identifier, { ",", identifier }, [ "," ], "}" ; + +union-type = + "union", "{", union-case, { ",", union-case }, [ "," ], "}" ; + +union-case = identifier, [ "(", type-expression, ")" ] ; + +primitive-type = + "unit" + | "boolean" + | "integer" + | "number" + | "string" + | "bytes" + | "json" ; + +worldspace-type = "entry" ; + +type-name = identifier ; +``` + +同じdocument内でtype名またはOperation名を重複して宣言してはならない。Named typeは同じdocument内で宣言されたtypeを参照する。 + +## Primitive types + +| Type | 意味 | WIP over HTTPSでの表現 | 制約 | +| --- | --- | --- | --- | +| `unit` | 値を持たない | JSON `null` | Optional fieldの省略とは区別する | +| `boolean` | 真偽値 | JSON boolean | — | +| `integer` | 符号付き整数 | 小数部を持たないJSON number | `-9007199254740991`以上、`9007199254740991`以下 | +| `number` | IEEE 754 binary64値 | JSON number | NaN、positive infinity、negative infinityは不可 | +| `string` | Unicode string | JSON string | — | +| `bytes` | 任意のoctet列 | RFC 4648 Section 4のbase64を格納したJSON string | paddingを含む | +| `json` | opaqueな任意のJSON value | JSON value | Clientは内部構造をschemaとして解釈しない | + +`integer`の範囲は、異なる言語のClient間で正確に交換できる範囲に制限している。この範囲を超える整数は`integer`としてencodeしない。`i64`、`u64`、`bigint`等の追加型とlosslessなwire表現は将来の拡張として扱う。 + +`json`は外部APIの応答など構造を事前に固定できない値のためのescape hatchであり、通常のOperation interfaceではより具体的な型を優先する。 + +## Composite types + +| Type | 構文 | 意味 | WIP over HTTPSでの表現 | +| --- | --- | --- | --- | +| Record | `{ field: Type }` | 名前付きfieldの集合 | JSON object | +| List | `[Type]` | 同じ型の順序付き値 | JSON array | +| Named type | `type Name = Type;` | 型への名前付け | 参照先と同じ表現 | +| Enum | `enum { first, second }` | payloadを持たないcaseの集合 | case名のJSON string | +| Union | `union { case(Type), empty }` | discriminatorを持つcaseの集合 | `$case`を持つJSON object | + +### Record + +Recordは名前付きfieldの集合である。 + +```wip +type Query = { + text: string, + limit?: integer, +}; +``` + +`?`を持たないfieldはrequired、`?`を持つfieldはoptionalである。Optionalはrecord fieldにのみ適用し、一般化しない。 + +WIP over HTTPSではrecordをJSON objectとしてencodeする。Optional fieldに値がない場合はfield自体を省略する。FieldをJSON `null`にすることは省略と同じ意味ではなく、そのfieldの型が`unit`または`json`として`null`を許す場合に限る。 + +宣言されていないfieldをrecordに含めてはならない。 + +### List + +Listは同じ型の0個以上の順序付き値を表す。 + +```wip +[entry] +``` + +WIP over HTTPSではJSON arrayとしてencodeする。 + +### Named type + +`type`宣言によって型へ名前を付けられる。 + +```wip +type IssueList = [entry]; +``` + +Aliasを参照する値のwire表現は、参照先の型と同じである。 + +### Enum + +Enumはpayloadを持たないcaseの集合である。 + +```wip +type Relation = enum { + parent, + sibling, + related, +}; +``` + +WIP over HTTPSではcase名をJSON stringとしてencodeする。 + +```json +"sibling" +``` + +### Union + +Unionはdiscriminatorを持つcaseの集合である。Caseは0個または1個のpayloadを持つ。 + +```wip +type LookupResult = union { + found(entry), + not_found, + ambiguous([entry]), +}; +``` + +WIP over HTTPSでは`$case` discriminatorを持つJSON objectとしてencodeする。Payloadを持つcaseは`value` fieldに値を格納する。 + +```json +{ + "$case": "found", + "value": "/items/123" +} +``` + +Payloadを持たないcaseには`value`を含めない。 + +```json +{ + "$case": "not_found" +} +``` + +## Worldspace type + +| Type | 意味 | WIP over HTTPSでの表現 | +| --- | --- | --- | +| `entry` | 同じWorldspace内のエントリを指すcanonical absolute path | JSON string | + +```json +"/items/123" +``` + +Wire上では通常の`string`と同じ表現だが、WIP IDL上で`entry`と宣言された位置にある値だけをClientがエントリとして認識する。通常の`string`やopaqueな`json`に含まれるpathらしい文字列を、Clientがエントリとして推測してはならない。 + +Operation Resultから`entry`を発見したClientは、そのpathを一時的に利用し、必要に応じて`fetch`または`fetch_tree`する。複数のpathを効率的に取得するためのbatchingや、取得結果をinvoke responseへ同梱する最適化は、`entry`型のwire表現とは分離する。 + +Operationが返したpathは、Operationの実行対象の子に限定されない。親、兄弟、関連Objectなど、Worldspace内の任意のエントリを指してよい。Operationの実行対象と返されたpathの間に、暗黙のTree edgeや意味的関係を推論してはならない。 + +`fetch`が返すエントリ公開情報と、`fetch_tree`が返すindexableなtreeはCore Protocolのresponseであり、WIP IDLの値型としては定義しない。 + +## Operation declarations + +Operationはinputとoutputをそれぞれ一つ宣言する。 + +```wip +type Relation = enum { + parent, + sibling, + related, +}; + +operation get_related_item { + input: { + relation: Relation, + limit?: integer, + }; + + output: { + items: [entry], + next_cursor?: string, + }; +} +``` + +Operationのinputは、直接またはNamed typeを介してrecordへ解決されなければならない。位置引数は定義しない。 + +引数を持たないOperationは空のrecordをinputにする。 + +```wip +operation refresh { + input: {}; + output: unit; +} +``` + +Outputには任意のtype expressionまたはNamed typeを使用できる。ただし、将来fieldを追加する可能性があるOperationではrecord outputを推奨する。 + +Operationのname、description、Guardなど、input / output型以外のinterface metadataとの対応付けは[Operation schema](../2.2-operation.md)で定義する。 + +## Schema exchange + +HostはOperation interfaceとともに、次のschema情報をClientへ提供する。 + +| 項目 | 内容 | +| --- | --- | +| Language identifier | 初期versionは`wip-idl/1` | +| Source | WIP IDL source、またはsourceを取得するための参照 | +| Digest | Cacheと同一性確認に利用するdigest | + +交換されるschemaの正本はWIP IDL sourceである。HostはJSON SchemaやIDL ASTを併記する必要はない。 + +ClientはIDLを内部ASTへparseし、必要に応じてAI provider用JSON Schema、GUI form、言語固有の型などを生成できる。 + +Sourceのcanonicalization規則、digest algorithm、参照方法の詳細はTransport Bindingで定義する。 + +## 未解決事項 + +- Sourceにdescriptionやdocumentation commentを含めるか。 +- String length、numeric range、pattern等のconstraintを導入するか。 +- Recursive named typeを許可するか。 +- `i64`、`u64`、`bigint`、`decimal`等を追加するか。 +- Sourceのcanonicalizationとdigestの厳密な計算方法。 +- `entry`が参照するpathのcanonicalization規則。 diff --git a/draft/wip-over-https.md b/draft/wip-over-https.md index d7f07d2..fd8f9e0 100644 --- a/draft/wip-over-https.md +++ b/draft/wip-over-https.md @@ -24,6 +24,79 @@ HTTPSを利用することで、TLS、認証、Proxy、Gateway、Observability 具体的なHTTP method、endpoint、content type、error mapping、認証方式、streaming表現などは、このbinding仕様側で定義する。 +## WIP IDLの取得 + +Operationのinput / output schemaは[WIP IDL](wip-idl.md)のsourceとして交換する。HostはIDLのlanguage identifier、sourceまたはsourceを取得するための参照、およびcacheと同一性確認に利用できるdigestを公開する。 + +HTTPS response全体をJSON envelopeにする場合、IDL sourceをJSON stringとして格納してよい。ただし、これはIDLをJSON ASTへ変換するものではない。BindingはIDL sourceを専用resourceおよびcontent typeで直接取得する方法を定義してもよい。 + +## Operation valueのJSON encoding + +WIP over HTTPSでは、`call_operation`のinputとoutputをWIP IDLに従うJSON valueとしてencodeする。JSONはHTTPS binding上の値表現であり、WIP Coreの型定義形式ではない。 + +`input`は常に名前付きfieldを持つJSON objectとする。位置引数は用いず、引数を持たないOperationには空のobjectを渡す。 + +概念的なrequest bodyは次の形になる。 + +```json +{ + "target": "/items/current", + "operation": "get_related_item", + "input": { + "relation": "sibling", + "limit": 10 + } +} +``` + +IDL型からJSONへのmappingはbindingで一意に定める。少なくとも次の規則を持つ。 + +- `boolean`、`string`、`number`、record、listは対応するJSON valueで表す。 +- optionalなrecord fieldはfieldの省略で表し、`null`とは区別する。 +- unit形式のenumはstring、payloadを持つunionは明示的なdiscriminatorを持つobjectで表す。 +- `bytes`はbase64でencodeしたstringとして表す。 +- 通常のJSON numberで表す`integer`は相互運用可能な安全範囲に制限する。それを超える整数型を設ける場合はdecimal string等のlosslessな表現を定義する。 +- WIP IDLの`entry`は、同じWorldspace内のcanonical absolute pathをJSON stringとして表す。 + +ClientはWIP IDLに従ってvalueをdecodeし、IDL上で`entry`と宣言された位置にある文字列だけをエントリとして認識する。通常の`string`や任意JSON値に含まれるpathらしい文字列を、エントリとして推測してはならない。 + +## Entry取得のbatching + +Operation Resultに複数のentry pathが含まれる場合、Clientはそれらを抽出して重複を除去し、必要なpathを取得できる。 + +Core Protocol上では各`fetch`を独立した観測として扱う。WIP over HTTPSは、複数の`fetch`を一つのHTTP requestへまとめ、個別に実行した場合と同じ結果を返すbatch表現を定義してよい。Batchingは通信上の最適化であり、Core Protocolへ別の意味を追加しない。 + +## Invoke responseへの同梱 + +Hostは追加のround tripを避けるため、Operation Resultに含まれるentry pathの公開情報を、invoke responseの`included` sidecarへ同梱してよい。 + +```json +{ + "output": { + "items": [ + "/items/123", + "/items/456" + ] + }, + "included": { + "/items/123": { + "type": "item", + "description": "First item", + "operations": [] + }, + "/items/456": { + "type": "item", + "description": "Second item", + "operations": [] + } + } +} +``` + +`included`のkeyはOperation Resultに現れるcanonical entry path、valueは同じpathに対する通常の`fetch`結果と同じ観測とする。`included`はOperationのtyped outputには含まれず、Operationのoutput schemaを変更しない。 + +Clientは`included`をfetch済みの観測として利用しても、無視して改めて取得してもよい。Clientが同梱を要求する方法、Hostが自動的に同梱できる条件、response sizeの上限はbindingの詳細として定める。 + ## 非目標 WIP over HTTPSはHost内部のAPIやデータソース接続方式を規定しない。また、WIP Core ProtocolそのものをHTTP依存のモデルへ変えることも目的としない。 \ No newline at end of file