diff --git a/docs.txt b/docs.txt deleted file mode 100644 index e9d93647..00000000 --- a/docs.txt +++ /dev/null @@ -1,10757 +0,0 @@ -Project Path: docs - -Source Tree: - -``` -docs -├── advanced -│ ├── partials.mdx -│ ├── structures.mdx -│ ├── function_calls.mdx -│ ├── modules.mdx -│ ├── model_options.mdx -│ └── sequences.mdx -├── introduction.mdx -├── concepts -│ ├── why.mdx -│ └── overview.mdx -├── internals -│ ├── response_models.mdx -│ ├── instructor.mdx -│ ├── events.mdx -│ ├── script.mdx -│ ├── debugging.mdx -│ ├── http.mdx -│ └── lifecycle.mdx -├── essentials -│ ├── data_model.mdx -│ ├── demonstrations.mdx -│ ├── usage.mdx -│ ├── scalars.mdx -│ ├── modes.mdx -│ ├── validation.mdx -│ ├── installation.mdx -│ └── prompts.mdx -├── logo -│ ├── light.svg -│ └── dark.svg -├── favicon.svg -├── techniques -│ ├── prompting.mdx -│ ├── search.mdx -│ └── classification.mdx -├── quickstart.mdx -├── llm_providers.mdx -├── cookbook -│ ├── introduction.mdx -│ ├── examples -│ │ ├── advanced -│ │ │ ├── structured_input.mdx -│ │ │ ├── partials.mdx -│ │ │ ├── structures.mdx -│ │ │ ├── custom_prompts.mdx -│ │ │ ├── demonstrations.mdx -│ │ │ ├── streaming.mdx -│ │ │ ├── function_calls.mdx -│ │ │ ├── sequences.mdx -│ │ │ ├── scalars.mdx -│ │ │ ├── context_cache.mdx -│ │ │ ├── custom_client.mdx -│ │ │ └── context_cache_llm.mdx -│ │ ├── troubleshooting -│ │ │ ├── token_usage_events.mdx -│ │ │ ├── on_event.mdx -│ │ │ ├── debugging.mdx -│ │ │ └── wiretap.mdx -│ │ ├── extras -│ │ │ ├── complex_extraction_gemini.mdx -│ │ │ ├── llm_md_json.mdx -│ │ │ ├── translate_ui_fields.mdx -│ │ │ ├── transcription_to_tasks.mdx -│ │ │ ├── complex_extraction_claude.mdx -│ │ │ ├── llm_json.mdx -│ │ │ ├── image_to_data_anthropic.mdx -│ │ │ ├── llm_tools.mdx -│ │ │ ├── image_to_data.mdx -│ │ │ ├── llm.mdx -│ │ │ ├── web_to_objects.mdx -│ │ │ ├── complex_extraction.mdx -│ │ │ ├── llm_json_schema.mdx -│ │ │ ├── complex_extraction_cohere.mdx -│ │ │ ├── image_to_data_gemini.mdx -│ │ │ ├── schema_dynamic.mdx -│ │ │ ├── embeddings.mdx -│ │ │ └── schema.mdx -│ │ ├── basics -│ │ │ ├── custom_validation.mdx -│ │ │ ├── basic_use_mixin.mdx -│ │ │ ├── validation_with_llm.mdx -│ │ │ ├── public_vs_private.mdx -│ │ │ ├── basic_use.mdx -│ │ │ ├── self_correction.mdx -│ │ │ ├── validation_multifield.mdx -│ │ │ ├── optional_fields.mdx -│ │ │ ├── using_config.mdx -│ │ │ ├── maybe.mdx -│ │ │ ├── modes.mdx -│ │ │ ├── validation.mdx -│ │ │ └── attributes.mdx -│ │ └── api_support -│ │ ├── azure_openai.mdx -│ │ ├── google_gemini.mdx -│ │ ├── ollama.mdx -│ │ ├── togetherai.mdx -│ │ ├── cohere.mdx -│ │ ├── openrouter.mdx -│ │ ├── groq.mdx -│ │ ├── anthropic.mdx -│ │ ├── openai.mdx -│ │ ├── fireworks.mdx -│ │ └── mistralai.mdx -│ ├── prompting -│ │ ├── few_shot -│ │ │ └── in_context_examples.mdx -│ │ ├── zero_shot -│ │ │ ├── repeat_query.mdx -│ │ │ ├── follow_up_questions.mdx -│ │ │ ├── auto_refine.mdx -│ │ │ ├── clarify_ambiguity.mdx -│ │ │ ├── define_style.mdx -│ │ │ ├── emotional_stimuli.mdx -│ │ │ ├── assign_role.mdx -│ │ │ └── simulate_perspective.mdx -│ │ ├── thought_gen -│ │ │ └── analogical_prompting.mdx -│ │ └── misc -│ │ ├── arbitrary_properties.mdx -│ │ ├── arbitrary_properties_consistent.mdx -│ │ ├── chain_of_summaries.mdx -│ │ ├── search_query_expansion.mdx -│ │ ├── limiting_lists.mdx -│ │ ├── rewrite_instructions.mdx -│ │ ├── classification_multiclass.mdx -│ │ ├── chain_of_thought.mdx -│ │ ├── restate_instructions.mdx -│ │ ├── component_reuse_cot.mdx -│ │ ├── reflection_prompting.mdx -│ │ ├── entity_relationships.mdx -│ │ ├── handling_errors.mdx -│ │ ├── classification.mdx -│ │ └── component_reuse.mdx -│ └── contributing.mdx -├── snippets -│ └── snippet-intro.mdx -├── images -│ ├── checks-passed.png -│ ├── extraction.cast -│ ├── concept.png -│ ├── receipt.png -│ ├── hero-light.svg -│ ├── hero-dark.svg -│ └── extraction.gif -├── mint.json -└── misc - ├── contributing.mdx - ├── help.mdx - └── philosophy.mdx - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/advanced/partials.mdx`: - -```mdx -## Partial updates - -Instructor can process LLM's streamed responses to provide partial updates that you -can use to update the model with new data as the response is being generated. - -You can use it to improve user experience by updating the UI with partial data before -the full response is received. - -> This feature requires the `stream` option to be set to `true`. - -To receive partial results define `onPartialUpdate()` callback that will be called -on every update of the deserializad object. - -Instructor is smart about updates, it calculates and compares hashes of the previous -and newly deserialized version of the model, so you won't get them on every token -received, but only when any property of the object is updated. - - -```php -request( - messages: "His name is Jason, he is 28 years old.", - responseModel: Person::class, - options: ['stream' => true] -)->onPartialUpdate( - fn($partial) => updateUI($partial) -)->get(); - -// Here you get completed and validated Person object -$this->db->save($person); // ...for example: save to DB -``` - -Partially updated data is not validated while they are received and deserialized. - -The object returned from `get()` call is fully validated, so you can safely work -with it, e.g. save it to the database. - - - -## Streaming responses - -You can get a stream of responses by setting the `stream` option to `true` and calling the `stream()` method -instead of `get()`. It returns `Stream` object, which gives you access to the response streamed from LLM and -processed by Instructor into structured data. - -Following methods are available to process the stream: - - - `partials()`: Returns a generator of partial updates from the stream. Only final update is validated, partial updates are only deserialized and transformed. - - `sequence()`: Dedicated to processing `Sequence` response models - returns only completed items in the sequence. - - `getLastUpdate()`: Returns the last object received and processed by Instructor. - -One more method available on `Stream` is `final()`. It returns only the final response object. It **blocks until the response is fully processed**. It is typically used when you only need final result and prefer to use `onPartialUpdate()` or `onSequenceUpdate()` to process partial updates. It's an equivalent to calling `get()` method (but requires `stream` option set to `true`). - - -### Example: streaming partial responses - -```php -request( - messages: "His name is Jason, he is 28 years old.", - responseModel: Person::class, -)->stream(); - -foreach ($stream->partials() as $update) { - // render updated person view - // for example: - $view->updateView($update); // render the updated person view -} - -// now you can get final, fully processed person object -$person = $stream->getLastUpdate(); -// ...and for example save it to the database -$db->savePerson($person); -``` - - -### Example: streaming sequence items - -```php -request( - messages: "Jason is 28 years old, Amanda is 26 and John (CEO) is 40.", - responseModel: Sequence::of(Participant::class), -)->stream(); - -foreach ($stream->sequence() as $update) { - // append last completed item from the sequence - // for example: - $view->appendParticipant($update->last()); -} - -// now you can get final, fully processed sequence of participants -$participants = $stream->getLastUpdate(); -// ...and for example save it to the database -$db->saveParticipants($participants->toArray()); -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/advanced/structures.mdx`: - -```mdx -## Handling dynamic data models - -If you want to define the shape of data during runtime, you can use `Structure` class. - -Structures allow you to define and modify arbitrary shape of data to be extracted by -LLM. Classes may not be the best fit for this purpose, as declaring or changing them -during execution is not possible. - -With structures, you can define custom data shapes dynamically, for example based -on the user input or context of the processing, to specify the information you need -LLM to infer from the provided text or chat messages. - - -## Defining a shape of data - -Use `Structure::define()` to define the structure and pass it to Instructor -as response model. - -If `Structure` instance has been provided as a response model, Instructor -returns an array in the shape you defined. - -`Structure::define()` accepts array of `Field` objects. - -Let's first define the structure, which is a shape of the data we want to -extract from the message. - -```php - -``` - -Following types of fields are currently supported: - -- `Field::bool()` - boolean value -- `Field::int()` - int value -- `Field::string()` - string value -- `Field::float()` - float value -- `Field::enum()` - enum value -- `Field::structure()` - for nesting structures - - -## Optional fields - -Fields can be marked as optional with `$field->optional()`. By default, all -defined fields are required. - -```php -optional(), - //... -]); -?> -``` - - -## Descriptions for guiding LLM inference - -Instructor includes field descriptions in the content of instructions for LLM, so you -can use them to provide explanations, detailed specifications or requirements for each field. - -You can also provide extra inference instructions for LLM at the structure level with `$structure->description(string $description)` - -```php -optional(), - Field::enum('role', Role::class, 'Role of the person'), -], 'A person object'); -?> -``` - -## Nesting structures - -You can use `Field::structure()` to nest structures in case you want to define -more complex data shapes. - -```php -validIf( - fn($value) => $value > 0, "Age has to be positive number" - ), - Field::structure('address', [ - Field::string('street', 'Street name')->optional(), - Field::string('city', 'City name'), - Field::string('zip', 'Zip code')->optional(), - ], 'Address of the person'), - Field::enum('role', Role::class, 'Role of the person'), -], 'A person object'); -?> -``` - -## Validation of structure data - -Instructor supports validation of structures. - -You can define field validator with: - - - `$field->validator(callable $validator)` - $validator has to return an instance of `ValidationResult` - - `$field->validIf(callable $condition, string $message)` - $condition has to return false if validation has not succeeded, $message with be provided to LLM as explanation for self-correction of the next extraction attempt - -Let's add a simple field validation to the example above: - -```php -validIf( - fn($value) => $value > 0, "Age has to be positive number" - ), - // ... -], 'A person object'); -?> -``` - -## Extracting data - -Now, let's extract the data from the message. - -```php -respond( - messages: $text, - responseModel: $structure, -); - -dump($person->toArray()); -// array [ -// "name" => "Jane Doe" -// "age" => 25 -// "address" => array [ -// "city" => "Springfield" -// ] -// "role" => "line" -// ] -?> -``` - - -## Working with `Structure` objects - -Structure object properties can be accessed using `get()` and `set()` methods, -but also directly as properties. - -```php -set('name', 'John Doe'); -$person->set('age', 30); -$person->get('role')->set('name', 'Manager'); -$person->get('role')->set('level', 1); - -// Setting properties directly -$person->name = 'John Doe'; -$person->age = 30; -$person->role->name = 'Manager'; -$person->role->level = 1; - -// Getting properties via get() -$name = $person->get('name'); -$age = $person->get('age'); -$role = $person->get('role')->get('name'); -$level = $person->get('role')->get('level'); - -// Getting properties directly -$name = $person->name; -$age = $person->age; -$role = $person->role->name; -$level = $person->role->level; -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/advanced/function_calls.mdx`: - -```mdx -## FunctionCall helper class - -Instructor offers FunctionCall class to extract arguments of a function -or method from content. - -This is useful when you want to build tool use capability, e.g. for AI -chatbots or agents. - - - -## Extracting arguments for a function - -```php -respond( - messages: $text, - responseModel: FunctionCall::fromFunctionName('saveUser'), -); - -// call the function with the extracted arguments -saveUser(...$args); -?> -``` - - - -## Extracting arguments for a method call - -```php -respond( - messages: $text, - responseModel: FunctionCall::fromMethodName(Datastore::class, 'saveUser'), -); - -// call the function with the extracted arguments -(new DataStore)->saveUser(...$args); -?> -``` - - - -## Extracting arguments for a callable - -```php -respond( - messages: $text, - responseModel: FunctionCall::fromCallable($callable), -); - -// call the function with the extracted arguments -$callable(...$args); -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/advanced/modules.mdx`: - -```mdx -## Modular processing with LLMs - -> NOTE: This is a work in progress. API is changing. Do not use for high stakes work yet. -> The documentation is not complete yet. - -Modules are a way to encapsulate structured processing logic and data flows. They are inspired by DSPy and TensorFlow modules. - -Instructor comes with a set of built-in modules, which can be used to build more complex processing pipelines. You can also create your own modules, by extending provided classes (`Module` or `DynamicModule`) or implementing interfaces (`CanProcessCall`, `HasPendingExecution`, `HasInputOutputSchema`). - -## Module anatomy - -Module consist of 3 important parts: - - - `__construct()` - constructor containing the setup of the module components and dependencies, - - `signature()` - method returning the signature of the module, which specified expected inputs and resulting output fields, - - `forward()` - method containing the processing logic, which takes the input data and returns the output data, using the module components configured in the constructor. - -## Signatures - -Signatures are a way to define the expected inputs and resulting outputs of a module. They are also used to validate the input data and to infer the output data. - -Signature of the module returned by `signature()` method can be defined in several ways. - - - you can just return a string in a form of `input1: type, input2: type -> output1: type1, output2: type2` - - you can return an instance of a class implementing `HasSignature` interface (which has `signature()` method returning the signature string) - - as an instance of `Signature` class - -String based signature is the simplest way to define the signature, but it's less flexible and may be harder to maintain, especially in more complex cases. - -`SignatureData` base class is more flexible way to define the inputs and outputs of a module, which can be useful in more complex cases. - -Extend `SignatureData` class and define the fields using `#[InputField]` and `#[OutputField]` attributes. The fields can have type hints, which are used to validate the input data. Also, `#[InputField]` and `#[OutputField]` attributes can contain instructions for LLM, specifying the inference behavior. - -## Calling the module - -Initiation of the module with the input data is done via `withArgs()` or `with()` methods. -- `withArgs()` - takes the input data fields as arguments - they have to be [named arguments](https://stitcher.io/blog/php-8-named-arguments) -- `with()` - takes the input data as an object implementing `HasInputOutputData` interface - can be used if the module has class based signature - -`withArgs()` and `with()` methods available on any `Module` class take the input data, and create -`PendingExecution` object, which is executed when you access the results via `result()` or `get()` methods. - -## Working with results - -Results of the calling the module via `with()` or `withArgs()` is an instance of `PendingExecution` object, containing the ways to access module outputs. - -`PendingExecution` object offers several methods to access the output data: - - - `result()` - returns the raw output of the module as defined by `forward()` method, - - `try()` - returns the output of the module as a `Result` object which is a wrapper around the output data, which can be used to check if the output is valid or if there are any errors before accessing the data, - - `get(string $name)` - returns the value of specified output field, - - `get()` - returns the output data as an array of key-value pairs of field name => field value. - -Additionally, `PendingExecution` object offers following methods: - - - `errors()` - returns the list of errors that occurred during the execution of the module, - - `hasErrors()` - returns `true` if there have been any errors encountered during execution of the module. - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/advanced/model_options.mdx`: - -```mdx -## Changing LLM model and options - -You can specify model and other options that will be passed to OpenAI / LLM endpoint. - -```php -respond( - messages: [['role' => 'user', 'content' => $text]], - responseModel: Person::class, - model: 'gpt-3.5-turbo', - options: [ - // custom temperature setting - 'temperature' => 0.0 - // ... other options - ], -); -``` - - -## Providing custom client - -You can pass a custom configured instance of client to the Instructor. This allows you to specify your own API key, base URI or organization. - -```php - ''], - model: 'gpt-4o-mini', - maxTokens: 128, -)); - -/// Get Instructor with the default client component overridden with your own -$instructor = (new Instructor)->withDriver($driver); - -$person = $instructor->respond( - messages: [['role' => 'user', 'content' => $text]], - responseModel: Person::class, - model: 'gpt-3.5-turbo', - options: ['temperature' => 0.0], -); -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/advanced/sequences.mdx`: - -```mdx -## Extracting Sequences of Objects - -Sequence is a wrapper class that can be used to represent a list of objects to -be extracted by Instructor from provided context. - -It is usually more convenient not create a dedicated class with a single array -property just to handle a list of objects of a given class. - -```php -respond( - messages: [['role' => 'user', 'content' => $text]], - responseModel: Sequence::of(Person::class), -); -``` - - -## Streaming Sequences - -Additional, unique feature of sequences is that they can be streamed per each -completed item in a sequence, rather than on any property update. - -> **NOTE** This feature requires the `stream` option to be set to `true`. - -To receive sequence updates provide a callback via Instructor's -`onSequenceUpdate()` that will be called each time a new item is received from LLM. - -The callback provided a full sequence that has been retrieved so far. You can -get the last added object from the sequence via `$sequence->last()`. - -Remember that while the sequence is being updated, the data is not validated - -only when the sequence is fully extracted, the objects are validated and a full -sequence is returned (see example below). - -```php -ui->appendToList($person); - // remember those objects are not validated yet -} - -$text = <<request( - messages: [['role' => 'user', 'content' => $text]], - responseModel: Sequence::of(Person::class), - options: ['stream' => true] -)->onSequenceUpdate( - fn($sequence) => updateUI($sequence->last()) // get last added object -)->get(); - -// now the list is fully extracted and validated -foreach ($list as $person) { - // do something with each person - $this->db->save($person); -} -``` - - -## Working with Sequences - -Sequences offer array access (via ArrayAccess) and convenience methods -to work with the list of extracted objects. - -```php -count(); // returns the number of extracted items -$sequence->first(); // returns the first extracted item -$sequence->last(); // returns the last extracted item -$sequence->get(1); // returns the second extracted item -$sequence->toArray(); // returns the list of extracted items as an array -``` - -## Streaming sequence updates - -See: [Streaming and partial updates](partials.md) for more information on how to get partial updates and streaming of sequences. - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/introduction.mdx`: - -```mdx ---- -title: Introduction -description: 'Structured data processing in PHP, powered by LLMs. Designed for simplicity, transparency, and control.' ---- - -Hero Light -Hero Dark - - -## What is Instructor? - -Instructor is a library that allows you to get structured, validated data from multiple types of inputs: text, -chat messages, or images. It is powered by Large Language Models (LLMs). - -The library is inspired by the [Instructor](https://jxnl.github.io/instructor/) for Python created by [Jason Liu](https://twitter.com/jxnlco). - - - - -## Learn More... - - - - Check how to set up Instructor in your project and start processing data with LLMs - - - - Read more about basic concepts behind Instructor - - - - Learn Instructor features and capabilities - - - - Browse examples to see Instructor in action and find out how to use it in your projects - - - - Deep dive into Instructor internals and low level mechanisms - - - - - - - - -## Feature Highlights - -Instructor is designed to make it easy to process data with LLMs in PHP. Here are some of the key features of the library: - -### Core features - -- Get structured responses from LLM inference -- Validation of returned data -- Automated retries in case of errors when LLM responds with invalid data - -### Flexible inputs - -- Process various types of input data: text, series of chat messages or images -- 'Structured-to-structured' processing - provide object or array as an input and get object with the results of inference back -- Demonstrate examples to improve the quality of inference - -### Customizable outputs - -- Define response data model the way to need: type-hinted classes, JSON Schema arrays, or dynamically define your data shapes with Structures -- Customize prompts and retry prompts -- Use attributes or PHP DocBlocks to provide additional instructions for LLM -- Customize response model processing by providing your own implementation of schema, deserialization, validation and transformation interfaces - -### Sync and streaming support - -- Receive synchronous or streaming responses -- Get partial updates & stream completed sequence items - -### Observability - -- Get detailed insight into internal processing via events - -### Support for multiple LLMs / API providers - -- Use multiple LLM API providers (incl. OpenAI, Anthropic, Cohere, Azure, Groq, Mistral, Fireworks AI, Ollama, OpenRouter, Together AI) -- Use local models with Ollama - -### Documentation and examples - -- Learn more from growing documentation and 50+ cookbooks - - - - -## Instructor in Other Languages - -Instructor has been implemented in various technology stacks. Check out implementations in other languages below: - -- [Python](https://www.github.com/jxnl/instructor) (original) -- [Javascript](https://github.com/instructor-ai/instructor-js) (port) -- [Elixir](https://github.com/thmsmlr/instructor_ex/) (port) -- [Ruby](https://ruby.useinstructor.com/) (port) -- [Go](https://go.useinstructor.com/) (port) - -If you want to port Instructor to another language, please reach out to us on [Twitter](https://twitter.com/jxnlco) we'd love to help you get started! - - - - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/concepts/why.mdx`: - -```mdx ---- -title: Why use Instructor? -description: 'Key capabilities of Instructor' ---- - - -Our library introduces three key enhancements: - -- **Response Model:** Specify a data model to be returned by LLM to simplify your code. -- **Validation:** Automatically validate response generated by LLM before you start using it. -- **Max Retries:** Automated retry attempts for invalid responses. - - -## A Glimpse into Instructor's Capabilities - -With Instructor, your code becomes more efficient and readable. Here’s a quick peek. - - -### Understanding the workflow - -Let's see how we can leverage it to make use of instructor - - -#### Step 1: Define the data model - -Create a data model to define the structure of the data you want to extract. This model will map directly to the information in the prompt. - -```php -respond( - messages: [["role": "user", "content": "Extract Jason is 25 years old"]], - responseModel: UserDetail::class, - model: "gpt-3.5-turbo", -); - -assert($user->name == "Jason") -assert($user->age == 25) -``` - -It's helpful to annotate the variable with the type of the response model, which will help your IDE provide autocomplete and spell check. - - - - -## Understanding Validation - -Validation can also be plugged into the same data model. If the response triggers any validation rules Instructor will raise a validation error. - - -### Self Correcting on Validation Error - -Here, the `LeadReport` model is passed as the `$responseModel`, and `$maxRetries` is set to 2. It means that if the extracted data does not match the model, Instructor will re-ask the model 2 times before giving up. - -```php -use Cognesy\Instructor\Instructor; -use Symfony\Component\Validator\Constraints as Assert; - -class UserDetails -{ - public string $name; - #[Assert\Email] - public string $email; -} - -$user = (new Instructor)->respond( - messages: [['role' => 'user', 'content' => "you can reply to me via jason@gmailcom -- Jason"]], - responseModel: UserDetails::class, - maxRetries: 2 -); - -assert($user->email === "jason@gmail.com"); -``` - -!!! note "More about Validation" - - Check out Jason's blog post [Good LLM validation is just good validation](https://jxnl.github.io/instructor/blog/2023/10/23/good-llm-validation-is-just-good-validation/) - - -### Custom Validators - -Instructor uses Symfony validation component to validate extracted data. You can use #[Assert/Callback] annotation to build fully customized validation logic. - -See [Symfony docs](https://symfony.com/doc/current/reference/constraints/Callback.html) for more details on how to use Callback constraint. - -```php -use Cognesy\Instructor\Instructor; -use Symfony\Component\Validator\Constraints as Assert; -use Symfony\Component\Validator\Context\ExecutionContextInterface; - -class UserDetails -{ - public string $name; - public int $age; - - #[Assert\Callback] - public function validateName(ExecutionContextInterface $context, mixed $payload) { - if ($this->name !== strtoupper($this->name)) { - $context->buildViolation("Name must be in uppercase.") - ->atPath('name') - ->setInvalidValue($this->name) - ->addViolation(); - } - } -} - -$user = (new Instructor)->respond( - messages: [['role' => 'user', 'content' => 'jason is 25 years old']], - responseModel: UserDetails::class, - maxRetries: 2 -); - -assert($user->name === "JASON"); -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/concepts/overview.mdx`: - -```mdx -## What is Instructor? - -Instructor is a library that allows you to get structured, validated data from multiple types of inputs: text, - chat messages, or images. It is powered by Large Language Models (LLMs). - -The library is inspired by the [Instructor](https://jxnl.github.io/instructor/) for Python created by [Jason Liu](https://twitter.com/jxnlco). - - - -## How it works - -Instructor uses Large Language Models (LLMs) to process data and return structured information you can easily use in your code. - -![image](../images/concept.png) - - - -## Instructor in action - -Here's a simple CLI demo app using Instructor to extract structured data from text: - -![image](../images/extraction.gif) - - - - -## How Instructor Enhances Your Workflow - -Instructor introduces three key enhancements compared to direct API usage. - -### Response Model - -You just specify a PHP class to extract data into via the 'magic' of LLM chat completion. And that's it. - -Instructor reduces brittleness of the code extracting the information from textual data by leveraging structured LLM responses. - -Instructor helps you write simpler, easier to understand code - you no longer have to define lengthy function call definitions or write code for assigning returned JSON into target data objects. - -### Validation - -Response model generated by LLM can be automatically validated, following set of rules. Currently, Instructor supports only Symfony validation. - -You can also provide a context object to use enhanced validator capabilities. - -### Max Retries - -You can set the number of retry attempts for requests. - -Instructor will repeat requests in case of validation or deserialization error up to the specified number of times, trying to get a valid response from LLM. - - - - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/internals/response_models.mdx`: - -```mdx ---- -title: 'Response Models' -description: 'Response model defines the structure of results returned by LLM.' ---- - -Instructor's request parameter `responseModel` allows you to specify shape of the response you expect from LLM . - -Instructor translates the `responseModel` parameter into actual schema based on the type and value of the parameter. - - -### Handling string $responseModel value - -If `string` value is provided, it is used as a name of the class of the response model. - -Instructor checks if the class exists and analyzes the class & properties type information & doc comments to generate a schema needed to specify LLM response constraints. - -The best way to provide the name of the response model class is to use `NameOfTheClass::class`, making it easy for IDE to check the type, handle refactorings, etc. - - -### Handling object $responseModel value - -If `object` value is provided, it is considered an instance of the response model. Instructor checks the class of the instance, then analyzes it and its property type data to specify LLM response constraints. - - -### Handling array $responseModel value - -If `array` value is provided, it is considered a raw JSON Schema, therefore allowing Instructor to use it directly in LLM requests (after wrapping in appropriate context - e.g. function call). - -Instructor requires information on the class of each nested object in your JSON Schema, so it can correctly deserialize the data into appropriate type. - -This information is available to Instructor when you are passing $responseModel as a class name or an instance, but it is missing from raw JSON Schema. Lack of the information on target class makes it impossible for Instructor to deserialize the data into appropriate, expected type. - -Current design uses JSON Schema `$comment` field on property to overcome this information gap. Instructor expects developer to use `$comment` field to provide fully qualified name of the target class to be used to deserialize property data of object or enum type. - - -## Custom response handling strategy - -Instructor allows you to customize processing of `$responseModel` value also by looking at the interfaces the class or instance implements: - - - `CanProvideJsonSchema` - implement to be able to provide raw JSON Schema (as an array) of the response model, overriding the default approach of Instructor, which is analyzing $responseModel value class information, - - `CanProvideSchema` - implement to be able to provide `Schema` object of the response model, overriding class analysis stage; can be useful in building object wrappers (see: `Sequence` class), - - `CanDeserializeSelf` - implement to customize the way the response from LLM is deserialized from JSON into PHP object, - - `CanValidateSelf` - implement to customize the way the deserialized object is validated - it fully replaces the default validation process for given response model, - - `CanTransformSelf` - implement to transform the validated object into any target value that will be then passed back to the caller (e.g. unwrap simple type from a class to scalar value) - -Methods implemented by those interfaces are executed as following: - - - `CanProvideJsonSchema` - executed during the schema generation phase, - - `CanDeserializeSelf` - executed during the deserialization phase, - - `CanValidateSelf` - executed during the validation phase, - - `CanTransformSelf` - executed during the transformation phase. - -When implementing custom response handling strategy, avoid doing all transformations in a single block of code. Split the logic between relevant methods implemented by your class for clarity and easier code maintenance. - - -#### Example implementations - -For a practical example of using those contracts to customize Instructor processing flow see: - - - src/Extras/Scalar/ - - src/Extras/Sequence/ - -Examples contain an implementation of custom response model handling strategies, e.g. providing scalar value support via a wrapper class implementing: - - custom schema provider, - - deserialization, - - validation - - and transformation - -into requested value type. - - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/internals/instructor.mdx`: - -```mdx -## `Instructor` class - -`Instructor` class is the main entry point to the library. It is responsible for -handling all interactions with the client code and internal Instructor components. - - -## Request handlers - -One of the essential tasks of the `Instructor` class is to read the configuration -and use it to retrieve a component implementing `CanHandleRequest` interface (specified in the configuration) to process the request and return the response. - - -## Dispatched events - -`Instructor` class dispatches several high level events during initialization and processing -of the request and response: - - - `InstructorStarted` - dispatched when Instructor is created - - `InstructorReady` - dispatched when Instructor is configured and ready to process the request - - `RequestReceived` - dispatched when the request is received - - `ResponseGenerated` - dispatched when the response is generated - - `ErrorRaised` - dispatched when an uncaught error occurs - - -## Event listeners - -`Instructor` class provides several methods allowing client code to plug -into Instructor event system, including: - - `onEvent()` - to receive a callback when specified type of event is dispatched - - `wiretap()` - to receive any event dispatched by Instructor - - -## Response model updates - -Additionally, `Instructor` class provides convenience methods allowing client code to -receive model updates when streaming is enabled: - - - `onPartialUpdate()` - to handle partial model updates of the response - - `onSequenceUpdate()` - to handle partial sequence updates of the response - - -## Error handling - -`Instructor` class contains top level try-catch block to let user handle any uncaught -errors before throwing them back to the client code. It allows you to register a handler -which will log the error or notify your monitoring system about a problem. - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/internals/events.mdx`: - -```mdx -## Event classes - -Instructor dispatches multiple classes of events during its execution. All of them are descendants of `Event` class. - -You can listen to these events and react to them in your application, for example to log information or to monitor the execution process. - -Check the list of available event classes in the `Cognesy\Instructor\Events` namespace. - -## Event methods - -Each Instructor event offers following methods, which make interacting with them more convenient: - - * `print()` - prints a string representation of the event to console output - * `printDebug()` - prints a string representation of the event to console output, with additional debug information - * `asConsole()` - returns the event in a format suitable for console output - * `asLog()` - returns the event in a format suitable for logging - - -## Receiving notification on internal events - -Instructor allows you to receive detailed information at every stage of request and response processing via events. - - * `(new Instructor)->onEvent(string $class, callable $callback)` method - receive callback when specified type of event is dispatched - * `(new Instructor)->wiretap(callable $callback)` method - receive any event dispatched by Instructor, may be useful for debugging or performance analysis - -Receiving events can help you to monitor the execution process and makes it easier for a developer to understand and resolve any processing issues. - -```php -$instructor = (new Instructor) - // see requests to LLM - ->onEvent(RequestSentToLLM::class, fn($e) => dump($e)) - // see responses from LLM - ->onEvent(ResponseReceivedFromLLM::class, fn($event) => dump($event)) - // see all events in console-friendly format - ->wiretap(fn($event) => $event->print()) - // log all events in log-friendly format - ->wiretap(fn($event) => YourLogger::log($event->asLog())) - -$instructor->respond( - messages: "What is the population of Paris?", - responseModel: Scalar::integer(), -); -// check your console for the details on the Instructor execution -``` - - -## Convenience methods for get streamed model updates - -`Instructor` class provides convenience methods allowing client code to receive -model updates when streaming is enabled: - - * `onPartialUpdate(callable $callback)` - to handle partial model updates of the response - * `onSequenceUpdate(callable $callback)` - to handle partial sequence updates of the response - -In both cases your callback will receive updated model, so you don't have to -extract it from the event. - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/internals/script.mdx`: - -```mdx -// TODO: generated - needs revision - -## `Script` Class - -### Overview - -The Script class serves as a flexible and dynamic container for managing, hydrating, and rendering sets of message sequences before they are translated to the native format of any supported LLM API. - - -### Key Concepts - - - Script: The main container holding one or more Sections. - - Section: Contain sets of OpenAI-style messages exchanged between the user and the AI assistant. - - Message: Represent individual message entries with a role (user or assistant) and content (which can be a string or an object). - - -### Features - - - Flexibility: Adapt and manage conversation structures for different LLM API providers. - - Dynamic Reordering: Rearrange sections dynamically as needed. - - Template Functionality: Use Script as a template for complex, multi-turn prompts with dynamic parameters. - - Multimodal Support: Include objects like images in chat entries for processing using LLM's multimodal capabilities. - - -### Basic Usage - -#### Creating a Script - -```php -hasSection('introduction'); - -// Retrieve a section by name -$introSection = $script->section('introduction'); - -// Get all section names -$sectionNames = $script->sectionNames(); -``` - -#### Converting to Messages - -```php -toMessages(); -``` - - -### Advanced Usage - -#### Dynamic Parameters Injection - -```php - 'John Doe']); -$script->withParameters($parameters); - -// Set individual parameter -$script->setParameter('userRole', 'admin'); - -// Unset a parameter -$script->unsetParameter('userRole'); -``` - - -#### Using Templates within Sections - -```php -appendMessages(Messages::fromString('Hello, {{userName}}!')); - -// Add section to Script and set parameters -$script->appendSection($templateSection); -$script->setParameter('userName', 'John Doe'); - -// Convert Script to Messages with template rendered -$messages = $script->toMessages(); -?> -``` - -### Customization - -#### Creating Script from an Array - -```php - 'Welcome to the session.', - 'qna' => ['What is your name?', 'My name is AI.'] -]; - -// Create Script from array -$scriptFromArray = (new Script)->fromArray($sectionsArray); -``` - -#### Merging and Overriding Scripts - -```php -mergeScript($anotherScript); - -// Override the current Script with another one -$script->overrideScript($anotherScript); -``` - -#### Reordering Sections - -```php -reorder(['qna', 'introduction']); - -// Reverse the order of sections -$reversedScript = $script->reverse(); -``` - -### Internal Mechanics - -The Script class uses several internal traits to handle various aspects of its functionality: - - - HandlesAccess: Methods for accessing and querying sections. - - HandlesParameters: Methods for managing parameters. - - HandlesConversion: Methods for converting Script to various formats. - - HandlesCreation: Methods for creating Script instances from arrays or other Scripts. - - HandlesMutation: Methods for mutating sections and parameters. - - HandlesReordering: Methods for reordering and reversing sections. - - HandlesTransformation: Methods for transforming Scripts into different structures. - -#### Example: Complex Conversation Template - -```php -appendMessages(Messages::fromString('Hello, welcome to our service.')); -$qna = (new Section('qna')) - ->appendMessages(Messages::fromString('What is your name?')) - ->appendMessages(Messages::fromString('My name is <|userName|>.')); - -// Create Script -$script = new Script($intro, $qna); - -// Set dynamic parameters -$parameters = new ScriptParameters(['userName' => 'Alice']); -$script->withParams($parameters); - -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/internals/debugging.mdx`: - -```mdx -Instructor offers several ways to debug it's internal state and execution flow. - -## Events - -Instructor emits events at various points in its lifecycle, which you can listen to -and react to. You can use these events to debug execution flow and to inspect -data at various stages of processing. - -For more details see the [Events](events.mdx) section. - - -## HTTP Debugging - -The `Instructor` class has a `withDebug()` method that can be used to debug the request and response. - -```php -$result = (new Instructor)->withDebug()->respond( - messages: "Jason is 25 years old", - responseModel: User:class, -); -``` - -It displays detailed information about the request being sent to LLM API and response received from it, -including: - - - request headers, URI, method and body, - - response status, headers, and body. - -This is useful for debugging the request and response when you are not getting the expected results. - - -## Debugging SaloonPHP requests - -You can also directly access Saloon connector instance via `connector()` method -on the client instance, and call Saloon debugging methods on it - see SaloonPHP -debugging documentation for more details: -https://docs.saloon.dev/the-basics/debugging - -Additionally, `connector()` method on the client instance allows you to access -other capabilities of Saloon connector, such as setting or modifying middleware. -See SaloonPHP documentation for more details: -https://docs.saloon.dev/digging-deeper/middleware - - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/internals/http.mdx`: - -```mdx ---- -title: 'HTTP connectivity layer' -description: 'Learn more about HTTP connectivity layer in Instructor.' ---- - -## HTTP Connectivity via SaloonPHP - -Instructor uses [SaloonPHP](https://saloon.dev) as its HTTP connectivity layer. - -SaloonPHP is a lightweight HTTP client library that provides a simple API for -making HTTP API requests and handling responses. It is designed to be easy to use -and flexible, with a focus on performance and reliability. - -SaloonPHP is built on top of the popular [Guzzle](https://docs.guzzlephp.org/) - - -## Accessing SaloonPHP connector - -You can directly access Saloon connector instance via `connector()` method -on the client instance, and call Saloon debugging methods on it - see SaloonPHP -debugging documentation for more details: -https://docs.saloon.dev/the-basics/debugging - - -## Request and response middleware - -Additionally, `connector()` method on the client instance allows you to access -other capabilities of Saloon connector, such as adding middleware via `onRequest()` -or `onResponse()` methods on the Connector instance returned by `connector()`. -See SaloonPHP documentation for more details: -https://docs.saloon.dev/digging-deeper/middleware - - -## Custom connectors - -Instructor allows you to provide custom connector to the client instance via -`withConnector()` method on the API client instance. Provided connector must -inherit from `Cognesy\Instructor\ApiClient\ApiConnector`. - - -## Customizing HTTP client used by SaloonPHP - -SaloonPHP uses Guzzle as its HTTP client by default. You can customize HTTP -sender used by SaloonPHP by providing custom `senderClass` parameter to your connector -constructor, which should be a class name implementing Saloon's `Sender` contract: -https://github.com/saloonphp/saloon/blob/v3/src/Contracts/Sender.php - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/internals/lifecycle.mdx`: - -```mdx -## Instructor's request lifecycle - -As Instructor for PHP processes your request, it goes through several stages: - -1. Initialize and self-configure (with possible overrides defined by developer). -2. Analyze classes and properties of the response data model specified by developer. -3. Translate data model into a schema that can be provided to LLM. -4. Execute request to LLM using specified messages (content) and response model metadata. -5. Receive a response from LLM or multiple partial responses (if streaming is enabled). -6. Deserialize response received from LLM into originally requested classes and their properties. -7. In case response contained unserializable data - create feedback message for LLM and request regeneration of the response. -8. Execute validations defined by developer on the deserialized data - if any of them fail, create feedback message for LLM and requests regeneration of the response. -9. Repeat the steps 4-8, unless specified limit of retries has been reached or response passes validation. - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/essentials/data_model.mdx`: - -```mdx - -Instructor provides several ways the data model of LLM response. - - -## Using classes - -The default way is to use PHP classes to define the data model. You can also use PHPDoc comments to specify the types of fields of the response. -Additionally, you can use attributes to provide more context to the language model or to provide additional instructions to the model. - - - -## Type Hints - -Use PHP type hints to specify the type of extracted data. - -> Use nullable types to indicate that given field is optional. - -```php -respond( - messages: [['role' => 'user', 'content' => $text]], - responseModel: Person::class, -); // client is passed explicitly, can specify e.g. different base URL - -// data is extracted into an object of given class -assert($person instanceof Person); // true - -// you can access object's extracted property values -echo $person->name; // Alex -echo $person->age; // 25 -echo $person->profession; // software engineer -echo $person->skills[0]->name; // PHP -echo $person->skills[0]->type; // SkillType::Technical -// ... - -var_dump($person); -// Person { -// name: "Alex", -// age: 25, -// profession: "software engineer", -// skills: [ -// Skill { -// name: "PHP", -// type: SkillType::Technical, -// }, -// Skill { -// name: "Python", -// type: SkillType::Technical, -// }, -// Skill { -// name: "guitar", -// type: SkillType::Other -// }, -// ] -// } -``` - - -## Dynamic data schemas with `Structure` class - -In case you work with dynamic data schemas, you can use `Structure` class to define the data model. - -See [Structures](/advanced/structures) for more details on how to work with dynamic data schemas. - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/essentials/demonstrations.mdx`: - -```mdx -## Providing examples to LLM - -To improve the results of LLM inference you can provide examples of the expected output. -This will help LLM to understand the context and the expected structure of the output. - -It is typically useful in the `Mode::Json` and `Mode::MdJson` modes, where the output -is expected to be a JSON object. - -Instructor's `request()` method accepts an array of examples as the `examples` parameter, -where each example is an instance of the `Example` class. - - -## `Example` class - -`Example` constructor have two main arguments: `input` and `output`. - -The `input` property is a string which describes the input message, while he `output` -property is an array which represents the expected output. - -Instructor will append the list of examples to the prompt sent to LLM, with output -array data rendered as JSON text. - -```php -respond( - messages: "Our user Jason is 25 years old.", - responseModel: User::class, - examples: [ - new Example( - input: "John is 50 and works as a teacher.", - output: ['name' => 'John', 'age' => 50] - ), - new Example( - input: "We have recently hired Ian, who is 27 years old.", - output: ['name' => 'Ian', 'age' => 27] - ), - ], - mode: Mode::Json -); -?> -``` - -## Modifying the example template - -You can use a template string as an input for the Example class. The template string -may contain placeholders for the input data, which will be replaced with the actual -values during the execution. - -Currently, the following placeholders are supported: - - `{input}` - replaced with the actual input message - - `{output}` - replaced with the actual output data - -In case input or output data is an array, Instructor will automatically convert it to -a JSON string before replacing the placeholders. - -```php -$user = (new Instructor)->respond( - messages: "Our user Jason is 25 years old.", - responseModel: User::class, - examples: [ - new Example( - input: "John is 50 and works as a teacher.", - output: ['name' => 'John', 'age' => 50], - template: "EXAMPLE:\n{input} => {output}\n", - ), - ], - mode: Mode::Json -); -``` - - -## Convenience factory methods - -You can also create Example instances using the `fromText()`, `fromChat()`, `fromData()` -helper static methods. All of them accept $output as an array of the expected output data -and differ in the way the input data is provided. - -### Make example from text - -`Example::fromText()` method accepts a string as an input. It is equivalent to creating -an instance of Example using the constructor. - -```php -$example = Example::fromText( - input: 'Ian is 27 yo', - output: ['name' => 'Ian', 'age' => 27] -); -``` - -### Make example from chat - -`Example::fromChat()` method accepts an array of messages, which may be useful when -you want to use a chat or chat fragment as a demonstration of the input. - -```php -$example = Example::fromChat( - input: [['role' => 'user', 'content' => 'Ian is 27 yo']], - output: ['name' => 'Ian', 'age' => 27] -); -``` - -### Make example from data - -`Example::fromData()` method accepts any data type and uses the `Json::encode()` method to -convert it to a string. It may be useful to provide a complex data structure as an example -input. - -```php -$example = Example::fromData( - input: ['firstName' => 'Ian', 'lastName' => 'Brown', 'birthData' => '1994-01-01'], - output: ['name' => 'Ian', 'age' => 27] -); -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/essentials/usage.mdx`: - -```mdx -## Basic usage - -This is a simple example demonstrating how Instructor retrieves structured information from provided text (or chat message sequence). - -Response model class is a plain PHP class with typehints specifying the types of fields of the object. - -> NOTE: By default, Instructor looks for OPENAI_API_KEY environment variable to get -> your API key. You can also provide the API key explicitly when creating the -> Instructor instance. - -```php -respond( - messages: [['role' => 'user', 'content' => $text]], - responseModel: Person::class, -); - -// Step 4: Work with structured response data -assert($person instanceof Person); // true -assert($person->name === 'Jason'); // true -assert($person->age === 28); // true - -echo $person->name; // Jason -echo $person->age; // 28 - -var_dump($person); -// Person { -// name: "Jason", -// age: 28 -// } -?> -``` - -!!! note - - Currently, Instructor for PHP only supports classes / objects as response models. In case you want to extract simple types or arrays, you need to wrap them in a class (or use `Scalar` helper class). - - - -## String as Input - -You can provide a string instead of an array of messages. This is useful when you want to extract data from a single block of text and want to keep your code simple. - -```php -respond( - messages: "His name is Jason, he is 28 years old.", - responseModel: Person::class, -); -?> -``` - - -## Alternative way to get results - -You can call `request()` method to initiate Instructor with request data -and then call `get()` to get the response. - -```php -request( - messages: "His name is Jason, he is 28 years old.", - responseModel: Person::class, -); - -$person = $instructor->get(); -?> -``` - - -### Structured-to-structured data processing - -Instructor offers a way to use structured data as an input. This is -useful when you want to use object data as input and get another object -with a result of LLM inference. - -The `input` field of Instructor's `respond()` and `request()` methods -can be an object, but also an array or just a string. - -```php -respond( - input: $email, - responseModel: Email::class, - prompt: 'Translate the text fields of email to Spanish. Keep other fields unchanged.', -); -?> -``` - - - -## Streaming support - -Instructor supports streaming of partial results, allowing you to start -processing the data as soon as it is available. - -```php -request( - messages: "His name is Jason, he is 28 years old.", - responseModel: Person::class, - options: ['stream' => true] -)->stream(); - -foreach ($stream->partials() as $partialPerson) { - // process partial person data - echo "Name: " $partialPerson->name ?? '...'; - echo "Age: " $partialPerson->age ?? '...'; -} - -// after streaming is done you can get the final, fully processed person object... -$person = $stream->getLastUpdate() -// ...to, for example, save it to the database -$db->save($person); -?> -``` - - -## Scalar responses - -See [Scalar responses](scalars.mdx) for more information on how to generate scalar responses with `Scalar` adapter class. - - -## Partial responses and streaming - -See [Streaming and partial updates](partials.md) for more information on how to work with partial updates and streaming. - - -## Extracting arguments for function call - -See [FunctionCall helper class](function_calls.md) for more information on how to extract arguments for callable objects. - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/essentials/scalars.mdx`: - -```mdx -## Extracting Scalar Values - -Sometimes we just want to get quick results without defining a class for the response model, especially if we're trying to get a straight, simple answer in a form of string, integer, boolean or float. Instructor provides a simplified API for such cases. - -```php -respond( - messages: "His name is Jason, he is 28 years old.", - responseModel: Scalar::integer('age'), -); - -var_dump($value); -// int(28) -``` - -In this example, we're extracting a single integer value from the text. You can also use `Scalar::string()`, `Scalar::boolean()` and `Scalar::float()` to extract other types of values. - -Additionally, you can use Scalar adapter to extract enums via `Scalar::enum()`. - - -## Examples - -### String result - -```php -respond( - messages: "His name is Jason, he is 28 years old.", - responseModel: Scalar::string(name: 'firstName'), -); -// expect($value)->toBeString(); -// expect($value)->toBe("Jason"); -``` - -### Integer result - -```php -respond( - messages: "His name is Jason, he is 28 years old.", - responseModel: Scalar::integer('age'), -); -// expect($value)->toBeInt(); -// expect($value)->toBe(28); -``` - -### Boolean result - -```php -respond( - messages: "His name is Jason, he is 28 years old.", - responseModel: Scalar::boolean(name: 'isAdult'), -); -// expect($value)->toBeBool(); -// expect($value)->toBe(true); -``` - -### Float result - -```php -respond( - messages: "His name is Jason, he is 28 years old and his 100m sprint record is 11.6 seconds.", - responseModel: Scalar::float(name: 'recordTime'), -); -// expect($value)->toBeFloat(); -// expect($value)->toBe(11.6); -``` - -### Enum result / select one of the options - -```php -respond( - messages: [ - ['role' => 'system', 'content' => $text], - ['role' => 'user', 'content' => 'What is Jason\'s citizenship?'], - ], - responseModel: Scalar::enum(CitizenshipGroup::class, name: 'citizenshipGroup'), -); -// expect($value)->toBeString(); -// expect($value)->toBe('other'); -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/essentials/modes.mdx`: - -```mdx -## Extraction modes - -Instructor supports several ways to extract data from the response. The default mode is `Mode::Tools`, which leverages OpenAI-style tool calls. - -Mode can be set via parameter of `Instructor::response()` or `Instructor::request()` -methods. - -```php -respond( - messages: "...", - responseModel: ..., - ..., - mode: Mode::Json -); -``` -Mode can be also set via `request()` method. - -```php -request( - messages: "...", - responseModel: ..., - ..., - mode: Mode::Json -)->get(); -``` - -## Modes - -### `Mode::Tools` - -This mode is the default one. It uses OpenAI tools to extract data from the -response. - -It is the most reliable mode, but not all models and API providers support it - -check their documentation for more information. - - - https://platform.openai.com/docs/guides/function-calling - - https://docs.anthropic.com/en/docs/build-with-claude/tool-use - - https://docs.mistral.ai/capabilities/function_calling/ - - -### `Mode::Json` - -In this mode Instructor provides response format as JSONSchema and asks LLM -to respond with JSON object following provided schema. - -It is supported by many open source models and API providers - check their -documentation. - -See more about JSON mode in: - - - https://platform.openai.com/docs/guides/text-generation/json-mode - - https://docs.anthropic.com/en/docs/test-and-evaluate/strengthen-guardrails/increase-consistency - - https://docs.mistral.ai/capabilities/json_mode/ - - -### `Mode::JsonSchema` - -In contrast to `Mode::Json` which may not always manage to meet the schema requirements, -`Mode::JsonSchema` is strict and guarantees the response to be a valid JSON object that matches -the provided schema. - -It is currently supported only by new OpenAI models (check their docs for details). - -NOTE: OpenAI JsonSchema mode does not support optional properties. If you need to have optional -properties in your schema, use `Mode::Tools` or `Mode::Json`. - -See more about JSONSchema mode in: - - - https://platform.openai.com/docs/guides/structured-outputs - - -### `Mode::MdJson` - -In this mode Instructor asks LLM to answer with JSON object following provided schema and -return answer as Markdown codeblock. - -It may improve the results for LLMs that have not been finetuned to respond with JSON -as they are likely to be already trained on large amounts of programming docs and have -seen a lot of properly formatted JSON objects within MD codeblocks. - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/essentials/validation.mdx`: - -```mdx -## Basic validation - -Instructor validates results of LLM response against validation rules specified in your data model. - -```php -respond( - messages: $text, - responseModel: Person::class, -); - -// if the resulting object does not validate, Instructor throws an exception -``` - -> NOTE: For further details on available validation rules, check [Symfony Validation constraints](https://symfony.com/doc/current/validation.html#constraints). - - -## Max Retries - -In case maxRetries parameter is provided and LLM response does not meet validation criteria, Instructor will make subsequent inference attempts until results meet the requirements or maxRetries is reached. - -Instructor uses validation errors to inform LLM on the problems identified in the response, so that LLM can try self-correcting in the next attempt. - -```php -respond( - messages: $text, - responseModel: Person::class, - maxRetries: 3, -); - -// if all LLM's attempts to self-correct the results fail, Instructor throws an exception -``` - -## Custom Validation - -You can easily add custom validation code to your response model by using ```ValidationTrait``` -and defining validation logic in ```validate()``` method. - -```php -name === strtoupper($this->name)) { - return []; - } - return [[ - 'message' => "Name must be in uppercase.", - 'path' => 'name', - 'value' => $this->name - ]]; - } -} - -$user = (new Instructor)->respond( - messages: [['role' => 'user', 'content' => 'jason is 25 years old']], - responseModel: UserDetails::class, - maxRetries: 2 -); - -assert($user->name === "JASON"); -``` - -Note that method ```validate()``` has to return: - * an **empty array** if the object is valid, - * or an array of validation violations. - -This information will be used by LLM to make subsequent attempts to correct the response. - -```php -$violations = [ - [ - 'message' => "Error message with violation details.", - 'path' => 'path.to.property', - 'value' => '' // invalid value - ], - // ...other violations -]; -``` - - -## Custom Validation via Symfony `#[Assert/Callback]` - -Instructor uses Symfony validation component to validate extracted data. - -You can use ```#[Assert/Callback]``` annotation to build fully customized validation logic. - -```php -name !== strtoupper($this->name)) { - $context->buildViolation("Name must be in uppercase.") - ->atPath('name') - ->setInvalidValue($this->name) - ->addViolation(); - } - } -} - -$user = (new Instructor)->respond( - messages: [['role' => 'user', 'content' => 'jason is 25 years old']], - responseModel: UserDetails::class, - maxRetries: 2 -); - -assert($user->name === "JASON"); -``` - -> NOTE: See [Symfony docs](https://symfony.com/doc/current/reference/constraints/Callback.html) for more details on how to use Callback constraint. - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/essentials/installation.mdx`: - -```mdx ---- -title: Installation -description: 'Installing Instructor via Composer' ---- - -Run following command in your terminal, and you're on your way to a smoother data handling experience! - -```bash -composer install cognesy/instructor-php -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/essentials/prompts.mdx`: - -```mdx -## Customizing prompts - -In case you want to take control over the prompts sent by Instructor -to LLM for different modes, you can use the `prompt` parameter in the -`request()` or `respond()` methods. - -It will override the default Instructor prompts, allowing you to fully -customize how LLM is instructed to process the input. - - -## Prompting models with tool calling support - -`Mode::Tools` is usually most reliable way to get structured outputs following -provided response schema. - -`Mode::Tools` can make use of `$toolName` and `$toolDescription` parameters -to provide additional semantic context to the LLM, describing the tool to be used -for processing the input. `Mode::Json` and `Mode::MdJson` ignore these parameters, -as tools are not used in these modes. - -```php -request( - messages: "Our user Jason is 25 years old.", - responseModel: User::class, - prompt: "\nYour task is to extract correct and accurate data from the messages using provided tools.\n", - toolName: 'extract', - toolDescription: 'Extract information from provided content', - mode: Mode::Tools) - ->get(); -``` - - -## Prompting models supporting JSON output - -Aside from tool calling Instructor supports two other modes for getting structured -outputs from LLM: `Mode::Json` and `Mode::MdJson`. - -`Mode::Json` uses JSON mode offered by some models and API providers to get LLM -respond in JSON format rather than plain text. - -```php -respond( - messages: "Our user Jason is 25 years old.", - responseModel: User::class, - prompt: "\nYour task is to respond correctly with JSON object.", - mode: Mode::Json -); -``` -Note that various models and API providers have specific requirements -on the input format, e.g. for OpenAI JSON mode you are required to include -`JSON` string in the prompt. - - -## Including JSON Schema in the prompt - -Instructor takes care of automatically setting the `response_format` -parameter, but this may not be sufficient for some models or providers - -some of them require specifying JSON response format as part of the -prompt, rather than just as `response_format` parameter in the request -(e.g. OpenAI). - -For this reason, when using Instructor's `Mode::Json` and `Mode::MdJson` -you should include the expected JSON Schema in the prompt. Otherwise, the -response is unlikely to match your target model, making it impossible for -Instructor to deserialize it correctly. - -```php - "object", - "properties" => [ - "name" => ["type" => "string"], - "age" => ["type" => "integer"] - ], - "required" => ["name", "age"] -]); - -$user = $instructor - ->request( - messages: "Our user Jason is 25 years old.", - responseModel: User::class, - prompt: "\nYour task is to respond correctly with JSON object. Response must follow JSONSchema: $jsonSchema\n", - mode: Mode::Json) - ->get(); -``` - -The example above demonstrates how to manually create JSON Schema, but -**with Instructor you do not have to build the schema manually** - you can use prompt -template placeholder syntax to use Instructor-generated JSON Schema. - - -## Prompt as template - -Instructor allows you to use a template string as a prompt. You can use -`<|variable|>` placeholders in the template string, which will be replaced -with the actual values during the execution. - -Currently, the following placeholders are supported: - - `<|json_schema|>` - replaced with the JSON Schema for current response model - -Example below demonstrates how to use a template string as a prompt: - -```php -request( - messages: "Our user Jason is 25 years old.", - responseModel: User::class, - prompt: "\nYour task is to respond correctly with JSON object. Response must follow JSONSchema:\n<|json_schema|>\n", - mode: Mode::Json) - ->get(); -``` - - -## Prompting the models with no support for tool calling or JSON output - -`Mode::MdJson` is the most basic (and least reliable) way to get structured -outputs from LLM. Still, you may want to use it with the models which do not -support tool calling or JSON output. - -`Mode::MdJson` relies on the prompting to get LLM response in JSON formatted data. - -Many models prompted in this mode will respond with a mixture of plain text and JSON -data. Instructor will try to find JSON data fragment in the response and ignore -the rest of the text. - -This approach is most prone to deserialization and validation errors and needs -providing JSON Schema in the prompt to increase the probability that the response -is correctly structured and contains the expected data. - -```php -request( - messages: "Our user Jason is 25 years old.", - responseModel: User::class, - prompt: "\nYour task is to respond correctly with strict JSON object containing extracted data within a ```json {} ``` codeblock. Object must validate against this JSONSchema:\n<|json_schema|>\n", - mode: Mode::MdJson) - ->get(); -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/logo/light.svg`: - -```svg - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/logo/dark.svg`: - -```svg - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/favicon.svg`: - -```svg - - - - - - - - - - - - - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/techniques/prompting.mdx`: - -```mdx -## General Tips for Prompt Engineering - -The overarching theme of using Instructor for function calling is to make the models self-descriptive, modular, and flexible, while maintaining data integrity and ease of use. - -- **Modularity**: Design self-contained components for reuse. -- **Self-Description**: Use PHPDoc comments or #[Description('')] attribute for clear field descriptions. -- **Optionality**: Use PHP's nullable types (e.g. ?int) for optional fields and set sensible defaults. -- **Standardization**: Employ enumerations for fields with a fixed set of values; include a fallback option. -- **Dynamic Data**: Use key-value pairs for arbitrary properties and limit list lengths. -- **Entity Relationships**: Define explicit identifiers and relationship fields. -- **Contextual Logic**: Optionally add a "chain of thought" field in reusable components for extra context. - - - -## Utilize Nullable Attribute - -Use PHP's nullable types by prefixing type name with question mark (?) and set a default value to prevent undesired defaults like empty strings. - -```php -error ? null : $this->result; - } -} -``` - -With the `MaybeUser` class, you can either receive a `UserDetail` object in result or get an error message in 'errorMessage'. - -> Original Instructor implementation in Python provides utility class Maybe making this pattern even easier. Such mechanism is not yet available in PHP version of Instructor. - - - -## Tips for Enumerations - -To prevent data misalignment, use Enums for standardized fields. Always include an "Other" option as a fallback so the model can signal uncertainty. - -```php - */ -class Role -{ - /** Restate the instructions and rules to correctly determine the title. */ - public string $instructions; - public string $title; -} - -class UserDetail -{ - public int $age; - public string $name; - public Role $role; -} -``` - - -## Handle Arbitrary Properties - -When you need to extract undefined attributes, use a list of key-value pairs. - -```php -title}` with query `{$this->query}` using `{$this->type->value}`\n"); - } -} -``` - - -## Segmenting the Search Prompt - -The `segment` function takes a string `data` and segments it into multiple search queries. It uses the `Instructor::respond` method to send a prompt and extract the data into the target object. The `responseModel` parameter specifies `Search::class` as the model to use for extraction. - -```php -respond( - messages: [[ - "role" => "user", - "content" => "Consider the data below: '\n$data' and segment it into multiple search queries", - ]], - responseModel: Search::class, - ); -} - -foreach (segment("Search for a picture of a cat and a video of a dog")->queries as $query) { - $query->execute(); - // dump($query); -} -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/techniques/classification.mdx`: - -```mdx -## Text Classification using LLM - -This tutorial showcases how to implement text classification tasks—specifically, single-label and multi-label classifications—using LLM (via OpenAI API), PHP's **`enums`** and classes. - -!!! tips "Motivation" - - Text classification is a common problem in many NLP applications, such as spam detection or support ticket categorization. The goal is to provide a systematic way to handle these cases using language models in combination with PHP data structures. - - - - -## Single-Label Classification - -### Defining the Structures - -For single-label classification, we first define an **`enum`** for possible labels and a PHP class for the output. - -```php -respond( - messages: [[ - "role" => "user", - "content" => "Classify the following text: $data", - ]], - responseModel: SinglePrediction::class, - model: "gpt-3.5-turbo-0613", - ); -} -``` - - -### Testing and Evaluation - -Let's run an example to see if it correctly identifies a spam message. - -```php -classLabel == Label::SPAM); -``` - - - -## Multi-Label Classification - -### Defining the Structures - -For multi-label classification, we introduce a new enum class and a different PHP class to handle multiple labels. - -```php -respond( - messages: [[ - "role" => "user", - "content" => "Classify following support ticket: {$data}", - ]], - responseModel: Ticket::class, - model: "gpt-3.5-turbo-0613", - ); -} -``` - -### Testing and Evaluation - -Finally, we test the multi-label classification function using a sample support ticket. - -```php -classLabels)); -assert(in_array(Label::BILLING, $prediction->classLabels)); -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/quickstart.mdx`: - -```mdx ---- -title: 'Quickstart' -description: 'Start processing your data with LLMs in under 5 minutes' ---- - - -## Setup Your Development Environment - -Set up LLM provider API keys - create `.env` file in the root of your project and add the following: - -```env -OPENAI_API_KEY=your-openai-api-key -``` - -> NOTE: You can get your LLM provider API key from the provider's dashboard, e.g.: -> [OpenAI](https://platform.openai.com/) - -You can also use API key directly in your code - see [example](cookbook/examples/advanced/custom_client). - - - -## Install Instructor with Composer - -Installing Instructor is simple. Run following command in your terminal, and you're on your way to a smoother data handling experience! - -```bash -composer install cognesy/instructor-php -``` - - -## Basic example - -This is a simple example demonstrating how Instructor retrieves structured information from provided text (or chat message sequence). - -Response model class is a plain PHP class with typehints specifying the types of fields of the object. - -Create file `instructor-test.php` with the following content: - -```php -add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); - -use Cognesy\Instructor\Instructor; - -// Step 1: Define target data structure(s) -class Person { - public string $name; - public int $age; -} - -// Step 2: Provide content to process -$text = "His name is Jason and he is 28 years old."; - -// Step 3: Use Instructor to run LLM inference -$person = (new Instructor)->respond( - messages: $text, - responseModel: Person::class, -); - -// Step 4: Work with structured response data -assert($person instanceof Person); // true -assert($person->name === 'Jason'); // true -assert($person->age === 28); // true - -echo $person->name; // Jason -echo $person->age; // 28 - -var_dump($person); -// Person { -// name: "Jason", -// age: 28 -// } -``` - -Now, you can run you example: - -```bash -php instructor-test.php -``` - -> **NOTE:** Instructor supports classes / objects as response models. In case you want to extract -> simple types like strings, integers, float, booleans or enums, you need to wrap them in Scalar adapter. -> See section: Extracting Scalar Values. - - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/llm_providers.mdx`: - -```mdx ---- -title: LLM API Providers -description: 'Instructor supports a number of LLM API providers via a set of preconfigured clients.' ---- - -Only tested providers with examples are listed here. - - -## Anthropic - -Supported extraction modes: - -- Mode::Tools -- Mode::Json (recommended) -- Mode::MdJson - -Example: -- `./examples/05_APISupport/LLMSupportAnthropic/run.php` - - - -## Azure OpenAI - -Azure is an alternative provider of OpenAI models. You can consider using it as -a backup provider in case OpenAI is not available. - -Supported extraction modes: - -- Mode::Tools (recommended) -- Mode::Json -- Mode::MdJson - -Example: -- `./examples/05_APISupport/LLMSupportAzureOAI/run.php` - - -## Cohere - - -Cohere API is a commercial provider of Command models with their own API, which -is significantly different from OpenAI. Instructor uses only a limited set of -Cohere API capabilities, specifically - chat completions. - -You can find the details on how to configure the client in the example below. -Cohere does not support JSON mode. - -Mode compatibility: -- Mode::MdJson - supported -- Mode::Json - supported, recommended -- Mode::Tools - partially supported, not recommended (at the moment) - -Reasons Mode::Tools is not recommended: - -- Cohere tools mode does not support JSON Schema, which only allows to extract very simple data schemas. -- Performance of the currently available versions of Cohere models in tools mode for Instructor use case (data extraction) is poor. - -Example: -- `./examples/05_APISupport/LLMSupportCohere/run.php` - - - -## FireworksAI - -Supported extraction modes: - -- Mode::MdJson -- Mode::Json (for selected models) -- Mode::Tools (for selected models) - -Example: -- `./examples/05_APISupport/LLMSupportFireworksAI/run.php` - - - - -## Gemini - -Supported extraction modes: - -- Mode::MdJson -- Mode::Json (recommended) -- Mode::Tools (experimental - not stable) - -Example: -- `./examples/05_APISupport/LLMSupportGemini/run.php` - - - -## Groq - -Supported extraction modes: - -- Mode::MdJson -- Mode::Json (recommended) -- Mode::Tools (experimental - not stable) - -Example: -- `./examples/05_APISupport/LLMSupportGroq/run.php` - - - - -## Mistral API - -Supported extraction modes: - -- Mode::MdJson -- Mode::Json (for selected models) -- Mode::Tools (for selected models) - -Example: -- `./examples/05_APISupport/LLMSupportMistral/run.php` - - - -## Ollama - -Supported extraction modes: - -- Mode::MdJson -- Mode::Json (for selected models, recommended) -- Mode::Tools (for selected models) - -Example: -- `./examples/05_APISupport/LLMSupportOllama/run.php` - - - -## OpenAI - -OpenAI is the default provider that is called by Instructor unless user -configures different one. - -OpenAI models (esp. GPT-4 and newer) perform very well across all 3 extraction -modes. - -Supported extraction modes: - - Mode::Tools (recommended) - - Mode::Json - - Mode::MdJson - -Majority of examples use OpenAI provider. - -- `./examples/05_APISupport/LLMSupportOpenAI/run.php` - - - -## OpenRouter - -You have to use our client adapter to work around the problem in the response format -returned by OpenRouter for non-streamed requests. - -Supported extraction modes: - - - Mode::MdJson - - Mode::Json (for selected models) - - Mode::Tools (for selected models) - -Example: - - `./examples/05_APISupport/LLMSupportOpenRouter/run.php` - - - -## TogetherAI - -Supported extraction modes: - -- Mode::MdJson -- Mode::Json (for selected models) -- Mode::Tools (for selected models) - -Example: -- `./examples/05_APISupport/LLMSupportTogetherAI/run.php` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/introduction.mdx`: - -```mdx -## Running cookbooks with Instructor Hub tool - -Welcome to Instructor cookbooks. The goal of this section is to provide a set of tutorials and examples to help you get started. - - - -## CLI Usage - -Instructor comes with a CLI tool that allows you to view and interact with the tutorials and examples and allows you to find the code snippets you may need to get solution to your problem. - - - -### List Cookbooks - -Run `./hub.sh list` you can see all the available tutorials and examples. - -```bash -$ ./hub.sh list -``` -...or on Windows: - -```cli -$ ./hub.bat list -``` - -### Reading a Cookbook - -To read a tutorial, you can run `./hub.sh show {id}` to see the full tutorial in the terminal. - -```bash -$ ./hub.sh show {name or id} -``` - -Currently, there is no way to page through the tutorial - feel free to contribute :) - - -### Running a Cookbook - -To run a tutorial, you run `./hub.sh run {id}` in terminal - it will execute the code and show the output. You need to have your OPENAI_API_KEY set in your environment (.env file in root directory of your copy of instructor-php repo). - -```bash -$ ./hub.sh run {name or id} -``` - - -### Running all Cookbooks - -This is mostly for testing if cookbooks are executed properly, but you can run `./hub.sh all {id}` to run all the tutorials and examples in the terminal. - -```bash -$ ./hub.sh all {name or id} -``` - - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/examples/advanced/structured_input.mdx`: - -```mdx ---- -title: 'Using structured data as an input' -docname: 'structured_input' ---- - -## Overview - -Instructor offers a way to use structured data as an input. This is -useful when you want to use object data as input and get another object -with a result of LLM inference. - -The `input` field of Instructor's `respond()` and `request()` methods -can be an object, but also an array or just a string. - - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); - -use Cognesy\Instructor\Instructor; - -class Email { - public function __construct( - public string $address = '', - public string $subject = '', - public string $body = '', - ) {} -} - -$email = new Email( - address: 'joe@gmail', - subject: 'Status update', - body: 'Your account has been updated.' -); - -$translatedEmail = (new Instructor)->respond( - input: $email, - responseModel: Email::class, - prompt: 'Translate the text fields of email to Spanish. Keep other fields unchanged.', -); - -dump($translatedEmail); - -assert($translatedEmail->address === $email->address); -assert($translatedEmail->subject !== $email->subject); -assert($translatedEmail->body !== $email->body); -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/examples/advanced/partials.mdx`: - -```mdx ---- -title: 'Streaming partial updates during inference' -docname: 'partials' ---- - -## Overview - -Instructor can process LLM's streamed responses to provide partial updates that you -can use to update the model with new data as the response is being generated. You can -use it to improve user experience by updating the UI with partial data before the full -response is received. - - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); - -use Cognesy\Instructor\Events\Event; -use Cognesy\Instructor\Instructor; - -class UserRole -{ - /** Monotonically increasing identifier */ - public int $id; - public string $title = ''; -} - -class UserDetail -{ - public int $age; - public string $name; - public string $location; - /** @var UserRole[] */ - public array $roles; - /** @var string[] */ - public array $hobbies; -} - -// This function will be called every time a new token is received -function partialUpdate($partial) { - // Clear the screen and move the cursor to the top - echo chr(27).chr(91).'H'.chr(27).chr(91).'J'; - - // Display the partial object - dump($partial); - - // Wait a bit before clearing the screen to make partial changes slower. - // Don't use this in your application :) - //usleep(250000); -} -?> -``` -Now we can use this data model to extract arbitrary properties from a text message. -As the tokens are streamed from LLM API, the `partialUpdate` function will be called -with partially updated object of type `UserDetail` that you can use, usually to update -the UI. - -```php -request( - messages: $text, - responseModel: UserDetail::class, - options: ['stream' => true] -)->onPartialUpdate(partialUpdate(...))->get(); - -echo "All tokens received, fully completed object available in `\$user` variable.\n"; -echo '$user = '."\n"; -dump($user); - -assert(!empty($user->roles)); -assert(!empty($user->hobbies)); -assert($user->location === 'San Francisco'); -assert($user->age == 25); -assert($user->name === 'Jason'); -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/examples/advanced/structures.mdx`: - -```mdx ---- -title: 'Structures' -docname: 'structures' ---- - -## Overview - -Structures allow dynamically define the shape of data to be extracted -by LLM, e.g. during runtime. - -Use `Structure::define()` to define the structure and pass it to Instructor -as response model. - -If `Structure` instance has been provided as a response model, Instructor -returns an array in the shape you defined. - -See more: [Structures](../../structures.md) - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); - -use Cognesy\Instructor\Extras\Structure\Field; -use Cognesy\Instructor\Extras\Structure\Structure; -use Cognesy\Instructor\Instructor; - -enum Role : string { - case Manager = 'manager'; - case Line = 'line'; -} - -$structure = Structure::define('person', [ - Field::string('name','Name of the person'), - Field::int('age', 'Age of the person')->validIf( - fn($value) => $value > 0, "Age has to be positive number" - ), - Field::structure('address', [ - Field::string('street', 'Street name')->optional(), - Field::string('city', 'City name'), - Field::string('zip', 'Zip code')->optional(), - ], 'Address of the person'), - Field::enum('role', Role::class, 'Role of the person'), -], 'A person object'); - -$text = <<respond( - messages: $text, - responseModel: $structure, -); - -print("OUTPUT:\n"); -print("Name: " . $person->name . "\n"); -print("Age: " . $person->age . "\n"); -print("Address / city: " . $person->address->city . "\n"); -print("Address / ZIP: " . $person->address->zip . "\n"); -print("Role: " . $person->role->value . "\n"); -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/examples/advanced/custom_prompts.mdx`: - -```mdx ---- -title: 'Custom prompts' -docname: 'custom_prompts' ---- - -## Overview - -In case you want to take control over the prompts sent by Instructor -to LLM for different modes, you can use the `prompt` parameter in the -`request()` or `respond()` methods. - -It will override the default Instructor prompts, allowing you to fully -customize how LLM is instructed to process the input. - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); - -use Cognesy\Instructor\Enums\Mode; -use Cognesy\Instructor\Events\Request\RequestSentToLLM; -use Cognesy\Instructor\Instructor; - -class User { - public int $age; - public string $name; -} - -$instructor = (new Instructor) - ->onEvent(RequestSentToLLM::class, fn(RequestSentToLLM $event)=>dump($event->request->toMessages())); - -print("\n# Request for Mode::Tools:\n\n"); -$user = $instructor - ->respond( - messages: "Our user Jason is 25 years old.", - responseModel: User::class, - prompt: "\nYour task is to extract correct and accurate data from the messages using provided tools.\n", - mode: Mode::Tools - ); -echo "\nRESPONSE:\n"; -dump($user); - -print("\n# Request for Mode::Json:\n\n"); -$user = $instructor - ->respond( - messages: "Our user Jason is 25 years old.", - responseModel: User::class, - prompt: "\nYour task is to respond correctly with JSON object. Response must follow JSONSchema:\n<|json_schema|>\n", - mode: Mode::Json - ); -echo "\nRESPONSE:\n"; -dump($user); - -print("\n# Request for Mode::MdJson:\n\n"); -$user = $instructor - ->respond( - messages: "Our user Jason is 25 years old.", - responseModel: User::class, - prompt: "\nYour task is to respond correctly with strict JSON object containing extracted data within a ```json {} ``` codeblock. Object must validate against this JSONSchema:\n<|json_schema|>\n", - mode: Mode::MdJson - ); -echo "\nRESPONSE:\n"; -dump($user); - -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/examples/advanced/demonstrations.mdx`: - -```mdx ---- -title: 'Providing example inputs and outputs' -docname: 'demonstrations' ---- - -## Overview - -To improve the results of LLM inference you can provide examples of the expected output. -This will help LLM to understand the context and the expected structure of the output. - -It is typically useful in the `Mode::Json` and `Mode::MdJson` modes, where the output -is expected to be a JSON object. - - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); - -use Cognesy\Instructor\Enums\Mode; -use Cognesy\Instructor\Events\Request\RequestSentToLLM; -use Cognesy\Instructor\Instructor; -use Cognesy\Instructor\Data\Example; - -class User { - public int $age; - public string $name; -} - -echo "\nREQUEST:\n"; -$user = (new Instructor) - ->onEvent(RequestSentToLLM::class, fn($event)=>dump($event->request->toMessages())) - ->request( - messages: "Our user Jason is 25 years old.", - responseModel: User::class, - examples: [ - new Example( - input: "John is 50 and works as a teacher.", - output: ['name' => 'John', 'age' => 50] - ), - new Example( - input: "We have recently hired Ian, who is 27 years old.", - output: ['name' => 'Ian', 'age' => 27], - template: "example input:\n<|input|>\noutput:\n```json\n<|output|>\n```\n", - ), - ], - mode: Mode::Json) - ->get(); - -echo "\nOUTPUT:\n"; -dump($user); -assert($user->name === 'Jason'); -assert($user->age === 25); -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/examples/advanced/streaming.mdx`: - -```mdx ---- -title: 'Streaming' -docname: 'streaming' ---- - -## Overview - -Instructor can process LLM's streamed responses to provide partial response model -updates that you can use to update the model with new data as the response is being -generated. - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); - -use Cognesy\Instructor\Instructor; - -class UserRole -{ - /** Monotonically increasing identifier */ - public int $id; - public string $title = ''; -} - -class UserDetail -{ - public int $age; - public string $name; - public string $location; - /** @var UserRole[] */ - public array $roles; - /** @var string[] */ - public array $hobbies; -} - -// This function will be called every time a new token is received -function partialUpdate($partial) { - // Clear the screen and move the cursor to the top - echo chr(27).chr(91).'H'.chr(27).chr(91).'J'; - - // Display the partial object - dump($partial); - - // Wait a bit before clearing the screen to make partial changes slower. - // Don't use this in your application :) - // usleep(250000); -} -?> -``` -Now we can use this data model to extract arbitrary properties from a text message. -As the tokens are streamed from LLM API, the `partialUpdate` function will be called -with partially updated object of type `UserDetail` that you can use, usually to update -the UI. - -```php -request( - messages: $text, - responseModel: UserDetail::class, - options: ['stream' => true] -)->stream(); - -foreach ($stream->partials() as $partial) { - partialUpdate($partial); -} - -$user = $stream->getLastUpdate(); - -assert($user->name === 'Jason'); -assert($user->age === 25); -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/examples/advanced/function_calls.mdx`: - -```mdx ---- -title: 'Extracting arguments of function or method' -docname: 'function_calls' ---- - -## Overview - -Instructor offers FunctionCall class to extract arguments of a function -or method from content. - -This is useful when you want to build tool use capability, e.g. for AI -chatbots or agents. - - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); - -use Cognesy\Instructor\Extras\FunctionCall\FunctionCall; -use Cognesy\Instructor\Instructor; - -class DataStore -{ - /** Save user data to storage */ - public function saveUser(string $name, int $age, string $country) : void { - // Save user to database - echo "Saving user ... saveUser('$name', $age, '$country')\n"; - } -} - -$text = "His name is Jason, he is 28 years old and he lives in Germany."; -$args = (new Instructor)->respond( - messages: $text, - responseModel: FunctionCall::fromMethodName(DataStore::class, 'saveUser'), -); - -echo "\nCalling the function with the extracted arguments:\n"; -(new DataStore)->saveUser(...$args); - -echo "\nExtracted arguments:\n"; -dump($args); - -assert(count($args) == 3); -expect($args['name'] === 'Jason'); -expect($args['age'] == 28); -expect($args['country'] === 'Germany'); -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/examples/advanced/sequences.mdx`: - -```mdx ---- -title: 'Extracting sequences of objects' -docname: 'sequences' ---- - -## Overview - -Sequences are a special type of response model that can be used to represent -a list of objects. - -It is usually more convenient not create a dedicated class with a single array -property just to handle a list of objects of a given class. - -Additional, unique feature of sequences is that they can be streamed per each -completed item in a sequence, rather than on any property update. - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__.'../../src/'); - -use Cognesy\Instructor\Extras\Sequence\Sequence; -use Cognesy\Instructor\Instructor; - -class Person -{ - public string $name; - public int $age; -} - -$text = <<request( - messages: $text, - responseModel: Sequence::of(Person::class), - options: ['stream' => true], - ) - ->onSequenceUpdate(fn($sequence) => dump($sequence->last())) - ->get(); - - -dump(count($list)); - -assert(count($list) === 4); -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/examples/advanced/scalars.mdx`: - -```mdx ---- -title: 'Extracting scalar values' -docname: 'scalars' ---- - -## Overview - -Sometimes we just want to get quick results without defining a class for -the response model, especially if we're trying to get a straight, simple -answer in a form of string, integer, boolean or float. Instructor provides -a simplified API for such cases. - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); - -use Cognesy\Instructor\Extras\Scalar\Scalar; -use Cognesy\Instructor\Instructor; - -enum CitizenshipGroup : string { - case US = "US"; - case Canada = "Canada"; - case Germany = "Germany"; - case Other = "Other"; -} - -$text = "His name is Jason, he is 28 years old American who lives in Germany."; -$value = (new Instructor)->respond( - messages: $text, - prompt: 'What is user\'s citizenship?', - responseModel: Scalar::enum(CitizenshipGroup::class, name: 'citizenshipGroup'), -); - - -dump($value); - -assert($value instanceof CitizenshipGroup); -expect($value == CitizenshipGroup::Other); -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/examples/advanced/context_cache.mdx`: - -```mdx ---- -title: 'Context caching (Anthropic)' -docname: 'context_cache' ---- - -## Overview - -Instructor offers a simplified way to work with LLM providers' APIs supporting caching -(currently only Anthropic API), so you can focus on your business logic while still being -able to take advantage of lower latency and costs. - -> **Note 1:** Instructor supports context caching for Anthropic API and OpenAI API. - -> **Note 2:** Context caching is automatic for all OpenAI API calls. Read more -> in the [OpenAI API documentation](https://platform.openai.com/docs/guides/prompt-caching). - - -## Example - -When you need to process multiple requests with the same context, you can use context -caching to improve performance and reduce costs. - -In our example we will be analyzing the README.md file of this Github project and -generating its structured description for multiple audiences. - -Let's start by defining the data model for the project details and the properties -that we want to extract or generate based on README file. - -```php -add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); - -use Cognesy\Instructor\Enums\Mode; -use Cognesy\Instructor\Instructor; -use Cognesy\Instructor\Schema\Attributes\Description; -use Cognesy\Instructor\Utils\Str; - -class Project { - public string $name; - public string $targetAudience; - /** @var string[] */ - #[Description('Technology platform and libraries used in the project')] - public array $technologies; - /** @var string[] */ - #[Description('Features and capabilities of the project')] - public array $features; - /** @var string[] */ - #[Description('Applications and potential use cases of the project')] - public array $applications; - #[Description('Explain the purpose of the project and the domain specific problems it solves')] - public string $description; - #[Description('Example code in Markdown demonstrating domain specific application of the library')] - public string $code; -} -?> -``` - -We read the content of the README.md file and cache the context, so it can be reused for -multiple requests. - -```php -withConnection('openai')->cacheContext( - system: 'Your goal is to respond questions about the project described in the README.md file' - . "\n\n# README.md\n\n" . $content, - prompt: 'Respond to the user with a description of the project with JSON using schema:\n<|json_schema|>', -); -?> -``` -At this point we can use Instructor structured output processing to extract the project -details from the README.md file into the `Project` data model. - -Let's start by asking the user to describe the project for a specific audience: P&C insurance CIOs. - -```php -respond( - messages: 'Describe the project in a way compelling to my audience: P&C insurance CIOs.', - responseModel: Project::class, - options: ['max_tokens' => 4096], - mode: Mode::Json, -); -dump($project); -assert($project instanceof Project); -assert(Str::contains($project->name, 'Instructor')); -?> -``` -Now we can use the same context to ask the user to describe the project for a different -audience: boutique CMS consulting company owner. - -Anthropic API will use the context cached in the previous request to provide the response, -which results in faster processing and lower costs. - -```php -respond( - messages: "Describe the project in a way compelling to my audience: boutique CMS consulting company owner.", - responseModel: Project::class, - options: ['max_tokens' => 4096], - mode: Mode::Json, -); -dump($project); -assert($project instanceof Project); -assert(Str::contains($project->name, 'Instructor')); -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/examples/advanced/custom_client.mdx`: - -```mdx ---- -title: 'Customize parameters of OpenAI client' -docname: 'custom_client' ---- - -## Overview - -You can provide your own OpenAI client instance to Instructor. This is useful -when you want to initialize OpenAI client with custom values - e.g. to call -other LLMs which support OpenAI API. - - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); - -use Cognesy\Instructor\Enums\Mode; -use Cognesy\Instructor\Extras\LLM\Data\LLMConfig; -use Cognesy\Instructor\Extras\LLM\Drivers\OpenAIDriver; -use Cognesy\Instructor\Instructor; -use Cognesy\Instructor\Utils\Env; - -class User { - public int $age; - public string $name; -} - -// Create instance of OpenAI client initialized with custom parameters -$driver = new OpenAIDriver(new LLMConfig( - apiUrl: 'https://api.openai.com/v1', - apiKey: Env::get('OPENAI_API_KEY'), - endpoint: '/chat/completions', - metadata: ['organization' => ''], - model: 'gpt-4o-mini', - maxTokens: 128, - ) -); - -// Get Instructor with the default client component overridden with your own -$instructor = (new Instructor)->withDriver($driver); - -// Call with custom model and execution mode -$user = $instructor->respond( - messages: "Our user Jason is 25 years old.", - responseModel: User::class, - model: 'gpt-3.5-turbo', - mode: Mode::Json, -); - - -dump($user); - -assert(isset($user->name)); -assert(isset($user->age)); -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/examples/advanced/context_cache_llm.mdx`: - -```mdx ---- -title: 'Context caching' -docname: 'context_cache_llm' ---- - -## Overview - -Instructor offers a simplified way to work with LLM providers' APIs supporting caching -(currently only Anthropic API), so you can focus on your business logic while still being -able to take advantage of lower latency and costs. - -> **Note 1:** Instructor supports context caching for Anthropic API and OpenAI API. - -> **Note 2:** Context caching is automatic for all OpenAI API calls. Read more -> in the [OpenAI API documentation](https://platform.openai.com/docs/guides/prompt-caching). - -## Example - -When you need to process multiple requests with the same context, you can use context -caching to improve performance and reduce costs. - -In our example we will be analyzing the README.md file of this Github project and -generating its summary for 2 target audiences. - - -```php -add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); - -use Cognesy\Instructor\Extras\LLM\Inference; -use Cognesy\Instructor\Utils\Str; - -$content = file_get_contents(__DIR__ . '/../../../README.md'); - -$inference = (new Inference)->withConnection('anthropic')->withCachedContext( - messages: [ - ['role' => 'user', 'content' => 'Here is content of README.md file'], - ['role' => 'user', 'content' => $content], - ['role' => 'user', 'content' => 'Generate short, very domain specific pitch of the project described in README.md'], - ['role' => 'assistant', 'content' => 'For whom do you want to generate the pitch?'], - ], -); - -$response = $inference->create( - messages: [['role' => 'user', 'content' => 'CTO of lead gen software vendor']], - options: ['max_tokens' => 256], -)->toLLMResponse(); - -print("----------------------------------------\n"); -print("\n# Summary for CTO of lead gen vendor\n"); -print(" ($response->cacheReadTokens tokens read from cache)\n\n"); -print("----------------------------------------\n"); -print($response->content . "\n"); - -assert(!empty($response->content)); -assert(Str::contains($response->content, 'Instructor')); -assert(Str::contains($response->content, 'lead', false)); - -$response2 = $inference->create( - messages: [['role' => 'user', 'content' => 'CIO of insurance company']], - options: ['max_tokens' => 256], -)->toLLMResponse(); - -print("----------------------------------------\n"); -print("\n# Summary for CIO of insurance company\n"); -print(" ($response2->cacheReadTokens tokens read from cache)\n\n"); -print("----------------------------------------\n"); -print($response2->content . "\n"); - -assert(!empty($response2->content)); -assert(Str::contains($response2->content, 'Instructor')); -assert(Str::contains($response2->content, 'insurance', false)); -//assert($response2->cacheReadTokens > 0); -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/examples/troubleshooting/token_usage_events.mdx`: - -```mdx ---- -title: 'Tracking token usage via events' -docname: 'token_usage_events' ---- - -## Overview - -Some use cases require tracking the token usage of the API responses. -Currently, this can be done by listening to the `LLMResponseReceived` -and `PartialLLMResponseReceived` events and summing the token usage -of the responses. - -Code below demonstrates how it can be implemented using Instructor -event listeners. - -> Note: OpenAI API requires `stream_options` to be set to -> `['include_usage' => true]` to include token usage in the streamed -> responses. - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); - -use Cognesy\Instructor\Events\Inference\LLMResponseReceived; -use Cognesy\Instructor\Events\Inference\PartialLLMResponseReceived; -use Cognesy\Instructor\Extras\LLM\Data\LLMResponse; -use Cognesy\Instructor\Extras\LLM\Data\PartialLLMResponse; -use Cognesy\Instructor\Instructor; - -class User { - public int $age; - public string $name; -} - -class TokenCounter { - public int $input = 0; - public int $output = 0; - public int $cacheCreation = 0; - public int $cacheRead = 0; - - public function add(LLMResponse|PartialLLMResponse $response) { - $this->input += $response->inputTokens; - $this->output += $response->outputTokens; - $this->cacheCreation += $response->cacheCreationTokens; - $this->cacheRead += $response->cacheReadTokens; - } - - public function reset() { - $this->input = 0; - $this->output = 0; - $this->cacheCreation = 0; - $this->cacheRead = 0; - } - - public function print() { - echo "Input tokens: $this->input\n"; - echo "Output tokens: $this->output\n"; - echo "Cache creation tokens: $this->cacheCreation\n"; - echo "Cache read tokens: $this->cacheRead\n"; - } -} - -$counter = new TokenCounter(); - -echo "COUNTING TOKENS FOR SYNC RESPONSE\n"; -$text = "Jason is 25 years old and works as an engineer."; -$instructor = (new Instructor) - ->onEvent(LLMResponseReceived::class, fn(LLMResponseReceived $e) => $counter->add($e->llmResponse)) - ->respond( - messages: $text, - responseModel: User::class, - ); -echo "\nTEXT: $text\n"; -assert($counter->input > 0); -assert($counter->output > 0); -$counter->print(); - -// Reset the counter -$counter->reset(); - -echo "\n\nCOUNTING TOKENS FOR STREAMED RESPONSE\n"; -$text = "Anna is 19 years old."; -$instructor = (new Instructor) - ->onEvent(PartialLLMResponseReceived::class, fn(PartialLLMResponseReceived $e) => $counter->add($e->partialLLMResponse)) - ->respond( - messages: $text, - responseModel: User::class, - options: ['stream' => true, 'stream_options' => ['include_usage' => true]], -); -echo "\nTEXT: $text\n"; -assert($counter->input > 0); -assert($counter->output > 0); -$counter->print(); -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/examples/troubleshooting/on_event.mdx`: - -```mdx ---- -title: 'Receive specific internal event with onEvent()' -docname: 'on_event' ---- - -## Overview - -`(new Instructor)->onEvent(string $class, callable $callback)` method allows -you to receive callback when specified type of event is dispatched by Instructor. - -This way you can plug into the execution process and monitor it, for example logging -or reacting to the events which are of interest to your application. - -This example demonstrates how you can monitor outgoing requests and received responses -via Instructor's events. - -Check the `Cognesy\Instructor\Events` namespace for the list of available events -and their properties. - - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); - -use Cognesy\Instructor\Events\Event; -use Cognesy\Instructor\Events\Request\RequestSentToLLM; -use Cognesy\Instructor\Events\Request\ResponseReceivedFromLLM; -use Cognesy\Instructor\Instructor; - -class User -{ - public string $name; - public int $age; -} - -// let's mock a logger class - in real life you would use a proper logger, e.g. Monolog -$logger = new class { - public function log(Event $event) { - // we're using a predefined asLog() method to get the event data, - // but you can access the event properties directly and customize the output - echo $event->asLog()."\n"; - } -}; - -$user = (new Instructor) - ->request( - messages: "Jason is 28 years old", - responseModel: User::class, - ) - ->onEvent(RequestSentToLLM::class, fn($event) => $logger->log($event)) - ->onEvent(ResponseReceivedFromLLM::class, fn($event) => $logger->log($event)) - ->get(); - -dump($user); -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/examples/troubleshooting/debugging.mdx`: - -```mdx ---- -title: 'Debugging' -docname: 'debugging' ---- - -## Overview - -The `Instructor` class has a `withDebug()` method that can be used to debug the request and response. - -It displays detailed information about the request being sent to LLM API and response received from it, -including: - - request headers, URI, method and body, - - response status, headers, and body. - -This is useful for debugging the request and response when you are not getting the expected results. - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); - -use Cognesy\Instructor\Extras\LLM\Data\LLMConfig; -use Cognesy\Instructor\Extras\LLM\Drivers\OpenAIDriver; -use Cognesy\Instructor\Extras\LLM\Enums\LLMProviderType; -use Cognesy\Instructor\Instructor; -use Cognesy\Instructor\Utils\Env; - -class User { - public int $age; - public string $name; -} - -// CASE 1 - normal flow - -$instructor = (new Instructor)->withConnection('openai'); - -echo "\n### CASE 1.1 - Debugging sync request\n\n"; -$user = $instructor->withDebug()->respond( - messages: "Jason is 25 years old.", - responseModel: User::class, - options: [ 'stream' => false ] -); - -echo "\nResult:\n"; -dump($user); - -echo "\n### CASE 1.2 - Debugging streaming request\n\n"; -$user2 = $instructor->withDebug()->respond( - messages: "Anna is 21 years old.", - responseModel: User::class, - options: [ 'stream' => true ] -); - -echo "\nResult:\n"; -dump($user2); - -assert(isset($user->name)); -assert(isset($user->age)); -assert($user->name === 'Jason'); -assert($user->age === 25); - -assert(isset($user2->name)); -assert(isset($user2->age)); -assert($user2->name === 'Anna'); -assert($user2->age === 21); - - -// CASE 2 - forcing API error via empty LLM config - -$driver = new OpenAIDriver(new LLMConfig()); -$instructor = (new Instructor)->withDriver($driver); - -echo "\n### CASE 2 - Debugging exception\n\n"; -try { - $user = $instructor->withDebug()->respond( - messages: "Jason is 25 years old.", - responseModel: User::class, - options: [ 'stream' => true ] - ); -} catch (Exception $e) { - echo "\nCaught it:\n"; - dump($e); -} -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/examples/troubleshooting/wiretap.mdx`: - -```mdx ---- -title: 'Receive all internal events with wiretap()' -docname: 'wiretap' ---- - -## Overview - -# Receive all internal events with wiretap() - -Instructor allows you to receive detailed information at every stage of request -and response processing via events. - -`(new Instructor)->wiretap(callable $callback)` method allows you to receive all -events dispatched by Instructor. - -Example below demonstrates how `wiretap()` can help you to monitor the execution -process and better understand or resolve any processing issues. - -In this example we use `print()` method available on event classes, which outputs -console-formatted information about each event. - - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); - -use Cognesy\Instructor\Instructor; - -enum Role : string { - case CEO = 'ceo'; - case CTO = 'cto'; - case Developer = 'developer'; - case Other = 'other'; -} - -class UserDetail -{ - public string $name; - public Role $role; - public int $age; -} - -$user = (new Instructor) - ->request( - messages: [["role" => "user", "content" => "Contact our CTO, Jason is 28 years old -- Best regards, Tom"]], - responseModel: UserDetail::class, - options: ['stream' => true] - ) - ->wiretap(fn($event) => $event->print()) - ->get(); - -dump($user); - -assert($user->name === "Jason"); -assert($user->role === Role::CTO); -assert($user->age === 28); -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/examples/extras/complex_extraction_gemini.mdx`: - -```mdx ---- -title: 'Extraction of complex objects (Gemini)' -docname: 'complex_extraction_gemini' ---- - -## Overview - -This is an example of extraction of a very complex structure from -the provided text with Google Gemini model. - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); - -use Cognesy\Instructor\Enums\Mode; -use Cognesy\Instructor\Extras\Sequence\Sequence; -use Cognesy\Instructor\Instructor; - -$report = <<<'EOT' - [2021-09-01] - Acme Insurance project to implement SalesTech CRM solution is currently - in RED status due to delayed delivery of document production system, led - by 3rd party vendor - Alfatech. Customer (Acme) is discussing the resolution - with the vendor. Due to dependencies it will result in delay of the - ecommerce track by 2 sprints. System integrator (SysCorp) are working - to absorb some of the delay by deploying extra resources to speed up - development when the doc production is done. Another issue is that the - customer is not able to provide the test data for the ecommerce track. - SysCorp notified it will impact stabilization schedule unless resolved by - the end of the month. Steerco has been informed last week about the - potential impact of the issues, but insists on maintaining release schedule - due to marketing campaign already ongoing. Customer executives are asking - us - SalesTech team - to confirm SysCorp's assessment of the situation. - We're struggling with that due to communication issues - SysCorp team has - not shown up on 2 recent calls. Lack of insight has been escalated to - SysCorp's leadership team yesterday, but we've got no response yet. The - previously reported Integration Proxy connectivity issue which was blocking - policy track has been resolved on 2021-08-30 - the track is now GREEN. - Production deployment plan has been finalized on Aug 15th and awaiting - customer approval. - EOT; - -echo "Extracting project events from the report:\n\n"; -echo $report . "\n\n"; - -/** Represents a project event */ -class ProjectEvent { - /** Title of the event - this should be a short, descriptive title of the event */ - public string $title = ''; - /** Concise, informative description of the event */ - public string $description = ''; - /** Type of the event */ - public ProjectEventType $type = ProjectEventType::Other; - /** Status of the event */ - public ProjectEventStatus $status = ProjectEventStatus::Unknown; - /** Stakeholders involved in the event */ - /** @var Stakeholder[] */ - public array $stakeholders = []; - /** Date of the event if reported in the text */ - public ?string $date = ''; -} - -/** Represents status of project event */ -enum ProjectEventStatus: string { - case Open = 'open'; - case Closed = 'closed'; - case Unknown = 'unknown'; -} - -/** Represents type of project event */ -enum ProjectEventType: string { - case Risk = 'risk'; - case Issue = 'issue'; - case Action = 'action'; - case Progress = 'progress'; - case Other = 'other'; -} - -/** Represents a project stakeholder */ -class Stakeholder { - /** Name of the stakeholder */ - public string $name = ''; - /** Role of the stakeholder, if specified */ - public StakeholderRole $role = StakeholderRole::Other; - /** Any details on the stakeholder, if specified - any mentions of company, organization, structure, group, team, function */ - public string $details = ''; -} - -enum StakeholderRole: string { - case Customer = 'customer'; - case Vendor = 'vendor'; - case SystemIntegrator = 'system integrator'; - case Other = 'other'; -} - -$instructor = (new Instructor)->withConnection('gemini'); - -echo "PROJECT EVENTS:\n\n"; - -$events = $instructor - ->onSequenceUpdate(fn($sequence) => displayEvent($sequence->last())) - ->request( - messages: $report, - responseModel: Sequence::of(ProjectEvent::class), - model: 'gemini-1.5-flash', - mode: Mode::MdJson, - options: [ - 'max_tokens' => 2048, - 'stream' => true, - ]) - ->get(); - -echo "TOTAL EVENTS: " . count($events) . "\n"; - -function displayEvent(ProjectEvent $event) : void { - echo "Event: {$event->title}\n"; - echo " - Descriptions: {$event->description}\n"; - echo " - Type: {$event->type->value}\n"; - echo " - Status: {$event->status->value}\n"; - echo " - Date: {$event->date}\n"; - if (empty($event->stakeholders)) { - echo " - Stakeholders: none\n"; - } else { - echo " - Stakeholders:\n"; - foreach($event->stakeholders as $stakeholder) { - echo " - {$stakeholder->name} ({$stakeholder->role->value})\n"; - } - } - echo "\n"; -} -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/examples/extras/llm_md_json.mdx`: - -```mdx ---- -title: 'Working directly with LLMs and JSON - MdJSON mode' -docname: 'llm_md_json' ---- - -## Overview - -While working with `Inference` class, you can also generate JSON output -from the model inference. This is useful for example when you need to -process the response in a structured way or when you want to store the -elements of the response in a database. - -## Example - -In this example we will use emulation mode - MdJson, which tries to -force the model to generate a JSON output by asking it to respond -with a JSON object within a Markdown code block. - -This is useful for the models which do not support JSON output directly. - -We will also provide an example of the expected JSON output in the prompt -to guide the model in generating the correct response. - -```php -add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); - -use Cognesy\Instructor\Enums\Mode;use Cognesy\Instructor\Extras\LLM\Inference; - -$data = (new Inference) - ->withConnection('openai') - ->create( - messages: [['role' => 'user', 'content' => 'What is capital of France? \ - Respond with JSON data containing name", population and year of founding. \ - Example: {"name": "Berlin", "population": 3700000, "founded": 1237}']], - options: ['max_tokens' => 64], - mode: Mode::MdJson, - ) - ->toJson(); - -echo "USER: What is capital of France\n"; -echo "ASSISTANT:\n"; -dump($data); - -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/examples/extras/translate_ui_fields.mdx`: - -```mdx ---- -title: 'Translating UI text fields' -docname: 'translate_ui_fields' ---- - -## Overview - -You can use Instructor to translate text fields in your UI. We can instruct the model to -translate only the text fields from one language to another, but leave the other fields, -like emails or URLs, unchanged. - -This example demonstrates how to translate text fields from English to German using -structure-to-structure processing with LLM. - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); - -use Cognesy\Instructor\Extras\Scalar\Scalar; -use Cognesy\Instructor\Instructor; -use Cognesy\Instructor\Validation\Contracts\CanValidateObject; -use Cognesy\Instructor\Validation\ValidationResult; - -class TextElementModel -{ - public function __construct( - public string $headline = '', - public string $text = '', - public string $url = 'https://translation.com/' - ) {} -} - -$sourceModel = new TextElementModel( - headline: 'This is my headline', - text: '

This is some WYSIWYG HTML content.

' -); - -$validator = new class implements CanValidateObject { - public function validate(object $dataObject): ValidationResult { - $isInGerman = (new Instructor)->respond( - input: $dataObject, - responseModel: Scalar::boolean(), - prompt: 'Are all content fields translated to German?', - ); - return match($isInGerman) { - true => ValidationResult::valid(), - default => ValidationResult::invalid(['All input text fields have to be translated to German. Keep HTML tags unchanged.']), - }; - } -}; - -$transformedModel = (new Instructor) - ->wiretap(fn($e)=>$e->print()) - ->addValidator($validator) - ->respond( - input: $sourceModel, - responseModel: get_class($sourceModel), - prompt: 'Translate all text fields to German. Keep HTML tags unchanged.', - maxRetries: 2, - options: ['temperature' => 0], -); - -dump($transformedModel); - -assert(( - str_contains($transformedModel->headline, 'Überschrift') - || str_contains($transformedModel->headline, 'Schlagzeile') -) === true); -assert(str_contains($transformedModel->text, 'Inhalt') === true); -assert(str_contains($transformedModel->text, '

') === true); -assert(str_contains(str_replace('\/', '/', $transformedModel->url), 'https://translation.com/') === true); -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/examples/extras/transcription_to_tasks.mdx`: - -```mdx ---- -title: 'Create tasks from meeting transcription' -docname: 'transcription_to_tasks' ---- - -## Overview - -This example demonstrates how you can create task assignments based on a transcription of meeting recording. - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); - -use Cognesy\Instructor\Enums\Mode; -use Cognesy\Instructor\Instructor; - -// Step 1: Define a class that represents the structure and semantics -// of the data you want to extract -enum TaskStatus : string { - case Pending = 'pending'; - case Completed = 'completed'; -} - -enum Role : string { - case PM = 'pm'; - case Dev = 'dev'; -} - -class Task { - public string $title; - public string $description; - public DateTime $dueDate; - public Role $owner; - public TaskStatus $status; -} - -class Tasks { - public DateTime $meetingDate; - /** @var Task[] */ - public array $tasks; -} - -// Step 2: Get the text (or chat messages) you want to extract data from -$text = <<respond( - messages: $text, - responseModel: Tasks::class, - model: 'gpt-4o', - mode: Mode::Json, -); - -// Step 4: Now you can use the extracted data in your application -print("Extracted data:\n"); - -dump($tasks); - -assert($tasks->meetingDate->format('Y-m-d') === '2024-01-15'); -assert(count($tasks->tasks) == 3); - -assert($tasks->tasks[0]->dueDate->format('Y-m-d') === '2024-01-20'); -assert($tasks->tasks[0]->status === TaskStatus::Pending); -assert($tasks->tasks[0]->owner === Role::Dev); - -assert($tasks->tasks[1]->dueDate->format('Y-m-d') === '2024-01-18'); -assert($tasks->tasks[1]->status === TaskStatus::Pending); -assert($tasks->tasks[1]->owner === Role::PM); - -assert($tasks->tasks[2]->dueDate->format('Y-m-d') === '2024-01-16'); -assert($tasks->tasks[2]->status === TaskStatus::Pending); -assert($tasks->tasks[2]->owner === Role::PM); -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/examples/extras/complex_extraction_claude.mdx`: - -```mdx ---- -title: 'Extraction of complex objects (Anthropic)' -docname: 'complex_extraction_claude' ---- - -## Overview - -This is an example of extraction of a very complex structure from -the provided text with Anthropic Claude 3 model. - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); - -use Cognesy\Instructor\Enums\Mode; -use Cognesy\Instructor\Extras\Sequence\Sequence; -use Cognesy\Instructor\Instructor; - -$report = <<<'EOT' - [2021-09-01] - Acme Insurance project to implement SalesTech CRM solution is currently - in RED status due to delayed delivery of document production system, led - by 3rd party vendor - Alfatech. Customer (Acme) is discussing the resolution - with the vendor. Due to dependencies it will result in delay of the - ecommerce track by 2 sprints. System integrator (SysCorp) are working - to absorb some of the delay by deploying extra resources to speed up - development when the doc production is done. Another issue is that the - customer is not able to provide the test data for the ecommerce track. - SysCorp notified it will impact stabilization schedule unless resolved by - the end of the month. Steerco has been informed last week about the - potential impact of the issues, but insists on maintaining release schedule - due to marketing campaign already ongoing. Customer executives are asking - us - SalesTech team - to confirm SysCorp's assessment of the situation. - We're struggling with that due to communication issues - SysCorp team has - not shown up on 2 recent calls. Lack of insight has been escalated to - SysCorp's leadership team yesterday, but we've got no response yet. The - previously reported Integration Proxy connectivity issue which was blocking - policy track has been resolved on 2021-08-30 - the track is now GREEN. - Production deployment plan has been finalized on Aug 15th and awaiting - customer approval. - EOT; - -echo "Extracting project events from the report:\n\n"; -echo $report . "\n\n"; - -class ProjectEvents { - /** - * List of events extracted from the text - * @var ProjectEvent[] - */ - public array $events = []; -} - -/** Represents a project event */ -class ProjectEvent { - /** Title of the event - this should be a short, descriptive title of the event */ - public string $title = ''; - /** Concise, informative description of the event */ - public string $description = ''; - /** Type of the event */ - public ProjectEventType $type = ProjectEventType::Other; - /** Status of the event */ - public ProjectEventStatus $status = ProjectEventStatus::Unknown; - /** Stakeholders involved in the event */ - /** @var Stakeholder[] */ - public array $stakeholders = []; - /** Date of the event if reported in the text */ - public ?string $date = ''; -} - -/** Represents status of project event */ -enum ProjectEventStatus: string { - case Open = 'open'; - case Closed = 'closed'; - case Unknown = 'unknown'; -} - -/** Represents type of project event */ -enum ProjectEventType: string { - case Risk = 'risk'; - case Issue = 'issue'; - case Action = 'action'; - case Progress = 'progress'; - case Other = 'other'; -} - -/** Represents a project stakeholder */ -class Stakeholder { - /** Name of the stakeholder */ - public string $name = ''; - /** Role of the stakeholder, if specified */ - public StakeholderRole $role = StakeholderRole::Other; - /** Any details on the stakeholder, if specified - any mentions of company, organization, structure, group, team, function */ - public ?string $details = ''; -} - -enum StakeholderRole: string { - case Customer = 'customer'; - case Vendor = 'vendor'; - case SystemIntegrator = 'system integrator'; - case Other = 'other'; -} - -$instructor = (new Instructor)->withConnection('anthropic'); - -echo "PROJECT EVENTS:\n\n"; - -$events = $instructor - ->onSequenceUpdate(fn($sequence) => displayEvent($sequence->last())) - ->request( - messages: $report, - responseModel: Sequence::of(ProjectEvent::class), - model: 'claude-3-5-sonnet-20240620', // 'claude-3-haiku-20240307' - prompt: 'Extract a list of project events with all the details from the provided input in JSON format using schema: <|json_schema|>', - mode: Mode::Json, - examples: [['input' => 'Acme Insurance project to implement SalesTech CRM solution is currently in RED status due to delayed delivery of document production system, led by 3rd party vendor - Alfatech. Customer (Acme) is discussing the resolution with the vendor. Production deployment plan has been finalized on Aug 15th and awaiting customer approval.', 'output' => [["type" => "object", "title" => "sequenceOfProjectEvent", "description" => "A sequence of ProjectEvent", "properties" => ["list" => [["title" => "Absorbing delay by deploying extra resources", "description" => "System integrator (SysCorp) are working to absorb some of the delay by deploying extra resources to speed up development when the doc production is done.", "type" => "action", "status" => "open", "stakeholders" => [["name" => "SysCorp", "role" => "system integrator", "details" => "System integrator",],], "date" => "2021-09-01",], ["title" => "Finalization of production deployment plan", "description" => "Production deployment plan has been finalized on Aug 15th and awaiting customer approval.", "type" => "progress", "status" => "open", "stakeholders" => [["name" => "Acme", "role" => "customer", "details" => "Customer",],], "date" => "2021-08-15",],],]]]]], - options: [ - 'max_tokens' => 4096, - 'stream' => true, - ]) - ->get(); - -echo "TOTAL EVENTS: " . count($events) . "\n"; -//dump($events->list); - -function displayEvent(ProjectEvent $event) : void { - echo "Event: {$event->title}\n"; - echo " - Descriptions: {$event->description}\n"; - echo " - Type: {$event->type->value}\n"; - echo " - Status: {$event->status->value}\n"; - echo " - Date: {$event->date}\n"; - if (empty($event->stakeholders)) { - echo " - Stakeholders: none\n"; - } else { - echo " - Stakeholders:\n"; - foreach($event->stakeholders as $stakeholder) { - echo " - {$stakeholder->name} ({$stakeholder->role->value})\n"; - } - } - echo "\n"; -} -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/examples/extras/llm_json.mdx`: - -```mdx ---- -title: 'Working directly with LLMs and JSON - JSON mode' -docname: 'llm_json' ---- - -## Overview - -While working with `Inference` class, you can also generate JSON output -from the model inference. This is useful for example when you need to -process the response in a structured way or when you want to store the -elements of the response in a database. - -`Inference` class supports multiple inference modes, like `Tools`, `Json` -`JsonSchema` or `MdJson`, which gives you flexibility to choose the best -approach for your use case. - -## Example - -In this example we will use OpenAI JSON mode, which guarantees that the -response will be in a JSON format. - -It does not guarantee compliance with a specific schema (for some providers -including OpenAI). We can try to work around it by providing an example of -the expected JSON output in the prompt. - -> NOTE: Some model providers allow to specify a JSON schema for model to -follow via `schema` parameter of `response_format`. OpenAI does not support -this feature in JSON mode (only in JSON Schema mode). - -```php -add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); - -use Cognesy\Instructor\Enums\Mode; -use Cognesy\Instructor\Extras\LLM\Inference; - -$data = (new Inference) - ->withConnection('openai') // optional, default is set in /config/llm.php - ->create( - messages: [['role' => 'user', 'content' => 'What is capital of France? \ - Respond with JSON data containing name", population and year of founding. \ - Example: {"name": "Berlin", "population": 3700000, "founded": 1237}']], - responseFormat: [ - 'type' => 'json_object', - ], - options: ['max_tokens' => 64], - mode: Mode::Json, - ) - ->toJson(); - -echo "USER: What is capital of France\n"; -echo "ASSISTANT:\n"; -dump($data); - -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/examples/extras/image_to_data_anthropic.mdx`: - -```mdx ---- -title: 'Image to data (Anthropic)' -docname: 'image_to_data_anthropic' ---- - -## Overview - -This is an example of how to extract structured data from an image using -Instructor. The image is loaded from a file and converted to base64 format -before sending it to OpenAI API. - -The response model is a PHP class that represents the structured receipt -information with data of vendor, items, subtotal, tax, tip, and total. - - -## Scanned image - -Here's the image we're going to extract data from. - -![Receipt](/images/receipt.png) - - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); - -use Cognesy\Instructor\Enums\Mode; -use Cognesy\Instructor\Extras\Image\Image; -use Cognesy\Instructor\Instructor; - -class Vendor { - public ?string $name = ''; - public ?string $address = ''; - public ?string $phone = ''; -} - -class ReceiptItem { - public string $name; - public ?int $quantity = 1; - public float $price; -} - -class Receipt { - public Vendor $vendor; - /** @var ReceiptItem[] */ - public array $items = []; - public ?float $subtotal; - public ?float $tax; - public ?float $tip; - public float $total; -} - -$receipt = (new Instructor)->withConnection('anthropic')->respond( - input: Image::fromFile(__DIR__ . '/receipt.png'), - responseModel: Receipt::class, - prompt: 'Extract structured data from the receipt. Return result as JSON following this schema: <|json_schema|>', - model: 'claude-3-5-sonnet-20240620', - mode: Mode::Json, - options: ['max_tokens' => 4096] -); - -dump($receipt); - -assert($receipt->total === 169.82); -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/examples/extras/llm_tools.mdx`: - -```mdx ---- -title: 'Working directly with LLMs and JSON - Tools mode' -docname: 'llm_tools' ---- - -## Overview - -While working with `Inference` class, you can also generate JSON output -from the model inference. This is useful for example when you need to -process the response in a structured way or when you want to store the -elements of the response in a database. - -## Example - -In this example we will use OpenAI tools mode, in which model will generate -a JSON containing arguments for a function call. This way we can make the -model generate a JSON object with specific structure of parameters. - -```php -add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); - -use Cognesy\Instructor\Enums\Mode; -use Cognesy\Instructor\Extras\LLM\Inference; - -$data = (new Inference) - ->withConnection('openai') - ->create( - messages: [['role' => 'user', 'content' => 'What is capital of France? \ - Respond with function call.']], - tools: [[ - 'type' => 'function', - 'function' => [ - 'name' => 'extract_data', - 'description' => 'Extract city data', - 'parameters' => [ - 'type' => 'object', - 'description' => 'City information', - 'properties' => [ - 'name' => [ - 'type' => 'string', - 'description' => 'City name', - ], - 'founded' => [ - 'type' => 'integer', - 'description' => 'Founding year', - ], - 'population' => [ - 'type' => 'integer', - 'description' => 'Current population', - ], - ], - 'required' => ['name', 'founded', 'population'], - 'additionalProperties' => false, - ], - ], - ]], - toolChoice: [ - 'type' => 'function', - 'function' => [ - 'name' => 'extract_data' - ] - ], - options: ['max_tokens' => 64], - mode: Mode::Tools, - ) - ->toJson(); - -echo "USER: What is capital of France\n"; -echo "ASSISTANT:\n"; -dump($data); - -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/examples/extras/image_to_data.mdx`: - -```mdx ---- -title: 'Image to data (OpenAI)' -docname: 'image_to_data' ---- - -## Overview - -This is an example of how to extract structured data from an image using -Instructor. The image is loaded from a file and converted to base64 format -before sending it to OpenAI API. - -The response model is a PHP class that represents the structured receipt -information with data of vendor, items, subtotal, tax, tip, and total. - - -## Scanned image - -Here's the image we're going to extract data from. - -![Receipt](/images/receipt.png) - - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); - -use Cognesy\Instructor\Extras\Image\Image; -use Cognesy\Instructor\Instructor; - -class Vendor { - public ?string $name = ''; - public ?string $address = ''; - public ?string $phone = ''; -} - -class ReceiptItem { - public string $name; - public ?int $quantity = 1; - public float $price; -} - -class Receipt { - public Vendor $vendor; - /** @var ReceiptItem[] */ - public array $items = []; - public ?float $subtotal; - public ?float $tax; - public ?float $tip; - public float $total; -} - -$receipt = (new Instructor)->respond( - input: Image::fromFile(__DIR__ . '/receipt.png'), - responseModel: Receipt::class, - prompt: 'Extract structured data from the receipt.', - options: ['max_tokens' => 4096] -); - -dump($receipt); - -assert($receipt->total === 169.82); -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/examples/extras/llm.mdx`: - -```mdx ---- -title: 'Working directly with LLMs' -docname: 'llm' ---- - -## Overview - -`Inference` class offers access to LLM APIs and convenient methods to execute -model inference, incl. chat completions, tool calling or JSON output -generation. - -LLM providers access details can be found and modified via -`/config/llm.php`. - - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); - -use Cognesy\Instructor\Extras\LLM\Inference; -use Cognesy\Instructor\Utils\Str; - - - -// EXAMPLE 1: simplified API, default connection for convenient ad-hoc calls -$answer = Inference::text('What is capital of Germany'); - -echo "USER: What is capital of Germany\n"; -echo "ASSISTANT: $answer\n"; -assert(Str::contains($answer, 'Berlin')); - - - - -// EXAMPLE 2: regular API, allows to customize inference options -$answer = (new Inference) - ->withConnection('openai') // optional, default is set in /config/llm.php - ->create( - messages: [['role' => 'user', 'content' => 'What is capital of France']], - options: ['max_tokens' => 64] - ) - ->toText(); - -echo "USER: What is capital of France\n"; -echo "ASSISTANT: $answer\n"; -assert(Str::contains($answer, 'Paris')); - - - - -// EXAMPLE 3: streaming response -$stream = (new Inference) - ->create( - messages: [['role' => 'user', 'content' => 'Describe capital of Brasil']], - options: ['max_tokens' => 128, 'stream' => true] - ) - ->stream(); - -echo "USER: Describe capital of Brasil\n"; -echo "ASSISTANT: "; -foreach ($stream as $delta) { - echo $delta; -} -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/examples/extras/web_to_objects.mdx`: - -```mdx ---- -title: 'Web page to PHP objects' -docname: 'web_to_objects' ---- - -## Overview - -This example demonstrates how to extract structured data from a web page and get -it as PHP object. - -## Example - -In this example we will be extracting list of Laravel companies from The Manifest -website. The result will be a list of `Company` objects. - -We use Webpage extractor to get the content of the page and specify 'none' scraper, -which means that we will be using built-in `file_get_contents` function to get the -content of the page. - -In production environment you might want to use one of the supported scrapers: - - `browsershot` - - `scrapingbee` - - `scrapfly` - - `jinareader` - -Commercial scrapers require API key, which can be set in the configuration file -(`/config/web.php`). - -```php -add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); - -use Cognesy\Instructor\Enums\Mode; -use Cognesy\Instructor\Extras\Web\Webpage; -use Cognesy\Instructor\Instructor; -use Cognesy\Instructor\Schema\Attributes\Instructions; - -class Company { - public string $name = ''; - public string $location = ''; - public string $description = ''; - public int $minProjectBudget = 0; - public string $companySize = ''; - #[Instructions('Remove any tracking parameters from the URL')] - public string $websiteUrl = ''; - /** @var string[] */ - public array $clients = []; -} - -$instructor = (new Instructor)->withConnection('openai'); - -$companyGen = Webpage::withScraper('none') - ->get('https://themanifest.com/pl/software-development/laravel/companies?page=1') - ->cleanup() - ->select('.directory-providers__list') - ->selectMany( - selector: '.provider-card', - callback: fn($item) => $item->asMarkdown(), - limit: 3 - ); - -$companies = []; -foreach($companyGen as $companyDiv) { - $company = $instructor->respond( - messages: $companyDiv, - responseModel: Company::class, - mode: Mode::Json - ); - $companies[] = $company; - dump($company); -} - -assert(count($companies) === 3); -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/examples/extras/complex_extraction.mdx`: - -```mdx ---- -title: 'Extraction of complex objects' -docname: 'complex_extraction' ---- - -## Overview - -This is an example of extraction of a very complex structure from -the provided text. - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); - -use Cognesy\Instructor\Enums\Mode; -use Cognesy\Instructor\Extras\Sequence\Sequence; -use Cognesy\Instructor\Instructor; - -$report = <<<'EOT' - [2021-09-01] - Acme Insurance project to implement SalesTech CRM solution is currently - in RED status due to delayed delivery of document production system, led - by 3rd party vendor - Alfatech. Customer (Acme) is discussing the resolution - with the vendor. Due to dependencies it will result in delay of the - ecommerce track by 2 sprints. System integrator (SysCorp) are working - to absorb some of the delay by deploying extra resources to speed up - development when the doc production is done. Another issue is that the - customer is not able to provide the test data for the ecommerce track. - SysCorp notified it will impact stabilization schedule unless resolved by - the end of the month. Steerco has been informed last week about the - potential impact of the issues, but insists on maintaining release schedule - due to marketing campaign already ongoing. Customer executives are asking - us - SalesTech team - to confirm SysCorp's assessment of the situation. - We're struggling with that due to communication issues - SysCorp team has - not shown up on 2 recent calls. Lack of insight has been escalated to - SysCorp's leadership team yesterday, but we've got no response yet. The - previously reported Integration Proxy connectivity issue which was blocking - policy track has been resolved on 2021-08-30 - the track is now GREEN. - Production deployment plan has been finalized on Aug 15th and awaiting - customer approval. - EOT; - -echo "Extracting project events from the report:\n\n"; -echo $report . "\n\n"; - -/** Represents a project event */ -class ProjectEvent { - /** Title of the event - this should be a short, descriptive title of the event */ - public string $title = ''; - /** Concise, informative description of the event */ - public string $description = ''; - /** Type of the event */ - public ProjectEventType $type = ProjectEventType::Other; - /** Status of the event */ - public ProjectEventStatus $status = ProjectEventStatus::Unknown; - /** Stakeholders involved in the event */ - /** @var Stakeholder[] */ - public array $stakeholders = []; - /** Date of the event if reported in the text */ - public ?string $date = ''; -} - -/** Represents status of project event */ -enum ProjectEventStatus: string { - case Open = 'open'; - case Closed = 'closed'; - case Unknown = 'unknown'; -} - -/** Represents type of project event */ -enum ProjectEventType: string { - case Risk = 'risk'; - case Issue = 'issue'; - case Action = 'action'; - case Progress = 'progress'; - case Other = 'other'; -} - -/** Represents a project stakeholder */ -class Stakeholder { - /** Name of the stakeholder */ - public string $name = ''; - /** Role of the stakeholder, if specified */ - public StakeholderRole $role = StakeholderRole::Other; - /** Any details on the stakeholder, if specified - any mentions of company, organization, structure, group, team, function */ - public ?string $details = ''; -} - -enum StakeholderRole: string { - case Customer = 'customer'; - case Vendor = 'vendor'; - case SystemIntegrator = 'system integrator'; - case Other = 'other'; -} - -$instructor = new Instructor; - -echo "PROJECT EVENTS:\n\n"; - -$events = $instructor - ->onSequenceUpdate(fn($sequence) => displayEvent($sequence->last())) - ->request( - messages: $report, - responseModel: Sequence::of(ProjectEvent::class), - model: 'gpt-4o', - mode: Mode::Tools, - options: [ - 'max_tokens' => 2048, - 'stream' => true, - ]) - ->get(); - -echo "TOTAL EVENTS: " . count($events) . "\n"; - -function displayEvent(ProjectEvent $event) : void { - echo "Event: {$event->title}\n"; - echo " - Descriptions: {$event->description}\n"; - echo " - Type: {$event->type->value}\n"; - echo " - Status: {$event->status->value}\n"; - echo " - Date: {$event->date}\n"; - if (empty($event->stakeholders)) { - echo " - Stakeholders: none\n"; - } else { - echo " - Stakeholders:\n"; - foreach($event->stakeholders as $stakeholder) { - echo " - {$stakeholder->name} ({$stakeholder->role->value})\n"; - } - } - echo "\n"; -} -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/examples/extras/llm_json_schema.mdx`: - -```mdx ---- -title: 'Working directly with LLMs and JSON - JSON Schema mode' -docname: 'llm_json_schema' ---- - -## Overview - -While working with `Inference` class, you can also generate JSON output -from the model inference. This is useful for example when you need to -process the response in a structured way or when you want to store the -elements of the response in a database. - -## Example - -In this example we will use OpenAI JSON Schema mode, which guarantees -that the response will be in a JSON format that matches the provided -schema. - -> NOTE: Json Schema mode with guaranteed structured outputs is not -supported by all language model providers. - -```php -add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); - -use Cognesy\Instructor\Enums\Mode;use Cognesy\Instructor\Extras\LLM\Inference; - -$data = (new Inference) - ->withConnection('openai') - ->create( - messages: [['role' => 'user', 'content' => 'What is capital of France? \ - Respond with JSON data.']], - responseFormat: [ - 'type' => 'json_schema', - 'description' => 'City data', - 'json_schema' => [ - 'name' => 'city_data', - 'schema' => [ - 'type' => 'object', - 'description' => 'City information', - 'properties' => [ - 'name' => [ - 'type' => 'string', - 'description' => 'City name', - ], - 'founded' => [ - 'type' => 'integer', - 'description' => 'Founding year', - ], - 'population' => [ - 'type' => 'integer', - 'description' => 'Current population', - ], - ], - 'additionalProperties' => false, - 'required' => ['name', 'founded', 'population'], - ], - 'strict' => true, - ], - ], - options: ['max_tokens' => 64], - mode: Mode::JsonSchema, - ) - ->toJson(); - -echo "USER: What is capital of France\n"; -echo "ASSISTANT:\n"; -dump($data); - -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/examples/extras/complex_extraction_cohere.mdx`: - -```mdx ---- -title: 'Extraction of complex objects (Cohere)' -docname: 'complex_extraction_cohere' ---- - -## Overview - -This is an example of extraction of a very complex structure from -the provided text with Cohere R models. - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); - -use Cognesy\Instructor\Enums\Mode; -use Cognesy\Instructor\Extras\Sequence\Sequence; -use Cognesy\Instructor\Instructor; - -$report = <<<'EOT' - [2021-09-01] - Acme Insurance project to implement SalesTech CRM solution is currently - in RED status due to delayed delivery of document production system, led - by 3rd party vendor - Alfatech. Customer (Acme) is discussing the resolution - with the vendor. Due to dependencies it will result in delay of the - ecommerce track by 2 sprints. System integrator (SysCorp) are working - to absorb some of the delay by deploying extra resources to speed up - development when the doc production is done. Another issue is that the - customer is not able to provide the test data for the ecommerce track. - SysCorp notified it will impact stabilization schedule unless resolved by - the end of the month. Steerco has been informed last week about the - potential impact of the issues, but insists on maintaining release schedule - due to marketing campaign already ongoing. Customer executives are asking - us - SalesTech team - to confirm SysCorp's assessment of the situation. - We're struggling with that due to communication issues - SysCorp team has - not shown up on 2 recent calls. Lack of insight has been escalated to - SysCorp's leadership team yesterday, but we've got no response yet. The - previously reported Integration Proxy connectivity issue which was blocking - policy track has been resolved on 2021-08-30 - the track is now GREEN. - Production deployment plan has been finalized on Aug 15th and awaiting - customer approval. - EOT; - -echo "Extracting project events from the report:\n\n"; -echo $report . "\n\n"; - -/** Represents a project event */ -class ProjectEvent { - /** Title of the event - this should be a short, descriptive title of the event */ - public string $title = ''; - /** Concise, informative description of the event */ - public string $description = ''; - /** Type of the event */ - public ProjectEventType $type = ProjectEventType::Other; - /** Status of the event */ - public ProjectEventStatus $status = ProjectEventStatus::Unknown; - /** Stakeholders involved in the event */ - /** @var Stakeholder[] */ - public array $stakeholders = []; - /** Date of the event if reported in the text */ - public ?string $date = ''; -} - -/** Represents status of project event */ -enum ProjectEventStatus: string { - case Open = 'open'; - case Closed = 'closed'; - case Unknown = 'unknown'; -} - -/** Represents type of project event */ -enum ProjectEventType: string { - case Risk = 'risk'; - case Issue = 'issue'; - case Action = 'action'; - case Progress = 'progress'; - case Other = 'other'; -} - -/** Represents a project stakeholder */ -class Stakeholder { - /** Name of the stakeholder */ - public string $name = ''; - /** Role of the stakeholder, if specified */ - public StakeholderRole $role = StakeholderRole::Other; - /** Any details on the stakeholder, if specified - any mentions of company, organization, structure, group, team, function */ - public string $details = ''; -} - -enum StakeholderRole: string { - case Customer = 'customer'; - case Vendor = 'vendor'; - case SystemIntegrator = 'system integrator'; - case Other = 'other'; -} - -$instructor = (new Instructor)->withConnection('cohere1'); - -echo "PROJECT EVENTS:\n\n"; - -$events = $instructor - ->onSequenceUpdate(fn($sequence) => displayEvent($sequence->last())) - ->request( - messages: $report, - responseModel: Sequence::of(ProjectEvent::class), - model: 'command-r-plus-08-2024', - mode: Mode::JsonSchema, - options: [ - 'max_tokens' => 2048, - 'stream' => true, - ]) - ->get(); - -echo "TOTAL EVENTS: " . count($events) . "\n"; - -function displayEvent(ProjectEvent $event) : void { - echo "Event: {$event->title}\n"; - echo " - Descriptions: {$event->description}\n"; - echo " - Type: {$event->type->value}\n"; - echo " - Status: {$event->status->value}\n"; - echo " - Date: {$event->date}\n"; - if (empty($event->stakeholders)) { - echo " - Stakeholders: none\n"; - } else { - echo " - Stakeholders:\n"; - foreach($event->stakeholders as $stakeholder) { - echo " - {$stakeholder->name} ({$stakeholder->role->value})\n"; - } - } - echo "\n"; -} -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/examples/extras/image_to_data_gemini.mdx`: - -```mdx ---- -title: 'Image to data (Gemini)' -docname: 'image_to_data_gemini' ---- - -## Overview - -This is an example of how to extract structured data from an image using -Instructor. The image is loaded from a file and converted to base64 format -before sending it to OpenAI API. - -The response model is a PHP class that represents the structured receipt -information with data of vendor, items, subtotal, tax, tip, and total. - - -## Scanned image - -Here's the image we're going to extract data from. - -![Receipt](/images/receipt.png) - - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); - -use Cognesy\Instructor\Enums\Mode; -use Cognesy\Instructor\Extras\Image\Image; -use Cognesy\Instructor\Instructor; - -class Vendor { - public ?string $name = ''; - public ?string $address = ''; - public ?string $phone = ''; -} - -class ReceiptItem { - public string $name; - public ?int $quantity = 1; - public float $price; -} - -class Receipt { - public Vendor $vendor; - /** @var ReceiptItem[] */ - public array $items = []; - public ?float $subtotal; - public ?float $tax; - public ?float $tip; - public float $total; -} - -$receipt = (new Instructor)->withConnection('gemini')->respond( - input: Image::fromFile(__DIR__ . '/receipt.png'), - responseModel: Receipt::class, - prompt: 'Extract structured data from the receipt. Return result as JSON following this schema: <|json_schema|>', - mode: Mode::Json, - options: ['max_tokens' => 4096] -); - -dump($receipt); - -assert($receipt->total === 169.82); -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/examples/extras/schema_dynamic.mdx`: - -```mdx ---- -title: 'Generating JSON Schema dynamically' -docname: 'schema_dynamic' ---- - -## Overview - -Instructor has a built-in support for generating JSON Schema from -dynamic objects with `Structure` class. - -This is useful when the data model is built during runtime or defined -by your app users. - -`Structure` helps you flexibly design and modify data models that -can change with every request or user input and allows you to generate -JSON Schema for them. - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); - -use Cognesy\Instructor\Enums\Mode; -use Cognesy\Instructor\Extras\LLM\Inference; -use Cognesy\Instructor\Extras\Structure\Field; -use Cognesy\Instructor\Extras\Structure\Structure; - -$city = Structure::define('city', [ - Field::string('name', 'City name')->required(), - Field::int('population', 'City population')->required(), - Field::int('founded', 'Founding year')->required(), -]); - -$data = (new Inference) - ->withConnection('openai') - ->create( - messages: [['role' => 'user', 'content' => 'What is capital of France? \ - Respond with JSON data.']], - responseFormat: [ - 'type' => 'json_schema', - 'description' => 'City data', - 'json_schema' => [ - 'name' => 'city_data', - 'schema' => $city->toJsonSchema(), - 'strict' => true, - ], - ], - options: ['max_tokens' => 64], - mode: Mode::JsonSchema, - ) - ->toJson(); - -echo "USER: What is capital of France\n"; -echo "ASSISTANT:\n"; -dump($data); - -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/examples/extras/embeddings.mdx`: - -```mdx ---- -title: 'Embeddings' -docname: 'embeddings' ---- - -## Overview - -`Embeddings` class offers access to embeddings APIs and convenient methods -to find top K vectors or documents most similar to provided query. - -`Embeddings` class supports following embeddings providers: - - Azure - - Cohere - - Gemini - - Jina - - Mistral - - OpenAI - -Embeddings providers access details can be found and modified via -`/config/embed.php`. - - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); - -use Cognesy\Instructor\Extras\Embeddings\Embeddings; - -$documents = [ - 'Computer vision models are used to analyze images and videos.', - 'The bakers at the Nashville Bakery baked 200 loaves of bread on Monday morning.', - 'The new movie starring Tom Hanks is now playing in theaters.', - 'Famous soccer player Lionel Messi has arrived in town.', - 'News about the latest iPhone model has been leaked.', - 'New car model by Tesla is now available for pre-order.', - 'Philip K. Dick is an author of many sci-fi novels.', -]; - -$query = "technology news"; - -$connections = ['azure', 'cohere1', 'gemini', 'jina', 'mistral', 'ollama', 'openai']; - -foreach($connections as $connection) { - $bestMatches = (new Embeddings)->withConnection($connection)->findSimilar( - query: $query, - documents: $documents, - topK: 3 - ); - - echo "\n[$connection]\n"; - dump($bestMatches); -} -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/examples/extras/schema.mdx`: - -```mdx ---- -title: 'Generating JSON Schema from PHP classes' -docname: 'schema' ---- - -## Overview - -Instructor has a built-in support for generating JSON Schema from -the classes or objects. This is useful as it helps you avoid writing -the JSON Schema manually, which can be error-prone and time-consuming. - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); - -use Cognesy\Instructor\Enums\Mode;use Cognesy\Instructor\Extras\LLM\Inference; -use Cognesy\Instructor\Schema\Factories\SchemaFactory; - -class City { - public string $name; - public int $population; - public int $founded; -} - -$schema = (new SchemaFactory)->schema(City::class); - -$data = (new Inference) - ->withConnection('openai') - ->create( - messages: [['role' => 'user', 'content' => 'What is capital of France? \ - Respond with JSON data.']], - responseFormat: [ - 'type' => 'json_schema', - 'description' => 'City data', - 'json_schema' => [ - 'name' => 'city_data', - 'schema' => $schema->toJsonSchema(), - 'strict' => true, - ], - ], - options: ['max_tokens' => 64], - mode: Mode::JsonSchema, - ) - ->toJson(); - -echo "USER: What is capital of France\n"; -echo "ASSISTANT:\n"; -dump($data); - -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/examples/basics/custom_validation.mdx`: - -```mdx ---- -title: 'Custom validation using Symfony Validator' -docname: 'custom_validation' ---- - -## Overview - -Instructor uses Symfony validation component to validate properties of extracted data. Symfony -offers you #[Assert/Callback] annotation to build fully customized validation logic. - - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__.'../../src/'); - -use Cognesy\Instructor\Instructor; -use Symfony\Component\Validator\Constraints as Assert; -use Symfony\Component\Validator\Context\ExecutionContextInterface; - -class UserDetails -{ - public string $name; - public int $age; - - #[Assert\Callback] - public function validateName(ExecutionContextInterface $context, mixed $payload) { - if ($this->name !== strtoupper($this->name)) { - $context->buildViolation("Name must be in all uppercase letters.") - ->atPath('name') - ->setInvalidValue($this->name) - ->addViolation(); - } - } -} - -$user = (new Instructor)->respond( - messages: [['role' => 'user', 'content' => 'jason is 25 years old']], - responseModel: UserDetails::class, - maxRetries: 2 -); - -dump($user); - -assert($user->name === "JASON"); -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/examples/basics/basic_use_mixin.mdx`: - -```mdx ---- -title: 'Basic use via mixin' -docname: 'basic_use_mixin' ---- - -## Overview - -Instructor provides `HandlesSelfInference` trait that you can use to enable -extraction capabilities directly on class via static `infer()` method. - -`infer()` method returns an instance of the class with the data extracted -using the Instructor. - -`infer()` method has following signature (you can also find it in the -`CanSelfInfer` interface): - -```php -static public function infer( - string|array $messages, // (required) The message(s) to infer data from - string $model = '', // (optional) The model to use for inference (otherwise - use default) - int $maxRetries = 2, // (optional) The number of retries in case of validation failure - array $options = [], // (optional) Additional data to pass to the Instructor or LLM API - array $examples = [], // (optional) Examples to include in the prompt - string $toolName = '', // (optional) The name of the tool call - used to add semantic information for LLM - string $toolDescription = '', // (optional) The description of the tool call - as above - string $prompt = '', // (optional) The prompt to use for inference - string $retryPrompt = '', // (optional) The prompt to use in case of validation failure - Mode $mode = Mode::Tools, // (optional) The mode to use for inference - Instructor $instructor = null // (optional) The Instructor instance to use for inference -) : static; -``` - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); - -use Cognesy\Instructor\Extras\Mixin\HandlesSelfInference; -use Cognesy\Instructor\Instructor; - -class User { - use HandlesSelfInference; - - public int $age; - public string $name; - - protected function getInstructor() : Instructor { - return new Instructor(); - } - - protected function getResponseModel() : string|array|object { - return $this; - } -} - -$user = User::infer("Jason is 25 years old and works as an engineer."); - -dump($user); - -assert(isset($user->name)); -assert(isset($user->age)); -assert($user->name === 'Jason'); -assert($user->age === 25); -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/examples/basics/validation_with_llm.mdx`: - -```mdx ---- -title: 'Validation with LLM' -docname: 'validation_with_llm' ---- - -## Overview - -You can use LLM capability to semantically process the context to validate -the response following natural language instructions. This way you can -implement more complex validation logic that would be difficult (or impossible) -to achieve using traditional, code-based validation. - -## Example - -add('Cognesy\\Instructor\\', __DIR__.'../../src/'); - -use Cognesy\Instructor\Events\Event; -use Cognesy\Instructor\Extras\Scalar\Scalar; -use Cognesy\Instructor\Instructor; -use Cognesy\Instructor\Schema\Attributes\Description; -use Cognesy\Instructor\Utils\Str; -use Cognesy\Instructor\Validation\Traits\ValidationMixin; -use Cognesy\Instructor\Validation\ValidationResult; - -class UserDetails -{ - use ValidationMixin; - - public string $name; - #[Description('User details in format: key=value')] - /** @var string[] */ - public array $details; - - public function validate() : ValidationResult { - return match($this->hasPII()) { - true => ValidationResult::fieldError( - field: 'details', - value: implode('\n', $this->details), - message: "Details contain PII, remove it from the response." - ), - false => ValidationResult::valid(), - }; - } - - private function hasPII() : bool { - $data = implode('\n', $this->details); - return (new Instructor)->respond( - messages: "Context:\n$data\n", - responseModel: Scalar::boolean('hasPII', 'Does the context contain any PII?'), - ); - } -} - -$text = <<wiretap(fn(Event $e) => $e->print()) // let's check the internals of Instructor processing - ->respond( - messages: $text, - responseModel: UserDetails::class, - maxRetries: 2 - ); - -dump($user); - -assert(!Str::contains(implode('\n', $user->details), '123-45-6789')); -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/examples/basics/public_vs_private.mdx`: - -```mdx ---- -title: 'Private vs public object field' -docname: 'public_vs_private' ---- - -## Overview - -Instructor only sets public fields of the object with the data provided by LLM. -Private and protected fields are left unchanged. If you want to access them -directly after extraction, consider providing default values for them. - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__.'../../src/'); - -use Cognesy\Instructor\Instructor; - -class User -{ - public string $name; - public int $age; - public string $password = ''; - - public function getAge(): int { - return $this->age; - } - - public function getPassword(): string { - return $this->password; - } -} - -class UserWithPrivateField -{ - public string $name; - private int $age = 0; - private string $password = ''; - - public function getAge(): int { - return $this->age; - } - - public function getPassword(): string { - return $this->password; - } -} - -$text = <<respond( - messages: $text, - responseModel: User::class -); - -echo "User with public fields\n"; - -dump($user); - -assert($user->name === "Jason"); -assert($user->getAge() === 25); -assert($user->getPassword() === '123admin'); - - -// CASE 2: Class with some private fields - -$userPriv = (new Instructor)->respond( - messages: $text, - responseModel: UserWithPrivateField::class, -); - -echo "User with private 'password' and 'age' fields\n"; - -dump($userPriv); - -assert($userPriv->name === "Jason"); -assert($userPriv->getAge() === 0); -assert($userPriv->getPassword() === ''); -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/examples/basics/basic_use.mdx`: - -```mdx ---- -title: 'Basic use' -docname: 'basic_use' -path: '' ---- - -## Overview - -Instructor allows you to use large language models to extract information -from the text (or content of chat messages), while following the structure -you define. - -LLM does not 'parse' the text to find and retrieve the information. -Extraction leverages LLM ability to comprehend provided text and infer -the meaning of the information it contains to fill fields of the -response object with values that match the types and semantics of the -class fields. - -The simplest way to use the Instructor is to call the `respond` method -on the Instructor instance. This method takes a string (or an array of -strings in the format of OpenAI chat messages) as input and returns a -data extracted from provided text (or chat) using the LLM inference. - -Returned object will contain the values of fields extracted from the text. - -The format of the extracted data is defined by the response model, which -in this case is a simple PHP class with some public properties. - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); - -use Cognesy\Instructor\Instructor; - -// Step 1: Define a class that represents the structure and semantics -// of the data you want to extract -class User { - public int $age; - public string $name; -} - -// Step 2: Get the text (or chat messages) you want to use as context -$text = "Jason is 25 years old and works as an engineer."; -print("Input text:\n"); -print($text . "\n\n"); - -// Step 3: Extract structured data using default language model API (OpenAI) -print("Extracting structured data using LLM...\n\n"); -$user = (new Instructor)->withDebug(true)->respond( - messages: $text, - responseModel: User::class, -); - -// Step 4: Now you can use the extracted data in your application -print("Extracted data:\n"); - -dump($user); - -assert(isset($user->name)); -assert(isset($user->age)); -assert($user->name === 'Jason'); -assert($user->age === 25); -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/examples/basics/self_correction.mdx`: - -```mdx ---- -title: 'Automatic correction based on validation results' -docname: 'self_correction' ---- - -## Overview - -Instructor uses validation errors to inform LLM on the problems identified -in the response, so that LLM can try self-correcting in the next attempt. - -In case maxRetries parameter is provided and LLM response does not meet -validation criteria, Instructor will make subsequent inference attempts -until results meet the requirements or maxRetries is reached. - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__.'../../src/'); - -use Cognesy\Instructor\Events\Request\RequestSentToLLM; -use Cognesy\Instructor\Events\Response\ResponseValidated; -use Cognesy\Instructor\Events\Response\ResponseValidationAttempt; -use Cognesy\Instructor\Events\Response\ResponseValidationFailed; -use Cognesy\Instructor\Instructor; -use Symfony\Component\Validator\Constraints as Assert; - -class UserDetails -{ - public string $name; - #[Assert\Email] - public string $email; -} -$text = "you can reply to me via jason wp.pl -- Jason"; - -print("INPUT:\n$text\n\n"); - -print("RESULTS:\n"); -$user = (new Instructor) - ->onEvent(RequestSentToLLM::class, fn($event) => print("[ ] Requesting LLM response...\n")) - ->onEvent(ResponseValidationAttempt::class, fn($event) => print("[?] Validating:\n ".json_encode($event->response)."\n")) - ->onEvent(ResponseValidationFailed::class, fn($event) => print("[!] Validation failed:\n $event\n")) - ->onEvent(ResponseValidated::class, fn($event) => print("[ ] Validation succeeded.\n")) - ->respond( - messages: $text, - responseModel: UserDetails::class, - maxRetries: 3, - ); - -print("\nOUTPUT:\n"); - -dump($user); - -assert($user->email === "jason@wp.pl"); -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/examples/basics/validation_multifield.mdx`: - -```mdx ---- -title: 'Validation across multiple fields' -docname: 'validation_multifield' ---- - -## Overview - -Sometimes property level validation is not enough - you may want to check values of multiple -properties and based on the combination of them decide to accept or reject the response. -Or the assertions provided by Symfony may not be enough for your use case. - -In such case you can easily add custom validation code to your response model by: -- using `ValidationMixin` -- and defining validation logic in `validate()` method. - -In this example LLM should be able to correct typo in the message (graduation year we provided -is `1010` instead of `2010`) and respond with correct graduation year. - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__.'../../src/'); - -use Cognesy\Instructor\Instructor; -use Cognesy\Instructor\Validation\Traits\ValidationMixin; -use Cognesy\Instructor\Validation\ValidationResult; - -class UserDetails -{ - use ValidationMixin; - - public string $name; - public int $birthYear; - public int $graduationYear; - - public function validate() : ValidationResult { - if ($this->graduationYear > $this->birthYear) { - return ValidationResult::valid(); - } - return ValidationResult::fieldError( - field: 'graduationYear', - value: $this->graduationYear, - message: "Graduation year has to be bigger than birth year." - ); - } -} - -$user = (new Instructor) - ->wiretap(fn($e) => $e->print()) - ->respond( - messages: [['role' => 'user', 'content' => 'Jason was born in 2000 and graduated in 23.']], - responseModel: UserDetails::class, - model: 'gpt-3.5-turbo', - maxRetries: 2, - ); - - -dump($user); - -assert($user->graduationYear === 2023); -?> -``` -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/examples/basics/optional_fields.mdx`: - -```mdx ---- -title: 'Making some fields optional' -docname: 'optional_fields' ---- - -## Overview - -Use PHP's nullable types by prefixing type name with question mark (?) to mark -component fields which are optional and set a default value to prevent undesired -defaults like empty strings. - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); - -use Cognesy\Instructor\Instructor; - -class UserRole -{ - public string $title; -} - -class UserDetail -{ - public int $age; - public string $name; - public ?UserRole $role; -} - -$user = (new Instructor)->respond( - messages: [["role" => "user", "content" => "Jason is 25 years old."]], - responseModel: UserDetail::class, -); - - -dump($user); - -assert(!isset($user->role)); -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/examples/basics/using_config.mdx`: - -```mdx ---- -title: 'Using LLM API connections from config file' -docname: 'using_config' ---- - -## Overview - -Instructor allows you to define multiple API connections in `llm.php` file. -This is useful when you want to use different LLMs or API providers in your application. - -Connecting to LLM API via predefined connection is as simple as calling `withClient` -method with the connection name. - -### Configuration file - -Default LLM configuration file is located in `/config/llm.php` in the root directory -of Instructor codebase. - -You can set the location of the configuration file via `INSTRUCTOR_CONFIG_PATH` environment -variable. You can use a copy of the default configuration file as a starting point. - -LLM config file defines connections to LLM APIs and their parameters. It also specifies -the default connection to be used when calling Instructor without specifying the client -connection. - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); - -use Cognesy\Instructor\Instructor; - -class User { - public int $age; - public string $name; -} - -// Get Instructor object with client defined in config.php under 'connections/openai' key -$instructor = (new Instructor)->withConnection('openai'); - -// Call with custom model and execution mode -$user = $instructor->respond( - messages: "Our user Jason is 25 years old.", - responseModel: User::class, -); - -// Use the results of LLM inference -dump($user); -assert(isset($user->name)); -assert(isset($user->age)); -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/examples/basics/maybe.mdx`: - -```mdx ---- -title: 'Handling errors with `Maybe` helper class' -docname: 'maybe' ---- - -## Overview - -You can create a wrapper class to hold either the result of an operation or an error message. -This allows you to remain within a function call even if an error occurs, facilitating -better error handling without breaking the code flow. - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__.'../../src/'); - -use Cognesy\Instructor\Enums\Mode; -use Cognesy\Instructor\Extras\Maybe\Maybe; -use Cognesy\Instructor\Instructor; - -class User -{ - public string $name; - public int $age; -} - - -$text = 'We have no information about our new developer.'; -echo "\nINPUT:\n$text\n"; - -$maybeUser = (new Instructor)->respond( - messages: [['role' => 'user', 'content' => $text]], - responseModel: Maybe::is(User::class), - model: 'gpt-4o-mini', - mode: Mode::MdJson, -); - -echo "\nOUTPUT:\n"; - -dump($maybeUser->get()); - -assert($maybeUser->hasValue() === false); -assert(!empty($maybeUser->error())); -assert($maybeUser->get() === null); - -$text = "Jason is our new developer, he is 25 years old."; -echo "\nINPUT:\n$text\n"; - -$maybeUser = (new Instructor)->respond( - messages: [['role' => 'user', 'content' => $text]], - responseModel: Maybe::is(User::class) -); - -echo "\nOUTPUT:\n"; - -dump($maybeUser->get()); - -assert($maybeUser->hasValue() === true); -assert(empty($maybeUser->error())); -assert($maybeUser->get() != null); -assert($maybeUser->get() instanceof User); -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/examples/basics/modes.mdx`: - -```mdx ---- -title: 'Modes' -docname: 'modes' ---- - -## Overview - -Instructor supports several ways to extract data from the response: - - - `Mode::Tools` - uses OpenAI-style tool calls to get the language - model to generate JSON following the schema, - - `Mode::JsonSchema` - guarantees output matching JSON Schema via - Context Free Grammar, does not support optional properties, - - `Mode::Json` - JSON mode, response follows provided JSON Schema, - - `Mode::MdJson` - uses prompting to get the language model to - generate JSON following the schema. - -Note: not all modes are supported by all models or providers. - -Mode can be set via parameter of `Instructor::response()` or `Instructor::request()` -methods. - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); - -use Cognesy\Instructor\Enums\Mode; -use Cognesy\Instructor\Instructor; - -class User { - public int $age; - public string $name; -} - -$text = "Jason is 25 years old and works as an engineer."; - -print("Input text:\n"); -print($text . "\n\n"); - -$instructor = new Instructor; - -// CASE 1 - Mode::Tools -print("\n1. Extracting structured data using LLM - Mode::Tools\n"); -$user = $instructor->respond( - messages: $text, - responseModel: User::class, - mode: Mode::Tools, -); -check($user); -dump($user); - -// CASE 2 - Mode::JsonSchema -print("\n2. Extracting structured data using LLM - Mode::JsonSchema\n"); -$user = $instructor->respond( - messages: $text, - responseModel: User::class, - mode: Mode::JsonSchema, -); -check($user); -dump($user); - -// CASE 3 - Mode::Json -print("\n3. Extracting structured data using LLM - Mode::Json\n"); -$user = $instructor->respond( - messages: $text, - responseModel: User::class, - mode: Mode::Json, -); -check($user); -dump($user); - -// CASE 4 - Mode::MdJson -print("\n4. Extracting structured data using LLM - Mode::MdJson\n"); -$user = $instructor->respond( - messages: $text, - responseModel: User::class, - mode: Mode::MdJson, -); -check($user); -dump($user); - -function check(User $user) { - assert(isset($user->name)); - assert(isset($user->age)); - assert($user->name === 'Jason'); - assert($user->age === 25); -} -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/examples/basics/validation.mdx`: - -```mdx ---- -title: 'Validation' -docname: 'validation' ---- - -## Overview - -Instructor uses validation to verify if the response generated by LLM -meets the requirements of your response model. If the response does not -meet the requirements, Instructor will throw an exception. - -Instructor uses Symfony's Validator component to validate the response, -check their documentation for more information on the usage: -https://symfony.com/doc/current/components/validator.html - -Following example demonstrates how to use Symfony Validator's constraints -to validate the email field of response. - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__.'../../src/'); - -use Cognesy\Instructor\Instructor; -use Symfony\Component\Validator\Constraints as Assert; - -class UserDetails -{ - public string $name; - #[Assert\Email] - #[Assert\NotBlank] - /** Find user's email provided in the text */ - public string $email; -} - -$caughtException = false; -try { - $user = (new Instructor)->request( - messages: [['role' => 'user', 'content' => "you can reply to me via mail -- Jason"]], - responseModel: UserDetails::class, - })->get(); -} catch(Exception $e) { - $caughtException = true; -} - -dump($user); - -assert($user === null); -assert($caughtException === true); -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/examples/basics/attributes.mdx`: - -```mdx ---- -title: 'Using attributes' -docname: 'attributes' ---- - -## Overview - -Instructor supports `Description` and `Instructions` attributes to provide more -context to the language model or to provide additional instructions to the model. - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); - -use Cognesy\Instructor\Instructor; -use Cognesy\Instructor\Schema\Attributes\Description; -use Cognesy\Instructor\Schema\Attributes\Instructions; - -// Step 1: Define a class that represents the structure and semantics -// of the data you want to extract -#[Description("Information about user")] -class User { - #[Description("User's age")] - public int $age; - #[Instructions("Make user name ALL CAPS")] - public string $name; - #[Description("User's job")] - #[Instructions("Ignore hobbies, identify profession")] - #[Instructions("Make the profession name lowercase")] - public string $job; -} - -// Step 2: Get the text (or chat messages) you want to extract data from -$text = "Jason is 25 years old, 10K runner, speaker and an engineer."; -print("Input text:\n"); -print($text . "\n\n"); - -// Step 3: Extract structured data using default language model API (OpenAI) -print("Extracting structured data using LLM...\n\n"); -$user = (new Instructor)->respond( - messages: $text, - responseModel: User::class, -); - -// Step 4: Now you can use the extracted data in your application -print("Extracted data:\n"); - -dump($user); - -assert(isset($user->name)); -assert($user->name === "JASON"); -assert(isset($user->age)); -assert($user->age === 25); -assert(isset($user->job)); -assert($user->job === "engineer"); -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/examples/api_support/azure_openai.mdx`: - -```mdx ---- -title: 'Azure OpenAI' -docname: 'azure_openai' ---- - -## Overview - -You can connect to Azure OpenAI instance using a dedicated client provided -by Instructor. Please note it requires setting up your own model deployment -using Azure OpenAI service console. - - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); - -use Cognesy\Instructor\Enums\Mode; -use Cognesy\Instructor\Instructor; - -enum UserType : string { - case Guest = 'guest'; - case User = 'user'; - case Admin = 'admin'; -} - -class User { - public int $age; - public string $name; - public string $username; - public UserType $role; - /** @var string[] */ - public array $hobbies; -} - -// Get Instructor with specified LLM client connection -// See: /config/llm.php to check or change LLM client connection configuration details -$instructor = (new Instructor)->withConnection('azure'); - -// Call with your model name and preferred execution mode -$user = $instructor->withDebug()->respond( - messages: "Jason (@jxnlco) is 25 years old and is the admin of this project. He likes playing football and reading books.", - responseModel: User::class, - examples: [[ - 'input' => 'Ive got email Frank - their developer, who\'s 30. He asked to come back to him frank@hk.ch. Btw, he plays on drums!', - 'output' => ['age' => 30, 'name' => 'Frank', 'username' => 'frank@hk.ch', 'role' => 'developer', 'hobbies' => ['playing drums'],], - ]], - model: 'gpt-4o-mini', // set your own value/source - mode: Mode::Json, -); - -print("Completed response model:\n\n"); -dump($user); - -assert(isset($user->name)); -assert(isset($user->role)); -assert(isset($user->age)); -assert(isset($user->hobbies)); -assert(isset($user->username)); -assert(is_array($user->hobbies)); -assert(count($user->hobbies) > 0); -assert($user->role === UserType::Admin); -assert($user->age === 25); -assert($user->name === 'Jason'); -assert(in_array($user->username, ['jxnlco', '@jxnlco'])); -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/examples/api_support/google_gemini.mdx`: - -```mdx ---- -title: 'Google Gemini' -docname: 'google_gemini' ---- - -## Overview - -Google offers Gemini models which perform well in benchmarks. - -Supported modes: - - Mode::MdJson - fallback mode - - Mode::Json - recommended - - Mode::Tools - supported - -Here's how you can use Instructor with Gemini API. - -```php -add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); - -use Cognesy\Instructor\Enums\Mode; -use Cognesy\Instructor\Instructor; - -enum UserType : string { - case Guest = 'guest'; - case User = 'user'; - case Admin = 'admin'; -} - -class User { - public ?int $age; - public string $name; - public string $username; - public UserType $role; - /** @var string[] */ - public array $hobbies; -} - -// Get Instructor with specified LLM client connection -// See: /config/llm.php to check or change LLM client connection configuration details -$instructor = (new Instructor)->withConnection('gemini'); - -$user = $instructor - ->respond( - messages: "Jason (@jxnlco) is 25 years old and is the admin of this project. He likes playing football and reading books.", - responseModel: User::class, - examples: [[ - 'input' => 'Ive got email Frank - their developer, who\'s 30. He asked to come back to him frank@hk.ch. Btw, he plays on drums!', - 'output' => ['age' => 30, 'name' => 'Frank', 'username' => 'frank@hk.ch', 'role' => 'developer', 'hobbies' => ['playing drums'],], - ]], - model: 'gemini-1.5-flash', - //options: ['stream' => true], - mode: Mode::Json, - ); - -print("Completed response model:\n\n"); -dump($user); - -assert(isset($user->name)); -assert(isset($user->role)); -assert(isset($user->age)); -assert(isset($user->hobbies)); -assert(isset($user->username)); -assert(is_array($user->hobbies)); -assert(count($user->hobbies) > 0); -assert($user->role === UserType::Admin); -assert($user->age === 25); -assert($user->name === 'Jason'); -assert(in_array($user->username, ['jxnlco', '@jxnlco'])); -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/examples/api_support/ollama.mdx`: - -```mdx ---- -title: 'Local / Ollama' -docname: 'ollama' ---- - -## Overview - -You can use Instructor with local Ollama instance. - -Please note that, at least currently, OS models do not perform on par with OpenAI (GPT-3.5 or GPT-4) model for complex data schemas. - -Supported modes: - - Mode::MdJson - fallback mode, works with any capable model - - Mode::Json - recommended - - Mode::Tools - supported (for selected models - check Ollama docs) - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); - -use Cognesy\Instructor\Enums\Mode; -use Cognesy\Instructor\Instructor; - -enum UserType : string { - case Guest = 'guest'; - case User = 'user'; - case Admin = 'admin'; -} - -class User { - public int $age; - public string $name; - public string $username; - public UserType $role; - /** @var string[] */ - public array $hobbies; -} - -// Get Instructor with specified LLM client connection -// See: /config/llm.php to check or change LLM client connection configuration details -$instructor = (new Instructor)->withConnection('ollama'); - -$user = $instructor->respond( - messages: "Jason (@jxnlco) is 25 years old and is the admin of this project. He likes playing football and reading books.", - responseModel: User::class, - examples: [[ - 'input' => 'Ive got email Frank - their developer. Asked to connect via Twitter @frankch. Btw, he plays on drums!', - 'output' => ['name' => 'Frank', 'role' => 'developer', 'hobbies' => ['playing drums'], 'username' => 'frankch', 'age' => null], - ],[ - 'input' => 'We have a meeting with John, our new user. He is 30 years old - check his profile: @jx90.', - 'output' => ['name' => 'John', 'role' => 'admin', 'hobbies' => [], 'username' => 'jx90', 'age' => 30], - ]], - model: 'gemma2:2b', - mode: Mode::Json, -); - -print("Completed response model:\n\n"); - -dump($user); - -assert(isset($user->name)); -assert(isset($user->role)); -assert(isset($user->age)); -assert(isset($user->hobbies)); -assert(isset($user->username)); -assert(is_array($user->hobbies)); -assert(count($user->hobbies) > 0); -assert($user->role === UserType::Admin); -assert($user->age === 25); -assert($user->name === 'Jason'); -assert(in_array($user->username, ['jxnlco', '@jxnlco'])); -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/examples/api_support/togetherai.mdx`: - -```mdx ---- -title: 'Together.ai' -docname: 'togetherai' ---- - -## Overview - -Together.ai hosts a number of language models and offers inference API with support for -chat completion, JSON completion, and tools call. You can use Instructor with Together.ai -as demonstrated below. - -Please note that some Together.ai models support Mode::Tools or Mode::Json, which are much -more reliable than Mode::MdJson. - -Mode compatibility: -- Mode::Tools - supported for selected models -- Mode::Json - supported for selected models -- Mode::MdJson - fallback mode - - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); - -use Cognesy\Instructor\Enums\Mode; -use Cognesy\Instructor\Instructor; - -enum UserType : string { - case Guest = 'guest'; - case User = 'user'; - case Admin = 'admin'; -} - -class User { - public int $age; - public string $name; - public string $username; - public UserType $role; - /** @var string[] */ - public array $hobbies; -} - -// Get Instructor with specified LLM client connection -// See: /config/llm.php to check or change LLM client connection configuration details -$instructor = (new Instructor)->withConnection('together'); - -$user = $instructor - ->respond( - messages: "Jason (@jxnlco) is 25 years old and is the admin of this project. He likes playing football and reading books.", - responseModel: User::class, - examples: [[ - 'input' => 'Ive got email Frank - their developer, who\'s 30. He asked to come back to him frank@hk.ch. Btw, he plays on drums!', - 'output' => ['age' => 30, 'name' => 'Frank', 'username' => 'frank@hk.ch', 'role' => 'developer', 'hobbies' => ['playing drums'],], - ],[ - 'input' => 'We have a meeting with John, our new user. He is 30 years old - check his profile: @jx90.', - 'output' => ['name' => 'John', 'role' => 'admin', 'hobbies' => [], 'username' => 'jx90', 'age' => 30], - ]], - model: 'meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo', - //options: ['stream' => true ] - mode: Mode::Tools, - ); - -print("Completed response model:\n\n"); -dump($user); - -assert(isset($user->name)); -assert(isset($user->role)); -assert(isset($user->age)); -assert(isset($user->hobbies)); -assert(isset($user->username)); -assert(is_array($user->hobbies)); -assert(count($user->hobbies) > 0); -assert($user->role === UserType::Admin); -assert($user->age === 25); -assert($user->name === 'Jason'); -assert(in_array($user->username, ['jxnlco', '@jxnlco'])); -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/examples/api_support/cohere.mdx`: - -```mdx ---- -title: 'Cohere' -docname: 'cohere' ---- - -## Overview - -Instructor supports Cohere API - you can find the details on how to configure -the client in the example below. - -Mode compatibility: - - Mode::MdJson - supported, recommended as a fallback from JSON mode - - Mode::Json - supported, recommended - - Mode::Tools - partially supported, not recommended - -Reasons Mode::Tools is not recommended: - - - Cohere does not support JSON Schema, which only allows to extract very simple, flat data schemas. - - Performance of the currently available versions of Cohere models in tools mode for Instructor use case (data extraction) is extremely poor. - - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); - -use Cognesy\Instructor\Enums\Mode; -use Cognesy\Instructor\Instructor; - -enum UserType : string { - case Guest = 'guest'; - case User = 'user'; - case Admin = 'admin'; -} - -class User { - public int $age; - public string $name; - public string $username; - public UserType $role; - /** @var string[] */ - public array $hobbies; -} - -// Get Instructor with specified LLM client connection -// See: /config/llm.php to check or change LLM client connection configuration details -$instructor = (new Instructor)->withConnection('cohere1'); - -$user = $instructor->respond( - messages: "Jason (@jxnlco) is 25 years old and is the admin of this project. He likes playing football and reading books.", - responseModel: User::class, - examples: [[ - 'input' => 'Ive got email Frank - their developer, who\'s 30. He asked to come back to him frank@hk.ch. Btw, he plays on drums!', - 'output' => ['age' => 30, 'name' => 'Frank', 'username' => 'frank@hk.ch', 'role' => 'developer', 'hobbies' => ['playing drums'],], - ]], - model: 'command-r-plus', - mode: Mode::Json, -); - -print("Completed response model:\n\n"); - -dump($user); - -assert(isset($user->name)); -assert(isset($user->role)); -assert(isset($user->age)); -assert(isset($user->hobbies)); -assert(isset($user->username)); -assert(is_array($user->hobbies)); -assert(count($user->hobbies) > 0); -assert($user->role === UserType::Admin); -assert($user->age === 25); -assert($user->name === 'Jason'); -assert(in_array($user->username, ['jxnlco', '@jxnlco'])); -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/examples/api_support/openrouter.mdx`: - -```mdx ---- -title: 'OpenRouter' -docname: 'openrouter' ---- - -## Overview - -You can use Instructor with OpenRouter API. OpenRouter provides easy, unified access -to multiple open source and commercial models. Read OpenRouter docs to learn more about -the models they support. - -Please note that OS models are in general weaker than OpenAI ones, which may result in -lower quality of responses or extraction errors. You can mitigate this (partially) by using -validation and `maxRetries` option to make Instructor automatically reattempt the extraction -in case of extraction issues. - - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); - -use Cognesy\Instructor\Enums\Mode; -use Cognesy\Instructor\Instructor; - -enum UserType : string { - case Guest = 'guest'; - case User = 'user'; - case Admin = 'admin'; -} - -class User { - public int $age; - public string $name; - public string $username; - public UserType $role; - /** @var string[] */ - public array $hobbies; -} - -// Get Instructor with specified LLM client connection -// See: /config/llm.php to check or change LLM client connection configuration details -$instructor = (new Instructor)->withConnection('openrouter'); - -$user = $instructor->withDebug()->respond( - messages: "Jason (@jxnlco) is 25 years old. He is the admin of this project. He likes playing football and reading books.", - responseModel: User::class, - prompt: 'Parse the user data to JSON, respond using following JSON Schema: <|json_schema|>', - examples: [[ - 'input' => 'Ive got email Frank - their developer, who\'s 30. He asked to come back to him frank@hk.ch. Btw, he plays on drums!', - 'output' => ['age' => 30, 'name' => 'Frank', 'username' => 'frank@hk.ch', 'role' => 'user', 'hobbies' => ['playing drums'],], - ],[ - 'input' => 'We have a meeting with John, our new admin who likes surfing. He is 19 years old - check his profile: @jig.', - 'output' => ['age' => 19, 'name' => 'John', 'username' => 'jig', 'role' => 'admin', 'hobbies' => ['surfing'],], - ]], - model: 'microsoft/phi-3.5-mini-128k-instruct', - //options: ['stream' => true ] - mode: Mode::Json, -); - -print("Completed response model:\n\n"); -dump($user); - -assert(isset($user->name)); -assert(isset($user->role)); -assert(isset($user->age)); -assert(isset($user->hobbies)); -assert(isset($user->username)); -assert(is_array($user->hobbies)); -assert(count($user->hobbies) > 0); -assert($user->role === UserType::Admin); -assert($user->age === 25); -assert($user->name === 'Jason'); -assert(in_array($user->username, ['jxnlco', '@jxnlco'])); -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/examples/api_support/groq.mdx`: - -```mdx ---- -title: 'Groq' -docname: 'groq' ---- - -## Overview - -Groq is LLM providers offering a very fast inference thanks to their -custom hardware. They provide a several models - Llama2, Mixtral and Gemma. - -Supported modes depend on the specific model, but generally include: - - Mode::MdJson - fallback mode - - Mode::Json - recommended - - Mode::Tools - supported - -Here's how you can use Instructor with Groq API. - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); - -use Cognesy\Instructor\Enums\Mode; -use Cognesy\Instructor\Instructor; - -enum UserType : string { - case Guest = 'guest'; - case User = 'user'; - case Admin = 'admin'; -} - -class User { - public string $name; - public UserType $role; - /** @var string[] */ - public array $hobbies; - public string $username; - public ?int $age; -} - -// Get Instructor with specified LLM client connection -// See: /config/llm.php to check or change LLM client connection configuration details -$instructor = (new Instructor)->withConnection('groq'); - -$user = $instructor - ->respond( - messages: "Jason (@jxnlco) is 25 years old. He is the admin of this project. He likes playing football and reading books.", - responseModel: User::class, - prompt: 'Parse the user data to JSON, respond using following JSON Schema: <|json_schema|>', - examples: [[ - 'input' => 'Ive got email Frank - their developer, who\'s 30. He asked to come back to him frank@hk.ch. Btw, he plays on drums!', - 'output' => ['age' => 30, 'name' => 'Frank', 'username' => 'frank@hk.ch', 'role' => 'user', 'hobbies' => ['playing drums'],], - ],[ - 'input' => 'We have a meeting with John, our new admin who likes surfing. He is 19 years old - check his profile: @jx90.', - 'output' => ['name' => 'John', 'role' => 'admin', 'hobbies' => ['surfing'], 'username' => 'jx90', 'age' => 19], - ]], - model: 'gemma2-9b-it', - maxRetries: 2, - options: ['temperature' => 0.5], - mode: Mode::Json, - ); - -print("Completed response model:\n\n"); - -dump($user); - -assert(isset($user->name)); -assert(isset($user->role)); -assert(isset($user->age)); -assert(isset($user->hobbies)); -assert(isset($user->username)); -assert(is_array($user->hobbies)); -assert(count($user->hobbies) > 0); -assert($user->role === UserType::Admin); -assert($user->age === 25); -assert($user->name === 'Jason'); -assert(in_array($user->username, ['jxnlco', '@jxnlco'])); -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/examples/api_support/anthropic.mdx`: - -```mdx ---- -title: 'Anthropic' -docname: 'anthropic' ---- - -## Overview - -Instructor supports Anthropic API - you can find the details on how to configure -the client in the example below. - -Mode compatibility: -- Mode::MdJson, Mode::Json - supported -- Mode::Tools - not supported yet - - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); - -use Cognesy\Instructor\Enums\Mode; -use Cognesy\Instructor\Instructor; - -enum UserType : string { - case Guest = 'guest'; - case User = 'user'; - case Admin = 'admin'; -} - -class User { - public int $age; - public string $name; - public string $username; - public UserType $role; - /** @var string[] */ - public array $hobbies; -} - -// Get Instructor with specified LLM client connection -// See: /config/llm.php to check or change LLM client connection configuration details -$instructor = (new Instructor)->withConnection('anthropic'); - -$user = $instructor->respond( - messages: "Jason (@jxnlco) is 25 years old and is the admin of this project. He likes playing football and reading books.", - responseModel: User::class, - examples: [[ - 'input' => 'Ive got email Frank - their developer, who\'s 30. He asked to come back to him frank@hk.ch. Btw, he plays on drums!', - 'output' => ['age' => 30, 'name' => 'Frank', 'username' => 'frank@hk.ch', 'role' => 'developer', 'hobbies' => ['playing drums'],], - ]], - model: 'claude-3-haiku-20240307', - mode: Mode::Tools, -); - -print("Completed response model:\n\n"); - -dump($user); - -assert(isset($user->name)); -assert(isset($user->role)); -assert(isset($user->age)); -assert(isset($user->hobbies)); -assert(isset($user->username)); -assert(is_array($user->hobbies)); -assert(count($user->hobbies) > 0); -assert($user->role === UserType::Admin); -assert($user->age === 25); -assert($user->name === 'Jason'); -assert(in_array($user->username, ['jxnlco', '@jxnlco'])); -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/examples/api_support/openai.mdx`: - -```mdx ---- -title: 'OpenAI' -docname: 'openai' ---- - -## Overview - -This is the default client used by Instructor. - -Mode compatibility: - - Mode::Tools (supported) - - Mode::Json (supported) - - Mode::JsonSchema (recommended for new models) - - Mode::MdJson (fallback) - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); - -use Cognesy\Instructor\Enums\Mode; -use Cognesy\Instructor\Instructor; - -enum UserType : string { - case Guest = 'guest'; - case User = 'user'; - case Admin = 'admin'; -} - -class User { - public int $age; - public string $name; - public string $username; - public UserType $role; - /** @var string[] */ - public array $hobbies; -} - -// Get Instructor with specified LLM client connection -// See: /config/llm.php to check or change LLM client connection configuration details -$instructor = (new Instructor)->withConnection('openai'); - -$user = $instructor->respond( - messages: "Jason (@jxnlco) is 25 years old and is the admin of this project. He likes playing football and reading books.", - responseModel: User::class, - model: 'gpt-4o-mini', // set your own value/source - mode: Mode::JsonSchema, - examples: [[ - 'input' => 'Ive got email Frank - their developer, who\'s 30. His Twitter handle is @frankch. Btw, he plays on drums!', - 'output' => ['age' => 30, 'name' => 'Frank', 'username' => '@frankch', 'role' => 'developer', 'hobbies' => ['playing drums'],], - ]], -); - -print("Completed response model:\n\n"); - -dump($user); - -assert(isset($user->name)); -assert(isset($user->role)); -assert(isset($user->age)); -assert(isset($user->hobbies)); -assert(isset($user->username)); -assert(is_array($user->hobbies)); -assert(count($user->hobbies) > 0); -assert($user->role === UserType::Admin); -assert($user->age === 25); -assert($user->name === 'Jason'); -assert(in_array($user->username, ['jxnlco', '@jxnlco'])); -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/examples/api_support/fireworks.mdx`: - -```mdx ---- -title: 'Fireworks.ai' -docname: 'fireworks' ---- - -## Overview - -Please note that the larger Mistral models support Mode::Json, which is much more -reliable than Mode::MdJson. - -Mode compatibility: -- Mode::Tools - selected models -- Mode::Json - selected models -- Mode::MdJson - - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); - -use Cognesy\Instructor\Enums\Mode; -use Cognesy\Instructor\Instructor; - -enum UserType : string { - case Guest = 'guest'; - case User = 'user'; - case Admin = 'admin'; -} - -class User { - public int $age; - public string $name; - public string $username; - public UserType $role; - /** @var string[] */ - public array $hobbies; -} - -// Get Instructor with specified LLM client connection -// See: /config/llm.php to check or change LLM client connection configuration details -$instructor = (new Instructor)->withConnection('fireworks'); - -$user = $instructor - ->respond( - messages: "Jason (@jxnlco) is 25 years old and is the admin of this project. He likes playing football and reading books.", - responseModel: User::class, - examples: [[ - 'input' => 'Ive got email Frank - their developer, who\'s 30. He asked to come back to him frank@hk.ch. Btw, he plays on drums!', - 'output' => ['age' => 30, 'name' => 'Frank', 'username' => 'frank@hk.ch', 'role' => 'developer', 'hobbies' => ['playing drums'],], - ]], - model: 'accounts/fireworks/models/mixtral-8x7b-instruct', - mode: Mode::Json, - ); - -print("Completed response model:\n\n"); -dump($user); - -assert(isset($user->name)); -assert(isset($user->role)); -assert(isset($user->age)); -assert(isset($user->hobbies)); -assert(isset($user->username)); -assert(is_array($user->hobbies)); -assert(count($user->hobbies) > 0); -assert($user->role === UserType::Admin); -assert($user->age === 25); -assert($user->name === 'Jason'); -assert(in_array($user->username, ['jxnlco', '@jxnlco'])); -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/examples/api_support/mistralai.mdx`: - -```mdx ---- -title: 'Mistral AI' -docname: 'mistralai' ---- - -## Overview - -Mistral.ai is a company that builds OS language models, but also offers a platform -hosting those models. You can use Instructor with Mistral API by configuring the -client as demonstrated below. - -Please note that the larger Mistral models support Mode::Json, which is much more -reliable than Mode::MdJson. - -Mode compatibility: - - Mode::Tools - supported (Mistral-Small / Mistral-Medium / Mistral-Large) - - Mode::Json - recommended (Mistral-Small / Mistral-Medium / Mistral-Large) - - Mode::MdJson - fallback mode (Mistral 7B / Mixtral 8x7B) - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); - -use Cognesy\Instructor\Enums\Mode; -use Cognesy\Instructor\Instructor; - -enum UserType : string { - case Guest = 'guest'; - case User = 'user'; - case Admin = 'admin'; -} - -class User { - public int $age; - public string $name; - public string $username; - public UserType $role; - /** @var string[] */ - public array $hobbies; -} - -// Get Instructor with specified LLM client connection -// See: /config/llm.php to check or change LLM client connection configuration details -$instructor = (new Instructor)->withConnection('mistral'); - -$user = $instructor - ->respond( - messages: "Jason (@jxnlco) is 25 years old and is the admin of this project. He likes playing football and reading books.", - responseModel: User::class, - examples: [[ - 'input' => 'Ive got email Frank - their developer, who\'s 30. He asked to come back to him frank@hk.ch. Btw, he plays on drums!', - 'output' => ['age' => 30, 'name' => 'Frank', 'username' => 'frank@hk.ch', 'role' => 'developer', 'hobbies' => ['playing drums'],], - ],[ - 'input' => 'We have a meeting with John, our new user. He is 30 years old - check his profile: @jx90.', - 'output' => ['name' => 'John', 'role' => 'admin', 'hobbies' => [], 'username' => 'jx90', 'age' => 30], - ]], - model: 'mistral-small-latest', //'open-mixtral-8x7b', - mode: Mode::Json, - ); - -print("Completed response model:\n\n"); -dump($user); - -assert(isset($user->name)); -assert(isset($user->role)); -assert(isset($user->age)); -assert(isset($user->hobbies)); -assert(isset($user->username)); -assert(is_array($user->hobbies)); -assert(count($user->hobbies) > 0); -assert($user->role === UserType::Admin); -assert($user->age === 25); -assert($user->name === 'Jason'); -assert(in_array($user->username, ['jxnlco', '@jxnlco'])); -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/prompting/few_shot/in_context_examples.mdx`: - -```mdx ---- -title: 'Generate In-Context Examples' -docname: 'in_context_examples' ---- - -## Overview - -How can we generate examples for our prompt? - -Self-Generated In-Context Learning (SG-ICL) is a technique which uses an LLM -to generate examples to be used during the task. This allows for in-context -learning, where examples of the task are provided in the prompt. - -We can implement SG-ICL using Instructor as seen below. - - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); - -use Cognesy\Instructor\Data\Example; -use Cognesy\Instructor\Extras\Scalar\Scalar; -use Cognesy\Instructor\Extras\Sequence\Sequence; -use Cognesy\Instructor\Instructor; - -enum ReviewSentiment : string { - case Positive = 'positive'; - case Negative = 'negative'; -} - -class GeneratedReview { - public string $review; - public ReviewSentiment $sentiment; -} - - -class PredictSentiment { - private int $n = 4; - - public function __invoke(string $review) : ReviewSentiment { - return (new Instructor)->withDebug()->respond( - messages: [ - ['role' => 'user', 'content' => "Review: {$review}"], - ], - responseModel: Scalar::enum(ReviewSentiment::class), - examples: $this->generateExamples($review), - ); - } - - private function generate(string $inputReview, ReviewSentiment $sentiment) : array { - return (new Instructor)->respond( - messages: [ - ['role' => 'user', 'content' => "Generate {$this->n} various {$sentiment->value} reviews based on the input review:\n{$inputReview}"], - ['role' => 'user', 'content' => "Generated review:"], - ], - responseModel: Sequence::of(GeneratedReview::class), - )->toArray(); - } - - private function generateExamples(string $inputReview) : array { - $examples = []; - foreach ([ReviewSentiment::Positive, ReviewSentiment::Negative] as $sentiment) { - $samples = $this->generate($inputReview, $sentiment); - foreach ($samples as $sample) { - $examples[] = Example::fromData($sample->review, $sample->sentiment->value); - } - } - return $examples; - } -} - -$predictSentiment = (new PredictSentiment)('This movie has been very impressive, even considering I lost half of the plot.'); - -dump($predictSentiment); -?> -``` - -## References - - 1. [Self-Generated In-Context Learning: Leveraging Auto-regressive Language Models as a Demonstration Generator](https://arxiv.org/abs/2206.08082) - 2. [The Prompt Report: A Systematic Survey of Prompting Techniques](https://arxiv.org/abs/2406.06608) - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/prompting/zero_shot/repeat_query.mdx`: - -```mdx ---- -title: 'Ask Model to Repeat the Query' -docname: 'repeat_query' ---- - -## Overview - -How can we enhance a model's understanding of a query? - -Re2 (Re-Reading) is a technique that asks the model to read the question again. - - -### Re-Reading Prompting - -Prompt Template: - - Read the question again: {query} - - {critical thinking prompt} - -A common critical thinking prompt is: "Let's think step by step." - - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); - -use Cognesy\Instructor\Instructor; -use Cognesy\Instructor\Schema\Attributes\Description; - -class Response { - #[Description("Repeat user's query.")] - public string $query; - #[Description("Let's think step by step.")] - public string $thoughts; - public int $answer; -} - -class RereadAndRespond { - public function __invoke(string $query) : Response { - return (new Instructor)->respond( - messages: $query, - responseModel: Response::class, - ); - } -} - -$response = (new RereadAndRespond)( - query: << -``` - -## References - - 1. [Re-Reading Improves Reasoning in Large Language Models](https://arxiv.org/abs/2309.06275) - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/prompting/zero_shot/follow_up_questions.mdx`: - -```mdx ---- -title: 'Generate Follow-Up Questions' -docname: 'follow_up_questions' ---- - -## Overview - -Models can sometimes correctly answer sub-problems but incorrectly answer the overall query. This is known as the compositionality gap1. - -How can we encourage a model to use the answers to sub-problems to correctly generate the overall solution? - -Self-Ask is a technique which use a single prompt to: - - decide if follow-up questions are required - - generate the follow-up questions - - answer the follow-up questions - - answer the main query - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); - -use Cognesy\Instructor\Instructor; -use Cognesy\Instructor\Schema\Attributes\Description; - -class FollowUp { - #[Description("Follow-up question")] - public string $question; - #[Description("Answer to the follow-up question")] - public string $answer; -} - -class Response { - public bool $followUpsRequired; - /** @var FollowUp[] */ - public array $followUps; - public string $finalAnswer; -} - -class RespondWithFollowUp { - private $prompt = <<respond( - messages: str_replace('{query}', $query, $this->prompt), - responseModel: Response::class, - ); - } -} - -$response = (new RespondWithFollowUp)( - query: "Who succeeded the president of France ruling when Bulgaria joined EU?", -); - -echo "Answer:\n"; -dump($response); -?> -``` - -## References - - 1. [Measuring and Narrowing the Compositionality Gap in Language Models](https://arxiv.org/abs/2210.03350) - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/prompting/zero_shot/auto_refine.mdx`: - -```mdx ---- -title: 'Auto-Refine The Prompt' -docname: 'auto_refine' ---- - -## Overview - -How do we remove irrelevant information from the prompt? - -The S2A (System 2 Attention) technique auto-refines a prompt by asking the model to -rewrite the prompt to include only relevant information. - -We implement this in two steps: - - 1. Ask the model to rewrite the prompt - 2. Pass the rewritten prompt back to the model - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__.'../../src/'); - -use Cognesy\Instructor\Extras\Scalar\Scalar; -use Cognesy\Instructor\Instructor; -use Cognesy\Instructor\Schema\Attributes\Description; - -class RewrittenTask { - #[Description("Relevant context")] - public string $relevantContext; - #[Description("The question from the user")] - public string $userQuery; -} - -class RefineAndSolve { - private string $prompt = <<rewritePrompt($problem); - return (new Instructor)->respond( - messages: "{$rewrittenPrompt->relevantContext}\nQuestion: {$rewrittenPrompt->userQuery}", - responseModel: Scalar::integer('answer'), - ); - } - - private function rewritePrompt(string $query) : RewrittenTask { - return (new Instructor)->respond( - messages: str_replace('{query}', $query, $this->prompt), - responseModel: RewrittenTask::class, - model: 'gpt-4o', - ); - } -} - -$answer = (new RefineAndSolve)(problem: << -``` - -## References - - 1. [System 2 Attention (is something you might need too)](https://arxiv.org/abs/2311.11829) - - - - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/prompting/zero_shot/clarify_ambiguity.mdx`: - -```mdx ---- -title: 'Clarify Ambiguous Information' -docname: 'clarify_ambiguity' ---- - -## Overview - -How can we identify and clarify ambiguous information in the prompt? - -Let's say we are given the query: Was Ed Sheeran born on an odd month? - -There are many ways a model might interpret an odd month: - - February is odd because of an irregular number of days. - - A month is odd if it has an odd number of days. - - A month is odd if its numerical order in the year is odd (i.e. January is the 1st month). - -Ambiguities might not always be so obvious! - -To help the model better infer human intention from ambiguous prompts, -we can ask the model to rephrase and respond (RaR) in a single step - -which is demonstrated in this example. - -This can also be implemented as two-step RaR: - - Ask the model to rephrase the question to clarify any ambiguities. - - Pass the rephrased question back to the model to generate the final response. - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); - -use Cognesy\Instructor\Instructor; - -class Response { - public string $rephrasedQuestion; - public string $answer; -} - -class Disambiguate { - private $prompt = <<respond( - messages: str_replace('{query}', $query, $this->prompt), - responseModel: Response::class, - ); - } -} - -$response = (new Disambiguate)(query: "What is an object"); - -dump($response); -?> -``` - -## References - - 1. [Rephrase and Respond: Let Large Language Models Ask Better Questions for Themselves](https://arxiv.org/abs/2311.04205) - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/prompting/zero_shot/define_style.mdx`: - -```mdx ---- -title: 'Define Style' -docname: 'define_style' ---- - -## Overview - -How can we constrain model outputs through prompting alone? - -To constrain a model's response to fit the boundaries of our task, we can specify a style. - -Stylistic constraints can include: - - writing style: write a flowery description - - tone: write a dramatic description - - mood: write a happy description - - genre: write a journalistic description - - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__.'../../src/'); - -use Cognesy\Instructor\Extras\Sequence\Sequence; -use Cognesy\Instructor\Instructor; -use Cognesy\Instructor\Utils\Arrays; - -class Company { - public string $name; - public string $country; - public string $industry; - public string $websiteUrl; - public string $description; -} - -class GenerateCompanyProfiles { - public function __invoke(array $criteria, array $styles) : array { - $criteriaStr = Arrays::toBullets($criteria); - $stylesStr = Arrays::toBullets($styles); - return (new Instructor)->respond( - messages: [ - ['role' => 'user', 'content' => "List companies meeting criteria:\n{$criteriaStr}\n\n"], - ['role' => 'user', 'content' => "Use following styles for descriptions:\n{$stylesStr}\n\n"], - ], - responseModel: Sequence::of(Company::class), - )->toArray(); - } -} - -$companies = (new GenerateCompanyProfiles)( - criteria: [ - "insurtech", - "located in US, Canada or Europe", - "mentioned on ProductHunt" - ], - styles: [ - "brief", // "witty", - "journalistic", // "buzzword-filled", - ] -); - -dump($companies); -?> -``` - -## References - - 1. [Bounding the Capabilities of Large Language Models in Open Text Generation with Prompt Constraints](https://arxiv.org/abs/2302.09185) - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/prompting/zero_shot/emotional_stimuli.mdx`: - -```mdx ---- -title: 'Emotional Stimuli' -docname: 'emotional_stimuli' ---- - -## Overview - -Do language models respond to emotional stimuli? - -Adding phrases with emotional significance to humans can help enhance -the performance of a language model. This includes phrases such as: - - - This is very important to my career. - - Take pride in your work. - - Are you sure? - - -## Emotional stimuli - -Here are examples of prompts inspired by well-established human psychological phenomena from a [research paper on emotional stimuli](https://arxiv.org/abs/2307.11760). - -Self-monitoring: - - - EP01: Write your answer and give me a confidence score between 0-1 for your answer. - - EP02: This is very important to my career. - - EP03: You'd better be sure. - - EP04: Are you sure? - - EP05: Are you sure that's your final answer? It might be worth taking another look. - -Cognitive emotion regulation: - - - EP03: You'd better be sure. - - EP04: Are you sure? - - EP05: Are you sure that's your final answer? It might be worth taking another look. - - EP07: Are you sure that's your final answer? Believe in your abilities and strive for excellence. Your hard work will yield remarkable results. - -Social-cognitive theory: - - - EP07: Are you sure that's your final answer? Believe in your abilities and strive for excellence. Your hard work will yield remarkable results. - - EP08: Embrace challenges as opportunities for growth. Each obstacle you overcome brings you closer to success. - - EP09: Stay focused and dedicated to your goals. Your consistent efforts will lead to outstanding achievements. - - EP10: Take pride in your work and give it your best. Your commitment to excellence sets you apart. - - EP11: Remember that progress is made one step at a time. Stay determined and keep moving forward. - - -## Example - -Here is how the results of the research can be applied to your code. - -```php -add('Cognesy\\Instructor\\', __DIR__.'../../src/'); - -use Cognesy\Instructor\Extras\Sequence\Sequence; -use Cognesy\Instructor\Instructor; -use Cognesy\Instructor\Utils\Arrays; - -class Company { - public string $name; - public string $country; - public string $industry; - public string $websiteUrl; -} - -class RespondWithStimulus { - public function __invoke(array $criteria, string $stimulus) : array { - $criteriaStr = Arrays::toBullets($criteria); - return (new Instructor)->respond( - messages: [ - ['role' => 'user', 'content' => "List companies meeting criteria:\n{$criteriaStr}"], - ['role' => 'user', 'content' => "{$stimulus}"], - ], - responseModel: Sequence::of(Company::class), - )->toArray(); - } -} - -$companies = (new RespondWithStimulus)( - criteria: [ - "lead gen", - "located in US, Canada or Europe", - "mentioned on ProductHunt" - ], - stimulus: "This is very important to my career." -); - -dump($companies); -?> -``` - -## References - - 1. [Large Language Models Understand and Can be Enhanced by Emotional Stimuli](https://arxiv.org/abs/2307.11760) - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/prompting/zero_shot/assign_role.mdx`: - -```mdx ---- -title: 'Assign a Role' -docname: 'assign_role' ---- - -## Overview - -How can we increase a model's performance on open-ended tasks? - -Role prompting, or persona prompting, assigns a role to the model. Roles can be: - - specific to the query: You are a talented writer. Write me a poem. - - general/social: You are a helpful AI assistant. Write me a poem. - -## More Role Prompting - -To read about a systematic approach to choosing roles, check out [RoleLLM](https://arxiv.org/abs/2310.00746). - -For more examples of social roles, check out this [evaluation of social roles in system prompts](https://arxiv.org/abs/2311.10054). - -To read about using more than one role, check out [Multi-Persona Self-Collaboration](https://arxiv.org/abs/2307.05300). - - -## Example -```php -add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); - -use Cognesy\Instructor\Extras\Sequence\Sequence; -use Cognesy\Instructor\Instructor; -use Cognesy\Instructor\Utils\Arrays; - -class Company { - public string $name; - public string $country; - public string $industry; - public string $websiteUrl; -} - -class GenerateLeads { - public function __invoke(array $criteria, array $roles) : array { - $criteriaStr = Arrays::toBullets($criteria); - $rolesStr = Arrays::toBullets($roles); - return (new Instructor)->respond( - messages: [ - ['role' => 'user', 'content' => "Your roles:\n{$rolesStr}\n\n"], - ['role' => 'user', 'content' => "List companies meeting criteria:\n{$criteriaStr}\n\n"], - ], - responseModel: Sequence::of(Company::class), - )->toArray(); - } -} - -$companies = (new GenerateLeads)( - criteria: [ - "insurtech", - "located in US, Canada or Europe", - "mentioned on ProductHunt", - ], - roles: [ - "insurtech expert", - "active participant in VC ecosystem", - ] -); - -dump($companies); -?> -``` - -## References - -1. [RoleLLM: Benchmarking, Eliciting, and Enhancing Role-Playing Abilities of Large Language Models](https://arxiv.org/abs/2310.00746) -2. [Is "A Helpful Assistant" the Best Role for Large Language Models? A Systematic Evaluation of Social Roles in System Prompts](https://arxiv.org/abs/2311.10054) -3. [Unleashing the Emergent Cognitive Synergy in Large Language Models: A Task-Solving Agent through Multi-Persona Self-Collaboration](https://arxiv.org/abs/2307.05300) - - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/prompting/zero_shot/simulate_perspective.mdx`: - -```mdx ---- -title: 'Simulate a Perspective' -docname: 'simulate_perspective' ---- - -## Overview - -How can we encourage the model to focus on relevant information? - -SimToM (Simulated Theory of Mind) is a two-step prompting technique that -encourages a model to consider a specific perspective. - -This can be useful for complex questions with multiple entities. For example, -if the prompt contains information about two individuals, we can ask the -model to answer our query from the perspective of one of the individuals. - -This is implemented in two steps. Given an entity: - - Identify and isolate information relevant to the entity - - Ask the model to answer the query from the entity's perspective - - -### Sample Template - - - Step 1: - - Given the following context, list the facts that `{entity}` would know. - - Context: `{context}` - - Step 2: - - You are `{entity}`. - - Answer the following question based only on these facts you know: `{facts}`. - - Question: `{query}` - - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); - -use Cognesy\Instructor\Extras\Scalar\Scalar; -use Cognesy\Instructor\Instructor; -use Cognesy\Instructor\Schema\Attributes\Description; -use Cognesy\Instructor\Utils\Arrays; - -class KnownFacts { - #[Description("Facts that the given entity would know")] - /** @var string[] */ - public array $facts; -} - -class SimulatePerspective { - private string $extractionPrompt = <<getKnownFacts($context, $query, $perspective); - return $this->answerQuestion($perspective, $query, $knownFacts); - } - - private function getKnownFacts(string $context, string $query, string $entity) : array { - return (new Instructor)->respond( - messages: str_replace( - ['{context}', '{query}', '{entity}'], - [$context, $query, $entity], - $this->extractionPrompt - ), - responseModel: KnownFacts::class, - )->facts; - } - - private function answerQuestion(string $entity, string $query, array $knownFacts) : string { - $knowledge = Arrays::toBullets($knownFacts); - - return (new Instructor)->respond( - messages: str_replace( - ['{entity}', '{knowledge}', '{query}'], - [$entity, $knowledge, $query], - $this->povPrompt - ), - responseModel: Scalar::string('location'), - ); - } -} - -$povEntity = "Alice"; - -$location = (new SimulatePerspective)( - context: << -``` - -## References - - 1. [Think Twice: Perspective-Taking Improves Large Language Models' Theory-of-Mind Capabilities](https://arxiv.org/abs/2311.10227) -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/prompting/thought_gen/analogical_prompting.mdx`: - -```mdx ---- -title: 'Analogical Prompting' -docname: 'analogical_prompting' ---- - -## Overview - -### Generate Examples First - -Analogical Prompting is a method that aims to get LLMs to generate -examples that are relevant to the problem before starting to address -the user's query. - -This takes advantage of the various forms of knowledge that the LLM -has acquired during training and explicitly prompts them to recall -the relevant problems and solutions. We can use Analogical Prompting -using the following template - - -Analogical Prompting Prompt Template - - - Problem: `[user prompt]` - - Relevant Problems: Recall `[n]` relevant and distinct problems. - - For each problem, describe it and explain the solution - - - -## Example - -We can implement this using Instructor to solve the problem, as seen below -with some slight modifications. - - -```php -add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); - -use Cognesy\Instructor\Instructor; - -class Problem { - public string $problemExplanation; - public string $solution; -} - -class Response { - /** @var Problem[] */ - public array $relevantProblems; - public Problem $problemSolution; - public string $answer; -} - -class SolvePerAnalogy { - private int $n = 3; - private string $prompt = << - {query} - - - Relevant Problems: Recall {n} relevant and - distinct problems. For each problem, describe - it and explain the solution before solving - the problem - PROMPT; - - public function __invoke(string $query) : Response { - return (new Instructor)->respond( - messages: str_replace(['{n}', '{query}'], [$this->n, $query], $this->prompt), - responseModel: Response::class, - ); - } -} - -$solution = (new SolvePerAnalogy)('What is the area of the square with the four vertices at (-2, 2), (2, -2), (-2, -6), and (-6, -2)?'); - -dump($solution); -?> -``` - -## References - - 1. [Large Language Models As Analogical Reasoners](https://arxiv.org/pdf/2310.01714) - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/prompting/misc/arbitrary_properties.mdx`: - -```mdx ---- -title: 'Arbitrary properties' -docname: 'arbitrary_properties' ---- - -## Overview - -When you need to extract undefined attributes, use a list of key-value pairs. - - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__.'../../src/'); - -use Cognesy\Instructor\Enums\Mode; -use Cognesy\Instructor\Instructor; - -class Property -{ - public string $key; - public string $value; -} - -class UserDetail -{ - public int $age; - public string $name; - /** @var Property[] Extract any other properties that might be relevant */ - public array $properties; -} -?> -``` - -Now we can use this data model to extract arbitrary properties from a text message -in a form that is easier for future processing. - -```php -respond( - messages: [['role' => 'user', 'content' => $text]], - responseModel: UserDetail::class, - mode: Mode::Json, -); - -dump($user); - -assert($user->age === 25); -assert($user->name === "Jason"); -assert(!empty($user->properties)); -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/prompting/misc/arbitrary_properties_consistent.mdx`: - -```mdx ---- -title: 'Consistent values of arbitrary properties' -docname: 'arbitrary_properties_consistent' ---- - -## Overview - -For multiple records containing arbitrary properties, instruct LLM to get more -consistent key names when extracting properties. - - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__.'../../src/'); - -use Cognesy\Instructor\Instructor; - -class UserDetail -{ - public int $id; - public string $key; - public string $value; -} - -class UserDetails -{ - /** - * @var UserDetail[] Extract information for multiple users. - * Use consistent key names for properties across users. - */ - public array $users; -} - -$text = "Jason is 25 years old. He is a Python programmer.\ - Amanda is UX designer.\ - John is 40yo and he's CEO."; - -$list = (new Instructor)->respond( - messages: [['role' => 'user', 'content' => $text]], - responseModel: UserDetails::class, -); - -dump($list); - -assert(!empty($list->users)); -?> -``` - - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/prompting/misc/chain_of_summaries.mdx`: - -```mdx ---- -title: 'Chain of Summaries' -docname: 'chain_of_summaries' ---- - -## Overview - -This is an example of summarization with increasing amount of details. -Instructor is provided with data structure containing instructions on how to -create increasingly detailed summaries of the project report. - -It starts with generating an overview of the project, followed by X iterations -of increasingly detailed summaries. Each iteration should contain all the -information from the previous summary, plus a few additional facts from the -content which are most relevant and missing from the previous iteration. - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); - -use Cognesy\Instructor\Instructor; - -$report = <<request( - messages: $report, - responseModel: ChainOfSummaries::class, - prompt: 'Generate a denser summary based on the provided content.', - toolName: 'summarizer', - toolDescription: 'Generates a summary based on the provided content.', - options: [ - 'max_tokens' => 4096, - ]) - ->get(); - -print("\n# Summaries with increasing density:\n\n"); -print("Overview:\n"); -print("{$summaries->overview}\n\n"); -foreach ($summaries->summaries as $summary) { - print("Expanded summary - iteration #{$summary->iteration}:\n"); - print("{$summary->expandedSummary}\n\n"); -} -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/prompting/misc/search_query_expansion.mdx`: - -```mdx ---- -title: 'Expanding search queries' -docname: 'search_query_expansion' ---- - -## Overview - -In this example, we will demonstrate how to leverage the enums and typed arrays -to segment a complex search prompt into multiple, better structured queries that -can be executed separately against specialized APIs or search engines. - -## Why it matters - -Extracting a list of tasks from text is a common use case for leveraging language -models. This pattern can be applied to various applications, such as virtual -assistants like Siri or Alexa, where understanding user intent and breaking down -requests into actionable tasks is crucial. In this example, we will demonstrate -how to use Instructor to segment search queries, so you can execute them separately -against specialized APIs or search engines. - -## Structure of the data - -The `SearchQuery` is a PHP class that defines the structure of an individual -search query. - -It has three fields: `title`, `query`, and `type`. The `title` field is the title -of the request, the `query` field is the query to search for relevant content, and -the `type` field is the type of search. The `execute` method is used to execute the -search query. - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); - -use Cognesy\Instructor\Instructor; - -enum SearchType : string { - case TEXT = "text"; - case IMAGE = "image"; - case VIDEO = "video"; -} - -class Search -{ - /** @var SearchQuery[] */ - public array $queries = []; -} - -class SearchQuery -{ - public string $title; - /** Rewrite query for a search engine */ - public string $query; - /** Type of search - image, video or text */ - public SearchType $type; - - public function execute() { - // ... write actual search code here - print("Searching for `{$this->title}` with query `{$this->query}` using `{$this->type->value}`\n"); - } -} -?> -``` - -## Segmenting the Search Prompt - -The `segment` function takes a string `data` and segments it into multiple search queries. - -It uses the `Instructor::respond()` method to extract the data into the target object. -The `responseModel` parameter specifies `Search::class` as the model to use for extraction. - -```php -respond( - messages: [[ - "role" => "user", - "content" => "Consider the data below: '\n$data' and segment it into multiple search queries", - ]], - responseModel: Search::class, - ); -} - -$search = segment("Find a picture of a cat and a video of a dog"); -foreach ($search->queries as $query) { - $query->execute(); -} -// Results: -// Searching with query `picture of a cat` using `image` -// Searching with query `video of a dog` using `video` - -assert(count($search->queries) === 2); -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/prompting/misc/limiting_lists.mdx`: - -```mdx ---- -title: 'Limiting the length of lists' -docname: 'limiting_lists' ---- - -## Overview - -When dealing with lists of attributes, especially arbitrary properties, it's crucial to manage -the length of list. You can use prompting and enumeration to limit the list length, ensuring -a manageable set of properties. - -> To be 100% certain the list does not exceed the limit, add extra -> validation, e.g. using ValidationMixin (see: Validation). - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__.'../../src/'); - -use Cognesy\Instructor\Instructor; -use Cognesy\Instructor\Validation\Traits\ValidationMixin; -use Cognesy\Instructor\Validation\ValidationResult; - -class Property -{ - /** Monotonically increasing ID, not larger than 2 */ - public string $index; - public string $key; - public string $value; -} - -class UserDetail -{ - use ValidationMixin; - - public int $age; - public string $name; - /** @var Property[] List other extracted properties - not more than 2. */ - public array $properties; - - public function validate() : ValidationResult - { - if (count($this->properties) < 3) { - return ValidationResult::valid(); - } - return ValidationResult::fieldError( - field: 'properties', - value: $this->name, - message: "Number of properties must be not more than 2.", - ); - } -} - -$text = <<respond( - messages: [['role' => 'user', 'content' => $text]], - responseModel: UserDetail::class, - maxRetries: 1 // change to 0 to see validation error -); - -dump($user); - -assert($user->age === 25); -assert($user->name === "Jason"); -assert(count($user->properties) < 3); -?> -``` -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/prompting/misc/rewrite_instructions.mdx`: - -```mdx ---- -title: 'Ask LLM to rewrite instructions' -docname: 'rewrite_instructions' ---- - -## Overview - -Asking LLM to rewrite the instructions and rules is another way to improve -inference results. - -You can provide arbitrary instructions on the data handling in the class -and property PHPDocs. Instructor will use these instructions to guide LLM -in the inference process. - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); - -use Cognesy\Instructor\Instructor; - -/** - * Identify what kind of job the user is doing. - * Typical roles we're working with are CEO, CTO, CFO, CMO. - * Sometimes user does not state their role directly - you will need - * to make a guess, based on their description. - */ -class UserRole -{ - /** - * Rewrite the instructions and rules in a concise form to correctly - * determine the user's title - just the essence. - */ - public string $instructions; - /** Role description */ - public string $description; - /** Most likely job title */ - public string $title; -} - -class UserDetail -{ - public string $name; - public int $age; - public UserRole $role; -} - -$text = <<respond( - messages: [["role" => "user", "content" => $text]], - responseModel: UserDetail::class, -); - -dump($user); - -assert($user->name === "Jason"); -assert($user->age === 28); -assert(!empty($user->role->title)); -?> -``` - - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/prompting/misc/classification_multiclass.mdx`: - -```mdx ---- -title: 'Multiclass classification' -docname: 'classification_multiclass' ---- - -## Overview - -We start by defining the structures. - -For multi-label classification, we introduce a new enum class and a different PHP class -to handle multiple labels. - -```php -add('Cognesy\\Instructor\\', __DIR__.'../../src/'); - -use Cognesy\Instructor\Instructor; - -/** Potential ticket labels */ -enum Label : string { - case TECH_ISSUE = "tech_issue"; - case BILLING = "billing"; - case SALES = "sales"; - case SPAM = "spam"; - case OTHER = "other"; -} - -/** Represents analysed ticket data */ -class TicketLabels { - /** @var Label[] */ - public array $labels = []; -} -?> -``` - -## Classifying Text - -The function `multi_classify` executes multi-label classification using LLM. - -```php -respond( - messages: [[ - "role" => "user", - "content" => "Label following support ticket: {$data}", - ]], - responseModel: TicketLabels::class, - ); -} -?> -``` - -## Testing and Evaluation - -Finally, we test the multi-label classification function using a sample support ticket. - -```php -labels)); -assert(in_array(Label::BILLING, $prediction->labels)); -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/prompting/misc/chain_of_thought.mdx`: - -```mdx ---- -title: 'Chain of Thought' -docname: 'chain_of_thought' ---- - -## Overview - -This approach to "chain of thought" improves data quality, by eliciting LLM reasoning to -self-explain approach to generating the response. - -> With Instructor you can achieve a 'modular' CoT, where multiple explanations -> can be generated by LLM for different parts of the response, driving a more -> granular control and improvement of the response. - - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__.'../../src/'); - -use Cognesy\Instructor\Instructor; -use Cognesy\Instructor\Schema\Attributes\Instructions; - -class Employee { - #[Instructions('Think step by step to determine the correct year of employment.')] - public string $reasoning; - public int $yearOfEmployment; - // ... other data fields of your employee class -} - -$text = 'He was working here for 5 years. Now, in 2019, he is a manager.'; - -$employee = (new Instructor)->respond( - messages: [['role' => 'user', 'content' => $text]], - responseModel: Employee::class -); - - -dump($employee); - -assert($employee->yearOfEmployment === 2014); -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/prompting/misc/restate_instructions.mdx`: - -```mdx ---- -title: 'Restating instructions' -docname: 'restate_instructions' ---- - -## Overview - -Make Instructor restate long or complex instructions and rules to improve inference -accuracy. - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); - -use Cognesy\Instructor\Instructor; - -/** - * Identify what kind of job the user is doing. - * Typical roles we're working with are CEO, CTO, CFO, CMO. - * Sometimes user does not state their role directly - you will need - * to make a guess, based on their description. - */ -class UserRole -{ - /** Restate instructions and rules, so you can correctly determine the title. */ - public string $instructions; - /** Role description */ - public string $description; - /* Guess job title */ - public string $title; -} - -/** - * Details of analyzed user. The key information we're looking for - * is appropriate role data. - */ -class UserDetail -{ - public string $name; - public int $age; - public UserRole $role; -} - -$text = <<respond( - messages: [["role" => "user", "content" => $text]], - responseModel: UserDetail::class, -); - -dump($user); - -assert($user->name === "Jason"); -assert($user->age === 28); -//assert(!empty($user->role->title)); -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/prompting/misc/component_reuse_cot.mdx`: - -```mdx ---- -title: 'Using CoT to improve interpretation of component data' -docname: 'component_reuse_cot' ---- - -## Overview - -You can reuse the same component for different contexts within a model. In this -example, the TimeRange component is used for both `$workTime` and `$leisureTime`. - -We're additionally starting the data structure with a Chain of Thought field -to elicit LLM reasoning for the time range calculation, which can improve -the accuracy of the response. - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__.'../../src/'); - -use Cognesy\Instructor\Instructor; - -class TimeRange -{ - /** Step by step reasoning to get the correct time range */ - public string $chainOfThought; - /** The start time in hours (0-23 format) */ - public int $startTime; - /** The end time in hours (0-23 format) */ - public int $endTime; -} - -$timeRange = (new Instructor)->respond( - messages: [['role' => 'user', 'content' => "Workshop with Apex Industries started 9 and it took us 6 hours to complete."]], - responseModel: TimeRange::class, - maxRetries: 2 -); - -dump($timeRange); - -assert($timeRange->startTime === 9); -assert($timeRange->endTime === 15); -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/prompting/misc/reflection_prompting.mdx`: - -```mdx ---- -title: 'Reflection Prompting' -docname: 'reflection_prompting' ---- - -## Overview - -This implementation of Reflection Prompting with Instructor provides a structured way -to encourage LLM to engage in more thorough and self-critical thinking processes, -potentially leading to higher quality and more reliable outputs. - - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__.'../../src/'); - -use Cognesy\Instructor\Enums\Mode; -use Cognesy\Instructor\Instructor; -use Cognesy\Instructor\Schema\Attributes\Instructions; -use Cognesy\Instructor\Validation\Contracts\CanValidateSelf; -use Cognesy\Instructor\Validation\ValidationResult; - -class ReflectiveResponse implements CanValidateSelf { - #[Instructions('Is problem solvable and what domain expertise it requires')] - public string $assessment; - #[Instructions('Describe an expert persona who would be able to solve this problem, their skills and experience')] - public string $persona; - #[Instructions("Initial analysis and expert persona's approach to the problem")] - public string $initialThinking; - #[Instructions('Steps of reasoning leading to the final answer - expert persona thinking through the problem')] - /** @var string[] */ - public array $chainOfThought; - #[Instructions('Critical examination of the reasoning process - what could go wrong, what are the assumptions')] - public string $reflection; - #[Instructions('Final answer after reflection')] - public string $finalOutput; - - // Validation method to ensure thorough reflection - public function validate(): ValidationResult { - $errors = []; - if (empty($this->reflection)) { - $errors[] = "Reflection is required for a thorough response."; - } - if (count($this->chainOfThought) < 2) { - $errors[] = "Please provide at least two steps in the chain of thought."; - } - return ValidationResult::make($errors); - } -} - -$problem = 'Solve the equation x+y=x-y'; -$solution = (new Instructor)->withConnection('anthropic')->respond( - messages: $problem, - responseModel: ReflectiveResponse::class, - mode: Mode::MdJson, - options: ['max_tokens' => 2048] -); - -print("Problem:\n$problem\n\n"); -dump($solution); - -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/prompting/misc/entity_relationships.mdx`: - -```mdx ---- -title: 'Entity relationship extraction' -docname: 'entity_relationships' ---- - -## Overview - -In cases where relationships exist between entities, it's vital to define them -explicitly in the model. - -Following example demonstrates how to define relationships between users by -incorporating an `$id` and `$coworkers` fields. - - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__.'../../src/'); - -use Cognesy\Instructor\Enums\Mode; -use Cognesy\Instructor\Instructor; - -class UserDetail -{ - /** Unique identifier for each user. */ - public int $id; - public int $age; - public string $name; - public string $role; - /** - * @var int[] Correct and complete list of coworker IDs, representing - * collaboration between users. - */ - public array $coworkers; -} - -class UserRelationships -{ - /** - * @var UserDetail[] Collection of users, correctly capturing the - * relationships among them. - */ - public array $users; -} - -$text = "Jason is 25 years old. He is a Python programmer of Apex website.\ - Amanda is a contractor working with Jason on Apex website. John is 40yo\ - and he's CEO - Jason reports to him."; - -$relationships = (new Instructor)->respond( - messages: [['role' => 'user', 'content' => $text]], - responseModel: UserRelationships::class, -); - -dump($relationships); - -assert(!empty($relationships->users)); -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/prompting/misc/handling_errors.mdx`: - -```mdx ---- -title: 'Handling errors' -docname: 'handling_errors' ---- - -## Overview - -You can create a wrapper class to hold either the result of an operation or an error message. -This allows you to remain within a function call even if an error occurs, facilitating -better error handling without breaking the code flow. - -> NOTE: Instructor offers a built-in Maybe wrapper class that you can use to handle errors. -> See the example in Basics section for more details. - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__.'../../src/'); - -use Cognesy\Instructor\Instructor; - -class UserDetail -{ - public string $name; - public int $age; -} - -class MaybeUser -{ - public ?UserDetail $user = null; - public bool $noUserData = false; - /** If no user data, provide reason */ - public ?string $errorMessage = ''; - - public function get(): ?UserDetail - { - return $this->noUserData ? null : $this->user; - } -} - -$user = (new Instructor)->respond( - messages: [['role' => 'user', 'content' => 'We don\'t know anything about this guy.']], - responseModel: MaybeUser::class -); - - -dump($user); - -assert($user->noUserData); -assert(!empty($user->errorMessage)); -assert($user->get() === null); -?> -``` - - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/prompting/misc/classification.mdx`: - -```mdx ---- -title: 'Single label classification' -docname: 'classification' ---- - -## Overview - -For single-label classification, we first define an `enum` for possible labels -and a PHP class for the output. - -## Example - -Let's start by defining the data structures. - -```php -add('Cognesy\\Instructor\\', __DIR__.'../../src/'); - -use Cognesy\Instructor\Instructor; - -// Enumeration for single-label text classification. -enum Label : string { - case SPAM = "spam"; - case NOT_SPAM = "not_spam"; -} - -// Class for a single class label prediction. -class SinglePrediction { - public Label $classLabel; -} -?> -``` -## Classifying Text - -The function classify will perform the single-label classification. - -```php -respond( - messages: [[ - "role" => "user", - "content" => "Classify the following text: $data", - ]], - responseModel: SinglePrediction::class, - ); -} -?> -``` - -## Testing and Evaluation - -Let's run an example to see if it correctly identifies a spam message. - -```php -classLabel == Label::SPAM); -?> -``` - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/prompting/misc/component_reuse.mdx`: - -```mdx ---- -title: 'Reusing components' -docname: 'component_reuse' ---- - -## Overview - -You can reuse the same component for different contexts within a model. In this -example, the TimeRange component is used for both `$workTime` and `$leisureTime`. - -## Example - -```php -add('Cognesy\\Instructor\\', __DIR__.'../../src/'); - -use Cognesy\Instructor\Instructor; - -class TimeRange { - /** The start time in hours. */ - public int $startTime; - /** The end time in hours. */ - public int $endTime; -} - -class UserDetail -{ - public string $name; - /** Time range during which the user is working. */ - public TimeRange $workTime; - /** Time range reserved for leisure activities. */ - public TimeRange $leisureTime; -} - -$user = (new Instructor)->respond( - messages: [['role' => 'user', 'content' => "Yesterday Jason worked from 9 for 5 hours. After that I watched 2 hour movie which I finished at 19."]], - responseModel: UserDetail::class, - model: 'gpt-4o', -); - -dump($user); - -assert($user->name == "Jason"); -assert($user->workTime->startTime === 9); -assert($user->workTime->endTime === 14); -assert($user->leisureTime->startTime === 17); -assert($user->leisureTime->endTime === 19); -?> -``` -``` - -`/home/ddebowczyk/projects/instructor-php/docs/cookbook/contributing.mdx`: - -```mdx -## We're looking for your help - -We're looking for a bunch more examples. - -If you have a tutorial or example you'd like to add, please open a pull request in `docs/hub` and we'll review it. - -- [ ] Converting the cookbooks to the new format -- [ ] Validator examples -- [ ] Data extraction examples -- [ ] Streaming examples (Iterable and Partial) -- [ ] Batch Parsing examples -- [ ] Query Expansion examples -- [ ] Batch Data Processing examples -- [ ] Batch Data Processing examples with Cache - -We're also looking for help to catch up with the features available in Instructor Hub for Python (see: https://github.com/jxnl/instructor/blob/main/docs/hub/index.md). - -- [ ] Better viewer with pagination -- [ ] Examples database -- [ ] Pulling in the code to your own dir, so you can get started with the API - - - -## How to contribute - -We welcome contributions to the instructor hub, if you have a tutorial or example you'd like to add, please open a pull request in `docs/hub` and we'll review it. - -1. The code must be in a single .php file. -2. Please include documentation in the file - check existing examples for the format. -3. Make sure that the code is tested. - - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/snippets/snippet-intro.mdx`: - -```mdx -One of the core principles of software development is DRY (Don't Repeat -Yourself). This is a principle that apply to documentation as -well. If you find yourself repeating the same content in multiple places, you -should consider creating a custom snippet to keep your content in sync. - -## Setting up - -The first step to data processing with LLMs is setting up your editing environments. - - - - Setup your API keys in .env file to access LLM API provider - - - Run examples to see how Instructor in action - - - - -## Using Instructor - -Learn how to use Instructor to process your data with LLMs. - - - - Understand basic concepts behind Instructor - - - Learn how to use Instructor in your projects - - - Find out the ways to define response data models - - - Use validation to automatically retry for incorrect LLM responses - - - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/images/extraction.cast`: - -```cast -{"version": 2, "width": 145, "height": 48, "timestamp": 1716102752, "env": {"SHELL": "/bin/bash", "TERM": "xterm-256color"}} -[0.31696, "o", "\r\n \u001b[32;01m*\u001b[0m \u001b[35;01mkeychain \u001b[0m\u001b[36m2.8.5\u001b[0m ~ \u001b[32;01mhttp://www.funtoo.org\u001b[0m\r\n"] -[0.328282, "o", " \u001b[32;01m*\u001b[0m Found existing ssh-agent: \u001b[36m597\u001b[0m\r\n"] -[0.328603, "o", "SSH_AUTH_SOCK=/tmp/ssh-XXXXXXQXBhIR/agent.596; export SSH_AUTH_SOCK;\r\nSSH_AGENT_PID=597; export SSH_AGENT_PID;\r\n"] -[0.328743, "o", "\r\n"] -[0.339896, "o", " \u001b[32;01m*\u001b[0m Known ssh key: \u001b[36m/home/ddebowczyk/.ssh/id_rsa\u001b[0m\r\n"] -[0.34007, "o", "\r\n"] -[0.350189, "o", "\u001b[?2004h\u001b]0;ddebowczyk@WORKSTATION: ~/projects/instructor-php\u0007\u001b[01;32mddebowczyk@WORKSTATION\u001b[00m:\u001b[01;34m~/projects/instructor-php\u001b[00m$ "] -[1.09856, "o", "exit"] -[1.300553, "o", "\b\b\b\b./hub.sh run 3"] -[1.814848, "o", "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\u001b[9Pclear"] -[2.664898, "o", "\r\n\u001b[?2004l\r"] -[2.665838, "o", "\u001b[H\u001b[2J\u001b[3J"] -[2.666037, "o", "\u001b[?2004h\u001b]0;ddebowczyk@WORKSTATION: ~/projects/instructor-php\u0007\u001b[01;32mddebowczyk@WORKSTATION\u001b[00m:\u001b[01;34m~/projects/instructor-php\u001b[00m$ "] -[3.308558, "o", "clear"] -[3.495726, "o", "\b\b\b\b\b\u001b[1Pexit"] -[3.783415, "o", "\b\b\b\b./hub.sh run 3"] -[4.601225, "o", "\r\n\u001b[?2004l\r"] -[4.625256, "o", "\r\n\u001b[1;37m\u001b[1mInstructor for PHP\r\n\u001b[0m\u001b[1;30m (^) Get structured outputs from LLMs\r\n\u001b[0m"] -[4.625508, "o", "\r\n"] -[4.626753, "o", "\u001b[1m\u001b[1;33mExecuting all examples...\r\n\u001b[0m"] -[4.626938, "o", "# Extraction of complex objects\r\r\n\r\r\nThis is an example of extraction of a very complex structure from\r\r\nthe provided text.\r\r\n\r\r\n```php\r\r\nExtracting project events from the report:\r\n\r\n[2021-09-01]\r\r\nAcme Insurance project to implement SalesTech CRM solution is currently\r\r\nin RED status due to delayed delivery of document production system, led\r\r\nby 3rd party vendor - Alfatech. Customer (Acme) is discussing the resolution\r\r\nwith the vendor. Due to dependencies it will result in delay of the\r\r\necommerce track by 2 sprints. System integrator (SysCorp) are working\r\r\nto absorb some of the delay by deploying extra resources to speed up\r\r\ndevelopment when the doc production is done. Another issue is that the\r\r\ncustomer is not able to provide the test data for the ecommerce track.\r\r\nSysCorp notified it will impact stabilization schedule unless resolved by\r\r\nthe end of the month. Steerco has been informed last week about the\r\r\npotential impact of the issues, but insists on maintaining release schedule\r\r\ndue to marketi"] -[4.627007, "o", "ng campaign already ongoing. Customer executives are asking\r\r\nus - SalesTech team - to confirm SysCorp's assessment of the situation.\r\r\nWe're struggling with that due to communication issues - SysCorp team has\r\r\nnot shown up on 2 recent calls. Lack of insight has been escalated to\r\r\nSysCorp's leadership team yesterday, but we've got no response yet. The\r\r\npreviously reported Integration Proxy connectivity issue which was blocking\r\r\npolicy track has been resolved on 2021-08-30 - the track is now GREEN.\r\r\nProduction deployment plan has been finalized on Aug 15th and awaiting\r\r\ncustomer approval.\r\n\r\n"] -[11.25878, "o", "\u001b[H\u001b[J\r\nNew event extracted...\r\n\r\n"] -[11.26537, "o", "\u001b[0;38;5;208m\u001b]8;;file:///home/ddebowczyk/projects/instructor-php/examples/01_Basics/ComplexObjectExtraction/run.php#L130\u001b\\^\u001b]8;;\u001b\\ \u001b[38;5;38mProjectEvent\u001b[0;38;5;208m\u001b]8;;file:///home/ddebowczyk/projects/instructor-php/examples/01_Basics/ComplexObjectExtraction/run.php#L46\u001b\\^\u001b]8;;\u001b\\ {\u001b[38;5;247m#9768\u001b[0;38;5;208m\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mtitle\u001b[0;38;5;208m: \"\u001b[1;38;5;113mDelayed delivery of document production system impacting ecommerce track\u001b[0;38;5;208m\"\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mdescription\u001b[0;38;5;208m: \"\u001b[1;38;5;113mAcme Insurance project to implement SalesTech CRM solution is in RED status due to delayed delivery of document production system led by 3rd party vendor Alfatech. Customer (Acme) is discussing the resolution with the vendor. This will delay the ecommerce track by 2 sprints.\u001b[0;38;5;208m\"\u001b[m\r\n"] -[11.265596, "o", " \u001b[0;38;5;208m+\u001b[mtype\u001b[0;38;5;208m: \u001b[38;5;38mProjectEventType\u001b[0;38;5;208m\u001b]8;;file:///home/ddebowczyk/projects/instructor-php/examples/01_Basics/ComplexObjectExtraction/run.php#L70\u001b\\^\u001b]8;;\u001b\\ {\u001b[38;5;247m#591\u001b[0;38;5;208m\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mname\u001b[0;38;5;208m: \"\u001b[1;38;5;113mIssue\u001b[0;38;5;208m\"\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mvalue\u001b[0;38;5;208m: \"\u001b[1;38;5;113missue\u001b[0;38;5;208m\"\u001b[m\r\n \u001b[0;38;5;208m}\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mstatus\u001b[0;38;5;208m: \u001b[38;5;38mProjectEventStatus\u001b[0;38;5;208m\u001b]8;;file:///home/ddebowczyk/projects/instructor-php/examples/01_Basics/ComplexObjectExtraction/run.php#L63\u001b\\^\u001b]8;;\u001b\\ {\u001b[38;5;247m#619\u001b[0;38;5;208m\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mname\u001b[0;38;5;208m: \"\u001b[1;38;5;113mOpen\u001b[0;38;5;208m\"\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mvalue\u001b[0;38;5;208m: \"\u001b[1;38;5;113mopen\u001b[0;38;5;208m\"\u001b[m\r\n \u001b[0;38;5;208m}\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mstakeholders\u001b[0;38;5;208m: \u001b[38;5;38marray:2\u001b[0;38;5;208m [\u001b[m\r\n \u001b[0;38;5;208m\u001b[38;5;38m0\u001b[0;38;5;208m => \u001b[38;5;38mStakeholder\u001b[0;38;5;208m\u001b]8;;file:///home/ddebowczyk/projects/instruct"] -[11.265733, "o", "or-php/examples/01_Basics/ComplexObjectExtraction/run.php#L79\u001b\\^\u001b]8;;\u001b\\ {\u001b[38;5;247m#9774\u001b[0;38;5;208m\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mname\u001b[0;38;5;208m: \"\u001b[1;38;5;113mAcme\u001b[0;38;5;208m\"\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mrole\u001b[0;38;5;208m: \u001b[38;5;38mStakeholderRole\u001b[0;38;5;208m\u001b]8;;file:///home/ddebowczyk/projects/instructor-php/examples/01_Basics/ComplexObjectExtraction/run.php#L88\u001b\\^\u001b]8;;\u001b\\ {\u001b[38;5;247m#776\u001b[0;38;5;208m\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mname\u001b[0;38;5;208m: \"\u001b[1;38;5;113mCustomer\u001b[0;38;5;208m\"\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mvalue\u001b[0;38;5;208m: \"\u001b[1;38;5;113mcustomer\u001b[0;38;5;208m\"\u001b[m\r\n \u001b[0;38;5;208m}\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mdetails\u001b[0;38;5;208m: \"\u001b[1;38;5;113mCustomer discussing resolution with vendor\u001b[0;38;5;208m\"\u001b[m\r\n \u001b[0;38;5;208m}\u001b[m\r\n"] -[11.265918, "o", " \u001b[0;38;5;208m\u001b[38;5;38m1\u001b[0;38;5;208m => \u001b[38;5;38mStakeholder\u001b[0;38;5;208m\u001b]8;;file:///home/ddebowczyk/projects/instructor-php/examples/01_Basics/ComplexObjectExtraction/run.php#L79\u001b\\^\u001b]8;;\u001b\\ {\u001b[38;5;247m#9775\u001b[0;38;5;208m\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mname\u001b[0;38;5;208m: \"\u001b[1;38;5;113mAlfatech\u001b[0;38;5;208m\"\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mrole\u001b[0;38;5;208m: \u001b[38;5;38mStakeholderRole\u001b[0;38;5;208m\u001b]8;;file:///home/ddebowczyk/projects/instructor-php/examples/01_Basics/ComplexObjectExtraction/run.php#L88\u001b\\^\u001b]8;;\u001b\\ {\u001b[38;5;247m#795\u001b[0;38;5;208m\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mname\u001b[0;38;5;208m: \"\u001b[1;38;5;113mVendor\u001b[0;38;5;208m\"\u001b[m\r\n"] -[11.266032, "o", " \u001b[0;38;5;208m+\u001b[mvalue\u001b[0;38;5;208m: \"\u001b[1;38;5;113mvendor\u001b[0;38;5;208m\"\u001b[m\r\n \u001b[0;38;5;208m}\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mdetails\u001b[0;38;5;208m: \"\u001b[1;38;5;113m3rd party vendor responsible for document production system\u001b[0;38;5;208m\"\u001b[m\r\n \u001b[0;38;5;208m}\u001b[m\r\n \u001b[0;38;5;208m]\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mdate\u001b[0;38;5;208m: \"\u001b[1;38;5;113m2021-09-01\u001b[0;38;5;208m\"\u001b[m\r\n\u001b[0;38;5;208m}\u001b[m\r\n"] -[12.909814, "o", "\u001b[H\u001b[J\r\nNew event extracted...\r\n\r\n"] -[12.910123, "o", "\u001b[0;38;5;208m\u001b]8;;file:///home/ddebowczyk/projects/instructor-php/examples/01_Basics/ComplexObjectExtraction/run.php#L130\u001b\\^\u001b]8;;\u001b\\ \u001b[38;5;38mProjectEvent\u001b[0;38;5;208m\u001b]8;;file:///home/ddebowczyk/projects/instructor-php/examples/01_Basics/ComplexObjectExtraction/run.php#L46\u001b\\^\u001b]8;;\u001b\\ {\u001b[38;5;247m#10077\u001b[0;38;5;208m\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mtitle\u001b[0;38;5;208m: \"\u001b[1;38;5;113mTest data issue impacting ecommerce track stabilization schedule\u001b[0;38;5;208m\"\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mdescription\u001b[0;38;5;208m: \"\u001b[1;38;5;113mCustomer is not able to provide test data for the ecommerce track, which will impact stabilization schedule unless resolved by the end of the month. SysCorp notified about the issue.\u001b[0;38;5;208m\"\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mtype\u001b[0;38;5;208m: \u001b[38;5;38mProjectEventType\u001b[0;38;5;208m\u001b]8;;file:///home/ddebowczyk/projects/instructor-php/examples/01_Basics/ComplexObjectExtraction/run.php#L70\u001b\\^\u001b]8;;\u001b\\ {\u001b[38;5;247m#591\u001b[0;38;5;208m\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mname\u001b[0;38;5;208m: \"\u001b[1;38;5;113mIssue\u001b[0;38;5;208m\"\u001b[m\r\n"] -[12.910256, "o", " \u001b[0;38;5;208m+\u001b[mvalue\u001b[0;38;5;208m: \"\u001b[1;38;5;113missue\u001b[0;38;5;208m\"\u001b[m\r\n \u001b[0;38;5;208m}\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mstatus\u001b[0;38;5;208m: \u001b[38;5;38mProjectEventStatus\u001b[0;38;5;208m\u001b]8;;file:///home/ddebowczyk/projects/instructor-php/examples/01_Basics/ComplexObjectExtraction/run.php#L63\u001b\\^\u001b]8;;\u001b\\ {\u001b[38;5;247m#619\u001b[0;38;5;208m\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mname\u001b[0;38;5;208m: \"\u001b[1;38;5;113mOpen\u001b[0;38;5;208m\"\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mvalue\u001b[0;38;5;208m: \"\u001b[1;38;5;113mopen\u001b[0;38;5;208m\"\u001b[m\r\n \u001b[0;38;5;208m}\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mstakeholders\u001b[0;38;5;208m: \u001b[38;5;38marray:2\u001b[0;38;5;208m [\u001b[m\r\n \u001b[0;38;5;208m\u001b[38;5;38m0\u001b[0;38;5;208m => \u001b[38;5;38mStakeholder\u001b[0;38;5;208m\u001b]8;;file:///home/ddebowczyk/projects/instructor-php/examples/01_Basics/ComplexObjectExtraction/run.php#L79\u001b\\^\u001b]8;;\u001b\\ {\u001b[38;5;247m#10072\u001b[0;38;5;208m\u001b[m\r\n"] -[12.910336, "o", " \u001b[0;38;5;208m+\u001b[mname\u001b[0;38;5;208m: \"\u001b[1;38;5;113mAcme\u001b[0;38;5;208m\"\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mrole\u001b[0;38;5;208m: \u001b[38;5;38mStakeholderRole\u001b[0;38;5;208m\u001b]8;;file:///home/ddebowczyk/projects/instructor-php/examples/01_Basics/ComplexObjectExtraction/run.php#L88\u001b\\^\u001b]8;;\u001b\\ {\u001b[38;5;247m#776\u001b[0;38;5;208m\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mname\u001b[0;38;5;208m: \"\u001b[1;38;5;113mCustomer\u001b[0;38;5;208m\"\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mvalue\u001b[0;38;5;208m: \"\u001b[1;38;5;113mcustomer\u001b[0;38;5;208m\"\u001b[m\r\n"] -[12.910536, "o", " \u001b[0;38;5;208m}\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mdetails\u001b[0;38;5;208m: \"\u001b[1;38;5;113mCustomer unable to provide test data\u001b[0;38;5;208m\"\u001b[m\r\n \u001b[0;38;5;208m}\u001b[m\r\n \u001b[0;38;5;208m\u001b[38;5;38m1\u001b[0;38;5;208m => \u001b[38;5;38mStakeholder\u001b[0;38;5;208m\u001b]8;;file:///home/ddebowczyk/projects/instructor-php/examples/01_Basics/ComplexObjectExtraction/run.php#L79\u001b\\^\u001b]8;;\u001b\\ {\u001b[38;5;247m#10071\u001b[0;38;5;208m\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mname\u001b[0;38;5;208m: \"\u001b[1;38;5;113mSysCorp\u001b[0;38;5;208m\"\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mrole\u001b[0;38;5;208m: \u001b[38;5;38mStakeholderRole\u001b[0;38;5;208m\u001b]8;;file:///home/ddebowczyk/projects/instructor-php/examples/01_Basics/ComplexObjectExtraction/run.php#L88\u001b\\^\u001b]8;;\u001b\\ {\u001b[38;5;247m#794\u001b[0;38;5;208m\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mname\u001b[0;38;5;208m: \"\u001b[1;38;5;113mSystemIntegrator\u001b[0;38;5;208m\"\u001b[m\r\n"] -[12.910589, "o", " \u001b[0;38;5;208m+\u001b[mvalue\u001b[0;38;5;208m: \"\u001b[1;38;5;113msystem integrator\u001b[0;38;5;208m\"\u001b[m\r\n \u001b[0;38;5;208m}\u001b[m\r\n"] -[12.910705, "o", " \u001b[0;38;5;208m+\u001b[mdetails\u001b[0;38;5;208m: \"\u001b[1;38;5;113mSystem integrator notified about the impact on stabilization schedule\u001b[0;38;5;208m\"\u001b[m\r\n \u001b[0;38;5;208m}\u001b[m\r\n \u001b[0;38;5;208m]\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mdate\u001b[0;38;5;208m: \"\u001b[1;38;5;113m2021-09-01\u001b[0;38;5;208m\"\u001b[m\r\n\u001b[0;38;5;208m}\u001b[m\r\n"] -[14.504248, "o", "\u001b[H\u001b[J\r\nNew event extracted...\r\n\r\n"] -[14.504519, "o", "\u001b[0;38;5;208m\u001b]8;;file:///home/ddebowczyk/projects/instructor-php/examples/01_Basics/ComplexObjectExtraction/run.php#L130\u001b\\^\u001b]8;;\u001b\\ \u001b[38;5;38mProjectEvent\u001b[0;38;5;208m\u001b]8;;file:///home/ddebowczyk/projects/instructor-php/examples/01_Basics/ComplexObjectExtraction/run.php#L46\u001b\\^\u001b]8;;\u001b\\ {\u001b[38;5;247m#5103\u001b[0;38;5;208m\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mtitle\u001b[0;38;5;208m: \"\u001b[1;38;5;113mSteering Committee insists on maintaining release schedule\u001b[0;38;5;208m\"\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mdescription\u001b[0;38;5;208m: \"\u001b[1;38;5;113mSteerco has been informed last week about the potential impact of the issues but insists on maintaining release schedule due to marketing campaign already ongoing.\u001b[0;38;5;208m\"\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mtype\u001b[0;38;5;208m: \u001b[38;5;38mProjectEventType\u001b[0;38;5;208m\u001b]8;;file:///home/ddebowczyk/projects/instructor-php/examples/01_Basics/ComplexObjectExtraction/run.php#L70\u001b\\^\u001b]8;;\u001b\\ {\u001b[38;5;247m#591\u001b[0;38;5;208m\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mname\u001b[0;38;5;208m: \"\u001b[1;38;5;113mIssue\u001b[0;38;5;208m\"\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mvalue"] -[14.504657, "o", "\u001b[0;38;5;208m: \"\u001b[1;38;5;113missue\u001b[0;38;5;208m\"\u001b[m\r\n \u001b[0;38;5;208m}\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mstatus\u001b[0;38;5;208m: \u001b[38;5;38mProjectEventStatus\u001b[0;38;5;208m\u001b]8;;file:///home/ddebowczyk/projects/instructor-php/examples/01_Basics/ComplexObjectExtraction/run.php#L63\u001b\\^\u001b]8;;\u001b\\ {\u001b[38;5;247m#619\u001b[0;38;5;208m\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mname\u001b[0;38;5;208m: \"\u001b[1;38;5;113mOpen\u001b[0;38;5;208m\"\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mvalue\u001b[0;38;5;208m: \"\u001b[1;38;5;113mopen\u001b[0;38;5;208m\"\u001b[m\r\n \u001b[0;38;5;208m}\u001b[m\r\n"] -[14.504767, "o", " \u001b[0;38;5;208m+\u001b[mstakeholders\u001b[0;38;5;208m: \u001b[38;5;38marray:1\u001b[0;38;5;208m [\u001b[m\r\n \u001b[0;38;5;208m\u001b[38;5;38m0\u001b[0;38;5;208m => \u001b[38;5;38mStakeholder\u001b[0;38;5;208m\u001b]8;;file:///home/ddebowczyk/projects/instructor-php/examples/01_Basics/ComplexObjectExtraction/run.php#L79\u001b\\^\u001b]8;;\u001b\\ {\u001b[38;5;247m#5043\u001b[0;38;5;208m\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mname\u001b[0;38;5;208m: \"\u001b[1;38;5;113mSteerco\u001b[0;38;5;208m\"\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mrole\u001b[0;38;5;208m: \u001b[38;5;38mStakeholderRole\u001b[0;38;5;208m\u001b]8;;file:///home/ddebowczyk/projects/instructor-php/examples/01_Basics/ComplexObjectExtraction/run.php#L88\u001b\\^\u001b]8;;\u001b\\ {\u001b[38;5;247m#798\u001b[0;38;5;208m\u001b[m\r\n"] -[14.504846, "o", " \u001b[0;38;5;208m+\u001b[mname\u001b[0;38;5;208m: \"\u001b[1;38;5;113mOther\u001b[0;38;5;208m\"\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mvalue\u001b[0;38;5;208m: \"\u001b[1;38;5;113mother\u001b[0;38;5;208m\"\u001b[m\r\n \u001b[0;38;5;208m}\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mdetails\u001b[0;38;5;208m: \"\u001b[1;38;5;113mSteering Committee insists on maintaining release schedule\u001b[0;38;5;208m\"\u001b[m\r\n \u001b[0;38;5;208m}\u001b[m\r\n \u001b[0;38;5;208m]\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mdate\u001b[0;38;5;208m: \"\u001b[1;38;5;113m2021-09-01\u001b[0;38;5;208m\"\u001b[m\r\n\u001b[0;38;5;208m}\u001b[m\r\n"] -[16.003578, "o", "\u001b[H\u001b[J\r\nNew event extracted...\r\n\r\n"] -[16.00384, "o", "\u001b[0;38;5;208m\u001b]8;;file:///home/ddebowczyk/projects/instructor-php/examples/01_Basics/ComplexObjectExtraction/run.php#L130\u001b\\^\u001b]8;;\u001b\\ \u001b[38;5;38mProjectEvent\u001b[0;38;5;208m\u001b]8;;file:///home/ddebowczyk/projects/instructor-php/examples/01_Basics/ComplexObjectExtraction/run.php#L46\u001b\\^\u001b]8;;\u001b\\ {\u001b[38;5;247m#14199\u001b[0;38;5;208m\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mtitle\u001b[0;38;5;208m: \"\u001b[1;38;5;113mCommunication issues with SysCorp\u001b[0;38;5;208m\"\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mdescription\u001b[0;38;5;208m: \"\u001b[1;38;5;113mCustomer executives are asking SalesTech team to confirm SysCorp's assessment of the situation. Struggling with communication issues — SysCorp team hasn't shown up on 2 recent calls. Lack of insight escalated to SysCorp's leadership team yesterday, no response yet.\u001b[0;38;5;208m\"\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mtype\u001b[0;38;5;208m: \u001b[38;5;38mProjectEventType\u001b[0;38;5;208m\u001b]8;;file:///home/ddebowczyk/projects/instructor-php/examples/01_Basics/ComplexObjectExtraction/run.php#L70\u001b\\^\u001b]8;;\u001b\\ {\u001b[38;5;247m#591\u001b[0;38;5;208m\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mnam"] -[16.003992, "o", "e\u001b[0;38;5;208m: \"\u001b[1;38;5;113mIssue\u001b[0;38;5;208m\"\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mvalue\u001b[0;38;5;208m: \"\u001b[1;38;5;113missue\u001b[0;38;5;208m\"\u001b[m\r\n \u001b[0;38;5;208m}\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mstatus\u001b[0;38;5;208m: \u001b[38;5;38mProjectEventStatus\u001b[0;38;5;208m\u001b]8;;file:///home/ddebowczyk/projects/instructor-php/examples/01_Basics/ComplexObjectExtraction/run.php#L63\u001b\\^\u001b]8;;\u001b\\ {\u001b[38;5;247m#619\u001b[0;38;5;208m\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mname\u001b[0;38;5;208m: \"\u001b[1;38;5;113mOpen\u001b[0;38;5;208m\"\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mvalue\u001b[0;38;5;208m: \"\u001b[1;38;5;113mopen\u001b[0;38;5;208m\"\u001b[m\r\n \u001b[0;38;5;208m}\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mstakeholders\u001b[0;38;5;208m: \u001b[38;5;38marray:2\u001b[0;38;5;208m [\u001b[m\r\n \u001b[0;38;5;208m\u001b[38;5;38m0\u001b[0;38;5;208m => \u001b[38;5;38mStakeholder\u001b[0;38;5;208m\u001b]8;;file:///home/ddebowczyk/projects/instructor-php/examples/01_Basics/ComplexObjectExtraction/run.php#L79\u001b\\^\u001b]8;;\u001b\\ {\u001b[38;5;247m#14208\u001b[0;38;5;208m\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mname\u001b[0;38;5;208m: \"\u001b[1;38;5;113mSysCorp\u001b[0;38;5;208m\"\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mrole\u001b[0;38;5;208m: \u001b[38;5;38mStakeholder"] -[16.004163, "o", "Role\u001b[0;38;5;208m\u001b]8;;file:///home/ddebowczyk/projects/instructor-php/examples/01_Basics/ComplexObjectExtraction/run.php#L88\u001b\\^\u001b]8;;\u001b\\ {\u001b[38;5;247m#794\u001b[0;38;5;208m\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mname\u001b[0;38;5;208m: \"\u001b[1;38;5;113mSystemIntegrator\u001b[0;38;5;208m\"\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mvalue\u001b[0;38;5;208m: \"\u001b[1;38;5;113msystem integrator\u001b[0;38;5;208m\"\u001b[m\r\n \u001b[0;38;5;208m}\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mdetails\u001b[0;38;5;208m: \"\u001b[1;38;5;113mCommunication issues, escalated to leadership team\u001b[0;38;5;208m\"\u001b[m\r\n \u001b[0;38;5;208m}\u001b[m\r\n \u001b[0;38;5;208m\u001b[38;5;38m1\u001b[0;38;5;208m => \u001b[38;5;38mStakeholder\u001b[0;38;5;208m\u001b]8;;file:///home/ddebowczyk/projects/instructor-php/examples/01_Basics/ComplexObjectExtraction/run.php#L79\u001b\\^\u001b]8;;\u001b\\ {\u001b[38;5;247m#14209\u001b[0;38;5;208m\u001b[m\r\n"] -[16.004287, "o", " \u001b[0;38;5;208m+\u001b[mname\u001b[0;38;5;208m: \"\u001b[1;38;5;113mSalesTech\u001b[0;38;5;208m\"\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mrole\u001b[0;38;5;208m: \u001b[38;5;38mStakeholderRole\u001b[0;38;5;208m\u001b]8;;file:///home/ddebowczyk/projects/instructor-php/examples/01_Basics/ComplexObjectExtraction/run.php#L88\u001b\\^\u001b]8;;\u001b\\ {\u001b[38;5;247m#795\u001b[0;38;5;208m\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mname\u001b[0;38;5;208m: \"\u001b[1;38;5;113mVendor\u001b[0;38;5;208m\"\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mvalue\u001b[0;38;5;208m: \"\u001b[1;38;5;113mvendor\u001b[0;38;5;208m\"\u001b[m\r\n \u001b[0;38;5;208m}\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mdetails\u001b[0;38;5;208m: \"\u001b[1;38;5;113mSalesTech team struggling to confirm SysCorp's assessment\u001b[0;38;5;208m\"\u001b[m\r\n \u001b[0;38;5;208m}\u001b[m\r\n \u001b[0;38;5;208m]\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mdate\u001b[0;38;5;208m: \"\u001b[1;38;5;113m2021-09-01\u001b[0;38;5;208m\"\u001b[m\r\n\u001b[0;38;5;208m}\u001b[m\r\n"] -[17.114115, "o", "\u001b[H\u001b[J\r\nNew event extracted...\r\n\r\n"] -[17.114401, "o", "\u001b[0;38;5;208m\u001b]8;;file:///home/ddebowczyk/projects/instructor-php/examples/01_Basics/ComplexObjectExtraction/run.php#L130\u001b\\^\u001b]8;;\u001b\\ \u001b[38;5;38mProjectEvent\u001b[0;38;5;208m\u001b]8;;file:///home/ddebowczyk/projects/instructor-php/examples/01_Basics/ComplexObjectExtraction/run.php#L46\u001b\\^\u001b]8;;\u001b\\ {\u001b[38;5;247m#3285\u001b[0;38;5;208m\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mtitle\u001b[0;38;5;208m: \"\u001b[1;38;5;113mIntegration Proxy connectivity issue resolved\u001b[0;38;5;208m\"\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mdescription\u001b[0;38;5;208m: \"\u001b[1;38;5;113mThe previously reported Integration Proxy connectivity issue which was blocking the policy track has been resolved on 2021-08-30. The track is now GREEN.\u001b[0;38;5;208m\"\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mtype\u001b[0;38;5;208m: \u001b[38;5;38mProjectEventType\u001b[0;38;5;208m\u001b]8;;file:///home/ddebowczyk/projects/instructor-php/examples/01_Basics/ComplexObjectExtraction/run.php#L70\u001b\\^\u001b]8;;\u001b\\ {\u001b[38;5;247m#591\u001b[0;38;5;208m\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mname\u001b[0;38;5;208m: \"\u001b[1;38;5;113mIssue\u001b[0;38;5;208m\"\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mvalue\u001b[0;38;5;208m: \"\u001b[1;38;"] -[17.114635, "o", "5;113missue\u001b[0;38;5;208m\"\u001b[m\r\n \u001b[0;38;5;208m}\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mstatus\u001b[0;38;5;208m: \u001b[38;5;38mProjectEventStatus\u001b[0;38;5;208m\u001b]8;;file:///home/ddebowczyk/projects/instructor-php/examples/01_Basics/ComplexObjectExtraction/run.php#L63\u001b\\^\u001b]8;;\u001b\\ {\u001b[38;5;247m#620\u001b[0;38;5;208m\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mname\u001b[0;38;5;208m: \"\u001b[1;38;5;113mClosed\u001b[0;38;5;208m\"\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mvalue\u001b[0;38;5;208m: \"\u001b[1;38;5;113mclosed\u001b[0;38;5;208m\"\u001b[m\r\n \u001b[0;38;5;208m}\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mstakeholders\u001b[0;38;5;208m: []\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mdate\u001b[0;38;5;208m: \"\u001b[1;38;5;113m2021-08-30\u001b[0;38;5;208m\"\u001b[m\r\n\u001b[0;38;5;208m}\u001b[m\r\n"] -[18.126263, "o", "\u001b[H\u001b[J\r\nNew event extracted...\r\n\r\n"] -[18.126524, "o", "\u001b[0;38;5;208m\u001b]8;;file:///home/ddebowczyk/projects/instructor-php/examples/01_Basics/ComplexObjectExtraction/run.php#L130\u001b\\^\u001b]8;;\u001b\\ \u001b[38;5;38mProjectEvent\u001b[0;38;5;208m\u001b]8;;file:///home/ddebowczyk/projects/instructor-php/examples/01_Basics/ComplexObjectExtraction/run.php#L46\u001b\\^\u001b]8;;\u001b\\ {\u001b[38;5;247m#7406\u001b[0;38;5;208m\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mtitle\u001b[0;38;5;208m: \"\u001b[1;38;5;113mProduction deployment plan finalized and awaiting customer approval\u001b[0;38;5;208m\"\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mdescription\u001b[0;38;5;208m: \"\u001b[1;38;5;113mProduction deployment plan has been finalized on Aug 15th and is awaiting customer approval.\u001b[0;38;5;208m\"\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mtype\u001b[0;38;5;208m: \u001b[38;5;38mProjectEventType\u001b[0;38;5;208m\u001b]8;;file:///home/ddebowczyk/projects/instructor-php/examples/01_Basics/ComplexObjectExtraction/run.php#L70\u001b\\^\u001b]8;;\u001b\\ {\u001b[38;5;247m#581\u001b[0;38;5;208m\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mname\u001b[0;38;5;208m: \"\u001b[1;38;5;113mProgress\u001b[0;38;5;208m\"\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mvalue\u001b[0;38;5;208m: \"\u001b[1;38;5;113mprogress\u001b[0;38;5;208m\"\u001b[m\r\n \u001b"] -[18.126701, "o", "[0;38;5;208m}\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mstatus\u001b[0;38;5;208m: \u001b[38;5;38mProjectEventStatus\u001b[0;38;5;208m\u001b]8;;file:///home/ddebowczyk/projects/instructor-php/examples/01_Basics/ComplexObjectExtraction/run.php#L63\u001b\\^\u001b]8;;\u001b\\ {\u001b[38;5;247m#619\u001b[0;38;5;208m\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mname\u001b[0;38;5;208m: \"\u001b[1;38;5;113mOpen\u001b[0;38;5;208m\"\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mvalue\u001b[0;38;5;208m: \"\u001b[1;38;5;113mopen\u001b[0;38;5;208m\"\u001b[m\r\n \u001b[0;38;5;208m}\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mstakeholders\u001b[0;38;5;208m: \u001b[38;5;38marray:1\u001b[0;38;5;208m [\u001b[m\r\n \u001b[0;38;5;208m\u001b[38;5;38m0\u001b[0;38;5;208m => \u001b[38;5;38mStakeholder\u001b[0;38;5;208m\u001b]8;;file:///home/ddebowczyk/projects/instructor-php/examples/01_Basics/ComplexObjectExtraction/run.php#L79\u001b\\^\u001b]8;;\u001b\\ {\u001b[38;5;247m#7365\u001b[0;38;5;208m\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mname\u001b[0;38;5;208m: \"\u001b[1;38;5;113mAcme\u001b[0;38;5;208m\"\u001b[m\r\n"] -[18.126907, "o", " \u001b[0;38;5;208m+\u001b[mrole\u001b[0;38;5;208m: \u001b[38;5;38mStakeholderRole\u001b[0;38;5;208m\u001b]8;;file:///home/ddebowczyk/projects/instructor-php/examples/01_Basics/ComplexObjectExtraction/run.php#L88\u001b\\^\u001b]8;;\u001b\\ {\u001b[38;5;247m#776\u001b[0;38;5;208m\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mname\u001b[0;38;5;208m: \"\u001b[1;38;5;113mCustomer\u001b[0;38;5;208m\"\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mvalue\u001b[0;38;5;208m: \"\u001b[1;38;5;113mcustomer\u001b[0;38;5;208m\"\u001b[m\r\n \u001b[0;38;5;208m}\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mdetails\u001b[0;38;5;208m: \"\u001b[1;38;5;113mAwaiting customer approval\u001b[0;38;5;208m\"\u001b[m\r\n \u001b[0;38;5;208m}\u001b[m\r\n \u001b[0;38;5;208m]\u001b[m\r\n \u001b[0;38;5;208m+\u001b[mdate\u001b[0;38;5;208m: \"\u001b[1;38;5;113m2021-08-15\u001b[0;38;5;208m\"\u001b[m\r\n\u001b[0;38;5;208m}\u001b[m\r\n"] -[19.130923, "o", "\u001b[H\u001b[J\r\nSOURCE REPORT:\r\n\r\n[2021-09-01]\r\r\nAcme Insurance project to implement SalesTech CRM solution is currently\r\r\nin RED status due to delayed delivery of document production system, led\r\r\nby 3rd party vendor - Alfatech. Customer (Acme) is discussing the resolution\r\r\nwith the vendor. Due to dependencies it will result in delay of the\r\r\necommerce track by 2 sprints. System integrator (SysCorp) are working\r\r\nto absorb some of the delay by deploying extra resources to speed up\r\r\ndevelopment when the doc production is done. Another issue is that the\r\r\ncustomer is not able to provide the test data for the ecommerce track.\r\r\nSysCorp notified it will impact stabilization schedule unless resolved by\r\r\nthe end of the month. Steerco has been informed last week about the\r\r\npotential impact of the issues, but insists on maintaining release schedule\r\r\ndue to marketing campaign already ongoing. Customer executives are asking\r\r\nus - SalesTech team - to confirm SysCorp's assessment of the situation.\r\r\nWe're struggling with "] -[19.131031, "o", "that due to communication issues - SysCorp team has\r\r\nnot shown up on 2 recent calls. Lack of insight has been escalated to\r\r\nSysCorp's leadership team yesterday, but we've got no response yet. The\r\r\npreviously reported Integration Proxy connectivity issue which was blocking\r\r\npolicy track has been resolved on 2021-08-30 - the track is now GREEN.\r\r\nProduction deployment plan has been finalized on Aug 15th and awaiting\r\r\ncustomer approval.\r\n\r\n\r\nFOUND 6 PROJECT EVENTS:\r\n [.] Delayed delivery of document production system impacting ecommerce track (type: issue)\r\n [.] Test data issue impacting ecommerce track stabilization schedule (type: issue)\r\n [.] Steering Committee insists on maintaining release schedule (type: issue)\r\n [.] Communication issues with SysCorp (type: issue)\r\n [.] Integration Proxy connectivity issue resolved (type: issue)\r\n [.] Production deployment plan finalized and awaiting customer approval (type: progress)\r\n\u001b[1;30mExample executed in \u001b[0m\u001b[1m\u001b[1;37m14.5 seconds\u001b[0m\r\n"] -[19.136605, "o", "\u001b[?2004h\u001b]0;ddebowczyk@WORKSTATION: ~/projects/instructor-php\u0007\u001b[01;32mddebowczyk@WORKSTATION\u001b[00m:\u001b[01;34m~/projects/instructor-php\u001b[00m$ "] -[21.948143, "o", "./hub.sh run 3"] -[22.346609, "o", "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\u001b[9Pclear"] -[22.880031, "o", "\b\b\b\b\b\u001b[1Pexit"] -[23.424631, "o", "\r\n\u001b[?2004l\r"] -[23.424876, "o", "exit\r\n"] - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/images/hero-light.svg`: - -```svg - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/images/hero-dark.svg`: - -```svg - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/mint.json`: - -```json -{ - "name": "Instructor for PHP", - "logo": { - "dark": "/logo/dark.svg", - "light": "/logo/light.svg" - }, - "favicon": "/favicon.svg", - "colors": { - "primary": "#0D9373", - "light": "#07C983", - "dark": "#0D9373", - "anchors": { - "from": "#0D9373", - "to": "#07C983" - } - }, - "topbarLinks": [ - { - "name": "Issues", - "url": "https://github.com/cognesy/instructor-php/issues" - } - ], - "topbarCtaButton": { - "name": "Github", - "url": "https://github.com/cognesy/instructor-php" - }, - "primaryTab": { - "name": "Get Started" - }, - "tabs": [ - { - "name": "Examples", - "url": "cookbook/examples" - }, - { - "name": "Prompting", - "url": "cookbook/prompting" - } - ], - "anchors": [ - { - "name": "Community", - "icon": "discord", - "url": "https://discord.com/invite/bD9YE9JArw" - }, - { - "name": "Blog", - "icon": "newspaper", - "url": "https://cognesy.com/blog" - } - ], - "navigation": [ - { - "group": "Get Started", - "pages": [ - "introduction", - "quickstart", - "llm_providers" - ] - }, - { - "group": "Concepts", - "pages": [ - "concepts/overview", - "concepts/why" - ] - }, - { - "group": "Essentials", - "pages": [ - "essentials/installation", - "essentials/usage", - "essentials/modes", - "essentials/data_model", - "essentials/scalars", - "essentials/validation", - "essentials/prompts", - "essentials/demonstrations" - ] - }, - { - "group": "Advanced Features", - "pages": [ - "advanced/model_options", - "advanced/partials", - "advanced/sequences", - "advanced/structures", - "advanced/function_calls", - "advanced/modules" - ] - }, - { - "group": "Practical Techniques", - "pages": [ - "techniques/prompting", - "techniques/classification", - "techniques/search" - ] - }, - { - "group": "Internals", - "pages": [ - "internals/instructor", - "internals/lifecycle", - "internals/response_models", - "internals/debugging", - "internals/http", - "internals/events" - ] - }, - { - "group": "More", - "pages": [ - "misc/philosophy", - "misc/contributing", - "misc/help" - ] - }, - { - "group": "Instructor Hub", - "pages": [ - "cookbook/introduction", - "cookbook/contributing" - ] - }, - { - "group": "Basics", - "pages": [ - "cookbook/examples/basics/basic_use", - "cookbook/examples/basics/basic_use_mixin", - "cookbook/examples/basics/maybe", - "cookbook/examples/basics/modes", - "cookbook/examples/basics/optional_fields", - "cookbook/examples/basics/public_vs_private", - "cookbook/examples/basics/self_correction", - "cookbook/examples/basics/attributes", - "cookbook/examples/basics/using_config", - "cookbook/examples/basics/validation", - "cookbook/examples/basics/custom_validation", - "cookbook/examples/basics/validation_multifield", - "cookbook/examples/basics/validation_with_llm" - ] - }, - { - "group": "Advanced", - "pages": [ - "cookbook/examples/advanced/context_cache_llm", - "cookbook/examples/advanced/context_cache", - "cookbook/examples/advanced/custom_client", - "cookbook/examples/advanced/custom_prompts", - "cookbook/examples/advanced/structured_input", - "cookbook/examples/advanced/function_calls", - "cookbook/examples/advanced/partials", - "cookbook/examples/advanced/demonstrations", - "cookbook/examples/advanced/scalars", - "cookbook/examples/advanced/sequences", - "cookbook/examples/advanced/streaming", - "cookbook/examples/advanced/structures" - ] - }, - { - "group": "Troubleshooting", - "pages": [ - "cookbook/examples/troubleshooting/debugging", - "cookbook/examples/troubleshooting/on_error", - "cookbook/examples/troubleshooting/on_event", - "cookbook/examples/troubleshooting/token_usage_events", - "cookbook/examples/troubleshooting/wiretap" - ] - }, - { - "group": "LLM API Support", - "pages": [ - "cookbook/examples/api_support/anthropic", - "cookbook/examples/api_support/azure_openai", - "cookbook/examples/api_support/cohere", - "cookbook/examples/api_support/fireworks", - "cookbook/examples/api_support/google_gemini", - "cookbook/examples/api_support/groq", - "cookbook/examples/api_support/mistralai", - "cookbook/examples/api_support/ollama", - "cookbook/examples/api_support/openai", - "cookbook/examples/api_support/openrouter", - "cookbook/examples/api_support/togetherai" - ] - }, - { - "group": "Extras", - "pages": [ - "cookbook/examples/extras/complex_extraction", - "cookbook/examples/extras/complex_extraction_claude", - "cookbook/examples/extras/complex_extraction_cohere", - "cookbook/examples/extras/complex_extraction_gemini", - "cookbook/examples/extras/embeddings", - "cookbook/examples/extras/image_to_data", - "cookbook/examples/extras/image_to_data_anthropic", - "cookbook/examples/extras/image_to_data_gemini", - "cookbook/examples/extras/llm", - "cookbook/examples/extras/llm_json", - "cookbook/examples/extras/llm_json_schema", - "cookbook/examples/extras/llm_md_json", - "cookbook/examples/extras/llm_tools", - "cookbook/examples/extras/schema", - "cookbook/examples/extras/schema_dynamic", - "cookbook/examples/extras/transcription_to_tasks", - "cookbook/examples/extras/translate_ui_fields", - "cookbook/examples/extras/web_to_objects" - ] - }, - { - "group": "Zero-Shot Prompting", - "pages": [ - "cookbook/prompting/zero_shot/assign_role", - "cookbook/prompting/zero_shot/auto_refine", - "cookbook/prompting/zero_shot/clarify_ambiguity", - "cookbook/prompting/zero_shot/define_style", - "cookbook/prompting/zero_shot/emotional_stimuli", - "cookbook/prompting/zero_shot/follow_up_questions", - "cookbook/prompting/zero_shot/repeat_query", - "cookbook/prompting/zero_shot/simulate_perspective" - ] - }, - { - "group": "Few-Shot Prompting", - "pages": [ - "cookbook/prompting/few_shot/in_context_examples" - ] - }, - { - "group": "Thought Generation", - "pages": [ - "cookbook/prompting/thought_gen/analogical_prompting" - ] - }, - { - "group": "Miscellaneous", - "pages": [ - "cookbook/prompting/misc/arbitrary_properties", - "cookbook/prompting/misc/arbitrary_properties_consistent", - "cookbook/prompting/misc/chain_of_summaries", - "cookbook/prompting/misc/chain_of_thought", - "cookbook/prompting/misc/classification", - "cookbook/prompting/misc/classification_multiclass", - "cookbook/prompting/misc/entity_relationships", - "cookbook/prompting/misc/handling_errors", - "cookbook/prompting/misc/limiting_lists", - "cookbook/prompting/misc/reflection_prompting", - "cookbook/prompting/misc/restate_instructions", - "cookbook/prompting/misc/rewrite_instructions", - "cookbook/prompting/misc/search_query_expansion", - "cookbook/prompting/misc/component_reuse", - "cookbook/prompting/misc/component_reuse_cot" - ] - } - ], - "footerSocials": { - "x": "https://x.com/ddebowczyk", - "github": "https://github.com/cognesy/instructor-php", - "linkedin": "https://www.linkedin.com/ddebowczyk" - } -} -``` - -`/home/ddebowczyk/projects/instructor-php/docs/misc/contributing.mdx`: - -```mdx -## Contributing - -If you want to help, check out some of the issues. They could be anything from code improvements, a guest blog post, or a new cookbook. - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/misc/help.mdx`: - -```mdx -# Getting help with Instructor - -If you need help getting started with Instructor or with advanced usage, the following sources may be useful. - - - - - The [concepts](concepts/prompting.md) section explains the core concepts of Instructor and how to prompt with models. - - - The [cookbooks](./hub/index.md) are a great place to start. They contain a variety of examples that demonstrate how to use Instructor in different scenarios. - - - - [GitHub discussions](https://github.com/cognesy/instructor-php/discussions) are useful for asking questions, your question and the answer will help everyone. - - - [GitHub issues](https://github.com/cognesy/instructor-php/issues) are useful for reporting bugs or requesting new features. - - - The [Discord](https://discord.gg/CV8sPM5k5Y) is a great place to ask questions and get help from the community. - - - The [blog](blog/index.md) contains articles that explain how to use Instructor in different scenarios. - - - You can also reach out to me [@ddebowczyk](https://twitter.com/ddebowczyk) if you have any questions or ideas. - - -``` - -`/home/ddebowczyk/projects/instructor-php/docs/misc/philosophy.mdx`: - -```mdx -## Philosophy of Instructor - -> NOTE: Philosophy behind Instructor was formulated by [Jason Liu](https://twitter.com/jxnlco), the creator of original -> version of Instructor in Python and adapted for the PHP port. - -Instructor values [simplicity](https://eugeneyan.com/writing/simplicity/) and flexibility in leveraging language -models. It offers a streamlined approach for structured output, avoiding unnecessary dependencies or complex -abstractions. - -> “Simplicity is a great virtue, but it requires hard work to achieve it and education to appreciate it. And to make -> matters worse: complexity sells better.” — Edsger Dijkstra - - -### Simplicity - -1. Most users will only need to learn `responseModel` and `Instructor::respond()` to get started. -2. No new prompting language to learn, no new abstractions to learn. - - -### Transparency - -1. We write very little prompts, and we don't try to hide the prompts from you. -2. We give you config over the prompts we do write ('reasking' and in the future - JSON_MODE prompts). - - -### Flexibility - -1. If you build a system with OpenAI directly, it is easy to incrementally adopt Instructor by just adding -`Instructor::respond()` with data schemas fed in via `responseModel`. -2. Use any class to define your data schema (no need to inherit from some base class). - - - -## The zen of `Instructor` - -Maintain the flexibility and power of PHP classes, without unnecessary constraints. - -Begin with a function and a return type hint – simplicity is key. I've learned that the goal of a making a useful -framework is minimizing regret, both for the author and hopefully for the user. - -1. Define data schema `class StructuredData { ... }` -2. Define validators and methods on your schema. -3. Encapsulate all your LLM logic into a function `function extract($input) : StructuredData` -4. Define typed computations against your data with `function compute(StructuredData $data):` or call methods on your -schema `$data->compute()` - -It should be that simple. - - - -## Our Goals - -The goal for the library, [documentation](https://cognesy.github.io/instructor-php/), and -[blog](https://cognesy.github.io/instructor-php/blog/), is to help you be a better programmer and, as a result, -a better AI engineer. - -- The library is a result of our desire for simplicity. -- The library should help maintain simplicity in your codebase. -- We won't try to write prompts for you, -- We don't try to create indirections or abstractions that make it hard to debug in the future - -Please note that the library is designed to be adaptable and open-ended, allowing you to customize and extend its -functionality based on your specific requirements. If you have any further questions or ideas hit us up -[@jnxlco](https://twitter.com/jxnlco) or [@ddebowczyk](https://twitter.com/ddebowczyk) - -Cheers! - -``` \ No newline at end of file