Skip to content

Commit 0b57170

Browse files
committed
Add pandas and numpy support for LLM-generated Python
Built-in pandas DataFrame/Series and numpy ndarray support, enabling LLMs to write realistic data-analysis scripts combining multiple operations in a single tool call. NumPy: array creation, math functions, arithmetic/broadcasting, comparisons, reshaping, and aggregation methods. Pandas: DataFrame/Series creation, indexing, filtering, sorting, groupby with aggregation, merge/concat/melt/pivot_table, apply, iterrows/itertuples, string/datetime accessors, and null handling. All types are heap-allocated with proper reference counting. Pure Rust implementation — no real pandas/numpy dependency.
1 parent bf7c7ef commit 0b57170

37 files changed

Lines changed: 9119 additions & 32 deletions

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,12 @@ What Monty **can** do:
3737
- Collect stdout and stderr and return it to the caller
3838
- Run async or sync code on the host via async or sync code on the host
3939
- Use a small subset of the standard library: `sys`, `os`, `typing`, `asyncio`, `re`, `datetime` (soon), `dataclasses` (soon), `json` (soon)
40+
- Use built-in `pandas` and `numpy` emulation — the subset of DataFrame, Series, and ndarray operations that LLMs commonly write (groupby, merge, pivot, filtering, aggregation, etc.) works out of the box, enabling LLMs to write data manipulation scripts as a single tool call
4041

4142
What Monty **cannot** do:
4243

4344
- Use the rest of the standard library
44-
- Use third party libraries (like Pydantic), support for external python library is not a goal
45+
- Use arbitrary third party libraries (like Pydantic) — however, `pandas` and `numpy` are built-in
4546
- define classes (support should come soon)
4647
- use match statements (again, support should come soon)
4748

@@ -356,7 +357,7 @@ Details on each row below:
356357

357358
### Monty
358359

359-
- **Language completeness**: No classes (yet), limited stdlib, no third-party libraries
360+
- **Language completeness**: No classes (yet), limited stdlib, but includes built-in pandas/numpy emulation for data manipulation
360361
- **Security**: Explicitly controlled filesystem, network, and env access, strict limits on execution time and memory usage
361362
- **Start latency**: Starts in microseconds
362363
- **Setup complexity**: just `pip install pydantic-monty` or `npm install @pydantic/monty`, ~4.5MB download

crates/monty/src/args.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crate::{
44
MontyObject, ResourceTracker, defer_drop, defer_drop_mut,
55
exception_private::{ExcType, RunError, RunResult, SimpleException},
66
expressions::{ExprLoc, Identifier},
7-
heap::{ContainsHeap, DropWithHeap, Heap, HeapGuard},
7+
heap::{ContainsHeap, DropWithHeap, Heap, HeapData, HeapGuard},
88
intern::{Interns, StringId},
99
parse::ParseError,
1010
types::{Dict, dict::DictIntoIter},
@@ -285,6 +285,22 @@ impl ArgValues {
285285
}
286286
}
287287

288+
/// Returns true if the first positional argument is callable (function/closure).
289+
///
290+
/// Used to distinguish `map(dict)` from `map(callable)` without consuming args.
291+
pub fn first_is_callable(&self, heap: &Heap<impl ResourceTracker>) -> bool {
292+
let first = match self {
293+
Self::One(v) | Self::Two(v, _) => Some(v),
294+
Self::ArgsKargs { args, .. } => args.first(),
295+
Self::Empty | Self::Kwargs(_) => None,
296+
};
297+
match first {
298+
Some(Value::DefFunction(_)) => true,
299+
Some(Value::Ref(id)) => matches!(heap.get(*id), HeapData::Closure(_)),
300+
_ => false,
301+
}
302+
}
303+
288304
/// Returns the number of positional arguments.
289305
///
290306
/// For `Kwargs` returns 0, for `ArgsKargs` returns only the positional args count.

0 commit comments

Comments
 (0)