feat(agentic): support brief-driven content generation
Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
parent
8f8e0f09ca
commit
3d528e6963
4 changed files with 100 additions and 10 deletions
|
|
@ -154,13 +154,17 @@ func (s *PrepSubsystem) cmdPrep(options core.Options) core.Result {
|
|||
|
||||
func (s *PrepSubsystem) cmdGenerate(options core.Options) core.Result {
|
||||
prompt := optionStringValue(options, "prompt", "_arg")
|
||||
if prompt == "" {
|
||||
core.Print(nil, "usage: core-agent generate --prompt=\"Draft a release note\" [--provider=claude] [--config='{\"max_tokens\":4000}']")
|
||||
return core.Result{Value: core.E("agentic.cmdGenerate", "prompt is required", nil), OK: false}
|
||||
briefID := optionStringValue(options, "brief_id", "brief-id")
|
||||
template := optionStringValue(options, "template")
|
||||
if prompt == "" && (briefID == "" || template == "") {
|
||||
core.Print(nil, "usage: core-agent generate --prompt=\"Draft a release note\" [--brief-id=brief_1 --template=help-article] [--provider=claude] [--config='{\"max_tokens\":4000}']")
|
||||
return core.Result{Value: core.E("agentic.cmdGenerate", "prompt or brief-id/template is required", nil), OK: false}
|
||||
}
|
||||
|
||||
result := s.handleContentGenerate(s.commandContext(), core.NewOptions(
|
||||
core.Option{Key: "prompt", Value: prompt},
|
||||
core.Option{Key: "brief_id", Value: briefID},
|
||||
core.Option{Key: "template", Value: template},
|
||||
core.Option{Key: "provider", Value: options.String("provider")},
|
||||
core.Option{Key: "config", Value: options.String("config")},
|
||||
))
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import (
|
|||
core "dappco.re/go/core"
|
||||
"dappco.re/go/core/forge"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// testPrepWithCore creates a PrepSubsystem backed by a real Core + Forge mock.
|
||||
|
|
@ -719,6 +720,40 @@ func TestCommands_CmdGenerate_Good(t *testing.T) {
|
|||
assert.Contains(t, output, "content: Release notes draft")
|
||||
}
|
||||
|
||||
func TestCommands_CmdGenerate_Good_BriefTemplate(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.Equal(t, "/v1/content/generate", r.URL.Path)
|
||||
assert.Equal(t, http.MethodPost, r.Method)
|
||||
|
||||
bodyResult := core.ReadAll(r.Body)
|
||||
require.True(t, bodyResult.OK)
|
||||
|
||||
var payload map[string]any
|
||||
parseResult := core.JSONUnmarshalString(bodyResult.Value.(string), &payload)
|
||||
require.True(t, parseResult.OK)
|
||||
assert.Equal(t, "brief_1", payload["brief_id"])
|
||||
assert.Equal(t, "help-article", payload["template"])
|
||||
|
||||
_, _ = w.Write([]byte(`{"data":{"id":"gen_2","provider":"claude","model":"claude-3.7-sonnet","content":"Template draft","status":"completed"}}`))
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
s := testPrepWithPlatformServer(t, server, "secret-token")
|
||||
output := captureStdout(t, func() {
|
||||
r := s.cmdGenerate(core.NewOptions(
|
||||
core.Option{Key: "brief_id", Value: "brief_1"},
|
||||
core.Option{Key: "template", Value: "help-article"},
|
||||
core.Option{Key: "provider", Value: "claude"},
|
||||
))
|
||||
assert.True(t, r.OK)
|
||||
})
|
||||
|
||||
assert.Contains(t, output, "provider: claude")
|
||||
assert.Contains(t, output, "model: claude-3.7-sonnet")
|
||||
assert.Contains(t, output, "status: completed")
|
||||
assert.Contains(t, output, "content: Template draft")
|
||||
}
|
||||
|
||||
func TestCommands_CmdExtract_Good(t *testing.T) {
|
||||
s, _ := testPrepWithCore(t, nil)
|
||||
target := core.JoinPath(t.TempDir(), "extract-test")
|
||||
|
|
|
|||
|
|
@ -41,9 +41,15 @@ type ContentBrief struct {
|
|||
UpdatedAt string `json:"updated_at,omitempty"`
|
||||
}
|
||||
|
||||
// input := agentic.ContentGenerateInput{Prompt: "Draft a release note", Provider: "claude"}
|
||||
// input := agentic.ContentGenerateInput{
|
||||
// BriefID: "brief_1",
|
||||
// Template: "help-article",
|
||||
// Provider: "claude",
|
||||
// }
|
||||
type ContentGenerateInput struct {
|
||||
Prompt string `json:"prompt"`
|
||||
Prompt string `json:"prompt,omitempty"`
|
||||
BriefID string `json:"brief_id,omitempty"`
|
||||
Template string `json:"template,omitempty"`
|
||||
Provider string `json:"provider,omitempty"`
|
||||
Config map[string]any `json:"config,omitempty"`
|
||||
}
|
||||
|
|
@ -151,6 +157,8 @@ type ContentFromPlanOutput struct {
|
|||
func (s *PrepSubsystem) handleContentGenerate(ctx context.Context, options core.Options) core.Result {
|
||||
_, output, err := s.contentGenerate(ctx, nil, ContentGenerateInput{
|
||||
Prompt: optionStringValue(options, "prompt"),
|
||||
BriefID: optionStringValue(options, "brief_id", "brief-id"),
|
||||
Template: optionStringValue(options, "template"),
|
||||
Provider: optionStringValue(options, "provider"),
|
||||
Config: optionAnyMapValue(options, "config"),
|
||||
})
|
||||
|
|
@ -261,7 +269,7 @@ func (s *PrepSubsystem) handleContentFromPlan(ctx context.Context, options core.
|
|||
func (s *PrepSubsystem) registerContentTools(server *mcp.Server) {
|
||||
mcp.AddTool(server, &mcp.Tool{
|
||||
Name: "content_generate",
|
||||
Description: "Generate content using the platform AI provider abstraction.",
|
||||
Description: "Generate content from a prompt or a brief/template pair using the platform AI provider abstraction.",
|
||||
}, s.contentGenerate)
|
||||
|
||||
mcp.AddTool(server, &mcp.Tool{
|
||||
|
|
@ -306,12 +314,22 @@ func (s *PrepSubsystem) registerContentTools(server *mcp.Server) {
|
|||
}
|
||||
|
||||
func (s *PrepSubsystem) contentGenerate(ctx context.Context, _ *mcp.CallToolRequest, input ContentGenerateInput) (*mcp.CallToolResult, ContentGenerateOutput, error) {
|
||||
if core.Trim(input.Prompt) == "" {
|
||||
return nil, ContentGenerateOutput{}, core.E("contentGenerate", "prompt is required", nil)
|
||||
hasPrompt := core.Trim(input.Prompt) != ""
|
||||
hasBrief := core.Trim(input.BriefID) != ""
|
||||
hasTemplate := core.Trim(input.Template) != ""
|
||||
if !hasPrompt && !(hasBrief && hasTemplate) {
|
||||
return nil, ContentGenerateOutput{}, core.E("contentGenerate", "prompt or brief_id plus template is required", nil)
|
||||
}
|
||||
|
||||
body := map[string]any{
|
||||
"prompt": input.Prompt,
|
||||
body := map[string]any{}
|
||||
if hasPrompt {
|
||||
body["prompt"] = input.Prompt
|
||||
}
|
||||
if input.BriefID != "" {
|
||||
body["brief_id"] = input.BriefID
|
||||
}
|
||||
if input.Template != "" {
|
||||
body["template"] = input.Template
|
||||
}
|
||||
if input.Provider != "" {
|
||||
body["provider"] = input.Provider
|
||||
|
|
|
|||
|
|
@ -53,6 +53,39 @@ func TestContent_HandleContentGenerate_Good(t *testing.T) {
|
|||
assert.Equal(t, 48, output.Result.OutputTokens)
|
||||
}
|
||||
|
||||
func TestContent_HandleContentGenerate_Good_BriefTemplate(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
require.Equal(t, "/v1/content/generate", r.URL.Path)
|
||||
require.Equal(t, http.MethodPost, r.Method)
|
||||
|
||||
bodyResult := core.ReadAll(r.Body)
|
||||
require.True(t, bodyResult.OK)
|
||||
|
||||
var payload map[string]any
|
||||
parseResult := core.JSONUnmarshalString(bodyResult.Value.(string), &payload)
|
||||
require.True(t, parseResult.OK)
|
||||
require.Equal(t, "brief_1", payload["brief_id"])
|
||||
require.Equal(t, "help-article", payload["template"])
|
||||
require.NotContains(t, payload, "prompt")
|
||||
|
||||
_, _ = w.Write([]byte(`{"data":{"result":{"id":"gen_2","provider":"claude","model":"claude-3.7-sonnet","content":"Template draft","status":"completed"}}}`))
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
subsystem := testPrepWithPlatformServer(t, server, "secret-token")
|
||||
result := subsystem.handleContentGenerate(context.Background(), core.NewOptions(
|
||||
core.Option{Key: "brief_id", Value: "brief_1"},
|
||||
core.Option{Key: "template", Value: "help-article"},
|
||||
core.Option{Key: "provider", Value: "claude"},
|
||||
))
|
||||
require.True(t, result.OK)
|
||||
|
||||
output, ok := result.Value.(ContentGenerateOutput)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, "gen_2", output.Result.ID)
|
||||
assert.Equal(t, "Template draft", output.Result.Content)
|
||||
}
|
||||
|
||||
func TestContent_HandleContentGenerate_Bad(t *testing.T) {
|
||||
subsystem := testPrepWithPlatformServer(t, nil, "secret-token")
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue