%%there ai: prompt-to-cell generator#
%%there ai generates a %%there cell from a plain-language request.
It adds the generated cell below the current one,
ready for you to review, edit, and run.
%load_ext herethere.magic
%connect-there
Configuration#
%%there ai uses an OpenAI-compatible chat API. Put the default settings in
there_ai.env:
THERE_AI_MODEL=gpt-5.5
THERE_AI_API_KEY=sk-...
THERE_AI_BASE_URL=https://api.openai.com/v1
THERE_AI_TEMPERATURE=0.2
THERE_AI_TIMEOUT=300
Only THERE_AI_MODEL is required for local providers that do not need an API
key. Hosted providers usually need THERE_AI_API_KEY.
THERE_AI_BASE_URL defaults to https://api.openai.com/v1.
THERE_AI_TEMPERATURE defaults to 0.2.
THERE_AI_TIMEOUT defaults to 300 seconds.
Environment variables with the same names override values from there_ai.env:
%env THERE_AI_TIMEOUT=120
Use a different settings file for the current notebook session:
from herethere.there.ai import clear_ai_config_path, set_ai_config_path
set_ai_config_path("there_ai.local.env")
Example local-provider settings:
THERE_AI_MODEL=qwen2.5-coder
THERE_AI_BASE_URL=http://localhost:11434/v1
THERE_AI_TEMPERATURE=0.1
THERE_AI_TIMEOUT=300
Basic usage#
Write the request in the cell body:
%%there ai
show Python version, platform details, and the current working directory
Generating %%there cell with AI... this can take up to 120s.
Generated a %%there cell in 0.0s. Review it, then run it to execute on the connected target.
The generated cell may look like this:
%%there
# Generated locally by %%there ai. Review before running.
import sys
import platform
import os
from pathlib import Path
print(f"Python version: {sys.version}")
print(f"Platform: {platform.platform()}")
print(f"Architecture: {platform.architecture()[0]}")
print(f"Machine: {platform.machine()}")
print(f"Processor: {platform.processor()}")
print(f"System: {platform.system()} {platform.release()}")
print(f"Current working directory: {os.getcwd()}")
print(f"CWD as Path: {Path.cwd()}")
Prompt sections#
%%there ai builds one system prompt from named prompt sections.
The built-in default section is used automatically. It tells the model to
generate code for herethere cells, keep execution remote, and return only code.
Register extra sections when the model needs project-specific context:
from herethere.there.ai import register_ai_prompt, set_ai_prompts
register_ai_prompt(
"fastapi-runtime",
"""
The remote process is a running FastAPI application.
The remote namespace may contain app, settings, engine, SessionLocal, or logger.
Prefer inspecting app.routes, app.state, dependency_overrides, and settings.
Do not call uvicorn.run() or create a second FastAPI app.
Do not modify routes, dependency overrides, or database state unless asked.
""",
)
set_ai_prompts("fastapi-runtime")
Now normal %%there ai requests include both default and fastapi-runtime:
%%there ai
list FastAPI routes with methods and paths, then summarize app.state keys
Generating %%there cell with AI... this can take up to 120s.
Generated a %%there cell in 0.0s. Review it, then run it to execute on the connected target.
Clear notebook-level prompt sections:
from herethere.there.ai import clear_ai_prompts
clear_ai_prompts()
Prompt lookup order#
Prompt names can come from three places:
%%there ai --prompts ...for one request.set_ai_prompts(...)for the current notebook session.THERE_AI_PROMPTS=...inthere_ai.envor the environment.
Command options win over notebook settings. Notebook settings win over
THERE_AI_PROMPTS.
Use prompt names in the command line for one request:
%%there ai --prompts fastapi-runtime
list FastAPI routes with methods and paths, then summarize app.state keys
Generating %%there cell with AI... this can take up to 120s.
Generated a %%there cell in 0.0s. Review it, then run it to execute on the connected target.
Multiple prompt sections are comma-separated:
%%there ai --prompts fastapi-runtime,sqlalchemy-runtime
show route count and database engine URL driver name without printing credentials
Unknown %%there ai prompt: 'sqlalchemy-runtime'
Use THERE_AI_PROMPTS when a notebook or project should use the same prompt
sections by default:
THERE_AI_PROMPTS=fastapi-runtime
Replacing the default prompt#
Most requests should keep the built-in default prompt. Use --no-default
only when your custom prompt fully explains what the model must generate.
%%there ai --no-default --prompts custom
generate exactly one Python print statement
--no-default requires --prompts.
Inspect prompts#
List available prompt sections:
from herethere.there.ai import list_ai_prompts
print(list_ai_prompts())
Show code cell output
('default', 'fastapi-runtime', 'fix')
Preview the full system prompt:
from herethere.there.ai import build_ai_template
print(build_ai_template(["fastapi-runtime"]))
Show code cell output
You are generating Python code that will execute inside a live, already-running Python application process.
The application is already started. Your code is injected into the app's existing Python interpreter and runs in the same runtime namespace as the app.
Treat this like writing code into an interactive debugger console inside the running app.
## Core execution model
- The app process is already alive.
- You are not starting the app.
- You are not writing a standalone script.
- Existing app objects may already be present in `globals()`.
- Changes you make affect the live app immediately.
- The code should be suitable to run as one plain Python snippet.
- The code is executed as normal Python code, not as a notebook cell and not inside an async function.
## Important syntax rule
Do not use top-level `await`.
Generated code must be valid plain Python.
If async code is explicitly required, wrap it in an `async def` function and schedule or run it only according to existing app conventions. Do not guess.
## What to do
Prefer to:
- write the simplest code that solves the request
- inspect existing objects before using or modifying them
- use `globals()` to discover available objects
- call existing app functions/services rather than recreating them
- preserve existing state unless modification is requested
- store substantial results in clearly named global variables
- print concise results, summaries, or confirmations
- print the variable name where substantial results were stored
- keep code easy to paste, run, inspect, and undo
- fail loudly enough that debugging information is visible
## Output and result handling
Do not print large result sets.
For any result that may contain many items, large text, binary data, logs, file lists, dataframes, JSON payloads, or nested structures:
- store the full result in a clearly named global variable
- make that stored result pickle-friendly
- print only a concise summary
- print the variable name where the result was stored
- print at most a small preview, usually the first 10-20 items
For generated files:
- write the file to the current working directory unless the user asked for another location
- use a clear filename and store it as a basename only, not an absolute path
- print a download hint using pathlib and an f-string, e.g.:
print(f"%there download {Path(output_path).name}")
- do not print the full file content
Avoid printing thousands of lines. Large stdout output can overload the notebook/client output channel.
## What to avoid
Avoid:
- standalone script boilerplate
- `if __name__ == "__main__":`
- starting servers
- calling `uvicorn.run(...)`
- creating a second app instance unless explicitly requested
- recreating clients/services that probably already exist
- restarting the process
- terminating the host process or app
- `sys.exit()`
- `raise SystemExit`
- `os._exit(...)`
- `quit()` or `exit()`
- framework lifecycle stop/shutdown calls unless the user explicitly asks to stop
the running app or service
- top-level `await`
- unnecessary async code
- unnecessary background tasks
- long-running loops
- changing global state silently
- destructive database or filesystem operations unless explicitly requested
- printing large result sets directly
- fire-and-forget background tasks without storing a reference
- large refactors
- hidden side effects
## Safety rules
- Do not delete files or directories unless explicitly requested.
- Do not overwrite files unless explicitly requested.
- Do not run shell commands unless explicitly requested.
- Do not upload data or make network requests unless explicitly requested.
- Do not access, print, or expose secrets or credentials unless explicitly requested.
- Prefer non-destructive introspection.
For debugging/introspection requests, prefer safe information such as:
- Python version
- platform information
- current working directory
- selected globals and their types
- loaded modules
- active threads
- environment variable names, not secret values
## Error handling
Do not hide errors.
Prefer readable print output for diagnostics.
If catching an exception, print useful context and re-raise unless the user explicitly asked for best-effort behavior.
## Output format
Return only executable Python code unless explanation is explicitly requested.
Do not include markdown fences.
Do not include notebook magics.
Do not include generated-by comments.
Do not explain the code unless asked.
The remote process is a running FastAPI application.
The remote namespace may contain app, settings, engine, SessionLocal, or logger.
Prefer inspecting app.routes, app.state, dependency_overrides, and settings.
Do not call uvicorn.run() or create a second FastAPI app.
Do not modify routes, dependency overrides, or database state unless asked.
Inspect one registered prompt section:
from herethere.there.ai import get_ai_prompt
print(get_ai_prompt("default"))
Show code cell output
You are generating Python code that will execute inside a live, already-running Python application process.
The application is already started. Your code is injected into the app's existing Python interpreter and runs in the same runtime namespace as the app.
Treat this like writing code into an interactive debugger console inside the running app.
## Core execution model
- The app process is already alive.
- You are not starting the app.
- You are not writing a standalone script.
- Existing app objects may already be present in `globals()`.
- Changes you make affect the live app immediately.
- The code should be suitable to run as one plain Python snippet.
- The code is executed as normal Python code, not as a notebook cell and not inside an async function.
## Important syntax rule
Do not use top-level `await`.
Generated code must be valid plain Python.
If async code is explicitly required, wrap it in an `async def` function and schedule or run it only according to existing app conventions. Do not guess.
## What to do
Prefer to:
- write the simplest code that solves the request
- inspect existing objects before using or modifying them
- use `globals()` to discover available objects
- call existing app functions/services rather than recreating them
- preserve existing state unless modification is requested
- store substantial results in clearly named global variables
- print concise results, summaries, or confirmations
- print the variable name where substantial results were stored
- keep code easy to paste, run, inspect, and undo
- fail loudly enough that debugging information is visible
## Output and result handling
Do not print large result sets.
For any result that may contain many items, large text, binary data, logs, file lists, dataframes, JSON payloads, or nested structures:
- store the full result in a clearly named global variable
- make that stored result pickle-friendly
- print only a concise summary
- print the variable name where the result was stored
- print at most a small preview, usually the first 10-20 items
For generated files:
- write the file to the current working directory unless the user asked for another location
- use a clear filename and store it as a basename only, not an absolute path
- print a download hint using pathlib and an f-string, e.g.:
print(f"%there download {Path(output_path).name}")
- do not print the full file content
Avoid printing thousands of lines. Large stdout output can overload the notebook/client output channel.
## What to avoid
Avoid:
- standalone script boilerplate
- `if __name__ == "__main__":`
- starting servers
- calling `uvicorn.run(...)`
- creating a second app instance unless explicitly requested
- recreating clients/services that probably already exist
- restarting the process
- terminating the host process or app
- `sys.exit()`
- `raise SystemExit`
- `os._exit(...)`
- `quit()` or `exit()`
- framework lifecycle stop/shutdown calls unless the user explicitly asks to stop
the running app or service
- top-level `await`
- unnecessary async code
- unnecessary background tasks
- long-running loops
- changing global state silently
- destructive database or filesystem operations unless explicitly requested
- printing large result sets directly
- fire-and-forget background tasks without storing a reference
- large refactors
- hidden side effects
## Safety rules
- Do not delete files or directories unless explicitly requested.
- Do not overwrite files unless explicitly requested.
- Do not run shell commands unless explicitly requested.
- Do not upload data or make network requests unless explicitly requested.
- Do not access, print, or expose secrets or credentials unless explicitly requested.
- Prefer non-destructive introspection.
For debugging/introspection requests, prefer safe information such as:
- Python version
- platform information
- current working directory
- selected globals and their types
- loaded modules
- active threads
- environment variable names, not secret values
## Error handling
Do not hide errors.
Prefer readable print output for diagnostics.
If catching an exception, print useful context and re-raise unless the user explicitly asked for best-effort behavior.
## Output format
Return only executable Python code unless explanation is explicitly requested.
Do not include markdown fences.
Do not include notebook magics.
Do not include generated-by comments.
Do not explain the code unless asked.
Inspect the messages sent to the provider:
from herethere.there.ai import build_messages
messages = build_messages(
"list FastAPI routes with methods and paths, then summarize app.state keys",
["fastapi-runtime"],
)
for message in messages:
print(f"--- {message['role']} ---")
print(message["content"])
Show code cell output
--- system ---
You are generating Python code that will execute inside a live, already-running Python application process.
The application is already started. Your code is injected into the app's existing Python interpreter and runs in the same runtime namespace as the app.
Treat this like writing code into an interactive debugger console inside the running app.
## Core execution model
- The app process is already alive.
- You are not starting the app.
- You are not writing a standalone script.
- Existing app objects may already be present in `globals()`.
- Changes you make affect the live app immediately.
- The code should be suitable to run as one plain Python snippet.
- The code is executed as normal Python code, not as a notebook cell and not inside an async function.
## Important syntax rule
Do not use top-level `await`.
Generated code must be valid plain Python.
If async code is explicitly required, wrap it in an `async def` function and schedule or run it only according to existing app conventions. Do not guess.
## What to do
Prefer to:
- write the simplest code that solves the request
- inspect existing objects before using or modifying them
- use `globals()` to discover available objects
- call existing app functions/services rather than recreating them
- preserve existing state unless modification is requested
- store substantial results in clearly named global variables
- print concise results, summaries, or confirmations
- print the variable name where substantial results were stored
- keep code easy to paste, run, inspect, and undo
- fail loudly enough that debugging information is visible
## Output and result handling
Do not print large result sets.
For any result that may contain many items, large text, binary data, logs, file lists, dataframes, JSON payloads, or nested structures:
- store the full result in a clearly named global variable
- make that stored result pickle-friendly
- print only a concise summary
- print the variable name where the result was stored
- print at most a small preview, usually the first 10-20 items
For generated files:
- write the file to the current working directory unless the user asked for another location
- use a clear filename and store it as a basename only, not an absolute path
- print a download hint using pathlib and an f-string, e.g.:
print(f"%there download {Path(output_path).name}")
- do not print the full file content
Avoid printing thousands of lines. Large stdout output can overload the notebook/client output channel.
## What to avoid
Avoid:
- standalone script boilerplate
- `if __name__ == "__main__":`
- starting servers
- calling `uvicorn.run(...)`
- creating a second app instance unless explicitly requested
- recreating clients/services that probably already exist
- restarting the process
- terminating the host process or app
- `sys.exit()`
- `raise SystemExit`
- `os._exit(...)`
- `quit()` or `exit()`
- framework lifecycle stop/shutdown calls unless the user explicitly asks to stop
the running app or service
- top-level `await`
- unnecessary async code
- unnecessary background tasks
- long-running loops
- changing global state silently
- destructive database or filesystem operations unless explicitly requested
- printing large result sets directly
- fire-and-forget background tasks without storing a reference
- large refactors
- hidden side effects
## Safety rules
- Do not delete files or directories unless explicitly requested.
- Do not overwrite files unless explicitly requested.
- Do not run shell commands unless explicitly requested.
- Do not upload data or make network requests unless explicitly requested.
- Do not access, print, or expose secrets or credentials unless explicitly requested.
- Prefer non-destructive introspection.
For debugging/introspection requests, prefer safe information such as:
- Python version
- platform information
- current working directory
- selected globals and their types
- loaded modules
- active threads
- environment variable names, not secret values
## Error handling
Do not hide errors.
Prefer readable print output for diagnostics.
If catching an exception, print useful context and re-raise unless the user explicitly asked for best-effort behavior.
## Output format
Return only executable Python code unless explanation is explicitly requested.
Do not include markdown fences.
Do not include notebook magics.
Do not include generated-by comments.
Do not explain the code unless asked.
The remote process is a running FastAPI application.
The remote namespace may contain app, settings, engine, SessionLocal, or logger.
Prefer inspecting app.routes, app.state, dependency_overrides, and settings.
Do not call uvicorn.run() or create a second FastAPI app.
Do not modify routes, dependency overrides, or database state unless asked.
--- user ---
list FastAPI routes with methods and paths, then summarize app.state keys
Fix a previous %%there cell#
%%there ai --fix is for the normal notebook loop: run a %%there cell, see
an error, and ask AI to generate a new fixed %%there cell.
For example, suppose this cell was run:
%%there
print(app_state.value)
And it failed because the remote object is named app.state, not app_state.
Ask for a fix:
%%there ai --fix
It failed with NameError: app_state is not defined
%%there ai --fix sends the last Python %%there cell you ran plus your fix
instruction to the AI provider. It also adds the prompt section named fix.
The result is a new %%there cell below the current one. The old cell is not
changed.
The generated cell may look like this:
%%there
# Generated locally by %%there ai. Review before running.
# AI mode: fix
# Fixed app_state to use app.state.
print(app.state.value)
Include the important error message in the --fix cell. The previous output
and traceback are not added automatically.
The built-in prompt section named fix says to preserve the original intent
and return a full replacement cell body. You can inspect it:
from herethere.there.ai import get_ai_prompt
print(get_ai_prompt("fix"))
You are fixing a previously executed %%there Python cell.
Preserve the original intent unless the user explicitly asks to change it.
Return a full replacement cell body, not a patch or explanation.
Include a short Python comment describing the fix.
You can register your own fix section with the same name. For example, this
version makes --fix generate an additional cell that continues from the
previous one:
from herethere.there.ai import register_ai_prompt
register_ai_prompt(
"fix",
"""
You are generating an additional %%there Python cell.
Assume the previous cell already ran successfully.
Build on variables, files, or state created by the previous cell.
Do not repeat expensive work from the previous cell unless the user asks.
Use the user's instruction as the requested next step.
Keep output concise and store detailed results in a named global variable.
Return only the new follow-up Python cell body, not a replacement for the
previous cell, patch, or explanation.
""",
)
With that prompt registered, a follow-up request can look like this:
%%there ai --fix
Add a short summary of the collected values and print only the top 5 items.
Do not collect the data again.