mirror of
https://github.com/Geckon01/JsonFicatorPHP.git
synced 2026-10-11 14:05:14 +00:00
5.4 KiB
5.4 KiB
JsonFicator Architecture
Overview
JsonFicator is a pipeline-based library that transforms unstructured natural language into structured data. The pipeline is:
Input text
→ Schema resolution (Array or Class)
→ JSON Schema conversion (for OpenAI strict mode)
→ Prompt building (system + user messages)
→ LLM generation (OpenAIProvider)
→ Response parsing (strip markdown, decode JSON)
→ Validation (check types, required fields, unexpected keys)
→ Hydration (array → PHP model)
→ JsonFicatorResult
Core Components
JsonFicator (Facade)
Entry point. Delegates to the pipeline:
toJson(string $input, array|class-string $schema, ?JsonFicatorOptions $options): JsonFicatorResult— returns array resulttoModel(string $input, class-string $schema, ?JsonFicatorOptions $options): JsonFicatorResult— returns hydrated model
Schema Layer
Internal representation is decoupled from any LLM provider.
JsonFicatorSchema
fields: SchemaField[]- Optional
modelClass: class-stringfor class-based schemas
SchemaField
name: stringtype: string— primitive (string,int,float,bool) orarrayorobjectnullable: boolrequired: booldescription: ?stringproperties: ?SchemaField[]— for nested objectsitems: ?SchemaField— for typed arraysclass: ?class-string— for nested model classes
Resolvers
ArraySchemaResolver— parses inline array syntax:string,int,float,bool?stringfor nullablearray,array<string>,array<int>,array<array>
ClassSchemaResolver— uses PHP reflection:- Supports primitives, nullable, arrays, nested
IJsonFicatorModelclasses - Reads
JsonFicatorDescription,JsonFicatorRequired,JsonFicatorOptionalattributes
- Supports primitives, nullable, arrays, nested
JsonSchemaConverter
Converts internal JsonFicatorSchema to OpenAI-compatible JSON Schema:
type: objectwithpropertiesrequiredarray based onSchemaField.required- Nullable fields use
anyOf: [{type}, {type: "null"}] - Arrays use
itemsschema - Nested objects recurse into
properties additionalProperties: falsewhenstrict: true
Provider Layer
LLMProviderInterface
public function generate(
string $input,
JsonFicatorSchema $schema,
?JsonFicatorOptions $options = null,
): array;
Returns raw decoded associative array from LLM.
OpenAIProvider
- Uses
openai-php/clientvianew \OpenAI\Factory()(avoids global\OpenAIclass collision) - Builds
ChatCompletionwithResponseFormat::createJsonSchema(..., strict: true) - Normalizes all exceptions to
ProviderException - Extracts
choices[0].message.contentand decodes JSON
Response Pipeline
ResponseParser
- Accepts
array(already decoded) orstring(raw JSON) - Strips markdown code fences (
json ...) - Decodes JSON string to associative array
- Throws
InvalidResponseExceptionif result is not an object (associative array)
Validator
Validates parsed data against internal JsonFicatorSchema:
- Required fields present
- Nullable rules respected
- Primitive type checks (
string,int,float,bool) - Array items validated recursively
- Nested objects validated against sub-schema
- Unexpected fields rejected when schema has
modelClass(class-based) or strict mode
ModelHydrator
Hydrates validated array into PHP model via reflection:
- Creates instance without constructor
- Sets public properties directly
- Handles nullable, nested models, arrays
- Supports union types (picks first matching type)
- Casts
int→stringwhen needed - Throws
ModelHydrationExceptionon type mismatch
Options & Result
JsonFicatorOptions
Immutable per-call configuration:
model: ?stringtemperature: ?floatmaxTokens: ?int
Methods: withModel(), withTemperature(), withMaxTokens()
JsonFicatorResult
Immutable wrapper:
toArray(): arraytoJson(): stringtoModel(): object(requires schema was a class)get(string $key, mixed $default = null): mixedgetSchema(): JsonFicatorSchema
Exception Hierarchy
JsonFicatorException (base)
├── SchemaException
├── ProviderException
├── InvalidResponseException
└── ModelHydrationException
All extend \Exception and are located in src/Exception/.
Design Decisions
- Internal schema representation — decoupled from OpenAI JSON Schema, allowing future providers (Claude, Gemini) to reuse the same validators and hydrators.
- Strict mode by default —
additionalProperties: falseprevents LLM hallucination of extra fields. - Readonly properties — all service classes use
private readonlyfor immutability. - No constructor in models — hydrator uses
newInstanceWithoutConstructor()to support plain DTOs. - Attributes over docblocks — PHP 8 attributes are native, cacheable, and type-safe.
- Factory over global class —
new \OpenAI\Factory()avoids fatal error whenvendor/openai-php/client/src/OpenAI.php(global class) is loaded via Composerfilesautoload.
Testing Strategy
- Unit tests — 76 tests covering every component in isolation with fake provider
- Integration tests — gated by
JSONFICATOR_OPENAI_API_KEYenv var; tests real API with small model (gpt-4o-mini) - Fixtures —
tests/Fixtures/contains sample models (VinRequest,Car,NestedRequest,DescribedModel,FakeProvider)