@marimo-team/frontend 0.20.5-dev33 → 0.20.5-dev36
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/{add-cell-with-ai-C9Xp-2Lu.js → add-cell-with-ai-BO7kWP2P.js} +4 -4
- package/dist/assets/{agent-panel-CFmSkdtw.js → agent-panel-C-OkUkue.js} +1 -1
- package/dist/assets/{cell-editor-BoDTqwYs.js → cell-editor-DFeqwbDq.js} +1 -1
- package/dist/assets/{chat-display-Dwlgqtli.js → chat-display-COL62Kpv.js} +1 -1
- package/dist/assets/{chat-panel-DP052nQF.js → chat-panel-CxYclPR7.js} +1 -1
- package/dist/assets/{edit-page-D7VD6LD4.js → edit-page-Bld_fe39.js} +3 -3
- package/dist/assets/{index-B7vfm6Sw.js → index-BMdbqayP.js} +4 -4
- package/dist/assets/{scratchpad-panel-BRTSBHvd.js → scratchpad-panel-w9Tu3fpH.js} +1 -1
- package/dist/index.html +3 -3
- package/package.json +1 -1
- package/src/core/ai/tools/__tests__/run-cells-tool.test.ts +127 -0
- package/src/core/ai/tools/run-cells-tool.ts +40 -51
- package/src/plugins/impl/plotly/PlotlyPlugin.tsx +8 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
import{s as oe}from"./chunk-LvLJmgfZ.js";import{d as ye,l as be,n as T,p as ae,u as we}from"./useEvent-WUlsM6MC.js";import{t as Ne}from"./react-Bj1aDYRI.js";import{P as ke,T as ze,kn as Ce,ui as Se,vi as Ae,yi as n,__tla as Oe}from"./cells-DJj8ja0V.js";import"./react-dom-CSu739Rf.js";import{t as se}from"./compiler-runtime-B3qBwwSJ.js";import"./tooltip-CQeE9o7x.js";import{h as ie}from"./useEventListener-DP66hBzA.js";import{y as Ie}from"./utils-C1ZciZbF.js";import{t as Pe}from"./constants-CdR60lKM.js";import{t as Le}from"./jsx-runtime-CP2fAiZX.js";import{t as z}from"./button-BwamPDJi.js";import{t as le}from"./cn-d_A4rA-x.js";import"./dist-Dr9qtoId.js";import{n as Me,__tla as Ee}from"./JsonOutput-D8vxudTc.js";import"./cjs-CVt8hZ5L.js";import"./main-Cmx6WhDY.js";import"./useNonce-BY4jV7Yb.js";import{r as Re}from"./requests-DZy_SQ_v.js";import{t as me}from"./createLucideIcon-BvP5LBIs.js";import{d as De,__tla as He}from"./layout-DQhzfSJn.js";import{n as Te,t as Ve,__tla as qe}from"./LazyAnyLanguageCodeMirror-BpSKccWW.js";import"./download-C06P9lnP.js";import"./markdown-renderer-D02U2W2X.js";import{u as Ge}from"./toDate-DDAiY7kz.js";import{t as Ue,__tla as Be}from"./cell-editor-
|
|
1
|
+
import{s as oe}from"./chunk-LvLJmgfZ.js";import{d as ye,l as be,n as T,p as ae,u as we}from"./useEvent-WUlsM6MC.js";import{t as Ne}from"./react-Bj1aDYRI.js";import{P as ke,T as ze,kn as Ce,ui as Se,vi as Ae,yi as n,__tla as Oe}from"./cells-DJj8ja0V.js";import"./react-dom-CSu739Rf.js";import{t as se}from"./compiler-runtime-B3qBwwSJ.js";import"./tooltip-CQeE9o7x.js";import{h as ie}from"./useEventListener-DP66hBzA.js";import{y as Ie}from"./utils-C1ZciZbF.js";import{t as Pe}from"./constants-CdR60lKM.js";import{t as Le}from"./jsx-runtime-CP2fAiZX.js";import{t as z}from"./button-BwamPDJi.js";import{t as le}from"./cn-d_A4rA-x.js";import"./dist-Dr9qtoId.js";import{n as Me,__tla as Ee}from"./JsonOutput-D8vxudTc.js";import"./cjs-CVt8hZ5L.js";import"./main-Cmx6WhDY.js";import"./useNonce-BY4jV7Yb.js";import{r as Re}from"./requests-DZy_SQ_v.js";import{t as me}from"./createLucideIcon-BvP5LBIs.js";import{d as De,__tla as He}from"./layout-DQhzfSJn.js";import{n as Te,t as Ve,__tla as qe}from"./LazyAnyLanguageCodeMirror-BpSKccWW.js";import"./download-C06P9lnP.js";import"./markdown-renderer-D02U2W2X.js";import{u as Ge}from"./toDate-DDAiY7kz.js";import{t as Ue,__tla as Be}from"./cell-editor-DFeqwbDq.js";import{t as Fe}from"./spinner-Dt4SmUsI.js";import{t as Je}from"./play-CkqWG85-.js";import"./dist-Jp_4M-Ei.js";import"./dist-Cf47zp7b.js";import"./dist-Bagw3W9X.js";import"./dist-t1vdhOX_.js";import"./dist-D3OoaVQX.js";import"./session-iz9BPeTp.js";import{r as Ke}from"./useTheme-Co8bbxoC.js";import"./Combination-lAuBMOg4.js";import{t as C}from"./tooltip-CKZGcDDd.js";import"./purify.es-BBn8CPhf.js";import"./dates-BFWtdOCb.js";import"./popover-XNIX-rv1.js";import"./vega-loader.browser-BJ9oKrvH.js";import"./defaultLocale-CuYNS33t.js";import"./defaultLocale-CGfP-Ye3.js";import{__tla as Qe}from"./chunk-5FQGJX7Z-Cyze_Wbx.js";import"./katex-FSdXOW5Y.js";import"./html-to-image-D-FY7miL.js";import{o as We}from"./focus-j9zawi1p.js";import{a as Xe}from"./renderShortcut-BjTUrvc5.js";import"./esm-Hlfcqnf0.js";import{n as Ye,r as Ze,t as ne}from"./react-resizable-panels.browser.esm-RgA6v8U3.js";import"./name-cell-input-DOyCua0o.js";import{n as $e,r as et}from"./panel-context-ICWm_stq.js";import{__tla as tt}from"./loro_wasm_bg-Bj93RhA2.js";import"./ws-D2eS61zr.js";import"./Inputs-DWdIc0Ie.js";import"./dist-DKPpxz2S.js";import"./dist-D_4TSZPK.js";import"./dist-CsQrBXB1.js";import"./dist-ykiUNgjx.js";import"./dist-CrFxmtyb.js";import"./dist-C1g0ZQqM.js";import"./dist-0PMxXIr6.js";import"./dist-wBVaU_K6.js";import"./dist-BIYYQefO.js";import"./esm-MvMf2P5n.js";import{t as rt}from"./kiosk-mode-BuLLP9k3.js";let ce,ot=Promise.all([(()=>{try{return Oe}catch{}})(),(()=>{try{return Ee}catch{}})(),(()=>{try{return He}catch{}})(),(()=>{try{return qe}catch{}})(),(()=>{try{return Be}catch{}})(),(()=>{try{return Qe}catch{}})(),(()=>{try{return tt}catch{}})()]).then(async()=>{var de=me("eraser",[["path",{d:"M21 21H8a2 2 0 0 1-1.42-.587l-3.994-3.999a2 2 0 0 1 0-2.828l10-10a2 2 0 0 1 2.829 0l5.999 6a2 2 0 0 1 0 2.828L12.834 21",key:"g5wo59"}],["path",{d:"m5.082 11.09 8.828 8.828",key:"1wx5vj"}]]),pe=me("history",[["path",{d:"M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8",key:"1357e3"}],["path",{d:"M3 3v5h5",key:"1xhq8a"}],["path",{d:"M12 7v5l4 2",key:"1fdv2h"}]]),he=se(),V=oe(Ne(),1),ue=15;const q=Se("marimo:scratchpadHistory:v1",[],Ce),fe=ae(!1),xe=ae(null,(e,s,i)=>{if(i=i.trim(),!i)return;let m=e(q);s(q,[i,...m.filter(c=>c!==i)].slice(0,ue))});var t=oe(Le(),1),_e={hide_code:!1,disabled:!1};const ve=()=>{var re;let e=(0,he.c)(60),s=ke(),[i]=Ie(),{theme:m}=Ke(),c=(0,V.useRef)(null),G=We(),{createNewCell:U,updateCellCode:d}=ze(),{sendRunScratchpad:p}=Re(),S=$e(),B=et(),l=s.cellRuntime[n],F=l==null?void 0:l.output,A=l==null?void 0:l.status,J=l==null?void 0:l.consoleOutputs,r=((re=s.cellData.__scratch__)==null?void 0:re.code)??"",K=ye(xe),[a,h]=be(fe),u=we(q),O;e[0]!==K||e[1]!==r||e[2]!==p?(O=()=>{p({code:r}),K(r)},e[0]=K,e[1]=r,e[2]=p,e[3]=O):O=e[3];let f=T(O),I;e[4]!==r||e[5]!==U||e[6]!==G?(I=()=>{r.trim()&&U({code:r,before:!1,cellId:G??"__end__"})},e[4]=r,e[5]=U,e[6]=G,e[7]=I):I=e[7];let Q=T(I),P;e[8]!==p||e[9]!==d?(P=()=>{d({cellId:n,code:"",formattingChange:!1}),p({code:""});let o=c.current;o&&o.dispatch({changes:{from:0,to:o.state.doc.length,insert:""}})},e[8]=p,e[9]=d,e[10]=P):P=e[10];let W=T(P),L;e[11]!==h||e[12]!==d?(L=o=>{h(!1),d({cellId:n,code:o,formattingChange:!1});let k=c.current;k&&k.dispatch({changes:{from:0,to:k.state.doc.length,insert:o}})},e[11]=h,e[12]=d,e[13]=L):L=e[13];let X=T(L),[Y,ge]=(0,V.useState)(),M;e[14]!==X||e[15]!==u||e[16]!==a||e[17]!==m?(M=()=>a?(0,t.jsx)("div",{className:"absolute inset-0 z-100 bg-background p-3 border-none overflow-auto",children:(0,t.jsx)("div",{className:"overflow-auto flex flex-col gap-3",children:u.map((o,k)=>(0,t.jsx)("div",{className:"border rounded-md hover:shadow-sm cursor-pointer hover:border-input overflow-hidden",onClick:()=>X(o),children:(0,t.jsx)(V.Suspense,{children:(0,t.jsx)(Ve,{language:"python",theme:m,basicSetup:{highlightActiveLine:!1,highlightActiveLineGutter:!1},value:o.trim(),editable:!1,readOnly:!0})})},k))})}):null,e[14]=X,e[15]=u,e[16]=a,e[17]=m,e[18]=M):M=e[18];let Z=M,E;e[19]!==W||e[20]!==Q||e[21]!==f||e[22]!==u.length||e[23]!==a||e[24]!==h||e[25]!==A?(E=()=>(0,t.jsxs)("div",{className:"flex items-center shrink-0 border-b",children:[(0,t.jsx)(C,{content:Xe("cell.run"),children:(0,t.jsx)(z,{"data-testid":"scratchpad-run-button",onClick:f,disabled:a,variant:"text",size:"xs",children:(0,t.jsx)(Je,{color:"var(--grass-11)",size:16})})}),(0,t.jsx)(C,{content:"Clear code and outputs",children:(0,t.jsx)(z,{disabled:a,size:"xs",variant:"text",onClick:W,children:(0,t.jsx)(de,{size:16})})}),(0,t.jsx)(rt,{children:(0,t.jsx)(C,{content:"Insert code",children:(0,t.jsx)(z,{disabled:a,size:"xs",variant:"text",onClick:Q,children:(0,t.jsx)(Te,{size:16})})})}),(A==="running"||A==="queued")&&(0,t.jsx)(Fe,{className:"inline",size:"small"}),(0,t.jsx)("div",{className:"flex-1"}),(0,t.jsx)(C,{content:"Toggle history",children:(0,t.jsx)(z,{size:"xs",variant:"text",className:le(a&&"bg-(--sky-3) rounded-none"),onClick:()=>h(!a),disabled:u.length===0,children:(0,t.jsx)(pe,{size:16})})}),(0,t.jsx)(C,{content:(0,t.jsx)("span",{className:"block max-w-prose",children:"Use this scratchpad to experiment with code without restrictions on variable names. Variables defined here aren't saved to notebook memory, and the code is not saved in the notebook file."}),children:(0,t.jsx)(z,{size:"xs",variant:"text",children:(0,t.jsx)(Ge,{size:16})})})]}),e[19]=W,e[20]=Q,e[21]=f,e[22]=u.length,e[23]=a,e[24]=h,e[25]=A,e[26]=E):E=e[26];let $=E,je=S==="vertical",R;e[27]===Symbol.for("react.memo_cache_sentinel")?(R=Ae.create(n),e[27]=R):R=e[27];let x;e[28]===$?x=e[29]:(x=$(),e[28]=$,e[29]=x);let D;e[30]===Symbol.for("react.memo_cache_sentinel")?(D=o=>{c.current=o},e[30]=D):D=e[30];let _;e[31]!==r||e[32]!==f||e[33]!==Y||e[34]!==m||e[35]!==i?(_=(0,t.jsx)("div",{className:"flex-1 overflow-auto",children:(0,t.jsx)(Ue,{theme:m,showPlaceholder:!1,id:n,code:r,config:_e,status:"idle",serializedEditorState:null,runCell:f,userConfig:i,editorViewRef:c,setEditorView:D,hidden:!1,showHiddenCode:ie.NOOP,languageAdapter:Y,setLanguageAdapter:ge})}),e[31]=r,e[32]=f,e[33]=Y,e[34]=m,e[35]=i,e[36]=_):_=e[36];let v;e[37]===Z?v=e[38]:(v=Z(),e[37]=Z,e[38]=v);let g;e[39]!==v||e[40]!==x||e[41]!==_?(g=(0,t.jsx)(ne,{defaultSize:40,minSize:20,maxSize:70,children:(0,t.jsxs)("div",{className:"h-full flex flex-col overflow-hidden relative",children:[x,_,v]})}),e[39]=v,e[40]=x,e[41]=_,e[42]=g):g=e[42];let ee=je?"h-1":"w-1",j;e[43]===ee?j=e[44]:(j=le("bg-border hover:bg-primary/50 transition-colors",ee),e[43]=ee,e[44]=j);let y;e[45]===j?y=e[46]:(y=(0,t.jsx)(Ze,{className:j}),e[45]=j,e[46]=y);let b;e[47]===F?b=e[48]:(b=(0,t.jsx)("div",{className:"flex-1 overflow-auto",children:(0,t.jsx)(Me,{allowExpand:!1,output:F,className:Pe.outputArea,cellId:n,stale:!1,loading:!1})}),e[47]=F,e[48]=b);let w;e[49]===J?w=e[50]:(w=(0,t.jsx)("div",{className:"overflow-auto shrink-0 max-h-[50%]",children:(0,t.jsx)(De,{consoleOutputs:J,className:"overflow-auto",stale:!1,cellName:"_",onSubmitDebugger:ie.NOOP,cellId:n,debuggerActive:!1})}),e[49]=J,e[50]=w);let N;e[51]!==b||e[52]!==w?(N=(0,t.jsx)(ne,{defaultSize:60,minSize:20,children:(0,t.jsxs)("div",{className:"h-full flex flex-col divide-y overflow-hidden",children:[b,w]})}),e[51]=b,e[52]=w,e[53]=N):N=e[53];let H;return e[54]!==S||e[55]!==B||e[56]!==g||e[57]!==y||e[58]!==N?(H=(0,t.jsx)("div",{className:"flex flex-col h-full overflow-hidden",id:R,children:(0,t.jsxs)(Ye,{direction:S,className:"h-full",children:[g,y,N]},B)}),e[54]=S,e[55]=B,e[56]=g,e[57]=y,e[58]=N,e[59]=H):H=e[59],H};let te;te=se(),ce=()=>{let e=(0,te.c)(1),s;return e[0]===Symbol.for("react.memo_cache_sentinel")?(s=(0,t.jsx)(ve,{}),e[0]=s):s=e[0],s}});export{ot as __tla,ce as default};
|
package/dist/index.html
CHANGED
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
<marimo-server-token data-token="{{ server_token }}" hidden></marimo-server-token>
|
|
67
67
|
<!-- /TODO -->
|
|
68
68
|
<title>{{ title }}</title>
|
|
69
|
-
<script type="module" crossorigin src="./assets/index-
|
|
69
|
+
<script type="module" crossorigin src="./assets/index-BMdbqayP.js"></script>
|
|
70
70
|
<link rel="modulepreload" crossorigin href="./assets/preload-helper-BW0IMuFq.js">
|
|
71
71
|
<link rel="modulepreload" crossorigin href="./assets/clsx-A78R3nYl.js">
|
|
72
72
|
<link rel="modulepreload" crossorigin href="./assets/cn-d_A4rA-x.js">
|
|
@@ -216,10 +216,10 @@
|
|
|
216
216
|
<link rel="modulepreload" crossorigin href="./assets/file-DVwc11C3.js">
|
|
217
217
|
<link rel="modulepreload" crossorigin href="./assets/image-CBwHq93J.js">
|
|
218
218
|
<link rel="modulepreload" crossorigin href="./assets/play-CkqWG85-.js">
|
|
219
|
-
<link rel="modulepreload" crossorigin href="./assets/add-cell-with-ai-
|
|
219
|
+
<link rel="modulepreload" crossorigin href="./assets/add-cell-with-ai-BO7kWP2P.js">
|
|
220
220
|
<link rel="modulepreload" crossorigin href="./assets/isEmpty-9KlyX4nB.js">
|
|
221
221
|
<link rel="modulepreload" crossorigin href="./assets/bot-message-square-BzawfL5p.js">
|
|
222
|
-
<link rel="modulepreload" crossorigin href="./assets/chat-display-
|
|
222
|
+
<link rel="modulepreload" crossorigin href="./assets/chat-display-COL62Kpv.js">
|
|
223
223
|
<link rel="modulepreload" crossorigin href="./assets/chart-no-axes-column-F2DZENgf.js">
|
|
224
224
|
<link rel="modulepreload" crossorigin href="./assets/square-function-DEp1ozoA.js">
|
|
225
225
|
<link rel="modulepreload" crossorigin href="./assets/spec-BQ0QuStT.js">
|
package/package.json
CHANGED
|
@@ -488,6 +488,133 @@ describe("RunStaleCellsTool", () => {
|
|
|
488
488
|
// Output should be capped (2000 chars content + "Output:\n" prefix + truncation message)
|
|
489
489
|
expect(output.length).toBeLessThan(2200);
|
|
490
490
|
});
|
|
491
|
+
|
|
492
|
+
it("should omit output for cells that exceed total output budget", async () => {
|
|
493
|
+
const cellIds = Array.from(
|
|
494
|
+
{ length: 25 },
|
|
495
|
+
(_, i) => `budget-cell-${i}` as CellId,
|
|
496
|
+
);
|
|
497
|
+
const cellData: Record<string, { code: string; edited: boolean }> = {};
|
|
498
|
+
for (const id of cellIds) {
|
|
499
|
+
cellData[id] = { code: "x = 1", edited: true };
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
const notebook = MockNotebook.notebookState({ cellData });
|
|
503
|
+
store.set(notebookAtom, notebook);
|
|
504
|
+
|
|
505
|
+
vi.mocked(runCells).mockImplementation(async () => {
|
|
506
|
+
const updatedNotebook = store.get(notebookAtom);
|
|
507
|
+
for (const id of cellIds) {
|
|
508
|
+
updatedNotebook.cellRuntime[id] = {
|
|
509
|
+
...updatedNotebook.cellRuntime[id],
|
|
510
|
+
status: "idle",
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
store.set(notebookAtom, updatedNotebook);
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
// Each cell produces ~2008 chars of formatted output ("Output:\n" + 2000 chars).
|
|
517
|
+
// After 20 cells the running total exceeds MAX_TOOL_OUTPUT_CHARS (40,000).
|
|
518
|
+
const content = "a".repeat(2000);
|
|
519
|
+
vi.mocked(getCellContextData).mockReturnValue({
|
|
520
|
+
cellOutput: {
|
|
521
|
+
outputType: "text",
|
|
522
|
+
processedContent: content,
|
|
523
|
+
imageUrl: null,
|
|
524
|
+
output: { mimetype: "text/plain", data: content },
|
|
525
|
+
},
|
|
526
|
+
consoleOutputs: null,
|
|
527
|
+
cellName: "cell",
|
|
528
|
+
} as never);
|
|
529
|
+
|
|
530
|
+
const result = await tool.handler({}, toolContext as never);
|
|
531
|
+
|
|
532
|
+
expect(result.cellsToOutput?.[cellIds[0]]?.cellOutput).toContain(
|
|
533
|
+
"Output:",
|
|
534
|
+
);
|
|
535
|
+
expect(result.cellsToOutput?.[cellIds[24]]?.cellOutput).toBe(
|
|
536
|
+
"Cell executed (output omitted due to context limits).",
|
|
537
|
+
);
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
it("should use higher truncation limit for error outputs", async () => {
|
|
541
|
+
const notebook = MockNotebook.notebookState({
|
|
542
|
+
cellData: {
|
|
543
|
+
[cellId1]: { code: "raise Exception()", edited: true },
|
|
544
|
+
},
|
|
545
|
+
});
|
|
546
|
+
store.set(notebookAtom, notebook);
|
|
547
|
+
|
|
548
|
+
vi.mocked(runCells).mockImplementation(async () => {
|
|
549
|
+
const updatedNotebook = store.get(notebookAtom);
|
|
550
|
+
updatedNotebook.cellRuntime[cellId1] = {
|
|
551
|
+
...updatedNotebook.cellRuntime[cellId1],
|
|
552
|
+
status: "idle",
|
|
553
|
+
};
|
|
554
|
+
store.set(notebookAtom, updatedNotebook);
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
// 2500 chars sits between MAX_TEXT_OUTPUT_CHARS (2000) and MAX_ERROR_OUTPUT_CHARS (3000)
|
|
558
|
+
const errorContent = "E".repeat(2500);
|
|
559
|
+
vi.mocked(getCellContextData).mockReturnValue({
|
|
560
|
+
cellOutput: {
|
|
561
|
+
outputType: "text",
|
|
562
|
+
processedContent: errorContent,
|
|
563
|
+
imageUrl: null,
|
|
564
|
+
output: {
|
|
565
|
+
mimetype: "application/vnd.marimo+error",
|
|
566
|
+
data: errorContent,
|
|
567
|
+
},
|
|
568
|
+
},
|
|
569
|
+
consoleOutputs: null,
|
|
570
|
+
cellName: "cell1",
|
|
571
|
+
} as never);
|
|
572
|
+
|
|
573
|
+
const result = await tool.handler({}, toolContext as never);
|
|
574
|
+
|
|
575
|
+
const output = result.cellsToOutput?.[cellId1]?.cellOutput ?? "";
|
|
576
|
+
expect(output).not.toContain("[TRUNCATED:");
|
|
577
|
+
expect(output).toContain(errorContent);
|
|
578
|
+
});
|
|
579
|
+
|
|
580
|
+
it("should truncate large console output", async () => {
|
|
581
|
+
const notebook = MockNotebook.notebookState({
|
|
582
|
+
cellData: {
|
|
583
|
+
[cellId1]: { code: 'print("x" * 10000)', edited: true },
|
|
584
|
+
},
|
|
585
|
+
});
|
|
586
|
+
store.set(notebookAtom, notebook);
|
|
587
|
+
|
|
588
|
+
vi.mocked(runCells).mockImplementation(async () => {
|
|
589
|
+
const updatedNotebook = store.get(notebookAtom);
|
|
590
|
+
updatedNotebook.cellRuntime[cellId1] = {
|
|
591
|
+
...updatedNotebook.cellRuntime[cellId1],
|
|
592
|
+
status: "idle",
|
|
593
|
+
};
|
|
594
|
+
store.set(notebookAtom, updatedNotebook);
|
|
595
|
+
});
|
|
596
|
+
|
|
597
|
+
const largeConsoleText = "x".repeat(10_000);
|
|
598
|
+
vi.mocked(getCellContextData).mockReturnValue({
|
|
599
|
+
cellOutput: null,
|
|
600
|
+
consoleOutputs: [
|
|
601
|
+
{
|
|
602
|
+
outputType: "text",
|
|
603
|
+
processedContent: largeConsoleText,
|
|
604
|
+
imageUrl: null,
|
|
605
|
+
output: { mimetype: "text/plain", data: largeConsoleText },
|
|
606
|
+
},
|
|
607
|
+
],
|
|
608
|
+
cellName: "cell1",
|
|
609
|
+
} as never);
|
|
610
|
+
|
|
611
|
+
const result = await tool.handler({}, toolContext as never);
|
|
612
|
+
|
|
613
|
+
const consoleOutput =
|
|
614
|
+
result.cellsToOutput?.[cellId1]?.consoleOutput ?? "";
|
|
615
|
+
expect(consoleOutput).toContain("[TRUNCATED:");
|
|
616
|
+
expect(consoleOutput.length).toBeLessThan(2200);
|
|
617
|
+
});
|
|
491
618
|
});
|
|
492
619
|
|
|
493
620
|
describe("cell execution completion", () => {
|
|
@@ -97,7 +97,7 @@ export class RunStaleCellsTool
|
|
|
97
97
|
|
|
98
98
|
await runCells({
|
|
99
99
|
cellIds: staleCells,
|
|
100
|
-
sendRun
|
|
100
|
+
sendRun,
|
|
101
101
|
prepareForRun,
|
|
102
102
|
notebook,
|
|
103
103
|
});
|
|
@@ -121,8 +121,8 @@ export class RunStaleCellsTool
|
|
|
121
121
|
const updatedNotebook = store.get(notebookAtom);
|
|
122
122
|
|
|
123
123
|
const cellsToOutput = new Map<CellId, CellOutput | null>();
|
|
124
|
-
let resultMessage = "";
|
|
125
124
|
let outputHasErrors = false;
|
|
125
|
+
let hasAnyConsoleOutput = false;
|
|
126
126
|
let totalOutputChars = 0;
|
|
127
127
|
|
|
128
128
|
for (const cellId of staleCells) {
|
|
@@ -135,12 +135,10 @@ export class RunStaleCellsTool
|
|
|
135
135
|
const hasConsoleOutput = consoleOutputs && consoleOutputs.length > 0;
|
|
136
136
|
|
|
137
137
|
// Track errors regardless of budget
|
|
138
|
-
if (cellOutput && this.outputHasErrors(cellOutput)) {
|
|
139
|
-
outputHasErrors = true;
|
|
140
|
-
}
|
|
141
138
|
if (
|
|
142
|
-
|
|
143
|
-
|
|
139
|
+
(cellOutput && this.outputHasErrors(cellOutput)) ||
|
|
140
|
+
(hasConsoleOutput &&
|
|
141
|
+
consoleOutputs.some((output) => this.outputHasErrors(output)))
|
|
144
142
|
) {
|
|
145
143
|
outputHasErrors = true;
|
|
146
144
|
}
|
|
@@ -167,6 +165,7 @@ export class RunStaleCellsTool
|
|
|
167
165
|
}
|
|
168
166
|
|
|
169
167
|
if (hasConsoleOutput) {
|
|
168
|
+
hasAnyConsoleOutput = true;
|
|
170
169
|
consoleOutputString = consoleOutputs
|
|
171
170
|
.map((output) => this.formatOutputString(output))
|
|
172
171
|
.join("\n");
|
|
@@ -175,8 +174,6 @@ export class RunStaleCellsTool
|
|
|
175
174
|
MAX_TEXT_OUTPUT_CHARS,
|
|
176
175
|
);
|
|
177
176
|
totalOutputChars += consoleOutputString.length;
|
|
178
|
-
resultMessage +=
|
|
179
|
-
"Console output represents the stdout or stderr of the cell (eg. print statements).";
|
|
180
177
|
}
|
|
181
178
|
|
|
182
179
|
cellsToOutput.set(cellId, {
|
|
@@ -202,57 +199,50 @@ export class RunStaleCellsTool
|
|
|
202
199
|
return {
|
|
203
200
|
status: "success",
|
|
204
201
|
cellsToOutput: Object.fromEntries(cellsToOutput),
|
|
205
|
-
message:
|
|
202
|
+
message: hasAnyConsoleOutput
|
|
203
|
+
? "Console output represents the stdout or stderr of the cell (eg. print statements)."
|
|
204
|
+
: undefined,
|
|
206
205
|
next_steps: nextSteps,
|
|
207
206
|
};
|
|
208
207
|
};
|
|
209
208
|
|
|
210
209
|
private outputHasErrors(cellOutput: BaseOutput): boolean {
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
output.mimetype === "application/vnd.marimo+
|
|
214
|
-
|
|
215
|
-
) {
|
|
216
|
-
return true;
|
|
217
|
-
}
|
|
218
|
-
return false;
|
|
210
|
+
return (
|
|
211
|
+
cellOutput.output.mimetype === "application/vnd.marimo+error" ||
|
|
212
|
+
cellOutput.output.mimetype === "application/vnd.marimo+traceback"
|
|
213
|
+
);
|
|
219
214
|
}
|
|
220
215
|
|
|
221
216
|
private formatOutputString(cellOutput: BaseOutput): string {
|
|
222
|
-
let outputString = "";
|
|
223
217
|
const { outputType, processedContent, imageUrl, output } = cellOutput;
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
outputString += "Output:\n";
|
|
239
|
-
let content: string;
|
|
240
|
-
if (processedContent) {
|
|
241
|
-
content = processedContent;
|
|
242
|
-
} else if (typeof output.data === "string") {
|
|
243
|
-
content = output.data;
|
|
244
|
-
} else {
|
|
245
|
-
content = JSON.stringify(output.data);
|
|
246
|
-
}
|
|
247
|
-
outputString += this.truncateString(content, maxChars);
|
|
248
|
-
}
|
|
249
|
-
} else if (outputType === "media") {
|
|
250
|
-
outputString += `Media Output: Contains ${output.mimetype} content`;
|
|
251
|
-
if (imageUrl) {
|
|
252
|
-
outputString += `\nImage URL: ${imageUrl}`;
|
|
253
|
-
}
|
|
218
|
+
|
|
219
|
+
if (outputType === "media") {
|
|
220
|
+
const base = `Media Output: Contains ${output.mimetype} content`;
|
|
221
|
+
return imageUrl ? `${base}\nImage URL: ${imageUrl}` : base;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (output.mimetype === "text/html") {
|
|
225
|
+
// text/html (e.g. plotly figures, rich dataframes) can be millions of
|
|
226
|
+
// chars and is not interpretable by LLMs — summarize instead
|
|
227
|
+
const dataLength =
|
|
228
|
+
typeof output.data === "string"
|
|
229
|
+
? output.data.length
|
|
230
|
+
: JSON.stringify(output.data).length;
|
|
231
|
+
return `HTML Output: text/html content (${dataLength.toLocaleString()} chars). Full output visible in notebook UI.`;
|
|
254
232
|
}
|
|
255
|
-
|
|
233
|
+
|
|
234
|
+
const maxChars = this.outputHasErrors(cellOutput)
|
|
235
|
+
? MAX_ERROR_OUTPUT_CHARS
|
|
236
|
+
: MAX_TEXT_OUTPUT_CHARS;
|
|
237
|
+
|
|
238
|
+
let content = processedContent;
|
|
239
|
+
if (!content) {
|
|
240
|
+
content =
|
|
241
|
+
typeof output.data === "string"
|
|
242
|
+
? output.data
|
|
243
|
+
: JSON.stringify(output.data);
|
|
244
|
+
}
|
|
245
|
+
return `Output:\n${this.truncateString(content, maxChars)}`;
|
|
256
246
|
}
|
|
257
247
|
|
|
258
248
|
private truncateString(str: string, maxLength: number): string {
|
|
@@ -261,7 +251,6 @@ export class RunStaleCellsTool
|
|
|
261
251
|
}
|
|
262
252
|
return `${str.slice(0, maxLength)}\n\n[TRUNCATED: ${str.length.toLocaleString()} → ${maxLength.toLocaleString()} chars. Full output visible in the notebook UI.]`;
|
|
263
253
|
}
|
|
264
|
-
|
|
265
254
|
/**
|
|
266
255
|
* Wait for cells to finish executing (status becomes "idle")
|
|
267
256
|
* Returns true if all cells finished executing, false if the timeout was reached
|
|
@@ -250,6 +250,7 @@ const STANDARD_POINT_KEYS: string[] = [
|
|
|
250
250
|
"lon",
|
|
251
251
|
"curveNumber",
|
|
252
252
|
"pointNumber",
|
|
253
|
+
"pointNumbers",
|
|
253
254
|
"pointIndex",
|
|
254
255
|
];
|
|
255
256
|
|
|
@@ -263,6 +264,8 @@ function extractPoints(
|
|
|
263
264
|
let parser: PlotlyTemplateParser | undefined;
|
|
264
265
|
|
|
265
266
|
return points.map((point) => {
|
|
267
|
+
const standardPointFields = pick(point, STANDARD_POINT_KEYS);
|
|
268
|
+
|
|
266
269
|
// Get the first hovertemplate
|
|
267
270
|
const hovertemplate = Array.isArray(point.data.hovertemplate)
|
|
268
271
|
? point.data.hovertemplate[0]
|
|
@@ -271,13 +274,16 @@ function extractPoints(
|
|
|
271
274
|
// For chart types with standard point keys (e.g. heatmaps),
|
|
272
275
|
// or when there's no hovertemplate, pick keys directly from the point.
|
|
273
276
|
if (!hovertemplate || point.data?.type === "heatmap") {
|
|
274
|
-
return
|
|
277
|
+
return standardPointFields;
|
|
275
278
|
}
|
|
276
279
|
|
|
277
280
|
// Update or create a parser
|
|
278
281
|
parser = parser
|
|
279
282
|
? parser.update(hovertemplate)
|
|
280
283
|
: createParser(hovertemplate);
|
|
281
|
-
return
|
|
284
|
+
return {
|
|
285
|
+
...standardPointFields,
|
|
286
|
+
...parser.parse(point),
|
|
287
|
+
};
|
|
282
288
|
});
|
|
283
289
|
}
|