Skip to content

Commit da3fe1e

Browse files
committed
Fixes in OpenResponses impl + refactoring of agents
1 parent ff0fade commit da3fe1e

File tree

104 files changed

+1206
-2045
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

104 files changed

+1206
-2045
lines changed

examples/A02_Advanced/ContextCachingOAIResponses/run.php

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,6 @@
99
so you can focus on your business logic while still being able to take advantage of lower
1010
latency and costs.
1111

12-
> **Note:** With the Responses API you can also chain server-side context
13-
> using `previous_response_id`. Prompt caching and token counters still depend
14-
> on model eligibility and prompt size.
15-
1612

1713
## Example
1814

@@ -60,6 +56,7 @@ class Project {
6056
```php
6157
<?php
6258
$content = file_get_contents(__DIR__ . '/../../../README.md');
59+
$cacheKey = 'context_cache_structured_oai_responses_readme_v1';
6360

6461
$cached = (new StructuredOutput)->using('openai-responses')->withCachedContext(
6562
system: 'Your goal is to respond questions about the project described in the README.md file'
@@ -75,11 +72,20 @@ class Project {
7572

7673
```php
7774
<?php
75+
/** @var array<string, mixed> $optionsBase */
76+
$optionsBase = [
77+
'max_output_tokens' => 4096,
78+
// Improve cache routing for identical prefixes between requests.
79+
'prompt_cache_key' => $cacheKey,
80+
'prompt_cache_retention' => 'in_memory',
81+
];
82+
7883
// get StructuredOutputResponse object to get access to usage and other metadata
7984
$response1 = $cached->with(
8085
messages: 'Describe the project in a way compelling to my audience: P&C insurance CIOs.',
8186
responseModel: Project::class,
82-
options: ['max_output_tokens' => 4096],
87+
model: 'gpt-4.1',
88+
options: $optionsBase,
8389
mode: OutputMode::JsonSchema,
8490
)->create();
8591

@@ -98,7 +104,7 @@ class Project {
98104

99105
// get usage information from response() method which returns raw InferenceResponse object
100106
$usage1 = $response1->response()->usage();
101-
echo "Usage: {$usage1->inputTokens} prompt tokens, {$usage1->cacheWriteTokens} cache write tokens\n";
107+
echo "Usage #1: {$usage1->inputTokens} prompt tokens, {$usage1->cacheWriteTokens} cache write tokens\n";
102108
?>
103109
```
104110
Now we can use the same context to ask the user to describe the project for a different
@@ -110,14 +116,15 @@ class Project {
110116
```php
111117
<?php
112118
// get StructuredOutputResponse object to get access to usage and other metadata
113-
$options2 = ['max_output_tokens' => 4096];
119+
$options2 = $optionsBase;
114120
if ($previousResponseId !== '') {
115121
$options2['previous_response_id'] = $previousResponseId;
116122
}
117123

118124
$response2 = $cached->with(
119125
messages: "Describe the project in a way compelling to my audience: boutique CMS consulting company owner.",
120126
responseModel: Project::class,
127+
model: 'gpt-4.1',
121128
options: $options2,
122129
mode: OutputMode::JsonSchema,
123130
)->create();
@@ -130,9 +137,7 @@ class Project {
130137

131138
// get usage information from response() method which returns raw InferenceResponse object
132139
$usage2 = $response2->response()->usage();
133-
echo "Usage: {$usage2->inputTokens} prompt tokens, {$usage2->cacheReadTokens} cache read tokens\n";
134-
if ($usage2->cacheReadTokens === 0) {
135-
echo "Note: cacheReadTokens is 0. Prompt caching applies only to eligible models and prompt sizes.\n";
136-
}
140+
echo "Usage #2: {$usage2->inputTokens} prompt tokens, {$usage2->cacheReadTokens} cache read tokens\n";
141+
assert($usage2->cacheReadTokens > 0, 'Expected prompt cache read tokens > 0');
137142
?>
138143
```

examples/A05_Extras/OpenAIResponsesStreamingStructuredOutput/run.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ class PersonProfile
4343

4444
$profile = (new StructuredOutput)
4545
->using('openai-responses')
46+
// ->withHttpDebugPreset('on') // enable HTTP stack debug output if needed
4647
->withOutputMode(OutputMode::JsonSchema)
4748
->withResponseClass(PersonProfile::class)
4849
->withMessages($text)

examples/B03_LLMTroubleshooting/HttpDebug/run.php

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,13 @@
55

66
## Overview
77

8-
Instructor PHP provides a way to debug HTTP calls made to LLM APIs via `withDebug()` method call
9-
on `Inference` object.
8+
Instructor PHP provides a way to debug HTTP calls made to LLM APIs via
9+
`withHttpDebugPreset()` (or the legacy `withDebugPreset()`) on the `Inference`
10+
object.
1011

11-
When debug mode is turned on all HTTP requests and responses are dumped to the console.
12+
When HTTP debug mode is enabled, the HTTP middleware stack prints request and
13+
response details (including streaming data) to the console and dispatches HTTP
14+
debug events.
1215

1316
## Example
1417

@@ -19,7 +22,7 @@
1922
use Cognesy\Polyglot\Inference\Inference;
2023

2124
$response = (new Inference)
22-
->withDebugPreset('on') // Enable debug mode
25+
->withHttpDebugPreset('on') // Enable HTTP debug middleware
2326
->with(
2427
messages: [['role' => 'user', 'content' => 'What is the capital of Brasil']],
2528
options: ['max_tokens' => 128]

packages/agents/src/Agent/Agent.php

Lines changed: 18 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,22 @@
22

33
namespace Cognesy\Agents\Agent;
44

5-
use Cognesy\Agents\Agent\StateProcessing\CanApplyProcessors;
6-
use Cognesy\Agents\Agent\StateProcessing\CanProcessAgentState;
7-
use Cognesy\Agents\Agent\StateProcessing\StateProcessors;
85
use Cognesy\Agents\Core\Collections\Tools;
9-
use Cognesy\Agents\Core\Tools\ToolExecutor;
106
use Cognesy\Agents\Core\Continuation\ContinuationCriteria;
11-
use Cognesy\Agents\Core\Continuation\Contracts\CanEvaluateContinuation;
127
use Cognesy\Agents\Core\Continuation\Enums\StopReason;
138
use Cognesy\Agents\Core\Contracts\CanControlAgentLoop;
9+
use Cognesy\Agents\Core\Contracts\CanEmitAgentEvents;
1410
use Cognesy\Agents\Core\Contracts\CanExecuteToolCalls;
1511
use Cognesy\Agents\Core\Contracts\CanHandleAgentErrors;
1612
use Cognesy\Agents\Core\Contracts\CanUseTools;
17-
use Cognesy\Agents\Core\Contracts\ToolInterface;
1813
use Cognesy\Agents\Core\Data\AgentState;
1914
use Cognesy\Agents\Core\Data\AgentStep;
2015
use Cognesy\Agents\Core\Data\CurrentExecution;
2116
use Cognesy\Agents\Core\Data\StepExecution;
2217
use Cognesy\Agents\Core\Enums\AgentStatus;
23-
use Cognesy\Agents\Core\ErrorHandling\AgentErrorHandler;
24-
use Cognesy\Agents\Core\ErrorHandling\ErrorPolicy;
25-
use Cognesy\Agents\Core\Events\AgentEventEmitter;
2618
use Cognesy\Agents\Core\Exceptions\AgentException;
2719
use Cognesy\Agents\Core\Lifecycle\CanObserveAgentLifecycle;
28-
use Cognesy\Events\Contracts\CanHandleEvents;
20+
use Cognesy\Agents\Core\Tools\ToolExecutor;
2921
use DateTimeImmutable;
3022
use Throwable;
3123

@@ -37,23 +29,15 @@
3729
*/
3830
class Agent implements CanControlAgentLoop
3931
{
40-
private readonly AgentEventEmitter $eventEmitter;
41-
4232
public function __construct(
4333
private readonly Tools $tools,
4434
private readonly CanExecuteToolCalls $toolExecutor,
4535
private readonly CanHandleAgentErrors $errorHandler,
46-
private readonly ?CanApplyProcessors $processors,
4736
private readonly ContinuationCriteria $continuationCriteria,
4837
private readonly CanUseTools $driver,
49-
AgentEventEmitter $eventEmitter,
50-
?CanHandleEvents $events = null,
38+
private readonly CanEmitAgentEvents $eventEmitter,
5139
private readonly ?CanObserveAgentLifecycle $observer = null,
52-
) {
53-
$this->eventEmitter = $events !== null
54-
? $eventEmitter->withEventHandler($events)
55-
: $eventEmitter;
56-
}
40+
) {}
5741

5842
// PUBLIC API //////////////////////////////////
5943

@@ -102,7 +86,7 @@ protected function onBeforeExecution(AgentState $state): AgentState {
10286
$this->eventEmitter->executionStarted($state, count($this->tools->names()));
10387

10488
if ($this->observer !== null) {
105-
$state = $this->observer->executionStarting($state);
89+
$state = $this->observer->beforeExecution($state);
10690
}
10791

10892
return $state;
@@ -112,7 +96,7 @@ protected function onBeforeStep(AgentState $state): AgentState {
11296
$this->eventEmitter->stepStarted($state);
11397

11498
if ($this->observer !== null) {
115-
$state = $this->observer->stepStarting($state);
99+
$state = $this->observer->beforeStep($state);
116100
}
117101

118102
return $state;
@@ -145,7 +129,7 @@ private function onAfterToolUse(AgentState $state, AgentStep $rawStep) : AgentSt
145129

146130
protected function onAfterStep(AgentState $state): AgentState {
147131
if ($this->observer !== null) {
148-
$state = $this->observer->stepEnding($state);
132+
$state = $this->observer->afterStep($state);
149133
}
150134

151135
$this->eventEmitter->stepCompleted($state);
@@ -161,7 +145,7 @@ protected function onAfterExecution(AgentState $state): AgentState {
161145
$finalState = $state->withStatus($status);
162146

163147
if ($this->observer !== null) {
164-
$finalState = $this->observer->executionEnding($finalState);
148+
$finalState = $this->observer->afterExecution($finalState);
165149
}
166150

167151
$this->eventEmitter->executionFinished($finalState);
@@ -199,7 +183,7 @@ protected function onError(Throwable $error, AgentState $state): AgentState {
199183
$agentException = $handling->exception instanceof AgentException
200184
? $handling->exception
201185
: AgentException::fromThrowable($handling->exception);
202-
$nextState = $this->observer->executionFailed($nextState, $agentException);
186+
$nextState = $this->observer->onError($nextState, $agentException);
203187
}
204188
}
205189

@@ -225,7 +209,7 @@ protected function shouldContinue(AgentState $state): bool {
225209
// We're about to stop - let the observer intervene if registered
226210
if ($this->observer !== null) {
227211
$stopReason = $state->stopReason() ?? StopReason::Completed;
228-
$decision = $this->observer->stopping($state, $stopReason);
212+
$decision = $this->observer->beforeStopDecision($state, $stopReason);
229213
if ($decision->isPrevented()) {
230214
return true; // Observer prevented stopping, continue execution
231215
}
@@ -235,10 +219,7 @@ protected function shouldContinue(AgentState $state): bool {
235219
}
236220

237221
private function performStep(AgentState $state): AgentState {
238-
return match(true) {
239-
($this->processors === null) => $this->useTools($state),
240-
default => $this->processors->apply($state, fn($s) => $this->useTools($s)),
241-
};
222+
return $this->useTools($state);
242223
}
243224

244225
private function useTools(AgentState $state): AgentState {
@@ -306,32 +287,27 @@ public function driver(): CanUseTools {
306287
return $this->driver;
307288
}
308289

309-
public function eventEmitter(): AgentEventEmitter {
290+
public function eventEmitter(): CanEmitAgentEvents {
310291
return $this->eventEmitter;
311292
}
312293

294+
public function observer(): ?CanObserveAgentLifecycle {
295+
return $this->observer;
296+
}
297+
313298
// MUTATORS /////////////////////////////////////////////
314299

315300
public function with(
316301
?Tools $tools = null,
317302
?CanExecuteToolCalls $toolExecutor = null,
318303
?CanHandleAgentErrors $errorHandler = null,
319-
?CanApplyProcessors $processors = null,
320304
?ContinuationCriteria $continuationCriteria = null,
321305
?CanUseTools $driver = null,
322-
?AgentEventEmitter $eventEmitter = null,
323-
?CanHandleEvents $events = null,
306+
?CanEmitAgentEvents $eventEmitter = null,
324307
?CanObserveAgentLifecycle $observer = null,
325308
): self {
326309
$resolvedTools = $tools ?? $this->tools;
327310

328-
// Resolve emitter: prefer explicit, then create new if events changed
329-
$resolvedEmitter = $eventEmitter ?? (
330-
$events !== null
331-
? $this->eventEmitter->withEventHandler($events)
332-
: $this->eventEmitter
333-
);
334-
335311
// If tools changed but no executor provided, create new executor
336312
$resolvedExecutor = $toolExecutor ?? (
337313
$tools !== null
@@ -343,52 +319,10 @@ public function with(
343319
tools: $resolvedTools,
344320
toolExecutor: $resolvedExecutor,
345321
errorHandler: $errorHandler ?? $this->errorHandler,
346-
processors: $processors ?? $this->processors,
347322
continuationCriteria: $continuationCriteria ?? $this->continuationCriteria,
348323
driver: $driver ?? $this->driver,
349-
eventEmitter: $resolvedEmitter,
324+
eventEmitter: $eventEmitter ?? $this->eventEmitter,
350325
observer: $observer ?? $this->observer,
351326
);
352327
}
353-
354-
public function withProcessors(CanProcessAgentState ...$processors): self {
355-
return $this->with(processors: new StateProcessors(...$processors));
356-
}
357-
358-
public function withDriver(CanUseTools $driver): self {
359-
return $this->with(driver: $driver);
360-
}
361-
362-
public function withContinuationCriteria(CanEvaluateContinuation ...$criteria): self {
363-
return $this->with(continuationCriteria: new ContinuationCriteria(...$criteria));
364-
}
365-
366-
public function withTools(array|ToolInterface|Tools $tools): self {
367-
return $this->with(tools: match (true) {
368-
is_array($tools) => new Tools(...$tools),
369-
$tools instanceof ToolInterface => new Tools($tools),
370-
$tools instanceof Tools => $tools,
371-
default => new Tools(),
372-
});
373-
}
374-
375-
public function withToolExecutor(CanExecuteToolCalls $toolExecutor): self {
376-
return $this->with(toolExecutor: $toolExecutor);
377-
}
378-
379-
public function withErrorHandler(CanHandleAgentErrors $errorHandler): self {
380-
return $this->with(errorHandler: $errorHandler);
381-
}
382-
383-
public function withErrorPolicy(ErrorPolicy $policy): self {
384-
return $this->with(errorHandler: AgentErrorHandler::withPolicy($policy));
385-
}
386-
387-
public function withObserver(?CanObserveAgentLifecycle $observer): self {
388-
return $this->with(observer: $observer);
389-
}
390-
391-
public function observer(): ?CanObserveAgentLifecycle {
392-
return $this->observer;
393-
}
394328
}

0 commit comments

Comments
 (0)