-
Notifications
You must be signed in to change notification settings - Fork 893
Go sdk #579
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
jgindin
wants to merge
4
commits into
google:main
Choose a base branch
from
jgindin:go-sdk
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Go sdk #579
Changes from 2 commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,122 @@ | ||
| # A2UI Agent Go SDK | ||
|
|
||
| This directory contains the Go implementation of the A2UI agent library, | ||
| enabling agents to "speak UI" using the A2UI protocol. | ||
|
|
||
| ## Overview | ||
|
|
||
| The `a2ui` package provides the core infrastructure for an agent to: | ||
|
|
||
| 1. **Declare Capability**: Signal support for the A2UI extension ( | ||
| `https://a2ui.org/a2a-extension/a2ui/v0.8`). | ||
| 2. **Validate Payloads**: Ensure generated A2UI JSON conforms to the required | ||
| schema before sending. | ||
| 3. **Transport UI**: Encapsulate A2UI payloads within A2A `DataPart`s for | ||
| transport to the client. | ||
|
|
||
| ## Components | ||
|
|
||
| The SDK mirrors the structure of the reference Python implementation: | ||
|
|
||
| * **`a2ui.go`**: Core constants (`ExtensionURI`, MIME types), types, and helper | ||
| functions for extension management (`GetA2UIAgentExtension`, | ||
| `TryActivateA2UIExtension`) and A2UI part manipulation (`CreateA2UIPart`, | ||
| `IsA2UIPart`, `GetA2UIDataPart`). | ||
| * **`schema.go`**: Utilities for A2UI schema manipulation, specifically wrapping | ||
| the schema in a JSON array to support streaming lists of components. | ||
| * **`toolset.go`**: | ||
| * **`SendA2UIToClientToolset`**: Manages the A2UI toolset lifecycle. | ||
| * **`SendA2UIJsonToClientTool`**: A tool exposed to the LLM that validates | ||
| generated JSON against the provided schema using | ||
| `github.com/santhosh-tekuri/jsonschema/v5` and prepares it for sending. | ||
| * **`ConvertSendA2UIToClientGenAIPartToA2APart`**: A helper to convert LLM | ||
| tool responses into A2A `Part`s. | ||
|
|
||
| ## Dependencies | ||
|
|
||
| * **A2A Protocol**: Uses the [A2A Go SDK](https://github.com/a2aproject/a2a-go) | ||
| for core definitions (`Part`, `DataPart`, `AgentExtension`, etc.). | ||
| * **JSON Schema Validation**: Uses `github.com/santhosh-tekuri/jsonschema/v5` | ||
| for robust runtime validation of agent-generated UI. | ||
|
|
||
| ## Usage | ||
|
|
||
| ### Initializing the Toolset | ||
|
|
||
| ```go | ||
| import ( | ||
| "github.com/google/A2UI/a2a_agents/go/a2ui" | ||
| // ... other imports | ||
| ) | ||
|
|
||
| // Define a provider for your A2UI schema (e.g., loaded from a file) | ||
| schemaProvider := func (ctx context.Context) (map[string]interface{}, error) { | ||
| return loadMySchema(), nil | ||
| } | ||
|
|
||
| // Check if A2UI should be enabled for this request | ||
| enabledProvider := func (ctx context.Context) (bool, error) { | ||
| // Logic to check if the client supports A2UI (e.g., checking requested extensions) | ||
| return a2ui.TryActivateA2UIExtension(ctx), nil | ||
| } | ||
|
|
||
| // Create the toolset | ||
| toolset := a2ui.NewSendA2UIToClientToolset( | ||
| a2ui.A2UIEnabledProvider(enabledProvider), | ||
| a2ui.A2UISchemaProvider(schemaProvider), | ||
| ) | ||
|
|
||
| // Get the tools to register with your LLM agent | ||
| tools, err := toolset.GetTools(ctx) | ||
| if err != nil { | ||
| // handle error | ||
| } | ||
| ``` | ||
|
|
||
| ## Building the SDK | ||
|
|
||
| To build the SDK, run the following command from the `a2a_agents/go` directory: | ||
|
|
||
| ```bash | ||
| go build ./a2ui | ||
| ``` | ||
|
|
||
| This will compile the `a2ui` package and report any syntax or dependency errors. | ||
| Since this is a library, it will not produce an executable binary. | ||
|
|
||
| ## Running Tests | ||
|
|
||
| To run the test suite from the `a2a_agents/go` directory: | ||
|
|
||
| ```bash | ||
| go test -v ./a2ui | ||
| ``` | ||
|
|
||
| ## Disclaimer | ||
|
|
||
| Important: The sample code provided is for demonstration purposes and | ||
| illustrates the mechanics of A2UI and the Agent-to-Agent (A2A) protocol. When | ||
| building production applications, it is critical to treat any agent operating | ||
| outside of your direct control as a potentially untrusted entity. | ||
|
|
||
| All operational data received from an external agent—including its AgentCard, | ||
| messages, artifacts, and task statuses—should be handled as untrusted input. For | ||
| example, a malicious agent could provide crafted data in its fields (e.g., name, | ||
| skills.description) that, if used without sanitization to construct prompts for | ||
| a Large Language Model (LLM), could expose your application to prompt injection | ||
| attacks. | ||
|
|
||
| Similarly, any UI definition or data stream received must be treated as | ||
| untrusted. Malicious agents could attempt to spoof legitimate interfaces to | ||
| deceive users (phishing), inject malicious scripts via property values (XSS), or | ||
| generate excessive layout complexity to degrade client performance (DoS). If | ||
| your application supports optional embedded content (such as iframes or web | ||
| views), additional care must be taken to prevent exposure to malicious external | ||
| sites. | ||
|
|
||
| Developer Responsibility: Failure to properly validate data and strictly sandbox | ||
| rendered content can introduce severe vulnerabilities. Developers are | ||
| responsible for implementing appropriate security measures—such as input | ||
| sanitization, Content Security Policies (CSP), strict isolation for optional | ||
| embedded content, and secure credential handling—to protect their systems and | ||
| users. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,123 @@ | ||
| package a2ui | ||
|
|
||
| // Copyright 2026 Google LLC | ||
| // | ||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||
| // you may not use this file except in compliance with the License. | ||
| // You may obtain a copy of the License at | ||
| // | ||
| // https://www.apache.org/licenses/LICENSE-2.0 | ||
| // | ||
| // Unless required by applicable law or agreed to in writing, software | ||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| // See the License for the specific language governing permissions and | ||
| // limitations under the License. | ||
|
|
||
| import ( | ||
| "context" | ||
| "fmt" | ||
| "log" | ||
|
|
||
| "github.com/a2aproject/a2a-go/a2a" | ||
| "github.com/a2aproject/a2a-go/a2asrv" | ||
| ) | ||
|
|
||
| const ( | ||
| // ExtensionURI is the URI for the A2UI extension. | ||
| ExtensionURI = "https://a2ui.org/a2a-extension/a2ui/v0.8" | ||
|
|
||
| // MIMETypeKey is the key for the MIME type in metadata. | ||
| MIMETypeKey = "mimeType" | ||
| // MIMEType is the MIME type for A2UI data. | ||
| MIMEType = "application/json+a2ui" | ||
|
|
||
| // ClientCapabilitiesKey is the key for A2UI client capabilities. | ||
| ClientCapabilitiesKey = "a2uiClientCapabilities" | ||
| // SupportedCatalogIDsKey is the key for supported catalog IDs. | ||
| SupportedCatalogIDsKey = "supportedCatalogIds" | ||
| // InlineCatalogsKey is the key for inline catalogs. | ||
| InlineCatalogsKey = "inlineCatalogs" | ||
|
|
||
| // StandardCatalogID is the ID for the standard catalog. | ||
| StandardCatalogID = "https://github.com/google/A2UI/blob/main/specification/v0_8/json/standard_catalog_definition.json" | ||
|
|
||
| // AgentExtensionSupportedCatalogIDsKey is the parameter key for supported catalogs in the agent extension. | ||
| AgentExtensionSupportedCatalogIDsKey = "supportedCatalogIds" | ||
| // AgentExtensionAcceptsInlineCatalogsKey is the parameter key for accepting inline catalogs. | ||
| AgentExtensionAcceptsInlineCatalogsKey = "acceptsInlineCatalogs" | ||
| ) | ||
|
|
||
| // CreateA2UIPart creates an A2A Part containing A2UI data. | ||
| func CreateA2UIPart(a2uiData map[string]interface{}) a2a.Part { | ||
| return &a2a.DataPart{ | ||
| Data: a2uiData, | ||
| Metadata: map[string]interface{}{ | ||
| MIMETypeKey: MIMEType, | ||
| }, | ||
| } | ||
| } | ||
|
|
||
| // IsA2UIPart checks if an A2A Part contains A2UI data. | ||
| func IsA2UIPart(part a2a.Part) bool { | ||
| dp, ok := part.(*a2a.DataPart) | ||
| if !ok { | ||
| return false | ||
| } | ||
| if dp.Metadata == nil { | ||
| return false | ||
| } | ||
| return dp.Metadata[MIMETypeKey] == MIMEType | ||
| } | ||
|
|
||
| // GetA2UIDataPart extracts the DataPart containing A2UI data from an A2A Part, if present. | ||
| func GetA2UIDataPart(part a2a.Part) (*a2a.DataPart, error) { | ||
| if IsA2UIPart(part) { | ||
| return part.(*a2a.DataPart), nil | ||
| } | ||
| return nil, fmt.Errorf("part is not an A2UI part") | ||
| } | ||
|
|
||
| // GetA2UIAgentExtension creates the A2UI AgentExtension configuration. | ||
| func GetA2UIAgentExtension(acceptsInlineCatalogs bool, supportedCatalogIDs []string) *a2a.AgentExtension { | ||
| params := make(map[string]interface{}) | ||
|
|
||
| if acceptsInlineCatalogs { | ||
| params[AgentExtensionAcceptsInlineCatalogsKey] = true | ||
| } | ||
|
|
||
| if len(supportedCatalogIDs) > 0 { | ||
| params[AgentExtensionSupportedCatalogIDsKey] = supportedCatalogIDs | ||
| } | ||
|
|
||
| var paramsOrNil map[string]interface{} | ||
| if len(params) > 0 { | ||
| paramsOrNil = params | ||
| } | ||
|
|
||
| return &a2a.AgentExtension{ | ||
| URI: ExtensionURI, | ||
| Description: "Provides agent driven UI using the A2UI JSON format.", | ||
| Params: paramsOrNil, | ||
| } | ||
| } | ||
|
|
||
| // TryActivateA2UIExtension activates the A2UI extension if requested. | ||
| func TryActivateA2UIExtension(ctx context.Context) bool { | ||
| exts, ok := a2asrv.ExtensionsFrom(ctx) | ||
| if !ok { | ||
| log.Println("TryActivateA2UIExtension: No extensions found in context") | ||
| return false | ||
| } | ||
|
|
||
| a2uiExt := &a2a.AgentExtension{URI: ExtensionURI} | ||
| requested := exts.Requested(a2uiExt) | ||
|
|
||
| log.Printf("TryActivateA2UIExtension: Checking URI %s. Requested: %v", ExtensionURI, requested) | ||
|
|
||
| if requested { | ||
| exts.Activate(a2uiExt) | ||
| return true | ||
| } | ||
| return false | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.