cycls 0.0.2.72__py3-none-any.whl → 0.0.2.73__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 +15 -2
- cycls/sdk.py +112 -117
- cycls/web.py +1 -1
- {cycls-0.0.2.72.dist-info → cycls-0.0.2.73.dist-info}/METADATA +52 -48
- {cycls-0.0.2.72.dist-info → cycls-0.0.2.73.dist-info}/RECORD +7 -7
- {cycls-0.0.2.72.dist-info → cycls-0.0.2.73.dist-info}/WHEEL +0 -0
- {cycls-0.0.2.72.dist-info → cycls-0.0.2.73.dist-info}/entry_points.txt +0 -0
cycls/__init__.py
CHANGED
|
@@ -1,2 +1,15 @@
|
|
|
1
|
-
from .sdk import
|
|
2
|
-
from .runtime import Runtime
|
|
1
|
+
from .sdk import function, agent
|
|
2
|
+
from .runtime import Runtime
|
|
3
|
+
|
|
4
|
+
def __getattr__(name):
|
|
5
|
+
from . import sdk
|
|
6
|
+
if name in ("api_key", "base_url"):
|
|
7
|
+
return getattr(sdk, name)
|
|
8
|
+
raise AttributeError(f"module 'cycls' has no attribute '{name}'")
|
|
9
|
+
|
|
10
|
+
def __setattr__(name, value):
|
|
11
|
+
from . import sdk
|
|
12
|
+
if name in ("api_key", "base_url"):
|
|
13
|
+
setattr(sdk, name, value)
|
|
14
|
+
return
|
|
15
|
+
raise AttributeError(f"module 'cycls' has no attribute '{name}'")
|
cycls/sdk.py
CHANGED
|
@@ -1,30 +1,21 @@
|
|
|
1
|
-
import os, time,
|
|
1
|
+
import os, time, uvicorn
|
|
2
2
|
from .runtime import Runtime
|
|
3
3
|
from .web import web, Config
|
|
4
4
|
from .auth import PK_LIVE, PK_TEST, JWKS_PROD, JWKS_TEST
|
|
5
5
|
import importlib.resources
|
|
6
|
-
from pydantic import BaseModel
|
|
7
|
-
from typing import Callable
|
|
8
6
|
|
|
9
7
|
CYCLS_PATH = importlib.resources.files('cycls')
|
|
10
8
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
domain: str
|
|
15
|
-
config: Config
|
|
16
|
-
|
|
17
|
-
def set_prod(config: Config, prod: bool):
|
|
18
|
-
config.prod = prod
|
|
19
|
-
config.pk = PK_LIVE if prod else PK_TEST
|
|
20
|
-
config.jwks = JWKS_PROD if prod else JWKS_TEST
|
|
9
|
+
# Module-level configuration
|
|
10
|
+
api_key = None
|
|
11
|
+
base_url = None
|
|
21
12
|
|
|
22
13
|
themes = {
|
|
23
14
|
"default": CYCLS_PATH.joinpath('default-theme'),
|
|
24
15
|
"dev": CYCLS_PATH.joinpath('dev-theme'),
|
|
25
16
|
}
|
|
26
17
|
|
|
27
|
-
def
|
|
18
|
+
def _resolve_theme(theme):
|
|
28
19
|
"""Resolve theme - accepts string name or path"""
|
|
29
20
|
if isinstance(theme, str):
|
|
30
21
|
if theme in themes:
|
|
@@ -32,77 +23,54 @@ def resolve_theme(theme):
|
|
|
32
23
|
raise ValueError(f"Unknown theme: {theme}. Available: {list(themes.keys())}")
|
|
33
24
|
return theme
|
|
34
25
|
|
|
35
|
-
def
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
26
|
+
def _set_prod(config: Config, prod: bool):
|
|
27
|
+
config.prod = prod
|
|
28
|
+
config.pk = PK_LIVE if prod else PK_TEST
|
|
29
|
+
config.jwks = JWKS_PROD if prod else JWKS_TEST
|
|
30
|
+
|
|
31
|
+
class AgentRuntime:
|
|
32
|
+
"""Wraps an agent function with local/deploy/modal capabilities."""
|
|
33
|
+
|
|
34
|
+
def __init__(self, func, name, theme, pip, apt, copy, copy_public, modal_keys, auth, org, domain, header, intro, title, plan, analytics):
|
|
35
|
+
self.func = func
|
|
36
|
+
self.name = name
|
|
37
|
+
self.theme = _resolve_theme(theme)
|
|
38
|
+
self.pip = pip
|
|
39
|
+
self.apt = apt
|
|
40
|
+
self.copy = copy
|
|
41
|
+
self.copy_public = copy_public
|
|
42
|
+
self.modal_keys = modal_keys
|
|
43
|
+
self.domain = domain or f"{name}.cycls.ai"
|
|
44
|
+
|
|
45
|
+
self.config = Config(
|
|
46
|
+
header=header,
|
|
47
|
+
intro=intro,
|
|
48
|
+
title=title,
|
|
49
|
+
auth=auth,
|
|
50
|
+
plan=plan,
|
|
51
|
+
analytics=analytics,
|
|
52
|
+
org=org,
|
|
53
|
+
)
|
|
44
54
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
self.theme = resolve_theme(theme)
|
|
49
|
-
self.key, self.modal_keys, self.pip, self.apt, self.copy, self.copy_public = key, modal_keys, pip, apt, copy, copy_public
|
|
50
|
-
self.base_url = base_url
|
|
51
|
-
|
|
52
|
-
self.registered_functions = []
|
|
53
|
-
|
|
54
|
-
def __call__(self, name=None, header="", intro="", title="", domain=None, auth=False, tier="free", analytics=False):
|
|
55
|
-
if tier=="cycls_pass":
|
|
56
|
-
auth=True
|
|
57
|
-
analytics=True
|
|
58
|
-
def decorator(f):
|
|
59
|
-
agent_name = name or f.__name__.replace('_', '-')
|
|
60
|
-
self.registered_functions.append(RegisteredAgent(
|
|
61
|
-
func=f,
|
|
62
|
-
name=agent_name,
|
|
63
|
-
domain=domain or f"{agent_name}.cycls.ai",
|
|
64
|
-
config=Config(
|
|
65
|
-
header=header,
|
|
66
|
-
intro=intro,
|
|
67
|
-
title=title,
|
|
68
|
-
auth=auth,
|
|
69
|
-
tier=tier,
|
|
70
|
-
analytics=analytics,
|
|
71
|
-
org=self.org,
|
|
72
|
-
),
|
|
73
|
-
))
|
|
74
|
-
return f
|
|
75
|
-
return decorator
|
|
55
|
+
def __call__(self, *args, **kwargs):
|
|
56
|
+
"""Make the runtime callable - delegates to the wrapped function."""
|
|
57
|
+
return self.func(*args, **kwargs)
|
|
76
58
|
|
|
77
59
|
def _local(self, port=8080, watch=True):
|
|
78
60
|
"""Run directly with uvicorn (no Docker)."""
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
agent = self.registered_functions[0]
|
|
84
|
-
if len(self.registered_functions) > 1:
|
|
85
|
-
print(f"⚠️ Warning: Multiple agents found. Running '{agent.name}'.")
|
|
86
|
-
print(f"🚀 Starting local server at localhost:{port}")
|
|
87
|
-
agent.config.public_path = self.theme
|
|
88
|
-
set_prod(agent.config, False)
|
|
89
|
-
uvicorn.run(web(agent.func, agent.config), host="0.0.0.0", port=port, reload=watch)
|
|
90
|
-
return
|
|
61
|
+
print(f"Starting local server at localhost:{port}")
|
|
62
|
+
self.config.public_path = self.theme
|
|
63
|
+
_set_prod(self.config, False)
|
|
64
|
+
uvicorn.run(web(self.func, self.config), host="0.0.0.0", port=port, reload=watch)
|
|
91
65
|
|
|
92
66
|
def _runtime(self, prod=False):
|
|
93
|
-
"""Create a Runtime instance for
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
return None
|
|
97
|
-
|
|
98
|
-
agent = self.registered_functions[0]
|
|
99
|
-
if len(self.registered_functions) > 1:
|
|
100
|
-
print(f"⚠️ Warning: Multiple agents found. Running '{agent.name}'.")
|
|
67
|
+
"""Create a Runtime instance for deployment."""
|
|
68
|
+
_set_prod(self.config, prod)
|
|
69
|
+
config_dict = self.config.model_dump()
|
|
101
70
|
|
|
102
|
-
|
|
103
|
-
func =
|
|
104
|
-
name =
|
|
105
|
-
config_dict = agent.config.model_dump()
|
|
71
|
+
# Extract to local variables to avoid capturing self in lambda (cloudpickle issue)
|
|
72
|
+
func = self.func
|
|
73
|
+
name = self.name
|
|
106
74
|
|
|
107
75
|
files = {str(self.theme): "theme", str(CYCLS_PATH)+"/web.py": "web.py"}
|
|
108
76
|
files.update({f: f for f in self.copy})
|
|
@@ -114,73 +82,100 @@ class Agent:
|
|
|
114
82
|
apt_packages=self.apt,
|
|
115
83
|
pip_packages=["fastapi[standard]", "pyjwt", "cryptography", "uvicorn", *self.pip],
|
|
116
84
|
copy=files,
|
|
117
|
-
base_url=
|
|
118
|
-
api_key=
|
|
85
|
+
base_url=base_url,
|
|
86
|
+
api_key=api_key
|
|
119
87
|
)
|
|
120
88
|
|
|
121
89
|
def local(self, port=8080, watch=True):
|
|
122
90
|
"""Run locally in Docker with file watching by default."""
|
|
123
|
-
# Child process spawned by watcher - run without watch
|
|
124
91
|
if os.environ.get('_CYCLS_WATCH_CHILD'):
|
|
125
92
|
watch = False
|
|
126
93
|
runtime = self._runtime(prod=False)
|
|
127
|
-
if runtime
|
|
128
|
-
runtime.watch(port=port) if watch else runtime.run(port=port)
|
|
94
|
+
runtime.watch(port=port) if watch else runtime.run(port=port)
|
|
129
95
|
|
|
130
96
|
def deploy(self, port=8080):
|
|
131
97
|
"""Deploy to production."""
|
|
132
|
-
if
|
|
133
|
-
print("
|
|
98
|
+
if api_key is None:
|
|
99
|
+
print("Error: Please set cycls.api_key")
|
|
134
100
|
return
|
|
135
101
|
runtime = self._runtime(prod=True)
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
102
|
+
runtime.deploy(port=port)
|
|
103
|
+
|
|
139
104
|
def modal(self, prod=False):
|
|
140
105
|
import modal
|
|
141
106
|
from modal.runner import run_app
|
|
142
|
-
|
|
107
|
+
|
|
108
|
+
# Extract to local variables to avoid capturing self in lambda
|
|
109
|
+
func = self.func
|
|
110
|
+
name = self.name
|
|
111
|
+
domain = self.domain
|
|
112
|
+
|
|
113
|
+
client = modal.Client.from_credentials(*self.modal_keys)
|
|
143
114
|
image = (modal.Image.debian_slim()
|
|
144
115
|
.pip_install("fastapi[standard]", "pyjwt", "cryptography", *self.pip)
|
|
145
116
|
.apt_install(*self.apt)
|
|
146
117
|
.add_local_dir(self.theme, "/root/theme")
|
|
147
118
|
.add_local_file(str(CYCLS_PATH)+"/web.py", "/root/web.py"))
|
|
148
|
-
|
|
119
|
+
|
|
149
120
|
for item in self.copy:
|
|
150
121
|
image = image.add_local_file(item, f"/root/{item}") if "." in item else image.add_local_dir(item, f'/root/{item}')
|
|
151
|
-
|
|
122
|
+
|
|
152
123
|
for item in self.copy_public:
|
|
153
124
|
image = image.add_local_file(item, f"/root/public/{item}") if "." in item else image.add_local_dir(item, f'/root/public/{item}')
|
|
154
125
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
126
|
+
app = modal.App("development", image=image)
|
|
127
|
+
|
|
128
|
+
_set_prod(self.config, prod)
|
|
129
|
+
config_dict = self.config.model_dump()
|
|
130
|
+
|
|
131
|
+
app.function(serialized=True, name=name)(
|
|
132
|
+
modal.asgi_app(label=name, custom_domains=[domain])
|
|
133
|
+
(lambda: __import__("web").web(func, config_dict))
|
|
134
|
+
)
|
|
160
135
|
|
|
161
|
-
for agent in self.registered_functions:
|
|
162
|
-
set_prod(agent.config, prod)
|
|
163
|
-
func = agent.func
|
|
164
|
-
name = agent.name
|
|
165
|
-
domain = agent.domain
|
|
166
|
-
config_dict = agent.config.model_dump()
|
|
167
|
-
self.app.function(serialized=True, name=name)(
|
|
168
|
-
modal.asgi_app(label=name, custom_domains=[domain])
|
|
169
|
-
(lambda: __import__("web").web(func, config_dict))
|
|
170
|
-
)
|
|
171
136
|
if prod:
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
self.app.deploy(client=self.client, name=self.registered_functions[0].name)
|
|
175
|
-
return
|
|
137
|
+
print(f"Deployed to => https://{domain}")
|
|
138
|
+
app.deploy(client=client, name=name)
|
|
176
139
|
else:
|
|
177
140
|
with modal.enable_output():
|
|
178
|
-
run_app(app=
|
|
179
|
-
print("
|
|
180
|
-
with modal.enable_output(), run_app(app=
|
|
141
|
+
run_app(app=app, client=client)
|
|
142
|
+
print("Modal development server is running. Press Ctrl+C to stop.")
|
|
143
|
+
with modal.enable_output(), run_app(app=app, client=client):
|
|
181
144
|
while True: time.sleep(10)
|
|
182
145
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
146
|
+
|
|
147
|
+
def agent(name=None, pip=[], apt=[], copy=[], copy_public=[], theme="default", modal_keys=["", ""], auth=False, org=None, domain=None, header="", intro="", title="", plan="free", analytics=False):
|
|
148
|
+
"""Decorator that transforms a function into a deployable agent."""
|
|
149
|
+
if plan == "cycls_pass":
|
|
150
|
+
auth = True
|
|
151
|
+
analytics = True
|
|
152
|
+
|
|
153
|
+
def decorator(func):
|
|
154
|
+
agent_name = name or func.__name__.replace('_', '-')
|
|
155
|
+
return AgentRuntime(
|
|
156
|
+
func=func,
|
|
157
|
+
name=agent_name,
|
|
158
|
+
theme=theme,
|
|
159
|
+
pip=pip,
|
|
160
|
+
apt=apt,
|
|
161
|
+
copy=copy,
|
|
162
|
+
copy_public=copy_public,
|
|
163
|
+
modal_keys=modal_keys,
|
|
164
|
+
auth=auth,
|
|
165
|
+
org=org,
|
|
166
|
+
domain=domain,
|
|
167
|
+
header=header,
|
|
168
|
+
intro=intro,
|
|
169
|
+
title=title,
|
|
170
|
+
plan=plan,
|
|
171
|
+
analytics=analytics,
|
|
172
|
+
)
|
|
173
|
+
return decorator
|
|
174
|
+
|
|
175
|
+
def function(python_version=None, pip=None, apt=None, run_commands=None, copy=None, name=None):
|
|
176
|
+
"""Decorator that transforms a Python function into a containerized, remotely executable object."""
|
|
177
|
+
def decorator(func):
|
|
178
|
+
func_name = name or func.__name__
|
|
179
|
+
copy_dict = {i: i for i in copy or []}
|
|
180
|
+
return Runtime(func, func_name.replace('_', '-'), python_version, pip, apt, run_commands, copy_dict, base_url, api_key)
|
|
181
|
+
return decorator
|
cycls/web.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cycls
|
|
3
|
-
Version: 0.0.2.
|
|
3
|
+
Version: 0.0.2.73
|
|
4
4
|
Summary: Distribute Intelligence
|
|
5
5
|
Author: Mohammed J. AlRujayi
|
|
6
6
|
Author-email: mj@cycls.com
|
|
@@ -32,6 +32,7 @@ Distribute Intelligence
|
|
|
32
32
|
|
|
33
33
|
<h4 align="center">
|
|
34
34
|
<a href="https://pypi.python.org/pypi/cycls"><img src="https://img.shields.io/pypi/v/cycls.svg?label=cycls+pypi&color=blueviolet" alt="cycls Python package on PyPi" /></a>
|
|
35
|
+
<a href="https://github.com/Cycls/cycls/actions/workflows/tests.yml"><img src="https://github.com/Cycls/cycls/actions/workflows/tests.yml/badge.svg" alt="Tests" /></a>
|
|
35
36
|
<a href="https://blog.cycls.com"><img src="https://img.shields.io/badge/newsletter-blueviolet.svg?logo=substack&label=cycls" alt="Cycls newsletter" /></a>
|
|
36
37
|
<a href="https://x.com/cyclsai">
|
|
37
38
|
<img src="https://img.shields.io/twitter/follow/CyclsAI" alt="Cycls Twitter" />
|
|
@@ -53,24 +54,27 @@ Write a function. Deploy it as an API, a web interface, or both. Add authenticat
|
|
|
53
54
|
```python
|
|
54
55
|
import cycls
|
|
55
56
|
|
|
56
|
-
|
|
57
|
+
cycls.api_key = "YOUR_CYCLS_API_KEY"
|
|
57
58
|
|
|
58
|
-
@agent("
|
|
59
|
-
async def
|
|
59
|
+
@cycls.agent(pip=["openai"])
|
|
60
|
+
async def agent(context):
|
|
60
61
|
from openai import AsyncOpenAI
|
|
61
62
|
client = AsyncOpenAI()
|
|
62
63
|
|
|
63
|
-
|
|
64
|
-
model="
|
|
65
|
-
|
|
66
|
-
stream=True
|
|
64
|
+
stream = await client.responses.create(
|
|
65
|
+
model="o3-mini",
|
|
66
|
+
input=context.messages,
|
|
67
|
+
stream=True,
|
|
68
|
+
reasoning={"effort": "medium", "summary": "auto"},
|
|
67
69
|
)
|
|
68
70
|
|
|
69
|
-
async for
|
|
70
|
-
if
|
|
71
|
-
yield
|
|
71
|
+
async for event in stream:
|
|
72
|
+
if event.type == "response.reasoning_summary_text.delta":
|
|
73
|
+
yield {"type": "thinking", "thinking": event.delta} # Renders as thinking bubble
|
|
74
|
+
elif event.type == "response.output_text.delta":
|
|
75
|
+
yield event.delta
|
|
72
76
|
|
|
73
|
-
agent.deploy() # Live at https://
|
|
77
|
+
agent.deploy() # Live at https://agent.cycls.ai
|
|
74
78
|
```
|
|
75
79
|
|
|
76
80
|
## Installation
|
|
@@ -87,7 +91,7 @@ Requires Docker.
|
|
|
87
91
|
- **Web Interface** - Chat UI served automatically
|
|
88
92
|
- **Authentication** - `auth=True` enables JWT-based access control
|
|
89
93
|
- **Analytics** - `analytics=True` tracks usage
|
|
90
|
-
- **Monetization** - `
|
|
94
|
+
- **Monetization** - `plan="cycls_pass"` integrates with [Cycls Pass](https://cycls.ai) subscriptions
|
|
91
95
|
- **Native UI Components** - Render thinking bubbles, tables, code blocks in responses
|
|
92
96
|
|
|
93
97
|
## Running
|
|
@@ -95,17 +99,33 @@ Requires Docker.
|
|
|
95
99
|
```python
|
|
96
100
|
agent.local() # Development with hot-reload (localhost:8080)
|
|
97
101
|
agent.local(watch=False) # Development without hot-reload
|
|
98
|
-
agent.deploy() # Production: https://agent
|
|
102
|
+
agent.deploy() # Production: https://agent.cycls.ai
|
|
99
103
|
```
|
|
100
104
|
|
|
101
105
|
Get an API key at [cycls.com](https://cycls.com).
|
|
102
106
|
|
|
107
|
+
## Authentication & Analytics
|
|
108
|
+
|
|
109
|
+
```python
|
|
110
|
+
@cycls.agent(pip=["openai"], auth=True, analytics=True)
|
|
111
|
+
async def agent(context):
|
|
112
|
+
# context.user available when auth=True
|
|
113
|
+
user = context.user # User(id, email, name, plans)
|
|
114
|
+
yield f"Hello {user.name}!"
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
| Flag | Description |
|
|
118
|
+
|------|-------------|
|
|
119
|
+
| `auth=True` | Universal user pool via Cycls Pass (Clerk-based). You can also use your own Clerk auth. |
|
|
120
|
+
| `analytics=True` | Rich usage metrics available on the Cycls dashboard. |
|
|
121
|
+
| `plan="cycls_pass"` | Monetization via Cycls Pass subscriptions. Enables both auth and analytics. |
|
|
122
|
+
|
|
103
123
|
## Native UI Components
|
|
104
124
|
|
|
105
125
|
Yield structured objects for rich streaming responses:
|
|
106
126
|
|
|
107
127
|
```python
|
|
108
|
-
@agent()
|
|
128
|
+
@cycls.agent()
|
|
109
129
|
async def demo(context):
|
|
110
130
|
yield {"type": "thinking", "thinking": "Analyzing the request..."}
|
|
111
131
|
yield "Here's what I found:\n\n"
|
|
@@ -128,32 +148,26 @@ async def demo(context):
|
|
|
128
148
|
| `{"type": "callout", "callout": "...", "style": "..."}` | Yes |
|
|
129
149
|
| `{"type": "image", "src": "..."}` | Yes |
|
|
130
150
|
|
|
131
|
-
###
|
|
151
|
+
### Thinking Bubbles
|
|
132
152
|
|
|
133
|
-
|
|
134
|
-
@agent()
|
|
135
|
-
async def chat(context):
|
|
136
|
-
from openai import AsyncOpenAI
|
|
137
|
-
client = AsyncOpenAI()
|
|
153
|
+
The `{"type": "thinking", "thinking": "..."}` component renders as a collapsible thinking bubble in the UI. Each yield appends to the same bubble until a different component type is yielded:
|
|
138
154
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
)
|
|
155
|
+
```python
|
|
156
|
+
# Multiple yields build one thinking bubble
|
|
157
|
+
yield {"type": "thinking", "thinking": "Let me "}
|
|
158
|
+
yield {"type": "thinking", "thinking": "analyze this..."}
|
|
159
|
+
yield {"type": "thinking", "thinking": " Done thinking."}
|
|
145
160
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
yield {"type": "thinking", "thinking": event.delta}
|
|
149
|
-
elif event.type == "response.output_text.delta":
|
|
150
|
-
yield event.delta
|
|
161
|
+
# Then output the response
|
|
162
|
+
yield "Here's what I found..."
|
|
151
163
|
```
|
|
152
164
|
|
|
165
|
+
This works seamlessly with OpenAI's reasoning models - just map reasoning summaries to the thinking component.
|
|
166
|
+
|
|
153
167
|
## Context Object
|
|
154
168
|
|
|
155
169
|
```python
|
|
156
|
-
@agent()
|
|
170
|
+
@cycls.agent()
|
|
157
171
|
async def chat(context):
|
|
158
172
|
context.messages # [{"role": "user", "content": "..."}]
|
|
159
173
|
context.messages.raw # Full data including UI component parts
|
|
@@ -183,16 +197,17 @@ See [docs/streaming-protocol.md](docs/streaming-protocol.md) for frontend integr
|
|
|
183
197
|
|
|
184
198
|
## Declarative Infrastructure
|
|
185
199
|
|
|
186
|
-
Define your entire runtime in
|
|
200
|
+
Define your entire runtime in the decorator:
|
|
187
201
|
|
|
188
202
|
```python
|
|
189
|
-
|
|
203
|
+
@cycls.agent(
|
|
190
204
|
pip=["openai", "pandas", "numpy"],
|
|
191
205
|
apt=["ffmpeg", "libmagic1"],
|
|
192
|
-
run_commands=["curl -sSL https://example.com/setup.sh | bash"],
|
|
193
206
|
copy=["./utils.py", "./models/", "/absolute/path/to/config.json"],
|
|
194
207
|
copy_public=["./assets/logo.png", "./static/"],
|
|
195
208
|
)
|
|
209
|
+
async def my_agent(context):
|
|
210
|
+
...
|
|
196
211
|
```
|
|
197
212
|
|
|
198
213
|
### `pip` - Python Packages
|
|
@@ -211,17 +226,6 @@ Install system-level dependencies via apt-get. Need ffmpeg for audio processing?
|
|
|
211
226
|
apt=["ffmpeg", "imagemagick", "libpq-dev"]
|
|
212
227
|
```
|
|
213
228
|
|
|
214
|
-
### `run_commands` - Shell Commands
|
|
215
|
-
|
|
216
|
-
Run arbitrary shell commands during the container build. Useful for custom setup scripts, downloading assets, or any build-time configuration.
|
|
217
|
-
|
|
218
|
-
```python
|
|
219
|
-
run_commands=[
|
|
220
|
-
"curl -sSL https://example.com/setup.sh | bash",
|
|
221
|
-
"chmod +x /app/scripts/*.sh"
|
|
222
|
-
]
|
|
223
|
-
```
|
|
224
|
-
|
|
225
229
|
### `copy` - Bundle Files and Directories
|
|
226
230
|
|
|
227
231
|
Include local files and directories in your container. Works with both relative and absolute paths. Copies files and entire directory trees.
|
|
@@ -237,7 +241,7 @@ copy=[
|
|
|
237
241
|
Then import them in your function:
|
|
238
242
|
|
|
239
243
|
```python
|
|
240
|
-
@agent()
|
|
244
|
+
@cycls.agent(copy=["./utils.py"])
|
|
241
245
|
async def chat(context):
|
|
242
246
|
from utils import helper_function # Your bundled module
|
|
243
247
|
...
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
cycls/__init__.py,sha256=
|
|
1
|
+
cycls/__init__.py,sha256=HEpBZpHkC-4YwDUqcKAcnulyPxcvKnplhx91nB2a7IU,464
|
|
2
2
|
cycls/auth.py,sha256=xkndHZyCfnlertMMEKerCJjf23N3fVcTRVTTSXTTuzg,247
|
|
3
3
|
cycls/cli.py,sha256=AKf0z7ZLau3GvBVR_IhB7agmq4nVaHkcuUafNyvv2_A,7978
|
|
4
4
|
cycls/default-theme/assets/index-B0ZKcm_V.css,sha256=wK9-NhEB8xPcN9Zv69zpOcfGTlFbMwyC9WqTmSKUaKw,6546
|
|
@@ -6,9 +6,9 @@ cycls/default-theme/assets/index-D5EDcI4J.js,sha256=sN4qRcAXa7DBd9JzmVcCoCwH4l8c
|
|
|
6
6
|
cycls/default-theme/index.html,sha256=bM-yW_g0cGrV40Q5yY3ccY0fM4zI1Wuu5I8EtGFJIxs,828
|
|
7
7
|
cycls/dev-theme/index.html,sha256=QJBHkdNuMMiwQU7o8dN8__8YQeQB45D37D-NCXIWB2Q,11585
|
|
8
8
|
cycls/runtime.py,sha256=lg7XKHd9fLV_bYksHv2LHf3Lq7HPAC3K5Tr8pNgQ7sM,21641
|
|
9
|
-
cycls/sdk.py,sha256=
|
|
10
|
-
cycls/web.py,sha256=
|
|
11
|
-
cycls-0.0.2.
|
|
12
|
-
cycls-0.0.2.
|
|
13
|
-
cycls-0.0.2.
|
|
14
|
-
cycls-0.0.2.
|
|
9
|
+
cycls/sdk.py,sha256=X8-VAVqtksO0VGJIxlg02HLmeFpwtwMHWu9PNksS5kw,6620
|
|
10
|
+
cycls/web.py,sha256=_QNH8K55vTm90Z7tvcRKal5IybjkB1GY7Pf9p3qu3r8,4659
|
|
11
|
+
cycls-0.0.2.73.dist-info/METADATA,sha256=IP8h-NzyerOy701pjRYDbY_Nbvb1WsoU6JTKYE8xQ34,8695
|
|
12
|
+
cycls-0.0.2.73.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
|
|
13
|
+
cycls-0.0.2.73.dist-info/entry_points.txt,sha256=vEhqUxFhhuzCKWtq02LbMnT3wpUqdfgcM3Yh-jjXom8,40
|
|
14
|
+
cycls-0.0.2.73.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|