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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cycls
3
- Version: 0.0.2.32
3
+ Version: 0.0.2.34
4
4
  Summary: Cycls SDK
5
5
  Author: Mohammed J. AlRujayi
6
6
  Author-email: mj@cycls.com
@@ -0,0 +1,2 @@
1
+ from .sdk import Agent
2
+ from .runtime import Runtime
@@ -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
- copy_lines = "\n".join([f"COPY {src} {dst}" for src, dst in self.copy.items()])
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
- _copy_path(Path(src), workdir / src)
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, time, modal, inspect, uvicorn
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
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "cycls"
3
- version = "0.0.2.32"
3
+ version = "0.0.2.34"
4
4
 
5
5
  packages = [{ include = "cycls" }]
6
6
  include = ["cycls/theme/**/*"]
@@ -1,2 +0,0 @@
1
- from .cycls import Agent
2
- from .sdk import function
@@ -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