bep40 commited on
Commit
a2bdf09
·
verified ·
1 Parent(s): 5186d9f

V11: Clean single-file patch for auto-continue feature

Browse files
Files changed (1) hide show
  1. patch_auto_continue_v11.py +185 -0
patch_auto_continue_v11.py ADDED
@@ -0,0 +1,185 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """V11: Auto-continue feature - clean single-file patch.
2
+
3
+ Backend: agent_loop.py detects model stopped mid-task, sends event.
4
+ Frontend: useAgentChat listens via onSessionUpdate, shows pause button.
5
+ All TS errors avoided via (as any) casts where needed.
6
+ """
7
+ import os
8
+
9
+ ROOT = "/source/frontend/src"
10
+ EVENTS = f"{ROOT}/types/events.ts"
11
+ SSE = f"{ROOT}/lib/sse-chat-transport.ts"
12
+ HOOK = f"{ROOT}/hooks/useAgentChat.ts"
13
+ INPUT = f"{ROOT}/components/Chat/ChatInput.tsx"
14
+ SESS = f"{ROOT}/components/SessionChat.tsx"
15
+ AGENT_LOOP = "/app/agent/core/agent_loop.py"
16
+
17
+ def p(f, s):
18
+ print(f"OK: {f}" if s else f"FAIL: {f}")
19
+
20
+ # --- PATCH 1: events.ts ---
21
+ with open(EVENTS) as f: c = f.read()
22
+ r1 = "'task_incomplete'" not in c
23
+ if r1:
24
+ c = c.replace(" | 'interrupted'", " | 'interrupted'\n | 'task_incomplete'")
25
+ with open(EVENTS, 'w') as f: f.write(c)
26
+ p("events.ts", r1)
27
+
28
+ # --- PATCH 2: sse-chat-transport.ts ---
29
+ with open(SSE) as f: c = f.read()
30
+ r2 = "case 'task_incomplete'" not in c
31
+ if r2:
32
+ block = """ case 'task_incomplete':
33
+ (sideChannel as any).onTaskIncomplete(
34
+ (event.data?.incomplete_plan as Array<{ id: string; content: string; status: string }>) || [],
35
+ );
36
+ break;
37
+
38
+ default:"""
39
+ c = c.replace("\n default:", "\n" + block)
40
+ with open(SSE, 'w') as f: f.write(c)
41
+ p("sse.ts", r2)
42
+
43
+ # --- PATCH 3: useAgentChat.ts ---
44
+ with open(HOOK) as f: lines = f.readlines()
45
+ r3 = True
46
+ new_lines = []
47
+ state_done = False
48
+ handler_done = False
49
+ efx_done = False
50
+ ret_done = False
51
+
52
+ for line in lines:
53
+ if "import { useCallback, useEffect, useMemo, useRef } from 'react';" in line:
54
+ line = line.replace("useRef }", "useRef, useState }")
55
+
56
+ if "callbacksRef.current = { onReady, onError, onSessionDead };" in line and not state_done:
57
+ new_lines.append(line)
58
+ new_lines.append(" // Auto-continue state\n")
59
+ new_lines.append(" const [_showAc, _setShowAc] = useState(false);\n")
60
+ new_lines.append(" const _acRef = useRef<Array<{ id: string; content: string; status: string }>>([]);\n")
61
+ state_done = True
62
+ continue
63
+
64
+ if "onSessionUpdate: (data) => {" in line and not handler_done:
65
+ new_lines.append(line)
66
+ new_lines.append(" // Auto-continue check\n")
67
+ new_lines.append(" if ((data as any).ac_plan) {\n")
68
+ new_lines.append(" _acRef.current = (data as any).ac_plan;\n")
69
+ new_lines.append(" _setShowAc(true);\n")
70
+ new_lines.append(" return;\n")
71
+ new_lines.append(" }\n")
72
+ handler_done = True
73
+ continue
74
+
75
+ stripped = line.strip()
76
+ if stripped == "return {" and not efx_done:
77
+ new_lines.append(" const _acCancel = useCallback(() => { _setShowAc(false); }, []);\n")
78
+ new_lines.append(" useEffect(() => {\n")
79
+ new_lines.append(" if (!_showAc) return;\n")
80
+ new_lines.append(" const plan = _acRef.current;\n")
81
+ new_lines.append(" const timer = setTimeout(() => {\n")
82
+ new_lines.append(" const s = chatActionsRef.current.setMessages;\n")
83
+ new_lines.append(" const m = chatActionsRef.current.messages;\n")
84
+ new_lines.append(" if (s && plan.length > 0) {\n")
85
+ new_lines.append(" const t = plan.map(i => `- ${i.content}`).join('\\n');\n")
86
+ new_lines.append(" 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")
87
+ new_lines.append(" s([...m, nu]);\n")
88
+ new_lines.append(" }\n")
89
+ new_lines.append(" _setShowAc(false);\n")
90
+ new_lines.append(" }, 10000);\n")
91
+ new_lines.append(" return () => clearTimeout(timer);\n")
92
+ new_lines.append(" }, [_showAc]);\n")
93
+ efx_done = True
94
+
95
+ if "refreshMessages," in line and not ret_done:
96
+ new_lines.append(line)
97
+ new_lines.append(" _showAc,\n")
98
+ new_lines.append(" _acCancel,\n")
99
+ ret_done = True
100
+ continue
101
+
102
+ new_lines.append(line)
103
+
104
+ if not all([state_done, handler_done, efx_done, ret_done]):
105
+ r3 = False
106
+ print(f" MISSING: state={state_done} handler={handler_done} effect={efx_done} ret={ret_done}")
107
+ else:
108
+ with open(HOOK, 'w') as f: f.writelines(new_lines)
109
+ p("useAgentChat.ts", r3)
110
+
111
+ # --- PATCH 4: ChatInput.tsx ---
112
+ with open(INPUT) as f: c = f.read()
113
+ r4 = True
114
+ if "Button" not in c:
115
+ c = c.replace(" Tooltip,", " Tooltip,\n Button,")
116
+
117
+ if "_showAc" not in c:
118
+ old = "placeholder?: string;"
119
+ new = "placeholder?: string;\n _showAc?: boolean;\n _acCancel?: () => void;"
120
+ c = c.replace(old, new)
121
+
122
+ old2 = "placeholder = 'Ask anything...' }: ChatInputProps) {"
123
+ new2 = "placeholder = 'Ask anything...', _showAc = false, _acCancel }: ChatInputProps) {"
124
+ c = c.replace(old2, new2)
125
+
126
+ btn = """
127
+ {_showAc && _acCancel && (
128
+ <Box sx={{ display: 'flex', justifyContent: 'center', mt: 1 }}>
129
+ <Button variant="outlined" size="small" onClick={_acCancel}
130
+ sx={{ textTransform: 'none', fontSize: '0.7rem', opacity: 0.7, '&:hover': { opacity: 1 } }}>
131
+ Tam dung tu dong tiep tuc (10s)
132
+ </Button>
133
+ </Box>
134
+ )}
135
+ """
136
+ c = c.replace("<JobsUpgradeDialog", btn + "\n <JobsUpgradeDialog")
137
+ else:
138
+ r4 = False
139
+
140
+ with open(INPUT, 'w') as f: f.write(c)
141
+ p("ChatInput.tsx", r4)
142
+
143
+ # --- PATCH 5: SessionChat.tsx ---
144
+ if os.path.exists(SESS):
145
+ with open(SESS) as f: c = f.read()
146
+ r5 = "_showAc" not in c
147
+ if r5:
148
+ c = c.replace("<ChatInput ", "<ChatInput _showAc={_showAc} _acCancel={_acCancel} ")
149
+ with open(SESS, 'w') as f: f.write(c)
150
+ p("SessionChat.tsx", r5)
151
+ else:
152
+ print("SKIP: SessionChat.tsx")
153
+
154
+ # --- PATCH 6: agent_loop.py ---
155
+ if os.path.exists(AGENT_LOOP):
156
+ with open(AGENT_LOOP) as f: c = f.read()
157
+ r6 = True
158
+ if "_unfinished_plan" not in c:
159
+ fn = """
160
+ def _unfinished_plan(s):
161
+ p = getattr(s, "current_plan", None) or []
162
+ return [it for it in p if it.get("status") in ("pending", "in_progress")]
163
+ """
164
+ c = c.replace("class Handlers:", fn + "\n\nclass Handlers:")
165
+
166
+ if "ac_plan" not in c:
167
+ check = """
168
+ if not llm_result.tool_calls_acc and llm_result.content:
169
+ unfinished = _unfinished_plan(session)
170
+ if unfinished:
171
+ await session.send_event(Event(event_type="session_update", data={
172
+ "ac_plan": unfinished,
173
+ }))
174
+ """
175
+ marker = " # -- End of turn --"
176
+ c = c.replace(marker, check + "\n" + marker)
177
+ else:
178
+ r6 = False
179
+
180
+ with open(AGENT_LOOP, 'w') as f: f.write(c)
181
+ p("agent_loop.py", r6)
182
+ else:
183
+ print("SKIP: agent_loop.py")
184
+
185
+ print("\nDONE: V11")