mirror of
https://github.com/aljazceru/dev-gpt.git
synced 2025-12-31 12:24:20 +01:00
feat: new structure
This commit is contained in:
37
src/options/__init__.py
Normal file
37
src/options/__init__.py
Normal file
@@ -0,0 +1,37 @@
|
||||
import os
|
||||
|
||||
|
||||
def get_latest_folder(path):
|
||||
return max([os.path.join(path, f) for f in os.listdir(path) if os.path.isdir(os.path.join(path, f))])
|
||||
|
||||
def get_latest_version_path(microservice_path):
|
||||
executor_name_path = get_latest_folder(microservice_path)
|
||||
latest_approach_path = get_latest_folder(executor_name_path)
|
||||
latest_version_path = get_latest_folder(latest_approach_path)
|
||||
return latest_version_path
|
||||
|
||||
def get_executor_name(microservice_path):
|
||||
return get_latest_folder(microservice_path).split('/')[-1]
|
||||
|
||||
|
||||
def validate_folder_is_correct(microservice_path):
|
||||
if not os.path.exists(microservice_path):
|
||||
raise ValueError(f'Path {microservice_path} does not exist')
|
||||
if not os.path.isdir(microservice_path):
|
||||
raise ValueError(f'Path {microservice_path} is not a directory')
|
||||
if len(os.listdir(microservice_path)) == 0:
|
||||
raise ValueError(f'Path {microservice_path} is empty. Please generate a microservice first. Type `gptdeploy generate` for further instructions.')
|
||||
if len(os.listdir(microservice_path)) > 1:
|
||||
raise ValueError(f'Path {microservice_path} needs to contain only one folder. Please make sure that you only have one microservice in this folder.')
|
||||
latest_version_path = get_latest_version_path(microservice_path)
|
||||
required_files = [
|
||||
'app.py',
|
||||
'requirements.txt',
|
||||
'Dockerfile',
|
||||
'config.yml',
|
||||
'microservice.py',
|
||||
'test_microservice.py',
|
||||
]
|
||||
for file_name in required_files:
|
||||
if not os.path.exists(os.path.join(latest_version_path, file_name)):
|
||||
raise ValueError(f'Path {latest_version_path} needs to contain a file named {file_name}')
|
||||
0
src/options/configure/__init__.py
Normal file
0
src/options/configure/__init__.py
Normal file
140
src/options/configure/key_handling.py
Normal file
140
src/options/configure/key_handling.py
Normal file
@@ -0,0 +1,140 @@
|
||||
import os
|
||||
import platform
|
||||
import re
|
||||
import subprocess
|
||||
|
||||
import click
|
||||
|
||||
try:
|
||||
import psutil
|
||||
except ImportError:
|
||||
psutil = None
|
||||
|
||||
|
||||
def get_shell():
|
||||
if psutil is None:
|
||||
return None
|
||||
|
||||
shell_names = ["bash", "zsh", "sh", "fish", "csh", "tcsh", "ksh", "dash"]
|
||||
|
||||
# Check the SHELL environment variable first
|
||||
shell_env = os.environ.get('SHELL')
|
||||
if shell_env:
|
||||
shell_name = os.path.basename(shell_env)
|
||||
if shell_name in shell_names:
|
||||
return shell_name
|
||||
|
||||
# Fallback to traversing the process tree
|
||||
try:
|
||||
p = psutil.Process(os.getpid())
|
||||
|
||||
# Traverse the process tree
|
||||
while p.parent():
|
||||
p = p.parent()
|
||||
if p.name() in shell_names:
|
||||
return p.name()
|
||||
|
||||
return None
|
||||
except Exception as e:
|
||||
click.echo(f"Error detecting shell: {e}")
|
||||
return None
|
||||
|
||||
|
||||
def get_shell_config(key):
|
||||
return {
|
||||
"bash": {"config_file": "~/.bashrc", "export_line": f"export OPENAI_API_KEY={key}"},
|
||||
"zsh": {"config_file": "~/.zshrc", "export_line": f"export OPENAI_API_KEY={key}"},
|
||||
"sh": {"config_file": "~/.profile", "export_line": f"export OPENAI_API_KEY={key}"},
|
||||
"fish": {
|
||||
"config_file": "~/.config/fish/config.fish",
|
||||
"export_line": f"set -gx OPENAI_API_KEY {key}",
|
||||
},
|
||||
"csh": {"config_file": "~/.cshrc", "export_line": f"setenv OPENAI_API_KEY {key}"},
|
||||
"tcsh": {"config_file": "~/.tcshrc", "export_line": f"setenv OPENAI_API_KEY {key}"},
|
||||
"ksh": {"config_file": "~/.kshrc", "export_line": f"export OPENAI_API_KEY={key}"},
|
||||
"dash": {"config_file": "~/.profile", "export_line": f"export OPENAI_API_KEY={key}"}
|
||||
}
|
||||
|
||||
|
||||
def set_env_variable(shell, key):
|
||||
shell_config = get_shell_config(key)
|
||||
if shell not in shell_config:
|
||||
click.echo("Sorry, your shell is not supported. Please add the key OPENAI_API_KEY manually.")
|
||||
return
|
||||
|
||||
config_file = os.path.expanduser(shell_config[shell]["config_file"])
|
||||
|
||||
try:
|
||||
with open(config_file, "r") as file:
|
||||
content = file.read()
|
||||
|
||||
export_line = shell_config[shell]['export_line']
|
||||
|
||||
# Update the existing API key if it exists, otherwise append it to the config file
|
||||
if f"OPENAI_API_KEY" in content:
|
||||
content = re.sub(r'OPENAI_API_KEY=.*', f'OPENAI_API_KEY={key}', content, flags=re.MULTILINE)
|
||||
|
||||
with open(config_file, "w") as file:
|
||||
file.write(content)
|
||||
else:
|
||||
with open(config_file, "a") as file:
|
||||
file.write(f"\n{export_line}\n")
|
||||
|
||||
click.echo(f'''
|
||||
✅ Success, OPENAI_API_KEY has been set in {config_file}.
|
||||
Please restart your shell to apply the changes or run:
|
||||
source {config_file}
|
||||
'''
|
||||
)
|
||||
|
||||
except FileNotFoundError:
|
||||
click.echo(f"Error: {config_file} not found. Please set the environment variable manually.")
|
||||
|
||||
|
||||
def set_api_key(key):
|
||||
system_platform = platform.system().lower()
|
||||
|
||||
if system_platform == "windows":
|
||||
set_env_variable_command = f'setx OPENAI_API_KEY "{key}"'
|
||||
subprocess.call(set_env_variable_command, shell=True)
|
||||
click.echo('''
|
||||
✅ Success, OPENAI_API_KEY has been set.
|
||||
Please restart your Command Prompt to apply the changes.
|
||||
'''
|
||||
)
|
||||
|
||||
elif system_platform in ["linux", "darwin"]:
|
||||
if "OPENAI_API_KEY" in os.environ or is_key_set_in_config_file(key):
|
||||
if not click.confirm("OPENAI_API_KEY is already set. Do you want to overwrite it?"):
|
||||
click.echo("Aborted.")
|
||||
return
|
||||
|
||||
shell = get_shell()
|
||||
if shell is None:
|
||||
click.echo(
|
||||
"Error: Unable to detect your shell or psutil is not available. Please set the environment variable manually.")
|
||||
return
|
||||
|
||||
set_env_variable(shell, key)
|
||||
else:
|
||||
click.echo("Sorry, this platform is not supported.")
|
||||
|
||||
|
||||
def is_key_set_in_config_file(key):
|
||||
shell = get_shell()
|
||||
if shell is None:
|
||||
return False
|
||||
|
||||
shell_config = get_shell_config(key)
|
||||
|
||||
config_file = os.path.expanduser(shell_config[shell]["config_file"])
|
||||
|
||||
try:
|
||||
with open(config_file, "r") as file:
|
||||
content = file.read()
|
||||
if f"OPENAI_API_KEY" in content:
|
||||
return True
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
|
||||
return False
|
||||
0
src/options/deploy/__init__.py
Normal file
0
src/options/deploy/__init__.py
Normal file
10
src/options/deploy/deployer.py
Normal file
10
src/options/deploy/deployer.py
Normal file
@@ -0,0 +1,10 @@
|
||||
from src.apis.jina_cloud import deploy_on_jcloud
|
||||
from src.options import validate_folder_is_correct, get_executor_name, get_latest_version_path
|
||||
|
||||
|
||||
class Deployer:
|
||||
def deploy(self, microservice_path):
|
||||
validate_folder_is_correct(microservice_path)
|
||||
executor_name = get_executor_name(microservice_path)
|
||||
latest_version_path = get_latest_version_path(microservice_path)
|
||||
deploy_on_jcloud(executor_name, latest_version_path)
|
||||
0
src/options/generate/__init__.py
Normal file
0
src/options/generate/__init__.py
Normal file
289
src/options/generate/generator.py
Normal file
289
src/options/generate/generator.py
Normal file
@@ -0,0 +1,289 @@
|
||||
import os
|
||||
import random
|
||||
import re
|
||||
|
||||
from src.apis import gpt
|
||||
from src.constants import FILE_AND_TAG_PAIRS, NUM_IMPLEMENTATION_STRATEGIES, MAX_DEBUGGING_ITERATIONS
|
||||
from src.apis.jina_cloud import process_error_message, push_executor
|
||||
from src.options.generate.prompt_tasks import general_guidelines, chain_of_thought_creation, executor_file_task, \
|
||||
not_allowed, chain_of_thought_optimization, test_executor_file_task, requirements_file_task, docker_file_task
|
||||
from src.utils.io import persist_file, get_all_microservice_files_with_content, get_microservice_path
|
||||
from src.utils.string_tools import print_colored
|
||||
|
||||
|
||||
class Generator:
|
||||
def __init__(self):
|
||||
self.gpt_session = gpt.GPTSession()
|
||||
|
||||
def extract_content_from_result(self, plain_text, file_name):
|
||||
pattern = fr"^\*\*{file_name}\*\*\n```(?:\w+\n)?([\s\S]*?)```"
|
||||
match = re.search(pattern, plain_text, re.MULTILINE)
|
||||
if match:
|
||||
return match.group(1).strip()
|
||||
else:
|
||||
return ''
|
||||
|
||||
def write_config_yml(self, microservice_name, dest_folder):
|
||||
config_content = f'''
|
||||
jtype: {microservice_name}
|
||||
py_modules:
|
||||
- microservice.py
|
||||
metas:
|
||||
name: {microservice_name}
|
||||
'''
|
||||
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):
|
||||
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```\n\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'
|
||||
|
||||
def generate_microservice(
|
||||
self,
|
||||
description,
|
||||
test,
|
||||
path,
|
||||
microservice_name,
|
||||
package,
|
||||
num_approach,
|
||||
is_chain_of_thought=False,
|
||||
):
|
||||
MICROSERVICE_FOLDER_v1 = get_microservice_path(path, microservice_name, package, num_approach, 1)
|
||||
os.makedirs(MICROSERVICE_FOLDER_v1)
|
||||
|
||||
print_colored('', '############# Microservice #############', 'red')
|
||||
user_query = (
|
||||
general_guidelines()
|
||||
+ executor_file_task(microservice_name, description, test, package)
|
||||
+ chain_of_thought_creation()
|
||||
)
|
||||
conversation = self.gpt_session.get_conversation()
|
||||
microservice_content_raw = conversation.query(user_query)
|
||||
if is_chain_of_thought:
|
||||
microservice_content_raw = conversation.query(
|
||||
f"General rules: " + not_allowed() + chain_of_thought_optimization('python', 'microservice.py'))
|
||||
microservice_content = self.extract_content_from_result(microservice_content_raw, 'microservice.py')
|
||||
|
||||
persist_file(microservice_content, os.path.join(MICROSERVICE_FOLDER_v1, 'microservice.py'))
|
||||
|
||||
print_colored('', '############# Test Microservice #############', 'red')
|
||||
user_query = (
|
||||
general_guidelines()
|
||||
+ self.wrap_content_in_code_block(microservice_content, 'microservice.py', 'python')
|
||||
+ test_executor_file_task(microservice_name, test)
|
||||
)
|
||||
conversation = self.gpt_session.get_conversation()
|
||||
test_microservice_content_raw = conversation.query(user_query)
|
||||
if is_chain_of_thought:
|
||||
test_microservice_content_raw = conversation.query(
|
||||
f"General rules: " + not_allowed() +
|
||||
chain_of_thought_optimization('python', 'test_microservice.py')
|
||||
+ "Don't add any additional tests. "
|
||||
)
|
||||
test_microservice_content = self.extract_content_from_result(test_microservice_content_raw, 'test_microservice.py')
|
||||
persist_file(test_microservice_content, os.path.join(MICROSERVICE_FOLDER_v1, 'test_microservice.py'))
|
||||
|
||||
print_colored('', '############# Requirements #############', 'red')
|
||||
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)
|
||||
if is_chain_of_thought:
|
||||
requirements_content_raw = conversation.query(
|
||||
chain_of_thought_optimization('', requirements_path) + "Keep the same version of jina ")
|
||||
|
||||
requirements_content = self.extract_content_from_result(requirements_content_raw, 'requirements.txt')
|
||||
persist_file(requirements_content, requirements_path)
|
||||
|
||||
print_colored('', '############# Dockerfile #############', 'red')
|
||||
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()
|
||||
)
|
||||
conversation = self.gpt_session.get_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 = self.extract_content_from_result(dockerfile_content_raw, 'Dockerfile')
|
||||
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...')
|
||||
|
||||
def generate_playground(self, microservice_name, microservice_path):
|
||||
print_colored('', '############# Playground #############', 'red')
|
||||
|
||||
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.
|
||||
The playground must be started with a custom host: streamlit run app.py -- --host grpc://...
|
||||
The playground must not let the user configure the --host grpc://... on the ui.
|
||||
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
|
||||
'''
|
||||
)
|
||||
conversation = self.gpt_session.get_conversation()
|
||||
conversation.query(user_query)
|
||||
playground_content_raw = conversation.query(
|
||||
f"General rules: " + not_allowed() + chain_of_thought_optimization('python', 'app.py'))
|
||||
playground_content = self.extract_content_from_result(playground_content_raw, 'app.py')
|
||||
persist_file(playground_content, os.path.join(microservice_path, 'app.py'))
|
||||
|
||||
|
||||
def debug_microservice(self, path, microservice_name, num_approach, packages, description, test):
|
||||
error_before = ''
|
||||
for i in range(1, MAX_DEBUGGING_ITERATIONS):
|
||||
print('Debugging iteration', i)
|
||||
print('Trying to build 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:
|
||||
os.makedirs(next_microservice_path)
|
||||
file_name_to_content = get_all_microservice_files_with_content(previous_microservice_path)
|
||||
all_files_string = self.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'
|
||||
+ description
|
||||
+ '\n\nHere is the test scenario the executor must pass:\n'
|
||||
+ test
|
||||
+ '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 the error might caused by. '
|
||||
'Decide which files need to be changed. '
|
||||
'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 = self.gpt_session.get_conversation()
|
||||
returned_files_raw = conversation.query(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:
|
||||
file_name_to_content[file_name] = updated_file
|
||||
|
||||
for file_name, content in file_name_to_content.items():
|
||||
persist_file(content, os.path.join(next_microservice_path, file_name))
|
||||
error_before = error
|
||||
|
||||
else:
|
||||
break
|
||||
if i == MAX_DEBUGGING_ITERATIONS - 1:
|
||||
raise self.MaxDebugTimeReachedException('Could not debug the microservice.')
|
||||
return get_microservice_path(path, microservice_name, packages, num_approach, i)
|
||||
|
||||
class MaxDebugTimeReachedException(BaseException):
|
||||
pass
|
||||
|
||||
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 = self.extract_content_from_result(name_raw, 'name.txt')
|
||||
return name
|
||||
|
||||
def get_possible_packages(self, description):
|
||||
print_colored('', '############# What package to use? #############', 'red')
|
||||
user_query = f'''
|
||||
Here is the task description of the problme you need to solve:
|
||||
"{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 = self.gpt_session.get_conversation()
|
||||
packages_raw = conversation.query(user_query)
|
||||
packages_csv_string = self.extract_content_from_result(packages_raw, 'packages.csv')
|
||||
packages = [package.split(',') for package in packages_csv_string.split('\n')]
|
||||
packages = packages[:NUM_IMPLEMENTATION_STRATEGIES]
|
||||
return packages
|
||||
|
||||
def generate(self, description, test, microservice_path):
|
||||
generated_name = self.generate_microservice_name(description)
|
||||
microservice_name = f'{generated_name}{random.randint(0, 10_000_000)}'
|
||||
packages_list = self.get_possible_packages(description)
|
||||
for num_approach, packages in enumerate(packages_list):
|
||||
try:
|
||||
self.generate_microservice(description, test, microservice_path, microservice_name, packages, num_approach)
|
||||
final_version_path = self.debug_microservice(microservice_path, microservice_name, num_approach, packages, description, test)
|
||||
self.generate_playground(microservice_name, final_version_path)
|
||||
except self.MaxDebugTimeReachedException:
|
||||
print('Could not debug the Microservice.')
|
||||
continue
|
||||
print(f'''
|
||||
You can now run or deploy your microservice:
|
||||
gptdeploy run --path {microservice_path}
|
||||
gptdeploy deploy --path {microservice_path}
|
||||
'''
|
||||
)
|
||||
break
|
||||
|
||||
80
src/options/generate/prompt_system.py
Normal file
80
src/options/generate/prompt_system.py
Normal file
@@ -0,0 +1,80 @@
|
||||
from src.constants import FLOW_URL_PLACEHOLDER
|
||||
|
||||
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:
|
||||
|
||||
**microservice.py**
|
||||
```python
|
||||
from jina import Executor, requests, DocumentArray, Document
|
||||
import json
|
||||
class MyInfoExecutor(Executor):
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__()
|
||||
|
||||
@requests() # each Executor must have exactly this decorator without parameters
|
||||
def foo(self, docs: DocumentArray, **kwargs) => DocumentArray:
|
||||
for d in docs:
|
||||
content = json.loads(d.text)
|
||||
...
|
||||
d.text = json.dumps(modified_content)
|
||||
return docs
|
||||
```
|
||||
|
||||
An Executor gets a DocumentArray as input and returns a DocumentArray as output.
|
||||
'''
|
||||
|
||||
docarray_example = f'''
|
||||
A DocumentArray is a python class that can be seen as a list of Documents.
|
||||
A Document is a python class that represents a single document.
|
||||
Here is the protobuf definition of a Document:
|
||||
|
||||
message DocumentProto {{
|
||||
// used to store json data the executor gets and returns
|
||||
string text = 1;
|
||||
}}
|
||||
|
||||
Here are examples of how a DocumentArray can be defined:
|
||||
|
||||
from jina import DocumentArray, Document
|
||||
import json
|
||||
|
||||
d1 = Document(text=json.dumps({{'he_says': 'hello'}}))
|
||||
|
||||
# you can load binary data into a document
|
||||
url = 'https://...'
|
||||
response = requests.get(url)
|
||||
obj_data = response.content
|
||||
base64_data = base64.b64encode(png_data).decode('utf-8')
|
||||
d2 = Document(text=json.dumps({{'image': base64_data}}))
|
||||
|
||||
array = numpy.array([1, 2, 3])
|
||||
array_list = array.tolist()
|
||||
d3 = Document(text=json.dumps(array_list))
|
||||
d4 = Document()
|
||||
d4.text = '{{"uri": "https://.../logo.png"}}'
|
||||
'''
|
||||
|
||||
|
||||
client_example = f'''
|
||||
After the executor is deployed, it can be called via Jina Client.
|
||||
Here is an example of a client file:
|
||||
|
||||
**client.py**
|
||||
```python
|
||||
from jina import Client, Document, DocumentArray
|
||||
client = Client(host='{FLOW_URL_PLACEHOLDER}')
|
||||
d = Document(uri='...')
|
||||
d.load_uri_to_blob()
|
||||
response = client.post('/', inputs=DocumentArray([d])) # the client must be called on '/'
|
||||
print(response[0].text)
|
||||
```
|
||||
'''
|
||||
|
||||
|
||||
system_base_definition = f'''
|
||||
You are a principal engineer working at Jina - an open source company."
|
||||
{executor_example}
|
||||
{docarray_example}
|
||||
{client_example}
|
||||
'''
|
||||
138
src/options/generate/prompt_tasks.py
Normal file
138
src/options/generate/prompt_tasks.py
Normal file
@@ -0,0 +1,138 @@
|
||||
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 "
|
||||
"Start from top-level and then fully implement all methods. "
|
||||
"\n"
|
||||
)
|
||||
|
||||
|
||||
def _task(task, tag_name, file_name):
|
||||
return (
|
||||
task + f"The code will go into {file_name}. Wrap the code into:\n"
|
||||
f"**{file_name}**\n"
|
||||
f"```{tag_name}\n"
|
||||
f"...code...\n"
|
||||
f"```\n\n"
|
||||
)
|
||||
|
||||
|
||||
def executor_file_task(executor_name, executor_description, test_scenario, package):
|
||||
return _task(f'''
|
||||
Write the executor called '{executor_name}'.
|
||||
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}'.
|
||||
Have in mind that d.uri is never a path to a local file. It is always a url.
|
||||
''' + not_allowed(),
|
||||
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 the most general case without any assumptions."
|
||||
if test_scenario else ""
|
||||
)
|
||||
+ "Use the following import to import the executor: "
|
||||
f"from microservice import {executor_name} "
|
||||
+ not_allowed()
|
||||
+ "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. "
|
||||
"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. "
|
||||
"The Dockerfile runs the test during the build process. "
|
||||
"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. "
|
||||
"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_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_creation():
|
||||
return (
|
||||
"First, write down some non-obvious thoughts about the challenges of the task and give multiple approaches on how you handle them. "
|
||||
"For example, the given package you could used in different ways and not all of them obay the rules: "
|
||||
+ "Discuss the pros and cons for all of these approaches and then decide for one of the approaches. "
|
||||
"Then write as I told you. "
|
||||
)
|
||||
|
||||
|
||||
def chain_of_thought_optimization(tag_name, file_name):
|
||||
return _task(
|
||||
f'First, write down an extensive list of obvious and non-obvious observations about {file_name} 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} "
|
||||
f"Write the whole content of {file_name} - even if you decided to change only a small thing or even nothing. ",
|
||||
tag_name,
|
||||
file_name
|
||||
)
|
||||
|
||||
def not_allowed():
|
||||
return '''
|
||||
The executor must not use the GPU.
|
||||
The executor must not access a database.
|
||||
The executor must not access a display.
|
||||
The executor 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 must not load data from the local file system unless it was created by the executor itself.
|
||||
The executor must not use a pre-trained model unless it is explicitly mentioned in the description.
|
||||
The executor must not train a model.
|
||||
The executor must not use any attribute of Document accept Document.text.
|
||||
'''
|
||||
1
src/options/run/__init__.py
Normal file
1
src/options/run/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from src.options.run.runner import Runner
|
||||
11
src/options/run/runner.py
Normal file
11
src/options/run/runner.py
Normal file
@@ -0,0 +1,11 @@
|
||||
from src.apis.jina_cloud import run_locally
|
||||
from src.options import validate_folder_is_correct, get_executor_name, get_latest_version_path
|
||||
|
||||
|
||||
class Runner():
|
||||
def run(self, microservice_path):
|
||||
validate_folder_is_correct(microservice_path)
|
||||
executor_name = get_executor_name(microservice_path)
|
||||
latest_version_path = get_latest_version_path(microservice_path)
|
||||
run_locally(executor_name, latest_version_path)
|
||||
|
||||
Reference in New Issue
Block a user