@younndai/lyt-llm 0.9.0

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.
@@ -0,0 +1,24 @@
1
+ /*
2
+ * Copyright 2026 MARLINK TRADING SRL (YounndAI)
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ // Adapter barrel — re-export every concrete adapter + its config + the
17
+ // per-adapter error types. Callers (and the brief's gold-standard smoke test
18
+ // from block-B Commit 7) reach the adapters through the package root index
19
+ // for a single import statement.
20
+ export { createAiRelayAdapter, } from "./ai-relay.js";
21
+ export { createOllamaAdapter, OllamaUnreachableError, OllamaHttpError, } from "./ollama.js";
22
+ export { createHarnessAdapter, } from "./harness.js";
23
+ export { createByokAdapter } from "./byok.js";
24
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/adapters/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,uEAAuE;AACvE,6EAA6E;AAC7E,2EAA2E;AAC3E,iCAAiC;AAEjC,OAAO,EACL,oBAAoB,GAIrB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,mBAAmB,EACnB,sBAAsB,EACtB,eAAe,GAGhB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,oBAAoB,GAKrB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,iBAAiB,EAA2C,MAAM,WAAW,CAAC"}
@@ -0,0 +1,28 @@
1
+ import type { LlmAdapter } from "../types.js";
2
+ export type FetchLike = (input: string, init?: {
3
+ method?: string;
4
+ headers?: Record<string, string>;
5
+ body?: string;
6
+ }) => Promise<{
7
+ ok: boolean;
8
+ status: number;
9
+ json(): Promise<unknown>;
10
+ }>;
11
+ export interface OllamaAdapterConfig {
12
+ fetchImpl: FetchLike;
13
+ endpoint?: string;
14
+ defaultModel?: string;
15
+ embedModel?: string;
16
+ liveSmoke?: boolean;
17
+ }
18
+ export declare class OllamaUnreachableError extends Error {
19
+ readonly name = "OllamaUnreachableError";
20
+ constructor(endpoint: string, cause: unknown);
21
+ }
22
+ export declare class OllamaHttpError extends Error {
23
+ readonly status: number;
24
+ readonly name = "OllamaHttpError";
25
+ constructor(status: number, body: unknown);
26
+ }
27
+ export declare function createOllamaAdapter(config: OllamaAdapterConfig): LlmAdapter;
28
+ //# sourceMappingURL=ollama.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ollama.d.ts","sourceRoot":"","sources":["../../src/adapters/ollama.ts"],"names":[],"mappings":"AA6BA,OAAO,KAAK,EAKV,UAAU,EACX,MAAM,aAAa,CAAC;AAErB,MAAM,MAAM,SAAS,GAAG,CACtB,KAAK,EAAE,MAAM,EACb,IAAI,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,KACxE,OAAO,CAAC;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,IAAI,OAAO,CAAC,OAAO,CAAC,CAAA;CAAE,CAAC,CAAC;AAExE,MAAM,WAAW,mBAAmB;IAElC,SAAS,EAAE,SAAS,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IAIpB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,qBAAa,sBAAuB,SAAQ,KAAK;IAC/C,SAAkB,IAAI,4BAA4B;gBACtC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO;CAG7C;AAED,qBAAa,eAAgB,SAAQ,KAAK;aAGtB,MAAM,EAAE,MAAM;IAFhC,SAAkB,IAAI,qBAAqB;gBAEzB,MAAM,EAAE,MAAM,EAC9B,IAAI,EAAE,OAAO;CAIhB;AAaD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,mBAAmB,GAAG,UAAU,CA8E3E"}
@@ -0,0 +1,148 @@
1
+ /*
2
+ * Copyright 2026 MARLINK TRADING SRL (YounndAI)
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ export class OllamaUnreachableError extends Error {
17
+ name = "OllamaUnreachableError";
18
+ constructor(endpoint, cause) {
19
+ super(`Ollama endpoint unreachable at ${endpoint}: ${stringifyCause(cause)}`);
20
+ }
21
+ }
22
+ export class OllamaHttpError extends Error {
23
+ status;
24
+ name = "OllamaHttpError";
25
+ constructor(status, body) {
26
+ super(`Ollama HTTP ${status}: ${stringifyCause(body)}`);
27
+ this.status = status;
28
+ }
29
+ }
30
+ function stringifyCause(cause) {
31
+ if (cause instanceof Error)
32
+ return cause.message;
33
+ try {
34
+ return JSON.stringify(cause);
35
+ }
36
+ catch {
37
+ return String(cause);
38
+ }
39
+ }
40
+ const DEFAULT_ENDPOINT = "http://localhost:11434";
41
+ export function createOllamaAdapter(config) {
42
+ const endpoint = (config.endpoint ?? DEFAULT_ENDPOINT).replace(/\/$/, "");
43
+ async function post(path, body) {
44
+ let res;
45
+ try {
46
+ res = await config.fetchImpl(`${endpoint}${path}`, {
47
+ method: "POST",
48
+ headers: { "Content-Type": "application/json" },
49
+ body: JSON.stringify(body),
50
+ });
51
+ }
52
+ catch (err) {
53
+ throw new OllamaUnreachableError(endpoint, err);
54
+ }
55
+ if (!res.ok) {
56
+ const body = await res.json().catch(() => ({ error: "<unparseable>" }));
57
+ throw new OllamaHttpError(res.status, body);
58
+ }
59
+ return await res.json();
60
+ }
61
+ return {
62
+ source: "ollama",
63
+ supports(mode) {
64
+ if (mode === "embed")
65
+ return Boolean(config.embedModel);
66
+ return true;
67
+ },
68
+ async generate(req) {
69
+ const model = req.model ?? config.defaultModel;
70
+ if (!model) {
71
+ throw new Error("ollama adapter requires either GenerateRequest.model or OllamaAdapterConfig.defaultModel");
72
+ }
73
+ const messages = [];
74
+ if (req.system)
75
+ messages.push({ role: "system", content: req.system });
76
+ messages.push({ role: "user", content: req.prompt });
77
+ const payload = await post("/v1/chat/completions", {
78
+ model,
79
+ messages,
80
+ ...(req.maxTokens !== undefined ? { max_tokens: req.maxTokens } : {}),
81
+ ...(req.temperature !== undefined ? { temperature: req.temperature } : {}),
82
+ });
83
+ const parsed = parseChatCompletion(payload);
84
+ return {
85
+ text: parsed.text,
86
+ sourceUsed: "ollama",
87
+ modelUsed: parsed.model ?? model,
88
+ tokensIn: parsed.tokensIn,
89
+ tokensOut: parsed.tokensOut,
90
+ // Local compute — zero marginal $ cost per arc §6.7 cost-shape table.
91
+ // Hardware cost is the user's responsibility and not surfaced here.
92
+ costUsd: 0,
93
+ };
94
+ },
95
+ async embed(req) {
96
+ const model = req.model ?? config.embedModel;
97
+ if (!model) {
98
+ throw new Error("ollama adapter requires either EmbedRequest.model or OllamaAdapterConfig.embedModel for embeddings");
99
+ }
100
+ const payload = await post("/v1/embeddings", {
101
+ model,
102
+ input: req.texts,
103
+ });
104
+ const parsed = parseEmbeddings(payload);
105
+ return {
106
+ vectors: parsed.vectors,
107
+ sourceUsed: "ollama",
108
+ modelUsed: parsed.model ?? model,
109
+ tokensIn: parsed.tokensIn,
110
+ costUsd: 0,
111
+ };
112
+ },
113
+ };
114
+ }
115
+ function parseChatCompletion(payload) {
116
+ if (!payload || typeof payload !== "object") {
117
+ throw new Error(`ollama: unparseable chat-completion response: ${JSON.stringify(payload)}`);
118
+ }
119
+ const p = payload;
120
+ const text = p.choices?.[0]?.message?.content;
121
+ if (typeof text !== "string") {
122
+ throw new Error(`ollama: chat-completion missing choices[0].message.content: ${JSON.stringify(payload)}`);
123
+ }
124
+ return {
125
+ text,
126
+ ...(p.model ? { model: p.model } : {}),
127
+ tokensIn: p.usage?.prompt_tokens ?? 0,
128
+ tokensOut: p.usage?.completion_tokens ?? 0,
129
+ };
130
+ }
131
+ function parseEmbeddings(payload) {
132
+ if (!payload || typeof payload !== "object") {
133
+ throw new Error(`ollama: unparseable embeddings response: ${JSON.stringify(payload)}`);
134
+ }
135
+ const p = payload;
136
+ const vectors = (p.data ?? []).map((d, idx) => {
137
+ if (!Array.isArray(d.embedding)) {
138
+ throw new Error(`ollama: data[${idx}].embedding is not a vector`);
139
+ }
140
+ return d.embedding;
141
+ });
142
+ return {
143
+ vectors,
144
+ ...(p.model ? { model: p.model } : {}),
145
+ tokensIn: p.usage?.prompt_tokens ?? 0,
146
+ };
147
+ }
148
+ //# sourceMappingURL=ollama.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ollama.js","sourceRoot":"","sources":["../../src/adapters/ollama.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import { type CostBudget } from "./types.js";
2
+ export interface CostTracker {
3
+ record(usd: number): void;
4
+ assertWithin(estimatedUsd: number): void;
5
+ totalUsd(): number;
6
+ callCount(): number;
7
+ reset(): void;
8
+ budget(): CostBudget | null;
9
+ }
10
+ export declare function createCostTracker(budget?: CostBudget): CostTracker;
11
+ //# sourceMappingURL=cost-budget.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cost-budget.d.ts","sourceRoot":"","sources":["../src/cost-budget.ts"],"names":[],"mappings":"AA4BA,OAAO,EAA2B,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAEtE,MAAM,WAAW,WAAW;IAE1B,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAI1B,YAAY,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IACzC,QAAQ,IAAI,MAAM,CAAC;IACnB,SAAS,IAAI,MAAM,CAAC;IACpB,KAAK,IAAI,IAAI,CAAC;IAGd,MAAM,IAAI,UAAU,GAAG,IAAI,CAAC;CAC7B;AAED,wBAAgB,iBAAiB,CAAC,MAAM,CAAC,EAAE,UAAU,GAAG,WAAW,CA8ClE"}
@@ -0,0 +1,71 @@
1
+ /*
2
+ * Copyright 2026 MARLINK TRADING SRL (YounndAI)
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ // Per-run cost tracker.
17
+ //
18
+ // Per arc-thoughts §6.7 ("cost-shape lock") + brief Open Decision #3 default
19
+ // (recommended: hard-stop on per-run, warn-on-monthly). The CostTracker
20
+ // accumulates per-call cost across the current run; the gateway calls
21
+ // `assertWithin()` BEFORE invoking the adapter so a known-over-budget call
22
+ // never even reaches the network.
23
+ //
24
+ // Implementation is in-memory + per-gateway-instance; block-B Commit 4 hands
25
+ // the gateway a fresh CostTracker at 5-step protocol step 3 start and reads
26
+ // `totalCostUsd()` at step 4 commit to populate `automator_runs.llm_cost_usd`.
27
+ import { CostBudgetExceededError } from "./types.js";
28
+ export function createCostTracker(budget) {
29
+ let accumulated = 0;
30
+ let calls = 0;
31
+ const snapshot = budget
32
+ ? {
33
+ perRunUsd: budget.perRunUsd,
34
+ ...(budget.monthlyUsd !== undefined ? { monthlyUsd: budget.monthlyUsd } : {}),
35
+ }
36
+ : null;
37
+ return {
38
+ record(usd) {
39
+ if (!Number.isFinite(usd) || usd < 0) {
40
+ throw new RangeError(`CostTracker.record() requires a non-negative finite number; got ${usd}`);
41
+ }
42
+ accumulated += usd;
43
+ calls += 1;
44
+ },
45
+ assertWithin(estimatedUsd) {
46
+ if (!Number.isFinite(estimatedUsd) || estimatedUsd < 0) {
47
+ throw new RangeError(`CostTracker.assertWithin() requires a non-negative finite number; got ${estimatedUsd}`);
48
+ }
49
+ if (!snapshot)
50
+ return;
51
+ const projected = accumulated + estimatedUsd;
52
+ if (projected > snapshot.perRunUsd) {
53
+ throw new CostBudgetExceededError(snapshot.perRunUsd, accumulated, estimatedUsd);
54
+ }
55
+ },
56
+ totalUsd() {
57
+ return accumulated;
58
+ },
59
+ callCount() {
60
+ return calls;
61
+ },
62
+ reset() {
63
+ accumulated = 0;
64
+ calls = 0;
65
+ },
66
+ budget() {
67
+ return snapshot;
68
+ },
69
+ };
70
+ }
71
+ //# sourceMappingURL=cost-budget.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cost-budget.js","sourceRoot":"","sources":["../src/cost-budget.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,wBAAwB;AACxB,EAAE;AACF,6EAA6E;AAC7E,wEAAwE;AACxE,sEAAsE;AACtE,2EAA2E;AAC3E,kCAAkC;AAClC,EAAE;AACF,6EAA6E;AAC7E,4EAA4E;AAC5E,+EAA+E;AAE/E,OAAO,EAAE,uBAAuB,EAAmB,MAAM,YAAY,CAAC;AAiBtE,MAAM,UAAU,iBAAiB,CAAC,MAAmB;IACnD,IAAI,WAAW,GAAG,CAAC,CAAC;IACpB,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,MAAM,QAAQ,GAAsB,MAAM;QACxC,CAAC,CAAC;YACE,SAAS,EAAE,MAAM,CAAC,SAAS;YAC3B,GAAG,CAAC,MAAM,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC9E;QACH,CAAC,CAAC,IAAI,CAAC;IAET,OAAO;QACL,MAAM,CAAC,GAAW;YAChB,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,EAAE,CAAC;gBACrC,MAAM,IAAI,UAAU,CAClB,mEAAmE,GAAG,EAAE,CACzE,CAAC;YACJ,CAAC;YACD,WAAW,IAAI,GAAG,CAAC;YACnB,KAAK,IAAI,CAAC,CAAC;QACb,CAAC;QACD,YAAY,CAAC,YAAoB;YAC/B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAC,IAAI,YAAY,GAAG,CAAC,EAAE,CAAC;gBACvD,MAAM,IAAI,UAAU,CAClB,yEAAyE,YAAY,EAAE,CACxF,CAAC;YACJ,CAAC;YACD,IAAI,CAAC,QAAQ;gBAAE,OAAO;YACtB,MAAM,SAAS,GAAG,WAAW,GAAG,YAAY,CAAC;YAC7C,IAAI,SAAS,GAAG,QAAQ,CAAC,SAAS,EAAE,CAAC;gBACnC,MAAM,IAAI,uBAAuB,CAAC,QAAQ,CAAC,SAAS,EAAE,WAAW,EAAE,YAAY,CAAC,CAAC;YACnF,CAAC;QACH,CAAC;QACD,QAAQ;YACN,OAAO,WAAW,CAAC;QACrB,CAAC;QACD,SAAS;YACP,OAAO,KAAK,CAAC;QACf,CAAC;QACD,KAAK;YACH,WAAW,GAAG,CAAC,CAAC;YAChB,KAAK,GAAG,CAAC,CAAC;QACZ,CAAC;QACD,MAAM;YACJ,OAAO,QAAQ,CAAC;QAClB,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,8 @@
1
+ import type { LlmGateway, LlmGatewayConfig } from "./types.js";
2
+ export declare function createLlmGateway(config?: LlmGatewayConfig): LlmGateway;
3
+ export type { CostBudget, EmbedRequest, EmbedResult, GenerateRequest, GenerateResult, HardConstraint, LlmAdapter, LlmCapability, LlmGateway, LlmGatewayConfig, LlmSource, MemscopeContext, } from "./types.js";
4
+ export { CostBudgetExceededError, HardConstraintViolationError, NoEligibleAdapterError, UnknownHardConstraintError, } from "./types.js";
5
+ export { createCostTracker, type CostTracker } from "./cost-budget.js";
6
+ export { DEFAULT_SOURCE_PREFERENCE, firstViolation, selectAdapter, type SelectAdapterArgs, } from "./routing.js";
7
+ export { createAiRelayAdapter, createOllamaAdapter, createHarnessAdapter, createByokAdapter, OllamaUnreachableError, OllamaHttpError, type AiRelayAdapterConfig, type AiRelayGenerateFn, type AiRelayEmbedFn, type OllamaAdapterConfig, type FetchLike, type HarnessAdapterConfig, type HarnessInvokeArgs, type HarnessInvokeResult, type HarnessInvokeFn, type ByokAdapterConfig, type ByokClient, } from "./adapters/index.js";
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAmCA,OAAO,KAAK,EAMV,UAAU,EACV,gBAAgB,EAEjB,MAAM,YAAY,CAAC;AAEpB,wBAAgB,gBAAgB,CAAC,MAAM,GAAE,gBAAqB,GAAG,UAAU,CA0D1E;AAKD,YAAY,EACV,UAAU,EACV,YAAY,EACZ,WAAW,EACX,eAAe,EACf,cAAc,EACd,cAAc,EACd,UAAU,EACV,aAAa,EACb,UAAU,EACV,gBAAgB,EAChB,SAAS,EACT,eAAe,GAChB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,uBAAuB,EACvB,4BAA4B,EAC5B,sBAAsB,EACtB,0BAA0B,GAC3B,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,iBAAiB,EAAE,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEvE,OAAO,EACL,yBAAyB,EACzB,cAAc,EACd,aAAa,EACb,KAAK,iBAAiB,GACvB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,oBAAoB,EACpB,mBAAmB,EACnB,oBAAoB,EACpB,iBAAiB,EACjB,sBAAsB,EACtB,eAAe,EACf,KAAK,oBAAoB,EACzB,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,mBAAmB,EACxB,KAAK,SAAS,EACd,KAAK,oBAAoB,EACzB,KAAK,iBAAiB,EACtB,KAAK,mBAAmB,EACxB,KAAK,eAAe,EACpB,KAAK,iBAAiB,EACtB,KAAK,UAAU,GAChB,MAAM,qBAAqB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,95 @@
1
+ /*
2
+ * Copyright 2026 MARLINK TRADING SRL (YounndAI)
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ // @younndai/lyt-llm — public entry point.
17
+ //
18
+ // Per arc-thoughts §6.12 (LOCKED 2026-05-27): a multi-source LLM gateway
19
+ // composing four adapters (ai-relay external / Ollama local / Harness
20
+ // CC-Codex / BYOK) behind a single createLlmGateway() surface.
21
+ //
22
+ // In Commit 2 the gateway ships standalone. The lyt-runner op composition
23
+ // (`std:llm.generate@v1`, `std:llm.stream@v1`, `std:llm.generate_object@v1`,
24
+ // `std:llm.embed@v1`) is deferred to block-B Commit 4 alongside the 5-step
25
+ // protocol — Commit 4 imports `createLlmGateway` + `registerLlmOps(runner,
26
+ // gateway)` (the latter shipped here in a future iteration) and wires them
27
+ // into the lyt-runner factory's `createLytRunner(config)`.
28
+ //
29
+ // Reference: zen-runner/src/index.ts L200-300 is the canonical Pattern A
30
+ // composition template; this gateway mirrors its factory + injected-deps
31
+ // shape so lyt-runner can compose both with identical ergonomics.
32
+ import { createCostTracker } from "./cost-budget.js";
33
+ import { DEFAULT_SOURCE_PREFERENCE, selectAdapter } from "./routing.js";
34
+ export function createLlmGateway(config = {}) {
35
+ const adapters = { ...(config.adapters ?? {}) };
36
+ const defaultPreference = config.preference ?? [...DEFAULT_SOURCE_PREFERENCE];
37
+ const tracker = createCostTracker(config.costBudget);
38
+ function preferenceFor(req) {
39
+ return req.sourcePreference && req.sourcePreference.length > 0
40
+ ? req.sourcePreference
41
+ : defaultPreference;
42
+ }
43
+ return {
44
+ async generate(req) {
45
+ const adapter = selectAdapter({
46
+ mode: "generate",
47
+ preference: preferenceFor(req),
48
+ adapters,
49
+ ...(req.hardConstraints !== undefined ? { hardConstraints: req.hardConstraints } : {}),
50
+ ...(req.memscope !== undefined ? { memscope: req.memscope } : {}),
51
+ });
52
+ // Pre-flight budget check. Per arc §6.7 cost-shape lock the per-run
53
+ // hard-stop fires BEFORE the call so we never burn cost on a request
54
+ // that's already over budget. The estimate is 0 here — we don't have
55
+ // a cheap pre-call cost estimate without tokenising the prompt
56
+ // (block-D will integrate @younndai/ai-relay's `estimateCost`); the
57
+ // assertWithin(0) call still throws if accumulated already exceeds
58
+ // the budget (edge case after a previous call put us over).
59
+ tracker.assertWithin(0);
60
+ const result = await adapter.generate(req);
61
+ tracker.record(result.costUsd);
62
+ return result;
63
+ },
64
+ async embed(req) {
65
+ const adapter = selectAdapter({
66
+ mode: "embed",
67
+ preference: preferenceFor(req),
68
+ adapters,
69
+ ...(req.hardConstraints !== undefined ? { hardConstraints: req.hardConstraints } : {}),
70
+ ...(req.memscope !== undefined ? { memscope: req.memscope } : {}),
71
+ });
72
+ tracker.assertWithin(0);
73
+ const result = await adapter.embed(req);
74
+ tracker.record(result.costUsd);
75
+ return result;
76
+ },
77
+ totalCostUsd() {
78
+ return tracker.totalUsd();
79
+ },
80
+ totalCalls() {
81
+ return tracker.callCount();
82
+ },
83
+ resetCostTracker() {
84
+ tracker.reset();
85
+ },
86
+ registeredSources() {
87
+ return Object.keys(adapters).filter((s) => adapters[s] !== undefined);
88
+ },
89
+ };
90
+ }
91
+ export { CostBudgetExceededError, HardConstraintViolationError, NoEligibleAdapterError, UnknownHardConstraintError, } from "./types.js";
92
+ export { createCostTracker } from "./cost-budget.js";
93
+ export { DEFAULT_SOURCE_PREFERENCE, firstViolation, selectAdapter, } from "./routing.js";
94
+ export { createAiRelayAdapter, createOllamaAdapter, createHarnessAdapter, createByokAdapter, OllamaUnreachableError, OllamaHttpError, } from "./adapters/index.js";
95
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import { HardConstraintViolationError, type LlmAdapter, type LlmSource, type HardConstraint, type MemscopeContext } from "./types.js";
2
+ export declare const DEFAULT_SOURCE_PREFERENCE: readonly LlmSource[];
3
+ export interface SelectAdapterArgs {
4
+ mode: "generate" | "embed";
5
+ preference: LlmSource[];
6
+ adapters: Partial<Record<LlmSource, LlmAdapter>>;
7
+ hardConstraints?: HardConstraint[];
8
+ memscope?: MemscopeContext;
9
+ }
10
+ export declare function selectAdapter(args: SelectAdapterArgs): LlmAdapter;
11
+ export declare function firstViolation(source: LlmSource, constraints: HardConstraint[], memscope?: MemscopeContext): HardConstraintViolationError | null;
12
+ //# sourceMappingURL=routing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routing.d.ts","sourceRoot":"","sources":["../src/routing.ts"],"names":[],"mappings":"AAmCA,OAAO,EACL,4BAA4B,EAC5B,KAAK,UAAU,EACf,KAAK,SAAS,EACd,KAAK,cAAc,EACnB,KAAK,eAAe,EAGrB,MAAM,YAAY,CAAC;AAKpB,eAAO,MAAM,yBAAyB,EAAE,SAAS,SAAS,EAKxD,CAAC;AAYH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC;IAC3B,UAAU,EAAE,SAAS,EAAE,CAAC;IACxB,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC,CAAC;IACjD,eAAe,CAAC,EAAE,cAAc,EAAE,CAAC;IACnC,QAAQ,CAAC,EAAE,eAAe,CAAC;CAC5B;AAMD,wBAAgB,aAAa,CAAC,IAAI,EAAE,iBAAiB,GAAG,UAAU,CAkCjE;AAKD,wBAAgB,cAAc,CAC5B,MAAM,EAAE,SAAS,EACjB,WAAW,EAAE,cAAc,EAAE,EAC7B,QAAQ,CAAC,EAAE,eAAe,GACzB,4BAA4B,GAAG,IAAI,CAQrC"}
@@ -0,0 +1,149 @@
1
+ /*
2
+ * Copyright 2026 MARLINK TRADING SRL (YounndAI)
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ // Routing policy engine.
17
+ //
18
+ // Per arc-thoughts §6.7 + §6.12: each @AUTOMATOR declares a
19
+ // `llm_source_preference` (ordered list) + `llm_hard_constraints` (gating
20
+ // predicates). The gateway walks the preference order, filters by
21
+ // hard-constraints + adapter availability + mode support, and picks the
22
+ // first match. No fallback ranking — explicit preference order is the
23
+ // contract.
24
+ //
25
+ // Default preference is `[harness, ai-relay, byok, ollama]` matching arc
26
+ // §6.7 cost-shape table: harness is the "use the €20 you already pay"
27
+ // adoption lever (zero marginal cost), ai-relay is the cheap-tier default
28
+ // for grunt work, byok is power-user direct, ollama is the local fallback.
29
+ //
30
+ // Constraint evaluation is fail-loud: unknown constraint kinds throw
31
+ // UnknownHardConstraintError rather than being silently ignored. Per arc
32
+ // §6.7: "hard_constraint is a hard contract; soft preferences belong in
33
+ // source_preference".
34
+ import { HardConstraintViolationError, UnknownHardConstraintError, NoEligibleAdapterError, } from "./types.js";
35
+ // Brief @TASK clause (4): "default `[cheap, harness, byok, local]` per arc §6.10".
36
+ // Translated to source vocabulary: cheap → ai-relay; harness → harness;
37
+ // byok → byok; local → ollama. Order preserved from arc §6.10 default.
38
+ export const DEFAULT_SOURCE_PREFERENCE = Object.freeze([
39
+ "ai-relay",
40
+ "harness",
41
+ "byok",
42
+ "ollama",
43
+ ]);
44
+ const KNOWN_CONSTRAINT_KINDS = new Set([
45
+ "local_only",
46
+ "never_external_when_private_memscope",
47
+ "max_cost_per_run_usd",
48
+ "provider",
49
+ ]);
50
+ const EXTERNAL_SOURCES = new Set(["ai-relay", "byok"]);
51
+ const LOCAL_SOURCES = new Set(["ollama", "harness"]);
52
+ // Walk preference in order; return the first adapter that (a) is registered,
53
+ // (b) supports the requested mode, and (c) passes every hard constraint.
54
+ // Throws NoEligibleAdapterError if no candidate qualifies — the caller
55
+ // surfaces this as a structured automator_run_events row.
56
+ export function selectAdapter(args) {
57
+ const constraints = args.hardConstraints ?? [];
58
+ // Validate constraint kinds up-front so we fail-loud BEFORE iterating
59
+ // preference. Defensive — keeps the "unknown constraint" diagnostic clean.
60
+ for (const c of constraints) {
61
+ if (!KNOWN_CONSTRAINT_KINDS.has(c.kind)) {
62
+ throw new UnknownHardConstraintError(c.kind);
63
+ }
64
+ }
65
+ const reasons = [];
66
+ for (const source of args.preference) {
67
+ const adapter = args.adapters[source];
68
+ if (!adapter) {
69
+ reasons.push(`${source}: no adapter registered`);
70
+ continue;
71
+ }
72
+ if (!adapter.supports(args.mode)) {
73
+ reasons.push(`${source}: does not support mode=${args.mode}`);
74
+ continue;
75
+ }
76
+ const violation = firstViolation(source, constraints, args.memscope);
77
+ if (violation) {
78
+ reasons.push(`${source}: ${violation.constraintKind} (${violation.detail})`);
79
+ continue;
80
+ }
81
+ return adapter;
82
+ }
83
+ throw new NoEligibleAdapterError(args.mode, args.preference, reasons.length > 0 ? reasons.join("; ") : "preference list is empty");
84
+ }
85
+ // Public helper for adapter-shopping callers (e.g., the lyt-runner pre-write
86
+ // hook from Commit 5 may pre-validate constraints before issuing the call).
87
+ // Returns the first violation, or null if every constraint is satisfied.
88
+ export function firstViolation(source, constraints, memscope) {
89
+ for (const c of constraints) {
90
+ const detail = evaluateOne(source, c, memscope);
91
+ if (detail !== null) {
92
+ return new HardConstraintViolationError(c.kind, source, detail);
93
+ }
94
+ }
95
+ return null;
96
+ }
97
+ // Per-constraint evaluator. Returns a non-null detail string when the
98
+ // constraint REJECTS this source, or null when the constraint accepts it.
99
+ //
100
+ // New constraint kinds must (1) be added to KNOWN_CONSTRAINT_KINDS,
101
+ // (2) get a branch here, and (3) be documented in
102
+ // the LYT design doc `lyt-yai-domain.md` §`@AUTOMATOR`.
103
+ function evaluateOne(source, c, memscope) {
104
+ switch (c.kind) {
105
+ case "local_only": {
106
+ if (!LOCAL_SOURCES.has(source)) {
107
+ return `source ${source} is external; only ${[...LOCAL_SOURCES].join(",")} allowed`;
108
+ }
109
+ return null;
110
+ }
111
+ case "never_external_when_private_memscope": {
112
+ if (memscope?.defaultView === "private" && EXTERNAL_SOURCES.has(source)) {
113
+ return `memscope default_view=private; source ${source} is external`;
114
+ }
115
+ return null;
116
+ }
117
+ case "max_cost_per_run_usd": {
118
+ // Adapter-mode constraint is advisory at routing time — actual
119
+ // enforcement happens in the cost-budget tracker at call time. We
120
+ // simply confirm the args shape is well-formed; if not, treat as
121
+ // rejection so the constraint can't be silently misconfigured.
122
+ const usd = c.args?.usd;
123
+ if (typeof usd !== "number" || !Number.isFinite(usd) || usd < 0) {
124
+ return `max_cost_per_run_usd requires args.usd:number>=0; got ${JSON.stringify(c.args)}`;
125
+ }
126
+ return null;
127
+ }
128
+ case "provider": {
129
+ // Pin to a specific provider name. Source-level scoping: ai-relay and
130
+ // byok are the provider-bearing surfaces; ollama and harness are
131
+ // implicitly local/integrated and don't honour provider pinning.
132
+ const requested = c.args?.name;
133
+ if (typeof requested !== "string" || requested.length === 0) {
134
+ return `provider requires args.name:string; got ${JSON.stringify(c.args)}`;
135
+ }
136
+ if (source === "ollama" || source === "harness") {
137
+ return `provider pinning ('${requested}') is incompatible with source '${source}'`;
138
+ }
139
+ return null;
140
+ }
141
+ default: {
142
+ // Unreachable — KNOWN_CONSTRAINT_KINDS is checked at the top of
143
+ // selectAdapter. Defensive against the gate being widened without
144
+ // a matching switch arm.
145
+ throw new UnknownHardConstraintError(c.kind);
146
+ }
147
+ }
148
+ }
149
+ //# sourceMappingURL=routing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routing.js","sourceRoot":"","sources":["../src/routing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,yBAAyB;AACzB,EAAE;AACF,4DAA4D;AAC5D,0EAA0E;AAC1E,kEAAkE;AAClE,wEAAwE;AACxE,sEAAsE;AACtE,YAAY;AACZ,EAAE;AACF,yEAAyE;AACzE,sEAAsE;AACtE,0EAA0E;AAC1E,2EAA2E;AAC3E,EAAE;AACF,qEAAqE;AACrE,yEAAyE;AACzE,wEAAwE;AACxE,sBAAsB;AAEtB,OAAO,EACL,4BAA4B,EAK5B,0BAA0B,EAC1B,sBAAsB,GACvB,MAAM,YAAY,CAAC;AAEpB,mFAAmF;AACnF,wEAAwE;AACxE,uEAAuE;AACvE,MAAM,CAAC,MAAM,yBAAyB,GAAyB,MAAM,CAAC,MAAM,CAAC;IAC3E,UAAU;IACV,SAAS;IACT,MAAM;IACN,QAAQ;CACT,CAAC,CAAC;AAEH,MAAM,sBAAsB,GAAG,IAAI,GAAG,CAAC;IACrC,YAAY;IACZ,sCAAsC;IACtC,sBAAsB;IACtB,UAAU;CACX,CAAC,CAAC;AAEH,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAY,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC;AAClE,MAAM,aAAa,GAAG,IAAI,GAAG,CAAY,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC;AAUhE,6EAA6E;AAC7E,yEAAyE;AACzE,uEAAuE;AACvE,0DAA0D;AAC1D,MAAM,UAAU,aAAa,CAAC,IAAuB;IACnD,MAAM,WAAW,GAAG,IAAI,CAAC,eAAe,IAAI,EAAE,CAAC;IAC/C,sEAAsE;IACtE,2EAA2E;IAC3E,KAAK,MAAM,CAAC,IAAI,WAAW,EAAE,CAAC;QAC5B,IAAI,CAAC,sBAAsB,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;YACxC,MAAM,IAAI,0BAA0B,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAC/C,CAAC;IACH,CAAC;IAED,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;QACrC,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACtC,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,CAAC,IAAI,CAAC,GAAG,MAAM,yBAAyB,CAAC,CAAC;YACjD,SAAS;QACX,CAAC;QACD,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACjC,OAAO,CAAC,IAAI,CAAC,GAAG,MAAM,2BAA2B,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;YAC9D,SAAS;QACX,CAAC;QACD,MAAM,SAAS,GAAG,cAAc,CAAC,MAAM,EAAE,WAAW,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;QACrE,IAAI,SAAS,EAAE,CAAC;YACd,OAAO,CAAC,IAAI,CAAC,GAAG,MAAM,KAAK,SAAS,CAAC,cAAc,KAAK,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC;YAC7E,SAAS;QACX,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,MAAM,IAAI,sBAAsB,CAC9B,IAAI,CAAC,IAAI,EACT,IAAI,CAAC,UAAU,EACf,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,0BAA0B,CACrE,CAAC;AACJ,CAAC;AAED,6EAA6E;AAC7E,4EAA4E;AAC5E,yEAAyE;AACzE,MAAM,UAAU,cAAc,CAC5B,MAAiB,EACjB,WAA6B,EAC7B,QAA0B;IAE1B,KAAK,MAAM,CAAC,IAAI,WAAW,EAAE,CAAC;QAC5B,MAAM,MAAM,GAAG,WAAW,CAAC,MAAM,EAAE,CAAC,EAAE,QAAQ,CAAC,CAAC;QAChD,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;YACpB,OAAO,IAAI,4BAA4B,CAAC,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;QAClE,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,sEAAsE;AACtE,0EAA0E;AAC1E,EAAE;AACF,oEAAoE;AACpE,kDAAkD;AAClD,wDAAwD;AACxD,SAAS,WAAW,CAClB,MAAiB,EACjB,CAAiB,EACjB,QAA0B;IAE1B,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC;QACf,KAAK,YAAY,CAAC,CAAC,CAAC;YAClB,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;gBAC/B,OAAO,UAAU,MAAM,sBAAsB,CAAC,GAAG,aAAa,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC;YACtF,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC;QACD,KAAK,sCAAsC,CAAC,CAAC,CAAC;YAC5C,IAAI,QAAQ,EAAE,WAAW,KAAK,SAAS,IAAI,gBAAgB,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;gBACxE,OAAO,yCAAyC,MAAM,cAAc,CAAC;YACvE,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC;QACD,KAAK,sBAAsB,CAAC,CAAC,CAAC;YAC5B,+DAA+D;YAC/D,kEAAkE;YAClE,iEAAiE;YACjE,+DAA+D;YAC/D,MAAM,GAAG,GAAI,CAAC,CAAC,IAAsC,EAAE,GAAG,CAAC;YAC3D,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,EAAE,CAAC;gBAChE,OAAO,yDAAyD,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;YAC3F,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC;QACD,KAAK,UAAU,CAAC,CAAC,CAAC;YAChB,sEAAsE;YACtE,iEAAiE;YACjE,iEAAiE;YACjE,MAAM,SAAS,GAAI,CAAC,CAAC,IAAuC,EAAE,IAAI,CAAC;YACnE,IAAI,OAAO,SAAS,KAAK,QAAQ,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC5D,OAAO,2CAA2C,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;YAC7E,CAAC;YACD,IAAI,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;gBAChD,OAAO,sBAAsB,SAAS,mCAAmC,MAAM,GAAG,CAAC;YACrF,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,CAAC,CAAC,CAAC;YACR,gEAAgE;YAChE,kEAAkE;YAClE,yBAAyB;YACzB,MAAM,IAAI,0BAA0B,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAC/C,CAAC;IACH,CAAC;AACH,CAAC"}