mirror of
https://github.com/aljazceru/dev-gpt.git
synced 2025-12-25 09:24:23 +01:00
Merge pull request #11 from jina-ai/refactor-langchain
refactor: langchain
This commit is contained in:
@@ -4,4 +4,5 @@ openai
|
||||
psutil
|
||||
jina
|
||||
jcloud
|
||||
jina-hubble-sdk
|
||||
jina-hubble-sdk
|
||||
langchain
|
||||
136
src/apis/gpt.py
136
src/apis/gpt.py
@@ -1,15 +1,17 @@
|
||||
import os
|
||||
from time import sleep
|
||||
|
||||
from typing import List, Tuple, Optional
|
||||
from typing import List, Any
|
||||
|
||||
import openai
|
||||
from openai.error import RateLimitError, Timeout, APIConnectionError
|
||||
from langchain import PromptTemplate
|
||||
from langchain.callbacks import CallbackManager
|
||||
from langchain.chat_models import ChatOpenAI
|
||||
from openai.error import RateLimitError
|
||||
from langchain.schema import AIMessage, HumanMessage, SystemMessage, BaseMessage
|
||||
from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler
|
||||
|
||||
from src.constants import PRICING_GPT4_PROMPT, PRICING_GPT4_GENERATION, PRICING_GPT3_5_TURBO_PROMPT, \
|
||||
PRICING_GPT3_5_TURBO_GENERATION, CHARS_PER_TOKEN
|
||||
from src.options.generate.prompt_system import system_base_definition, executor_example, docarray_example, client_example
|
||||
from src.utils.io import timeout_generator_wrapper, GenerationTimeoutError
|
||||
from src.options.generate.templates_system import template_system_message_base, executor_example, docarray_example, client_example
|
||||
from src.utils.string_tools import print_colored
|
||||
|
||||
|
||||
@@ -18,21 +20,10 @@ class GPTSession:
|
||||
self.task_description = task_description
|
||||
self.test_description = test_description
|
||||
self.configure_openai_api_key()
|
||||
if model == 'gpt-4' and self.is_gpt4_available():
|
||||
self.supported_model = 'gpt-4'
|
||||
self.pricing_prompt = PRICING_GPT4_PROMPT
|
||||
self.pricing_generation = PRICING_GPT4_GENERATION
|
||||
else:
|
||||
if model == 'gpt-4':
|
||||
print_colored('GPT-4 is not available. Using GPT-3.5-turbo instead.', 'yellow')
|
||||
model = 'gpt-3.5-turbo'
|
||||
self.supported_model = model
|
||||
self.pricing_prompt = PRICING_GPT3_5_TURBO_PROMPT
|
||||
self.pricing_generation = PRICING_GPT3_5_TURBO_GENERATION
|
||||
self.chars_prompt_so_far = 0
|
||||
self.chars_generation_so_far = 0
|
||||
self.model_name = 'gpt-4' if model == 'gpt-4' and self.is_gpt4_available() else 'gpt-3.5-turbo'
|
||||
|
||||
def configure_openai_api_key(self):
|
||||
@staticmethod
|
||||
def configure_openai_api_key():
|
||||
if 'OPENAI_API_KEY' not in os.environ:
|
||||
raise Exception('''
|
||||
You need to set OPENAI_API_KEY in your environment.
|
||||
@@ -41,7 +32,8 @@ If you have updated it already, please restart your terminal.
|
||||
)
|
||||
openai.api_key = os.environ['OPENAI_API_KEY']
|
||||
|
||||
def is_gpt4_available(self):
|
||||
@staticmethod
|
||||
def is_gpt4_available():
|
||||
try:
|
||||
for i in range(5):
|
||||
try:
|
||||
@@ -58,89 +50,55 @@ If you have updated it already, please restart your terminal.
|
||||
continue
|
||||
return True
|
||||
except openai.error.InvalidRequestError:
|
||||
print_colored('GPT-4 is not available. Using GPT-3.5-turbo instead.', 'yellow')
|
||||
return False
|
||||
|
||||
def cost_callback(self, chars_prompt, chars_generation):
|
||||
self.chars_prompt_so_far += chars_prompt
|
||||
self.chars_generation_so_far += chars_generation
|
||||
print('\n')
|
||||
money_prompt = self.calculate_money_spent(self.chars_prompt_so_far, self.pricing_prompt)
|
||||
money_generation = self.calculate_money_spent(self.chars_generation_so_far, self.pricing_generation)
|
||||
print('Total money spent so far on openai.com:', f'${money_prompt + money_generation}')
|
||||
print('\n')
|
||||
|
||||
def get_conversation(self, system_definition_examples: List[str] = ['executor', 'docarray', 'client']):
|
||||
return _GPTConversation(self.supported_model, self.cost_callback, self.task_description, self.test_description, system_definition_examples)
|
||||
return _GPTConversation(self.model_name, self.task_description, self.test_description, system_definition_examples)
|
||||
|
||||
def calculate_money_spent(self, num_chars, price):
|
||||
return round(num_chars / CHARS_PER_TOKEN * price / 1000, 3)
|
||||
|
||||
class AssistantStreamingStdOutCallbackHandler(StreamingStdOutCallbackHandler):
|
||||
def on_llm_new_token(self, token: str, **kwargs: Any) -> None:
|
||||
"""Run on new LLM token. Only available when streaming is enabled."""
|
||||
print_colored('', token, 'green', end='')
|
||||
|
||||
|
||||
class _GPTConversation:
|
||||
def __init__(self, model: str, cost_callback, task_description, test_description, system_definition_examples: List[str] = ['executor', 'docarray', 'client']):
|
||||
self.model = model
|
||||
self.cost_callback = cost_callback
|
||||
self.prompt_list: List[Optional[Tuple]] = [None]
|
||||
self.set_system_definition(task_description, test_description, system_definition_examples)
|
||||
def __init__(self, model: str, task_description, test_description, system_definition_examples: List[str] = ['executor', 'docarray', 'client']):
|
||||
self._chat = ChatOpenAI(
|
||||
model_name=model,
|
||||
streaming=True,
|
||||
callback_manager=CallbackManager([AssistantStreamingStdOutCallbackHandler()]),
|
||||
verbose=os.environ['VERBOSE'].lower() == 'true',
|
||||
temperature=0,
|
||||
)
|
||||
self.messages: List[BaseMessage] = []
|
||||
self.system_message = self._create_system_message(task_description, test_description, system_definition_examples)
|
||||
if os.environ['VERBOSE'].lower() == 'true':
|
||||
print_colored('system', self.prompt_list[0][1], 'magenta')
|
||||
print_colored('system', self.system_message.content, 'magenta')
|
||||
|
||||
def query(self, prompt: str):
|
||||
def chat(self, prompt: str):
|
||||
chat_message = HumanMessage(content=prompt)
|
||||
self.messages.append(chat_message)
|
||||
if os.environ['VERBOSE'].lower() == 'true':
|
||||
print_colored('user', prompt, 'blue')
|
||||
self.prompt_list.append(('user', prompt))
|
||||
response = self.get_response(self.prompt_list)
|
||||
self.prompt_list.append(('assistant', response))
|
||||
return response
|
||||
print_colored('assistant', '', 'green', end='')
|
||||
response = self._chat([self.system_message] + self.messages)
|
||||
if os.environ['VERBOSE'].lower() == 'true':
|
||||
print()
|
||||
self.messages.append(response)
|
||||
return response.content
|
||||
|
||||
def set_system_definition(self, task_description, test_description, system_definition_examples: List[str] = []):
|
||||
system_message = system_base_definition(task_description, test_description)
|
||||
@staticmethod
|
||||
def _create_system_message(task_description, test_description, system_definition_examples: List[str] = []) -> SystemMessage:
|
||||
system_message = PromptTemplate.from_template(template_system_message_base).format(
|
||||
task_description=task_description,
|
||||
test_description=test_description,
|
||||
)
|
||||
if 'executor' in system_definition_examples:
|
||||
system_message += f'\n{executor_example}'
|
||||
if 'docarray' in system_definition_examples:
|
||||
system_message += f'\n{docarray_example}'
|
||||
if 'client' in system_definition_examples:
|
||||
system_message += f'\n{client_example}'
|
||||
self.prompt_list[0] = ('system', system_message)
|
||||
|
||||
def get_response_from_stream(self, response_generator):
|
||||
response_generator_with_timeout = timeout_generator_wrapper(response_generator, 10)
|
||||
complete_string = ''
|
||||
for chunk in response_generator_with_timeout:
|
||||
delta = chunk['choices'][0]['delta']
|
||||
if 'content' in delta:
|
||||
content = delta['content']
|
||||
print_colored('' if complete_string else 'assistant', content, 'green', end='')
|
||||
complete_string += content
|
||||
return complete_string
|
||||
|
||||
def get_response(self, prompt_list: List[Tuple[str, str]]):
|
||||
for i in range(10):
|
||||
try:
|
||||
response_generator = openai.ChatCompletion.create(
|
||||
temperature=0,
|
||||
max_tokens=None,
|
||||
model=self.model,
|
||||
stream=True,
|
||||
messages=[
|
||||
{
|
||||
"role": prompt[0],
|
||||
"content": prompt[1]
|
||||
}
|
||||
for prompt in prompt_list
|
||||
]
|
||||
)
|
||||
|
||||
complete_string = self.get_response_from_stream(response_generator)
|
||||
|
||||
except (RateLimitError, Timeout, ConnectionError, APIConnectionError, GenerationTimeoutError) as e:
|
||||
print('/n', e)
|
||||
print('retrying...')
|
||||
sleep(3)
|
||||
continue
|
||||
chars_prompt = sum(len(prompt[1]) for prompt in prompt_list)
|
||||
chars_generation = len(complete_string)
|
||||
self.cost_callback(chars_prompt, chars_generation)
|
||||
return complete_string
|
||||
raise Exception('Failed to get response')
|
||||
|
||||
return SystemMessage(content=system_message)
|
||||
|
||||
@@ -2,6 +2,7 @@ import functools
|
||||
import os
|
||||
|
||||
import click
|
||||
from langchain.callbacks import get_openai_callback
|
||||
|
||||
from src.apis.jina_cloud import jina_auth_login
|
||||
from src.options.configure.key_handling import set_api_key
|
||||
@@ -64,7 +65,10 @@ def generate(
|
||||
|
||||
from src.options.generate.generator import Generator
|
||||
generator = Generator(description, test, model=model)
|
||||
generator.generate(path)
|
||||
with get_openai_callback() as cb:
|
||||
generator.generate(path)
|
||||
print(f"Prompt/Completion/Total Tokens: {cb.prompt_tokens}/{cb.completion_tokens}/{cb.total_tokens}")
|
||||
print(f"Total Cost on OpenAI (USD): ${cb.total_cost}")
|
||||
|
||||
@main.command()
|
||||
@path_param
|
||||
|
||||
@@ -23,12 +23,6 @@ FILE_AND_TAG_PAIRS = [
|
||||
|
||||
FLOW_URL_PLACEHOLDER = 'jcloud.jina.ai'
|
||||
|
||||
PRICING_GPT4_PROMPT = 0.03
|
||||
PRICING_GPT4_GENERATION = 0.06
|
||||
PRICING_GPT3_5_TURBO_PROMPT = 0.002
|
||||
PRICING_GPT3_5_TURBO_GENERATION = 0.002
|
||||
|
||||
CHARS_PER_TOKEN = 3.4
|
||||
|
||||
NUM_IMPLEMENTATION_STRATEGIES = 5
|
||||
MAX_DEBUGGING_ITERATIONS = 10
|
||||
|
||||
@@ -5,10 +5,13 @@ import re
|
||||
from src.apis import gpt
|
||||
from src.apis.jina_cloud import process_error_message, push_executor
|
||||
from src.constants import FILE_AND_TAG_PAIRS, NUM_IMPLEMENTATION_STRATEGIES, MAX_DEBUGGING_ITERATIONS, \
|
||||
PROBLEMATIC_PACKAGES
|
||||
from src.options.generate.prompt_tasks import general_guidelines, executor_file_task, \
|
||||
not_allowed_executor, chain_of_thought_optimization, test_executor_file_task, requirements_file_task, \
|
||||
docker_file_task, not_allowed_docker
|
||||
PROBLEMATIC_PACKAGES, EXECUTOR_FILE_NAME, EXECUTOR_FILE_TAG, TEST_EXECUTOR_FILE_NAME, TEST_EXECUTOR_FILE_TAG, \
|
||||
REQUIREMENTS_FILE_NAME, REQUIREMENTS_FILE_TAG, DOCKER_FILE_NAME, DOCKER_FILE_TAG
|
||||
from src.options.generate.templates_user import template_generate_microservice_name, template_generate_possible_packages, \
|
||||
template_solve_code_issue, \
|
||||
template_solve_dependency_issue, template_is_dependency_issue, template_generate_playground, \
|
||||
template_generate_executor, template_generate_test, template_generate_requirements, template_generate_dockerfile, \
|
||||
template_chain_of_thought, template_summarize_error
|
||||
from src.utils.io import persist_file, get_all_microservice_files_with_content, get_microservice_path
|
||||
from src.utils.string_tools import print_colored
|
||||
|
||||
@@ -33,8 +36,7 @@ class Generator:
|
||||
return ''
|
||||
|
||||
def write_config_yml(self, microservice_name, dest_folder):
|
||||
config_content = f'''
|
||||
jtype: {microservice_name}
|
||||
config_content = f'''jtype: {microservice_name}
|
||||
py_modules:
|
||||
- microservice.py
|
||||
metas:
|
||||
@@ -43,23 +45,15 @@ metas:
|
||||
with open(os.path.join(dest_folder, 'config.yml'), 'w') as f:
|
||||
f.write(config_content)
|
||||
|
||||
def files_to_string(self, file_name_to_content):
|
||||
def files_to_string(self, file_name_to_content, restrict_keys=None):
|
||||
all_microservice_files_string = ''
|
||||
for file_name, tag in FILE_AND_TAG_PAIRS:
|
||||
if file_name in file_name_to_content:
|
||||
all_microservice_files_string += f'**{file_name}**\n'
|
||||
all_microservice_files_string += f'```{tag}\n'
|
||||
all_microservice_files_string += file_name_to_content[file_name]
|
||||
all_microservice_files_string += '\n```'
|
||||
return all_microservice_files_string
|
||||
|
||||
def wrap_content_in_code_block(self, microservice_content, file_name, tag):
|
||||
return f'**{file_name}**\n```{tag}\n{microservice_content}\n```\n\n'
|
||||
if file_name in file_name_to_content and (not restrict_keys or file_name in restrict_keys):
|
||||
all_microservice_files_string += f'**{file_name}**\n```{tag}\n{file_name_to_content[file_name]}\n```\n\n'
|
||||
return all_microservice_files_string.strip()
|
||||
|
||||
def generate_microservice(
|
||||
self,
|
||||
description,
|
||||
test,
|
||||
path,
|
||||
microservice_name,
|
||||
packages,
|
||||
@@ -68,111 +62,121 @@ metas:
|
||||
MICROSERVICE_FOLDER_v1 = get_microservice_path(path, microservice_name, packages, num_approach, 1)
|
||||
os.makedirs(MICROSERVICE_FOLDER_v1)
|
||||
|
||||
print_colored('', '############# Microservice #############', 'blue')
|
||||
user_query = (
|
||||
general_guidelines()
|
||||
+ executor_file_task(microservice_name, description, test, packages)
|
||||
)
|
||||
print_colored('', '\n############# Microservice #############', 'blue')
|
||||
conversation = self.gpt_session.get_conversation()
|
||||
microservice_content_raw = conversation.query(user_query)
|
||||
microservice_content = self.extract_content_from_result(microservice_content_raw, 'microservice.py',
|
||||
match_single_block=True)
|
||||
microservice_content_raw = conversation.chat(
|
||||
template_generate_executor.format(
|
||||
microservice_name=microservice_name,
|
||||
microservice_description=self.task_description,
|
||||
test=self.test_description,
|
||||
packages=packages,
|
||||
file_name_purpose=EXECUTOR_FILE_NAME,
|
||||
tag_name=EXECUTOR_FILE_TAG,
|
||||
file_name=EXECUTOR_FILE_NAME,
|
||||
)
|
||||
)
|
||||
microservice_content = self.extract_content_from_result(
|
||||
microservice_content_raw, 'microservice.py', match_single_block=True
|
||||
)
|
||||
if microservice_content == '':
|
||||
microservice_content_raw = conversation.query('You must add the executor code.')
|
||||
microservice_content_raw = conversation.chat('You must add the executor code.')
|
||||
microservice_content = self.extract_content_from_result(
|
||||
microservice_content_raw, 'microservice.py', match_single_block=True
|
||||
)
|
||||
persist_file(microservice_content, os.path.join(MICROSERVICE_FOLDER_v1, 'microservice.py'))
|
||||
|
||||
print_colored('', '############# Test Microservice #############', 'blue')
|
||||
user_query = (
|
||||
general_guidelines()
|
||||
+ self.wrap_content_in_code_block(microservice_content, 'microservice.py', 'python')
|
||||
+ test_executor_file_task(microservice_name, test)
|
||||
)
|
||||
print_colored('', '\n############# Test Microservice #############', 'blue')
|
||||
conversation = self.gpt_session.get_conversation()
|
||||
test_microservice_content_raw = conversation.query(user_query)
|
||||
test_microservice_content_raw = conversation.chat(
|
||||
template_generate_test.format(
|
||||
code_files_wrapped=self.files_to_string({'microservice.py': microservice_content}),
|
||||
microservice_name=microservice_name,
|
||||
test_description=self.test_description,
|
||||
file_name_purpose=TEST_EXECUTOR_FILE_NAME,
|
||||
tag_name=TEST_EXECUTOR_FILE_TAG,
|
||||
file_name=TEST_EXECUTOR_FILE_NAME,
|
||||
)
|
||||
)
|
||||
test_microservice_content = self.extract_content_from_result(
|
||||
test_microservice_content_raw, 'microservice.py', match_single_block=True
|
||||
)
|
||||
persist_file(test_microservice_content, os.path.join(MICROSERVICE_FOLDER_v1, 'test_microservice.py'))
|
||||
|
||||
print_colored('', '############# Requirements #############', 'blue')
|
||||
print_colored('', '\n############# Requirements #############', 'blue')
|
||||
requirements_path = os.path.join(MICROSERVICE_FOLDER_v1, 'requirements.txt')
|
||||
user_query = (
|
||||
general_guidelines()
|
||||
+ self.wrap_content_in_code_block(microservice_content, 'microservice.py', 'python')
|
||||
+ self.wrap_content_in_code_block(test_microservice_content, 'test_microservice.py', 'python')
|
||||
+ requirements_file_task()
|
||||
)
|
||||
conversation = self.gpt_session.get_conversation()
|
||||
requirements_content_raw = conversation.query(user_query)
|
||||
requirements_content_raw = conversation.chat(
|
||||
template_generate_requirements.format(
|
||||
code_files_wrapped=self.files_to_string(
|
||||
{'microservice.py': microservice_content, 'test_microservice.py': test_microservice_content}
|
||||
),
|
||||
file_name_purpose=REQUIREMENTS_FILE_NAME,
|
||||
file_name=REQUIREMENTS_FILE_NAME,
|
||||
tag_name=REQUIREMENTS_FILE_TAG,
|
||||
)
|
||||
)
|
||||
|
||||
requirements_content = self.extract_content_from_result(requirements_content_raw, 'requirements.txt',
|
||||
match_single_block=True)
|
||||
persist_file(requirements_content, requirements_path)
|
||||
|
||||
print_colored('', '############# Dockerfile #############', 'blue')
|
||||
user_query = (
|
||||
general_guidelines()
|
||||
+ self.wrap_content_in_code_block(microservice_content, 'microservice.py', 'python')
|
||||
+ self.wrap_content_in_code_block(test_microservice_content, 'test_microservice.py', 'python')
|
||||
+ self.wrap_content_in_code_block(requirements_content, 'requirements.txt', '')
|
||||
+ docker_file_task()
|
||||
)
|
||||
print_colored('', '\n############# Dockerfile #############', 'blue')
|
||||
conversation = self.gpt_session.get_conversation()
|
||||
dockerfile_content_raw = conversation.query(user_query)
|
||||
dockerfile_content = self.extract_content_from_result(dockerfile_content_raw, 'Dockerfile',
|
||||
match_single_block=True)
|
||||
dockerfile_content_raw = conversation.chat(
|
||||
template_generate_dockerfile.format(
|
||||
code_files_wrapped=self.files_to_string(
|
||||
{
|
||||
'microservice.py': microservice_content,
|
||||
'test_microservice.py': test_microservice_content,
|
||||
'requirements.txt': requirements_content,
|
||||
}
|
||||
),
|
||||
file_name_purpose=DOCKER_FILE_NAME,
|
||||
file_name=DOCKER_FILE_NAME,
|
||||
tag_name=DOCKER_FILE_TAG,
|
||||
)
|
||||
)
|
||||
dockerfile_content = self.extract_content_from_result(
|
||||
dockerfile_content_raw, 'Dockerfile', match_single_block=True
|
||||
)
|
||||
persist_file(dockerfile_content, os.path.join(MICROSERVICE_FOLDER_v1, 'Dockerfile'))
|
||||
|
||||
self.write_config_yml(microservice_name, MICROSERVICE_FOLDER_v1)
|
||||
print('First version of the microservice generated. Start iterating on it to make the tests pass...')
|
||||
print('\nFirst version of the microservice generated. Start iterating on it to make the tests pass...')
|
||||
|
||||
def generate_playground(self, microservice_name, microservice_path):
|
||||
print_colored('', '############# Playground #############', 'blue')
|
||||
print_colored('', '\n############# Playground #############', 'blue')
|
||||
|
||||
file_name_to_content = get_all_microservice_files_with_content(microservice_path)
|
||||
user_query = (
|
||||
general_guidelines()
|
||||
+ self.wrap_content_in_code_block(file_name_to_content['microservice.py'], 'microservice.py', 'python')
|
||||
+ self.wrap_content_in_code_block(file_name_to_content['test_microservice.py'], 'test_microservice.py',
|
||||
'python')
|
||||
+ f'''
|
||||
Create a playground for the executor {microservice_name} using streamlit.
|
||||
The playground must look like it was made by a professional designer.
|
||||
All the ui elements are well thought out to make them visually appealing and easy to use.
|
||||
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('/', inputs=DocumentArray([d])) # always use '/'
|
||||
print(response[0].text) # can also be blob in case of image/audio..., this should be visualized in the streamlit app
|
||||
```
|
||||
Note that the response will always be in response[0].text
|
||||
You must provide the complete app.py file with the exact same syntax to wrap the code.
|
||||
The playground (app.py) must read the host from sys.argv[-1] because it will be started with a custom host: streamlit run app.py -- --host grpc://...
|
||||
The playground (app.py) must not let the user configure the host on the ui.
|
||||
'''
|
||||
)
|
||||
conversation = self.gpt_session.get_conversation([])
|
||||
conversation.query(user_query)
|
||||
playground_content_raw = conversation.query(chain_of_thought_optimization('python', 'app.py', 'the playground'))
|
||||
conversation.chat(
|
||||
template_generate_playground.format(
|
||||
code_files_wrapped=self.files_to_string(file_name_to_content, ['microservice.py', 'test_microservice.py']),
|
||||
microservice_name=microservice_name,
|
||||
|
||||
)
|
||||
)
|
||||
playground_content_raw = conversation.chat(
|
||||
template_chain_of_thought.format(
|
||||
file_name_purpose='app.py/the playground',
|
||||
file_name='app.py',
|
||||
tag_name='python',
|
||||
)
|
||||
)
|
||||
playground_content = self.extract_content_from_result(playground_content_raw, 'app.py', match_single_block=True)
|
||||
persist_file(playground_content, os.path.join(microservice_path, 'app.py'))
|
||||
|
||||
def debug_microservice(self, path, microservice_name, num_approach, packages, description, test):
|
||||
def debug_microservice(self, path, microservice_name, num_approach, packages):
|
||||
for i in range(1, MAX_DEBUGGING_ITERATIONS):
|
||||
print('Debugging iteration', i)
|
||||
print('Trying to build the microservice. Might take a while...')
|
||||
print('Trying to debug the microservice. Might take a while...')
|
||||
previous_microservice_path = get_microservice_path(path, microservice_name, packages, num_approach, i)
|
||||
next_microservice_path = get_microservice_path(path, microservice_name, packages, num_approach, i + 1)
|
||||
log_hubble = push_executor(previous_microservice_path)
|
||||
error = process_error_message(log_hubble)
|
||||
if error:
|
||||
print('An error occurred during the build process. Feeding the error back to the assistent...')
|
||||
self.do_debug_iteration(description, error, next_microservice_path,
|
||||
previous_microservice_path, test)
|
||||
self.do_debug_iteration(error, next_microservice_path, previous_microservice_path)
|
||||
if i == MAX_DEBUGGING_ITERATIONS - 1:
|
||||
raise self.MaxDebugTimeReachedException('Could not debug the microservice.')
|
||||
else:
|
||||
@@ -181,8 +185,7 @@ The playground (app.py) must not let the user configure the host on the ui.
|
||||
|
||||
return get_microservice_path(path, microservice_name, packages, num_approach, i)
|
||||
|
||||
def do_debug_iteration(self, description, error, next_microservice_path, previous_microservice_path,
|
||||
test):
|
||||
def do_debug_iteration(self, error, next_microservice_path, previous_microservice_path):
|
||||
os.makedirs(next_microservice_path)
|
||||
file_name_to_content = get_all_microservice_files_with_content(previous_microservice_path)
|
||||
|
||||
@@ -193,12 +196,16 @@ The playground (app.py) must not let the user configure the host on the ui.
|
||||
key: val for key, val in file_name_to_content.items() if
|
||||
key in ['requirements.txt', 'Dockerfile']
|
||||
})
|
||||
user_query = self.get_user_query_dependency_issue(all_files_string, summarized_error)
|
||||
user_query = template_solve_dependency_issue.format(
|
||||
summarized_error=summarized_error, all_files_string=all_files_string,
|
||||
)
|
||||
else:
|
||||
user_query = self.get_user_query_code_issue(description, summarized_error, file_name_to_content,
|
||||
test)
|
||||
user_query = template_solve_code_issue.format(
|
||||
task_description=self.task_description, test_description=self.test_description,
|
||||
summarized_error=summarized_error, all_files_string=self.files_to_string(file_name_to_content),
|
||||
)
|
||||
conversation = self.gpt_session.get_conversation()
|
||||
returned_files_raw = conversation.query(user_query)
|
||||
returned_files_raw = conversation.chat(user_query)
|
||||
for file_name, tag in FILE_AND_TAG_PAIRS:
|
||||
updated_file = self.extract_content_from_result(returned_files_raw, file_name)
|
||||
if updated_file and (not is_dependency_issue or file_name in ['requirements.txt', 'Dockerfile']):
|
||||
@@ -207,87 +214,6 @@ The playground (app.py) must not let the user configure the host on the ui.
|
||||
for file_name, content in file_name_to_content.items():
|
||||
persist_file(content, os.path.join(next_microservice_path, file_name))
|
||||
|
||||
|
||||
def get_user_query_dependency_issue(self, all_files_string, summarized_error):
|
||||
user_query = (
|
||||
f'''
|
||||
Your task is to provide guidance on how to solve an error that occurred during the Docker build process.
|
||||
Here is the summary of the error that occurred:
|
||||
{summarized_error}
|
||||
|
||||
To solve this error, you should:
|
||||
1. Suggest 3 to 5 possible solutions on how to solve it. You have no access to the documentation of the package.
|
||||
2. Decide for the best solution and explain it in detail.
|
||||
3. Write down the files that need to be changed, but not files that don't need to be changed.
|
||||
For files that need to be changed, you must provide the complete file with the exact same syntax to wrap the code.
|
||||
Obey the following rules: {not_allowed_docker()}
|
||||
|
||||
You are given the following files:
|
||||
|
||||
{all_files_string}"
|
||||
|
||||
Output all 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 following syntax to wrap the code:
|
||||
|
||||
**...**
|
||||
```
|
||||
...code...
|
||||
```
|
||||
|
||||
Example:
|
||||
|
||||
**requirements.txt**
|
||||
```
|
||||
jina==2.0.0
|
||||
```
|
||||
|
||||
'''
|
||||
)
|
||||
return user_query
|
||||
|
||||
def get_user_query_code_issue(self, description, summarized_error, file_name_to_content, test):
|
||||
all_files_string = self.files_to_string(file_name_to_content)
|
||||
return f'''
|
||||
General rules: {not_allowed_executor()}
|
||||
Here is the description of the task the executor must solve:
|
||||
{description}
|
||||
|
||||
Here is the test scenario the executor must pass:\n{test}
|
||||
Here are all the files I use:
|
||||
{all_files_string}
|
||||
|
||||
|
||||
Here is the summary of the error that occurred:
|
||||
{summarized_error}
|
||||
|
||||
To solve this error, you should:
|
||||
1. Suggest 3 to 5 possible solutions on how to solve it. You have no access to the documentation of the package.
|
||||
2. Decide for the best solution and explain it in detail.
|
||||
3. Write down the files that need to be changed, but not files that don't need to be changed.
|
||||
Obey the following rules:
|
||||
{not_allowed_executor()}
|
||||
{not_allowed_docker()}
|
||||
|
||||
|
||||
Output all 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 following syntax to wrap the code:
|
||||
|
||||
**...**
|
||||
```...
|
||||
...code...
|
||||
```
|
||||
|
||||
Example:
|
||||
|
||||
**microservice.py**
|
||||
```python
|
||||
print('hello world')
|
||||
```
|
||||
|
||||
'''
|
||||
|
||||
class MaxDebugTimeReachedException(BaseException):
|
||||
pass
|
||||
|
||||
@@ -298,68 +224,21 @@ print('hello world')
|
||||
|
||||
print_colored('', 'Is it a dependency issue?', 'blue')
|
||||
conversation = self.gpt_session.get_conversation([])
|
||||
answer = conversation.query(
|
||||
f'Your task is to assist in identifying the root cause of a Docker build error for a python application. '
|
||||
f'The error message is as follows::\n\n{error}\n\n'
|
||||
f'The docker file is as follows:\n\n{docker_file}\n\n'
|
||||
f'Is this a dependency installation failure? Answer with "yes" or "no".'
|
||||
)
|
||||
answer = conversation.chat(template_is_dependency_issue.format(error=error, docker_file=docker_file))
|
||||
return 'yes' in answer.lower()
|
||||
|
||||
def generate_microservice_name(self, description):
|
||||
conversation = self.gpt_session.get_conversation()
|
||||
user_query = f'''
|
||||
Generate a name for the executor matching the description:
|
||||
"{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_raw = conversation.chat(template_generate_microservice_name.format(description=description))
|
||||
name = self.extract_content_from_result(name_raw, 'name.txt')
|
||||
return name
|
||||
|
||||
def get_possible_packages(self, description):
|
||||
def get_possible_packages(self):
|
||||
print_colored('', '############# What packages to use? #############', 'blue')
|
||||
user_query = f'''
|
||||
Here is the task description of the problem you need to solve:
|
||||
"{description}"
|
||||
1. Write down all the non-trivial subtasks you need to solve.
|
||||
2. Find out what is the core problem to solve.
|
||||
3. List up to 15 Python packages that are specifically designed or have functionalities to solve the complete core problem.
|
||||
4. For each of the 15 package think if it fulfills the following requirements:
|
||||
a) specifically designed or have functionalities to solve the complete core problem.
|
||||
b) has a stable api among different versions
|
||||
c) does not have system requirements
|
||||
d) can solve the task when running in a docker container
|
||||
e) the implementation of the core problem using the package would obey the following rules:
|
||||
{not_allowed_executor()}
|
||||
When answering, just write "yes" or "no".
|
||||
|
||||
5. Output the most suitable 5 python packages starting with the best one.
|
||||
If the package is mentioned in the description, then it is automatically the best one.
|
||||
|
||||
The output must be a list of lists wrapped into ``` and starting with **packages.csv** like this:
|
||||
**packages.csv**
|
||||
```
|
||||
package1a, package1b ...
|
||||
package2a, package2b, package2c
|
||||
package3a ...
|
||||
package4a ...
|
||||
package5a ...
|
||||
...
|
||||
```
|
||||
'''
|
||||
conversation = self.gpt_session.get_conversation()
|
||||
packages_raw = conversation.query(user_query)
|
||||
packages_raw = conversation.chat(
|
||||
template_generate_possible_packages.format(description=self.task_description)
|
||||
)
|
||||
packages_csv_string = self.extract_content_from_result(packages_raw, 'packages.csv')
|
||||
packages_list = [[pkg.strip() for pkg in packages_string.split(',')] for packages_string in packages_csv_string.split('\n')]
|
||||
packages_list = packages_list[:NUM_IMPLEMENTATION_STRATEGIES]
|
||||
@@ -368,14 +247,16 @@ package5a ...
|
||||
def generate(self, microservice_path):
|
||||
generated_name = self.generate_microservice_name(self.task_description)
|
||||
microservice_name = f'{generated_name}{random.randint(0, 10_000_000)}'
|
||||
packages_list = self.get_possible_packages(self.task_description)
|
||||
packages_list = [packages for packages in packages_list if len(set(packages).intersection(set(PROBLEMATIC_PACKAGES))) == 0]
|
||||
packages_list = self.get_possible_packages()
|
||||
packages_list = [
|
||||
packages for packages in packages_list if len(set(packages).intersection(set(PROBLEMATIC_PACKAGES))) == 0
|
||||
]
|
||||
for num_approach, packages in enumerate(packages_list):
|
||||
try:
|
||||
self.generate_microservice(self.task_description, self.test_description, microservice_path, microservice_name, packages,
|
||||
num_approach)
|
||||
final_version_path = self.debug_microservice(microservice_path, microservice_name, num_approach,
|
||||
packages, self.task_description, self.test_description)
|
||||
self.generate_microservice(microservice_path, microservice_name, packages, num_approach)
|
||||
final_version_path = self.debug_microservice(
|
||||
microservice_path, microservice_name, num_approach, packages
|
||||
)
|
||||
self.generate_playground(microservice_name, final_version_path)
|
||||
except self.MaxDebugTimeReachedException:
|
||||
print('Could not debug the Microservice with the approach:', packages)
|
||||
@@ -394,11 +275,5 @@ gptdeploy deploy --path {microservice_path}
|
||||
|
||||
def summarize_error(self, error):
|
||||
conversation = self.gpt_session.get_conversation([])
|
||||
user_query = f'''
|
||||
Here is an error message I encountered during the docker build process:
|
||||
"{error}"
|
||||
Your task is to summarize the error message as compact and informative as possible while maintaining all information necessary to debug the core issue.
|
||||
Warnings are not worth mentioning.
|
||||
'''
|
||||
error_summary = conversation.query(user_query)
|
||||
error_summary = conversation.chat(template_summarize_error.format(error=error))
|
||||
return error_summary
|
||||
|
||||
@@ -1,157 +0,0 @@
|
||||
from src.constants import EXECUTOR_FILE_NAME, REQUIREMENTS_FILE_NAME, TEST_EXECUTOR_FILE_NAME, DOCKER_FILE_NAME, \
|
||||
DOCKER_FILE_TAG, CLIENT_FILE_TAG, CLIENT_FILE_NAME, STREAMLIT_FILE_TAG, STREAMLIT_FILE_NAME, EXECUTOR_FILE_TAG, \
|
||||
REQUIREMENTS_FILE_TAG, TEST_EXECUTOR_FILE_TAG
|
||||
|
||||
|
||||
def general_guidelines():
|
||||
return '''
|
||||
The code you write is production ready.
|
||||
Every file starts with comments describing what the code is doing before the first import.
|
||||
Comments can only be written within code blocks.
|
||||
Then all imports are listed.
|
||||
It is important to import all modules that could be needed in the Executor code.
|
||||
Always import:
|
||||
from jina import Executor, DocumentArray, Document, requests
|
||||
import json
|
||||
from io import BytesIO
|
||||
import requests as req
|
||||
|
||||
|
||||
Start from top-level and then fully implement all methods.
|
||||
|
||||
'''
|
||||
|
||||
|
||||
def _task(task, tag_name, file_name, purpose=None):
|
||||
into_string = file_name
|
||||
if purpose:
|
||||
into_string += f"/{purpose}"
|
||||
|
||||
return (
|
||||
task + f"The code will go into {into_string}. Make sure to wrap the code into ``` marks even if you only "
|
||||
f"output code:\n"
|
||||
f"**{file_name}**\n"
|
||||
f"```{tag_name}\n"
|
||||
f"...code...\n"
|
||||
f"```\nYou must provide the complete file with the exact same syntax to wrap the code."
|
||||
)
|
||||
|
||||
|
||||
def executor_file_task(executor_name, executor_description, test_scenario, package):
|
||||
return _task(f'''
|
||||
Write the executor called '{executor_name}'. The name is very important to keep.
|
||||
It matches the following description: '{executor_description}'.
|
||||
It will be tested with the following scenario: '{test_scenario}'.
|
||||
For the implementation use the following package: '{package}'.
|
||||
|
||||
Obey the following rules:
|
||||
Have in mind that d.uri is never a path to a local file. It is always a url.
|
||||
{not_allowed_executor()}
|
||||
Your approach:
|
||||
1. Identify the core challenge when implementing the executor.
|
||||
2. Think about possible solutions for these challenges.
|
||||
3. Decide for one of the solutions.
|
||||
4. Write the code.
|
||||
''', EXECUTOR_FILE_TAG, EXECUTOR_FILE_NAME)
|
||||
|
||||
|
||||
def test_executor_file_task(executor_name, test_scenario):
|
||||
return _task(
|
||||
"Write a small unit test for the executor. "
|
||||
"Start the test with an extensive comment about the test case. "
|
||||
+ (
|
||||
f"Write a single test case that tests the following scenario: '{test_scenario}'. "
|
||||
f"In case the test scenario is not precise enough, test a general case without any assumptions."
|
||||
if test_scenario else ""
|
||||
)
|
||||
+ "Use the following import to import the executor: "
|
||||
f"```\nfrom microservice import {executor_name}\n```"
|
||||
+ not_allowed_executor()
|
||||
+ "The test must not open local files. "
|
||||
+ "The test must not mock a function of the executor. "
|
||||
+ "The test must not use other data than the one provided in the test scenario. ",
|
||||
TEST_EXECUTOR_FILE_TAG,
|
||||
TEST_EXECUTOR_FILE_NAME
|
||||
)
|
||||
|
||||
def requirements_file_task():
|
||||
return _task(
|
||||
"Write the content of the requirements.txt file. "
|
||||
"Make sure to include pytest. "
|
||||
"Make sure that jina==3.14.1. "
|
||||
"Make sure that docarray==0.21.0. "
|
||||
"All versions are fixed using ~=, ==, <, >, <=, >=. The package versions should not have conflicts. ",
|
||||
REQUIREMENTS_FILE_TAG,
|
||||
REQUIREMENTS_FILE_NAME
|
||||
)
|
||||
|
||||
|
||||
def docker_file_task():
|
||||
return _task(
|
||||
"Write the Dockerfile that defines the environment with all necessary dependencies that the executor uses. "
|
||||
"It is important to make sure that all libs are installed that are required by the python packages. "
|
||||
"Usually libraries are installed with apt-get. "
|
||||
"Be aware that the machine the docker container is running on does not have a GPU - only CPU. "
|
||||
"Add the config.yml file to the Dockerfile. Note that the Dockerfile only has access to the files: "
|
||||
"microservice.py, requirements.txt, config.yml, test_microservice.py. "
|
||||
"The base image of the Dockerfile is FROM jinaai/jina:3.14.1-py39-standard. "
|
||||
'The entrypoint is ENTRYPOINT ["jina", "executor", "--uses", "config.yml"]. '
|
||||
'Make sure the all files are in the /workdir. '
|
||||
"The Dockerfile runs the test during the build process. " + not_allowed_docker(),
|
||||
DOCKER_FILE_TAG,
|
||||
DOCKER_FILE_NAME
|
||||
)
|
||||
|
||||
|
||||
def client_file_task():
|
||||
return _task(
|
||||
"Write the client file. ",
|
||||
CLIENT_FILE_TAG,
|
||||
CLIENT_FILE_NAME
|
||||
)
|
||||
|
||||
|
||||
def streamlit_file_task():
|
||||
return _task(
|
||||
"Write the streamlit file allowing to make requests . ",
|
||||
STREAMLIT_FILE_TAG,
|
||||
STREAMLIT_FILE_NAME
|
||||
)
|
||||
|
||||
def chain_of_thought_optimization(tag_name, file_name, file_name_function=None):
|
||||
file_name_or_function = file_name
|
||||
if file_name_function:
|
||||
file_name_or_function += f"/{file_name_function}"
|
||||
return _task(
|
||||
f'First, write down an extensive list of obvious and non-obvious observations about {file_name_or_function} that could need an adjustment. Explain why. '
|
||||
f"Think if all the changes are required and finally decide for the changes you want to make, "
|
||||
f"but you are not allowed disregard the instructions in the previous message. "
|
||||
f"Be very hesitant to change the code. Only make a change if you are sure that it is necessary. "
|
||||
|
||||
f"Output only {file_name_or_function} "
|
||||
f"Write the whole content of {file_name_or_function} - even if you decided to change only a small thing or even nothing. ",
|
||||
tag_name,
|
||||
file_name,
|
||||
file_name_function
|
||||
)
|
||||
|
||||
def not_allowed_executor():
|
||||
return '''
|
||||
The executor and the test must not use the GPU.
|
||||
The executor and the test must not access a database.
|
||||
The executor and the test must not access a display.
|
||||
The executor and the test must not access external apis except it is explicitly mentioned in the description or test case (e.g. by mentioning the api that should be used or by providing a URL to access the data).
|
||||
The executor and the test must not load data from the local file system unless it was created by the executor itself.
|
||||
The executor and the test must not use a pre-trained model unless it is explicitly mentioned in the description.
|
||||
The executor and the test must not train a model.
|
||||
The executor and the test must not use any attribute of Document accept Document.text.
|
||||
The executor and the test must not contain prototype or placeholder implementations.
|
||||
The executor and the test must run in a docker container based on debian.
|
||||
'''
|
||||
|
||||
def not_allowed_docker():
|
||||
return '''
|
||||
Note that the Dockerfile only has access to the files: microservice.py, requirements.txt, config.yml, test_microservice.py.
|
||||
Note that the Dockerfile runs the test_microservice.py during the build process.
|
||||
The Dockerfile must not attach a virtual display when running test_microservice.py.
|
||||
'''
|
||||
@@ -1,5 +1,5 @@
|
||||
from src.constants import FLOW_URL_PLACEHOLDER
|
||||
from src.options.generate.prompt_tasks import not_allowed_executor, not_allowed_docker
|
||||
from src.options.generate.templates_user import not_allowed_docker_string, not_allowed_executor_string
|
||||
|
||||
executor_example = '''Using the Jina framework, users can define executors.
|
||||
Here is an example of how an executor can be defined. It always starts with a comment:
|
||||
@@ -72,22 +72,18 @@ print(response[0].text)
|
||||
```'''
|
||||
|
||||
|
||||
def system_base_definition(task_description, test_description):
|
||||
return f'''
|
||||
It is the year 2021.
|
||||
template_system_message_base = f'''It is the year 2021.
|
||||
You are a principal engineer working at Jina - an open source company.
|
||||
You accurately satisfy all of the user's requirements.
|
||||
To be more specific, you help the user to build a microservice with the following requirements:
|
||||
```
|
||||
{task_description}
|
||||
{{task_description}}
|
||||
```
|
||||
and the following test scenario:
|
||||
```
|
||||
{test_description}
|
||||
{{test_description}}
|
||||
```
|
||||
|
||||
You must obey the following rules:
|
||||
{not_allowed_executor()}
|
||||
{not_allowed_docker()}
|
||||
|
||||
'''
|
||||
{not_allowed_executor_string}
|
||||
{not_allowed_docker_string}'''
|
||||
290
src/options/generate/templates_user.py
Normal file
290
src/options/generate/templates_user.py
Normal file
@@ -0,0 +1,290 @@
|
||||
from langchain import PromptTemplate
|
||||
|
||||
|
||||
general_guidelines_string = '''The code you write is production ready. Every file starts with comments describing what the code is doing before the first import. Comments can only be written within code blocks.
|
||||
Then all imports are listed. It is important to import all modules that could be needed in the Executor code. Always import:
|
||||
from jina import Executor, DocumentArray, Document, requests
|
||||
import json
|
||||
from io import BytesIO
|
||||
import requests as req
|
||||
|
||||
Start from top-level and then fully implement all methods.'''
|
||||
|
||||
|
||||
not_allowed_docker_string = '''Note that the Dockerfile only has access to the files: microservice.py, requirements.txt, config.yml, test_microservice.py.
|
||||
Note that the Dockerfile runs the test_microservice.py during the build process.
|
||||
The Dockerfile must not attach a virtual display when running test_microservice.py.'''
|
||||
|
||||
|
||||
not_allowed_executor_string = '''The executor and the test must not use the GPU.
|
||||
The executor and the test must not access a database.
|
||||
The executor and the test must not access a display.
|
||||
The executor and the test must not access external apis except unless it is explicitly mentioned in the description or test case (e.g. by mentioning the api that should be used or by providing a URL to access the data).
|
||||
The executor and the test must not load data from the local file system unless it was created by the executor itself.
|
||||
The executor and the test must not use a pre-trained model unless it is explicitly mentioned in the description.
|
||||
The executor and the test must not train a model.
|
||||
The executor and the test must not use any attribute of Document accept Document.text.
|
||||
The executor and the test must not contain prototype or placeholder implementations.
|
||||
The executor and the test must run in a docker container based on debian.'''
|
||||
|
||||
|
||||
template_generate_microservice_name = PromptTemplate.from_template(
|
||||
'''Generate a name for the executor matching the description:
|
||||
"{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
|
||||
```'''
|
||||
)
|
||||
|
||||
|
||||
template_generate_possible_packages = PromptTemplate.from_template(
|
||||
'''Here is the task description of the problem you need to solve:
|
||||
"{description}"
|
||||
1. Write down all the non-trivial subtasks you need to solve.
|
||||
2. Find out what is the core problem to solve.
|
||||
3. List up to 15 Python packages that are specifically designed or have functionalities to solve the complete core problem.
|
||||
4. For each of the 15 package think if it fulfills the following requirements:
|
||||
a) specifically designed or have functionalities to solve the complete core problem.
|
||||
b) has a stable api among different versions
|
||||
c) does not have system requirements
|
||||
d) can solve the task when running in a docker container
|
||||
e) the implementation of the core problem using the package would obey the following rules:
|
||||
''' + not_allowed_executor_string + '''
|
||||
|
||||
When answering, just write "yes" or "no".
|
||||
|
||||
5. Output the most suitable 5 python packages starting with the best one.
|
||||
If the package is mentioned in the description, then it is automatically the best one.
|
||||
|
||||
The output must be a list of lists wrapped into ``` and starting with **packages.csv** like this:
|
||||
**packages.csv**
|
||||
```
|
||||
package1a, package1b ...
|
||||
package2a, package2b, package2c
|
||||
package3a ...
|
||||
package4a ...
|
||||
package5a ...
|
||||
...
|
||||
```
|
||||
''')
|
||||
|
||||
|
||||
template_code_wrapping_string = '''The code will go into {file_name_purpose}. Make sure to wrap the code into ``` marks even if you only output code:
|
||||
**{file_name}**
|
||||
```{tag_name}
|
||||
...code...
|
||||
```
|
||||
You must provide the complete file with the exact same syntax to wrap the code.'''
|
||||
|
||||
|
||||
template_generate_executor = PromptTemplate.from_template(
|
||||
general_guidelines_string + '''
|
||||
|
||||
Write the executor called '{microservice_name}'. The name is very important to keep.
|
||||
It matches the following description: '{microservice_description}'.
|
||||
It will be tested with the following scenario: '{test}'.
|
||||
For the implementation use the following package: '{packages}'.
|
||||
|
||||
Obey the following rules:
|
||||
Have in mind that d.uri is never a path to a local file. It is always a url.
|
||||
''' + not_allowed_executor_string + '''
|
||||
|
||||
Your approach:
|
||||
1. Identify the core challenge when implementing the executor.
|
||||
2. Think about solutions for these challenges.
|
||||
3. Decide for one of the solutions.
|
||||
4. Write the code.
|
||||
''' + '\n' + template_code_wrapping_string
|
||||
)
|
||||
|
||||
|
||||
template_generate_test = PromptTemplate.from_template(
|
||||
general_guidelines_string + '''
|
||||
|
||||
{code_files_wrapped}
|
||||
|
||||
Write a single test case that tests the following scenario: '{test_description}'. In case the test scenario is not precise enough, test a general case without any assumptions.
|
||||
Start the test with an extensive comment about the test case.
|
||||
|
||||
Use the following import to import the executor:
|
||||
```
|
||||
from microservice import {microservice_name}
|
||||
```
|
||||
|
||||
''' + not_allowed_executor_string + '''
|
||||
The test must not open local files.
|
||||
The test must not mock a function of the executor.
|
||||
The test must not use other data than the one provided in the test scenario.
|
||||
''' + '\n' + template_code_wrapping_string
|
||||
)
|
||||
|
||||
|
||||
template_generate_requirements = PromptTemplate.from_template(
|
||||
general_guidelines_string + '''
|
||||
|
||||
{code_files_wrapped}
|
||||
|
||||
Write the content of the requirements.txt file. Make sure to include pytest. Make sure that jina==3.14.1. Make sure that docarray==0.21.0.
|
||||
All versions are fixed using ~=, ==, <, >, <=, >=. The package versions must not have conflicts.
|
||||
''' + '\n' + template_code_wrapping_string
|
||||
)
|
||||
|
||||
|
||||
template_generate_dockerfile = PromptTemplate.from_template(
|
||||
general_guidelines_string + '''
|
||||
|
||||
{code_files_wrapped}
|
||||
|
||||
Write the Dockerfile that defines the environment with all necessary dependencies that the executor uses.
|
||||
It is important to make sure that all libs are installed that are required by the python packages.
|
||||
Usually libraries are installed with apt-get.
|
||||
Be aware that the machine the docker container is running on does not have a GPU - only CPU.
|
||||
Add the config.yml file to the Dockerfile.
|
||||
Note that the Dockerfile only has access to the files: microservice.py, requirements.txt, config.yml, test_microservice.py.
|
||||
The base image of the Dockerfile is FROM jinaai/jina:3.14.1-py39-standard.
|
||||
The entrypoint is ENTRYPOINT ["jina", "executor", "--uses", "config.yml"].
|
||||
Make sure the all files are in the /workdir.
|
||||
The Dockerfile runs the test during the build process.
|
||||
''' + not_allowed_docker_string + '\n' + template_code_wrapping_string
|
||||
)
|
||||
|
||||
|
||||
template_summarize_error = PromptTemplate.from_template(
|
||||
'''Here is an error message I encountered during the docker build process:
|
||||
"{error}"
|
||||
Your task is to summarize the error message as compact and informative as possible while maintaining all information necessary to debug the core issue.
|
||||
Warnings are not worth mentioning.'''
|
||||
)
|
||||
|
||||
|
||||
template_is_dependency_issue = PromptTemplate.from_template(
|
||||
'''Your task is to assist in identifying the root cause of a Docker build error for a python application.
|
||||
The error message is as follows:
|
||||
|
||||
{error}
|
||||
|
||||
The docker file is as follows:
|
||||
|
||||
{docker_file}
|
||||
|
||||
Is this a dependency installation failure? Answer with "yes" or "no".'''
|
||||
)
|
||||
|
||||
|
||||
template_solve_dependency_issue = PromptTemplate.from_template(
|
||||
'''Your task is to provide guidance on how to solve an error that occurred during the Docker build process.
|
||||
Here is the summary of the error that occurred:
|
||||
{summarized_error}
|
||||
|
||||
To solve this error, you should:
|
||||
1. Suggest 3 to 5 possible solutions on how to solve it. You have no access to the documentation of the package.
|
||||
2. Decide for the best solution and explain it in detail.
|
||||
3. Write down the files that need to be changed, but not files that don't need to be changed.
|
||||
For files that need to be changed, you must provide the complete file with the exact same syntax to wrap the code.
|
||||
Obey the following rules:
|
||||
''' + not_allowed_docker_string + '''
|
||||
|
||||
You are given the following files:
|
||||
|
||||
{all_files_string}
|
||||
|
||||
Output all 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 following syntax to wrap the code:
|
||||
|
||||
**...**
|
||||
```
|
||||
...code...
|
||||
```
|
||||
|
||||
Example:
|
||||
|
||||
**requirements.txt**
|
||||
```
|
||||
jina==2.0.0
|
||||
```
|
||||
'''
|
||||
)
|
||||
|
||||
|
||||
template_solve_code_issue = PromptTemplate.from_template(
|
||||
'''General rules:
|
||||
''' + not_allowed_executor_string + '''
|
||||
|
||||
Here is the description of the task the executor must solve:
|
||||
{task_description}
|
||||
|
||||
Here is the test scenario the executor must pass:
|
||||
{test_description}
|
||||
Here are all the files I use:
|
||||
{all_files_string}
|
||||
|
||||
|
||||
Here is the summary of the error that occurred:
|
||||
{summarized_error}
|
||||
|
||||
To solve this error, you should:
|
||||
1. Suggest 3 to 5 possible solutions on how to solve it. You have no access to the documentation of the package.
|
||||
2. Decide for the best solution and explain it in detail.
|
||||
3. Write down the files that need to be changed, but not files that don't need to be changed.
|
||||
Obey the following rules:
|
||||
''' + f'{not_allowed_executor_string}\n{not_allowed_docker_string}' + '''
|
||||
|
||||
Output all 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 following syntax to wrap the code:
|
||||
|
||||
**...**
|
||||
```...
|
||||
...code...
|
||||
```
|
||||
|
||||
Example:
|
||||
|
||||
**microservice.py**
|
||||
```python
|
||||
print('hello world')
|
||||
```'''
|
||||
)
|
||||
|
||||
|
||||
template_generate_playground = PromptTemplate.from_template(
|
||||
general_guidelines_string + '''
|
||||
|
||||
{code_files_wrapped}
|
||||
|
||||
Create a playground for the executor {microservice_name} using streamlit.
|
||||
The playground must look like it was made by a professional designer.
|
||||
All the ui elements are well thought out to make them visually appealing and easy to use.
|
||||
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('/', inputs=DocumentArray([d])) # always use '/'
|
||||
print(response[0].text) # can also be blob in case of image/audio..., this should be visualized in the streamlit app
|
||||
```
|
||||
Note that the response will always be in response[0].text
|
||||
You must provide the complete app.py file with the exact same syntax to wrap the code.
|
||||
The playground (app.py) must read the host from sys.argv because it will be started with a custom host: streamlit run app.py -- --host grpc://...
|
||||
The playground (app.py) must not let the user configure the host on the ui.
|
||||
'''
|
||||
)
|
||||
|
||||
|
||||
template_chain_of_thought = PromptTemplate.from_template(
|
||||
'''First, write down an extensive list of obvious and non-obvious observations about {file_name_purpose} that could need an adjustment. Explain why.
|
||||
Think if all the changes are required and finally decide for the changes you want to make, but you are not allowed disregard the instructions in the previous message.
|
||||
Be very hesitant to change the code. Only make a change if you are sure that it is necessary.
|
||||
|
||||
Output only {file_name_purpose}
|
||||
Write the whole content of {file_name_purpose} - even if you decided to change only a small thing or even nothing.
|
||||
''' + '\n' + template_code_wrapping_string
|
||||
)
|
||||
@@ -31,28 +31,6 @@ def get_all_microservice_files_with_content(folder_path):
|
||||
return file_name_to_content
|
||||
|
||||
|
||||
class GenerationTimeoutError(Exception):
|
||||
pass
|
||||
|
||||
def timeout_generator_wrapper(generator, timeout):
|
||||
def generator_func():
|
||||
for item in generator:
|
||||
yield item
|
||||
|
||||
def wrapper() -> Generator:
|
||||
gen = generator_func()
|
||||
while True:
|
||||
try:
|
||||
with concurrent.futures.ThreadPoolExecutor() as executor:
|
||||
future = executor.submit(next, gen)
|
||||
yield future.result(timeout=timeout)
|
||||
except StopIteration:
|
||||
break
|
||||
except concurrent.futures.TimeoutError:
|
||||
raise GenerationTimeoutError(f"Generation took too long")
|
||||
|
||||
return wrapper()
|
||||
|
||||
@contextmanager
|
||||
def suppress_stdout():
|
||||
original_stdout = sys.stdout
|
||||
|
||||
Reference in New Issue
Block a user