gui-now 1.0.0__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.
- gui_now-1.0.0/LICENSE +21 -0
- gui_now-1.0.0/PKG-INFO +144 -0
- gui_now-1.0.0/README.md +114 -0
- gui_now-1.0.0/pyproject.toml +31 -0
- gui_now-1.0.0/setup.cfg +4 -0
- gui_now-1.0.0/src/gui_now.egg-info/PKG-INFO +144 -0
- gui_now-1.0.0/src/gui_now.egg-info/SOURCES.txt +13 -0
- gui_now-1.0.0/src/gui_now.egg-info/dependency_links.txt +1 -0
- gui_now-1.0.0/src/gui_now.egg-info/requires.txt +17 -0
- gui_now-1.0.0/src/gui_now.egg-info/top_level.txt +1 -0
- gui_now-1.0.0/src/guinow/__init__.py +26 -0
- gui_now-1.0.0/src/guinow/client.py +261 -0
- gui_now-1.0.0/src/guinow/crewai.py +47 -0
- gui_now-1.0.0/src/guinow/langchain.py +47 -0
- gui_now-1.0.0/src/guinow/llamaindex.py +54 -0
gui_now-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Stratus Labs
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
gui_now-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: gui-now
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Python SDK + agent tools for gui.now — instant shareable HTML canvases
|
|
5
|
+
License: MIT
|
|
6
|
+
Project-URL: Homepage, https://gui.now
|
|
7
|
+
Project-URL: Repository, https://github.com/gui-now/python
|
|
8
|
+
Keywords: gui-now,html,canvas,sharing,langchain,llamaindex,crewai,agent
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Requires-Python: >=3.9
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
License-File: LICENSE
|
|
16
|
+
Provides-Extra: langchain
|
|
17
|
+
Requires-Dist: langchain-core>=0.1.0; extra == "langchain"
|
|
18
|
+
Requires-Dist: pydantic>=2.0; extra == "langchain"
|
|
19
|
+
Provides-Extra: llamaindex
|
|
20
|
+
Requires-Dist: llama-index-core>=0.10.0; extra == "llamaindex"
|
|
21
|
+
Provides-Extra: crewai
|
|
22
|
+
Requires-Dist: crewai>=0.1.0; extra == "crewai"
|
|
23
|
+
Requires-Dist: pydantic>=2.0; extra == "crewai"
|
|
24
|
+
Provides-Extra: all
|
|
25
|
+
Requires-Dist: langchain-core>=0.1.0; extra == "all"
|
|
26
|
+
Requires-Dist: llama-index-core>=0.10.0; extra == "all"
|
|
27
|
+
Requires-Dist: crewai>=0.1.0; extra == "all"
|
|
28
|
+
Requires-Dist: pydantic>=2.0; extra == "all"
|
|
29
|
+
Dynamic: license-file
|
|
30
|
+
|
|
31
|
+
# gui-now
|
|
32
|
+
|
|
33
|
+
Python SDK + agent tools for [GUI](https://gui.now) — instant shareable HTML canvases.
|
|
34
|
+
|
|
35
|
+
## Install
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pip install gui-now
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
With framework integrations:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
pip install gui-now[langchain]
|
|
45
|
+
pip install gui-now[llamaindex]
|
|
46
|
+
pip install gui-now[crewai]
|
|
47
|
+
pip install gui-now[all]
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Quick Start
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
from guinow import create_canvas
|
|
54
|
+
|
|
55
|
+
url = create_canvas(html="<h1>Hello World</h1>", title="My Canvas")
|
|
56
|
+
print(url) # https://gui.now/abc123
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Full Client
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
from guinow import GuiNowClient
|
|
63
|
+
|
|
64
|
+
client = GuiNowClient(api_key="your-pro-key") # or set GUI_NOW_API_KEY env var
|
|
65
|
+
result = client.create(
|
|
66
|
+
html="<h1>Dashboard</h1>",
|
|
67
|
+
title="Q1 Report",
|
|
68
|
+
expires="7d",
|
|
69
|
+
)
|
|
70
|
+
print(result.url)
|
|
71
|
+
print(result.expires_at)
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Every input format
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
client.create(html="<h1>Hi</h1>") # raw HTML
|
|
78
|
+
client.create(markdown="# Hi\n\nRendered server-side.") # Markdown
|
|
79
|
+
client.create_diagram("graph TD\n A-->B", title="Flow") # Mermaid
|
|
80
|
+
client.create(frames=[ # multi-tab canvas
|
|
81
|
+
{"html": "<h1>Overview</h1>", "label": "Overview"},
|
|
82
|
+
{"html": "<h1>Detail</h1>", "label": "Detail"},
|
|
83
|
+
])
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Update and extend
|
|
87
|
+
|
|
88
|
+
`create()` returns an `edit_token`. Keep it — it is the only way to change a
|
|
89
|
+
canvas afterwards. Free tier allows 3 edits per canvas; Pro is unlimited.
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
canvas = client.create(html="<h1>Draft</h1>")
|
|
93
|
+
|
|
94
|
+
client.update(canvas.id, canvas.edit_token, html="<h1>Final</h1>")
|
|
95
|
+
client.update(canvas.id, canvas.edit_token, title="Renamed")
|
|
96
|
+
client.update(canvas.id, canvas.edit_token, password=None) # remove protection
|
|
97
|
+
|
|
98
|
+
client.extend(canvas.id) # push expiry to 24h from now
|
|
99
|
+
canvas.open() # open the URL in a browser
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Omitting `password` leaves existing protection alone; passing `None` removes it.
|
|
103
|
+
|
|
104
|
+
## LangChain
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
from guinow.langchain import GuiNowTool
|
|
108
|
+
|
|
109
|
+
tool = GuiNowTool()
|
|
110
|
+
# Add to your agent's tools list
|
|
111
|
+
agent = initialize_agent(tools=[tool], ...)
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## LlamaIndex
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
from guinow.llamaindex import get_guinow_tool
|
|
118
|
+
|
|
119
|
+
tool = get_guinow_tool()
|
|
120
|
+
agent = ReActAgent.from_tools([tool], ...)
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## CrewAI
|
|
124
|
+
|
|
125
|
+
```python
|
|
126
|
+
from guinow.crewai import GuiNowTool
|
|
127
|
+
|
|
128
|
+
tool = GuiNowTool()
|
|
129
|
+
agent = Agent(tools=[tool], ...)
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Environment Variables
|
|
133
|
+
|
|
134
|
+
| Variable | Description |
|
|
135
|
+
|----------|-------------|
|
|
136
|
+
| `GUI_NOW_API_KEY` | Pro API key for higher rate limits and longer expiry |
|
|
137
|
+
| `GUI_NOW_URL` | Override the API base URL (defaults to `https://gui.now`) |
|
|
138
|
+
|
|
139
|
+
`GUI_NEW_API_KEY` and `GUI_NEW_URL` are the pre-rename names and are still read
|
|
140
|
+
as fallbacks, so keys exported before the move to gui.now keep working.
|
|
141
|
+
|
|
142
|
+
## License
|
|
143
|
+
|
|
144
|
+
MIT
|
gui_now-1.0.0/README.md
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# gui-now
|
|
2
|
+
|
|
3
|
+
Python SDK + agent tools for [GUI](https://gui.now) — instant shareable HTML canvases.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install gui-now
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
With framework integrations:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pip install gui-now[langchain]
|
|
15
|
+
pip install gui-now[llamaindex]
|
|
16
|
+
pip install gui-now[crewai]
|
|
17
|
+
pip install gui-now[all]
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Quick Start
|
|
21
|
+
|
|
22
|
+
```python
|
|
23
|
+
from guinow import create_canvas
|
|
24
|
+
|
|
25
|
+
url = create_canvas(html="<h1>Hello World</h1>", title="My Canvas")
|
|
26
|
+
print(url) # https://gui.now/abc123
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Full Client
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
from guinow import GuiNowClient
|
|
33
|
+
|
|
34
|
+
client = GuiNowClient(api_key="your-pro-key") # or set GUI_NOW_API_KEY env var
|
|
35
|
+
result = client.create(
|
|
36
|
+
html="<h1>Dashboard</h1>",
|
|
37
|
+
title="Q1 Report",
|
|
38
|
+
expires="7d",
|
|
39
|
+
)
|
|
40
|
+
print(result.url)
|
|
41
|
+
print(result.expires_at)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Every input format
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
client.create(html="<h1>Hi</h1>") # raw HTML
|
|
48
|
+
client.create(markdown="# Hi\n\nRendered server-side.") # Markdown
|
|
49
|
+
client.create_diagram("graph TD\n A-->B", title="Flow") # Mermaid
|
|
50
|
+
client.create(frames=[ # multi-tab canvas
|
|
51
|
+
{"html": "<h1>Overview</h1>", "label": "Overview"},
|
|
52
|
+
{"html": "<h1>Detail</h1>", "label": "Detail"},
|
|
53
|
+
])
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Update and extend
|
|
57
|
+
|
|
58
|
+
`create()` returns an `edit_token`. Keep it — it is the only way to change a
|
|
59
|
+
canvas afterwards. Free tier allows 3 edits per canvas; Pro is unlimited.
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
canvas = client.create(html="<h1>Draft</h1>")
|
|
63
|
+
|
|
64
|
+
client.update(canvas.id, canvas.edit_token, html="<h1>Final</h1>")
|
|
65
|
+
client.update(canvas.id, canvas.edit_token, title="Renamed")
|
|
66
|
+
client.update(canvas.id, canvas.edit_token, password=None) # remove protection
|
|
67
|
+
|
|
68
|
+
client.extend(canvas.id) # push expiry to 24h from now
|
|
69
|
+
canvas.open() # open the URL in a browser
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Omitting `password` leaves existing protection alone; passing `None` removes it.
|
|
73
|
+
|
|
74
|
+
## LangChain
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
from guinow.langchain import GuiNowTool
|
|
78
|
+
|
|
79
|
+
tool = GuiNowTool()
|
|
80
|
+
# Add to your agent's tools list
|
|
81
|
+
agent = initialize_agent(tools=[tool], ...)
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## LlamaIndex
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
from guinow.llamaindex import get_guinow_tool
|
|
88
|
+
|
|
89
|
+
tool = get_guinow_tool()
|
|
90
|
+
agent = ReActAgent.from_tools([tool], ...)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## CrewAI
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
from guinow.crewai import GuiNowTool
|
|
97
|
+
|
|
98
|
+
tool = GuiNowTool()
|
|
99
|
+
agent = Agent(tools=[tool], ...)
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Environment Variables
|
|
103
|
+
|
|
104
|
+
| Variable | Description |
|
|
105
|
+
|----------|-------------|
|
|
106
|
+
| `GUI_NOW_API_KEY` | Pro API key for higher rate limits and longer expiry |
|
|
107
|
+
| `GUI_NOW_URL` | Override the API base URL (defaults to `https://gui.now`) |
|
|
108
|
+
|
|
109
|
+
`GUI_NEW_API_KEY` and `GUI_NEW_URL` are the pre-rename names and are still read
|
|
110
|
+
as fallbacks, so keys exported before the move to gui.now keep working.
|
|
111
|
+
|
|
112
|
+
## License
|
|
113
|
+
|
|
114
|
+
MIT
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "gui-now"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "Python SDK + agent tools for gui.now — instant shareable HTML canvases"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = {text = "MIT"}
|
|
11
|
+
requires-python = ">=3.9"
|
|
12
|
+
keywords = ["gui-now", "html", "canvas", "sharing", "langchain", "llamaindex", "crewai", "agent"]
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Development Status :: 4 - Beta",
|
|
15
|
+
"Intended Audience :: Developers",
|
|
16
|
+
"License :: OSI Approved :: MIT License",
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
[project.optional-dependencies]
|
|
21
|
+
langchain = ["langchain-core>=0.1.0", "pydantic>=2.0"]
|
|
22
|
+
llamaindex = ["llama-index-core>=0.10.0"]
|
|
23
|
+
crewai = ["crewai>=0.1.0", "pydantic>=2.0"]
|
|
24
|
+
all = ["langchain-core>=0.1.0", "llama-index-core>=0.10.0", "crewai>=0.1.0", "pydantic>=2.0"]
|
|
25
|
+
|
|
26
|
+
[project.urls]
|
|
27
|
+
Homepage = "https://gui.now"
|
|
28
|
+
Repository = "https://github.com/gui-now/python"
|
|
29
|
+
|
|
30
|
+
[tool.setuptools.packages.find]
|
|
31
|
+
where = ["src"]
|
gui_now-1.0.0/setup.cfg
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: gui-now
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Python SDK + agent tools for gui.now — instant shareable HTML canvases
|
|
5
|
+
License: MIT
|
|
6
|
+
Project-URL: Homepage, https://gui.now
|
|
7
|
+
Project-URL: Repository, https://github.com/gui-now/python
|
|
8
|
+
Keywords: gui-now,html,canvas,sharing,langchain,llamaindex,crewai,agent
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Requires-Python: >=3.9
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
License-File: LICENSE
|
|
16
|
+
Provides-Extra: langchain
|
|
17
|
+
Requires-Dist: langchain-core>=0.1.0; extra == "langchain"
|
|
18
|
+
Requires-Dist: pydantic>=2.0; extra == "langchain"
|
|
19
|
+
Provides-Extra: llamaindex
|
|
20
|
+
Requires-Dist: llama-index-core>=0.10.0; extra == "llamaindex"
|
|
21
|
+
Provides-Extra: crewai
|
|
22
|
+
Requires-Dist: crewai>=0.1.0; extra == "crewai"
|
|
23
|
+
Requires-Dist: pydantic>=2.0; extra == "crewai"
|
|
24
|
+
Provides-Extra: all
|
|
25
|
+
Requires-Dist: langchain-core>=0.1.0; extra == "all"
|
|
26
|
+
Requires-Dist: llama-index-core>=0.10.0; extra == "all"
|
|
27
|
+
Requires-Dist: crewai>=0.1.0; extra == "all"
|
|
28
|
+
Requires-Dist: pydantic>=2.0; extra == "all"
|
|
29
|
+
Dynamic: license-file
|
|
30
|
+
|
|
31
|
+
# gui-now
|
|
32
|
+
|
|
33
|
+
Python SDK + agent tools for [GUI](https://gui.now) — instant shareable HTML canvases.
|
|
34
|
+
|
|
35
|
+
## Install
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pip install gui-now
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
With framework integrations:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
pip install gui-now[langchain]
|
|
45
|
+
pip install gui-now[llamaindex]
|
|
46
|
+
pip install gui-now[crewai]
|
|
47
|
+
pip install gui-now[all]
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Quick Start
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
from guinow import create_canvas
|
|
54
|
+
|
|
55
|
+
url = create_canvas(html="<h1>Hello World</h1>", title="My Canvas")
|
|
56
|
+
print(url) # https://gui.now/abc123
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Full Client
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
from guinow import GuiNowClient
|
|
63
|
+
|
|
64
|
+
client = GuiNowClient(api_key="your-pro-key") # or set GUI_NOW_API_KEY env var
|
|
65
|
+
result = client.create(
|
|
66
|
+
html="<h1>Dashboard</h1>",
|
|
67
|
+
title="Q1 Report",
|
|
68
|
+
expires="7d",
|
|
69
|
+
)
|
|
70
|
+
print(result.url)
|
|
71
|
+
print(result.expires_at)
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Every input format
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
client.create(html="<h1>Hi</h1>") # raw HTML
|
|
78
|
+
client.create(markdown="# Hi\n\nRendered server-side.") # Markdown
|
|
79
|
+
client.create_diagram("graph TD\n A-->B", title="Flow") # Mermaid
|
|
80
|
+
client.create(frames=[ # multi-tab canvas
|
|
81
|
+
{"html": "<h1>Overview</h1>", "label": "Overview"},
|
|
82
|
+
{"html": "<h1>Detail</h1>", "label": "Detail"},
|
|
83
|
+
])
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Update and extend
|
|
87
|
+
|
|
88
|
+
`create()` returns an `edit_token`. Keep it — it is the only way to change a
|
|
89
|
+
canvas afterwards. Free tier allows 3 edits per canvas; Pro is unlimited.
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
canvas = client.create(html="<h1>Draft</h1>")
|
|
93
|
+
|
|
94
|
+
client.update(canvas.id, canvas.edit_token, html="<h1>Final</h1>")
|
|
95
|
+
client.update(canvas.id, canvas.edit_token, title="Renamed")
|
|
96
|
+
client.update(canvas.id, canvas.edit_token, password=None) # remove protection
|
|
97
|
+
|
|
98
|
+
client.extend(canvas.id) # push expiry to 24h from now
|
|
99
|
+
canvas.open() # open the URL in a browser
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Omitting `password` leaves existing protection alone; passing `None` removes it.
|
|
103
|
+
|
|
104
|
+
## LangChain
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
from guinow.langchain import GuiNowTool
|
|
108
|
+
|
|
109
|
+
tool = GuiNowTool()
|
|
110
|
+
# Add to your agent's tools list
|
|
111
|
+
agent = initialize_agent(tools=[tool], ...)
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## LlamaIndex
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
from guinow.llamaindex import get_guinow_tool
|
|
118
|
+
|
|
119
|
+
tool = get_guinow_tool()
|
|
120
|
+
agent = ReActAgent.from_tools([tool], ...)
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## CrewAI
|
|
124
|
+
|
|
125
|
+
```python
|
|
126
|
+
from guinow.crewai import GuiNowTool
|
|
127
|
+
|
|
128
|
+
tool = GuiNowTool()
|
|
129
|
+
agent = Agent(tools=[tool], ...)
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Environment Variables
|
|
133
|
+
|
|
134
|
+
| Variable | Description |
|
|
135
|
+
|----------|-------------|
|
|
136
|
+
| `GUI_NOW_API_KEY` | Pro API key for higher rate limits and longer expiry |
|
|
137
|
+
| `GUI_NOW_URL` | Override the API base URL (defaults to `https://gui.now`) |
|
|
138
|
+
|
|
139
|
+
`GUI_NEW_API_KEY` and `GUI_NEW_URL` are the pre-rename names and are still read
|
|
140
|
+
as fallbacks, so keys exported before the move to gui.now keep working.
|
|
141
|
+
|
|
142
|
+
## License
|
|
143
|
+
|
|
144
|
+
MIT
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
src/gui_now.egg-info/PKG-INFO
|
|
5
|
+
src/gui_now.egg-info/SOURCES.txt
|
|
6
|
+
src/gui_now.egg-info/dependency_links.txt
|
|
7
|
+
src/gui_now.egg-info/requires.txt
|
|
8
|
+
src/gui_now.egg-info/top_level.txt
|
|
9
|
+
src/guinow/__init__.py
|
|
10
|
+
src/guinow/client.py
|
|
11
|
+
src/guinow/crewai.py
|
|
12
|
+
src/guinow/langchain.py
|
|
13
|
+
src/guinow/llamaindex.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
guinow
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""gui.now Python SDK — create shareable HTML canvases."""
|
|
2
|
+
|
|
3
|
+
from guinow.client import (
|
|
4
|
+
CanvasResult,
|
|
5
|
+
GuiNowClient,
|
|
6
|
+
GuiNowError,
|
|
7
|
+
RateLimitError,
|
|
8
|
+
create,
|
|
9
|
+
create_canvas,
|
|
10
|
+
create_diagram,
|
|
11
|
+
extend,
|
|
12
|
+
update,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"CanvasResult",
|
|
17
|
+
"GuiNowClient",
|
|
18
|
+
"GuiNowError",
|
|
19
|
+
"RateLimitError",
|
|
20
|
+
"create",
|
|
21
|
+
"create_canvas",
|
|
22
|
+
"create_diagram",
|
|
23
|
+
"extend",
|
|
24
|
+
"update",
|
|
25
|
+
]
|
|
26
|
+
__version__ = "1.0.0"
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
"""Core client for the gui.now API."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import os
|
|
7
|
+
import urllib.error
|
|
8
|
+
import urllib.request
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
from typing import Any, Optional
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
DEFAULT_BASE_URL = "https://gui.now"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _default_base_url() -> str:
|
|
17
|
+
# GUI_NEW_URL is the pre-rename name; still honoured so existing setups
|
|
18
|
+
# keep working without the user having to re-export anything.
|
|
19
|
+
return (
|
|
20
|
+
os.environ.get("GUI_NOW_URL")
|
|
21
|
+
or os.environ.get("GUI_NEW_URL")
|
|
22
|
+
or DEFAULT_BASE_URL
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _default_api_key() -> Optional[str]:
|
|
27
|
+
# GUI_NEW_API_KEY is the pre-rename name; see above.
|
|
28
|
+
return os.environ.get("GUI_NOW_API_KEY") or os.environ.get("GUI_NEW_API_KEY")
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@dataclass
|
|
32
|
+
class CanvasResult:
|
|
33
|
+
id: str
|
|
34
|
+
url: str
|
|
35
|
+
edit_token: str
|
|
36
|
+
expires_at: str
|
|
37
|
+
pro: bool
|
|
38
|
+
format: str
|
|
39
|
+
password_protected: bool = False
|
|
40
|
+
|
|
41
|
+
def open(self) -> None:
|
|
42
|
+
"""Open the canvas URL in the default browser."""
|
|
43
|
+
import webbrowser
|
|
44
|
+
|
|
45
|
+
webbrowser.open(self.url)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class GuiNowError(Exception):
|
|
49
|
+
pass
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class RateLimitError(GuiNowError):
|
|
53
|
+
def __init__(self, retry_after: str = "3600"):
|
|
54
|
+
self.retry_after = retry_after
|
|
55
|
+
super().__init__(f"Rate limited. Retry after {retry_after} seconds.")
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class _Unset:
|
|
59
|
+
"""Sentinel so update() can tell 'omitted' from an explicit None."""
|
|
60
|
+
|
|
61
|
+
def __repr__(self) -> str: # pragma: no cover - debugging aid
|
|
62
|
+
return "<unset>"
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
_UNSET = _Unset()
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class GuiNowClient:
|
|
69
|
+
"""Client for the gui.now API."""
|
|
70
|
+
|
|
71
|
+
def __init__(
|
|
72
|
+
self,
|
|
73
|
+
api_key: Optional[str] = None,
|
|
74
|
+
base_url: Optional[str] = None,
|
|
75
|
+
):
|
|
76
|
+
self.api_key = api_key or _default_api_key()
|
|
77
|
+
self.base_url = (base_url or _default_base_url()).rstrip("/")
|
|
78
|
+
|
|
79
|
+
# ---- create -----------------------------------------------------------
|
|
80
|
+
|
|
81
|
+
def create(
|
|
82
|
+
self,
|
|
83
|
+
*,
|
|
84
|
+
html: Optional[str] = None,
|
|
85
|
+
markdown: Optional[str] = None,
|
|
86
|
+
mermaid: Optional[str] = None,
|
|
87
|
+
frames: Optional[list] = None,
|
|
88
|
+
title: Optional[str] = None,
|
|
89
|
+
theme: Optional[str] = None,
|
|
90
|
+
expires: Optional[str] = None,
|
|
91
|
+
password: Optional[str] = None,
|
|
92
|
+
) -> CanvasResult:
|
|
93
|
+
"""Create a canvas.
|
|
94
|
+
|
|
95
|
+
Exactly one content argument is needed:
|
|
96
|
+
html: raw HTML.
|
|
97
|
+
markdown: Markdown, rendered server-side.
|
|
98
|
+
mermaid: a Mermaid diagram, rendered pannable and zoomable.
|
|
99
|
+
frames: multi-tab views, [{"html": ..., "label": ...}, ...].
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
title: display name for the toolbar and OG meta.
|
|
103
|
+
theme: "dark" (default) or "light".
|
|
104
|
+
expires: Pro only: "1h", "24h", "7d", "14d", "30d".
|
|
105
|
+
password: Pro only: password-protect the canvas.
|
|
106
|
+
"""
|
|
107
|
+
body: dict[str, Any] = {}
|
|
108
|
+
if html is not None:
|
|
109
|
+
body["html"] = html
|
|
110
|
+
if markdown is not None:
|
|
111
|
+
body["markdown"] = markdown
|
|
112
|
+
if mermaid is not None:
|
|
113
|
+
body["mermaid"] = mermaid
|
|
114
|
+
if frames is not None:
|
|
115
|
+
body["frames"] = frames
|
|
116
|
+
if title is not None:
|
|
117
|
+
body["title"] = title
|
|
118
|
+
if theme is not None:
|
|
119
|
+
body["theme"] = theme
|
|
120
|
+
if expires is not None:
|
|
121
|
+
body["expires"] = expires
|
|
122
|
+
if password is not None:
|
|
123
|
+
body["password"] = password
|
|
124
|
+
|
|
125
|
+
if not any(
|
|
126
|
+
body.get(k) for k in ("html", "markdown", "mermaid", "frames")
|
|
127
|
+
):
|
|
128
|
+
raise GuiNowError(
|
|
129
|
+
"One of html, markdown, mermaid or frames is required"
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
return _parse(self._request("POST", "/api/canvas", body))
|
|
133
|
+
|
|
134
|
+
def create_diagram(
|
|
135
|
+
self,
|
|
136
|
+
mermaid: str,
|
|
137
|
+
*,
|
|
138
|
+
title: Optional[str] = None,
|
|
139
|
+
theme: Optional[str] = None,
|
|
140
|
+
) -> CanvasResult:
|
|
141
|
+
"""Create a Mermaid diagram canvas."""
|
|
142
|
+
return self.create(mermaid=mermaid, title=title, theme=theme)
|
|
143
|
+
|
|
144
|
+
# ---- update -----------------------------------------------------------
|
|
145
|
+
|
|
146
|
+
def update(
|
|
147
|
+
self,
|
|
148
|
+
canvas_id: str,
|
|
149
|
+
edit_token: str,
|
|
150
|
+
*,
|
|
151
|
+
html: Optional[str] = None,
|
|
152
|
+
title: Optional[str] = None,
|
|
153
|
+
frames: Optional[list] = None,
|
|
154
|
+
password: Any = _UNSET,
|
|
155
|
+
) -> dict:
|
|
156
|
+
"""Replace a canvas's content. Free tier allows 3 edits; Pro unlimited.
|
|
157
|
+
|
|
158
|
+
Pass password=None to remove password protection, or omit it to leave
|
|
159
|
+
whatever protection the canvas already has untouched.
|
|
160
|
+
"""
|
|
161
|
+
body: dict[str, Any] = {}
|
|
162
|
+
if html is not None:
|
|
163
|
+
body["html"] = html
|
|
164
|
+
if title is not None:
|
|
165
|
+
body["title"] = title
|
|
166
|
+
if frames is not None:
|
|
167
|
+
body["frames"] = frames
|
|
168
|
+
if password is not _UNSET:
|
|
169
|
+
body["password"] = password
|
|
170
|
+
|
|
171
|
+
if not body:
|
|
172
|
+
raise GuiNowError("update needs at least one field to change")
|
|
173
|
+
|
|
174
|
+
return self._request(
|
|
175
|
+
"PUT",
|
|
176
|
+
f"/api/canvas/{canvas_id}",
|
|
177
|
+
body,
|
|
178
|
+
headers={"Authorization": f"Bearer {edit_token}"},
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
def extend(self, canvas_id: str) -> dict:
|
|
182
|
+
"""Extend the canvas expiry to 24 hours from now."""
|
|
183
|
+
return self._request("POST", f"/api/canvas/{canvas_id}/extend", {})
|
|
184
|
+
|
|
185
|
+
# ---- transport --------------------------------------------------------
|
|
186
|
+
|
|
187
|
+
def _request(
|
|
188
|
+
self,
|
|
189
|
+
method: str,
|
|
190
|
+
path: str,
|
|
191
|
+
body: dict,
|
|
192
|
+
headers: Optional[dict] = None,
|
|
193
|
+
) -> dict:
|
|
194
|
+
req_headers = {"Content-Type": "application/json"}
|
|
195
|
+
if self.api_key:
|
|
196
|
+
req_headers["x-api-key"] = self.api_key
|
|
197
|
+
if headers:
|
|
198
|
+
req_headers.update(headers)
|
|
199
|
+
|
|
200
|
+
req = urllib.request.Request(
|
|
201
|
+
f"{self.base_url}{path}",
|
|
202
|
+
data=json.dumps(body).encode("utf-8"),
|
|
203
|
+
headers=req_headers,
|
|
204
|
+
method=method,
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
try:
|
|
208
|
+
with urllib.request.urlopen(req) as resp:
|
|
209
|
+
return json.loads(resp.read().decode("utf-8"))
|
|
210
|
+
except urllib.error.HTTPError as e:
|
|
211
|
+
if e.code == 429:
|
|
212
|
+
raise RateLimitError(e.headers.get("Retry-After", "3600")) from e
|
|
213
|
+
detail = e.read().decode("utf-8", errors="replace")
|
|
214
|
+
try:
|
|
215
|
+
detail = json.loads(detail).get("error", detail)
|
|
216
|
+
except json.JSONDecodeError:
|
|
217
|
+
pass
|
|
218
|
+
raise GuiNowError(f"API error ({e.code}): {detail}") from e
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def _parse(d: dict) -> CanvasResult:
|
|
222
|
+
return CanvasResult(
|
|
223
|
+
id=d["id"],
|
|
224
|
+
url=d["url"],
|
|
225
|
+
edit_token=d["edit_token"],
|
|
226
|
+
expires_at=d["expires_at"],
|
|
227
|
+
pro=d.get("pro", False),
|
|
228
|
+
format=d.get("format", "html"),
|
|
229
|
+
password_protected=d.get("password_protected", False),
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
# ---- module-level conveniences using a default client ---------------------
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def create(**kwargs) -> CanvasResult:
|
|
237
|
+
return GuiNowClient().create(**kwargs)
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def create_diagram(mermaid: str, **kwargs) -> CanvasResult:
|
|
241
|
+
return GuiNowClient().create_diagram(mermaid, **kwargs)
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def update(canvas_id: str, edit_token: str, **kwargs) -> dict:
|
|
245
|
+
return GuiNowClient().update(canvas_id, edit_token, **kwargs)
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def extend(canvas_id: str) -> dict:
|
|
249
|
+
return GuiNowClient().extend(canvas_id)
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def create_canvas(
|
|
253
|
+
html: Optional[str] = None,
|
|
254
|
+
markdown: Optional[str] = None,
|
|
255
|
+
title: Optional[str] = None,
|
|
256
|
+
expires: Optional[str] = None,
|
|
257
|
+
) -> str:
|
|
258
|
+
"""Quick helper — create a canvas and return just the URL."""
|
|
259
|
+
return GuiNowClient().create(
|
|
260
|
+
html=html, markdown=markdown, title=title, expires=expires
|
|
261
|
+
).url
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""CrewAI tool for gui.now."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Optional, Type
|
|
6
|
+
|
|
7
|
+
try:
|
|
8
|
+
from crewai.tools import BaseTool
|
|
9
|
+
from pydantic import BaseModel, Field
|
|
10
|
+
except ImportError:
|
|
11
|
+
raise ImportError(
|
|
12
|
+
"crewai and pydantic are required. "
|
|
13
|
+
"Install with: pip install gui-now[crewai]"
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
from guinow.client import GuiNowClient
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class CreateCanvasInput(BaseModel):
|
|
20
|
+
html: Optional[str] = Field(None, description="Raw HTML content for the canvas")
|
|
21
|
+
markdown: Optional[str] = Field(None, description="Markdown content (rendered server-side)")
|
|
22
|
+
title: Optional[str] = Field(None, description="Optional canvas title")
|
|
23
|
+
expires: Optional[str] = Field(None, description="Expiry: 1h, 24h, 7d, 14d, 30d")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class GuiNowTool(BaseTool):
|
|
27
|
+
"""CrewAI tool to create shareable HTML canvases on gui.now."""
|
|
28
|
+
|
|
29
|
+
name: str = "create_gui_canvas"
|
|
30
|
+
description: str = (
|
|
31
|
+
"Create a shareable HTML canvas on gui.now. "
|
|
32
|
+
"Send HTML or Markdown content and get a live URL anyone can view. "
|
|
33
|
+
"Use for dashboards, reports, previews, or any visual output."
|
|
34
|
+
)
|
|
35
|
+
args_schema: Type[BaseModel] = CreateCanvasInput
|
|
36
|
+
api_key: Optional[str] = None
|
|
37
|
+
|
|
38
|
+
def _run(
|
|
39
|
+
self,
|
|
40
|
+
html: Optional[str] = None,
|
|
41
|
+
markdown: Optional[str] = None,
|
|
42
|
+
title: Optional[str] = None,
|
|
43
|
+
expires: Optional[str] = None,
|
|
44
|
+
) -> str:
|
|
45
|
+
client = GuiNowClient(api_key=self.api_key)
|
|
46
|
+
result = client.create(html=html, markdown=markdown, title=title, expires=expires)
|
|
47
|
+
return f"Canvas created: {result.url} (expires {result.expires_at})"
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""LangChain tool for gui.now."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Optional, Type
|
|
6
|
+
|
|
7
|
+
try:
|
|
8
|
+
from langchain_core.tools import BaseTool
|
|
9
|
+
from pydantic import BaseModel, Field
|
|
10
|
+
except ImportError:
|
|
11
|
+
raise ImportError(
|
|
12
|
+
"langchain_core and pydantic are required. "
|
|
13
|
+
"Install with: pip install gui-now[langchain]"
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
from guinow.client import GuiNowClient
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class CreateCanvasInput(BaseModel):
|
|
20
|
+
html: Optional[str] = Field(None, description="Raw HTML content for the canvas")
|
|
21
|
+
markdown: Optional[str] = Field(None, description="Markdown content (rendered server-side)")
|
|
22
|
+
title: Optional[str] = Field(None, description="Optional canvas title")
|
|
23
|
+
expires: Optional[str] = Field(None, description="Expiry: 1h, 24h, 7d, 14d, 30d")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class GuiNowTool(BaseTool):
|
|
27
|
+
"""LangChain tool to create shareable HTML canvases on gui.now."""
|
|
28
|
+
|
|
29
|
+
name: str = "create_gui_canvas"
|
|
30
|
+
description: str = (
|
|
31
|
+
"Create a shareable HTML canvas on gui.now. "
|
|
32
|
+
"Send HTML or Markdown content and get a live URL anyone can view. "
|
|
33
|
+
"Use for dashboards, reports, previews, or any visual output."
|
|
34
|
+
)
|
|
35
|
+
args_schema: Type[BaseModel] = CreateCanvasInput
|
|
36
|
+
api_key: Optional[str] = None
|
|
37
|
+
|
|
38
|
+
def _run(
|
|
39
|
+
self,
|
|
40
|
+
html: Optional[str] = None,
|
|
41
|
+
markdown: Optional[str] = None,
|
|
42
|
+
title: Optional[str] = None,
|
|
43
|
+
expires: Optional[str] = None,
|
|
44
|
+
) -> str:
|
|
45
|
+
client = GuiNowClient(api_key=self.api_key)
|
|
46
|
+
result = client.create(html=html, markdown=markdown, title=title, expires=expires)
|
|
47
|
+
return f"Canvas created: {result.url} (expires {result.expires_at})"
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""LlamaIndex tool for gui.now."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
try:
|
|
8
|
+
from llama_index.core.tools import FunctionTool
|
|
9
|
+
except ImportError:
|
|
10
|
+
raise ImportError(
|
|
11
|
+
"llama_index is required. "
|
|
12
|
+
"Install with: pip install gui-now[llamaindex]"
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
from guinow.client import GuiNowClient
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _create_canvas(
|
|
19
|
+
html: str = "",
|
|
20
|
+
markdown: str = "",
|
|
21
|
+
title: str = "",
|
|
22
|
+
expires: str = "",
|
|
23
|
+
) -> str:
|
|
24
|
+
"""Create a shareable HTML canvas on gui.now.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
html: Raw HTML content for the canvas
|
|
28
|
+
markdown: Markdown content (rendered server-side). Use instead of html for text content.
|
|
29
|
+
title: Optional canvas title
|
|
30
|
+
expires: Expiry duration: 1h, 24h, 7d, 14d, 30d
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
The shareable canvas URL
|
|
34
|
+
"""
|
|
35
|
+
client = GuiNowClient()
|
|
36
|
+
result = client.create(
|
|
37
|
+
html=html or None,
|
|
38
|
+
markdown=markdown or None,
|
|
39
|
+
title=title or None,
|
|
40
|
+
expires=expires or None,
|
|
41
|
+
)
|
|
42
|
+
return f"Canvas created: {result.url} (expires {result.expires_at})"
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def get_guinow_tool() -> FunctionTool:
|
|
46
|
+
"""Get a LlamaIndex FunctionTool for gui.now canvas creation."""
|
|
47
|
+
return FunctionTool.from_defaults(
|
|
48
|
+
fn=_create_canvas,
|
|
49
|
+
name="create_gui_canvas",
|
|
50
|
+
description=(
|
|
51
|
+
"Create a shareable HTML canvas on gui.now. "
|
|
52
|
+
"Send HTML or Markdown content and get a live URL anyone can view."
|
|
53
|
+
),
|
|
54
|
+
)
|