Repository navigation
Conversation
* feat: add embedding engine and service layer (itomig-de#50) Adds an embedding layer contributed by Amine Ait-Ali (Combodo). Rebased onto current main; autoload regenerated with --no-dev. * chore(deps): update LLPhant to 1.0.0 (itomig-de#61) Adapts the single call site to the renamed ChatInterface method (generateChatOrReturnFunctionCalled -> generateChatOrReturnFunctionToCall). No functional changes to itomig-ai-base; unified LLPhant tool-call semantics fix double tool execution for Anthropic and Ollama. * chore(deps): update LLPhant to 1.0.1
…ion (itomig-de#66) * fix(phpstan): add missing IssueLog::Info and DBObject::GetKey stubs The stub file used for standalone analysis was incomplete: IssueLog::Info() and DBObject::GetKey() both exist in iTop 3.2 (core/log.class.inc.php:728, core/dbobject.class.php:1596) but were absent from the attrappes, so every call site was reported as an undefined method. Removes three false positives, including a pre-existing one in AIObjectTools.php:90. No production code is affected. * feat(guardrail): add iAIGuardrail extension point for content moderation AI-Base processes content that may be attacker-controlled, and hands model output back to sinks that are not always reviewed by a human. It had no content moderation and no place to add any. Introduce iAIGuardrail, discovered via InterfaceDiscovery like the existing tool provider contract, so an extension can screen AI content without any calling code changing. AIService consults it at three points: - input: the prompt, resp. the latest user turn of a conversation - output: the model's answer, before it reaches the caller - tool_result: a tool's return value, before it re-enters the history (the arrival point for indirect prompt injection) Callers may declare their context via the new AIService::setSurface() so a guardrail can apply a context-specific policy set. Omitting it keeps the 'default' surface. Two properties are deliberate. IsEnabledFor() is consulted before every check and must stay cheap, so callers without a guardrail pay nothing. Guardrail failures are fail-open: an exception from an implementation is logged and the AI call proceeds, since a guardrail outage must not take down every AI feature. Blocking is expressed by returning a verdict, which surfaces to the caller as AIGuardrailBlockedException. Discovery is cached statically because several tool providers construct a fresh AIService per call. Purely additive: with no guardrail installed, behaviour is unchanged. A Shieldstral-based implementation ships separately as itomig-ai-guardrail. * feat(guardrail): screen the assembled system prompt and pass the iTop context Adds DIRECTION_SYSTEM_PROMPT and screens the system prompt in GetCompletion() and ContinueConversation(), before the input it governs. The configured system prompt is a trusted source; what it has become by the time it is sent is not necessarily. Two things change it at runtime. Placeholder substitution: PerformSystemInstruction() already applies sprintf() to the 'translate' instruction, and a general placeholder feature would widen that to values resolved from the database. And consumers appending data: ai-response appends ticket JSON to the prompt, including a public log that may contain text arriving by mail-to-ticket. Neither was screened. The prompt is constant for the duration of a call, so it is checked once per call rather than per turn or per tool round. Also passes the active iTop context tags to the guardrail as $aContext['itop_context'], read from ContextTag::GetStack(). This answers a different question than the caller-declared surface: 'ticket.summarize' may be triggered by an agent in the console or by a background job with nobody reviewing the result. An empty stack is a normal state — not every entry point sets a tag — and guardrails must treat it as "channel unknown". IsEnabledFor() gains the context as a fourth parameter so that the decision whether to screen at all can depend on the channel, for example screening everything under GUI:Portal but only output under CRON. BREAKING for iAIGuardrail implementations: PHP requires an implementation to declare every parameter of an interface method, including optional ones, so IsEnabledFor() implementations must be updated. No implementation exists in a released version of this module; the reference implementation in itomig-ai-guardrail is adjusted separately. No change for consumers of AIService: no existing signature is altered, and AIGuardrailBlockedException remains within the established hierarchy. A caller can distinguish a prompt problem from a content problem via GetDirection().
…he suite PHPUnit includes every test file while assembling the suite, before any test bootstrap runs. The named top-level class RecordingGuardrail forced PHP to resolve iAIGuardrail at file-load time, when the extension autoloader (registered via env-production in setUp) is not yet available, aborting the whole suite with 'Interface not found'. The fake is now an anonymous class built by a factory method at test runtime, matching the pattern already used in FunctionCallingTest. The fake also records the context argument in Check(), which testItopContextIsPassedToGuardrail asserts on but the previous fake never captured. Verified against a fresh iTop 3.3.0-dev install: all 7 suites pass (64 tests, 370 assertions).
There was a problem hiding this comment.
🟡 Changes recommended
Image validation gaps can permit URL payloads or forward non-image data to providers.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Adds image inputs to AI conversations through vision-capable OpenAI and Anthropic engines.
Changes:
- Introduces vision engine contracts and provider-specific message conversion.
- Classifies unsupported-vision responses and adds tests.
- Documents image payloads and refreshes module/autoload metadata.
File summaries
| File | Description |
|---|---|
src/Contracts/iAIVisionEngine.php |
Defines vision-message capability. |
src/Engine/OpenAIEngine.php |
Builds OpenAI vision messages. |
src/Engine/AnthropicAIEngine.php |
Builds Anthropic vision messages. |
src/Engine/GenericAIEngine.php |
Classifies vision capability errors. |
src/Service/AIService.php |
Converts image-bearing history entries. |
src/Exception/AIVisionUnsupportedException.php |
Adds a vision-specific exception. |
tests/php-unit-tests/unitary-tests/VisionEngineMessageTest.php |
Tests provider message construction. |
tests/php-unit-tests/unitary-tests/ImageInputConversationTest.php |
Tests conversation image handling. |
tests/php-unit-tests/unitary-tests/GenericAIEngineExceptionTest.php |
Tests error classification. |
tests/php-unit-tests/unitary-tests/GuardrailContractTest.php |
Applies formatting updates. |
README.md |
Documents image input usage. |
module.itomig-ai-base.php |
Updates version and syntax. |
vendor/composer/autoload_static.php |
Registers new classes. |
vendor/composer/autoload_classmap.php |
Registers new classes. |
vendor/composer/platform_check.php |
Updates generated platform failure handling. |
vendor/autoload.php |
Updates generated autoload failure handling. |
vendor/composer/LICENSE |
Applies whitespace changes. |
Review details
- Files reviewed: 12/17 changed files
- Comments generated: 2
- Review effort level: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+112
to
+116
| try { | ||
| $aAnthropicImages[] = new AnthropicImage( | ||
| AnthropicImageType::from($sMediaType), | ||
| $sData | ||
| ); |
Comment on lines
+119
to
+122
| $aImageSources[] = new ImageSource( | ||
| $sData, | ||
| ImageQuality::High | ||
| ); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Base information
Symptom (bug) / Objective (enhancement)
Reproduction procedure (bug)
Cause (bug)
Proposed solution (bug and enhancement)
Checklist before requesting a review