auto-model-router 0.6.2 → 0.6.3
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.
|
@@ -26,16 +26,21 @@ jobs:
|
|
|
26
26
|
- name: Install dependencies
|
|
27
27
|
run: npm install
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
# Publish only when the tagged version is NEWER than npm's latest. A tag that
|
|
30
|
+
# reaches GitHub late (git push --tags after the fact) must never publish an
|
|
31
|
+
# old version as a fresh release.
|
|
32
|
+
- name: Check if version is newer than the published one
|
|
30
33
|
id: version
|
|
31
34
|
run: |
|
|
32
35
|
LOCAL=$(node -p "require('./package.json').version")
|
|
33
36
|
PUBLISHED=$(npm view auto-model-router version 2>/dev/null || echo "0.0.0")
|
|
34
37
|
echo "local=$LOCAL published=$PUBLISHED"
|
|
35
|
-
|
|
38
|
+
NEWEST=$(printf '%s\n' "$PUBLISHED" "$LOCAL" | sort -V | tail -1)
|
|
39
|
+
if [ "$LOCAL" != "$PUBLISHED" ] && [ "$NEWEST" = "$LOCAL" ]; then
|
|
36
40
|
echo "changed=true" >> "$GITHUB_OUTPUT"
|
|
37
41
|
else
|
|
38
42
|
echo "changed=false" >> "$GITHUB_OUTPUT"
|
|
43
|
+
echo "::warning::$LOCAL is not newer than the published $PUBLISHED; nothing published"
|
|
39
44
|
fi
|
|
40
45
|
|
|
41
46
|
- name: Publish to npm
|
|
@@ -45,6 +50,7 @@ jobs:
|
|
|
45
50
|
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
46
51
|
|
|
47
52
|
- name: Create GitHub Release
|
|
53
|
+
if: steps.version.outputs.changed == 'true'
|
|
48
54
|
uses: softprops/action-gh-release@v2
|
|
49
55
|
with:
|
|
50
56
|
generate_release_notes: true
|
|
@@ -7,14 +7,14 @@
|
|
|
7
7
|
},
|
|
8
8
|
"metadata": {
|
|
9
9
|
"description": "auto-model-router: a local cost/complexity-aware model router for Oh My Pi, backed by OpenRouter",
|
|
10
|
-
"version": "0.6.
|
|
10
|
+
"version": "0.6.3",
|
|
11
11
|
"pluginRoot": "."
|
|
12
12
|
},
|
|
13
13
|
"plugins": [
|
|
14
14
|
{
|
|
15
15
|
"name": "auto-model-router",
|
|
16
16
|
"description": "Local cost/complexity-aware model router for Oh My Pi, backed by OpenRouter. Runs in-process, routes per turn by price and task complexity, with budget caps, mid-stream escalation, and cache-aware hysteresis.",
|
|
17
|
-
"version": "0.6.
|
|
17
|
+
"version": "0.6.3",
|
|
18
18
|
"author": {
|
|
19
19
|
"name": "drewappling",
|
|
20
20
|
"email": "drewappling@gmail.com"
|
package/README.md
CHANGED
|
@@ -1267,6 +1267,11 @@ adds the Codex provider and the Aider settings, and prints (or with `--profile`
|
|
|
1267
1267
|
the environment lines for Claude Code. `--harness omp,hermes` restricts it; `--dry-run`
|
|
1268
1268
|
shows the changes. Delete `remote.json` to go back to a local router. (`join` is an alias.)
|
|
1269
1269
|
|
|
1270
|
+
In remote mode omp sends `X-Agentdox-Scope` derived from the workspace folder, so one
|
|
1271
|
+
remote router serves every repo on the machine with that repo's shared context. The remote
|
|
1272
|
+
decides what to do with it: a team edition that pins a scope on the member's group
|
|
1273
|
+
overrides it, and one that pins none follows the workspace.
|
|
1274
|
+
|
|
1270
1275
|
## Multiple coding harnesses, one router
|
|
1271
1276
|
|
|
1272
1277
|
**One router process for everything.** omp's embed extension binds a private
|
|
@@ -63,7 +63,7 @@ export const REMOTE_MODELS: readonly { id: string; name: string }[] = [
|
|
|
63
63
|
* the session and subagent tags, and the virtual models. Costs are USD per
|
|
64
64
|
* million tokens, like the embedded config.
|
|
65
65
|
*/
|
|
66
|
-
export function remoteProviderRegistration(remote: RemoteRouter, sessionId: string, subagent: boolean, blend: { inputPerMtok: number; outputPerMtok: number }): {
|
|
66
|
+
export function remoteProviderRegistration(remote: RemoteRouter, sessionId: string, subagent: boolean, blend: { inputPerMtok: number; outputPerMtok: number }, agentdoxScope = ""): {
|
|
67
67
|
baseUrl: string;
|
|
68
68
|
api: string;
|
|
69
69
|
apiKey: string;
|
|
@@ -73,6 +73,10 @@ export function remoteProviderRegistration(remote: RemoteRouter, sessionId: stri
|
|
|
73
73
|
const headers: Record<string, string> = {};
|
|
74
74
|
if (sessionId !== "") headers["X-Omp-Session"] = sessionId;
|
|
75
75
|
if (subagent) headers["X-Omp-Subagent"] = "1";
|
|
76
|
+
// Which project's shared context this workspace draws on. The bridge lives on the remote
|
|
77
|
+
// router, so this is sent whatever the local config says; the remote decides what to do with
|
|
78
|
+
// it (a team that pins a scope for the group overrides it, and one that pins none follows it).
|
|
79
|
+
if (agentdoxScope !== "") headers["X-Agentdox-Scope"] = agentdoxScope;
|
|
76
80
|
const round = (v: number): number => Math.round(v * 1e4) / 1e4;
|
|
77
81
|
return {
|
|
78
82
|
baseUrl: `${remote.url}/v1`,
|
|
@@ -32,17 +32,7 @@ import type { RouterConfig } from "../src/config/types.ts";
|
|
|
32
32
|
|
|
33
33
|
import type { ExtensionAPI } from "@oh-my-pi/pi-coding-agent";
|
|
34
34
|
|
|
35
|
-
import {
|
|
36
|
-
buildProviderConfig,
|
|
37
|
-
EMBED_DUMMY_API_KEY,
|
|
38
|
-
EMBED_PROVIDER_ID,
|
|
39
|
-
embedPortPath,
|
|
40
|
-
readEmbedPort,
|
|
41
|
-
modelsYmlPort,
|
|
42
|
-
probeEmbed,
|
|
43
|
-
resolveEmbedPort,
|
|
44
|
-
writeEmbedPort,
|
|
45
|
-
} from "./embed-logic.ts";
|
|
35
|
+
import { EMBED_DUMMY_API_KEY, EMBED_PROVIDER_ID, buildProviderConfig, deriveAgentdoxScope, embedPortPath, modelsYmlPort, probeEmbed, readEmbedPort, resolveEmbedPort, writeEmbedPort } from "./embed-logic.ts";
|
|
46
36
|
|
|
47
37
|
/** omp's models.yml as text, or "" when it does not exist / cannot be read. */
|
|
48
38
|
function readModelsYml(): string {
|
|
@@ -173,7 +163,7 @@ export default function (pi: ExtensionAPI): void {
|
|
|
173
163
|
// locally; the other extensions find it through remote.json.
|
|
174
164
|
const remote = readRemoteRouter(routerHome());
|
|
175
165
|
if (remote !== null) {
|
|
176
|
-
pi.registerProvider(EMBED_PROVIDER_ID, remoteProviderRegistration(remote, sessionId, !ctx.hasUI, cfg.ledger.fallbackBlend));
|
|
166
|
+
pi.registerProvider(EMBED_PROVIDER_ID, remoteProviderRegistration(remote, sessionId, !ctx.hasUI, cfg.ledger.fallbackBlend, deriveAgentdoxScope(process.cwd())));
|
|
177
167
|
pi.setLabel(`auto-model-router remote (${remote.url.replace(/^https?:\/\//, "")})`);
|
|
178
168
|
writeEmbedLog(`remote mode url=${remote.url} user=${remote.userId} session=${sessionId}`);
|
|
179
169
|
return;
|
package/package.json
CHANGED
package/test/remote.test.ts
CHANGED
|
@@ -22,6 +22,10 @@ describe("remote-logic", () => {
|
|
|
22
22
|
expect(reg).toMatchObject({ baseUrl: "https://team.example/v1", api: "openai-completions", apiKey: "amrt_k", headers: { "X-Omp-Session": "sess-1", "X-Omp-Subagent": "1" } });
|
|
23
23
|
expect(reg.models.map((m) => m.id)).toEqual(["auto", "auto-cheap", "auto-max"]);
|
|
24
24
|
expect(reg.models[0]!.cost).toEqual({ input: 1, output: 4, cacheRead: 0.1, cacheWrite: 1.25 });
|
|
25
|
+
// The workspace's project travels with the turn, so one remote router serves every repo on
|
|
26
|
+
// the machine with the right context; the remote may still override it.
|
|
27
|
+
expect(reg.headers["X-Agentdox-Scope"]).toBeUndefined();
|
|
28
|
+
expect(remoteProviderRegistration(t, "", false, { inputPerMtok: 1, outputPerMtok: 4 }, "omp-router").headers).toEqual({ "X-Agentdox-Scope": "omp-router" });
|
|
25
29
|
const dir = mkdtempSync(join(tmpdir(), "amr-remote-"));
|
|
26
30
|
expect(readRemoteRouter(dir)).toBeNull();
|
|
27
31
|
writeFileSync(join(dir, "remote.json"), JSON.stringify({ url: "https://t", key: "k" }));
|