from __future__ import annotations import argparse from pathlib import Path import ollama DEFAULT_MODEL = "qwen2.5:7b" DEFAULT_FILES = [ "README.md", "Auto_Seat.ino", "upload.ps1", ] def read_context(paths: list[str]) -> str: chunks: list[str] = [] for item in paths: path = Path(item) if not path.exists() or not path.is_file(): continue text = path.read_text(encoding="utf-8", errors="replace") chunks.append(f"## {path.as_posix()}\n{text}") return "\n\n".join(chunks) def ask(model: str, context: str, question: str) -> str: system_prompt = ( "You are a local assistant for this Arduino Auto_Seat project. " "Answer in Korean by default. Use the provided project context when it is relevant. " "If the context does not contain the answer, say that clearly and reason from general knowledge." ) if context: user_prompt = f"Project context:\n{context}\n\nQuestion:\n{question}" else: user_prompt = question response = ollama.chat( model=model, messages=[ {"role": "system", "content": system_prompt}, {"role": "user", "content": user_prompt}, ], ) return response["message"]["content"] def main() -> None: parser = argparse.ArgumentParser(description="Local LLM chat for the Auto_Seat project.") parser.add_argument("--model", default=DEFAULT_MODEL, help=f"Ollama model name. Default: {DEFAULT_MODEL}") parser.add_argument( "--no-context", action="store_true", help="Chat without loading project files into the prompt.", ) args = parser.parse_args() context = "" if args.no_context else read_context(DEFAULT_FILES) print(f"Local LLM ready: {args.model}") print("Ask anything. Type /exit to quit.") while True: try: question = input("\nYou> ").strip() except (EOFError, KeyboardInterrupt): print() break if question.lower() in {"/exit", "/quit", "exit", "quit"}: break if not question: continue try: answer = ask(args.model, context, question) except Exception as exc: print(f"\nError: {exc}") continue print(f"\nLLM> {answer}") if __name__ == "__main__": main()