cycls 0.0.2.32__tar.gz → 0.0.2.34__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.
- {cycls-0.0.2.32 → cycls-0.0.2.34}/PKG-INFO +1 -1
- cycls-0.0.2.34/cycls/__init__.py +2 -0
- {cycls-0.0.2.32 → cycls-0.0.2.34}/cycls/runtime.py +12 -2
- cycls-0.0.2.34/cycls/sdk.py +102 -0
- cycls-0.0.2.32/cycls/cycls.py → cycls-0.0.2.34/cycls/web.py +2 -76
- {cycls-0.0.2.32 → cycls-0.0.2.34}/pyproject.toml +1 -1
- cycls-0.0.2.32/cycls/__init__.py +0 -2
- cycls-0.0.2.32/cycls/sdk.py +0 -22
- {cycls-0.0.2.32 → cycls-0.0.2.34}/README.md +0 -0
- {cycls-0.0.2.32 → cycls-0.0.2.34}/cycls/theme/assets/index-D0-uI8sw.js +0 -0
- {cycls-0.0.2.32 → cycls-0.0.2.34}/cycls/theme/index.html +0 -0
|
@@ -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."""
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import json, time, modal, inspect, uvicorn
|
|
2
|
+
from .runtime import Runtime
|
|
3
|
+
from modal.runner import run_app
|
|
4
|
+
from .web import web
|
|
5
|
+
import importlib.resources
|
|
6
|
+
|
|
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
|
|
@@ -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
|
cycls-0.0.2.32/cycls/__init__.py
DELETED
cycls-0.0.2.32/cycls/sdk.py
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
from .runtime import Runtime
|
|
2
|
-
|
|
3
|
-
def function(python_version="3.12", pip_install=None, apt_install=None, run_commands=None, copy=None, name=None, base_url=None, api_key=None):
|
|
4
|
-
# """
|
|
5
|
-
# A decorator factory that transforms a Python function into a containerized,
|
|
6
|
-
# remotely executable object.
|
|
7
|
-
|
|
8
|
-
# Args:
|
|
9
|
-
# pip (list[str], optional): A list of pip packages to install.
|
|
10
|
-
# apt (list[str], optional): A list of apt packages to install.
|
|
11
|
-
# copy (list[str], optional): A list of local paths to copy to the
|
|
12
|
-
# same path inside the image. For static dependencies.
|
|
13
|
-
# name (str, optional): A name for this function. Defaults to the function's name.
|
|
14
|
-
|
|
15
|
-
# Returns:
|
|
16
|
-
# A decorator that replaces the decorated function with a Runtime instance.
|
|
17
|
-
# """
|
|
18
|
-
def decorator(func):
|
|
19
|
-
Name = name or func.__name__ # should be moved to runtime... or default?
|
|
20
|
-
copy_dict = {i:i for i in copy or []}
|
|
21
|
-
return Runtime(func, Name.replace('_', '-'), python_version, pip_install, apt_install, run_commands, copy_dict, base_url, api_key)
|
|
22
|
-
return decorator
|
|
File without changes
|
|
File without changes
|
|
File without changes
|