cycls 0.0.2.48__py3-none-any.whl → 0.0.2.50__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/runtime.py
CHANGED
|
@@ -386,7 +386,7 @@ COPY {self.payload_file} {self.io_dir}/
|
|
|
386
386
|
data=data_payload,
|
|
387
387
|
files=files,
|
|
388
388
|
headers=headers,
|
|
389
|
-
timeout=1800 # Set a long timeout for the entire process
|
|
389
|
+
timeout=5*1800 # Set a long timeout for the entire process
|
|
390
390
|
)
|
|
391
391
|
|
|
392
392
|
# 4. Handle the server's response
|
cycls/sdk.py
CHANGED
|
@@ -4,25 +4,24 @@ from modal.runner import run_app
|
|
|
4
4
|
from .web import web
|
|
5
5
|
import importlib.resources
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
cycls_path = importlib.resources.files('cycls')
|
|
7
|
+
CYCLS_PATH = importlib.resources.files('cycls')
|
|
9
8
|
|
|
10
|
-
def function(python_version=None, pip=None, apt=None, run_commands=None, copy=None, name=None, base_url=None,
|
|
9
|
+
def function(python_version=None, pip=None, apt=None, run_commands=None, copy=None, name=None, base_url=None, key=None):
|
|
11
10
|
# """
|
|
12
11
|
# A decorator factory that transforms a Python function into a containerized,
|
|
13
12
|
# remotely executable object.
|
|
14
13
|
def decorator(func):
|
|
15
14
|
Name = name or func.__name__
|
|
16
15
|
copy_dict = {i:i for i in copy or []}
|
|
17
|
-
return Runtime(func, Name.replace('_', '-'), python_version, pip, apt, run_commands, copy_dict, base_url,
|
|
16
|
+
return Runtime(func, Name.replace('_', '-'), python_version, pip, apt, run_commands, copy_dict, base_url, key)
|
|
18
17
|
return decorator
|
|
19
18
|
|
|
20
19
|
class Agent:
|
|
21
|
-
def __init__(self, theme=
|
|
20
|
+
def __init__(self, theme=CYCLS_PATH.joinpath('theme'), org=None, api_token=None, pip=[], apt=[], copy=[], copy_public=[], modal_keys=["",""], key=None, base_url=None):
|
|
22
21
|
self.org, self.api_token = org, api_token
|
|
23
22
|
self.theme = theme
|
|
24
|
-
self.
|
|
25
|
-
self.
|
|
23
|
+
self.key, self.modal_keys, self.pip, self.apt, self.copy, self.copy_public = key, modal_keys, pip, apt, copy, copy_public
|
|
24
|
+
self.base_url = base_url
|
|
26
25
|
|
|
27
26
|
self.registered_functions = []
|
|
28
27
|
|
|
@@ -55,7 +54,7 @@ class Agent:
|
|
|
55
54
|
if not self.registered_functions:
|
|
56
55
|
print("Error: No @agent decorated function found.")
|
|
57
56
|
return
|
|
58
|
-
if (self.
|
|
57
|
+
if (self.key is None) and prod:
|
|
59
58
|
print("🛑 Error: Please add your Cycls API key")
|
|
60
59
|
return
|
|
61
60
|
|
|
@@ -65,36 +64,37 @@ class Agent:
|
|
|
65
64
|
|
|
66
65
|
i["config"][1] = False
|
|
67
66
|
|
|
68
|
-
copy={str(self.theme):"theme", str(
|
|
67
|
+
copy={str(self.theme):"theme", str(CYCLS_PATH)+"/web.py":"web.py"}
|
|
69
68
|
copy.update({i:i for i in self.copy})
|
|
70
69
|
copy.update({i:f"public/{i}" for i in self.copy_public})
|
|
71
70
|
|
|
72
|
-
def
|
|
71
|
+
def server(port):
|
|
73
72
|
import uvicorn, logging
|
|
74
73
|
# This one-liner hides the confusing "0.0.0.0" message
|
|
75
74
|
logging.getLogger("uvicorn.error").addFilter(type("F",(),{"filter": lambda s,r: "0.0.0.0" not in r.getMessage()})())
|
|
76
|
-
print(f"\n
|
|
75
|
+
print(f"\n🔨 Visit {i["name"]} => http://localhost:{port}\n")
|
|
77
76
|
uvicorn.run(__import__("web").web(i["func"], *i["config"]), host="0.0.0.0", port=port)
|
|
78
77
|
|
|
79
78
|
new = Runtime(
|
|
80
79
|
# func=lambda port: __import__("uvicorn").run(__import__("web").web(i["func"], *i["config"]), host="0.0.0.0", port=port),
|
|
81
|
-
func=
|
|
80
|
+
func=server,
|
|
82
81
|
name=i["name"],
|
|
83
82
|
apt_packages=self.apt,
|
|
84
83
|
pip_packages=["fastapi[standard]", "pyjwt", "cryptography", "uvicorn", *self.pip],
|
|
85
84
|
copy=copy,
|
|
86
|
-
|
|
85
|
+
base_url=self.base_url,
|
|
86
|
+
api_key=self.key
|
|
87
87
|
)
|
|
88
88
|
new.deploy(port=port) if prod else new.run(port=port)
|
|
89
89
|
return
|
|
90
90
|
|
|
91
91
|
def modal(self, prod=False):
|
|
92
|
-
self.client = modal.Client.from_credentials(*self.
|
|
92
|
+
self.client = modal.Client.from_credentials(*self.modal_keys)
|
|
93
93
|
image = (modal.Image.debian_slim()
|
|
94
94
|
.pip_install("fastapi[standard]", "pyjwt", "cryptography", *self.pip)
|
|
95
95
|
.apt_install(*self.apt)
|
|
96
96
|
.add_local_dir(self.theme, "/root/theme")
|
|
97
|
-
.add_local_file(str(
|
|
97
|
+
.add_local_file(str(CYCLS_PATH)+"/web.py", "/root/web.py"))
|
|
98
98
|
|
|
99
99
|
for item in self.copy:
|
|
100
100
|
image = image.add_local_file(item, f"/root/{item}") if "." in item else image.add_local_dir(item, f'/root/{item}')
|
|
@@ -128,5 +128,5 @@ class Agent:
|
|
|
128
128
|
|
|
129
129
|
# docker system prune -af
|
|
130
130
|
# poetry config pypi-token.pypi <your-token>
|
|
131
|
-
# poetry run python
|
|
131
|
+
# poetry run python cake.py
|
|
132
132
|
# poetry publish --build
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cycls
|
|
3
|
-
Version: 0.0.2.
|
|
3
|
+
Version: 0.0.2.50
|
|
4
4
|
Summary: Cycls SDK
|
|
5
5
|
Author: Mohammed J. AlRujayi
|
|
6
6
|
Author-email: mj@cycls.com
|
|
@@ -40,15 +40,7 @@ The Distribution SDK for AI Agents.
|
|
|
40
40
|
|
|
41
41
|
# Cycls 🚲
|
|
42
42
|
|
|
43
|
-
`cycls` is
|
|
44
|
-
|
|
45
|
-
### Design Philosophy
|
|
46
|
-
`cycls` is an anti-framework. We treat the boilerplate, config files, and infrastructure that surround modern applications as a bug to be eliminated. A developer's focus is the most valuable resource, and context-switching is its greatest enemy.
|
|
47
|
-
|
|
48
|
-
Our zero-config approach makes your Python script the single source of truth for the entire application. When your code is all you need, you stay focused, iterate faster, and ship with confidence.
|
|
49
|
-
|
|
50
|
-
This philosophy has a powerful side-effect: it makes development genuinely iterative. The self-contained nature of an agent encourages you to 'build in cycles'—starting simple and adding complexity without penalty. This same simplicity also makes `cycls` an ideal target for code generation. Because the entire application can be expressed in one file, LLMs can write, modify, and reason about `cycls` agents far more effectively than with traditional frameworks. It's a seamless interface for both human and machine.
|
|
51
|
-
|
|
43
|
+
`cycls` is an open-source SDK for building and publishing AI agents. With a single decorator and one command, you can deploy your code as a web application complete with a front-end UI and an OpenAI-compatible API endpoint.
|
|
52
44
|
|
|
53
45
|
## Key Features
|
|
54
46
|
|
|
@@ -67,6 +59,8 @@ This philosophy has a powerful side-effect: it makes development genuinely itera
|
|
|
67
59
|
pip install cycls
|
|
68
60
|
```
|
|
69
61
|
|
|
62
|
+
**Note:** You must have [Docker](https://www.docker.com/get-started) installed and running on your machine.
|
|
63
|
+
|
|
70
64
|
## How to Use
|
|
71
65
|
### 1. Local Development: "Hello, World!"
|
|
72
66
|
|
|
@@ -81,9 +75,9 @@ agent = cycls.Agent()
|
|
|
81
75
|
# Decorate your function to register it as an agent
|
|
82
76
|
@agent()
|
|
83
77
|
async def hello(context):
|
|
84
|
-
yield "
|
|
78
|
+
yield "Hello, World!"
|
|
85
79
|
|
|
86
|
-
agent.
|
|
80
|
+
agent.deploy(prod=False)
|
|
87
81
|
```
|
|
88
82
|
|
|
89
83
|
Run it from your terminal:
|
|
@@ -97,20 +91,19 @@ This will start a local server. Open your browser to http://localhost:8080 to in
|
|
|
97
91
|
This example creates a more advanced agent that calls the OpenAI API. It will be deployed to the cloud with authentication enabled.
|
|
98
92
|
|
|
99
93
|
```py
|
|
100
|
-
# deploy.py
|
|
101
94
|
import cycls
|
|
102
95
|
|
|
103
96
|
# Initialize the agent with dependencies and API keys
|
|
104
97
|
agent = cycls.Agent(
|
|
105
98
|
pip=["openai"],
|
|
106
|
-
|
|
99
|
+
key="YOUR_CYCLS_KEY" # Get yours from https://cycls.com
|
|
107
100
|
)
|
|
108
101
|
|
|
109
102
|
# A helper function to call the LLM
|
|
110
103
|
async def llm(messages):
|
|
111
|
-
# Import inside the function: 'openai' is
|
|
104
|
+
# Import inside the function: 'openai' is needed at runtime in the container.
|
|
112
105
|
import openai
|
|
113
|
-
client = openai.AsyncOpenAI(api_key="
|
|
106
|
+
client = openai.AsyncOpenAI(api_key="YOUR_OPENAI_API_KEY")
|
|
114
107
|
model = "gpt-4o"
|
|
115
108
|
response = await client.chat.completions.create(
|
|
116
109
|
model=model,
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
cycls/__init__.py,sha256=bVT0dYTXLdSC3ZURgtm-DEOj-VO6RUM6zGsJB0zuj6Y,61
|
|
2
|
-
cycls/runtime.py,sha256=
|
|
3
|
-
cycls/sdk.py,sha256=
|
|
2
|
+
cycls/runtime.py,sha256=hLBtwtGz0FCW1-EPCJy6kMdF2fB3i6Df_H8-bm7qeK0,18223
|
|
3
|
+
cycls/sdk.py,sha256=jdgQDIQ1YP1y725bYYs6NRsA5UGor7Sso59BTcdBY1w,5869
|
|
4
4
|
cycls/theme/assets/index-CJ_4y8Hq.js,sha256=5D1ALXOnkyWH3KkmJ4DeYLzlN4JONL1xg3cCgUVyvLA,1103624
|
|
5
5
|
cycls/theme/index.html,sha256=GGoeZQYDvZpkqby9gxMK7XgCazsmJtJ5M4fDJE8IhwU,664
|
|
6
6
|
cycls/web.py,sha256=oc1a-Z3dFy4Ub7u0wF5_wrsvoosT3NgrOw4iWJ5KFns,4979
|
|
7
|
-
cycls-0.0.2.
|
|
8
|
-
cycls-0.0.2.
|
|
9
|
-
cycls-0.0.2.
|
|
7
|
+
cycls-0.0.2.50.dist-info/METADATA,sha256=IdbN2LxpZVv15FxZaOUl-N9otz_CQ7IDZ6zRtaxPwo0,4800
|
|
8
|
+
cycls-0.0.2.50.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
|
|
9
|
+
cycls-0.0.2.50.dist-info/RECORD,,
|
|
File without changes
|