Add .gitignore and pyproject.toml files

This commit is contained in:
2024-11-08 09:19:13 -05:00
commit 02a56ed0b9
7 changed files with 273 additions and 0 deletions
+10
View File
@@ -0,0 +1,10 @@
# Python-generated files
__pycache__/
*.py[oc]
build/
dist/
wheels/
*.egg-info
# Virtual environments
.venv
View File
+19
View File
@@ -0,0 +1,19 @@
[project]
name = "simplechat"
version = "0.1.0"
description = "A chat interface for AI models using Simplemind."
readme = "README.md"
requires-python = ">=3.11"
dependencies = [
"appdirs",
"sqlalchemy",
"records",
"docopt",
"rich",
"prompt_toolkit",
"pydantic",
"xerox"
]
[project.scripts]
simplechat = "simplechat.cli:main"
+1
View File
@@ -0,0 +1 @@
# from .db import db
+159
View File
@@ -0,0 +1,159 @@
import docopt
import re
import xerox
import simplemind as sm
from rich.console import Console
from rich.panel import Panel
from .db import Database
from .settings import get_db_path
AVAILABLE_PROVIDERS = ["xai", "openai", "anthropic", "ollama"]
AVAILABLE_COMMANDS = ["/copy", "/paste", "/help", "/exit", "/clear", "/invoke"]
__doc__ = """Simplechat CLI
Usage:
simplechat [--provider=<provider>] [--model=<model>]
simplechat (-h | --help)
Options:
-h --help Show this screen.
--provider=<provider> LLM provider to use (openai/anthropic/xai/ollama)
--model=<model> Specific model to use (e.g. o1-preview)
"""
class Simplechat:
def __init__(self):
self.llm_model = None
self.llm_provider = None
# Prepare the database path.
self.db_path = get_db_path()
self.db_url = f"sqlite:///{self.db_path}"
# Initialize the database.
self.db = Database(self.db_url)
self.sm = sm.Session()
def __str__(self):
return f"<Simplechat db_path={self.db_path!r}>"
def __repr__(self):
return f"<Simplechat db_path={self.db_path!r}>"
def set_llm(self, llm_provider, llm_model):
self.llm_provider = llm_provider
self.llm_model = llm_model
self.sm = sm.Session(llm_provider=llm_provider, llm_model=llm_model)
def set_llm_provider(self, llm_provider):
if llm_provider not in AVAILABLE_PROVIDERS:
raise ValueError(f"Unsupported provider: {llm_provider!r}")
self.llm_provider = llm_provider
def repl(self):
"""Start an interactive REPL session."""
from prompt_toolkit import PromptSession
from prompt_toolkit.styles import Style
from rich.console import Console
from prompt_toolkit.completion import WordCompleter
command_completer = WordCompleter(
AVAILABLE_COMMANDS, pattern=re.compile(r"/\w*")
)
console = Console()
style = Style.from_dict(
{
"prompt": "#00aa00 bold",
}
)
session = PromptSession(
style=style, message=[("class:prompt", ">>> ")], completer=command_completer
)
console.print("[bold green]Welcome to Simplechat![/bold green]")
# console.print(f"Using provider: {self.llm_provider or 'default'}")
# console.print(f"Using model: {self.llm_model or 'default'}")
console.print("Type '/help' for available commands\n")
while True:
try:
# Get the user input with autocompletion
user_input = session.prompt().strip()
# Handle commands
if user_input.startswith("/"):
# Exit command.
if user_input.lower() in ("/exit", "/quit", "/q"):
break
# Help command.
elif user_input == "/help":
console.print("\nAvailable commands:")
for cmd in AVAILABLE_COMMANDS:
console.print(f" {cmd}")
console.print()
continue
# Copy to clipboard.
elif user_input == "/copy":
console.print(
"[bold green]Copying to clipboard...[/bold green]"
)
xerox.copy(user_input)
# Paste from clipboard.
elif user_input == "/paste":
console.print(
"[bold green]Pasting from clipboard...[/bold green]"
)
clipboard_content = xerox.paste()
if clipboard_content:
# Print the pasted content
console.print() # Add blank line
console.print(
Panel.fit(
clipboard_content,
title="[bold]Pasted Content[/bold]",
border_style="blue",
)
)
continue
# Send the input to the LLM and get response
response = self.sm.send(user_input)
console.print(f"\n[bold blue]Assistant:[/bold blue] {response}\n")
except KeyboardInterrupt:
exit(1)
except EOFError:
break
except Exception as e:
console.print(f"[bold red]Error:[/bold red] {str(e)}\n")
console.print("\nGoodbye!")
def main():
args = docopt.docopt(__doc__)
simplechat = Simplechat()
# Set the LLM provider and model.
simplechat.set_llm(llm_provider=args["--provider"], llm_model=args["--model"])
# Start the conversation.
simplechat.repl()
if __name__ == "__main__":
main()
+41
View File
@@ -0,0 +1,41 @@
from records import Database as RecordsDatabase
class Database:
def __init__(self, db_path="sqlite:///simplechat.db", *, migrate=True):
# Initialize the database.
self.db = RecordsDatabase(db_path)
if migrate:
# Perform migration.
self.migrate()
def migrate(self):
"""Creates the tables."""
scheme_1 = """
CREATE TABLE IF NOT EXISTS memory (
entity TEXT,
source TEXT,
last_mentioned TIMESTAMP,
mention_count INTEGER DEFAULT 1,
PRIMARY KEY (entity, source)
)
"""
scheme_2 = """
CREATE TABLE IF NOT EXISTS essence_markers (
marker_type TEXT,
marker_text TEXT,
timestamp TIMESTAMP,
PRIMARY KEY (marker_type, marker_text)
)
"""
schemes = [scheme_1, scheme_2]
for scheme in schemes:
self.query(scheme)
@property
def query(self):
return self.db.query
+43
View File
@@ -0,0 +1,43 @@
import os
import pathlib
from appdirs import AppDirs
_NAME = "simplechat"
_AUTHOR = "simplechat"
_DEFAULT_DB_NAME = "simplechat.db"
def get_app_dir():
"""Returns the AppDirs object for the application."""
return AppDirs(_NAME, _AUTHOR)
def mkdir_p(path):
"""Create a directory if it doesn't exist."""
if not os.path.exists(path):
return os.makedirs(path)
def get_config_dir():
"""Returns the full path to the configuration directory."""
appdir = get_app_dir()
config_dir = appdir.user_config_dir
# Make the directory, if it doesn't exist.
mkdir_p(config_dir)
return pathlib.Path(appdir.user_config_dir)
def get_db_path():
"""Returns the full path to the database file."""
config_dir = get_config_dir()
return config_dir / _DEFAULT_DB_NAME
if __name__ == "__main__":
print(get_db_path())