Skip to content

Commit

Permalink
1.0.4 docs and integrate metametameta
Browse files Browse the repository at this point in the history
  • Loading branch information
matthewdeanmartin committed Jan 21, 2024
1 parent 29808d0 commit 5058a73
Show file tree
Hide file tree
Showing 84 changed files with 6,326 additions and 2,974 deletions.
9 changes: 9 additions & 0 deletions .pylintrc_spell
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[SPELLING]
# Spelling dictionary name. Available dictionaries: none. To make it working
# install python-enchant package.
spelling-dict=en_US
# A path to a file that contains private dictionary; one word per line.
spelling-private-dict-file=private_dictionary.txt

[MESSAGES CONTROL]
disable=all
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).


## [1.0.4] - 2024-01-20

### Added

- Started tracking changes.

37 changes: 30 additions & 7 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -100,17 +100,40 @@ mypy:
docker:
docker build -t ai_shell -f Dockerfile .

check_docs:
interrogate ai_shell
pydoctest --config .pydoctest.json | grep -v "__init__" | grep -v "ToolKit" | grep -v "__main__" | grep -v "Unable to parse"

make_docs:
pdoc ai_shell ai_todo --html -o docs --force
#check_docs:
# interrogate ai_shell
# pydoctest --config .pydoctest.json | grep -v "__init__" | grep -v "ToolKit" | grep -v "__main__" | grep -v "Unable to parse"
#
#make_docs:
# pdoc ai_shell ai_todo --html -o docs --force

.PHONY: gen_code
gen_code:
echo "Should check mypy and docstrings first."
cd ai_shell && cd code_generate && python generate_schema.py
cd ai_shell && cd code_generate && python generate_cli.py
cd ai_shell && cd code_generate && python generate_toolkit.py
pwd
pwd

check_docs:
$(VENV) interrogate ai_shell --verbose
$(VENV) pydoctest --config .pydoctest.json | grep -v "__init__" | grep -v "__main__" | grep -v "Unable to parse" | grep -v "openai_toolkit"

make_docs:
pdoc ai_shell --html -o docs --force

check_md:
$(VENV) mdformat README.md docs/*.md
# $(VENV) linkcheckMarkdown README.md # it is attempting to validate ssl certs
$(VENV) markdownlint README.md --config .markdownlintrc

check_spelling:
$(VENV) pylint ai_shell --enable C0402 --rcfile=.pylintrc_spell
$(VENV) codespell README.md --ignore-words=private_dictionary.txt
$(VENV) codespell ai_shell --ignore-words=private_dictionary.txt

check_changelog:
# pipx install keepachangelog-manager
$(VENV) changelogmanager validate

check_all: check_docs check_md check_spelling check_changelog
29 changes: 29 additions & 0 deletions ai_shell/__about__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
"""Metadata for ai_shell."""

__all__ = [
"__title__",
"__version__",
"__description__",
"__author__",
"__author_email__",
"__keywords__",
"__status__",
"__license__",
"__readme__",
"__repository__",
"__homepage__",
"__documentation__",
]

__title__ = "ai_shell"
__version__ = "1.0.4"
__description__ = "Filesystem Shell interface that an OpenAI Assitant can use as a tool."
__author__ = "Matthew Martin"
__author_email__ = "[email protected]"
__keywords__ = ["openai", "chatgpt"]
__status__ = "3 - Alpha"
__license__ = "MIT"
__readme__ = "README.md"
__repository__ = "https://github.com/matthewdeanmartin/ai_shell"
__homepage__ = "https://github.com/matthewdeanmartin/ai_shell"
__documentation__ = "https://github.com/matthewdeanmartin/ai_shell"
15 changes: 15 additions & 0 deletions ai_shell/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
"""
Filesystem shell tools for OpenAI Assistant
.. include:: ../README.md
.. include:: ../docs/DocumentEditing.md
.. include:: ../docs/Features.md
.. include:: ../docs/Manual.md
.. include:: ../docs/UseCases.md
.. include:: ../docs/Design.md
.. include:: ../CHANGELOG.md
"""
from ai_shell.ai_logs.log_to_markdown import DialogLoggerWithMarkdown
from ai_shell.ai_logs.logging_utils import configure_logging
Expand Down
23 changes: 22 additions & 1 deletion ai_shell/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"""
import argparse

from ai_shell.__about__ import __description__, __version__
from ai_shell.answer_tool import AnswerCollectorTool
from ai_shell.cat_tool import CatTool
from ai_shell.cut_tool import CutTool
Expand All @@ -22,6 +23,8 @@
from ai_shell.utils.console_utils import pretty_console

CONFIG = Config()


# pylint: disable=unused-argument


Expand Down Expand Up @@ -592,7 +595,25 @@ def pytest_command(args):

def run():
"""Create the main parser"""
parser = argparse.ArgumentParser(prog="ais", description="AI Shell Command Line Interface")
program = "ais"
parser = argparse.ArgumentParser(
prog=program,
allow_abbrev=False,
description=__description__,
epilog="""
Examples:
ais cat hello.py
""",
formatter_class=argparse.RawDescriptionHelpFormatter,
)
parser.add_argument(
"-V",
"--version",
action="version",
version=f"%(prog)s {__version__}",
help="Show program's version number and exit.",
)
subparsers = parser.add_subparsers(dest="subcommand", help="sub-command help")
# Create a parser for the "head" command
head_parser = subparsers.add_parser("head", help="""Return the first \'lines\' or \'byte_count\' from a file..""")
Expand Down
45 changes: 29 additions & 16 deletions ai_shell/ai_logs/log_to_markdown.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,10 @@ def __init__(self, base_directory: str) -> None:
self.log_file_path = os.path.join(self.base_directory, f"dialog_{log_number}.md")
os.makedirs(os.path.dirname(self.log_file_path), exist_ok=True)

# Context manger handles this, I think.
# Context manager handles this, I think.
# pylint: disable=consider-using-with
self.log_file = open(self.log_file_path, "a", buffering=1, encoding="utf-8")
self.pending_tools: dict[str, dict[str, str]] = {}

def write_header(self, bot_name: str, model: str, bot_instructions: str) -> None:
"""
Expand Down Expand Up @@ -131,27 +132,31 @@ def add_toolkit(self, tools: list[str]) -> None:
self.log_file.write(f"{toolkit_message}\n\n")
logger.info(toolkit_message.replace("\n", ""))

def add_tool(self, tool_name: str, tool_args: str) -> None:
def add_tool(self, tool_call_id: str, tool_name: str, tool_args: str) -> None:
"""
Logs a single tool and its arguments used in the dialog.
Args:
tool_call_id (str): The unique identifier for the tool call.
tool_name (str): The name of the tool.
tool_args (str): The arguments passed to the tool, in JSON string format.
"""
bot_wants = f"Bot wants to use **Tool**: `{tool_name}`, **Args**: {tool_args}"
self.log_file.write(f"{bot_wants}\n\n")
bot_wants = f"Bot wants to use `{tool_name}`\n"
self.log_file.write(bot_wants)
logger.info(bot_wants)
try:
json_bits = json.loads(tool_args)
except BaseException:
self.log_file.write(f"Bad JSON: {tool_args}")
self.log_file.write(f"Bot gave us Bad JSON: {tool_args}")
self.pending_tools[tool_call_id] = {"tool_name": tool_name, "tool_args": tool_args}
return
args_lines: list[str] = []
for name, value in json_bits.items():
if value is not None:
pair = f"{name} : {value}"
self.log_file.write(f"{pair}\n")
logger.info(pair)
args_lines.append(f" - {pair}\n")
# logger.info(pair)
self.pending_tools[tool_call_id] = {"tool_name": tool_name, "tool_args": "\n".join(args_lines)}

def add_tool_result(self, tool_results: list[dict[str, Any]]) -> None:
"""
Expand All @@ -160,17 +165,25 @@ def add_tool_result(self, tool_results: list[dict[str, Any]]) -> None:
Args:
tool_results (List[Dict[str, Any]]): A list of dictionaries containing the tool results.
"""
# result should always be of the same dict type
# tool_result = {"tool_call_id": tool_call.id, "output": json_result}
for result in tool_results:
self.log_file.write("### Result\n\n")
if isinstance(result, dict):
for key, value in result.items():
if key == "output":
# json.loads here should work, it isn't bot-json
self.log_file.write(f" {try_markpickle(json.loads(value))}\n")
else:
self.log_file.write(f"{key}: {value}\n")
self.log_file.write(f"Tool call Id: {result['tool_call_id']}\n")
self.log_file.write(f"Tool name: {self.pending_tools[result['tool_call_id']]['tool_name']}\n")
self.log_file.write(f"Tool args:\n {self.pending_tools[result['tool_call_id']]['tool_args']}\n")
del self.pending_tools["tool_call_id"]
json_string = result["output"]
# json.loads here should work, it isn't bot-json
any_type = json.loads(json_string)
if isinstance(any_type, dict):
if "type" in any_type and "title" in any_type and "status" in any_type and "detail" in any_type:
self.log_file.write(f"ERROR: {any_type['type']} : {any_type['detail']}\n")
else:
for key, value in any_type.items():
self.log_file.write(f" - {key} : {value}\n")
else:
self.log_file.write(f"{result}\n")
self.log_file.write(f"{any_type}\n")

def add_error(self, error: Exception) -> None:
"""
Expand Down Expand Up @@ -215,7 +228,7 @@ def run() -> None:
dialog_logger_md.add_user("Hello, bot!")
dialog_logger_md.add_bot("Hello, user!")
dialog_logger_md.add_toolkit(["python", "dalle"])
dialog_logger_md.add_tool("python", "calculate something")
dialog_logger_md.add_tool("xyzzy", "python", "calculate something")
# In case of an error
# dialog_logger_md.add_error("Some error occurred")

Expand Down
33 changes: 19 additions & 14 deletions ai_shell/ai_logs/logging_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
Code-as-config for logging.
"""
import os

import logging
from typing import Any

import bug_trail_core
Expand Down Expand Up @@ -33,13 +31,13 @@ def configure_logging() -> dict[str, Any]:
"class": "logging.StreamHandler",
"stream": "ext://sys.stdout", # Default is stderr
},
"bug_trail": {
"level": "DEBUG",
# "formatter": "standard",
"class": "bug_trail_core.BugTrailHandler",
"db_path": bug_trail_config.database_path,
"minimum_level": logging.DEBUG,
},
# "bug_trail": {
# "level": "DEBUG",
# # "formatter": "standard",
# "class": "bug_trail_core.BugTrailHandler",
# "db_path": bug_trail_config.database_path,
# "minimum_level": logging.DEBUG,
# },
# "json": {
# # "()": "json_file_handler_factory",
# "level": "DEBUG",
Expand Down Expand Up @@ -73,24 +71,31 @@ def configure_logging() -> dict[str, Any]:

for name in debug_level_modules:
logging_config["loggers"][name] = {
"handlers": ["default", "bug_trail"],
"handlers": [
"default",
# "bug_trail"
],
"level": "DEBUG",
"propagate": False,
}

for name in info_level_modules:
logging_config["loggers"][name] = {
"handlers": ["default", "bug_trail"],
"handlers": [
"default",
# "bug_trail"
],
"level": "INFO",
"propagate": False,
}

for name in warn_level_modules:
logging_config["loggers"][name] = {
"handlers": ["default", "bug_trail"],
"handlers": [
"default",
# "bug_trail"
],
"level": "WARNING",
"propagate": False,
}
return logging_config

print()
2 changes: 1 addition & 1 deletion ai_shell/bot_glue/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ async def basic_tool_loop(
done = "NOPE"
tools_used_this_round = -1

# TODO: intialize this in constructor
# TODO: initialize this in constructor
if (
self.allow_self_certification
and hasattr(self.toolkit, "tool_answer_collector")
Expand Down
2 changes: 1 addition & 1 deletion ai_shell/bot_glue/tool_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ async def loop_tools(
raise TypeError("Missing required_action")
for tool_call in run.required_action.submit_tool_outputs.tool_calls:
tool_use_count += 1
dialog_logger_md.add_tool(tool_call.function.name, tool_call.function.arguments)
dialog_logger_md.add_tool(tool_call.id, tool_call.function.name, tool_call.function.arguments)
results = await kit.process_tool_calls(run, print)
dialog_logger_md.add_tool_result(results)
# submit results
Expand Down
23 changes: 22 additions & 1 deletion ai_shell/code_generate/generate_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ def generate_the_cli(target_file: str) -> None:
import argparse
from ai_shell.utils.console_utils import pretty_console
from ai_shell.utils.config_manager import Config
from ai_shell.__about__ import __version__, __description__
CONFIG = Config()
# pylint: disable=unused-argument
Expand All @@ -169,7 +170,27 @@ def generate_the_cli(target_file: str) -> None:

argparse_part = """\n\ndef run():
\"\"\"Create the main parser\"\"\"
parser = argparse.ArgumentParser(prog='ais', description='AI Shell Command Line Interface')
program = 'ais'
parser = argparse.ArgumentParser(
prog=program,
allow_abbrev=False,
description=__description__,
epilog=f\"\"\"
Examples:
ais cat hello.py
\"\"\",
formatter_class=argparse.RawDescriptionHelpFormatter,
)
parser.add_argument(
"-V",
"--version",
action="version",
version=f"%(prog)s {__version__}",
help="Show program's version number and exit.",
)
)
subparsers = parser.add_subparsers(dest='subcommand', help='sub-command help')
"""
for ns, _data in meta.items():
Expand Down
Loading

0 comments on commit 5058a73

Please sign in to comment.