Skip to content

Commit 3b1b190

Browse files
committed
improvements
1 parent 6620625 commit 3b1b190

File tree

17 files changed

+221
-308
lines changed

17 files changed

+221
-308
lines changed

src/Agents/Agent.php

Lines changed: 7 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use Cortex\LLM\Data\Usage;
1111
use Cortex\Prompts\Prompt;
1212
use Cortex\Events\AgentEnd;
13+
use Illuminate\Support\Str;
1314
use Cortex\Contracts\ToolKit;
1415
use Cortex\Events\AgentStart;
1516
use Cortex\JsonSchema\Schema;
@@ -32,7 +33,6 @@
3233
use Illuminate\Support\Collection;
3334
use Cortex\Events\AgentStreamChunk;
3435
use Cortex\LLM\Data\ChatStreamResult;
35-
use Cortex\Agents\Contracts\Middleware;
3636
use Cortex\Events\Contracts\AgentEvent;
3737
use Cortex\Exceptions\GenericException;
3838
use Cortex\Memory\Stores\InMemoryStore;
@@ -48,15 +48,13 @@
4848
use Cortex\Support\Traits\DispatchesEvents;
4949
use Illuminate\Contracts\Support\Arrayable;
5050
use Cortex\LLM\Contracts\LLM as LLMContract;
51+
use Cortex\Agents\Concerns\HandlesMiddleware;
5152
use Cortex\Agents\Stages\TrackAgentStepStart;
5253
use Cortex\Prompts\Builders\ChatPromptBuilder;
53-
use Cortex\Agents\Middleware\AfterModelWrapper;
5454
use Cortex\LLM\Data\Messages\MessageCollection;
55-
use Cortex\Agents\Middleware\BeforeModelWrapper;
5655
use Cortex\LLM\Data\Messages\MessagePlaceholder;
5756
use Cortex\Prompts\Templates\ChatPromptTemplate;
5857
use Cortex\Agents\Contracts\AfterModelMiddleware;
59-
use Cortex\Agents\Middleware\BeforePromptWrapper;
6058
use Cortex\Agents\Contracts\BeforeModelMiddleware;
6159
use Cortex\Agents\Contracts\BeforePromptMiddleware;
6260
use Cortex\Contracts\ChatMemory as ChatMemoryContract;
@@ -67,6 +65,7 @@ class Agent implements Pipeable
6765
{
6866
use CanPipe;
6967
use DispatchesEvents;
68+
use HandlesMiddleware;
7069

7170
protected LLMContract $llm;
7271

@@ -383,57 +382,6 @@ protected function executionStages(): array
383382
];
384383
}
385384

386-
/**
387-
* Get the middleware of a specific type.
388-
* If middleware implements multiple middleware interfaces, wrap it appropriately
389-
* to delegate to the correct hook method (beforePrompt, beforeModel, or afterModel).
390-
*
391-
* @param class-string<\Cortex\Agents\Contracts\Middleware> $type
392-
*
393-
* @return array<int, \Cortex\Agents\Contracts\Middleware>
394-
*/
395-
protected function getMiddleware(string $type): array
396-
{
397-
return array_map(
398-
function (Middleware $middleware) use ($type): Middleware {
399-
// Wrap all hook-based middleware to ensure hook methods are called
400-
if (! $this->isHookMiddlewareType($type)) {
401-
return $middleware;
402-
}
403-
404-
// If middleware implements multiple interfaces, wrap to delegate to correct hook
405-
// If it only implements one interface, still wrap to ensure hook method is called
406-
return $this->wrapMiddleware($middleware, $type);
407-
},
408-
array_filter($this->middleware, fn(Middleware $middleware): bool => $middleware instanceof $type),
409-
);
410-
}
411-
412-
/**
413-
* Check if the given type is a hook-based middleware interface.
414-
*/
415-
protected function isHookMiddlewareType(string $type): bool
416-
{
417-
return in_array($type, [
418-
BeforePromptMiddleware::class,
419-
BeforeModelMiddleware::class,
420-
AfterModelMiddleware::class,
421-
], true);
422-
}
423-
424-
/**
425-
* Wrap middleware to delegate to the appropriate hook method.
426-
*/
427-
protected function wrapMiddleware(Middleware $middleware, string $type): Middleware
428-
{
429-
return match ($type) {
430-
BeforePromptMiddleware::class => new BeforePromptWrapper($middleware),
431-
BeforeModelMiddleware::class => new BeforeModelWrapper($middleware),
432-
AfterModelMiddleware::class => new AfterModelWrapper($middleware),
433-
default => $middleware,
434-
};
435-
}
436-
437385
/**
438386
* @param array<int, \Cortex\LLM\Contracts\Message> $messages
439387
* @param array<string, mixed> $input
@@ -546,8 +494,10 @@ protected static function buildPromptTemplate(
546494
*/
547495
protected static function buildMemory(ChatPromptTemplate $prompt, ?Store $memoryStore = null): ChatMemoryContract
548496
{
549-
$memoryStore ??= new InMemoryStore();
550-
$memoryStore->setMessages($prompt->messages->withoutPlaceholders());
497+
$memoryStore ??= new InMemoryStore(
498+
threadId: Str::uuid7()->toString(),
499+
messages: $prompt->messages->withoutPlaceholders(),
500+
);
551501

552502
return new ChatMemory($memoryStore);
553503
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Cortex\Agents\Concerns;
6+
7+
use Cortex\Agents\Contracts\Middleware;
8+
use Cortex\Agents\Middleware\AfterModelWrapper;
9+
use Cortex\Agents\Middleware\BeforeModelWrapper;
10+
use Cortex\Agents\Contracts\AfterModelMiddleware;
11+
use Cortex\Agents\Middleware\BeforePromptWrapper;
12+
use Cortex\Agents\Contracts\BeforeModelMiddleware;
13+
use Cortex\Agents\Contracts\BeforePromptMiddleware;
14+
15+
trait HandlesMiddleware
16+
{
17+
/**
18+
* Get the middleware of a specific type.
19+
* If middleware implements multiple middleware interfaces, wrap it appropriately
20+
* to delegate to the correct hook method (beforePrompt, beforeModel, or afterModel).
21+
*
22+
* @param class-string<\Cortex\Agents\Contracts\Middleware> $type
23+
*
24+
* @return array<int, \Cortex\Agents\Contracts\Middleware>
25+
*/
26+
protected function getMiddleware(string $type): array
27+
{
28+
return array_map(
29+
function (Middleware $middleware) use ($type): Middleware {
30+
// Wrap all hook-based middleware to ensure hook methods are called
31+
if (! $this->isHookMiddlewareType($type)) {
32+
return $middleware;
33+
}
34+
35+
// If middleware implements multiple interfaces, wrap to delegate to correct hook
36+
// If it only implements one interface, still wrap to ensure hook method is called
37+
return $this->wrapMiddleware($middleware, $type);
38+
},
39+
array_filter($this->middleware, fn(Middleware $middleware): bool => $middleware instanceof $type),
40+
);
41+
}
42+
43+
/**
44+
* Check if the given type is a hook-based middleware interface.
45+
*
46+
* @param class-string<\Cortex\Agents\Contracts\Middleware> $type
47+
*/
48+
protected function isHookMiddlewareType(string $type): bool
49+
{
50+
return in_array($type, [
51+
BeforePromptMiddleware::class,
52+
BeforeModelMiddleware::class,
53+
AfterModelMiddleware::class,
54+
], true);
55+
}
56+
57+
/**
58+
* Wrap middleware to delegate to the appropriate hook method.
59+
*/
60+
protected function wrapMiddleware(Middleware $middleware, string $type): Middleware
61+
{
62+
return match ($type) {
63+
BeforePromptMiddleware::class => new BeforePromptWrapper($middleware), // @phpstan-ignore argument.type
64+
BeforeModelMiddleware::class => new BeforeModelWrapper($middleware), // @phpstan-ignore argument.type
65+
AfterModelMiddleware::class => new AfterModelWrapper($middleware), // @phpstan-ignore argument.type
66+
default => $middleware,
67+
};
68+
}
69+
}

src/Agents/Contracts/AgentBuilder.php

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
use Cortex\Contracts\ToolKit;
99
use Cortex\LLM\Contracts\LLM;
1010
use Cortex\LLM\Enums\ToolChoice;
11-
use Cortex\Memory\Contracts\Store;
1211
use Cortex\JsonSchema\Types\ObjectSchema;
1312
use Cortex\LLM\Enums\StructuredOutputMode;
1413
use Cortex\Prompts\Builders\ChatPromptBuilder;
@@ -72,11 +71,6 @@ public function strict(): bool;
7271
*/
7372
public function initialPromptVariables(): array;
7473

75-
/**
76-
* Specify the memory store for the agent.
77-
*/
78-
public function memoryStore(): ?Store;
79-
8074
/**
8175
* Specify the middleware for the agent.
8276
*

src/Agents/Middleware/AfterModelWrapper.php

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
use Closure;
88
use Cortex\Pipeline\RuntimeConfig;
99
use Cortex\Support\Traits\CanPipe;
10-
use Cortex\Agents\Contracts\Middleware;
1110
use Cortex\Agents\Contracts\AfterModelMiddleware;
1211

1312
/**
@@ -19,15 +18,12 @@ class AfterModelWrapper implements AfterModelMiddleware
1918
use CanPipe;
2019

2120
public function __construct(
22-
protected Middleware $middleware,
21+
protected AfterModelMiddleware $middleware,
2322
) {}
2423

2524
public function handlePipeable(mixed $payload, RuntimeConfig $config, Closure $next): mixed
2625
{
27-
/** @var AfterModelMiddleware $middleware */
28-
$middleware = $this->middleware;
29-
30-
return $middleware->afterModel($payload, $config, $next);
26+
return $this->middleware->afterModel($payload, $config, $next);
3127
}
3228

3329
public function afterModel(mixed $payload, RuntimeConfig $config, Closure $next): mixed

src/Agents/Middleware/BeforeModelWrapper.php

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
use Closure;
88
use Cortex\Pipeline\RuntimeConfig;
99
use Cortex\Support\Traits\CanPipe;
10-
use Cortex\Agents\Contracts\Middleware;
1110
use Cortex\Agents\Contracts\BeforeModelMiddleware;
1211

1312
/**
@@ -19,15 +18,12 @@ class BeforeModelWrapper implements BeforeModelMiddleware
1918
use CanPipe;
2019

2120
public function __construct(
22-
protected Middleware $middleware,
21+
protected BeforeModelMiddleware $middleware,
2322
) {}
2423

2524
public function handlePipeable(mixed $payload, RuntimeConfig $config, Closure $next): mixed
2625
{
27-
/** @var BeforeModelMiddleware $middleware */
28-
$middleware = $this->middleware;
29-
30-
return $middleware->beforeModel($payload, $config, $next);
26+
return $this->middleware->beforeModel($payload, $config, $next);
3127
}
3228

3329
public function beforeModel(mixed $payload, RuntimeConfig $config, Closure $next): mixed

src/Agents/Middleware/BeforePromptWrapper.php

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
use Closure;
88
use Cortex\Pipeline\RuntimeConfig;
99
use Cortex\Support\Traits\CanPipe;
10-
use Cortex\Agents\Contracts\Middleware;
1110
use Cortex\Agents\Contracts\BeforePromptMiddleware;
1211

1312
/**
@@ -19,15 +18,12 @@ class BeforePromptWrapper implements BeforePromptMiddleware
1918
use CanPipe;
2019

2120
public function __construct(
22-
protected Middleware $middleware,
21+
protected BeforePromptMiddleware $middleware,
2322
) {}
2423

2524
public function handlePipeable(mixed $payload, RuntimeConfig $config, Closure $next): mixed
2625
{
27-
/** @var BeforePromptMiddleware $middleware */
28-
$middleware = $this->middleware;
29-
30-
return $middleware->beforePrompt($payload, $config, $next);
26+
return $this->middleware->beforePrompt($payload, $config, $next);
3127
}
3228

3329
public function beforePrompt(mixed $payload, RuntimeConfig $config, Closure $next): mixed

src/Contracts/ChatMemory.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,10 @@ public function setVariables(array $variables): static;
4444
* @return array<string, mixed>
4545
*/
4646
public function getVariables(): array;
47+
48+
/**
49+
* Get the thread ID for this memory instance.
50+
* Delegates to the underlying store - the store is the source of truth for threadId.
51+
*/
52+
public function getThreadId(): string;
4753
}

src/Memory/ChatMemory.php

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace Cortex\Memory;
66

7+
use Illuminate\Support\Str;
78
use Cortex\LLM\Contracts\Message;
89
use Cortex\Memory\Contracts\Store;
910
use Cortex\Memory\Stores\InMemoryStore;
@@ -17,14 +18,23 @@ class ChatMemory implements ChatMemoryContract
1718
*/
1819
protected array $variables = [];
1920

21+
protected Store $store;
22+
2023
/**
21-
* @param Store $store The store to use for message persistence
24+
* @param Store|null $store The store to use for message persistence (must have threadId)
2225
* @param int|null $limit The maximum number of messages to return
2326
*/
2427
public function __construct(
25-
protected Store $store = new InMemoryStore(),
28+
?Store $store = null,
2629
protected ?int $limit = null,
27-
) {}
30+
) {
31+
// Create store if not provided (with auto-generated threadId)
32+
if ($store === null) {
33+
$store = new InMemoryStore(threadId: Str::uuid7()->toString());
34+
}
35+
36+
$this->store = $store;
37+
}
2838

2939
public function addMessage(Message $message): void
3040
{
@@ -84,4 +94,9 @@ public function reset(): void
8494
$this->store->reset();
8595
$this->setVariables([]);
8696
}
97+
98+
public function getThreadId(): string
99+
{
100+
return $this->store->getThreadId();
101+
}
87102
}

0 commit comments

Comments
 (0)