pi-unigpt/DEVELOPMENT.md
Benjamin Hackl ec9a2b173a
All checks were successful
CI / test (22) (push) Successful in 23s
CI / test (24) (push) Successful in 21s
CI: reference actions by full GitHub URL
2026-11-03 21:36:56 +01:00

4 KiB
Raw Permalink Blame History

Development

Setup

pnpm install
pnpm run ci   # type check + tests

To test your checkout in pi, install it from the local path:

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

CI is defined in .github/workflows/ci.yml, which Gitea Actions reads as well. It runs once Actions are enabled for the repository and a runner is available.

The workflow checks out code with ischanx/checkout, not actions/checkout. Upstream builds the clone URL from the server's origin and drops the /git subpath of imsc.uni-graz.at, so cloning fails (actions/checkout#1370). The fork's v6.0.2 tag fixes that; its v4 tags are identical to upstream and do not. The action is pinned to a commit hash because it runs with the CI token. When upgrading, review the fork's diff against upstream first.

All actions are referenced by full URL (uses: https://github.com/...). Short names like actions/setup-node@v4 would be looked up in the instance's default actions registry instead of on GitHub.