Skip to content

Commit 36bcdc1

Browse files
committed
Now getting a message back but it’s a single chunk
1 parent aa50329 commit 36bcdc1

3 files changed

Lines changed: 94 additions & 46 deletions

File tree

claude-agent-github-wiki/app/response/[runId]/page.tsx

Lines changed: 48 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,11 @@ import {
1818
CheckCircle,
1919
Loader2,
2020
XCircle,
21-
Terminal,
22-
FileText,
2321
} from "lucide-react";
2422
import { agentStream } from "@/trigger/agent-stream";
2523
import { analyzeRepo } from "@/trigger/analyze-repo";
2624
import { useState } from "react";
2725
import ReactMarkdown from "react-markdown";
28-
import type { SDKMessage } from "@anthropic-ai/claude-agent-sdk";
2926

3027
export default function ResponsePage() {
3128
const params = useParams();
@@ -45,9 +42,24 @@ export default function ResponsePage() {
4542
// Subscribe to agent stream
4643
const { parts, error: streamError } = useRealtimeStream(agentStream, runId, {
4744
accessToken,
48-
timeoutInSeconds: 60,
45+
timeoutInSeconds: 300, // 5 minute timeout
46+
throttleInMs: 50,
4947
});
5048

49+
// Debug logging
50+
console.log("[Frontend] Stream state:", {
51+
runId,
52+
accessToken: accessToken ? "present" : "missing",
53+
partsLength: parts?.length || 0,
54+
streamError: streamError?.message,
55+
runStatus: run?.status,
56+
});
57+
58+
// Log when parts change
59+
if (parts && parts.length > 0) {
60+
console.log(`[Frontend] Received ${parts.length} parts, latest:`, parts[parts.length - 1]?.slice(0, 100));
61+
}
62+
5163
const handleAbort = async () => {
5264
setIsAborting(true);
5365
try {
@@ -70,6 +82,27 @@ export default function ResponsePage() {
7082
router.push("/");
7183
};
7284

85+
// Process streamed text chunks
86+
const getFormattedContent = () => {
87+
if (!parts || parts.length === 0) return null;
88+
89+
// parts is now an array of text strings
90+
const combinedText = parts.join("");
91+
92+
// Return the combined text as markdown
93+
if (combinedText.trim()) {
94+
return (
95+
<div className="prose prose-sm dark:prose-invert max-w-none">
96+
<ReactMarkdown>{combinedText}</ReactMarkdown>
97+
</div>
98+
);
99+
}
100+
101+
return null;
102+
};
103+
104+
const formattedContent = getFormattedContent();
105+
73106
// Determine status
74107
const getStatus = () => {
75108
if (runError || streamError) return "error";
@@ -176,27 +209,25 @@ export default function ResponsePage() {
176209
)}
177210

178211
{/* Response Content */}
179-
{parts && parts.length > 0 && (
212+
{(formattedContent || (parts && parts.length > 0)) && (
180213
<Card>
181214
<CardHeader>
182215
<CardTitle>Analysis Result</CardTitle>
183-
<CardDescription>
184-
Claude is analyzing your repository. Tool usage is shown in
185-
collapsible sections.
186-
</CardDescription>
187216
</CardHeader>
188-
{parts.map((part, i) => (
189-
<CardContent key={i} className="space-y-4">
190-
{part}
191-
</CardContent>
192-
))}
217+
<CardContent>
218+
{formattedContent || (
219+
<div className="text-muted-foreground">
220+
Waiting for response... (Received {parts?.length || 0} chunks)
221+
</div>
222+
)}
223+
</CardContent>
193224
</Card>
194225
)}
195226

196-
{/* Show message count for debugging */}
197-
{parts && (
227+
{/* Show streaming status for debugging */}
228+
{parts && parts.length > 0 && status === "running" && (
198229
<div className="mt-4 text-xs text-muted-foreground text-center">
199-
Received {parts.length} messages
230+
Streaming response... ({parts.length} text chunks received)
200231
</div>
201232
)}
202233

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import { streams } from "@trigger.dev/sdk";
2-
import { type SDKMessage } from "@anthropic-ai/claude-agent-sdk";
32

4-
// REALTIME STREAMS V2: Define typed stream for all agent messages
5-
// This stream will carry every SDKMessage from the Claude Agent (text, tool_use, tool_result)
3+
// REALTIME STREAMS V2: Define typed stream for text responses only
4+
// This stream will carry just the text strings from Claude's responses
65
export const agentStream = streams.define<string>({
76
id: "agent-messages",
8-
});
7+
});

claude-agent-github-wiki/trigger/analyze-repo.ts

Lines changed: 43 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { query, type SDKMessage } from "@anthropic-ai/claude-agent-sdk";
2-
import { metadata, schemaTask, streams } from "@trigger.dev/sdk";
1+
import { query } from "@anthropic-ai/claude-agent-sdk";
2+
import { logger, metadata, schemaTask } from "@trigger.dev/sdk";
33
import { z } from "zod";
44
import { mkdtemp, rm } from "fs/promises";
55
import { tmpdir } from "os";
@@ -90,17 +90,19 @@ export const analyzeRepo = schemaTask({
9090
const systemPrompt =
9191
`You are analyzing the ${repoName} repository that has been cloned to your current working directory.
9292
93-
First, provide a brief 2-3 sentence overview of what this repository contains and its main purpose by examining the file structure and key files like README.md, package.json, etc.
93+
Your task is to answer the user's question about this repository. Use the available tools (Bash, Read, Grep, Glob) to explore the codebase as needed, but DO NOT narrate your exploration process. The user cannot see your tool usage.
9494
95-
Then, provide a detailed response to the user's question. Focus on technical accuracy and be specific with file references and code examples where relevant.
95+
After exploring the repository, provide:
96+
1. A brief 2-3 sentence overview of what this repository contains and its main purpose
97+
2. A detailed answer to the user's specific question
9698
97-
Start by exploring the repository structure with ls and reading key files, then answer this question: ${question}`;
99+
User's question: ${question}
100+
101+
Important: Only include your final analysis in your response. Do not include phrases like "Let me explore", "Let me check", "Now let me examine", etc. The user only wants to see the final answer.`;
98102

99103
metadata.set("status", "Generating response...");
100104
metadata.set("progress", 70);
101105

102-
const messages: SDKMessage[] = [];
103-
104106
// Use Claude Agent SDK to analyze the repository
105107
const result = query({
106108
prompt: systemPrompt,
@@ -123,16 +125,41 @@ Start by exploring the repository structure with ls and reading key files, then
123125
metadata.set("status", "Streaming response...");
124126
metadata.set("progress", 90);
125127

126-
// Stream the response
127-
const { stream: readableStream, waitUntilComplete } = agentStream.pipe(
128-
result,
129-
);
128+
// Create an async generator that yields text strings from the messages
129+
const extractText = async function* () {
130+
for await (const message of result) {
131+
logger.debug("Message type", { type: message.type });
132+
133+
// Extract text from assistant messages
134+
if (message.type === "assistant" && message.message?.content) {
135+
for (const block of message.message.content) {
136+
if (block.type === "text") {
137+
const text = block.text;
138+
logger.debug("Streaming text block", {
139+
length: text.length,
140+
preview: text.slice(0, 100),
141+
});
142+
143+
// Split large text blocks into smaller chunks for better streaming
144+
// This helps when Claude sends a large response in a single message
145+
const chunkSize = 20; // Send 20 chars at a time
146+
if (text.length > chunkSize) {
147+
for (let i = 0; i < text.length; i += chunkSize) {
148+
yield text.slice(i, i + chunkSize);
149+
}
150+
} else {
151+
yield text;
152+
}
153+
}
154+
}
155+
}
156+
}
157+
};
130158

159+
// Use pipe to stream the text - this properly handles backpressure
160+
const { waitUntilComplete } = agentStream.pipe(extractText());
131161
await waitUntilComplete();
132162

133-
// Wait for the stream to complete
134-
await readableStream.getReader().closed;
135-
136163
metadata.set("status", "Completed");
137164
metadata.set("progress", 100);
138165

@@ -161,19 +188,10 @@ Start by exploring the repository structure with ls and reading key files, then
161188
metadata.set("status", "Failed");
162189
metadata.set("error", error.message);
163190

164-
// Stream error message
191+
// Stream error message as plain text
165192
agentStream.writer({
166193
execute: async ({ write }) => {
167-
write({
168-
type: "assistant",
169-
message: {
170-
role: "assistant",
171-
content: [{
172-
type: "text",
173-
text: `Error: ${error.message}`,
174-
}],
175-
},
176-
} as SDKMessage);
194+
write(`Error: ${error.message}`);
177195
},
178196
});
179197

0 commit comments

Comments
 (0)