1 parent
15608bb8e2
commit
2bc498aa73
4 files changed
+80
-36
No files matched your search
@@ -22,6 +22,7 @@ go_library(
|
||||
"@org_golang_google_adk_v2//memory",
|
||||
"@org_golang_google_adk_v2//model",
|
||||
"@org_golang_google_adk_v2//model/gemini",
|
||||
"@org_golang_google_adk_v2//model/openaimodel",
|
||||
"@org_golang_google_adk_v2//session",
|
||||
"@org_golang_google_adk_v2//tool",
|
||||
"@org_golang_google_adk_v2//tool/geminitool",
|
||||
|
||||
+59
-36
@@ -22,6 +22,7 @@ import (
|
||||
"google.golang.org/adk/v2/memory"
|
||||
"google.golang.org/adk/v2/model"
|
||||
"google.golang.org/adk/v2/model/gemini"
|
||||
"google.golang.org/adk/v2/model/openaimodel"
|
||||
"google.golang.org/adk/v2/session"
|
||||
"google.golang.org/adk/v2/tool"
|
||||
"google.golang.org/adk/v2/tool/geminitool"
|
||||
@@ -34,25 +35,38 @@ import (
|
||||
const (
|
||||
short = "Start an agent to automate YouTube workflows"
|
||||
long = "Start an agent to automate YouTube workflows."
|
||||
example = `# console mode
|
||||
yutu agent --args "console" --model "google:gemini-3.7-flash" --api-key "YOUR_KEY"
|
||||
# web mode
|
||||
yutu agent --args "web api a2a webui" --model "google:gemini-3.7-flash" --api-key "YOUR_KEY"`
|
||||
example = `# Gemini (default)
|
||||
yutu agent --provider google --model gemini-3.7-flash --api-key "YOUR_KEY"
|
||||
# OpenAI
|
||||
yutu agent --provider openai --model gpt-5.6-terra
|
||||
# OpenAI-compatible endpoint (DeepSeek, Ollama, vLLM, etc.)
|
||||
yutu agent --provider openai-compatible --model deepseek-v4-flash --base-url "https://api.deepseek.com/"
|
||||
# Custom Gemini endpoint
|
||||
yutu agent --provider google --model gemini-3.7-flash --base-url "https://custom-endpoint.example.com/"`
|
||||
argsUsage = "Launcher arguments as a single string"
|
||||
modelUsage = "Model in provider:modelName format"
|
||||
providerUsage = "LLM provider (google, openai, openai-compatible)"
|
||||
modelUsage = "Model name"
|
||||
apiKeyUsage = "API key for the model provider"
|
||||
baseURLUsage = "Base URL for the model provider's API endpoint"
|
||||
instructionUsage = "Override the built-in agent instruction"
|
||||
agentDescription = "YouTube growth strategist and workflow assistant — retrieve, create, update, and delete YouTube content."
|
||||
|
||||
agentDescription = "YouTube growth strategist and workflow assistant — retrieves, creates, updates, and deletes YouTube content."
|
||||
errInvalidModelSpec = "invalid model spec %q: expected provider:modelName, e.g. google:gemini-3.7-flash"
|
||||
errUnsupportedProvider = "unsupported provider %q: only \"google\" is supported"
|
||||
errUnsupportedProvider = "unsupported provider %q: supported providers are google, openai, openai-compatible"
|
||||
errModelConfig = "model configuration error"
|
||||
errMCPConnect = "failed to connect to MCP server"
|
||||
errMCPToolSet = "failed to create MCP tool set"
|
||||
errSkillToolset = "failed to create skill toolset"
|
||||
errBuildAgent = "failed to build agent"
|
||||
errLaunchAgent = "failed to launch agent"
|
||||
)
|
||||
|
||||
var (
|
||||
launcherArgs string
|
||||
modelSpec string
|
||||
apiKey string
|
||||
instructionOverride string
|
||||
launcherArgs string
|
||||
provider string
|
||||
modelName string
|
||||
apiKey string
|
||||
baseURL string
|
||||
inst string
|
||||
|
||||
//go:embed INSTRUCTION.md
|
||||
instruction string
|
||||
@@ -81,27 +95,32 @@ func init() {
|
||||
cmd.RootCmd.AddCommand(agentCmd)
|
||||
agentCmd.Flags().StringVarP(&launcherArgs, "args", "a", "console", argsUsage)
|
||||
agentCmd.Flags().StringVarP(
|
||||
&modelSpec, "model", "m", "google:gemini-3.7-flash", modelUsage,
|
||||
&provider, "provider", "p", "google", providerUsage,
|
||||
)
|
||||
agentCmd.Flags().StringVarP(
|
||||
&modelName, "model", "m", "gemini-3.7-flash", modelUsage,
|
||||
)
|
||||
agentCmd.Flags().StringVar(&apiKey, "api-key", "", apiKeyUsage)
|
||||
agentCmd.Flags().StringVarP(
|
||||
&instructionOverride, "instruction", "i", "", instructionUsage,
|
||||
)
|
||||
agentCmd.Flags().StringVar(&baseURL, "base-url", "", baseURLUsage)
|
||||
agentCmd.Flags().StringVarP(&inst, "instruction", "i", "", instructionUsage)
|
||||
}
|
||||
|
||||
func newModel(ctx context.Context, spec string) (model.LLM, error) {
|
||||
parts := strings.SplitN(spec, ":", 2)
|
||||
if len(parts) != 2 || parts[0] == "" || parts[1] == "" {
|
||||
return nil, fmt.Errorf(errInvalidModelSpec, spec)
|
||||
}
|
||||
|
||||
provider, modelName := parts[0], parts[1]
|
||||
func newModel(ctx context.Context) (model.LLM, error) {
|
||||
switch provider {
|
||||
case "google":
|
||||
return gemini.NewModel(
|
||||
ctx, modelName, &genai.ClientConfig{
|
||||
cfg := &genai.ClientConfig{
|
||||
APIKey: apiKey,
|
||||
Backend: genai.BackendGeminiAPI,
|
||||
}
|
||||
if baseURL != "" {
|
||||
cfg.HTTPOptions.BaseURL = baseURL
|
||||
}
|
||||
return gemini.NewModel(ctx, modelName, cfg)
|
||||
case "openai", "openai-compatible":
|
||||
return openaimodel.NewModel(
|
||||
ctx, modelName, &openaimodel.ClientConfig{
|
||||
APIKey: apiKey,
|
||||
Backend: genai.BackendGeminiAPI,
|
||||
BaseURL: baseURL,
|
||||
},
|
||||
)
|
||||
default:
|
||||
@@ -121,33 +140,37 @@ func newSkillToolset(ctx context.Context) (tool.Toolset, error) {
|
||||
func buildAgent(
|
||||
m model.LLM, mcpToolSet, skillToolset tool.Toolset,
|
||||
) (agent.Agent, error) {
|
||||
var tools []tool.Tool
|
||||
if provider == "google" {
|
||||
tools = append(tools, geminitool.GoogleSearch{})
|
||||
}
|
||||
return llmagent.New(
|
||||
llmagent.Config{
|
||||
Name: "Miffy",
|
||||
Model: m,
|
||||
Description: agentDescription,
|
||||
Instruction: instruction,
|
||||
Tools: []tool.Tool{geminitool.GoogleSearch{}},
|
||||
Tools: tools,
|
||||
Toolsets: []tool.Toolset{mcpToolSet, skillToolset},
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func launch(ctx context.Context, writer io.Writer, args []string) {
|
||||
if instructionOverride != "" {
|
||||
instruction = instructionOverride
|
||||
if inst != "" {
|
||||
instruction = inst
|
||||
}
|
||||
|
||||
m, err := newModel(ctx, modelSpec)
|
||||
m, err := newModel(ctx)
|
||||
if err != nil {
|
||||
slog.ErrorContext(ctx, "model configuration error", "error", err)
|
||||
slog.ErrorContext(ctx, errModelConfig, "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
clientTransport, serverTransport := mcp.NewInMemoryTransports()
|
||||
_, err = cmd.Server.Connect(ctx, serverTransport, nil)
|
||||
if err != nil {
|
||||
slog.ErrorContext(ctx, "failed to connect to MCP server", "error", err)
|
||||
slog.ErrorContext(ctx, errMCPConnect, "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
@@ -157,19 +180,19 @@ func launch(ctx context.Context, writer io.Writer, args []string) {
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
slog.ErrorContext(ctx, "failed to create MCP tool set", "error", err)
|
||||
slog.ErrorContext(ctx, errMCPToolSet, "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
skillToolset, err := newSkillToolset(ctx)
|
||||
if err != nil {
|
||||
slog.ErrorContext(ctx, "failed to create skill toolset", "error", err)
|
||||
slog.ErrorContext(ctx, errSkillToolset, "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
miffy, err := buildAgent(m, mcpToolSet, skillToolset)
|
||||
if err != nil {
|
||||
slog.ErrorContext(ctx, "failed to build agent", "error", err)
|
||||
slog.ErrorContext(ctx, errBuildAgent, "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
@@ -183,7 +206,7 @@ func launch(ctx context.Context, writer io.Writer, args []string) {
|
||||
}
|
||||
l := full.NewLauncher()
|
||||
if err := l.Execute(ctx, config, args); err != nil {
|
||||
slog.ErrorContext(ctx, "failed to launch agent", "error", err)
|
||||
slog.ErrorContext(ctx, errLaunchAgent, "error", err)
|
||||
_, _ = fmt.Fprintln(writer, l.CommandLineSyntax())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
@@ -40,11 +40,16 @@ require (
|
||||
github.com/mattn/go-runewidth v0.0.28 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
|
||||
github.com/openai/openai-go/v3 v3.49.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/segmentio/asm v1.2.1 // indirect
|
||||
github.com/segmentio/encoding v0.5.4 // indirect
|
||||
github.com/tc-hib/go-winres v0.3.3 // indirect
|
||||
github.com/tc-hib/winres v0.2.1 // indirect
|
||||
github.com/tidwall/gjson v1.19.0 // indirect
|
||||
github.com/tidwall/match v1.2.0 // indirect
|
||||
github.com/tidwall/pretty v1.2.1 // indirect
|
||||
github.com/tidwall/sjson v1.2.5 // indirect
|
||||
github.com/urfave/cli/v2 v2.25.7 // indirect
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
||||
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
|
||||
|
||||
@@ -75,6 +75,10 @@ github.com/modelcontextprotocol/go-sdk v1.7.0 h1:yqjY2dsbKAC0LSuWZVBMrHgiG8ukXv6
|
||||
github.com/modelcontextprotocol/go-sdk v1.7.0/go.mod h1:dL7u98E/zjJTGzEq+j30jQ8K2k1mb6LeAH4inEcSGts=
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
||||
github.com/openai/openai-go/v3 v3.49.0 h1:/BObwLKdgHJ3AbJ7AXK8BRDfjuTAV+eTmEQFbG86wnQ=
|
||||
github.com/openai/openai-go/v3 v3.49.0/go.mod h1:nCqcbgPr2jYDV8wiCWPA2tRL56mWXdb6QavBmBwzSis=
|
||||
github.com/openai/openai-go/v3 v3.54.0 h1:qeNvIpFB/wzX7pI8USN1eruvzCS+XqOZ8mx73lGF6Lg=
|
||||
github.com/openai/openai-go/v3 v3.54.0/go.mod h1:ufI1+K+t0ijRB3gk8eztiw1crcDpsBuxRQL4sbLIrts=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
@@ -98,6 +102,17 @@ github.com/tc-hib/go-winres v0.3.3 h1:DQ50qlvDVhqrDOY0svTxZFZWfKWtZtfnXXHPCw6lqh
|
||||
github.com/tc-hib/go-winres v0.3.3/go.mod h1:5NGzOtuvjSqnpIEi2o1h48MKZzP9olvrf+PeY2t1uoA=
|
||||
github.com/tc-hib/winres v0.2.1 h1:YDE0FiP0VmtRaDn7+aaChp1KiF4owBiJa5l964l5ujA=
|
||||
github.com/tc-hib/winres v0.2.1/go.mod h1:C/JaNhH3KBvhNKVbvdlDWkbMDO9H4fKKDaN7/07SSuk=
|
||||
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/gjson v1.19.0 h1:xwxm7n691Uf3u5OFjzngavjGTh55KX5q/9w9xHW88JU=
|
||||
github.com/tidwall/gjson v1.19.0/go.mod h1:V37/opeE/JbLUOfH0QTXiNez2l0RUjYUhpT4szFQAfc=
|
||||
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||
github.com/tidwall/match v1.2.0 h1:0pt8FlkOwjN2fPt4bIl4BoNxb98gGHN2ObFEDkrfZnM=
|
||||
github.com/tidwall/match v1.2.0/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
|
||||
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
|
||||
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
|
||||
github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs=
|
||||
github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
|
||||
|
||||
Reference in new issue
Block a user