Skip to content

Commit 9b74cda

Browse files
committed
chore: decouple schema from models
1 parent 4479461 commit 9b74cda

11 files changed

Lines changed: 111 additions & 94 deletions

File tree

src/Agents/Adapter.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,13 @@ abstract public function getModel(): string;
8888
*/
8989
abstract public function setModel(string $model): self;
9090

91+
/**
92+
* Check if the model supports JSON schema
93+
*
94+
* @return bool
95+
*/
96+
abstract public function isSchemaSupported(): bool;
97+
9198
/**
9299
* Get the current agent
93100
*

src/Agents/Adapters/Anthropic.php

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,16 @@ public function __construct(
8686
$this->setModel($model);
8787
}
8888

89+
/**
90+
* Check if the model supports JSON schema
91+
*
92+
* @return bool
93+
*/
94+
public function isSchemaSupported(): bool
95+
{
96+
return true;
97+
}
98+
8999
/**
90100
* Send a message to the Anthropic API
91101
*
@@ -155,7 +165,15 @@ public function send(array $messages, ?callable $listener = null): Message
155165

156166
if (isset($schema)) {
157167
$payload['tools'] = [
158-
$schema->toSchema(Schema::MODEL_ANTHROPIC),
168+
[
169+
'name' => $schema->getName(),
170+
'description' => $schema->getDescription(),
171+
'input_schema' => [
172+
'type' => 'object',
173+
'properties' => $schema->getProperties(),
174+
'required' => $schema->getRequired(),
175+
],
176+
],
159177
];
160178
$payload['tool_choice'] = [
161179
'type' => 'tool',

src/Agents/Adapters/Deepseek.php

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,16 @@ public function __construct(
7070
$this->setModel($model);
7171
}
7272

73+
/**
74+
* Check if the model supports JSON schema
75+
*
76+
* @return bool
77+
*/
78+
public function isSchemaSupported(): bool
79+
{
80+
return true;
81+
}
82+
7383
/**
7484
* Send a message to the Deepseek API
7585
*
@@ -111,7 +121,7 @@ public function send(array $messages, ?callable $listener = null): Message
111121

112122
$schema = $this->getAgent()->getSchema();
113123
if ($schema !== null) {
114-
$systemMessage .= "\n\n"."USE THE JSON SCHEMA BELOW TO GENERATE A VALID JSON RESPONSE: \n".json_encode($schema->toJson());
124+
$systemMessage .= "\n\n"."USE THE JSON SCHEMA BELOW TO GENERATE A VALID JSON RESPONSE: \n".$schema->toJson();
115125
}
116126

117127
if (! empty($systemMessage)) {

src/Agents/Adapters/Gemini.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,16 @@ public function __construct(
9393
$this->setModel($model);
9494
}
9595

96+
/**
97+
* Check if the model supports JSON schema
98+
*
99+
* @return bool
100+
*/
101+
public function isSchemaSupported(): bool
102+
{
103+
return false;
104+
}
105+
96106
/**
97107
* Send a message to the API
98108
*

src/Agents/Adapters/OpenAI.php

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,16 @@ public function __construct(
104104
$this->setModel($model);
105105
}
106106

107+
/**
108+
* Check if the model supports JSON schema
109+
*
110+
* @return bool
111+
*/
112+
public function isSchemaSupported(): bool
113+
{
114+
return true;
115+
}
116+
107117
/**
108118
* Send a message to the API
109119
*
@@ -161,7 +171,16 @@ public function send(array $messages, ?callable $listener = null): Message
161171
if ($schema !== null) {
162172
$payload['response_format'] = [
163173
'type' => 'json_schema',
164-
'json_schema' => $schema->toSchema(Schema::MODEL_OPENAI),
174+
'json_schema' => [
175+
'name' => $schema->getName(),
176+
'strict' => true,
177+
'schema' => [
178+
'type' => 'object',
179+
'properties' => $schema->getProperties(),
180+
'required' => $schema->getRequired(),
181+
'additionalProperties' => false,
182+
],
183+
],
165184
];
166185
$payload['stream'] = false;
167186
} else {

src/Agents/Adapters/Perplexity.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,16 @@ public function __construct(
6666
);
6767
}
6868

69+
/**
70+
* Check if the model supports JSON schema
71+
*
72+
* @return bool
73+
*/
74+
public function isSchemaSupported(): bool
75+
{
76+
return false;
77+
}
78+
6979
/**
7080
* Get available models
7181
*

src/Agents/Adapters/XAI.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,16 @@ public function __construct(
4949
);
5050
}
5151

52+
/**
53+
* Check if the model supports JSON schema
54+
*
55+
* @return bool
56+
*/
57+
public function isSchemaSupported(): bool
58+
{
59+
return false;
60+
}
61+
5262
/**
5363
* Get available models
5464
*

src/Agents/Agent.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,10 @@ public function getSchema(): ?Schema
128128
*/
129129
public function setSchema(Schema $schema): self
130130
{
131+
if (! $this->adapter->isSchemaSupported()) {
132+
throw new \Exception('Schema is not supported for this model');
133+
}
134+
131135
$this->schema = $schema;
132136

133137
return $this;

src/Agents/Schema.php

Lines changed: 9 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,6 @@
66

77
class Schema
88
{
9-
public const MODEL_OPENAI = 'openai';
10-
11-
public const MODEL_ANTHROPIC = 'anthropic';
12-
139
protected string $name;
1410

1511
protected string $description;
@@ -69,49 +65,19 @@ public function getRequired(): array
6965
}
7066

7167
/**
72-
* Convert the schema parameters to a JSON Schema object
73-
*
74-
* @param string $model - the model to use (anthropic, openai) default is openai
7568
* @return array<string, mixed>
7669
*/
77-
public function toSchema(string $model = self::MODEL_OPENAI): array
70+
public function getProperties(): array
7871
{
79-
if (! in_array($model, $this->getValidModels())) {
80-
throw new \InvalidArgumentException(
81-
'Invalid model selected. Must be one of: '.implode(', ', $this->getValidModels())
82-
);
83-
}
84-
85-
if ($model === self::MODEL_ANTHROPIC) {
86-
return [
87-
'name' => $this->name,
88-
'description' => $this->description,
89-
'input_schema' => [
90-
'type' => 'object',
91-
'properties' => $this->object->getProperties(),
92-
'required' => $this->required,
93-
],
94-
];
95-
}
96-
97-
return [
98-
'name' => $this->name,
99-
'strict' => true,
100-
'schema' => [
101-
'type' => 'object',
102-
'properties' => $this->object->getProperties(),
103-
'required' => $this->required,
104-
'additionalProperties' => false,
105-
],
106-
];
72+
return $this->object->getProperties();
10773
}
10874

10975
/**
110-
* Convert the schema parameters to a simple JSON object
76+
* Convert the schema parameters to a simple JSON string
11177
*
112-
* @return array<string, string>
78+
* @return string
11379
*/
114-
public function toJson(): array
80+
public function toJson(): string
11581
{
11682
$json = [];
11783
foreach ($this->object->getProperties() as $property => $value) {
@@ -120,19 +86,10 @@ public function toJson(): array
12086
$json[$property] = $description.' ('.$type.')';
12187
}
12288

123-
return $json;
124-
}
89+
if (! json_encode($json)) {
90+
throw new \Exception('Invalid JSON: '.json_last_error_msg());
91+
}
12592

126-
/**
127-
* Returns an array of valid models
128-
*
129-
* @return array<int, string>
130-
*/
131-
public function getValidModels(): array
132-
{
133-
return [
134-
self::MODEL_OPENAI,
135-
self::MODEL_ANTHROPIC,
136-
];
93+
return json_encode($json);
13794
}
13895
}

tests/Agents/Conversation/ConversationBase.php

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,6 @@
44

55
use PHPUnit\Framework\TestCase;
66
use Utopia\Agents\Adapter;
7-
use Utopia\Agents\Adapters\Gemini;
8-
use Utopia\Agents\Adapters\Perplexity;
9-
use Utopia\Agents\Adapters\XAI;
107
use Utopia\Agents\Agent;
118
use Utopia\Agents\Conversation;
129
use Utopia\Agents\Messages\Text;
@@ -125,11 +122,7 @@ public function testSend(): void
125122

126123
public function testSchema(): void
127124
{
128-
if (
129-
$this->adapter->getModel() === GEMINI::MODEL_GEMINI_2_0_FLASH_LITE ||
130-
$this->adapter->getModel() === PERPLEXITY::MODEL_SONAR ||
131-
$this->adapter->getModel() === XAI::MODEL_GROK_2_LATEST
132-
) {
125+
if (! $this->adapter->isSchemaSupported()) {
133126
$this->markTestSkipped('Structured output hasn\'t been implemented for this model');
134127
}
135128

0 commit comments

Comments
 (0)