cycls 0.0.2.68__py3-none-any.whl → 0.0.2.70__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
|
@@ -9,6 +9,9 @@ from pathlib import Path
|
|
|
9
9
|
from contextlib import contextmanager
|
|
10
10
|
import tarfile
|
|
11
11
|
|
|
12
|
+
# Enable BuildKit for faster builds with better caching
|
|
13
|
+
os.environ["DOCKER_BUILDKIT"] = "1"
|
|
14
|
+
|
|
12
15
|
# --- Top-Level Helper Functions ---
|
|
13
16
|
|
|
14
17
|
def _bootstrap_script(payload_file: str, result_file: str) -> str:
|
|
@@ -325,6 +328,73 @@ COPY {self.payload_file} {self.io_dir}/
|
|
|
325
328
|
print(f"\n🛑 Operation stopped: {e}")
|
|
326
329
|
return None
|
|
327
330
|
|
|
331
|
+
def watch(self, *args, **kwargs):
|
|
332
|
+
"""Runs the container with file watching - restarts script on changes."""
|
|
333
|
+
try:
|
|
334
|
+
from watchfiles import watch as watchfiles_watch
|
|
335
|
+
except ImportError:
|
|
336
|
+
print("❌ watchfiles not installed. Run: pip install watchfiles")
|
|
337
|
+
return
|
|
338
|
+
|
|
339
|
+
import inspect
|
|
340
|
+
import subprocess
|
|
341
|
+
|
|
342
|
+
# Get the main script (the outermost .py file in the stack)
|
|
343
|
+
main_script = None
|
|
344
|
+
for frame_info in inspect.stack():
|
|
345
|
+
filename = frame_info.filename
|
|
346
|
+
if filename.endswith('.py') and not filename.startswith('<'):
|
|
347
|
+
main_script = Path(filename).resolve()
|
|
348
|
+
# main_script is now the outermost/first script in the call chain
|
|
349
|
+
|
|
350
|
+
# Build watch paths: main script + copy sources
|
|
351
|
+
watch_paths = []
|
|
352
|
+
if main_script and main_script.exists():
|
|
353
|
+
watch_paths.append(main_script)
|
|
354
|
+
watch_paths.extend([Path(src).resolve() for src in self.copy.keys() if Path(src).exists()])
|
|
355
|
+
|
|
356
|
+
if not watch_paths:
|
|
357
|
+
print("⚠️ No files to watch. Running without watch mode.")
|
|
358
|
+
return self.run(*args, **kwargs)
|
|
359
|
+
|
|
360
|
+
print(f"👀 Watching for changes:")
|
|
361
|
+
for p in watch_paths:
|
|
362
|
+
print(f" {p}")
|
|
363
|
+
print()
|
|
364
|
+
|
|
365
|
+
while True:
|
|
366
|
+
# Run the script in a subprocess so we survive errors
|
|
367
|
+
print(f"🚀 Running {main_script.name}...")
|
|
368
|
+
proc = subprocess.Popen(
|
|
369
|
+
[sys.executable, str(main_script)],
|
|
370
|
+
env={**os.environ, '_CYCLS_WATCH_CHILD': '1'}
|
|
371
|
+
)
|
|
372
|
+
|
|
373
|
+
try:
|
|
374
|
+
# Watch for changes
|
|
375
|
+
for changes in watchfiles_watch(*watch_paths):
|
|
376
|
+
changed_files = [str(c[1]) for c in changes]
|
|
377
|
+
print(f"\n🔄 Changes detected:")
|
|
378
|
+
for f in changed_files:
|
|
379
|
+
print(f" {f}")
|
|
380
|
+
break
|
|
381
|
+
|
|
382
|
+
print("\n🔄 Restarting...\n")
|
|
383
|
+
proc.terminate()
|
|
384
|
+
try:
|
|
385
|
+
proc.wait(timeout=3)
|
|
386
|
+
except subprocess.TimeoutExpired:
|
|
387
|
+
proc.kill()
|
|
388
|
+
|
|
389
|
+
except KeyboardInterrupt:
|
|
390
|
+
print("\n🛑 Stopping...")
|
|
391
|
+
proc.terminate()
|
|
392
|
+
try:
|
|
393
|
+
proc.wait(timeout=3)
|
|
394
|
+
except subprocess.TimeoutExpired:
|
|
395
|
+
proc.kill()
|
|
396
|
+
return
|
|
397
|
+
|
|
328
398
|
def build(self, *args, **kwargs):
|
|
329
399
|
"""Builds a self-contained, deployable Docker image locally."""
|
|
330
400
|
print("📦 Building self-contained image for deployment...")
|
cycls/sdk.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import time, inspect, uvicorn
|
|
1
|
+
import os, time, inspect, 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
|
|
@@ -74,7 +74,8 @@ class Agent:
|
|
|
74
74
|
return f
|
|
75
75
|
return decorator
|
|
76
76
|
|
|
77
|
-
def
|
|
77
|
+
def _local(self, port=8080, watch=True):
|
|
78
|
+
"""Run directly with uvicorn (no Docker)."""
|
|
78
79
|
if not self.registered_functions:
|
|
79
80
|
print("Error: No @agent decorated function found.")
|
|
80
81
|
return
|
|
@@ -85,16 +86,14 @@ class Agent:
|
|
|
85
86
|
print(f"🚀 Starting local server at localhost:{port}")
|
|
86
87
|
agent.config.public_path = self.theme
|
|
87
88
|
set_prod(agent.config, False)
|
|
88
|
-
uvicorn.run(web(agent.func, agent.config), host="0.0.0.0", port=port)
|
|
89
|
+
uvicorn.run(web(agent.func, agent.config), host="0.0.0.0", port=port, reload=watch)
|
|
89
90
|
return
|
|
90
91
|
|
|
91
|
-
def
|
|
92
|
+
def _runtime(self, prod=False):
|
|
93
|
+
"""Create a Runtime instance for the first registered agent."""
|
|
92
94
|
if not self.registered_functions:
|
|
93
95
|
print("Error: No @agent decorated function found.")
|
|
94
|
-
return
|
|
95
|
-
if (self.key is None) and prod:
|
|
96
|
-
print("🛑 Error: Please add your Cycls API key")
|
|
97
|
-
return
|
|
96
|
+
return None
|
|
98
97
|
|
|
99
98
|
agent = self.registered_functions[0]
|
|
100
99
|
if len(self.registered_functions) > 1:
|
|
@@ -109,7 +108,7 @@ class Agent:
|
|
|
109
108
|
files.update({f: f for f in self.copy})
|
|
110
109
|
files.update({f: f"public/{f}" for f in self.copy_public})
|
|
111
110
|
|
|
112
|
-
|
|
111
|
+
return Runtime(
|
|
113
112
|
func=lambda port: __import__("web").serve(func, config_dict, name, port),
|
|
114
113
|
name=name,
|
|
115
114
|
apt_packages=self.apt,
|
|
@@ -118,8 +117,24 @@ class Agent:
|
|
|
118
117
|
base_url=self.base_url,
|
|
119
118
|
api_key=self.key
|
|
120
119
|
)
|
|
121
|
-
|
|
122
|
-
|
|
120
|
+
|
|
121
|
+
def local(self, port=8080, watch=True):
|
|
122
|
+
"""Run locally in Docker with file watching by default."""
|
|
123
|
+
# Child process spawned by watcher - run without watch
|
|
124
|
+
if os.environ.get('_CYCLS_WATCH_CHILD'):
|
|
125
|
+
watch = False
|
|
126
|
+
runtime = self._runtime(prod=False)
|
|
127
|
+
if runtime:
|
|
128
|
+
runtime.watch(port=port) if watch else runtime.run(port=port)
|
|
129
|
+
|
|
130
|
+
def deploy(self, port=8080):
|
|
131
|
+
"""Deploy to production."""
|
|
132
|
+
if self.key is None:
|
|
133
|
+
print("🛑 Error: Please add your Cycls API key")
|
|
134
|
+
return
|
|
135
|
+
runtime = self._runtime(prod=True)
|
|
136
|
+
if runtime:
|
|
137
|
+
runtime.deploy(port=port)
|
|
123
138
|
|
|
124
139
|
def modal(self, prod=False):
|
|
125
140
|
import modal
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cycls
|
|
3
|
-
Version: 0.0.2.
|
|
3
|
+
Version: 0.0.2.70
|
|
4
4
|
Summary: Distribute Intelligence
|
|
5
5
|
Author: Mohammed J. AlRujayi
|
|
6
6
|
Author-email: mj@cycls.com
|
|
@@ -70,7 +70,7 @@ async def chat(context):
|
|
|
70
70
|
if chunk.choices[0].delta.content:
|
|
71
71
|
yield chunk.choices[0].delta.content
|
|
72
72
|
|
|
73
|
-
agent.deploy(
|
|
73
|
+
agent.deploy() # Live at https://my-agent.cycls.ai
|
|
74
74
|
```
|
|
75
75
|
|
|
76
76
|
## Installation
|
|
@@ -90,11 +90,12 @@ Requires Docker.
|
|
|
90
90
|
- **Monetization** - `tier="cycls_pass"` integrates with [Cycls Pass](https://cycls.ai) subscriptions
|
|
91
91
|
- **Native UI Components** - Render thinking bubbles, tables, code blocks in responses
|
|
92
92
|
|
|
93
|
-
##
|
|
93
|
+
## Running
|
|
94
94
|
|
|
95
95
|
```python
|
|
96
|
-
agent.
|
|
97
|
-
agent.
|
|
96
|
+
agent.local() # Development with hot-reload (localhost:8080)
|
|
97
|
+
agent.local(watch=False) # Development without hot-reload
|
|
98
|
+
agent.deploy() # Production: https://agent-name.cycls.ai
|
|
98
99
|
```
|
|
99
100
|
|
|
100
101
|
Get an API key at [cycls.com](https://cycls.com).
|
|
@@ -4,9 +4,9 @@ cycls/default-theme/assets/index-B0ZKcm_V.css,sha256=wK9-NhEB8xPcN9Zv69zpOcfGTlF
|
|
|
4
4
|
cycls/default-theme/assets/index-D5EDcI4J.js,sha256=sN4qRcAXa7DBd9JzmVcCoCwH4l8cNCM-U9QGUjBvWSo,1346506
|
|
5
5
|
cycls/default-theme/index.html,sha256=bM-yW_g0cGrV40Q5yY3ccY0fM4zI1Wuu5I8EtGFJIxs,828
|
|
6
6
|
cycls/dev-theme/index.html,sha256=QJBHkdNuMMiwQU7o8dN8__8YQeQB45D37D-NCXIWB2Q,11585
|
|
7
|
-
cycls/runtime.py,sha256=
|
|
8
|
-
cycls/sdk.py,sha256=
|
|
7
|
+
cycls/runtime.py,sha256=LgOrQ6Arh-GWSJqckfra-CvjeSekTvGvjHOBxu7JTQQ,21408
|
|
8
|
+
cycls/sdk.py,sha256=6oRKP44TJN9HKdNw9OYzDlZFDUMUhoCMt8TEwyu26dI,7368
|
|
9
9
|
cycls/web.py,sha256=3M3qaWTNY3dpgd7Vq5aXREp-cIFsHrDqBQ1YkGrOaUk,4659
|
|
10
|
-
cycls-0.0.2.
|
|
11
|
-
cycls-0.0.2.
|
|
12
|
-
cycls-0.0.2.
|
|
10
|
+
cycls-0.0.2.70.dist-info/METADATA,sha256=CNYz8lGGGn3lIutuVV4oIy_6e0q4eg8ghkNIErNt5p4,8008
|
|
11
|
+
cycls-0.0.2.70.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
|
|
12
|
+
cycls-0.0.2.70.dist-info/RECORD,,
|
|
File without changes
|