Skip to content

Commit da82279

Browse files
fix: don't import broken agents (#1278)
* fix: don't import broken agents * fix: clear errors
1 parent 29ea53e commit da82279

File tree

3 files changed

+85
-5
lines changed

3 files changed

+85
-5
lines changed

src/ui/src/builder/BuilderHeaderMoreDropdown.vue

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,28 @@
2323
v-model="importConfirmCheckbox"
2424
label="Yes, I want to replace the current agent with the imported file"
2525
/>
26+
<div
27+
v-if="importErrorSummary"
28+
class="BuilderHeaderMoreDropdown__importError"
29+
>
30+
<p class="BuilderHeaderMoreDropdown__importError__header">
31+
Failed to import the agent
32+
</p>
33+
34+
<p class="BuilderHeaderMoreDropdown__importError__summary">
35+
{{ importErrorSummary }}
36+
</p>
37+
38+
<details
39+
v-if="importErrorDetails"
40+
class="BuilderHeaderMoreDropdown__importError__details"
41+
>
42+
<summary>Show details</summary>
43+
<pre class="BuilderHeaderMoreDropdown__importError__trace">{{
44+
importErrorDetails
45+
}}</pre>
46+
</details>
47+
</div>
2648
</WdsModal>
2749
</template>
2850

@@ -60,6 +82,8 @@ const toasts = useToasts();
6082
const importInProgress = ref(false);
6183
const importConfirmShown = ref(false);
6284
const importConfirmCheckbox = ref(false);
85+
const importErrorSummary = ref("");
86+
const importErrorDetails = ref("");
6387
6488
const importModalActions = computed<ModalAction[]>(() => [
6589
{
@@ -68,6 +92,8 @@ const importModalActions = computed<ModalAction[]>(() => [
6892
fn: () => {
6993
importConfirmCheckbox.value = false;
7094
importConfirmShown.value = false;
95+
importErrorSummary.value = "";
96+
importErrorDetails.value = "";
7197
},
7298
},
7399
{
@@ -105,6 +131,8 @@ async function importModalConfirm() {
105131
if (!file) return;
106132
107133
importInProgress.value = true;
134+
importErrorSummary.value = "";
135+
importErrorDetails.value = "";
108136
109137
try {
110138
// Prepare form data
@@ -117,6 +145,9 @@ async function importModalConfirm() {
117145
body: formData,
118146
});
119147
if (!response.ok) {
148+
const parsed = await response.json();
149+
importErrorSummary.value = parsed?.detail?.summary;
150+
importErrorDetails.value = parsed?.detail?.details;
120151
throw new Error("Failed to connect to import API");
121152
}
122153
importConfirmShown.value = false;
@@ -162,4 +193,50 @@ async function onSelect(key: string) {
162193
.BuilderHeaderMoreDropdown:deep(.SharedMoreDropdown__dropdown) {
163194
min-width: 220px;
164195
}
196+
197+
.BuilderHeaderMoreDropdown__importError {
198+
margin-top: 16px;
199+
padding: 12px 14px;
200+
border-radius: 8px;
201+
background: rgba(255, 149, 0, 0.08);
202+
border: 1px solid rgba(255, 149, 0, 0.35);
203+
grid-column: 2 / 4;
204+
}
205+
206+
.BuilderHeaderMoreDropdown__importError__header {
207+
font-weight: 600;
208+
color: var(--wdsColorOrange4);
209+
font-size: 14px;
210+
}
211+
212+
.BuilderHeaderMoreDropdown__importError__summary {
213+
margin-top: 6px;
214+
font-size: 14px;
215+
}
216+
217+
.BuilderHeaderMoreDropdown__importError__details {
218+
margin-top: 10px;
219+
}
220+
221+
.BuilderHeaderMoreDropdown__importError__details summary {
222+
cursor: pointer;
223+
font-size: 12px;
224+
user-select: none;
225+
}
226+
227+
.BuilderHeaderMoreDropdown__importError__details summary:hover {
228+
text-decoration: underline;
229+
}
230+
231+
.BuilderHeaderMoreDropdown__importError__trace {
232+
margin-top: 8px;
233+
max-height: 220px;
234+
overflow: auto;
235+
padding: 10px;
236+
border-radius: 6px;
237+
background: rgba(0, 0, 0, 0.05);
238+
font-size: 12px;
239+
line-height: 1.4;
240+
white-space: pre;
241+
}
165242
</style>

src/writer/app_runner.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1207,6 +1207,9 @@ async def import_zip(self, zip_path: str):
12071207
if not os.path.isdir(wf_dir_path):
12081208
raise ValueError(".wf directory not found alongside main.py in the archive.")
12091209

1210+
# Parse files to ensure there are no errors
1211+
wf_project.read_files(main_py_dir)
1212+
12101213
# Passed all checks; replace current app contents
12111214

12121215
logging.info("Copying app at %s", main_py_dir)

src/writer/serve.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -263,9 +263,9 @@ async def export_zip():
263263
@app.post("/api/import")
264264
async def import_zip(file: UploadFile = File(...)):
265265
if serve_mode != "edit":
266-
raise HTTPException(status_code=403, detail="Invalid mode.")
266+
raise HTTPException(status_code=403, detail={"summary": "Invalid mode. Expected 'edit'"})
267267
if not file.filename or not file.filename.endswith(".zip"):
268-
raise HTTPException(status_code=400, detail="Only .zip files are supported.")
268+
raise HTTPException(status_code=400, detail={"summary": "Only .zip files are supported."})
269269

270270
MAX_FILE_SIZE = 200 * 1024 * 1024
271271

@@ -278,13 +278,13 @@ async def import_zip(file: UploadFile = File(...)):
278278
if size > MAX_FILE_SIZE:
279279
tmp.close()
280280
os.unlink(tmp.name)
281-
raise HTTPException(status_code=413, detail=f"File too large. Max file size: {MAX_FILE_SIZE}")
281+
raise HTTPException(status_code=413, detail={"summary": f"File too large. Max file size: {MAX_FILE_SIZE}"})
282282
tmp.write(chunk)
283283
tmp_path = tmp.name
284284
await app_runner.import_zip(tmp_path)
285285
os.remove(tmp_path)
286-
except ValueError:
287-
raise HTTPException(status_code=400, detail="Invalid upload.")
286+
except ValueError as e:
287+
raise HTTPException(status_code=400, detail={"summary": "Invalid archive contents", "details": traceback.format_exc()}) from e
288288

289289
@app.post("/api/autogen")
290290
async def autogen(requestBody: AutogenRequestBody, request: Request):

0 commit comments

Comments
 (0)