✨ Support OpenAI(compatible) LLM providers

#130
This commit is contained in:
OpenWaygate committed 2026-08-29 17:14:15 +08:00
1 parent 15608bb8e2
commit 2bc498aa73
4 files changed
+80 -36

No files matched your search

+1
View File
@@ -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
View File
@@ -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)
}
+5
View File
@@ -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
+15
View File
@@ -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=