tileward 0.1.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.
- tileward-0.1.0/.github/workflows/ci.yml +28 -0
- tileward-0.1.0/.github/workflows/publish.yml +44 -0
- tileward-0.1.0/.gitignore +17 -0
- tileward-0.1.0/LICENSE +21 -0
- tileward-0.1.0/PKG-INFO +348 -0
- tileward-0.1.0/README.md +290 -0
- tileward-0.1.0/docs/device-auth.md +99 -0
- tileward-0.1.0/pyproject.toml +87 -0
- tileward-0.1.0/src/tileward/__init__.py +55 -0
- tileward-0.1.0/src/tileward/_http.py +365 -0
- tileward-0.1.0/src/tileward/_mcp.py +251 -0
- tileward-0.1.0/src/tileward/_version.py +1 -0
- tileward-0.1.0/src/tileward/auth.py +200 -0
- tileward-0.1.0/src/tileward/cli/__init__.py +1 -0
- tileward-0.1.0/src/tileward/cli/commands/__init__.py +1 -0
- tileward-0.1.0/src/tileward/cli/commands/account.py +95 -0
- tileward-0.1.0/src/tileward/cli/commands/auth.py +181 -0
- tileward-0.1.0/src/tileward/cli/commands/chat.py +174 -0
- tileward-0.1.0/src/tileward/cli/commands/config.py +103 -0
- tileward-0.1.0/src/tileward/cli/commands/context.py +239 -0
- tileward-0.1.0/src/tileward/cli/commands/docs.py +166 -0
- tileward-0.1.0/src/tileward/cli/commands/guard.py +107 -0
- tileward-0.1.0/src/tileward/cli/commands/keys.py +143 -0
- tileward-0.1.0/src/tileward/cli/commands/models.py +53 -0
- tileward-0.1.0/src/tileward/cli/main.py +263 -0
- tileward-0.1.0/src/tileward/cli/output.py +134 -0
- tileward-0.1.0/src/tileward/client.py +215 -0
- tileward-0.1.0/src/tileward/config.py +243 -0
- tileward-0.1.0/src/tileward/errors.py +129 -0
- tileward-0.1.0/src/tileward/py.typed +0 -0
- tileward-0.1.0/src/tileward/resources/__init__.py +1 -0
- tileward-0.1.0/src/tileward/resources/account.py +67 -0
- tileward-0.1.0/src/tileward/resources/chat.py +258 -0
- tileward-0.1.0/src/tileward/resources/context.py +273 -0
- tileward-0.1.0/src/tileward/resources/documents.py +180 -0
- tileward-0.1.0/src/tileward/resources/guard.py +91 -0
- tileward-0.1.0/src/tileward/resources/keys.py +102 -0
- tileward-0.1.0/src/tileward/resources/models.py +106 -0
- tileward-0.1.0/tests/conftest.py +41 -0
- tileward-0.1.0/tests/test_cli.py +205 -0
- tileward-0.1.0/tests/test_config.py +69 -0
- tileward-0.1.0/tests/test_device_auth.py +264 -0
- tileward-0.1.0/tests/test_errors.py +64 -0
- tileward-0.1.0/tests/test_mcp.py +120 -0
- tileward-0.1.0/tests/test_resources.py +289 -0
- tileward-0.1.0/tests/test_transport.py +171 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
name: ci
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
strategy:
|
|
12
|
+
fail-fast: false
|
|
13
|
+
matrix:
|
|
14
|
+
# The floor and the ceiling of what pyproject claims. A package that says 3.9+ and is only
|
|
15
|
+
# ever run on 3.12 is claiming something nobody checks.
|
|
16
|
+
python-version: ["3.9", "3.13"]
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
- uses: actions/setup-python@v5
|
|
20
|
+
with:
|
|
21
|
+
python-version: ${{ matrix.python-version }}
|
|
22
|
+
- run: pip install -e ".[dev]"
|
|
23
|
+
- run: pytest -q
|
|
24
|
+
- run: ruff check src tests
|
|
25
|
+
- name: mypy
|
|
26
|
+
# The type check targets one version; running it twice would report the same result twice.
|
|
27
|
+
if: matrix.python-version == '3.13'
|
|
28
|
+
run: mypy src/tileward
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
name: publish
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
|
|
8
|
+
# NO API TOKEN. This uses PyPI Trusted Publishing (OIDC): PyPI is configured to trust this
|
|
9
|
+
# repository and this workflow filename, and mints a short-lived credential per run. Nothing
|
|
10
|
+
# long-lived is stored in the repo, in Actions secrets, or on anyone's laptop.
|
|
11
|
+
#
|
|
12
|
+
# Setup on PyPI, once: Publishing -> add a pending publisher for project `tileward`,
|
|
13
|
+
# owner `Tileward-com`, repository `tileward-cli`, workflow `publish.yml`.
|
|
14
|
+
permissions:
|
|
15
|
+
contents: read
|
|
16
|
+
|
|
17
|
+
jobs:
|
|
18
|
+
test:
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
steps:
|
|
21
|
+
- uses: actions/checkout@v4
|
|
22
|
+
- uses: actions/setup-python@v5
|
|
23
|
+
with:
|
|
24
|
+
python-version: "3.11"
|
|
25
|
+
- run: pip install -e ".[dev]"
|
|
26
|
+
- run: pytest -q
|
|
27
|
+
|
|
28
|
+
publish:
|
|
29
|
+
# Never publish a red build. A version number cannot be reused on PyPI, so a broken upload is
|
|
30
|
+
# permanent -- the only fix is a new version, and the bad one stays installable.
|
|
31
|
+
needs: test
|
|
32
|
+
runs-on: ubuntu-latest
|
|
33
|
+
permissions:
|
|
34
|
+
id-token: write
|
|
35
|
+
steps:
|
|
36
|
+
- uses: actions/checkout@v4
|
|
37
|
+
- uses: actions/setup-python@v5
|
|
38
|
+
with:
|
|
39
|
+
python-version: "3.11"
|
|
40
|
+
- run: pip install build
|
|
41
|
+
- run: python -m build
|
|
42
|
+
- name: Check the long description renders on PyPI
|
|
43
|
+
run: pip install twine && twine check dist/*
|
|
44
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
tileward-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Tileward
|
|
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.
|
tileward-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: tileward
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Tileward client library and CLI (twcli): run models, govern them, and work with context and documents.
|
|
5
|
+
Project-URL: Homepage, https://tileward.com
|
|
6
|
+
Project-URL: Documentation, https://tileward.com/docs
|
|
7
|
+
Project-URL: Source, https://github.com/Tileward-com/tileward-cli
|
|
8
|
+
Project-URL: Issues, https://github.com/Tileward-com/tileward-cli/issues
|
|
9
|
+
Project-URL: Changelog, https://github.com/Tileward-com/tileward-cli/releases
|
|
10
|
+
Project-URL: Dashboard, https://app.tileward.com/account
|
|
11
|
+
Author: Tileward
|
|
12
|
+
License: MIT License
|
|
13
|
+
|
|
14
|
+
Copyright (c) 2026 Tileward
|
|
15
|
+
|
|
16
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
17
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
18
|
+
in the Software without restriction, including without limitation the rights
|
|
19
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
20
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
21
|
+
furnished to do so, subject to the following conditions:
|
|
22
|
+
|
|
23
|
+
The above copyright notice and this permission notice shall be included in all
|
|
24
|
+
copies or substantial portions of the Software.
|
|
25
|
+
|
|
26
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
27
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
28
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
29
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
30
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
31
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
32
|
+
SOFTWARE.
|
|
33
|
+
License-File: LICENSE
|
|
34
|
+
Keywords: cli,context,governance,llm,mcp,openai-compatible,tileward
|
|
35
|
+
Classifier: Development Status :: 4 - Beta
|
|
36
|
+
Classifier: Environment :: Console
|
|
37
|
+
Classifier: Intended Audience :: Developers
|
|
38
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
39
|
+
Classifier: Programming Language :: Python :: 3
|
|
40
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
41
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
42
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
43
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
44
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
45
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
46
|
+
Classifier: Typing :: Typed
|
|
47
|
+
Requires-Python: >=3.9
|
|
48
|
+
Requires-Dist: click>=8.0
|
|
49
|
+
Requires-Dist: httpx<1,>=0.24
|
|
50
|
+
Requires-Dist: rich>=13.0
|
|
51
|
+
Provides-Extra: dev
|
|
52
|
+
Requires-Dist: mypy>=1.8; extra == 'dev'
|
|
53
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
54
|
+
Requires-Dist: pytest>=7.4; extra == 'dev'
|
|
55
|
+
Requires-Dist: respx>=0.20; extra == 'dev'
|
|
56
|
+
Requires-Dist: ruff>=0.5; extra == 'dev'
|
|
57
|
+
Description-Content-Type: text/markdown
|
|
58
|
+
|
|
59
|
+
<p align="center">
|
|
60
|
+
<img src="https://tileward.com/logo.png" alt="Tileward" width="120">
|
|
61
|
+
</p>
|
|
62
|
+
|
|
63
|
+
<h1 align="center">tileward</h1>
|
|
64
|
+
|
|
65
|
+
<p align="center">
|
|
66
|
+
<strong>Run large models on hardware you own, governed.</strong><br>
|
|
67
|
+
The Tileward client library and command line, in one package.
|
|
68
|
+
</p>
|
|
69
|
+
|
|
70
|
+
<p align="center">
|
|
71
|
+
<a href="https://pypi.org/project/tileward/"><img alt="PyPI" src="https://img.shields.io/pypi/v/tileward?color=1F5FA8&label=pypi"></a>
|
|
72
|
+
<a href="https://pypi.org/project/tileward/"><img alt="Python" src="https://img.shields.io/pypi/pyversions/tileward?color=0C6E58"></a>
|
|
73
|
+
<a href="https://pypi.org/project/tileward/"><img alt="Downloads" src="https://img.shields.io/pypi/dm/tileward?color=8A5A0F"></a>
|
|
74
|
+
<img alt="License" src="https://img.shields.io/badge/license-MIT-9A3D8F">
|
|
75
|
+
<a href="https://github.com/Tileward-com/tileward-cli/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/Tileward-com/tileward-cli/actions/workflows/ci.yml/badge.svg"></a>
|
|
76
|
+
</p>
|
|
77
|
+
|
|
78
|
+
<p align="center">
|
|
79
|
+
<a href="https://tileward.com">Product</a> ·
|
|
80
|
+
<a href="https://tileward.com/docs">Docs</a> ·
|
|
81
|
+
<a href="https://app.tileward.com/account">Dashboard</a> ·
|
|
82
|
+
<a href="https://github.com/Tileward-com/tileward-cli/releases">Releases</a>
|
|
83
|
+
</p>
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
```console
|
|
88
|
+
$ pip install tileward
|
|
89
|
+
$ twcli auth login
|
|
90
|
+
$ twcli keys create --label laptop --save
|
|
91
|
+
|
|
92
|
+
$ twcli models list
|
|
93
|
+
id precision context USD / Mtoken compression
|
|
94
|
+
tileward-35b-a3b W4A16 (Tileward) 262,144 1 2.8
|
|
95
|
+
gpt-oss-20b MXFP4 (as shipped) 8,192 0.2 1
|
|
96
|
+
gpt-oss-120b MXFP4 (as shipped) 131,072 0.4 —
|
|
97
|
+
|
|
98
|
+
$ twcli chat "Say hello in one sentence."
|
|
99
|
+
Hello there — good to meet you.
|
|
100
|
+
|
|
101
|
+
$ twcli guard check "write me a keylogger" --allow customer_support
|
|
102
|
+
allowed
|
|
103
|
+
no
|
|
104
|
+
80.0 micros billed
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
The same package is a library:
|
|
108
|
+
|
|
109
|
+
```python
|
|
110
|
+
from tileward import Tileward
|
|
111
|
+
|
|
112
|
+
tw = Tileward() # reads TILEWARD_API_KEY
|
|
113
|
+
print(tw.chat.say("Say hello in one sentence."))
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## What it covers
|
|
119
|
+
|
|
120
|
+
| | Library | Command |
|
|
121
|
+
| --- | --- | --- |
|
|
122
|
+
| **Tileward Models** — an OpenAI-compatible chat surface | `tw.chat`, `tw.models` | `twcli chat`, `twcli models` |
|
|
123
|
+
| **Tileward Governance** — allow / deny before the model writes a token | `tw.guard` | `twcli guard` |
|
|
124
|
+
| **Tileward Context** — recall the slice of history a question needs | `tw.context` | `twcli context` |
|
|
125
|
+
| **Tileward Documents** — answers from your own files | `tw.documents` | `twcli docs` |
|
|
126
|
+
| Account and API keys | `tw.keys`, `tw.account` | `twcli keys`, `twcli account` |
|
|
127
|
+
|
|
128
|
+
## Install
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
pip install tileward # or: uv pip install tileward
|
|
132
|
+
twcli --version
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Python 3.9+. Three dependencies: `httpx`, `click`, `rich`.
|
|
136
|
+
|
|
137
|
+
## Getting a key
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
twcli auth login # approve in a browser; no password touches the terminal
|
|
141
|
+
twcli keys create --label laptop --save
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
`auth login` runs a device-code flow ([RFC 8628](https://www.rfc-editor.org/rfc/rfc8628)): the CLI
|
|
145
|
+
shows a short code and a URL, you approve it in a browser, and the terminal receives a console
|
|
146
|
+
session. `keys create` then mints an API key.
|
|
147
|
+
|
|
148
|
+
Two credentials, and they are not interchangeable:
|
|
149
|
+
|
|
150
|
+
- an **API key** (`tw_live_…`) calls the model, the guard, Context and Documents;
|
|
151
|
+
- a **console session** manages the account — minting keys, reading billing, binding policies.
|
|
152
|
+
|
|
153
|
+
A key that could mint keys would survive its own revocation, so it cannot. If you already have a
|
|
154
|
+
key, skip the login:
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
export TILEWARD_API_KEY="tw_live_..."
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## The command line
|
|
161
|
+
|
|
162
|
+
```
|
|
163
|
+
twcli auth login|logout|status sign in, and see what this machine holds
|
|
164
|
+
twcli models list|show what is served right now
|
|
165
|
+
twcli chat [PROMPT] [-i] one prompt, a pipe, or a REPL
|
|
166
|
+
twcli guard check TEXT [--allow ...] allow / deny, no generation
|
|
167
|
+
twcli context recall|remember|pin|forget|topics|stats|threads|reset|purge
|
|
168
|
+
twcli docs ls|add|rm|search your own files
|
|
169
|
+
twcli keys list|create|rotate|revoke mint and retire API keys
|
|
170
|
+
twcli account show|usage|audit|savings balance, plan, and history
|
|
171
|
+
twcli config show|set|set-key|profiles stored settings
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Some things it is built to do:
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
# every command speaks JSON, and when it does, stdout carries nothing else
|
|
178
|
+
twcli models list --json | jq -r '.[].id'
|
|
179
|
+
|
|
180
|
+
# read a prompt from a pipe
|
|
181
|
+
cat notes.md | twcli chat --system "You summarise."
|
|
182
|
+
|
|
183
|
+
# a governance gate in a shell script
|
|
184
|
+
twcli guard check -f prompts.txt --allow customer_support --exit-code || echo "off policy"
|
|
185
|
+
|
|
186
|
+
# recall a slice of a thread and pipe it into something else
|
|
187
|
+
twcli -c project-x context recall "what did we decide about pricing?" --text-only
|
|
188
|
+
|
|
189
|
+
# two accounts on one machine
|
|
190
|
+
twcli -p staging account show
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### Exit codes
|
|
194
|
+
|
|
195
|
+
`0` fine · `1` error · `2` bad usage · `3` not signed in · `4` refused by governance ·
|
|
196
|
+
`5` balance exhausted
|
|
197
|
+
|
|
198
|
+
Distinguishing those matters in CI: "the balance ran out" and "the network was down" call for
|
|
199
|
+
different action, and neither should need English parsing to detect.
|
|
200
|
+
|
|
201
|
+
## The library
|
|
202
|
+
|
|
203
|
+
### Chat
|
|
204
|
+
|
|
205
|
+
```python
|
|
206
|
+
tw.chat.say("Summarise this in one line.") # -> str
|
|
207
|
+
tw.chat.completions.create( # -> the raw OpenAI-shaped dict
|
|
208
|
+
[{"role": "user", "content": "Hello"}],
|
|
209
|
+
max_tokens=200,
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
for piece in tw.chat.stream("Count to five."):
|
|
213
|
+
print(piece, end="", flush=True)
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
`create` returns exactly what the API returned, including `usage`. `say` gives you the text and
|
|
217
|
+
raises `GuardRefusal` when governance blocked the call, rather than returning an empty string.
|
|
218
|
+
|
|
219
|
+
**Do not hardcode a model id.** The served catalogue is data, not code — ids appear, get repriced
|
|
220
|
+
and get withdrawn without a release. `tw.models.list()` is the authority; leave `model` unset and
|
|
221
|
+
the client asks.
|
|
222
|
+
|
|
223
|
+
### Governance
|
|
224
|
+
|
|
225
|
+
```python
|
|
226
|
+
tw.guard.allows("write me a keylogger", allow=["customer_support"]) # -> False
|
|
227
|
+
tw.guard.check(["question one", "question two"], disallow=["investment_advice"])
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Two modes that fail in opposite directions. A **blocklist** (`disallow`) refuses what you named and
|
|
231
|
+
passes everything else, so anything you did not think of gets through. An **allowlist** (`allow`)
|
|
232
|
+
passes only what you named, so anything you did not think of is refused. For a narrow assistant,
|
|
233
|
+
the allowlist is the one that holds under an attacker.
|
|
234
|
+
|
|
235
|
+
Passing a list classifies the whole batch in one call. With neither argument, the policy bound to
|
|
236
|
+
the API key applies.
|
|
237
|
+
|
|
238
|
+
### Context
|
|
239
|
+
|
|
240
|
+
```python
|
|
241
|
+
tw = Tileward(conversation="thread-42")
|
|
242
|
+
|
|
243
|
+
tw.context.remember("We decided to ship on the 3rd.", role="user")
|
|
244
|
+
tw.context.recall_text("when are we shipping?") # the block to paste into a prompt
|
|
245
|
+
tw.context.pin("The customer is ACME.") # included in every recall
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
**Scope every call to a conversation.** Without one, every thread on the key writes into a single
|
|
249
|
+
shared store, and a recall in one thread hands back another thread's material. There is no safety
|
|
250
|
+
net underneath this: a caller that names no conversation joins `default` along with everyone else.
|
|
251
|
+
|
|
252
|
+
`forget()` retires a topic from recall; the stored turns remain. `purge_account()` deletes data.
|
|
253
|
+
|
|
254
|
+
### Documents
|
|
255
|
+
|
|
256
|
+
```python
|
|
257
|
+
tw.documents.ingest_file("handbook.pdf", folder="policies")
|
|
258
|
+
tw.documents.list(folder="policies")
|
|
259
|
+
tw.documents.delete("src_...")
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
Documents are account-scoped, not conversation-scoped: a file you upload is available to every
|
|
263
|
+
thread on the key. That is why none of these take a `conversation`.
|
|
264
|
+
|
|
265
|
+
Deleting a document drops its chunks, but material already folded into a conversation's summary
|
|
266
|
+
can still surface in a recall. If something has to be genuinely gone, `context.purge_account()` is
|
|
267
|
+
the operation that means it.
|
|
268
|
+
|
|
269
|
+
### Async
|
|
270
|
+
|
|
271
|
+
Every method has an awaitable twin on `AsyncTileward`, with the same names and arguments.
|
|
272
|
+
|
|
273
|
+
```python
|
|
274
|
+
from tileward import AsyncTileward
|
|
275
|
+
|
|
276
|
+
async with AsyncTileward() as tw:
|
|
277
|
+
print(await tw.chat.say("Hello"))
|
|
278
|
+
async for piece in tw.chat.stream("Count to five."):
|
|
279
|
+
print(piece, end="")
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### Errors
|
|
283
|
+
|
|
284
|
+
```python
|
|
285
|
+
from tileward import errors
|
|
286
|
+
|
|
287
|
+
try:
|
|
288
|
+
tw.chat.say("...")
|
|
289
|
+
except errors.InsufficientBalanceError: # 402 — top up; calls resume immediately
|
|
290
|
+
...
|
|
291
|
+
except errors.GuardRefusal as exc: # governance blocked it; exc.completion has the detail
|
|
292
|
+
...
|
|
293
|
+
except errors.APIError as exc: # exc.status, exc.code, exc.request_id
|
|
294
|
+
...
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
A governed refusal is **not** an HTTP error. It arrives as an ordinary completion with
|
|
298
|
+
`finish_reason: "content_filter"` and zero tokens billed — you are not charged for a refusal —
|
|
299
|
+
which is why `create` passes it through and only `say` raises.
|
|
300
|
+
|
|
301
|
+
## Configuration
|
|
302
|
+
|
|
303
|
+
Highest wins: an explicit argument, then the environment, then the profile, then the default.
|
|
304
|
+
|
|
305
|
+
| Environment | What it sets |
|
|
306
|
+
| --- | --- |
|
|
307
|
+
| `TILEWARD_API_KEY` | the key used for models, guard, Context and Documents |
|
|
308
|
+
| `TILEWARD_SESSION` | a console session, for the account surface |
|
|
309
|
+
| `TILEWARD_BASE_URL` | the API host — serves `/v1` only |
|
|
310
|
+
| `TILEWARD_CONSOLE_URL` | the console host — the account surface and `auth login` live here |
|
|
311
|
+
| `TILEWARD_CONTEXT_URL` | the Context host |
|
|
312
|
+
| `TILEWARD_CONVERSATION` | default conversation scope |
|
|
313
|
+
| `TILEWARD_MODEL` | default model id |
|
|
314
|
+
| `TILEWARD_PROFILE` | which stored profile to read |
|
|
315
|
+
| `TILEWARD_CONFIG_DIR` | where the files live (default `~/.config/tileward`) |
|
|
316
|
+
|
|
317
|
+
Settings go in `config.json`; credentials go in `credentials.json`, written `0600` in a `0700`
|
|
318
|
+
directory. They are separate files so showing your config never has to redact, and pasting it into
|
|
319
|
+
an issue has not pasted a key.
|
|
320
|
+
|
|
321
|
+
In a server process, `Tileward(load_config=False)` ignores the files entirely, so a developer's
|
|
322
|
+
`~/.config` cannot change how production behaves.
|
|
323
|
+
|
|
324
|
+
## Using it from another framework
|
|
325
|
+
|
|
326
|
+
The chat surface is OpenAI-compatible, so anything that speaks OpenAI works by changing two things
|
|
327
|
+
— the base URL and the key.
|
|
328
|
+
|
|
329
|
+
```python
|
|
330
|
+
from openai import OpenAI
|
|
331
|
+
from tileward import openai_base_url
|
|
332
|
+
|
|
333
|
+
client = OpenAI(base_url=openai_base_url(), api_key="tw_live_...")
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
That helper exists so nobody has to guess whether `/v1` belongs on the end.
|
|
337
|
+
|
|
338
|
+
## Links
|
|
339
|
+
|
|
340
|
+
- **Product** — [tileward.com](https://tileward.com)
|
|
341
|
+
- **Dashboard and keys** — [app.tileward.com/account](https://app.tileward.com/account)
|
|
342
|
+
- **What we ship and how each number was measured** — [tileward.com/llms.txt](https://tileward.com/llms.txt)
|
|
343
|
+
- **How `auth login` works** — [docs/device-auth.md](https://github.com/Tileward-com/tileward-cli/blob/main/docs/device-auth.md)
|
|
344
|
+
- **Issues** — [github.com/Tileward-com/tileward-cli/issues](https://github.com/Tileward-com/tileward-cli/issues)
|
|
345
|
+
|
|
346
|
+
## License
|
|
347
|
+
|
|
348
|
+
MIT — see [LICENSE](https://github.com/Tileward-com/tileward-cli/blob/main/LICENSE).
|