cycls 0.0.2.33__py3-none-any.whl → 0.0.2.35__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.
- cycls/__init__.py +2 -2
- cycls/runtime.py +70 -27
- cycls/sdk.py +105 -20
- cycls/{cycls.py → web.py} +2 -76
- {cycls-0.0.2.33.dist-info → cycls-0.0.2.35.dist-info}/METADATA +1 -1
- cycls-0.0.2.35.dist-info/RECORD +9 -0
- cycls-0.0.2.33.dist-info/RECORD +0 -9
- {cycls-0.0.2.33.dist-info → cycls-0.0.2.35.dist-info}/WHEEL +0 -0
cycls/__init__.py
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
from .
|
|
2
|
-
from .
|
|
1
|
+
from .sdk import Agent
|
|
2
|
+
from .runtime import Runtime
|
cycls/runtime.py
CHANGED
|
@@ -9,14 +9,6 @@ from pathlib import Path
|
|
|
9
9
|
from contextlib import contextmanager
|
|
10
10
|
import tarfile
|
|
11
11
|
|
|
12
|
-
# --- Docker Client Initialization ---
|
|
13
|
-
try:
|
|
14
|
-
docker_client = docker.from_env()
|
|
15
|
-
except docker.errors.DockerException:
|
|
16
|
-
print("❌ Error: Docker is not running or not installed.")
|
|
17
|
-
print("Please start the Docker daemon and try again.")
|
|
18
|
-
sys.exit(1)
|
|
19
|
-
|
|
20
12
|
# --- Top-Level Helper Functions ---
|
|
21
13
|
|
|
22
14
|
def _bootstrap_script(payload_file: str, result_file: str) -> str:
|
|
@@ -84,7 +76,7 @@ class Runtime:
|
|
|
84
76
|
"""
|
|
85
77
|
def __init__(self, func, name, python_version=None, pip_packages=None, apt_packages=None, run_commands=None, copy=None, base_url=None, api_key=None):
|
|
86
78
|
self.func = func
|
|
87
|
-
self.python_version = python_version or "
|
|
79
|
+
self.python_version = python_version or f"{sys.version_info.major}.{sys.version_info.minor}"
|
|
88
80
|
self.pip_packages = sorted(pip_packages or [])
|
|
89
81
|
self.apt_packages = sorted(apt_packages or [])
|
|
90
82
|
self.run_commands = sorted(run_commands or [])
|
|
@@ -104,6 +96,50 @@ class Runtime:
|
|
|
104
96
|
self.tag = self._generate_base_tag()
|
|
105
97
|
|
|
106
98
|
self.api_key = api_key
|
|
99
|
+
self._docker_client = None
|
|
100
|
+
self.managed_label = f"cycls.runtime"
|
|
101
|
+
|
|
102
|
+
@property
|
|
103
|
+
def docker_client(self):
|
|
104
|
+
"""
|
|
105
|
+
Lazily initializes and returns a Docker client.
|
|
106
|
+
This ensures Docker is only required for methods that actually use it.
|
|
107
|
+
"""
|
|
108
|
+
if self._docker_client is None:
|
|
109
|
+
try:
|
|
110
|
+
print("🐳 Initializing Docker client...")
|
|
111
|
+
client = docker.from_env()
|
|
112
|
+
client.ping()
|
|
113
|
+
self._docker_client = client
|
|
114
|
+
except docker.errors.DockerException:
|
|
115
|
+
print("\n❌ Error: Docker is not running or is not installed.")
|
|
116
|
+
print(" This is required for local 'run' and 'build' operations.")
|
|
117
|
+
print(" Please start the Docker daemon and try again.")
|
|
118
|
+
sys.exit(1)
|
|
119
|
+
return self._docker_client
|
|
120
|
+
|
|
121
|
+
def _perform_auto_cleanup(self):
|
|
122
|
+
"""Performs a simple, automatic cleanup of old Docker resources."""
|
|
123
|
+
try:
|
|
124
|
+
for container in self.docker_client.containers.list(all=True, filters={"label": self.managed_label}):
|
|
125
|
+
container.remove(force=True)
|
|
126
|
+
|
|
127
|
+
cleaned_images = 0
|
|
128
|
+
for image in self.docker_client.images.list(name=self.image_prefix):
|
|
129
|
+
is_current = self.tag in image.tags
|
|
130
|
+
is_deployable = any(t.startswith(f"{self.image_prefix}:deploy-") for t in image.tags)
|
|
131
|
+
|
|
132
|
+
if not is_current and not is_deployable:
|
|
133
|
+
self.docker_client.images.remove(image.id, force=True)
|
|
134
|
+
cleaned_images += 1
|
|
135
|
+
|
|
136
|
+
if cleaned_images > 0:
|
|
137
|
+
print(f"🧹 Cleaned up {cleaned_images} old image version(s).")
|
|
138
|
+
|
|
139
|
+
self.docker_client.images.prune(filters={'label': self.managed_label})
|
|
140
|
+
|
|
141
|
+
except Exception as e:
|
|
142
|
+
print(f"⚠️ An error occurred during cleanup: {e}")
|
|
107
143
|
|
|
108
144
|
def _generate_base_tag(self) -> str:
|
|
109
145
|
"""Creates a unique tag for the base Docker image based on its dependencies."""
|
|
@@ -132,7 +168,11 @@ class Runtime:
|
|
|
132
168
|
if self.apt_packages else ""
|
|
133
169
|
)
|
|
134
170
|
run_shell_commands = "\n".join([f"RUN {cmd}" for cmd in self.run_commands]) if self.run_commands else ""
|
|
135
|
-
|
|
171
|
+
|
|
172
|
+
# fixes absolute path copy, but causes collision
|
|
173
|
+
copy_lines = "\n".join([f"COPY {Path(src).name} {dst}" for src, dst in self.copy.items()])
|
|
174
|
+
# copy_lines = "\n".join([f"COPY {src} {dst}" for src, dst in self.copy.items()])
|
|
175
|
+
|
|
136
176
|
expose_line = f"EXPOSE {port}" if port else ""
|
|
137
177
|
|
|
138
178
|
return f"""
|
|
@@ -166,26 +206,31 @@ COPY {self.payload_file} {self.io_dir}/
|
|
|
166
206
|
(workdir / self.payload_file).write_bytes(payload_bytes)
|
|
167
207
|
|
|
168
208
|
if self.copy:
|
|
209
|
+
# for src in self.copy.keys():
|
|
210
|
+
# _copy_path(Path(src), workdir / src)
|
|
211
|
+
|
|
212
|
+
# fixes absolute path copy
|
|
169
213
|
for src in self.copy.keys():
|
|
170
|
-
|
|
214
|
+
src_path = Path(src)
|
|
215
|
+
dest_path = workdir / src_path.name if src_path.is_absolute() else workdir / src
|
|
216
|
+
_copy_path(src_path, dest_path)
|
|
171
217
|
|
|
172
218
|
def _build_image_if_needed(self):
|
|
173
219
|
"""Checks if the base Docker image exists locally and builds it if not."""
|
|
174
220
|
try:
|
|
175
|
-
docker_client.images.get(self.tag)
|
|
221
|
+
self.docker_client.images.get(self.tag)
|
|
176
222
|
print(f"✅ Found cached base image: {self.tag}")
|
|
177
223
|
return
|
|
178
224
|
except docker.errors.ImageNotFound:
|
|
179
225
|
print(f"🛠️ Building new base image: {self.tag}")
|
|
180
226
|
|
|
181
|
-
|
|
182
|
-
with tempfile.TemporaryDirectory(dir="/tmp") as tmpdir_str:
|
|
227
|
+
with tempfile.TemporaryDirectory() as tmpdir_str:
|
|
183
228
|
tmpdir = Path(tmpdir_str)
|
|
184
229
|
# Prepare context without payload for the base image
|
|
185
230
|
self._prepare_build_context(tmpdir)
|
|
186
231
|
|
|
187
232
|
print("--- 🐳 Docker Build Logs (Base Image) ---")
|
|
188
|
-
response_generator = docker_client.api.build(
|
|
233
|
+
response_generator = self.docker_client.api.build(
|
|
189
234
|
path=str(tmpdir),
|
|
190
235
|
tag=self.tag,
|
|
191
236
|
forcerm=True,
|
|
@@ -206,12 +251,12 @@ COPY {self.payload_file} {self.io_dir}/
|
|
|
206
251
|
def runner(self, *args, **kwargs):
|
|
207
252
|
"""Context manager to set up, run, and tear down the container for local execution."""
|
|
208
253
|
port = kwargs.get('port', None)
|
|
254
|
+
self._perform_auto_cleanup()
|
|
209
255
|
self._build_image_if_needed()
|
|
210
256
|
container = None
|
|
211
257
|
ports_mapping = {f'{port}/tcp': port} if port else None
|
|
212
258
|
|
|
213
|
-
|
|
214
|
-
with tempfile.TemporaryDirectory(dir="/tmp") as tmpdir_str:
|
|
259
|
+
with tempfile.TemporaryDirectory() as tmpdir_str:
|
|
215
260
|
tmpdir = Path(tmpdir_str)
|
|
216
261
|
payload_path = tmpdir / self.payload_file
|
|
217
262
|
result_path = tmpdir / self.result_file
|
|
@@ -220,10 +265,11 @@ COPY {self.payload_file} {self.io_dir}/
|
|
|
220
265
|
cloudpickle.dump((self.func, args, kwargs), f)
|
|
221
266
|
|
|
222
267
|
try:
|
|
223
|
-
container = docker_client.containers.create(
|
|
268
|
+
container = self.docker_client.containers.create(
|
|
224
269
|
image=self.tag,
|
|
225
270
|
volumes={str(tmpdir): {'bind': self.io_dir, 'mode': 'rw'}},
|
|
226
|
-
ports=ports_mapping
|
|
271
|
+
ports=ports_mapping,
|
|
272
|
+
labels={self.managed_label: "true"}
|
|
227
273
|
)
|
|
228
274
|
container.start()
|
|
229
275
|
yield container, result_path
|
|
@@ -271,19 +317,18 @@ COPY {self.payload_file} {self.io_dir}/
|
|
|
271
317
|
final_tag = f"{self.image_prefix}:deploy-{payload_hash}"
|
|
272
318
|
|
|
273
319
|
try:
|
|
274
|
-
docker_client.images.get(final_tag)
|
|
320
|
+
self.docker_client.images.get(final_tag)
|
|
275
321
|
print(f"✅ Found cached deployable image: {final_tag}")
|
|
276
322
|
return final_tag
|
|
277
323
|
except docker.errors.ImageNotFound:
|
|
278
324
|
print(f"🛠️ Building new deployable image: {final_tag}")
|
|
279
325
|
|
|
280
|
-
|
|
281
|
-
with tempfile.TemporaryDirectory(dir="/tmp") as tmpdir_str:
|
|
326
|
+
with tempfile.TemporaryDirectory() as tmpdir_str:
|
|
282
327
|
tmpdir = Path(tmpdir_str)
|
|
283
328
|
self._prepare_build_context(tmpdir, include_payload=True, args=args, kwargs=kwargs)
|
|
284
329
|
|
|
285
330
|
print("--- 🐳 Docker Build Logs (Final Image) ---")
|
|
286
|
-
response_generator = docker_client.api.build(
|
|
331
|
+
response_generator = self.docker_client.api.build(
|
|
287
332
|
path=str(tmpdir), tag=final_tag, forcerm=True, decode=True
|
|
288
333
|
)
|
|
289
334
|
try:
|
|
@@ -309,8 +354,7 @@ COPY {self.payload_file} {self.io_dir}/
|
|
|
309
354
|
payload_hash = hashlib.sha256(cloudpickle.dumps((self.func, args, kwargs))).hexdigest()[:16]
|
|
310
355
|
archive_name = f"source-{self.tag.split(':')[1]}-{payload_hash}.tar.gz"
|
|
311
356
|
|
|
312
|
-
|
|
313
|
-
with tempfile.TemporaryDirectory(dir="/tmp") as tmpdir_str:
|
|
357
|
+
with tempfile.TemporaryDirectory() as tmpdir_str:
|
|
314
358
|
tmpdir = Path(tmpdir_str)
|
|
315
359
|
self._prepare_build_context(tmpdir, include_payload=True, args=args, kwargs=kwargs)
|
|
316
360
|
|
|
@@ -375,8 +419,7 @@ COPY {self.payload_file} {self.io_dir}/
|
|
|
375
419
|
|
|
376
420
|
port = kwargs.get('port', 8080)
|
|
377
421
|
|
|
378
|
-
|
|
379
|
-
with tempfile.TemporaryDirectory(dir="/tmp") as tmpdir_str:
|
|
422
|
+
with tempfile.TemporaryDirectory() as tmpdir_str:
|
|
380
423
|
tmpdir = Path(tmpdir_str)
|
|
381
424
|
self._prepare_build_context(tmpdir, include_payload=True, args=args, kwargs=kwargs)
|
|
382
425
|
|
cycls/sdk.py
CHANGED
|
@@ -1,22 +1,107 @@
|
|
|
1
|
+
import json, time, modal, inspect, uvicorn
|
|
1
2
|
from .runtime import Runtime
|
|
3
|
+
from modal.runner import run_app
|
|
4
|
+
from .web import web
|
|
5
|
+
import importlib.resources
|
|
2
6
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
7
|
+
theme_path = importlib.resources.files('cycls').joinpath('theme')
|
|
8
|
+
cycls_path = importlib.resources.files('cycls')
|
|
9
|
+
|
|
10
|
+
class Agent:
|
|
11
|
+
def __init__(self, theme=theme_path, org=None, api_token=None, pip=[], apt=[], copy=[], keys=["",""], api_key=None):
|
|
12
|
+
self.org, self.api_token = org, api_token
|
|
13
|
+
self.theme = theme
|
|
14
|
+
self.keys, self.pip, self.apt, self.copy = keys, pip, apt, copy
|
|
15
|
+
self.api_key = api_key
|
|
16
|
+
|
|
17
|
+
self.registered_functions = []
|
|
18
|
+
|
|
19
|
+
def __call__(self, name=None, header="", intro="", domain=None, auth=False):
|
|
20
|
+
def decorator(f):
|
|
21
|
+
self.registered_functions.append({
|
|
22
|
+
"func": f,
|
|
23
|
+
"config": ["public", False, self.org, self.api_token, header, intro, auth],
|
|
24
|
+
# "name": name,
|
|
25
|
+
"name": name or (f.__name__).replace('_', '-'),
|
|
26
|
+
"domain": domain or f"{name}.cycls.ai",
|
|
27
|
+
})
|
|
28
|
+
return f
|
|
29
|
+
return decorator
|
|
30
|
+
|
|
31
|
+
def run(self, port=8080):
|
|
32
|
+
if not self.registered_functions:
|
|
33
|
+
print("Error: No @agent decorated function found.")
|
|
34
|
+
return
|
|
35
|
+
|
|
36
|
+
i = self.registered_functions[0]
|
|
37
|
+
if len(self.registered_functions) > 1:
|
|
38
|
+
print(f"⚠️ Warning: Multiple agents found. Running '{i['name']}'.")
|
|
39
|
+
print(f"🚀 Starting local server at localhost:{port}")
|
|
40
|
+
i["config"][0], i["config"][6] = self.theme, False
|
|
41
|
+
uvicorn.run(web(i["func"], *i["config"]), host="0.0.0.0", port=port)
|
|
42
|
+
return
|
|
43
|
+
|
|
44
|
+
def cycls(self, prod=False, port=8080):
|
|
45
|
+
if not self.registered_functions:
|
|
46
|
+
print("Error: No @agent decorated function found.")
|
|
47
|
+
return
|
|
48
|
+
if (self.api_key is None) and prod:
|
|
49
|
+
print("🛑 Error: Please add your Cycls API key")
|
|
50
|
+
return
|
|
51
|
+
|
|
52
|
+
i = self.registered_functions[0]
|
|
53
|
+
if len(self.registered_functions) > 1:
|
|
54
|
+
print(f"⚠️ Warning: Multiple agents found. Running '{i['name']}'.")
|
|
55
|
+
|
|
56
|
+
i["config"][6] = False
|
|
57
|
+
|
|
58
|
+
copy={str(cycls_path.joinpath('theme')):"public", str(cycls_path)+"/web.py":"app/web.py"}
|
|
59
|
+
copy.update({i:i for i in self.copy})
|
|
60
|
+
|
|
61
|
+
new = Runtime(
|
|
62
|
+
func=lambda port: __import__("uvicorn").run(__import__("web").web(i["func"], *i["config"]), host="0.0.0.0", port=port),
|
|
63
|
+
name=i["name"],
|
|
64
|
+
apt_packages=self.apt,
|
|
65
|
+
pip_packages=["fastapi[standard]", "pyjwt", "cryptography", "uvicorn", *self.pip],
|
|
66
|
+
copy=copy,
|
|
67
|
+
api_key=self.api_key
|
|
68
|
+
)
|
|
69
|
+
new.deploy(port=port) if prod else new.run(port=port)
|
|
70
|
+
return
|
|
71
|
+
|
|
72
|
+
def push(self, prod=False):
|
|
73
|
+
self.client = modal.Client.from_credentials(*self.keys)
|
|
74
|
+
image = (modal.Image.debian_slim()
|
|
75
|
+
.pip_install("fastapi[standard]", "pyjwt", "cryptography", *self.pip)
|
|
76
|
+
.apt_install(*self.apt)
|
|
77
|
+
.add_local_dir(self.theme, "/root/public")
|
|
78
|
+
.add_local_file(str(cycls_path)+"/web.py", "/root/web.py"))
|
|
79
|
+
for item in self.copy:
|
|
80
|
+
image = image.add_local_file(item, f"/root/{item}") if "." in item else image.add_local_dir(item, f'/root/{item}')
|
|
81
|
+
self.app = modal.App("development", image=image)
|
|
82
|
+
|
|
83
|
+
if not self.registered_functions:
|
|
84
|
+
print("Error: No @agent decorated function found.")
|
|
85
|
+
return
|
|
86
|
+
|
|
87
|
+
for i in self.registered_functions:
|
|
88
|
+
i["config"][1] = True if prod else False
|
|
89
|
+
self.app.function(serialized=True, name=i["name"])(
|
|
90
|
+
modal.asgi_app(label=i["name"], custom_domains=[i["domain"]])
|
|
91
|
+
(lambda: __import__("web").web(i["func"], *i["config"]))
|
|
92
|
+
)
|
|
93
|
+
if prod:
|
|
94
|
+
for i in self.registered_functions:
|
|
95
|
+
print(f"✅ Deployed to ⇒ https://{i['domain']}")
|
|
96
|
+
self.app.deploy(client=self.client, name=self.registered_functions[0]["name"])
|
|
97
|
+
return
|
|
98
|
+
else:
|
|
99
|
+
with modal.enable_output():
|
|
100
|
+
run_app(app=self.app, client=self.client)
|
|
101
|
+
print(" Modal development server is running. Press Ctrl+C to stop.")
|
|
102
|
+
with modal.enable_output(), run_app(app=self.app, client=self.client):
|
|
103
|
+
while True: time.sleep(10)
|
|
104
|
+
|
|
105
|
+
# poetry config pypi-token.pypi <your-token>
|
|
106
|
+
# poetry run python agent.py
|
|
107
|
+
# poetry publish --build
|
cycls/{cycls.py → web.py}
RENAMED
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
import json,
|
|
2
|
-
from modal.runner import run_app
|
|
3
|
-
|
|
4
|
-
import importlib.resources
|
|
5
|
-
theme_path = importlib.resources.files('cycls').joinpath('theme')
|
|
1
|
+
import json, inspect
|
|
6
2
|
|
|
7
3
|
async def openai_encoder(stream): # clean up the meta data / new API?
|
|
8
4
|
async for message in stream:
|
|
@@ -41,7 +37,6 @@ XwIDAQAB
|
|
|
41
37
|
"""
|
|
42
38
|
|
|
43
39
|
def web(func, front_end_path="", prod=False, org=None, api_token=None, header="", intro="", auth=True): # API auth
|
|
44
|
-
print(front_end_path)
|
|
45
40
|
from fastapi import FastAPI, Request, HTTPException, status, Depends
|
|
46
41
|
from fastapi.responses import StreamingResponse , HTMLResponse
|
|
47
42
|
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
|
|
@@ -97,73 +92,4 @@ def web(func, front_end_path="", prod=False, org=None, api_token=None, header=""
|
|
|
97
92
|
"pk_live": "pk_live_Y2xlcmsuY3ljbHMuY29tJA", "pk_test": "pk_test_c2VsZWN0LXNsb3RoLTU4LmNsZXJrLmFjY291bnRzLmRldiQ"
|
|
98
93
|
})
|
|
99
94
|
app.mount("/", StaticFiles(directory=front_end_path, html=False))
|
|
100
|
-
return app
|
|
101
|
-
|
|
102
|
-
class Agent:
|
|
103
|
-
def __init__(self, theme=theme_path, org=None, api_token=None, pip=[], apt=[], copy=[], keys=["",""]):
|
|
104
|
-
self.org, self.api_token = org, api_token
|
|
105
|
-
self.theme = theme
|
|
106
|
-
self.keys, self.pip, self.apt, self.copy = keys, pip, apt, copy
|
|
107
|
-
|
|
108
|
-
self.registered_functions = []
|
|
109
|
-
|
|
110
|
-
def __call__(self, name="", header="", intro="", domain=None, auth=False):
|
|
111
|
-
def decorator(f):
|
|
112
|
-
self.registered_functions.append({
|
|
113
|
-
"func": f,
|
|
114
|
-
"config": ["public", False, self.org, self.api_token, header, intro, auth],
|
|
115
|
-
"name": name,
|
|
116
|
-
"domain": domain or f"{name}.cycls.ai",
|
|
117
|
-
})
|
|
118
|
-
return f
|
|
119
|
-
return decorator
|
|
120
|
-
|
|
121
|
-
def run(self, port=8000):
|
|
122
|
-
if not self.registered_functions:
|
|
123
|
-
print("Error: No @agent decorated function found.")
|
|
124
|
-
return
|
|
125
|
-
|
|
126
|
-
i = self.registered_functions[0]
|
|
127
|
-
if len(self.registered_functions) > 1:
|
|
128
|
-
print(f"⚠️ Warning: Multiple agents found. Running '{i['name']}'.")
|
|
129
|
-
print(f"🚀 Starting local server at localhost:{port}")
|
|
130
|
-
i["config"][0], i["config"][6] = self.theme, False
|
|
131
|
-
uvicorn.run(web(i["func"], *i["config"]), host="0.0.0.0", port=port)
|
|
132
|
-
return
|
|
133
|
-
|
|
134
|
-
def push(self, prod=False):
|
|
135
|
-
self.client = modal.Client.from_credentials(*self.keys)
|
|
136
|
-
image = (modal.Image.debian_slim()
|
|
137
|
-
.pip_install("fastapi[standard]", "pyjwt", "cryptography", *self.pip)
|
|
138
|
-
.apt_install(*self.apt)
|
|
139
|
-
.add_local_dir(self.theme, "/root/public")
|
|
140
|
-
.add_local_python_source("cycls"))
|
|
141
|
-
for item in self.copy:
|
|
142
|
-
image = image.add_local_file(item, f"/root/{item}") if "." in item else image.add_local_dir(item, f'/root/{item}')
|
|
143
|
-
self.app = modal.App("development", image=image)
|
|
144
|
-
|
|
145
|
-
if not self.registered_functions:
|
|
146
|
-
print("Error: No @agent decorated function found.")
|
|
147
|
-
return
|
|
148
|
-
|
|
149
|
-
for i in self.registered_functions:
|
|
150
|
-
i["config"][1] = True if prod else False
|
|
151
|
-
self.app.function(serialized=True, name=i["name"])(
|
|
152
|
-
modal.asgi_app(label=i["name"], custom_domains=[i["domain"]])
|
|
153
|
-
(lambda: web(i["func"], *i["config"]))
|
|
154
|
-
)
|
|
155
|
-
if prod:
|
|
156
|
-
for i in self.registered_functions:
|
|
157
|
-
print(f"✅ Deployed to ⇒ https://{i['domain']}")
|
|
158
|
-
self.app.deploy(client=self.client, name=self.registered_functions[0]["name"])
|
|
159
|
-
return
|
|
160
|
-
else:
|
|
161
|
-
with modal.enable_output():
|
|
162
|
-
run_app(app=self.app, client=self.client)
|
|
163
|
-
print(" Modal development server is running. Press Ctrl+C to stop.")
|
|
164
|
-
with modal.enable_output(), run_app(app=self.app, client=self.client):
|
|
165
|
-
while True: time.sleep(10)
|
|
166
|
-
|
|
167
|
-
# poetry config pypi-token.pypi <your-token>
|
|
168
|
-
# poetry run python agent.py
|
|
169
|
-
# poetry publish --build
|
|
95
|
+
return app
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
cycls/__init__.py,sha256=ysfA4R_r_INokmpZGK2S8wf2ypLCqwyj0WdfX-hx0bs,51
|
|
2
|
+
cycls/runtime.py,sha256=U0dMUs9dxgjMTbtv9dzK3MQ57g8kun0ICqA30Cx9cBA,18187
|
|
3
|
+
cycls/sdk.py,sha256=gEdxYDtYE6FOWwLS33k0h5wuzwdcB9kvTMrkEBmz2iE,4573
|
|
4
|
+
cycls/theme/assets/index-D0-uI8sw.js,sha256=aUsqm9HZtEJz38o-0MW12ZVeOlSeKigwc_fYJBntiyI,1068551
|
|
5
|
+
cycls/theme/index.html,sha256=epB4cgSjC7xJOXpVuCwt9r7ivoGvLiXSrxsoOgINw58,895
|
|
6
|
+
cycls/web.py,sha256=nSEJMUQoPaz8qjgVmsC1JiDRv9Y1UKVzTH4_pRHp_VE,4260
|
|
7
|
+
cycls-0.0.2.35.dist-info/METADATA,sha256=DztQ_n4XO8rvuwGq-bN7Y028yINUNjcH3XYbjF-OKsI,5666
|
|
8
|
+
cycls-0.0.2.35.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
|
|
9
|
+
cycls-0.0.2.35.dist-info/RECORD,,
|
cycls-0.0.2.33.dist-info/RECORD
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
cycls/__init__.py,sha256=9oKTafASGMuJbq_Sk0sfCSQKKwUD8mcdYFovDDzVuW4,50
|
|
2
|
-
cycls/cycls.py,sha256=rLhssEPs4EwDbNsO5cGdKkP888dOaYToMPKVLZWan5U,7479
|
|
3
|
-
cycls/runtime.py,sha256=y_ilfi418m-9ZsRvY7H7zrO0FYdhz2X-hnZ9DACaSW4,16289
|
|
4
|
-
cycls/sdk.py,sha256=EcUYi0pGKtCajKzwLg3uH8CRPfc78KBHXgB6ShABxdE,1110
|
|
5
|
-
cycls/theme/assets/index-D0-uI8sw.js,sha256=aUsqm9HZtEJz38o-0MW12ZVeOlSeKigwc_fYJBntiyI,1068551
|
|
6
|
-
cycls/theme/index.html,sha256=epB4cgSjC7xJOXpVuCwt9r7ivoGvLiXSrxsoOgINw58,895
|
|
7
|
-
cycls-0.0.2.33.dist-info/METADATA,sha256=HKNRnxvSkLwolKtk2-QUgQUGzbpKHSqv_hOx9Gpjnis,5666
|
|
8
|
-
cycls-0.0.2.33.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
|
|
9
|
-
cycls-0.0.2.33.dist-info/RECORD,,
|
|
File without changes
|