naas-abi-cli 1.0.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.
- naas_abi_cli/README.md +1 -0
- naas_abi_cli/__init__.py +0 -0
- naas_abi_cli/cli/__init__.py +62 -0
- naas_abi_cli/cli/agent.py +30 -0
- naas_abi_cli/cli/chat.py +26 -0
- naas_abi_cli/cli/config.py +49 -0
- naas_abi_cli/cli/deploy.py +218 -0
- naas_abi_cli/cli/init.py +13 -0
- naas_abi_cli/cli/module.py +28 -0
- naas_abi_cli/cli/new/__init__.py +4 -0
- naas_abi_cli/cli/new/module.py +10 -0
- naas_abi_cli/cli/new/new.py +6 -0
- naas_abi_cli/cli/new/project.py +60 -0
- naas_abi_cli/cli/new/templates/project/.github/workflows/release.yaml +31 -0
- naas_abi_cli/cli/new/templates/project/README.md +0 -0
- naas_abi_cli/cli/new/templates/project/pyproject.toml +8 -0
- naas_abi_cli/cli/new/templates/project/{{project_name_snake}}/__init__.py +0 -0
- naas_abi_cli/cli/run.py +18 -0
- naas_abi_cli/cli/secret.py +79 -0
- naas_abi_cli/cli/utils/Copier.py +66 -0
- naas_abi_cli-1.0.0.dist-info/METADATA +9 -0
- naas_abi_cli-1.0.0.dist-info/RECORD +24 -0
- naas_abi_cli-1.0.0.dist-info/WHEEL +4 -0
- naas_abi_cli-1.0.0.dist-info/entry_points.txt +2 -0
naas_abi_cli/README.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
naas_abi_cli/__init__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import subprocess
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
import click
|
|
6
|
+
|
|
7
|
+
from .agent import agent
|
|
8
|
+
from .chat import chat
|
|
9
|
+
from .config import config
|
|
10
|
+
from .deploy import deploy
|
|
11
|
+
from .init import init
|
|
12
|
+
from .module import module
|
|
13
|
+
from .new import new
|
|
14
|
+
from .run import run
|
|
15
|
+
from .secret import secrets
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@click.group("abi")
|
|
19
|
+
def _main():
|
|
20
|
+
pass
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
_main.add_command(secrets)
|
|
24
|
+
_main.add_command(config)
|
|
25
|
+
_main.add_command(module)
|
|
26
|
+
_main.add_command(agent)
|
|
27
|
+
_main.add_command(chat)
|
|
28
|
+
_main.add_command(new)
|
|
29
|
+
_main.add_command(init)
|
|
30
|
+
_main.add_command(deploy)
|
|
31
|
+
_main.add_command(run)
|
|
32
|
+
|
|
33
|
+
ran = False
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def main():
|
|
37
|
+
global ran
|
|
38
|
+
if ran:
|
|
39
|
+
return
|
|
40
|
+
ran = True
|
|
41
|
+
|
|
42
|
+
# Check how the project is being runned.
|
|
43
|
+
if os.getenv("LOCAL_UV_RAN") is None:
|
|
44
|
+
if "pyproject.toml" in os.listdir(os.getcwd()):
|
|
45
|
+
with open("pyproject.toml", "r") as file:
|
|
46
|
+
if "naas-abi-cli" in file.read():
|
|
47
|
+
arguments = (
|
|
48
|
+
"uv run --active python -m naas_abi_cli.cli".split(" ")
|
|
49
|
+
+ sys.argv[1:]
|
|
50
|
+
)
|
|
51
|
+
subprocess.run(
|
|
52
|
+
arguments,
|
|
53
|
+
cwd=os.getcwd(),
|
|
54
|
+
env={**os.environ, "LOCAL_UV_RAN": "true"},
|
|
55
|
+
check=True,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
return
|
|
59
|
+
_main()
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
main()
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import click
|
|
2
|
+
from rich.console import Console
|
|
3
|
+
from rich.table import Table
|
|
4
|
+
|
|
5
|
+
from naas_abi_core.engine.Engine import Engine
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@click.group("agent")
|
|
9
|
+
def agent():
|
|
10
|
+
pass
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@agent.command("list")
|
|
14
|
+
def list():
|
|
15
|
+
engine = Engine()
|
|
16
|
+
engine.load()
|
|
17
|
+
|
|
18
|
+
console = Console()
|
|
19
|
+
table = Table(
|
|
20
|
+
title="Available Agents", show_header=True, header_style="bold magenta"
|
|
21
|
+
)
|
|
22
|
+
table.add_column("Module", style="cyan", no_wrap=True)
|
|
23
|
+
table.add_column("Agent", style="green")
|
|
24
|
+
|
|
25
|
+
modules = engine.modules
|
|
26
|
+
for module in modules:
|
|
27
|
+
for agent in modules[module].agents:
|
|
28
|
+
table.add_row(module, agent.__name__)
|
|
29
|
+
|
|
30
|
+
console.print(table)
|
naas_abi_cli/cli/chat.py
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import click
|
|
2
|
+
|
|
3
|
+
from naas_abi_core import logger
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@click.command("chat")
|
|
7
|
+
@click.argument("module-name", type=str, required=True, default="naas_abi")
|
|
8
|
+
@click.argument("agent-name", type=str, required=True, default="AbiAgent")
|
|
9
|
+
def chat(module_name: str, agent_name: str):
|
|
10
|
+
from naas_abi_core.engine.Engine import Engine
|
|
11
|
+
|
|
12
|
+
engine = Engine()
|
|
13
|
+
engine.load(module_names=[module_name])
|
|
14
|
+
|
|
15
|
+
from naas_abi_core.apps.terminal_agent.main import run_agent
|
|
16
|
+
|
|
17
|
+
if module_name not in engine.modules:
|
|
18
|
+
raise ValueError(f"Module {module_name} not found")
|
|
19
|
+
|
|
20
|
+
logger.debug(f"Module agents: {engine.modules[module_name].agents}")
|
|
21
|
+
|
|
22
|
+
for agent_class in engine.modules[module_name].agents:
|
|
23
|
+
logger.debug(f"Agent class: {agent_class.__name__}")
|
|
24
|
+
if agent_class.__name__ == agent_name:
|
|
25
|
+
run_agent(agent_class.New())
|
|
26
|
+
break
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
import yaml
|
|
5
|
+
|
|
6
|
+
from naas_abi_core.engine.engine_configuration.EngineConfiguration import (
|
|
7
|
+
EngineConfiguration,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@click.group("config")
|
|
12
|
+
def config():
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@config.command("validate")
|
|
17
|
+
@click.option("--configuration-file", type=str, required=False, default=None)
|
|
18
|
+
def validate(configuration_file: str | None):
|
|
19
|
+
configuration_content: str | None = None
|
|
20
|
+
|
|
21
|
+
if configuration_file is not None:
|
|
22
|
+
if not os.path.exists(configuration_file):
|
|
23
|
+
raise FileNotFoundError(
|
|
24
|
+
f"Configuration file {configuration_file} not found"
|
|
25
|
+
)
|
|
26
|
+
with open(configuration_file, "r") as file:
|
|
27
|
+
configuration_content = file.read()
|
|
28
|
+
|
|
29
|
+
EngineConfiguration.load_configuration(configuration_content)
|
|
30
|
+
print("Configuration is valid")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@config.command("render")
|
|
34
|
+
@click.option("--configuration-file", type=str, required=False, default=None)
|
|
35
|
+
def render(configuration_file: str | None):
|
|
36
|
+
configuration_content: str | None = None
|
|
37
|
+
|
|
38
|
+
if configuration_file is not None:
|
|
39
|
+
if not os.path.exists(configuration_file):
|
|
40
|
+
raise FileNotFoundError(
|
|
41
|
+
f"Configuration file {configuration_file} not found"
|
|
42
|
+
)
|
|
43
|
+
with open(configuration_file, "r") as file:
|
|
44
|
+
configuration_content = file.read()
|
|
45
|
+
|
|
46
|
+
configuration: EngineConfiguration = EngineConfiguration.load_configuration(
|
|
47
|
+
configuration_content
|
|
48
|
+
)
|
|
49
|
+
print(yaml.dump(configuration.model_dump(), indent=2))
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import subprocess
|
|
2
|
+
from uuid import uuid4
|
|
3
|
+
|
|
4
|
+
import click
|
|
5
|
+
import requests
|
|
6
|
+
from naas_abi_core import logger
|
|
7
|
+
from naas_abi_core.engine.engine_configuration.EngineConfiguration import (
|
|
8
|
+
EngineConfiguration,
|
|
9
|
+
)
|
|
10
|
+
from pydantic import BaseModel
|
|
11
|
+
from rich.console import Console
|
|
12
|
+
from rich.markdown import Markdown
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@click.group("deploy")
|
|
16
|
+
def deploy():
|
|
17
|
+
pass
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class Container(BaseModel):
|
|
21
|
+
name: str
|
|
22
|
+
image: str
|
|
23
|
+
port: int
|
|
24
|
+
cpu: str
|
|
25
|
+
memory: str
|
|
26
|
+
env: dict
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class Space(BaseModel):
|
|
30
|
+
name: str
|
|
31
|
+
containers: list[Container]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class NaasAPIClient:
|
|
35
|
+
naas_api_key: str
|
|
36
|
+
base_url: str
|
|
37
|
+
|
|
38
|
+
def __init__(self, naas_api_key: str):
|
|
39
|
+
self.naas_api_key = naas_api_key
|
|
40
|
+
self.base_url = "https://api.naas.ai"
|
|
41
|
+
|
|
42
|
+
def create_registry(self, name: str):
|
|
43
|
+
response = requests.post(
|
|
44
|
+
f"{self.base_url}/registry/",
|
|
45
|
+
headers={"Authorization": f"Bearer {self.naas_api_key}"},
|
|
46
|
+
json={"name": name},
|
|
47
|
+
)
|
|
48
|
+
if response.status_code == 409:
|
|
49
|
+
return self.get_registry(name)
|
|
50
|
+
response.raise_for_status()
|
|
51
|
+
return response.json()
|
|
52
|
+
|
|
53
|
+
def get_registry(self, name: str):
|
|
54
|
+
response = requests.get(
|
|
55
|
+
f"{self.base_url}/registry/{name}",
|
|
56
|
+
headers={"Authorization": f"Bearer {self.naas_api_key}"},
|
|
57
|
+
)
|
|
58
|
+
response.raise_for_status()
|
|
59
|
+
return response.json()
|
|
60
|
+
|
|
61
|
+
def get_registry_credentials(self, name: str):
|
|
62
|
+
response = requests.get(
|
|
63
|
+
f"{self.base_url}/registry/{name}/credentials",
|
|
64
|
+
headers={"Authorization": f"Bearer {self.naas_api_key}"},
|
|
65
|
+
)
|
|
66
|
+
response.raise_for_status()
|
|
67
|
+
return response.json()
|
|
68
|
+
|
|
69
|
+
def update_space(self, space: Space) -> dict:
|
|
70
|
+
response = requests.put(
|
|
71
|
+
f"{self.base_url}/space/{space.name}",
|
|
72
|
+
headers={"Authorization": f"Bearer {self.naas_api_key}"},
|
|
73
|
+
json=space.model_dump(),
|
|
74
|
+
)
|
|
75
|
+
response.raise_for_status()
|
|
76
|
+
return response.json()
|
|
77
|
+
|
|
78
|
+
def create_space(self, space: Space) -> dict:
|
|
79
|
+
response = requests.post(
|
|
80
|
+
f"{self.base_url}/space/",
|
|
81
|
+
headers={"Authorization": f"Bearer {self.naas_api_key}"},
|
|
82
|
+
json=space.model_dump(),
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
if response.status_code == 409:
|
|
86
|
+
return self.update_space(space)
|
|
87
|
+
|
|
88
|
+
response.raise_for_status()
|
|
89
|
+
return response.json()
|
|
90
|
+
|
|
91
|
+
def get_space(self, name: str) -> dict:
|
|
92
|
+
response = requests.get(
|
|
93
|
+
f"{self.base_url}/space/{name}",
|
|
94
|
+
headers={"Authorization": f"Bearer {self.naas_api_key}"},
|
|
95
|
+
)
|
|
96
|
+
response.raise_for_status()
|
|
97
|
+
return response.json()
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class NaasDeployer:
|
|
101
|
+
image_name: str
|
|
102
|
+
|
|
103
|
+
naas_api_client: NaasAPIClient
|
|
104
|
+
|
|
105
|
+
def __init__(self, configuration: EngineConfiguration):
|
|
106
|
+
self.configuration = configuration
|
|
107
|
+
self.image_name = str(uuid4())
|
|
108
|
+
if configuration.deploy is None:
|
|
109
|
+
# Fail fast with a clear, user-facing error instead of an assertion.
|
|
110
|
+
raise click.ClickException(
|
|
111
|
+
"Deploy configuration is missing; please add a deploy section before running this command."
|
|
112
|
+
)
|
|
113
|
+
self.naas_api_client = NaasAPIClient(configuration.deploy.naas_api_key)
|
|
114
|
+
|
|
115
|
+
def docker_build(self, image_name: str):
|
|
116
|
+
subprocess.run(
|
|
117
|
+
f"docker build -t {image_name} . --platform linux/amd64", shell=True
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
@staticmethod
|
|
121
|
+
def env_list_to_dict(env: list[str]) -> dict:
|
|
122
|
+
env_dict: dict[str, str] = {}
|
|
123
|
+
for env_var in env:
|
|
124
|
+
if "=" not in env_var:
|
|
125
|
+
raise click.ClickException(
|
|
126
|
+
f"Invalid environment variable '{env_var}'. Expected format KEY=VALUE."
|
|
127
|
+
)
|
|
128
|
+
key, value = env_var.split("=", 1)
|
|
129
|
+
if not key:
|
|
130
|
+
raise click.ClickException(
|
|
131
|
+
f"Invalid environment variable '{env_var}'. Missing key before '='."
|
|
132
|
+
)
|
|
133
|
+
env_dict[key] = value
|
|
134
|
+
return env_dict
|
|
135
|
+
|
|
136
|
+
def deploy(self, env: list[str]):
|
|
137
|
+
assert self.configuration.deploy is not None
|
|
138
|
+
registry = self.naas_api_client.create_registry(
|
|
139
|
+
self.configuration.deploy.space_name
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
uid = str(uuid4())
|
|
143
|
+
|
|
144
|
+
image_name = f"{registry['registry']['uri']}:{uid}"
|
|
145
|
+
self.docker_build(image_name)
|
|
146
|
+
credentials = self.naas_api_client.get_registry_credentials(
|
|
147
|
+
self.configuration.deploy.space_name
|
|
148
|
+
)
|
|
149
|
+
docker_login_command = f"docker login -u {credentials['credentials']['username']} -p {credentials['credentials']['password']} {registry['registry']['uri']}"
|
|
150
|
+
subprocess.run(docker_login_command, shell=True)
|
|
151
|
+
subprocess.run(f"docker push {image_name}", shell=True)
|
|
152
|
+
|
|
153
|
+
image_sha = (
|
|
154
|
+
subprocess.run(
|
|
155
|
+
"docker inspect --format='{{index .RepoDigests 0}}' "
|
|
156
|
+
+ image_name
|
|
157
|
+
+ " | cut -d'@' -f2",
|
|
158
|
+
shell=True,
|
|
159
|
+
capture_output=True,
|
|
160
|
+
)
|
|
161
|
+
.stdout.strip()
|
|
162
|
+
.decode("utf-8")
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
image_name_with_sha = f"{image_name.replace(':' + uid, '')}@{image_sha}"
|
|
166
|
+
|
|
167
|
+
self.naas_api_client.create_space(
|
|
168
|
+
Space(
|
|
169
|
+
name=self.configuration.deploy.space_name,
|
|
170
|
+
containers=[
|
|
171
|
+
Container(
|
|
172
|
+
name="api",
|
|
173
|
+
image=image_name_with_sha,
|
|
174
|
+
port=9879,
|
|
175
|
+
cpu="1",
|
|
176
|
+
memory="1Gi",
|
|
177
|
+
# env=self.env_list_to_dict(env) | {
|
|
178
|
+
# "NAAS_API_KEY": self.configuration.deploy.naas_api_key,
|
|
179
|
+
# "ENV": "prod",
|
|
180
|
+
# },
|
|
181
|
+
env=self.configuration.deploy.env | self.env_list_to_dict(env),
|
|
182
|
+
)
|
|
183
|
+
],
|
|
184
|
+
)
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
self.naas_api_client.get_space(self.configuration.deploy.space_name)
|
|
188
|
+
|
|
189
|
+
Console().print(
|
|
190
|
+
Markdown(f"""
|
|
191
|
+
# Deployment successful
|
|
192
|
+
|
|
193
|
+
- Space: {self.configuration.deploy.space_name}
|
|
194
|
+
- Image: {image_name_with_sha}
|
|
195
|
+
- URL: https://{self.configuration.deploy.space_name}.default.space.naas.ai
|
|
196
|
+
|
|
197
|
+
""")
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
@deploy.command("naas")
|
|
202
|
+
@click.option(
|
|
203
|
+
"-e",
|
|
204
|
+
"--env",
|
|
205
|
+
multiple=True,
|
|
206
|
+
help="Environment variables to set (e.g. -e FOO=BAR -e BAZ=QUX)",
|
|
207
|
+
)
|
|
208
|
+
def naas(env: list[str]):
|
|
209
|
+
configuration: EngineConfiguration = EngineConfiguration.load_configuration()
|
|
210
|
+
|
|
211
|
+
if configuration.deploy is None:
|
|
212
|
+
logger.error(
|
|
213
|
+
"Deploy configuration not found in the yaml configuration file. Please add a deploy section to the configuration file."
|
|
214
|
+
)
|
|
215
|
+
raise click.ClickException("Missing deploy configuration; aborting.")
|
|
216
|
+
|
|
217
|
+
deployer = NaasDeployer(configuration)
|
|
218
|
+
deployer.deploy(env)
|
naas_abi_cli/cli/init.py
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import click
|
|
2
|
+
from naas_abi_core.engine.engine_configuration.EngineConfiguration import (
|
|
3
|
+
EngineConfiguration,
|
|
4
|
+
)
|
|
5
|
+
from rich.console import Console
|
|
6
|
+
from rich.table import Table
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@click.group("module")
|
|
10
|
+
def module():
|
|
11
|
+
pass
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@module.command("list")
|
|
15
|
+
def list() -> None:
|
|
16
|
+
configuration: EngineConfiguration = EngineConfiguration.load_configuration()
|
|
17
|
+
|
|
18
|
+
console = Console()
|
|
19
|
+
table = Table(
|
|
20
|
+
title="Available Modules", show_header=True, header_style="bold magenta"
|
|
21
|
+
)
|
|
22
|
+
table.add_column("Module", style="cyan", no_wrap=True)
|
|
23
|
+
table.add_column("Enabled", style="green")
|
|
24
|
+
|
|
25
|
+
for module in configuration.modules:
|
|
26
|
+
table.add_row(module.module, str(module.enabled))
|
|
27
|
+
|
|
28
|
+
console.print(table)
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import subprocess
|
|
3
|
+
|
|
4
|
+
import click
|
|
5
|
+
|
|
6
|
+
import naas_abi_cli
|
|
7
|
+
from naas_abi_cli.cli.utils.Copier import Copier
|
|
8
|
+
|
|
9
|
+
from .new import new
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@new.command("project")
|
|
13
|
+
@click.argument("project-name", required=False, default=None)
|
|
14
|
+
@click.argument("project-path", required=False, default=None)
|
|
15
|
+
def new_project(project_name: str | None, project_path: str | None):
|
|
16
|
+
# Defaults must be evaluated at runtime so they reflect the caller's CWD.
|
|
17
|
+
if project_name is None:
|
|
18
|
+
project_name = os.path.basename(os.getcwd())
|
|
19
|
+
if project_path is None:
|
|
20
|
+
project_path = os.getcwd()
|
|
21
|
+
# Resolve relative segments (., ..) and user home (~) to a normalized absolute path.
|
|
22
|
+
project_path = os.path.abspath(os.path.expanduser(project_path))
|
|
23
|
+
|
|
24
|
+
# Ensure the last path component matches the project name, not just the suffix.
|
|
25
|
+
if os.path.basename(os.path.normpath(project_path)) != project_name:
|
|
26
|
+
project_path = os.path.join(project_path, project_name)
|
|
27
|
+
print(project_path)
|
|
28
|
+
|
|
29
|
+
if not os.path.exists(project_path):
|
|
30
|
+
os.makedirs(project_path, exist_ok=True)
|
|
31
|
+
elif len(os.listdir(project_path)) > 0:
|
|
32
|
+
print(f"Folder {project_path} already exists and is not empty.")
|
|
33
|
+
exit(1)
|
|
34
|
+
|
|
35
|
+
copier = Copier(
|
|
36
|
+
templates_path=os.path.join(
|
|
37
|
+
os.path.dirname(naas_abi_cli.__file__), "cli/new/templates/project"
|
|
38
|
+
),
|
|
39
|
+
destination_path=project_path,
|
|
40
|
+
)
|
|
41
|
+
copier.copy(
|
|
42
|
+
values={
|
|
43
|
+
"project_name": project_name,
|
|
44
|
+
"project_name_snake": project_name.replace("-", "_"),
|
|
45
|
+
}
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
# Run dependency install without shell to avoid quoting issues on paths with spaces.
|
|
49
|
+
subprocess.run(
|
|
50
|
+
[
|
|
51
|
+
"uv",
|
|
52
|
+
"add",
|
|
53
|
+
"naas-abi-core[all]",
|
|
54
|
+
"naas-abi-marketplace[ai-chatgpt]",
|
|
55
|
+
"naas-abi",
|
|
56
|
+
"naas-abi-cli",
|
|
57
|
+
],
|
|
58
|
+
cwd=project_path,
|
|
59
|
+
check=True,
|
|
60
|
+
)
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
name: Deploy
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
deploy:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
|
|
12
|
+
steps:
|
|
13
|
+
- name: Checkout repository
|
|
14
|
+
uses: actions/checkout@v4
|
|
15
|
+
|
|
16
|
+
- name: Set up Python
|
|
17
|
+
uses: actions/setup-python@v5
|
|
18
|
+
with:
|
|
19
|
+
python-version: "3.11"
|
|
20
|
+
|
|
21
|
+
- name: Install dependencies
|
|
22
|
+
run: |
|
|
23
|
+
python -m pip install uv
|
|
24
|
+
|
|
25
|
+
- name: Deploy
|
|
26
|
+
env:
|
|
27
|
+
NAAS_API_KEY: ${{ '{{ secrets.NAAS_API_KEY }}' }}
|
|
28
|
+
ENV: prod
|
|
29
|
+
run: |
|
|
30
|
+
uv sync --no-dev
|
|
31
|
+
uv run python -m naas_abi_cli.cli deploy naas
|
|
File without changes
|
|
File without changes
|
naas_abi_cli/cli/run.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import click
|
|
2
|
+
from naas_abi_core.engine.Engine import Engine
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
@click.group("run")
|
|
6
|
+
def run():
|
|
7
|
+
pass
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@run.command("script")
|
|
11
|
+
@click.argument("path", type=str, required=True)
|
|
12
|
+
def run_script(path: str):
|
|
13
|
+
engine = Engine()
|
|
14
|
+
engine.load()
|
|
15
|
+
|
|
16
|
+
import runpy
|
|
17
|
+
|
|
18
|
+
runpy.run_path(path, run_name="__main__")
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@click.group("secrets")
|
|
7
|
+
def secrets():
|
|
8
|
+
pass
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@secrets.group("naas")
|
|
12
|
+
def naas():
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@naas.command("push-env-as-base64")
|
|
17
|
+
@click.option(
|
|
18
|
+
"--naas-api-key", type=str, required=True, default=os.getenv("NAAS_API_KEY")
|
|
19
|
+
)
|
|
20
|
+
@click.option("--naas-api-url", type=str, required=True, default="https://api.naas.ai")
|
|
21
|
+
@click.option("--naas-secret-name", type=str, required=True, default="abi_secrets")
|
|
22
|
+
@click.option("--env-file", type=str, required=True, default=".env.prod")
|
|
23
|
+
def push_env_to_naas(naas_api_key, naas_api_url, naas_secret_name, env_file):
|
|
24
|
+
import base64
|
|
25
|
+
|
|
26
|
+
from naas_abi_core.services.secret.adaptors.secondary.NaasSecret import NaasSecret
|
|
27
|
+
|
|
28
|
+
naas_secret = NaasSecret(naas_api_key, naas_api_url)
|
|
29
|
+
|
|
30
|
+
envfile_content = ""
|
|
31
|
+
|
|
32
|
+
with open(env_file, "r") as envfile:
|
|
33
|
+
envfile_content = envfile.read()
|
|
34
|
+
|
|
35
|
+
print(envfile_content)
|
|
36
|
+
|
|
37
|
+
base64_content = base64.b64encode(envfile_content.encode("utf-8")).decode("utf-8")
|
|
38
|
+
naas_secret.set(naas_secret_name, base64_content)
|
|
39
|
+
|
|
40
|
+
print(f"Pushed {env_file} to Naas as base64 secret {naas_secret_name}")
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@naas.command("list")
|
|
44
|
+
@click.option(
|
|
45
|
+
"--naas-api-key", type=str, required=True, default=os.getenv("NAAS_API_KEY")
|
|
46
|
+
)
|
|
47
|
+
@click.option("--naas-api-url", type=str, required=True, default="https://api.naas.ai")
|
|
48
|
+
def list_secrets(naas_api_key, naas_api_url):
|
|
49
|
+
from naas_abi_core.services.secret.adaptors.secondary.NaasSecret import NaasSecret
|
|
50
|
+
|
|
51
|
+
naas_secret = NaasSecret(naas_api_key, naas_api_url)
|
|
52
|
+
|
|
53
|
+
naas_secret.list()
|
|
54
|
+
|
|
55
|
+
for key, value in naas_secret.list().items():
|
|
56
|
+
print(f"{key}: {value}")
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@naas.command("get-base64-env")
|
|
60
|
+
@click.option(
|
|
61
|
+
"--naas-api-key", type=str, required=True, default=os.getenv("NAAS_API_KEY")
|
|
62
|
+
)
|
|
63
|
+
@click.option("--naas-api-url", type=str, required=True, default="https://api.naas.ai")
|
|
64
|
+
@click.option("--naas-secret-name", type=str, required=True, default="abi_secrets")
|
|
65
|
+
def get_secret(naas_api_key, naas_api_url, naas_secret_name):
|
|
66
|
+
from naas_abi_core.services.secret.adaptors.secondary.Base64Secret import (
|
|
67
|
+
Base64Secret,
|
|
68
|
+
)
|
|
69
|
+
from naas_abi_core.services.secret.adaptors.secondary.NaasSecret import NaasSecret
|
|
70
|
+
|
|
71
|
+
naas_secret = NaasSecret(naas_api_key, naas_api_url)
|
|
72
|
+
base64_secret = Base64Secret(naas_secret, naas_secret_name)
|
|
73
|
+
|
|
74
|
+
for key, value in base64_secret.list().items():
|
|
75
|
+
# If value is multiline
|
|
76
|
+
if "\n" in value:
|
|
77
|
+
print(f'{key}="{value}"')
|
|
78
|
+
else:
|
|
79
|
+
print(f"{key}={value}")
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import shutil
|
|
3
|
+
|
|
4
|
+
import jinja2
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Copier:
|
|
8
|
+
templates_path: str
|
|
9
|
+
destination_path: str
|
|
10
|
+
values: dict
|
|
11
|
+
|
|
12
|
+
def __init__(self, templates_path: str, destination_path: str):
|
|
13
|
+
# Normalize paths to avoid double-joining relative segments during recursion.
|
|
14
|
+
self.templates_path = os.path.abspath(templates_path)
|
|
15
|
+
self.destination_path = os.path.abspath(destination_path)
|
|
16
|
+
|
|
17
|
+
def template_file_to_file(
|
|
18
|
+
self, template_path: str, values: dict, destination_path: str
|
|
19
|
+
) -> None:
|
|
20
|
+
destination_path = self.template_string(destination_path, values)
|
|
21
|
+
with open(destination_path, "w", encoding="utf-8") as file:
|
|
22
|
+
file.write(self.template_file(template_path, values))
|
|
23
|
+
|
|
24
|
+
def template_file(self, template_path: str, values: dict) -> str:
|
|
25
|
+
with open(template_path, "r", encoding="utf-8") as file:
|
|
26
|
+
return self.template_string(file.read(), values)
|
|
27
|
+
|
|
28
|
+
def template_string(self, template_string: str, values: dict) -> str:
|
|
29
|
+
return jinja2.Template(template_string).render(values)
|
|
30
|
+
|
|
31
|
+
def copy(self, values: dict, templates_path: str | None = None):
|
|
32
|
+
if templates_path is None:
|
|
33
|
+
templates_path = self.templates_path
|
|
34
|
+
elif not os.path.isabs(templates_path):
|
|
35
|
+
templates_path = os.path.join(self.templates_path, templates_path)
|
|
36
|
+
|
|
37
|
+
relative_templates_path = os.path.relpath(
|
|
38
|
+
templates_path, start=self.templates_path
|
|
39
|
+
)
|
|
40
|
+
target_path = (
|
|
41
|
+
self.destination_path
|
|
42
|
+
if relative_templates_path == "."
|
|
43
|
+
else os.path.join(self.destination_path, relative_templates_path)
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
for file in os.listdir(templates_path):
|
|
47
|
+
if os.path.isfile(os.path.join(templates_path, file)):
|
|
48
|
+
if "config" in file and file.endswith(".yaml"):
|
|
49
|
+
shutil.copy(
|
|
50
|
+
os.path.join(templates_path, file),
|
|
51
|
+
self.template_string(os.path.join(target_path, file), values),
|
|
52
|
+
)
|
|
53
|
+
else:
|
|
54
|
+
self.template_file_to_file(
|
|
55
|
+
os.path.join(templates_path, file),
|
|
56
|
+
values,
|
|
57
|
+
os.path.join(target_path, file),
|
|
58
|
+
)
|
|
59
|
+
elif os.path.isdir(os.path.join(templates_path, file)):
|
|
60
|
+
os.makedirs(
|
|
61
|
+
self.template_string(os.path.join(target_path, file), values),
|
|
62
|
+
exist_ok=True,
|
|
63
|
+
)
|
|
64
|
+
self.copy(values, os.path.join(templates_path, file))
|
|
65
|
+
else:
|
|
66
|
+
print(f"Skipping {file}")
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: naas-abi-cli
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Abi cli allowing you to build your AI system.
|
|
5
|
+
Author-email: Maxime Jublou <maxime@naas.ai>, Florent Ravenel <florent@naas.ai>, Jeremy Ravenel <jeremy@naas.ai>
|
|
6
|
+
Requires-Python: <4,>=3.10
|
|
7
|
+
Requires-Dist: naas-abi-core[qdrant]>=1.1.2
|
|
8
|
+
Requires-Dist: naas-abi-marketplace[ai-chatgpt]>=1.1.0
|
|
9
|
+
Requires-Dist: naas-abi>=1.0.6
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
naas_abi_cli/README.md,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
2
|
+
naas_abi_cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
+
naas_abi_cli/cli/__init__.py,sha256=JJbXMmjdp4TUYkgpVhuTOH5acecBeYT-26xl8jX_Nv0,1340
|
|
4
|
+
naas_abi_cli/cli/agent.py,sha256=fMdbC7HsrOfZSf5zVRHWSmyrejI5mUdRlAT5v5YHXzk,658
|
|
5
|
+
naas_abi_cli/cli/chat.py,sha256=3t_TJ7vqCNs0MIIXOtlSke3nzy4rMSEJtB3P6pKItMo,856
|
|
6
|
+
naas_abi_cli/cli/config.py,sha256=CcdDX6HKCP32NjRhbVsCOwLUC9LmaqTm2sv8W5rOt00,1484
|
|
7
|
+
naas_abi_cli/cli/deploy.py,sha256=QD64psPMMkG2cpaaVTKnNgy65h3iEXkr_myP_GANa1A,6896
|
|
8
|
+
naas_abi_cli/cli/init.py,sha256=Pcy2-hy-FvpXf3UOKMP6agWyFrCl9z-KP5ktEWltPy0,220
|
|
9
|
+
naas_abi_cli/cli/module.py,sha256=TBl-SpeGUcy1Rrp40Irbt34yQS00xJcNje-OijNE4Hk,717
|
|
10
|
+
naas_abi_cli/cli/run.py,sha256=OLrAs0mtnI0jvyW5Bb_Jd3Sp-bWDMdulCcsPixEq-6s,308
|
|
11
|
+
naas_abi_cli/cli/secret.py,sha256=u_yUZgVEcns-CM-qsIIZUHX8j8T6aioJYluqSQhnXFE,2491
|
|
12
|
+
naas_abi_cli/cli/new/__init__.py,sha256=i-lOPJh8DL729CFhZyuXZibsaswsqPj5e7u_N68xXeM,156
|
|
13
|
+
naas_abi_cli/cli/new/module.py,sha256=e4gXnE7iQ4aCJpAU-0iMrHQ11T-PS11UDuifBLwyYFs,183
|
|
14
|
+
naas_abi_cli/cli/new/new.py,sha256=sfNmeoNZLGhjKRKSHuwEl1vtuxWR_SII0qOo9BVoEwY,55
|
|
15
|
+
naas_abi_cli/cli/new/project.py,sha256=ptYIh9_M-bOr-LEj6hzfKIYVLCaQtKhJXhP5OGKH0TQ,1912
|
|
16
|
+
naas_abi_cli/cli/new/templates/project/README.md,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
17
|
+
naas_abi_cli/cli/new/templates/project/pyproject.toml,sha256=LQfOYOCSYvI6BP5TGYoWV4dlfSAUVFBZJkXqf-em48o,165
|
|
18
|
+
naas_abi_cli/cli/new/templates/project/.github/workflows/release.yaml,sha256=7uV4SE20zvAfJKr1caKLbHIC3eyBmS-LOwjkKe6plhI,596
|
|
19
|
+
naas_abi_cli/cli/new/templates/project/{{project_name_snake}}/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
20
|
+
naas_abi_cli/cli/utils/Copier.py,sha256=YDYY9rxbL0TFB8IHKZc1ZR0w9tMUlX29jhVw4To8OQs,2640
|
|
21
|
+
naas_abi_cli-1.0.0.dist-info/METADATA,sha256=tpCdX-vwPS8duptF-8OBiuUsziQbq1NArlz2jOXIMx4,381
|
|
22
|
+
naas_abi_cli-1.0.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
23
|
+
naas_abi_cli-1.0.0.dist-info/entry_points.txt,sha256=ufNXhYVU3uo5dcZ8e1kdEJv1oh2Vons7LHJPg35cP4w,46
|
|
24
|
+
naas_abi_cli-1.0.0.dist-info/RECORD,,
|