-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathclass-wp-agent-conversation-request.php
More file actions
244 lines (208 loc) · 8.17 KB
/
Copy pathclass-wp-agent-conversation-request.php
File metadata and controls
244 lines (208 loc) · 8.17 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
<?php
/**
* Agent conversation runner request contract.
*
* @package AgentsAPI
*/
namespace AgentsAPI\AI;
use AgentsAPI\AI\Tools\WP_Agent_Tool_Declaration;
use AgentsAPI\Core\Workspace\WP_Agent_Workspace_Scope;
defined( 'ABSPATH' ) || exit;
/**
* Neutral request object for conversation runner implementations.
*/
// phpcs:disable WordPress.Security.EscapeOutput.ExceptionNotEscaped -- Validation exceptions are not rendered output.
class WP_Agent_Conversation_Request {
public const DEFAULT_MAX_TURNS = 10;
/** @var array<int, array<string, mixed>> Initial conversation messages. */
private array $messages;
/** @var array<int, array<string, mixed>> Runtime tool declarations available to the run. */
private array $tools;
/** @var WP_Agent_Execution_Principal|null Execution principal for the run. */
private ?WP_Agent_Execution_Principal $principal;
/** @var array<string, mixed> Caller-owned runtime context. */
private array $runtime_context;
/** @var array<string, mixed> Caller-owned metadata. */
private array $metadata;
/** @var int Maximum turn budget requested by caller. */
private int $max_turns;
/** @var bool Whether to stop after one orchestration turn. */
private bool $single_turn;
/** @var WP_Agent_Workspace_Scope|null Workspace scope for persistence/audit adapters. */
private ?WP_Agent_Workspace_Scope $workspace;
/** @var \WP_Agent_Runtime_Overrides Runtime overrides for the request. */
private \WP_Agent_Runtime_Overrides $runtime_overrides;
/**
* @param array<mixed> $messages Initial conversation messages.
* @param array<mixed> $tools Runtime tool declarations available to the run.
* @param WP_Agent_Execution_Principal|null $principal Execution principal for the run.
* @param array<string, mixed> $runtime_context Caller-owned runtime context.
* @param array<string, mixed> $metadata Caller-owned metadata.
* @param int $max_turns Maximum conversation turns.
* @param bool $single_turn Single-turn orchestration flag.
* @param WP_Agent_Workspace_Scope|null $workspace Workspace scope for persistence/audit adapters.
* @param \WP_Agent_Runtime_Overrides|null $runtime_overrides Runtime overrides for the request.
*/
public function __construct(
array $messages,
array $tools,
?WP_Agent_Execution_Principal $principal = null,
array $runtime_context = array(),
array $metadata = array(),
int $max_turns = self::DEFAULT_MAX_TURNS,
bool $single_turn = false,
?WP_Agent_Workspace_Scope $workspace = null,
?\WP_Agent_Runtime_Overrides $runtime_overrides = null
) {
$this->messages = WP_Agent_Message::normalize_many( $messages );
$this->tools = self::normalize_tools( $tools );
$this->principal = $principal;
$this->runtime_overrides = $runtime_overrides ?? new \WP_Agent_Runtime_Overrides();
$this->runtime_context = self::normalize_json_array( $this->apply_runtime_overrides_to_context( $runtime_context ), 'runtime_context' );
$this->metadata = self::normalize_json_array( $metadata, 'metadata' );
$this->max_turns = max( 1, $max_turns );
$this->single_turn = $single_turn;
$this->workspace = $workspace;
}
/** @return array<int, array<string, mixed>> Initial conversation messages. */
public function messages(): array {
return $this->messages;
}
/** @return array<int, array<string, mixed>> Runtime tool declarations available to the run. */
public function tools(): array {
return $this->tools;
}
/** @return WP_Agent_Execution_Principal|null Execution principal for the run. */
public function principal(): ?WP_Agent_Execution_Principal {
return $this->principal;
}
/** @return array<string, mixed> Caller-owned runtime context. */
public function runtimeContext(): array {
return $this->runtime_context;
}
/** @return array<string, mixed> Caller-owned metadata. */
public function metadata(): array {
return $this->metadata;
}
/** @return int Maximum turn budget requested by caller. */
public function maxTurns(): int {
return $this->max_turns;
}
/** @return bool Whether to stop after one orchestration turn. */
public function singleTurn(): bool {
return $this->single_turn;
}
/** @return WP_Agent_Workspace_Scope|null Workspace scope for persistence/audit adapters. */
public function workspace(): ?WP_Agent_Workspace_Scope {
return $this->workspace;
}
/** @return \WP_Agent_Runtime_Overrides Runtime overrides for the request. */
public function runtimeOverrides(): \WP_Agent_Runtime_Overrides {
return $this->runtime_overrides;
}
/**
* Return a normalized array representation.
*
* @return array<string, mixed>
*/
public function to_array(): array {
return array(
'messages' => $this->messages,
'tools' => $this->tools,
'principal' => $this->principal ? $this->principal->to_array() : null,
'runtime_context' => $this->runtime_context,
'metadata' => $this->metadata,
'max_turns' => $this->max_turns,
'single_turn' => $this->single_turn,
'workspace' => $this->workspace ? $this->workspace->to_array() : null,
'runtime_overrides' => $this->runtime_overrides->to_array(),
);
}
/**
* Add runner-facing overrides to runtime context.
*
* @param array<string, mixed> $runtime_context Caller runtime context.
* @return array<string, mixed> Runtime context with nullable overrides removed.
*/
private function apply_runtime_overrides_to_context( array $runtime_context ): array {
$overrides = $this->runtime_overrides->to_array();
foreach ( array( 'system_prompt', 'provider_id', 'model_id', 'temperature', 'max_iterations', 'tier_1_tools', 'greeting' ) as $key ) {
$value = $overrides[ $key ] ?? null;
if ( null !== $value && array() !== $value ) {
$runtime_context[ $key ] = $value;
}
}
return $runtime_context;
}
/**
* Normalize runtime tool declarations.
*
* @param array<mixed> $tools Runtime tool declarations.
* @return array<int, array<string, mixed>>
*/
private static function normalize_tools( array $tools ): array {
$normalized = array();
foreach ( $tools as $index => $tool ) {
if ( ! is_array( $tool ) ) {
throw self::invalid( 'tools[' . $index . ']', 'must be an array' );
}
try {
$normalized[] = self::string_keyed_array( WP_Agent_Tool_Declaration::normalizeForConversationRequest( self::string_keyed_array( $tool ) ) );
} catch ( \InvalidArgumentException $error ) {
throw self::invalid( 'tools[' . $index . ']', $error->getMessage() );
}
}
return $normalized;
}
/**
* Validate that a caller-owned array is JSON-serializable.
*
* @param array<mixed> $value Raw array.
* @param string $path Field path.
* @return array<string, mixed>
*/
private static function normalize_json_array( array $value, string $path ): array {
if ( false === self::jsonEncode( $value ) ) {
throw self::invalid( $path, 'must be JSON serializable' );
}
return self::string_keyed_array( $value );
}
/**
* Normalize an associative array to string keys.
*
* @param array<mixed> $value Raw array.
* @return array<string, mixed>
*/
private static function string_keyed_array( array $value ): array {
$normalized = array();
foreach ( $value as $key => $item ) {
if ( is_string( $key ) ) {
$normalized[ $key ] = $item;
}
}
return $normalized;
}
/**
* Encode JSON without throwing on older PHP configurations.
*
* @param mixed $value Value to encode.
* @return string|false
*/
private static function jsonEncode( $value ) {
try {
return wp_json_encode( $value, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR );
} catch ( \JsonException $e ) {
return false;
}
}
/**
* Build a machine-readable validation exception.
*
* @param string $path Field path.
* @param string $reason Failure reason.
* @return \InvalidArgumentException Validation exception.
*/
private static function invalid( string $path, string $reason ): \InvalidArgumentException {
return new \InvalidArgumentException( 'invalid_agent_conversation_request: ' . $path . ' ' . $reason );
}
}