Skip to main content

Structured Data

NodeSet is a specialized prompt for rendering structured lists — scoring rubrics, evaluation criteria, taxonomies, classification labels. Instead of hardcoding numbered lists in strings, you define the data and let NodeSet handle formatting.

Inline Data

The simplest approach is inline items:
use Cognesy\Xprompt\NodeSet;

class ScoringCriteria extends NodeSet
{
    public array $items = [
        ['label' => 'Clarity', 'content' => 'Writing is clear and unambiguous'],
        ['label' => 'Evidence', 'content' => 'Claims are supported by sources'],
        ['label' => 'Structure', 'content' => 'Argument flows logically'],
    ];
}

echo ScoringCriteria::make();
// @doctest id="47af"
Output:
1. **Clarity** -- Writing is clear and unambiguous

2. **Evidence** -- Claims are supported by sources

3. **Structure** -- Argument flows logically
// @doctest id="7e09"

YAML Data Files

For larger datasets, load from a YAML file:
class ReviewCriteria extends NodeSet
{
    public string $dataFile = 'criteria.yml';
    public ?string $templateDir = __DIR__ . '/data';
    public string $sortKey = 'priority';
}
// @doctest id="2de6"
The file data/criteria.yml:
- id: clarity
  label: Clarity
  content: Writing is clear and unambiguous
  priority: 1
- id: evidence
  label: Evidence
  content: Claims are supported by sources
  priority: 2
  children:
    - id: citations
      content: All sources are properly cited
# @doctest id="e655"
Items are sorted by priority and children render as indented sub-items:
1. **Clarity** -- Writing is clear and unambiguous

2. **Evidence** -- Claims are supported by sources
   - All sources are properly cited
// @doctest id="f523"
Note: YAML data files require symfony/yaml. Install it with composer require symfony/yaml.

Custom Formatting

Override renderNode() to change how each item appears:
class NumberedLabels extends NodeSet
{
    public array $items = [
        ['label' => 'Bug', 'content' => 'Functional defect'],
        ['label' => 'Style', 'content' => 'Code style issue'],
    ];

    public function renderNode(int $index, array $node, mixed ...$ctx): string
    {
        return "- [{$node['label']}] {$node['content']}";
    }
}
// @doctest id="8b27"
Output:
- [Bug] Functional defect

- [Style] Code style issue
// @doctest id="459f"

Dynamic Data

Override nodes() to generate items at runtime:
class DynamicLabels extends NodeSet
{
    public function nodes(mixed ...$ctx): array
    {
        return array_map(
            fn(string $label) => ['label' => $label, 'content' => ''],
            $ctx['labels'] ?? [],
        );
    }
}

echo DynamicLabels::with(labels: ['Bug', 'Feature', 'Chore']);
// @doctest id="f8f9"

Using in Composition

NodeSet is a regular Prompt, so it composes naturally:
class ReviewSystem extends Prompt
{
    public function body(mixed ...$ctx): array
    {
        return [
            Persona::with(role: 'reviewer'),
            "## Scoring Criteria",
            ScoringCriteria::make(),
            "## Document\n\n" . $ctx['content'],
        ];
    }
}
// @doctest id="33b7"

Next Steps