ab967d1c by Ean Schuessler

Refactor Agent Runtime: General-purpose LLM Request Service

BREAKING: Introduces new LLM Request/Response pattern for CSR agents

Major Changes:
- Add new SystemMessage types: SmtyLlmRequest, SmtyLlmResponse
- Extend SystemMessage entity with callback and audit fields:
  * parentSystemMessageId - Links Response → Request
  * callbackServiceName - Service to call when LLM completes
  * callbackParameters - JSON params for callback
  * sourceTypeEnumId/sourceId - Audit trail (Order, CommEvent, etc.)
  * llmResponse - Raw response stored for debugging

New Services:
- AgentServices.process#LLMRequest: General-purpose async LLM service
  * Any trigger (SECA, UI, Order, etc.) can call this
  * Creates SmtyLlmRequest SystemMessage
  * Triggers async processing via poller
  * Validates callback service exists before creating task

Refactored Services:
- AgentServices.run#AgentTaskTurn: Universal agent processor
  * Supports both SmtyAgentTask (legacy) and SmtyLlmRequest (new)
  * ALWAYS provides MCP tools to LLM (for CSR agent pattern)
  * Creates SmtyLlmResponse and calls callback for new pattern
  * Maintains backward compatibility with SmtyAgentTask

- AgentServices.callback#CommunicationEvent: Saves LLM responses to conversation
  * Callback service for CommunicationEvent-triggered requests
  * Maintains conversation thread via rootCommEventId

Updated SECA:
- AgentTriggerOnCommunication now calls AgentServices.process#LLMRequest
  * Uses callback pattern instead of direct SystemMessage creation
  * Enables full audit trail via SystemMessage Request :left_right_arrow: Response

Benefits:
- General-purpose: Any trigger can request LLM processing (orders, inventory, support, etc.)
- Traceability: Full audit trail via linked SystemMessages
- RBAC: Agent impersonates users, respects permissions
- Same UX: Agent uses same screens humans use (via MCP tools)
- Flexible: Different callbacks handle responses differently
1 parent 5c6a9826
...@@ -12,4 +12,8 @@ ...@@ -12,4 +12,8 @@
12 <!-- Agent Task Message Type --> 12 <!-- Agent Task Message Type -->
13 <moqui.service.message.SystemMessageType systemMessageTypeId="SmtyAgentTask" description="Agent Task"/> 13 <moqui.service.message.SystemMessageType systemMessageTypeId="SmtyAgentTask" description="Agent Task"/>
14 14
15 <!-- LLM Request/Response Message Types -->
16 <moqui.service.message.SystemMessageType systemMessageTypeId="SmtyLlmRequest" description="LLM Request"/>
17 <moqui.service.message.SystemMessageType systemMessageTypeId="SmtyLlmResponse" description="LLM Response"/>
18
15 </entity-facade-xml> 19 </entity-facade-xml>
......
...@@ -20,12 +20,38 @@ ...@@ -20,12 +20,38 @@
20 <description>Specific AI configuration used for this task.</description> 20 <description>Specific AI configuration used for this task.</description>
21 </field> 21 </field>
22 <field name="rootCommEventId" type="id"> 22 <field name="rootCommEventId" type="id">
23 <description>The root CommunicationEvent ID for the conversation thread.</description> 23 <description>The root CommunicationEvent ID for conversation thread.</description>
24 </field>
25
26 <!-- Callback and Audit Fields for LLM Request/Response Pattern -->
27 <field name="parentSystemMessageId" type="id">
28 <description>Parent SystemMessage ID (links Response to Request).</description>
29 </field>
30 <field name="callbackServiceName" type="text-medium">
31 <description>Service to call with LLM response when complete.</description>
32 </field>
33 <field name="callbackParameters" type="text-very-long">
34 <description>JSON parameters to pass to callback service.</description>
35 </field>
36 <field name="sourceTypeEnumId" type="id">
37 <description>Type of entity that triggered this request (Order, CommEvent, etc.).</description>
38 </field>
39 <field name="sourceId" type="id">
40 <description>ID of the triggering entity (orderId, communicationEventId, etc.).</description>
41 </field>
42 <field name="llmResponse" type="text-very-long">
43 <description>Raw LLM response content (stored for audit/debug).</description>
24 </field> 44 </field>
25 45
26 <relationship type="one" title="RequestedBy" related="mantle.party.Party"> 46 <relationship type="one" title="RequestedBy" related="mantle.party.Party">
27 <key-map field-name="requestedByPartyId" related="partyId"/> 47 <key-map field-name="requestedByPartyId" related="partyId"/>
28 </relationship> 48 </relationship>
49 <relationship type="one" title="ParentSystemMessage" related="moqui.service.message.SystemMessage">
50 <key-map field-name="parentSystemMessageId" related="systemMessageId"/>
51 </relationship>
52 <relationship type="many" title="ChildSystemMessages" related="moqui.service.message.SystemMessage" fk-name="SysMsgToSysMsg">
53 <key-map field-name="systemMessageId" related="parentSystemMessageId"/>
54 </relationship>
29 <!-- Note: effectiveUserId links to UserAccount, but we don't force FK to allow system users --> 55 <!-- Note: effectiveUserId links to UserAccount, but we don't force FK to allow system users -->
30 <relationship type="one" related="mantle.product.store.ProductStore"> 56 <relationship type="one" related="mantle.product.store.ProductStore">
31 <key-map field-name="productStoreId"/> 57 <key-map field-name="productStoreId"/>
......
1 <?xml version="1.0" encoding="UTF-8"?> 1 <?xml version="1.0" encoding="UTF-8"?>
2 <secas xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://moqui.org/xsd/service-eca-3.xsd"> 2 <secas xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://moqui.org/xsd/service-eca-3.xsd">
3 <!-- SECA for CommunicationEvent to Agent (uses SmtyAgentTask pattern) -->
3 <seca id="AgentTriggerOnCommunication" service="create#mantle.party.communication.CommunicationEvent" when="post-service"> 4 <seca id="AgentTriggerOnCommunication" service="create#mantle.party.communication.CommunicationEvent" when="post-service">
4 <actions> 5 <actions>
5 <script><![CDATA[ 6 <script><![CDATA[
...@@ -14,16 +15,17 @@ ...@@ -14,16 +15,17 @@
14 15
15 ec.logger.info("SECA AgentTriggerOnCommunication: Creating SmtyAgentTask SystemMessage for thread ${rootId}") 16 ec.logger.info("SECA AgentTriggerOnCommunication: Creating SmtyAgentTask SystemMessage for thread ${rootId}")
16 17
17 // Trigger Agent Turn 18 // Create Agent Task using process#LLMRequest with callback
18 ec.service.sync().name("create#moqui.service.message.SystemMessage").parameters([ 19 ec.service.async().name("AgentServices.process#LLMRequest").parameters([
19 systemMessageTypeId: 'SmtyAgentTask', 20 prompt: body,
20 statusId: 'SmsgReceived',
21 requestedByPartyId: fromPartyId, 21 requestedByPartyId: fromPartyId,
22 effectiveUserId: ec.user.userId, 22 effectiveUserId: ec.user.userId,
23 productStoreId: 'POPC_DEFAULT', 23 productStoreId: 'POPC_DEFAULT',
24 aiConfigId: 'DEFAULT', 24 aiConfigId: 'DEFAULT',
25 rootCommEventId: rootId, 25 callbackServiceName: "AgentServices.callback#CommunicationEvent",
26 isOutgoing: 'N' 26 callbackParameters: [rootCommEventId: rootId, communicationEventId: communicationEventId],
27 sourceTypeEnumId: "CommunicationEvent",
28 sourceId: communicationEventId
27 ]).call() 29 ]).call()
28 } 30 }
29 ]]></script> 31 ]]></script>
......