Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions Modelfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
FROM kimi-k2.7-code:cloud

SYSTEM """You are an expert data scientist and Python/Pandas assistant.
You help users understand and write Pandas code for real-life data analysis tasks.
When asked about code, provide clear, working examples with brief explanations.
Focus on practical, idiomatic Pandas usage."""

PARAMETER temperature 0.2
PARAMETER num_ctx 16384
188 changes: 188 additions & 0 deletions claude
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
#!/usr/bin/env python3
"""
Ollama-backed Claude-like assistant launcher.

Usage:
ollama launch claude --model kimi-k2.7-code:cloud
./claude --model kimi-k2.7-code:cloud
"""

import sys
import argparse
import json
import os
import urllib.request
import urllib.error

OLLAMA_BASE_URL = os.environ.get("OLLAMA_HOST", "http://localhost:11434")

SYSTEM_PROMPT = """You are an expert data scientist and Python/Pandas assistant.
You help users understand and write Pandas code for real-life data analysis tasks.
When asked about code, provide clear, working examples with brief explanations.
Focus on practical, idiomatic Pandas usage."""


def check_ollama_running():
try:
req = urllib.request.urlopen(f"{OLLAMA_BASE_URL}/api/tags", timeout=3)
return req.status == 200
except Exception:
return False


def list_local_models():
try:
req = urllib.request.urlopen(f"{OLLAMA_BASE_URL}/api/tags", timeout=5)
data = json.loads(req.read())
return [m["name"] for m in data.get("models", [])]
except Exception:
return []


def stream_chat(model, messages):
payload = json.dumps({
"model": model,
"messages": messages,
"stream": True,
}).encode()

req = urllib.request.Request(
f"{OLLAMA_BASE_URL}/api/chat",
data=payload,
headers={"Content-Type": "application/json"},
method="POST",
)

full_response = ""
try:
with urllib.request.urlopen(req) as resp:
for line in resp:
line = line.strip()
if not line:
continue
chunk = json.loads(line)
token = chunk.get("message", {}).get("content", "")
if token:
print(token, end="", flush=True)
full_response += token
if chunk.get("done"):
break
except urllib.error.URLError as e:
print(f"\nError communicating with Ollama: {e}", file=sys.stderr)
sys.exit(1)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Chat errors terminate whole REPL

Medium Severity

On a URLError while streaming a reply, stream_chat calls sys.exit(1) instead of returning an error to the REPL. A transient network or Ollama glitch during one turn ends the entire interactive session instead of reporting the failure and allowing another prompt.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 3d8202f. Configure here.


print()
return full_response


def pull_model(model):
print(f"Pulling model '{model}'... (this may take a while)")
payload = json.dumps({"name": model, "stream": True}).encode()
req = urllib.request.Request(
f"{OLLAMA_BASE_URL}/api/pull",
data=payload,
headers={"Content-Type": "application/json"},
method="POST",
)
try:
with urllib.request.urlopen(req) as resp:
for line in resp:
line = line.strip()
if not line:
continue
chunk = json.loads(line)
status = chunk.get("status", "")
if status:
print(f"\r{status}", end="", flush=True)
if chunk.get("status") == "success":
break
print("\nModel ready.")
except urllib.error.URLError as e:
print(f"\nFailed to pull model: {e}", file=sys.stderr)
sys.exit(1)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull failures still report ready

High Severity

pull_model always prints that the model is ready after the NDJSON stream ends, even when Ollama never sent a status of success or emitted an error object in the stream. main then starts the REPL as if the pull succeeded, so missing or mistyped models (including the default cloud model) can fail only after a misleading success message.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 3d8202f. Configure here.



def repl(model):
print(f"Claude (via Ollama / {model})")
print("Type your message and press Enter. Use Ctrl+C or 'exit' to quit.\n")

messages = [{"role": "system", "content": SYSTEM_PROMPT}]

while True:
try:
user_input = input("You: ").strip()
except (KeyboardInterrupt, EOFError):
print("\nGoodbye.")
break

if not user_input:
continue
if user_input.lower() in ("exit", "quit", "/exit", "/quit"):
print("Goodbye.")
break

messages.append({"role": "user", "content": user_input})
print("Assistant: ", end="", flush=True)
response = stream_chat(model, messages)
messages.append({"role": "assistant", "content": response})


def main():
parser = argparse.ArgumentParser(
prog="claude",
description="Launch a Claude-like assistant backed by a local Ollama model.",
)
parser.add_argument(
"--model",
default="kimi-k2.7-code:cloud",
help="Ollama model to use (default: kimi-k2.7-code:cloud)",
)
parser.add_argument(
"--list-models",
action="store_true",
help="List locally available Ollama models and exit.",
)
parser.add_argument(
"--pull",
action="store_true",
help="Pull the model before starting (download if not present).",
)
args = parser.parse_args()

if not check_ollama_running():
print(
"Ollama is not running or not installed.\n"
"Install it from https://ollama.com and run: ollama serve\n"
"Then re-run this command.",
file=sys.stderr,
)
sys.exit(1)

if args.list_models:
models = list_local_models()
if models:
print("Available models:")
for m in models:
print(f" {m}")
else:
print("No models found locally. Run with --pull to download one.")
return

local_models = list_local_models()
model = args.model

if model not in local_models:
if args.pull:
pull_model(model)
else:
print(
f"Model '{model}' is not available locally.\n"
f"Run with --pull to download it, or choose from: {local_models or ['(none)']}"
)
sys.exit(1)

repl(model)


if __name__ == "__main__":
main()