feat(schema): support open agent discovery under shared base URL via API Catalog#642
feat(schema): support open agent discovery under shared base URL via API Catalog#642ognis1205 wants to merge 3 commits intoa2aproject:mainfrom
Conversation
30ab1a1 to
60ba67b
Compare
|
Awesome!!! |
|
Hey @cdavernas , Thanks for the comment! The schema defined in this PR is implemented to conform to the following RFC and Internet Draft. |
|
The generated By the way, I like the simplicity of this approach. |
|
Hey @darkhaniop, thanks again for the comment and review! I double-checked the JSON Schema spec, and it turns out that by default, all additional properties are allowed—meaning the schema will accept extra fields even without explicitly specifying https://json-schema.org/understanding-json-schema/reference/object#additionalproperties It seems that the presence of That can definitely make the generated schema a bit confusing to read. That said, a couple of points to keep in mind:
What do you think? Let me know if I’m missing anything important. P.S. |
|
@ognis1205, you are right about the current schema output being okay for validation, I did not know that. I just checked the json-schema.org docs you linked, and realized that I was incorrect in thinking that by default any unmatched property is disallowed. From the linked JSON schema docs page:
I think your bullet points raise a valid question about SDKs implementing the interfaces as defined in Pydantic model generation testCode generation testTo check how explicitly stating I used the {
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"WithoutAddProps": {
"properties": {
"href": {"type": "string"}
}
},
"WithAddProps": {
"additionalProperties": true,
"properties": {
"href": {"type": "string"}
}
}
}
}Which results in (omitting the root model): class WithoutAddProps(BaseModel):
href: Optional[str] = None
class WithAddProps(BaseModel):
class Config:
extra = Extra.allow
href: Optional[str] = NoneWhile a link object with extra fields validates successfully against both types, the first variant would lose extra fields on deserialization: link_obj = {"href": "https://example.com", "title": "Example"}
obj_wo_add_props = WithoutAddProps.model_validate(link_obj)
obj_w_add_props = WithAddProps.model_validate(link_obj)
print("Loaded using WithoutAddProps:\n", obj_wo_add_props.model_dump())
print("Loaded using WithAddProps:\n", obj_w_add_props.model_dump())Output: P.S. Although it looks like |
|
Hey @darkhaniop, thank you for sharing your investigation! This really helps! For now, it’s still uncertain whether this feature request itself will be accepted by the community, so I think it might be better to prioritize getting the protocol evaluated by a broader audience—through PR demos including That said, depending on how things develop, the points you raised could become important, so it’s definitely worth keeping them in mind for the future. |
|
I’ve removed the WIP status from this PR due to changes in the repository structure. The related SDK implementation will continue in |
60ba67b to
615e33b
Compare
PoC: SDK Example for Agent Mounting and API Catalog DiscoveryI’ve implemented a proof-of-concept SDK that demonstrates how to define and mount multiple A2A agents using {
"linkset": [
{
"anchor": "http://localhost:9999/a2a/hello/",
"describedby": [
{
"href": "http://localhost:9999/a2a/hello/agent.json",
"type": "application/json"
}
]
},
{
"anchor": "http://localhost:9999/a2a/echo/",
"describedby": [
{
"href": "http://localhost:9999/a2a/echo/agent.json",
"type": "application/json"
}
]
}
]
}Minimal Example Code@click.command()
@click.option('--host', default='localhost')
@click.option('--port', default=9999)
def main(host: str, port: int):
hello_skill = AgentSkill(
id='hello_world',
name='Returns hello world',
description='just returns hello world',
tags=['hello world'],
examples=['hi', 'hello world'],
)
hello_card = AgentCard(
name='Hello World Agent',
description='Just a hello world agent',
url=f'http://{host}:{port}/a2a/hello',
version='1.0.0',
defaultInputModes=['text'],
defaultOutputModes=['text'],
capabilities=AgentCapabilities(streaming=True),
skills=[hello_skill],
supportsAuthenticatedExtendedCard=False,
)
hello_handler = DefaultRequestHandler(
agent_executor=HelloWorldAgentExecutor(),
task_store=InMemoryTaskStore(),
)
hello_agent = A2AStarletteRouteBuilder(
agent_card=hello_card,
http_handler=hello_handler,
)
echo_skill = AgentSkill(
id="echo",
name="Echo input",
description="Returns the input text as is",
tags=["echo"],
examples=["Hello!", "Repeat after me"],
)
echo_card = AgentCard(
name="Echo Agent",
description="An agent that echoes back your input.",
url=f"http://{host}:{port}/a2a/echo",
version="1.0.0",
defaultInputModes=["text"],
defaultOutputModes=["text"],
capabilities=AgentCapabilities(streaming=True),
skills=[echo_skill],
supportsAuthenticatedExtendedCard=False,
)
echo_handler = DefaultRequestHandler(
agent_executor=EchoAgentExecutor(),
task_store=InMemoryTaskStore(),
)
echo_agent = A2AStarletteRouteBuilder(
agent_card=echo_card,
http_handler=echo_handler,
)
server = (
A2AStarletteBuilder()
.mount(hello_agent)
.mount(echo_agent)
.build()
)
uvicorn.run(server, host=host, port=port)
if __name__ == "__main__":
try:
main()
except Exception:
print(traceback.format_exc(), file=sys.stderr)This can serve as a practical SDK example when reviewing this PR. The following is the related PoC implementation of the SDK:
|
615e33b to
10a75d9
Compare
10a75d9 to
b2ea7c0
Compare
82c177a to
746f844
Compare
d25b439 to
33ed3bb
Compare
749d40b to
63940dd
Compare
|
The API Catalog draft is now a finalized RFC https://datatracker.ietf.org/doc/rfc9727/ |
Thanks @Kevsy @darrelmiller for all the hard work in getting this over the line — huge respect for pushing this through the standardization process. I’ll go over the finalized RFC again carefully. Looking forward to seeing it adopted more widely! |
|
Also, if you (and/or the community) think this issue/PR would be better opened or led by you, @darrelmiller, please don’t hesitate to let me know — happy to step back if needed. |
a5497f3 to
6e903e9
Compare
6e903e9 to
a4120fd
Compare
d9d7b62 to
da16733
Compare
da16733 to
5991b2f
Compare
5991b2f to
21c14f8
Compare
21c14f8 to
00f4482
Compare
00f4482 to
b24fd2f
Compare
b24fd2f to
1e02e8a
Compare
1e02e8a to
7441e65
Compare
e5b9f52 to
d075e00
Compare
d075e00 to
95ace5a
Compare
There was a problem hiding this comment.
TODO:
Migrate this definition to the protobuf schema according to
https://github.com/a2aproject/A2A/blob/33f065d3daf6c4bc6f0ada184bab149eadc1985a/specification/json/README.md.
Done.
696401e to
02f23ef
Compare
02f23ef to
df01930
Compare
df01930 to
018233d
Compare
…API Catalog Signed-off-by: Shingo OKAWA <shingo.okawa.g.h.c@gmail.com>
Signed-off-by: Shingo OKAWA <shingo.okawa.g.h.c@gmail.com> chore(proto): disable pluralized-name lint for RFC-defined fields Signed-off-by: Shingo OKAWA <shingo.okawa.g.h.c@gmail.com> chore(proto): disable pluralized-name lint for RFC-defined fields Signed-off-by: Shingo OKAWA <shingo.okawa.g.h.c@gmail.com>
Signed-off-by: Shingo OKAWA <shingo.okawa.g.h.c@gmail.com> fix(docs): replace typographic apostrophe Signed-off-by: Shingo OKAWA <shingo.okawa.g.h.c@gmail.com>
018233d to
5168a75
Compare
Description
Thank you for opening a Pull Request!
Before submitting your PR, there are a few things you can do to make sure it goes smoothly:
CONTRIBUTINGGuide.nox -s formatfrom the repository root to format)Fixes #641 🦕
Points for Review
application/json+agent-cardbe added as an IANA-registered media type?Note: This PR focuses on the schema/spec-level support. A separate PR demonstrating the PoC SDK implementation and demo using this proposal is available at a2a-python #109, and is intended to complement this spec-level change.