RawIntent 5.2.1
Make a small API with a few lines of Python.
Start here
Install this local version with python -m pip install . from the project folder.
Train a tiny language model
Install the optional machine-learning dependency with python -m pip install '.[ml]'.
This educational character model starts with random weights and practices on your
own UTF-8 text. It is small and does not have the knowledge or quality of a large
pretrained assistant. A longer text file and more practice rounds take more time.
Save this as tiny_llm.py next to corpus.txt:
from rawintent import AIModel
AIModel = AIModel()
@AIModel.TRAIN(TRAINFILE="corpus.txt", MODELFILE="my_model.pt", STEPS=1000)
def train_model():
print("Training finished")
@AIModel.PROMPT(SYSTEM_PROMPT="You are a helpful assistant.")
def ask(prompt):
pass
train_model()
print(ask("Hello", tokens=80))
STEPS means practice rounds: each round shows the model short pieces of your
text and asks it to guess the next character. CONTEXT controls how many
characters it sees at once. Training creates a checkpoint file; the prompt
decorator can load it again on a later run.
1. Make something people can read
Save this as app.py:
from rawintent import API
api = API()
api.reply("hello", "Hello, world!", public=True)
api.start()
Run python app.py. Open http://127.0.0.1:8000/hello in your browser.
You will see "Hello, world!". Keep the program running while you use the API.
API()makes your API.reply("hello", ...)puts an answer at/hello.public=Truemeans people can read it without a key.start()starts the server.
2. Make something people can ask to do
Replace app.py with:
from rawintent import API
api = API()
@api.action(public=True)
def add(a=0, b=0):
return a + b
api.start()
Stop the previous server with Ctrl+C, then run python app.py again.
The function name add becomes the address /add.
a=0 and b=0 mean these inputs are whole numbers and default to zero.
Use 0.0 for decimals, "" for text, or False for yes/no values.
Explicit type annotations still work when you want them.
In another terminal, run a second Python file:
from rawintent import send
answer = send("http://127.0.0.1:8000/add", a=2, b=3)
print(answer) # 5
That's it: give send() an address and the values to send. It handles the JSON
request and closes its connection for you. Use read(address) for fixed replies
and other GET endpoints. Supply query values as read(address, name="Alice").
The address must include http:// or https://; pass inputs as named values,
not as a query string in the address.
3. Add a key when you want private access
Change @api.action(public=True) to just @api.action, then restart the server.
It now requires a key. In a separate file, run this once from the same folder
where you started your server:
from rawintent import make_key
print(make_key())
Copy the printed key. Then send it with your request:
from rawintent import send
answer = send("http://127.0.0.1:8000/add", key="PASTE_YOUR_KEY_HERE", a=2, b=3)
print(answer)
Keep your real key private; an environment variable is a good place to keep it
when sharing your code. examples/easy_client.py shows that approach. Create a
key once and reuse it; each call to make_key() creates a different key.
If you set API(key_store=...), use the same path in make_key(store=...).
Run and try your API in one file
For a quick demo or a test, use api.running(). It starts a real local server,
waits until it is ready, and stops it when you leave the block:
from rawintent import API
api = API(key_store=":memory:")
@api.action
def multiply(a=0, b=0):
return a * b
key = api.create_key("example")
with api.running(key=key) as server:
print(server.send("multiply", a=6, b=7)) # 42
There is no need to write socket, thread, waiting, or shutdown code.
server.send("name", ...) calls an action. server.read("name", ...) reads an
endpoint. server.url gives the address if you want to use a different client.
A free port is selected automatically. Set port=8000 if you need a fixed port.
The key is optional for public endpoints. Passing a key does not bypass permissions, expiration, or revocation. This example uses an in-memory key store so its demo key disappears when the program ends; normal apps use the default persistent store. Each running block starts a new server; a finished context cannot be re-entered.
api.running(timeout=10) bounds startup waiting, request timeouts, and graceful
shutdown. Async startup can be cancelled on timeout; blocking user code cannot
be forcibly terminated in a Python thread. A shutdown that cannot finish raises
a timeout error. This helper listens on local loopback and serves HTTP endpoints;
use api.start() or the ASGI server commands for normal hosting or WebSockets.
Handy shortcuts
api.reply("status", {"running": True}, public=True)
@api.action(name="say-hello", public=True)
def greet(name="friend"):
return f"Hello, {name}!"
Use send("http://127.0.0.1:8000/say-hello", name="Sam") to call it.
Visit http://127.0.0.1:8000/docs to try your API in a browser.
For several requests, reuse a client:
from rawintent import Client
with Client("http://127.0.0.1:8000", api_key="YOUR_KEY") as client:
print(client.call("add", a=2, b=3))
AsyncClient also supports await client.call(...).
send() and read() reserve the argument key for authentication; use the
existing Client.post(..., json={...}) or Client.get(..., params={...}) interface
if your endpoint itself has an input named key.
These helpers are additions: the 5.0 endpoint decorators, clients, and key management still work. The full API reference follows.
Optional onion search
Install the optional dependencies with python -m pip install '.[onion-search]'.
Run Tor or Tor Browser locally, then search through its SOCKS proxy:
from rawintent import OnionSearcher
with OnionSearcher() as searcher:
for result in searcher.search("example topic", limit=5):
print(result.title, result.url, result.description)
The default proxy is socks5h://127.0.0.1:9150 (Tor Browser); set
proxy="socks5h://127.0.0.1:9050" for a typical Tor service. The socks5h
scheme resolves onion hostnames through Tor. You can also run
rawintent-onion-search "example topic" --limit 5; use --json for JSON output.
The default engine is configurable with engine_url, and must be a v3 .onion
URL. The library returns search-engine results only; it does not visit result
sites. Onion services and search engines may be unavailable or return unsafe,
misleading, or outdated content. Use Tor only in ways allowed by local law.
Build your own API: define endpoints, create API keys, and send requests.
Version 5 replaces the English-language engine and neural compiler completely.
It is a breaking redesign, not a compatible update to RawIntent 4. Pin
rawintent==4.1.0 if you still need the old language engine.
Install
pip install rawintent
For an unpublished local checkout, use python -m pip install ..
Python 3.10 or later is required.
Create a server
Save this as app.py:
from rawintent import API
api = API("My API")
@api.get("/hello")
def hello(name: str = "world"):
return {"message": f"Hello, {name}!"}
@api.post("/add")
def add(a: float, b: float):
return {"result": a + b}
@api.get("/health", public=True)
def health():
return {"status": "ok"}
if __name__ == "__main__":
api.run()
Run python app.py. It listens at http://127.0.0.1:8000.
Open http://127.0.0.1:8000/docs to try endpoints in your browser.
You can also generate the starter and launch it with:
rawintent init app.py
rawintent serve app:api --reload
python -m rawintent works anywhere the console command is unavailable.
init will not overwrite an existing file.
Create a key and send requests
Run this in another terminal from the same working directory as your server:
rawintent keys create --name my-client
Copy the returned ri_... token. It is displayed only at creation and cannot be
recovered from the database. Put it in an environment variable or a secret store.
In PowerShell you can capture the key directly:
$env:RAWINTENT_API_KEY = python -m rawintent keys create --name my-client
python examples/client.py
In your client script:
import os
from rawintent import Client
with Client("http://127.0.0.1:8000", api_key=os.environ["RAWINTENT_API_KEY"]) as client:
print(client.get("/hello", params={"name": "Vincent"}))
print(client.post("/add", json={"a": 2, "b": 3}))
Output:
{'message': 'Hello, Vincent!'}
{'result': 5.0}
Any HTTP client can call your API. Send the key in the X-API-Key header.
The interactive docs have an Authorize button for the same key.
Keys belong to the server/database that issued them; they are not PyPI tokens
or credentials for a hosted RawIntent service.
Endpoint rules
@api.get,@api.post,@api.put,@api.patch, and@api.deleteregister routes.- Endpoints require a valid key unless
public=Trueis explicit. - GET and DELETE scalar arguments come from query parameters.
- POST, PUT, and PATCH arguments become fields of a JSON object.
{item_id}in a route is a path parameter and takes precedence over the body.- Python type annotations validate inputs. Missing or invalid input returns HTTP 422.
- Both regular
defandasync defhandlers work. - Dictionary and list results become JSON. Return
Responsefor other formats. @api.route("/status", methods=["GET", "HEAD"])supports other HTTP methods.- Register body-based and query-based methods separately.
@api.put("/items/{item_id}")
def update_item(item_id: int, title: str, count: int = 1):
return {"id": item_id, "title": title, "count": count}
Call with client.put("/items/42", json={"title": "Notebook", "count": 3}).
For a raw JSON body or explicit query arguments, use exported FastAPI helpers:
from rawintent import Body, Query
@api.post("/echo")
def echo(data: dict = Body(...), verbose: bool = Query(False)):
return {"data": data, "verbose": verbose}
Body(...) uses the entire JSON document. Ordinary parameters instead use named
fields, so def echo(data: dict) expects {"data": {...}}.
BaseModel, Field, Depends, Request, Response, and HTTPException are
also exported. See FastAPI parameter documentation
for explicit parameter declarations and nested Pydantic models.
Key management
The default SQLite registry is .rawintent/keys.sqlite3, relative to the process
working directory. Use an absolute path for servers launched from different directories:
api = API("My API", key_store="C:/my-api/keys.sqlite3")
Use the same file with CLI commands:
rawintent keys create --store C:/my-api/keys.sqlite3 --name reader --scope items:read --expires-in 86400
rawintent keys list --store C:/my-api/keys.sqlite3
rawintent keys revoke KEY_ID --store C:/my-api/keys.sqlite3
keys list shows IDs, names, prefixes, scopes, expiration, and active status;
it never prints secret tokens. Revocation takes effect on the next authentication
check, including across server processes sharing the same SQLite file.
Create keys programmatically with api.create_key("client-name"), or use
KeyStore(path).create(...). Create them during provisioning, not each time
an application imports or a development server reloads.
For a local app that should save and reuse one client key automatically, call
api.load_or_create_key(...) before starting the server:
api = API(key_store=".rawintent/keys.sqlite3")
key = api.load_or_create_key(".rawintent/client-key.txt", name="local-client")
api.start()
The helper creates the key in the API's key store if the file is missing, then returns the saved key on later runs. The key file contains the secret in plain text; keep it private and out of source control.
@api.get("/items", scopes=["items:read"])
def list_items():
return []
A key created with scopes=["items:read"] can access that endpoint. Missing
required scopes return 403. Keys with scopes=None (the default) have all scopes;
scopes=[] allows ordinary protected endpoints but no named scopes. Scope names
are exact strings, with no wildcard expansion. Expired or revoked keys return 401.
request.state.api_key contains authenticated KeyInfo inside a protected handler.
Async clients and errors
import asyncio
import os
from rawintent import AsyncClient, APIError
async def main():
async with AsyncClient("http://127.0.0.1:8000", os.environ["RAWINTENT_API_KEY"]) as client:
try:
print(await client.post("/add", json={"a": 2, "b": 3}))
except APIError as error:
print(error.status_code, error.detail)
asyncio.run(main())
Clients parse JSON responses, return text for non-JSON responses, and return None
for empty responses. HTTP errors and redirects raise APIError. Network errors
and timeouts retain their HTTPX exception types. The default timeout is 30 seconds;
set timeout=10 when constructing a client to change it. Writes are not retried.
Client requests accept paths on their configured server, not arbitrary external
URLs. Redirects are not followed. A base URL ending in /v1 retains that prefix
for calls such as client.get("/items").
Hosting
RawIntent builds on FastAPI,
Uvicorn, and HTTPX.
api is an ASGI application; run rawintent serve app:api --host 0.0.0.0 --port 8000
or uvicorn app:api on your host. Use a TLS reverse proxy for HTTPS outside local
development. Disable interactive docs with API(docs=False) if desired.
Docs and the OpenAPI schema are public when enabled, but protected endpoint calls
still require a key. api.app exposes the underlying FastAPI app for advanced
middleware and lifespan configuration. Routes added directly to api.app use
FastAPI rules and do not automatically get RawIntent key authentication.
The SQLite registry must live on persistent storage. Keep it and your tokens out of source control. The library does not provide hosting, billing, user accounts, rate limiting, or a public key-signup portal. Key creation is a local administrative operation; there is no unauthenticated web endpoint that issues credentials.
Development
python -m pip install -e ".[dev]"
python -m pytest -q
python -m build
python -m twine check dist/*
Metadata
Release files for rawintent 5.2.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| rawintent-5.2.1.tar.gz | 35.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| rawintent-5.2.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 61.3 kB
Release files / rawintent-5.2.1.tar.gz
| Download URL | rawintent-5.2.1.tar.gz |
|---|---|
| Size | 35.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
dd202ab252b3ae5f9b1eec2c0cab5e0531971951ec7d71307144719e0c8740ab
|
|
BLAKE2b-256 checksum How to use checksums |
204a204b6dfd3be563fe204a865c444c3bc470b8b35d271ae3cfd5c11b41e80c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.15
|
Release files / rawintent-5.2.1-py3-none-any.whl
| Download URL | rawintent-5.2.1-py3-none-any.whl |
|---|---|
| Size | 26.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
88110409d3ec53b63b2e06b326d92a99567e17d97693dba3a6775597b151ee20
|
|
BLAKE2b-256 checksum How to use checksums |
e8f8d078c33699a18071636d87908aea47a1e029a7743494b58dbf9f11b0cf0a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.15
|