refactor: cleanup

This commit is contained in:
Florian Hönicke
2023-04-04 03:06:00 +02:00
parent 0c46b0daa9
commit 0fc213c807
25 changed files with 433 additions and 553 deletions

View File

@@ -1,31 +1,51 @@
[![Watch the video](https://i.imgur.com/vKb2F1B.png)](https://user-images.githubusercontent.com/11627845/226220484-17810f7c-b184-4a03-9af2-3a977fbb014b.mov) # 🤖 GPT Deploy
This project streamlines the creation and deployment of microservices.
Simply describe your task using natural language, and the system will automatically build and deploy your microservice.
To ensure the executor accurately aligns with your intended task, you can also provide test scenarios.
# Overview
The graphic below illustrates the process of creating a microservice and deploying it to the cloud.
```mermaid
graph TB
A[User Input: Task Description & Test Scenarios] --> B{GPT Deploy}
B -->|Identify Strategies| C[Strategy 1]
B -->|Identify Strategies| D[Strategy 2]
B -->|Identify Strategies| E[Strategy N]
C --> F[executor.py, test_executor.py, requirements.txt, Dockerfile]
D --> G[executor.py, test_executor.py, requirements.txt, Dockerfile]
E --> H[executor.py, test_executor.py, requirements.txt, Dockerfile]
F --> I{Build Image}
G --> I
H --> I
I -->|Fail| J[Apply Fix and Retry]
J --> I
I -->|Success| K[Push Docker Image to Registry]
K --> L[Deploy Microservice]
L --> M[Create Streamlit Playground]
M --> N[User Tests Microservice]
```
1. GPT Deploy identifies several strategies to implement your task.
2. It tests each strategy until it finds one that works.
3. For each strategy, it creates the following files:
- executor.py: This is the main implementation of the microservice.
- test_executor.py: These are test cases to ensure the microservice works as expected.
- requirements.txt: This file lists the packages needed by the microservice and its tests.
- Dockerfile: This file is used to run the microservice in a container and also runs the tests when building the image.
4. GPT Deploy attempts to build the image. If the build fails, it uses the error message to apply a fix and tries again to build the image.
5. Once it finds a successful strategy, it:
- Pushes the Docker image to the registry.
- Deploys the microservice.
- Creates a Streamlit playground where you can test the microservice.
6. If it fails 10 times in a row, it moves on to the next approach.
# 🔮 vision [//]: # ([![Watch the video](https://i.imgur.com/vKb2F1B.png)](https://user-images.githubusercontent.com/11627845/226220484-17810f7c-b184-4a03-9af2-3a977fbb014b.mov))
create, deploy and update your microservice infrastructure
# 🏗 frontend description
The microchain-frontend is used to define the graph of microservice, their interfaces and their functionality.
Based on this definition, the backend will be generated automatically.
# 🏗 usage single microservice
## you provide
- input_modality
- output_modality
- description of the functionality of the transformation the microservice is handling
- examples of input and output pairs
## you get
- a microservice together with a playground
- the code to run requests
# 🤏 limitations for now # 🤏 limitations for now
- stateless microservices only - stateless microservices only
- deterministic microservices only to make sure input and output pairs can be used - deterministic microservices only to make sure input and output pairs can be used
# TODO: # 🔮 vision
- [ ] attach playground Use natural language interface to create, deploy and update your microservice infrastructure.
- [ ] subtask executors
-

View File

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

Before

Width:  |  Height:  |  Size: 9.4 KiB

After

Width:  |  Height:  |  Size: 9.4 KiB

View File

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

490
main.py
View File

@@ -1,17 +1,16 @@
# import importlib import random
from src import gpt, jina_cloud
from src.jina_cloud import push_executor, process_error_message
from src.prompt_tasks import general_guidelines, executor_file_task, chain_of_thought_creation, test_executor_file_task, \
chain_of_thought_optimization, requirements_file_task, docker_file_task, not_allowed
from src.utils.io import recreate_folder, persist_file
from src.utils.string_tools import print_colored
import os import os
import re import re
#
# from src import gpt, jina_cloud
# from src.constants import FILE_AND_TAG_PAIRS, EXECUTOR_FOLDER_v1, EXECUTOR_FOLDER_v2, CLIENT_FILE_NAME, STREAMLIT_FILE_NAME
# from src.jina_cloud import update_client_line_in_file
# from src.prompt_system import system_base_definition
# from src.prompt_tasks import general_guidelines, executor_file_task, requirements_file_task, \
# test_executor_file_task, docker_file_task, client_file_task, streamlit_file_task, chain_of_thought_creation
# from src.utils.io import recreate_folder
# from src.utils.string_tools import find_differences
#
#
from src.constants import FILE_AND_TAG_PAIRS from src.constants import FILE_AND_TAG_PAIRS
@@ -22,16 +21,7 @@ def extract_content_from_result(plain_text, file_name):
return match.group(1).strip() return match.group(1).strip()
else: else:
return '' return ''
#
#
# def extract_and_write(plain_text, dest_folder):
# for file_name, tag in FILE_AND_TAG_PAIRS:
# clean = extract_content_from_result(plain_text, file_name)
# full_path = os.path.join(dest_folder, file_name)
# with open(full_path, 'w') as f:
# f.write(clean)
#
#
def write_config_yml(executor_name, dest_folder): def write_config_yml(executor_name, dest_folder):
config_content = f''' config_content = f'''
jtype: {executor_name} jtype: {executor_name}
@@ -42,8 +32,7 @@ metas:
''' '''
with open(os.path.join(dest_folder, 'config.yml'), 'w') as f: with open(os.path.join(dest_folder, 'config.yml'), 'w') as f:
f.write(config_content) f.write(config_content)
#
#
def get_all_executor_files_with_content(folder_path): def get_all_executor_files_with_content(folder_path):
file_name_to_content = {} file_name_to_content = {}
for filename in os.listdir(folder_path): for filename in os.listdir(folder_path):
@@ -55,58 +44,7 @@ def get_all_executor_files_with_content(folder_path):
file_name_to_content[filename] = content file_name_to_content[filename] = content
return file_name_to_content return file_name_to_content
#
#
#
#
# def build_prototype_implementation(executor_description, executor_name, input_doc_field, input_modality,
# output_doc_field, output_modality, test_in, test_out):
# system_definition = (
# system_base_definition
# + "The user is asking you to create an executor with all the necessary files "
# "and you write the complete code without leaving something out. "
# )
# user_query = (
# general_guidelines()
# + executor_file_task(executor_name, executor_description, input_modality, input_doc_field,
# output_modality, output_doc_field)
# + test_executor_file_task(executor_name, test_in, test_out)
# + requirements_file_task()
# + docker_file_task()
# + client_file_task()
# + streamlit_file_task()
# + chain_of_thought_creation()
# )
# plain_text = gpt.get_response(system_definition, user_query)
# return plain_text
#
#
# def build_production_ready_implementation(all_executor_files_string):
# system_definition = (
# system_base_definition
# + f"The user gives you the code of the executor and all other files needed ({', '.join([e[0] for e in FILE_AND_TAG_PAIRS])}) "
# f"The files may contain bugs. Fix all of them. "
#
# )
# user_query = (
# 'Make it production ready. '
# "Fix all files and add all missing code. "
# "Keep the same format as given to you. "
# f"Some files might have only prototype implementations and are not production ready. Add all the missing code. "
# f"Some imports might be missing. Make sure to add them. "
# f"Some libraries might be missing from the requirements.txt. Make sure to install them."
# f"Somthing might be wrong in the Dockerfile. For example, some libraries might be missing. Install them."
# f"Or not all files are copied to the right destination in the Dockerfile. Copy them to the correct destination. "
# "First write down an extensive list of obvious and non-obvious observations about the parts that could need an adjustment. Explain why. "
# "Think about if all the changes are required and finally decide for the changes you want to make. "
# f"Output all the files even the ones that did not change. "
# "Here are the files: \n\n"
# + all_executor_files_string
# )
# all_executor_files_string_improved = gpt.get_response(system_definition, user_query)
# print('DIFFERENCES:', find_differences(all_executor_files_string, all_executor_files_string_improved))
# return all_executor_files_string_improved
#
def files_to_string(file_name_to_content): def files_to_string(file_name_to_content):
all_executor_files_string = '' all_executor_files_string = ''
for file_name, tag in FILE_AND_TAG_PAIRS: for file_name, tag in FILE_AND_TAG_PAIRS:
@@ -116,84 +54,322 @@ def files_to_string(file_name_to_content):
all_executor_files_string += file_name_to_content[file_name] all_executor_files_string += file_name_to_content[file_name]
all_executor_files_string += '\n```\n\n' all_executor_files_string += '\n```\n\n'
return all_executor_files_string return all_executor_files_string
#
#
# def main( def wrap_content_in_code_block(executor_content, file_name, tag):
# executor_name, return f'**{file_name}**\n```{tag}\n{executor_content}\n```\n\n'
# executor_description,
# input_modality,
# input_doc_field, def create_executor(
# output_modality, executor_description,
# output_doc_field, test_scenario,
# test_in, executor_name,
# test_out, package,
# do_validation=True is_chain_of_thought=False,
# ): ):
# recreate_folder(EXECUTOR_FOLDER_v1) EXECUTOR_FOLDER_v1 = get_executor_path(package, 1)
# recreate_folder(EXECUTOR_FOLDER_v2) recreate_folder(EXECUTOR_FOLDER_v1)
# recreate_folder('flow') recreate_folder('flow')
#
# all_executor_files_string = build_prototype_implementation(executor_description, executor_name, input_doc_field, input_modality, print_colored('', '############# Executor #############', 'red')
# output_doc_field, output_modality, test_in, test_out) user_query = (
# extract_and_write(all_executor_files_string, EXECUTOR_FOLDER_v1) general_guidelines()
# write_config_yml(executor_name, EXECUTOR_FOLDER_v1) + executor_file_task(executor_name, executor_description, test_scenario, package)
# file_name_to_content_v1 = get_all_executor_files_with_content(EXECUTOR_FOLDER_v1) + chain_of_thought_creation()
# all_executor_files_string_no_instructions = files_to_string(file_name_to_content_v1) )
# conversation = gpt.Conversation()
# all_executor_files_string_improved = build_production_ready_implementation(all_executor_files_string_no_instructions) executor_content_raw = conversation.query(user_query)
# extract_and_write(all_executor_files_string_improved, EXECUTOR_FOLDER_v2) if is_chain_of_thought:
# write_config_yml(executor_name, EXECUTOR_FOLDER_v2) executor_content_raw = conversation.query(
# f"General rules: " + not_allowed() + chain_of_thought_optimization('python', 'executor.py'))
# jina_cloud.push_executor(EXECUTOR_FOLDER_v2) executor_content = extract_content_from_result(executor_content_raw, 'executor.py')
#
# host = jina_cloud.deploy_flow(executor_name, do_validation, 'flow') persist_file(executor_content, EXECUTOR_FOLDER_v1 + '/executor.py')
#
# update_client_line_in_file(os.path.join(EXECUTOR_FOLDER_v1, CLIENT_FILE_NAME), host) print_colored('', '############# Test Executor #############', 'red')
# update_client_line_in_file(os.path.join(EXECUTOR_FOLDER_v1, STREAMLIT_FILE_NAME), host) user_query = (
# update_client_line_in_file(os.path.join(EXECUTOR_FOLDER_v2, CLIENT_FILE_NAME), host) general_guidelines()
# update_client_line_in_file(os.path.join(EXECUTOR_FOLDER_v2, STREAMLIT_FILE_NAME), host) + wrap_content_in_code_block(executor_content, 'executor.py', 'python')
# + test_executor_file_task(executor_name, test_scenario)
# if do_validation: )
# importlib.import_module("executor_v1.client") conversation = gpt.Conversation()
# test_executor_content_raw = conversation.query(user_query)
# return get_all_executor_files_with_content(EXECUTOR_FOLDER_v2) if is_chain_of_thought:
# test_executor_content_raw = conversation.query(
# f"General rules: " + not_allowed() +
# if __name__ == '__main__': chain_of_thought_optimization('python', 'test_executor.py')
# # ######### Level 2 task ######### + "Don't add any additional tests. "
# # main( )
# # executor_name='My3DTo2DExecutor', test_executor_content = extract_content_from_result(test_executor_content_raw, 'test_executor.py')
# # executor_description="The executor takes 3D objects in obj format as input and outputs a 2D image projection of that object", persist_file(test_executor_content, EXECUTOR_FOLDER_v1 + '/test_executor.py')
# # input_modality='3d',
# # input_doc_field='blob', print_colored('', '############# Requirements #############', 'red')
# # output_modality='image', user_query = (
# # output_doc_field='blob', general_guidelines()
# # test_in='https://raw.githubusercontent.com/makehumancommunity/communityassets-wip/master/clothes/leotard_fs/leotard_fs.obj', + wrap_content_in_code_block(executor_content, 'executor.py', 'python')
# # test_out='the output should be exactly one image in png format', + wrap_content_in_code_block(test_executor_content, 'test_executor.py', 'python')
# # do_validation=False + requirements_file_task()
# # ) )
# conversation = gpt.Conversation()
# ######## Level 1 task ######### requirements_content_raw = conversation.query(user_query)
# main( if is_chain_of_thought:
# executor_name='MyCoolOcrExecutor', requirements_content_raw = conversation.query(
# executor_description="OCR detector", chain_of_thought_optimization('', 'requirements.txt') + "Keep the same version of jina ")
# input_modality='image',
# input_doc_field='uri', requirements_content = extract_content_from_result(requirements_content_raw, 'requirements.txt')
# output_modality='text', persist_file(requirements_content, EXECUTOR_FOLDER_v1 + '/requirements.txt')
# output_doc_field='text',
# test_in='https://miro.medium.com/v2/resize:fit:1024/0*4ty0Adbdg4dsVBo3.png', print_colored('', '############# Dockerfile #############', 'red')
# test_out='output should contain the string "Hello, world"', user_query = (
# do_validation=False general_guidelines()
# ) + wrap_content_in_code_block(executor_content, 'executor.py', 'python')
# + wrap_content_in_code_block(test_executor_content, 'test_executor.py', 'python')
# # main( + wrap_content_in_code_block(requirements_content, 'requirements.txt', '')
# # executor_name='MySentimentAnalyzer', + docker_file_task()
# # executor_description="Sentiment analysis executor", )
# # input_modality='text', conversation = gpt.Conversation()
# # input_doc_field='text', dockerfile_content_raw = conversation.query(user_query)
# # output_modality='sentiment', if is_chain_of_thought:
# # output_doc_field='sentiment_label', dockerfile_content_raw = conversation.query(
# # test_in='This is a fantastic product! I love it!', f"General rules: " + not_allowed() + chain_of_thought_optimization('dockerfile', 'Dockerfile'))
# # test_out='positive', dockerfile_content = extract_content_from_result(dockerfile_content_raw, 'Dockerfile')
# # do_validation=False persist_file(dockerfile_content, EXECUTOR_FOLDER_v1 + '/Dockerfile')
# # )
write_config_yml(executor_name, EXECUTOR_FOLDER_v1)
def create_playground(executor_name, executor_path, host):
print_colored('', '############# Playground #############', 'red')
file_name_to_content = get_all_executor_files_with_content(executor_path)
user_query = (
general_guidelines()
+ wrap_content_in_code_block(file_name_to_content['executor.py'], 'executor.py', 'python')
+ wrap_content_in_code_block(file_name_to_content['test_executor.py'], 'test_executor.py', 'python')
+ f'''
Create a playground for the executor {executor_name} using streamlit.
The executor is hosted on {host}.
This is an example how you can connect to the executor assuming the document (d) is already defined:
from jina import Client, Document, DocumentArray
client = Client(host='{host}')
response = client.post('/process', inputs=DocumentArray([d]))
print(response[0].text) # can also be blob in case of image/audio..., this should be visualized in the streamlit app
'''
)
conversation = gpt.Conversation()
conversation.query(user_query)
playground_content_raw = conversation.query(
f"General rules: " + not_allowed() + chain_of_thought_optimization('python', 'app.py'))
playground_content = extract_content_from_result(playground_content_raw, 'app.py')
persist_file(playground_content, f'{executor_path}/app.py')
def get_executor_path(package, version):
package_path = '_'.join(package)
return f'executor/{package_path}/v{version}'
def debug_executor(package, executor_description, test_scenario):
MAX_DEBUGGING_ITERATIONS = 10
error_before = ''
for i in range(1, MAX_DEBUGGING_ITERATIONS):
previous_executor_path = get_executor_path(package, i)
next_executor_path = get_executor_path(package, i + 1)
log_hubble = push_executor(previous_executor_path)
error = process_error_message(log_hubble)
if error:
recreate_folder(next_executor_path)
file_name_to_content = get_all_executor_files_with_content(previous_executor_path)
all_files_string = files_to_string(file_name_to_content)
user_query = (
f"General rules: " + not_allowed()
+ 'Here is the description of the task the executor must solve:\n'
+ executor_description
+ '\n\nHere is the test scenario the executor must pass:\n'
+ test_scenario
+ 'Here are all the files I use:\n'
+ all_files_string
+ (('This is an error that is already fixed before:\n'
+ error_before) if error_before else '')
+ '\n\nNow, I get the following error:\n'
+ error + '\n'
+ 'Think quickly about possible reasons. '
'Then output the files that need change. '
"Don't output files that don't need change. "
"If you output a file, then write the complete file. "
"Use the exact same syntax to wrap the code:\n"
f"**...**\n"
f"```...\n"
f"...code...\n"
f"```\n\n"
)
conversation = gpt.Conversation()
returned_files_raw = conversation.query(user_query)
for file_name, tag in FILE_AND_TAG_PAIRS:
updated_file = extract_content_from_result(returned_files_raw, file_name)
if updated_file:
file_name_to_content[file_name] = updated_file
for file_name, content in file_name_to_content.items():
persist_file(content, f'{next_executor_path}/{file_name}')
error_before = error
else:
break
if i == MAX_DEBUGGING_ITERATIONS - 1:
raise MaxDebugTimeReachedException('Could not debug the executor.')
return get_executor_path(package, i)
class MaxDebugTimeReachedException(BaseException):
pass
def generate_executor_name(executor_description):
conversation = gpt.Conversation()
user_query = f'''
Generate a name for the executor matching the description:
"{executor_description}"
The executor name must fulfill the following criteria:
- camel case
- start with a capital letter
- only consists of lower and upper case characters
- end with Executor.
The output is a the raw string wrapped into ``` and starting with **name.txt** like this:
**name.txt**
```
PDFParserExecutor
```
'''
name_raw = conversation.query(user_query)
name = extract_content_from_result(name_raw, 'name.txt')
return name
def main(
executor_description,
test_scenario,
threads=3,
):
generated_name = generate_executor_name(executor_description)
executor_name = f'{generated_name}{random.randint(0, 1000_000)}'
packages = get_possible_packages(executor_description, threads)
recreate_folder('executor')
for package in packages:
try:
create_executor(executor_description, test_scenario, executor_name, package)
# executor_name = 'MicroChainExecutor790050'
executor_path = debug_executor(package, executor_description, test_scenario)
# print('Executor can be built locally, now we will push it to the cloud.')
# jina_cloud.push_executor(executor_path)
print('Deploy a jina flow')
host = jina_cloud.deploy_flow(executor_name, executor_path)
print(f'Flow is deployed create the playground for {host}')
create_playground(executor_name, executor_path, host)
except MaxDebugTimeReachedException:
print('Could not debug the executor.')
continue
print(
'Executor name:', executor_name, '\n',
'Executor path:', executor_path, '\n',
'Host:', host, '\n',
'Playground:', f'streamlit run {executor_path}/app.py', '\n',
)
break
def get_possible_packages(executor_description, threads):
print_colored('', '############# What package to use? #############', 'red')
user_query = f'''
Here is the task description of the problme you need to solve:
"{executor_description}"
First, write down all the subtasks you need to solve which require python packages.
For each subtask:
Provide a list of 1 to 3 python packages you could use to solve the subtask. Prefer modern packages.
For each package:
Write down some non-obvious thoughts about the challenges you might face for the task and give multiple approaches on how you handle them.
For example, there might be some packages you must not use because they do not obay the rules:
{not_allowed()}
Discuss the pros and cons for all of these packages.
Create a list of package subsets that you could use to solve the task.
The list is sorted in a way that the most promising subset of packages is at the top.
The maximum length of the list is 5.
The output must be a list of lists wrapped into ``` and starting with **packages.csv** like this:
**packages.csv**
```
package1,package2
package2,package3,...
...
```
'''
conversation = gpt.Conversation()
packages_raw = conversation.query(user_query)
packages_csv_string = extract_content_from_result(packages_raw, 'packages.csv')
packages = [package.split(',') for package in packages_csv_string.split('\n')]
packages = packages[:threads]
return packages
if __name__ == '__main__':
# accomplished tasks:
# main(
# executor_description="The executor takes a url of a website as input and classifies it as either individual or business.",
# test_scenario='Takes https://jina.ai/ as input and returns "business". Takes https://hanxiao.io/ as input and returns "individual". ',
# )
# needs to prove:
# ######## Level 1 task #########
# main(
# executor_description="The executor takes a pdf file as input, parses it and returns the text.",
# input_modality='pdf',
# output_modality='text',
# test_scenario='Takes https://www2.deloitte.com/content/dam/Deloitte/de/Documents/about-deloitte/Deloitte-Unternehmensgeschichte.pdf and returns a string that is at least 100 characters long',
# )
# main(
# executor_description="The executor takes a url of a website as input and returns the logo of the website as an image.",
# test_scenario='Takes https://jina.ai/ as input and returns an svg image of the logo.',
# )
# # # ######## Level 1 task #########
# main(
# executor_description="The executor takes a pdf file as input, parses it and returns the text.",
# input_modality='pdf',
# output_modality='text',
# test_scenario='Takes https://www2.deloitte.com/content/dam/Deloitte/de/Documents/about-deloitte/Deloitte-Unternehmensgeschichte.pdf and returns a string that is at least 100 characters long',
# )
# ######## Level 2 task #########
# main(
# executor_description="OCR detector",
# input_modality='image',
# output_modality='text',
# test_scenario='Takes https://miro.medium.com/v2/resize:fit:1024/0*4ty0Adbdg4dsVBo3.png as input and returns a string that contains "Hello, world"',
# )
# ######## Level 3 task #########
main(
executor_description="The executor takes an mp3 file as input and returns bpm and pitch in a json.",
test_scenario='Takes https://cdn.pixabay.com/download/audio/2023/02/28/audio_550d815fa5.mp3 as input and returns a json with bpm and pitch',
)
######### Level 4 task #########
# main(
# executor_description="The executor takes 3D objects in obj format as input "
# "and outputs a 2D image projection of that object where the full object is shown. ",
# input_modality='3d',
# output_modality='image',
# test_scenario='Test that 3d object from https://raw.githubusercontent.com/polygonjs/polygonjs-assets/master/models/wolf.obj '
# 'is put in and out comes a 2d rendering of it',
# )
# ######## Level 8 task #########
# main(
# executor_description="The executor takes an image as input and returns a list of bounding boxes of all animals in the image.",
# input_modality='blob',
# output_modality='json',
# test_scenario='Take the image from https://thumbs.dreamstime.com/b/dog-professor-red-bow-tie-glasses-white-background-isolated-dog-professor-glasses-197036807.jpg as input and assert that the list contains at least one bounding box. ',
# )

View File

@@ -1,306 +0,0 @@
import json
import random
from main import extract_content_from_result, write_config_yml, get_all_executor_files_with_content, files_to_string
from src import gpt, jina_cloud
from src.constants import FILE_AND_TAG_PAIRS
from src.jina_cloud import push_executor, process_error_message
from src.prompt_tasks import general_guidelines, executor_file_task, chain_of_thought_creation, test_executor_file_task, \
chain_of_thought_optimization, requirements_file_task, docker_file_task, not_allowed
from src.utils.io import recreate_folder, persist_file
from src.utils.string_tools import print_colored
def wrap_content_in_code_block(executor_content, file_name, tag):
return f'**{file_name}**\n```{tag}\n{executor_content}\n```\n\n'
def create_executor(
executor_description,
test_scenario,
executor_name,
package,
is_chain_of_thought=False,
):
EXECUTOR_FOLDER_v1 = get_executor_path(package, 1)
recreate_folder(EXECUTOR_FOLDER_v1)
recreate_folder('flow')
print_colored('', '############# Executor #############', 'red')
user_query = (
general_guidelines()
+ executor_file_task(executor_name, executor_description, test_scenario, package)
+ chain_of_thought_creation()
)
conversation = gpt.Conversation()
executor_content_raw = conversation.query(user_query)
if is_chain_of_thought:
executor_content_raw = conversation.query(
f"General rules: " + not_allowed() + chain_of_thought_optimization('python', 'executor.py'))
executor_content = extract_content_from_result(executor_content_raw, 'executor.py')
persist_file(executor_content, EXECUTOR_FOLDER_v1 + '/executor.py')
print_colored('', '############# Test Executor #############', 'red')
user_query = (
general_guidelines()
+ wrap_content_in_code_block(executor_content, 'executor.py', 'python')
+ test_executor_file_task(executor_name, test_scenario)
)
conversation = gpt.Conversation()
test_executor_content_raw = conversation.query(user_query)
if is_chain_of_thought:
test_executor_content_raw = conversation.query(
f"General rules: " + not_allowed() +
chain_of_thought_optimization('python', 'test_executor.py')
+ "Don't add any additional tests. "
)
test_executor_content = extract_content_from_result(test_executor_content_raw, 'test_executor.py')
persist_file(test_executor_content, EXECUTOR_FOLDER_v1 + '/test_executor.py')
print_colored('', '############# Requirements #############', 'red')
user_query = (
general_guidelines()
+ wrap_content_in_code_block(executor_content, 'executor.py', 'python')
+ wrap_content_in_code_block(test_executor_content, 'test_executor.py', 'python')
+ requirements_file_task()
)
conversation = gpt.Conversation()
requirements_content_raw = conversation.query(user_query)
if is_chain_of_thought:
requirements_content_raw = conversation.query(
chain_of_thought_optimization('', 'requirements.txt') + "Keep the same version of jina ")
requirements_content = extract_content_from_result(requirements_content_raw, 'requirements.txt')
persist_file(requirements_content, EXECUTOR_FOLDER_v1 + '/requirements.txt')
print_colored('', '############# Dockerfile #############', 'red')
user_query = (
general_guidelines()
+ wrap_content_in_code_block(executor_content, 'executor.py', 'python')
+ wrap_content_in_code_block(test_executor_content, 'test_executor.py', 'python')
+ wrap_content_in_code_block(requirements_content, 'requirements.txt', '')
+ docker_file_task()
)
conversation = gpt.Conversation()
dockerfile_content_raw = conversation.query(user_query)
if is_chain_of_thought:
dockerfile_content_raw = conversation.query(
f"General rules: " + not_allowed() + chain_of_thought_optimization('dockerfile', 'Dockerfile'))
dockerfile_content = extract_content_from_result(dockerfile_content_raw, 'Dockerfile')
persist_file(dockerfile_content, EXECUTOR_FOLDER_v1 + '/Dockerfile')
write_config_yml(executor_name, EXECUTOR_FOLDER_v1)
def create_playground(executor_name, executor_path, host):
print_colored('', '############# Playground #############', 'red')
file_name_to_content = get_all_executor_files_with_content(executor_path)
user_query = (
general_guidelines()
+ wrap_content_in_code_block(file_name_to_content['executor.py'], 'executor.py', 'python')
+ wrap_content_in_code_block(file_name_to_content['test_executor.py'], 'test_executor.py', 'python')
+ f'''
Create a playground for the executor {executor_name} using streamlit.
The executor is hosted on {host}.
This is an example how you can connect to the executor assuming the document (d) is already defined:
from jina import Client, Document, DocumentArray
client = Client(host='{host}')
response = client.post('/process', inputs=DocumentArray([d]))
print(response[0].text) # can also be blob in case of image/audio..., this should be visualized in the streamlit app
'''
)
conversation = gpt.Conversation()
conversation.query(user_query)
playground_content_raw = conversation.query(
f"General rules: " + not_allowed() + chain_of_thought_optimization('python', 'app.py'))
playground_content = extract_content_from_result(playground_content_raw, 'app.py')
persist_file(playground_content, f'{executor_path}/app.py')
def get_executor_path(package, version):
package_path = '_'.join(package)
return f'executor/{package_path}/v{version}'
def debug_executor(package, executor_description, test_scenario):
MAX_DEBUGGING_ITERATIONS = 10
error_before = ''
for i in range(1, MAX_DEBUGGING_ITERATIONS):
previous_executor_path = get_executor_path(package, i)
next_executor_path = get_executor_path(package, i + 1)
log_hubble = push_executor(previous_executor_path)
error = process_error_message(log_hubble)
if error:
recreate_folder(next_executor_path)
file_name_to_content = get_all_executor_files_with_content(previous_executor_path)
all_files_string = files_to_string(file_name_to_content)
user_query = (
f"General rules: " + not_allowed()
+ 'Here is the description of the task the executor must solve:\n'
+ executor_description
+ '\n\nHere is the test scenario the executor must pass:\n'
+ test_scenario
+ 'Here are all the files I use:\n'
+ all_files_string
+ (('This is an error that is already fixed before:\n'
+ error_before) if error_before else '')
+ '\n\nNow, I get the following error:\n'
+ error + '\n'
+ 'Think quickly about possible reasons. '
'Then output the files that need change. '
"Don't output files that don't need change. "
"If you output a file, then write the complete file. "
"Use the exact same syntax to wrap the code:\n"
f"**...**\n"
f"```...\n"
f"...code...\n"
f"```\n\n"
)
conversation = gpt.Conversation()
returned_files_raw = conversation.query(user_query)
for file_name, tag in FILE_AND_TAG_PAIRS:
updated_file = extract_content_from_result(returned_files_raw, file_name)
if updated_file:
file_name_to_content[file_name] = updated_file
for file_name, content in file_name_to_content.items():
persist_file(content, f'{next_executor_path}/{file_name}')
error_before = error
else:
break
if i == MAX_DEBUGGING_ITERATIONS - 1:
raise MaxDebugTimeReachedException('Could not debug the executor.')
return get_executor_path(package, i)
class MaxDebugTimeReachedException(BaseException):
pass
def main(
executor_description,
test_scenario,
threads=3,
):
executor_name = f'MicroChainExecutor{random.randint(0, 1000_000)}'
packages = get_possible_packages(executor_description, threads)
recreate_folder('executor')
for package in packages:
try:
create_executor(executor_description, test_scenario, executor_name, package)
# executor_name = 'MicroChainExecutor790050'
executor_path = debug_executor(package, executor_description, test_scenario)
# print('Executor can be built locally, now we will push it to the cloud.')
# jina_cloud.push_executor(executor_path)
print('Deploy a jina flow')
host = jina_cloud.deploy_flow(executor_name, executor_path)
print(f'Flow is deployed create the playground for {host}')
create_playground(executor_name, executor_path, host)
except MaxDebugTimeReachedException:
print('Could not debug the executor.')
continue
print(
'Executor name:', executor_name, '\n',
'Executor path:', executor_path, '\n',
'Host:', host, '\n',
'Playground:', f'streamlit run {executor_path}/app.py', '\n',
)
break
def get_possible_packages(executor_description, threads):
print_colored('', '############# What package to use? #############', 'red')
user_query = f'''
Here is the task description of the problme you need to solve:
"{executor_description}"
First, write down all the subtasks you need to solve which require python packages.
For each subtask:
Provide a list of 1 to 3 python packages you could use to solve the subtask. Prefer modern packages.
For each package:
Write down some non-obvious thoughts about the challenges you might face for the task and give multiple approaches on how you handle them.
For example, there might be some packages you must not use because they do not obay the rules:
{not_allowed()}
Discuss the pros and cons for all of these packages.
Create a list of package subsets that you could use to solve the task.
The list is sorted in a way that the most promising subset of packages is at the top.
The maximum length of the list is 5.
The output must be a list of lists wrapped into ``` and starting with **packages.csv** like this:
**packages.csv**
```
package1,package2
package2,package3,...
...
```
'''
conversation = gpt.Conversation()
packages_raw = conversation.query(user_query)
packages_csv_string = extract_content_from_result(packages_raw, 'packages.csv')
packages = [package.split(',') for package in packages_csv_string.split('\n')]
packages = packages[:threads]
return packages
if __name__ == '__main__':
# accomplished tasks:
# main(
# executor_description="The executor takes a url of a website as input and classifies it as either individual or business.",
# test_scenario='Takes https://jina.ai/ as input and returns "business". Takes https://hanxiao.io/ as input and returns "individual". ',
# )
# needs to prove:
# ######## Level 1 task #########
# main(
# executor_description="The executor takes a pdf file as input, parses it and returns the text.",
# input_modality='pdf',
# output_modality='text',
# test_scenario='Takes https://www2.deloitte.com/content/dam/Deloitte/de/Documents/about-deloitte/Deloitte-Unternehmensgeschichte.pdf and returns a string that is at least 100 characters long',
# )
# main(
# executor_description="The executor takes a url of a website as input and returns the logo of the website as an image.",
# test_scenario='Takes https://jina.ai/ as input and returns an svg image of the logo.',
# )
# # # ######## Level 1 task #########
# main(
# executor_description="The executor takes a pdf file as input, parses it and returns the text.",
# input_modality='pdf',
# output_modality='text',
# test_scenario='Takes https://www2.deloitte.com/content/dam/Deloitte/de/Documents/about-deloitte/Deloitte-Unternehmensgeschichte.pdf and returns a string that is at least 100 characters long',
# )
# ######## Level 2 task #########
# main(
# executor_description="OCR detector",
# input_modality='image',
# output_modality='text',
# test_scenario='Takes https://miro.medium.com/v2/resize:fit:1024/0*4ty0Adbdg4dsVBo3.png as input and returns a string that contains "Hello, world"',
# )
# ######## Level 3 task #########
main(
executor_description="The executor takes an mp3 file as input and returns bpm and pitch in a json.",
test_scenario='Takes https://cdn.pixabay.com/download/audio/2023/02/28/audio_550d815fa5.mp3 as input and returns a json with bpm and pitch',
)
######### Level 4 task #########
# main(
# executor_description="The executor takes 3D objects in obj format as input "
# "and outputs a 2D image projection of that object where the full object is shown. ",
# input_modality='3d',
# output_modality='image',
# test_scenario='Test that 3d object from https://raw.githubusercontent.com/polygonjs/polygonjs-assets/master/models/wolf.obj '
# 'is put in and out comes a 2d rendering of it',
# )
# ######## Level 8 task #########
# main(
# executor_description="The executor takes an image as input and returns a list of bounding boxes of all animals in the image.",
# input_modality='blob',
# output_modality='json',
# test_scenario='Take the image from https://thumbs.dreamstime.com/b/dog-professor-red-bow-tie-glasses-white-background-isolated-dog-professor-glasses-197036807.jpg as input and assert that the list contains at least one bounding box. ',
# )

122
server.py
View File

@@ -1,67 +1,55 @@
# from fastapi import FastAPI from fastapi import FastAPI
# from fastapi.exceptions import RequestValidationError from fastapi.exceptions import RequestValidationError
# from pydantic import BaseModel, HttpUrl from jina import Flow
# from typing import Optional, Dict from pydantic import BaseModel, HttpUrl
# from typing import Optional, Dict
# from starlette.middleware.cors import CORSMiddleware
# from starlette.requests import Request from starlette.middleware.cors import CORSMiddleware
# from starlette.responses import JSONResponse from starlette.requests import Request
# from starlette.responses import JSONResponse
# from main import main Flow.plot()
# from main import main
# app = FastAPI()
# app = FastAPI()
# # Define the request model
# class CreateRequest(BaseModel): # Define the request model
# executor_name: str class CreateRequest(BaseModel):
# executor_description: str test_scenario: str
# input_modality: str executor_description: str
# input_doc_field: str
# output_modality: str # Define the response model
# output_doc_field: str class CreateResponse(BaseModel):
# test_in: str result: Dict[str, str]
# test_out: str success: bool
# message: Optional[str]
# # Define the response model
# class CreateResponse(BaseModel): @app.post("/create", response_model=CreateResponse)
# result: Dict[str, str] def create_endpoint(request: CreateRequest):
# success: bool
# message: Optional[str] result = main(
# executor_description=request.executor_description,
# @app.post("/create", response_model=CreateResponse) test_scenario=request.test_scenario,
# def create_endpoint(request: CreateRequest): )
# return CreateResponse(result=result, success=True, message=None)
# result = main(
# executor_name=request.executor_name,
# executor_description=request.executor_description, app.add_middleware(
# input_modality=request.input_modality, CORSMiddleware,
# input_doc_field=request.input_doc_field, allow_origins=["*"],
# output_modality=request.output_modality, allow_credentials=True,
# output_doc_field=request.output_doc_field, allow_methods=["*"],
# test_in=request.test_in, allow_headers=["*"],
# test_out=request.test_out, )
# do_validation=False
# ) # Add a custom exception handler for RequestValidationError
# return CreateResponse(result=result, success=True, message=None) @app.exception_handler(RequestValidationError)
# def validation_exception_handler(request: Request, exc: RequestValidationError):
# return JSONResponse(
# app.add_middleware( status_code=422,
# CORSMiddleware, content={"detail": exc.errors()},
# allow_origins=["*"], )
# allow_credentials=True,
# allow_methods=["*"],
# allow_headers=["*"], if __name__ == "__main__":
# ) import uvicorn
# uvicorn.run("server:app", host="0.0.0.0", port=8000, log_level="info")
# # Add a custom exception handler for RequestValidationError
# @app.exception_handler(RequestValidationError)
# def validation_exception_handler(request: Request, exc: RequestValidationError):
# return JSONResponse(
# status_code=422,
# content={"detail": exc.errors()},
# )
#
#
# if __name__ == "__main__":
# import uvicorn
# uvicorn.run("server:app", host="0.0.0.0", port=8000, log_level="info")

View File

@@ -15,8 +15,10 @@ total_chars_prompt = 0
total_chars_generation = 0 total_chars_generation = 0
class Conversation: class Conversation:
def __init__(self): def __init__(self, prompt_list: List[Tuple[str, str]] = None):
self.prompt_list = [('system', system_base_definition)] if prompt_list is None:
prompt_list = [('system', system_base_definition)]
self.prompt_list = prompt_list
print_colored('system', system_base_definition, 'magenta') print_colored('system', system_base_definition, 'magenta')
def query(self, prompt: str): def query(self, prompt: str):