Loading the notebook…
Loading the notebook…
Notebooks · Workspace, live UI and observability · 73
Everything from the CLI round, exercised offline:
Its imports and arguments are checked against shipit-agent 3.0. The outputs were saved when it was written, for an earlier release.
from pathlib import Path
import sys
ROOT = Path.cwd().resolve().parent if Path.cwd().name == "notebooks" else Path.cwd().resolve()
if str(ROOT) not in sys.path:
sys.path.insert(0, str(ROOT))
print("setup OK")setup OK
from shipit_agent.cli import main
print("── shipit models ──")
main(["models"])
print()
print("── shipit run (echo provider, JSON) ──")
main(["run", "hello from the notebook", "--provider", "echo", "--json"])── shipit models ──
anthropic
claude-opus-5 most capable Claude for complex agents (best)
→ claude-sonnet-5 flagship balance of speed and intelligence
claude-haiku-4-5-20251001 fastest/cheapest Claude
openai
gpt-5.6 newest flagship family
→ gpt-5.5 current production API recommendation (best)
gpt-5.5-2026-04-23 pinned snapshot of gpt-5.5
bedrock
→ google.gemma-4-31b best open-weight agentic model (best)
google.gemma-4-26b-a4b fast MoE Gemma (simple tool args only)
bedrock/openai.gpt-oss-120b-1:0 reasoning/coding heavyweight
bedrock/anthropic.claude-sonnet-5-v1:0 Claude Sonnet 5 on Bedrock
bedrock/anthropic.claude-haiku-4-5-v1:0 Claude Haiku on Bedrock
ollama
→ ollama/llama3.1 local default
ollama/qwen3.5 strong local tool-caller
→ = provider default · use --provider X --model Y, or set $SHIPIT_LLM_PROVIDER / $SHIPIT_MODEL
── shipit run (echo provider, JSON) ──
{
"output": "You are Shipit, a capable general-purpose agent runtime.\n\nCore behavior:\n- Be accurate, direct, and execution-oriented.\n- Solve the user's task end-to-end when possible instead of stopping at analysis.\n- Use tools when they materially improve correctness, freshness, or efficiency.\n- Prefer structured evidence over guesses.\n\nTool behavior:\n- Read tool descriptions and tool prompts carefully before calling them.\n- Use the smallest correct tool for the job.\n- When a task is complex, plan before acting.\n- When information may be outdated, prefer web and external tools over… (76,800 more characters)
"summary": {
"duration_seconds": 0.006,
"iterations": 1,
"tool_calls": 0,
"tool_failures": 0,
"usage": {},
"tools": {}
}
}0
git_ops — structured git, no shellimport subprocess, tempfile
from shipit_agent.tools import GitOpsTool
from shipit_agent.tools.base import ToolContext
repo = Path(tempfile.mkdtemp(prefix="shipit_git_"))
for argv in (["init","-q"], ["config","user.email","[email]"],
["config","user.name","Notebook"]):
subprocess.run(["git", *argv], cwd=repo, check=True, capture_output=True)
(repo / "app.py").write_text("def greet():\n return 'hello'\n")
git = GitOpsTool(root_dir=repo)
ctx = ToolContext(prompt="", system_prompt="", state={})
git.run(ctx, action="add")
print(git.run(ctx, action="commit", message="feat: greet").text.splitlines()[0])
(repo / "app.py").write_text("def greet():\n return 'hello world'\n")
print(git.run(ctx, action="diff").text[:300])
print()
print("push gated:", git.run(ctx, action="push").text[:60])git commit (ok): git diff (ok): app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.py b/app.py index abf5389..28b7d43 100644 --- a/app.py +++ b/app.py @@ -1,2 +1,2 @@ def greet(): - return 'hello' + return 'hello world' push gated: git push is disabled on this tool. Construct GitOpsTool(allo
notebook_edit — cells, structurallyimport json as _json
from shipit_agent.tools import NotebookEditTool
nb_path = repo / "analysis.ipynb"
nb_path.write_text(_json.dumps({"cells":[
{"cell_type":"markdown","metadata":{},"source":"# Analysis"},
{"cell_type":"code","metadata":{},"execution_count":1,
"source":"x = 1","outputs":[]}],
"metadata":{},"nbformat":4,"nbformat_minor":5}))
nbt = NotebookEditTool(root_dir=repo)
print(nbt.run(ctx, path="analysis.ipynb", action="list").text)
nbt.run(ctx, path="analysis.ipynb", action="edit", index=1, source="x = 42")
nbt.run(ctx, path="analysis.ipynb", action="add", source="print(x)")
print()
print(nbt.run(ctx, path="analysis.ipynb", action="list").text)analysis.ipynb — 2 cells 0 markdown [ ] # Analysis 1 code [1] x = 1 analysis.ipynb — 3 cells 0 markdown [ ] # Analysis 1 code [1] x = 42 2 code [ ] print(x)
A Gemma-style model emits the call as TEXT — the runtime promotes it, executes the tool, and the run succeeds anyway.
from shipit_agent import Agent, FunctionTool, format_activity
from shipit_agent.llms.base import LLMResponse
class TextCallLLM:
def __init__(self): self.turn = 0
def complete(self, *, messages, tools=None, **_):
self.turn += 1
if self.turn == 1:
return LLMResponse(content=(
'I will add them.\n<tool_call>{"name": "add", '
'"arguments": {"a": 2, "b": 3}}</tool_call>'))
return LLMResponse(content="The sum is 5.")
def add(a: int, b: int, **_): return str(a + b)
agent = Agent(llm=TextCallLLM(),
tools=[FunctionTool.from_callable(add, name="add")],
auto_use_skills=False)
result = agent.run("2+3?")
print("healed:", any(e.type == "tool_call_healed" for e in result.events))
print(format_activity(result))healed: True ⚙ add(a=2, b=3) ✓ 0ms └ 5 ✔ run completed · 1 tool call · 2 iterations
Turn 1 narrates intent with no call → one tightly-capped nudge → turn 2 actually calls the tool.
from shipit_agent.llms.base import ToolCall
class StallingLLM:
def __init__(self): self.turn = 0
def complete(self, *, messages, tools=None, **_):
self.turn += 1
if self.turn == 1:
return LLMResponse(content="Let me use the add tool for this.")
if self.turn == 2:
return LLMResponse(tool_calls=[ToolCall(name="add", arguments={"a":2,"b":3})])
return LLMResponse(content="The sum is 5.")
agent = Agent(llm=StallingLLM(),
tools=[FunctionTool.from_callable(add, name="add")],
auto_use_skills=False, max_iterations=6)
result = agent.run("2+3?")
nudges = [e for e in result.events if e.type == "tool_call_healed" and e.payload.get("nudge")]
print(f"nudged {len(nudges)}× → final: {result.output}")nudged 1× → final: The sum is 5.
AgentServer — your agent behind an OpenAI-compatible APIimport json as _json, urllib.request
from shipit_agent.serve import AgentServer
class EchoLLM:
def complete(self, *, messages, **_):
last = [m for m in messages if (m.get("role") if isinstance(m, dict) else m.role) == "user"][-1]
text = last.get("content") if isinstance(last, dict) else last.content
return LLMResponse(content=f"agent says: {text}")
srv = AgentServer(Agent(llm=EchoLLM(), auto_use_skills=False),
model_name="shipit-nb", api_key="demo")
port = srv.start(port=0)
req = urllib.request.Request(
f"http://127.0.0.1:{port}/v1/chat/completions",
data=_json.dumps({"messages":[{"role":"user","content":"ping"}]}).encode(),
headers={"Content-Type":"application/json","Authorization":"Bearer demo"})
with urllib.request.urlopen(req) as resp:
print(_json.loads(resp.read())["choices"][0]["message"]["content"])
srv.stop()
print("(any OpenAI SDK can do the same — base_url =", f"http://127.0.0.1:{port}/v1)")agent says: ping
(any OpenAI SDK can do the same — base_url = http://127.0.0.1:58095/v1)
CLI catalogs, structured git, notebook editing, healing, nudging, and the
agent-as-API — the full CLI power layer, all verified offline.
See also: shipit --help · notebooks 71–72 · tests/test_agent_server_cli.py