agenta 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of agenta might be problematic. Click here for more details.
- agenta/__init__.py +1 -0
- agenta/agenta.py +109 -0
- agenta/cli.py +168 -0
- agenta/client/Readme.md +3 -0
- agenta/client/__init__.py +0 -0
- agenta/client/api_models.py +18 -0
- agenta/client/client.py +39 -0
- agenta/config.py +21 -0
- agenta/config.toml +3 -0
- agenta/docker/docker-assets/Dockerfile.template +13 -0
- agenta/docker/docker-assets/README.md +1 -0
- agenta/docker/docker-assets/main.py +8 -0
- agenta/docker/docker_utils.py +75 -0
- agenta/templates/simple_prompt/README.md +9 -0
- agenta/templates/simple_prompt/app.py +20 -0
- agenta/templates/simple_prompt/requirements.txt +3 -0
- agenta/templates/simple_prompt/template.toml +1 -0
- agenta-0.1.0.dist-info/METADATA +118 -0
- agenta-0.1.0.dist-info/RECORD +21 -0
- agenta-0.1.0.dist-info/WHEEL +4 -0
- agenta-0.1.0.dist-info/entry_points.txt +3 -0
agenta/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .agenta import post, get, TextParam, FloatParam
|
agenta/agenta.py
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import functools
|
|
3
|
+
import inspect
|
|
4
|
+
import os
|
|
5
|
+
import sys
|
|
6
|
+
from typing import Any, Callable, Optional
|
|
7
|
+
|
|
8
|
+
from dotenv import load_dotenv
|
|
9
|
+
from fastapi import FastAPI
|
|
10
|
+
from fastapi.middleware.cors import CORSMiddleware
|
|
11
|
+
|
|
12
|
+
app = FastAPI()
|
|
13
|
+
|
|
14
|
+
origins = [
|
|
15
|
+
"http://localhost:3000",
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
app.add_middleware(
|
|
19
|
+
CORSMiddleware,
|
|
20
|
+
allow_origins=origins,
|
|
21
|
+
allow_credentials=True,
|
|
22
|
+
allow_methods=["*"],
|
|
23
|
+
allow_headers=["*"],
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class TextParam(str):
|
|
28
|
+
|
|
29
|
+
@classmethod
|
|
30
|
+
def __modify_schema__(cls, field_schema):
|
|
31
|
+
field_schema.update({"x-parameter": "text"})
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class FloatParam(float):
|
|
35
|
+
|
|
36
|
+
@classmethod
|
|
37
|
+
def __modify_schema__(cls, field_schema):
|
|
38
|
+
field_schema.update({"x-parameter": "float"})
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def post(func: Callable[..., Any]):
|
|
42
|
+
load_dotenv() # TODO: remove later when we have a better way to inject env variables
|
|
43
|
+
sig = inspect.signature(func)
|
|
44
|
+
func_params = sig.parameters
|
|
45
|
+
|
|
46
|
+
# find the optional parameters for the app
|
|
47
|
+
app_params = {name: param for name, param in func_params.items()
|
|
48
|
+
if param.annotation in {TextParam, FloatParam}}
|
|
49
|
+
# find the default values for the optional parameters
|
|
50
|
+
for name, param in app_params.items():
|
|
51
|
+
default_value = param.default if param.default is not param.empty else None
|
|
52
|
+
app_params[name] = default_value
|
|
53
|
+
|
|
54
|
+
@functools.wraps(func)
|
|
55
|
+
def wrapper(*args, **kwargs):
|
|
56
|
+
kwargs = {**app_params, **kwargs}
|
|
57
|
+
return func(*args, **kwargs)
|
|
58
|
+
|
|
59
|
+
new_params = []
|
|
60
|
+
for name, param in sig.parameters.items():
|
|
61
|
+
if name in app_params:
|
|
62
|
+
new_params.append(
|
|
63
|
+
inspect.Parameter(
|
|
64
|
+
name,
|
|
65
|
+
inspect.Parameter.KEYWORD_ONLY,
|
|
66
|
+
default=app_params[name],
|
|
67
|
+
annotation=Optional[param.annotation]
|
|
68
|
+
|
|
69
|
+
)
|
|
70
|
+
)
|
|
71
|
+
else:
|
|
72
|
+
new_params.append(param)
|
|
73
|
+
|
|
74
|
+
wrapper.__signature__ = sig.replace(parameters=new_params)
|
|
75
|
+
|
|
76
|
+
route = "/generate"
|
|
77
|
+
app.post(route)(wrapper)
|
|
78
|
+
|
|
79
|
+
# check if the module is being run as the main script
|
|
80
|
+
if os.path.splitext(os.path.basename(sys.argv[0]))[0] == os.path.splitext(os.path.basename(inspect.getfile(func)))[0]:
|
|
81
|
+
parser = argparse.ArgumentParser()
|
|
82
|
+
# add arguments to the command-line parser
|
|
83
|
+
for name, param in sig.parameters.items():
|
|
84
|
+
if name in app_params:
|
|
85
|
+
# For optional parameters, we add them as options
|
|
86
|
+
parser.add_argument(f"--{name}", type=type(param.default),
|
|
87
|
+
default=param.default)
|
|
88
|
+
else:
|
|
89
|
+
# For required parameters, we add them as arguments
|
|
90
|
+
parser.add_argument(name, type=param.annotation)
|
|
91
|
+
|
|
92
|
+
args = parser.parse_args()
|
|
93
|
+
print(func(**vars(args)))
|
|
94
|
+
|
|
95
|
+
return wrapper
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def get(func):
|
|
99
|
+
"""get decorator
|
|
100
|
+
|
|
101
|
+
Arguments:
|
|
102
|
+
func -- _description_
|
|
103
|
+
|
|
104
|
+
Returns:
|
|
105
|
+
_description_
|
|
106
|
+
"""
|
|
107
|
+
route = f"/{func.__name__}"
|
|
108
|
+
app.get(route)(func)
|
|
109
|
+
return func
|
agenta/cli.py
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import shutil
|
|
3
|
+
import sys
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
import click
|
|
7
|
+
import questionary
|
|
8
|
+
import toml
|
|
9
|
+
from agenta.client import client
|
|
10
|
+
from agenta.docker.docker_utils import build_and_upload_docker_image
|
|
11
|
+
from docker.models.images import Image as DockerImage
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def add_variant(variant_name: str, app_folder: str) -> str:
|
|
15
|
+
"""Add a new variant.
|
|
16
|
+
Returns the name of the variant. (useful for serve)"""
|
|
17
|
+
app_path = Path(app_folder)
|
|
18
|
+
config_file = app_path / 'config.toml'
|
|
19
|
+
if not config_file.exists():
|
|
20
|
+
click.echo("Please run agenta init first")
|
|
21
|
+
return None
|
|
22
|
+
else:
|
|
23
|
+
config = toml.load(config_file)
|
|
24
|
+
app_name = config['app-name']
|
|
25
|
+
if 'variants' not in config:
|
|
26
|
+
config['variants'] = []
|
|
27
|
+
app_file = app_path / 'app.py'
|
|
28
|
+
if not app_file.exists():
|
|
29
|
+
click.echo(click.style(f"No app.py exists! Please make sure you are in the right directory", fg='red'))
|
|
30
|
+
return None
|
|
31
|
+
env_file = app_path / '.env'
|
|
32
|
+
if not env_file.exists():
|
|
33
|
+
continue_without_env = questionary.confirm(
|
|
34
|
+
'No .env file found! Are you sure you handled the API keys needed in your application?\n Do you want to continue without it?').ask()
|
|
35
|
+
if not continue_without_env:
|
|
36
|
+
click.echo("Operation cancelled.")
|
|
37
|
+
sys.exit(0)
|
|
38
|
+
|
|
39
|
+
if not variant_name:
|
|
40
|
+
variant_name = questionary.text('Please enter the variant name').ask()
|
|
41
|
+
|
|
42
|
+
if variant_name in config['variants']:
|
|
43
|
+
overwrite = questionary.confirm(
|
|
44
|
+
'This variant already exists. Do you want to overwrite it?').ask()
|
|
45
|
+
if not overwrite:
|
|
46
|
+
click.echo("Operation cancelled.")
|
|
47
|
+
sys.exit(0)
|
|
48
|
+
else:
|
|
49
|
+
config['variants'].append(variant_name)
|
|
50
|
+
try:
|
|
51
|
+
docker_image: DockerImage = build_and_upload_docker_image(
|
|
52
|
+
folder=app_path, app_name=app_name, variant_name=variant_name)
|
|
53
|
+
except Exception as ex:
|
|
54
|
+
click.echo(click.style(f"Error while building image: {ex}", fg='red'))
|
|
55
|
+
return None
|
|
56
|
+
try:
|
|
57
|
+
client.add_variant_to_server(app_name, variant_name, docker_image)
|
|
58
|
+
except Exception as ex:
|
|
59
|
+
click.echo(click.style(f"Error while adding variant: {ex}", fg='red'))
|
|
60
|
+
return None
|
|
61
|
+
click.echo(click.style(f"Variant {variant_name} for App {app_name} added successfully", fg='green'))
|
|
62
|
+
# Last step us to save the config file
|
|
63
|
+
toml.dump(config, config_file.open('w'))
|
|
64
|
+
return variant_name
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def start_variant(variant_name: str, app_folder: str):
|
|
68
|
+
app_folder = Path(app_folder)
|
|
69
|
+
config_file = app_folder / 'config.toml'
|
|
70
|
+
if not config_file.exists():
|
|
71
|
+
click.echo("Please run agenta init first")
|
|
72
|
+
return
|
|
73
|
+
else:
|
|
74
|
+
config = toml.load(config_file)
|
|
75
|
+
app_name = config['app-name']
|
|
76
|
+
if 'variants' not in config:
|
|
77
|
+
click.echo("No variants found. Please add a variant first.")
|
|
78
|
+
return
|
|
79
|
+
|
|
80
|
+
if not variant_name:
|
|
81
|
+
variant_name = questionary.select(
|
|
82
|
+
'Please choose a variant',
|
|
83
|
+
choices=config['variants']
|
|
84
|
+
).ask()
|
|
85
|
+
|
|
86
|
+
endpoint = client.start_variant(app_name, variant_name)
|
|
87
|
+
click.echo(
|
|
88
|
+
f"Started variant {variant_name} for App {app_name}. Endpoint: {endpoint}")
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@click.group()
|
|
92
|
+
def cli():
|
|
93
|
+
pass
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
@click.command(name='serve')
|
|
97
|
+
@click.argument('app_folder', default='.')
|
|
98
|
+
def serve_cli(app_folder: str):
|
|
99
|
+
"""Add a variant and start its container."""
|
|
100
|
+
variant_name = add_variant(variant_name='', app_folder=app_folder)
|
|
101
|
+
if variant_name: # otherwise we failed
|
|
102
|
+
start_variant(variant_name=variant_name, app_folder=app_folder)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
@click.command(name='add-variant')
|
|
106
|
+
@click.argument('app_folder', default='.')
|
|
107
|
+
@click.option('--variant_name', default='')
|
|
108
|
+
def add_variant_cli(variant_name: str, app_folder: str):
|
|
109
|
+
return add_variant(variant_name, app_folder)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
@click.command()
|
|
113
|
+
@click.option('--app_name', default='')
|
|
114
|
+
def init(app_name: str):
|
|
115
|
+
"""Initialize a new Agenta app with the template files."""
|
|
116
|
+
if not app_name:
|
|
117
|
+
app_name = questionary.text('Please enter the app name').ask()
|
|
118
|
+
|
|
119
|
+
config = {"app-name": app_name}
|
|
120
|
+
with open('config.toml', 'w') as config_file:
|
|
121
|
+
toml.dump(config, config_file)
|
|
122
|
+
|
|
123
|
+
# Ask for init option
|
|
124
|
+
init_option = questionary.select(
|
|
125
|
+
"How do you want to initialize your app?",
|
|
126
|
+
choices=['Blank App', 'From Template']
|
|
127
|
+
).ask()
|
|
128
|
+
|
|
129
|
+
# If the user selected the second option, show a list of available templates
|
|
130
|
+
if init_option == 'From Template':
|
|
131
|
+
current_dir = Path.cwd()
|
|
132
|
+
template_dir = Path(__file__).parent / "templates"
|
|
133
|
+
templates = [folder.name for folder in template_dir.iterdir()
|
|
134
|
+
if folder.is_dir()]
|
|
135
|
+
template_desc = [toml.load(
|
|
136
|
+
(template_dir / name / 'template.toml'))['short_desc'] for name in templates]
|
|
137
|
+
|
|
138
|
+
# Show the templates to the user
|
|
139
|
+
template = questionary.select(
|
|
140
|
+
"Which template do you want to use?",
|
|
141
|
+
choices=[questionary.Choice(title=f"{template} - {template_desc}", value=template)
|
|
142
|
+
for template, template_desc in zip(templates, template_desc)]
|
|
143
|
+
).ask()
|
|
144
|
+
|
|
145
|
+
# Copy the template files to the current directory
|
|
146
|
+
chosen_template_dir = template_dir / template
|
|
147
|
+
for file in chosen_template_dir.glob("*"):
|
|
148
|
+
if file.name != 'template.toml':
|
|
149
|
+
shutil.copy(file, current_dir / file.name)
|
|
150
|
+
click.echo("App initialized successfully")
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
@click.command(name='start')
|
|
154
|
+
@click.option('--variant_name', default=None)
|
|
155
|
+
@click.argument('app_folder', default=".")
|
|
156
|
+
def start_variant_cli(variant_name: str, app_folder: str):
|
|
157
|
+
"""Start a variant."""
|
|
158
|
+
start_variant(variant_name, app_folder)
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
# Add the commands to the CLI group
|
|
162
|
+
cli.add_command(add_variant_cli)
|
|
163
|
+
cli.add_command(init)
|
|
164
|
+
cli.add_command(start_variant_cli)
|
|
165
|
+
cli.add_command(serve_cli)
|
|
166
|
+
|
|
167
|
+
if __name__ == '__main__':
|
|
168
|
+
cli()
|
agenta/client/Readme.md
ADDED
|
File without changes
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from pydantic import BaseModel
|
|
2
|
+
from typing import List, Optional, Dict, Any
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class AppVariant(BaseModel):
|
|
6
|
+
app_name: str
|
|
7
|
+
variant_name: str
|
|
8
|
+
parameters: Optional[Dict[str, Any]]
|
|
9
|
+
previous_variant_name: Optional[str]
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Image(BaseModel):
|
|
13
|
+
docker_id: str
|
|
14
|
+
tags: str
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class URI(BaseModel):
|
|
18
|
+
uri: str
|
agenta/client/client.py
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
from agenta.client.api_models import AppVariant, Image
|
|
2
|
+
from docker.models.images import Image as DockerImage
|
|
3
|
+
import requests
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def add_variant_to_server(app_name: str, variant_name: str, docker_image: DockerImage):
|
|
7
|
+
"""Adds a variant to the server.
|
|
8
|
+
|
|
9
|
+
Arguments:
|
|
10
|
+
app_name -- Name of the app
|
|
11
|
+
variant_name -- Name of the variant
|
|
12
|
+
image_name -- Name of the image
|
|
13
|
+
"""
|
|
14
|
+
image: Image = Image(docker_id=docker_image.id,
|
|
15
|
+
tags=f"{docker_image.tags[0]}")
|
|
16
|
+
app_variant: AppVariant = AppVariant(
|
|
17
|
+
app_name=app_name, variant_name=variant_name)
|
|
18
|
+
# TODO: save uri as a config
|
|
19
|
+
response = requests.post("http://localhost/api/app_variant/add/from_image/",
|
|
20
|
+
json={"app_variant": app_variant.dict(), "image": image.dict()})
|
|
21
|
+
if response.status_code != 200:
|
|
22
|
+
raise Exception(
|
|
23
|
+
f"Request to add variant failed with status code {response.status_code}. Response: {response.text}")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def start_variant(app_name: str, variant_name: str) -> str:
|
|
27
|
+
"""Starts a container with the variant an expose its endpoint
|
|
28
|
+
|
|
29
|
+
Arguments:
|
|
30
|
+
app_name --
|
|
31
|
+
variant_name -- _description_
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
The endpoint of the container
|
|
35
|
+
"""
|
|
36
|
+
response = requests.post("http://localhost/api/app_variant/start/",
|
|
37
|
+
json={"app_name": app_name, "variant_name": variant_name})
|
|
38
|
+
assert response.status_code == 200
|
|
39
|
+
return response.json()['uri']
|
agenta/config.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from pydantic import BaseSettings
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import toml
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
# Load the settings from the .toml file
|
|
7
|
+
toml_config = toml.load(f"{Path(__file__).parent}/config.toml")
|
|
8
|
+
|
|
9
|
+
# Set the environment variables from the TOML configurations
|
|
10
|
+
os.environ["DOCKER_REGISTRY_URL"] = toml_config["docker_registry_url"]
|
|
11
|
+
os.environ["DATABASE_URL"] = toml_config["database_url"]
|
|
12
|
+
os.environ["REGISTRY"] = toml_config["registry"]
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Settings(BaseSettings):
|
|
16
|
+
docker_registry_url: str
|
|
17
|
+
database_url: str
|
|
18
|
+
registry: str
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
settings = Settings()
|
agenta/config.toml
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
FROM python:3.8-slim
|
|
2
|
+
|
|
3
|
+
WORKDIR /app
|
|
4
|
+
|
|
5
|
+
COPY . .
|
|
6
|
+
|
|
7
|
+
RUN pip install --no-cache-dir -r requirements.txt
|
|
8
|
+
RUN pip install fastapi uvicorn python-dotenv
|
|
9
|
+
EXPOSE 80
|
|
10
|
+
|
|
11
|
+
CMD ["python", "main.py"]
|
|
12
|
+
# uvicorn agenta_backend.main:app --reload --host 0.0.0.0 --port 8881
|
|
13
|
+
# TODO: add root_path as env variable and give it to the docker to solve the docs issue
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
The code here is just used when creating the template to dockerize the app. It is not part of the cli.
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import shutil
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from tempfile import TemporaryDirectory
|
|
5
|
+
|
|
6
|
+
import docker
|
|
7
|
+
from agenta.config import settings
|
|
8
|
+
from docker.models.images import Image
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def create_dockerfile(out_folder: Path):
|
|
12
|
+
"""Creates a dockerfile based on the template in the out_folder.
|
|
13
|
+
|
|
14
|
+
Arguments:
|
|
15
|
+
out_folder -- Folder in which to create the Dockerfile.
|
|
16
|
+
"""
|
|
17
|
+
assert Path(out_folder).exists(), f"Folder {out_folder} does not exist."
|
|
18
|
+
dockerfile_template = Path(__file__).parent / \
|
|
19
|
+
"docker-assets" / "Dockerfile.template"
|
|
20
|
+
dockerfile_path = out_folder / "Dockerfile"
|
|
21
|
+
shutil.copy(dockerfile_template, dockerfile_path)
|
|
22
|
+
return dockerfile_path
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def build_and_upload_docker_image(folder: Path, variant_name: str, app_name: str) -> Image:
|
|
26
|
+
"""Builds an image from the folder and returns the path
|
|
27
|
+
|
|
28
|
+
Arguments:
|
|
29
|
+
folder -- The folder containg the app code
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
The image object
|
|
33
|
+
TODO: Check that the variant name does not exist
|
|
34
|
+
TODO: Deal with different app names (probably we need to look then at multiple tags)
|
|
35
|
+
TODO: Error handling
|
|
36
|
+
"""
|
|
37
|
+
# Initialize Docker client
|
|
38
|
+
client = docker.from_env()
|
|
39
|
+
|
|
40
|
+
with TemporaryDirectory() as temp_dir:
|
|
41
|
+
# Create a Dockerfile for the app
|
|
42
|
+
# TODO: Later do this in the temp dir
|
|
43
|
+
dockerfile_path = create_dockerfile(folder)
|
|
44
|
+
shutil.copy(Path(__file__).parent.parent / "agenta.py", folder)
|
|
45
|
+
shutil.copy(Path(__file__).parent /
|
|
46
|
+
"docker-assets" / "main.py", folder)
|
|
47
|
+
|
|
48
|
+
# Copy the app files to a temporary directory
|
|
49
|
+
shutil.copytree(folder, temp_dir, dirs_exist_ok=True)
|
|
50
|
+
|
|
51
|
+
# Build the Docker image
|
|
52
|
+
registry = settings.registry
|
|
53
|
+
tag = f"{registry}/{app_name.lower()}_{variant_name.lower()}:latest"
|
|
54
|
+
print("Building Docker image...")
|
|
55
|
+
try:
|
|
56
|
+
image, build_log = client.images.build(
|
|
57
|
+
path=temp_dir,
|
|
58
|
+
tag=tag,
|
|
59
|
+
rm=True # Remove intermediate containers after a successful build
|
|
60
|
+
)
|
|
61
|
+
except docker.errors.BuildError as e:
|
|
62
|
+
print("Error building Docker image:\n", e)
|
|
63
|
+
raise e
|
|
64
|
+
|
|
65
|
+
# Print the build log
|
|
66
|
+
for line in build_log:
|
|
67
|
+
print(line)
|
|
68
|
+
# Upload the Docker image to the Agenta registry
|
|
69
|
+
print("Uploading Docker image...")
|
|
70
|
+
client.images.push(repository=f"{registry}", tag="latest")
|
|
71
|
+
print("Docker image uploaded successfully.")
|
|
72
|
+
|
|
73
|
+
# Clean up the temporary Dockerfile
|
|
74
|
+
dockerfile_path.unlink()
|
|
75
|
+
return image
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Using this template
|
|
2
|
+
|
|
3
|
+
Please make sure to create a `.env` file with your OpenAI API key before running the app.
|
|
4
|
+
OPENAI_API_KEY=sk-xxxxxxx
|
|
5
|
+
|
|
6
|
+
## Running
|
|
7
|
+
You can first test your app locally by using by running `python app.py`, after experimenting, send it to evaluation by running `agenta up folder`.
|
|
8
|
+
You will find then the app in the dashboard, where you can evaluate it and compare it to previous versions.
|
|
9
|
+
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from agenta import post, TextParam, FloatParam
|
|
2
|
+
from dotenv import load_dotenv
|
|
3
|
+
from langchain.chains import LLMChain
|
|
4
|
+
from langchain.llms import OpenAI
|
|
5
|
+
from langchain.prompts import PromptTemplate
|
|
6
|
+
|
|
7
|
+
default_prompt = "What is a good name for a company that makes {product}?"
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@post
|
|
11
|
+
def generate(product: str, temperature: FloatParam = 0.9, prompt_template: TextParam = default_prompt) -> str:
|
|
12
|
+
llm = OpenAI(temperature=temperature)
|
|
13
|
+
prompt = PromptTemplate(
|
|
14
|
+
input_variables=["product"],
|
|
15
|
+
template=prompt_template,
|
|
16
|
+
)
|
|
17
|
+
chain = LLMChain(llm=llm, prompt=prompt)
|
|
18
|
+
output = chain.run(product=product)
|
|
19
|
+
|
|
20
|
+
return output
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
short_desc="Simple app that uses one prompt using langchain"
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: agenta
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary:
|
|
5
|
+
Author: Mahmoud Mabrouk
|
|
6
|
+
Author-email: mahmoudmabrouk.mail@gmail.com
|
|
7
|
+
Requires-Python: >=3.9,<4.0
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
12
|
+
Requires-Dist: click (>=8.1.3,<9.0.0)
|
|
13
|
+
Requires-Dist: docker (>=6.1.1,<7.0.0)
|
|
14
|
+
Requires-Dist: fastapi (>=0.95.1,<0.96.0)
|
|
15
|
+
Requires-Dist: ipdb (>=0.13.13,<0.14.0)
|
|
16
|
+
Requires-Dist: python-dotenv (>=1.0.0,<2.0.0)
|
|
17
|
+
Requires-Dist: questionary (>=1.10.0,<2.0.0)
|
|
18
|
+
Requires-Dist: toml (>=0.10.2,<0.11.0)
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
|
|
21
|
+
# Agenta CLI tool.
|
|
22
|
+
|
|
23
|
+
The Agenta CLI tool enables users to add new versions of their code to the Agenta platform, allowing them to be evaluated, compared to other versions, and deployed.
|
|
24
|
+
Installation
|
|
25
|
+
|
|
26
|
+
To install dependencies, run the following command:
|
|
27
|
+
|
|
28
|
+
```poetry install```
|
|
29
|
+
|
|
30
|
+
## Requirements
|
|
31
|
+
You need to have docker installed to be able to use the cli locally.
|
|
32
|
+
|
|
33
|
+
## How does it work
|
|
34
|
+
|
|
35
|
+
The CLI allows you to push in one command your projects for evaluation in the dashboard.
|
|
36
|
+
|
|
37
|
+
You only need to modify your code very slightly by adding a decorator to your chat/ and ingest functions.
|
|
38
|
+
|
|
39
|
+
Under the hood the cli packages your code into a docker container and send it to a registry.
|
|
40
|
+
|
|
41
|
+
Later the dashboard spins off these containers and runs the benchmarks and evaluations over them.
|
|
42
|
+
## Quickstart
|
|
43
|
+
|
|
44
|
+
To enter the virtual environment, run the following command:
|
|
45
|
+
|
|
46
|
+
```poetry shell```
|
|
47
|
+
|
|
48
|
+
To init a new project and start from a simple template. Run the following command in an empty folder:
|
|
49
|
+
|
|
50
|
+
```agenta init```
|
|
51
|
+
|
|
52
|
+
Build your code and add it to the platform to be tested in the UI. This command builds a Docker image for the version and upload the container to the registry:
|
|
53
|
+
|
|
54
|
+
```agenta add-variant```
|
|
55
|
+
|
|
56
|
+
Start your code as a containarized service locally:
|
|
57
|
+
|
|
58
|
+
```agenta start```
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
## How to write code to be user in agenta
|
|
62
|
+
|
|
63
|
+
To write code that can be used in Agenta, you need to structure your project as follows:
|
|
64
|
+
|
|
65
|
+
### Project Structure
|
|
66
|
+
The project folder should contain:
|
|
67
|
+
1. A Python file named `app.py`
|
|
68
|
+
2. A `requirements.txt` file with all the necessary dependencies for running your code
|
|
69
|
+
|
|
70
|
+
### app.py
|
|
71
|
+
|
|
72
|
+
The `app.py` file should have the following functions:
|
|
73
|
+
0. import `agenta`
|
|
74
|
+
1. A function named chat that is exposed using the `@agenta.post` decorator
|
|
75
|
+
2. An optional function named ingest that is exposed using the `@agenta.post` decorator
|
|
76
|
+
|
|
77
|
+
### Example Project Structure
|
|
78
|
+
|
|
79
|
+
Here's an example of how your project folder might look:
|
|
80
|
+
|
|
81
|
+
my_agenda_project/
|
|
82
|
+
│
|
|
83
|
+
├── app.py
|
|
84
|
+
└── requirements.txt
|
|
85
|
+
|
|
86
|
+
### Example app.py
|
|
87
|
+
```python
|
|
88
|
+
from agenta import post, get
|
|
89
|
+
|
|
90
|
+
class ChatInput(BaseModel):
|
|
91
|
+
messages: List[str]
|
|
92
|
+
|
|
93
|
+
class ChatOutput(BaseModel):
|
|
94
|
+
response: str
|
|
95
|
+
|
|
96
|
+
@post
|
|
97
|
+
def chat(input_data: ChatInput) -> ChatOutput:
|
|
98
|
+
# Your chat function implementation here
|
|
99
|
+
response = "Hello, World!"
|
|
100
|
+
return ChatOutput(response=response)
|
|
101
|
+
|
|
102
|
+
class IngestInput(BaseModel):
|
|
103
|
+
data: List[str]
|
|
104
|
+
|
|
105
|
+
class IngestOutput(BaseModel):
|
|
106
|
+
status: str
|
|
107
|
+
|
|
108
|
+
@post
|
|
109
|
+
def ingest(input_data: IngestInput) -> IngestOutput:
|
|
110
|
+
# Your ingest function implementation here (if needed)
|
|
111
|
+
status = "Data ingestion successful."
|
|
112
|
+
return IngestOutput(status=status)
|
|
113
|
+
|
|
114
|
+
```
|
|
115
|
+
## How to deal with secrets:
|
|
116
|
+
- For now we are using a .env file to store secrets.
|
|
117
|
+
- Create an .env file to store your secrets.
|
|
118
|
+
- The .env file is built into the image and uploaded to the local registry. THIS IS NOT GOOD. WE WILL FIX IT ASAP.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
agenta/__init__.py,sha256=tdL1GYHorlEPZ5lBWRykbw-sCgpjCu3yxXW_eSy5lGs,53
|
|
2
|
+
agenta/agenta.py,sha256=Rc3RIIpHzjbkQPHZljsabNur-cz2IrMZ3b2m_wwLyt8,3016
|
|
3
|
+
agenta/cli.py,sha256=3L9SuAeXkShWtTD3JaAKMHra7AtCw6WwwFwPpctGnLE,5902
|
|
4
|
+
agenta/client/Readme.md,sha256=umhMce1Gq_der9pH4M_pP4NQ8rHa3MENJLM9OrdUZ50,131
|
|
5
|
+
agenta/client/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
+
agenta/client/api_models.py,sha256=gqIIZay_A7kg2d451gO9QTgaKxGuAk3YKtWtArIzMNw,325
|
|
7
|
+
agenta/client/client.py,sha256=ZQuNNXUTvzkRDNhi3h3vkXNCOChT47TWxKfz59OoZo4,1479
|
|
8
|
+
agenta/config.py,sha256=2Dsjtu05TwFqpW1tIhqIQB1FkjmsOgPXq7oKfotPkP8,550
|
|
9
|
+
agenta/config.toml,sha256=Mb8IvTJrQGIKkDH3CEvp2ViuyB7Upm1MlZU8Ksqxw7w,91
|
|
10
|
+
agenta/docker/docker-assets/Dockerfile.template,sha256=cB0mbr2aA9kxC9l22qLmTA5tp2RKyux7gL5keGEBv2k,337
|
|
11
|
+
agenta/docker/docker-assets/README.md,sha256=XHxwh2ks_ozrtAU7SLbL3J14SB2holG6buoTxwmMiZM,102
|
|
12
|
+
agenta/docker/docker-assets/main.py,sha256=GfwENA9ekPKamxhgtCPiWtjufHIFl19N0CKos8Aos0o,256
|
|
13
|
+
agenta/docker/docker_utils.py,sha256=1Hn11JrmLglWnb0ly9PdnCzpyrkto61lbpxK8OocV00,2585
|
|
14
|
+
agenta/templates/simple_prompt/README.md,sha256=oIRlmJFMoDzewQ5QYFPUIaStMXZ1_YriHxD2cNOC5l4,407
|
|
15
|
+
agenta/templates/simple_prompt/app.py,sha256=JDPgAg-Bt6kH-CDUZJu-hB-svi0mOTVnHx8YPHRGoJ4,642
|
|
16
|
+
agenta/templates/simple_prompt/requirements.txt,sha256=EtWeoX1zf7dxGzIeorHcJirfmiyMWIL7QpbnjvXuaNg,31
|
|
17
|
+
agenta/templates/simple_prompt/template.toml,sha256=DQBtRrF4GU8LBEXOZ-GGuINXMQDKGTEG5y37tnvIUIE,60
|
|
18
|
+
agenta-0.1.0.dist-info/METADATA,sha256=aRu4Dsm7aQkl0n7C2S-KSt2aaMwUzMr194r8vbv-34A,3531
|
|
19
|
+
agenta-0.1.0.dist-info/WHEEL,sha256=7Z8_27uaHI_UZAc4Uox4PpBhQ9Y5_modZXWMxtUi4NU,88
|
|
20
|
+
agenta-0.1.0.dist-info/entry_points.txt,sha256=Cl3WM6oc5vsDQLUIaBj2EoXvQw2l6J_rXLx8AaMG9EU,41
|
|
21
|
+
agenta-0.1.0.dist-info/RECORD,,
|