Repository navigation
6.0.0
What's Changed
- fix!: Replay structured requests with their original schema and count the tokens of every attempt. by @pocketarc in #14
- fix: Return the response when a cache write fails. by @pocketarc in #15
ai_workflow_requests has a new schema_name column
The nullable column holds the schema name that each structured request was sent with. AiWorkflowReplayer uses it to replay the request with the same schema name. For requests logged before the migration, which have no schema_name, AiWorkflowReplayer still uses the name schema.
If ai-workflow.logging.enabled is true, AI calls fail with a database error until the migration has run. Run the migration before the upgraded app handles any AI calls.
Copy the new migration into database/migrations with the current timestamp as its filename prefix. Then run the migration:
cp vendor/pocketarc/ai-workflow/database/migrations/0001_01_01_000010_add_schema_name_to_ai_workflow_requests.php \
"database/migrations/$(date +%Y_%m_%d_%H%M%S)_add_schema_name_to_ai_workflow_requests.php"
php artisan migrateWith the current timestamp, the new migration runs after the ai-workflow migrations that you published before. If those filenames start with the date on which you published them, the original 0001_01_01_000010 prefix would make the new migration run before the migration that creates ai_workflow_requests, so migrate:fresh would fail.
Do not run vendor:publish --tag=ai-workflow-migrations for this upgrade. With Laravel's default database.migrations.update_date_on_publish setting, the command publishes every ai-workflow migration again with a new timestamp. migrate then fails on the first copy, which creates tables that already exist.
sendStructuredData() wraps request errors in StructuredDataRequestException
sendStructuredData() now throws StructuredDataRequestException when an attempt fails with an exception that does not extend AiWorkflowException, such as a provider error, a rate-limit error or a connection failure. getPrevious() returns the original exception, and getCode() returns its code, or 0 if that code is not an integer. The attempts property holds the number of the attempt that failed. usage() returns the tokens of the earlier attempts plus those of the failed attempt's response, if one was received. An exception that extends AiWorkflowException, such as GuardrailViolationException, is rethrown as the same instance.
If you catch Prism or laravel-integrations exceptions around sendStructuredData(), catch StructuredDataRequestException instead and check getPrevious(). The other AiService methods still throw the original exceptions.
Other behaviour changes
StructuredDataResult->usagenow holds the tokens of every attempt, including attempts that failed validation. No tokens are counted for an attempt served from the cache.- Every exception that extends
AiWorkflowExceptionhas ausage()method. It returns the tokens of the responses received before the exception was thrown, ornullif no response was received. For an exception thrown bysendStructuredData(),usage()is nevernulland covers every attempt. - When a middleware such as an output guardrail rejects a response, or the finish reason is
Unknown,ErrororOther, the row logged for the failed request now contains that response (the text or structured output and the finish reason) and its tokens. - When the structured output cannot be decoded and the prompt has a
fallback_model,sendStructuredMessages()andsendStructuredMessagesWithTools()now log the failed primary request and dispatchAiWorkflowRequestFailedfor it. They do the same for the fallback request if it fails. InsendStructuredMessages(), the fallback request now runs through the middleware, and its response is cached. - If an
AiWorkflowRequestCompletedlistener throws an exception after a successful request, the request is no longer logged a second time as failed, andAiWorkflowRequestFailedis no longer dispatched. - If a cache write fails,
AiServicepasses the exception to Laravel'sreport()and still returns the response. Before,AiServicethrew the exception, so the caller lost the response, the request was logged a second time as failed, andAiWorkflowRequestFailedwas dispatched. sendStructuredData()no longer caches a response that fails validation.- A response is now cached under a key built from the request as it was before middleware ran, which is the key used for cache reads. Before, a request was never served from the cache if a middleware had rewritten its messages or system prompt.
AiWorkflowReplayernow rebuilds the recorded schema exactly, includinganyOf. On MySQL, which reorders JSON object keys, it restores each object's property order from itsrequiredlist when that list names every property.
Full Changelog: 5.1.0...6.0.0