Skip to content

Commit 197b4e7

Browse files
committed
Merge branch 'main' into pr-204
# Conflicts: # mdl/grammar/parser/MDLParser.interp # mdl/grammar/parser/mdl_parser.go
2 parents e451acd + 78982ad commit 197b4e7

29 files changed

Lines changed: 9151 additions & 7415 deletions

cmd/mxcli/lsp_completions_gen.go

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

mdl/ast/ast_query.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,9 @@ const (
9090
ShowImportMappings // SHOW IMPORT MAPPINGS [IN module]
9191
ShowExportMappings // SHOW EXPORT MAPPINGS [IN module]
9292
ShowModels // SHOW MODELS [IN module] (agent-editor Model documents)
93+
ShowAgents // SHOW AGENTS [IN module] (agent-editor Agent documents)
94+
ShowKnowledgeBases // SHOW KNOWLEDGE BASES [IN module] (agent-editor KB documents)
95+
ShowConsumedMCPServices // SHOW CONSUMED MCP SERVICES [IN module] (agent-editor MCP documents)
9396
)
9497

9598
// String returns the human-readable name of the show object type.
@@ -217,6 +220,12 @@ func (t ShowObjectType) String() string {
217220
return "EXPORT MAPPINGS"
218221
case ShowModels:
219222
return "MODELS"
223+
case ShowAgents:
224+
return "AGENTS"
225+
case ShowKnowledgeBases:
226+
return "KNOWLEDGE BASES"
227+
case ShowConsumedMCPServices:
228+
return "CONSUMED MCP SERVICES"
220229
default:
221230
return "UNKNOWN"
222231
}
@@ -295,6 +304,9 @@ const (
295304
DescribeImportMapping // DESCRIBE IMPORT MAPPING Module.Name
296305
DescribeExportMapping // DESCRIBE EXPORT MAPPING Module.Name
297306
DescribeModel // DESCRIBE MODEL Module.Name (agent-editor Model document)
307+
DescribeAgent // DESCRIBE AGENT Module.Name (agent-editor Agent document)
308+
DescribeKnowledgeBase // DESCRIBE KNOWLEDGE BASE Module.Name (agent-editor KB document)
309+
DescribeConsumedMCPService // DESCRIBE CONSUMED MCP SERVICE Module.Name (agent-editor MCP document)
298310
)
299311

300312
// String returns the human-readable name of the describe object type.
@@ -370,6 +382,12 @@ func (t DescribeObjectType) String() string {
370382
return "EXPORT MAPPING"
371383
case DescribeModel:
372384
return "MODEL"
385+
case DescribeAgent:
386+
return "AGENT"
387+
case DescribeKnowledgeBase:
388+
return "KNOWLEDGE BASE"
389+
case DescribeConsumedMCPService:
390+
return "CONSUMED MCP SERVICE"
373391
default:
374392
return "UNKNOWN"
375393
}
Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
// Package executor - Commands for agent-editor Agent documents.
4+
//
5+
// Handles `SHOW AGENTS [IN module]` and `DESCRIBE AGENT Module.Name`.
6+
// The underlying BSON is a CustomBlobDocuments$CustomBlobDocument with
7+
// CustomDocumentType = "agenteditor.agent".
8+
package executor
9+
10+
import (
11+
"fmt"
12+
"strings"
13+
14+
"github.com/mendixlabs/mxcli/mdl/ast"
15+
"github.com/mendixlabs/mxcli/sdk/agenteditor"
16+
)
17+
18+
// showAgentEditorAgents handles SHOW AGENTS [IN module].
19+
func (e *Executor) showAgentEditorAgents(moduleName string) error {
20+
if e.reader == nil {
21+
return fmt.Errorf("not connected to a project")
22+
}
23+
24+
agents, err := e.reader.ListAgentEditorAgents()
25+
if err != nil {
26+
return fmt.Errorf("failed to list agents: %w", err)
27+
}
28+
29+
h, err := e.getHierarchy()
30+
if err != nil {
31+
return err
32+
}
33+
34+
result := &TableResult{
35+
Columns: []string{"Qualified Name", "Module", "Name", "Usage", "Model", "Tools", "KBs"},
36+
}
37+
38+
for _, a := range agents {
39+
modID := h.FindModuleID(a.ContainerID)
40+
modName := h.GetModuleName(modID)
41+
if moduleName != "" && modName != moduleName {
42+
continue
43+
}
44+
modelName := ""
45+
if a.Model != nil {
46+
modelName = a.Model.QualifiedName
47+
}
48+
result.Rows = append(result.Rows, []any{
49+
fmt.Sprintf("%s.%s", modName, a.Name),
50+
modName,
51+
a.Name,
52+
a.UsageType,
53+
modelName,
54+
len(a.Tools),
55+
len(a.KBTools),
56+
})
57+
}
58+
59+
result.Summary = fmt.Sprintf("(%d agent(s))", len(result.Rows))
60+
return e.writeResult(result)
61+
}
62+
63+
// describeAgentEditorAgent handles DESCRIBE AGENT Module.Name. Emits a
64+
// round-trippable CREATE AGENT statement reflecting the Contents JSON.
65+
func (e *Executor) describeAgentEditorAgent(name ast.QualifiedName) error {
66+
if e.reader == nil {
67+
return fmt.Errorf("not connected to a project")
68+
}
69+
70+
a := e.findAgentEditorAgent(name.Module, name.Name)
71+
if a == nil {
72+
return fmt.Errorf("agent not found: %s", name)
73+
}
74+
75+
h, err := e.getHierarchy()
76+
if err != nil {
77+
return err
78+
}
79+
modID := h.FindModuleID(a.ContainerID)
80+
modName := h.GetModuleName(modID)
81+
qualifiedName := fmt.Sprintf("%s.%s", modName, a.Name)
82+
83+
if a.Documentation != "" {
84+
fmt.Fprintf(e.output, "/**\n * %s\n */\n", a.Documentation)
85+
}
86+
87+
fmt.Fprintf(e.output, "CREATE AGENT %s (\n", qualifiedName)
88+
89+
// Build property lines. User-set properties are emitted in a stable
90+
// order; empty values are omitted.
91+
var lines []string
92+
if a.UsageType != "" {
93+
lines = append(lines, fmt.Sprintf(" UsageType: %s", a.UsageType))
94+
}
95+
if a.Description != "" {
96+
lines = append(lines, fmt.Sprintf(" Description: '%s'", escapeSQLString(a.Description)))
97+
}
98+
if a.Model != nil && a.Model.QualifiedName != "" {
99+
lines = append(lines, fmt.Sprintf(" Model: %s", a.Model.QualifiedName))
100+
}
101+
if a.Entity != nil && a.Entity.QualifiedName != "" {
102+
lines = append(lines, fmt.Sprintf(" Entity: %s", a.Entity.QualifiedName))
103+
}
104+
if len(a.Variables) > 0 {
105+
var parts []string
106+
for _, v := range a.Variables {
107+
kind := "String"
108+
if v.IsAttributeInEntity {
109+
kind = "EntityAttribute"
110+
}
111+
parts = append(parts, fmt.Sprintf("\"%s\": %s", v.Key, kind))
112+
}
113+
lines = append(lines, fmt.Sprintf(" Variables: (%s)", strings.Join(parts, ", ")))
114+
}
115+
if a.MaxTokens != nil {
116+
lines = append(lines, fmt.Sprintf(" MaxTokens: %d", *a.MaxTokens))
117+
}
118+
if a.ToolChoice != "" {
119+
lines = append(lines, fmt.Sprintf(" ToolChoice: %s", a.ToolChoice))
120+
}
121+
if a.Temperature != nil {
122+
lines = append(lines, fmt.Sprintf(" Temperature: %g", *a.Temperature))
123+
}
124+
if a.TopP != nil {
125+
lines = append(lines, fmt.Sprintf(" TopP: %g", *a.TopP))
126+
}
127+
if a.SystemPrompt != "" {
128+
lines = append(lines, fmt.Sprintf(" SystemPrompt: '%s'", escapeSQLString(a.SystemPrompt)))
129+
}
130+
if a.UserPrompt != "" {
131+
lines = append(lines, fmt.Sprintf(" UserPrompt: '%s'", escapeSQLString(a.UserPrompt)))
132+
}
133+
134+
for i, line := range lines {
135+
if i < len(lines)-1 {
136+
fmt.Fprintln(e.output, line+",")
137+
} else {
138+
fmt.Fprintln(e.output, line)
139+
}
140+
}
141+
142+
// Body with TOOL / MCP SERVICE / KNOWLEDGE BASE blocks if present.
143+
hasBody := len(a.Tools) > 0 || len(a.KBTools) > 0
144+
if hasBody {
145+
fmt.Fprintln(e.output, ")")
146+
fmt.Fprintln(e.output, "{")
147+
148+
for i, t := range a.Tools {
149+
emitToolBlock(e, t)
150+
if i < len(a.Tools)-1 || len(a.KBTools) > 0 {
151+
fmt.Fprintln(e.output)
152+
}
153+
}
154+
for i, kb := range a.KBTools {
155+
emitKBBlock(e, kb)
156+
if i < len(a.KBTools)-1 {
157+
fmt.Fprintln(e.output)
158+
}
159+
}
160+
161+
fmt.Fprintln(e.output, "};")
162+
} else {
163+
fmt.Fprintln(e.output, ");")
164+
}
165+
fmt.Fprintln(e.output, "/")
166+
return nil
167+
}
168+
169+
// emitToolBlock writes one TOOL or MCP SERVICE block for the agent body.
170+
func emitToolBlock(e *Executor, t agenteditor.AgentTool) {
171+
switch t.ToolType {
172+
case "MCP":
173+
if t.Document == nil {
174+
// malformed — skip
175+
return
176+
}
177+
fmt.Fprintf(e.output, " MCP SERVICE %s {\n", t.Document.QualifiedName)
178+
fmt.Fprintf(e.output, " Enabled: %t\n", t.Enabled)
179+
if t.Description != "" {
180+
fmt.Fprintf(e.output, " Description: '%s'\n", escapeSQLString(t.Description))
181+
}
182+
fmt.Fprintln(e.output, " }")
183+
default:
184+
// Microflow or unknown tool type — emit generic TOOL block.
185+
name := t.Name
186+
if name == "" {
187+
name = "Tool_" + strings.ReplaceAll(t.ID, "-", "")[:8]
188+
}
189+
fmt.Fprintf(e.output, " TOOL %s {\n", name)
190+
if t.ToolType != "" {
191+
fmt.Fprintf(e.output, " ToolType: %s,\n", t.ToolType)
192+
}
193+
if t.Document != nil && t.Document.QualifiedName != "" {
194+
fmt.Fprintf(e.output, " Document: %s,\n", t.Document.QualifiedName)
195+
}
196+
fmt.Fprintf(e.output, " Enabled: %t", t.Enabled)
197+
if t.Description != "" {
198+
fmt.Fprintln(e.output, ",")
199+
fmt.Fprintf(e.output, " Description: '%s'\n", escapeSQLString(t.Description))
200+
} else {
201+
fmt.Fprintln(e.output)
202+
}
203+
fmt.Fprintln(e.output, " }")
204+
}
205+
}
206+
207+
// emitKBBlock writes one KNOWLEDGE BASE block for the agent body.
208+
func emitKBBlock(e *Executor, kb agenteditor.AgentKBTool) {
209+
name := kb.Name
210+
if name == "" {
211+
name = "KB_" + strings.ReplaceAll(kb.ID, "-", "")[:8]
212+
}
213+
fmt.Fprintf(e.output, " KNOWLEDGE BASE %s {\n", name)
214+
if kb.Document != nil && kb.Document.QualifiedName != "" {
215+
fmt.Fprintf(e.output, " Source: %s,\n", kb.Document.QualifiedName)
216+
}
217+
if kb.CollectionIdentifier != "" {
218+
fmt.Fprintf(e.output, " Collection: '%s',\n", escapeSQLString(kb.CollectionIdentifier))
219+
}
220+
if kb.MaxResults != 0 {
221+
fmt.Fprintf(e.output, " MaxResults: %d,\n", kb.MaxResults)
222+
}
223+
if kb.Description != "" {
224+
fmt.Fprintf(e.output, " Description: '%s',\n", escapeSQLString(kb.Description))
225+
}
226+
fmt.Fprintf(e.output, " Enabled: %t\n", kb.Enabled)
227+
fmt.Fprintln(e.output, " }")
228+
}
229+
230+
// findAgentEditorAgent looks up an agent by module and name.
231+
func (e *Executor) findAgentEditorAgent(moduleName, agentName string) *agenteditor.Agent {
232+
agents, err := e.reader.ListAgentEditorAgents()
233+
if err != nil {
234+
return nil
235+
}
236+
h, err := e.getHierarchy()
237+
if err != nil {
238+
return nil
239+
}
240+
for _, a := range agents {
241+
modID := h.FindModuleID(a.ContainerID)
242+
modName := h.GetModuleName(modID)
243+
if a.Name == agentName && modName == moduleName {
244+
return a
245+
}
246+
}
247+
return nil
248+
}

0 commit comments

Comments
 (0)