# gizlang:hub

the files of Hugging Face Hub repositories in a local folder, at REPO/FILE as the hub serves them. A file that the folder does not have is downloaded once from HUB/REPO/resolve/REVISION/FILE (HUB: $HF_ENDPOINT, else https://huggingface.co; with $HF_TOKEN when it is set) and written to its place at once. The pin of a repository (.pins/REPO.pin in the folder) keeps the commit that its first download resolved and the SHA-256 of each file downloaded: later downloads use that commit, and each read checks the file against its SHA-256. A snapshot downloads the data files of a repository folder (a model's weights stay on disk: Hub.path). And the client of a chat model behind an OpenAI-compatible API (modelApi), for the stack's builder and the database's pgml.transform.

```gizlang
import * as Hub from "gizlang:hub";
```

## Types

### `Hub`

```gizlang
type Hub = { sys: System; path: string; download: bool; allow: Vec<string>; limit: i64; endpoint: string; }
```

A models folder: `path`, the System that reads it, whether a missing file is downloaded, and the hub it comes from
(`endpoint`: $HF_ENDPOINT, a mirror or a local server, else https://huggingface.co). `allow`: the
repositories that may be downloaded ("owner/name", or "owner/" for all of an owner; empty: all). `limit`: the most
bytes of one downloaded file (0: no limit).

- `sys`: `gizlang_system.System`
- `path`: `string`
- `download`: `bool`
- `allow`: `Vec<string>`
- `limit`: `i64`
- `endpoint`: `string`

### `Held`

```gizlang
type Held = { repo: string; kind: string; }
```

A repository in the folder and what its files make: "encoder" (a sentence-transformers model: modules.json),
"cross-encoder" (a sequence classification model) or "model" (another config.json).

- `repo`: `string`
- `kind`: `string`

### `ModelApi`

```gizlang
type ModelApi = { endpoint: Http.Url; key: string; model: string; pool: Http.Pool; }
```

The API's base URL, its key, the model to use when a caller names none (AI_MODEL; "": none) and the connections
kept open to it. Every copy shares the one pool, so the rounds of all callers reuse connections.

- `endpoint`: `gizlang_http.Url`
- `key`: `string`
- `model`: `string`
- `pool`: `gizlang_http.Pool`

### `Sampling`

```gizlang
type Sampling = { maxTokens: i64; temperature: f64; topP: f64; seed: i64; stop: Vec<string>; }
```

The sampling options of a request, as the OpenAI API names them: max_tokens (0: the model's default),
temperature and top_p (a negative value: the default), seed (-1: none) and the stop strings.

- `maxTokens`: `i64`
- `temperature`: `f64`
- `topP`: `f64`
- `seed`: `i64`
- `stop`: `Vec<string>`

### `Call`

```gizlang
type Call = { id: string; name: string; arguments: string; }
```

A tool call the model made: its id, the tool, and its arguments (a JSON object's text).

- `id`: `string`
- `name`: `string`
- `arguments`: `string`

### `Fragment`

```gizlang
type Fragment = { index: i64; id: string; name: string; arguments: string; }
```

A piece of a tool call as a stream sends it: the call's index, and its id and name (first piece) or more of its
arguments.

- `index`: `i64`
- `id`: `string`
- `name`: `string`
- `arguments`: `string`

### `Chunk`

```gizlang
type Chunk = { text: string; reasoning: string; details: Vec<string>; calls: Vec<Fragment>; finish: string; tokens: i64; error: string; input: i64; cached: i64; cost: i64; }
```

What one event added: text, reasoning (and its provider details, which a tool round sends back), tool call
pieces, why the turn ended, and in the last event the usage: the tokens it wrote, the prompt tokens it read and of
those the ones read from the provider's cache, and the cost in millionths of a dollar (OpenRouter's `cost`); or an error.

- `text`: `string`
- `reasoning`: `string`
- `details`: `Vec<string>`
- `calls`: `Vec<gizlang_hub.Fragment>`
- `finish`: `string`
- `tokens`: `i64`
- `error`: `string`
- `input`: `i64`
- `cached`: `i64`
- `cost`: `i64`

### `Refusal`

```gizlang
type Refusal = { status: i64; message: string; }
```

Why a model API did not answer: its HTTP status (0: no connection) and its message.

- `status`: `i64`
- `message`: `string`

### `Picture`

```gizlang
type Picture = { data: bytes; media: string; }
```

A picture a model drew: its bytes and media type (image/png, image/jpeg, image/webp).

- `data`: `bytes`
- `media`: `string`

## Functions

### `hub`

```gizlang
function hub(sys: System, path: string, download: bool): Hub
```

The hub of the models folder `path`; `download` false reads only the files already there.

Effects: fail, io.

### `folder`

```gizlang
function folder(sys: System): string
```

The shared models folder: $GIZLANG_CACHE/huggingface, else ~/.cache/gizlang/huggingface; "" when neither is set.

Effects: fail, io.

### `validRepo`

```gizlang
function validRepo(repo: string): bool
```

Whether `repo` names a repository ("owner/name" or "name") and not a path out of the folder or into .pins.

Effects: fail.

### `dataFile`

```gizlang
function dataFile(name: string): bool
```

Whether a model reads a file of this name: JSON, safetensors, tokenizer and template files. Never code: no model
runs code of its own.

Effects: fail.

### `modelApi`

```gizlang
function modelApi(sys: System): Option<ModelApi>
```

The model API of AI_URL (https://openrouter.ai/api/v1 when unset), AI_API_KEY and AI_MODEL. None when the URL is
not http(s), or with no key for the default URL; a URL set by AI_URL (a local server) may have no key.

Effects: fail, write, io.

### `sampling`

```gizlang
function sampling(): Sampling
```

The model's defaults for every option.

Effects: fail.

### `stream`

```gizlang
function stream(c: ModelApi, model: string, effort: string, messages: Vec<bytes>, tools: bytes, answer: bool, options: Sampling): Http.Incoming throws string
```

Starts a streamed completion of `messages` (JSON objects, as `message` writes them) by `model` with `options`,
offering `tools` (a JSON array, b"" for none); `effort` ("", "low", "medium", "high") sets how hard it reasons;
`answer` keeps the tools described (earlier turns call them) but has the model write instead of calling one. An
answer other than 200 is its error message.

Effects: fail, read, write, console, io.

### `opened`

```gizlang
function opened(c: ModelApi, model: string, effort: string, messages: Vec<bytes>, tools: bytes, answer: bool, options: Sampling): Result<Http.Incoming, Refusal>
```

`stream`, with the status of a refusal (a gateway tries the next route on 0, 408, 429 and 5xx).

Effects: fail, read, write, console, io.

### `requestBody`

```gizlang
function requestBody(model: string, effort: string, messages: Vec<bytes>, tools: bytes, answer: bool, options: Sampling): bytes
```

The body of `stream`'s request.

Effects: fail.

### `posted`

```gizlang
function posted(c: ModelApi, body: bytes, extra: Vec<Http.Header> = []): Result<Http.Incoming, Refusal>
```

Posts `body`, a chat completion request, to `c` with the `extra` headers (a gateway route's own, which replace
these of the same name); an answer other than 200 is its refusal.

Effects: fail, read, write, console, io.

### `chunk`

```gizlang
function chunk(data: string): Chunk
```

One event's data read; `"[DONE]"` and an unreadable event read as nothing.

Effects: fail.

### `gather`

```gizlang
function gather(calls: mut Vec<Call>, f: Fragment): void
```

Adds a call's piece to the calls made so far.

Effects: fail.

### `system`

```gizlang
function system(fixed: string, varying: string): bytes
```

The system message: `fixed` (the same for every request of a task) marked as a cache breakpoint, so a provider
that caches by prefix (Anthropic and Gemini through OpenRouter; OpenAI caches without the mark) reads the tools and
it from cache on every later round; then `varying`, what changes between tasks.

Effects: fail.

### `breakpoint`

```gizlang
function breakpoint(message: bytes): bytes
```

`message` (as `message`, `calling` or `result` write it) with its text content marked as a cache breakpoint: put
on the last message of a request, a provider that caches by prefix reads the whole conversation before it from
cache on the next request, which appends to it. A message without text content is returned as it is.

Effects: fail.

### `message`

```gizlang
function message(role: string, content: string): bytes
```

A message: {"role": role, "content": content}.

Effects: fail.

### `calling`

```gizlang
function calling(content: string, calls: Vec<Call>, details: Vec<string>): bytes
```

The model's own turn that called tools, with the reasoning details the provider needs to continue it.

Effects: fail.

### `result`

```gizlang
function result(id: string, content: string): bytes
```

A tool's result for the call `id`.

Effects: fail.

### `pieces`

```gizlang
function pieces(c: ModelApi, model: string, messages: Vec<bytes>, options: Sampling): Vec<string> throws string
```

The pieces of text of a completion with no tools, in the order the stream sent them.

Effects: fail, read, write, console, io.

### `texts`

```gizlang
function texts(opened: Http.Incoming): Vec<string> throws string
```

The text pieces of an opened stream, which it closes.

Effects: fail, read, write, console, io.

### `complete`

```gizlang
function complete(c: ModelApi, model: string, messages: Vec<bytes>, options: Sampling): string throws string
```

The whole text of a completion with no tools, as for a title or a SQL transform.

Effects: fail, read, write, console, io.

### `picture`

```gizlang
function picture(c: ModelApi, model: string, prompt: string): Picture throws string
```

Has `model` (one that answers with images, as OpenRouter's image models do) draw `prompt`, and answers the first
picture: its data URL decoded.

Effects: fail, read, write, console, io.

## Methods

### `Hub.allows`

```gizlang
function Hub.allows(self: Hub, repo: string): bool
```

Whether the hub may download `repo`.

Effects: fail.

### `Hub.file`

```gizlang
function Hub.file(self: Hub, repo: string, file: string): Option<bytes> throws string
```

FILE of REPO: from the folder, downloaded first when it is missing, downloads are on and `allow` names REPO; none
when neither has it. A file with a SHA-256 in the pin of REPO must have it.

Effects: fail, read, write, console, io.

### `Hub.repositories`

```gizlang
function Hub.repositories(self: Hub): Vec<Held> throws string
```

The repositories that the folder holds ("owner/name" or "name", with a config.json), in name order.

Effects: fail, io.

### `Hub.path`

```gizlang
function Hub.path(self: Hub, repo: string, file: string): Option<string> throws string
```

The path of FILE of REPO in the folder, downloaded first as Hub.file downloads it; none when neither has it. It
reads nothing: a weight file stays on disk, and its SHA-256 is checked once, when it is downloaded.

Effects: fail, read, write, console, io.

### `Hub.snapshot`

```gizlang
function Hub.snapshot(self: Hub, repo: string, folder: string): string throws string
```

The local path of folder `folder` of REPO ("" for its top) with all its data files (dataFile; of the safetensors
files, only those that an index there names, when there is one), downloaded first when they are missing. The pin
keeps the folder listed, so a later snapshot needs no network. Downloads off, the folder holds what it holds.

Effects: fail, read, write, console, io.

