Commit graph

10 commits

Author SHA1 Message Date
Anthony Damhet
4868f65bd8
UI|UX: AI design (#38789)
* WIP AI design

- Chat Interface
- French Translation

* AI Home Screen

Added a home page for the AI assistant, with pre-recorded prompts

* Fix missing user

* Add copyright

* Fix working tools and alignment
2026-06-14 01:05:01 +02:00
Pichinov-Jose
9ccdbff271
New: granular right for AI Assistant usage (#38312)
The AI module currently has no declared rights ($this->rights stays
empty), so per-user/group attribution of "who is allowed to talk to
the LLM" is not possible. Assistant access is gated only by
isModEnabled('ai'), meaning any authenticated user on an AI-enabled
install can send organisational data to the configured LLM provider
-- which is a GDPR / EU AI Act concern (see issue #38331).

This patch introduces ONE proper Dolibarr right that admins can
attribute per user/group via the standard permission UI
(Users -> Permissions):

  ai/assistant/use   -> right to use the AI Assistant page
                        NOT granted by default; admins must
                        explicitly assign it to authorized users

Per the feedback from @sonikf and @eldy on PR #38312:

- Setup access intentionally stays a hard-coded $user->admin check.
  The technical setup of the AI module (API keys, provider URLs) is
  an admin task in line with how every other Dolibarr module is
  configured -- a dedicated 'ai/setup/write' right would only
  duplicate responsibility that admins already hold.

- ai/assistant/use defaults to OFF (not granted) so that, on a
  fresh install or after enabling the module, no user can talk to
  the LLM until the admin explicitly authorises them. This matches
  the EU AI Act expectation that AI usage is an opt-in per-user
  decision owned by the GDPR DPO / AI module DPA.

Access checks added:

- htdocs/ai/assistant/index.php
    + if (!$user->hasRight('ai','assistant','use')) accessforbidden();
  Added after the existing isModEnabled('ai') / AI_MCP_ENABLED gate.

- htdocs/ai/assistant/parse_intent.php
    + if (!$user->hasRight('ai','assistant','use')) accessforbidden();
  Defence in depth: the AJAX endpoint that actually talks to the LLM
  cannot be reached by a user who lacks the right, even via direct
  call bypassing the Assistant page.

- htdocs/ai/admin/setup.php
    Unchanged behaviour: $user->admin check stays (just a comment
    clarifying the reason).

The right is declared between the existing MODULEBUILDER PERMISSIONS
placeholders in modAi.class.php so the standard right-installation
flow applies: after applying this change, administrators must
disable/re-enable the AI module once to register the new right in
llx_rights_def (existing Dolibarr mechanism, no migration script
needed).

Tested:
- Fresh non-admin user (no extra right granted) -> Assistant page
  shows accessforbidden; parse_intent.php also blocks direct call.
- Same user with 'ai/assistant/use' granted by an admin ->
  Assistant accessible, LLM round-trip works.
- Admin user -> Assistant accessible transparently (admins hold
  every declared right by definition); Setup accessible via the
  unchanged $user->admin check.
- Module disabled -> both still blocked by the existing
  isModEnabled gate.

Refs: #38331 (EU AI Act / GDPR for the AI module).

Co-authored-by: Laurent Destailleur <eldy@destailleur.fr>
2026-06-11 09:33:14 +02:00
sonikf
0a35b04674
New AI tool access control (#38668)
* Add new language entries for AI tool management

* Update file reference in assistant.php documentation

* Implement isSystem method in conversation class

* Add Tool Access Control tab

* Initial commit AI general css

* Initial commit AI general js file

* Implement isSystem method in mcptool class

Add isSystem method to determine tool visibility.

* Enhance MCPServer to enforce tool allow-list

* Enhance McpHandler with tool context handling

Added support for tool context in McpHandler class.

* Remove empty line at the start of mcp.class.php

* Refactor tool schema handling for LLM integration

* Modify McpHandler initialization in execute_tool.php

Updated McpHandler initialization to include configuration and context for assistant.

* New AI tool access control

* Fix pre-commit

* fix pre-commit

* fix pre-commit

* fix pre-commit

* fix pre-commit

* fix phan php-stan
2026-06-10 11:41:10 +02:00
Laurent Destailleur
99e364ccdd ci 2026-05-22 08:43:13 +02:00
Laurent Destailleur
56009b9bf8 NEW Add count tool for AI 2026-05-21 19:33:50 +02:00
Laurent Destailleur
3e3ca74a67 Debug AI assistant and MCP server 2026-05-20 01:27:19 +02:00
Laurent Destailleur
71daa93adc Split AI_MCP_ENABLED into AI_MCP_ENABLED + AI_ASSISTANT_ENABLED 2026-05-20 00:05:39 +02:00
Pichinov-Jose
6ee5130f24
Fix: parse_intent.php model resolution (array default and wrong constant) (#38300)
Two bugs combined to make UniversalLLMAdapter::__construct() crash with:

  TypeError: Argument #4 ($model) must be of type string, array given

at parse_intent.php line 301 when the user-configured model wasn't
properly read.

(1) Wrong shape assumption -- getListOfAIServices() declares model
defaults as a NESTED array of the form:

    $servicesList[$key]['textgeneration'] = ['default' => 'model-name']

The previous code read $servicesList[$key]['textgeneration'] (without
['default']) and got the inner array back. That array was then passed
as the $defModel fallback to a (string)-typed constructor argument.

(2) Wrong constant -- the admin UI ("Prompt and custom AI models" tab
in setup.php) stores the per-function model under
AI_API_<SERVICE>_MODEL_TEXT, matching the convention already used by
Ai::generateContent() for the same data. The previous code read
AI_API_<SERVICE>_MODEL (no _TEXT suffix), which is never written by
that form. So the user-configured model was silently ignored and we
fell back to the (array) default from bug #1.

Together these two bugs reliably reproduced the TypeError on any
default install of the AI module where the user had set a custom
text model via the admin UI.

This patch:

- Walks $servicesList[$key]['textgeneration'] correctly (extracting
  ['default'] when it's an array, accepting strings for backward
  compat).
- Tries AI_API_<SERVICE>_MODEL_TEXT first (the constant the admin
  UI writes), and falls back to the legacy AI_API_<SERVICE>_MODEL
  for compatibility with anyone who might have set it manually.
- Defensively coerces the final value to a string and falls back to
  the default if for any reason it's still not a string.

Tested with google (gemini-2.5-flash) and chatgpt configured via
the admin UI -> UniversalLLMAdapter receives a string and the
AI request goes through.
2026-05-18 01:15:23 +02:00
Pichinov-Jose
d0df12ab81
Qual: preserve full tool descriptions for modern LLMs (#38301)
cleanToolSchemaForLLM() truncates each tool description to 3 words
whenever the schema has more than 20 tools. This heuristic likely
made sense for GPT-3.5 (4K context window) but is harmful with the
current crop of LLMs:

- Gemini 2.5 Flash: 1M token context
- GPT-4o: 128K
- Claude Sonnet: 1M

Three words per description is far too aggressive and actively
breaks tool selection. For example:

- create_other_document  -> "Create documents other"
- create_sales_order     -> "Create a CUSTOMER"
- add_line_item          -> "Add a single"

The LLM then has no way to know that create_other_document is what
should be used for supplier_invoice, or that add_line_item modifies
an EXISTING draft. We've observed Gemini concluding that
"supplier_invoice creation is not available" because the truncated
description didn't mention it.

Two complementary changes:

1. Raise the threshold from 20 to 100 -- this effectively disables
   compression for normal installs (~30 tools) while leaving a
   safety net for very large custom installs that register dozens
   of additional tools via the addMcpTools hook.

2. Even when compression IS active, never touch the tool-level
   description. Parameter-level compression (stripping defaults,
   descriptions of optional fields, collapsing complex objects)
   still applies and continues to save tokens. The right answer
   for truly oversized schemas is to filter the toolset upstream
   (which filterToolsProfessional() already does), not to mutilate
   each tool's description.

Tested with: a 30-tool schema sent in full now allows the LLM to
correctly select create_other_document with object_type
'supplier_invoice', 'proposal' and 'supplier_proposal' --
previously it returned a "feature not available" message.
2026-05-18 00:08:47 +02:00
sonikf
faa8005eb4
NEW: MCP server and AI Assistant (#37888)
* fix list query

* Update other.lang

* remove duplicate

* initial commit

* AI Assistant initial commit

* Rename htdocs/ai/index.php to htdocs/ai/assistant/index.php

* AI Assistant initial commit

* Initial commit

* Rename htdocs/ai/ai_assistant.css to htdocs/ai/css/ai_assistant.css

* Initial commit

* Rename htdocs/ai/ai_assistant.js to htdocs/ai/js/ai_assistant.js

* Initial commit

* Rename htdocs/ai/mcp_server.php to htdocs/ai/server/mcp_server.php

* Initial commit

* category tools

* Rename htdocs/ai/categories.php to htdocs/ai/tools/categories.php

* Global tools to restrict AI

* Tools for crud actions on Dolibarr objects

* Tools for products and services.

* Tools to generate reports

* Missing PHPDoc

* Tools for thirdparties and contacts

* Tool for generating navigation URLs in Dolibarr

* Tools related to invoices and payments

* Update ai_assistant.js

* Initial commit

* Rename htdocs/ai/purge_logs.php to htdocs/ai/scripts/purge_logs.php

* fix pre commit

* re fix pre-commit

* fix pre-commit

* Rename categories.php to categories.class.php

* Rename conversation.php to conversation.class.php

* Rename crud_objects.php to crud_objects.class.php

* Rename invoices.php to invoices.class.php

* Rename navigation.php to navigation.class.php

* Rename products.php to products.class.php

* Rename reports.php to reports.class.php

* Rename thirdparty.php to thirdparty.class.php

* fix pre-commit

* Update parse_intent.php

* fix pre commit

* fix phpdoc

* fix pre-commit

* fix pre-commit

* fix pre-commit

* fix pre-commit

* fix pre-commit

* fix pre-commit

* fix pre-commit

* fix pre-commit

* fix pre-commit

* fix pre-commit

* fix pre-commit

* fix pre-commit

* fix pre-commit

* fix pre-commit

* fix pre-commit

* fix pre-commit

* fix pre-commit

* fix pre-commit

* fix pre-commit

* fix pre-commit

* fix pre-commit

* fix pre-commit

* fix pre-commit

* fix pre-commit

* fix pre-commit

* add missing phpdoc

* fix pre-commit

* fix phpdoc

* fix phpdoc

* fix pre-commit

* fix pre-commit

* fix phpdoc

* fix pre-commit

* fix pre-commit

* fix phpdoc

* fix phpdoc

* fix phpdoc

* fix phpdoc

* fix php-stan

* fix php-stan

* fix phan

* fix phan

* fix phan

* security check

* security check

* security check

* security check

* enhance security check

* enhance security check

* fix pre-commit

* Update log_viewer.php

* Improve access control logic in log_viewer.php

Refactor access control checks for admin and module activation.

* fix phan

* fix phan

* fix phan

* fix phan

* fix phan

* try fix phan

* fix pre-commit

* fix phan

* try fix phan

* fix phan

* fix php-stan

* fix phan php-stan

* fix pre-commit

* fix phan php-stan

* fix pre-commit

* fix phan php-stan

* fix phan php-stan

* fix phn php-stan

* fix phan php-stan

* fix phan php-stan

* fix phan php-stan

* fix phan php-stan

* fix pre-commit

* fix phan php-stan

* fix php-stan

* Fix MCP tool registration for .class.php file naming

* fix typo

* fix ci

* fix pre-commit

* fix ci

* fix pre-commit

* Update categories.class.php

* fix ci

* fix ci

* fix ci

* fix ci

* fix ci

* fix ci

* fix ci

* fix ci

* fix ci

* add missing user in constructor

* add missing user in constructor

* improve invoice validation error handling

* add missing user in constructor

* Rename mcp_protocol.php to mcp_protocol.class.php

* Update mcp_server.php

---------

Co-authored-by: Laurent Destailleur <eldy@destailleur.fr>
2026-05-11 01:59:55 +02:00