Spaces:
Running
Running
| """V14: Auto-continue. Use ASCII-only text for reliable build.""" | |
| import os, sys | |
| ROOT = "/source/frontend/src" | |
| FILES = { | |
| "events.ts": ROOT + "/types/events.ts", | |
| "sse.ts": ROOT + "/lib/sse-chat-transport.ts", | |
| "hook.ts": ROOT + "/hooks/useAgentChat.ts", | |
| "input.tsx": ROOT + "/components/Chat/ChatInput.tsx", | |
| "sess.tsx": ROOT + "/components/SessionChat.tsx", | |
| "agent.py": "/app/agent/core/agent_loop.py", | |
| } | |
| # 1. events.ts | |
| c = open(FILES["events.ts"]).read() | |
| if "'task_incomplete'" not in c: | |
| c = c.replace(" | 'interrupted'", " | 'interrupted'\n | 'task_incomplete'") | |
| open(FILES["events.ts"], 'w').write(c) | |
| print("OK: events.ts") | |
| # 2. sse.ts | |
| c = open(FILES["sse.ts"]).read() | |
| if "case 'task_incomplete'" not in c: | |
| block = """ case 'task_incomplete': | |
| (sideChannel as any).onTaskIncomplete( | |
| (event.data?.incomplete_plan as Array<{ id: string; content: string; status: string }>) || [], | |
| ); | |
| break; | |
| default:""" | |
| c = c.replace("\n default:", "\n" + block) | |
| open(FILES["sse.ts"], 'w').write(c) | |
| print("OK: sse.ts") | |
| # 3. hook.ts - useAgentChat.ts | |
| c = open(FILES["hook.ts"]).read() | |
| # useState import | |
| if "useState" not in c: | |
| c = c.replace( | |
| "import { useCallback, useEffect, useMemo, useRef } from 'react';", | |
| "import { useCallback, useEffect, useMemo, useRef, useState } from 'react';" | |
| ) | |
| # State after callbacksRef | |
| if "_showAc" not in c: | |
| state_block = ( | |
| " // Auto-continue state\n" | |
| " const [_showAc, _setShowAc] = useState(false);\n" | |
| " const _acRef = useRef<Array<{ id: string; content: string; status: string }>>([]);\n" | |
| ) | |
| c = c.replace( | |
| "callbacksRef.current = { onReady, onError, onSessionDead };", | |
| "callbacksRef.current = { onReady, onError, onSessionDead };\n" + state_block | |
| ) | |
| # Handler in onSessionUpdate | |
| if "ac_plan" not in c: | |
| handler = ( | |
| " // Auto-continue check\n" | |
| " if ((data as any).ac_plan) {\n" | |
| " _acRef.current = (data as any).ac_plan;\n" | |
| " _setShowAc(true);\n" | |
| " return;\n" | |
| " }\n" | |
| ) | |
| c = c.replace("onSessionUpdate: (data) => {", "onSessionUpdate: (data) => {\n" + handler) | |
| # Cancel + effect before return | |
| if "_acCancel" not in c: | |
| cancel = " const _acCancel = useCallback(() => { _setShowAc(false); }, []);\n" | |
| effect = ( | |
| " useEffect(() => {\n" | |
| " if (!_showAc) return;\n" | |
| " const plan = _acRef.current;\n" | |
| " const timer = setTimeout(() => {\n" | |
| " const s = chatActionsRef.current.setMessages;\n" | |
| " const m = chatActionsRef.current.messages;\n" | |
| " if (s && plan.length > 0) {\n" | |
| " const t = plan.map((i: { content: string }) => '- ' + i.content).join('\\\\n');\n" | |
| " const nu = { id: 'ac-'+Date.now(), role: 'user' as const, parts: [{ type: 'text' as const, text: '[TIEP TUC] Task chua hoan thanh:\\\\n' + t }], content: '' };\n" | |
| " s([...m, nu]);\n" | |
| " }\n" | |
| " _setShowAc(false);\n" | |
| " }, 10000);\n" | |
| " return () => clearTimeout(timer);\n" | |
| " }, [_showAc]);\n\n" | |
| ) | |
| c = c.replace( | |
| " return {\n messages: chat.messages,", | |
| cancel + effect + " return {\n messages: chat.messages," | |
| ) | |
| # Return values | |
| if "_showAc," not in c: | |
| c = c.replace("refreshMessages,\n };", "refreshMessages,\n _showAc,\n _acCancel,\n };") | |
| open(FILES["hook.ts"], 'w').write(c) | |
| print("OK: useAgentChat.ts") | |
| # 4. input.tsx - ChatInput.tsx | |
| c = open(FILES["input.tsx"]).read() | |
| if "Button" not in c: | |
| # Match the exact MUI import block | |
| c = c.replace("import {\n Alert,\n Box,\n TextField,\n IconButton,\n CircularProgress,\n Typography,\n Menu,\n MenuItem,\n ListItemIcon,\n ListItemText,\n Chip,\n LinearProgress,\n Snackbar,\n Tooltip,\n} from '@mui/material';", | |
| "import {\n Alert,\n Box,\n TextField,\n IconButton,\n CircularProgress,\n Typography,\n Menu,\n MenuItem,\n ListItemIcon,\n ListItemText,\n Chip,\n LinearProgress,\n Snackbar,\n Tooltip,\n Button,\n} from '@mui/material';") | |
| if "_showAc" not in c: | |
| c = c.replace("placeholder?: string;", "placeholder?: string;\n _showAc?: boolean;\n _acCancel?: () => void;") | |
| c = c.replace("placeholder = 'Ask anything...' }: ChatInputProps) {", | |
| "placeholder = 'Ask anything...', _showAc = false, _acCancel }: ChatInputProps) {") | |
| if "TAM_DUNG" not in c: | |
| btn = ( | |
| " {_showAc && _acCancel && (\n" | |
| " <Box sx={{ display: 'flex', justifyContent: 'center', mt: 1 }}>\n" | |
| " <Button variant=\"outlined\" size=\"small\" onClick={_acCancel}\n" | |
| " sx={{ textTransform: 'none', fontSize: '0.7rem', opacity: 0.7, '&:hover': { opacity: 1 } }}>\n" | |
| " TAM_DUNG_TU_DONG_TIEP_TUC\n" | |
| " </Button>\n" | |
| " </Box>\n" | |
| " )}\n" | |
| ) | |
| c = c.replace("<JobsUpgradeDialog", btn + " <JobsUpgradeDialog") | |
| open(FILES["input.tsx"], 'w').write(c) | |
| print("OK: ChatInput.tsx") | |
| # 5. sess.tsx - SessionChat.tsx | |
| if os.path.exists(FILES["sess.tsx"]): | |
| c = open(FILES["sess.tsx"]).read() | |
| if "_showAc" not in c: | |
| c = c.replace("<ChatInput ", "<ChatInput _showAc={_showAc} _acCancel={_acCancel} ") | |
| open(FILES["sess.tsx"], 'w').write(c) | |
| print("OK: SessionChat.tsx") | |
| # 6. agent.py | |
| if os.path.exists(FILES["agent.py"]): | |
| c = open(FILES["agent.py"]).read() | |
| if "_unfinished_plan" not in c: | |
| fn = "\n\ndef _unfinished_plan(s):\n p = getattr(s, 'current_plan', None) or []\n return [it for it in p if it.get('status') in ('pending', 'in_progress')]\n" | |
| c = c.replace("class Handlers:", fn + "\n\nclass Handlers:") | |
| if "ac_plan" not in c: | |
| check = ( | |
| "\n if not llm_result.tool_calls_acc and llm_result.content:\n" | |
| " unfinished = _unfinished_plan(session)\n" | |
| " if unfinished:\n" | |
| " await session.send_event(Event(event_type='session_update', data={\n" | |
| " 'ac_plan': unfinished,\n" | |
| " }))\n" | |
| ) | |
| marker = " # -- End of turn --" | |
| if marker in c: | |
| c = c.replace(marker, check + marker) | |
| open(FILES["agent.py"], 'w').write(c) | |
| print("OK: agent_loop.py") | |
| else: | |
| print("SKIP: agent_loop.py") | |
| print("\nDONE: V14") |