Skip to content

[Agent] Unable to access sources metadata with streaming #833

@JorrinKievit

Description

@JorrinKievit

I am implementing the Symfony AI Agent in combination with the ai-sdk. I want to display the used sources in the UI, so I want to dispatch these in an event stream using the ai-sdk Data Stream Protocol. However, during streaming, the metadata seems to reset and sources are always null. Without the stream option this works fine.

Should this be done in a different order? The sources are present at some point, but seem to reset while actually streaming the content back.

This is basically all that I am doing:

public function __invoke(ChatRequest $request): StreamedResponse
{
    $platform = PlatformFactory::create(env('OPENAI_API_KEY'), HttpClient::create());
    
    $toolBox = new ToolBox([
        new RandomTool(),
    ]);
    $processor = new AgentProcessor($toolBox, includeSources: true);
    $agent = new Agent($platform, 'gpt-4o-mini', [$processor], [$processor]);

    $messages = new MessageBag();
    foreach ($request->messages() as $message) {
        foreach ($message['parts'] as $part) {
            if ($part['type'] !== 'text') {
                continue;
            }
            $messages->add(Message::ofUser($part['text']));
        }
    }

    $result = $agent->call($messages, [
        'stream' => true
    ]);

    $response = new StreamedResponse(function () use ($result) {
        $messageId = 'msg_' . bin2hex(random_bytes(16));
        $textBlockId = 'text_' . bin2hex(random_bytes(16));
        
        $this->sendSSE(['type' => 'start', 'messageId' => $messageId]);
        $this->sendSSE(['type' => 'text-start', 'id' => $textBlockId]);
        
        foreach ($result->getContent() as $chunk) {
            $text = (string)$chunk;
            if ($text !== '') {
                $this->sendSSE([
                    'type' => 'text-delta',
                    'id' => $textBlockId,
                    'delta' => $text
                ]);
            }
        }
        
        $this->sendSSE(['type' => 'text-end', 'id' => $textBlockId]);
        
        // This is always null
        $sources = $result->getMetadata()->get('sources', []);
        foreach ($sources as $source) {
            if (!$source instanceof Source) {
                continue;
            }
            $this->sendSSE([
                'type' => 'source-url',
                'sourceId' => $source->getReference(),
                'url' => $source->getReference(),
                'title' => $source->getName()
            ]);
        }
        
        $this->sendSSE(['type' => 'finish']);
        
        echo "data: [DONE]\n\n";
        flush();
    });

    
    $response->headers->set('Content-Type', 'text/event-stream');
    $response->headers->set('Cache-Control', 'no-cache');
    $response->headers->set('Connection', 'keep-alive');
    $response->headers->set('X-Accel-Buffering', 'no');
    $response->headers->set('x-vercel-ai-ui-message-stream', 'v1');
    
    return $response;
}

private function sendSSE(array $data): void
{
    echo 'data: ' . json_encode($data, JSON_UNESCAPED_UNICODE) . "\n\n";
    flush();
}

Metadata

Metadata

Assignees

Labels

AgentIssues & PRs about the AI Agent componentBugSomething isn't workingStatus: Needs Review

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions