bep40 commited on
Commit
b2beaf0
·
verified ·
1 Parent(s): 82b68c4

V12: Fix two-line return match + Button import format + TS arrow param type

Browse files
Files changed (1) hide show
  1. patch_auto_continue_v12.py +209 -0
patch_auto_continue_v12.py ADDED
@@ -0,0 +1,209 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """V12: Auto-continue feature - final fix.
2
+
3
+ Key fixes for V11 bugs:
4
+ 1. Button import: check exact MUI import format
5
+ 2. useAgentChat: match two-line `return {\n messages: chat.messages,` instead of bare `return {`
6
+ 3. Use \u unicode escapes for Vietnamese text
7
+ """
8
+
9
+ import os
10
+
11
+ ROOT = "/source/frontend/src"
12
+ EVENTS = f"{ROOT}/types/events.ts"
13
+ SSE = f"{ROOT}/lib/sse-chat-transport.ts"
14
+ HOOK = f"{ROOT}/hooks/useAgentChat.ts"
15
+ INPUT = f"{ROOT}/components/Chat/ChatInput.tsx"
16
+ SESS = f"{ROOT}/components/SessionChat.tsx"
17
+ AGENT = "/app/agent/core/agent_loop.py"
18
+
19
+ def ok(f):
20
+ print(f"OK: {f}")
21
+
22
+ # --- 1. events.ts: add task_incomplete ---
23
+ with open(EVENTS) as f:
24
+ c = f.read()
25
+ if "'task_incomplete'" not in c:
26
+ c = c.replace(" | 'interrupted'", " | 'interrupted'\n | 'task_incomplete'")
27
+ with open(EVENTS, 'w') as f:
28
+ f.write(c)
29
+ ok("events.ts")
30
+
31
+ # --- 2. sse-chat-transport.ts: add case handler ---
32
+ with open(SSE) as f:
33
+ c = f.read()
34
+ if "case 'task_incomplete'" not in c:
35
+ block = """ case 'task_incomplete':
36
+ (sideChannel as any).onTaskIncomplete(
37
+ (event.data?.incomplete_plan as Array<{ id: string; content: string; status: string }>) || [],
38
+ );
39
+ break;
40
+
41
+ default:"""
42
+ c = c.replace("\n default:", "\n" + block)
43
+ with open(SSE, 'w') as f:
44
+ f.write(c)
45
+ ok("sse.ts")
46
+
47
+ # --- 3. useAgentChat.ts: add state, handler, effect, return ---
48
+ with open(HOOK) as f:
49
+ content = f.read()
50
+
51
+ # Add useState to import
52
+ if "useState" not in content:
53
+ content = content.replace(
54
+ "import { useCallback, useEffect, useMemo, useRef } from 'react';",
55
+ "import { useCallback, useEffect, useMemo, useRef, useState } from 'react';"
56
+ )
57
+
58
+ # Add state after callbacksRef line
59
+ state_block = """\
60
+ // Auto-continue state
61
+ const [_showAc, _setShowAc] = useState(false);
62
+ const _acRef = useRef<Array<{ id: string; content: string; status: string }>>([]);
63
+ """
64
+ if "_showAc" not in content:
65
+ content = content.replace(
66
+ "callbacksRef.current = { onReady, onError, onSessionDead };",
67
+ "callbacksRef.current = { onReady, onError, onSessionDead };\n" + state_block
68
+ )
69
+
70
+ # Add handler inside onSessionUpdate
71
+ handler_code = """\
72
+ // Auto-continue check
73
+ if ((data as any).ac_plan) {
74
+ _acRef.current = (data as any).ac_plan;
75
+ _setShowAc(true);
76
+ return;
77
+ }
78
+ """
79
+ if "ac_plan" not in content:
80
+ content = content.replace(
81
+ "onSessionUpdate: (data) => {",
82
+ "onSessionUpdate: (data) => {\n" + handler_code
83
+ )
84
+
85
+ # Add cancel + effect BEFORE the two-line return marker
86
+ cancel_effect = """\
87
+ const _acCancel = useCallback(() => { _setShowAc(false); }, []);
88
+ useEffect(() => {
89
+ if (!_showAc) return;
90
+ const plan = _acRef.current;
91
+ const timer = setTimeout(() => {
92
+ const s = chatActionsRef.current.setMessages;
93
+ const m = chatActionsRef.current.messages;
94
+ if (s && plan.length > 0) {
95
+ const t = plan.map((i: { content: string }) => `- ${i.content}`).join('\\n');
96
+ 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: '' };
97
+ s([...m, nu]);
98
+ }
99
+ _setShowAc(false);
100
+ }, 10000);
101
+ return () => clearTimeout(timer);
102
+ }, [_showAc]);
103
+
104
+ """
105
+ if "_acCancel" not in content:
106
+ # Match the EXACT two-line return pattern
107
+ content = content.replace(
108
+ " return {\n messages: chat.messages,",
109
+ cancel_effect + " return {\n messages: chat.messages,"
110
+ )
111
+
112
+ # Add return values
113
+ if "_showAc," not in content:
114
+ content = content.replace(
115
+ "refreshMessages,\n };",
116
+ "refreshMessages,\n _showAc,\n _acCancel,\n };"
117
+ )
118
+
119
+ with open(HOOK, 'w') as f:
120
+ f.write(content)
121
+ ok("useAgentChat.ts")
122
+
123
+ # --- 4. ChatInput.tsx: add Button + props + pause button ---
124
+ with open(INPUT) as f:
125
+ content = f.read()
126
+
127
+ # Add Button to MUI imports - match the actual format
128
+ if "Button" not in content:
129
+ content = content.replace(
130
+ " Tooltip,",
131
+ " Tooltip,\n Button,"
132
+ )
133
+
134
+ # Add props to interface
135
+ if "_showAc" not in content:
136
+ content = content.replace(
137
+ "placeholder?: string;",
138
+ "placeholder?: string;\n _showAc?: boolean;\n _acCancel?: () => void;"
139
+ )
140
+
141
+ # Add to destructured function params
142
+ if "_showAc" not in content or "_acCancel" not in content:
143
+ content = content.replace(
144
+ "placeholder = 'Ask anything...' }: ChatInputProps) {",
145
+ "placeholder = 'Ask anything...', _showAc = false, _acCancel }: ChatInputProps) {"
146
+ )
147
+
148
+ # Add pause button before JobsUpgradeDialog
149
+ if "Tam dung" not in content:
150
+ btn = """
151
+ {_showAc && _acCancel && (
152
+ <Box sx={{ display: 'flex', justifyContent: 'center', mt: 1 }}>
153
+ <Button variant="outlined" size="small" onClick={_acCancel}
154
+ sx={{ textTransform: 'none', fontSize: '0.7rem', opacity: 0.7, '&:hover': { opacity: 1 } }}>
155
+ T\u1ea1m d\u1eebng t\u1ef1 \u0111\u1ed9ng ti\u1ebfp t\u1ee5c (10s)
156
+ </Button>
157
+ </Box>
158
+ )}
159
+ """
160
+ content = content.replace("<JobsUpgradeDialog", btn + "\n <JobsUpgradeDialog")
161
+
162
+ with open(INPUT, 'w') as f:
163
+ f.write(content)
164
+ ok("ChatInput.tsx")
165
+
166
+ # --- 5. SessionChat.tsx: pass props ---
167
+ if os.path.exists(SESS):
168
+ with open(SESS) as f:
169
+ c = f.read()
170
+ if "_showAc" not in c:
171
+ c = c.replace("<ChatInput ", "<ChatInput _showAc={_showAc} _acCancel={_acCancel} ")
172
+ with open(SESS, 'w') as f:
173
+ f.write(c)
174
+ ok("SessionChat.tsx")
175
+
176
+ # --- 6. agent_loop.py: detection ---
177
+ if os.path.exists(AGENT):
178
+ with open(AGENT) as f:
179
+ c = f.read()
180
+
181
+ if "_unfinished_plan" not in c:
182
+ fn = """
183
+
184
+ def _unfinished_plan(s):
185
+ p = getattr(s, "current_plan", None) or []
186
+ return [it for it in p if it.get("status") in ("pending", "in_progress")]
187
+ """
188
+ c = c.replace("class Handlers:", fn + "\n\nclass Handlers:")
189
+
190
+ if "ac_plan" not in c:
191
+ check = """
192
+ if not llm_result.tool_calls_acc and llm_result.content:
193
+ unfinished = _unfinished_plan(session)
194
+ if unfinished:
195
+ await session.send_event(Event(event_type="session_update", data={
196
+ "ac_plan": unfinished,
197
+ }))
198
+ """
199
+ marker = " # -- End of turn --"
200
+ if marker in c:
201
+ c = c.replace(marker, check + marker)
202
+
203
+ with open(AGENT, 'w') as f:
204
+ f.write(c)
205
+ ok("agent_loop.py")
206
+ else:
207
+ print("SKIP: agent_loop.py (backend)")
208
+
209
+ print("\nDONE: V12")