@wrenai/genbi 0.0.4 → 0.0.5
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 +64 -2
- package/dist/assets/{echarts-LTFC-ysd.js → echarts-DYM4hjvk.js} +2 -2
- package/dist/assets/index-A_Wrq7Ey.css +1 -0
- package/dist/assets/{index-Bo--QMUN.js → index-DsH129tV.js} +18 -17
- package/dist/index.html +2 -2
- package/dist-server/harness/bundle/schema.d.ts +38 -0
- package/dist-server/harness/bundle/schema.js +18 -10
- package/dist-server/harness/bundle/version.js +1 -1
- package/dist-server/harness/cli-args.d.ts +10 -2
- package/dist-server/harness/cli-args.js +24 -8
- package/dist-server/harness/cli.js +75 -9
- package/dist-server/harness/compile/cache.js +4 -4
- package/dist-server/harness/compile/compose-profile.d.ts +51 -14
- package/dist-server/harness/compile/compose-profile.js +85 -19
- package/dist-server/harness/compile/context-loader.d.ts +56 -0
- package/dist-server/harness/compile/context-loader.js +237 -0
- package/dist-server/harness/compile/errors.d.ts +19 -0
- package/dist-server/harness/compile/errors.js +35 -0
- package/dist-server/harness/compile/index.d.ts +4 -3
- package/dist-server/harness/compile/index.js +4 -3
- package/dist-server/harness/compile/pipeline.d.ts +15 -7
- package/dist-server/harness/compile/pipeline.js +62 -14
- package/dist-server/harness/compile/resolve-binary.d.ts +2 -2
- package/dist-server/harness/compile/resolve-binary.js +3 -3
- package/dist-server/harness/compile/types.d.ts +29 -0
- package/dist-server/harness/compile/warble-identity.d.ts +11 -0
- package/dist-server/harness/compile/warble-identity.js +13 -0
- package/dist-server/harness/components/ai-step.d.ts +4 -0
- package/dist-server/harness/components/ai-step.js +40 -0
- package/dist-server/harness/components/broker.d.ts +60 -0
- package/dist-server/harness/components/broker.js +117 -0
- package/dist-server/harness/components/capability-card.d.ts +98 -0
- package/dist-server/harness/components/capability-card.js +169 -0
- package/dist-server/harness/components/context.d.ts +2 -0
- package/dist-server/harness/components/context.js +30 -0
- package/dist-server/harness/components/display.d.ts +6 -0
- package/dist-server/harness/components/display.js +29 -0
- package/dist-server/harness/components/egress-judge.d.ts +12 -0
- package/dist-server/harness/components/egress-judge.js +27 -0
- package/dist-server/harness/components/egress.d.ts +142 -0
- package/dist-server/harness/components/egress.js +469 -0
- package/dist-server/harness/components/in-process.d.ts +7 -0
- package/dist-server/harness/components/in-process.js +245 -0
- package/dist-server/harness/components/normalize.d.ts +35 -0
- package/dist-server/harness/components/normalize.js +245 -0
- package/dist-server/harness/components/plan.d.ts +31 -0
- package/dist-server/harness/components/plan.js +184 -0
- package/dist-server/harness/components/report.d.ts +207 -0
- package/dist-server/harness/components/report.js +309 -0
- package/dist-server/harness/components/runner.d.ts +193 -0
- package/dist-server/harness/components/runner.js +492 -0
- package/dist-server/harness/components/semantic-guards.d.ts +20 -0
- package/dist-server/harness/components/semantic-guards.js +42 -0
- package/dist-server/harness/components/terminal-json.d.ts +18 -0
- package/dist-server/harness/components/terminal-json.js +82 -0
- package/dist-server/harness/components/usage.d.ts +14 -0
- package/dist-server/harness/components/usage.js +19 -0
- package/dist-server/harness/components/wren-access.d.ts +29 -0
- package/dist-server/harness/components/wren-access.js +233 -0
- package/dist-server/harness/components/zone-dry-run.d.ts +43 -0
- package/dist-server/harness/components/zone-dry-run.js +107 -0
- package/dist-server/harness/components/zone-gate.d.ts +76 -0
- package/dist-server/harness/components/zone-gate.js +144 -0
- package/dist-server/harness/events/index.d.ts +1 -1
- package/dist-server/harness/events/types.d.ts +52 -0
- package/dist-server/harness/index.d.ts +9 -2
- package/dist-server/harness/index.js +5 -1
- package/dist-server/harness/loop/executor.d.ts +1 -0
- package/dist-server/harness/loop/executor.js +37 -15
- package/dist-server/harness/npm-package-resolve.d.ts +2 -0
- package/dist-server/harness/npm-package-resolve.js +16 -4
- package/dist-server/harness/providers/adapters/openai-compatible.d.ts +13 -0
- package/dist-server/harness/providers/adapters/openai-compatible.js +17 -0
- package/dist-server/harness/providers/adapters/openai.d.ts +7 -0
- package/dist-server/harness/providers/adapters/openai.js +6 -0
- package/dist-server/harness/providers/adapters/pi-ai.d.ts +48 -0
- package/dist-server/harness/providers/adapters/pi-ai.js +518 -0
- package/dist-server/harness/providers/binding.d.ts +45 -4
- package/dist-server/harness/providers/binding.js +44 -8
- package/dist-server/harness/providers/index.d.ts +9 -3
- package/dist-server/harness/providers/index.js +10 -3
- package/dist-server/harness/providers/zone.d.ts +96 -0
- package/dist-server/harness/providers/zone.js +112 -0
- package/dist-server/harness/render/answering-query.d.ts +29 -0
- package/dist-server/harness/render/answering-query.js +62 -0
- package/dist-server/harness/render/envelope.d.ts +6 -0
- package/dist-server/harness/render/envelope.js +10 -3
- package/dist-server/harness/route/adapter-spec.d.ts +8 -9
- package/dist-server/harness/route/adapter-spec.js +13 -14
- package/dist-server/harness/route/codex-ask-events.js +2 -1
- package/dist-server/harness/route/codex-ask.d.ts +4 -0
- package/dist-server/harness/route/codex-ask.js +69 -30
- package/dist-server/harness/route/codex-host-query.d.ts +52 -0
- package/dist-server/harness/route/codex-host-query.js +191 -0
- package/dist-server/harness/route/codex-local-manifest.d.ts +9 -1
- package/dist-server/harness/route/codex-local-manifest.js +96 -15
- package/dist-server/harness/route/codex-wren-proxy.d.ts +33 -0
- package/dist-server/harness/route/codex-wren-proxy.js +227 -0
- package/dist-server/harness/route/describe.js +45 -6
- package/dist-server/harness/route/in-process.d.ts +22 -1
- package/dist-server/harness/route/in-process.js +54 -4
- package/dist-server/harness/route/route.js +5 -2
- package/dist-server/harness/route/types.d.ts +30 -1
- package/dist-server/harness/session/run.js +30 -10
- package/dist-server/harness/setup/runner.d.ts +2 -0
- package/dist-server/harness/setup/runner.js +38 -17
- package/dist-server/harness/setup/yaml-scalar.d.ts +23 -0
- package/dist-server/harness/setup/yaml-scalar.js +62 -0
- package/dist-server/server/adopt.js +1 -17
- package/dist-server/server/app.d.ts +1 -0
- package/dist-server/server/app.js +163 -14
- package/dist-server/server/auth-choice.d.ts +13 -1
- package/dist-server/server/auth-choice.js +66 -1
- package/dist-server/server/bin.js +177 -25
- package/dist-server/server/codex-local-grounding.d.ts +18 -0
- package/dist-server/server/codex-local-grounding.js +92 -0
- package/dist-server/server/codex-native-session.d.ts +25 -0
- package/dist-server/server/codex-native-session.js +1 -0
- package/dist-server/server/codex-runtime-composition.d.ts +34 -0
- package/dist-server/server/codex-runtime-composition.js +250 -0
- package/dist-server/server/codex-structured-ask.d.ts +23 -0
- package/dist-server/server/codex-structured-ask.js +102 -0
- package/dist-server/server/conn-config.js +4 -16
- package/dist-server/server/conversation-socket.d.ts +16 -0
- package/dist-server/server/conversation-socket.js +65 -0
- package/dist-server/server/db.d.ts +46 -0
- package/dist-server/server/db.js +88 -9
- package/dist-server/server/env-detect.d.ts +6 -0
- package/dist-server/server/env-detect.js +7 -0
- package/dist-server/server/fold.d.ts +22 -4
- package/dist-server/server/fold.js +37 -5
- package/dist-server/server/harness.d.ts +2 -2
- package/dist-server/server/harness.js +33 -7
- package/dist-server/server/interactive-terminal.d.ts +10 -6
- package/dist-server/server/interactive-terminal.js +33 -8
- package/dist-server/server/managed-wren-runtime.d.ts +116 -0
- package/dist-server/server/managed-wren-runtime.js +670 -0
- package/dist-server/server/native-artifacts.d.ts +23 -7
- package/dist-server/server/native-artifacts.js +43 -12
- package/dist-server/server/native-component-context.d.ts +19 -0
- package/dist-server/server/native-component-context.js +76 -0
- package/dist-server/server/native-component-preparation.d.ts +20 -0
- package/dist-server/server/native-component-preparation.js +129 -0
- package/dist-server/server/native-component-runtime.d.ts +36 -0
- package/dist-server/server/native-component-runtime.js +92 -0
- package/dist-server/server/native-components.d.ts +84 -0
- package/dist-server/server/native-components.js +160 -0
- package/dist-server/server/native-dispatch-process.d.ts +8 -0
- package/dist-server/server/native-dispatch-process.js +65 -0
- package/dist-server/server/native-runtime-spec.d.ts +89 -0
- package/dist-server/server/native-runtime-spec.js +169 -0
- package/dist-server/server/native-sessions.d.ts +172 -19
- package/dist-server/server/native-sessions.js +839 -244
- package/dist-server/server/native-shutdown.d.ts +6 -0
- package/dist-server/server/native-shutdown.js +28 -0
- package/dist-server/server/native-wren-home.d.ts +18 -0
- package/dist-server/server/native-wren-home.js +262 -0
- package/dist-server/server/node-pty-host.d.ts +2 -1
- package/dist-server/server/node-pty-host.js +3 -2
- package/dist-server/server/profile-registry.d.ts +139 -0
- package/dist-server/server/profile-registry.js +620 -0
- package/dist-server/server/runtime-binding.d.ts +4 -1
- package/dist-server/server/runtime-binding.js +50 -2
- package/dist-server/server/runtime-host/claude-component-step.d.ts +25 -0
- package/dist-server/server/runtime-host/claude-component-step.js +186 -0
- package/dist-server/server/runtime-host/codex-app-server.d.ts +75 -0
- package/dist-server/server/runtime-host/codex-app-server.js +239 -0
- package/dist-server/server/runtime-host/codex-compatibility.d.ts +110 -0
- package/dist-server/server/runtime-host/codex-compatibility.js +162 -0
- package/dist-server/server/runtime-host/codex-component-step.d.ts +19 -0
- package/dist-server/server/runtime-host/codex-component-step.js +308 -0
- package/dist-server/server/runtime-host/codex-conversation.d.ts +109 -0
- package/dist-server/server/runtime-host/codex-conversation.js +295 -0
- package/dist-server/server/runtime-host/codex-direct-tools.d.ts +23 -0
- package/dist-server/server/runtime-host/codex-direct-tools.js +59 -0
- package/dist-server/server/runtime-host/codex-events.d.ts +647 -0
- package/dist-server/server/runtime-host/codex-events.js +66 -0
- package/dist-server/server/runtime-host/codex-policy.d.ts +16 -0
- package/dist-server/server/runtime-host/codex-policy.js +151 -0
- package/dist-server/server/runtime-host/codex-process.d.ts +8 -0
- package/dist-server/server/runtime-host/codex-process.js +96 -0
- package/dist-server/server/runtime-host/codex-rpc.d.ts +47 -0
- package/dist-server/server/runtime-host/codex-rpc.js +182 -0
- package/dist-server/server/runtime-host/codex-session.d.ts +74 -0
- package/dist-server/server/runtime-host/codex-session.js +492 -0
- package/dist-server/server/runtime-host/codex-terminal-protocol.d.ts +35 -0
- package/dist-server/server/runtime-host/codex-terminal-protocol.js +243 -0
- package/dist-server/server/runtime-host/codex-terminal.d.ts +16 -0
- package/dist-server/server/runtime-host/codex-terminal.js +120 -0
- package/dist-server/server/runtime-host/index.d.ts +5 -0
- package/dist-server/server/runtime-host/index.js +3 -0
- package/dist-server/server/runtime-host/local.d.ts +29 -0
- package/dist-server/server/runtime-host/local.js +90 -0
- package/dist-server/server/runtime-host/policy.d.ts +8 -0
- package/dist-server/server/runtime-host/policy.js +78 -0
- package/dist-server/server/runtime-host/types.d.ts +52 -0
- package/dist-server/server/runtime-host/types.js +62 -0
- package/dist-server/server/structured-query-evidence.d.ts +7 -0
- package/dist-server/server/structured-query-evidence.js +47 -0
- package/dist-server/server/structured-runtime.d.ts +52 -0
- package/dist-server/server/structured-runtime.js +130 -0
- package/dist-server/server/structured-scope.d.ts +4 -0
- package/dist-server/server/structured-scope.js +12 -0
- package/dist-server/server/turn.d.ts +6 -1
- package/dist-server/server/turn.js +82 -25
- package/dist-server/server/wire-types.d.ts +67 -3
- package/dist-server/server/wren-profiles.js +1 -1
- package/managed-wren/README.md +60 -0
- package/managed-wren/dependency-wheels.json +282 -0
- package/managed-wren/manifest.json +373 -0
- package/managed-wren/release-inputs.json +45 -0
- package/managed-wren/runtime-tree.cjs +115 -0
- package/managed-wren/source-inputs.json +40 -0
- package/package.json +15 -6
- package/profiles/genbi-default/context/binding.yml +17 -7
- package/profiles/genbi-default/context/context.json +146 -0
- package/profiles/genbi-default/ir.golden.json +39 -43
- package/profiles/genbi-default/profile.yml +31 -0
- package/profiles/genbi-enrich-context/context/binding.yml +14 -3
- package/profiles/genbi-enrich-context/context/context.json +146 -0
- package/profiles/genbi-enrich-context/ir.golden.json +4 -5
- package/profiles/genbi-monitor/context/binding.yml +13 -5
- package/profiles/genbi-monitor/context/context.json +146 -0
- package/profiles/genbi-monitor/ir.golden.json +13 -7
- package/profiles/genbi-report/context/binding.yml +19 -0
- package/profiles/genbi-report/context/context.json +223 -0
- package/profiles/genbi-report/ir.golden.json +328 -0
- package/profiles/genbi-report/profile.yml +38 -0
- package/profiles/genbi-setup/ir.golden.json +3 -1
- package/dist/assets/index-Xs4q7pNo.css +0 -1
|
@@ -0,0 +1,518 @@
|
|
|
1
|
+
export const PI_AI_ADAPTER_ID = "pi-ai";
|
|
2
|
+
const PI_AI_PACKAGE = "@earendil-works/pi-ai";
|
|
3
|
+
const METADATA_KEY = "pi-ai";
|
|
4
|
+
let runtime;
|
|
5
|
+
/**
|
|
6
|
+
* Loads pi-ai on first use. It is an optional dependency (it needs Node
|
|
7
|
+
* >= 22.19 and pulls several vendor SDKs), so nothing imports it until a
|
|
8
|
+
* gateway-bound model is actually called.
|
|
9
|
+
*/
|
|
10
|
+
function loadPiAi() {
|
|
11
|
+
runtime ??= Promise.all([
|
|
12
|
+
import("@earendil-works/pi-ai"),
|
|
13
|
+
import("@earendil-works/pi-ai/providers/all"),
|
|
14
|
+
import("@earendil-works/pi-ai/api/openai-completions.lazy"),
|
|
15
|
+
]).then(([core, builtins, completions]) => ({ core, builtins, completions }), (error) => {
|
|
16
|
+
runtime = undefined;
|
|
17
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
18
|
+
throw new Error(`The pi-ai adapter (gateway mode) needs the optional dependency ${PI_AI_PACKAGE}, which could not be loaded: ${reason}. ` +
|
|
19
|
+
`Install it alongside this package on Node.js >= 22.19, or choose a different runtime mode.`);
|
|
20
|
+
});
|
|
21
|
+
return runtime;
|
|
22
|
+
}
|
|
23
|
+
// No environment variables and no files: credentials only ever arrive as the
|
|
24
|
+
// per-request `apiKey`.
|
|
25
|
+
const SEALED_AUTH_CONTEXT = {
|
|
26
|
+
env: async () => undefined,
|
|
27
|
+
fileExists: async () => false,
|
|
28
|
+
};
|
|
29
|
+
/** An API-key auth that only accepts the key passed with the request. */
|
|
30
|
+
function explicitKeyAuth(name) {
|
|
31
|
+
return {
|
|
32
|
+
name,
|
|
33
|
+
resolve: async ({ credential }) => ({ auth: credential?.key ? { apiKey: credential.key } : {} }),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
const ZERO_COST = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
|
|
37
|
+
async function resolveTarget(config) {
|
|
38
|
+
const { core, builtins, completions } = await loadPiAi();
|
|
39
|
+
// An empty in-memory credential store: nothing stored, so pi-ai never
|
|
40
|
+
// resolves a stored or OAuth credential.
|
|
41
|
+
const models = core.createModels({ credentials: new core.InMemoryCredentialStore(), authContext: SEALED_AUTH_CONTEXT });
|
|
42
|
+
if (config.baseUrl !== undefined) {
|
|
43
|
+
const model = {
|
|
44
|
+
id: config.model,
|
|
45
|
+
name: config.model,
|
|
46
|
+
api: "openai-completions",
|
|
47
|
+
provider: config.provider,
|
|
48
|
+
baseUrl: config.baseUrl,
|
|
49
|
+
reasoning: false,
|
|
50
|
+
input: ["text", "image"],
|
|
51
|
+
cost: ZERO_COST,
|
|
52
|
+
contextWindow: 128_000,
|
|
53
|
+
maxTokens: 16_384,
|
|
54
|
+
};
|
|
55
|
+
models.setProvider(core.createProvider({
|
|
56
|
+
id: config.provider,
|
|
57
|
+
name: config.provider,
|
|
58
|
+
baseUrl: config.baseUrl,
|
|
59
|
+
auth: { apiKey: explicitKeyAuth(config.provider) },
|
|
60
|
+
models: [model],
|
|
61
|
+
api: completions.openAICompletionsApi(),
|
|
62
|
+
}));
|
|
63
|
+
return { models, model };
|
|
64
|
+
}
|
|
65
|
+
const builtin = builtins.builtinProviders().find((candidate) => candidate.id === config.provider);
|
|
66
|
+
if (builtin === undefined) {
|
|
67
|
+
throw new Error(`pi-ai has no built-in provider "${config.provider}"; set a base URL to use it as a custom OpenAI-compatible endpoint. ` +
|
|
68
|
+
`Known providers: ${builtins.builtinProviders().map((candidate) => candidate.id).join(", ")}`);
|
|
69
|
+
}
|
|
70
|
+
if (builtin.auth.apiKey === undefined) {
|
|
71
|
+
throw new Error(`pi-ai provider "${config.provider}" only supports OAuth sign-in, which gateway mode does not use.`);
|
|
72
|
+
}
|
|
73
|
+
// Register the provider with its API-key auth only: OAuth sign-in is never offered.
|
|
74
|
+
const apiKeyOnly = { ...builtin, auth: { apiKey: builtin.auth.apiKey } };
|
|
75
|
+
models.setProvider(apiKeyOnly);
|
|
76
|
+
const model = models.getModel(config.provider, config.model);
|
|
77
|
+
if (model === undefined) {
|
|
78
|
+
// A provider may mix wire APIs across its models, so an uncatalogued id
|
|
79
|
+
// cannot safely borrow another model's API. Fail instead of guessing.
|
|
80
|
+
throw new Error(`pi-ai's catalog has no model "${config.model}" for provider "${config.provider}"; ` +
|
|
81
|
+
`pick a catalogued model id, or set a base URL to call it as a custom OpenAI-compatible endpoint.`);
|
|
82
|
+
}
|
|
83
|
+
return { models, model };
|
|
84
|
+
}
|
|
85
|
+
function unsupported(warnings, feature, details) {
|
|
86
|
+
warnings.push(details === undefined ? { type: "unsupported", feature } : { type: "unsupported", feature, details });
|
|
87
|
+
}
|
|
88
|
+
function piOptions(options) {
|
|
89
|
+
return options?.[METADATA_KEY];
|
|
90
|
+
}
|
|
91
|
+
function stringOption(options, key) {
|
|
92
|
+
const value = piOptions(options)?.[key];
|
|
93
|
+
return typeof value === "string" ? value : undefined;
|
|
94
|
+
}
|
|
95
|
+
function toBase64(data) {
|
|
96
|
+
return typeof data === "string" ? data : Buffer.from(data).toString("base64");
|
|
97
|
+
}
|
|
98
|
+
function parseToolInput(input) {
|
|
99
|
+
if (typeof input === "string") {
|
|
100
|
+
try {
|
|
101
|
+
const parsed = JSON.parse(input);
|
|
102
|
+
return isJsonObject(parsed) ? parsed : {};
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
return {};
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return isJsonObject(input) ? input : {};
|
|
109
|
+
}
|
|
110
|
+
function isJsonObject(value) {
|
|
111
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
112
|
+
}
|
|
113
|
+
function toolResultContent(output) {
|
|
114
|
+
switch (output.type) {
|
|
115
|
+
case "text":
|
|
116
|
+
return { content: [{ type: "text", text: output.value }], isError: false };
|
|
117
|
+
case "json":
|
|
118
|
+
return { content: [{ type: "text", text: JSON.stringify(output.value) }], isError: false };
|
|
119
|
+
case "error-text":
|
|
120
|
+
return { content: [{ type: "text", text: output.value }], isError: true };
|
|
121
|
+
case "error-json":
|
|
122
|
+
return { content: [{ type: "text", text: JSON.stringify(output.value) }], isError: true };
|
|
123
|
+
case "execution-denied":
|
|
124
|
+
return { content: [{ type: "text", text: output.reason ?? "Tool execution was denied." }], isError: true };
|
|
125
|
+
case "content": {
|
|
126
|
+
const content = [];
|
|
127
|
+
for (const part of output.value) {
|
|
128
|
+
if (part.type === "text")
|
|
129
|
+
content.push({ type: "text", text: part.text });
|
|
130
|
+
else if (part.type === "file" && part.mediaType.startsWith("image/") && part.data.type === "data") {
|
|
131
|
+
content.push({ type: "image", data: toBase64(part.data.data), mimeType: part.mediaType });
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return { content, isError: false };
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
const ZERO_USAGE = {
|
|
139
|
+
input: 0,
|
|
140
|
+
output: 0,
|
|
141
|
+
cacheRead: 0,
|
|
142
|
+
cacheWrite: 0,
|
|
143
|
+
totalTokens: 0,
|
|
144
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
|
145
|
+
};
|
|
146
|
+
/** Maps the AI SDK prompt and tools onto a pi-ai `Context`. */
|
|
147
|
+
export function toPiContext(prompt, options, model, warnings) {
|
|
148
|
+
const system = [];
|
|
149
|
+
const messages = [];
|
|
150
|
+
const timestamp = Date.now();
|
|
151
|
+
for (const message of prompt) {
|
|
152
|
+
switch (message.role) {
|
|
153
|
+
case "system":
|
|
154
|
+
system.push(message.content);
|
|
155
|
+
break;
|
|
156
|
+
case "user": {
|
|
157
|
+
const content = [];
|
|
158
|
+
for (const part of message.content) {
|
|
159
|
+
if (part.type === "text")
|
|
160
|
+
content.push({ type: "text", text: part.text });
|
|
161
|
+
else if (part.data.type === "text")
|
|
162
|
+
content.push({ type: "text", text: part.data.text });
|
|
163
|
+
else if (part.mediaType.startsWith("image/") && part.data.type === "data") {
|
|
164
|
+
content.push({ type: "image", data: toBase64(part.data.data), mimeType: part.mediaType });
|
|
165
|
+
}
|
|
166
|
+
else
|
|
167
|
+
unsupported(warnings, "file part", `${part.mediaType} (${part.data.type})`);
|
|
168
|
+
}
|
|
169
|
+
messages.push({ role: "user", content, timestamp });
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
case "assistant": {
|
|
173
|
+
const content = [];
|
|
174
|
+
for (const part of message.content) {
|
|
175
|
+
if (part.type === "text")
|
|
176
|
+
content.push({ type: "text", text: part.text });
|
|
177
|
+
else if (part.type === "reasoning") {
|
|
178
|
+
const signature = stringOption(part.providerOptions, "signature");
|
|
179
|
+
content.push({ type: "thinking", thinking: part.text, ...(signature !== undefined ? { thinkingSignature: signature } : {}) });
|
|
180
|
+
}
|
|
181
|
+
else if (part.type === "tool-call") {
|
|
182
|
+
const thoughtSignature = stringOption(part.providerOptions, "thoughtSignature");
|
|
183
|
+
content.push({
|
|
184
|
+
type: "toolCall",
|
|
185
|
+
id: part.toolCallId,
|
|
186
|
+
name: part.toolName,
|
|
187
|
+
arguments: parseToolInput(part.input),
|
|
188
|
+
...(thoughtSignature !== undefined ? { thoughtSignature } : {}),
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
else if (part.type !== "tool-result")
|
|
192
|
+
unsupported(warnings, `assistant ${part.type} part`);
|
|
193
|
+
}
|
|
194
|
+
const assistant = {
|
|
195
|
+
role: "assistant",
|
|
196
|
+
content,
|
|
197
|
+
api: model.api,
|
|
198
|
+
provider: model.provider,
|
|
199
|
+
model: model.id,
|
|
200
|
+
usage: ZERO_USAGE,
|
|
201
|
+
stopReason: content.some((part) => part.type === "toolCall") ? "toolUse" : "stop",
|
|
202
|
+
timestamp,
|
|
203
|
+
};
|
|
204
|
+
messages.push(assistant);
|
|
205
|
+
break;
|
|
206
|
+
}
|
|
207
|
+
case "tool":
|
|
208
|
+
for (const part of message.content) {
|
|
209
|
+
if (part.type !== "tool-result")
|
|
210
|
+
continue;
|
|
211
|
+
const result = {
|
|
212
|
+
role: "toolResult",
|
|
213
|
+
toolCallId: part.toolCallId,
|
|
214
|
+
toolName: part.toolName,
|
|
215
|
+
...toolResultContent(part.output),
|
|
216
|
+
timestamp,
|
|
217
|
+
};
|
|
218
|
+
messages.push(result);
|
|
219
|
+
}
|
|
220
|
+
break;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
const responseFormat = options.responseFormat;
|
|
224
|
+
if (responseFormat?.type === "json") {
|
|
225
|
+
// pi-ai has no provider-neutral structured-output switch; ask for JSON in
|
|
226
|
+
// the instructions and let the caller validate the text as usual.
|
|
227
|
+
warnings.push({ type: "compatibility", feature: "responseFormat", details: "requested through the system prompt" });
|
|
228
|
+
system.push(responseFormat.schema !== undefined
|
|
229
|
+
? `Respond with only a JSON value that matches this JSON schema:\n${JSON.stringify(responseFormat.schema)}`
|
|
230
|
+
: "Respond with only a JSON value.");
|
|
231
|
+
}
|
|
232
|
+
const tools = [];
|
|
233
|
+
const toolChoice = options.toolChoice?.type ?? "auto";
|
|
234
|
+
if (toolChoice === "required" || toolChoice === "tool")
|
|
235
|
+
unsupported(warnings, `toolChoice ${toolChoice}`, "sent as auto");
|
|
236
|
+
if (toolChoice !== "none") {
|
|
237
|
+
for (const tool of options.tools ?? []) {
|
|
238
|
+
if (tool.type !== "function") {
|
|
239
|
+
unsupported(warnings, "provider tool", tool.name);
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
// pi-ai's TypeBox schemas are plain JSON Schema objects at runtime.
|
|
243
|
+
tools.push({ name: tool.name, description: tool.description ?? "", parameters: tool.inputSchema });
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
return {
|
|
247
|
+
...(system.length > 0 ? { systemPrompt: system.join("\n\n") } : {}),
|
|
248
|
+
messages,
|
|
249
|
+
...(tools.length > 0 ? { tools } : {}),
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
/** Maps pi-ai's stop reason onto the AI SDK's unified finish reason. */
|
|
253
|
+
export function toFinishReason(stopReason) {
|
|
254
|
+
switch (stopReason) {
|
|
255
|
+
case "stop":
|
|
256
|
+
return { unified: "stop", raw: stopReason };
|
|
257
|
+
case "toolUse":
|
|
258
|
+
return { unified: "tool-calls", raw: stopReason };
|
|
259
|
+
case "length":
|
|
260
|
+
return { unified: "length", raw: stopReason };
|
|
261
|
+
case "error":
|
|
262
|
+
return { unified: "error", raw: stopReason };
|
|
263
|
+
default:
|
|
264
|
+
return { unified: "other", raw: stopReason };
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
/** Maps pi-ai usage (where `input` excludes cached tokens) onto AI SDK usage. */
|
|
268
|
+
export function toUsage(usage) {
|
|
269
|
+
const reasoning = usage.reasoning;
|
|
270
|
+
return {
|
|
271
|
+
inputTokens: {
|
|
272
|
+
total: usage.input + usage.cacheRead + usage.cacheWrite,
|
|
273
|
+
noCache: usage.input,
|
|
274
|
+
cacheRead: usage.cacheRead,
|
|
275
|
+
cacheWrite: usage.cacheWrite,
|
|
276
|
+
},
|
|
277
|
+
outputTokens: {
|
|
278
|
+
total: usage.output,
|
|
279
|
+
text: reasoning !== undefined ? Math.max(0, usage.output - reasoning) : usage.output,
|
|
280
|
+
reasoning,
|
|
281
|
+
},
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
function providerMetadata(message) {
|
|
285
|
+
const metadata = {
|
|
286
|
+
provider: message.provider,
|
|
287
|
+
api: message.api,
|
|
288
|
+
cost: { ...message.usage.cost },
|
|
289
|
+
};
|
|
290
|
+
if (message.responseModel !== undefined)
|
|
291
|
+
metadata["responseModel"] = message.responseModel;
|
|
292
|
+
return { [METADATA_KEY]: metadata };
|
|
293
|
+
}
|
|
294
|
+
function signatureMetadata(key, value) {
|
|
295
|
+
return value !== undefined ? { [METADATA_KEY]: { [key]: value } } : undefined;
|
|
296
|
+
}
|
|
297
|
+
function toContent(message) {
|
|
298
|
+
const content = [];
|
|
299
|
+
for (const part of message.content) {
|
|
300
|
+
if (part.type === "text")
|
|
301
|
+
content.push({ type: "text", text: part.text });
|
|
302
|
+
else if (part.type === "thinking") {
|
|
303
|
+
const metadata = signatureMetadata("signature", part.thinkingSignature);
|
|
304
|
+
content.push({ type: "reasoning", text: part.thinking, ...(metadata ? { providerMetadata: metadata } : {}) });
|
|
305
|
+
}
|
|
306
|
+
else {
|
|
307
|
+
const metadata = signatureMetadata("thoughtSignature", part.thoughtSignature);
|
|
308
|
+
content.push({
|
|
309
|
+
type: "tool-call",
|
|
310
|
+
toolCallId: part.id,
|
|
311
|
+
toolName: part.name,
|
|
312
|
+
input: JSON.stringify(part.arguments),
|
|
313
|
+
...(metadata ? { providerMetadata: metadata } : {}),
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
return content;
|
|
318
|
+
}
|
|
319
|
+
class PiAiLanguageModel {
|
|
320
|
+
config;
|
|
321
|
+
specificationVersion = "v4";
|
|
322
|
+
provider;
|
|
323
|
+
modelId;
|
|
324
|
+
supportedUrls = {};
|
|
325
|
+
target;
|
|
326
|
+
constructor(config) {
|
|
327
|
+
this.config = config;
|
|
328
|
+
this.provider = `${PI_AI_ADAPTER_ID}.${config.provider}`;
|
|
329
|
+
this.modelId = config.model;
|
|
330
|
+
}
|
|
331
|
+
resolve() {
|
|
332
|
+
this.target ??= resolveTarget(this.config).catch((error) => {
|
|
333
|
+
this.target = undefined;
|
|
334
|
+
throw error;
|
|
335
|
+
});
|
|
336
|
+
return this.target;
|
|
337
|
+
}
|
|
338
|
+
/** Removes the configured key from any text that may surface in an error or log. */
|
|
339
|
+
redact(text) {
|
|
340
|
+
const key = this.config.apiKey;
|
|
341
|
+
return key !== undefined && key.length > 0 ? text.split(key).join("[redacted]") : text;
|
|
342
|
+
}
|
|
343
|
+
failure(message) {
|
|
344
|
+
const detail = message.errorMessage ?? message.stopReason;
|
|
345
|
+
return new Error(this.redact(`pi-ai ${this.config.provider}/${this.config.model} request failed: ${detail}`));
|
|
346
|
+
}
|
|
347
|
+
async start(options) {
|
|
348
|
+
const { models, model } = await this.resolve();
|
|
349
|
+
const warnings = [];
|
|
350
|
+
const context = toPiContext(options.prompt, options, model, warnings);
|
|
351
|
+
for (const [feature, value] of [
|
|
352
|
+
["topP", options.topP],
|
|
353
|
+
["topK", options.topK],
|
|
354
|
+
["presencePenalty", options.presencePenalty],
|
|
355
|
+
["frequencyPenalty", options.frequencyPenalty],
|
|
356
|
+
["seed", options.seed],
|
|
357
|
+
["stopSequences", options.stopSequences],
|
|
358
|
+
]) {
|
|
359
|
+
if (value !== undefined)
|
|
360
|
+
unsupported(warnings, feature);
|
|
361
|
+
}
|
|
362
|
+
if (options.reasoning !== undefined && options.reasoning !== "provider-default")
|
|
363
|
+
unsupported(warnings, "reasoning");
|
|
364
|
+
const headers = { ...this.config.headers };
|
|
365
|
+
for (const [name, value] of Object.entries(options.headers ?? {}))
|
|
366
|
+
if (value !== undefined)
|
|
367
|
+
headers[name] = value;
|
|
368
|
+
const streamOptions = {
|
|
369
|
+
...(this.config.apiKey !== undefined ? { apiKey: this.config.apiKey } : {}),
|
|
370
|
+
...(Object.keys(headers).length > 0 ? { headers } : {}),
|
|
371
|
+
...(this.config.fetch !== undefined ? { fetch: this.config.fetch } : {}),
|
|
372
|
+
...(options.abortSignal !== undefined ? { signal: options.abortSignal } : {}),
|
|
373
|
+
...(options.maxOutputTokens !== undefined ? { maxTokens: options.maxOutputTokens } : {}),
|
|
374
|
+
...(options.temperature !== undefined ? { temperature: options.temperature } : {}),
|
|
375
|
+
};
|
|
376
|
+
return { events: models.stream(model, context, streamOptions), warnings };
|
|
377
|
+
}
|
|
378
|
+
async doGenerate(options) {
|
|
379
|
+
const { events, warnings } = await this.start(options);
|
|
380
|
+
let final;
|
|
381
|
+
for await (const event of events) {
|
|
382
|
+
if (event.type === "done")
|
|
383
|
+
final = event.message;
|
|
384
|
+
else if (event.type === "error")
|
|
385
|
+
throw this.failure(event.error);
|
|
386
|
+
}
|
|
387
|
+
if (final === undefined)
|
|
388
|
+
throw new Error(`pi-ai ${this.config.provider}/${this.config.model} stream ended without a result`);
|
|
389
|
+
return {
|
|
390
|
+
content: toContent(final),
|
|
391
|
+
finishReason: toFinishReason(final.stopReason),
|
|
392
|
+
usage: toUsage(final.usage),
|
|
393
|
+
providerMetadata: providerMetadata(final),
|
|
394
|
+
response: {
|
|
395
|
+
...(final.responseId !== undefined ? { id: final.responseId } : {}),
|
|
396
|
+
modelId: final.responseModel ?? final.model,
|
|
397
|
+
timestamp: new Date(final.timestamp),
|
|
398
|
+
},
|
|
399
|
+
warnings,
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
async doStream(options) {
|
|
403
|
+
const { events, warnings } = await this.start(options);
|
|
404
|
+
const failure = (message) => this.failure(message);
|
|
405
|
+
const stream = new ReadableStream({
|
|
406
|
+
async start(controller) {
|
|
407
|
+
controller.enqueue({ type: "stream-start", warnings });
|
|
408
|
+
const toolInputIds = new Map();
|
|
409
|
+
try {
|
|
410
|
+
for await (const event of events) {
|
|
411
|
+
for (const part of toStreamParts(event, toolInputIds, failure))
|
|
412
|
+
controller.enqueue(part);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
catch (error) {
|
|
416
|
+
controller.enqueue({ type: "error", error });
|
|
417
|
+
}
|
|
418
|
+
controller.close();
|
|
419
|
+
},
|
|
420
|
+
});
|
|
421
|
+
return { stream };
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
/** Maps one pi-ai stream event onto zero or more AI SDK stream parts. */
|
|
425
|
+
export function toStreamParts(event, toolInputIds, failure) {
|
|
426
|
+
switch (event.type) {
|
|
427
|
+
case "start":
|
|
428
|
+
return [];
|
|
429
|
+
case "text_start":
|
|
430
|
+
return [{ type: "text-start", id: `text-${event.contentIndex}` }];
|
|
431
|
+
case "text_delta":
|
|
432
|
+
return [{ type: "text-delta", id: `text-${event.contentIndex}`, delta: event.delta }];
|
|
433
|
+
case "text_end":
|
|
434
|
+
return [{ type: "text-end", id: `text-${event.contentIndex}` }];
|
|
435
|
+
case "thinking_start":
|
|
436
|
+
return [{ type: "reasoning-start", id: `reasoning-${event.contentIndex}` }];
|
|
437
|
+
case "thinking_delta":
|
|
438
|
+
return [{ type: "reasoning-delta", id: `reasoning-${event.contentIndex}`, delta: event.delta }];
|
|
439
|
+
case "thinking_end": {
|
|
440
|
+
const block = event.partial.content[event.contentIndex];
|
|
441
|
+
const metadata = signatureMetadata("signature", block?.type === "thinking" ? block.thinkingSignature : undefined);
|
|
442
|
+
return [{ type: "reasoning-end", id: `reasoning-${event.contentIndex}`, ...(metadata ? { providerMetadata: metadata } : {}) }];
|
|
443
|
+
}
|
|
444
|
+
case "toolcall_start": {
|
|
445
|
+
const block = event.partial.content[event.contentIndex];
|
|
446
|
+
const call = block?.type === "toolCall" ? block : undefined;
|
|
447
|
+
const id = call?.id || `tool-${event.contentIndex}`;
|
|
448
|
+
toolInputIds.set(event.contentIndex, id);
|
|
449
|
+
return [{ type: "tool-input-start", id, toolName: call?.name ?? "" }];
|
|
450
|
+
}
|
|
451
|
+
case "toolcall_delta": {
|
|
452
|
+
const id = toolInputIds.get(event.contentIndex) ?? `tool-${event.contentIndex}`;
|
|
453
|
+
return [{ type: "tool-input-delta", id, delta: event.delta }];
|
|
454
|
+
}
|
|
455
|
+
case "toolcall_end": {
|
|
456
|
+
const id = toolInputIds.get(event.contentIndex) ?? `tool-${event.contentIndex}`;
|
|
457
|
+
const metadata = signatureMetadata("thoughtSignature", event.toolCall.thoughtSignature);
|
|
458
|
+
return [
|
|
459
|
+
{ type: "tool-input-end", id },
|
|
460
|
+
{
|
|
461
|
+
type: "tool-call",
|
|
462
|
+
toolCallId: event.toolCall.id || id,
|
|
463
|
+
toolName: event.toolCall.name,
|
|
464
|
+
input: JSON.stringify(event.toolCall.arguments),
|
|
465
|
+
...(metadata ? { providerMetadata: metadata } : {}),
|
|
466
|
+
},
|
|
467
|
+
];
|
|
468
|
+
}
|
|
469
|
+
case "done":
|
|
470
|
+
return [
|
|
471
|
+
{
|
|
472
|
+
type: "response-metadata",
|
|
473
|
+
...(event.message.responseId !== undefined ? { id: event.message.responseId } : {}),
|
|
474
|
+
modelId: event.message.responseModel ?? event.message.model,
|
|
475
|
+
timestamp: new Date(event.message.timestamp),
|
|
476
|
+
},
|
|
477
|
+
{
|
|
478
|
+
type: "finish",
|
|
479
|
+
finishReason: toFinishReason(event.message.stopReason),
|
|
480
|
+
usage: toUsage(event.message.usage),
|
|
481
|
+
providerMetadata: providerMetadata(event.message),
|
|
482
|
+
},
|
|
483
|
+
];
|
|
484
|
+
case "error":
|
|
485
|
+
return [
|
|
486
|
+
{ type: "error", error: failure(event.error) },
|
|
487
|
+
{
|
|
488
|
+
type: "finish",
|
|
489
|
+
finishReason: toFinishReason(event.error.stopReason),
|
|
490
|
+
usage: toUsage(event.error.usage),
|
|
491
|
+
providerMetadata: providerMetadata(event.error),
|
|
492
|
+
},
|
|
493
|
+
];
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
/**
|
|
497
|
+
* Lists the catalog that ships inside pi-ai — no network, no credentials.
|
|
498
|
+
* OAuth-only providers are left out because gateway mode cannot use them.
|
|
499
|
+
*/
|
|
500
|
+
export async function listPiAiCatalog(provider) {
|
|
501
|
+
const { builtins } = await loadPiAi();
|
|
502
|
+
const providers = builtins.builtinProviders().filter((candidate) => candidate.auth.apiKey !== undefined).map((candidate) => candidate.id);
|
|
503
|
+
if (provider === undefined || !providers.includes(provider))
|
|
504
|
+
return { providers, models: [] };
|
|
505
|
+
const catalogued = builtins.getBuiltinModels(provider);
|
|
506
|
+
return { providers, models: catalogued.map((model) => ({ id: model.id, name: model.name })) };
|
|
507
|
+
}
|
|
508
|
+
function requireNonEmpty(value, field) {
|
|
509
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
510
|
+
throw new Error(`pi-ai adapter config requires a non-empty "${field}"`);
|
|
511
|
+
}
|
|
512
|
+
return value;
|
|
513
|
+
}
|
|
514
|
+
export function createPiAiAdapter(config) {
|
|
515
|
+
requireNonEmpty(config.provider, "provider");
|
|
516
|
+
requireNonEmpty(config.model, "model");
|
|
517
|
+
return new PiAiLanguageModel(config);
|
|
518
|
+
}
|
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
import type { LanguageModel } from "ai";
|
|
2
2
|
import type { ProviderRegistry } from "./registry.js";
|
|
3
|
-
|
|
3
|
+
import type { DisclosurePolicy, Zone, ZoneRoles } from "./zone.js";
|
|
4
|
+
/**
|
|
5
|
+
* Names an adapter id + the config to realize it with, plus the deployment
|
|
6
|
+
* zone the realized model runs in. `zone` is a fact about where the endpoint
|
|
7
|
+
* is deployed and who may see private data through it; it is declared by the
|
|
8
|
+
* operator on the binding and never inferred from the endpoint URL, the model
|
|
9
|
+
* id or the adapter kind.
|
|
10
|
+
*/
|
|
4
11
|
export interface AdapterSpec<Config = unknown> {
|
|
5
12
|
readonly adapter: string;
|
|
6
13
|
readonly config: Config;
|
|
14
|
+
readonly zone?: Zone;
|
|
7
15
|
}
|
|
8
16
|
/**
|
|
9
17
|
* Runtime-injected tier -> adapter binding. This is a plain object supplied
|
|
@@ -11,13 +19,46 @@ export interface AdapterSpec<Config = unknown> {
|
|
|
11
19
|
* be committed to a bundle fixture. `tier` names are open strings owned by
|
|
12
20
|
* the bundle's steps; the binding only needs to cover the tiers a given run
|
|
13
21
|
* actually uses.
|
|
22
|
+
*
|
|
23
|
+
* Keys are either a bare tier name (`strong`), shared by every mount that
|
|
24
|
+
* names that tier, or a mount-qualified `<mount>/<tier>` key
|
|
25
|
+
* (`answer_query/strong`) that wins for that one mount. Resolution is keyed on
|
|
26
|
+
* (mount, tier): the exact mount-qualified key first, then the bare tier. A
|
|
27
|
+
* flat map with no `/` keys is therefore still a complete, valid binding.
|
|
14
28
|
*/
|
|
15
29
|
export interface TierBinding {
|
|
16
30
|
readonly tiers: Readonly<Record<string, AdapterSpec>>;
|
|
31
|
+
/** The egress disclosure policy the verification step enforces (hybrid split only). */
|
|
32
|
+
readonly disclosurePolicy?: DisclosurePolicy;
|
|
33
|
+
/** Named non-step tiers (judge, render) the zone gate must also check. */
|
|
34
|
+
readonly roles?: ZoneRoles;
|
|
17
35
|
}
|
|
18
|
-
/**
|
|
19
|
-
export declare function
|
|
36
|
+
/** The binding key a `(mount, tier)` pair is looked up under before falling back to the bare tier. */
|
|
37
|
+
export declare function tierBindingKey(mount: string, tier: string): string;
|
|
38
|
+
/** Splits a binding key into its mount (when qualified) and tier. */
|
|
39
|
+
export declare function parseTierBindingKey(key: string): {
|
|
40
|
+
readonly mount?: string;
|
|
41
|
+
readonly tier: string;
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Resolves the `AdapterSpec` for a tier as seen from one mount: the exact
|
|
45
|
+
* `<mount>/<tier>` key wins, otherwise the bare tier key. Returns the key that
|
|
46
|
+
* matched so callers (the zone gate, the dry-run) can report which entry
|
|
47
|
+
* actually bound the step.
|
|
48
|
+
*/
|
|
49
|
+
export declare function resolveTierSpec(binding: TierBinding, tier: string, mount?: string): {
|
|
50
|
+
readonly key: string;
|
|
51
|
+
readonly spec: AdapterSpec;
|
|
52
|
+
} | undefined;
|
|
53
|
+
/** Resolve an open-string tier name to a concrete language model instance, keyed on (mount, tier). */
|
|
54
|
+
export declare function resolveTierModel(binding: TierBinding, tier: string, registry: ProviderRegistry, mount?: string): LanguageModel;
|
|
20
55
|
/** Convenience: resolve the model for a bundle step via its `tier` field. */
|
|
21
56
|
export declare function resolveStepModel(step: {
|
|
22
57
|
readonly tier: string;
|
|
23
|
-
}, binding: TierBinding, registry: ProviderRegistry): LanguageModel;
|
|
58
|
+
}, binding: TierBinding, registry: ProviderRegistry, mount?: string): LanguageModel;
|
|
59
|
+
/**
|
|
60
|
+
* Whether a binding carries any zone information at all. A binding with no
|
|
61
|
+
* `zone` on any spec, no disclosure policy and no roles is the pre-existing
|
|
62
|
+
* single-zone binding; the zone gate is armed only for zone-aware bindings.
|
|
63
|
+
*/
|
|
64
|
+
export declare function isZoneAwareBinding(binding: TierBinding): boolean;
|
|
@@ -1,13 +1,49 @@
|
|
|
1
1
|
import { UnknownTierError } from "./errors.js";
|
|
2
|
-
/**
|
|
3
|
-
export function
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
/** The binding key a `(mount, tier)` pair is looked up under before falling back to the bare tier. */
|
|
3
|
+
export function tierBindingKey(mount, tier) {
|
|
4
|
+
return `${mount}/${tier}`;
|
|
5
|
+
}
|
|
6
|
+
/** Splits a binding key into its mount (when qualified) and tier. */
|
|
7
|
+
export function parseTierBindingKey(key) {
|
|
8
|
+
const slash = key.indexOf("/");
|
|
9
|
+
if (slash <= 0 || slash === key.length - 1)
|
|
10
|
+
return { tier: key };
|
|
11
|
+
return { mount: key.slice(0, slash), tier: key.slice(slash + 1) };
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Resolves the `AdapterSpec` for a tier as seen from one mount: the exact
|
|
15
|
+
* `<mount>/<tier>` key wins, otherwise the bare tier key. Returns the key that
|
|
16
|
+
* matched so callers (the zone gate, the dry-run) can report which entry
|
|
17
|
+
* actually bound the step.
|
|
18
|
+
*/
|
|
19
|
+
export function resolveTierSpec(binding, tier, mount) {
|
|
20
|
+
if (mount !== undefined) {
|
|
21
|
+
const qualified = tierBindingKey(mount, tier);
|
|
22
|
+
const exact = binding.tiers[qualified];
|
|
23
|
+
if (exact)
|
|
24
|
+
return { key: qualified, spec: exact };
|
|
7
25
|
}
|
|
8
|
-
|
|
26
|
+
const shared = binding.tiers[tier];
|
|
27
|
+
return shared ? { key: tier, spec: shared } : undefined;
|
|
28
|
+
}
|
|
29
|
+
/** Resolve an open-string tier name to a concrete language model instance, keyed on (mount, tier). */
|
|
30
|
+
export function resolveTierModel(binding, tier, registry, mount) {
|
|
31
|
+
const resolved = resolveTierSpec(binding, tier, mount);
|
|
32
|
+
if (!resolved) {
|
|
33
|
+
throw new UnknownTierError(mount !== undefined ? tierBindingKey(mount, tier) : tier);
|
|
34
|
+
}
|
|
35
|
+
return registry.create(resolved.spec.adapter, resolved.spec.config);
|
|
9
36
|
}
|
|
10
37
|
/** Convenience: resolve the model for a bundle step via its `tier` field. */
|
|
11
|
-
export function resolveStepModel(step, binding, registry) {
|
|
12
|
-
return resolveTierModel(binding, step.tier, registry);
|
|
38
|
+
export function resolveStepModel(step, binding, registry, mount) {
|
|
39
|
+
return resolveTierModel(binding, step.tier, registry, mount);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Whether a binding carries any zone information at all. A binding with no
|
|
43
|
+
* `zone` on any spec, no disclosure policy and no roles is the pre-existing
|
|
44
|
+
* single-zone binding; the zone gate is armed only for zone-aware bindings.
|
|
45
|
+
*/
|
|
46
|
+
export function isZoneAwareBinding(binding) {
|
|
47
|
+
return binding.disclosurePolicy !== undefined || binding.roles !== undefined
|
|
48
|
+
|| Object.values(binding.tiers).some((spec) => spec.zone !== undefined);
|
|
13
49
|
}
|
|
@@ -1,18 +1,24 @@
|
|
|
1
1
|
export { UnknownAdapterError, UnknownTierError } from "./errors.js";
|
|
2
2
|
export { createProviderRegistry } from "./registry.js";
|
|
3
3
|
export type { AdapterFactory, ProviderRegistry } from "./registry.js";
|
|
4
|
-
export { resolveStepModel, resolveTierModel } from "./binding.js";
|
|
4
|
+
export { isZoneAwareBinding, parseTierBindingKey, resolveStepModel, resolveTierModel, resolveTierSpec, tierBindingKey, } from "./binding.js";
|
|
5
5
|
export type { AdapterSpec, TierBinding } from "./binding.js";
|
|
6
|
+
export { ANSWER_SHAPES, ZONES, answerShapeSchema, disclosurePolicySchema, parseDisclosurePolicy, parseTierBindingDocument, readTierBindingFile, TierBindingParseError, tierBindingDocumentSchema, zoneRolesSchema, zoneSchema, } from "./zone.js";
|
|
7
|
+
export type { AnswerShape, DisclosurePolicy, TierBindingDocument, Zone, ZoneRoles } from "./zone.js";
|
|
6
8
|
export { createMockAdapter, MOCK_ADAPTER_ID } from "./adapters/mock.js";
|
|
7
9
|
export type { MockAdapterConfig } from "./adapters/mock.js";
|
|
8
|
-
export { createOpenAICompatibleAdapter, OPENAI_COMPATIBLE_ADAPTER_ID, } from "./adapters/openai-compatible.js";
|
|
10
|
+
export { createOpenAICompatibleAdapter, mergeRequestBody, OPENAI_COMPATIBLE_ADAPTER_ID, } from "./adapters/openai-compatible.js";
|
|
9
11
|
export type { OpenAICompatibleAdapterConfig } from "./adapters/openai-compatible.js";
|
|
10
12
|
export { ANTHROPIC_ADAPTER_ID, createAnthropicAdapter } from "./adapters/anthropic.js";
|
|
11
13
|
export type { AnthropicAdapterConfig } from "./adapters/anthropic.js";
|
|
14
|
+
export { createOpenAIAdapter, OPENAI_ADAPTER_ID } from "./adapters/openai.js";
|
|
15
|
+
export type { OpenAIAdapterConfig } from "./adapters/openai.js";
|
|
16
|
+
export { createPiAiAdapter, listPiAiCatalog, PI_AI_ADAPTER_ID } from "./adapters/pi-ai.js";
|
|
17
|
+
export type { PiAiAdapterConfig, PiAiCatalog } from "./adapters/pi-ai.js";
|
|
12
18
|
import { type ProviderRegistry } from "./registry.js";
|
|
13
19
|
/**
|
|
14
20
|
* A provider registry pre-populated with the built-in adapters
|
|
15
|
-
* (`mock`, `openai-compatible`, `anthropic`). Callers can still
|
|
21
|
+
* (`mock`, `openai-compatible`, `anthropic`, `openai`, `pi-ai`). Callers can still
|
|
16
22
|
* `register()` additional adapter ids onto the returned registry.
|
|
17
23
|
*/
|
|
18
24
|
export declare function createDefaultProviderRegistry(): ProviderRegistry;
|