Note
oneshot was recently used in a bid to The Gemma 4 Good Hackathon!
oneshot is a small command-line tool for sending single, stateless requests to large language models. It is built for cases where you do not want an ongoing chat session—just a prompt, a response, and optionally a CSV file with the results.
At the moment, oneshot supports:
- Ollama
- OpenAI Responses API
- Text-only and image + text prompts
- Single requests and batch processing
- Terminal output or CSV output
In this project, one shot means there is no stored conversation history. Each request is assembled from the configuration file, sent to the chosen provider, and handled independently.
That makes oneshot useful for tasks like:
- running the same prompt over many images
- evaluating a CSV of text prompts
- extracting text from scanned images
- quick ad-hoc model calls from the command line
- Simple CLI:
oneshot -c <config.toml> - TOML-based configuration
- Batch image processing from a directory
- Batch text processing from a CSV file
- Structured CSV output with query id, provider, timestamp, model name, response text, elapsed time, and usage information
- Compatible with local models through Ollama and hosted models through OpenAI
- Python 3.13+
- Access to either:
- an Ollama server, or
- an OpenAI API key
- For image workflows:
.png,.jpg, or.jpegfiles
uv pip install git+https://github.com/joheli/oneshot.gitThis installs the oneshot command-line application.
git clone https://github.com/joheli/oneshot.git
cd oneshot
uv venv --python 3.13
uv pip install .The repository ships with example configuration files.
oneshot -c oneshot_images.tomlThis runs a prompt over the sample images in demo/input and writes the results to a CSV file.
oneshot -c oneshot_text_from_images.tomloneshot -c oneshot_batch_text.tomloneshot --help
oneshot -c path/to/config.tomlThe only CLI option is the config file:
-c,--config: path to the TOML configuration file
If omitted, oneshot looks for a file named oneshot.toml in the current directory.
oneshot is controlled entirely through a TOML file with three top-level sections:
[vendor][query][out]
Choose and configure at least one provider.
[vendor.ollama]
host = "http://localhost:11434"[vendor.openai]
api_key = "sk-..."Your [query].target must match a configured provider.
This section defines what gets sent to the model.
Supported query types:
singleton-textsingleton-imagebatch-textbatch-image
Common fields:
[query]
type = "batch-text"
target = "ollama"
model_name = "gemma3:4b"
temperature = 0Controls where results go.
[out]
mode = "file"
csv_file = "demo/output/out.csv"
csv_file_separator = ";"
# further optional parameters:
response_to_file_length_threshold = 300 # default = 300, answers over 300 characters are written to separate file `response_to_file_filename`
response_to_file_filename = "~qid~_llm_response.txt" # `~qid~` is a placeholder and will be replaced by query id Output modes:
standard→ print responses to the terminalfile→ save responses to CSV and optionally long responses to text files
Send one text prompt.
Expected fields under [query.details]:
instructionscontextquestion
Example:
[query]
type = "singleton-text"
target = "openai"
model_name = "gpt-4o-mini"
temperature = 0
[query.details]
instructions = "Answer briefly and accurately."
context = "Paris is the capital of France."
question = "What is the capital of France?"Starting version 0.1.6 you can replace any text content with a path to a text file. The path is labeled with prefix "[>]". This works both in singleton-text as well as batch-text mode.
Send one image plus a text prompt.
Expected fields:
instructionsquestionimage
Example:
[query]
type = "singleton-image"
target = "ollama"
model_name = "gemma3:4b"
temperature = 0.2
[query.details]
instructions = "Answer briefly."
question = "Do you see raspberries?"
image = "demo/input/berries.png"Optionally, you can add reference images to query.details under ref_imgs, e.g. ref_imgs = ["demo/input/ref_clear.png", "demo/input/ref_cloud.png"].
Read prompts from a CSV file and send one request per row.
Expected fields:
csv_filecsv_file_separatorcolname_query_idcolname_instructionscolname_questions- optionally
colname_contexts
Example:
[query]
type = "batch-text"
target = "ollama"
model_name = "gemma3:4b"
temperature = 0
[query.details]
csv_file = "demo/input/queries.csv"
csv_file_separator = ","
colname_query_id = "qid"
colname_instructions = "instructions"
colname_questions = "questions"
colname_contexts = "contexts"A minimal CSV for batch-text should look like this:
qid,instructions,questions,contexts
q1,"Answer briefly.","What is 2 + 2?",""
q2,"Use the context.","Who is the daughter?","Dragan and Yasmina have a daughter called Sohar."
q3,"You are helpful","Summarize in two sentences","[>]demo/input/context.txt"Starting version 0.1.6 you can replace any text content with a path to a text file. The path is labeled with prefix "[>]".
Run the same prompt over all images in a directory.
Expected fields:
instructionsquestionimg_dir- optionally,
ref_imgs: specifies reference images, e.g.ref_imgs = ["demo/input/ref_clear.png", "demo/input/ref_cloud.png"]. - optionally
img_dir_glob: specifies which files to match inimg_dir - optionally
img_qid: "filename" as default, i.e. the whole filename is used as the query id (qid) in the output csv file - optionally
img_qid_regex: ifimg_qidis set to 'filename-regex', a regex pattern extracting a character sequence of interest from the filename is used as qid
Example:
[query]
type = "batch-image"
target = "ollama"
model_name = "gemma3:4b"
temperature = 0.2
[query.details]
instructions = "Describe the image in one sentence."
question = "What is shown here?"
img_dir = "demo/input"
img_dir_glob = "*.png"- At least one provider must be configured in
[vendor]. - The selected provider in
[query].targetmust also be configured. - For file output, if the output CSV exists the filename is appended an integer.
- For
batch-text, the input CSV must contain the configured column names. - For
batch-image, each matching file becomes one request. - The example files in the repository are a good starting point:
oneshot_images.tomloneshot_text_from_images.tomloneshot_batch_text.toml
oneshot is a good fit when you want a lightweight, scriptable interface for LLM calls without building a chat app or workflow engine.
It is especially handy for:
- local experimentation with Ollama
- quick model benchmarking on repeated prompts
- image labeling or extraction jobs
- generating CSV outputs for downstream analysis
Thanks to the creators of:
- NumPy
- Polars
- Pydantic
- Rich
- Typer
- uv
MIT
- Record and monitor reasons for stop, (e.g.
"done_reason":"length"with ollama or"finish_reason":"stop"with OpenAI) - Programmatically attempt increasing context window (e.g.
num_ctx, andnum_predictfor ollama), if context window is too small (for OpenAI context window is fixed but"max_tokens"controls the numbers of tokens generated) - Implement timeout in
requests.postcalls