From f8fd4ad20670a22aaea07d95f4307c47c2812e03 Mon Sep 17 00:00:00 2001 From: 201430098137 <1850396756@qq.com> Date: Fri, 21 Nov 2025 10:03:37 +0800 Subject: [PATCH 1/9] feat(tool):implement email toolSet(send mail) and add example to use --- examples/email/README.md | 70 ++++++++++ examples/email/main.go | 261 ++++++++++++++++++++++++++++++++++++ examples/go.mod | 2 + examples/go.sum | 4 + go.mod | 2 + go.sum | 4 + tool/email/email.go | 110 +++++++++++++++ tool/email/email_test.go | 79 +++++++++++ tool/email/sendmail.go | 117 ++++++++++++++++ tool/email/sendmail_test.go | 61 +++++++++ 10 files changed, 710 insertions(+) create mode 100644 examples/email/README.md create mode 100644 examples/email/main.go create mode 100644 tool/email/email.go create mode 100644 tool/email/email_test.go create mode 100644 tool/email/sendmail.go create mode 100644 tool/email/sendmail_test.go diff --git a/examples/email/README.md b/examples/email/README.md new file mode 100644 index 000000000..51ec6dbed --- /dev/null +++ b/examples/email/README.md @@ -0,0 +1,70 @@ +# email Example + +This example demonstrates how to handle various types of email (qq,gmail) using OpenAI-compatible models. + +## Features + +- **send email**: send email from your_email to other email, you can send whatever you want, but not support attachment + +## Quick Start + +```bash +# set your open base url, +export OPENAI_BASE_URL="http://ieval.woa.com/openapi/v1" +# Set your API key +export OPENAI_API_KEY="your-api-key-here" + +go run main.go -model gpt-4o-mini +``` + +## Example Session + +``` +🚀 Send Email Chat Demo +Model: gpt-5 +Streaming: true +Type 'exit' to end the conversation +Available tools: send_email +================================================== +✅ Email chat ready! Session: email-session-1763626040 +💡 Try asking questions like: + - send an email to zhuangguang5524621@gmail.com +👤 You: send an email to zhuangguang5524621@gmail.com +🤖 Assistant: I can send the email for you. Please provide the following so I can proceed: +- Email account credentials for sending: account name and password +- Subject line +- Email body/content +- Optional: the display “From” name you want to appear +Recipient to confirm: zhuangguang5524621@gmail.com +If you prefer, I can also draft the email content first and share it with you for approval before sending. +👤 You: name:zhuangguang5524621@gmail.com passwd: "xxxxxx" send an email to 1850396756@qq.com subject: hello content:
内容
+🤖 Assistant: +🔍 email initiated: + • email_send_email (ID: call_DxMS5B7zqSCj8jiEVx6pyG56) + Query: {"auth":{"name":"zhuangguang5524621@gmail.com","password":"xxxxx"},"mail_list":[{"to_email":"1850396756@qq.com","subject":"hello","content":"内容
"}]} +🔄 send email... +✅ send email results (ID: call_DxMS5B7zqSCj8jiEVx6pyG56): {"message":""} +Your email has been sent successfully. +Details: +- From: zhuangguang5524621@gmail.com +- To: 1850396756@qq.com +- Subject: hello +- Content (HTML): +内容
+If you’d like to send more emails or schedule one, let me know the details. +👤 You: exit +👋 Goodbye! +``` + +## How It Works + +1. **Setup**: The example creates an LLM agent with access to the email tool +2. **User Input**: Users can ask to send email +3. **Tool Detection**: The AI automatically decides when to use the email tool or ask more information of send email +4. **Email Send Execution**: The email tool performs send email and returns structured results +5. **Response Generation**: The AI uses the search results to provide informed, up-to-date responses + +## API Design & Limitations + +### Why These Limitations Exist +1. the send email tool use smtp protocol, mailbox have speed limit of send email. \ No newline at end of file diff --git a/examples/email/main.go b/examples/email/main.go new file mode 100644 index 000000000..45101c2ba --- /dev/null +++ b/examples/email/main.go @@ -0,0 +1,261 @@ +package main + +import ( + "bufio" + "context" + "flag" + "fmt" + "log" + "os" + "strings" + "time" + "trpc.group/trpc-go/trpc-agent-go/agent/llmagent" + "trpc.group/trpc-go/trpc-agent-go/event" + "trpc.group/trpc-go/trpc-agent-go/model" + "trpc.group/trpc-go/trpc-agent-go/model/openai" + "trpc.group/trpc-go/trpc-agent-go/runner" + "trpc.group/trpc-go/trpc-agent-go/tool" + "trpc.group/trpc-go/trpc-agent-go/tool/email" +) + +var ( + streaming = flag.Bool("streaming", true, "Enable streaming mode for responses") + modelName = flag.String("model", "deepseek-chat", "Name of the model to use") +) + +func main() { + // Parse command line flags. + flag.Parse() + + fmt.Printf("🚀 Send Email Chat Demo\n") + fmt.Printf("Model: %s\n", *modelName) + fmt.Printf("Streaming: %t\n", *streaming) + fmt.Printf("Type 'exit' to end the conversation\n") + fmt.Printf("Available tools: send_email\n") + fmt.Println(strings.Repeat("=", 50)) + + // Create and run the chat. + chat := &emailChat{ + modelName: *modelName, + streaming: *streaming, + } + + if err := chat.run(); err != nil { + log.Fatal("Chat failed: %v", err) + } +} + +type emailChat struct { + modelName string + runner runner.Runner + userID string + sessionID string + streaming bool +} + +// run starts the interactive chat session. +func (c *emailChat) run() error { + ctx := context.Background() + + // Setup the runner. + if err := c.setup(ctx); err != nil { + return fmt.Errorf("setup failed: %w", err) + } + + // Start interactive chat. + return c.startChat(ctx) +} + +// setup creates the runner with LLM agent and send email tool. +func (c *emailChat) setup(ctx context.Context) error { + // Create OpenAI model. + modelInstance := openai.New(c.modelName) + + // Create email tool. + // For basic usage: + emailTool, err := email.NewToolSet() + if err != nil { + return fmt.Errorf("create file tool set: %w", err) + } + + // Create LLM agent with email tool. + genConfig := model.GenerationConfig{ + MaxTokens: intPtr(2000), + Temperature: floatPtr(0.7), + Stream: c.streaming, // Enable streaming + } + + agentName := "email-assistant" + llmAgent := llmagent.New( + agentName, + llmagent.WithModel(modelInstance), + llmagent.WithDescription("A helpful AI assistant with access to email sending capabilities"), + llmagent.WithInstruction("Use the email tool to send emails. ask user to provide account credentials"), + llmagent.WithGenerationConfig(genConfig), + llmagent.WithToolSets([]tool.ToolSet{emailTool}), + ) + + // Create runner. + appName := "email-agent" + c.runner = runner.NewRunner( + appName, + llmAgent, + ) + + // Setup identifiers. + c.userID = "user" + c.sessionID = fmt.Sprintf("email-session-%d", time.Now().Unix()) + + fmt.Printf("✅ Email chat ready! Session: %s\n\n", c.sessionID) + return nil +} + +// startChat runs the interactive conversation loop. +func (c *emailChat) startChat(ctx context.Context) error { + scanner := bufio.NewScanner(os.Stdin) + + // Print welcome message with examples. + fmt.Println("💡 Try asking questions like:") + fmt.Println(" - send an email to zhuangguang5524621@gmail.com user:your_email password:your_password subject:subject content:content") + fmt.Println() + + for { + fmt.Print("👤 You: ") + if !scanner.Scan() { + break + } + + userInput := strings.TrimSpace(scanner.Text()) + if userInput == "" { + continue + } + + // Handle exit command. + if strings.ToLower(userInput) == "exit" { + fmt.Println("👋 Goodbye!") + return nil + } + + // Process the user message. + if err := c.processMessage(ctx, userInput); err != nil { + fmt.Printf("❌ Error: %v\n", err) + } + + fmt.Println() // Add spacing between turns + } + + if err := scanner.Err(); err != nil { + return fmt.Errorf("input scanner error: %w", err) + } + + return nil +} + +// processMessage handles a single message exchange. +func (c *emailChat) processMessage(ctx context.Context, userMessage string) error { + message := model.NewUserMessage(userMessage) + // Run the agent through the runner. + eventChan, err := c.runner.Run(ctx, c.userID, c.sessionID, message) + if err != nil { + return fmt.Errorf("failed to run agent: %w", err) + } + // Process streaming response. + return c.processResponse(eventChan) +} + +// processResponse handles the response with email tool visualization. +func (c *emailChat) processResponse(eventChan <-chan *event.Event) error { + fmt.Print("🤖 Assistant: ") + + var ( + fullContent string + toolCallsDetected bool + assistantStarted bool + ) + + for event := range eventChan { + + // Handle errors. + if event.Error != nil { + fmt.Printf("\n❌ Error: %s\n", event.Error.Message) + continue + } + + // Detect and display tool calls. + if len(event.Response.Choices) > 0 && len(event.Response.Choices[0].Message.ToolCalls) > 0 { + toolCallsDetected = true + if assistantStarted { + fmt.Printf("\n") + } + fmt.Printf("🔍 email initiated:\n") + for _, toolCall := range event.Response.Choices[0].Message.ToolCalls { + fmt.Printf(" • %s (ID: %s)\n", toolCall.Function.Name, toolCall.ID) + if len(toolCall.Function.Arguments) > 0 { + fmt.Printf(" Query: %s\n", string(toolCall.Function.Arguments)) + } + } + fmt.Printf("\n🔄 send email...\n") + } + + // Detect tool responses. + if event.Response != nil && len(event.Response.Choices) > 0 { + hasToolResponse := false + for _, choice := range event.Response.Choices { + if choice.Message.Role == model.RoleTool && choice.Message.ToolID != "" { + fmt.Printf("✅ send email results (ID: %s): %s\n", + choice.Message.ToolID, + strings.TrimSpace(choice.Message.Content)) + hasToolResponse = true + } + } + if hasToolResponse { + continue + } + } + + // Process content from choices. + if len(event.Response.Choices) > 0 { + choice := event.Response.Choices[0] + + if !assistantStarted { + if toolCallsDetected { + fmt.Printf("\n🤖 Assistant: ") + } + assistantStarted = true + } + + // Handle content based on streaming mode. + var content string + if c.streaming { + // Streaming mode: use delta content. + content = choice.Delta.Content + } else { + // Non-streaming mode: use full message content. + content = choice.Message.Content + } + + if content != "" { + fmt.Print(content) + fullContent += content + } + } + + // Check if this is the final event. + if event.Done { + fmt.Printf("\n") + break + } + } + + return nil +} + +// intPtr returns a pointer to the given int. +func intPtr(i int) *int { + return &i +} + +// floatPtr returns a pointer to the given float64. +func floatPtr(f float64) *float64 { + return &f +} diff --git a/examples/go.mod b/examples/go.mod index 1a521ceaa..894f5a697 100644 --- a/examples/go.mod +++ b/examples/go.mod @@ -115,5 +115,7 @@ require ( google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect google.golang.org/grpc v1.67.0 // indirect google.golang.org/protobuf v1.34.2 // indirect + gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect + gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/examples/go.sum b/examples/go.sum index d523073a6..28444dfa9 100644 --- a/examples/go.sum +++ b/examples/go.sum @@ -221,9 +221,13 @@ google.golang.org/grpc v1.67.0 h1:IdH9y6PF5MPSdAntIcpjQ+tXO41pcQsfZV2RxtQgVcw= google.golang.org/grpc v1.67.0/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk= +gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE= +gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/go.mod b/go.mod index a20b83452..b7193815d 100644 --- a/go.mod +++ b/go.mod @@ -34,6 +34,7 @@ require ( golang.org/x/text v0.21.0 google.golang.org/grpc v1.65.0 google.golang.org/protobuf v1.34.2 + gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df trpc.group/trpc-go/trpc-a2a-go v0.2.5-0.20251023030722-7f02b57fd14a trpc.group/trpc-go/trpc-mcp-go v0.0.10 ) @@ -82,6 +83,7 @@ require ( golang.org/x/sys v0.30.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240822170219-fc7c04adadcd // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240822170219-fc7c04adadcd // indirect + gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 66d69cf63..98a53aa68 100644 --- a/go.sum +++ b/go.sum @@ -180,9 +180,13 @@ google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk= +gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE= +gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/tool/email/email.go b/tool/email/email.go new file mode 100644 index 000000000..e332df874 --- /dev/null +++ b/tool/email/email.go @@ -0,0 +1,110 @@ +// +// Tencent is pleased to support the open source community by making trpc-agent-go available. +// +// Copyright (C) 2025 Tencent. All rights reserved. +// +// trpc-agent-go is licensed under the Apache License Version 2.0. +// +// + +// Package email provides send email tools for AI agents. +// This tool can send emails to personal email and some Corporate Email. +package email + +import ( + "context" + "trpc.group/trpc-go/trpc-agent-go/tool" +) + +const ( + // default name + defaultName = "email" +) + +type MailboxType int32 + +const ( + // unknown mail + MAIL_UNKNOWN MailboxType = 0 + // qq mail + MAIL_QQ MailboxType = 1 + // 163 mail + MAIL_163 MailboxType = 2 + // google email + MAIL_GMAIL MailboxType = 3 +) + +func MailboxTypeToString(mailboxType MailboxType) string { + switch mailboxType { + case MAIL_QQ: + return "qq" + case MAIL_163: + return "163" + case MAIL_GMAIL: + return "gmail" + default: + return "unknown" + } +} + +// Option is a functional option for configuring the file tool set. +type Option func(*emailToolSet) + +// WithSendEmailEnabled enables or disables the send email functionality, default is true. +func WithSendEmailEnabled(enabled bool) Option { + return func(f *emailToolSet) { + f.sendEmailEnabled = enabled + } +} + +// WithName sets the name of the email tool set. +func WithName(name string) Option { + return func(f *emailToolSet) { + f.name = name + } +} + +// emailToolSet implements the ToolSet interface for file operations. +type emailToolSet struct { + sendEmailEnabled bool + tools []tool.Tool + name string +} + +// Tools implements the ToolSet interface. +func (e *emailToolSet) Tools(ctx context.Context) []tool.Tool { + return e.tools +} + +// Name implements the ToolSet interface. +func (e *emailToolSet) Name() string { + return e.name +} + +// Close implements the ToolSet interface. +func (e *emailToolSet) Close() error { + // No resources to clean up for file tools. + return nil +} + +// NewToolSet creates a new file tool set with the given options. +func NewToolSet(opts ...Option) (tool.ToolSet, error) { + emailToolSet := &emailToolSet{ + sendEmailEnabled: true, + tools: nil, + name: defaultName, + } + + // Apply user-provided options. + for _, opt := range opts { + opt(emailToolSet) + } + + // Create function tools based on enabled features. + var tools []tool.Tool + if emailToolSet.sendEmailEnabled { + tools = append(tools, emailToolSet.sendMailTool()) + } + emailToolSet.tools = tools + return emailToolSet, nil +} diff --git a/tool/email/email_test.go b/tool/email/email_test.go new file mode 100644 index 000000000..306748157 --- /dev/null +++ b/tool/email/email_test.go @@ -0,0 +1,79 @@ +package email + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestWithSendEmailEnabled(t *testing.T) { + t.Run("set true", func(t *testing.T) { + e := &emailToolSet{} + opt := WithSendEmailEnabled(true) + opt(e) + assert.True(t, e.sendEmailEnabled) + }) + + t.Run("set false", func(t *testing.T) { + e := &emailToolSet{} + opt := WithSendEmailEnabled(false) + opt(e) + assert.False(t, e.sendEmailEnabled) + }) +} + +func TestWithName(t *testing.T) { + type args struct { + name string + } + tests := []struct { + name string + args args + check func(*emailToolSet) + }{ + { + name: "normal name", + args: args{name: "aFrqvjeXQ"}, + check: func(e *emailToolSet) { + assert.Equal(t, "aFrqvjeXQ", e.name) + }, + }, + { + name: "empty name", + args: args{name: ""}, + check: func(e *emailToolSet) { + assert.Equal(t, "", e.name) + }, + }, + { + name: "unicode name", + args: args{name: "测试邮箱"}, + check: func(e *emailToolSet) { + assert.Equal(t, "测试邮箱", e.name) + }, + }, + { + name: "very long name", + args: args{name: string(make([]byte, 1024))}, + check: func(e *emailToolSet) { + assert.Len(t, e.name, 1024) + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + e := &emailToolSet{} + opt := WithName(tt.args.name) + opt(e) + tt.check(e) + }) + } +} + +func TestNewToolSet_Default(t *testing.T) { + set, err := NewToolSet() + assert.NoError(t, err) + ets := set.(*emailToolSet) + assert.Equal(t, true, ets.sendEmailEnabled) + assert.Equal(t, defaultName, ets.name) +} diff --git a/tool/email/sendmail.go b/tool/email/sendmail.go new file mode 100644 index 000000000..764089592 --- /dev/null +++ b/tool/email/sendmail.go @@ -0,0 +1,117 @@ +package email + +import ( + "context" + "fmt" + "gopkg.in/gomail.v2" + "strings" + "trpc.group/trpc-go/trpc-agent-go/tool" + "trpc.group/trpc-go/trpc-agent-go/tool/function" +) + +// sendMailRequest represents the input for the send mail operation. +type sendMailRequest struct { + Auth Auth `json:"auth" jsonschema:"description=auth of the mail."` + MailList []*mail `json:"mail_list" jsonschema:"description=The list of mail."` +} + +type mail struct { + ToEmail string `json:"to_email" jsonschema:"description=send to email."` + Subject string `json:"subject" jsonschema:"description=subject of the mail"` + Content string `json:"content" jsonschema:"description=content of the mail"` +} + +// Auth is a struct for email authentication. +type Auth struct { + Name string `json:"name" jsonschema:"description=name of the mail."` + Password string `json:"password" jsonschema:"description=password of the mail."` +} + +// sendMailResponse represents the output from the send mail operation. +type sendMailResponse struct { + Message string `json:"message"` +} + +// sendMail performs the send mail operation. +func (e *emailToolSet) sendMail(ctx context.Context, req *sendMailRequest) (rsp *sendMailResponse, err error) { + rsp = &sendMailResponse{} + + mailBoxType, err := checkMailBoxType(req.Auth.Name) + if err != nil { + rsp.Message = fmt.Sprintf("checkMailBoxType ERROR: %v", err) + return rsp, nil + } + + var addr string + var port int + switch mailBoxType { + case MAIL_QQ: + //qq email + addr = "smtp.qq.com" + port = 465 + case MAIL_GMAIL: + //gmail email + addr = "smtp.gmail.com" + port = 587 + default: + // not support + rsp.Message = fmt.Sprintf("not support mailbox type:%s", MailboxTypeToString(mailBoxType)) + return rsp, nil + } + + dialer := gomail.NewDialer(addr, port, req.Auth.Name, req.Auth.Password) + s, err := dialer.Dial() + if err != nil { + rsp.Message = fmt.Sprintf("the address or password is incorrect,please check: %v", err) + return rsp, fmt.Errorf("the address or password is incorrect,please check: %w", err) + } + + message := gomail.NewMessage() + for _, m := range req.MailList { + message.SetHeader("From", req.Auth.Name) + message.SetHeader("To", m.ToEmail) + message.SetHeader("Subject", m.Subject) + message.SetBody("text/html", m.Content) + if err := gomail.Send(s, message); err != nil { + rsp.Message = fmt.Sprintf("send ERROR: %v", err) + return rsp, fmt.Errorf("send ERROR: %w", err) + } + message.Reset() + } + + return +} + +// checkMailBoxType checks the mailbox type. +func checkMailBoxType(email string) (MailboxType, error) { + // to lower + email = strings.ToLower(email) + + // split by name and domain + parts := strings.Split(email, "@") + if len(parts) != 2 { + return MAIL_UNKNOWN, fmt.Errorf("invalid email address") + } + + domain := parts[1] + + switch domain { + case "qq.com", "vip.qq.com", "foxmail.com": + return MAIL_QQ, nil + case "gmail.com", "googlemail.com": + return MAIL_GMAIL, nil + case "163.com": + return MAIL_163, nil + default: + return MAIL_UNKNOWN, nil + } +} + +// sendMailTool returns a callable tool for send mail. +func (e *emailToolSet) sendMailTool() tool.CallableTool { + return function.NewFunctionTool( + e.sendMail, + function.WithName("send_email"), + function.WithDescription("send mail to other"), + ) +} diff --git a/tool/email/sendmail_test.go b/tool/email/sendmail_test.go new file mode 100644 index 000000000..e794534aa --- /dev/null +++ b/tool/email/sendmail_test.go @@ -0,0 +1,61 @@ +package email + +import ( + "context" + "testing" +) + +func TestMailTool_sendMail(t *testing.T) { + toolSet, err := NewToolSet( + WithSendEmailEnabled(true), + WithName("email"), + ) + if err != nil { + t.Errorf("NewToolSet failed, err: %v", err) + } + + tests := []struct { + Name string + Password string + ToEmail string + Subject string + Content string + }{ + // qq to gmail + { + Name: "1850396756@qq.com", + Password: "", + ToEmail: "zhuangguang5524621@gmail.com", + Subject: "test", + Content: "test", + }, + // gmail to qq + { + Name: "zhuangguang5524621@gmail.com", + Password: "", + ToEmail: "1850396756@qq.com", + Subject: "test", + Content: "test", + }, + } + for _, tt := range tests { + + rsp, err := toolSet.(*emailToolSet).sendMail(context.Background(), &sendMailRequest{ + Auth: Auth{ + Name: tt.Name, + Password: tt.Password, + }, + MailList: []*mail{ + { + ToEmail: tt.ToEmail, + Subject: tt.Subject, + Content: tt.Content, + }, + }, + }) + if err != nil { + t.Errorf("send mail failed, err: %v", err) + } + t.Logf("rsp: %+v", rsp) + } +} From 3ad38fee53d3629eb745e8da9f3fe9aae1d69582 Mon Sep 17 00:00:00 2001 From: 201430098137 <1850396756@qq.com> Date: Fri, 21 Nov 2025 19:47:51 +0800 Subject: [PATCH 2/9] fix:goimports and golangci-lint and remove some test --- examples/email/main.go | 5 +- tool/email/email.go | 17 +++-- tool/email/sendmail.go | 11 +++- tool/email/sendmail_test.go | 120 +++++++++++++++++++++++++++++++++++- 4 files changed, 140 insertions(+), 13 deletions(-) diff --git a/examples/email/main.go b/examples/email/main.go index 45101c2ba..23705867b 100644 --- a/examples/email/main.go +++ b/examples/email/main.go @@ -9,6 +9,7 @@ import ( "os" "strings" "time" + "trpc.group/trpc-go/trpc-agent-go/agent/llmagent" "trpc.group/trpc-go/trpc-agent-go/event" "trpc.group/trpc-go/trpc-agent-go/model" @@ -41,7 +42,7 @@ func main() { } if err := chat.run(); err != nil { - log.Fatal("Chat failed: %v", err) + log.Fatalf("Chat failed: %s", err.Error()) } } @@ -67,7 +68,7 @@ func (c *emailChat) run() error { } // setup creates the runner with LLM agent and send email tool. -func (c *emailChat) setup(ctx context.Context) error { +func (c *emailChat) setup(_ context.Context) error { // Create OpenAI model. modelInstance := openai.New(c.modelName) diff --git a/tool/email/email.go b/tool/email/email.go index e332df874..c91297cf8 100644 --- a/tool/email/email.go +++ b/tool/email/email.go @@ -13,6 +13,7 @@ package email import ( "context" + "trpc.group/trpc-go/trpc-agent-go/tool" ) @@ -21,27 +22,33 @@ const ( defaultName = "email" ) +// MailboxType mailbox type MailboxType int32 const ( - // unknown mail + // MAIL_UNKNOWN unknown mail MAIL_UNKNOWN MailboxType = 0 - // qq mail + // MAIL_QQ qq mail MAIL_QQ MailboxType = 1 - // 163 mail + // MAIL_163 163 mail MAIL_163 MailboxType = 2 - // google email + // MAIL_GMAIL google mail MAIL_GMAIL MailboxType = 3 ) +// MailboxTypeToString convert mailbox type to string func MailboxTypeToString(mailboxType MailboxType) string { switch mailboxType { + // qq mail case MAIL_QQ: return "qq" + // 163 mail case MAIL_163: return "163" + // google mail case MAIL_GMAIL: return "gmail" + // unknown mail default: return "unknown" } @@ -72,7 +79,7 @@ type emailToolSet struct { } // Tools implements the ToolSet interface. -func (e *emailToolSet) Tools(ctx context.Context) []tool.Tool { +func (e *emailToolSet) Tools(_ context.Context) []tool.Tool { return e.tools } diff --git a/tool/email/sendmail.go b/tool/email/sendmail.go index 764089592..9b7605831 100644 --- a/tool/email/sendmail.go +++ b/tool/email/sendmail.go @@ -3,8 +3,9 @@ package email import ( "context" "fmt" - "gopkg.in/gomail.v2" "strings" + + "gopkg.in/gomail.v2" "trpc.group/trpc-go/trpc-agent-go/tool" "trpc.group/trpc-go/trpc-agent-go/tool/function" ) @@ -33,7 +34,8 @@ type sendMailResponse struct { } // sendMail performs the send mail operation. -func (e *emailToolSet) sendMail(ctx context.Context, req *sendMailRequest) (rsp *sendMailResponse, err error) { +// go smtp not support context, one send one mail, can't stop +func (e *emailToolSet) sendMail(_ context.Context, req *sendMailRequest) (rsp *sendMailResponse, err error) { rsp = &sendMailResponse{} mailBoxType, err := checkMailBoxType(req.Auth.Name) @@ -63,8 +65,11 @@ func (e *emailToolSet) sendMail(ctx context.Context, req *sendMailRequest) (rsp s, err := dialer.Dial() if err != nil { rsp.Message = fmt.Sprintf("the address or password is incorrect,please check: %v", err) - return rsp, fmt.Errorf("the address or password is incorrect,please check: %w", err) + return rsp, nil } + defer func() { + _ = s.Close() + }() message := gomail.NewMessage() for _, m := range req.MailList { diff --git a/tool/email/sendmail_test.go b/tool/email/sendmail_test.go index e794534aa..322d2a00d 100644 --- a/tool/email/sendmail_test.go +++ b/tool/email/sendmail_test.go @@ -3,6 +3,8 @@ package email import ( "context" "testing" + + "github.com/stretchr/testify/assert" ) func TestMailTool_sendMail(t *testing.T) { @@ -21,7 +23,7 @@ func TestMailTool_sendMail(t *testing.T) { Subject string Content string }{ - // qq to gmail + { Name: "1850396756@qq.com", Password: "", @@ -29,7 +31,7 @@ func TestMailTool_sendMail(t *testing.T) { Subject: "test", Content: "test", }, - // gmail to qq + { Name: "zhuangguang5524621@gmail.com", Password: "", @@ -40,6 +42,10 @@ func TestMailTool_sendMail(t *testing.T) { } for _, tt := range tests { + if tt.Password == "" { + t.Skip("no passwd skip") + } + rsp, err := toolSet.(*emailToolSet).sendMail(context.Background(), &sendMailRequest{ Auth: Auth{ Name: tt.Name, @@ -54,8 +60,116 @@ func TestMailTool_sendMail(t *testing.T) { }, }) if err != nil { - t.Errorf("send mail failed, err: %v", err) + t.Errorf("send mail err: %v", err) } t.Logf("rsp: %+v", rsp) } } + +func Test_checkMailBoxType(t *testing.T) { + type args struct { + email string + } + tests := []struct { + name string + args args + want MailboxType + wantErr bool + }{ + { + name: "QQ domain", + args: args{email: "user@qq.com"}, + want: MAIL_QQ, + wantErr: false, + }, + { + name: "QQ vip domain", + args: args{email: "user@vip.qq.com"}, + want: MAIL_QQ, + wantErr: false, + }, + { + name: "Foxmail domain", + args: args{email: "user@foxmail.com"}, + want: MAIL_QQ, + wantErr: false, + }, + { + name: "Gmail domain", + args: args{email: "user@gmail.com"}, + want: MAIL_GMAIL, + wantErr: false, + }, + { + name: "Googlemail domain", + args: args{email: "user@googlemail.com"}, + want: MAIL_GMAIL, + wantErr: false, + }, + { + name: "163 domain", + args: args{email: "user@163.com"}, + want: MAIL_163, + wantErr: false, + }, + { + name: "Unknown domain", + args: args{email: "user@example.com"}, + want: MAIL_UNKNOWN, + wantErr: false, + }, + { + name: "Empty email", + args: args{email: ""}, + want: MAIL_UNKNOWN, + wantErr: true, + }, + { + name: "No @ symbol", + args: args{email: "invalid-email"}, + want: MAIL_UNKNOWN, + wantErr: true, + }, + { + name: "Multiple @ symbols", + args: args{email: "user@@example.com"}, + want: MAIL_UNKNOWN, + wantErr: true, + }, + { + name: "Uppercase QQ domain", + args: args{email: "USER@QQ.COM"}, + want: MAIL_QQ, + wantErr: false, + }, + { + name: "Mixed case Gmail", + args: args{email: "User@GmAiL.cOm"}, + want: MAIL_GMAIL, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := checkMailBoxType(tt.args.email) + if (err != nil) != tt.wantErr { + t.Errorf("checkMailBoxType() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("checkMailBoxType() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_emailToolSet_sendMailTool(t *testing.T) { + e := &emailToolSet{} + + got := e.sendMailTool() + assert.NotNil(t, got) + + decl := got.Declaration() + assert.Equal(t, "send_email", decl.Name) + assert.Equal(t, "send mail to other", decl.Description) +} From 123b3a50b40a60c2e028d74850bb74c8bbb75343 Mon Sep 17 00:00:00 2001 From: 201430098137 <1850396756@qq.com> Date: Wed, 26 Nov 2025 15:29:14 +0800 Subject: [PATCH 3/9] fix:change email package and complete code --- examples/go.mod | 11 +-- examples/go.sum | 14 ++-- go.mod | 9 ++- go.sum | 5 ++ tool/email/email.go | 11 +-- tool/email/email_test.go | 49 ------------ tool/email/sendmail.go | 167 +++++++++++++++++++++++++++++---------- 7 files changed, 151 insertions(+), 115 deletions(-) diff --git a/examples/go.mod b/examples/go.mod index 894f5a697..460f7cf16 100644 --- a/examples/go.mod +++ b/examples/go.mod @@ -1,6 +1,8 @@ module trpc.group/trpc-go/trpc-agent-go/examples -go 1.23.0 +go 1.24.0 + +toolchain go1.24.10 replace ( trpc.group/trpc-go/trpc-agent-go => ../ @@ -92,6 +94,7 @@ require ( github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.1 // indirect github.com/tidwall/sjson v1.2.5 // indirect + github.com/wneessen/go-mail v0.7.2 // indirect github.com/yosida95/uritemplate/v3 v3.0.2 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 // indirect @@ -108,14 +111,12 @@ require ( golang.org/x/crypto v0.32.0 // indirect golang.org/x/net v0.34.0 // indirect golang.org/x/oauth2 v0.26.0 // indirect - golang.org/x/sync v0.10.0 // indirect + golang.org/x/sync v0.17.0 // indirect golang.org/x/sys v0.35.0 // indirect - golang.org/x/text v0.21.0 // indirect + golang.org/x/text v0.29.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240822170219-fc7c04adadcd // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect google.golang.org/grpc v1.67.0 // indirect google.golang.org/protobuf v1.34.2 // indirect - gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect - gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/examples/go.sum b/examples/go.sum index 28444dfa9..e8f7ff769 100644 --- a/examples/go.sum +++ b/examples/go.sum @@ -158,6 +158,8 @@ 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/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= +github.com/wneessen/go-mail v0.7.2 h1:xxPnhZ6IZLSgxShebmZ6DPKh1b6OJcoHfzy7UjOkzS8= +github.com/wneessen/go-mail v0.7.2/go.mod h1:+TkW6QP3EVkgTEqHtVmnAE/1MRhmzb8Y9/W3pweuS+k= github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4= github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= @@ -201,16 +203,16 @@ golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= golang.org/x/oauth2 v0.26.0 h1:afQXWNNaeC4nvZ0Ed9XvCCzXM6UHJG7iCg0W4fPqSBE= golang.org/x/oauth2 v0.26.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= -golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= +golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= -golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= +golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= google.golang.org/genproto/googleapis/api v0.0.0-20240822170219-fc7c04adadcd h1:BBOTEWLuuEGQy9n1y9MhVJ9Qt0BDu21X8qZs71/uPZo= @@ -221,13 +223,9 @@ google.golang.org/grpc v1.67.0 h1:IdH9y6PF5MPSdAntIcpjQ+tXO41pcQsfZV2RxtQgVcw= google.golang.org/grpc v1.67.0/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= -gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk= -gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE= -gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/go.mod b/go.mod index b7193815d..22f133391 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,8 @@ module trpc.group/trpc-go/trpc-agent-go -go 1.21 +go 1.24.0 + +toolchain go1.24.10 require ( github.com/bmatcuk/doublestar/v4 v4.9.1 @@ -30,8 +32,8 @@ require ( go.opentelemetry.io/otel/trace v1.29.0 go.opentelemetry.io/proto/otlp v1.3.1 go.uber.org/zap v1.27.0 - golang.org/x/sync v0.10.0 - golang.org/x/text v0.21.0 + golang.org/x/sync v0.17.0 + golang.org/x/text v0.29.0 google.golang.org/grpc v1.65.0 google.golang.org/protobuf v1.34.2 gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df @@ -74,6 +76,7 @@ require ( github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.1 // indirect github.com/tidwall/sjson v1.2.5 // indirect + github.com/wneessen/go-mail v0.7.2 // indirect github.com/yosida95/uritemplate/v3 v3.0.2 // indirect go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.42.0 // indirect go.uber.org/multierr v1.10.0 // indirect diff --git a/go.sum b/go.sum index 98a53aa68..027167aaf 100644 --- a/go.sum +++ b/go.sum @@ -124,6 +124,8 @@ 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/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= +github.com/wneessen/go-mail v0.7.2 h1:xxPnhZ6IZLSgxShebmZ6DPKh1b6OJcoHfzy7UjOkzS8= +github.com/wneessen/go-mail v0.7.2/go.mod h1:+TkW6QP3EVkgTEqHtVmnAE/1MRhmzb8Y9/W3pweuS+k= github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4= github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4= github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE= @@ -168,10 +170,13 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= +golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= google.golang.org/genproto/googleapis/api v0.0.0-20240822170219-fc7c04adadcd h1:BBOTEWLuuEGQy9n1y9MhVJ9Qt0BDu21X8qZs71/uPZo= google.golang.org/genproto/googleapis/api v0.0.0-20240822170219-fc7c04adadcd/go.mod h1:fO8wJzT2zbQbAjbIoos1285VfEIYKDDY+Dt+WpTkh6g= google.golang.org/genproto/googleapis/rpc v0.0.0-20240822170219-fc7c04adadcd h1:6TEm2ZxXoQmFWFlt1vNxvVOa1Q0dXFQD1m/rYjXmS0E= diff --git a/tool/email/email.go b/tool/email/email.go index c91297cf8..cd9b43086 100644 --- a/tool/email/email.go +++ b/tool/email/email.go @@ -64,18 +64,10 @@ func WithSendEmailEnabled(enabled bool) Option { } } -// WithName sets the name of the email tool set. -func WithName(name string) Option { - return func(f *emailToolSet) { - f.name = name - } -} - // emailToolSet implements the ToolSet interface for file operations. type emailToolSet struct { sendEmailEnabled bool tools []tool.Tool - name string } // Tools implements the ToolSet interface. @@ -85,7 +77,7 @@ func (e *emailToolSet) Tools(_ context.Context) []tool.Tool { // Name implements the ToolSet interface. func (e *emailToolSet) Name() string { - return e.name + return "email" } // Close implements the ToolSet interface. @@ -99,7 +91,6 @@ func NewToolSet(opts ...Option) (tool.ToolSet, error) { emailToolSet := &emailToolSet{ sendEmailEnabled: true, tools: nil, - name: defaultName, } // Apply user-provided options. diff --git a/tool/email/email_test.go b/tool/email/email_test.go index 306748157..dc97f7ae4 100644 --- a/tool/email/email_test.go +++ b/tool/email/email_test.go @@ -22,58 +22,9 @@ func TestWithSendEmailEnabled(t *testing.T) { }) } -func TestWithName(t *testing.T) { - type args struct { - name string - } - tests := []struct { - name string - args args - check func(*emailToolSet) - }{ - { - name: "normal name", - args: args{name: "aFrqvjeXQ"}, - check: func(e *emailToolSet) { - assert.Equal(t, "aFrqvjeXQ", e.name) - }, - }, - { - name: "empty name", - args: args{name: ""}, - check: func(e *emailToolSet) { - assert.Equal(t, "", e.name) - }, - }, - { - name: "unicode name", - args: args{name: "测试邮箱"}, - check: func(e *emailToolSet) { - assert.Equal(t, "测试邮箱", e.name) - }, - }, - { - name: "very long name", - args: args{name: string(make([]byte, 1024))}, - check: func(e *emailToolSet) { - assert.Len(t, e.name, 1024) - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - e := &emailToolSet{} - opt := WithName(tt.args.name) - opt(e) - tt.check(e) - }) - } -} - func TestNewToolSet_Default(t *testing.T) { set, err := NewToolSet() assert.NoError(t, err) ets := set.(*emailToolSet) assert.Equal(t, true, ets.sendEmailEnabled) - assert.Equal(t, defaultName, ets.name) } diff --git a/tool/email/sendmail.go b/tool/email/sendmail.go index 9b7605831..4e3919622 100644 --- a/tool/email/sendmail.go +++ b/tool/email/sendmail.go @@ -2,21 +2,34 @@ package email import ( "context" + "errors" "fmt" "strings" - "gopkg.in/gomail.v2" + "trpc.group/trpc-go/trpc-agent-go/log" + + gomail "github.com/wneessen/go-mail" "trpc.group/trpc-go/trpc-agent-go/tool" "trpc.group/trpc-go/trpc-agent-go/tool/function" + + "net/mail" +) + +const ( + qqMail = "smtp.qq.com" + qqPort = 465 + gmailMail = "smtp.gmail.com" + gmailPort = 587 ) // sendMailRequest represents the input for the send mail operation. type sendMailRequest struct { - Auth Auth `json:"auth" jsonschema:"description=auth of the mail."` - MailList []*mail `json:"mail_list" jsonschema:"description=The list of mail."` + Auth Auth `json:"auth" jsonschema:"description=auth of the mail."` + MailList []*Mail `json:"mail_list" jsonschema:"description=The list of mail."` + Extra ExtraData `json:"extra" jsonschema:"description=extra data of the mail. optional. default is empty."` } -type mail struct { +type Mail struct { ToEmail string `json:"to_email" jsonschema:"description=send to email."` Subject string `json:"subject" jsonschema:"description=subject of the mail"` Content string `json:"content" jsonschema:"description=content of the mail"` @@ -28,6 +41,11 @@ type Auth struct { Password string `json:"password" jsonschema:"description=password of the mail."` } +type ExtraData struct { + SvrAddr string `json:"svr_addr" jsonschema:"description=server address of the mail. optional. default is empty."` + Port int `json:"port" jsonschema:"description=port of the mail. optional. default is empty."` +} + // sendMailResponse represents the output from the send mail operation. type sendMailResponse struct { Message string `json:"message"` @@ -35,70 +53,123 @@ type sendMailResponse struct { // sendMail performs the send mail operation. // go smtp not support context, one send one mail, can't stop -func (e *emailToolSet) sendMail(_ context.Context, req *sendMailRequest) (rsp *sendMailResponse, err error) { +func (e *emailToolSet) sendMail(ctx context.Context, req *sendMailRequest) (rsp *sendMailResponse, err error) { rsp = &sendMailResponse{} - mailBoxType, err := checkMailBoxType(req.Auth.Name) + addr, port, isSSL, err := e.getEmailAddr(req) if err != nil { - rsp.Message = fmt.Sprintf("checkMailBoxType ERROR: %v", err) - return rsp, nil + rsp.Message = fmt.Sprintf("getSvrAddrAndPort ERROR: %v", err) + return } - var addr string - var port int - switch mailBoxType { - case MAIL_QQ: - //qq email - addr = "smtp.qq.com" - port = 465 - case MAIL_GMAIL: - //gmail email - addr = "smtp.gmail.com" - port = 587 - default: - // not support - rsp.Message = fmt.Sprintf("not support mailbox type:%s", MailboxTypeToString(mailBoxType)) - return rsp, nil + opts := []gomail.Option{ + gomail.WithPort(port), + gomail.WithSMTPAuth(gomail.SMTPAuthAutoDiscover), + gomail.WithUsername(req.Auth.Name), + gomail.WithPassword(req.Auth.Password), + gomail.WithoutNoop(), + gomail.WithDebugLog(), + } + if isSSL { + opts = append(opts, gomail.WithSSL()) + } else { + opts = append(opts, gomail.WithTLSPolicy(gomail.TLSMandatory)) } - dialer := gomail.NewDialer(addr, port, req.Auth.Name, req.Auth.Password) - s, err := dialer.Dial() + client, err := gomail.NewClient( + addr, + opts..., + ) if err != nil { rsp.Message = fmt.Sprintf("the address or password is incorrect,please check: %v", err) return rsp, nil } + defer func() { - _ = s.Close() + _ = client.Close() }() - message := gomail.NewMessage() + messages := make([]*gomail.Msg, 0, len(req.MailList)) for _, m := range req.MailList { - message.SetHeader("From", req.Auth.Name) - message.SetHeader("To", m.ToEmail) - message.SetHeader("Subject", m.Subject) - message.SetBody("text/html", m.Content) - if err := gomail.Send(s, message); err != nil { - rsp.Message = fmt.Sprintf("send ERROR: %v", err) - return rsp, fmt.Errorf("send ERROR: %w", err) + message := gomail.NewMsg() + err = message.From(req.Auth.Name) + if err != nil { + rsp.Message = fmt.Sprintf("fromm email err: %v", err) + return rsp, nil + } + err = message.To(m.ToEmail) + if err != nil { + rsp.Message = fmt.Sprintf("to email err: %v", err) + return rsp, nil + } + message.Subject(m.Subject) + message.SetBodyString(gomail.TypeTextHTML, m.Content) + messages = append(messages, message) + } + + // batch send email, not stop if one failed, return err which join all send error message + if err := client.DialAndSendWithContext(ctx, messages...); err != nil { + //qq mail special error handle + //https://github.com/wneessen/go-mail/issues/463 + if addr == qqMail && qqHandleError(err) == nil { + + } else { + rsp.Message = fmt.Sprintf("send ERROR: %v, host:%s, port:%d", err, addr, port) + return rsp, nil } - message.Reset() } return } +// getEmailAddr gets the email address and port. +func (e *emailToolSet) getEmailAddr(req *sendMailRequest) (addr string, port int, isSSL bool, err error) { + mailBoxType, err := checkMailBoxType(req.Auth.Name) + if err != nil { + err = fmt.Errorf("checkMailBoxType ERROR: %v", err) + return + } + + if req.Extra.SvrAddr != "" { + addr = req.Extra.SvrAddr + port = req.Extra.Port + } else { + switch mailBoxType { + case MAIL_QQ: + //qq email + addr = qqMail + port = qqPort + isSSL = true + case MAIL_GMAIL: + //gmail email + addr = gmailMail + port = gmailPort + isSSL = false + default: + // not support + err = fmt.Errorf("not support mailbox type:%s", MailboxTypeToString(mailBoxType)) + return + } + } + return +} + // checkMailBoxType checks the mailbox type. func checkMailBoxType(email string) (MailboxType, error) { + + addr, err := mail.ParseAddress(email) + if err != nil { + return MAIL_UNKNOWN, fmt.Errorf("parse email address ERROR: %w", err) + } // to lower - email = strings.ToLower(email) + emailAddr := strings.ToLower(addr.Address) // split by name and domain - parts := strings.Split(email, "@") - if len(parts) != 2 { + lastAt := strings.LastIndex(emailAddr, "@") + if lastAt < 0 { return MAIL_UNKNOWN, fmt.Errorf("invalid email address") } - - domain := parts[1] + domain := emailAddr[lastAt:] switch domain { case "qq.com", "vip.qq.com", "foxmail.com": @@ -120,3 +191,19 @@ func (e *emailToolSet) sendMailTool() tool.CallableTool { function.WithDescription("send mail to other"), ) } + +func qqHandleError(err error) error { + log.Infof("err: %v %T", err, err) + + var sendErr *gomail.SendError + // Check if this is an SMTP RESET error after successful delivery + if errors.As(err, &sendErr) { + if sendErr.Reason == gomail.ErrSMTPReset { + // https://github.com/wneessen/go-mail/issues/463 + log.Warnf("⚠️ Mail delivered successfully but SMTP RESET failed: %s", err) + return nil // Don't treat this as a delivery failure since mail was sent + } + return err + } + return err +} From 16adf500ad3d1b2f38e073163e0f4c8c0821c56a Mon Sep 17 00:00:00 2001 From: 201430098137 <1850396756@qq.com> Date: Wed, 26 Nov 2025 18:09:52 +0800 Subject: [PATCH 4/9] go.mod: go mod tidy --- agent/dify/go.mod | 2 +- codeexecutor/jupyter/go.mod | 2 +- examples/email/go.mod | 49 ++++++++++++++ examples/email/go.sum | 95 +++++++++++++++++++++++++++ examples/go.mod | 29 +------- examples/go.sum | 76 +-------------------- go.mod | 11 +--- go.sum | 9 --- knowledge/embedder/huggingface/go.mod | 2 +- knowledge/vectorstore/pgvector/go.mod | 2 +- knowledge/vectorstore/tcvector/go.mod | 2 +- memory/postgres/go.mod | 2 +- memory/redis/go.mod | 2 +- session/postgres/go.mod | 2 +- session/redis/go.mod | 2 +- tool/email/go.mod | 21 ++++++ tool/email/go.sum | 22 +++++++ tool/email/sendmail.go | 6 +- tool/email/sendmail_test.go | 3 +- 19 files changed, 208 insertions(+), 131 deletions(-) create mode 100644 examples/email/go.mod create mode 100644 examples/email/go.sum create mode 100644 tool/email/go.mod create mode 100644 tool/email/go.sum diff --git a/agent/dify/go.mod b/agent/dify/go.mod index b8eb49fca..ebf2a0b9c 100644 --- a/agent/dify/go.mod +++ b/agent/dify/go.mod @@ -1,6 +1,6 @@ module trpc.group/trpc-go/trpc-agent-go/agent/dify -go 1.21 +go 1.21.0 replace trpc.group/trpc-go/trpc-agent-go => ../.. diff --git a/codeexecutor/jupyter/go.mod b/codeexecutor/jupyter/go.mod index 93842e4b8..6e81b9985 100644 --- a/codeexecutor/jupyter/go.mod +++ b/codeexecutor/jupyter/go.mod @@ -1,6 +1,6 @@ module trpc.group/trpc-go/trpc-agent-go/codeexecutor/jupyter -go 1.21 +go 1.21.0 replace trpc.group/trpc-go/trpc-agent-go => ../.. diff --git a/examples/email/go.mod b/examples/email/go.mod new file mode 100644 index 000000000..69bfe711f --- /dev/null +++ b/examples/email/go.mod @@ -0,0 +1,49 @@ +module trpc.group/trpc-go/trpc-agent-go/examples/email + +go 1.24.0 + +replace ( + trpc.group/trpc-go/trpc-agent-go => ../.. + trpc.group/trpc-go/trpc-agent-go/tool/email => ../../tool/email +) + +require ( + trpc.group/trpc-go/trpc-agent-go v0.5.0 + trpc.group/trpc-go/trpc-agent-go/tool/email v0.0.0-00010101000000-000000000000 +) + +require ( + github.com/bmatcuk/doublestar/v4 v4.9.1 // indirect + github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect + github.com/openai/openai-go v1.12.0 // indirect + github.com/panjf2000/ants/v2 v2.10.0 // indirect + github.com/spaolacci/murmur3 v1.1.0 // indirect + github.com/tidwall/gjson v1.14.4 // indirect + github.com/tidwall/match v1.1.1 // indirect + github.com/tidwall/pretty v1.2.1 // indirect + github.com/tidwall/sjson v1.2.5 // indirect + github.com/wneessen/go-mail v0.7.2 // indirect + go.opentelemetry.io/otel v1.29.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.29.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.29.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.29.0 // indirect + go.opentelemetry.io/otel/metric v1.29.0 // indirect + go.opentelemetry.io/otel/sdk v1.29.0 // indirect + go.opentelemetry.io/otel/trace v1.29.0 // indirect + go.opentelemetry.io/proto/otlp v1.3.1 // indirect + go.uber.org/multierr v1.10.0 // indirect + go.uber.org/zap v1.27.0 // indirect + golang.org/x/net v0.34.0 // indirect + golang.org/x/sync v0.17.0 // indirect + golang.org/x/sys v0.30.0 // indirect + golang.org/x/text v0.29.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240822170219-fc7c04adadcd // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240822170219-fc7c04adadcd // indirect + google.golang.org/grpc v1.65.0 // indirect + google.golang.org/protobuf v1.34.2 // indirect + trpc.group/trpc-go/trpc-a2a-go v0.2.5-0.20251023030722-7f02b57fd14a // indirect +) diff --git a/examples/email/go.sum b/examples/email/go.sum new file mode 100644 index 000000000..2072da155 --- /dev/null +++ b/examples/email/go.sum @@ -0,0 +1,95 @@ +github.com/bmatcuk/doublestar/v4 v4.9.1 h1:X8jg9rRZmJd4yRy7ZeNDRnM+T3ZfHv15JiBJ/avrEXE= +github.com/bmatcuk/doublestar/v4 v4.9.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I= +github.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs= +github.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +github.com/openai/openai-go v1.12.0 h1:NBQCnXzqOTv5wsgNC36PrFEiskGfO5wccfCWDo9S1U0= +github.com/openai/openai-go v1.12.0/go.mod h1:g461MYGXEXBVdV5SaR/5tNzNbSfwTBBefwc+LlDCK0Y= +github.com/panjf2000/ants/v2 v2.10.0 h1:zhRg1pQUtkyRiOFo2Sbqwjp0GfBNo9cUY2/Grpx1p+8= +github.com/panjf2000/ants/v2 v2.10.0/go.mod h1:7ZxyxsqE4vvW0M7LSD8aI3cKwgFhBHbxnlN8mDqHa1I= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= +github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM= +github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/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/wneessen/go-mail v0.7.2 h1:xxPnhZ6IZLSgxShebmZ6DPKh1b6OJcoHfzy7UjOkzS8= +github.com/wneessen/go-mail v0.7.2/go.mod h1:+TkW6QP3EVkgTEqHtVmnAE/1MRhmzb8Y9/W3pweuS+k= +go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw= +go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.29.0 h1:dIIDULZJpgdiHz5tXrTgKIMLkus6jEFa7x5SOKcyR7E= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.29.0/go.mod h1:jlRVBe7+Z1wyxFSUs48L6OBQZ5JwH2Hg/Vbl+t9rAgI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.29.0 h1:nSiV3s7wiCam610XcLbYOmMfJxB9gO4uK3Xgv5gmTgg= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.29.0/go.mod h1:hKn/e/Nmd19/x1gvIHwtOwVWM+VhuITSWip3JUDghj0= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.29.0 h1:JAv0Jwtl01UFiyWZEMiJZBiTlv5A50zNs8lsthXqIio= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.29.0/go.mod h1:QNKLmUEAq2QUbPQUfvw4fmv0bgbK7UlOSFCnXyfvSNc= +go.opentelemetry.io/otel/metric v1.29.0 h1:vPf/HFWTNkPu1aYeIsc98l4ktOQaL6LeSoeV2g+8YLc= +go.opentelemetry.io/otel/metric v1.29.0/go.mod h1:auu/QWieFVWx+DmQOUMgj0F8LHWdgalxXqvp7BII/W8= +go.opentelemetry.io/otel/sdk v1.29.0 h1:vkqKjk7gwhS8VaWb0POZKmIEDimRCMsopNYnriHyryo= +go.opentelemetry.io/otel/sdk v1.29.0/go.mod h1:pM8Dx5WKnvxLCb+8lG1PRNIDxu9g9b9g59Qr7hfAAok= +go.opentelemetry.io/otel/sdk/metric v1.29.0 h1:K2CfmJohnRgvZ9UAj2/FhIf/okdWcNdBwe1m8xFXiSY= +go.opentelemetry.io/otel/sdk/metric v1.29.0/go.mod h1:6zZLdCl2fkauYoZIOn/soQIDSWFmNSRcICarHfuhNJQ= +go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4= +go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ= +go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= +go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= +go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= +golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= +golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= +golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= +golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= +google.golang.org/genproto/googleapis/api v0.0.0-20240822170219-fc7c04adadcd h1:BBOTEWLuuEGQy9n1y9MhVJ9Qt0BDu21X8qZs71/uPZo= +google.golang.org/genproto/googleapis/api v0.0.0-20240822170219-fc7c04adadcd/go.mod h1:fO8wJzT2zbQbAjbIoos1285VfEIYKDDY+Dt+WpTkh6g= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240822170219-fc7c04adadcd h1:6TEm2ZxXoQmFWFlt1vNxvVOa1Q0dXFQD1m/rYjXmS0E= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240822170219-fc7c04adadcd/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= +google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= +google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +trpc.group/trpc-go/trpc-a2a-go v0.2.5-0.20251023030722-7f02b57fd14a h1:dOon6HF2sPRFnhCLEiAeKPc21JHL2eX7UBWjIR8PLaY= +trpc.group/trpc-go/trpc-a2a-go v0.2.5-0.20251023030722-7f02b57fd14a/go.mod h1:Gtytau9Uoc3oPo/dpHvKit+tQn9Qlk5XFG1RiZTGqfk= diff --git a/examples/go.mod b/examples/go.mod index 460f7cf16..f8cdf604f 100644 --- a/examples/go.mod +++ b/examples/go.mod @@ -33,25 +33,13 @@ require ( go.uber.org/zap v1.27.0 trpc.group/trpc-go/trpc-a2a-go v0.2.5-0.20251023030722-7f02b57fd14a trpc.group/trpc-go/trpc-agent-go v0.4.0 - trpc.group/trpc-go/trpc-agent-go/codeexecutor/container v0.4.0 - trpc.group/trpc-go/trpc-agent-go/codeexecutor/jupyter v0.0.0-00010101000000-000000000000 trpc.group/trpc-go/trpc-mcp-go v0.0.10 ) require ( - github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect - github.com/Microsoft/go-winio v0.4.14 // indirect github.com/bmatcuk/doublestar/v4 v4.9.1 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect - github.com/containerd/errdefs v1.0.0 // indirect - github.com/containerd/errdefs/pkg v0.3.0 // indirect - github.com/containerd/log v0.1.0 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect - github.com/distribution/reference v0.6.0 // indirect - github.com/docker/docker v28.4.0+incompatible // indirect - github.com/docker/go-connections v0.5.0 // indirect - github.com/docker/go-units v0.5.0 // indirect - github.com/felixge/httpsnoop v1.0.4 // indirect github.com/getkin/kin-openapi v0.124.0 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect @@ -59,14 +47,11 @@ require ( github.com/go-openapi/swag v0.22.8 // indirect github.com/goccy/go-json v0.10.3 // indirect github.com/golang-jwt/jwt/v5 v5.2.3 // indirect - github.com/gorilla/mux v1.8.1 // indirect - github.com/gorilla/websocket v1.5.3 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/invopop/yaml v0.2.0 // indirect github.com/josharian/intern v1.0.0 // indirect - github.com/klauspost/compress v1.18.0 // indirect github.com/lestrrat-go/blackmagic v1.0.2 // indirect github.com/lestrrat-go/httpcc v1.0.1 // indirect github.com/lestrrat-go/httprc v1.0.6 // indirect @@ -74,21 +59,10 @@ require ( github.com/lestrrat-go/jwx/v2 v2.1.4 // indirect github.com/lestrrat-go/option v1.0.1 // indirect github.com/mailru/easyjson v0.9.0 // indirect - github.com/moby/docker-image-spec v1.3.1 // indirect - github.com/moby/go-archive v0.1.0 // indirect - github.com/moby/patternmatcher v0.6.0 // indirect - github.com/moby/sys/sequential v0.6.0 // indirect - github.com/moby/sys/user v0.4.0 // indirect - github.com/moby/sys/userns v0.1.0 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect - github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/opencontainers/image-spec v1.0.2 // indirect github.com/panjf2000/ants/v2 v2.10.0 // indirect github.com/perimeterx/marshmallow v1.1.5 // indirect - github.com/pkg/errors v0.9.1 // indirect - github.com/rs/cors v1.11.1 // indirect github.com/segmentio/asm v1.2.0 // indirect - github.com/sirupsen/logrus v1.9.3 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/tidwall/gjson v1.14.4 // indirect github.com/tidwall/match v1.1.1 // indirect @@ -97,7 +71,6 @@ require ( github.com/wneessen/go-mail v0.7.2 // indirect github.com/yosida95/uritemplate/v3 v3.0.2 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.42.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.42.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.42.0 // indirect @@ -108,7 +81,7 @@ require ( go.opentelemetry.io/otel/sdk/metric v1.38.0 // indirect go.opentelemetry.io/proto/otlp v1.3.1 // indirect go.uber.org/multierr v1.10.0 // indirect - golang.org/x/crypto v0.32.0 // indirect + golang.org/x/crypto v0.33.0 // indirect golang.org/x/net v0.34.0 // indirect golang.org/x/oauth2 v0.26.0 // indirect golang.org/x/sync v0.17.0 // indirect diff --git a/examples/go.sum b/examples/go.sum index e8f7ff769..515cc31f9 100644 --- a/examples/go.sum +++ b/examples/go.sum @@ -1,34 +1,12 @@ -github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk= -github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= -github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= -github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= -github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU= -github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= github.com/bmatcuk/doublestar/v4 v4.9.1 h1:X8jg9rRZmJd4yRy7ZeNDRnM+T3ZfHv15JiBJ/avrEXE= github.com/bmatcuk/doublestar/v4 v4.9.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= -github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= -github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= -github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE= -github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= -github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= -github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40= -github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= -github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/docker v28.4.0+incompatible h1:KVC7bz5zJY/4AZe/78BIvCnPsLaC9T/zh72xnlrTTOk= -github.com/docker/docker v28.4.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= -github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= -github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= -github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= -github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/getkin/kin-openapi v0.124.0 h1:VSFNMB9C9rTKBnQ/fpyDU8ytMTr4dWI9QovSKj9kz/M= github.com/getkin/kin-openapi v0.124.0/go.mod h1:wb1aSZA/iWmorQP9KTAS/phLj/t17B5jT7+fS8ed9NM= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= @@ -50,10 +28,6 @@ github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= -github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= -github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= -github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys= github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -65,9 +39,6 @@ github.com/invopop/yaml v0.2.0 h1:7zky/qH+O0DwAyoobXUqvVBwgBFRxKoQ/3FjcVpjTMY= github.com/invopop/yaml v0.2.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= -github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -88,59 +59,26 @@ github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4 github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= github.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs= github.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= -github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= -github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= -github.com/moby/go-archive v0.1.0 h1:Kk/5rdW/g+H8NHdJW2gsXyZ7UnzvJNOy6VKJqueWdcQ= -github.com/moby/go-archive v0.1.0/go.mod h1:G9B+YoujNohJmrIYFBpSd54GTUB4lt9S+xVQvsJyFuo= -github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= -github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= -github.com/moby/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw= -github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs= -github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= -github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko= -github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs= -github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs= -github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= -github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= -github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ= -github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= -github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= -github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/openai/openai-go v1.12.0 h1:NBQCnXzqOTv5wsgNC36PrFEiskGfO5wccfCWDo9S1U0= github.com/openai/openai-go v1.12.0/go.mod h1:g461MYGXEXBVdV5SaR/5tNzNbSfwTBBefwc+LlDCK0Y= -github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= -github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= -github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/panjf2000/ants/v2 v2.10.0 h1:zhRg1pQUtkyRiOFo2Sbqwjp0GfBNo9cUY2/Grpx1p+8= github.com/panjf2000/ants/v2 v2.10.0/go.mod h1:7ZxyxsqE4vvW0M7LSD8aI3cKwgFhBHbxnlN8mDqHa1I= github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s= github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= -github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA= -github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys= github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= -github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= -github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= -github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= @@ -164,8 +102,6 @@ github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zI github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg= go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.42.0 h1:ZtfnDL+tUrs1F0Pzfwbg2d59Gru9NCH3bgSHBM6LDwU= @@ -196,8 +132,8 @@ go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= -golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= -golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= +golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus= +golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= golang.org/x/oauth2 v0.26.0 h1:afQXWNNaeC4nvZ0Ed9XvCCzXM6UHJG7iCg0W4fPqSBE= @@ -205,16 +141,10 @@ golang.org/x/oauth2 v0.26.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbht golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= -golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= -golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= google.golang.org/genproto/googleapis/api v0.0.0-20240822170219-fc7c04adadcd h1:BBOTEWLuuEGQy9n1y9MhVJ9Qt0BDu21X8qZs71/uPZo= google.golang.org/genproto/googleapis/api v0.0.0-20240822170219-fc7c04adadcd/go.mod h1:fO8wJzT2zbQbAjbIoos1285VfEIYKDDY+Dt+WpTkh6g= google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ= @@ -230,8 +160,6 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= -gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA= trpc.group/trpc-go/trpc-a2a-go v0.2.5-0.20251023030722-7f02b57fd14a h1:dOon6HF2sPRFnhCLEiAeKPc21JHL2eX7UBWjIR8PLaY= trpc.group/trpc-go/trpc-a2a-go v0.2.5-0.20251023030722-7f02b57fd14a/go.mod h1:Gtytau9Uoc3oPo/dpHvKit+tQn9Qlk5XFG1RiZTGqfk= trpc.group/trpc-go/trpc-mcp-go v0.0.10 h1:kKPfevmikMojfOgtUBf5SJQ/v6aDugckodgyH1uDu2Q= diff --git a/go.mod b/go.mod index ac99aaa1b..687eefddb 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,6 @@ module trpc.group/trpc-go/trpc-agent-go -go 1.24.0 - -toolchain go1.24.10 +go 1.21.0 require ( github.com/bmatcuk/doublestar/v4 v4.9.1 @@ -30,11 +28,10 @@ require ( go.opentelemetry.io/otel/trace v1.29.0 go.opentelemetry.io/proto/otlp v1.3.1 go.uber.org/zap v1.27.0 - golang.org/x/sync v0.17.0 - golang.org/x/text v0.29.0 + golang.org/x/sync v0.10.0 + golang.org/x/text v0.21.0 google.golang.org/grpc v1.65.0 google.golang.org/protobuf v1.34.2 - gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df trpc.group/trpc-go/trpc-a2a-go v0.2.5-0.20251023030722-7f02b57fd14a trpc.group/trpc-go/trpc-mcp-go v0.0.10 ) @@ -74,7 +71,6 @@ require ( github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.1 // indirect github.com/tidwall/sjson v1.2.5 // indirect - github.com/wneessen/go-mail v0.7.2 // indirect github.com/yosida95/uritemplate/v3 v3.0.2 // indirect go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.42.0 // indirect go.uber.org/multierr v1.10.0 // indirect @@ -84,6 +80,5 @@ require ( golang.org/x/sys v0.30.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240822170219-fc7c04adadcd // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240822170219-fc7c04adadcd // indirect - gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 3d98c97a1..79dce7615 100644 --- a/go.sum +++ b/go.sum @@ -120,8 +120,6 @@ 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/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= -github.com/wneessen/go-mail v0.7.2 h1:xxPnhZ6IZLSgxShebmZ6DPKh1b6OJcoHfzy7UjOkzS8= -github.com/wneessen/go-mail v0.7.2/go.mod h1:+TkW6QP3EVkgTEqHtVmnAE/1MRhmzb8Y9/W3pweuS+k= github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4= github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4= github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE= @@ -166,13 +164,10 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= -golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= -golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= google.golang.org/genproto/googleapis/api v0.0.0-20240822170219-fc7c04adadcd h1:BBOTEWLuuEGQy9n1y9MhVJ9Qt0BDu21X8qZs71/uPZo= google.golang.org/genproto/googleapis/api v0.0.0-20240822170219-fc7c04adadcd/go.mod h1:fO8wJzT2zbQbAjbIoos1285VfEIYKDDY+Dt+WpTkh6g= google.golang.org/genproto/googleapis/rpc v0.0.0-20240822170219-fc7c04adadcd h1:6TEm2ZxXoQmFWFlt1vNxvVOa1Q0dXFQD1m/rYjXmS0E= @@ -181,13 +176,9 @@ google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= -gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk= -gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE= -gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/knowledge/embedder/huggingface/go.mod b/knowledge/embedder/huggingface/go.mod index 62bae699c..c8b347e7e 100644 --- a/knowledge/embedder/huggingface/go.mod +++ b/knowledge/embedder/huggingface/go.mod @@ -1,6 +1,6 @@ module trpc.group/trpc-go/trpc-agent-go/knowledge/embedder/huggingface -go 1.21 +go 1.21.0 replace trpc.group/trpc-go/trpc-agent-go => ../../.. diff --git a/knowledge/vectorstore/pgvector/go.mod b/knowledge/vectorstore/pgvector/go.mod index 737b8ad4c..d4170c2b8 100644 --- a/knowledge/vectorstore/pgvector/go.mod +++ b/knowledge/vectorstore/pgvector/go.mod @@ -1,6 +1,6 @@ module trpc.group/trpc-go/trpc-agent-go/knowledge/vectorstore/pgvector -go 1.21 +go 1.21.0 replace ( trpc.group/trpc-go/trpc-agent-go => ../../../ diff --git a/knowledge/vectorstore/tcvector/go.mod b/knowledge/vectorstore/tcvector/go.mod index 65b77c35c..1720db3ef 100644 --- a/knowledge/vectorstore/tcvector/go.mod +++ b/knowledge/vectorstore/tcvector/go.mod @@ -1,6 +1,6 @@ module trpc.group/trpc-go/trpc-agent-go/knowledge/vectorstore/tcvector -go 1.21 +go 1.21.0 replace ( trpc.group/trpc-go/trpc-agent-go => ../../../ diff --git a/memory/postgres/go.mod b/memory/postgres/go.mod index faf7d71a4..cb145a681 100644 --- a/memory/postgres/go.mod +++ b/memory/postgres/go.mod @@ -1,6 +1,6 @@ module trpc.group/trpc-go/trpc-agent-go/memory/postgres -go 1.21 +go 1.21.0 require ( github.com/DATA-DOG/go-sqlmock v1.5.2 diff --git a/memory/redis/go.mod b/memory/redis/go.mod index bce2ddc5e..bd722a3db 100644 --- a/memory/redis/go.mod +++ b/memory/redis/go.mod @@ -1,6 +1,6 @@ module trpc.group/trpc-go/trpc-agent-go/memory/redis -go 1.21 +go 1.21.0 replace ( trpc.group/trpc-go/trpc-agent-go => ../../ diff --git a/session/postgres/go.mod b/session/postgres/go.mod index eff04d131..2afce0c9c 100644 --- a/session/postgres/go.mod +++ b/session/postgres/go.mod @@ -1,6 +1,6 @@ module trpc.group/trpc-go/trpc-agent-go/session/postgres -go 1.21 +go 1.21.0 replace ( trpc.group/trpc-go/trpc-agent-go => ../../ diff --git a/session/redis/go.mod b/session/redis/go.mod index b3ad0dcc5..addeab2e2 100644 --- a/session/redis/go.mod +++ b/session/redis/go.mod @@ -1,6 +1,6 @@ module trpc.group/trpc-go/trpc-agent-go/session/redis -go 1.21 +go 1.21.0 replace ( trpc.group/trpc-go/trpc-agent-go => ../../ diff --git a/tool/email/go.mod b/tool/email/go.mod new file mode 100644 index 000000000..a043a0def --- /dev/null +++ b/tool/email/go.mod @@ -0,0 +1,21 @@ +module trpc.group/trpc-go/trpc-agent-go/tool/email + +go 1.24.0 + +replace trpc.group/trpc-go/trpc-agent-go => ../.. + +require ( + github.com/stretchr/testify v1.11.1 + github.com/wneessen/go-mail v0.7.2 + trpc.group/trpc-go/trpc-agent-go v0.5.0 +) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + go.uber.org/multierr v1.10.0 // indirect + go.uber.org/zap v1.27.0 // indirect + golang.org/x/text v0.29.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + trpc.group/trpc-go/trpc-a2a-go v0.2.5-0.20251023030722-7f02b57fd14a // indirect +) diff --git a/tool/email/go.sum b/tool/email/go.sum new file mode 100644 index 000000000..cdf7d1e06 --- /dev/null +++ b/tool/email/go.sum @@ -0,0 +1,22 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/wneessen/go-mail v0.7.2 h1:xxPnhZ6IZLSgxShebmZ6DPKh1b6OJcoHfzy7UjOkzS8= +github.com/wneessen/go-mail v0.7.2/go.mod h1:+TkW6QP3EVkgTEqHtVmnAE/1MRhmzb8Y9/W3pweuS+k= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= +go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= +golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +trpc.group/trpc-go/trpc-a2a-go v0.2.5-0.20251023030722-7f02b57fd14a h1:dOon6HF2sPRFnhCLEiAeKPc21JHL2eX7UBWjIR8PLaY= +trpc.group/trpc-go/trpc-a2a-go v0.2.5-0.20251023030722-7f02b57fd14a/go.mod h1:Gtytau9Uoc3oPo/dpHvKit+tQn9Qlk5XFG1RiZTGqfk= diff --git a/tool/email/sendmail.go b/tool/email/sendmail.go index 4e3919622..82c0ed758 100644 --- a/tool/email/sendmail.go +++ b/tool/email/sendmail.go @@ -94,7 +94,7 @@ func (e *emailToolSet) sendMail(ctx context.Context, req *sendMailRequest) (rsp message := gomail.NewMsg() err = message.From(req.Auth.Name) if err != nil { - rsp.Message = fmt.Sprintf("fromm email err: %v", err) + rsp.Message = fmt.Sprintf("from email err: %v", err) return rsp, nil } err = message.To(m.ToEmail) @@ -161,8 +161,10 @@ func checkMailBoxType(email string) (MailboxType, error) { if err != nil { return MAIL_UNKNOWN, fmt.Errorf("parse email address ERROR: %w", err) } + log.Infof("addr: %v", addr) // to lower emailAddr := strings.ToLower(addr.Address) + log.Infof("emailAddr: %v", emailAddr) // split by name and domain lastAt := strings.LastIndex(emailAddr, "@") @@ -170,6 +172,8 @@ func checkMailBoxType(email string) (MailboxType, error) { return MAIL_UNKNOWN, fmt.Errorf("invalid email address") } domain := emailAddr[lastAt:] + domain = strings.TrimPrefix(domain, "@") + log.Infof("domain: %v", domain) switch domain { case "qq.com", "vip.qq.com", "foxmail.com": diff --git a/tool/email/sendmail_test.go b/tool/email/sendmail_test.go index 322d2a00d..a33f455d4 100644 --- a/tool/email/sendmail_test.go +++ b/tool/email/sendmail_test.go @@ -10,7 +10,6 @@ import ( func TestMailTool_sendMail(t *testing.T) { toolSet, err := NewToolSet( WithSendEmailEnabled(true), - WithName("email"), ) if err != nil { t.Errorf("NewToolSet failed, err: %v", err) @@ -51,7 +50,7 @@ func TestMailTool_sendMail(t *testing.T) { Name: tt.Name, Password: tt.Password, }, - MailList: []*mail{ + MailList: []*Mail{ { ToEmail: tt.ToEmail, Subject: tt.Subject, From 1a4c778e82c3ab0ca2b154d7392e74d6881f4e54 Mon Sep 17 00:00:00 2001 From: 201430098137 <1850396756@qq.com> Date: Thu, 27 Nov 2025 10:21:49 +0800 Subject: [PATCH 5/9] tests: email tool add more test case --- tool/email/email_test.go | 110 ++++++++++++++++++++++++++++++++ tool/email/sendmail_test.go | 124 ++++++++++++++++++++++++++++++++++++ 2 files changed, 234 insertions(+) diff --git a/tool/email/email_test.go b/tool/email/email_test.go index dc97f7ae4..64f7d06b8 100644 --- a/tool/email/email_test.go +++ b/tool/email/email_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "trpc.group/trpc-go/trpc-agent-go/tool" ) func TestWithSendEmailEnabled(t *testing.T) { @@ -28,3 +29,112 @@ func TestNewToolSet_Default(t *testing.T) { ets := set.(*emailToolSet) assert.Equal(t, true, ets.sendEmailEnabled) } + +// 由CodeBuddy(内网版)生成于2025.11.27 09:52:42 +func Test_emailToolSet_Close(t *testing.T) { + type fields struct { + sendEmailEnabled bool + tools []tool.Tool + } + tests := []struct { + name string + fields fields + wantErr bool + }{ + { + name: "zero value", + fields: fields{}, + wantErr: false, + }, + { + name: "enabled with tools", + fields: fields{ + sendEmailEnabled: true, + tools: []tool.Tool{}, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + e := &emailToolSet{ + sendEmailEnabled: tt.fields.sendEmailEnabled, + tools: tt.fields.tools, + } + err := e.Close() + if tt.wantErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + }) + } +} + +// 由CodeBuddy(内网版)生成于2025.11.27 09:52:43 +func Test_emailToolSet_Name(t *testing.T) { + tests := []struct { + name string + fields struct { + sendEmailEnabled bool + tools []tool.Tool + } + want string + }{ + { + name: "default zero value", + fields: struct { + sendEmailEnabled bool + tools []tool.Tool + }{}, + want: "email", + }, + { + name: "non-zero fields", + fields: struct { + sendEmailEnabled bool + tools []tool.Tool + }{ + sendEmailEnabled: true, + tools: []tool.Tool{}, + }, + want: "email", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + e := &emailToolSet{ + sendEmailEnabled: tt.fields.sendEmailEnabled, + tools: tt.fields.tools, + } + assert.Equal(t, tt.want, e.Name()) + }) + } +} + +// 由CodeBuddy(内网版)生成于2025.11.27 09:52:44 +func TestMailboxTypeToString(t *testing.T) { + type args struct { + mailboxType MailboxType + } + tests := []struct { + name string + args args + want string + }{ + {"qq", args{mailboxType: MAIL_QQ}, "qq"}, + {"163", args{mailboxType: MAIL_163}, "163"}, + {"gmail", args{mailboxType: MAIL_GMAIL}, "gmail"}, + {"zero", args{mailboxType: 0}, "unknown"}, + {"negative", args{mailboxType: -1}, "unknown"}, + {"undefined", args{mailboxType: 99}, "unknown"}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := MailboxTypeToString(tt.args.mailboxType); got != tt.want { + t.Errorf("MailboxTypeToString() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/tool/email/sendmail_test.go b/tool/email/sendmail_test.go index a33f455d4..639905528 100644 --- a/tool/email/sendmail_test.go +++ b/tool/email/sendmail_test.go @@ -172,3 +172,127 @@ func Test_emailToolSet_sendMailTool(t *testing.T) { assert.Equal(t, "send_email", decl.Name) assert.Equal(t, "send mail to other", decl.Description) } + +func Test_emailToolSet_getEmailAddr(t *testing.T) { + type args struct { + req *sendMailRequest + } + tests := []struct { + name string + e *emailToolSet + args args + wantAddr string + wantPort int + wantIsSSL bool + wantErr bool + }{ + { + name: "custom server", + e: &emailToolSet{}, + args: args{ + req: &sendMailRequest{ + Auth: Auth{Name: "user@example.com"}, + Extra: ExtraData{ + SvrAddr: "smtp.example.com", + Port: 2525, + }, + }, + }, + wantAddr: "smtp.example.com", + wantPort: 2525, + wantIsSSL: false, + wantErr: false, + }, + { + name: "qq mail", + e: &emailToolSet{}, + args: args{ + req: &sendMailRequest{ + Auth: Auth{Name: "123@qq.com"}, + }, + }, + wantAddr: qqMail, + wantPort: qqPort, + wantIsSSL: true, + wantErr: false, + }, + { + name: "gmail", + e: &emailToolSet{}, + args: args{ + req: &sendMailRequest{ + Auth: Auth{Name: "abc@gmail.com"}, + }, + }, + wantAddr: gmailMail, + wantPort: gmailPort, + wantIsSSL: false, + wantErr: false, + }, + { + name: "invalid email format", + e: &emailToolSet{}, + args: args{ + req: &sendMailRequest{ + Auth: Auth{Name: "not-an-email"}, + }, + }, + wantErr: true, + }, + { + name: "unsupported domain", + e: &emailToolSet{}, + args: args{ + req: &sendMailRequest{ + Auth: Auth{Name: "user@163.com"}, + }, + }, + wantErr: true, + }, + { + name: "empty auth name", + e: &emailToolSet{}, + args: args{ + req: &sendMailRequest{ + Auth: Auth{Name: ""}, + }, + }, + wantErr: true, + }, + { + name: "zero port with custom server", + e: &emailToolSet{}, + args: args{ + req: &sendMailRequest{ + Auth: Auth{Name: "user@example.com"}, + Extra: ExtraData{ + SvrAddr: "smtp.example.com", + Port: 0, + }, + }, + }, + wantAddr: "smtp.example.com", + wantPort: 0, + wantIsSSL: false, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotAddr, gotPort, gotIsSSL, err := tt.e.getEmailAddr(tt.args.req) + if (err != nil) != tt.wantErr { + t.Errorf("getEmailAddr() error = %v, wantErr %v", err, tt.wantErr) + return + } + if gotAddr != tt.wantAddr { + t.Errorf("getEmailAddr() gotAddr = %v, want %v", gotAddr, tt.wantAddr) + } + if gotPort != tt.wantPort { + t.Errorf("getEmailAddr() gotPort = %v, want %v", gotPort, tt.wantPort) + } + if gotIsSSL != tt.wantIsSSL { + t.Errorf("getEmailAddr() gotIsSSL = %v, want %v", gotIsSSL, tt.wantIsSSL) + } + }) + } +} From 270d9ee03fb9274addc881270cd74a7d6ebbb50a Mon Sep 17 00:00:00 2001 From: 201430098137 <1850396756@qq.com> Date: Thu, 27 Nov 2025 11:52:13 +0800 Subject: [PATCH 6/9] tests: email tool add more test case --- tool/email/sendmail.go | 3 +- tool/email/sendmail_test.go | 86 ++++++++++++++++++++++++++++++++++--- 2 files changed, 82 insertions(+), 7 deletions(-) diff --git a/tool/email/sendmail.go b/tool/email/sendmail.go index 82c0ed758..b5a0e8b80 100644 --- a/tool/email/sendmail.go +++ b/tool/email/sendmail.go @@ -52,7 +52,6 @@ type sendMailResponse struct { } // sendMail performs the send mail operation. -// go smtp not support context, one send one mail, can't stop func (e *emailToolSet) sendMail(ctx context.Context, req *sendMailRequest) (rsp *sendMailResponse, err error) { rsp = &sendMailResponse{} @@ -81,7 +80,7 @@ func (e *emailToolSet) sendMail(ctx context.Context, req *sendMailRequest) (rsp opts..., ) if err != nil { - rsp.Message = fmt.Sprintf("the address or password is incorrect,please check: %v", err) + rsp.Message = fmt.Sprintf("the server address err: %v", err) return rsp, nil } diff --git a/tool/email/sendmail_test.go b/tool/email/sendmail_test.go index 639905528..187638601 100644 --- a/tool/email/sendmail_test.go +++ b/tool/email/sendmail_test.go @@ -7,7 +7,7 @@ import ( "github.com/stretchr/testify/assert" ) -func TestMailTool_sendMail(t *testing.T) { +func Test_emailToolSet_sendMail(t *testing.T) { toolSet, err := NewToolSet( WithSendEmailEnabled(true), ) @@ -21,6 +21,7 @@ func TestMailTool_sendMail(t *testing.T) { ToEmail string Subject string Content string + wantErr bool }{ { @@ -29,6 +30,7 @@ func TestMailTool_sendMail(t *testing.T) { ToEmail: "zhuangguang5524621@gmail.com", Subject: "test", Content: "test", + wantErr: false, }, { @@ -37,13 +39,76 @@ func TestMailTool_sendMail(t *testing.T) { ToEmail: "1850396756@qq.com", Subject: "test", Content: "test", + wantErr: false, }, } for _, tt := range tests { + rsp, err := toolSet.(*emailToolSet).sendMail(context.Background(), &sendMailRequest{ + Auth: Auth{ + Name: tt.Name, + Password: tt.Password, + }, + MailList: []*Mail{ + { + ToEmail: tt.ToEmail, + Subject: tt.Subject, + Content: tt.Content, + }, + }, + }) + t.Logf("rsp: %+v err:%v", rsp, err) if tt.Password == "" { - t.Skip("no passwd skip") + t.Logf("password is empty, skip") + continue } + if rsp.Message != "" { + if tt.wantErr == false { + t.Errorf("send mail err: %s", rsp.Message) + } + } else if tt.wantErr == true { + t.Errorf("should err but not") + } + + } +} + +func Test_emailToolSet_sendMail2(t *testing.T) { + toolSet, err := NewToolSet( + WithSendEmailEnabled(true), + ) + if err != nil { + t.Errorf("NewToolSet failed, err: %v", err) + } + + tests := []struct { + Name string + Password string + ToEmail string + Subject string + Content string + wantErr bool + }{ + // error case + { + Name: "18503@96756@qq.com", + Password: "", + ToEmail: "zhuangguang5524621@gmail.com", + Subject: "test", + Content: "test", + wantErr: true, + }, + // error case + { + Name: "zhuangguang5524621@gmail.com", + Password: "", + ToEmail: "185039@6756@qq.com", + Subject: "test", + Content: "test", + wantErr: true, + }, + } + for _, tt := range tests { rsp, err := toolSet.(*emailToolSet).sendMail(context.Background(), &sendMailRequest{ Auth: Auth{ @@ -58,10 +123,15 @@ func TestMailTool_sendMail(t *testing.T) { }, }, }) - if err != nil { - t.Errorf("send mail err: %v", err) + t.Logf("rsp: %+v err:%v", rsp, err) + if rsp.Message != "" { + if tt.wantErr == false { + t.Errorf("send mail err: %s", rsp.Message) + } + } else if tt.wantErr == true { + t.Errorf("should err but not") } - t.Logf("rsp: %+v", rsp) + } } @@ -147,6 +217,12 @@ func Test_checkMailBoxType(t *testing.T) { want: MAIL_GMAIL, wantErr: false, }, + { + name: "not valid email", + args: args{email: "UserGmAiL.cOm"}, + want: MAIL_UNKNOWN, + wantErr: true, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { From 2c07d06d6dd23296f49d264f4b930ace830acbc8 Mon Sep 17 00:00:00 2001 From: 201430098137 <1850396756@qq.com> Date: Thu, 27 Nov 2025 12:14:14 +0800 Subject: [PATCH 7/9] doc: fix ReadMe.md --- examples/email/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/email/README.md b/examples/email/README.md index 51ec6dbed..34dadb196 100644 --- a/examples/email/README.md +++ b/examples/email/README.md @@ -10,7 +10,7 @@ This example demonstrates how to handle various types of email (qq,gmail) using ```bash # set your open base url, -export OPENAI_BASE_URL="http://ieval.woa.com/openapi/v1" +export OPENAI_BASE_URL="https://api.openai.com/v1" # Set your API key export OPENAI_API_KEY="your-api-key-here" From 37d2b9d21c3b71ecd3b7420cf968f0ef57389e77 Mon Sep 17 00:00:00 2001 From: 201430098137 <1850396756@qq.com> Date: Thu, 27 Nov 2025 16:08:41 +0800 Subject: [PATCH 8/9] fix: adjust the instruction --- examples/email/main.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/email/main.go b/examples/email/main.go index 23705867b..a9edff5ee 100644 --- a/examples/email/main.go +++ b/examples/email/main.go @@ -91,7 +91,8 @@ func (c *emailChat) setup(_ context.Context) error { agentName, llmagent.WithModel(modelInstance), llmagent.WithDescription("A helpful AI assistant with access to email sending capabilities"), - llmagent.WithInstruction("Use the email tool to send emails. ask user to provide account credentials"), + llmagent.WithInstruction("Use the email tool to send emails. ask user to provide account credentials. "+ + "if sending failed, error message contain web link, please tell the link to user"), llmagent.WithGenerationConfig(genConfig), llmagent.WithToolSets([]tool.ToolSet{emailTool}), ) From 87c608fa67b717f301ec644ae89bfd16c7b8ade6 Mon Sep 17 00:00:00 2001 From: 201430098137 <1850396756@qq.com> Date: Thu, 27 Nov 2025 17:13:43 +0800 Subject: [PATCH 9/9] tool(email):add 163 mail support --- examples/email/README.md | 2 +- tool/email/sendmail.go | 15 +++++++++++---- tool/email/sendmail_test.go | 31 ++++++++++++++++++++++++++----- 3 files changed, 38 insertions(+), 10 deletions(-) diff --git a/examples/email/README.md b/examples/email/README.md index 34dadb196..be684cada 100644 --- a/examples/email/README.md +++ b/examples/email/README.md @@ -1,6 +1,6 @@ # email Example -This example demonstrates how to handle various types of email (qq,gmail) using OpenAI-compatible models. +This example demonstrates how to handle various types of email (qq,gmail,163) using OpenAI-compatible models. ## Features diff --git a/tool/email/sendmail.go b/tool/email/sendmail.go index b5a0e8b80..71df409bc 100644 --- a/tool/email/sendmail.go +++ b/tool/email/sendmail.go @@ -16,10 +16,12 @@ import ( ) const ( - qqMail = "smtp.qq.com" - qqPort = 465 - gmailMail = "smtp.gmail.com" - gmailPort = 587 + qqMail = "smtp.qq.com" + qqPort = 465 + gmailMail = "smtp.gmail.com" + gmailPort = 587 + netEase163Mail = "smtp.163.com" + netEase1163Port = 465 ) // sendMailRequest represents the input for the send mail operation. @@ -144,6 +146,11 @@ func (e *emailToolSet) getEmailAddr(req *sendMailRequest) (addr string, port int addr = gmailMail port = gmailPort isSSL = false + case MAIL_163: + //163 email + addr = netEase163Mail + port = netEase1163Port + isSSL = true default: // not support err = fmt.Errorf("not support mailbox type:%s", MailboxTypeToString(mailBoxType)) diff --git a/tool/email/sendmail_test.go b/tool/email/sendmail_test.go index 187638601..5128df389 100644 --- a/tool/email/sendmail_test.go +++ b/tool/email/sendmail_test.go @@ -23,7 +23,7 @@ func Test_emailToolSet_sendMail(t *testing.T) { Content string wantErr bool }{ - + // qq to gmail { Name: "1850396756@qq.com", Password: "", @@ -32,7 +32,7 @@ func Test_emailToolSet_sendMail(t *testing.T) { Content: "test", wantErr: false, }, - + // gmail to qq { Name: "zhuangguang5524621@gmail.com", Password: "", @@ -41,6 +41,15 @@ func Test_emailToolSet_sendMail(t *testing.T) { Content: "test", wantErr: false, }, + // 163 to gmail + { + Name: "18218025138@163.com", + Password: "", + ToEmail: "zhuangguang5524621@gmail.com", + Subject: "test", + Content: "test", + wantErr: false, + }, } for _, tt := range tests { @@ -59,8 +68,7 @@ func Test_emailToolSet_sendMail(t *testing.T) { }) t.Logf("rsp: %+v err:%v", rsp, err) if tt.Password == "" { - t.Logf("password is empty, skip") - continue + t.Skip("password is empty, skip") } if rsp.Message != "" { if tt.wantErr == false { @@ -305,6 +313,19 @@ func Test_emailToolSet_getEmailAddr(t *testing.T) { wantIsSSL: false, wantErr: false, }, + { + name: "163 mail", + e: &emailToolSet{}, + args: args{ + req: &sendMailRequest{ + Auth: Auth{Name: "user@163.com"}, + }, + }, + wantAddr: netEase163Mail, + wantPort: netEase1163Port, + wantIsSSL: true, + wantErr: false, + }, { name: "invalid email format", e: &emailToolSet{}, @@ -320,7 +341,7 @@ func Test_emailToolSet_getEmailAddr(t *testing.T) { e: &emailToolSet{}, args: args{ req: &sendMailRequest{ - Auth: Auth{Name: "user@163.com"}, + Auth: Auth{Name: "user@icloud.com"}, }, }, wantErr: true,