cycls 0.0.2.21__tar.gz → 0.0.2.23__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.21 → cycls-0.0.2.23}/PKG-INFO +7 -5
- {cycls-0.0.2.21 → cycls-0.0.2.23}/README.md +6 -4
- {cycls-0.0.2.21 → cycls-0.0.2.23}/cycls/cycls.py +27 -39
- {cycls-0.0.2.21 → cycls-0.0.2.23}/pyproject.toml +1 -1
- {cycls-0.0.2.21 → cycls-0.0.2.23}/cycls/__init__.py +0 -0
- {cycls-0.0.2.21 → cycls-0.0.2.23}/cycls/tuns +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: cycls
|
|
3
|
-
Version: 0.0.2.
|
|
3
|
+
Version: 0.0.2.23
|
|
4
4
|
Summary: Cycls SDK
|
|
5
5
|
Author: Mohammed Jamal
|
|
6
6
|
Author-email: mj@cycls.com
|
|
@@ -49,13 +49,13 @@ from cycls import Cycls
|
|
|
49
49
|
|
|
50
50
|
cycls = Cycls()
|
|
51
51
|
|
|
52
|
-
@cycls("@spark")
|
|
52
|
+
@cycls("@spark") # pick a unique handle name
|
|
53
53
|
def app(x):
|
|
54
54
|
return x.content + "from spark"
|
|
55
55
|
|
|
56
56
|
cycls.push()
|
|
57
57
|
```
|
|
58
|
-
`cycls.push()` will then publish the app `@spark` on [cycls.com/@spark](https://cycls.com/@spark)
|
|
58
|
+
`cycls.push()` will then publish the app `@spark-dev` on [cycls.com/@spark-dev](https://cycls.com/@spark-dev) in development mode. Make sure to pick a unique app name; Cycls maintains a global namespace for handles.
|
|
59
59
|
## Async Apps
|
|
60
60
|
For performance, make the function asynchronous. The following is an async app with message `history` and session `id`
|
|
61
61
|
```py
|
|
@@ -87,6 +87,8 @@ cycls.push()
|
|
|
87
87
|
```
|
|
88
88
|
|
|
89
89
|
### Try it live
|
|
90
|
-
- [cycls.com/@groq](https://cycls.com/@groq)
|
|
91
|
-
- [cycls.com/@openai](https://cycls.com/@openai)
|
|
90
|
+
- [cycls.com/@groq](https://cycls.com/@groq) | [groq.py](https://github.com/Cycls/examples/blob/main/groq.py)
|
|
91
|
+
- [cycls.com/@openai](https://cycls.com/@openai) | [openai.py](https://github.com/Cycls/examples/blob/main/openai.py)
|
|
92
|
+
- [cycls.com/@anthropic](https://cycls.com/@anthropic) | [anthropic.py](https://github.com/Cycls/examples/blob/main/anthropic.py)
|
|
93
|
+
- [cycls.com/@gemini](https://cycls.com/@gemini) | [gemini.py](https://github.com/Cycls/examples/blob/main/gemini.py)
|
|
92
94
|
|
|
@@ -31,13 +31,13 @@ from cycls import Cycls
|
|
|
31
31
|
|
|
32
32
|
cycls = Cycls()
|
|
33
33
|
|
|
34
|
-
@cycls("@spark")
|
|
34
|
+
@cycls("@spark") # pick a unique handle name
|
|
35
35
|
def app(x):
|
|
36
36
|
return x.content + "from spark"
|
|
37
37
|
|
|
38
38
|
cycls.push()
|
|
39
39
|
```
|
|
40
|
-
`cycls.push()` will then publish the app `@spark` on [cycls.com/@spark](https://cycls.com/@spark)
|
|
40
|
+
`cycls.push()` will then publish the app `@spark-dev` on [cycls.com/@spark-dev](https://cycls.com/@spark-dev) in development mode. Make sure to pick a unique app name; Cycls maintains a global namespace for handles.
|
|
41
41
|
## Async Apps
|
|
42
42
|
For performance, make the function asynchronous. The following is an async app with message `history` and session `id`
|
|
43
43
|
```py
|
|
@@ -69,5 +69,7 @@ cycls.push()
|
|
|
69
69
|
```
|
|
70
70
|
|
|
71
71
|
### Try it live
|
|
72
|
-
- [cycls.com/@groq](https://cycls.com/@groq)
|
|
73
|
-
- [cycls.com/@openai](https://cycls.com/@openai)
|
|
72
|
+
- [cycls.com/@groq](https://cycls.com/@groq) | [groq.py](https://github.com/Cycls/examples/blob/main/groq.py)
|
|
73
|
+
- [cycls.com/@openai](https://cycls.com/@openai) | [openai.py](https://github.com/Cycls/examples/blob/main/openai.py)
|
|
74
|
+
- [cycls.com/@anthropic](https://cycls.com/@anthropic) | [anthropic.py](https://github.com/Cycls/examples/blob/main/anthropic.py)
|
|
75
|
+
- [cycls.com/@gemini](https://cycls.com/@gemini) | [gemini.py](https://github.com/Cycls/examples/blob/main/gemini.py)
|
|
@@ -10,7 +10,7 @@ import inspect
|
|
|
10
10
|
import logging
|
|
11
11
|
logging.basicConfig(level=logging.ERROR)
|
|
12
12
|
|
|
13
|
-
O = lambda x,y: print(f"✦/✧ {str(x).ljust(
|
|
13
|
+
O = lambda x,y: print(f"✦/✧ {str(x).ljust(12)} | {y}")
|
|
14
14
|
|
|
15
15
|
import os
|
|
16
16
|
current_dir = os.path.dirname(os.path.abspath(__file__))
|
|
@@ -42,30 +42,32 @@ async def create_ssh_tunnel(x,y,z='tuns.sh'):
|
|
|
42
42
|
except (OSError, asyncssh.Error) as e:
|
|
43
43
|
O("tunnel",f"disconnected ({e})")
|
|
44
44
|
|
|
45
|
-
def register(handles, net,
|
|
45
|
+
def register(handles, net, api_key):
|
|
46
46
|
try:
|
|
47
47
|
with httpx.Client() as client:
|
|
48
|
-
response = client.post(f"{net}/register", json={"handles":handles, "
|
|
48
|
+
response = client.post(f"{net}/register", json={"handles":handles, "net":net, "api_key":api_key})
|
|
49
|
+
data = response.json()
|
|
49
50
|
if response.status_code==200:
|
|
50
|
-
data = (response.json()).get("content")
|
|
51
51
|
for i in data:
|
|
52
|
-
O(i[
|
|
52
|
+
O(f"{i['status']}/{i['mode']}", f"{net}/{i['handle']}" if i["status"] != "taken" else "")
|
|
53
|
+
return True
|
|
53
54
|
else:
|
|
54
|
-
|
|
55
|
+
O("failed", data.get("error"))
|
|
56
|
+
return False
|
|
55
57
|
except Exception as e:
|
|
56
|
-
|
|
58
|
+
O("error",e)
|
|
59
|
+
return False
|
|
57
60
|
|
|
58
61
|
class Cycls:
|
|
59
|
-
def __init__(self, url="", net="https://cycls.com", port=find_available_port(8001),
|
|
62
|
+
def __init__(self, url="", net="https://cycls.com", port=find_available_port(8001), api_key=None):
|
|
60
63
|
import uuid
|
|
61
|
-
self.subdomain = str(uuid.uuid4())[:8]
|
|
64
|
+
self.subdomain = str(uuid.uuid4())[:8] #!
|
|
62
65
|
self.server = FastAPI()
|
|
63
66
|
self.net = net
|
|
64
67
|
self.port = port
|
|
65
68
|
self.url = url
|
|
66
69
|
self.apps = {}
|
|
67
|
-
self.
|
|
68
|
-
self.email = email
|
|
70
|
+
self.api_key = api_key
|
|
69
71
|
|
|
70
72
|
def __call__(self, handle):
|
|
71
73
|
def decorator(func):
|
|
@@ -76,20 +78,13 @@ class Cycls:
|
|
|
76
78
|
def sync_wrapper(*args, **kwargs):
|
|
77
79
|
return StreamingResponse(func(*args, **kwargs))
|
|
78
80
|
wrapper = async_wrapper if inspect.iscoroutinefunction(func) else sync_wrapper
|
|
79
|
-
|
|
80
|
-
if self.url != "": self.prod=True #!
|
|
81
|
-
if not self.prod:
|
|
82
|
-
self.apps[handle + "-dev"] = wrapper
|
|
83
|
-
else:
|
|
84
|
-
self.apps[handle] = wrapper
|
|
85
|
-
|
|
81
|
+
self.apps[handle] = wrapper
|
|
86
82
|
return wrapper
|
|
87
83
|
return decorator
|
|
88
84
|
|
|
89
85
|
async def gateway(self, request: Request):
|
|
90
86
|
data = await request.json()
|
|
91
87
|
handle = data.get('handle')
|
|
92
|
-
print(handle,self.apps)
|
|
93
88
|
if handle in self.apps:
|
|
94
89
|
func = self.apps[handle]
|
|
95
90
|
message = Message(**data)
|
|
@@ -97,29 +92,22 @@ class Cycls:
|
|
|
97
92
|
return {"error": "Handle not found"}
|
|
98
93
|
|
|
99
94
|
def push(self):
|
|
100
|
-
if self.email:
|
|
101
|
-
O("email",self.email)
|
|
102
95
|
O("port",self.port)
|
|
103
|
-
if self.
|
|
104
|
-
|
|
105
|
-
|
|
96
|
+
if self.url=="":
|
|
97
|
+
self.url = f"https://{self.subdomain}-cycls.tuns.sh"
|
|
98
|
+
mode = "dev"
|
|
106
99
|
else:
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
@self.server.on_event("startup")
|
|
114
|
-
def startup_event():
|
|
115
|
-
if self.prod:
|
|
116
|
-
pass
|
|
117
|
-
else:
|
|
100
|
+
mode = "prod"
|
|
101
|
+
config = [{"handle": handle, "url": self.url+"/gateway", "mode":mode} for handle in list(self.apps.keys())]
|
|
102
|
+
if on := register(config, self.net, self.api_key):
|
|
103
|
+
self.server.post("/gateway")(self.gateway)
|
|
104
|
+
@self.server.on_event("startup")
|
|
105
|
+
def startup_event():
|
|
118
106
|
asyncio.create_task(create_ssh_tunnel(f"{self.subdomain}-cycls", self.port))
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
107
|
+
try:
|
|
108
|
+
uvicorn.run(self.server, host="127.0.0.1", port=self.port, log_level="error")
|
|
109
|
+
except KeyboardInterrupt:
|
|
110
|
+
print(" ");O("exit","done")
|
|
123
111
|
|
|
124
112
|
async def call(self, handle, content):
|
|
125
113
|
data = {"handle":handle, "content":content, "session":{}, "agent":"yes"}
|
|
File without changes
|
|
File without changes
|