feat: add PHPStan static analysis and fix all level-max errors#25
feat: add PHPStan static analysis and fix all level-max errors#25
Conversation
- Add phpstan.neon config targeting src/ and tests/ at level max - Upgrade phpstan/phpstan from 1.9.x-dev to ^2.0 for PHP 8.4 support - Upgrade laravel/pint from 1.2.* to ^1.18 to fix PHP 8.4 deprecation warnings in composer format/lint - Broaden Agent::setInstructions() type from array<string, string> to array<string, string|list<string>> so callers can pass array values per instruction key - Fix all adapters (Anthropic, Deepseek, Gemini, Ollama, OpenAI, Perplexity, XAI) to handle array instruction values and properly type json_decode results - Fix Schema::toJson() to safely access mixed property values - Remove redundant PHPUnit assertions that were always-true per static analysis
|
Warning Rate limit exceeded
⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📒 Files selected for processing (1)
WalkthroughThis pull request updates dependencies (PHPStan, PHPUnit, Laravel Pint), introduces configuration files (phpstan.neon, pint.json), modernizes existing configurations (Dockerfile, phpunit.xml), and applies systematic code cleanup across the agents codebase. The primary changes include: removing documentation annotations from core classes and adapter implementations; introducing defensive parsing logic (type guards, null checks) in streaming response handlers; improving error handling across adapters; and updating test files with syntax changes and assertion removals. The functional interface change adds an optional nullable listener parameter to the Adapter::send() method. Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes 🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
tests/Agents/AgentTest.php (1)
99-141:⚠️ Potential issue | 🟡 Minor
assertGreaterThan(0, $result['modelLoadingDuration'])is fragile for warm-model runs.The Ollama adapter now returns
load_durationas-is (anint) when present, including0when the model is already loaded. AnassertGreaterThan(0, 0)will fail in that case. Whenload_durationis absent, the adapter returnsnull, and the assertion will also fail.Consider using
assertGreaterThanOrEqual(0, ...)or only asserting that the field exists (assertArrayHasKey) rather than checking its magnitude.🔧 Suggested fix
- $this->assertGreaterThan(0, $result['modelLoadingDuration']); + $this->assertGreaterThanOrEqual(0, $result['modelLoadingDuration']);(applies to both
test_embed_returns_array_with_embedding_adapterline 109 andtest_embedding_dimensionsline 132)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/Agents/AgentTest.php` around lines 99 - 141, The failing assertion is that test_embed_returns_array_with_embedding_adapter and test_embedding_dimensions use assertGreaterThan(0, $result['modelLoadingDuration']) which is fragile because Agent::embed (Ollama adapter) can return 0 or null for load_duration; replace those strict >0 checks with a non-negative check (e.g. use assertGreaterThanOrEqual(0, ...) when value is numeric) or simply assert the key exists (assertArrayHasKey('modelLoadingDuration', $result)) and only assert magnitude when not null, referencing the test methods test_embed_returns_array_with_embedding_adapter and test_embedding_dimensions and the $result['modelLoadingDuration'] reference.src/Agents/Adapters/Anthropic.php (1)
271-310:⚠️ Potential issue | 🟠 MajorAccumulate multiple deltas per chunk to avoid truncation.
Right now Line 301 overwrites$block; if a chunk contains multiplecontent_block_deltalines, earlier deltas are lost. Consider appending and emitting the delta text.🔧 Suggested fix
- if ($deltaType === 'text_delta' && isset($json['delta']['text']) && is_string($json['delta']['text'])) { - $block = $json['delta']['text']; - } - - if (! empty($block)) { - if ($listener !== null) { - $listener($block); - } - } + if ($deltaType === 'text_delta' && isset($json['delta']['text']) && is_string($json['delta']['text'])) { + $deltaText = $json['delta']['text']; + if ($deltaText !== '') { + $block .= $deltaText; + if ($listener !== null) { + $listener($deltaText); + } + } + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Agents/Adapters/Anthropic.php` around lines 271 - 310, The handler for 'content_block_delta' currently assigns $block = $json['delta']['text'], which overwrites previous deltas and causes truncation; instead accumulate deltas by appending $json['delta']['text'] to $block (e.g. $block .= ...) so multiple 'content_block_delta' events per chunk are preserved, ensure $block is initialized/cleared when entering 'content_block_start' and only emit to $listener when $block is non-empty; update references in the 'content_block_delta' and 'content_block_start' cases and keep the $listener($block) call as the emission point.
🧹 Nitpick comments (3)
src/Agents/Adapters/XAI.php (2)
154-156:is_scalaraccepts booleans, which cast to"1"or"".
is_scalarreturnstrueforboolas well asstring/int/float. If$json['code']isfalse,(string) falseyields"", producing"() ..."as the error type. Narrowing tois_stringis safer.♻️ Suggested fix
- $errorType = isset($json['code']) && is_scalar($json['code']) ? (string) $json['code'] : 'unknown_error'; + $errorType = isset($json['code']) && is_string($json['code']) ? $json['code'] : 'unknown_error';🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Agents/Adapters/XAI.php` around lines 154 - 156, The code that computes $errorType uses is_scalar which accepts booleans and causes false to cast to an empty string; change the check in the $errorType assignment to use is_string($json['code']) instead of is_scalar(...) so only actual strings are accepted, keep the cast to (string) and the fallback 'unknown_error' unchanged; leave the $errorMessage logic as-is (is_string check) and update the assignment of $errorType where it's defined to use is_string to prevent boolean -> "" error types.
128-137:$blockis overwritten per loop iteration rather than accumulated.If a single HTTP chunk carries more than one
data:SSE line, the listener receives all content pieces correctly, butprocess()returns only the final piece — so the parent's$content .= $this->process(...)accumulator will silently drop all but the last. This is pre-existing behaviour, but it's worth addressing while this code is being touched.♻️ Suggested fix
- if (isset($delta['content']) && is_string($delta['content'])) { - $block = $delta['content']; + if (isset($delta['content']) && is_string($delta['content'])) { + $block .= $delta['content']; if (! empty($block) && $listener !== null) { $listener($block);The same pattern appears in
Perplexity::process()andGemini::process()and should be updated there as well.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Agents/Adapters/XAI.php` around lines 128 - 137, The code in XAI::process is overwriting $block for each SSE/data piece instead of accumulating them, causing callers' accumulators to only get the last piece; change the logic in XAI::process to append new content (e.g., use concatenation on $block or a local $accumulator) when multiple delta['content'] pieces are present so the listener and return value contain all pieces from the chunk, and apply the same pattern to Perplexity::process() and Gemini::process() where the identical overwrite occurs (look for $choices, $firstChoice, $delta, and $block variables to locate the spots to replace overwrite with accumulation).src/Agents/Adapters/Gemini.php (1)
302-307:json_encodefailure is silently swallowed via cast.
json_encodereturnsfalsewhen encoding fails (e.g., non-UTF-8 sequences indetails). Castingfalsetostringyields""in PHP, so$errorDetailssilently disappears rather than indicating an encoding failure.♻️ Suggested fix
- $errorDetails = isset($error['details']) ? (string) json_encode($error['details'], JSON_PRETTY_PRINT) : ''; + $encoded = isset($error['details']) ? json_encode($error['details'], JSON_PRETTY_PRINT) : ''; + $errorDetails = $encoded !== false ? $encoded : '(details not encodable)';🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Agents/Adapters/Gemini.php` around lines 302 - 307, The current assignment to $errorDetails uses (string) json_encode($error['details'], JSON_PRETTY_PRINT) which silently hides encoding failures because json_encode returns false and casting yields an empty string; update the logic in Gemini.php where $errorDetails is set so you call json_encode and check its return (e.g., $encoded = json_encode($error['details'], JSON_PRETTY_PRINT)); if $encoded === false, build a fallback string using print_r($error['details'], true) or var_export and append json_last_error_msg() to indicate the encoding error, otherwise use the $encoded value; ensure you still produce a human-readable $errorDetails used in the final return.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/Agents/Adapters/OpenAI.php`:
- Around line 268-276: The code in the OpenAI adapter currently overwrites
$block with a single delta per parsed chunk, losing earlier deltas when a chunk
contains multiple "data:" lines; change the extraction logic in the stream
parsing (around the variables $choices, $firstChoice, $delta, $block, and
$listener) to accumulate/appending each delta's 'content' into $block (e.g.,
concatenate each found $delta['content'] instead of assigning), then only call
$listener($block) when the accumulated block is non-empty; ensure you still
validate is_string($delta['content']) and handle empty/null $listener as before.
---
Outside diff comments:
In `@src/Agents/Adapters/Anthropic.php`:
- Around line 271-310: The handler for 'content_block_delta' currently assigns
$block = $json['delta']['text'], which overwrites previous deltas and causes
truncation; instead accumulate deltas by appending $json['delta']['text'] to
$block (e.g. $block .= ...) so multiple 'content_block_delta' events per chunk
are preserved, ensure $block is initialized/cleared when entering
'content_block_start' and only emit to $listener when $block is non-empty;
update references in the 'content_block_delta' and 'content_block_start' cases
and keep the $listener($block) call as the emission point.
In `@tests/Agents/AgentTest.php`:
- Around line 99-141: The failing assertion is that
test_embed_returns_array_with_embedding_adapter and test_embedding_dimensions
use assertGreaterThan(0, $result['modelLoadingDuration']) which is fragile
because Agent::embed (Ollama adapter) can return 0 or null for load_duration;
replace those strict >0 checks with a non-negative check (e.g. use
assertGreaterThanOrEqual(0, ...) when value is numeric) or simply assert the key
exists (assertArrayHasKey('modelLoadingDuration', $result)) and only assert
magnitude when not null, referencing the test methods
test_embed_returns_array_with_embedding_adapter and test_embedding_dimensions
and the $result['modelLoadingDuration'] reference.
---
Nitpick comments:
In `@src/Agents/Adapters/Gemini.php`:
- Around line 302-307: The current assignment to $errorDetails uses (string)
json_encode($error['details'], JSON_PRETTY_PRINT) which silently hides encoding
failures because json_encode returns false and casting yields an empty string;
update the logic in Gemini.php where $errorDetails is set so you call
json_encode and check its return (e.g., $encoded =
json_encode($error['details'], JSON_PRETTY_PRINT)); if $encoded === false, build
a fallback string using print_r($error['details'], true) or var_export and
append json_last_error_msg() to indicate the encoding error, otherwise use the
$encoded value; ensure you still produce a human-readable $errorDetails used in
the final return.
In `@src/Agents/Adapters/XAI.php`:
- Around line 154-156: The code that computes $errorType uses is_scalar which
accepts booleans and causes false to cast to an empty string; change the check
in the $errorType assignment to use is_string($json['code']) instead of
is_scalar(...) so only actual strings are accepted, keep the cast to (string)
and the fallback 'unknown_error' unchanged; leave the $errorMessage logic as-is
(is_string check) and update the assignment of $errorType where it's defined to
use is_string to prevent boolean -> "" error types.
- Around line 128-137: The code in XAI::process is overwriting $block for each
SSE/data piece instead of accumulating them, causing callers' accumulators to
only get the last piece; change the logic in XAI::process to append new content
(e.g., use concatenation on $block or a local $accumulator) when multiple
delta['content'] pieces are present so the listener and return value contain all
pieces from the chunk, and apply the same pattern to Perplexity::process() and
Gemini::process() where the identical overwrite occurs (look for $choices,
$firstChoice, $delta, and $block variables to locate the spots to replace
overwrite with accumulation).
ℹ️ Review info
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
composer.lockis excluded by!**/*.lock
📒 Files selected for processing (32)
.github/workflows/code-ql-analysis.ymlcomposer.jsonphpstan.neonsrc/Agents/Adapter.phpsrc/Agents/Adapters/Anthropic.phpsrc/Agents/Adapters/Deepseek.phpsrc/Agents/Adapters/Gemini.phpsrc/Agents/Adapters/Ollama.phpsrc/Agents/Adapters/OpenAI.phpsrc/Agents/Adapters/Perplexity.phpsrc/Agents/Adapters/XAI.phpsrc/Agents/Agent.phpsrc/Agents/Conversation.phpsrc/Agents/DiffCheck/DiffCheck.phpsrc/Agents/Message.phpsrc/Agents/Messages/Image.phpsrc/Agents/Messages/Text.phpsrc/Agents/Role.phpsrc/Agents/Roles/Assistant.phpsrc/Agents/Roles/User.phpsrc/Agents/Schema.phpsrc/Agents/Schema/SchemaObject.phptests/Agents/AgentTest.phptests/Agents/Conversation/ConversationBase.phptests/Agents/DiffCheck/DiffCheckOpenAITest.phptests/Agents/DiffCheck/DiffCheckTest.phptests/Agents/Messages/ImageTest.phptests/Agents/Messages/TextTest.phptests/Agents/Roles/AssistantTest.phptests/Agents/Roles/UserTest.phptests/Agents/Schema/SchemaObjectTest.phptests/Agents/SchemaTest.php
💤 Files with no reviewable changes (5)
- src/Agents/Message.php
- src/Agents/Role.php
- src/Agents/Messages/Text.php
- src/Agents/Roles/User.php
- src/Agents/Roles/Assistant.php
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/Agents/Adapters/Anthropic.php (1)
301-309:⚠️ Potential issue | 🟠 Major
$blockoverwrite drops earlier deltas; misplaced listener call emits stale content.Two issues in
content_block_deltahandling:
Truncation (line 302):
$block = $json['delta']['text']overwrites instead of appends. Anthropic's streaming protocol specifies "one or more content_block_delta events" per content block, and the official example shows two separatetext_deltaevents ("Hello" then "!") in the same content block. Ifutopia-php/fetchdelivers these in a single network chunk, only the last delta survives in$block, and$content .= $this->process(...)accumulates a truncated response.Stale listener (lines 305-309): The
if (! empty($block))/$listener($block)call sits outside thetext_deltaguard. Anthropic streaming also deliversthinking_deltaandsignature_deltaascontent_block_deltaevents in extended-thinking models. When those arrive,$blockretains its previous value, and the listener re-emits already-delivered text.The correct pattern (already used in
Deepseek::process()) is to accumulate and call the listener inside the conditional:🐛 Proposed fix
- if ($deltaType === 'text_delta' && isset($json['delta']['text']) && is_string($json['delta']['text'])) { - $block = $json['delta']['text']; - } - - if (! empty($block)) { - if ($listener !== null) { - $listener($block); - } - } + if ($deltaType === 'text_delta' && isset($json['delta']['text']) && is_string($json['delta']['text'])) { + $deltaText = $json['delta']['text']; + if ($deltaText !== '') { + $block .= $deltaText; + if ($listener !== null) { + $listener($deltaText); + } + } + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Agents/Adapters/Anthropic.php` around lines 301 - 309, In content_block_delta handling update the logic so text deltas are appended not overwritten and the listener is invoked only when a new text delta is processed: inside the branch that checks $deltaType === 'text_delta' and isset($json['delta']['text']) replace the assignment $block = $json['delta']['text'] with an append (e.g. $block .= ...) and immediately call $listener($block) if $listener !== null; remove the separate outer if (! empty($block))/$listener call so non-text deltas (thinking_delta, signature_delta) do not re-emit stale content; refer to the content_block_delta handling, $deltaType, $json['delta']['text'], $block and $listener to locate the code.
♻️ Duplicate comments (1)
src/Agents/Adapters/OpenAI.php (1)
269-278:$block = $delta['content']still overwrites instead of accumulating.The same truncation issue from the previous review is unaddressed: if a single network chunk contains multiple
data:lines (e.g., "Hello" + " world"),$blockis overwritten each iteration and only the last delta is returned fromprocess(), causing$contentto be missing earlier tokens.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Agents/Adapters/OpenAI.php` around lines 269 - 278, In process() of OpenAI.php the line `$block = $delta['content']` overwrites prior tokens when a network chunk contains multiple SSE `data:` lines; change this to accumulate the delta content (e.g., append `$delta['content']` to an existing buffer) instead of replacing it so earlier tokens are preserved, and ensure the buffer variable (used later as `$content`/return value) is initialized outside the loop and updated (e.g., `$block .= $delta['content']` or append into `$content`) before invoking `$listener($block)` or returning.
🧹 Nitpick comments (3)
Dockerfile (1)
1-1: Consider updating thecomposer:2.0image to a more recent tag.
composer:2.0is a very old pinned tag (Composer is now at 2.7+). It may lack security fixes incomposeritself and its bundled PHP runtime. Prefercomposer:2(floating latest 2.x) or a higher explicit pin (e.g.,composer:2.7).🔧 Suggested update
-FROM composer:2.0 AS composer +FROM composer:2 AS composer🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Dockerfile` at line 1, The Dockerfile pins the base stage to an outdated image "FROM composer:2.0"; update this to a newer Composer image tag (e.g., "composer:2" to follow latest 2.x or a specific newer pin like "composer:2.7") to get security and runtime fixes. Edit the Dockerfile's FROM line (referencing the existing FROM composer:2.0) and replace the tag with the chosen updated tag, rebuild and verify the build and runtime Composer behavior.phpunit.xml (1)
2-5: LGTM — schema URL and attribute cleanup are correct for PHPUnit 12.The schema definition for PHPUnit 12.0 can always be found at
https://schema.phpunit.de/12.0/phpunit.xsd, so thexsi:noNamespaceSchemaLocationvalue is correct. RemovingverboseandstopOnFailureis also appropriate as both were deprecated in earlier PHPUnit versions.One optional alternative: pointing to
vendor/phpunit/phpunit/phpunit.xsd(the local vendored copy) means IDEs and validators always use the exact installed version, which avoids any minor drift when^12resolves to12.1or12.2.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@phpunit.xml` around lines 2 - 5, No change required: the xsi:noNamespaceSchemaLocation value is correct for PHPUnit 12 and removing deprecated attributes (verbose, stopOnFailure) is appropriate; optionally, if you want IDE/validator stability against minor patch-version drift, change the xsi:noNamespaceSchemaLocation to point at the vendored schema (vendor/phpunit/phpunit/phpunit.xsd) so tools use the exact installed version instead of the remote https://schema.phpunit.de/12.0/phpunit.xsd.src/Agents/Adapters/Anthropic.php (1)
222-223:$contentreused with a different type in the non-stream path.
$contentis declared asstringon line 181, then reassigned tomixed($json['content'] ?? null) on line 223. The final non-stream return path uses$text, so there is no runtime impact, but the reuse creates a confusing type change. Consider a dedicated variable name (e.g.$responseContentBlocks) for the response body content array.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Agents/Adapters/Anthropic.php` around lines 222 - 223, The variable $content is declared as a string earlier and then reassigned to an array/mixed ($json['content'] ?? null), causing a confusing type change; change the reassigned variable name (for example to $responseContentBlocks) in the non-stream handling branch where $json is inspected, update any subsequent references that expect the array to use the new name, and leave the original $content (string) and $text usages untouched so types remain consistent.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@src/Agents/Adapters/Anthropic.php`:
- Around line 301-309: In content_block_delta handling update the logic so text
deltas are appended not overwritten and the listener is invoked only when a new
text delta is processed: inside the branch that checks $deltaType ===
'text_delta' and isset($json['delta']['text']) replace the assignment $block =
$json['delta']['text'] with an append (e.g. $block .= ...) and immediately call
$listener($block) if $listener !== null; remove the separate outer if (!
empty($block))/$listener call so non-text deltas (thinking_delta,
signature_delta) do not re-emit stale content; refer to the content_block_delta
handling, $deltaType, $json['delta']['text'], $block and $listener to locate the
code.
---
Duplicate comments:
In `@src/Agents/Adapters/OpenAI.php`:
- Around line 269-278: In process() of OpenAI.php the line `$block =
$delta['content']` overwrites prior tokens when a network chunk contains
multiple SSE `data:` lines; change this to accumulate the delta content (e.g.,
append `$delta['content']` to an existing buffer) instead of replacing it so
earlier tokens are preserved, and ensure the buffer variable (used later as
`$content`/return value) is initialized outside the loop and updated (e.g.,
`$block .= $delta['content']` or append into `$content`) before invoking
`$listener($block)` or returning.
---
Nitpick comments:
In `@Dockerfile`:
- Line 1: The Dockerfile pins the base stage to an outdated image "FROM
composer:2.0"; update this to a newer Composer image tag (e.g., "composer:2" to
follow latest 2.x or a specific newer pin like "composer:2.7") to get security
and runtime fixes. Edit the Dockerfile's FROM line (referencing the existing
FROM composer:2.0) and replace the tag with the chosen updated tag, rebuild and
verify the build and runtime Composer behavior.
In `@phpunit.xml`:
- Around line 2-5: No change required: the xsi:noNamespaceSchemaLocation value
is correct for PHPUnit 12 and removing deprecated attributes (verbose,
stopOnFailure) is appropriate; optionally, if you want IDE/validator stability
against minor patch-version drift, change the xsi:noNamespaceSchemaLocation to
point at the vendored schema (vendor/phpunit/phpunit/phpunit.xsd) so tools use
the exact installed version instead of the remote
https://schema.phpunit.de/12.0/phpunit.xsd.
In `@src/Agents/Adapters/Anthropic.php`:
- Around line 222-223: The variable $content is declared as a string earlier and
then reassigned to an array/mixed ($json['content'] ?? null), causing a
confusing type change; change the reassigned variable name (for example to
$responseContentBlocks) in the non-stream handling branch where $json is
inspected, update any subsequent references that expect the array to use the new
name, and leave the original $content (string) and $text usages untouched so
types remain consistent.
ℹ️ Review info
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
composer.lockis excluded by!**/*.lock
📒 Files selected for processing (13)
Dockerfilecomposer.jsonphpunit.xmlpint.jsonsrc/Agents/Adapters/Anthropic.phpsrc/Agents/Adapters/Deepseek.phpsrc/Agents/Adapters/Gemini.phpsrc/Agents/Adapters/Ollama.phpsrc/Agents/Adapters/OpenAI.phpsrc/Agents/DiffCheck/DiffCheck.phptests/Agents/AgentTest.phptests/Agents/Conversation/ConversationBase.phptests/Agents/DiffCheck/DiffCheckOpenAITest.php
💤 Files with no reviewable changes (2)
- tests/Agents/Conversation/ConversationBase.php
- tests/Agents/AgentTest.php
🚧 Files skipped from review as they are similar to previous changes (4)
- composer.json
- pint.json
- tests/Agents/DiffCheck/DiffCheckOpenAITest.php
- src/Agents/Adapters/Gemini.php
Summary
phpstan.neonconfig running level max analysis onsrc/andtests/phpstan/phpstanfrom1.9.x-dev→^2.0for PHP 8.4 compatibilitylaravel/pintfrom1.2.*→^1.18to eliminate PHP 8.4 deprecation warnings duringcomposer format/lintAgent::setInstructions()type fromarray<string, string>→array<string, string|list<string>>to match how callers (e.g. Appwrite cloud) actually use itjson_decoderesultsSchema::toJson()to safely access mixed property valuesTest plan
composer checkpasses with 0 PHPStan errorscomposer lintpasses with no deprecation warningscomposer formatruns cleanly with no deprecation warningsSummary by CodeRabbit
New Features
Bug Fixes
Chores