プロジェクトシステムの利用 (Working with the Project System)
プロジェクトシステム (Project System) は、すべてのワークフローにおけるファイルの整理、命名、保存を一元管理する Griptape Nodes のファイル管理フレームワークです。ハードコードされたファイルパスを排除し、一貫性があり設定可能なファイル操作アプローチを提供します。このページでは、ノード開発者側の観点から、ノードがプロジェクトシステムを介してファイルを保存する方法について説明します。
主要な概念 (Concepts)
プロジェクトシステムの基盤となる概念 — ワークスペース、プロジェクトテンプレート、シチュエーション、マクロ、ディレクトリ、環境変数 — は、プロジェクトシステムガイド で詳しく解説されています:
- 概要 — 各コンポーネントの連携の仕組み
- シチュエーション (Situations) — 名前付きのファイル保存シナリオ、衝突解決ポリシー、デフォルトシチュエーションの一覧
- マクロ (Macros) — ファイルパスの構築に使用されるテンプレート構文
- ディレクトリ (Directories) — マクロ内で参照される論理名から実パスへのマッピング
- カスタマイズガイド (Customization Guide) — ユーザーが
griptape-nodes-project.ymlを通じてパスやシチュエーションをオーバーライドする方法
ノード作成者向けに要約すると:シチュエーション (Situation) はファイル保存シナリオ(例: save_node_output)に名前を付け、その マクロ (Macro) テンプレート(例: {outputs}/{node_name?:_}{file_name_base}{_index?:03}.{file_extension})が具体的なファイルパスを生成します。ユーザーはノードのコードを変更することなく、これらすべてをカスタマイズできます。
ノード内でのプロジェクトシステムの利用パターン
ノード内でプロジェクトファイルを操作する主なパターンは 2 つあります:
パターン 1: ProjectFileParameter(ノード出力に推奨)
ノードが、ユーザーがカスタマイズできる出力ファイルパラメータを持つ場合は、ProjectFileParameter を使用します。
from griptape_nodes.exe_types.param_components.project_file_parameter import ProjectFileParameter
from griptape_nodes.exe_types.core_types import Parameter, ParameterMode
from griptape.artifacts.video_url_artifact import VideoUrlArtifact
class MyVideoNode(ControlNode):
def __init__(self, **kwargs) -> None:
super().__init__(**kwargs)
# 通常の出力パラメータを追加
self.add_parameter(
Parameter(
name="output_video",
output_type="VideoUrlArtifact",
tooltip="生成された動画",
allowed_modes={ParameterMode.OUTPUT},
)
)
# 出力ファイル設定用のプロジェクトファイルパラメータを追加
# `situation` はこのノードがどのシチュエーションで保存するかを宣言します。
# デフォルトは "save_node_output" ですが、明示的に指定して選択を可視化します。
self._output_video_file = ProjectFileParameter(
node=self,
name="output_video_file",
default_filename="output_video.mp4",
situation="save_node_output",
)
self._output_video_file.add_parameter()
def process(self) -> None:
# ... video_bytes の生成処理 ...
# build_file() を使用して ProjectFileDestination を取得
dest = self._output_video_file.build_file()
saved = dest.write_bytes(video_bytes)
# 保存先の場所を用いて出力パラメータを設定
self.parameter_output_values["output_video"] = VideoUrlArtifact(saved.location)
重要なポイント:
ProjectFileParameterはユーザーが設定可能な UI パラメータを作成しますbuild_file()を呼び出してProjectFileDestinationインスタンスを取得しますwrite_bytes()を使用してファイルを保存しますsaved.location経由で保存されたファイルの URL/パスにアクセスします
シチュエーションの決定方法:
situation 引数は、ノードがそのシチュエーションを宣言する唯一の場所です。これはコンストラクタ引数であり、ノード上のパラメータではありません。ユーザーがノードの前面でシチュエーションフィールドを見たり編集したりすることはありません。デフォルトは save_node_output であるため省略可能ですが、コードを読む人がどのシチュエーションが使用されているかを把握できるように明示的に渡してください。
これによって作成される UI パラメータ(慣例として output_file という名前)は、パスではなくファイル名を保持します。build_file() はそのファイル名を file_name_base と file_extension に分割し、シチュエーションのマクロを介して解決するため、パラメータの値は最終パスの一部にすぎません。save_node_output ノードでユーザーが render.png と入力すると、outputs/MyNode_render.png が生成されます。
シチュエーション名はパラメータの値自体には表示されませんが、add_parameter() が生成するツールチップ(Output filename (uses 'save_node_output' situation template))に表示されるため、ユーザーはここから確認できます。
ユーザーは、パラメータの歯車アイコンボタンをクリックすることで、ノードごとにシチュエーションをオーバーライドできます。これにより、宣言されたシチュエーションがあらかじめ入力された FileOutputSettings ノードが生成されます。そのノードが接続されると、build_file() はそのノードの FileDestination を返し、元の situation 引数はバイパスされます。ノードの操作内容を正直に表すシチュエーションを選択し、ユーザーが必要に応じて変更できるように設計してください。
❌ よくある間違い: write_bytes() の戻り値を受け取らない
# 誤り - このように書いてはいけません:
dest = self._output_video_file.build_file()
dest.write_bytes(video_bytes) # ❌ 戻り値がキャプチャされていない
artifact = VideoUrlArtifact(dest.location) # 保存されたファイルではなく dest を使用している
# これは次のエラーで失敗します: "Failed because missing required variables: file_extension, file_name_base"
失敗する理由: {file_extension} や {file_name_base} などのマクロ変数は、write_bytes() がファイルを保存して保存済みファイルオブジェクトを返す際に解決されるものであり、build_file() の時点では解決されていません。書き込み前に dest.location を参照すると、マクロ解決エラーが発生します。
# 正しい実装:
dest = self._output_video_file.build_file()
saved = dest.write_bytes(video_bytes) # ✅ 戻り値をキャプチャ
artifact = VideoUrlArtifact(saved.location) # 保存済みファイルの解決された location を使用
saved オブジェクトには、すべてのマクロが展開・解決された完全なファイルパスが含まれています。
パターン 2: ProjectFileDestination の直接利用(ユーティリティ関数向け)
ユーティリティ関数の内部や、ユーザーによる設定が不要な場合は、ProjectFileDestination.from_situation() を直接使用します。
from griptape_nodes.files.project_file import ProjectFileDestination
from griptape.artifacts.video_url_artifact import VideoUrlArtifact
def frames_to_video_artifact(frames: list, fps: int = 30, video_format: str = "mp4") -> VideoUrlArtifact:
"""フレームのリストを VideoUrlArtifact に変換します。"""
# ... フレームを video_bytes に処理 ...
# プロジェクトファイルシステムを使用して保存
dest = ProjectFileDestination.from_situation(filename=f"video.{video_format}", situation="save_node_output")
saved = dest.write_bytes(video_bytes)
return VideoUrlArtifact(saved.location)
重要なポイント:
- 名前付きシチュエーションを指定して保存先を作成するには
from_situation()を使用します filenameパラメータはベースとなるファイル名です(シチュエーションのマクロによって変換されます)- シチュエーション(例: "save_node_output")によって最終的なパスと衝突動作が決定されます
StaticFilesManager からの移行
旧パターン(非推奨)
from griptape_nodes.retained_mode.griptape_nodes import GriptapeNodes
import uuid
def old_save_video(video_bytes: bytes) -> VideoUrlArtifact:
filename = f"{uuid.uuid4()}.mp4"
url = GriptapeNodes.StaticFilesManager().save_static_file(video_bytes, filename)
return VideoUrlArtifact(url)
新パターン
from griptape_nodes.files.project_file import ProjectFileDestination
def new_save_video(video_bytes: bytes) -> VideoUrlArtifact:
dest = ProjectFileDestination.from_situation(filename="video.mp4", situation="save_node_output")
saved = dest.write_bytes(video_bytes)
return VideoUrlArtifact(saved.location)
移行のメリット:
- UUID を手動生成する必要がなくなります
- すべてのノードで一貫したファイル整理が可能になります
- プロジェクトテンプレートを介してユーザーがファイルパスを設定可能になります
- ファイルの追跡と管理が向上します
- ファイル名の重複(衝突)が自動的に処理されます
一般的なシチュエーションと使い分け
save_node_output: ノードによって生成されたファイル(画像、動画、音声など)を保存する主要なシチュエーションcopy_external_file: 外部ソースからファイルをインポート/コピーする場合download_url: URL からファイルをダウンロードして保存する場合save_preview: サムネイルやプレビュー画像を生成して保存する場合save_static_file: 実行間で変更されない静的アセット用
デフォルトのシチュエーション、マクロ、衝突ポリシーの全一覧については、シチュエーション (Situations) を参照してください。
ユーザーは、ノードのコードを変更することなく、これら(パス、マクロ、衝突ポリシー)を 2 つの方法でオーバーライドできます:
- プロジェクト全体:
griptape-nodes-project.ymlでシチュエーションを再定義すると、そのシチュエーションを使用するすべてのノードに反映されます。カスタマイズガイド を参照してください。 - ノード単位:
ProjectFileParameterの歯車ボタンからFileOutputSettingsノードを接続すると、そのノード単体のシチュエーション、マクロ、衝突ポリシーをオーバーライドできます。
ベストプラクティス (Best Practices)
- ファイルの保存には常にプロジェクトシステムを使用する — ハードコードされたパスは絶対に使用しないでください。
- 適切なパターンを選択する: ユーザーが設定可能な出力には
ProjectFileParameterを、ユーティリティ関数にはProjectFileDestinationを使用します。 - 意味に合ったシチュエーションを使用する: 処理内容を最も的確に表すシチュエーションを選択してください。
- 命名はマクロに任せる: UUID やタイムスタンプを自前で生成せず、シチュエーションのマクロと衝突ポリシーに委ねてください。
- 一時ファイルを適切に処理する: 中間処理には Python の
tempfileを使用し、最終結果のみをプロジェクトシステム経由で保存してください。 - 一時ファイルをクリーンアップする: プロジェクトシステムにコピーした後は、必ず一時ファイルを削除(クリーンアップ)してください。
完全な実装例: 動画処理ノード
import tempfile
from pathlib import Path
from typing import Any
from griptape.artifacts.video_url_artifact import VideoUrlArtifact
from griptape_nodes.exe_types.core_types import Parameter, ParameterMode
from griptape_nodes.exe_types.node_types import ControlNode, AsyncResult
from griptape_nodes.exe_types.param_components.project_file_parameter import ProjectFileParameter
from griptape_nodes.files.file import File
class ProcessVideo(ControlNode):
def __init__(self, **kwargs) -> None:
super().__init__(**kwargs)
self.add_parameter(
Parameter(
name="input_video",
input_types=["VideoUrlArtifact"],
type="VideoUrlArtifact",
tooltip="処理対象の入力動画",
)
)
self.add_parameter(
Parameter(
name="output_video",
output_type="VideoUrlArtifact",
tooltip="処理された動画",
allowed_modes={ParameterMode.OUTPUT},
)
)
# 出力用のプロジェクトファイルパラメータを追加
self._output_video_file = ProjectFileParameter(
node=self,
name="output_video_file",
default_filename="processed_video.mp4",
situation="save_node_output",
)
self._output_video_file.add_parameter()
def process(self) -> AsyncResult:
yield lambda: self._process()
def _process(self) -> None:
# 入力動画を取得
input_artifact = self.get_parameter_value("input_video")
input_bytes = File(input_artifact.value).read_bytes()
# 一時ディレクトリ/ファイルで処理を実行
with tempfile.NamedTemporaryFile(suffix=".mp4", delete=False) as temp_file:
temp_path = Path(temp_file.name)
try:
# 入力を一時ファイルに書き出し
temp_path.write_bytes(input_bytes)
# ... temp_path に対して実際の動画処理を実行 ...
# 処理結果を読み込み
output_bytes = temp_path.read_bytes()
# プロジェクトシステムを使用して保存
dest = self._output_video_file.build_file()
saved = dest.write_bytes(output_bytes)
# 出力パラメータに設定
self.parameter_output_values["output_video"] = VideoUrlArtifact(saved.location)
finally:
# 一時ファイルのクリーンアップ
if temp_path.exists():
temp_path.unlink()