Skip to content

Commit df54c58

Browse files
Merge pull request #1025 from Dhruvkumar-Microsoft/psl-TASfeature-dk
fix: updated the backend and frontend to align the feature branch
2 parents 8a8ede7 + 5b4d1ce commit df54c58

13 files changed

Lines changed: 901 additions & 52 deletions

File tree

infra/scripts/post_deploy.sh

Lines changed: 814 additions & 0 deletions
Large diffs are not rendered by default.

package-lock.json

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/App/src/hooks/useAutoScroll.tsx

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { useCallback, useRef } from 'react';
66

77
export function useAutoScroll() {
88
const messagesContainerRef = useRef<HTMLDivElement>(null);
9+
const finalResultRef = useRef<HTMLDivElement>(null);
910

1011
const scrollToBottom = useCallback(() => {
1112
setTimeout(() => {
@@ -16,7 +17,19 @@ export function useAutoScroll() {
1617
}, 100);
1718
}, []);
1819

19-
return { messagesContainerRef, scrollToBottom };
20+
// Scroll to the final result message instead of the absolute bottom.
21+
// Falls back to scrollToBottom when the anchor is not yet mounted.
22+
const scrollToFinalResult = useCallback(() => {
23+
setTimeout(() => {
24+
if (finalResultRef.current) {
25+
finalResultRef.current.scrollIntoView({ behavior: 'smooth', block: 'start' });
26+
} else {
27+
scrollToBottom();
28+
}
29+
}, 150);
30+
}, [scrollToBottom]);
31+
32+
return { messagesContainerRef, finalResultRef, scrollToBottom, scrollToFinalResult };
2033
}
2134

2235
export default useAutoScroll;

src/App/src/hooks/usePlanWebSocket.tsx

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ import { ToastIntent } from '@/components/toast/InlineToaster';
5252
interface UsePlanWebSocketProps {
5353
planId: string | undefined;
5454
scrollToBottom: () => void;
55+
scrollToFinalResult: () => void;
5556
formatErrorMessage: (content: string) => string;
5657
showToast: (content: React.ReactNode, intent?: ToastIntent, options?: { dismissible?: boolean; timeoutMs?: number | null }) => number;
5758
}
@@ -91,6 +92,7 @@ function persistAgentMessage(
9192
export function usePlanWebSocket({
9293
planId,
9394
scrollToBottom,
95+
scrollToFinalResult,
9496
formatErrorMessage,
9597
showToast,
9698
}: UsePlanWebSocketProps) {
@@ -132,13 +134,14 @@ export function usePlanWebSocket({
132134
const unsub = webSocketService.on(
133135
WebsocketMessageType.AGENT_MESSAGE_STREAMING,
134136
(msg: any) => {
135-
const line = PlanDataService.simplifyHumanClarification(msg.data.content);
137+
const line = PlanDataService.simplifyHumanClarification(msg.data?.content || msg.content || '');
136138
dispatch(setShowBufferingText(true));
137139
dispatch(appendToStreamingBuffer(line));
140+
scrollToBottom();
138141
},
139142
);
140143
return unsub;
141-
}, [dispatch]);
144+
}, [dispatch, scrollToBottom]);
142145

143146
// ── USER_CLARIFICATION_REQUEST ────────────────────────────────
144147
useEffect(() => {
@@ -188,15 +191,15 @@ export function usePlanWebSocket({
188191
timestamp: Date.now(),
189192
steps: [],
190193
next_steps: [],
191-
content: '\u{1F389}\u{1F389} ' + (finalMessage.data?.content || ''),
194+
content: finalMessage.data?.content || '',
192195
raw_data: finalMessage,
193196
};
194197
dispatch(setShowBufferingText(true));
195198
dispatch(addAgentMessage(agentMessageData));
196199
dispatch(setSelectedTeam(planData?.team || null));
197200
/* P0: single compound action replaces setShowProcessingPlanSpinner(false) + markPlanCompleted() */
198201
dispatch(planCompletedFinal());
199-
scrollToBottom();
202+
scrollToFinalResult();
200203
webSocketService.disconnect();
201204
persistAgentMessage(agentMessageData, planData, dispatch, true, streamingMessageBuffer);
202205
} else if (messageStatus === 'error') {
@@ -222,7 +225,7 @@ export function usePlanWebSocket({
222225
},
223226
);
224227
return unsub;
225-
}, [dispatch, scrollToBottom, planData, streamingMessageBuffer, formatErrorMessage, showToast]);
228+
}, [dispatch, scrollToBottom, scrollToFinalResult, planData, streamingMessageBuffer, formatErrorMessage, showToast]);
226229

227230
// ── ERROR_MESSAGE ─────────────────────────────────────────────
228231
useEffect(() => {

src/App/src/pages/PlanPage.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,7 @@ const PlanPage: React.FC = () => {
8686
const navigate = useNavigate();
8787
const dispatch = useAppDispatch();
8888
const { showToast, dismissToast } = useInlineToaster();
89-
const { messagesContainerRef, scrollToBottom } = useAutoScroll();
90-
const finalResultRef = useRef<HTMLDivElement>(null);
89+
const { messagesContainerRef, finalResultRef, scrollToBottom, scrollToFinalResult } = useAutoScroll();
9190
const { loadPlanData, resetPlanVariables } = usePlanActions();
9291

9392
/* ── Redux Selectors (granular — Point 10) ──────────────── */
@@ -135,7 +134,7 @@ const PlanPage: React.FC = () => {
135134
}, []);
136135

137136
/* ── WebSocket subscriptions (extracted hook) ───────────── */
138-
usePlanWebSocket({ planId, scrollToBottom, formatErrorMessage, showToast });
137+
usePlanWebSocket({ planId, scrollToBottom, scrollToFinalResult, formatErrorMessage, showToast });
139138

140139
/* ── Navigation with cancellation check ─────────────────── */
141140
const handleNavigationWithAlert = useCallback(
@@ -363,6 +362,7 @@ const PlanPage: React.FC = () => {
363362
planApprovalRequest={planApprovalRequest}
364363
waitingForPlan={waitingForPlan}
365364
messagesContainerRef={messagesContainerRef}
365+
finalResultRef={finalResultRef}
366366
streamingMessageBuffer={streamingMessageBuffer}
367367
showBufferingText={showBufferingText}
368368
agentMessages={agentMessages}

src/backend/Dockerfile

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,6 @@ RUN uv sync --frozen --no-install-project --no-dev
1515
# Backend app setup
1616
COPY . /app
1717
RUN uv sync --frozen --no-dev
18-
# Fix: agent-framework-azure-ai-search ships an empty agent_framework/__init__.py
19-
# that overwrites the real one from agent-framework-core. Reinstall core last.
20-
RUN uv pip install --reinstall --no-deps agent-framework-core==1.2.2
2118

2219

2320
FROM base

src/backend/api/router.py

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -315,16 +315,6 @@ async def process_request(
315315
)
316316
await memory_store.add_plan(plan)
317317

318-
# Ensure orchestration is initialized before running
319-
# Force rebuild for each new task since Magentic workflows cannot be reused after completion
320-
team_service = TeamService(memory_store)
321-
await OrchestrationManager.get_current_or_new_orchestration(
322-
user_id=user_id,
323-
team_config=team,
324-
team_switched=False,
325-
team_service=team_service,
326-
force_rebuild=True, # Always rebuild workflow for new tasks
327-
)
328318

329319
track_event_if_configured(
330320
"Plan_Created",

src/backend/app.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
# Configure logging levels FIRST, before any logging calls
88
logging.basicConfig(level=getattr(logging, config.AZURE_BASIC_LOGGING_LEVEL.upper(), logging.INFO))
99

10-
10+
from api.router import app_router
1111
from azure.monitor.opentelemetry import configure_azure_monitor
1212
from common.config.app_config import config
1313
from common.models.messages import UserLanguage

src/backend/callbacks/response_handlers.py

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -108,20 +108,15 @@ async def streaming_agent_response_callback(
108108
return
109109

110110
try:
111-
# Handle various streaming update object shapes
112111
chunk_text = getattr(update, "text", None)
113-
114-
# If text is None, don't fall back to str(update) as that would show object repr
115-
# Just skip if there's no actual text content
116-
if chunk_text is None:
117-
# Check if update is a Message
118-
if isinstance(update, Message):
119-
chunk_text = update.text or ""
120-
elif hasattr(update, "content"):
121-
chunk_text = str(update.content) if update.content else ""
122-
else:
123-
# Skip if no text content available
124-
return
112+
if not chunk_text:
113+
contents = getattr(update, "contents", []) or []
114+
collected = []
115+
for item in contents:
116+
txt = getattr(item, "text", None)
117+
if txt:
118+
collected.append(str(txt))
119+
chunk_text = "".join(collected) if collected else ""
125120

126121
cleaned = clean_citations(chunk_text or "")
127122

@@ -150,4 +145,4 @@ async def streaming_agent_response_callback(
150145
)
151146
logger.debug("Streaming chunk (agent=%s final=%s len=%d)", agent_id, is_final, len(cleaned))
152147
except Exception as e:
153-
logger.error("streaming_agent_response_callback error: %s", e)
148+
logger.error("streaming_agent_response_callback error: %s", e)

src/backend/common/utils/agent_utils.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11

2+
import logging
23
import secrets
34
import string
45
from typing import Optional
@@ -20,3 +21,22 @@ def generate_assistant_id(prefix: str = "asst_", length: int = 24) -> str:
2021
# cryptographically strong randomness
2122
random_part = "".join(secrets.choice(alphabet) for _ in range(length))
2223
return f"{prefix}{random_part}"
24+
25+
26+
async def get_database_team_agent_id(
27+
memory_store: DatabaseBase, team_config: TeamConfiguration, agent_name: str
28+
) -> Optional[str]:
29+
"""Retrieve existing team agent from database, if any."""
30+
agent_id = None
31+
try:
32+
currentAgent = await memory_store.get_team_agent(
33+
team_id=team_config.team_id, agent_name=agent_name
34+
)
35+
if currentAgent and currentAgent.agent_foundry_id:
36+
agent_id = currentAgent.agent_foundry_id
37+
38+
except (
39+
Exception
40+
) as ex: # Consider narrowing this to specific exceptions if possible
41+
logging.error("Failed to initialize Get database team agent: %s", ex)
42+
return agent_id

0 commit comments

Comments
 (0)