Skip to content

Commit 50bac33

Browse files
refactor(frontend): dedup streamChat, remove dead code, tighten imports and memoization
1 parent 8007ba0 commit 50bac33

11 files changed

Lines changed: 55 additions & 173 deletions

File tree

content-gen/src/app/frontend/src/App.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ function App() {
3232
newConversation,
3333
confirmBrief,
3434
cancelBrief,
35-
productsStartOver,
3635
selectProduct,
3736
toggleHistory,
3837
} = useConversationActions();
@@ -63,7 +62,6 @@ function App() {
6362
onBriefCancel={cancelBrief}
6463
onGenerateContent={generateContent}
6564
onRegenerateContent={generateContent}
66-
onProductsStartOver={productsStartOver}
6765
onProductSelect={selectProduct}
6866
onNewConversation={newConversation}
6967
/>

content-gen/src/app/frontend/src/api/index.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@ import type {
1010
AppConfig,
1111
} from '../types';
1212
import httpClient from './httpClient';
13-
import { parseSSEStream } from '../utils/sseParser';
14-
import { getGenerationStage } from '../utils/generationStages';
13+
import { parseSSEStream, getGenerationStage } from '../utils';
1514

1615
/**
1716
* Get application configuration including feature flags
@@ -178,7 +177,6 @@ export async function* streamGenerateContent(
178177
} as AgentResponse;
179178
}
180179
} catch (error) {
181-
console.error(`Error polling task ${taskId}:`, error);
182180
// Continue polling on transient errors
183181
if (attempts >= maxAttempts) {
184182
throw error;

content-gen/src/app/frontend/src/components/ChatPanel.tsx

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ interface ChatPanelProps {
3030
onBriefCancel?: () => void;
3131
onGenerateContent?: () => void;
3232
onRegenerateContent?: () => void;
33-
onProductsStartOver?: () => void;
3433
onProductSelect?: (product: Product) => void;
3534
onNewConversation?: () => void;
3635
}
@@ -42,7 +41,6 @@ export const ChatPanel = memo(function ChatPanel({
4241
onBriefCancel,
4342
onGenerateContent,
4443
onRegenerateContent,
45-
onProductsStartOver,
4644
onProductSelect,
4745
onNewConversation,
4846
}: ChatPanelProps) {
@@ -88,9 +86,6 @@ export const ChatPanel = memo(function ChatPanel({
8886

8987
const isInputDisabled = useMemo(() => isLoading, [isLoading]);
9088

91-
const startOverFallback = useCallback(() => {}, []);
92-
const effectiveProductsStartOver = onProductsStartOver || startOverFallback;
93-
9489
return (
9590
<div className="chat-container">
9691
{/* Messages Area */}
@@ -142,7 +137,6 @@ export const ChatPanel = memo(function ChatPanel({
142137
products={selectedProducts}
143138
availableProducts={availableProducts}
144139
onConfirm={onGenerateContent!}
145-
onStartOver={effectiveProductsStartOver}
146140
isAwaitingResponse={isLoading}
147141
onProductSelect={onProductSelect}
148142
disabled={isLoading}

content-gen/src/app/frontend/src/components/InlineContentPreview.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@ import {
77
import { ShieldError20Regular } from '@fluentui/react-icons';
88
import type { GeneratedContent, Product } from '../types';
99
import { useWindowSize } from '../hooks/useWindowSize';
10-
import { isContentFilterError, getErrorMessage } from '../utils/contentErrors';
11-
import { downloadImage } from '../utils/downloadImage';
10+
import { isContentFilterError, getErrorMessage, downloadImage } from '../utils';
1211
import { useCopyToClipboard } from '../hooks/useCopyToClipboard';
1312
import { ImagePreviewCard } from './ImagePreviewCard';
1413
import { ComplianceSection } from './ComplianceSection';

content-gen/src/app/frontend/src/components/ProductReview.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import { ProductCard } from './ProductCard';
1313
interface ProductReviewProps {
1414
products: Product[];
1515
onConfirm: () => void;
16-
onStartOver: () => void;
1716
isAwaitingResponse?: boolean;
1817
availableProducts?: Product[];
1918
onProductSelect?: (product: Product) => void;
@@ -23,7 +22,6 @@ interface ProductReviewProps {
2322
export const ProductReview = memo(function ProductReview({
2423
products,
2524
onConfirm,
26-
onStartOver: _onStartOver,
2725
isAwaitingResponse = false,
2826
availableProducts = [],
2927
onProductSelect,

content-gen/src/app/frontend/src/components/ViolationCard.tsx

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { memo } from 'react';
1+
import { memo, useMemo } from 'react';
22
import {
33
Text,
44
} from '@fluentui/react-components';
@@ -17,7 +17,7 @@ export interface ViolationCardProps {
1717
* A single compliance violation row with severity-coloured icon and background.
1818
*/
1919
export const ViolationCard = memo(function ViolationCard({ violation }: ViolationCardProps) {
20-
const getSeverityStyles = () => {
20+
const { icon, bg } = useMemo(() => {
2121
switch (violation.severity) {
2222
case 'error':
2323
return {
@@ -35,9 +35,7 @@ export const ViolationCard = memo(function ViolationCard({ violation }: Violatio
3535
bg: '#deecf9',
3636
};
3737
}
38-
};
39-
40-
const { icon, bg } = getSeverityStyles();
38+
}, [violation.severity]);
4139

4240
return (
4341
<div

content-gen/src/app/frontend/src/hooks/useChatOrchestrator.ts

Lines changed: 49 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { useCallback, type MutableRefObject } from 'react';
22

3-
import type { GeneratedContent } from '../types';
3+
import type { AgentResponse, GeneratedContent } from '../types';
44
import { createMessage, createErrorMessage, matchesAnyKeyword, createNameSwapper } from '../utils';
55
import {
66
useAppDispatch,
@@ -25,6 +25,42 @@ import {
2525
selectConversationTitle,
2626
setConversationTitle,
2727
} from '../store';
28+
import type { AppDispatch } from '../store';
29+
30+
/* ------------------------------------------------------------------ */
31+
/* Shared helper — consumes a streamChat generator and dispatches */
32+
/* the final assistant message. Used by branches 1-b, 3-b, 4-b. */
33+
/* ------------------------------------------------------------------ */
34+
35+
async function consumeStreamChat(
36+
stream: AsyncGenerator<AgentResponse>,
37+
dispatch: AppDispatch,
38+
): Promise<void> {
39+
let fullContent = '';
40+
let currentAgent = '';
41+
let messageAdded = false;
42+
43+
for await (const response of stream) {
44+
if (response.type === 'agent_response') {
45+
fullContent = response.content;
46+
currentAgent = response.agent || '';
47+
if ((response.is_final || response.requires_user_input) && !messageAdded) {
48+
dispatch(addMessage(createMessage('assistant', fullContent, currentAgent)));
49+
messageAdded = true;
50+
}
51+
} else if (response.type === 'error') {
52+
dispatch(
53+
addMessage(
54+
createMessage(
55+
'assistant',
56+
response.content || 'An error occurred while processing your request.',
57+
),
58+
),
59+
);
60+
messageAdded = true;
61+
}
62+
}
63+
}
2864

2965
/* ------------------------------------------------------------------ */
3066
/* Hook */
@@ -130,42 +166,11 @@ export function useChatOrchestrator(
130166
}
131167
} else {
132168
// --- 1-b General question while brief is pending -----------
133-
let fullContent = '';
134-
let currentAgent = '';
135-
let messageAdded = false;
136-
137169
dispatch(setGenerationStatus('Processing your question...'));
138-
for await (const response of streamChat(
139-
content,
140-
conversationId,
141-
userId,
142-
signal,
143-
)) {
144-
if (response.type === 'agent_response') {
145-
fullContent = response.content;
146-
currentAgent = response.agent || '';
147-
if (
148-
(response.is_final || response.requires_user_input) &&
149-
!messageAdded
150-
) {
151-
dispatch(
152-
addMessage(createMessage('assistant', fullContent, currentAgent)),
153-
);
154-
messageAdded = true;
155-
}
156-
} else if (response.type === 'error') {
157-
dispatch(
158-
addMessage(
159-
createMessage(
160-
'assistant',
161-
response.content ||
162-
'An error occurred while processing your request.',
163-
),
164-
),
165-
);
166-
messageAdded = true;
167-
}
168-
}
170+
await consumeStreamChat(
171+
streamChat(content, conversationId, userId, signal),
172+
dispatch,
173+
);
169174
dispatch(setGenerationStatus(''));
170175
}
171176

@@ -321,41 +326,11 @@ export function useChatOrchestrator(
321326
);
322327
} else {
323328
// --- 3-b General question after content generation --------
324-
let fullContent = '';
325-
let currentAgent = '';
326-
let messageAdded = false;
327-
328329
dispatch(setGenerationStatus('Processing your request...'));
329-
for await (const response of streamChat(
330-
content,
331-
conversationId,
332-
userId,
333-
signal,
334-
)) {
335-
if (response.type === 'agent_response') {
336-
fullContent = response.content;
337-
currentAgent = response.agent || '';
338-
if (
339-
(response.is_final || response.requires_user_input) &&
340-
!messageAdded
341-
) {
342-
dispatch(
343-
addMessage(createMessage('assistant', fullContent, currentAgent)),
344-
);
345-
messageAdded = true;
346-
}
347-
} else if (response.type === 'error') {
348-
dispatch(
349-
addMessage(
350-
createMessage(
351-
'assistant',
352-
response.content || 'An error occurred.',
353-
),
354-
),
355-
);
356-
messageAdded = true;
357-
}
358-
}
330+
await consumeStreamChat(
331+
streamChat(content, conversationId, userId, signal),
332+
dispatch,
333+
);
359334
dispatch(setGenerationStatus(''));
360335
}
361336

@@ -426,42 +401,11 @@ export function useChatOrchestrator(
426401
}
427402
} else {
428403
// --- 4-b Generic chat -----------------------------------
429-
let fullContent = '';
430-
let currentAgent = '';
431-
let messageAdded = false;
432-
433404
dispatch(setGenerationStatus('Processing your request...'));
434-
for await (const response of streamChat(
435-
content,
436-
conversationId,
437-
userId,
438-
signal,
439-
)) {
440-
if (response.type === 'agent_response') {
441-
fullContent = response.content;
442-
currentAgent = response.agent || '';
443-
if (
444-
(response.is_final || response.requires_user_input) &&
445-
!messageAdded
446-
) {
447-
dispatch(
448-
addMessage(createMessage('assistant', fullContent, currentAgent)),
449-
);
450-
messageAdded = true;
451-
}
452-
} else if (response.type === 'error') {
453-
dispatch(
454-
addMessage(
455-
createMessage(
456-
'assistant',
457-
response.content ||
458-
'An error occurred while processing your request.',
459-
),
460-
),
461-
);
462-
messageAdded = true;
463-
}
464-
}
405+
await consumeStreamChat(
406+
streamChat(content, conversationId, userId, signal),
407+
dispatch,
408+
);
465409
dispatch(setGenerationStatus(''));
466410
}
467411
}

content-gen/src/app/frontend/src/hooks/useConversationActions.ts

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -182,19 +182,6 @@ export function useConversationActions() {
182182
/* ------------------------------------------------------------ */
183183
/* Product actions */
184184
/* ------------------------------------------------------------ */
185-
const productsStartOver = useCallback(() => {
186-
dispatch(setSelectedProducts([]));
187-
dispatch(setConfirmedBrief(null));
188-
dispatch(
189-
addMessage(
190-
createMessage(
191-
'assistant',
192-
'Starting over. Please provide your creative brief to begin a new campaign.',
193-
),
194-
),
195-
);
196-
}, [dispatch]);
197-
198185
const selectProduct = useCallback(
199186
(product: Product) => {
200187
const isSelected = selectedProducts.some(
@@ -224,7 +211,6 @@ export function useConversationActions() {
224211
newConversation,
225212
confirmBrief,
226213
cancelBrief,
227-
productsStartOver,
228214
selectProduct,
229215
toggleHistory,
230216
};

content-gen/src/app/frontend/src/hooks/useDebounce.ts

Lines changed: 0 additions & 29 deletions
This file was deleted.

content-gen/src/app/frontend/src/utils/index.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,6 @@ export { BRIEF_FIELD_LABELS, BRIEF_DISPLAY_ORDER, BRIEF_FIELD_KEYS } from './bri
2323
// String utilities
2424
export { createNameSwapper, matchesAnyKeyword } from './stringUtils';
2525

26-
// Production API utilities
27-
export { retryRequest, RequestCache, throttle } from './apiUtils';
28-
export type { RetryOptions } from './apiUtils';
29-
3026
// Content error detection
3127
export { isContentFilterError, getErrorMessage } from './contentErrors';
3228

0 commit comments

Comments
 (0)