86 lines
3.3 KiB
Markdown
86 lines
3.3 KiB
Markdown
# Development
|
||
|
||
## Setup
|
||
|
||
```sh
|
||
pnpm install
|
||
pnpm run ci # type check + tests
|
||
```
|
||
|
||
To test your checkout in pi, install it from the local path:
|
||
|
||
```sh
|
||
pi install /absolute/path/to/pi-unigpt
|
||
```
|
||
|
||
Changes take effect after `/reload`.
|
||
|
||
Tests use fake keys, mocked discovery, and a local HTTP server. They never
|
||
contact uniGPT or read your pi configuration.
|
||
|
||
## Layout
|
||
|
||
| File | Purpose |
|
||
| --- | --- |
|
||
| `src/provider.ts` | The pi provider: login, model discovery, model metadata |
|
||
| `src/index.ts` | Extension entry point and the `/unigpt-refresh` command |
|
||
| `test/provider.test.ts` | Discovery, login, caching, and transport tests |
|
||
| `test/command.test.ts` | `/unigpt-refresh` behavior |
|
||
|
||
## How it works
|
||
|
||
**Endpoints.** The API base is
|
||
`https://api.mygpt.uni-graz.at/api/v1/llms/openai`. Discovery calls
|
||
`GET /models` with bearer auth; inference uses pi's built-in OpenAI
|
||
Responses transport on `POST /responses`. Every request must also carry a
|
||
`deployment-id` header naming the model, which each discovered model sets.
|
||
|
||
**Model metadata.** `/models` returns only IDs (e.g. `gpt-5.4`). Those IDs
|
||
are OpenAI model names, so `lookupCatalogMetadata` reads name, context
|
||
window, output limit, and image support from pi's bundled catalog
|
||
(`openai` and `azure` providers), taking the smaller value where the two
|
||
differ. This keeps the extension free of a hardcoded model table: new
|
||
models get correct limits when pi updates. Unknown IDs fall back to pi's
|
||
defaults for custom models (128k context, 16k output, text only).
|
||
|
||
The uniGPT web app has richer endpoints
|
||
(`/api/v1/tenants/uni/ai-models[/detailed]`) and an OpenAPI spec
|
||
(`/api/v1/docs/openapi.yaml`), but they require a browser session token
|
||
and reject API keys. If uniGPT ever exposes limits to API keys, prefer
|
||
them over the catalog lookup.
|
||
|
||
**Reasoning.** uniGPT ignores `reasoning.effort` and applies a fixed effort
|
||
per deployment (verified against the live API), so every model sets
|
||
`reasoning: false` to avoid offering thinking levels that do nothing.
|
||
|
||
**Caching and errors.** pi persists the discovered catalog and restores it
|
||
offline. A failed or malformed refresh keeps the previous catalog; single
|
||
malformed entries are skipped. Error messages never include the API key or
|
||
server response bodies.
|
||
|
||
**Login.** `/login unigpt` validates the key against `/models` before pi
|
||
stores it. Validation always uses the default endpoint, even if a user
|
||
overrides `baseUrl` in `models.json`.
|
||
|
||
## Compatibility
|
||
|
||
Supported: pi 1.3 and newer (`peerDependencies`). Development pins pi 1.5.
|
||
On 1.0–1.2 the tests pass and a live session worked, but `pnpm run check`
|
||
fails: pi's `Provider` typing rejects this provider, and 1.0–1.1 also lack
|
||
`RefreshModelsContext.baseUrl`, so `baseUrl` overrides are ignored there. To check another version, pin both
|
||
`@earendil-works/*` dev dependencies to it and run `pnpm run ci`.
|
||
|
||
## Known server-side issues
|
||
|
||
As of November 2026, `gpt-5-nano` and `gpt-4.1-nano` return HTTP 500
|
||
(`'NoneType' object has no attribute 'decrypt'`) for every request.
|
||
|
||
## Releasing
|
||
|
||
1. Run `pnpm run ci`.
|
||
2. Bump `version` in `package.json` and commit.
|
||
3. Tag and push: `git tag vX.Y.Z && git push origin main vX.Y.Z`.
|
||
4. Update the version in the README's "Pinning a version" example.
|
||
|
||
CI is defined in `.gitea/workflows/ci.yml` and runs once Gitea Actions are
|
||
enabled for the repository.
|