@magnitudedev/pi-extension 0.0.1-alpha.0 → 0.0.1-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -3
- package/dist/magnitude-plugin.json +5 -4
- package/dist/magnitude.js +132 -65
- package/dist/skills/magnitude/SKILL.md +16 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -3,15 +3,36 @@
|
|
|
3
3
|
This Pi package adds Magnitude model management commands and live local-inference progress to Pi's
|
|
4
4
|
built-in working row above the editor.
|
|
5
5
|
|
|
6
|
-
Requires Pi 0.83.0 or newer
|
|
6
|
+
Requires Pi 0.83.0 or newer. Install from your terminal:
|
|
7
|
+
|
|
8
|
+
```sh
|
|
9
|
+
pi install npm:@magnitudedev/pi-extension
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
On the next fresh interactive Pi launch, Magnitude asks **“Set up local models with Magnitude now?”**
|
|
13
|
+
Yes sends this prompt to your current Pi agent and starts onboarding:
|
|
14
|
+
|
|
15
|
+
> Set up local models for me with the Magnitude CLI. Install it with `npm i -g @magnitudedev/cli` (or my package manager), then run `magnitude docs onboarding` and follow the instructions.
|
|
16
|
+
|
|
17
|
+
No or Escape leaves “You can set up local models anytime with `/magnitude-setup`.” in the conversation.
|
|
18
|
+
The offer is remembered per Pi profile across restarts, reloads, and package updates. Existing
|
|
19
|
+
Magnitude model configurations, non-interactive modes, conversations, and startup prompts are left
|
|
20
|
+
alone. Run `/magnitude-setup` whenever you want to start onboarding yourself.
|
|
21
|
+
|
|
22
|
+
The package includes the Magnitude usage skill; an already-loaded skill of that name takes
|
|
23
|
+
precedence without a collision warning. `--no-skills` disables this fallback too. The CLI is not
|
|
24
|
+
an npm dependency and is not required to load the extension. Your Pi agent needs a working model
|
|
25
|
+
and credentials to carry out onboarding.
|
|
26
|
+
|
|
27
|
+
If you already have Magnitude and a local model, connect directly:
|
|
7
28
|
|
|
8
29
|
```sh
|
|
9
30
|
magnitude connections add pi
|
|
10
31
|
```
|
|
11
32
|
|
|
12
33
|
Restart Pi or run `/reload` after connecting. `PI_CODING_AGENT_DIR`, if set, is honored.
|
|
13
|
-
Installing the package directly
|
|
14
|
-
install the Magnitude CLI, start its service
|
|
34
|
+
Installing the package directly does not configure provider models,
|
|
35
|
+
install the Magnitude CLI, or start its service; the connection command
|
|
15
36
|
does all connection configuration and can be run safely after a standalone package install.
|
|
16
37
|
|
|
17
38
|
The extension bundles Magnitude's private SDK. Model status, loading, and stopping use the existing
|
|
@@ -28,6 +49,7 @@ latency, and token-weighted generation throughput. Pi extensions execute with yo
|
|
|
28
49
|
|
|
29
50
|
Commands:
|
|
30
51
|
|
|
52
|
+
- `/magnitude-setup` — ask your current agent to set up Magnitude local models
|
|
31
53
|
- `/load-model [model-id]` — load an installed model
|
|
32
54
|
- `/stop-model` — stop the active model
|
|
33
55
|
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@magnitudedev/pi-extension",
|
|
3
|
-
"version": "0.0.1-alpha.
|
|
3
|
+
"version": "0.0.1-alpha.1",
|
|
4
4
|
"rpcVersion": 1,
|
|
5
|
-
"contentFingerprint": "
|
|
5
|
+
"contentFingerprint": "292fa6fd784d957591e91f9a67c2460b7cf503149b66cdfe22ecd68ab33ca87d",
|
|
6
6
|
"files": {
|
|
7
|
-
"README.md": "
|
|
8
|
-
"dist/magnitude.js": "
|
|
7
|
+
"README.md": "098773e588d568025e90b7aaa716879ddb7e8e2e87492fe86042fce856f66520",
|
|
8
|
+
"dist/magnitude.js": "968f6bf0f605a4500cab5e4852edb5f8c67d1d4dd16d0574e13f1ac922d93522",
|
|
9
|
+
"dist/skills/magnitude/SKILL.md": "37de67f0c7b831415f6298c8785b7ae97d5cfe454dc778651d96114840d2ace2"
|
|
9
10
|
}
|
|
10
11
|
}
|
package/dist/magnitude.js
CHANGED
|
@@ -3364,13 +3364,14 @@ var makeClient = (options) => Effect6.gen(function* () {
|
|
|
3364
3364
|
yield* Deferred2.fail(active, new ConnectionClosed({}));
|
|
3365
3365
|
})).pipe(Effect6.zipRight(Scope2.close(scope, Exit.void))));
|
|
3366
3366
|
const unavailable = (message) => new ServiceUnavailable({ origin, message });
|
|
3367
|
+
const decodeHealth = Schema47.decodeUnknown(Schema47.Union(MagnitudeHealthResponseSchema, AcnHealthResponseSchema));
|
|
3367
3368
|
const probe = http.get(`${origin}/health`).pipe(Effect6.timeoutFail({
|
|
3368
3369
|
duration: "2 seconds",
|
|
3369
3370
|
onTimeout: () => unavailable("Magnitude service health timed out")
|
|
3370
3371
|
}), Effect6.mapError((error) => error instanceof ServiceUnavailable ? error : unavailable("Magnitude service is unavailable")), Effect6.flatMap((response) => response.status !== 200 && response.status !== 503 ? Effect6.fail(new InvalidServiceResponse({
|
|
3371
3372
|
origin,
|
|
3372
3373
|
message: `Health returned HTTP ${response.status}`
|
|
3373
|
-
})) : response.json.pipe(Effect6.flatMap(
|
|
3374
|
+
})) : response.json.pipe(Effect6.flatMap(decodeHealth), Effect6.map((health) => ("rpcVersion" in health) ? health : { ...health, rpcVersion: 0 }), Effect6.mapError(() => new InvalidServiceResponse({
|
|
3374
3375
|
origin,
|
|
3375
3376
|
message: "Invalid Magnitude health response"
|
|
3376
3377
|
})))));
|
|
@@ -3391,36 +3392,35 @@ var makeClient = (options) => Effect6.gen(function* () {
|
|
|
3391
3392
|
version: health.version,
|
|
3392
3393
|
rpcVersion: health.rpcVersion
|
|
3393
3394
|
});
|
|
3395
|
+
const startService = (start2) => Effect6.scoped(Effect6.gen(function* () {
|
|
3396
|
+
yield* report({ _tag: "Starting", phase: "PreparingAcn" });
|
|
3397
|
+
yield* probe.pipe(Effect6.flatMap((health) => Option13.match(serviceProgressFromHealth(health.state), {
|
|
3398
|
+
onNone: () => Effect6.void,
|
|
3399
|
+
onSome: report
|
|
3400
|
+
})), Effect6.ignore, Effect6.zipRight(Effect6.sleep("250 millis")), Effect6.forever, Effect6.forkScoped);
|
|
3401
|
+
yield* start2.pipe(Stream4.runForEach(report));
|
|
3402
|
+
}));
|
|
3394
3403
|
const acquire = Effect6.gen(function* () {
|
|
3395
3404
|
let started = false;
|
|
3396
3405
|
while (true) {
|
|
3397
|
-
const observed = yield* Effect6.either(probe);
|
|
3398
|
-
if (observed._tag === "
|
|
3399
|
-
|
|
3400
|
-
return yield* observed.left;
|
|
3401
|
-
if (!started && Option13.isSome(starter)) {
|
|
3402
|
-
started = true;
|
|
3403
|
-
yield* report({ _tag: "Starting", phase: "PreparingAcn" });
|
|
3404
|
-
yield* Effect6.scoped(Effect6.gen(function* () {
|
|
3405
|
-
yield* probe.pipe(Effect6.flatMap((health) => Option13.match(serviceProgressFromHealth(health.state), {
|
|
3406
|
-
onNone: () => Effect6.void,
|
|
3407
|
-
onSome: report
|
|
3408
|
-
})), Effect6.ignore, Effect6.zipRight(Effect6.sleep("250 millis")), Effect6.forever, Effect6.forkScoped);
|
|
3409
|
-
yield* starter.value.start.pipe(Stream4.runForEach(report));
|
|
3410
|
-
}));
|
|
3411
|
-
continue;
|
|
3412
|
-
}
|
|
3413
|
-
if (!started)
|
|
3414
|
-
return yield* observed.left;
|
|
3415
|
-
} else {
|
|
3416
|
-
const health = observed.right;
|
|
3417
|
-
const service = yield* validate(health);
|
|
3406
|
+
const observed = yield* Effect6.either(probe.pipe(Effect6.flatMap((health) => validate(health).pipe(Effect6.map((service) => ({ health, service }))))));
|
|
3407
|
+
if (observed._tag === "Right") {
|
|
3408
|
+
const { health, service } = observed.right;
|
|
3418
3409
|
if (health.state._tag === "Ready")
|
|
3419
3410
|
return service;
|
|
3420
3411
|
yield* Option13.match(serviceProgressFromHealth(health.state), {
|
|
3421
3412
|
onNone: () => Effect6.void,
|
|
3422
3413
|
onSome: report
|
|
3423
3414
|
});
|
|
3415
|
+
} else if (started) {
|
|
3416
|
+
if (observed.left._tag !== "ServiceUnavailable")
|
|
3417
|
+
return yield* observed.left;
|
|
3418
|
+
} else if (Option13.isSome(starter)) {
|
|
3419
|
+
started = true;
|
|
3420
|
+
yield* startService(starter.value.start);
|
|
3421
|
+
continue;
|
|
3422
|
+
} else {
|
|
3423
|
+
return yield* observed.left;
|
|
3424
3424
|
}
|
|
3425
3425
|
yield* Effect6.sleep("250 millis");
|
|
3426
3426
|
}
|
|
@@ -5841,8 +5841,73 @@ var registerMagnitudeCommands = (pi, sdk = clientLayer2()) => {
|
|
|
5841
5841
|
};
|
|
5842
5842
|
};
|
|
5843
5843
|
|
|
5844
|
+
// extensions/onboarding.ts
|
|
5845
|
+
import * as FileSystem from "@effect/platform/FileSystem";
|
|
5846
|
+
import * as NodeFileSystem2 from "@effect/platform-node/NodeFileSystem";
|
|
5847
|
+
import { getAgentDir, parseArgs } from "@earendil-works/pi-coding-agent";
|
|
5848
|
+
import { Effect as Effect11, Exit as Exit3, Scope as Scope4 } from "effect";
|
|
5849
|
+
import { fileURLToPath } from "node:url";
|
|
5850
|
+
var SETUP_PROMPT = "Set up local models for me with the Magnitude CLI. Install it with `npm i -g @magnitudedev/cli` (or my package manager), then run `magnitude docs onboarding` and follow the instructions.";
|
|
5851
|
+
var SETUP_QUESTION = "Set up local models with Magnitude now?";
|
|
5852
|
+
var SETUP_REMINDER = "You can set up local models anytime with `/magnitude-setup`.";
|
|
5853
|
+
var claimSetupOffer = (agentDir) => Effect11.gen(function* () {
|
|
5854
|
+
const fs = yield* FileSystem.FileSystem;
|
|
5855
|
+
const directory = `${agentDir}/magnitude`;
|
|
5856
|
+
yield* fs.makeDirectory(directory, { recursive: true });
|
|
5857
|
+
return yield* fs.writeFileString(`${directory}/setup-offered`, "", { flag: "wx" }).pipe(Effect11.as(true), Effect11.catchTag("SystemError", (error) => error.reason === "AlreadyExists" ? Effect11.succeed(false) : Effect11.fail(error)));
|
|
5858
|
+
});
|
|
5859
|
+
function canOfferSetup(ctx, argv) {
|
|
5860
|
+
if (ctx.mode !== "tui" || !ctx.isIdle() || ctx.hasPendingMessages() || ctx.ui.getEditorText().trim())
|
|
5861
|
+
return false;
|
|
5862
|
+
if (ctx.sessionManager.getEntries().some((entry) => entry.type === "message"))
|
|
5863
|
+
return false;
|
|
5864
|
+
if (ctx.modelRegistry.getAll().some((model2) => model2.provider === "magnitude"))
|
|
5865
|
+
return false;
|
|
5866
|
+
const args = parseArgs([...argv]);
|
|
5867
|
+
return args.messages.length === 0 && args.fileArgs.length === 0;
|
|
5868
|
+
}
|
|
5869
|
+
var offerSetup = (pi, ctx, agentDir, signal) => Effect11.gen(function* () {
|
|
5870
|
+
if (!(yield* claimSetupOffer(agentDir)) || signal.aborted)
|
|
5871
|
+
return;
|
|
5872
|
+
const accepted = yield* Effect11.tryPromise(() => ctx.ui.confirm(SETUP_QUESTION, "", { signal }));
|
|
5873
|
+
if (signal.aborted)
|
|
5874
|
+
return;
|
|
5875
|
+
if (accepted && ctx.isIdle() && !ctx.hasPendingMessages())
|
|
5876
|
+
pi.sendUserMessage(SETUP_PROMPT);
|
|
5877
|
+
else
|
|
5878
|
+
pi.sendMessage({ customType: "magnitude-setup", content: SETUP_REMINDER, display: true });
|
|
5879
|
+
});
|
|
5880
|
+
function registerMagnitudeOnboarding(pi) {
|
|
5881
|
+
const abort = new AbortController;
|
|
5882
|
+
const scope = Effect11.runSync(Scope4.make());
|
|
5883
|
+
pi.registerCommand("magnitude-setup", {
|
|
5884
|
+
description: "Set up local models with Magnitude",
|
|
5885
|
+
handler: async (_args, ctx) => {
|
|
5886
|
+
if (!ctx.isIdle() || ctx.hasPendingMessages()) {
|
|
5887
|
+
ctx.ui.notify("Wait for the current task to finish, then run /magnitude-setup.", "warning");
|
|
5888
|
+
return;
|
|
5889
|
+
}
|
|
5890
|
+
pi.sendUserMessage(SETUP_PROMPT);
|
|
5891
|
+
}
|
|
5892
|
+
});
|
|
5893
|
+
pi.on("resources_discover", () => {
|
|
5894
|
+
if (parseArgs(process.argv.slice(2)).noSkills || pi.getCommands().some((command) => command.source === "skill" && command.name === "skill:magnitude"))
|
|
5895
|
+
return;
|
|
5896
|
+
return { skillPaths: [fileURLToPath(new URL("./skills/magnitude/SKILL.md", import.meta.url))] };
|
|
5897
|
+
});
|
|
5898
|
+
pi.on("session_start", (event, ctx) => {
|
|
5899
|
+
if (event.reason !== "startup" || !canOfferSetup(ctx, process.argv.slice(2)))
|
|
5900
|
+
return;
|
|
5901
|
+
Effect11.runFork(Effect11.forkIn(offerSetup(pi, ctx, getAgentDir(), abort.signal).pipe(Effect11.provide(NodeFileSystem2.layer), Effect11.catchAll(() => Effect11.sync(() => ctx.ui.notify(`Magnitude setup could not open. ${SETUP_REMINDER}`, "warning")))), scope));
|
|
5902
|
+
});
|
|
5903
|
+
return () => {
|
|
5904
|
+
abort.abort();
|
|
5905
|
+
return Effect11.runPromise(Scope4.close(scope, Exit3.void));
|
|
5906
|
+
};
|
|
5907
|
+
}
|
|
5908
|
+
|
|
5844
5909
|
// extensions/observing-fetch.ts
|
|
5845
|
-
import { Effect as
|
|
5910
|
+
import { Effect as Effect12, Either as Either3, Schema as Schema52 } from "effect";
|
|
5846
5911
|
|
|
5847
5912
|
// extensions/protocol.ts
|
|
5848
5913
|
import { Either as Either2, Schema as Schema51 } from "effect";
|
|
@@ -5913,21 +5978,21 @@ class SseDataParser {
|
|
|
5913
5978
|
observe(decoded.right);
|
|
5914
5979
|
}
|
|
5915
5980
|
}
|
|
5916
|
-
var observeResponse = (response, request, signal) =>
|
|
5981
|
+
var observeResponse = (response, request, signal) => Effect12.scoped(Effect12.gen(function* () {
|
|
5917
5982
|
if (!response.ok || response.body === null) {
|
|
5918
5983
|
yield* request.fail;
|
|
5919
5984
|
return;
|
|
5920
5985
|
}
|
|
5921
|
-
const reader = yield*
|
|
5986
|
+
const reader = yield* Effect12.acquireRelease(Effect12.sync(() => response.body.getReader()), (reader2) => Effect12.promise(() => reader2.cancel()).pipe(Effect12.interruptible, Effect12.timeout("100 millis"), Effect12.ignore, Effect12.ensuring(Effect12.sync(() => reader2.releaseLock()))));
|
|
5922
5987
|
if (signal.aborted) {
|
|
5923
5988
|
yield* request.fail;
|
|
5924
5989
|
return;
|
|
5925
5990
|
}
|
|
5926
5991
|
const decoder = new TextDecoder;
|
|
5927
5992
|
const parser = new SseDataParser;
|
|
5928
|
-
const drain =
|
|
5993
|
+
const drain = Effect12.gen(function* () {
|
|
5929
5994
|
while (true) {
|
|
5930
|
-
const result = yield*
|
|
5995
|
+
const result = yield* Effect12.tryPromise(() => reader.read());
|
|
5931
5996
|
if (result.done)
|
|
5932
5997
|
break;
|
|
5933
5998
|
const values = [];
|
|
@@ -5940,35 +6005,35 @@ var observeResponse = (response, request, signal) => Effect11.scoped(Effect11.ge
|
|
|
5940
6005
|
}
|
|
5941
6006
|
yield* request.finish;
|
|
5942
6007
|
});
|
|
5943
|
-
const aborted =
|
|
5944
|
-
const abort = () => resume(
|
|
6008
|
+
const aborted = Effect12.async((resume) => {
|
|
6009
|
+
const abort = () => resume(Effect12.interrupt);
|
|
5945
6010
|
signal.addEventListener("abort", abort, { once: true });
|
|
5946
6011
|
if (signal.aborted)
|
|
5947
6012
|
abort();
|
|
5948
|
-
return
|
|
6013
|
+
return Effect12.sync(() => signal.removeEventListener("abort", abort));
|
|
5949
6014
|
});
|
|
5950
|
-
yield*
|
|
5951
|
-
})).pipe(
|
|
6015
|
+
yield* Effect12.raceFirst(drain, aborted).pipe(Effect12.onError(() => request.fail));
|
|
6016
|
+
})).pipe(Effect12.catchAllCause(() => Effect12.void));
|
|
5952
6017
|
var makeObservingFetch = (fetchImplementation, begin, scope) => {
|
|
5953
6018
|
const observingFetch = async (input, init) => {
|
|
5954
6019
|
const request = new Request(input, init);
|
|
5955
6020
|
request.headers.set(MAGNITUDE_PROGRESS_HEADER, "true");
|
|
5956
|
-
const progress =
|
|
5957
|
-
observe: () =>
|
|
5958
|
-
finish:
|
|
5959
|
-
fail:
|
|
6021
|
+
const progress = Effect12.runSync(Effect12.suspend(begin).pipe(Effect12.catchAllCause(() => Effect12.succeed({
|
|
6022
|
+
observe: () => Effect12.void,
|
|
6023
|
+
finish: Effect12.void,
|
|
6024
|
+
fail: Effect12.void
|
|
5960
6025
|
}))));
|
|
5961
6026
|
try {
|
|
5962
6027
|
const response = await fetchImplementation(request);
|
|
5963
6028
|
try {
|
|
5964
6029
|
const observation = observeResponse(response.clone(), progress, request.signal);
|
|
5965
|
-
|
|
6030
|
+
Effect12.runFork(Effect12.forkIn(observation, scope));
|
|
5966
6031
|
} catch {
|
|
5967
|
-
|
|
6032
|
+
Effect12.runSync(progress.fail.pipe(Effect12.catchAllCause(() => Effect12.void)));
|
|
5968
6033
|
}
|
|
5969
6034
|
return response;
|
|
5970
6035
|
} catch (error) {
|
|
5971
|
-
|
|
6036
|
+
Effect12.runSync(progress.fail.pipe(Effect12.catchAllCause(() => Effect12.void)));
|
|
5972
6037
|
throw error;
|
|
5973
6038
|
}
|
|
5974
6039
|
};
|
|
@@ -5979,7 +6044,7 @@ var makeObservingFetch = (fetchImplementation, begin, scope) => {
|
|
|
5979
6044
|
|
|
5980
6045
|
// extensions/progress.ts
|
|
5981
6046
|
import { Text } from "@earendil-works/pi-tui";
|
|
5982
|
-
import { Data as Data3, Effect as
|
|
6047
|
+
import { Data as Data3, Effect as Effect13, Fiber as Fiber2, Option as Option17, Queue, Schema as Schema53 } from "effect";
|
|
5983
6048
|
var MAGNITUDE_SUMMARY_WIDGET_KEY = "magnitude-inference-summary";
|
|
5984
6049
|
var RequestId = Schema53.Number.pipe(Schema53.int(), Schema53.brand("ProgressRequestId"));
|
|
5985
6050
|
|
|
@@ -6036,21 +6101,21 @@ var formatLiveProgress = ({ progress, modelName, startedAt }, now) => {
|
|
|
6036
6101
|
}
|
|
6037
6102
|
}
|
|
6038
6103
|
};
|
|
6039
|
-
var makeProgressTracker = (ui, now = () => performance.now()) =>
|
|
6104
|
+
var makeProgressTracker = (ui, now = () => performance.now()) => Effect13.gen(function* () {
|
|
6040
6105
|
let state = new Idle2;
|
|
6041
6106
|
let nextId = 0;
|
|
6042
6107
|
let active;
|
|
6043
6108
|
let latest;
|
|
6044
6109
|
let nextResponseId = 0;
|
|
6045
6110
|
const wake = yield* Queue.sliding(1);
|
|
6046
|
-
const present = (f) =>
|
|
6111
|
+
const present = (f) => Effect13.sync(f).pipe(Effect13.catchAllCause(() => Effect13.void));
|
|
6047
6112
|
const clearSummary = present(() => ui.setWidget(MAGNITUDE_SUMMARY_WIDGET_KEY, undefined));
|
|
6048
6113
|
const resetRow = present(() => ui.setWorkingMessage());
|
|
6049
6114
|
const render2 = present(() => {
|
|
6050
6115
|
if (active)
|
|
6051
6116
|
ui.setWorkingMessage(formatLiveProgress(active.phase, now()));
|
|
6052
6117
|
});
|
|
6053
|
-
const finalize =
|
|
6118
|
+
const finalize = Effect13.gen(function* () {
|
|
6054
6119
|
if (state._tag !== "Settled")
|
|
6055
6120
|
return;
|
|
6056
6121
|
if ([...state.run.responses.values()].some(Option17.isNone))
|
|
@@ -6069,7 +6134,7 @@ var makeProgressTracker = (ui, now = () => performance.now()) => Effect12.gen(fu
|
|
|
6069
6134
|
const summary = `● ${settled.run.modelName} worked for ${duration(settled.settledAt - settled.run.startedAt)}` + ` · ${seconds(timings[0].time_to_first_token_ms)} TTFT` + (decodeMs > 0 && tokens > 0 ? ` · ${(tokens * 1000 / decodeMs).toFixed(1)} tok/s` : "");
|
|
6070
6135
|
yield* present(() => ui.setWidget(MAGNITUDE_SUMMARY_WIDGET_KEY, (_tui, theme) => new Text(theme.fg("muted", summary), 0, 0)));
|
|
6071
6136
|
});
|
|
6072
|
-
const clear =
|
|
6137
|
+
const clear = Effect13.gen(function* () {
|
|
6073
6138
|
if (state._tag === "Disposed")
|
|
6074
6139
|
return;
|
|
6075
6140
|
if (state._tag !== "Idle")
|
|
@@ -6079,7 +6144,7 @@ var makeProgressTracker = (ui, now = () => performance.now()) => Effect12.gen(fu
|
|
|
6079
6144
|
yield* resetRow;
|
|
6080
6145
|
yield* clearSummary;
|
|
6081
6146
|
});
|
|
6082
|
-
const startRun = (modelName) =>
|
|
6147
|
+
const startRun = (modelName) => Effect13.gen(function* () {
|
|
6083
6148
|
if (state._tag === "Disposed")
|
|
6084
6149
|
return;
|
|
6085
6150
|
if (state._tag === "Settled")
|
|
@@ -6089,14 +6154,14 @@ var makeProgressTracker = (ui, now = () => performance.now()) => Effect12.gen(fu
|
|
|
6089
6154
|
state = runMachine.transition(state, "Working", { run: { startedAt: now(), modelName, requests: new Map, completed: new Map, responses: new Map, accepted: new Set } });
|
|
6090
6155
|
yield* clearSummary;
|
|
6091
6156
|
});
|
|
6092
|
-
const timer = yield*
|
|
6157
|
+
const timer = yield* Effect13.forever(Effect13.gen(function* () {
|
|
6093
6158
|
yield* Queue.take(wake);
|
|
6094
6159
|
while (active) {
|
|
6095
|
-
yield*
|
|
6160
|
+
yield* Effect13.sleep("100 millis");
|
|
6096
6161
|
yield* render2;
|
|
6097
6162
|
}
|
|
6098
|
-
})).pipe(
|
|
6099
|
-
yield*
|
|
6163
|
+
})).pipe(Effect13.forkScoped);
|
|
6164
|
+
yield* Effect13.addFinalizer(() => Effect13.gen(function* () {
|
|
6100
6165
|
yield* clear;
|
|
6101
6166
|
if (state._tag !== "Disposed")
|
|
6102
6167
|
state = runMachine.transition(state, "Disposed", {});
|
|
@@ -6105,19 +6170,19 @@ var makeProgressTracker = (ui, now = () => performance.now()) => Effect12.gen(fu
|
|
|
6105
6170
|
return {
|
|
6106
6171
|
startRun,
|
|
6107
6172
|
clear,
|
|
6108
|
-
settleRun:
|
|
6173
|
+
settleRun: Effect13.gen(function* () {
|
|
6109
6174
|
if (state._tag === "Working")
|
|
6110
6175
|
state = runMachine.transition(state, "Settled", { settledAt: now() });
|
|
6111
6176
|
yield* finalize;
|
|
6112
6177
|
}),
|
|
6113
|
-
beginResponse: (modelName) =>
|
|
6178
|
+
beginResponse: (modelName) => Effect13.gen(function* () {
|
|
6114
6179
|
yield* startRun(modelName);
|
|
6115
6180
|
const run = state._tag === "Working" ? state.run : undefined;
|
|
6116
6181
|
const responseId = ++nextResponseId;
|
|
6117
6182
|
const requests = [];
|
|
6118
6183
|
run?.responses.set(responseId, Option17.none());
|
|
6119
6184
|
const belongs = () => run !== undefined && (state._tag === "Working" || state._tag === "Settled") && state.run === run;
|
|
6120
|
-
const end = (successful) =>
|
|
6185
|
+
const end = (successful) => Effect13.gen(function* () {
|
|
6121
6186
|
if (!belongs() || Option17.isSome(run.responses.get(responseId)))
|
|
6122
6187
|
return;
|
|
6123
6188
|
run.responses.set(responseId, Option17.some(successful));
|
|
@@ -6139,16 +6204,16 @@ var makeProgressTracker = (ui, now = () => performance.now()) => Effect12.gen(fu
|
|
|
6139
6204
|
}
|
|
6140
6205
|
yield* finalize;
|
|
6141
6206
|
});
|
|
6142
|
-
const begin =
|
|
6207
|
+
const begin = Effect13.gen(function* () {
|
|
6143
6208
|
if (!belongs() || Option17.isSome(run.responses.get(responseId)))
|
|
6144
|
-
return { observe: () =>
|
|
6209
|
+
return { observe: () => Effect13.void, finish: Effect13.void, fail: Effect13.void };
|
|
6145
6210
|
const id = RequestId.make(++nextId);
|
|
6146
6211
|
requests.push(id);
|
|
6147
6212
|
latest = id;
|
|
6148
6213
|
active = undefined;
|
|
6149
6214
|
run?.requests.set(id, new Observing({ timings: Option17.none() }));
|
|
6150
6215
|
yield* resetRow;
|
|
6151
|
-
const close = (failed) =>
|
|
6216
|
+
const close = (failed) => Effect13.gen(function* () {
|
|
6152
6217
|
if (!belongs())
|
|
6153
6218
|
return;
|
|
6154
6219
|
const request = run.requests.get(id);
|
|
@@ -6168,7 +6233,7 @@ var makeProgressTracker = (ui, now = () => performance.now()) => Effect12.gen(fu
|
|
|
6168
6233
|
yield* finalize;
|
|
6169
6234
|
});
|
|
6170
6235
|
return {
|
|
6171
|
-
observe: (observation) =>
|
|
6236
|
+
observe: (observation) => Effect13.gen(function* () {
|
|
6172
6237
|
if (!belongs())
|
|
6173
6238
|
return;
|
|
6174
6239
|
const request = run.requests.get(id);
|
|
@@ -6199,34 +6264,35 @@ var makeProgressTracker = (ui, now = () => performance.now()) => Effect12.gen(fu
|
|
|
6199
6264
|
});
|
|
6200
6265
|
|
|
6201
6266
|
// extensions/magnitude.ts
|
|
6202
|
-
import { Effect as
|
|
6267
|
+
import { Effect as Effect14, Exit as Exit4, Scope as Scope6 } from "effect";
|
|
6203
6268
|
function magnitudeExtension(pi) {
|
|
6204
6269
|
const completions = openAICompletionsApi();
|
|
6205
6270
|
let tracker;
|
|
6206
6271
|
let scope;
|
|
6207
6272
|
const disposeCommands = registerMagnitudeCommands(pi);
|
|
6208
|
-
const
|
|
6273
|
+
const disposeOnboarding = registerMagnitudeOnboarding(pi);
|
|
6274
|
+
const perform = (effect) => Effect14.runSync((effect ?? Effect14.void).pipe(Effect14.catchAllCause(() => Effect14.void)));
|
|
6209
6275
|
pi.registerProvider("magnitude", {
|
|
6210
6276
|
api: "openai-completions",
|
|
6211
6277
|
streamSimple: (model2, context, options) => {
|
|
6212
6278
|
if (model2.api !== "openai-completions") {
|
|
6213
6279
|
throw new Error(`Magnitude for Pi requires openai-completions, received ${model2.api}`);
|
|
6214
6280
|
}
|
|
6215
|
-
const response = tracker &&
|
|
6281
|
+
const response = tracker && Effect14.runSync(tracker.beginResponse(model2.name));
|
|
6216
6282
|
const stream = completions.streamSimple(model2, context, {
|
|
6217
6283
|
...options,
|
|
6218
|
-
fetch: scope ? makeObservingFetch(options?.fetch ?? globalThis.fetch, () => response?.begin ??
|
|
6284
|
+
fetch: scope ? makeObservingFetch(options?.fetch ?? globalThis.fetch, () => response?.begin ?? Effect14.succeed({ observe: () => Effect14.void, finish: Effect14.void, fail: Effect14.void }), scope) : options?.fetch ?? globalThis.fetch
|
|
6219
6285
|
});
|
|
6220
6286
|
if (response && scope)
|
|
6221
|
-
|
|
6287
|
+
Effect14.runFork(Effect14.forkIn(Effect14.tryPromise(() => stream.result()).pipe(Effect14.flatMap((message) => response.end(message.stopReason !== "error" && message.stopReason !== "aborted")), Effect14.catchAllCause(() => response.end(false))), scope));
|
|
6222
6288
|
return stream;
|
|
6223
6289
|
}
|
|
6224
6290
|
});
|
|
6225
6291
|
pi.on("session_start", async (_event, ctx) => {
|
|
6226
6292
|
if (scope)
|
|
6227
|
-
await
|
|
6228
|
-
scope =
|
|
6229
|
-
tracker = await
|
|
6293
|
+
await Effect14.runPromise(Scope6.close(scope, Exit4.void));
|
|
6294
|
+
scope = Effect14.runSync(Scope6.make());
|
|
6295
|
+
tracker = await Effect14.runPromise(makeProgressTracker(ctx.ui).pipe(Scope6.extend(scope)));
|
|
6230
6296
|
});
|
|
6231
6297
|
pi.on("model_select", (event) => {
|
|
6232
6298
|
if (event.model.provider !== "magnitude")
|
|
@@ -6238,8 +6304,9 @@ function magnitudeExtension(pi) {
|
|
|
6238
6304
|
});
|
|
6239
6305
|
pi.on("agent_settled", () => perform(tracker?.settleRun));
|
|
6240
6306
|
pi.on("session_shutdown", async () => {
|
|
6307
|
+
await disposeOnboarding();
|
|
6241
6308
|
if (scope)
|
|
6242
|
-
await
|
|
6309
|
+
await Effect14.runPromise(Scope6.close(scope, Exit4.void));
|
|
6243
6310
|
tracker = undefined;
|
|
6244
6311
|
scope = undefined;
|
|
6245
6312
|
await disposeCommands();
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: magnitude
|
|
3
|
+
description: Set up and operate Magnitude local inference through its CLI, recommend hardware-fit local models, monitor acquisition and loading, and connect an agent harness. Use for Magnitude service, model, catalog, setup, or harness requests.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Magnitude
|
|
7
|
+
|
|
8
|
+
Magnitude profiles the local machine, assesses compatible model configurations, acquires and runs
|
|
9
|
+
selected models, and connects them to supported agent harnesses.
|
|
10
|
+
|
|
11
|
+
For agent-guided setup, read `magnitude docs onboarding` completely and follow it. It contains the
|
|
12
|
+
current CLI-only workflow, model-selection evidence, progress semantics, and harness connection
|
|
13
|
+
rules.
|
|
14
|
+
|
|
15
|
+
For the general non-interactive command contract, read `magnitude docs cli`. Command output is
|
|
16
|
+
designed to be read directly by both agents and people.
|