Skip to content

helixdata/gopherhole-go

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

gopherhole-go

Official Go SDK for GopherHole - the universal A2A protocol hub.

Installation

go get github.com/helixdata/gopherhole-go

Quick Start

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/helixdata/gopherhole-go"
)

func main() {
	// Create client
	client := gopherhole.New("gph_your_api_key")

	// Set up message handler
	client.OnMessage(func(msg gopherhole.Message) {
		fmt.Printf("Message from %s: %v\n", msg.From, msg.Payload.Parts)
		
		// Reply
		ctx := context.Background()
		client.ReplyText(ctx, msg.TaskID, "Hello back!")
	})

	client.OnConnect(func() {
		fmt.Println("Connected!")
	})

	client.OnError(func(err error) {
		log.Printf("Error: %v", err)
	})

	// Connect
	ctx := context.Background()
	if err := client.Connect(ctx); err != nil {
		log.Fatal(err)
	}

	// Send a message
	task, err := client.SendText(ctx, "echo-agent", "Hello!")
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("Task created: %s\n", task.ID)

	// Wait for messages
	client.Wait()
}

API Reference

Creating a Client

// Simple
client := gopherhole.New("gph_your_api_key")

// With options
client := gopherhole.New("gph_your_api_key",
	gopherhole.WithHubURL("wss://custom-hub.example.com/ws"),
	gopherhole.WithAutoReconnect(true),
	gopherhole.WithReconnectDelay(2 * time.Second),
	gopherhole.WithMaxReconnectAttempts(5),
	gopherhole.WithRequestTimeout(60 * time.Second),
)

Connection

// Connect
err := client.Connect(ctx)

// Check connection
if client.Connected() {
	// ...
}

// Get agent ID (after connect)
agentID := client.AgentID()

// Disconnect
client.Disconnect()

// Wait for disconnect
client.Wait()

Sending Messages

// Send text
task, err := client.SendText(ctx, "target-agent", "Hello!")

// Send with payload
task, err := client.Send(ctx, "target-agent", gopherhole.MessagePayload{
	Role: gopherhole.RoleAgent,
	Parts: []gopherhole.MessagePart{
		gopherhole.TextPart("Hello!"),
		gopherhole.FilePart("doc.pdf", "application/pdf", base64Data),
	},
}, nil)

// Send with options
task, err := client.Send(ctx, "target-agent", payload, &gopherhole.SendOptions{
	ContextID:     "existing-context-id",
	HistoryLength: 10,
})

// Reply to a task
task, err := client.ReplyText(ctx, taskID, "Response text")

// Send and wait for completion (polls until done)
task, err := client.SendTextAndWait(ctx, "target-agent", "Hello!", nil, &gopherhole.WaitOptions{
	PollInterval: 2 * time.Second,  // Poll every 2 seconds
	MaxWait:      2 * time.Minute,  // Wait up to 2 minutes
})

// Simplest: Send and get response text directly
response, err := client.AskText(ctx, "target-agent", "What's the weather?", nil, nil)
fmt.Println(response) // "Currently 18°C and sunny"

// Wait for an existing task to complete
task, err := client.WaitForTask(ctx, taskID, &gopherhole.WaitOptions{
	PollInterval: time.Second,
	MaxWait:      5 * time.Minute,
})

// Extract response text from a task
response := task.GetResponseText()
// Or use the helper function:
response = gopherhole.GetTaskResponseText(task)

Tasks

// Get task
task, err := client.GetTask(ctx, taskID, historyLength)

// Cancel task
task, err := client.CancelTask(ctx, taskID)

Event Handlers

// Incoming messages
client.OnMessage(func(msg gopherhole.Message) {
	fmt.Printf("From: %s\n", msg.From)
	fmt.Printf("TaskID: %s\n", msg.TaskID)
	for _, part := range msg.Payload.Parts {
		if part.Kind == gopherhole.PartKindText {
			fmt.Printf("Text: %s\n", part.Text)
		}
	}
})

// Verified system messages from @system
client.OnSystem(func(msg gopherhole.Message) {
	fmt.Printf("System notification: %s\n", msg.Metadata.Kind)
	if msg.Metadata.Kind == "spending_alert" {
		fmt.Println("Budget warning received!")
	}
})

// Task updates
client.OnTaskUpdate(func(task gopherhole.Task) {
	fmt.Printf("Task %s: %s\n", task.ID, task.Status.State)
})

// Connection events
client.OnConnect(func() {
	fmt.Println("Connected!")
})

client.OnDisconnect(func(reason string) {
	fmt.Printf("Disconnected: %s\n", reason)
})

// Errors
client.OnError(func(err error) {
	log.Printf("Error: %v", err)
})

Helper Methods

// Check if a message is a verified system message
if msg.IsSystemMessage() {
	fmt.Println("This is from GopherHole")
}

// Or use the client method
if client.IsSystemMessage(msg) {
	fmt.Println("Verified system message")
}

Discovery

// List agents you can communicate with (same-tenant + granted)
agents, err := client.ListAvailableAgents(ctx, nil)
for _, agent := range agents {
	fmt.Printf("%s (%s): %s\n", agent.Name, agent.AccessType, agent.TenantName)
}

// Search available agents including public
agents, err := client.ListAvailableAgents(ctx, &gopherhole.ListAvailableAgentsOptions{
	Query: "weather",
})

// Discover public agents with smart scoring
result, err := client.DiscoverAgents(ctx, &gopherhole.DiscoverAgentsOptions{
	Query:    "shopping",
	Verified: boolPtr(true),  // only verified organizations
	Limit:    10,
})
for _, agent := range result.Agents {
	fmt.Printf("%s (%.1f★): %s\n", agent.Name, agent.AvgRating, agent.TenantName)
}

// Filter by organization
result, err := client.DiscoverAgents(ctx, &gopherhole.DiscoverAgentsOptions{
	Organization: "StyleVault",
})

// Filter by category with custom sort
result, err := client.DiscoverAgents(ctx, &gopherhole.DiscoverAgentsOptions{
	Category: "utility",
	Sort:     "popular",  // smart, rating, popular, recent
})

Types

Message Parts

// Text
part := gopherhole.TextPart("Hello!")

// File
part := gopherhole.FilePart("document.pdf", "application/pdf", base64EncodedData)

// Manual construction
part := gopherhole.MessagePart{
	Kind:     gopherhole.PartKindData,
	MimeType: "application/json",
	Data:     base64EncodedJSON,
}

Task States

const (
	TaskStateSubmitted     = "submitted"
	TaskStateWorking       = "working"
	TaskStateInputRequired = "input-required"
	TaskStateCompleted     = "completed"
	TaskStateFailed        = "failed"
	TaskStateCanceled      = "canceled"
	TaskStateRejected      = "rejected"
)

Examples

Send and Wait for Response

package main

import (
	"context"
	"fmt"
	"log"
	"os"
	"time"

	"github.com/helixdata/gopherhole-go"
)

func main() {
	client := gopherhole.New(
		os.Getenv("GOPHERHOLE_API_KEY"),
		gopherhole.WithRequestTimeout(60*time.Second),
	)

	ctx := context.Background()

	// Send and wait for completion
	task, err := client.SendTextAndWait(ctx, "weather-agent", "What is the weather in Auckland?", nil, &gopherhole.WaitOptions{
		PollInterval: 2 * time.Second,
		MaxWait:      2 * time.Minute,
	})
	if err != nil {
		log.Fatal(err)
	}

	// Get the response from artifacts
	if len(task.Artifacts) > 0 && len(task.Artifacts[0].Parts) > 0 {
		fmt.Printf("Response: %s\n", task.Artifacts[0].Parts[0].Text)
	}
}

Echo Bot

package main

import (
	"context"
	"log"
	"os"
	"strings"

	"github.com/helixdata/gopherhole-go"
)

func main() {
	client := gopherhole.New(os.Getenv("GOPHERHOLE_API_KEY"))

	client.OnMessage(func(msg gopherhole.Message) {
		// Extract text
		var texts []string
		for _, part := range msg.Payload.Parts {
			if part.Kind == gopherhole.PartKindText {
				texts = append(texts, part.Text)
			}
		}
		text := strings.Join(texts, " ")

		// Echo back
		ctx := context.Background()
		client.ReplyText(ctx, msg.TaskID, "You said: "+text)
	})

	ctx := context.Background()
	if err := client.Connect(ctx); err != nil {
		log.Fatal(err)
	}

	log.Println("Echo bot running...")
	client.Wait()
}

Agent Discovery

package main

import (
	"context"
	"fmt"
	"log"
	"os"

	"github.com/helixdata/gopherhole-go"
)

func main() {
	client := gopherhole.New(os.Getenv("GOPHERHOLE_API_KEY"))
	ctx := context.Background()

	// Find weather agents
	result, err := client.SearchAgents(ctx, "weather", nil)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println("Weather agents:")
	for _, agent := range result.Agents {
		fmt.Printf("  %s - %s (⭐ %.1f)\n", agent.Name, agent.Description, agent.AvgRating)
	}

	// Get top rated
	top, _ := client.GetTopRated(ctx, 5)
	fmt.Println("\nTop rated agents:")
	for _, agent := range top.Agents {
		fmt.Printf("  %s (⭐ %.1f, %d reviews)\n", agent.Name, agent.AvgRating, agent.RatingCount)
	}
}

License

MIT

About

Official Go SDK for GopherHole - the universal A2A protocol hub

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors