Workshop materials from OU Libraries · Digital Scholarship & Data Services (DSDS).
Fourteen notebooks, two helper modules, a runnable script and a hands-on agent lab. You will call hosted models through OU's LiteLLM gateway, run open models yourself on the NRP Nautilus GPU cluster, and build four complete research use cases — from coding survey responses to extracting data from scanned documents.
Everything runs on OU infrastructure. Nothing needs a credit card or a laptop GPU.
🧪 Pilot service. The OU AI Sandbox — LiteLLM gateway, LibreChat, and the NRP Nautilus JupyterHub — is run by DSDS. Need a key, found a bug, or want to use this in a course? varunsaya@ou.edu · dsds@ou.edu
You need two things:
- A JupyterHub account on one of OU's two entry points:
- https://researcher.lib.ou.edu — research support, GPU access, the shared model cache (Only for GPU access and HuggingFace models)
- https://jupyter.lib.ou.edu — general coursework and everyday use
- A LiteLLM virtual key for Part 1 — email dsds@ou.edu. Part 2 works without one.
Part 2 (HuggingFace_Inference/) needs the shared model cache at /hfcache/hub, which
is mounted on researcher.lib.ou.edu. A CPU server is fine for most of it; the 🟠
cells are much faster with a GPU.
Open a terminal in JupyterLab: File ▸ New ▸ Terminal. Then:
cd /home/jovyan
git clone https://github.com/OULibraries/Programming_with_AI_Fall_26.git workshop26
cd workshop26Everything in this workshop assumes that path: /home/jovyan/workshop26. The
workshop26 at the end of the clone command is what renames the folder — don't drop it.
Check it worked:
ls
# HuggingFace_Inference LiteLLM labs README.md requirements.txt .env.exampleIn the JupyterLab file browser on the left, click into workshop26 and start with
LiteLLM/10_gateway_first_calls.ipynb.
If the instructor pushes a fix during the workshop:
cd /home/jovyan/workshop26
git stash # park your own edits
git pull
git stash pop # put them backThe Nautilus images already have nearly everything. Only if something is missing:
pip install -r requirements.txt
⚠️ Part 2 exception: never pip-installtorch,transformersorhuggingface_hub. They are in the image, and your own copy would eat into a 20 GB home quota. If one is genuinely missing, email dsds@ou.edu.
Part 1 needs a LiteLLM virtual key. Never type a key into a notebook cell — it gets
saved inside the .ipynb and ends up on GitHub.
The notebooks look in three places, in this order:
| # | Where | How to set it |
|---|---|---|
| 1 | LITELLM_API_KEY environment variable |
export LITELLM_API_KEY="sk-..." in a terminal |
| 2 | ~/.config/ou-ai/litellm.key |
notebook 10, Step 1 writes it (mode 600) — recommended |
| 3 | A .env file in this folder |
cp .env.example .env, then edit it |
If none exist, you get a hidden prompt. Option 2 is best: it lives outside the repo, so it can never be committed, and opencode (notebook 13) reads the same file.
Two rules that matter for the rest of your career:
- Keys are budgets. Anyone with your key spends yours.
- If a key leaks, tell DSDS immediately so it can be rotated.
workshop26/
├── LiteLLM/ Part 1 — hosted models through the OU gateway
│ ├── ou_ai.py shared gateway client (notebooks 11+ import this)
│ ├── crossref_mcp.py the MCP tool server for notebooks 12 and 13
│ ├── 19_rag_over_research_pdfs.py runnable RAG script (not a notebook)
│ ├── 10 … 18 .ipynb the notebooks, in order
│ └── data/
│ ├── library_survey_synthetic.csv input for notebook 11
│ └── Images/workshop_attendance.jpg sample image for notebook 10
├── HuggingFace_Inference/ Part 2 — open models you run yourself on Nautilus
│ └── 00 … 07 .ipynb
├── labs/opencode-lab/ the hands-on lab for notebook 13
├── requirements.txt
└── .env.example
Start here. Run them in order; each builds on the last.
| Notebook | What you'll do | Needs |
|---|---|---|
10_gateway_first_calls |
Connect, list models, chat, stream, track cost, compare models | key |
11_structured_outputs_at_scale |
Messy text → a clean dataset: schema, validation, retries, async | key, ou_ai.py |
12_tools_and_mcp |
Tool calling, a real agent loop, and packaging tools with MCP | key, ou_ai.py, crossref_mcp.py |
13_opencode_setup |
Install the opencode coding agent and point it at the gateway | key, ou_ai.py, labs/ |
14_usecase_text_to_sql |
Ask questions of a database in plain English | key |
15_usecase_semantic_search_rag |
Embeddings, semantic search, and grounded answers | key |
17_usecase_qualitative_interviews |
Code interview transcripts, verify quotes, check agreement | key |
18_usecase_document_image_extraction |
Pull structured data out of scanned receipts and tables | key, vision model |
Plus 19_rag_over_research_pdfs.py — the same RAG pattern as a command-line script
you can run over your own PDFs:
cd /home/jovyan/workshop26/LiteLLM
python 19_rag_over_research_pdfs.py --docs "data/*.csv" --query "What do users complain about?" --no-llm
python 19_rag_over_research_pdfs.py --docs "readings/*.pdf" --query "What methods are used?" --top_k 4--no-llm is retrieval-only: no key, no cost. Useful for debugging your method.
| Notebook | What you'll do |
|---|---|
00_start_here |
Where your code runs, where the models live, your first pipeline |
01_huggingface_hub_basics |
The shared cache, offline mode, gated models, reclaiming disk space |
02_text_and_llms |
Base vs. chat models, translation, QA, summarization, classification, code |
04_vision_models |
Classify, tag with your own vocabulary, caption, detect objects |
06_audio_models |
Speech to text and text to speech |
07_litellm_gateway |
The same gateway as Part 1, from the Part 2 environment |
Numbering skips 03 and 05. Those topics — embeddings/semantic search and OCR/multimodal — are taught in Part 1 instead, in notebooks 15 and 18. Nothing is missing; the numbers are kept stable so they match the slides.
Notebook 13 installs opencode, an AI coding agent, and sends you here. The lab is a small Python script with real bugs and thin test coverage:
cd /home/jovyan/workshop26/labs/opencode-lab
git init -q 2>/dev/null; git add -A; git commit -qm "start of lab"
opencodeThen follow EXERCISES.md. Two of the twelve tests fail on purpose — they describe
the behavior the code should have. Fixing the code, not the tests, is the exercise.
The folder also has an AGENTS.md (house rules that any coding agent reads) and a
custom /cite command in .opencode/commands/ that uses the Crossref MCP tools.
Every dataset here is synthetic. The survey responses, interview transcripts, receipts and handbook pages were made up for teaching. No real people, no real patron data. Use them freely.
Two files ship with the repo:
LiteLLM/data/library_survey_synthetic.csv— input for notebook 11LiteLLM/data/Images/workshop_attendance.jpg— sample image for notebook 10
Everything else under data/ is generated on first run by notebooks 14, 15, 17 and
18, and is gitignored. If a data file seems to be missing, run the notebook's setup
cells from the top — they create it.
The one thing that is real is Crossref (notebook 12 and the /cite command). It's a
live API over 150M+ scholarly works, needs no key, and is the point: a model with a real
lookup tool stops inventing citations.
- Drop images in
LiteLLM/data/Images/orHuggingFace_Inference/data/Images/. - Point
19_rag_over_research_pdfs.pyat your own PDFs with--docs. - FERPA/IRB: don't put identifiable student data or restricted human-subjects text into prompts. Keep sources on JupyterHub; only retrieved chunks leave the machine.
Your home directory has a 20 GB limit. A single chat model is 3–10 GB, so a few careless cells can fill it and stop your server from starting.
Every Part 2 notebook opens with STEP 0a and STEP 0b. Run them first, in order, every time:
- 0a points every cache at the shared read-only cache and blocks downloads. It must
run before anything imports
transformers, because the libraries read these settings once, at import. - 0b verifies 0a actually took effect and defines the helpers (
is_ready,compare,free,out_path).
| Path | What lives there | |
|---|---|---|
| 📦 Shared model cache | /hfcache/hub |
Every model. Read-only, shared, costs you nothing. |
| 🧽 Scratch | /tmp/hf_scratch |
Lock files and settings. Deleted when your server stops. |
| 🏠 Your home | /home/jovyan |
Your notebooks and small results in ./outputs. Never models. |
If 0b raises an error: Kernel ▸ Restart Kernel, then run 0a → 0b again, in order.
Need a model that isn't cached? Browse https://huggingface.co/models, then request it via the AI Services Resource Request form or email dsds@ou.edu. Please don't work around the download block — it protects everyone's home quota.
| Symptom | Fix |
|---|---|
ModuleNotFoundError: ou_ai |
You're not in LiteLLM/. ou_ai.py must sit next to the notebook. |
RuntimeError: Storage rules not active |
Something imported HF before STEP 0a. Kernel ▸ Restart Kernel, rerun 0a then 0b. |
401 from the gateway |
Bad or expired key. Check it, or ask DSDS. |
400 / 404 on a model |
Wrong model name. Copy it exactly from the Step 2 model list. |
429 |
Rate limit or budget exhausted. Check /key/info (notebook 10, Step 7). |
5xx |
Provider hiccup. The SDK already retried 3×; try again or switch model. |
| A model "isn't ready" in Part 2 | It's not on the shared cache. The cell skips it by design — request it from DSDS. |
crossref_mcp.py not found |
Run notebook 12 from inside LiteLLM/, not from the repo root. |
| opencode: "command not found" | source ~/.bashrc, or use the full path ~/.opencode/bin/opencode. |
Reporting a problem — email varunsaya@ou.edu or dsds@ou.edu with the notebook name, the cell, and the full error message. That feedback is how this gets better.
The workshop teaches the habits, not just the API:
- You own the result. Run the code, read the tests, check the numbers. A model — or an agent that says "all tests pass" — can be confidently wrong.
- Log provenance. Record the model id, its revision, your exact prompt, and the cost, next to your results. Notebooks 11 and 02 show the pattern.
- Check agreement before you publish. Compare a random sample against human coding and report it. Notebook 11 Step 6 does this properly.
- Grounding beats guessing. Give a model a real lookup tool instead of trusting its memory. That's notebook 12.
- Tool results are untrusted input. A web page can say "ignore your instructions." Never give an agent both private data and a way to send it out.
- Commit before you let an agent edit.
git diffis how you see what it really did.
Teaching material from OU Libraries Digital Scholarship & Data Services. Model licenses are governed by their own model cards — check them before publishing results.
Programming with Artificial Intelligence · OU Libraries DSDS · dsds@ou.edu