Skip to content

Commit 061264c

Browse files
akoclaude
andcommitted
feat: add CREATE/DROP MODEL for agent-editor Model documents
Implements write support for the agent-editor Model document type, the first CustomBlobDocument writer in the codebase. New commands: - CREATE MODEL Module.Name (Provider: MxCloudGenAI, Key: Module.Const) - DROP MODEL Module.Name Infrastructure built for reuse by other agent-editor doc types: - sdk/mpr/writer_customblob.go — generic CustomBlobDocument BSON wrapper writer (sets $Type, $ID, Metadata with CreatedByExtension and ReadableTypeName, Contents JSON, ExportLevel, Excluded) - sdk/mpr/writer_agenteditor_model.go — Model-specific Contents JSON encoder matching Studio Pro's exact output shape - Executor.resolveConstantRef() — resolves a qualified constant name to {documentId, qualifiedName} for the providerFields.key reference Validation: - Rejects duplicate model names in the same project - Rejects missing Key constant references with actionable error - Defaults Provider to MxCloudGenAI when omitted Byte-for-byte verified: Contents JSON produced by CREATE MODEL matches the Studio-Pro-created Agents.MyFirstModel exactly (same field names, order, nesting, and values including the resolved constant documentId UUID). Grammar: createModelStatement rule with modelProperty alternatives (identifierOrKeyword for Provider, qualifiedName for Key, STRING_LITERAL for Portal-populated metadata). DROP MODEL added to dropStatement rule. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 4aafd76 commit 061264c

17 files changed

Lines changed: 10551 additions & 9567 deletions

mdl/ast/ast_agenteditor.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
package ast
4+
5+
// CreateModelStmt represents:
6+
//
7+
// CREATE MODEL Module.Name (
8+
// Provider: MxCloudGenAI,
9+
// Key: Module.SomeConstant
10+
// -- optional Portal-populated fields:
11+
// [, DisplayName: '...']
12+
// [, KeyName: '...']
13+
// [, KeyId: '...']
14+
// [, Environment: '...']
15+
// [, ResourceName: '...']
16+
// [, DeepLinkURL: '...']
17+
// );
18+
type CreateModelStmt struct {
19+
Name QualifiedName
20+
Documentation string
21+
Provider string // "MxCloudGenAI" by default
22+
Key *QualifiedName // qualified name of the String constant holding the Portal key
23+
DisplayName string // optional Portal-populated metadata
24+
KeyName string // optional Portal-populated metadata
25+
KeyID string // optional Portal-populated metadata
26+
Environment string // optional Portal-populated metadata
27+
ResourceName string // optional Portal-populated metadata
28+
DeepLinkURL string // optional Portal-populated metadata
29+
}
30+
31+
func (s *CreateModelStmt) isStatement() {}
32+
33+
// DropModelStmt represents: DROP MODEL Module.Name
34+
type DropModelStmt struct {
35+
Name QualifiedName
36+
}
37+
38+
func (s *DropModelStmt) isStatement() {}

mdl/backend/infrastructure.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,6 @@ type AgentEditorBackend interface {
5050
ListAgentEditorKnowledgeBases() ([]*agenteditor.KnowledgeBase, error)
5151
ListAgentEditorConsumedMCPServices() ([]*agenteditor.ConsumedMCPService, error)
5252
ListAgentEditorAgents() ([]*agenteditor.Agent, error)
53+
CreateAgentEditorModel(m *agenteditor.Model) error
54+
DeleteAgentEditorModel(id string) error
5355
}

mdl/backend/mock/mock_infrastructure.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,3 +187,6 @@ func (m *MockBackend) ListAgentEditorAgents() ([]*agenteditor.Agent, error) {
187187
}
188188
return nil, nil
189189
}
190+
191+
func (m *MockBackend) CreateAgentEditorModel(_ *agenteditor.Model) error { return nil }
192+
func (m *MockBackend) DeleteAgentEditorModel(_ string) error { return nil }

mdl/backend/mpr/backend.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -700,3 +700,9 @@ func (b *MprBackend) ListAgentEditorConsumedMCPServices() ([]*agenteditor.Consum
700700
func (b *MprBackend) ListAgentEditorAgents() ([]*agenteditor.Agent, error) {
701701
return b.reader.ListAgentEditorAgents()
702702
}
703+
func (b *MprBackend) CreateAgentEditorModel(m *agenteditor.Model) error {
704+
return b.writer.CreateAgentEditorModel(m)
705+
}
706+
func (b *MprBackend) DeleteAgentEditorModel(id string) error {
707+
return b.writer.DeleteAgentEditorModel(id)
708+
}

mdl/executor/cmd_agenteditor_models.go

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,99 @@ func describeAgentEditorModel(ctx *ExecContext, name ast.QualifiedName) error {
130130
return nil
131131
}
132132

133+
// execCreateAgentEditorModel handles CREATE MODEL Module.Name (...).
134+
func execCreateAgentEditorModel(ctx *ExecContext, s *ast.CreateModelStmt) error {
135+
if !ctx.Connected() {
136+
return mdlerrors.NewNotConnected()
137+
}
138+
139+
module, err := findOrCreateModule(ctx, s.Name.Module)
140+
if err != nil {
141+
return err
142+
}
143+
144+
if existing := findAgentEditorModel(ctx, s.Name.Module, s.Name.Name); existing != nil {
145+
return mdlerrors.NewAlreadyExists("model", s.Name.String())
146+
}
147+
148+
var keyRef *agenteditor.ConstantRef
149+
if s.Key != nil {
150+
keyRef, err = resolveConstantRef(ctx, *s.Key)
151+
if err != nil {
152+
return fmt.Errorf("CREATE MODEL %s: %w", s.Name, err)
153+
}
154+
}
155+
156+
provider := s.Provider
157+
if provider == "" {
158+
provider = "MxCloudGenAI"
159+
}
160+
161+
m := &agenteditor.Model{
162+
ContainerID: module.ID,
163+
Name: s.Name.Name,
164+
Documentation: s.Documentation,
165+
Provider: provider,
166+
Key: keyRef,
167+
DisplayName: s.DisplayName,
168+
KeyName: s.KeyName,
169+
KeyID: s.KeyID,
170+
Environment: s.Environment,
171+
ResourceName: s.ResourceName,
172+
DeepLinkURL: s.DeepLinkURL,
173+
}
174+
175+
if err := ctx.Backend.CreateAgentEditorModel(m); err != nil {
176+
return mdlerrors.NewBackend("create model", err)
177+
}
178+
invalidateHierarchy(ctx)
179+
fmt.Fprintf(ctx.Output, "Created model: %s\n", s.Name)
180+
return nil
181+
}
182+
183+
// execDropAgentEditorModel handles DROP MODEL Module.Name.
184+
func execDropAgentEditorModel(ctx *ExecContext, s *ast.DropModelStmt) error {
185+
if !ctx.Connected() {
186+
return mdlerrors.NewNotConnected()
187+
}
188+
189+
m := findAgentEditorModel(ctx, s.Name.Module, s.Name.Name)
190+
if m == nil {
191+
return mdlerrors.NewNotFound("model", s.Name.String())
192+
}
193+
194+
if err := ctx.Backend.DeleteAgentEditorModel(string(m.ID)); err != nil {
195+
return mdlerrors.NewBackend("delete model", err)
196+
}
197+
fmt.Fprintf(ctx.Output, "Dropped model: %s\n", s.Name)
198+
return nil
199+
}
200+
201+
// resolveConstantRef looks up a String constant by qualified name and
202+
// returns a ConstantRef ready to embed in a Model/KnowledgeBase
203+
// document's providerFields.key field.
204+
func resolveConstantRef(ctx *ExecContext, name ast.QualifiedName) (*agenteditor.ConstantRef, error) {
205+
consts, err := ctx.Backend.ListConstants()
206+
if err != nil {
207+
return nil, fmt.Errorf("failed to list constants: %w", err)
208+
}
209+
h, err := getHierarchy(ctx)
210+
if err != nil {
211+
return nil, err
212+
}
213+
for _, c := range consts {
214+
modID := h.FindModuleID(c.ContainerID)
215+
modName := h.GetModuleName(modID)
216+
if c.Name == name.Name && modName == name.Module {
217+
return &agenteditor.ConstantRef{
218+
DocumentID: string(c.ID),
219+
QualifiedName: name.String(),
220+
}, nil
221+
}
222+
}
223+
return nil, fmt.Errorf("constant not found: %s", name)
224+
}
225+
133226
// findAgentEditorModel looks up a model by module and name.
134227
func findAgentEditorModel(ctx *ExecContext, moduleName, modelName string) *agenteditor.Model {
135228
models, err := ctx.Backend.ListAgentEditorModels()

mdl/executor/register_stubs.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,3 +442,12 @@ func registerImportHandlers(r *Registry) {
442442
return execImport(ctx, stmt.(*ast.ImportStmt))
443443
})
444444
}
445+
446+
func registerAgentEditorHandlers(r *Registry) {
447+
r.Register(&ast.CreateModelStmt{}, func(ctx *ExecContext, stmt ast.Statement) error {
448+
return execCreateAgentEditorModel(ctx, stmt.(*ast.CreateModelStmt))
449+
})
450+
r.Register(&ast.DropModelStmt{}, func(ctx *ExecContext, stmt ast.Statement) error {
451+
return execDropAgentEditorModel(ctx, stmt.(*ast.DropModelStmt))
452+
})
453+
}

mdl/executor/registry.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ func NewRegistry() *Registry {
5555
registerFragmentHandlers(r)
5656
registerSQLHandlers(r)
5757
registerImportHandlers(r)
58+
registerAgentEditorHandlers(r)
5859
return r
5960
}
6061

mdl/grammar/MDLParser.g4

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ createStatement
105105
| createConfigurationStatement
106106
| createPublishedRestServiceStatement
107107
| createDataTransformerStatement
108+
| createModelStatement
108109
)
109110
;
110111

@@ -287,6 +288,7 @@ dropStatement
287288
| DROP REST CLIENT qualifiedName
288289
| DROP PUBLISHED REST SERVICE qualifiedName
289290
| DROP DATA TRANSFORMER qualifiedName
291+
| DROP MODEL qualifiedName // DROP MODEL Module.Name (agent-editor)
290292
| DROP CONFIGURATION STRING_LITERAL
291293
| DROP FOLDER STRING_LITERAL IN (qualifiedName | IDENTIFIER)
292294
;
@@ -883,6 +885,25 @@ imageName
883885
| keyword
884886
;
885887

888+
// =============================================================================
889+
// AGENT-EDITOR MODEL CREATION
890+
// =============================================================================
891+
// CREATE MODEL Module.Name (
892+
// Provider: MxCloudGenAI,
893+
// Key: Module.SomeConstant
894+
// [, DisplayName: '...', KeyName: '...', etc. — Portal-populated metadata]
895+
// );
896+
createModelStatement
897+
: MODEL qualifiedName
898+
LPAREN modelProperty (COMMA modelProperty)* RPAREN
899+
;
900+
901+
modelProperty
902+
: identifierOrKeyword COLON identifierOrKeyword // Provider: MxCloudGenAI
903+
| identifierOrKeyword COLON qualifiedName // Key: Module.Constant
904+
| identifierOrKeyword COLON STRING_LITERAL // DisplayName: 'GPT-4 Turbo' etc.
905+
;
906+
886907
// =============================================================================
887908
// JSON STRUCTURE CREATION
888909
// =============================================================================

mdl/grammar/parser/MDLParser.interp

Lines changed: 3 additions & 1 deletion
Large diffs are not rendered by default.

mdl/grammar/parser/mdl_lexer.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)