Spaces:
Running
Running
looking decent
Browse files- Dockerfile +1 -1
- app.py +446 -392
- layouts/app.slides.json +4 -0
- requirements.txt +603 -5
Dockerfile
CHANGED
|
@@ -16,4 +16,4 @@ RUN mkdir -p /app/__marimo__ && \
|
|
| 16 |
chmod -R 755 /app
|
| 17 |
USER user
|
| 18 |
|
| 19 |
-
CMD ["marimo", "run", "app.py", "--
|
|
|
|
| 16 |
chmod -R 755 /app
|
| 17 |
USER user
|
| 18 |
|
| 19 |
+
CMD ["marimo", "run", "app.py", "--token-password","${CICEROJOBAPPS_API_KEY}","--host", "0.0.0.0", "--port", "7860"]
|
app.py
CHANGED
|
@@ -1,470 +1,524 @@
|
|
| 1 |
import marimo
|
| 2 |
|
| 3 |
-
__generated_with = "0.
|
| 4 |
app = marimo.App()
|
| 5 |
|
| 6 |
|
| 7 |
@app.cell
|
| 8 |
-
def
|
| 9 |
import marimo as mo
|
| 10 |
|
| 11 |
-
mo.md(
|
| 12 |
return (mo,)
|
| 13 |
|
| 14 |
|
| 15 |
@app.cell
|
| 16 |
-
def
|
| 17 |
-
|
| 18 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
|
| 20 |
|
| 21 |
-
@app.cell
|
| 22 |
-
def
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
marimo is a **reactive** Python notebook.
|
| 26 |
-
|
| 27 |
-
This means that unlike traditional notebooks, marimo notebooks **run
|
| 28 |
-
automatically** when you modify them or
|
| 29 |
-
interact with UI elements, like this slider: {slider}.
|
| 30 |
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
|
|
|
| 35 |
|
| 36 |
|
| 37 |
@app.cell(hide_code=True)
|
| 38 |
-
def
|
| 39 |
-
mo.
|
| 40 |
-
{
|
| 41 |
-
"Tip: disabling automatic execution": mo.md(
|
| 42 |
-
rf"""
|
| 43 |
-
marimo lets you disable automatic execution: just go into the
|
| 44 |
-
notebook settings and set
|
| 45 |
-
|
| 46 |
-
"Runtime > On Cell Change" to "lazy".
|
| 47 |
-
|
| 48 |
-
When the runtime is lazy, after running a cell, marimo marks its
|
| 49 |
-
descendants as stale instead of automatically running them. The
|
| 50 |
-
lazy runtime puts you in control over when cells are run, while
|
| 51 |
-
still giving guarantees about the notebook state.
|
| 52 |
-
"""
|
| 53 |
-
)
|
| 54 |
-
}
|
| 55 |
-
)
|
| 56 |
-
return
|
| 57 |
|
|
|
|
| 58 |
|
| 59 |
-
|
| 60 |
-
def __(mo):
|
| 61 |
-
mo.md(
|
| 62 |
-
"""
|
| 63 |
-
Tip: This is a tutorial notebook. You can create your own notebooks
|
| 64 |
-
by entering `marimo edit` at the command line.
|
| 65 |
-
"""
|
| 66 |
-
).callout()
|
| 67 |
-
return
|
| 68 |
|
| 69 |
|
| 70 |
-
|
| 71 |
-
def __(mo):
|
| 72 |
-
mo.md(
|
| 73 |
-
"""
|
| 74 |
-
## 1. Reactive execution
|
| 75 |
-
|
| 76 |
-
A marimo notebook is made up of small blocks of Python code called
|
| 77 |
-
cells.
|
| 78 |
-
|
| 79 |
-
marimo reads your cells and models the dependencies among them: whenever
|
| 80 |
-
a cell that defines a global variable is run, marimo
|
| 81 |
-
**automatically runs** all cells that reference that variable.
|
| 82 |
-
|
| 83 |
-
Reactivity keeps your program state and outputs in sync with your code,
|
| 84 |
-
making for a dynamic programming environment that prevents bugs before they
|
| 85 |
-
happen.
|
| 86 |
-
"""
|
| 87 |
-
)
|
| 88 |
-
return
|
| 89 |
|
|
|
|
| 90 |
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
)
|
| 107 |
-
if changed
|
| 108 |
-
else mo.md(
|
| 109 |
-
"""
|
| 110 |
-
**π See it in action.** In the next cell, change the value of the
|
| 111 |
-
variable `changed` to `True`, then click the run button.
|
| 112 |
-
"""
|
| 113 |
-
)
|
| 114 |
)
|
| 115 |
-
return
|
| 116 |
|
| 117 |
|
| 118 |
@app.cell
|
| 119 |
-
def
|
| 120 |
-
|
| 121 |
-
return (changed,)
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
@app.cell(hide_code=True)
|
| 125 |
-
def __(mo):
|
| 126 |
-
mo.accordion(
|
| 127 |
-
{
|
| 128 |
-
"Tip: execution order": (
|
| 129 |
-
"""
|
| 130 |
-
The order of cells on the page has no bearing on
|
| 131 |
-
the order in which cells are executed: marimo knows that a cell
|
| 132 |
-
reading a variable must run after the cell that defines it. This
|
| 133 |
-
frees you to organize your code in the way that makes the most
|
| 134 |
-
sense for you.
|
| 135 |
-
"""
|
| 136 |
-
)
|
| 137 |
-
}
|
| 138 |
-
)
|
| 139 |
return
|
| 140 |
|
| 141 |
|
| 142 |
-
@app.cell
|
| 143 |
-
def
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 152 |
|
| 153 |
|
| 154 |
-
@app.cell
|
| 155 |
-
def
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
|
| 162 |
-
|
| 163 |
-
)
|
| 164 |
-
|
| 165 |
-
|
| 166 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 167 |
|
| 168 |
|
| 169 |
-
@app.cell
|
| 170 |
-
def
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
"Tip: private variables": (
|
| 174 |
-
"""
|
| 175 |
-
Variables prefixed with an underscore are "private" to a cell, so
|
| 176 |
-
they can be defined by multiple cells.
|
| 177 |
-
"""
|
| 178 |
-
)
|
| 179 |
-
}
|
| 180 |
-
)
|
| 181 |
-
return
|
| 182 |
|
| 183 |
|
| 184 |
-
@app.cell
|
| 185 |
-
def
|
| 186 |
-
|
| 187 |
-
|
| 188 |
-
|
| 189 |
-
|
| 190 |
-
|
| 191 |
-
|
| 192 |
-
|
| 193 |
-
|
| 194 |
-
|
| 195 |
-
|
| 196 |
-
|
| 197 |
-
|
| 198 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 199 |
return
|
| 200 |
|
| 201 |
|
| 202 |
@app.cell
|
| 203 |
-
def
|
| 204 |
-
|
| 205 |
return
|
| 206 |
|
| 207 |
|
| 208 |
@app.cell
|
| 209 |
-
def
|
| 210 |
-
|
| 211 |
-
|
|
|
|
| 212 |
|
|
|
|
|
|
|
| 213 |
|
| 214 |
-
|
| 215 |
-
|
| 216 |
-
|
| 217 |
-
return (
|
| 218 |
|
| 219 |
|
| 220 |
@app.cell
|
| 221 |
-
def
|
| 222 |
-
|
| 223 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 224 |
|
| 225 |
|
| 226 |
@app.cell
|
| 227 |
-
def
|
| 228 |
-
mo.md("# " + icon.value * repetitions.value)
|
| 229 |
return
|
| 230 |
|
| 231 |
|
| 232 |
-
@app.cell
|
| 233 |
-
def
|
| 234 |
-
mo.
|
| 235 |
-
|
| 236 |
-
|
| 237 |
-
|
| 238 |
-
|
| 239 |
-
|
| 240 |
-
|
| 241 |
-
|
| 242 |
-
|
| 243 |
-
|
| 244 |
-
|
| 245 |
-
|
| 246 |
-
|
| 247 |
-
|
| 248 |
-
|
| 249 |
-
|
| 250 |
-
|
| 251 |
-
|
| 252 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 253 |
|
| 254 |
|
| 255 |
-
@app.cell
|
| 256 |
-
def
|
| 257 |
-
|
| 258 |
-
"""
|
| 259 |
-
## 4. Running notebooks as apps
|
| 260 |
-
|
| 261 |
-
marimo notebooks can double as apps. Click the app window icon in the
|
| 262 |
-
bottom-right to see this notebook in "app view."
|
| 263 |
-
|
| 264 |
-
Serve a notebook as an app with `marimo run` at the command-line.
|
| 265 |
-
Of course, you can use marimo just to level-up your
|
| 266 |
-
notebooking, without ever making apps.
|
| 267 |
-
"""
|
| 268 |
-
)
|
| 269 |
return
|
| 270 |
|
| 271 |
|
| 272 |
-
@app.cell
|
| 273 |
-
def
|
| 274 |
-
|
| 275 |
-
|
| 276 |
-
|
| 277 |
-
|
| 278 |
-
|
| 279 |
-
|
| 280 |
-
|
| 281 |
-
|
| 282 |
-
|
| 283 |
-
|
| 284 |
-
|
| 285 |
-
|
| 286 |
-
|
| 287 |
-
|
| 288 |
-
|
| 289 |
-
|
| 290 |
-
|
| 291 |
-
|
| 292 |
-
|
| 293 |
-
|
| 294 |
-
|
| 295 |
-
with
|
| 296 |
-
|
| 297 |
-
|
| 298 |
-
|
| 299 |
-
|
| 300 |
-
|
| 301 |
-
|
| 302 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 303 |
|
| 304 |
-
**Tutorials.** marimo comes packaged with tutorials:
|
| 305 |
|
| 306 |
-
|
| 307 |
-
|
| 308 |
-
|
| 309 |
-
|
| 310 |
-
|
| 311 |
-
- `sql`: how to use SQL
|
| 312 |
-
- `layout`: layout elements in marimo
|
| 313 |
-
- `fileformat`: how marimo's file format works
|
| 314 |
-
- `markdown-format`: for using `.md` files in marimo
|
| 315 |
-
- `for-jupyter-users`: if you are coming from Jupyter
|
| 316 |
|
| 317 |
-
Start a tutorial with `marimo tutorial`; for example,
|
| 318 |
|
| 319 |
-
|
| 320 |
-
marimo tutorial dataflow
|
| 321 |
-
```
|
| 322 |
|
| 323 |
-
|
| 324 |
-
[our GitHub repo](https://www.github.com/marimo-team/marimo/tree/main/examples).
|
| 325 |
-
"""
|
| 326 |
-
)
|
| 327 |
-
return
|
| 328 |
|
|
|
|
| 329 |
|
| 330 |
-
|
| 331 |
-
|
| 332 |
-
|
| 333 |
-
|
| 334 |
-
|
|
|
|
|
|
|
| 335 |
|
| 336 |
-
|
| 337 |
-
"""
|
| 338 |
-
)
|
| 339 |
return
|
| 340 |
|
| 341 |
|
| 342 |
@app.cell
|
| 343 |
-
def
|
| 344 |
-
mo.
|
| 345 |
-
return
|
| 346 |
-
|
| 347 |
-
|
| 348 |
-
@app.cell(hide_code=True)
|
| 349 |
-
def __(mo):
|
| 350 |
-
mo.md("""## Finally, a fun fact""")
|
| 351 |
return
|
| 352 |
|
| 353 |
|
| 354 |
-
@app.cell(hide_code=True)
|
| 355 |
-
def __(mo):
|
| 356 |
-
mo.md(
|
| 357 |
-
"""
|
| 358 |
-
The name "marimo" is a reference to a type of algae that, under
|
| 359 |
-
the right conditions, clumps together to form a small sphere
|
| 360 |
-
called a "marimo moss ball". Made of just strands of algae, these
|
| 361 |
-
beloved assemblages are greater than the sum of their parts.
|
| 362 |
-
"""
|
| 363 |
-
)
|
| 364 |
-
return
|
| 365 |
-
|
| 366 |
-
|
| 367 |
-
@app.cell(hide_code=True)
|
| 368 |
-
def __():
|
| 369 |
-
tips = {
|
| 370 |
-
"Saving": (
|
| 371 |
-
"""
|
| 372 |
-
**Saving**
|
| 373 |
-
|
| 374 |
-
- _Name_ your app using the box at the top of the screen, or
|
| 375 |
-
with `Ctrl/Cmd+s`. You can also create a named app at the
|
| 376 |
-
command line, e.g., `marimo edit app_name.py`.
|
| 377 |
-
|
| 378 |
-
- _Save_ by clicking the save icon on the bottom right, or by
|
| 379 |
-
inputting `Ctrl/Cmd+s`. By default marimo is configured
|
| 380 |
-
to autosave.
|
| 381 |
-
"""
|
| 382 |
-
),
|
| 383 |
-
"Running": (
|
| 384 |
-
"""
|
| 385 |
-
1. _Run a cell_ by clicking the play ( β· ) button on the top
|
| 386 |
-
right of a cell, or by inputting `Ctrl/Cmd+Enter`.
|
| 387 |
-
|
| 388 |
-
2. _Run a stale cell_ by clicking the yellow run button on the
|
| 389 |
-
right of the cell, or by inputting `Ctrl/Cmd+Enter`. A cell is
|
| 390 |
-
stale when its code has been modified but not run.
|
| 391 |
-
|
| 392 |
-
3. _Run all stale cells_ by clicking the play ( β· ) button on
|
| 393 |
-
the bottom right of the screen, or input `Ctrl/Cmd+Shift+r`.
|
| 394 |
-
"""
|
| 395 |
-
),
|
| 396 |
-
"Console Output": (
|
| 397 |
-
"""
|
| 398 |
-
Console output (e.g., `print()` statements) is shown below a
|
| 399 |
-
cell.
|
| 400 |
-
"""
|
| 401 |
-
),
|
| 402 |
-
"Creating, Moving, and Deleting Cells": (
|
| 403 |
-
"""
|
| 404 |
-
1. _Create_ a new cell above or below a given one by clicking
|
| 405 |
-
the plus button to the left of the cell, which appears on
|
| 406 |
-
mouse hover.
|
| 407 |
-
|
| 408 |
-
2. _Move_ a cell up or down by dragging on the handle to the
|
| 409 |
-
right of the cell, which appears on mouse hover.
|
| 410 |
-
|
| 411 |
-
3. _Delete_ a cell by clicking the trash bin icon. Bring it
|
| 412 |
-
back by clicking the undo button on the bottom right of the
|
| 413 |
-
screen, or with `Ctrl/Cmd+Shift+z`.
|
| 414 |
-
"""
|
| 415 |
-
),
|
| 416 |
-
"Disabling Automatic Execution": (
|
| 417 |
-
"""
|
| 418 |
-
Via the notebook settings (gear icon) or footer panel, you
|
| 419 |
-
can disable automatic execution. This is helpful when
|
| 420 |
-
working with expensive notebooks or notebooks that have
|
| 421 |
-
side-effects like database transactions.
|
| 422 |
-
"""
|
| 423 |
-
),
|
| 424 |
-
"Disabling Cells": (
|
| 425 |
-
"""
|
| 426 |
-
You can disable a cell via the cell context menu.
|
| 427 |
-
marimo will never run a disabled cell or any cells that depend on it.
|
| 428 |
-
This can help prevent accidental execution of expensive computations
|
| 429 |
-
when editing a notebook.
|
| 430 |
-
"""
|
| 431 |
-
),
|
| 432 |
-
"Code Folding": (
|
| 433 |
-
"""
|
| 434 |
-
You can collapse or fold the code in a cell by clicking the arrow
|
| 435 |
-
icons in the line number column to the left, or by using keyboard
|
| 436 |
-
shortcuts.
|
| 437 |
-
|
| 438 |
-
Use the command palette (`Ctrl/Cmd+k`) or a keyboard shortcut to
|
| 439 |
-
quickly fold or unfold all cells.
|
| 440 |
-
"""
|
| 441 |
-
),
|
| 442 |
-
"Code Formatting": (
|
| 443 |
-
"""
|
| 444 |
-
If you have [ruff](https://github.com/astral-sh/ruff) installed,
|
| 445 |
-
you can format a cell with the keyboard shortcut `Ctrl/Cmd+b`.
|
| 446 |
-
"""
|
| 447 |
-
),
|
| 448 |
-
"Command Palette": (
|
| 449 |
-
"""
|
| 450 |
-
Use `Ctrl/Cmd+k` to open the command palette.
|
| 451 |
-
"""
|
| 452 |
-
),
|
| 453 |
-
"Keyboard Shortcuts": (
|
| 454 |
-
"""
|
| 455 |
-
Open the notebook menu (top-right) or input `Ctrl/Cmd+Shift+h` to
|
| 456 |
-
view a list of all keyboard shortcuts.
|
| 457 |
-
"""
|
| 458 |
-
),
|
| 459 |
-
"Configuration": (
|
| 460 |
-
"""
|
| 461 |
-
Configure the editor by clicking the gears icon near the top-right
|
| 462 |
-
of the screen.
|
| 463 |
-
"""
|
| 464 |
-
),
|
| 465 |
-
}
|
| 466 |
-
return (tips,)
|
| 467 |
-
|
| 468 |
-
|
| 469 |
if __name__ == "__main__":
|
| 470 |
app.run()
|
|
|
|
| 1 |
import marimo
|
| 2 |
|
| 3 |
+
__generated_with = "0.17.0"
|
| 4 |
app = marimo.App()
|
| 5 |
|
| 6 |
|
| 7 |
@app.cell
|
| 8 |
+
def _():
|
| 9 |
import marimo as mo
|
| 10 |
|
| 11 |
+
mo.md('# <center>Cicero Jobs</center>')
|
| 12 |
return (mo,)
|
| 13 |
|
| 14 |
|
| 15 |
@app.cell
|
| 16 |
+
def _():
|
| 17 |
+
import glob
|
| 18 |
+
import os
|
| 19 |
+
from pydantic_ai import Agent, BinaryContent, DocumentUrl
|
| 20 |
+
from pydantic import BaseModel, Field
|
| 21 |
+
return Agent, BaseModel, BinaryContent, DocumentUrl, Field
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
@app.function
|
| 25 |
+
def create_job_application_prompt(resume: str, cover_letter: str, further_instructions: str | None = None) -> str:
|
| 26 |
+
fi = further_instructions or ''
|
| 27 |
+
return f"""
|
| 28 |
+
<JobApplicationGenerationRequest>
|
| 29 |
+
<Persona>
|
| 30 |
+
You are an expert human resources professional and a resume and cover letter drafting specialist, with a deep focus on the in-house legal market. Your task is to generate a complete set of application materials based on the provided resume and job description.
|
| 31 |
+
</Persona>
|
| 32 |
+
|
| 33 |
+
<InputData>
|
| 34 |
+
<Resume>
|
| 35 |
+
{resume}
|
| 36 |
+
</Resume>
|
| 37 |
+
<JobOpening>
|
| 38 |
+
{cover_letter}
|
| 39 |
+
</JobOpening>
|
| 40 |
+
<FurtherInstructions>
|
| 41 |
+
{fi}
|
| 42 |
+
</FurtherInstructions>
|
| 43 |
+
</InputData>
|
| 44 |
+
|
| 45 |
+
<OutputSpecification>
|
| 46 |
+
Your final output must be a structured object with four distinct fields: `resume`, `cover_letter`, `letter_to_recruiter`, and `tips`. Follow the detailed instructions for each field below.
|
| 47 |
+
</OutputSpecification>
|
| 48 |
+
|
| 49 |
+
<CoreProcessingInstructions>
|
| 50 |
+
1. **Analyze and Synthesize:** Meticulously analyze the provided `<Resume>` and the `<JobOpening>`. Your primary goal is to tailor the candidate's materials to perfectly align with the requirements and keywords of the job description.
|
| 51 |
+
2. **Adhere to Writing Guidelines:** In all generated content, strictly apply the principles outlined in the `<WritingGuidelines>` section. This includes using appropriate tense, structuring bullet points effectively (PAR, STAR), incorporating action verbs, and maintaining a professional brand.
|
| 52 |
+
3. **Fact-Based Generation:** Do not invent skills or experiences. All generated content must be directly supported by the information in the provided `<Resume>`. If a skill is treated superficially, you may suggest enhancements or ask clarifying questions in the `tips` section.
|
| 53 |
+
4. **Keyword Integration:** Identify core competencies, role-specific skills, and industry-relevant keywords from the job posting. Systematically integrate these exact keywords (avoiding synonyms unless instructed) into all generated documents to optimize for Applicant Tracking Systems (ATS) and recruiter review.
|
| 54 |
+
</CoreProcessingInstructions>
|
| 55 |
+
|
| 56 |
+
<GenerationTasks>
|
| 57 |
+
<Task id="resume">
|
| 58 |
+
<Description>
|
| 59 |
+
Generate a complete, richly formatted Markdown resume, fully tailored for the specific job opening.
|
| 60 |
+
</Description>
|
| 61 |
+
<Instructions>
|
| 62 |
+
- Return the **entire, updated resume**, not just the changed parts. The final output should be a single, complete Markdown document.
|
| 63 |
+
- **Summary Section:** Draft a concise (30-90 words) and impactful summary. This is your elevator pitch.
|
| 64 |
+
- If not extremely excessive, match the professional title based on the job opening:
|
| 65 |
+
- DO: "Commercial Counsel" -> "Commercial and Corporate Attorney"
|
| 66 |
+
- DO: "Corporate Counsel" -> "Corporate Attorney"
|
| 67 |
+
- DO NOT: Attorney --> General Counsel
|
| 68 |
+
- DO NOT: intern --> manager
|
| 69 |
+
- Incorporate the candidate's years of experience, but adjust as follows:
|
| 70 |
+
- If the required experience is incompatible with the candidate's seniority, use phrases like "seasoned" or "experienced" instead of a number.
|
| 71 |
+
- For roles requiring 3-4 years, do not overemphasize seniority.
|
| 72 |
+
- For roles requiring 5+ years, explicitly state the number of years of experience.
|
| 73 |
+
- **Skills Section:**
|
| 74 |
+
- Identify the most important keywords from the job posting. Prioritize keywords that appear multiple times, are standard for the role, or showcase unique relevant skills.
|
| 75 |
+
- Include industry-standard skills that may not be in the job description but are valuable for the role.
|
| 76 |
+
- Format the skills as a comma-separated list: `Skilla, skillb, skillc`.
|
| 77 |
+
- **Experience Section:**
|
| 78 |
+
- Adapt the language in the resume to use the exact keywords from the job posting, replacing any synonyms.
|
| 79 |
+
- Ensure bullet points are unique, highlight quantifiable achievements, and provide context (e.g., team size, project value).
|
| 80 |
+
- Structure bullet points using the C.A.R. technique, front-loading the result where possible.
|
| 81 |
+
- **Formatting:** Ensure the length and structure of the updated resume are similar to the original provided document.
|
| 82 |
+
- MANDATORY: Your answer must be perfect Markdown syntax (will be rendered by mistune)
|
| 83 |
+
- MANDATORY: headers such as the candidate name, section titles (e.g., Summary, Experience, Skills), and contact information must be wrapped in <p style=\"text-align:center;\"> ... </p> tags.
|
| 84 |
+
- MANDATORY: Must end wishing the candidate good luck and explaining finding a job is a numbers game. Motivate them!
|
| 85 |
+
|
| 86 |
+
</Instructions>
|
| 87 |
+
</Task>
|
| 88 |
+
|
| 89 |
+
<Task id="cover_letter">
|
| 90 |
+
<Description>
|
| 91 |
+
Generate a compelling, personalized, and richly formatted Markdown cover letter.
|
| 92 |
+
</Description>
|
| 93 |
+
<Instructions>
|
| 94 |
+
- The cover letter must be under 400 words and structured in 3-4 paragraphs.
|
| 95 |
+
- Address it to the hiring manager. If a name is available in the job posting, use it.
|
| 96 |
+
- The content should be approximately 60% based on the job opening and 40% on the resume.
|
| 97 |
+
- Explicitly mention experiences from the resume that are directly relevant to the role. Highlight leadership experience (e.g., managing paralegals).
|
| 98 |
+
- When mentioning past employers, do not append suffixes like "LLP" or "Inc."
|
| 99 |
+
- Perform a quick web search for information about the company to add a sentence or two showing genuine interest and research.
|
| 100 |
+
- Ensure all statements are directly supported by the resume.
|
| 101 |
+
</Instructions>
|
| 102 |
+
</Task>
|
| 103 |
+
|
| 104 |
+
<Task id="letter_to_recruiter">
|
| 105 |
+
<Description>
|
| 106 |
+
Generate a concise, professional, and richly formatted Markdown message for LinkedIn or email outreach.
|
| 107 |
+
</Description>
|
| 108 |
+
<Instructions>
|
| 109 |
+
- The message should be 5-7 sentences long.
|
| 110 |
+
- Draft a clear and professional subject line (e.g., "Regarding the Corporate Counsel Position").
|
| 111 |
+
- Address the hiring manager by name if available.
|
| 112 |
+
- Briefly introduce the candidate, state the position being applied for, and highlight 1-2 key qualifications that make them a perfect fit.
|
| 113 |
+
- End with a clear call to action, such as expressing eagerness to discuss the role further.
|
| 114 |
+
- Return the message without any additional markup or bullet points.
|
| 115 |
+
</Instructions>
|
| 116 |
+
</Task>
|
| 117 |
+
|
| 118 |
+
<Task id="tips">
|
| 119 |
+
<Description>
|
| 120 |
+
Generate 5-7 actionable tips specific to this application, formatted as a bulleted list in Markdown.
|
| 121 |
+
</Description>
|
| 122 |
+
<Instructions>
|
| 123 |
+
- Analyze the resume against the job description to identify strengths and potential gaps.
|
| 124 |
+
- Provide concrete advice. For example:
|
| 125 |
+
- "During the interview, be prepared to elaborate on your experience with [Specific Skill from Job Posting] by using the project at [Previous Company] as a prime example."
|
| 126 |
+
- "The job emphasizes [Company Value or Mission]. Consider mentioning how your work in [Relevant Experience] aligns with this value."
|
| 127 |
+
- "Your resume mentions [Superficial Skill]. I recommend adding a bullet point with a quantifiable result to strengthen this, such as..."
|
| 128 |
+
- "Research the company's recent acquisition of [Company X] and be prepared to discuss its potential legal integration challenges."
|
| 129 |
+
- The tips should be formatted as a simple Markdown bulleted list.
|
| 130 |
+
</Instructions>
|
| 131 |
+
</Task>
|
| 132 |
+
</GenerationTasks>
|
| 133 |
+
|
| 134 |
+
<WritingGuidelines>
|
| 135 |
+
<Section name="Summary">
|
| 136 |
+
Not always required: If you've included a cover letter, your summary might be redundant. Valuable for quickly grabbing attention and highlighting your most relevant qualifications, as well as filling the gap if you're not submitting a cover letter.
|
| 137 |
+
Focus on:
|
| 138 |
+
- Who you are and what you do: Clearly state your professional identity and area of expertise.
|
| 139 |
+
- Your achievements: Highlight measurable results, awards, problems solved, and times you exceeded expectations.
|
| 140 |
+
</Section>
|
| 141 |
+
<Section name="Work Experience">
|
| 142 |
+
This is the section of your resume in which you detail your career history.
|
| 143 |
+
- Use bullets for skim value.
|
| 144 |
+
- Use simple present tense for current roles (e.g., manages) and simple past tense for past roles (e.g., managed).
|
| 145 |
+
- Provide context for each position: company type, industry, size, and your role's impact on the bottom line.
|
| 146 |
+
- Focus on your main functions and their relevance to the target job.
|
| 147 |
+
- Emphasize quantifiable impact: landing clients, saving costs, automating tasks, hitting targets.
|
| 148 |
+
</Section>
|
| 149 |
+
<Section name="Bullet Point Structuring">
|
| 150 |
+
- PAR (Problem-Action-Result): Ideal for showing accomplishments and impact. Example: "Reduced customer support response time by 20% by implementing a new ticketing system."
|
| 151 |
+
- STAR (Situation-Task-Action-Result): Provides a more detailed narrative for demonstrating specific competencies. Example: "Managed inventory levels in a fast-paced retail setting; achieved a 15% reduction in inventory costs."
|
| 152 |
+
- Result-First: Leads with the outcome to grab attention. Example: "Increased sales revenue by 18% through targeted marketing campaigns."
|
| 153 |
+
- Action Verb + Skill + Result: Concise and effective for technical roles. Example: "Developed a user-friendly website using HTML, CSS, and JavaScript, resulting in a 30% increase in website traffic."
|
| 154 |
+
- Prioritize your most relevant bullet points for each specific job application.
|
| 155 |
+
</Section>
|
| 156 |
+
<Section name="Technical Skills">
|
| 157 |
+
- Showcase specific knowledge in tools, software, and processes.
|
| 158 |
+
- Hard Skills: Programming languages (Java, Python), software (Adobe Creative Suite), cloud platforms (AWS, Azure).
|
| 159 |
+
- Soft Technical Skills: Troubleshooting, data analysis, project management, technical writing.
|
| 160 |
+
- Use the PAR method to demonstrate technical skills in context. Example: "Problem: Website traffic was declining. Action: Implemented optimization techniques using HTML, CSS, JavaScript. Result: Increased traffic by 25%."
|
| 161 |
+
</Section>
|
| 162 |
+
<Section name="Soft Skills">
|
| 163 |
+
- Avoid overused terms like "Problem-solving" or "Driven".
|
| 164 |
+
- Instead of listing soft skills, demonstrate them through your achievement-oriented bullet points. Example: Instead of "excellent communication skills," write "Presented quarterly performance updates to internal stakeholders."
|
| 165 |
+
</Section>
|
| 166 |
+
<Section name="Professional Brand and Summary Details">
|
| 167 |
+
- Your brand is the unique combination of your skills, experience, and personality.
|
| 168 |
+
- Your summary is your sales pitch, answering "Who are you and why should I hire you?"
|
| 169 |
+
- It should be concise (60-120 words), factual, data-driven, and placed at the top of your resume.
|
| 170 |
+
- Use a combination of present tense ("who you are") and past tense ("what you've achieved"). Example: "Project manager (PMP) with 11 years of experience... Successfully completed three plant projects in 2022 valued at $15MM."
|
| 171 |
+
</Section>
|
| 172 |
+
<Section name="Experience Section Details">
|
| 173 |
+
- List your most relevant responsibilities and bullets first.
|
| 174 |
+
- Avoid generic statements; provide specific context and quantifiable metrics.
|
| 175 |
+
- Use the C.A.R (Challenge, Action, Result) technique. Where possible, front-load the bullet with the result.
|
| 176 |
+
- Use a short introductory paragraph for each role to provide a high-level overview.
|
| 177 |
+
- Utilize strong action verbs (e.g., Achieved, Coordinated, Implemented, Streamlined, Managed, Maximized).
|
| 178 |
+
</Section>
|
| 179 |
+
</WritingGuidelines>
|
| 180 |
+
</JobApplicationGenerationRequest>
|
| 181 |
+
"""
|
| 182 |
|
| 183 |
|
| 184 |
+
@app.cell(hide_code=True)
|
| 185 |
+
def _(BaseModel, Field):
|
| 186 |
+
class ApplicationMaterials(BaseModel):
|
| 187 |
+
"""Structured output for resume and cover letter generation."""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 188 |
|
| 189 |
+
resume: str = Field(..., description='Markdown Richly Formatted resume highlighting relevant experience for the job')
|
| 190 |
+
cover_letter: str = Field(..., description='Markdown Richly Formatted and Compelling, personalized cover letter (3-4 paragraphs)')
|
| 191 |
+
letter_to_recruiter: str = Field(..., description='Markdown Richly Formatted and Concise professional message for LinkedIn/email outreach (5-7 sentences)')
|
| 192 |
+
tips: str = Field(..., description='Markdown Richly Formatted and 5-7 actionable tips specific to this application, formatted as bullet points')
|
| 193 |
+
return (ApplicationMaterials,)
|
| 194 |
|
| 195 |
|
| 196 |
@app.cell(hide_code=True)
|
| 197 |
+
def _(mo):
|
| 198 |
+
resume_button = mo.ui.file(kind='area', filetypes=['.pdf', '.txt', '.doc', '.docx'], label='Or drop your pdf/doc/docx/txt file rΓ©sumΓ© here!')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 199 |
|
| 200 |
+
job_posting_area = mo.ui.text_area(placeholder='π Drop your job posting here!', full_width=True, rows=27)
|
| 201 |
|
| 202 |
+
resume_area = mo.ui.text_area(placeholder='π Provide the full content of your rΓ©sumΓ©. Uploading file in pdf/docx/doc/txt is preferred.', full_width=True, rows=20)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 203 |
|
| 204 |
|
| 205 |
+
job_url = mo.ui.text(placeholder='https://example.com/job-posting', label='Alternatively, provide the URL of the job posting', full_width=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 206 |
|
| 207 |
+
continue_button = mo.ui.button(value=None, kind='success', tooltip='Click submit to continue.', label='Submit All (or press Ctrl/Cmd Enter)', keyboard_shortcut='Ctrl+Enter', full_width=True)
|
| 208 |
|
| 209 |
+
continue_render = mo.vstack(
|
| 210 |
+
[
|
| 211 |
+
mo.md("# Let's start with your materials!"),
|
| 212 |
+
mo.vstack(
|
| 213 |
+
[
|
| 214 |
+
mo.md('## '),
|
| 215 |
+
mo.hstack(
|
| 216 |
+
[mo.vstack([mo.md('### Resume:'), resume_button, resume_area], align='center'), mo.vstack([mo.md('### Job Posting:'), job_posting_area, job_url], align='center')], align='center'
|
| 217 |
+
),
|
| 218 |
+
continue_button,
|
| 219 |
+
],
|
| 220 |
+
align='start',
|
| 221 |
+
),
|
| 222 |
+
],
|
| 223 |
+
align='center',
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 224 |
)
|
| 225 |
+
return continue_render, job_posting_area, job_url, resume_button
|
| 226 |
|
| 227 |
|
| 228 |
@app.cell
|
| 229 |
+
def _(continue_render):
|
| 230 |
+
continue_render
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 231 |
return
|
| 232 |
|
| 233 |
|
| 234 |
+
@app.cell
|
| 235 |
+
def _(job_posting_area, job_url, mo):
|
| 236 |
+
if job_posting_area.value != '':
|
| 237 |
+
buceta = 'Vamo'
|
| 238 |
+
job_posting = job_posting_area.value
|
| 239 |
+
elif job_posting_area.value == '' and job_url.value == '':
|
| 240 |
+
buceta = None
|
| 241 |
+
job_posting = None
|
| 242 |
+
elif job_posting_area.value == '' and job_url.value != '':
|
| 243 |
+
buceta = 'Vamo'
|
| 244 |
+
import httpx
|
| 245 |
+
|
| 246 |
+
url = f'https://r.jina.ai/{job_url.value}'
|
| 247 |
+
headers = {f'Authorization': 'Bearer '}
|
| 248 |
+
response = httpx.get(url, headers=headers)
|
| 249 |
+
job_posting = response.text
|
| 250 |
+
|
| 251 |
+
mo.stop(buceta is None, mo.callout(mo.md('β οΈ **Please provide your materials before continuing!**'), kind='danger'))
|
| 252 |
+
return (job_posting,)
|
| 253 |
|
| 254 |
|
| 255 |
+
@app.cell
|
| 256 |
+
def _(resume_button):
|
| 257 |
+
# Helper function to determine media type from filename
|
| 258 |
+
import io
|
| 259 |
+
|
| 260 |
+
|
| 261 |
+
try:
|
| 262 |
+
if resume_button.contents() is not '':
|
| 263 |
+
print(f'we got button contents: {resume_button.contents()}')
|
| 264 |
+
resume_contents = resume_button.contents()
|
| 265 |
+
try:
|
| 266 |
+
from markitdown import MarkItDown
|
| 267 |
+
|
| 268 |
+
md = MarkItDown(enable_plugins=True)
|
| 269 |
+
forcing_bytes = io.BytesIO(resume_contents)
|
| 270 |
+
docx_converted = md.convert(forcing_bytes)
|
| 271 |
+
resume_complete = str(docx_converted.text_content)
|
| 272 |
+
print('Converted to markdown')
|
| 273 |
+
except Exception as e:
|
| 274 |
+
print(f'an exception occurrred {e}')
|
| 275 |
+
if isinstance(resume_contents, (bytes, bytearray)):
|
| 276 |
+
resume_complete = io.BytesIO(resume_contents)
|
| 277 |
+
else:
|
| 278 |
+
resume_complete = resume_contents
|
| 279 |
+
else:
|
| 280 |
+
resume_contents = ''
|
| 281 |
+
except Exception:
|
| 282 |
+
resume_contents = ''
|
| 283 |
+
return io, resume_complete
|
| 284 |
|
| 285 |
|
| 286 |
+
@app.cell
|
| 287 |
+
def _(job_posting, resume_complete):
|
| 288 |
+
final_prompt = create_job_application_prompt(resume_complete, job_posting)
|
| 289 |
+
return (final_prompt,)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 290 |
|
| 291 |
|
| 292 |
+
@app.cell
|
| 293 |
+
def _(BinaryContent, DocumentUrl, job_posting_area, job_url):
|
| 294 |
+
job_posting_complete = ''
|
| 295 |
+
job_posting_mime = 'text/plain'
|
| 296 |
+
is_url = False
|
| 297 |
+
|
| 298 |
+
job_area_value = job_posting_area.value if hasattr(job_posting_area, 'value') else None
|
| 299 |
+
job_url_value = job_url.value if hasattr(job_url, 'value') else None
|
| 300 |
+
|
| 301 |
+
if job_area_value:
|
| 302 |
+
job_posting_complete = job_area_value
|
| 303 |
+
job_posting_mime = 'text/plain'
|
| 304 |
+
is_url = False
|
| 305 |
+
elif job_url_value:
|
| 306 |
+
job_posting_complete = job_url_value.strip()
|
| 307 |
+
# Basic URL detection
|
| 308 |
+
if job_posting_complete.lower().startswith(('http://', 'https://')):
|
| 309 |
+
is_url = True
|
| 310 |
+
job_posting_mime = None
|
| 311 |
+
else:
|
| 312 |
+
# If user provided something in the URL field that's not an HTTP URL, treat it as plain text
|
| 313 |
+
is_url = False
|
| 314 |
+
job_posting_mime = 'text/plain'
|
| 315 |
+
else:
|
| 316 |
+
job_posting_complete = ''
|
| 317 |
+
job_posting_mime = 'text/plain'
|
| 318 |
+
is_url = False
|
| 319 |
+
|
| 320 |
+
# Build inputs for the agent
|
| 321 |
+
if is_url:
|
| 322 |
+
job_posting_input = DocumentUrl(job_posting_complete)
|
| 323 |
+
else:
|
| 324 |
+
job_posting_input = BinaryContent(data=job_posting_complete, media_type=job_posting_mime or 'text/plain')
|
| 325 |
return
|
| 326 |
|
| 327 |
|
| 328 |
@app.cell
|
| 329 |
+
def _(resume_complete):
|
| 330 |
+
print(resume_complete)
|
| 331 |
return
|
| 332 |
|
| 333 |
|
| 334 |
@app.cell
|
| 335 |
+
def _(Agent, ApplicationMaterials, final_prompt):
|
| 336 |
+
import asyncio
|
| 337 |
+
import uvloop
|
| 338 |
+
import nest_asyncio
|
| 339 |
|
| 340 |
+
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
|
| 341 |
+
nest_asyncio.apply()
|
| 342 |
|
| 343 |
+
career_agent = Agent('gemini-2.5-flash', output_type=ApplicationMaterials)
|
| 344 |
+
if final_prompt is not None:
|
| 345 |
+
result = asyncio.run(career_agent.run(final_prompt))
|
| 346 |
+
return (result,)
|
| 347 |
|
| 348 |
|
| 349 |
@app.cell
|
| 350 |
+
def _(result):
|
| 351 |
+
import json
|
| 352 |
+
|
| 353 |
+
_output = getattr(result, 'output', None)
|
| 354 |
+
|
| 355 |
+
_parsed = None
|
| 356 |
+
if isinstance(_output, str):
|
| 357 |
+
try:
|
| 358 |
+
_parsed = json.loads(_output)
|
| 359 |
+
except Exception:
|
| 360 |
+
_parsed = None
|
| 361 |
+
elif isinstance(_output, dict):
|
| 362 |
+
_parsed = _output
|
| 363 |
+
else:
|
| 364 |
+
try:
|
| 365 |
+
_parsed = {}
|
| 366 |
+
for k in dir(_output):
|
| 367 |
+
if k.startswith('_') or k.startswith('model_'):
|
| 368 |
+
continue
|
| 369 |
+
try:
|
| 370 |
+
v = getattr(_output, k)
|
| 371 |
+
except Exception:
|
| 372 |
+
continue
|
| 373 |
+
if callable(v):
|
| 374 |
+
continue
|
| 375 |
+
_parsed[k] = v
|
| 376 |
+
except Exception:
|
| 377 |
+
_parsed = None
|
| 378 |
+
|
| 379 |
+
|
| 380 |
+
def _get_field(*keys, default=None):
|
| 381 |
+
if _parsed and isinstance(_parsed, dict):
|
| 382 |
+
for k in keys:
|
| 383 |
+
if k in _parsed:
|
| 384 |
+
return _parsed[k]
|
| 385 |
+
for k in keys:
|
| 386 |
+
if hasattr(_output, k):
|
| 387 |
+
try:
|
| 388 |
+
return getattr(_output, k)
|
| 389 |
+
except Exception:
|
| 390 |
+
continue
|
| 391 |
+
return default
|
| 392 |
+
|
| 393 |
+
|
| 394 |
+
processed_data = {
|
| 395 |
+
'resume': _get_field('resume', 'resume_text', default=None),
|
| 396 |
+
'cover_letter': _get_field('cover_letter', 'coverletter', 'cover_letter_text', default=None),
|
| 397 |
+
'recruiter_message': _get_field('letter_to_recruiter', 'recruiter_message', 'message_to_recruiter', default=None),
|
| 398 |
+
'tips': _get_field('tips', 'advice', 'suggestions', default=None),
|
| 399 |
+
}
|
| 400 |
+
return (processed_data,)
|
| 401 |
|
| 402 |
|
| 403 |
@app.cell
|
| 404 |
+
def _():
|
|
|
|
| 405 |
return
|
| 406 |
|
| 407 |
|
| 408 |
+
@app.cell
|
| 409 |
+
def _(mo, processed_data):
|
| 410 |
+
preview_section = mo.vstack([
|
| 411 |
+
mo.md('## Preview Your Documents'),
|
| 412 |
+
mo.md('---'),
|
| 413 |
+
mo.md('### Here is your rΓ©sumΓ©, how it looks like?'),
|
| 414 |
+
mo.md('---'),
|
| 415 |
+
mo.md(processed_data.get('resume')),
|
| 416 |
+
mo.md('---'),
|
| 417 |
+
mo.md('---'),
|
| 418 |
+
mo.md('### πΌ Here is your Cover Letter'),
|
| 419 |
+
mo.md('---'),
|
| 420 |
+
mo.md(processed_data.get('cover_letter')),
|
| 421 |
+
mo.md('---'),
|
| 422 |
+
mo.md('---'),
|
| 423 |
+
mo.md('### π§ Here is your message to reach out to the recruiter'),
|
| 424 |
+
mo.md('---'),
|
| 425 |
+
mo.md(processed_data.get('recruiter_message')),
|
| 426 |
+
mo.md('---'),
|
| 427 |
+
mo.md('---'),
|
| 428 |
+
mo.md('### π‘ Here are some additional tips'),
|
| 429 |
+
mo.md('---'),
|
| 430 |
+
mo.md(processed_data.get('tips')),
|
| 431 |
+
mo.md('---'),
|
| 432 |
+
mo.md('---'),
|
| 433 |
+
])
|
| 434 |
+
return (preview_section,)
|
| 435 |
|
| 436 |
|
| 437 |
+
@app.cell
|
| 438 |
+
def _(preview_section):
|
| 439 |
+
preview_section
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 440 |
return
|
| 441 |
|
| 442 |
|
| 443 |
+
@app.cell
|
| 444 |
+
def _(io):
|
| 445 |
+
from html4docx import HtmlToDocx
|
| 446 |
+
import mistune
|
| 447 |
+
from mistune.plugins.abbr import abbr
|
| 448 |
+
from mistune.plugins.def_list import def_list
|
| 449 |
+
from mistune.plugins.footnotes import footnotes
|
| 450 |
+
from mistune.plugins.formatting import insert, mark, strikethrough, subscript, superscript
|
| 451 |
+
from mistune.plugins.table import table
|
| 452 |
+
from mistune.plugins.task_lists import task_lists
|
| 453 |
+
|
| 454 |
+
# Define mistune plugins to use
|
| 455 |
+
MISTUNE_PLUGINS = [strikethrough, footnotes, table, task_lists, insert, def_list, abbr, mark, subscript, superscript]
|
| 456 |
+
|
| 457 |
+
|
| 458 |
+
def _build_markdown_parser(hard_wrap: bool = True):
|
| 459 |
+
"""Create a Mistune Markdown parser with desired plugins and hard-wrap behavior."""
|
| 460 |
+
renderer = mistune.HTMLRenderer()
|
| 461 |
+
inline = mistune.InlineParser(hard_wrap=hard_wrap)
|
| 462 |
+
return mistune.Markdown(renderer=renderer, inline=inline, plugins=MISTUNE_PLUGINS)
|
| 463 |
+
|
| 464 |
+
|
| 465 |
+
def markdown_to_docx_bytes(markdown_text: str) -> bytes | str:
|
| 466 |
+
"""Asynchronously converts markdown text to DOCX bytes with legal formatting."""
|
| 467 |
+
try:
|
| 468 |
+
markdown_parser = _build_markdown_parser(hard_wrap=True)
|
| 469 |
+
html_content = markdown_parser(markdown_text)
|
| 470 |
+
try:
|
| 471 |
+
parser = HtmlToDocx()
|
| 472 |
+
|
| 473 |
+
docx_document = parser.parse_html_string(html_content)
|
| 474 |
+
# Save to memory
|
| 475 |
+
docx_bytes = io.BytesIO()
|
| 476 |
+
docx_document.save(docx_bytes)
|
| 477 |
+
docx_bytes.seek(0)
|
| 478 |
+
|
| 479 |
+
print('Successfully converted markdown to DOCX using html4docx with mistune')
|
| 480 |
+
return docx_bytes.getvalue()
|
| 481 |
+
|
| 482 |
+
except Exception as e:
|
| 483 |
+
print("Couldn't do it")
|
| 484 |
+
return markdown_text
|
| 485 |
+
|
| 486 |
+
except Exception as e:
|
| 487 |
+
print("Couldn't do it")
|
| 488 |
+
return markdown_text
|
| 489 |
+
return (markdown_to_docx_bytes,)
|
| 490 |
|
|
|
|
| 491 |
|
| 492 |
+
@app.cell
|
| 493 |
+
def _(markdown_to_docx_bytes, mo, processed_data):
|
| 494 |
+
final_resume = markdown_to_docx_bytes(processed_data.get('resume'))
|
| 495 |
+
final_cover_letter = markdown_to_docx_bytes(processed_data.get('cover_letter'))
|
| 496 |
+
final_recruiter_message = markdown_to_docx_bytes(processed_data.get('recruiter_message'))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 497 |
|
|
|
|
| 498 |
|
| 499 |
+
resume_download = mo.download(data=final_resume, filename='resume.docx', label='RΓ©sumΓ©')
|
|
|
|
|
|
|
| 500 |
|
| 501 |
+
cover_letter_download = mo.download(data=final_cover_letter, filename='cover_letter.docx', label='Cover Letter')
|
|
|
|
|
|
|
|
|
|
|
|
|
| 502 |
|
| 503 |
+
message_download = mo.download(data=final_recruiter_message, filename='recruiter_message.docx', label='Recruiter Message')
|
| 504 |
|
| 505 |
+
downloads_section = mo.vstack([
|
| 506 |
+
mo.md('## π Your Documents Are Ready!'),
|
| 507 |
+
mo.md('Click the buttons below to download your documents:'),
|
| 508 |
+
mo.hstack([resume_download, cover_letter_download, message_download], justify='center', gap=2),
|
| 509 |
+
mo.md('---'),
|
| 510 |
+
mo.callout(mo.md('β
**Success!** Your documents have been generated and are ready to download.'), kind='success'),
|
| 511 |
+
])
|
| 512 |
|
| 513 |
+
downloads_section
|
|
|
|
|
|
|
| 514 |
return
|
| 515 |
|
| 516 |
|
| 517 |
@app.cell
|
| 518 |
+
def _(mo):
|
| 519 |
+
mo.md('# <center>Time to do another application! You got this!')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 520 |
return
|
| 521 |
|
| 522 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 523 |
if __name__ == "__main__":
|
| 524 |
app.run()
|
layouts/app.slides.json
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"type": "slides",
|
| 3 |
+
"data": {}
|
| 4 |
+
}
|
requirements.txt
CHANGED
|
@@ -1,5 +1,603 @@
|
|
| 1 |
-
|
| 2 |
-
#
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# This file was autogenerated by uv via the following command:
|
| 2 |
+
# uv pip compile pyproject.toml -o requirements.txt
|
| 3 |
+
ag-ui-protocol==0.1.9
|
| 4 |
+
# via pydantic-ai-slim
|
| 5 |
+
aiohappyeyeballs==2.6.1
|
| 6 |
+
# via aiohttp
|
| 7 |
+
aiohttp==3.13.0
|
| 8 |
+
# via huggingface-hub
|
| 9 |
+
aiosignal==1.4.0
|
| 10 |
+
# via aiohttp
|
| 11 |
+
annotated-types==0.7.0
|
| 12 |
+
# via pydantic
|
| 13 |
+
anthropic==0.70.0
|
| 14 |
+
# via
|
| 15 |
+
# resume-generator (pyproject.toml)
|
| 16 |
+
# pydantic-ai-slim
|
| 17 |
+
anyio==4.11.0
|
| 18 |
+
# via
|
| 19 |
+
# anthropic
|
| 20 |
+
# google-genai
|
| 21 |
+
# groq
|
| 22 |
+
# httpx
|
| 23 |
+
# mcp
|
| 24 |
+
# openai
|
| 25 |
+
# pydantic-evals
|
| 26 |
+
# sse-starlette
|
| 27 |
+
# starlette
|
| 28 |
+
argcomplete==3.6.2
|
| 29 |
+
# via pydantic-ai-slim
|
| 30 |
+
attrs==25.4.0
|
| 31 |
+
# via
|
| 32 |
+
# aiohttp
|
| 33 |
+
# cyclopts
|
| 34 |
+
# jsonschema
|
| 35 |
+
# referencing
|
| 36 |
+
authlib==1.6.5
|
| 37 |
+
# via fastmcp
|
| 38 |
+
basedpyright==1.31.7
|
| 39 |
+
# via resume-generator (pyproject.toml)
|
| 40 |
+
beautifulsoup4==4.14.2
|
| 41 |
+
# via
|
| 42 |
+
# html-for-docx
|
| 43 |
+
# markdownify
|
| 44 |
+
# markitdown
|
| 45 |
+
black==25.9.0
|
| 46 |
+
# via python-lsp-server
|
| 47 |
+
boto3==1.40.53
|
| 48 |
+
# via pydantic-ai-slim
|
| 49 |
+
botocore==1.40.53
|
| 50 |
+
# via
|
| 51 |
+
# boto3
|
| 52 |
+
# s3transfer
|
| 53 |
+
cachetools==6.2.1
|
| 54 |
+
# via google-auth
|
| 55 |
+
certifi==2025.10.5
|
| 56 |
+
# via
|
| 57 |
+
# httpcore
|
| 58 |
+
# httpx
|
| 59 |
+
# requests
|
| 60 |
+
cffi==2.0.0
|
| 61 |
+
# via cryptography
|
| 62 |
+
charset-normalizer==3.4.4
|
| 63 |
+
# via
|
| 64 |
+
# markitdown
|
| 65 |
+
# pdfminer-six
|
| 66 |
+
# requests
|
| 67 |
+
click==8.3.0
|
| 68 |
+
# via
|
| 69 |
+
# black
|
| 70 |
+
# magika
|
| 71 |
+
# marimo
|
| 72 |
+
# uvicorn
|
| 73 |
+
cobble==0.1.4
|
| 74 |
+
# via mammoth
|
| 75 |
+
cohere==5.19.0
|
| 76 |
+
# via pydantic-ai-slim
|
| 77 |
+
colorama==0.4.6
|
| 78 |
+
# via griffe
|
| 79 |
+
coloredlogs==15.0.1
|
| 80 |
+
# via onnxruntime
|
| 81 |
+
cryptography==46.0.3
|
| 82 |
+
# via
|
| 83 |
+
# authlib
|
| 84 |
+
# pdfminer-six
|
| 85 |
+
cyclopts==3.24.0
|
| 86 |
+
# via fastmcp
|
| 87 |
+
defusedxml==0.7.1
|
| 88 |
+
# via markitdown
|
| 89 |
+
distro==1.9.0
|
| 90 |
+
# via
|
| 91 |
+
# anthropic
|
| 92 |
+
# groq
|
| 93 |
+
# openai
|
| 94 |
+
dnspython==2.8.0
|
| 95 |
+
# via email-validator
|
| 96 |
+
docstring-parser==0.17.0
|
| 97 |
+
# via
|
| 98 |
+
# anthropic
|
| 99 |
+
# cyclopts
|
| 100 |
+
docstring-to-markdown==0.17
|
| 101 |
+
# via python-lsp-server
|
| 102 |
+
docutils==0.22.2
|
| 103 |
+
# via
|
| 104 |
+
# marimo
|
| 105 |
+
# rich-rst
|
| 106 |
+
email-validator==2.3.0
|
| 107 |
+
# via pydantic
|
| 108 |
+
eval-type-backport==0.2.2
|
| 109 |
+
# via mistralai
|
| 110 |
+
exceptiongroup==1.3.0
|
| 111 |
+
# via fastmcp
|
| 112 |
+
executing==2.2.1
|
| 113 |
+
# via logfire
|
| 114 |
+
fastavro==1.12.1
|
| 115 |
+
# via cohere
|
| 116 |
+
fastmcp==2.12.4
|
| 117 |
+
# via resume-generator (pyproject.toml)
|
| 118 |
+
filelock==3.20.0
|
| 119 |
+
# via huggingface-hub
|
| 120 |
+
flatbuffers==25.9.23
|
| 121 |
+
# via onnxruntime
|
| 122 |
+
frozenlist==1.8.0
|
| 123 |
+
# via
|
| 124 |
+
# aiohttp
|
| 125 |
+
# aiosignal
|
| 126 |
+
fsspec==2025.9.0
|
| 127 |
+
# via huggingface-hub
|
| 128 |
+
genai-prices==0.0.32
|
| 129 |
+
# via pydantic-ai-slim
|
| 130 |
+
google-auth==2.41.1
|
| 131 |
+
# via
|
| 132 |
+
# google-genai
|
| 133 |
+
# pydantic-ai-slim
|
| 134 |
+
google-genai==1.45.0
|
| 135 |
+
# via pydantic-ai-slim
|
| 136 |
+
googleapis-common-protos==1.70.0
|
| 137 |
+
# via opentelemetry-exporter-otlp-proto-http
|
| 138 |
+
griffe==1.14.0
|
| 139 |
+
# via pydantic-ai-slim
|
| 140 |
+
groq==0.32.0
|
| 141 |
+
# via pydantic-ai-slim
|
| 142 |
+
h11==0.16.0
|
| 143 |
+
# via
|
| 144 |
+
# httpcore
|
| 145 |
+
# uvicorn
|
| 146 |
+
hf-xet==1.1.10
|
| 147 |
+
# via huggingface-hub
|
| 148 |
+
html-for-docx==1.0.10
|
| 149 |
+
# via resume-generator (pyproject.toml)
|
| 150 |
+
httpcore==1.0.9
|
| 151 |
+
# via httpx
|
| 152 |
+
httpx==0.28.1
|
| 153 |
+
# via
|
| 154 |
+
# anthropic
|
| 155 |
+
# cohere
|
| 156 |
+
# fastmcp
|
| 157 |
+
# genai-prices
|
| 158 |
+
# google-genai
|
| 159 |
+
# groq
|
| 160 |
+
# mcp
|
| 161 |
+
# mistralai
|
| 162 |
+
# openai
|
| 163 |
+
# pydantic-ai-slim
|
| 164 |
+
# pydantic-graph
|
| 165 |
+
httpx-sse==0.4.0
|
| 166 |
+
# via
|
| 167 |
+
# cohere
|
| 168 |
+
# mcp
|
| 169 |
+
huggingface-hub==0.35.3
|
| 170 |
+
# via
|
| 171 |
+
# pydantic-ai-slim
|
| 172 |
+
# tokenizers
|
| 173 |
+
humanfriendly==10.0
|
| 174 |
+
# via coloredlogs
|
| 175 |
+
idna==3.11
|
| 176 |
+
# via
|
| 177 |
+
# anyio
|
| 178 |
+
# email-validator
|
| 179 |
+
# httpx
|
| 180 |
+
# requests
|
| 181 |
+
# yarl
|
| 182 |
+
importlib-metadata==8.7.0
|
| 183 |
+
# via
|
| 184 |
+
# docstring-to-markdown
|
| 185 |
+
# opentelemetry-api
|
| 186 |
+
iniconfig==2.1.0
|
| 187 |
+
# via pytest
|
| 188 |
+
invoke==2.2.1
|
| 189 |
+
# via mistralai
|
| 190 |
+
isodate==0.7.2
|
| 191 |
+
# via openapi-core
|
| 192 |
+
itsdangerous==2.2.0
|
| 193 |
+
# via marimo
|
| 194 |
+
jedi==0.19.2
|
| 195 |
+
# via
|
| 196 |
+
# marimo
|
| 197 |
+
# python-lsp-server
|
| 198 |
+
jiter==0.11.0
|
| 199 |
+
# via
|
| 200 |
+
# anthropic
|
| 201 |
+
# openai
|
| 202 |
+
jmespath==1.0.1
|
| 203 |
+
# via
|
| 204 |
+
# boto3
|
| 205 |
+
# botocore
|
| 206 |
+
jsonschema==4.25.1
|
| 207 |
+
# via
|
| 208 |
+
# mcp
|
| 209 |
+
# openapi-core
|
| 210 |
+
# openapi-schema-validator
|
| 211 |
+
# openapi-spec-validator
|
| 212 |
+
jsonschema-path==0.3.4
|
| 213 |
+
# via
|
| 214 |
+
# openapi-core
|
| 215 |
+
# openapi-spec-validator
|
| 216 |
+
jsonschema-specifications==2025.9.1
|
| 217 |
+
# via
|
| 218 |
+
# jsonschema
|
| 219 |
+
# openapi-schema-validator
|
| 220 |
+
lazy-object-proxy==1.12.0
|
| 221 |
+
# via openapi-spec-validator
|
| 222 |
+
logfire==4.13.2
|
| 223 |
+
# via pydantic-ai-slim
|
| 224 |
+
logfire-api==4.13.2
|
| 225 |
+
# via
|
| 226 |
+
# pydantic-evals
|
| 227 |
+
# pydantic-graph
|
| 228 |
+
loro==1.8.1
|
| 229 |
+
# via marimo
|
| 230 |
+
lxml==6.0.2
|
| 231 |
+
# via
|
| 232 |
+
# markitdown
|
| 233 |
+
# python-docx
|
| 234 |
+
magika==0.6.2
|
| 235 |
+
# via markitdown
|
| 236 |
+
mammoth==1.11.0
|
| 237 |
+
# via markitdown
|
| 238 |
+
marimo==0.17.0
|
| 239 |
+
# via resume-generator (pyproject.toml)
|
| 240 |
+
markdown==3.9
|
| 241 |
+
# via
|
| 242 |
+
# marimo
|
| 243 |
+
# pymdown-extensions
|
| 244 |
+
markdown-it-py==4.0.0
|
| 245 |
+
# via rich
|
| 246 |
+
markdownify==1.2.0
|
| 247 |
+
# via markitdown
|
| 248 |
+
markitdown==0.1.3
|
| 249 |
+
# via resume-generator (pyproject.toml)
|
| 250 |
+
markupsafe==3.0.3
|
| 251 |
+
# via werkzeug
|
| 252 |
+
mcp==1.17.0
|
| 253 |
+
# via
|
| 254 |
+
# resume-generator (pyproject.toml)
|
| 255 |
+
# fastmcp
|
| 256 |
+
# pydantic-ai-slim
|
| 257 |
+
mdurl==0.1.2
|
| 258 |
+
# via markdown-it-py
|
| 259 |
+
mistralai==1.9.11
|
| 260 |
+
# via pydantic-ai-slim
|
| 261 |
+
mistune==3.1.4
|
| 262 |
+
# via resume-generator (pyproject.toml)
|
| 263 |
+
more-itertools==10.8.0
|
| 264 |
+
# via openapi-core
|
| 265 |
+
mpmath==1.3.0
|
| 266 |
+
# via sympy
|
| 267 |
+
msgspec-m==0.19.2
|
| 268 |
+
# via marimo
|
| 269 |
+
multidict==6.7.0
|
| 270 |
+
# via
|
| 271 |
+
# aiohttp
|
| 272 |
+
# yarl
|
| 273 |
+
mypy-extensions==1.1.0
|
| 274 |
+
# via black
|
| 275 |
+
narwhals==2.8.0
|
| 276 |
+
# via marimo
|
| 277 |
+
nest-asyncio==1.6.0
|
| 278 |
+
# via resume-generator (pyproject.toml)
|
| 279 |
+
nexus-rpc==1.1.0
|
| 280 |
+
# via temporalio
|
| 281 |
+
nodejs-wheel-binaries==22.20.0
|
| 282 |
+
# via basedpyright
|
| 283 |
+
numpy==2.3.4
|
| 284 |
+
# via
|
| 285 |
+
# magika
|
| 286 |
+
# onnxruntime
|
| 287 |
+
onnxruntime==1.23.1
|
| 288 |
+
# via magika
|
| 289 |
+
openai==2.3.0
|
| 290 |
+
# via
|
| 291 |
+
# resume-generator (pyproject.toml)
|
| 292 |
+
# pydantic-ai-slim
|
| 293 |
+
openapi-core==0.19.5
|
| 294 |
+
# via fastmcp
|
| 295 |
+
openapi-pydantic==0.5.1
|
| 296 |
+
# via fastmcp
|
| 297 |
+
openapi-schema-validator==0.6.3
|
| 298 |
+
# via
|
| 299 |
+
# openapi-core
|
| 300 |
+
# openapi-spec-validator
|
| 301 |
+
openapi-spec-validator==0.7.2
|
| 302 |
+
# via openapi-core
|
| 303 |
+
opentelemetry-api==1.37.0
|
| 304 |
+
# via
|
| 305 |
+
# opentelemetry-exporter-otlp-proto-http
|
| 306 |
+
# opentelemetry-instrumentation
|
| 307 |
+
# opentelemetry-instrumentation-httpx
|
| 308 |
+
# opentelemetry-sdk
|
| 309 |
+
# opentelemetry-semantic-conventions
|
| 310 |
+
# pydantic-ai-slim
|
| 311 |
+
opentelemetry-exporter-otlp-proto-common==1.37.0
|
| 312 |
+
# via opentelemetry-exporter-otlp-proto-http
|
| 313 |
+
opentelemetry-exporter-otlp-proto-http==1.37.0
|
| 314 |
+
# via logfire
|
| 315 |
+
opentelemetry-instrumentation==0.58b0
|
| 316 |
+
# via
|
| 317 |
+
# logfire
|
| 318 |
+
# opentelemetry-instrumentation-httpx
|
| 319 |
+
opentelemetry-instrumentation-httpx==0.58b0
|
| 320 |
+
# via logfire
|
| 321 |
+
opentelemetry-proto==1.37.0
|
| 322 |
+
# via
|
| 323 |
+
# opentelemetry-exporter-otlp-proto-common
|
| 324 |
+
# opentelemetry-exporter-otlp-proto-http
|
| 325 |
+
opentelemetry-sdk==1.37.0
|
| 326 |
+
# via
|
| 327 |
+
# logfire
|
| 328 |
+
# opentelemetry-exporter-otlp-proto-http
|
| 329 |
+
opentelemetry-semantic-conventions==0.58b0
|
| 330 |
+
# via
|
| 331 |
+
# opentelemetry-instrumentation
|
| 332 |
+
# opentelemetry-instrumentation-httpx
|
| 333 |
+
# opentelemetry-sdk
|
| 334 |
+
opentelemetry-util-http==0.58b0
|
| 335 |
+
# via opentelemetry-instrumentation-httpx
|
| 336 |
+
packaging==25.0
|
| 337 |
+
# via
|
| 338 |
+
# black
|
| 339 |
+
# huggingface-hub
|
| 340 |
+
# marimo
|
| 341 |
+
# onnxruntime
|
| 342 |
+
# opentelemetry-instrumentation
|
| 343 |
+
# pytest
|
| 344 |
+
parse==1.20.2
|
| 345 |
+
# via openapi-core
|
| 346 |
+
parso==0.8.5
|
| 347 |
+
# via jedi
|
| 348 |
+
pathable==0.4.4
|
| 349 |
+
# via jsonschema-path
|
| 350 |
+
pathspec==0.12.1
|
| 351 |
+
# via black
|
| 352 |
+
pdfminer-six==20250506
|
| 353 |
+
# via markitdown
|
| 354 |
+
platformdirs==4.5.0
|
| 355 |
+
# via black
|
| 356 |
+
pluggy==1.6.0
|
| 357 |
+
# via
|
| 358 |
+
# pytest
|
| 359 |
+
# python-lsp-server
|
| 360 |
+
prompt-toolkit==3.0.52
|
| 361 |
+
# via pydantic-ai-slim
|
| 362 |
+
propcache==0.4.1
|
| 363 |
+
# via
|
| 364 |
+
# aiohttp
|
| 365 |
+
# yarl
|
| 366 |
+
protobuf==6.33.0
|
| 367 |
+
# via
|
| 368 |
+
# googleapis-common-protos
|
| 369 |
+
# logfire
|
| 370 |
+
# onnxruntime
|
| 371 |
+
# opentelemetry-proto
|
| 372 |
+
# temporalio
|
| 373 |
+
psutil==7.1.0
|
| 374 |
+
# via marimo
|
| 375 |
+
pyasn1==0.6.1
|
| 376 |
+
# via
|
| 377 |
+
# pyasn1-modules
|
| 378 |
+
# rsa
|
| 379 |
+
pyasn1-modules==0.4.2
|
| 380 |
+
# via google-auth
|
| 381 |
+
pycparser==2.23
|
| 382 |
+
# via cffi
|
| 383 |
+
pydantic==2.12.2
|
| 384 |
+
# via
|
| 385 |
+
# resume-generator (pyproject.toml)
|
| 386 |
+
# ag-ui-protocol
|
| 387 |
+
# anthropic
|
| 388 |
+
# cohere
|
| 389 |
+
# fastmcp
|
| 390 |
+
# genai-prices
|
| 391 |
+
# google-genai
|
| 392 |
+
# groq
|
| 393 |
+
# mcp
|
| 394 |
+
# mistralai
|
| 395 |
+
# openai
|
| 396 |
+
# openapi-pydantic
|
| 397 |
+
# pydantic-ai-slim
|
| 398 |
+
# pydantic-evals
|
| 399 |
+
# pydantic-graph
|
| 400 |
+
# pydantic-settings
|
| 401 |
+
pydantic-ai==1.1.0
|
| 402 |
+
# via resume-generator (pyproject.toml)
|
| 403 |
+
pydantic-ai-slim==1.1.0
|
| 404 |
+
# via
|
| 405 |
+
# pydantic-ai
|
| 406 |
+
# pydantic-evals
|
| 407 |
+
pydantic-core==2.41.4
|
| 408 |
+
# via
|
| 409 |
+
# cohere
|
| 410 |
+
# pydantic
|
| 411 |
+
pydantic-evals==1.1.0
|
| 412 |
+
# via pydantic-ai-slim
|
| 413 |
+
pydantic-graph==1.1.0
|
| 414 |
+
# via pydantic-ai-slim
|
| 415 |
+
pydantic-settings==2.11.0
|
| 416 |
+
# via mcp
|
| 417 |
+
pygments==2.19.2
|
| 418 |
+
# via
|
| 419 |
+
# marimo
|
| 420 |
+
# pytest
|
| 421 |
+
# rich
|
| 422 |
+
pymdown-extensions==10.16.1
|
| 423 |
+
# via marimo
|
| 424 |
+
pyperclip==1.11.0
|
| 425 |
+
# via
|
| 426 |
+
# fastmcp
|
| 427 |
+
# pydantic-ai-slim
|
| 428 |
+
pytest==8.4.2
|
| 429 |
+
# via resume-generator (pyproject.toml)
|
| 430 |
+
python-dateutil==2.9.0.post0
|
| 431 |
+
# via
|
| 432 |
+
# botocore
|
| 433 |
+
# mistralai
|
| 434 |
+
python-docx==1.2.0
|
| 435 |
+
# via
|
| 436 |
+
# resume-generator (pyproject.toml)
|
| 437 |
+
# html-for-docx
|
| 438 |
+
# python-docx-replace
|
| 439 |
+
python-docx-replace==0.4.4
|
| 440 |
+
# via resume-generator (pyproject.toml)
|
| 441 |
+
python-dotenv==1.1.1
|
| 442 |
+
# via
|
| 443 |
+
# resume-generator (pyproject.toml)
|
| 444 |
+
# fastmcp
|
| 445 |
+
# magika
|
| 446 |
+
# pydantic-settings
|
| 447 |
+
python-lsp-jsonrpc==1.1.2
|
| 448 |
+
# via python-lsp-server
|
| 449 |
+
python-lsp-server==1.13.1
|
| 450 |
+
# via resume-generator (pyproject.toml)
|
| 451 |
+
python-multipart==0.0.20
|
| 452 |
+
# via mcp
|
| 453 |
+
pytokens==0.2.0
|
| 454 |
+
# via black
|
| 455 |
+
pyyaml==6.0.3
|
| 456 |
+
# via
|
| 457 |
+
# huggingface-hub
|
| 458 |
+
# jsonschema-path
|
| 459 |
+
# marimo
|
| 460 |
+
# mistralai
|
| 461 |
+
# pydantic-evals
|
| 462 |
+
# pymdown-extensions
|
| 463 |
+
referencing==0.36.2
|
| 464 |
+
# via
|
| 465 |
+
# jsonschema
|
| 466 |
+
# jsonschema-path
|
| 467 |
+
# jsonschema-specifications
|
| 468 |
+
requests==2.32.5
|
| 469 |
+
# via
|
| 470 |
+
# cohere
|
| 471 |
+
# google-genai
|
| 472 |
+
# huggingface-hub
|
| 473 |
+
# jsonschema-path
|
| 474 |
+
# markitdown
|
| 475 |
+
# opentelemetry-exporter-otlp-proto-http
|
| 476 |
+
# pydantic-ai-slim
|
| 477 |
+
rfc3339-validator==0.1.4
|
| 478 |
+
# via openapi-schema-validator
|
| 479 |
+
rich==14.2.0
|
| 480 |
+
# via
|
| 481 |
+
# cyclopts
|
| 482 |
+
# fastmcp
|
| 483 |
+
# logfire
|
| 484 |
+
# pydantic-ai-slim
|
| 485 |
+
# pydantic-evals
|
| 486 |
+
# rich-rst
|
| 487 |
+
rich-rst==1.3.2
|
| 488 |
+
# via cyclopts
|
| 489 |
+
rpds-py==0.27.1
|
| 490 |
+
# via
|
| 491 |
+
# jsonschema
|
| 492 |
+
# referencing
|
| 493 |
+
rsa==4.9.1
|
| 494 |
+
# via google-auth
|
| 495 |
+
ruff==0.14.0
|
| 496 |
+
# via resume-generator (pyproject.toml)
|
| 497 |
+
s3transfer==0.14.0
|
| 498 |
+
# via boto3
|
| 499 |
+
six==1.17.0
|
| 500 |
+
# via
|
| 501 |
+
# markdownify
|
| 502 |
+
# python-dateutil
|
| 503 |
+
# rfc3339-validator
|
| 504 |
+
sniffio==1.3.1
|
| 505 |
+
# via
|
| 506 |
+
# anthropic
|
| 507 |
+
# anyio
|
| 508 |
+
# groq
|
| 509 |
+
# openai
|
| 510 |
+
soupsieve==2.8
|
| 511 |
+
# via beautifulsoup4
|
| 512 |
+
sqlglot==27.27.0
|
| 513 |
+
# via resume-generator (pyproject.toml)
|
| 514 |
+
sse-starlette==3.0.2
|
| 515 |
+
# via mcp
|
| 516 |
+
starlette==0.48.0
|
| 517 |
+
# via
|
| 518 |
+
# marimo
|
| 519 |
+
# mcp
|
| 520 |
+
# pydantic-ai-slim
|
| 521 |
+
sympy==1.14.0
|
| 522 |
+
# via onnxruntime
|
| 523 |
+
temporalio==1.18.0
|
| 524 |
+
# via pydantic-ai-slim
|
| 525 |
+
tenacity==9.1.2
|
| 526 |
+
# via
|
| 527 |
+
# google-genai
|
| 528 |
+
# pydantic-ai-slim
|
| 529 |
+
tokenizers==0.22.1
|
| 530 |
+
# via cohere
|
| 531 |
+
tomlkit==0.13.3
|
| 532 |
+
# via marimo
|
| 533 |
+
tqdm==4.67.1
|
| 534 |
+
# via
|
| 535 |
+
# huggingface-hub
|
| 536 |
+
# openai
|
| 537 |
+
ty==0.0.1a22
|
| 538 |
+
# via resume-generator (pyproject.toml)
|
| 539 |
+
types-protobuf==6.32.1.20250918
|
| 540 |
+
# via temporalio
|
| 541 |
+
types-requests==2.32.4.20250913
|
| 542 |
+
# via cohere
|
| 543 |
+
typing-extensions==4.15.0
|
| 544 |
+
# via
|
| 545 |
+
# anthropic
|
| 546 |
+
# beautifulsoup4
|
| 547 |
+
# cohere
|
| 548 |
+
# docstring-to-markdown
|
| 549 |
+
# google-genai
|
| 550 |
+
# groq
|
| 551 |
+
# huggingface-hub
|
| 552 |
+
# logfire
|
| 553 |
+
# nexus-rpc
|
| 554 |
+
# openai
|
| 555 |
+
# openapi-core
|
| 556 |
+
# opentelemetry-api
|
| 557 |
+
# opentelemetry-exporter-otlp-proto-http
|
| 558 |
+
# opentelemetry-sdk
|
| 559 |
+
# opentelemetry-semantic-conventions
|
| 560 |
+
# pydantic
|
| 561 |
+
# pydantic-core
|
| 562 |
+
# python-docx
|
| 563 |
+
# temporalio
|
| 564 |
+
# typing-inspection
|
| 565 |
+
typing-inspection==0.4.2
|
| 566 |
+
# via
|
| 567 |
+
# mistralai
|
| 568 |
+
# pydantic
|
| 569 |
+
# pydantic-ai-slim
|
| 570 |
+
# pydantic-graph
|
| 571 |
+
# pydantic-settings
|
| 572 |
+
ujson==5.11.0
|
| 573 |
+
# via
|
| 574 |
+
# python-lsp-jsonrpc
|
| 575 |
+
# python-lsp-server
|
| 576 |
+
urllib3==2.5.0
|
| 577 |
+
# via
|
| 578 |
+
# botocore
|
| 579 |
+
# requests
|
| 580 |
+
# types-requests
|
| 581 |
+
uvicorn==0.37.0
|
| 582 |
+
# via
|
| 583 |
+
# marimo
|
| 584 |
+
# mcp
|
| 585 |
+
uvloop==0.21.0
|
| 586 |
+
# via resume-generator (pyproject.toml)
|
| 587 |
+
wcwidth==0.2.14
|
| 588 |
+
# via prompt-toolkit
|
| 589 |
+
websockets==15.0.1
|
| 590 |
+
# via
|
| 591 |
+
# resume-generator (pyproject.toml)
|
| 592 |
+
# google-genai
|
| 593 |
+
# marimo
|
| 594 |
+
werkzeug==3.1.1
|
| 595 |
+
# via openapi-core
|
| 596 |
+
wrapt==1.17.3
|
| 597 |
+
# via
|
| 598 |
+
# opentelemetry-instrumentation
|
| 599 |
+
# opentelemetry-instrumentation-httpx
|
| 600 |
+
yarl==1.22.0
|
| 601 |
+
# via aiohttp
|
| 602 |
+
zipp==3.23.0
|
| 603 |
+
# via importlib-metadata
|