Streaming Responses in Java and C# with the Official SDKs
Claude streams responses via server-sent events, and the Java and C# SDKs expõem esse stream através de seus mecanismos nativos: um iterador bloqueante em Java, um IAsyncEnumerable em C#. Esta página mostra ambos lado a lado.
Resumo
O streaming permite que um chamador comece a renderizar a resposta de Claude assim que os tokens chegam, em vez de esperar pela resposta completa.
Nos bastidores, cada SDK oficial consome a mesma sequência de eventos server-sent: um evento message_start, uma série de eventos content_block_delta carregando fragmentos de texto ou tool_use, e um evento message_stop finalizando tudo.
Java expõe essa sequência através de um iterador de streaming síncrono sobre o qual você itera, correspondendo à forma como o SDK Java expõe chamadas bloqueantes em geral.
C# o expõe através de um padrão de streaming assíncrono construído sobre IAsyncEnumerable ou um estilo de callback de evento, correspondendo às convenções async/await do .NET.
Ambos acabam produzindo o mesmo resultado prático: seu código vê o texto à medida que é gerado, mais uma mensagem final montada assim que o stream fecha.
Receita
Cartão de receita de referência rápida - pronto para copiar e colar.
StreamResponse<MessageStreamEvent> stream = client.messages().createStreaming(
MessageCreateParams.builder()
.model(Model.CLAUDE_SONNET_5)
.maxTokens(512)
.addUserMessage("Escreva um haicai sobre streaming de dados.")
.build()
);
stream.stream().forEach(event -> {
if (event.isContentBlockDelta()) {
event.asContentBlockDelta().delta().text().ifPresent(System.out::print);
}
});await foreach (var streamEvent in client.Messages.StreamClaudeMessageAsync(new MessageParameters
{
Model = AnthropicModels.ClaudeSonnet5,
MaxTokens = 512,
Messages = new List<Message> { new Message(RoleType.User, "Escreva um haicai sobre streaming de dados.") }
}))
{
if (streamEvent is TextDeltaEvent textDelta)
{
Console.Write(textDelta.Text);
}
}Quando usar isso:
- Você está construindo uma interface de chat ou ferramenta de linha de comando onde os usuários devem ver a saída à medida que é gerada, não após uma espera de vários segundos.
- Você deseja iniciar o pós-processamento de texto (como TTS em nível de frase ou logging incremental) antes que a resposta completa termine.
- Você está transmitindo os argumentos de um bloco
tool_useà medida que são gerados, para integrações de ferramentas sensíveis à latência.
Exemplo de Trabalho
Um programa Java completo e um programa C# completo, cada um transmitindo uma resposta e imprimindo deltas de texto à medida que chegam.
import com.anthropic.client.AnthropicClient;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;
import com.anthropic.core.http.StreamResponse;
import com.anthropic.models.messages.MessageCreateParams;
import com.anthropic.models.messages.MessageStreamEvent;
import com.anthropic.models.messages.Model;
public class StreamingExample {
public static void main(String[] args) {
AnthropicClient client = AnthropicOkHttpClient.fromEnv();
MessageCreateParams params = MessageCreateParams.builder()
.model(Model.CLAUDE_SONNET_5)
.maxTokens(512)
.addUserMessage("Liste três benefícios dos eventos server-sent.")
.build();
try (StreamResponse<MessageStreamEvent> stream = client.messages().createStreaming(params)) {
stream.stream().forEach(event -> {
if (event.isContentBlockDelta()) {
event.asContentBlockDelta().delta().text().ifPresent(System.out::print);
} else if (event.isMessageStop()) {
System.out.println();
System.out.println("[stream complete]");
}
});
}
}
}using Anthropic.SDK;
using Anthropic.SDK.Messaging;
var client = new AnthropicClient(Environment.GetEnvironmentVariable("ANTHROPIC_API_KEY"));
var parameters = new MessageParameters
{
Model = AnthropicModels.ClaudeSonnet5,
MaxTokens = 512,
Messages = new List<Message>
{
new Message(RoleType.User, "Liste três benefícios dos eventos server-sent.")
}
};
await foreach (var streamEvent in client.Messages.StreamClaudeMessageAsync(parameters))
{
switch (streamEvent)
{
case TextDeltaEvent textDelta:
Console.Write(textDelta.Text);
break;
case MessageStopEvent:
Console.WriteLine();
Console.WriteLine("[stream complete]");
break;
}
}O que isso demonstra:
- O
StreamResponsedo Java é um iteradorAutoCloseableconsumido comstream(), portanto, envolvê-lo em um bloco try-with-resources garante que a conexão HTTP subjacente feche mesmo em caso de exceção. - Java distingue os tipos de evento com verificações de estilo
isContentBlockDelta()/asContentBlockDelta(), espelhando como ele lê blocos de conteúdo regulares. - O
await foreachdo C# sobre umIAsyncEnumerableé a maneira idiomática do .NET de consumir qualquer stream assíncrono, não algo específico deste SDK. - C# distingue os tipos de evento com uma correspondência de padrão
switchno tipo de tempo de execução do evento, o equivalente a stream assíncrono das verificaçõesisX()do Java.
Mergulho Profundo
Como Funciona
- Ambos os SDKs abrem uma única conexão HTTP com
Accept: text/event-streame a mantêm aberta até que o modelo termine ou ocorra um erro. - Cada evento server-sent nessa conexão se torna um item no iterador ou sequência assíncrona do SDK; o SDK analisa a carga útil bruta do evento em um objeto de evento tipado antes de entregá-lo ao seu código.
- O texto chega incrementalmente como eventos
content_block_delta; um blocotool_usetambém pode chegar incrementalmente como deltas JSON parciais em vez de tudo de uma vez. - O stream termina com um evento
message_stop; se a conexão cair antes que esse evento chegue, ambos os SDKs o expõem como um erro em vez de uma parada antecipada silenciosa.
Tipos de Evento que Você Verá
| Evento | Significado |
|---|---|
message_start | A resposta começou; carrega metadados iniciais da mensagem (ID, modelo, role). |
content_block_start | Um novo bloco de conteúdo (texto ou tool_use) começou. |
content_block_delta | Um pedaço incremental de um bloco de conteúdo: um fragmento de texto ou um fragmento JSON parcial para entrada de tool_use. |
content_block_stop | O bloco de conteúdo atual está completo. |
message_delta | Atualizações de metadados de mensagem de nível superior, incluindo o stop_reason final. |
message_stop | A resposta está completamente concluída. |
Notas Java
// Acumulando o texto completo ao lado de sua transmissão, para logging ou armazenamento.
StringBuilder fullText = new StringBuilder();
stream.stream().forEach(event -> {
if (event.isContentBlockDelta()) {
event.asContentBlockDelta().delta().text().ifPresent(text -> {
System.out.print(text);
fullText.append(text);
});
}
});
System.out.println("\nResposta completa: " + fullText);Notas C#
// Acumulando o texto completo ao lado de sua transmissão, usando um StringBuilder.
var fullText = new StringBuilder();
await foreach (var streamEvent in client.Messages.StreamClaudeMessageAsync(parameters))
{
if (streamEvent is TextDeltaEvent textDelta)
{
Console.Write(textDelta.Text);
fullText.Append(textDelta.Text);
}
}
Console.WriteLine($"\nResposta completa: {fullText}");Armadilhas
- Não fechar o stream Java -
StreamResponsemantém uma conexão HTTP aberta; falhar em fechá-la (ou usar try-with-resources) vaza conexões sob carga. Correção: sempre envolva chamadas de streaming em try-with-resources ou chameclose()explicitamente. - Bloquear a thread chamadora em C# com
.Resultou.Wait()- chamar um método de streaming síncronamente anula o propósito deIAsyncEnumerablee arrisca deadlocks em alguns contextos de hospedagem. Correção: useawait foreachdentro de um métodoasyncem toda a cadeia de chamadas. - Assumir que o texto chega em palavras ou frases completas - fragmentos de
content_block_deltapodem ser divididos no meio de uma palavra. Correção: bufferize e renderize incrementalmente sem assumir que os limites dos deltas se alinham com qualquer coisa linguística. - Analisar JSON de entrada de
tool_useantes que o stream seja concluído - deltas JSON parciais para um blocotool_usenão são JSON válido por si só até serem totalmente montados. Correção: acumule os fragmentos brutos e analise apenas quandocontent_block_stopfor disparado para esse bloco. - Ignorar o
stop_reasondemessage_delta- código que lê apenas deltas de texto pode perder que a resposta parou cedo devido amax_tokensem vez de completar naturalmente. Correção: verifique ostop_reasonfinal demessage_deltaou da mensagem montada antes de tratar a saída como completa. - Tratar uma conexão interrompida como um fim normal de stream - ambos os SDKs distinguem um
message_stoplimpo de uma falha de conexão, mas código que não verifica erros de stream pode tratar silenciosamente uma resposta truncada como completa. Correção: lide com a exceção ou caminho de erro que ambos os SDKs expõem ao lado da iteração do caminho feliz.
Alternativas
| Alternativa | Use Quando | Não Use Quando |
|---|---|---|
Messages.New não-streaming / chamada síncrona | A resposta completa é necessária antes que qualquer processamento possa começar de qualquer maneira (por exemplo, saída estruturada que você deve analisar completamente) | Você está construindo uma interface interativa onde a latência percebida importa |
| Iterador de streaming do Go | Seu serviço é escrito em Go em vez de Java ou C# | Você está especificamente trabalhando no ecossistema JVM ou .NET |
| Camada de compatibilidade OpenAI com streaming | Você está em migração de uma base de código Java ou C# existente integrada com OpenAI e deseja o iterador de chunk no formato OpenAI temporariamente | Você deseja acesso nativo completo a tipos de eventos de streaming específicos do Claude, como deltas de tool_use |
FAQs
A chamada de streaming do Java bloqueia a thread chamadora?
Sim, a iteração stream() do Java é síncrona/bloqueante, correspondendo à forma como o restante do SDK Java expõe as chamadas. Execute-a em uma thread de segundo plano se precisar manter uma thread de UI responsiva.
A chamada de streaming do C# é realmente assíncrona?
Sim, é construída sobre IAsyncEnumerable e consumida com await foreach, seguindo a convenção padrão de streaming assíncrono do .NET. Evite bloqueá-la síncronamente com .Result ou .Wait().
Qual tipo de evento carrega o texto real à medida que é gerado?
Eventos content_block_delta carregam fragmentos de texto incrementais (ou JSON parcial de tool_use). Ambos os SDKs expõem isso como um delta tipado que você verifica quanto ao conteúdo de texto.
Como sei quando o stream está completamente pronto?
Ambos os SDKs expõem um evento message_stop (ou seu equivalente tipado) como o último item no stream. Verifique-o explicitamente em vez de assumir que o término do loop significa sucesso.
Posso transmitir os argumentos de um bloco tool_use?
Sim. A entrada de um bloco tool_use pode chegar como deltas JSON parciais em vários eventos content_block_delta; acumule os fragmentos e analise o JSON apenas quando o evento content_block_stop do bloco for disparado.
O que acontece se a conexão de rede cair no meio do stream?
Ambos os SDKs expõem isso como um erro distinto de um message_stop limpo. Lide com esse caminho de erro explicitamente para que uma resposta truncada não seja confundida com uma completa.
Preciso fechar o stream Java manualmente?
Sim, a menos que você use try-with-resources, pois StreamResponse mantém uma conexão HTTP aberta que deve ser liberada quando você terminar.
O protocolo subjacente é diferente entre Java e C#?
Não. Ambos consomem os mesmos eventos server-sent do mesmo endpoint de streaming da API Messages. Apenas o mecanismo em nível de linguagem para iterar sobre esses eventos difere.
Posso acumular o texto completo da resposta enquanto também o transmito?
Sim, em ambas as linguagens. Anexe cada delta de texto a um StringBuilder (em Java e C#) à medida que ele chega, ao lado de qualquer renderização incremental que você esteja fazendo.
O Go suporta streaming da mesma forma?
Go expõe o streaming através de seu próprio padrão de iterador, consumindo os mesmos eventos server-sent subjacentes. Veja o artigo específico do Go nesta seção para esse exemplo.
Relacionado
- Chamando Claude do Go com o SDK Oficial - o padrão de streaming equivalente em Go, usando sua própria convenção de iterador
- Noções Básicas de Outros SDKs Oficiais - primeiras chamadas não-streaming em Java e C#, antes de adicionar streaming
- Como os Eventos Server-Sent Potencializam as Respostas de Streaming do Claude - o protocolo de eventos subjacente que ambos os SDKs consomem aqui
- Tratando JSON Parcial Durante Chamadas de Ferramentas Transmitidas - uma análise mais aprofundada da acumulação de deltas
tool_usemencionada em Armadilhas - Referência de Tipos de Eventos de Streaming - o catálogo completo de tipos de eventos server-sent referenciados na tabela acima
Versões de Stack: Escrito contra a linha de modelos Claude atual em ~junho de 2026 - Claude Fable 5, Claude Opus 4.8, Claude Sonnet 5 (o padrão), e Claude Haiku 4.5 - e os SDKs oficiais atuais para Go, Java, C#, PHP e Ruby. Nomes de modelos, versões de SDK e preços mudam rapidamente - verifique os detalhes atuais em platform.claude.com/docs antes de confiar neles.