Skip to content

Commit 37a087f

Browse files
authored
fix(broadcasting): Citation event broadcasting (#927)
1 parent aa0f29d commit 37a087f

6 files changed

Lines changed: 56 additions & 5 deletions

File tree

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
"symplify/rule-doc-generator-contracts": "^11.2",
4646
"phpstan/phpdoc-parser": "^2.0",
4747
"spatie/laravel-ray": "^1.39",
48-
"laravel/mcp": "^0.5.2"
48+
"laravel/mcp": "^0.6.0"
4949
},
5050
"autoload-dev": {
5151
"psr-4": {
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Prism\Prism\Events\Broadcasting;
6+
7+
class CitationBroadcast extends StreamEventBroadcast {}

src/Streaming/Adapters/BroadcastAdapter.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use Illuminate\Support\Collection;
1111
use InvalidArgumentException;
1212
use Prism\Prism\Events\Broadcasting\ArtifactBroadcast;
13+
use Prism\Prism\Events\Broadcasting\CitationBroadcast;
1314
use Prism\Prism\Events\Broadcasting\ErrorBroadcast;
1415
use Prism\Prism\Events\Broadcasting\ProviderToolEventBroadcast;
1516
use Prism\Prism\Events\Broadcasting\StepFinishBroadcast;
@@ -26,6 +27,7 @@
2627
use Prism\Prism\Events\Broadcasting\ToolCallDeltaBroadcast;
2728
use Prism\Prism\Events\Broadcasting\ToolResultBroadcast;
2829
use Prism\Prism\Streaming\Events\ArtifactEvent;
30+
use Prism\Prism\Streaming\Events\CitationEvent;
2931
use Prism\Prism\Streaming\Events\ErrorEvent;
3032
use Prism\Prism\Streaming\Events\ProviderToolEvent;
3133
use Prism\Prism\Streaming\Events\StepFinishEvent;
@@ -86,6 +88,7 @@ protected function broadcastEvent(StreamEvent $event): ShouldBroadcast
8688
ToolCallDeltaEvent::class => new ToolCallDeltaBroadcast($event, $this->channels),
8789
ToolResultEvent::class => new ToolResultBroadcast($event, $this->channels),
8890
ArtifactEvent::class => new ArtifactBroadcast($event, $this->channels),
91+
CitationEvent::class => new CitationBroadcast($event, $this->channels),
8992
ProviderToolEvent::class => new ProviderToolEventBroadcast($event, $this->channels),
9093
ErrorEvent::class => new ErrorBroadcast($event, $this->channels),
9194
StepFinishEvent::class => new StepFinishBroadcast($event, $this->channels),

src/Testing/PrismFake.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use Prism\Prism\Images\Request as ImageRequest;
1919
use Prism\Prism\Images\Response as ImageResponse;
2020
use Prism\Prism\Moderation\Response as ModerationResponse;
21+
use Prism\Prism\Prism;
2122
use Prism\Prism\Providers\Provider;
2223
use Prism\Prism\Streaming\EventID;
2324
use Prism\Prism\Streaming\Events\StepFinishEvent;
@@ -153,7 +154,7 @@ public function speechToText(SpeechToTextRequest $request): AudioTextResponse
153154
*
154155
* Behavior:
155156
* 1. Records the incoming {@link TextRequest}
156-
* 2. Pulls the next fixture from the list supplied to {@see \Prism\Prism\Prism::fake()}.
157+
* 2. Pulls the next fixture from the list supplied to {@see Prism::fake()}.
157158
* 3. Yields an appropriate stream of events.
158159
*
159160
* Supported fixture type:

tests/Streaming/Adapters/BroadcastAdapterTest.php

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
use Illuminate\Broadcasting\Channel;
66
use Illuminate\Broadcasting\PrivateChannel;
77
use Illuminate\Support\Facades\Event;
8+
use Prism\Prism\Enums\Citations\CitationSourceType;
89
use Prism\Prism\Enums\FinishReason;
10+
use Prism\Prism\Events\Broadcasting\CitationBroadcast;
911
use Prism\Prism\Events\Broadcasting\ErrorBroadcast;
1012
use Prism\Prism\Events\Broadcasting\ProviderToolEventBroadcast;
1113
use Prism\Prism\Events\Broadcasting\StreamEndBroadcast;
@@ -20,6 +22,7 @@
2022
use Prism\Prism\Events\Broadcasting\ToolCallDeltaBroadcast;
2123
use Prism\Prism\Events\Broadcasting\ToolResultBroadcast;
2224
use Prism\Prism\Streaming\Adapters\BroadcastAdapter;
25+
use Prism\Prism\Streaming\Events\CitationEvent;
2326
use Prism\Prism\Streaming\Events\ErrorEvent;
2427
use Prism\Prism\Streaming\Events\ProviderToolEvent;
2528
use Prism\Prism\Streaming\Events\StreamEndEvent;
@@ -34,6 +37,7 @@
3437
use Prism\Prism\Streaming\Events\ToolCallDeltaEvent;
3538
use Prism\Prism\Streaming\Events\ToolCallEvent;
3639
use Prism\Prism\Streaming\Events\ToolResultEvent;
40+
use Prism\Prism\ValueObjects\Citation;
3741
use Prism\Prism\ValueObjects\ToolCall;
3842
use Prism\Prism\ValueObjects\ToolResult;
3943
use Prism\Prism\ValueObjects\Usage;
@@ -345,6 +349,42 @@ function createBroadcastEventGenerator(array $events): Generator
345349
});
346350
});
347351

352+
it('broadcasts citation events correctly', function (): void {
353+
$citation = new Citation(
354+
sourceType: CitationSourceType::Document,
355+
source: 0,
356+
sourceText: 'The quick brown fox',
357+
sourceTitle: 'Test Document',
358+
);
359+
360+
$event = new CitationEvent(
361+
id: 'evt-123',
362+
timestamp: 1640995200,
363+
citation: $citation,
364+
messageId: 'msg-456',
365+
blockIndex: 0,
366+
);
367+
$channel = new Channel('test-channel');
368+
369+
$adapter = new BroadcastAdapter($channel);
370+
($adapter)(createBroadcastEventGenerator([$event]));
371+
372+
Event::assertDispatched(CitationBroadcast::class, function ($broadcastEvent) use ($channel): bool {
373+
expect($broadcastEvent->broadcastAs())->toBe('citation');
374+
expect($broadcastEvent->broadcastOn())->toBe([$channel]);
375+
376+
$data = $broadcastEvent->broadcastWith();
377+
expect($data['id'])->toBe('evt-123');
378+
expect($data['message_id'])->toBe('msg-456');
379+
expect($data['block_index'])->toBe(0);
380+
expect($data['citation']['source_type'])->toBe('document');
381+
expect($data['citation']['source_text'])->toBe('The quick brown fox');
382+
expect($data['citation']['source_title'])->toBe('Test Document');
383+
384+
return true;
385+
});
386+
});
387+
348388
it('validates broadcast event structure for multiple event types', function (): void {
349389
$usage = new Usage(promptTokens: 10, completionTokens: 5);
350390
$events = [

tests/Tools/LaravelMcpToolTest.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,9 @@ public function schema(JsonSchema $schema): array
6464

6565
$result = $tool->handle();
6666

67-
expect($result)->toContain('"status": "success"')
68-
->and($result)->toContain('"id": 123')
69-
->and($result)->toContain('"name": "Test"');
67+
expect($result)->toContain('"status":"success"')
68+
->and($result)->toContain('"id":123')
69+
->and($result)->toContain('"name":"Test"');
7070
});
7171

7272
it('can handle a tool that returns a ResponseFactory from Response::make()', function (): void {

0 commit comments

Comments
 (0)