poml / toolkit
POML Toolkit
Installs: 0
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
Type:project
pkg:composer/poml/toolkit
Requires
- php: ^8.0
This package is auto-updated.
Last update: 2025-09-15 19:14:02 UTC
README
Overview
The POML Toolkit is a PHP-based orchestration engine designed to manage complex prompt workflows using a custom XML-based language called POML (Prompt Orchestration Markup Language). Its primary purpose is to enable developers to define, parse, and execute multi-step AI prompt chains that integrate with language models, custom tools, conditional logic, and state management. This toolkit is particularly useful for building AI-driven applications where prompts need to be sequenced, variables extracted from responses, and decisions made based on runtime conditions.
Key Features
- Prompt Execution: Send prompts to AI models via connectors (e.g., dummy for testing or HTTP for real APIs like OpenAI).
- Tool Integration: Call PHP functions as tools within the workflow, with parameter resolution from state.
- Conditional Logic: Support for if/else steps with simple condition evaluation (e.g.,
{{var}} == 'value'
). - Variable Extraction: Automatically extract values from JSON responses into state variables.
- State Management: Persistent state across steps for variable sharing and result tracking.
- Error Handling: Custom exceptions for engine and parser errors.
Architecture
The toolkit follows a modular design with the following core components:
- Steps (Implement StepInterface): Abstract interface for executable steps.
PromptStep
: Executes a prompt via a connector and extracts variables.ToolStep
: Invokes a registered PHP callable with resolved parameters.ConditionalStep
: Evaluates a condition and executes then/else branches.
- Orchestration: Container for a sequence of steps parsed from POML XML.
- Connectors (Implement ModelConnectorInterface): Interfaces for AI model interactions.
DummyConnector
: For testing, returns predefined responses.HttpConnector
: For real HTTP-based API calls (e.g., to OpenAI).
- PomlParser: Parses POML XML using SimpleXML, validating structure and building step objects.
- OrchestrationEngine: Central executor that registers connectors/tools, runs the orchestration, and manages state via
StateManager
. - StateManager: Handles variable resolution (e.g.,
{{var}}
), storage, and retrieval. - Exceptions:
EngineException
for runtime errors,ParserException
for XML issues.
Dependencies
From composer.json
:
- PHP ^8.0 (strict types enforced).
- Autoloading via PSR-4 for
Poml\Toolkit\
namespace mapping tosrc/
. No external libraries are required beyond PHP's built-in extensions (e.g., SimpleXML for parsing, cURL implicitly via HTTP connector if used).
Architecture Diagram
graph TD A[Poml XML Input] --> B[PomlParser] B --> C[Orchestration Object with Steps] C --> D[OrchestrationEngine] D --> E[Register Connectors and Tools] E --> F[Run Orchestration] F --> G[StateManager for Variables and Results] G --> H[Execute Steps: Prompt, Tool, Conditional] H --> I[Connectors: Dummy or HTTP] I --> J[Tools: PHP Callables] J --> G H --> K[Variable Extraction from JSON] K --> G L[Conditional Evaluation] --> M[Then/Else Branches] M --> HLoading
Implemented Changes and Improvements
This version includes several enhancements based on best practices for robustness and maintainability:
- Autoloader Improvements: Switched to PSR-4 compliant autoloading for better namespace organization and Composer integration.
- Error Handling Refactor: Introduced specific exceptions (
EngineException
,ParserException
) instead of generic ones. For example, connector/tool not found throwsEngineException
; invalid XML throwsParserException
with detailed libxml errors. - Step Refactorizations:
StepInterface
now enforces a cleanexecute(StateManager $state): mixed
contract. Subclasses likePromptStep
handle JSON decoding safely withjson_last_error()
checks.ConditionalStep
uses regex for simple condition parsing (e.g., equality checks) with fallback to truthy evaluation. - Optimizations: Parsing uses efficient SimpleXML with internal error suppression and clear/reset. Engine execution avoids recursion depth issues by linear step processing; conditional branches are executed sequentially without loops. Variable resolution in
StateManager
(not shown but inferred) uses string replacement for efficiency.
These changes improve reliability, reduce boilerplate, and enhance debuggability.
Installation Instructions
- Prerequisites: Ensure PHP 8.0+ is installed with SimpleXML extension enabled.
- Clone or Download: Place the project in your workspace (e.g.,
c:/Users/kuasa/Documents/VScode/poml
). - Install Dependencies:
This sets up the autoloader; no additional packages are needed.composer install
- Configuration:
- For testing: No setup required (uses DummyConnector).
- For real AI models: Set environment variables, e.g.,
export OPENAI_API_KEY=your_key_here
(or useputenv()
in PHP). - Edit connectors in code if needed (e.g., register
HttpConnector
with API URL and key).
- Execution:
- Run the example:
php example.php
- This parses a sample POML XML, registers a tool and connector, and executes the workflow, printing outputs.
- Run the example:
If issues arise (e.g., missing extensions), check PHP error logs.
Usage Examples
The example.php
demonstrates a full workflow: generating JSON, extracting a variable, and conditionally calling a tool.
<?php declare(strict_types=1); require 'vendor/autoload.php'; use Poml\Toolkit\OrchestrationEngine; use Poml\Toolkit\Parsing\PomlParser; use Poml\Toolkit\Connectors\DummyConnector; use Poml\Toolkit\Connectors\HttpConnector; echo "--- POML Toolkit Advanced Example ---\n\n"; // 1. Define a tool (a simple PHP function) function send_notification(string $message): string { $output = "NOTIFICATION SENT: {$message}\n"; echo $output; return "Sent successfully"; } // 2. Define the complex POML workflow $pomlXml = <<<'XML' <?xml version="1.0"?> <poml> <prompt model="json-generator"> <message>Generate a JSON object with a user 'name' and a 'status' of 'active'.</message> <!-- Extract the status field from the JSON response into a variable called 'user_status' --> <variable name="user_status" from="json" path="status"/> </prompt> <!-- Check the variable we just extracted --> <if condition="{{user_status}} == 'active'"> <tool function="notify"> <param name="message">User is active, proceeding to next step.</param> </tool> <else> <tool function="notify"> <param name="message">User is not active, aborting.</param> </tool> </else> </if> </poml> XML; // 3. Set up the Orchestration Engine $engine = new OrchestrationEngine(); $parser = new PomlParser(); // 4. Register tools and connectors $engine->registerTool('notify', 'send_notification'); // Use a DummyConnector that returns a valid JSON for this example. class JsonDummyConnector extends DummyConnector { public function execute(string $prompt): string { return '{"name": "John Doe", "status": "active"}'; } } $engine->registerConnector('json-generator', new JsonDummyConnector()); /* To use a real HTTP endpoint, you would do this instead: $apiKey = getenv('OPENAI_API_KEY'); // It's best practice to use environment variables $apiUrl = 'https://api.openai.com/v1/chat/completions'; $engine->registerConnector( 'gpt-4', new HttpConnector($apiUrl, 'gpt-4', $apiKey) ); */ // 5. Parse and run the orchestration try { echo "Parsing POML...\n"; $orchestration = $parser->parse($pomlXml); echo "Running orchestration...\n\n"; $finalResult = $engine->run($orchestration); echo "\nOrchestration finished.\n"; echo "Final Result (from 'last_result'): " . print_r($finalResult, true) . "\n"; } catch (\Exception $e) { echo "\nAn error occurred: " . $e->getMessage() . "\n"; echo "Trace: \n" . $e->getTraceAsString() . "\n"; } ?>
Expected output includes parsed POML execution, notification sent, and final result.
Best Practices
- Environment Variables: Always use
getenv()
for sensitive data like API keys to avoid hardcoding. - Input Validation: In custom tools, validate parameters to prevent injection attacks (e.g., sanitize strings).
- Error Handling: Wrap
run()
in try-catch to handleEngineException
orParserException
gracefully. - Testing: Use
DummyConnector
for unit tests; mock responses for integration. - Scalability: For complex workflows, break into multiple orchestrations; monitor state size to avoid memory issues.
- POML Design: Keep XML concise; use meaningful variable names; test conditions thoroughly.
Security and Vulnerability Notes
-
Resolved Vulnerabilities:
- Connector Errors: Previously unhandled missing connectors now throw
EngineException
, preventing silent failures. - Parsing Issues: XML parsing uses
libxml_use_internal_errors(true)
to suppress warnings and throwsParserException
on invalid structure, mitigating XML injection risks. - Tool Invocation: Parameters are resolved but not executed as code; use strict types in tools to avoid type errors.
- Connector Errors: Previously unhandled missing connectors now throw
-
Corrected Inefficiencies:
- Parsing Optimization: SimpleXML is lightweight; no recursive parsing depth issues. Variable extraction is top-level only for speed.
- Condition Evaluation: Regex-based for simple ops, avoiding full expression parsers; no infinite loops as branches are linear.
-
Security Considerations:
- Input Validation: POML XML should be trusted or sanitized externally;
resolve()
in StateManager replaces{{var}}
but doesn't execute code—extend with filters if needed. - API Keys: Never commit keys; use
.env
files (add to.gitignore
). - JSON Handling:
json_decode
with error checks prevents malformed response crashes. - Recommendations: Run in isolated environments for untrusted POML; audit tools for side effects; use HTTPS for
HttpConnector
.
- Input Validation: POML XML should be trusted or sanitized externally;
For contributions or issues, please refer to the code structure in src/
.