
NAME
Samaki -- Stitch together snippets of code and data
SYNOPSIS
Usage:
samaki [<wkdir>] -- Browse pages
samaki [--wkdir[=Any]] new -- Open a new page for editing
samaki edit <target> -- Edit a page with the given name
samaki <file> -- Edit a file
samaki reset-conf -- Reset the configuration to the default
samaki conf -- Edit the configuration file
DESCRIPTION
Samaki is a system for writing queries and snippets of programs in a multiple languages in one file. It's a bit like Jupyter notebooks (or R or Observable notebooks), but with multiple languages in one notebook. It has a plugin architecture for defining the types of cells, and for describing the types of output. Outputs from cells are serialized, often as CSV files. Cells can reference each others' content or output.
Some use cases for samaki include
Many examples of can be found in the eg directory
Here's one example:
-- duck
select 'hello' as world;
-- duck
select 'earth' as planet;
-- llm
Which planet from the sun is 〈 cells(1).rows[0]<planet> 〉?
To use this:
save it as a file, e.g. "planets.samaki"
run `samaki planets'
highlight the second cell and press enter to run the query
press r to refresh the page, notice that it has changed to
"Which planet from the sun is earth?"
highlight the third cell and press enter to run the LLM query
A samaki page (or notebook) consists of two things
a text file, ending in .samaki
a directory containing data files.
The directory name will be the same as the basename of the file, and it will be created if it doesn't exist. e.g.
taxi-data.samaki
taxi-data/
cell-0.csv
cell-1.csv
... other data files ...
The samaki file is a text file divided into cells, each of which looks like this:
-- <cell type> [ : name ]?
| conf-key 1 : conf-value 1
| conf-key 2 : conf-value 2
[... cell content ..]
That is:
New cells are indicated with a line starting with with two dashes and a space ("-- ") by the type of cell mark the division. (Other dashes like "─" can be used instead of a double dash)
The type of the cell should be a single word with alphanumeric characters.
An optional colon and name can give a name to the cell.
At the top of each cell, optional configuration options can be set as name : value pairs with a leading pipe symbol (|
)
Another example: a cell named "the_answer" that runs a query and uses a duckdb file named life.duckdb
-- duck : the_answer
| file: life.duckdb
select 42 as life_the_universe_and_everything
Cells may reference other cells by using angle brackets, as shown above:
〈 cells(0).content 〉
alternatively, three less-than signs in a row can be used, like this:
<<< cells(0).content >>>
Cells can be referenced by name or by number, e.g.
〈 cells('the_answer').content 〉
refers to the contents of the above cell.
The API for results from cells is still evolving, but at a minimum, it has the name of an output file, which is intended to have any output generated by the cell. The behavior of output is determined by the type of cell (see plugins and configuration below).
CONFIGURATION
Samaki is configured with a set of regular expressions which are used to determine how to handle each cell. The "type" of the cell above is matched against the regexes, and whichever one matches first will be used to parse the input and generate output.
Samaki comes with a default configuration file and some default plugins. The default configuration looks like this
# samaki-conf.raku
#
%*samaki-conf =
plugins => [
/ duck / => 'Samaki::Plugin::Duck',
/ llm / => 'Samaki::Plugin::LLM',
/ text / => 'Samaki::Plugin::Text',
/ bash / => 'Samaki::Plugin::Bash',
/ html/ => 'Samaki::Plugin::HTML',
],
plugouts => [
/ csv / => 'Samaki::Plugout::Duckview',
/ csv / => 'Samaki::Plugout::DataTable',
/ html / => 'Samaki::Plugout::HTML',
/ .* / => 'Samaki::Plugout::Raw',
]
;
PLUGINS
Plugin classes should do the Samaki::Plugin
role, and at a minimum should implement the execute
method. (and have name
and description
attributes). The usual RAKULIB
directories are searched for plugins, so adding local plugins is a matter of adding a new calss and placing it into this search path.
In addition to the strings above, a class definition may be placed directly into the configuration file, and this definition can reference other plugins.
For instance, this defines a plugin called python
for executing python code:
/ python / => class SamakiPython does Samaki::Plugin {
has $.name = 'samaki-python';
has $.description = 'run some python!';
method execute(:$cell, :$mode, :$page, :$out) {
my $content = $cell.get-content(:$mode, :$page);
$content ==> spurt("in.py");
shell "python in.py > out.py 2> errs.py";
$out.put: slurp "out.py";
}
Alternatively, the Process
plugin provides a convenient way to run external processes, and stream the results, so this will also work, and send unbuffered output to the bottom pane:
use Samaki::Plugin::Process;
%*samaki-conf =
plugins => [
...
/ python / => class SamakiPython does Samaki::Plugin::Process[
name => 'python',
cmd => 'python3' ] {
has %.add-env = PYTHONUNBUFFERED => '1';
},
...
PLUGOUTS
Output files are also matched against a sequence of regexes, and these can be used for visualizing or showing output.
These should also implement execute
which has this signature:
method execute(IO::Path :$path!, IO::Path :$data-dir!, Str :$name!) { ... }
Plugouts are intended to either visualize or export data. The plugout for viewing an HTML file is basically:
method execute(IO::Path :$path!, IO::Path :$data-dir!, Str :$name!) {
shell <<open $path>>;
}
USAGE
For help, type samaki -h
.
Run samaki
for the first time to see a welcome page and a demo!
Have fun!
TODO
A lot, especially documentation.
Contributions are welcome!
AUTHOR
Brian Duggan (bduggan at matatu.org)