Skip to content

[Agent] Streaming ResultInterface parts #680

@franzwilding

Description

@franzwilding

I'm trying to use symfony/ai as a backend for https://ai-sdk.dev. ai-sdk requires the backend to send the content in a structured way (also when streaming). This allows the frontend to display status information, internal tool calling, reasoning etc. (like in le chat or chat-gpt).

Basic content streaming of one text part works well, by wrapping each chunk of the content response in a small envelope:

$result = $defaultAgent->call($messages, ['stream' => true]);
$response = new StreamedResponse(function () use ($result) {

        $send = function (string $type, array $data = []) {
            echo 'data: ' . json_encode(array_merge(['type' => $type], $data), JSON_UNESCAPED_SLASHES) . "\n\n";
            @ob_flush(); @flush();
        };

        $send('start');
        $textId = (string) new Ulid();
        $send('text-start', ['id' => $textId]);
        
        foreach ($result->getContent() as $part) {
            $send('text-delta', ['id' => $textId, 'delta' => $part]);
        }

        $send('text-end', ['id' => $textId]);
        $send('finish');
        echo "data: [DONE]\n\n";
        @ob_flush();
        @flush();
}

However when i want to stream tool calling status or internal reasoning, things are getting more complicated.

I tried to implement a custom OutputProcessor, however the internal agentic flow breaks, when replacing tool_calling result with a custom envenlope.

The best result I got, was writing to the output-stream directly from an output-processor, however this is not very elegant or maintanable.

Basically what I need would be:

  • a better $result->getResultPart()s generator, that will not return simple content, but instances of ResultInterface or mybe a new FinalResultInterface?
  • A new TextChunkResult to not get a mixture of string and ResultInterface objects. This would allow me to add my envelopes in the $result->getResultParts() loop depending on the part class.

I tink this would be a very nice feature for any UI that wants to display whats going on inside one agent call.

Metadata

Metadata

Assignees

No one assigned

    Labels

    RFCRFC = Request For Comments (proposals about features that you want to be discussed)

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions