Skip to content

feat(ai+interop): AIFunction schema auto-generation + A2A protocol adapter#126

Open
eanzhao wants to merge 9 commits intodevfrom
feature/maf-update
Open

feat(ai+interop): AIFunction schema auto-generation + A2A protocol adapter#126
eanzhao wants to merge 9 commits intodevfrom
feature/maf-update

Conversation

@eanzhao
Copy link
Copy Markdown
Contributor

@eanzhao eanzhao commented Apr 6, 2026

Summary

Issue #98: AIFunction Schema Auto-Generation

  • AgentToolSchemaGenerator — auto-generate JSON Schema from C# types using .NET JsonSchemaExporter
  • AgentToolBase<TParams> — typed base class with auto-derived schema and typed ExecuteAsync
  • LLMResponseFormat (Text / JsonObject / JsonSchema) with ForJsonSchema<T>() for structured output
  • ResponseFormat propagated through all internal request rebuild paths

Issue #99: A2A Protocol Adapter Layer

  • Three-layer architecture: Abstractions, Application, Hosting
  • A2AAdapterService converts A2A JSON-RPC to IActorDispatchPort.DispatchAsync via EventEnvelope
  • HTTP endpoints: /.well-known/agent.json, /a2a (JSON-RPC), /a2a/subscribe/{taskId} (SSE)
  • InMemoryA2ATaskStore with interface for future persistence

Test plan

  • AgentToolSchemaGeneratorTests — 10 tests
  • AgentToolBaseTests — 8 tests
  • LLMResponseFormatTests — 6 tests
  • InMemoryA2ATaskStoreTests — 8 tests
  • A2AAdapterServiceTests — 12 tests
  • JsonRpcModelTests — 5 tests
  • dotnet build aevatar.slnx — 0 errors
  • All 49 tests pass

Closes #98
Closes #99

🤖 Generated with Claude Code

eanzhao and others added 2 commits April 6, 2026 23:20
… support

Introduce AgentToolSchemaGenerator, AgentToolBase<TParams>, and LLMResponseFormat
to eliminate hand-written JSON Schema maintenance and enable structured LLM output.

Closes #98

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Propagate ResponseFormat in TornadoLLMProvider request rebuild
- Add ConcurrentDictionary caching to AgentToolSchemaGenerator
- Fix JSON injection in AgentToolBase error path
- Revert unintentional global.json SDK version change

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ea0a0eeab9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

string? schemaDescription = null) =>
new LLMResponseFormatJsonSchema(
AgentToolSchemaGenerator.GenerateSchema<T>(),
schemaName ?? typeof(T).Name,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Sanitize inferred schema names for generic formats

ForJsonSchema<T>() uses typeof(T).Name directly, which produces invalid identifiers for common CLR types (for example generic types become names like MyType\1). That value is then forwarded unchanged into ChatResponseFormat.ForJsonSchema(...), so structured-output requests can fail for these types when providers validate schema names. Please sanitize/normalize the inferred name (or pass nullto let the downstream layer infer safely) before constructingLLMResponseFormatJsonSchema`.

Useful? React with 👍 / 👎.

string? schemaDescription = null)
{
Kind = LLMResponseFormatKind.JsonSchema;
Schema = schema;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Clone JsonElement schema before storing it

The constructor stores the incoming JsonElement by reference, so callers who pass JsonDocument.Parse(...).RootElement without Clone() end up with a schema tied to a disposed document. When the provider later reads Schema, this can throw ObjectDisposedException at runtime. Clone the element at assignment time to decouple LLMResponseFormatJsonSchema from caller document lifetimes.

Useful? React with 👍 / 👎.

…nt interoperability

Implement the A2A (Agent-to-Agent) protocol adapter with three-layer architecture:
- Abstractions: A2A protocol types (AgentCard, A2ATask, Message, Part, JSON-RPC)
- Application: A2AAdapterService (protocol conversion) + InMemoryA2ATaskStore
- Hosting: HTTP endpoints (/.well-known/agent.json, /a2a JSON-RPC, /a2a/subscribe SSE)

Closes #99

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@eanzhao eanzhao changed the title feat(ai): AIFunction schema auto-generation and structured output feat(ai+interop): AIFunction schema auto-generation + A2A protocol adapter Apr 6, 2026
eanzhao and others added 6 commits April 6, 2026 23:54
- Replace [JsonDerivedType] with custom PartJsonConverter that uses
  A2A-spec "type" field discriminator for Part deserialization
- Replace SSE polling (Task.Delay) with channel-based subscription
  via IA2ATaskStore.SubscribeAsync / ChannelReader
- Add 6 new tests for Part serialization round-trip and TaskSendParams
  deserialization from JSON-RPC format

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix subscribe-after-terminal race: SubscribeAsync now checks current
  task state under lock and immediately delivers final status + completes
- Fix index-after-removal in NotifySubscribers: separate write and
  complete paths to avoid accessing wrong subscriber after RemoveAt
- Add 3 subscription tests including subscribe-after-terminal-state

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Sanitize generic CLR type names in ForJsonSchema<T>() to avoid
  invalid schema names like MyType`1
- Clone JsonElement in LLMResponseFormatJsonSchema constructor to
  decouple from caller's JsonDocument lifetime

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Resolve leftover merge conflict markers — keep dev-side content with
frontmatter and updated docs paths.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Cover edge cases: SanitizeTypeName generic suffix, concurrent schema caching,
whitespace/extra-property deserialization, multi-text-part joining, negative
historyLength, terminal-state cancel rejection, multiple subscribers, artifact
notifications, JSON-RPC model round-trips, and DataPart serialization.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(interop): A2A Protocol 适配层 — 跨框架 Agent 互操作 feat(ai): AIFunction Schema 自动生成 — 消除 IAgentTool 手写 JSON Schema

1 participant