cycls 0.0.2.32__py3-none-any.whl → 0.0.2.34__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 +12 -2
- cycls/sdk.py +100 -20
- cycls/{cycls.py → web.py} +2 -76
- {cycls-0.0.2.32.dist-info → cycls-0.0.2.34.dist-info}/METADATA +1 -1
- cycls-0.0.2.34.dist-info/RECORD +9 -0
- cycls-0.0.2.32.dist-info/RECORD +0 -9
- {cycls-0.0.2.32.dist-info → cycls-0.0.2.34.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
|
@@ -132,7 +132,11 @@ class Runtime:
|
|
|
132
132
|
if self.apt_packages else ""
|
|
133
133
|
)
|
|
134
134
|
run_shell_commands = "\n".join([f"RUN {cmd}" for cmd in self.run_commands]) if self.run_commands else ""
|
|
135
|
-
|
|
135
|
+
|
|
136
|
+
# fixes absolute path copy, but causes collision
|
|
137
|
+
copy_lines = "\n".join([f"COPY {Path(src).name} {dst}" for src, dst in self.copy.items()])
|
|
138
|
+
# copy_lines = "\n".join([f"COPY {src} {dst}" for src, dst in self.copy.items()])
|
|
139
|
+
|
|
136
140
|
expose_line = f"EXPOSE {port}" if port else ""
|
|
137
141
|
|
|
138
142
|
return f"""
|
|
@@ -166,8 +170,14 @@ COPY {self.payload_file} {self.io_dir}/
|
|
|
166
170
|
(workdir / self.payload_file).write_bytes(payload_bytes)
|
|
167
171
|
|
|
168
172
|
if self.copy:
|
|
173
|
+
# for src in self.copy.keys():
|
|
174
|
+
# _copy_path(Path(src), workdir / src)
|
|
175
|
+
|
|
176
|
+
# fixes absolute path copy
|
|
169
177
|
for src in self.copy.keys():
|
|
170
|
-
|
|
178
|
+
src_path = Path(src)
|
|
179
|
+
dest_path = workdir / src_path.name if src_path.is_absolute() else workdir / src
|
|
180
|
+
_copy_path(src_path, dest_path)
|
|
171
181
|
|
|
172
182
|
def _build_image_if_needed(self):
|
|
173
183
|
"""Checks if the base Docker image exists locally and builds it if not."""
|
cycls/sdk.py
CHANGED
|
@@ -1,22 +1,102 @@
|
|
|
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="", 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
|
+
"domain": domain or f"{name}.cycls.ai",
|
|
26
|
+
})
|
|
27
|
+
return f
|
|
28
|
+
return decorator
|
|
29
|
+
|
|
30
|
+
def run(self, port=8080):
|
|
31
|
+
if not self.registered_functions:
|
|
32
|
+
print("Error: No @agent decorated function found.")
|
|
33
|
+
return
|
|
34
|
+
|
|
35
|
+
i = self.registered_functions[0]
|
|
36
|
+
if len(self.registered_functions) > 1:
|
|
37
|
+
print(f"⚠️ Warning: Multiple agents found. Running '{i['name']}'.")
|
|
38
|
+
print(f"🚀 Starting local server at localhost:{port}")
|
|
39
|
+
i["config"][0], i["config"][6] = self.theme, False
|
|
40
|
+
uvicorn.run(web(i["func"], *i["config"]), host="0.0.0.0", port=port)
|
|
41
|
+
return
|
|
42
|
+
|
|
43
|
+
def deploy(self, prod=False, port=8080):
|
|
44
|
+
if not self.registered_functions:
|
|
45
|
+
print("Error: No @agent decorated function found.")
|
|
46
|
+
return
|
|
47
|
+
if (self.api_key is None) and prod:
|
|
48
|
+
print("🛑 Error: Please add your Cycls API key")
|
|
49
|
+
return
|
|
50
|
+
|
|
51
|
+
i = self.registered_functions[0]
|
|
52
|
+
if len(self.registered_functions) > 1:
|
|
53
|
+
print(f"⚠️ Warning: Multiple agents found. Running '{i['name']}'.")
|
|
54
|
+
|
|
55
|
+
i["config"][6] = False
|
|
56
|
+
|
|
57
|
+
new = Runtime(
|
|
58
|
+
func=lambda port: __import__("uvicorn").run(__import__("web").web(i["func"], *i["config"]), host="0.0.0.0", port=port),
|
|
59
|
+
name="web-agent",
|
|
60
|
+
pip_packages=["fastapi[standard]", "pyjwt", "cryptography", "uvicorn", *self.pip],
|
|
61
|
+
copy={str(cycls_path.joinpath('theme')):"public", str(cycls_path)+"/web.py":"app/web.py"},
|
|
62
|
+
api_key=self.api_key
|
|
63
|
+
)
|
|
64
|
+
new.deploy(port=port) if prod else new.run(port=port)
|
|
65
|
+
return
|
|
66
|
+
|
|
67
|
+
def push(self, prod=False):
|
|
68
|
+
self.client = modal.Client.from_credentials(*self.keys)
|
|
69
|
+
image = (modal.Image.debian_slim()
|
|
70
|
+
.pip_install("fastapi[standard]", "pyjwt", "cryptography", *self.pip)
|
|
71
|
+
.apt_install(*self.apt)
|
|
72
|
+
.add_local_dir(self.theme, "/root/public")
|
|
73
|
+
.add_local_file(str(cycls_path)+"/web.py", "/root/web.py"))
|
|
74
|
+
for item in self.copy:
|
|
75
|
+
image = image.add_local_file(item, f"/root/{item}") if "." in item else image.add_local_dir(item, f'/root/{item}')
|
|
76
|
+
self.app = modal.App("development", image=image)
|
|
77
|
+
|
|
78
|
+
if not self.registered_functions:
|
|
79
|
+
print("Error: No @agent decorated function found.")
|
|
80
|
+
return
|
|
81
|
+
|
|
82
|
+
for i in self.registered_functions:
|
|
83
|
+
i["config"][1] = True if prod else False
|
|
84
|
+
self.app.function(serialized=True, name=i["name"])(
|
|
85
|
+
modal.asgi_app(label=i["name"], custom_domains=[i["domain"]])
|
|
86
|
+
(lambda: __import__("web").web(i["func"], *i["config"]))
|
|
87
|
+
)
|
|
88
|
+
if prod:
|
|
89
|
+
for i in self.registered_functions:
|
|
90
|
+
print(f"✅ Deployed to ⇒ https://{i['domain']}")
|
|
91
|
+
self.app.deploy(client=self.client, name=self.registered_functions[0]["name"])
|
|
92
|
+
return
|
|
93
|
+
else:
|
|
94
|
+
with modal.enable_output():
|
|
95
|
+
run_app(app=self.app, client=self.client)
|
|
96
|
+
print(" Modal development server is running. Press Ctrl+C to stop.")
|
|
97
|
+
with modal.enable_output(), run_app(app=self.app, client=self.client):
|
|
98
|
+
while True: time.sleep(10)
|
|
99
|
+
|
|
100
|
+
# poetry config pypi-token.pypi <your-token>
|
|
101
|
+
# poetry run python agent.py
|
|
102
|
+
# 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=rUQnaweV9y5fPcKxTmOqrQO2nT_A5wqgSElonY6GNmw,16366
|
|
3
|
+
cycls/sdk.py,sha256=tzTWNzWVdQPh-I-AmhkkgQ0M3xJyMy-x-vFAlq1XihU,4408
|
|
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.34.dist-info/METADATA,sha256=fRwJekFv5Rzy2izqhMtf6IhMmHd3GBdZRnwEO66FxbY,5666
|
|
8
|
+
cycls-0.0.2.34.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
|
|
9
|
+
cycls-0.0.2.34.dist-info/RECORD,,
|
cycls-0.0.2.32.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=quBV8G8DkqsK_QzTraiYe7P9x9W5X1Zl8XDsvhUlPus,15939
|
|
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.32.dist-info/METADATA,sha256=si7x9VRhZUM9ZlrBN3Iusp6iemuHyWoOK-UT6WIZrmA,5666
|
|
8
|
-
cycls-0.0.2.32.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
|
|
9
|
-
cycls-0.0.2.32.dist-info/RECORD,,
|
|
File without changes
|