Skip to content

Commit a2ac89f

Browse files
committed
Release version 1.21.0
1 parent 54a5740 commit a2ac89f

File tree

285 files changed

+5285
-1560
lines changed

Some content is hidden

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

285 files changed

+5285
-1560
lines changed

.beads/issues.jsonl

Lines changed: 53 additions & 0 deletions
Large diffs are not rendered by default.

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,8 @@
140140
"pestphp/pest-plugin": true,
141141
"php-http/discovery": true
142142
},
143-
"sort-packages": true
143+
"sort-packages": true,
144+
"process-timeout": 600
144145
},
145146
"require": {
146147
"php": "^8.3|^8.4|^8.5",

docs-build/cookbook/instructor/advanced/dsn.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ class User {
1010

1111
$user = (new StructuredOutput)
1212
//->wiretap(fn($e) => $e->print())
13-
->withDsn('preset=xai,model=grok-2')
13+
->withDsn('preset=xai,model=grok-3')
1414
->withMessages("Our user Jason is 25 years old.")
1515
->withresponseClass(User::class)
1616
->get();

docs-build/cookbook/polyglot/llm_advanced/dsn.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use Cognesy\Polyglot\Inference\Inference;
55
use Cognesy\Utils\Str;
66

77
$answer = (new Inference)
8-
->withDsn('preset=xai,model=grok-2')
8+
->withDsn('preset=xai,model=grok-3')
99
->with(
1010
messages: [['role' => 'user', 'content' => 'What is the capital of France']],
1111
options: ['max_tokens' => 64]

docs-build/cookbook/polyglot/llm_extras/agent_basic.mdx

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
<?php
1+
<?php declare(strict_types=1);
22
require 'examples/boot.php';
33

44
use Cognesy\Addons\Agent\AgentBuilder;
55
use Cognesy\Addons\Agent\Core\Data\AgentState;
6+
use Cognesy\Addons\Agent\Core\Enums\AgentStatus;
67
use Cognesy\Messages\Messages;
78

89
// Build a basic agent
@@ -24,4 +25,21 @@ $response = $finalState->currentStep()?->outputMessages()->toString() ?? 'No res
2425
echo "Answer: {$response}\n";
2526
echo "Steps: {$finalState->stepCount()}\n";
2627
echo "Status: {$finalState->status()->value}\n";
28+
29+
if ($finalState->status() === AgentStatus::Failed) {
30+
$debug = $finalState->debug();
31+
$stepType = $finalState->currentStep()?->stepType()?->value;
32+
if ($stepType !== null) {
33+
echo "Step type: {$stepType}\n";
34+
}
35+
if (($debug['stopReason'] ?? '') !== '') {
36+
echo "Stop reason: {$debug['stopReason']}\n";
37+
}
38+
if (($debug['resolvedBy'] ?? '') !== '') {
39+
echo "Resolved by: {$debug['resolvedBy']}\n";
40+
}
41+
if (($debug['errors'] ?? '') !== '') {
42+
echo "Errors: {$debug['errors']}\n";
43+
}
44+
}
2745
?>

docs-build/cookbook/polyglot/llm_extras/agent_ctrl_events.mdx

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<?php
1+
<?php declare(strict_types=1);
22
require 'examples/boot.php';
33

44
use Cognesy\AgentCtrl\AgentCtrl;
@@ -23,7 +23,12 @@ $agent->onEvent(AgentExecutionStarted::class, function(AgentExecutionStarted $ev
2323
});
2424

2525
$agent->onEvent(AgentTextReceived::class, function(AgentTextReceived $event): void {
26-
$preview = strlen($event->text) > 50 ? substr($event->text, 0, 50) . '...' : $event->text;
26+
$preview = $event->text;
27+
if (strlen($event->text) > 50) {
28+
$preview = substr($event->text, 0, 50) . '...';
29+
// replace new lines with spaces to keep output clean
30+
$preview = str_replace("\n", ' ', $preview);
31+
}
2732
echo "-> Text: \"{$preview}\"\n";
2833
});
2934

@@ -32,7 +37,11 @@ $agent->onEvent(AgentToolUsed::class, function(AgentToolUsed $event): void {
3237
});
3338

3439
$agent->onEvent(AgentExecutionCompleted::class, function(AgentExecutionCompleted $event): void {
35-
echo "-> Completed: {$event->toolCallCount} tool calls, cost: $" . number_format($event->cost, 4) . "\n";
40+
$costLabel = 'n/a';
41+
if ($event->cost !== null) {
42+
$costLabel = '$' . number_format($event->cost, 4);
43+
}
44+
echo "-> Completed: {$event->toolCallCount} tool calls, cost: {$costLabel}\n";
3645
});
3746

3847
// Execute with streaming to see events in real-time

docs-build/cookbook/polyglot/llm_extras/agent_structured_output.mdx

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<?php
1+
<?php declare(strict_types=1);
22
require 'examples/boot.php';
33

44
use Cognesy\Addons\Agent\AgentBuilder;
@@ -183,6 +183,14 @@ while ($agent->hasNextStep($state)) {
183183
$step = $state->currentStep();
184184
echo "Step {$state->stepCount()}: [{$step->stepType()->value}]\n";
185185

186+
if ($step->hasErrors()) {
187+
$errors = $step->errorsAsString();
188+
if ($errors !== '') {
189+
$errors = str_replace("\n", "\n !! ", $errors);
190+
echo " !! {$errors}\n";
191+
}
192+
}
193+
186194
if ($step->hasToolCalls()) {
187195
foreach ($step->toolCalls()->all() as $toolCall) {
188196
$args = $toolCall->args();
@@ -197,6 +205,15 @@ while ($agent->hasNextStep($state)) {
197205

198206
// Show tool execution results
199207
foreach ($step->toolExecutions()->all() as $execution) {
208+
if ($execution->hasError()) {
209+
$errorMessage = $execution->errorMessage();
210+
$label = 'Unknown tool execution error';
211+
if ($errorMessage !== '') {
212+
$label = $errorMessage;
213+
}
214+
echo " ← Error: {$label}\n";
215+
continue;
216+
}
200217
$result = $execution->value();
201218
$resultStr = match (true) {
202219
is_string($result) => $result,

docs-build/cookbook/polyglot/llm_extras/agent_subagents.mdx

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
<?php
1+
<?php declare(strict_types=1);
22
require 'examples/boot.php';
33

44
use Cognesy\Addons\Agent\AgentBuilder;
55
use Cognesy\Addons\Agent\Capabilities\File\UseFileTools;
6+
use Cognesy\Addons\Agent\Capabilities\Subagent\SpawnSubagentTool;
67
use Cognesy\Addons\Agent\Capabilities\Subagent\UseSubagents;
78
use Cognesy\Addons\Agent\Core\Data\AgentState;
89
use Cognesy\Addons\Agent\Registry\AgentRegistry;
@@ -31,6 +32,8 @@ $registry->register(new AgentSpec(
3132
tools: ['read_file'],
3233
));
3334

35+
SpawnSubagentTool::clearSubagentStates();
36+
3437
// Build main orchestration agent
3538
$agent = AgentBuilder::base()
3639
->withCapability(new UseFileTools($workDir))
@@ -40,9 +43,9 @@ $agent = AgentBuilder::base()
4043
// Task requiring multiple isolated reviews
4144
$task = <<<TASK
4245
Review these three files and provide a summary:
43-
1. src/Agent/AgentBuilder.php
44-
2. src/Agent/AgentState.php
45-
3. src/Agent/Agent.php
46+
1. packages/addons/src/Agent/AgentBuilder.php
47+
2. packages/addons/src/Agent/Core/Data/AgentState.php
48+
3. packages/addons/src/Agent/Agent.php
4649

4750
For each file, spawn a reviewer subagent. Then summarize the findings.
4851
TASK;
@@ -81,6 +84,6 @@ echo $summary . "\n\n";
8184

8285
echo "Stats:\n";
8386
echo " Steps: {$state->stepCount()}\n";
84-
echo " Subagents spawned: " . ($state->metadata()->get('subagent_count') ?? 0) . "\n";
87+
echo " Subagents spawned: " . count(SpawnSubagentTool::getSubagentStates()) . "\n";
8588
echo " Status: {$state->status()->value}\n";
8689
?>

docs-build/cookbook/polyglot/llm_extras/chat_with_summary.mdx

Lines changed: 91 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
<?php
1+
<?php declare(strict_types=1);
22

33
require 'examples/boot.php';
44

55
use Cognesy\Addons\Chat\ChatFactory;
66
use Cognesy\Addons\Chat\Collections\Participants;
77
use Cognesy\Addons\Chat\Data\ChatState;
8+
use Cognesy\Addons\Chat\Events\MessageBufferSummarized;
9+
use Cognesy\Addons\Chat\Events\MessagesMovedToBuffer;
810
use Cognesy\Addons\Chat\Participants\LLMParticipant;
911
use Cognesy\Addons\Chat\Participants\ScriptedParticipant;
1012
use Cognesy\Addons\Chat\Utils\SummarizeMessages;
@@ -17,10 +19,49 @@ use Cognesy\Addons\StepByStep\StateProcessing\Processors\MoveMessagesToBuffer;
1719
use Cognesy\Addons\StepByStep\StateProcessing\Processors\SummarizeBuffer;
1820
use Cognesy\Addons\StepByStep\StateProcessing\StateProcessors;
1921
use Cognesy\Events\Dispatchers\EventDispatcher;
22+
use Cognesy\Messages\Message;
2023
use Cognesy\Messages\Messages;
2124
use Cognesy\Polyglot\Inference\LLMProvider;
25+
use Cognesy\Utils\Tokenizer;
2226

2327
$events = new EventDispatcher();
28+
$bufferTokenLimit = 600;
29+
$summaryTriggerLimit = 200;
30+
$summaryMaxTokens = 512;
31+
$bufferTokens = 0;
32+
33+
$events->addListener(MessagesMovedToBuffer::class, function(MessagesMovedToBuffer $event) use (
34+
$bufferTokenLimit,
35+
$summaryTriggerLimit,
36+
&$bufferTokens
37+
): void {
38+
$overflow = Messages::fromArray($event->data['overflow'] ?? []);
39+
$keep = Messages::fromArray($event->data['keep'] ?? []);
40+
$overflowTokens = Tokenizer::tokenCount($overflow->toString());
41+
if ($overflowTokens === 0) {
42+
return;
43+
}
44+
$keepTokens = Tokenizer::tokenCount($keep->toString());
45+
$totalTokens = $overflowTokens + $keepTokens;
46+
$bufferTokens += $overflowTokens;
47+
48+
echo "\n>>> Buffer overflow detected (messages {$totalTokens} tokens > {$bufferTokenLimit}). ";
49+
echo "Moved {$overflowTokens} tokens to buffer ({$bufferTokens}/{$summaryTriggerLimit}).\n";
50+
});
51+
52+
$events->addListener(MessageBufferSummarized::class, function(MessageBufferSummarized $event) use (
53+
$summaryTriggerLimit,
54+
&$bufferTokens
55+
): void {
56+
$buffer = Messages::fromArray($event->data['buffer'] ?? []);
57+
$bufferTokens = Tokenizer::tokenCount($buffer->toString());
58+
$summaryText = (string) ($event->data['summary'] ?? '');
59+
$summaryTokens = Tokenizer::tokenCount($summaryText);
60+
61+
echo "\n>>> Summarization triggered ({$bufferTokens} tokens > {$summaryTriggerLimit}).\n";
62+
echo ">>> Summary tokens: {$summaryTokens}\n";
63+
$bufferTokens = 0;
64+
});
2465

2566
$student = new ScriptedParticipant(
2667
name: 'student',
@@ -60,30 +101,55 @@ $chat = ChatFactory::default(
60101
),
61102
),
62103
processors: new StateProcessors(
63-
new AccumulateTokenUsage(),
64-
new AppendStepMessages(),
65-
new MoveMessagesToBuffer(
66-
maxTokens: 128,
67-
bufferSection: 'buffer',
68-
events: $events
69-
),
70104
new SummarizeBuffer(
71-
maxBufferTokens: 128,
72-
maxSummaryTokens: 512,
105+
maxBufferTokens: $summaryTriggerLimit,
106+
maxSummaryTokens: $summaryMaxTokens,
73107
bufferSection: 'buffer',
74108
summarySection: 'summary',
75109
summarizer: new SummarizeMessages(llm: LLMProvider::using('openai')),
76110
events: $events,
77111
),
112+
new MoveMessagesToBuffer(
113+
maxTokens: $bufferTokenLimit,
114+
bufferSection: 'buffer',
115+
events: $events
116+
),
117+
new AppendStepMessages(),
118+
new AccumulateTokenUsage(),
78119
),
79120
events: $events,
80121
);//->wiretap(fn(Event $e) => $e->printDebug());
81122

82-
$context = "# CONTEXT\n\n" . file_get_contents(__DIR__ . '/summary.md');
123+
$context = "# CONTEXT\n\nThis is a long-running coaching session about improving sales performance using the Challenger Sale approach.";
124+
$seedHistory = Messages::fromString(content: $context, role: 'system')
125+
->appendMessage(Message::fromString(
126+
content: 'I keep stalling after the first discovery call and need a clear Challenger-style way to reframe the conversation and surface urgency.',
127+
role: 'user'
128+
))
129+
->appendMessage(Message::fromString(
130+
content: 'Lead with a tailored insight that challenges their current assumption, then ask two pointed questions that quantify the gap and cost.',
131+
role: 'assistant'
132+
))
133+
->appendMessage(Message::fromString(
134+
content: 'Prospects compare us to cheaper tools, so I need a concise way to defend ROI without sounding defensive.',
135+
role: 'user'
136+
))
137+
->appendMessage(Message::fromString(
138+
content: 'Use a short payback story with a concrete metric, then contrast the cost of delay against a specific business outcome.',
139+
role: 'assistant'
140+
))
141+
->appendMessage(Message::fromString(
142+
content: 'We sell across industries and I struggle to adapt the story without rebuilding the whole deck.',
143+
role: 'user'
144+
))
145+
->appendMessage(Message::fromString(
146+
content: 'Keep a core narrative and swap only the pains and metrics so the challenge-then-resolution arc stays consistent.',
147+
role: 'assistant'
148+
));
83149

84-
$state = (new ChatState)->withMessages(
85-
Messages::fromString(content: $context, role: 'system')
86-
);
150+
$state = (new ChatState)->withMessages($seedHistory);
151+
152+
echo "Messages keep at most {$bufferTokenLimit} tokens; buffer summarizes past {$summaryTriggerLimit} tokens.\n";
87153

88154
while ($chat->hasNextStep($state)) {
89155
$state = $chat->nextStep($state);
@@ -92,7 +158,11 @@ while ($chat->hasNextStep($state)) {
92158
$name = $step?->participantName() ?? 'unknown';
93159
$content = trim($step?->outputMessages()->toString() ?? '');
94160
echo "\n--- Step " . ($state->stepCount()) . " ($name) ---\n";
95-
echo ($content ?: '[eot]'). "\n";
161+
$display = $content;
162+
if ($display === '') {
163+
$display = '[eot]';
164+
}
165+
echo $display . "\n";
96166
// echo "---------------------\n";
97167
// echo "SUMMARY:\n" . $state->store()->section('summary')->get()?->toString();
98168
// echo "---------------------\n";
@@ -101,4 +171,10 @@ while ($chat->hasNextStep($state)) {
101171
// echo "MESSAGES:\n" . $state->store()->section('messages')->get()?->toString();
102172
// echo "=====================\n";
103173
}
174+
175+
$summaryText = trim($state->store()->section('summary')->messages()->toString());
176+
if ($summaryText !== '') {
177+
echo "\n--- Final Summary ---\n";
178+
echo $summaryText . "\n";
179+
}
104180
?>

docs-build/cookbook/polyglot/llm_extras/tool_use.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use Cognesy\Addons\StepByStep\Continuation\Criteria\ErrorPolicyCriterion;
66
use Cognesy\Addons\StepByStep\Continuation\Criteria\ExecutionTimeLimit;
77
use Cognesy\Addons\StepByStep\Continuation\Criteria\StepsLimit;
88
use Cognesy\Addons\StepByStep\Continuation\Criteria\TokenUsageLimit;
9-
use Cognesy\Addons\StepByStep\Continuation\ErrorPolicy;
9+
use Cognesy\Addons\StepByStep\ErrorHandling\ErrorPolicy;
1010
use Cognesy\Addons\ToolUse\Collections\Tools;
1111
use Cognesy\Addons\ToolUse\Data\ToolUseState;
1212
use Cognesy\Addons\ToolUse\Drivers\ReAct\ContinuationCriteria\StopOnFinalDecision;

0 commit comments

Comments
 (0)