-
-
Notifications
You must be signed in to change notification settings - Fork 173
Open
Labels
PlatformIssues & PRs about the AI Platform componentIssues & PRs about the AI Platform componentRFCRFC = Request For Comments (proposals about features that you want to be discussed)RFC = Request For Comments (proposals about features that you want to be discussed)
Description
When declaring a tool which takes as argument an interface with a DiscriminatorMap, generated json schema only considers interface properties and doesn't use oneOff to describe all possible implementations.
Right now discriminator map is only supported within array of polymorphic types, see #585
Tool Code
#[AsTool(
name: 'navigator',
description: <<<TEXT
Navigate to various resources
TEXT,
method: 'navigate'
)]
final class ToolNavigator implements ToolInterface
{
public function __construct(
private UrlGeneratorInterface $urlGenerator,
) {}
public function toolName(): string
{
return 'navigator';
}
public function navigate(Filterable $filter): string
{
return match(true) {
$filter instanceof OrderFilter => $this->urlGenerator->generate(
'order_searchresult',
[...$filter->toUrlParameter()],
),
$filter instanceof PurchaseContractFilter => $this->urlGenerator->generate(
'contract_searchresult',
[...$filter->toUrlParameter()],
),
};
}
}Interface
<?php
#[DiscriminatorMap(
typeProperty: 'type',
mapping: [
'order' => OrderFilter::class,
'purchase_contract' => PurchaseContractFilter::class,
]
)]
interface Filterable
{
public string $type {get;}
}Implementations
<?php
final class OrderFilter implements Filterable
{
public function __construct(
#[With(const: 'order')]
public string $type = 'order',
public ?string $number = null,
public ?string $userresponsible = null,
public ?\DateTimeImmutable $departureDate = null,
) {}
}<?php
final class PurchaseContractFilter implements Filterable
{
public function __construct(
#[With(const: 'purchase_contract')]
public string $type = 'purchase_contract',
public ?string $contractNumber = null,
public ?string $subsidiary = null,
) {}
}Note: I tried using union types instead of interface for the tool, the json schema is correctly generated then but since all properties are optional in the implemenatation classes symfony serializer always deserializes tool call results into the first class OrderFilter by creating an empty object instead of actually matching properties.
Code patch
From f6f37e0d8b51aea166b5ebe78aedf60cbac166c6 Mon Sep 17 00:00:00 2001
From: asrar <aszenz@gmail.com>
Date: Tue, 16 Dec 2025 21:15:13 +0100
Subject: [PATCH] Implement DiscriminatorMap handling for polymorphism
Added handling for polymorphic interfaces using DiscriminatorMap attribute in the JSON schema factory.
---
src/platform/src/Contract/JsonSchema/Factory.php | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/src/platform/src/Contract/JsonSchema/Factory.php b/src/platform/src/Contract/JsonSchema/Factory.php
index 776537e4fe..dafd845986 100644
--- a/src/platform/src/Contract/JsonSchema/Factory.php
+++ b/src/platform/src/Contract/JsonSchema/Factory.php
@@ -229,6 +229,19 @@ private function getTypeSchema(Type $type): array
if (\in_array($className, ['DateTime', 'DateTimeImmutable', 'DateTimeInterface'], true)) {
return ['type' => 'string', 'format' => 'date-time'];
} else {
+ // Check for the DiscriminatorMap attribute to handle polymorphic interfaces
+ $discriminatorMapping = $this->findDiscriminatorMapping($className);
+ if ($discriminatorMapping) {
+ $discriminators = [];
+ foreach ($discriminatorMapping as $_ => $discriminator) {
+ $discriminators[] = $this->buildProperties($discriminator);
+ }
+
+ return [
+ 'type' => 'object',
+ 'oneOf' => $discriminators,
+ ];
+ }
// Recursively build the schema for an object type
return $this->buildProperties($className) ?? ['type' => 'object'];
}
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
PlatformIssues & PRs about the AI Platform componentIssues & PRs about the AI Platform componentRFCRFC = Request For Comments (proposals about features that you want to be discussed)RFC = Request For Comments (proposals about features that you want to be discussed)