Skip to content

Commit ef85cb7

Browse files
committed
Merge remote-tracking branch 'upstream/main' into delete-minimal-catalog
2 parents a19dc58 + 8e4b5c0 commit ef85cb7

63 files changed

Lines changed: 246 additions & 219 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/PULL_REQUEST_TEMPLATE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,5 @@ If you need help, consider asking for advice on the [discussion board].
2121
[CHANGELOG]: ../CHANGELOG.md
2222
[CLA]: https://cla.developers.google.com/
2323
[Contributors Guide]: ../CONTRIBUTING.md
24-
[discussion board]: https://github.com/google/A2UI/discussions
24+
[discussion board]: https://github.com/a2ui-project/a2ui/discussions
2525
[Style Guide]: ../CONTRIBUTING.md#coding-style

.github/workflows/docs.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ jobs:
3939
contents: write
4040
actions: read
4141

42-
if: github.repository == 'google/A2UI'
42+
if: github.repository == 'a2ui-project/a2ui'
4343

4444
steps:
4545
- name: Checkout Code

.github/workflows/e2e_test.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515
e2e_test:
1616
# Do not run on forked branches,
1717
# because the test does not have access to secrets in forks.
18-
if: github.repository == 'google/a2ui'
18+
if: github.repository == 'a2ui-project/a2ui'
1919
runs-on: ubuntu-latest
2020
permissions:
2121
contents: read

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,8 +114,8 @@ Pick the path that matches where you want to start:
114114
Prerequisites: Node.js 18+, [uv](https://docs.astral.sh/uv/), and a [Gemini API key](https://aistudio.google.com/apikey).
115115

116116
```bash
117-
git clone https://github.com/google/A2UI.git
118-
cd A2UI
117+
git clone https://github.com/a2ui-project/a2ui.git
118+
cd a2ui
119119
export GEMINI_API_KEY="your_gemini_api_key"
120120
cd samples/client/lit
121121
npm run demo:restaurant

agent_sdks/agent_sdk_guide.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ Create the helper utilities to wrap JSON in transport Parts (if needed for your
262262

263263
### Step 5: Sample Applications
264264

265-
Create a simple sample (like a command-line agent or local server) to verify that the SDK works end-to-end. Refer to the reference Python samples (e.g., `samples/agent/adk/contact_lookup`) for inspiration.
265+
Create a simple sample (like a command-line agent or local server) to verify that the SDK works end-to-end. Refer to the reference Python samples (e.g., `samples/agent/adk/restaurant_finder`) for inspiration.
266266

267267
> [!IMPORTANT]
268268
> Keep the SDK idiomatic to your language. Don't force Python-isms if it doesn't make sense (e.g., use builder patterns in Java/Kotlin or macros in C++ if they are more ergonomic).

agent_sdks/kotlin/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ repositories {
3939

4040
dependencies {
4141
api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3")
42-
implementation("com.networknt:json-schema-validator:1.5.1")
42+
implementation("com.networknt:json-schema-validator:2.0.1")
4343
implementation("com.fasterxml.jackson.core:jackson-databind:2.17.2")
4444

4545
// Core Dependencies

agent_sdks/kotlin/src/main/kotlin/com/google/a2ui/schema/Validator.kt

Lines changed: 32 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@
1616

1717
package com.google.a2ui.schema
1818

19-
import com.fasterxml.jackson.databind.ObjectMapper
20-
import com.networknt.schema.JsonSchema
21-
import com.networknt.schema.JsonSchemaFactory
22-
import com.networknt.schema.SchemaValidatorsConfig
23-
import com.networknt.schema.SpecVersion
19+
import com.networknt.schema.InputFormat
20+
import com.networknt.schema.Schema
21+
import com.networknt.schema.SchemaRegistry
22+
import com.networknt.schema.SchemaRegistryConfig
23+
import com.networknt.schema.dialect.Dialects
2424
import kotlinx.serialization.json.Json
2525
import kotlinx.serialization.json.JsonArray
2626
import kotlinx.serialization.json.JsonElement
@@ -44,21 +44,18 @@ constructor(
4444
private val catalog: A2uiCatalog,
4545
private val schemaMappings: Map<String, String> = emptyMap(),
4646
) {
47-
private val validator: JsonSchema = buildValidator()
48-
private val mapper = ObjectMapper()
49-
private val shared0_9Factory: JsonSchemaFactory by lazy {
50-
JsonSchemaFactory.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V202012))
51-
.schemaMappers { schemaMappers ->
52-
schemaMappings.forEach { (prefix, target) -> schemaMappers.mapPrefix(prefix, target) }
47+
private val shared0_9Registry: SchemaRegistry by lazy {
48+
SchemaRegistry.withDialect(Dialects.getDraft202012()) { builder ->
49+
builder.schemaIdResolvers { schemaIdResolvers ->
50+
schemaMappings.forEach { (prefix, target) -> schemaIdResolvers.mapPrefix(prefix, target) }
5351
}
54-
.build()
55-
}
56-
private val sharedConfig: SchemaValidatorsConfig by lazy {
57-
SchemaValidatorsConfig.builder().build()
52+
}
5853
}
59-
private val subValidators = mutableMapOf<String, JsonSchema>()
54+
private val sharedConfig: SchemaRegistryConfig by lazy { SchemaRegistryConfig.builder().build() }
55+
private val subValidators = mutableMapOf<String, Schema>()
56+
private val validator: Schema = buildValidator()
6057

61-
private fun buildValidator(): JsonSchema =
58+
private fun buildValidator(): Schema =
6259
if (catalog.version == A2uiVersion.VERSION_0_8) build0_8Validator() else build0_9Validator()
6360

6461
private fun injectAdditionalProperties(
@@ -121,7 +118,7 @@ constructor(
121118
return bundled as JsonObject
122119
}
123120

124-
private fun build0_8Validator(): JsonSchema {
121+
private fun build0_8Validator(): Schema {
125122
val bundledSchema = bundle0_8Schemas()
126123
val fullSchema = SchemaResourceLoader.wrapAsJsonArray(bundledSchema).toMutableMap()
127124
fullSchema[KEY_DOLLAR_SCHEMA] = JsonPrimitive(SCHEMA_DRAFT_2020_12)
@@ -132,38 +129,29 @@ constructor(
132129
val baseDirUri = baseUri.substringBeforeLast("/")
133130
val commonTypesUri = "$baseDirUri/$FILE_COMMON_TYPES"
134131

135-
val config = SchemaValidatorsConfig.builder().build()
136-
137132
val jsonFmt = Json { prettyPrint = false }
138133

139-
val factory =
140-
JsonSchemaFactory.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V202012))
141-
.schemaMappers { schemaMappers ->
142-
schemaMappings.forEach { (prefix, target) -> schemaMappers.mapPrefix(prefix, target) }
143-
schemaMappers.mapPrefix(FILE_COMMON_TYPES, commonTypesUri)
134+
val registry =
135+
SchemaRegistry.withDialect(Dialects.getDraft202012()) { builder ->
136+
builder.schemaIdResolvers { schemaIdResolvers ->
137+
schemaMappings.forEach { (prefix, target) -> schemaIdResolvers.mapPrefix(prefix, target) }
138+
schemaIdResolvers.mapPrefix(FILE_COMMON_TYPES, commonTypesUri)
144139
}
145-
.build()
140+
builder.schemaRegistryConfig(sharedConfig)
141+
}
146142

147143
val schemaString = jsonFmt.encodeToString(JsonElement.serializer(), JsonObject(fullSchema))
148-
return factory.getSchema(schemaString, config)
144+
return registry.getSchema(schemaString, InputFormat.JSON)
149145
}
150146

151-
private fun build0_9Validator(): JsonSchema {
147+
private fun build0_9Validator(): Schema {
152148
val fullSchema =
153149
SchemaResourceLoader.wrapAsJsonArray(catalog.serverToClientSchema).toMutableMap()
154150
fullSchema[KEY_DOLLAR_SCHEMA] = JsonPrimitive(SCHEMA_DRAFT_2020_12)
155151

156-
val config = SchemaValidatorsConfig.builder().build()
157-
val factory =
158-
JsonSchemaFactory.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V202012))
159-
.schemaMappers { schemaMappers ->
160-
schemaMappings.forEach { (prefix, target) -> schemaMappers.mapPrefix(prefix, target) }
161-
}
162-
.build()
163-
164152
val jsonFmt = Json { prettyPrint = false }
165153
val schemaString = jsonFmt.encodeToString(JsonElement.serializer(), JsonObject(fullSchema))
166-
return factory.getSchema(schemaString, config)
154+
return shared0_9Registry.getSchema(schemaString, InputFormat.JSON)
167155
}
168156

169157
/**
@@ -185,9 +173,8 @@ constructor(
185173
// Basic schema validation
186174
val jsonFmt = Json { prettyPrint = false }
187175
val messagesString = jsonFmt.encodeToString(JsonElement.serializer(), messages)
188-
val jsonNode = mapper.readTree(messagesString)
189176

190-
val errors = validator.validate(jsonNode)
177+
val errors = validator.validate(messagesString, InputFormat.JSON)
191178
if (errors.isNotEmpty()) {
192179
val msg = buildString {
193180
append("Validation failed:")
@@ -290,7 +277,7 @@ constructor(
290277
}
291278
}
292279

293-
private fun getSubValidator(defName: String): JsonSchema {
280+
private fun getSubValidator(defName: String): Schema {
294281
return subValidators.getOrPut(defName) {
295282
val defs =
296283
catalog.serverToClientSchema["\$defs"] as? JsonObject
@@ -310,22 +297,21 @@ constructor(
310297

311298
val jsonFmt = Json { prettyPrint = false }
312299
val schemaString = jsonFmt.encodeToString(JsonElement.serializer(), tempSchema)
313-
shared0_9Factory.getSchema(schemaString, sharedConfig)
300+
shared0_9Registry.getSchema(schemaString, InputFormat.JSON)
314301
}
315302
}
316303

317304
private fun getFormattedErrors(
318-
validator: JsonSchema,
305+
validator: Schema,
319306
instance: JsonElement,
320307
basePath: String,
321308
): List<String> {
322309
val jsonFmt = Json { prettyPrint = false }
323310
val instanceStr = jsonFmt.encodeToString(JsonElement.serializer(), instance)
324-
val jsonNode = mapper.readTree(instanceStr)
325-
val errors = validator.validate(jsonNode)
311+
val errors = validator.validate(instanceStr, InputFormat.JSON)
326312

327313
return errors.map { err ->
328-
val msg = err.message ?: ""
314+
val msg = err.toString()
329315
val unexpectedRegex =
330316
Regex(
331317
"property '(.*?)' is not defined in the schema and the schema does not allow additional properties"
@@ -419,7 +405,7 @@ constructor(
419405
)
420406
val jsonFmt = Json { prettyPrint = false }
421407
val schemaString = jsonFmt.encodeToString(JsonElement.serializer(), tempSchema)
422-
shared0_9Factory.getSchema(schemaString, sharedConfig)
408+
shared0_9Registry.getSchema(schemaString, InputFormat.JSON)
423409
}
424410

425411
return getFormattedErrors(validator, comp, path)

agent_sdks/python/README.md

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -77,21 +77,7 @@ uv run pyink .
7777

7878
## Releasing the SDK
7979

80-
To release a new version of the SDK, follow these steps:
81-
82-
1. Update the version in `src/a2ui/version.py`.
83-
2. Run the release script from the `agent_sdks/python` directory:
84-
85-
```bash
86-
./release.sh
87-
```
88-
89-
The script will build the package, upload it to the Artifact Registry, and trigger the release pipeline.
90-
91-
## Tracking the release
92-
93-
1. Check the release status through Sponge: go/spng2?q=PROJECT%3Aa2ui%2Fpypi
94-
2. You will see logs for the jobs involved in your release. A successful release is confirmed when the final job, named "publisher", completes successfully.
80+
See internal guidance at go/a2ui-release-pipy.
9581

9682
## Disclaimer
9783

agent_sdks/python/src/a2ui/adk/a2a/part_converter.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,17 +55,25 @@ class A2uiPartConverter:
5555
This converter handles both tool-based A2UI (via `send_a2ui_json_to_client`)
5656
and text-based A2UI (via A2UI delimiter tags). It uses the provided
5757
catalog to validate and fix JSON payloads.
58+
59+
Args:
60+
a2ui_catalog: The A2UI catalog.
61+
bypass_tool_check: If True, bypass tool validation.
62+
fallback_text: Optional text to fall back on if parsing fails.
63+
version: The A2UI version to use. Defaults to "0.8".
5864
"""
5965

6066
def __init__(
6167
self,
6268
a2ui_catalog: A2uiCatalog,
6369
bypass_tool_check: bool = False,
6470
fallback_text: Optional[str] = None,
71+
version: str = constants.VERSION_0_8,
6572
):
6673
self._catalog = a2ui_catalog
6774
self._bypass_tool_check = bypass_tool_check
6875
self._fallback_text = fallback_text
76+
self._version = version
6977

7078
def convert(self, part: genai_types.Part) -> list[a2a_types.Part]:
7179
"""Converts a GenAI part to A2A parts, with A2UI validation.
@@ -97,7 +105,10 @@ def convert(self, part: genai_types.Part) -> list[a2a_types.Part]:
97105
):
98106
json_data = response_dict.get(constants.A2UI_VALIDATED_JSON_KEY)
99107
if json_data:
100-
return [create_a2ui_part(message) for message in json_data]
108+
return [
109+
create_a2ui_part(message, version=self._version)
110+
for message in json_data
111+
]
101112

102113
if is_send_a2ui_json_to_client_response:
103114
logger.info("No result in A2UI tool response")
@@ -111,6 +122,7 @@ def convert(self, part: genai_types.Part) -> list[a2a_types.Part]:
111122
result,
112123
validator=self._catalog.validator,
113124
fallback_text=self._fallback_text,
125+
version=self._version,
114126
)
115127

116128
# 2. Handle Tool Calls (FunctionCall) - Skip sending to client
@@ -126,6 +138,7 @@ def convert(self, part: genai_types.Part) -> list[a2a_types.Part]:
126138
text,
127139
validator=self._catalog.validator,
128140
fallback_text=self._fallback_text,
141+
version=self._version,
129142
)
130143

131144
# 4. Default conversion for other parts

agent_sdks/python/src/a2ui/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,4 @@
2121
# Switch to VCS-based versioning which derives version
2222
# from Git tags automatically with the hatch-vcs plugin
2323
# when we start using Git tags.
24-
__version__ = "0.2.2"
24+
__version__ = "0.2.4"

0 commit comments

Comments
 (0)