Merge branch 'ipc' into debugging_ipc

This commit is contained in:
Zvonimir Sabljic
2023-09-14 09:40:36 +02:00
17 changed files with 241 additions and 70 deletions

7
pilot/const/ipc.py Normal file
View File

@@ -0,0 +1,7 @@
MESSAGE_TYPE = {
'verbose': 'verbose',
'stream': 'stream',
'user_input_request': 'user_input_request',
'info': 'info',
'local': 'local',
}

View File

@@ -1,6 +1,6 @@
from playhouse.shortcuts import model_to_dict
from peewee import *
from termcolor import colored
from fabulous.color import yellow, red
from functools import reduce
import operator
import psycopg2
@@ -26,6 +26,28 @@ from database.models.command_runs import CommandRuns
from database.models.user_inputs import UserInputs
from database.models.files import File
DB_NAME = os.getenv("DB_NAME")
DB_HOST = os.getenv("DB_HOST")
DB_PORT = os.getenv("DB_PORT")
DB_USER = os.getenv("DB_USER")
DB_PASSWORD = os.getenv("DB_PASSWORD")
def get_created_apps():
return [model_to_dict(app) for app in App.select()]
def get_created_apps_with_steps():
apps = get_created_apps()
for app in apps:
app['id'] = str(app['id'])
app['steps'] = get_progress_steps(app['id'])
app['development_steps'] = get_all_app_development_steps(app['id'])
# TODO this is a quick way to remove the unnecessary fields from the response
app['steps'] = {outer_k: {k: v for k, v in inner_d.items() if k in {'created_at', 'completeted_at', 'completed'}} if inner_d is not None else None for outer_k, inner_d in app['steps'].items()}
app['development_steps'] = [{k: v for k, v in dev_step.items() if k in {'id', 'created_at'}} for dev_step in app['development_steps']]
return apps
def get_all_app_development_steps(app_id):
return [model_to_dict(dev_step) for dev_step in DevelopmentSteps.select().where(DevelopmentSteps.app == app_id)]
def save_user(user_id, email, password):
try:
@@ -188,7 +210,7 @@ def hash_and_save_step(Model, app_id, hash_data_args, data_fields, message):
.execute())
record = Model.get_by_id(inserted_id)
logger.debug(colored(f"{message} with id {record.id}", "yellow"))
logger.debug(yellow(f"{message} with id {record.id}"))
except IntegrityError:
print(f"A record with hash_id {hash_id} already exists for {Model.__name__}.")
return None
@@ -299,7 +321,7 @@ def delete_all_subsequent_steps(project):
def delete_subsequent_steps(model, step):
if step is None:
return
logger.info(colored(f"Deleting subsequent {model.__name__} steps after {step.id}", "red"))
logger.info(red(f"Deleting subsequent {model.__name__} steps after {step.id}"))
subsequent_steps = model.select().where(model.previous_step == step.id)
for subsequent_step in subsequent_steps:
if subsequent_step:
@@ -335,7 +357,7 @@ def delete_unconnected_steps_from(step, previous_step_field_name):
).order_by(DevelopmentSteps.id.desc())
for unconnected_step in unconnected_steps:
print(colored(f"Deleting unconnected {step.__class__.__name__} step {unconnected_step.id}", "red"))
print(red(f"Deleting unconnected {step.__class__.__name__} step {unconnected_step.id}"))
unconnected_step.delete_instance()

View File

@@ -1,6 +1,6 @@
import re
import subprocess
from termcolor import colored
from fabulous.color import yellow, bold
from database.database import get_development_step_from_hash_id, save_development_step, delete_all_subsequent_steps
from helpers.files import get_files_content
@@ -54,7 +54,7 @@ class AgentConvo:
development_step = get_development_step_from_hash_id(self.agent.project, prompt_path, prompt_data, self.agent.project.llm_req_num)
if development_step is not None and self.agent.project.skip_steps:
# if we do, use it
print(colored(f'Restoring development step with id {development_step.id}', 'yellow'))
print(yellow(f'Restoring development step with id {development_step.id}'))
self.agent.project.checkpoints['last_development_step'] = development_step
self.agent.project.restore_files(development_step.id)
response = development_step.llm_response
@@ -120,7 +120,7 @@ class AgentConvo:
# Continue conversation until GPT response equals END_RESPONSE
while response != END_RESPONSE:
print(colored("Do you want to add anything else? If not, ", 'yellow') + colored('just press ENTER.', 'yellow', attrs=['bold']))
print(yellow("Do you want to add anything else? If not, ") + yellow(bold('just press ENTER.')))
user_message = ask_user(self.agent.project, response, False)
if user_message == "":
@@ -195,7 +195,7 @@ class AgentConvo:
print_msg = capitalize_first_word_with_underscores(self.high_level_step)
if self.log_to_user:
if self.agent.project.checkpoints['last_development_step'] is not None:
print(colored("\nDev step ", 'yellow') + colored(str(self.agent.project.checkpoints['last_development_step']), 'yellow', attrs=['bold']) + '\n', end='')
print(yellow("\nDev step ") + yellow(bold(str(self.agent.project.checkpoints['last_development_step']))) + '\n', end='')
print(f"\n{content}\n")
logger.info(f"{print_msg}: {content}\n")

View File

@@ -1,9 +1,13 @@
import json
import os
import time
from termcolor import colored
from fabulous.color import bold, green, yellow
from const.common import IGNORE_FOLDERS, STEPS
from database.models.app import App
from database.database import get_app, delete_unconnected_steps_from, delete_all_app_development_data
from helpers.ipc import IPCClient
from const.ipc import MESSAGE_TYPE
from utils.questionary import styled_text
from helpers.files import get_files_content, clear_directory, update_file
from helpers.cli import build_directory_tree
@@ -20,7 +24,7 @@ from utils.files import get_parent_folder
class Project:
def __init__(self, args, name=None, description=None, user_stories=None, user_tasks=None, architecture=None,
development_plan=None, current_step=None):
development_plan=None, current_step=None, ipc_client_instance=None):
"""
Initialize a project.
@@ -47,6 +51,9 @@ class Project:
self.root_path = ''
self.skip_until_dev_step = None
self.skip_steps = None
self.ipc_client_instance = ipc_client_instance
# self.restore_files({dev_step_id_to_start_from})
if current_step is not None:
@@ -64,15 +71,29 @@ class Project:
# if development_plan is not None:
# self.development_plan = development_plan
print(green(bold('\n------------------ STARTING NEW PROJECT ----------------------')))
print(f"If you wish to continue with this project in future run:")
print(green(bold(f'python main.py app_id={args["app_id"]}')))
print(green(bold('--------------------------------------------------------------\n')))
def start(self):
"""
Start the project.
"""
self.project_manager = ProductOwner(self)
print(json.dumps({
"project_stage": "project_description"
}), type='info')
self.project_manager.get_project_description()
print(json.dumps({
"project_stage": "user_stories"
}), type='info')
self.user_stories = self.project_manager.get_user_stories()
# self.user_tasks = self.project_manager.get_user_tasks()
print(json.dumps({
"project_stage": "architecture"
}), type='info')
self.architect = Architect(self)
self.architecture = self.architect.get_architecture()
@@ -111,6 +132,15 @@ class Project:
break
# TODO END
self.developer = Developer(self)
print(json.dumps({
"project_stage": "environment_setup"
}), type='info')
self.developer.set_up_environment();
print(json.dumps({
"project_stage": "coding"
}), type='info')
self.developer.start_coding()
def get_directory_tree(self, with_descriptions=False):
@@ -275,7 +305,7 @@ class Project:
delete_unconnected_steps_from(self.checkpoints['last_user_input'], 'previous_step')
def ask_for_human_intervention(self, message, description=None, cbs={}):
print(colored(message, "yellow", attrs=['bold']))
print(yellow(bold(message)))
if description is not None:
print('\n' + '-'*100 + '\n' +
colored(description, 'white', attrs=['bold']) +
@@ -291,3 +321,14 @@ class Project:
return cbs[answer]()
elif answer != '':
return { 'user_input': answer }
def log(self, text, message_type):
if self.ipc_client_instance is None or self.ipc_client_instance.client is None:
print(text)
else:
self.ipc_client_instance.send({
'type': MESSAGE_TYPE[message_type],
'content': str(text),
})
if message_type == MESSAGE_TYPE['user_input_request']:
return self.ipc_client_instance.listen()

View File

@@ -1,7 +1,7 @@
from utils.utils import step_already_finished
from helpers.Agent import Agent
import json
from termcolor import colored
from fabulous.color import green, bold
from const.function_calls import ARCHITECTURE
from utils.utils import execute_step, find_role_from_step, generate_app_data
@@ -27,7 +27,7 @@ class Architect(Agent):
return step['architecture']
# ARCHITECTURE
print(colored(f"Planning project architecture...\n", "green", attrs=['bold']))
print(green(bold(f"Planning project architecture...\n")))
logger.info(f"Planning project architecture...")
architecture = self.convo_architecture.send_message('architecture/technologies.prompt',

View File

@@ -1,6 +1,6 @@
import json
import uuid
from termcolor import colored
from fabulous.color import yellow, green, red, bold
from helpers.exceptions.TokenLimitError import TokenLimitError
from const.code_execution import MAX_COMMAND_DEBUG_TRIES
from helpers.exceptions.TooDeepRecursionError import TooDeepRecursionError
@@ -32,7 +32,7 @@ class Developer(Agent):
self.project.skip_steps = False if ('skip_until_dev_step' in self.project.args and self.project.args['skip_until_dev_step'] == '0') else True
# DEVELOPMENT
print(colored(f"Ok, great, now, let's start with the actual development...\n", "green", attrs=['bold']))
print(green(bold(f"Ok, great, now, let's start with the actual development...\n")))
logger.info(f"Starting to create the actual code...")
for i, dev_task in enumerate(self.project.development_plan):
@@ -301,7 +301,7 @@ class Developer(Agent):
})
return
# ENVIRONMENT SETUP
print(colored(f"Setting up the environment...\n", "green"))
print(green(f"Setting up the environment...\n"))
logger.info(f"Setting up the environment...")
os_info = get_os_info()

View File

@@ -1,4 +1,4 @@
from termcolor import colored
from fabulous.color import bold, green, yellow
from helpers.AgentConvo import AgentConvo
from helpers.Agent import Agent
@@ -43,7 +43,7 @@ class ProductOwner(Agent):
self.project,
generate_messages_from_description(main_prompt, self.project.args['app_type'], self.project.args['name']))
print(colored('Project Summary:\n', 'green', attrs=['bold']))
print(green(bold('Project Summary:\n')))
high_level_summary = convo_project_description.send_message('utils/summary.prompt',
{'conversation': '\n'.join([f"{msg['role']}: {msg['content']}" for msg in high_level_messages])})
@@ -73,7 +73,7 @@ class ProductOwner(Agent):
# USER STORIES
msg = f"User Stories:\n"
print(colored(msg, "green", attrs=['bold']))
print(green(bold(msg)))
logger.info(msg)
self.project.user_stories = self.convo_user_stories.continuous_conversation('user_stories/specs.prompt', {
@@ -107,7 +107,7 @@ class ProductOwner(Agent):
# USER TASKS
msg = f"User Tasks:\n"
print(colored(msg, "green", attrs=['bold']))
print(green(bold(msg)))
logger.info(msg)
self.project.user_tasks = self.convo_user_stories.continuous_conversation('user_stories/user_tasks.prompt',

View File

@@ -1,7 +1,7 @@
from utils.utils import step_already_finished
from helpers.Agent import Agent
import json
from termcolor import colored
from fabulous.color import green, bold
from const.function_calls import DEV_STEPS
from helpers.cli import build_directory_tree
from helpers.AgentConvo import AgentConvo
@@ -29,7 +29,7 @@ class TechLead(Agent):
return step['development_plan']
# DEVELOPMENT PLANNING
print(colored(f"Starting to create the action plan for development...\n", "green", attrs=['bold']))
print(green(bold(f"Starting to create the action plan for development...\n")))
logger.info(f"Starting to create the action plan for development...")
# TODO add clarifications

View File

@@ -7,7 +7,7 @@ import time
import uuid
import platform
from termcolor import colored
from fabulous.color import yellow, green, white, red, bold
from database.database import get_command_run_from_hash_id, save_command_run
from const.function_calls import DEBUG_STEPS_BREAKDOWN
from helpers.exceptions.TooDeepRecursionError import TooDeepRecursionError
@@ -100,8 +100,8 @@ def execute_command(project, command, timeout=None, force=False):
timeout = min(max(timeout, MIN_COMMAND_RUN_TIME), MAX_COMMAND_RUN_TIME)
if not force:
print(colored(f'\n--------- EXECUTE COMMAND ----------', 'yellow', attrs=['bold']))
print(colored(f'Can i execute the command: `') + colored(command, 'yellow', attrs=['bold']) + colored(f'` with {timeout}ms timeout?'))
print(yellow(bold(f'\n--------- EXECUTE COMMAND ----------')))
print(f'Can i execute the command: `' + yellow(bold(command)) + f'` with {timeout}ms timeout?')
answer = styled_text(
project,
@@ -119,7 +119,7 @@ def execute_command(project, command, timeout=None, force=False):
if command_run is not None and project.skip_steps:
# if we do, use it
project.checkpoints['last_command_run'] = command_run
print(colored(f'Restoring command run response id {command_run.id}:\n```\n{command_run.cli_response}```', 'yellow'))
print(yellow(f'Restoring command run response id {command_run.id}:\n```\n{command_run.cli_response}```'))
return command_run.cli_response
return_value = None
@@ -137,7 +137,7 @@ def execute_command(project, command, timeout=None, force=False):
while True and return_value is None:
elapsed_time = time.time() - start_time
if timeout is not None:
print(colored(f'\rt: {round(elapsed_time * 1000)}ms : ', 'white', attrs=['bold']), end='', flush=True)
print(white(bold(f'\rt: {round(elapsed_time * 1000)}ms : ')), end='', flush=True)
# Check if process has finished
if process.poll() is not None:
@@ -146,7 +146,7 @@ def execute_command(project, command, timeout=None, force=False):
while not q.empty():
output_line = q.get_nowait()
if output_line not in output:
print(colored('CLI OUTPUT:', 'green') + output_line, end='')
print(green('CLI OUTPUT:') + output_line, end='')
output += output_line
break
@@ -163,7 +163,7 @@ def execute_command(project, command, timeout=None, force=False):
if line:
output += line
print(colored('CLI OUTPUT:', 'green') + line, end='')
print(green('CLI OUTPUT:') + line, end='')
# Read stderr
try:
@@ -173,7 +173,7 @@ def execute_command(project, command, timeout=None, force=False):
if stderr_line:
stderr_output += stderr_line
print(colored('CLI ERROR:', 'red') + stderr_line, end='') # Print with different color for distinction
print(red('CLI ERROR:') + stderr_line, end='') # Print with different color for distinction
except (KeyboardInterrupt, TimeoutError) as e:
interrupted = True
@@ -268,9 +268,9 @@ def run_command_until_success(command, timeout, convo, additional_message=None,
{'cli_response': cli_response, 'command': command, 'additional_message': additional_message})
if response != 'DONE':
print(colored(f'Got incorrect CLI response:', 'red'))
print(red(f'Got incorrect CLI response:'))
print(cli_response)
print(colored('-------------------', 'red'))
print(red('-------------------'))
try:
# This catch is necessary to return the correct value (cli_response) to continue development function so

View File

@@ -1,4 +1,4 @@
from termcolor import colored
from fabulous.color import green
import os
@@ -11,7 +11,7 @@ def update_file(path, new_content):
# Write content to the file
with open(path, 'w') as file:
file.write(new_content)
print(colored(f"Updated file {path}", "green"))
print(green(f"Updated file {path}"))
def get_files_content(directory, ignore=[]):
return_array = []

45
pilot/helpers/ipc.py Normal file
View File

@@ -0,0 +1,45 @@
# ipc.py
import socket
import json
import time
from utils.utils import json_serial
class IPCClient:
def __init__(self, port):
self.ready = False
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print("Connecting to the external process...")
try:
client.connect(('localhost', int(port)))
self.client = client
print("Connected!")
except ConnectionRefusedError:
self.client = None
print("Connection refused, make sure you started the external process")
def handle_request(self, message_content):
print(f"Received request from the external process: {message_content}")
return message_content # For demonstration, we're just echoing back the content
def listen(self):
if self.client is None:
print("Not connected to the external process!")
return
while True:
data = self.client.recv(4096)
message = json.loads(data)
if message['type'] == 'response':
# self.client.close()
return message['content']
def send(self, data):
serialized_data = json.dumps(data, default=json_serial)
print(serialized_data, type='local')
data_length = len(serialized_data)
self.client.sendall(data_length.to_bytes(4, byteorder='big'))
self.client.sendall(serialized_data.encode('utf-8'))
time.sleep(0.1)

View File

@@ -1,9 +1,15 @@
# main.py
from __future__ import print_function, unicode_literals
import builtins
import json
import sys
from dotenv import load_dotenv
from helpers.ipc import IPCClient
from const.ipc import MESSAGE_TYPE
from utils.utils import json_serial
load_dotenv()
from helpers.Project import Project
@@ -11,8 +17,7 @@ from helpers.Project import Project
from utils.arguments import get_arguments
from utils.exit import exit_gpt_pilot
from logger.logger import logger
from database.database import database_exists, create_database, tables_exist, create_tables
from database.database import database_exists, create_database, tables_exist, create_tables, get_created_apps_with_steps
def init():
# Check if the "euclid" database exists, if not, create it
@@ -30,11 +35,54 @@ def init():
return arguments
def get_custom_print(args):
built_in_print = builtins.print
def print_to_external_process(*args, **kwargs):
# message = " ".join(map(str, args))
message = args[0]
if 'type' not in kwargs:
kwargs['type'] = 'verbose'
elif kwargs['type'] == MESSAGE_TYPE['local']:
local_print(*args, **kwargs)
return
ipc_client_instance.send({
'type': MESSAGE_TYPE[kwargs['type']],
'content': message,
})
if kwargs['type'] == MESSAGE_TYPE['user_input_request']:
return ipc_client_instance.listen()
def local_print(*args, **kwargs):
message = " ".join(map(str, args))
if 'type' in kwargs:
if kwargs['type'] == MESSAGE_TYPE['info']:
return
del kwargs['type']
built_in_print(message, **kwargs)
ipc_client_instance = None
if '--external-log-process-port' in args:
ipc_client_instance = IPCClient(args['--external-log-process-port'])
return print_to_external_process, ipc_client_instance
else:
return local_print, ipc_client_instance
if __name__ == "__main__":
try:
args = init()
project = Project(args)
project.start()
builtins.print, ipc_client_instance = get_custom_print(args)
if '--get-created-apps-with-steps' in args:
print({ 'db_data': get_created_apps_with_steps() }, type='info')
else:
# TODO get checkpoint from database and fill the project with it
project = Project(args, ipc_client_instance=ipc_client_instance)
project.start()
except KeyboardInterrupt:
exit_gpt_pilot()
except Exception as e:

View File

@@ -1,6 +1,6 @@
# prompts/prompts.py
from termcolor import colored
from fabulous.color import yellow
import questionary
from const import common
@@ -102,14 +102,13 @@ def get_additional_info_from_user(project, messages, role):
while True:
if isinstance(message, dict) and 'text' in message:
message = message['text']
print(colored(
f"Please check this message and say what needs to be changed. If everything is ok just press ENTER",
"yellow"))
print(yellow(f"Please check this message and say what needs to be changed. If everything is ok just press ENTER",))
answer = ask_user(project, message, False)
if answer.lower() == '':
break
response = create_gpt_chat_completion(
generate_messages_from_custom_conversation(role, [get_prompt('utils/update.prompt'), message, answer], 'user'), 'additional_info')
generate_messages_from_custom_conversation(role, [get_prompt('utils/update.prompt'), message, answer], 'user'),
'additional_info')
message = response

View File

@@ -1,8 +1,6 @@
import sys
import uuid
from termcolor import colored
from database.database import get_app
@@ -49,8 +47,4 @@ def get_arguments():
if 'step' not in arguments:
arguments['step'] = None
print(colored('\n------------------ STARTING NEW PROJECT ----------------------', 'green', attrs=['bold']))
print(f"If you wish to continue with this project in future run:")
print(colored(f'python main.py app_id={arguments["app_id"]}', 'green', attrs=['bold']))
print(colored('--------------------------------------------------------------\n', 'green', attrs=['bold']))
return arguments

View File

@@ -10,16 +10,12 @@ from jinja2 import Environment, FileSystemLoader
from const.llm import MIN_TOKENS_FOR_GPT_RESPONSE, MAX_GPT_MODEL_TOKENS, MAX_QUESTIONS, END_RESPONSE
from logger.logger import logger
from termcolor import colored
from fabulous.color import red
from helpers.exceptions.TokenLimitError import TokenLimitError
from utils.utils import get_prompt_components, fix_json
from utils.spinner import spinner_start, spinner_stop
def connect_to_llm():
pass
def get_prompt(prompt_name, data=None):
if data is None:
data = {}
@@ -78,8 +74,6 @@ def num_tokens_from_functions(functions, model=model):
for o in v['enum']:
function_tokens += 3
function_tokens += len(encoding.encode(o))
# else:
# print(f"Warning: not supported field {field}")
function_tokens += 11
num_tokens += function_tokens
@@ -89,7 +83,8 @@ def num_tokens_from_functions(functions, model=model):
def create_gpt_chat_completion(messages: List[dict], req_type, min_tokens=MIN_TOKENS_FOR_GPT_RESPONSE,
function_calls=None):
function_calls=None):
tokens_in_messages = round(get_tokens_in_messages(messages) * 1.2) # add 20% to account for not 100% accuracy
if function_calls is not None:
tokens_in_messages += round(
@@ -173,7 +168,8 @@ def retry_on_exception(func):
@retry_on_exception
def stream_gpt_completion(data, req_type):
terminal_width = os.get_terminal_size().columns
# TODO add type dynamically - this isn't working when connected to the external process
terminal_width = 50#os.get_terminal_size().columns
lines_printed = 2
buffer = "" # A buffer to accumulate incoming data
@@ -187,6 +183,7 @@ def stream_gpt_completion(data, req_type):
# spinner = spinner_start(colored("Waiting for OpenAI API response...", 'yellow'))
# print(colored("Stream response from OpenAI:", 'yellow'))
api_key = os.getenv("OPENAI_API_KEY")
logger.info(f'Request data: {data}')
@@ -217,6 +214,7 @@ def stream_gpt_completion(data, req_type):
gpt_response = ''
function_calls = {'name': '', 'arguments': ''}
for line in response.iter_lines():
# Ignore keep-alive new lines
if line:
@@ -252,7 +250,7 @@ def stream_gpt_completion(data, req_type):
if 'arguments' in json_line['function_call']:
function_calls['arguments'] += json_line['function_call']['arguments']
print(json_line['function_call']['arguments'], end='', flush=True)
print(json_line['function_call']['arguments'], type='stream', end='', flush=True)
if 'content' in json_line:
content = json_line.get('content')
@@ -265,9 +263,9 @@ def stream_gpt_completion(data, req_type):
buffer = "" # reset the buffer
gpt_response += content
print(content, end='', flush=True)
print(content, type='stream', end='', flush=True)
print('\n')
print('\n', type='stream')
if function_calls['arguments'] != '':
logger.info(f'Response via function call: {function_calls["arguments"]}')
function_calls['arguments'] = load_data_to_json(function_calls['arguments'])

View File

@@ -1,8 +1,9 @@
from prompt_toolkit.styles import Style
import questionary
from termcolor import colored
from fabulous.color import yellow, bold
from database.database import save_user_input, get_user_input_from_hash_id
from const.ipc import MESSAGE_TYPE
custom_style = Style.from_dict({
'question': '#FFFFFF bold', # the color and style of the question
@@ -25,14 +26,19 @@ def styled_text(project, question, ignore_user_input_count=False):
if user_input is not None and user_input.user_input is not None and project.skip_steps:
# if we do, use it
project.checkpoints['last_user_input'] = user_input
print(colored(f'Restoring user input id {user_input.id}: ', 'yellow'), end='')
print(colored(f'{user_input.user_input}', 'yellow', attrs=['bold']))
print(yellow(bold(f'Restoring user input id {user_input.id}: ')), end='')
print(yellow(bold(f'{user_input.user_input}')))
return user_input.user_input
config = {
'style': custom_style,
}
response = questionary.text(question, **config).unsafe_ask() # .ask() is included here
if project.ipc_client_instance is None or project.ipc_client_instance.client is None:
config = {
'style': custom_style,
}
response = questionary.text(question, **config).unsafe_ask() # .ask() is included here
else:
response = print(question, type='user_input_request')
print(response)
if not ignore_user_input_count:
user_input = save_user_input(project, question, response)

View File

@@ -1,13 +1,15 @@
# utils/utils.py
import datetime
import os
import platform
import uuid
import distro
import json
import hashlib
import re
from jinja2 import Environment, FileSystemLoader
from termcolor import colored
from fabulous.color import green
from const.llm import MAX_QUESTIONS, END_RESPONSE
from const.common import ROLES, STEPS
@@ -119,7 +121,7 @@ def step_already_finished(args, step):
args.update(step['app_data'])
message = f"{capitalize_first_word_with_underscores(step['step'])} already done for this app_id: {args['app_id']}. Moving to next step..."
print(colored(message, "green"))
print(green(message))
logger.info(message)
@@ -171,3 +173,12 @@ def clean_filename(filename):
cleaned_filename = re.sub(r'\s', '_', cleaned_filename)
return cleaned_filename
def json_serial(obj):
"""JSON serializer for objects not serializable by default json code"""
if isinstance(obj, (datetime.datetime, datetime.date)):
return obj.isoformat()
elif isinstance(obj, uuid.UUID):
return str(obj)
else:
return str(obj)