Skip to content

Commit

Permalink
feat: migrate to micro-step control management
Browse files Browse the repository at this point in the history
  • Loading branch information
ivangabriele committed Nov 27, 2023
1 parent 7ec31a2 commit b99638a
Show file tree
Hide file tree
Showing 44 changed files with 1,201 additions and 477 deletions.
70 changes: 44 additions & 26 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,40 +23,52 @@ jobs:
- name: Install dependencies
run: poetry install

- name: Setup Lint
run: cp ./env.sample.jsonc ./env.jsonc
- name: Setup Envs (OpenAI)
run: cp ./env.openai-sample.jsonc ./env.jsonc

- name: Lint
run: make lint

# type:
# name: Type
# runs-on: ubuntu-latest
# steps:
# - name: Checkout code
# uses: actions/checkout@v4
- name: Setup Envs (OpenSource)
run: cp ./env.opensource-sample.jsonc ./env.jsonc

# - name: Setup Python
# uses: actions/setup-python@v4
# with:
# python-version: "3.10"
- name: Lint
run: make lint

type:
name: Type
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: "3.10"

# - name: Install Poetry
# uses: snok/install-poetry@v1
# with:
# version: "1.6.1"
- name: Install Poetry
uses: snok/install-poetry@v1
with:
version: "1.6.1"

- name: Install mypy
run: pip install mypy

- name: Install dependencies
run: poetry install

# - name: Install mypy
# run: pip install mypy
- name: Setup Envs (OpenAI)
run: cp ./env.openai-sample.jsonc ./env.jsonc

# - name: Install dependencies
# run: poetry install
- name: Check Types
run: make type

# - name: Setup Types
# run: cp ./env.sample.jsonc ./env.jsonc
- name: Setup Envs (OpenSource)
run: cp ./env.opensource-sample.jsonc ./env.jsonc

# - name: Check Types
# run: make type
- name: Check Types
run: make type

test:
name: Unit Test
Expand All @@ -78,8 +90,14 @@ jobs:
- name: Install dependencies
run: poetry install

- name: Setup Unit Tests
run: cp ./env.sample.jsonc ./env.jsonc
- name: Setup Envs (OpenAI)
run: cp ./env.openai-sample.jsonc ./env.jsonc

- name: Run Unit Tests
run: make test

- name: Setup Envs (OpenSource)
run: cp ./env.opensource-sample.jsonc ./env.jsonc

- name: Run Unit Tests
run: make test
4 changes: 2 additions & 2 deletions .pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ max-line-length=120
ignore-paths=.cache,__pycache__

[MESSAGES CONTROL]
; TODO Re-enable these checks before releasing the first version.
disable=C0301,C0114,C0115,C0116
; TODO Re-enable some of these checks before releasing the first version.
disable=C0301,C0114,C0115,C0116,R0801,R0902,R0903,W0511,W0718
5 changes: 4 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
.PHONY: clean lint install run test type

clean:
find ./project -mindepth 1 -type f,d ! -name '.gitkeep' -exec rm -Rf {} +
find ./project -maxdepth 1 -mindepth 1 -type f,d ! -name '.gitkeep' -exec rm -fr {} +

install:
poetry install

lint:
poetry run pylint $(shell find . -name '*.py')

ran:
poetry run python ./niam.py

run:
poetry run python ./main.py

Expand Down
5 changes: 3 additions & 2 deletions actions/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from .fetch_web_page import fetch_web_page
from .read_file import read_file
from .read_file import ReadFileAction
from .run_bash_command import run_bash_command
from .run_rust_file import run_rust_file
from .search_web import search_web
from .search_web_types import WebSearchApiResponse
from .write_file import write_file
from .update_tasks import UpdateTasksAction
from .write_file import WriteFileAction
25 changes: 25 additions & 0 deletions actions/base_action.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from typing import Any, Dict, List, TypedDict


class ActionDesciptionParametersDict(TypedDict):
type: "object"
properties: Dict[str, Any]
required: List[str]


class ActionDesciptionDict(TypedDict):
name: str
description: str
parameters: ActionDesciptionParametersDict


class BaseAction:
_description: ActionDesciptionDict

@classmethod
def get_description(cls):
return cls._description

@staticmethod
def run(*args, **kwargs):
pass
9 changes: 6 additions & 3 deletions actions/fetch_web_page.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ def fetch_web_page(url: str) -> str:
body_tag = soup.body if soup.body else soup
for tag in body_tag.find_all(True):
if tag.name != "a":
if tag.name == "script" or tag.name == "style":
if tag.name in ("script", "style"):
tag.extract()
else:
tag.unwrap()
if soup.html:
soup.html.unwrap()

markdown_lines = _extract_content(body_tag)
markdown_lines = _extract_content(tag=body_tag, markdown_lines=None)

markdown_source = " ".join(markdown_lines)
markdown_source = re.sub(r"\s+", " ", markdown_source)
Expand All @@ -42,7 +42,10 @@ def fetch_web_page(url: str) -> str:
return f"An error occurred: {e}"


def _extract_content(tag: Tag, markdown_lines: List[str] = []) -> List[str]:
def _extract_content(tag: Tag, markdown_lines: List[str] | None) -> List[str]:
if markdown_lines is None:
markdown_lines = []

for child in tag.children:
if isinstance(child, NavigableString):
if child.strip(): # Only non-empty strings
Expand Down
36 changes: 29 additions & 7 deletions actions/read_file.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,34 @@
import os

from actions.base_action import BaseAction

from constants import PROJECT_DIRECTORY_PATH


def read_file(relative_path: str) -> str:
"""
Read content from a file and return it.
"""
full_path = os.path.join(PROJECT_DIRECTORY_PATH, relative_path)
with open(full_path, "r") as file:
return file.read()
class ReadFileAction(BaseAction):
_description = {
"name": "read_file",
"description": "Read and return a file content.",
"parameters": {
"type": "object",
"properties": {
"relative_path": {
"type": "string",
"description": "Relative path of the file. Path is relative to the project directory.",
},
},
"required": ["relative_path"],
},
}

# pylint: disable=arguments-differ
@staticmethod
def read_file(relative_path: str) -> str:
"""
Read content from a file and return it.
"""

full_path = os.path.join(PROJECT_DIRECTORY_PATH, relative_path)

with open(file=full_path, mode="r", encoding="utf-8") as file:
return file.read()
12 changes: 7 additions & 5 deletions actions/search_web.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import json
from typing import Union
from dacite import from_dict
import json
import requests

from actions.search_web_types import WebSearchApiResponse

from constants import PROJECT_CONFIG
from constants import DEFAULT_REQUEST_TIMEOUT, PROJECT_CONFIG


def search_web(query: str) -> str:
Expand All @@ -14,8 +14,8 @@ def search_web(query: str) -> str:
# If it's an `str`, that means it's an error
if isinstance(brave_search_api_result_or_error, str):
return brave_search_api_result_or_error
else:
brave_search_api_result = brave_search_api_result_or_error

brave_search_api_result = brave_search_api_result_or_error

# Simplify the data
simplified_response_data = {
Expand Down Expand Up @@ -62,7 +62,9 @@ def _fetch_brave_search_api(query: str) -> Union[WebSearchApiResponse, str]:
"extra_snippets": "true",
}

response = requests.get(endpoint, headers=headers, params=params)
response = requests.get(
endpoint, headers=headers, params=params, timeout=DEFAULT_REQUEST_TIMEOUT
)

if response.status_code != 200:
return f"Error: {response.status_code} - {response.reason}"
Expand Down
53 changes: 53 additions & 0 deletions actions/update_tasks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import json
from typing import List

from actions.base_action import BaseAction
from actions.write_file import WriteFileAction
import typedefs


class UpdateTasksAction(BaseAction):
_description = {
"name": "update_tasks",
"description": "Update the entire task list file by replacing it with the new tasks.",
"parameters": {
"type": "object",
"properties": {
"file_name": {
"type": "string",
"description": "Tasks file name.",
},
"tasks_as_strs": {
"type": "array",
"description": "List of tasks.",
"items": {
"type": "string",
},
},
},
"required": ["updated_tasks"],
},
}

# pylint: disable=arguments-differ
@staticmethod
def run(file_name: str, tasks_as_strs: List[str]) -> str:
"""
Write content to a file. Create the file and/or directories if they don't exist.
"""

tasks = [
(index, UpdateTasksAction.get_task_from_task_as_str)
for index, item in enumerate(tasks_as_strs)
]
tasks_as_json = json.dumps(tasks)

return WriteFileAction.run(relative_path=file_name, file_source=tasks_as_json)

@staticmethod
def get_task_from_task_as_str(tasks_as_str: str, index: int) -> typedefs.TaskDict:
return {
"index": index,
"description": tasks_as_str,
"is_done": False,
}
48 changes: 36 additions & 12 deletions actions/write_file.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,43 @@
import os
from actions.base_action import BaseAction

from constants import PROJECT_DIRECTORY_PATH


def write_file(relative_path: str, file_source: str) -> str:
"""
Write content to a file. Create the file and/or directories if they don't exist.
"""
full_path = os.path.join(PROJECT_DIRECTORY_PATH, relative_path)
class WriteFileAction(BaseAction):
_description = {
"name": "write_file",
"description": "Write content to a file, creating it if necessary.",
"parameters": {
"type": "object",
"properties": {
"relative_path": {
"type": "string",
"description": "Relative path of the file. Path is relative to the project directory.",
},
"file_source": {
"type": "string",
"description": """Content to write.""",
},
},
"required": ["relative_path", "file_source"],
},
}

try:
os.makedirs(os.path.dirname(full_path), exist_ok=True)
with open(full_path, "w") as file:
file.write(file_source)
# pylint: disable=arguments-differ
@staticmethod
def run(relative_path: str, file_source: str) -> str:
"""
Write content to a file. Create the file and/or directories if they don't exist.
"""
full_path = os.path.join(PROJECT_DIRECTORY_PATH, relative_path)

return f"Done."
except Exception as e:
return f"Error: {str(e)}"
try:
os.makedirs(os.path.dirname(full_path), exist_ok=True)
with open(file=full_path, mode="w", encoding="utf-8") as file:
file.write(file_source)

return f"File successfully written to `{relative_path}`."

except Exception as e:
return f"Error: {str(e)}"
3 changes: 2 additions & 1 deletion agents/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from .base_agent import BaseAgent
from .ceo import CEO
from .functionner import Functioneer
from .planner import Planner
from .product_owner import ProductOwner
from .software_engineer import SoftwareEngineer
from .user_experience_designer import UserExperienceDesigner
Loading

0 comments on commit b99638a

Please sign in to comment.