naas-abi-cli 1.0.0__tar.gz

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.
@@ -0,0 +1,96 @@
1
+ # Environment files
2
+ .env
3
+ .env.*
4
+ !.env.example
5
+
6
+ # Virtual Environment
7
+ .venv/
8
+ venv/
9
+ ENV/
10
+ env/
11
+ uv.lock
12
+
13
+ # Config
14
+ config.yaml
15
+ config.*.yaml
16
+
17
+ # Python
18
+ __pycache__/
19
+ *.py[cod]
20
+ *$py.class
21
+ *.so
22
+ .Python
23
+ build/
24
+ develop-eggs/
25
+ dist/
26
+ downloads/
27
+ eggs/
28
+ .eggs/
29
+ #lib/
30
+ lib64/
31
+ parts/
32
+ sdist/
33
+ var/
34
+ wheels/
35
+ *.egg-info/
36
+ .installed.cfg
37
+ *.egg
38
+ .mypy_cache/
39
+ .pytest_cache/
40
+ .ruff_cache/
41
+ .pytype
42
+
43
+ # IDEs and editors
44
+ .idea/
45
+ .vscode/
46
+ *.swp
47
+ *.swo
48
+ .DS_Store
49
+
50
+ # Docker
51
+ .docker/
52
+
53
+ # Logs
54
+ *.log
55
+
56
+ # Project specific
57
+ thread_id.txt
58
+ agentic-director/
59
+
60
+ # Storage - ignore everything in storage except README files
61
+ storage/*
62
+ !storage/README.md
63
+ !storage/*/README.md
64
+ !storage/datastore/
65
+ !storage/triplestore/
66
+ !storage/vectorstore/
67
+ storage/datastore/*
68
+ storage/triplestore/*
69
+ storage/vectorstore/*
70
+
71
+ # Ignore data files globally
72
+ *.csv
73
+ *.parquet
74
+ *.json
75
+ *.pdf
76
+ *.doc
77
+ *.docx
78
+ *.ppt
79
+ *.pptx
80
+ *.xls
81
+ *.xlsx
82
+ *.jpg
83
+ *.png
84
+ *.gif
85
+ *.jpeg
86
+ *.webp
87
+ *.heic
88
+
89
+
90
+ *.pid
91
+ sandbox/
92
+
93
+ # Coverage reports
94
+ .coverage
95
+ coverage.xml
96
+ htmlcov/
@@ -0,0 +1,5 @@
1
+ Collecting uv
2
+ Using cached uv-0.9.18-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (11 kB)
3
+ Using cached uv-0.9.18-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (22.2 MB)
4
+ Installing collected packages: uv
5
+ Successfully installed uv-0.9.18
@@ -0,0 +1,7 @@
1
+ # CHANGELOG
2
+
3
+ <!-- version list -->
4
+
5
+ ## v1.0.0 (2025-12-19)
6
+
7
+ - Initial Release
@@ -0,0 +1,3 @@
1
+ install:
2
+ #@echo "Starting `abi` setup on your machine. It will replace any existing `abi`. You will then have access to the `abi` command in your terminal."
3
+ uv tool install --editable . --force
@@ -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
File without changes
@@ -0,0 +1 @@
1
+
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)
@@ -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)
@@ -0,0 +1,13 @@
1
+ import os
2
+
3
+ import click
4
+
5
+
6
+ @click.command("init")
7
+ @click.argument("path")
8
+ def init(path: str):
9
+ if path == ".":
10
+ path = os.getcwd()
11
+
12
+ os.makedirs(path, exist_ok=True)
13
+ # os.exec(f"cd {path} && uv init .")
@@ -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,4 @@
1
+ import naas_abi_cli.cli.new.module as module # noqa: F401
2
+ import naas_abi_cli.cli.new.project as project # noqa: F401
3
+
4
+ from .new import new # noqa: F401
@@ -0,0 +1,10 @@
1
+ import click
2
+
3
+ from .new import new
4
+
5
+
6
+ @new.command("module")
7
+ @click.argument("module-name")
8
+ @click.argument("module-path")
9
+ def new_module(module_name: str, module_path: str):
10
+ pass
@@ -0,0 +1,6 @@
1
+ import click
2
+
3
+
4
+ @click.group("new")
5
+ def new():
6
+ pass
@@ -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
@@ -0,0 +1,8 @@
1
+ [project]
2
+ name = "{{ project_name }}"
3
+ version = "0.1.0"
4
+ description = "Add your description here"
5
+ readme = "README.md"
6
+ requires-python = ">=3.12"
7
+ dependencies = [
8
+ ]
@@ -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,71 @@
1
+ [project]
2
+ name = "naas-abi-cli"
3
+ version = "1.0.0"
4
+ description = "Abi cli allowing you to build your AI system."
5
+ authors = [{ name = "Maxime Jublou", email = "maxime@naas.ai" },{ name = "Florent Ravenel", email = "florent@naas.ai" }, { name = "Jeremy Ravenel", email = "jeremy@naas.ai" }]
6
+ requires-python = ">=3.10,<4"
7
+ readme = "README.md"
8
+ dependencies = [
9
+ "naas-abi>=1.0.6",
10
+ "naas-abi-core[qdrant]>=1.1.2",
11
+ "naas-abi-marketplace[ai-chatgpt]>=1.1.0",
12
+ ]
13
+
14
+ [project.scripts]
15
+ abi = "naas_abi_cli.cli:main"
16
+
17
+ [build-system]
18
+ requires = ["hatchling"]
19
+ build-backend = "hatchling.build"
20
+
21
+ [tool.hatch.metadata]
22
+ allow-direct-references = true
23
+
24
+ [dependency-groups]
25
+ dev = [
26
+ "mypy>=1.19.0",
27
+ "pytest>=9.0.1",
28
+ "python-semantic-release>=9.21.0",
29
+ ]
30
+
31
+ [tool.semantic_release]
32
+ commit_parser = "conventional-monorepo"
33
+ commit_message = """\
34
+ chore(release): naas-abi-cli@{version}`
35
+
36
+ Automatically generated by python-semantic-release
37
+ """
38
+ tag_format = "naas-abi-cli-v{version}"
39
+ version_toml = ["pyproject.toml:project.version"]
40
+ build_command = """
41
+ python -m pip install uv>=0.9.13
42
+ uv lock --upgrade-package "$PACKAGE_NAME"
43
+ git add uv.lock
44
+ uv build
45
+ """
46
+
47
+ [tool.semantic_release.commit_parser_options]
48
+ path_filters = ["."]
49
+ # scope_prefix = "naas-abi-cli-"
50
+
51
+ [tool.semantic_release.branches.main]
52
+ match = "main"
53
+ prerelease = false
54
+
55
+ [tool.semantic_release.branches.dev]
56
+ match = "^dev"
57
+ prerelease = true
58
+ prerelease_token = "dev"
59
+
60
+ # [tool.hatch.build.targets.wheel.force-include]
61
+ # "assets" = "assets"
62
+
63
+ # [tool.hatch.build.targets.sdist.force-include]
64
+ # "assets" = "assets"
65
+
66
+ [tool.uv.sources]
67
+ naas-abi = { path = "../naas-abi", editable = true }
68
+ naas-abi-core = { path = "../naas-abi-core", editable = true }
69
+ naas-abi-marketplace = { path = "../naas-abi-marketplace", editable = true }
70
+
71
+