mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-11-14 10:39:32 +01:00
The types, the module skeletons and the test fake that let the runtime, the install job and the window be built at the same time. Types: - src/types/local-model-install.ts (new): the runtime pin, the install states, steps and error codes with one sentence and one next step each, the progress, the `install` object of GET /api/local-model, the bodies and 409 answers of the install verbs, the state file. `LocalModelStatus` moves here; `install` is optional until the route emits it. - src/types/local-model.ts: the catalogue row's Mac memory figures, the measured speed of a tested entry, the `measured` budget source, `RecommendOptions`, and the row tag, the coding agents and the install total of a candidate. All optional until the catalogue and `recommend()` carry them, so the read-only stage keeps compiling and its tests stay green. Modules with final signatures and bodies that throw: runtime.ts (the compiled pin is final), ollama-client.ts, developer-tools.ts, state-store.ts, install-job.ts (the state, step and transition tables are final), connect.ts, custom-model-hosts-writer.ts, cli-install-runner.ts. Nothing imports them yet, so the server is unchanged. Implemented: paths.ts (every folder of the feature, from dataPath()), and `modelFitsBudget()` in recommend.ts, the judgement the job makes with the measured budget before it downloads a model. `recommend()` takes the options parameter and does not read it yet. Test fake: test/fixtures/fake-ollama.ts, a runtime on a port the system picks, in the shapes measured on a Mac: version, tags, ps, the pull stream (MLX and GGUF lines, an error line, a cut stream, a resumed pull), chat with and without a tool call, the model list, a runtime archive with Range support, and a fake child process that logs the device total. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
58 lines
2.9 KiB
TypeScript
58 lines
2.9 KiB
TypeScript
/**
|
|
* @fileoverview The ONE reader and writer of `local-model.json`, the state file of the local
|
|
* model install (`LocalModelStateFile`).
|
|
*
|
|
* CONTRACT STAGE: `IDLE_LOCAL_MODEL_STATE` is final. Every function below has its final
|
|
* signature and a body that throws; the bodies are written next.
|
|
*
|
|
* Why one module: the install job writes the state, the step and the digest, and the caller
|
|
* of the runtime supervisor writes the pid and the port. Two writers with their own temp file
|
|
* and rename would lose each other's change, which is the fault `mutateCustomModelHosts()`
|
|
* exists to end for the endpoint store. So every write goes through `mutateLocalModelState()`.
|
|
*
|
|
* The shape of `mutateSettingsFile()` (src/web/settings-writer.ts):
|
|
* - Serialized: mutations run one at a time on one promise chain.
|
|
* - Atomic: each write goes to a temp file with a random name beside the target and is
|
|
* renamed over it. The file is mode 0600.
|
|
* - Reading is TOTAL, unlike the settings writer: the file holds no setting of the user, only
|
|
* what this feature recorded, so a missing file, one that does not parse or one with
|
|
* another `schema` reads as `IDLE_LOCAL_MODEL_STATE` and the next write replaces it.
|
|
* - The path is `localModelPaths().stateFile`. The file itself holds no path.
|
|
*/
|
|
|
|
import type { LocalModelStateFile } from '../types.js';
|
|
|
|
/** Marks a body the contract stage left unwritten. */
|
|
function notImplemented(what: string, ..._args: unknown[]): never {
|
|
throw new Error(`${what} is not implemented yet`);
|
|
}
|
|
|
|
/** The state when nothing was ever installed. What an unreadable file reads as. */
|
|
export const IDLE_LOCAL_MODEL_STATE: LocalModelStateFile = Object.freeze({ schema: 1, state: 'idle' });
|
|
|
|
/**
|
|
* The state a file's text stands for. Pure and total: text that is not a JSON object of
|
|
* `schema: 1` with a known `state` is `IDLE_LOCAL_MODEL_STATE`. Members it does not know are
|
|
* dropped, and a member of the wrong type is dropped rather than trusted.
|
|
*/
|
|
export function parseLocalModelState(raw: string): LocalModelStateFile {
|
|
return notImplemented('parseLocalModelState', raw);
|
|
}
|
|
|
|
/** The stored state. Never throws: a file that cannot be read is `IDLE_LOCAL_MODEL_STATE`. */
|
|
export async function readLocalModelState(): Promise<LocalModelStateFile> {
|
|
return notImplemented('readLocalModelState');
|
|
}
|
|
|
|
/**
|
|
* Runs one change to the state file. `fn` receives the current state and returns the state to
|
|
* write, or `undefined` to write nothing. Resolves with the state before and after the change
|
|
* (`after` is `undefined` when nothing was written). `updatedAt` is stamped here. A failed
|
|
* write rejects its own caller only; the chain keeps going.
|
|
*/
|
|
export function mutateLocalModelState(
|
|
fn: (current: LocalModelStateFile) => LocalModelStateFile | undefined
|
|
): Promise<{ before: LocalModelStateFile; after: LocalModelStateFile | undefined }> {
|
|
return notImplemented('mutateLocalModelState', fn);
|
|
}
|