Skip to content
This repository was archived by the owner on Apr 14, 2025. It is now read-only.

Commit 52a7a9c

Browse files
committed
feat: migrate to micro-step control management [WIP]
1 parent 7ec31a2 commit 52a7a9c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+1201
-477
lines changed

.github/workflows/check.yml

Lines changed: 44 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -23,40 +23,52 @@ jobs:
2323
- name: Install dependencies
2424
run: poetry install
2525

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

2929
- name: Lint
3030
run: make lint
3131

32-
# type:
33-
# name: Type
34-
# runs-on: ubuntu-latest
35-
# steps:
36-
# - name: Checkout code
37-
# uses: actions/checkout@v4
32+
- name: Setup Envs (OpenSource)
33+
run: cp ./env.opensource-sample.jsonc ./env.jsonc
3834

39-
# - name: Setup Python
40-
# uses: actions/setup-python@v4
41-
# with:
42-
# python-version: "3.10"
35+
- name: Lint
36+
run: make lint
37+
38+
type:
39+
name: Type
40+
runs-on: ubuntu-latest
41+
steps:
42+
- name: Checkout code
43+
uses: actions/checkout@v4
44+
45+
- name: Setup Python
46+
uses: actions/setup-python@v4
47+
with:
48+
python-version: "3.10"
4349

44-
# - name: Install Poetry
45-
# uses: snok/install-poetry@v1
46-
# with:
47-
# version: "1.6.1"
50+
- name: Install Poetry
51+
uses: snok/install-poetry@v1
52+
with:
53+
version: "1.6.1"
54+
55+
- name: Install mypy
56+
run: pip install mypy
57+
58+
- name: Install dependencies
59+
run: poetry install
4860

49-
# - name: Install mypy
50-
# run: pip install mypy
61+
- name: Setup Envs (OpenAI)
62+
run: cp ./env.openai-sample.jsonc ./env.jsonc
5163

52-
# - name: Install dependencies
53-
# run: poetry install
64+
- name: Check Types
65+
run: make type
5466

55-
# - name: Setup Types
56-
# run: cp ./env.sample.jsonc ./env.jsonc
67+
- name: Setup Envs (OpenSource)
68+
run: cp ./env.opensource-sample.jsonc ./env.jsonc
5769

58-
# - name: Check Types
59-
# run: make type
70+
- name: Check Types
71+
run: make type
6072

6173
test:
6274
name: Unit Test
@@ -78,8 +90,14 @@ jobs:
7890
- name: Install dependencies
7991
run: poetry install
8092

81-
- name: Setup Unit Tests
82-
run: cp ./env.sample.jsonc ./env.jsonc
93+
- name: Setup Envs (OpenAI)
94+
run: cp ./env.openai-sample.jsonc ./env.jsonc
95+
96+
- name: Run Unit Tests
97+
run: make test
98+
99+
- name: Setup Envs (OpenSource)
100+
run: cp ./env.opensource-sample.jsonc ./env.jsonc
83101

84102
- name: Run Unit Tests
85103
run: make test

.pylintrc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@ max-line-length=120
55
ignore-paths=.cache,__pycache__
66

77
[MESSAGES CONTROL]
8-
; TODO Re-enable these checks before releasing the first version.
9-
disable=C0301,C0114,C0115,C0116
8+
; TODO Re-enable some of these checks before releasing the first version.
9+
disable=C0301,C0114,C0115,C0116,R0801,R0902,R0903,W0511,W0718

Makefile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
.PHONY: clean lint install run test type
22

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

66
install:
77
poetry install
88

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

12+
ran:
13+
poetry run python ./niam.py
14+
1215
run:
1316
poetry run python ./main.py
1417

actions/__init__.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
from .fetch_web_page import fetch_web_page
2-
from .read_file import read_file
2+
from .read_file import ReadFileAction
33
from .run_bash_command import run_bash_command
44
from .run_rust_file import run_rust_file
55
from .search_web import search_web
66
from .search_web_types import WebSearchApiResponse
7-
from .write_file import write_file
7+
from .update_tasks import UpdateTasksAction
8+
from .write_file import WriteFileAction

actions/base_action.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
from typing import Any, Dict, List, TypedDict
2+
3+
4+
class ActionDesciptionParametersDict(TypedDict):
5+
type: "object"
6+
properties: Dict[str, Any]
7+
required: List[str]
8+
9+
10+
class ActionDesciptionDict(TypedDict):
11+
name: str
12+
description: str
13+
parameters: ActionDesciptionParametersDict
14+
15+
16+
class BaseAction:
17+
_description: ActionDesciptionDict
18+
19+
@classmethod
20+
def get_description(cls):
21+
return cls._description
22+
23+
@staticmethod
24+
def run(*args, **kwargs):
25+
pass

actions/fetch_web_page.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,14 @@ def fetch_web_page(url: str) -> str:
1818
body_tag = soup.body if soup.body else soup
1919
for tag in body_tag.find_all(True):
2020
if tag.name != "a":
21-
if tag.name == "script" or tag.name == "style":
21+
if tag.name in ("script", "style"):
2222
tag.extract()
2323
else:
2424
tag.unwrap()
2525
if soup.html:
2626
soup.html.unwrap()
2727

28-
markdown_lines = _extract_content(body_tag)
28+
markdown_lines = _extract_content(tag=body_tag, markdown_lines=None)
2929

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

4444

45-
def _extract_content(tag: Tag, markdown_lines: List[str] = []) -> List[str]:
45+
def _extract_content(tag: Tag, markdown_lines: List[str] | None) -> List[str]:
46+
if markdown_lines is None:
47+
markdown_lines = []
48+
4649
for child in tag.children:
4750
if isinstance(child, NavigableString):
4851
if child.strip(): # Only non-empty strings

actions/read_file.py

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,34 @@
11
import os
22

3+
from actions.base_action import BaseAction
4+
35
from constants import PROJECT_DIRECTORY_PATH
46

57

6-
def read_file(relative_path: str) -> str:
7-
"""
8-
Read content from a file and return it.
9-
"""
10-
full_path = os.path.join(PROJECT_DIRECTORY_PATH, relative_path)
11-
with open(full_path, "r") as file:
12-
return file.read()
8+
class ReadFileAction(BaseAction):
9+
_description = {
10+
"name": "read_file",
11+
"description": "Read and return a file content.",
12+
"parameters": {
13+
"type": "object",
14+
"properties": {
15+
"relative_path": {
16+
"type": "string",
17+
"description": "Relative path of the file. Path is relative to the project directory.",
18+
},
19+
},
20+
"required": ["relative_path"],
21+
},
22+
}
23+
24+
# pylint: disable=arguments-differ
25+
@staticmethod
26+
def read_file(relative_path: str) -> str:
27+
"""
28+
Read content from a file and return it.
29+
"""
30+
31+
full_path = os.path.join(PROJECT_DIRECTORY_PATH, relative_path)
32+
33+
with open(file=full_path, mode="r", encoding="utf-8") as file:
34+
return file.read()

actions/search_web.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1+
import json
12
from typing import Union
23
from dacite import from_dict
3-
import json
44
import requests
55

66
from actions.search_web_types import WebSearchApiResponse
77

8-
from constants import PROJECT_CONFIG
8+
from constants import DEFAULT_REQUEST_TIMEOUT, PROJECT_CONFIG
99

1010

1111
def search_web(query: str) -> str:
@@ -14,8 +14,8 @@ def search_web(query: str) -> str:
1414
# If it's an `str`, that means it's an error
1515
if isinstance(brave_search_api_result_or_error, str):
1616
return brave_search_api_result_or_error
17-
else:
18-
brave_search_api_result = brave_search_api_result_or_error
17+
18+
brave_search_api_result = brave_search_api_result_or_error
1919

2020
# Simplify the data
2121
simplified_response_data = {
@@ -62,7 +62,9 @@ def _fetch_brave_search_api(query: str) -> Union[WebSearchApiResponse, str]:
6262
"extra_snippets": "true",
6363
}
6464

65-
response = requests.get(endpoint, headers=headers, params=params)
65+
response = requests.get(
66+
endpoint, headers=headers, params=params, timeout=DEFAULT_REQUEST_TIMEOUT
67+
)
6668

6769
if response.status_code != 200:
6870
return f"Error: {response.status_code} - {response.reason}"

actions/update_tasks.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import json
2+
from typing import List
3+
4+
from actions.base_action import BaseAction
5+
from actions.write_file import WriteFileAction
6+
import typedefs
7+
8+
9+
class UpdateTasksAction(BaseAction):
10+
_description = {
11+
"name": "update_tasks",
12+
"description": "Update the entire task list file by replacing it with the new tasks.",
13+
"parameters": {
14+
"type": "object",
15+
"properties": {
16+
"file_name": {
17+
"type": "string",
18+
"description": "Tasks file name.",
19+
},
20+
"tasks_as_strs": {
21+
"type": "array",
22+
"description": "List of tasks.",
23+
"items": {
24+
"type": "string",
25+
},
26+
},
27+
},
28+
"required": ["updated_tasks"],
29+
},
30+
}
31+
32+
# pylint: disable=arguments-differ
33+
@staticmethod
34+
def run(file_name: str, tasks_as_strs: List[str]) -> str:
35+
"""
36+
Write content to a file. Create the file and/or directories if they don't exist.
37+
"""
38+
39+
tasks = [
40+
(index, UpdateTasksAction.get_task_from_task_as_str)
41+
for index, item in enumerate(tasks_as_strs)
42+
]
43+
tasks_as_json = json.dumps(tasks)
44+
45+
return WriteFileAction.run(relative_path=file_name, file_source=tasks_as_json)
46+
47+
@staticmethod
48+
def get_task_from_task_as_str(tasks_as_str: str, index: int) -> typedefs.TaskDict:
49+
return {
50+
"index": index,
51+
"description": tasks_as_str,
52+
"is_done": False,
53+
}

actions/write_file.py

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,43 @@
11
import os
2+
from actions.base_action import BaseAction
23

34
from constants import PROJECT_DIRECTORY_PATH
45

56

6-
def write_file(relative_path: str, file_source: str) -> str:
7-
"""
8-
Write content to a file. Create the file and/or directories if they don't exist.
9-
"""
10-
full_path = os.path.join(PROJECT_DIRECTORY_PATH, relative_path)
7+
class WriteFileAction(BaseAction):
8+
_description = {
9+
"name": "write_file",
10+
"description": "Write content to a file, creating it if necessary.",
11+
"parameters": {
12+
"type": "object",
13+
"properties": {
14+
"relative_path": {
15+
"type": "string",
16+
"description": "Relative path of the file. Path is relative to the project directory.",
17+
},
18+
"file_source": {
19+
"type": "string",
20+
"description": """Content to write.""",
21+
},
22+
},
23+
"required": ["relative_path", "file_source"],
24+
},
25+
}
1126

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

17-
return f"Done."
18-
except Exception as e:
19-
return f"Error: {str(e)}"
35+
try:
36+
os.makedirs(os.path.dirname(full_path), exist_ok=True)
37+
with open(file=full_path, mode="w", encoding="utf-8") as file:
38+
file.write(file_source)
39+
40+
return f"File successfully written to `{relative_path}`."
41+
42+
except Exception as e:
43+
return f"Error: {str(e)}"

agents/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
from .base_agent import BaseAgent
1+
from .ceo import CEO
22
from .functionner import Functioneer
3+
from .planner import Planner
34
from .product_owner import ProductOwner
45
from .software_engineer import SoftwareEngineer
56
from .user_experience_designer import UserExperienceDesigner

0 commit comments

Comments
 (0)