agent

package
v0.3.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 11, 2026 License: MIT Imports: 23 Imported by: 0

README

ODP Agent package

Package agent composes the Agent side of ODP. It inspects live Service Documents, navigates one Service's catalog, and performs bounded discovery across Services returned by the canonical directory.

Inspect a Service

client, err := agent.NewServiceClient(agent.ServiceClientOptions{
	ServiceURL: "https://compute.example",
})
if err != nil {
	return err
}

inspection, err := client.Inspect(ctx)
if err != nil {
	return err
}

inspection.Capabilities.Operations
inspection.Capabilities.Enrollment
inspection.Capabilities.Payments

The client validates /.well-known/odp, accepts at most five same-origin redirects, and applies a four-hour fallback freshness when the Service does not publish HTTP cache metadata. Supply a Cache for persistent storage; otherwise each client owns an in-memory cache.

Navigate a catalog

List and search methods return lazy Go iterators. Iteration stops network activity when the caller stops consuming results.

for offering, err := range client.ListOfferings(ctx, agent.ListOptions{MaxItems: 20}) {
	if err != nil {
		return err
	}
	consume(offering)
}

for offering, err := range client.SearchOfferings(ctx, agent.OfferingSearchOptions{
	Query:    "gpu",
	MaxItems: 10,
}) {
	if err != nil {
		return err
	}
	consume(offering)
}

offering, err := client.GetOffering(ctx, "gpu-h100", odp.RepresentationFull)

details, err := client.GetOfferingDetails(ctx, "gpu-h100")

The same client lists, retrieves, and searches Collections and lists the direct Offerings in a Collection. Page methods expose continuations and search refinements when page-level metadata is needed.

Short-lived clients can resume a Service-provided continuation with the corresponding ContinueList... or ContinueSearch... item or page method. The client retrieves the opaque reference with GET and applies the same-origin, response, redirect, and traversal limits.

Service Documents use a four-hour fallback freshness, Collections use one hour, and Offerings use five minutes. Service-provided Cache-Control or Expires metadata takes precedence. Search responses are cached only when the Service supplies explicit freshness metadata. Supplying a custom HTTP client disables catalog caching unless CachePartition identifies its stable access context; this prevents authenticated or payment-dependent representations from sharing a public cache key.

RequestError preserves ODP Problem Details and response headers, exposes a stable error code, and marks rate-limit and server failures as retryable so application transports can compose AEP, MPP, and x402 challenges.

Use search capabilities and Offering details

Capability methods combine Service-wide definitions with the selected Collection and resolve linked definition pages. Duplicate definitions and Sorts that reference unavailable Filters are omitted and reported through SearchCapabilityCatalog.Issues.

capabilities, err := client.GetOfferingSearchCapabilities(ctx, "accelerators")
if err != nil {
	return err
}

memory := capabilities.Filters["accelerator-memory"]

GetOfferingDetails validates Attributes against the Offering's JSON Schema, bundles external schema references, and converts Action targets to absolute URLs. Unavailable schemas, invalid Attributes, and unusable Actions are omitted from the corresponding enriched fields and reported in OfferingDetails.Issues; the protocol Offering remains available in OfferingDetails.Offering.

details, err := client.GetOfferingDetails(ctx, "gpu-h100")
if err != nil {
	return err
}

resolved, err := client.ResolveAction(ctx, "gpu-h100", "purchase")
if err != nil {
	return err
}

ResolveAction returns an HTTP Action's request schema or an OpenAPI 3.1 document and its uniquely selected operation. It does not invoke the Action. An OpenAPI Action may omit its URL when the Service Document declares http.openapi.url; an Action URL overrides that Service-wide default. Supporting schemas and OpenAPI documents use a separate anonymous HTTP client and must use HTTPS. Supply SupportingHTTPClient only when those requests need custom network transport; keep it free of Service credentials. Attribute Schemas use a 24-hour fallback freshness, while OpenAPI documents require explicit HTTP freshness metadata.

Discover Offerings across Services

Agent searches Services through the canonical directory, then queries each selected Service with bounded concurrency. Events remain in directory order. A failed Service produces an issue event without discarding successful results from other Services.

odpAgent, err := agent.New(agent.AgentOptions{Environment: directory.Sandbox})
if err != nil {
	return err
}

request := agent.FederatedSearchRequest{
	Services: directory.SearchRequest{
		Filters: &directory.ServiceFilters{Keywords: []string{"gpu"}},
	},
	Offerings: agent.OfferingSearchOptions{Query: "accelerator"},
}

for event, err := range odpAgent.SearchOfferingsAcrossServices(ctx, request) {
	if err != nil {
		return err
	}
	switch event.Type {
	case agent.DiscoveryOffering:
		consume(event.Service, *event.Offering)
	case agent.DiscoveryIssue:
		report(event.Service, event.Err)
	}
}

The defaults search at most 10 Services, retain at most 10 terse Offerings from each, and run four Service requests concurrently. The directory origin is fixed by the selected production or sandbox environment.

See the runnable Agent example, which clearly labels and isolates its mock directory while querying live ODP Services.

Documentation

Overview

Package agent provides Agent-side ODP Service inspection, catalog navigation, and discovery.

Index

Constants

View Source
const (
	ServiceDocumentFallback = 4 * time.Hour
	CollectionFallback      = time.Hour
	OfferingFallback        = 5 * time.Minute
)

Variables

View Source
var ErrUnsupportedOperation = errors.New("ODP Service does not advertise the requested operation")

Functions

This section is empty.

Types

type Agent

type Agent struct {
	// contains filtered or unexported fields
}

func New

func New(options AgentOptions) (*Agent, error)

func (*Agent) Environment

func (agent *Agent) Environment() directory.Environment

func (*Agent) SearchOfferingsAcrossServices

func (agent *Agent) SearchOfferingsAcrossServices(ctx context.Context, request FederatedSearchRequest) iter.Seq2[DiscoveryEvent, error]

type AgentOptions

type AgentOptions struct {
	Directory           *directory.Client
	DirectoryHTTPClient *http.Client
	Environment         directory.Environment
	ServiceClient       ServiceClientFactory
}

type Cache

type CacheFallbacks

type CacheFallbacks struct {
	Collection      time.Duration
	Offering        time.Duration
	ServiceDocument time.Duration
}

type CacheRecord

type CacheRecord struct {
	Body         []byte
	ETag         string
	ExpiresAt    time.Time
	FinalURL     string
	LastModified string
	Status       int
	StoredAt     time.Time
}

type Capabilities

type Capabilities struct {
	Enrollment []odp.EnrollmentProtocol
	Operations []odp.OperationDescriptor
	Payments   []odp.PaymentProtocol
}

type CapabilityIssue

type CapabilityIssue struct {
	Kind    CapabilityKind
	Message string
	Scope   CapabilityScope
}

type CapabilityKind

type CapabilityKind string
const (
	CapabilityKindFilters CapabilityKind = "filters"
	CapabilityKindSorts   CapabilityKind = "sorts"
)

type CapabilityScope

type CapabilityScope string
const (
	CapabilityScopeCollection CapabilityScope = "collection"
	CapabilityScopeService    CapabilityScope = "service"
)

type CollectionSearchOptions

type CollectionSearchOptions struct {
	Limit          int
	MaxItems       int
	MaxPages       int
	ParentID       odp.Optional[string]
	Query          string
	Representation odp.Representation
}

type ContinuationOptions added in v0.2.0

type ContinuationOptions struct {
	MaxItems       int
	MaxPages       int
	Representation odp.Representation
}

type DiscoveredAction

type DiscoveredAction struct {
	Authentication odp.AuthenticationRequirement
	Description    string
	HTTP           *DiscoveredHTTPAction
	ID             string
	OpenAPI        *DiscoveredOpenAPIAction
	Rel            odp.ActionRelation
}

type DiscoveredHTTPAction

type DiscoveredHTTPAction struct {
	Method               string
	Request              *odp.ActionRequest
	ResponseContentTypes []string
	URL                  string
}

type DiscoveredOpenAPIAction

type DiscoveredOpenAPIAction struct {
	OperationID string
	URL         string
}

type DiscoveryEvent

type DiscoveryEvent struct {
	Err      error
	Offering *odp.Offering
	Service  directory.Service
	Type     DiscoveryEventType
}

type DiscoveryEventType

type DiscoveryEventType string
const (
	DiscoveryOffering DiscoveryEventType = "offering"
	DiscoveryIssue    DiscoveryEventType = "issue"
)

type FederatedSearchRequest

type FederatedSearchRequest struct {
	Concurrency            int
	MaxOfferingsPerService int
	MaxServices            int
	Offerings              OfferingSearchOptions
	Services               directory.SearchRequest
}

type Freshness

type Freshness string
const (
	FreshnessFetched     Freshness = "fetched"
	FreshnessFresh       Freshness = "fresh"
	FreshnessRevalidated Freshness = "revalidated"
)

type Inspection

type Inspection struct {
	Capabilities  Capabilities
	Document      odp.ServiceDocument
	FinalURL      string
	Freshness     Freshness
	RequestedURL  string
	ServiceOrigin string
}

type ListOptions

type ListOptions struct {
	Limit          int
	MaxItems       int
	MaxPages       int
	Representation odp.Representation
}

type MemoryCache

type MemoryCache struct {
	// contains filtered or unexported fields
}

func NewMemoryCache

func NewMemoryCache() *MemoryCache

func (*MemoryCache) Delete

func (cache *MemoryCache) Delete(_ context.Context, key string) error

func (*MemoryCache) Get

func (cache *MemoryCache) Get(_ context.Context, key string) (CacheRecord, bool, error)

func (*MemoryCache) Set

func (cache *MemoryCache) Set(_ context.Context, key string, record CacheRecord) error

type OfferingDetails

type OfferingDetails struct {
	odp.Offering
	Actions         []DiscoveredAction
	AttributeSchema map[string]any
	Issues          []OfferingIssue
}

type OfferingIssue

type OfferingIssue struct {
	ActionID string
	Message  string
	Scope    OfferingIssueScope
}

type OfferingIssueScope

type OfferingIssueScope string
const (
	OfferingIssueAction          OfferingIssueScope = "action"
	OfferingIssueAttributeSchema OfferingIssueScope = "attribute_schema"
	OfferingIssueAttributes      OfferingIssueScope = "attributes"
)

type OfferingSearchOptions

type OfferingSearchOptions struct {
	CollectionID       string
	Filters            []odp.FilterExpression
	IncludeDescendants bool
	Limit              int
	MaxItems           int
	MaxPages           int
	Query              string
	Refinements        []string
	Representation     odp.Representation
	Sort               string
}

type RequestError

type RequestError struct {
	Code      string
	Header    http.Header
	Problem   *odp.ProblemDetails
	Retryable bool
	Status    int
}

func (*RequestError) Error

func (err *RequestError) Error() string

type ResolvedAction

type ResolvedAction struct {
	Action          DiscoveredAction
	OpenAPIDocument map[string]any
	Operation       map[string]any
	RequestSchema   map[string]any
}

type ResolvedSortDefinition

type ResolvedSortDefinition struct {
	odp.SortDefinition
	Filters []odp.FilterDefinition
}

type SearchCapabilityCatalog

type SearchCapabilityCatalog struct {
	Filters map[string]odp.FilterDefinition
	Issues  []CapabilityIssue
	Sorts   map[string]ResolvedSortDefinition
}

type ServiceClient

type ServiceClient struct {
	// contains filtered or unexported fields
}

func NewServiceClient

func NewServiceClient(options ServiceClientOptions) (*ServiceClient, error)

func (*ServiceClient) ContinueListCollectionPages added in v0.2.0

func (client *ServiceClient) ContinueListCollectionPages(ctx context.Context, next string, options ContinuationOptions) iter.Seq2[odp.Page[odp.Collection], error]

func (*ServiceClient) ContinueListCollections added in v0.2.0

func (client *ServiceClient) ContinueListCollections(ctx context.Context, next string, options ContinuationOptions) iter.Seq2[odp.Collection, error]

func (*ServiceClient) ContinueListOfferingPages added in v0.2.0

func (client *ServiceClient) ContinueListOfferingPages(ctx context.Context, next string, options ContinuationOptions) iter.Seq2[odp.OfferingPage[odp.Offering], error]

func (*ServiceClient) ContinueListOfferings added in v0.2.0

func (client *ServiceClient) ContinueListOfferings(ctx context.Context, next string, options ContinuationOptions) iter.Seq2[odp.Offering, error]

func (*ServiceClient) ContinueSearchCollectionPages added in v0.2.0

func (client *ServiceClient) ContinueSearchCollectionPages(ctx context.Context, next string, options ContinuationOptions) iter.Seq2[odp.Page[odp.Collection], error]

func (*ServiceClient) ContinueSearchCollections added in v0.2.0

func (client *ServiceClient) ContinueSearchCollections(ctx context.Context, next string, options ContinuationOptions) iter.Seq2[odp.Collection, error]

func (*ServiceClient) ContinueSearchOfferingPages added in v0.2.0

func (client *ServiceClient) ContinueSearchOfferingPages(ctx context.Context, next string, options ContinuationOptions) iter.Seq2[odp.OfferingPage[odp.Offering], error]

func (*ServiceClient) ContinueSearchOfferings added in v0.2.0

func (client *ServiceClient) ContinueSearchOfferings(ctx context.Context, next string, options ContinuationOptions) iter.Seq2[odp.Offering, error]

func (*ServiceClient) GetCollection

func (client *ServiceClient) GetCollection(ctx context.Context, id string, representation odp.Representation) (odp.Collection, error)

func (*ServiceClient) GetCollectionSearchCapabilities

func (client *ServiceClient) GetCollectionSearchCapabilities(ctx context.Context, id string) (SearchCapabilityCatalog, error)

func (*ServiceClient) GetOffering

func (client *ServiceClient) GetOffering(ctx context.Context, id string, representation odp.Representation) (odp.Offering, error)

func (*ServiceClient) GetOfferingDetails

func (client *ServiceClient) GetOfferingDetails(ctx context.Context, id string) (OfferingDetails, error)

func (*ServiceClient) GetOfferingSearchCapabilities

func (client *ServiceClient) GetOfferingSearchCapabilities(ctx context.Context, collectionID string) (SearchCapabilityCatalog, error)

func (*ServiceClient) Inspect

func (client *ServiceClient) Inspect(ctx context.Context) (Inspection, error)

func (*ServiceClient) ListCollectionOfferingPages

func (client *ServiceClient) ListCollectionOfferingPages(ctx context.Context, collectionID string, options ListOptions) iter.Seq2[odp.OfferingPage[odp.Offering], error]

func (*ServiceClient) ListCollectionOfferings

func (client *ServiceClient) ListCollectionOfferings(ctx context.Context, collectionID string, options ListOptions) iter.Seq2[odp.Offering, error]

func (*ServiceClient) ListCollectionPages

func (client *ServiceClient) ListCollectionPages(ctx context.Context, options ListOptions) iter.Seq2[odp.Page[odp.Collection], error]

func (*ServiceClient) ListCollections

func (client *ServiceClient) ListCollections(ctx context.Context, options ListOptions) iter.Seq2[odp.Collection, error]

func (*ServiceClient) ListOfferingPages

func (client *ServiceClient) ListOfferingPages(ctx context.Context, options ListOptions) iter.Seq2[odp.OfferingPage[odp.Offering], error]

func (*ServiceClient) ListOfferings

func (client *ServiceClient) ListOfferings(ctx context.Context, options ListOptions) iter.Seq2[odp.Offering, error]

func (*ServiceClient) ResolveAction

func (client *ServiceClient) ResolveAction(ctx context.Context, offeringID, actionID string) (ResolvedAction, error)

func (*ServiceClient) SearchCollectionPages

func (client *ServiceClient) SearchCollectionPages(ctx context.Context, options CollectionSearchOptions) iter.Seq2[odp.Page[odp.Collection], error]

func (*ServiceClient) SearchCollections

func (client *ServiceClient) SearchCollections(ctx context.Context, options CollectionSearchOptions) iter.Seq2[odp.Collection, error]

func (*ServiceClient) SearchOfferingPages

func (client *ServiceClient) SearchOfferingPages(ctx context.Context, options OfferingSearchOptions) iter.Seq2[odp.OfferingPage[odp.Offering], error]

func (*ServiceClient) SearchOfferings

func (client *ServiceClient) SearchOfferings(ctx context.Context, options OfferingSearchOptions) iter.Seq2[odp.Offering, error]

type ServiceClientFactory

type ServiceClientFactory func(context.Context, directory.Service) (*ServiceClient, error)

type ServiceClientOptions

type ServiceClientOptions struct {
	AcceptLanguage       string
	Cache                Cache
	CacheFallbacks       CacheFallbacks
	CachePartition       string
	HTTPClient           *http.Client
	SupportingHTTPClient *http.Client
	InitialPageSize      int
	MaxRedirects         int
	ServiceURL           string
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL