plainbook 0.0.8__py3-none-any.whl → 0.0.10__py3-none-any.whl

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.
plainbook/plainbook.py CHANGED
@@ -16,6 +16,22 @@ class ExecutionError(Exception):
16
16
  """Custom exception for execution errors in Plainbook."""
17
17
  pass
18
18
 
19
+ def getlist(value):
20
+ """Utility to ensure a value is a list."""
21
+ if isinstance(value, list):
22
+ return value
23
+ else:
24
+ return [value]
25
+
26
+ def tostring(value):
27
+ """Utility to ensure a value is a string."""
28
+ if isinstance(value, str):
29
+ return value
30
+ elif isinstance(value, list):
31
+ return "".join(value)
32
+ else:
33
+ return str(value)
34
+
19
35
  class Plainbook(object):
20
36
  """This class implements an Plainbook and its operations."""
21
37
 
@@ -26,7 +42,9 @@ class Plainbook(object):
26
42
  self.nb = None
27
43
  self._lock = threading.Lock()
28
44
  self.last_executed_cell = -1
29
- self.load_notebook()
45
+ self._load_notebook()
46
+ # Context for notebook processing.
47
+ self.input_files = []
30
48
  # Starts the kernel.
31
49
  self.km = KernelManager()
32
50
  self.km.start_kernel()
@@ -41,11 +59,20 @@ class Plainbook(object):
41
59
  # Register the cleanup function
42
60
  atexit.register(self._shutdown)
43
61
 
44
- def load_notebook(self):
62
+ def _load_notebook(self):
45
63
  """Loads the notebook from the specified path. If the file is missing, create an empty notebook."""
46
64
  try:
47
65
  with open(self.path) as f:
48
66
  self.nb = nbformat.read(f, as_version=4)
67
+ for cell in self.nb.cells:
68
+ cell.source = tostring(cell.source)
69
+ if cell.cell_type == 'code':
70
+ if 'explanation' not in cell.metadata:
71
+ cell.metadata['explanation'] = ""
72
+ else:
73
+ cell.metadata['explanation'] = tostring(cell.metadata['explanation'])
74
+ if 'codegen' not in cell.metadata:
75
+ cell.metadata['codegen'] = False
49
76
  except (FileNotFoundError, OSError):
50
77
  # Ensure parent directory exists
51
78
  parent = os.path.dirname(self.path) or "."
@@ -245,6 +272,7 @@ class Plainbook(object):
245
272
  else:
246
273
  new_cell = nbformat.v4.new_code_cell(source="", execution_count=None, outputs=[])
247
274
  new_cell.metadata['explanation'] = []
275
+ new_cell.metadata['codegen'] = False
248
276
  self.nb.cells.insert(index, new_cell)
249
277
  # Inserting code cells before the last executed cell requires resetting the kernel.
250
278
  if cell_type == 'code' and index <= self.last_executed_cell:
@@ -296,10 +324,12 @@ class Plainbook(object):
296
324
  """Sets the source code of a cell at the given index."""
297
325
  with self._lock:
298
326
  assert 0 <= index < len(self.nb.cells)
299
- self.nb.cells[index].source = source
300
- if self.nb.cells[index].cell_type == 'code':
327
+ cell = self.nb.cells[index]
328
+ cell.source = source
329
+ if cell.cell_type == 'code':
330
+ cell.metadata['codegen'] = False
301
331
  # Reset outputs and execution count on code cell edit
302
- self.nb.cells[index].outputs = []
332
+ cell.outputs = []
303
333
  if index <= self.last_executed_cell:
304
334
  # We need to restart.
305
335
  self._reset_kernel()
@@ -312,6 +342,7 @@ class Plainbook(object):
312
342
  cell = self.nb.cells[index]
313
343
  assert cell.cell_type == 'code'
314
344
  cell.metadata['explanation'] = explanation
345
+ cell.metadata['codegen'] = False
315
346
  self._write()
316
347
 
317
348
  # Methods to support AI
@@ -321,15 +352,16 @@ class Plainbook(object):
321
352
  Needs to be called with the lock held."""
322
353
  cell = self.nb.cells[index]
323
354
  if cell.cell_type == 'code':
324
- explanation = cell.metadata.get('explanation', [])
325
- explanation = ["# " + line for line in explanation]
326
- explanation_text = "\n".join(explanation) + "\n"
327
- code_text = "\n".join(cell.source)
328
- return explanation_text + code_text
355
+ explanation = cell.metadata.get('explanation', "")
356
+ explanation = ["# " + line for line in explanation.splitlines(keepends=True)]
357
+ explanation_text = "".join(explanation) + "\n"
358
+ source_code = cell.source + "\n"
359
+ return explanation_text + source_code
329
360
  elif cell.cell_type == 'markdown':
330
- return "\n".join(["# " + line for line in cell.source])
361
+ commented_lines = ["# " + line for line in cell.source.splitlines(keepends=True)]
362
+ return "".join(commented_lines) + "\n"
331
363
  else:
332
- return ""
364
+ return "\n"
333
365
 
334
366
  def _get_code_for_ai(self, index):
335
367
  """Returns the concatenated source code of all previous code cells for context."""
@@ -346,21 +378,26 @@ class Plainbook(object):
346
378
  cell = self.nb.cells[index]
347
379
  assert cell.cell_type == 'code'
348
380
  instructions = cell.metadata.get('explanation')
381
+ files_context = self._get_files_context()
382
+ error_context = self._get_error_context(index)
349
383
  previous_code = self._get_code_for_ai(index)
350
384
  # Mark that an AI request is pending
351
385
  try:
352
- new_code = gemini_generate_code(api_key, previous_code, instructions)
386
+ new_code = gemini_generate_code(
387
+ api_key, previous_code=previous_code, instructions=instructions,
388
+ file_context=files_context, error_context=error_context)
353
389
  # If we are still in a request, update the cell.
354
390
  if self.ai_request_pending:
355
391
  cell.source = new_code
392
+ cell.metadata['codegen'] = True
356
393
  # Reset outputs and execution count
357
394
  cell.outputs = []
358
395
  if index <= self.last_executed_cell:
359
396
  self._reset_kernel()
360
397
  self._write()
361
- return new_code
398
+ return new_code, True
362
399
  else:
363
- return None
400
+ return None, False
364
401
  finally:
365
402
  self.ai_request_pending = False
366
403
 
@@ -400,3 +437,32 @@ class Plainbook(object):
400
437
  cell.metadata['validation'] = {}
401
438
  cell.metadata['validation']['is_hidden'] = is_hidden
402
439
  self._write()
440
+
441
+ def set_input_files(self, files):
442
+ """Sets the input files for the notebook."""
443
+ self.input_files = files
444
+
445
+ def _get_files_context(self):
446
+ """Builds the AI context including input files."""
447
+ context_parts = [
448
+ "Here is a list of file names and paths. "
449
+ "The user may mention input files; to access them, the full path should be used."
450
+ ]
451
+ for file in self.input_files:
452
+ context_parts.append(f"* File name: {file['name']} path: {file['path']}\n")
453
+ return "\n".join(context_parts) + "\n"
454
+
455
+ def _get_error_context(self, cell_index):
456
+ """If the cell has an error, include its traceback as context."""
457
+ context_parts = [
458
+ "The previous attempt to run this cell resulted in this error traceback:"
459
+ ]
460
+ cell = self.nb.cells[cell_index]
461
+ if cell.cell_type != 'code':
462
+ return None
463
+ for output in reversed(getlist(cell.get('outputs', []))):
464
+ if output.output_type == 'error':
465
+ traceback = context_parts + getlist(output.get('traceback', []))
466
+ return "\n".join(traceback)
467
+ return None
468
+
@@ -29,100 +29,108 @@
29
29
  </style>
30
30
  </head>
31
31
  <body class="has-navbar-fixed-top">
32
+
33
+ <div id="app"></div>
32
34
 
33
35
  <script type="text/x-template" id="app-template">
34
36
 
35
- <app-navbar
36
- :is-locked="isLocked"
37
- :running="running"
38
- :has-notebook="!!notebook"
39
- :last-run-index="lastRunIndex"
40
- :cell-count="notebook ? notebook.cells.length : 0"
41
- :has-api-key="!!geminiApiKey"
42
- @lock="lockNotebook"
43
- @refresh="reloadNotebook"
44
- @interrupt="interruptKernel"
45
- @regenerate-all="regenerateAllCode"
46
- @reset-run-all="resetAndRunAllCells"
47
- @open-info="showInfo = true"
48
- @open-settings="showSettings = true"
49
- />
37
+ <app-navbar
38
+ :is-locked="isLocked"
39
+ :running="running"
40
+ :has-notebook="!!notebook"
41
+ :last-run-index="lastRunIndex"
42
+ :cell-count="notebook ? notebook.cells.length : 0"
43
+ :has-api-key="!!geminiApiKey"
44
+ @lock="lockNotebook"
45
+ @refresh="reloadNotebook"
46
+ @interrupt="interruptKernel"
47
+ @regenerate-all="regenerateAllCode"
48
+ @reset-run-all="resetAndRunAllCells"
49
+ @open-info="showInfo = true"
50
+ @open-settings="showSettings = true"
51
+ />
50
52
 
51
- <div v-if="uiError" class="notification mb-0 mt-0 px-4 pl-2 pr-6 has-text-danger has-background-danger-light"
52
- style="width: 100%; border-radius: 0; align-items: center; justify-content: space-between;">
53
- <span><strong>Error:</strong> {{ uiError }}</span>
54
- <button class="delete" @click="closeUiError"></button>
55
- </div>
53
+ <input-file
54
+ :auth-token="authToken"
55
+ />
56
+
57
+ <ui-error
58
+ :error="uiError"
59
+ @close="closeUiError"
60
+ />
56
61
 
57
- <div class="section notebook-area">
58
- <h1 class="title">
59
- {{ notebook_name }}
60
- </h1>
61
- <div class="notebook-container px-2 py-2">
62
- <div v-if="loading" class="has-text-centered py-6">
63
- <button class="button is-loading is-ghost is-large">Loading</button>
64
- <p class="has-text-grey">Fetching notebook data...</p>
65
- </div>
66
- <div v-else-if="error" class="notification is-danger is-light">
67
- <button class="delete" @click="error = null"></button>
68
- <strong>Error:</strong> {{ error }}
69
- </div>
70
- <div v-else="notebook">
62
+ <div class="section notebook-area">
63
+ <h1 class="title">
64
+ {{ notebook_name }}
65
+ </h1>
66
+ <div class="notebook-container px-2 py-2">
67
+ <div v-if="loading" class="has-text-centered py-6">
68
+ <button class="button is-loading is-ghost is-large">Loading</button>
69
+ <p class="has-text-grey">Fetching notebook data...</p>
70
+ </div>
71
+ <div v-else-if="error" class="notification is-danger is-light">
72
+ <button class="delete" @click="error = null"></button>
73
+ <strong>Error:</strong> {{ error }}
74
+ </div>
75
+ <div v-else="notebook">
76
+
77
+ <template v-for="(cell, index) in notebook.cells" :key="index">
71
78
 
72
79
  <cell-insertion-zone v-if="!isLocked"
73
- @insert="(celltype) => insertCell(0, celltype)"
80
+ :is-last="false"
81
+ @insert="(celltype) => insertCell(index, celltype)"
82
+ />
83
+
84
+ <notebook-cell
85
+ :cell="cell"
86
+ :is-active="activeIndex === index"
87
+ :needs-running="index > lastRunIndex"
88
+ :is-locked="isLocked"
89
+ :last-run-index="lastRunIndex"
90
+ :as-read="asRead"
91
+ :markdown-edit-key="markdownEditKey[index]"
92
+ :explanation-edit-key="explanationEditKey[index]"
93
+ @click="setActiveCell(index)"
94
+
95
+ @save-markdown="(content) => sendMarkdownToServer(content, index)"
96
+ @save-explanation="(content) => sendExplanationToServer(content, index)"
97
+ @save-code="(content) => sendCodeToServer(content, index)"
98
+ @save-and-run="(content) => saveExplanationAndRun(content, index)"
99
+
100
+ @run-cell="runCell(index)"
101
+ @generate-code="generateCode(index)"
102
+ @validate-code="validateCode(index)"
103
+ @dismiss-validation="dismissValidation(index)"
104
+
105
+ @delete="deleteCell(index)"
106
+ @move-up="moveCell(index, -1)"
107
+ @move-down="moveCell(index, 1)"
74
108
  />
75
109
 
76
- <template v-for="(cell, index) in notebook.cells" :key="index">
77
-
78
- <notebook-cell
79
- :cell="cell"
80
- :is-active="activeIndex === index"
81
- :needs-running="index > lastRunIndex"
82
- :is-locked="isLocked"
83
- :last-run-index="lastRunIndex"
84
- :as-read="asRead"
85
- :markdown-edit-key="markdownEditKey[index]"
86
- :explanation-edit-key="explanationEditKey[index]"
87
- @click="setActiveCell(index)"
88
-
89
- @save-markdown="(content) => sendMarkdownToServer(content, index)"
90
- @save-explanation="(content) => sendExplanationToServer(content, index)"
91
- @save-code="(content) => sendCodeToServer(content, index)"
92
- @save-and-run="(content) => saveExplanationAndRun(content, index)"
93
-
94
- @run-cell="runCell(index)"
95
- @generate-code="generateCode(index)"
96
- @validate-code="validateCode(index)"
97
- @dismiss-validation="dismissValidation(index)"
98
-
99
- @delete="deleteCell(index)"
100
- @move-up="moveCell(index, -1)"
101
- @move-down="moveCell(index, 1)"
102
- />
103
-
104
- <cell-insertion-zone v-if="!isLocked"
105
- @insert="(celltype) => insertCell(index + 1, celltype)"
106
- />
107
-
108
- </template>
109
110
 
110
- </div>
111
+ </template>
112
+
113
+ <cell-insertion-zone v-if="!isLocked"
114
+ :is-last="true"
115
+ @insert="(celltype) => insertCell(notebook.cells.length, celltype)"
116
+ />
117
+
111
118
  </div>
112
119
  </div>
120
+ </div>
113
121
 
114
- <!-- Settings Modal -->
115
- <settings-modal
116
- :is-active="showSettings"
117
- :api-key="geminiApiKey"
118
- @close="showSettings = false"
119
- @save="(newKey) => {showSettings = false; saveSettings(newKey); }"
120
- />
121
- <!-- Info Modal -->
122
- <info-modal
123
- :is-active="showInfo"
124
- @close="showInfo = false"
125
- />
122
+ <!-- Settings Modal -->
123
+ <settings-modal
124
+ :is-active="showSettings"
125
+ :api-key="geminiApiKey"
126
+ @close="showSettings = false"
127
+ @save="(newKey) => {showSettings = false; saveSettings(newKey); }"
128
+ />
129
+ <!-- Info Modal -->
130
+ <info-modal
131
+ :is-active="showInfo"
132
+ @close="showInfo = false"
133
+ />
126
134
 
127
135
  </script>
128
136
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plainbook
3
- Version: 0.0.8
3
+ Version: 0.0.10
4
4
  Summary: Plain Language Notebooks
5
5
  Author-email: Luca de Alfaro <dealfaro@acm.org>
6
6
  License-Expression: BSD-3-Clause
@@ -1,10 +1,10 @@
1
1
  plainbook/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
2
- plainbook/gemini.py,sha256=4lgvMdQdcKyMZLSnu9yhwlaI4LaSvvyr_-SA0DdI628,2915
3
- plainbook/main.py,sha256=XruVZYj-nQ-2V9wiGKO7AV0-Via0Rz2CUafuX1id3J8,10172
4
- plainbook/plainbook.py,sha256=zgi3e50r9vFfMLxXuMVcvyB9I7yEJOgZthxnz3RxuKw,17063
2
+ plainbook/gemini.py,sha256=v0jCIE8MFQ7Fnctah9pV9YAkGHZ7E2vUAnKAPUQw_VA,3175
3
+ plainbook/main.py,sha256=52rfzj-ZwnJu0ozadBDa7bINqvEK71M2OduiIf5LhIc,11693
4
+ plainbook/plainbook.py,sha256=2PmEyfpShY1Gn_3-pApNyhXL-NidgyjH081AtPbmwjk,19808
5
5
  plainbook/css/font-awesome.css,sha256=NuCn4IvuZXdBaFKJOAcsU2Q3ZpwbdFisd5dux4jkQ5w,37414
6
6
  plainbook/css/font-awesome.min.css,sha256=eZrrJcwDc_3uDhsdt61sL2oOBY362qM3lon1gyExkL0,31000
7
- plainbook/css/main.css,sha256=HnC5t_S2JeKQtJctKZMQ-8la6vWHlUchi88P02h7kJ0,692270
7
+ plainbook/css/main.css,sha256=sWH9Nanyr0pZwVQRkqV8-zQjrK602PrxNHgW0_15lU4,692344
8
8
  plainbook/css/prism.min.css,sha256=ko4j5rn874LF8dHwW29_xabhh8YBleWfvxb8nQce4Fc,1789
9
9
  plainbook/fonts/FontAwesome.otf,sha256=RE3UNmYV_8ShbQErL6kBNwZdPMtBD6b9Xk3de15P_NU,134808
10
10
  plainbook/fonts/fontawesome-webfont.eot,sha256=e_yrbbmdXPvxcFygU23ceFhUMsxfpBu9etDwCQM7KXk,165742
@@ -13,24 +13,26 @@ plainbook/fonts/fontawesome-webfont.ttf,sha256=qljzPyOaD7AvXHpsRcBD16msmgkzNYBml
13
13
  plainbook/fonts/fontawesome-webfont.woff,sha256=ugxZ3rVFD1y0Gz-TYJ7i0NmVQVh33foiPoqKdTNHTwc,98024
14
14
  plainbook/fonts/fontawesome-webfont.woff2,sha256=Kt78vAQefRj88tQXh53FoJmXqmTWdbejxLbOM9oT8_4,77160
15
15
  plainbook/js/AppNavbar.js,sha256=nEX2CXdI0136Usj-YVts6QXV4xd_vbsXh9-mPvgbi5E,3968
16
- plainbook/js/CellInsertionZone.js,sha256=XPo2hmuG9lfVzYbZ1bBO-44HiyCFngHzxoUa56v42s8,676
17
- plainbook/js/CodeCell.js,sha256=cK6YOKVu58g4h7KmH2aL3LA99oxe8Z3ZZpLRF0k6XZY,6943
18
- plainbook/js/ExplanationEditor.js,sha256=FDflmKZKzNHkKgXHwsYrbQDLDK1ROVg00-5p9cj2jlc,7088
19
- plainbook/js/InfoModal.js,sha256=ShylUmDQdQ-2g-z6PlvZ_TTlxCSdNGJJICu-RAAwab8,1442
16
+ plainbook/js/CellInsertionZone.js,sha256=En2hItfUGdV5s2_vk41ovCkM8XLpIlLQ8wRNNmQAyzU,728
17
+ plainbook/js/CodeCell.js,sha256=Xi7M0-0ew3_UJvPoJDJBsaf9PZ3MBKKdZ4nkMVODp-o,6959
18
+ plainbook/js/ExplanationEditor.js,sha256=gPwe3fLKBy8dnprKloeCHYDgBh2znse6clacGWq0g-I,7444
19
+ plainbook/js/InfoModal.js,sha256=t1Rjz658gfuoszoE0chfi2qFqJwmCgjlaYIeIATe_Nk,1436
20
+ plainbook/js/InputFile.js,sha256=l93wGretLdu9iqcTdatTI3LQxEo-sTTi9phKPQC6zF0,7928
20
21
  plainbook/js/MarkdownCell.js,sha256=SxU_LO4TGcxOEy6sKognF_gHUXwmqR6BDsv4z0tlgVQ,5292
21
- plainbook/js/NotebookCell.js,sha256=PNWdd6VSWtD1nHeUCHPEZWIwBMQT26rjN6-P0rPCEZI,3239
22
+ plainbook/js/NotebookCell.js,sha256=-HMSZlf287IEZJQSRCKv6u-9Yd2Wvq0DB4mEvWD5Jts,3677
22
23
  plainbook/js/OutputRenderer.js,sha256=KXECI2xd0Sk_TnZnAfqfhL0dEIT9pjZZTXo6Qdiw0o4,2981
23
24
  plainbook/js/SettingsModal.js,sha256=HpU2gKj26zZHhilcfp3hghEsW-sxsgq1HBlcsIBDGCI,1689
25
+ plainbook/js/UiError.js,sha256=F3tQ3I66-3kbAsGlD9OVWWrRu9LNJ3QCSkr0tWX-R5w,439
24
26
  plainbook/js/ValidationCell.js,sha256=4XdlBNELTpeARiNVh55_7ay24CEY-aUNJy1knX8I9MU,1595
25
27
  plainbook/js/markdown-it.min.js,sha256=hNyljag6giCsjv_yKmxK8_VeHzvMDvc5u8AzmRvm1BI,103012
26
- plainbook/js/nb.js,sha256=CZs32sAevGsea7PUjJK0Prd-7_mz1G82_S9_Hpx5gmQ,22822
28
+ plainbook/js/nb.js,sha256=xLQb5IDMe1Smpa1HsH2qgNnRCG9Cl51mRdyC8AoV5_k,23464
27
29
  plainbook/js/prism-python.min.js,sha256=7UOFaFvPLUk1yNu6tL3hZgPaEyngktK_NsPa3WfpqFw,2113
28
30
  plainbook/js/prism.min.js,sha256=57iL3cbHV7L8jLET4kaYAasUp47BqPraTWOR41c_X58,18997
29
31
  plainbook/js/vue.esm-browser.js,sha256=75FuLhUTPk19sncwNIrm0BGEL0_Qw298-_v01fPWYoI,542872
30
- plainbook/views/index.html,sha256=2LEoKQMoAuCwDSTg8kpstvINJUL33QTcfLkEspS1wYI,5557
31
- plainbook-0.0.8.dist-info/licenses/LICENSE.md,sha256=JW6Hp9mBeCkRkCSeiT2KmIU3XA5mJorjcIlM2cOOxyU,1462
32
- plainbook-0.0.8.dist-info/METADATA,sha256=oqeZaTNU720_qAR6HqaiCu6Kvmpd7pj8ofV1hxkvOvo,3023
33
- plainbook-0.0.8.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
34
- plainbook-0.0.8.dist-info/entry_points.txt,sha256=fktud-zRh9ZZ4rXv5w6kW-78u44lSYklXY1ttMTB9k8,50
35
- plainbook-0.0.8.dist-info/top_level.txt,sha256=Y3jOV2n79dczNw9tJ7acufz3y3ns2pWw8b06sIN4ltc,10
36
- plainbook-0.0.8.dist-info/RECORD,,
32
+ plainbook/views/index.html,sha256=hBuTsAh86BGUQxjyIQG1ZWTAsrOI3Rh6NNbppqze6Hc,5188
33
+ plainbook-0.0.10.dist-info/licenses/LICENSE.md,sha256=JW6Hp9mBeCkRkCSeiT2KmIU3XA5mJorjcIlM2cOOxyU,1462
34
+ plainbook-0.0.10.dist-info/METADATA,sha256=4lblvcP571gixqk-P7chUSKy-37nyUgMSWQotiPutDc,3024
35
+ plainbook-0.0.10.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
36
+ plainbook-0.0.10.dist-info/entry_points.txt,sha256=fktud-zRh9ZZ4rXv5w6kW-78u44lSYklXY1ttMTB9k8,50
37
+ plainbook-0.0.10.dist-info/top_level.txt,sha256=Y3jOV2n79dczNw9tJ7acufz3y3ns2pWw8b06sIN4ltc,10
38
+ plainbook-0.0.10.dist-info/RECORD,,