-
Notifications
You must be signed in to change notification settings - Fork 0
Add Ollama-backed Claude launcher with kimi-k2.7-code:cloud model #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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 |
| 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) | ||
|
|
||
| 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) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Pull failures still report readyHigh Severity
Additional Locations (1)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() | ||


There was a problem hiding this comment.
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
URLErrorwhile streaming a reply,stream_chatcallssys.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)
claude#L123-L127Reviewed by Cursor Bugbot for commit 3d8202f. Configure here.