skillrun 0.4.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.
- package/CHANGELOG.md +24 -0
- package/LICENSE +21 -0
- package/README.md +226 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +258 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +14 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +9 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/models.d.ts +68 -0
- package/dist/models.d.ts.map +1 -0
- package/dist/models.js +245 -0
- package/dist/models.js.map +1 -0
- package/dist/prompt.d.ts +3 -0
- package/dist/prompt.d.ts.map +1 -0
- package/dist/prompt.js +18 -0
- package/dist/prompt.js.map +1 -0
- package/dist/providers.d.ts +45 -0
- package/dist/providers.d.ts.map +1 -0
- package/dist/providers.js +235 -0
- package/dist/providers.js.map +1 -0
- package/dist/skills.d.ts +61 -0
- package/dist/skills.d.ts.map +1 -0
- package/dist/skills.js +253 -0
- package/dist/skills.js.map +1 -0
- package/package.json +84 -0
package/dist/models.d.ts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { type ProviderKind } from "./providers.js";
|
|
2
|
+
export type ModelTask = "coding" | "planning" | "general" | "embedding";
|
|
3
|
+
export interface ModelCapabilities {
|
|
4
|
+
tools: boolean;
|
|
5
|
+
thinking: boolean;
|
|
6
|
+
vision: boolean;
|
|
7
|
+
embedding: boolean;
|
|
8
|
+
}
|
|
9
|
+
export interface ModelProfile {
|
|
10
|
+
/** Model id exactly as the provider names it (e.g. "qwen3.5-coder:latest"). */
|
|
11
|
+
id: string;
|
|
12
|
+
provider: ProviderKind;
|
|
13
|
+
family: string;
|
|
14
|
+
parameterSize: string | null;
|
|
15
|
+
quantization: string | null;
|
|
16
|
+
contextLength: number | null;
|
|
17
|
+
capabilities: ModelCapabilities;
|
|
18
|
+
/** Heuristic 0..1 fitness for code work. */
|
|
19
|
+
codingScore: number;
|
|
20
|
+
/** Why the score landed where it did — surfaced by `skill-router models pick`. */
|
|
21
|
+
reason: string;
|
|
22
|
+
}
|
|
23
|
+
export interface ProfileHints {
|
|
24
|
+
provider?: ProviderKind;
|
|
25
|
+
capabilities?: Partial<ModelCapabilities>;
|
|
26
|
+
parameterSize?: string | null;
|
|
27
|
+
quantization?: string | null;
|
|
28
|
+
contextLength?: number | null;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Infer a model profile from its id plus any provider-reported hints.
|
|
32
|
+
* Inference is deliberately explicit so `models pick` can explain itself.
|
|
33
|
+
*/
|
|
34
|
+
export declare function inferProfile(id: string, hints?: ProfileHints): ModelProfile;
|
|
35
|
+
/** Enumerate models installed on a local Ollama server (GET /api/tags). */
|
|
36
|
+
export declare function listOllamaModels(baseUrl?: string, opts?: {
|
|
37
|
+
timeoutMs?: number;
|
|
38
|
+
}): Promise<ModelProfile[]>;
|
|
39
|
+
export interface PickOptions {
|
|
40
|
+
task?: ModelTask;
|
|
41
|
+
requireTools?: boolean;
|
|
42
|
+
requireThinking?: boolean;
|
|
43
|
+
minContext?: number;
|
|
44
|
+
exclude?: string[];
|
|
45
|
+
}
|
|
46
|
+
export interface PickResult {
|
|
47
|
+
model: ModelProfile;
|
|
48
|
+
score: number;
|
|
49
|
+
reason: string;
|
|
50
|
+
}
|
|
51
|
+
/** Choose the best available model for a task, or null when nothing qualifies. */
|
|
52
|
+
export declare function pickModel(models: ModelProfile[], opts?: PickOptions): PickResult | null;
|
|
53
|
+
export interface ResolvedModel extends PickResult {
|
|
54
|
+
source: "env" | "auto" | "default";
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Resolve the model to run with: explicit env pin → best installed local model →
|
|
58
|
+
* documented default. Never throws — a missing Ollama server degrades to the default.
|
|
59
|
+
*/
|
|
60
|
+
export declare function resolveModelForTask(opts?: {
|
|
61
|
+
task?: ModelTask;
|
|
62
|
+
baseUrl?: string;
|
|
63
|
+
env?: NodeJS.ProcessEnv;
|
|
64
|
+
timeoutMs?: number;
|
|
65
|
+
}): Promise<ResolvedModel>;
|
|
66
|
+
/** Plain-text table so any model set can be inspected from the terminal. */
|
|
67
|
+
export declare function formatModelTable(models: ModelProfile[]): string;
|
|
68
|
+
//# sourceMappingURL=models.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../src/models.ts"],"names":[],"mappings":"AAMA,OAAO,EAAkB,KAAK,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEnE,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,UAAU,GAAG,SAAS,GAAG,WAAW,CAAC;AAExE,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,+EAA+E;IAC/E,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,YAAY,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,YAAY,EAAE,iBAAiB,CAAC;IAChC,4CAA4C;IAC5C,WAAW,EAAE,MAAM,CAAC;IACpB,kFAAkF;IAClF,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB,YAAY,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAC1C,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAwCD;;;GAGG;AACH,wBAAgB,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,GAAE,YAAiB,GAAG,YAAY,CA2E/E;AAaD,2EAA2E;AAC3E,wBAAsB,gBAAgB,CACpC,OAAO,SAA2B,EAClC,IAAI,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,GAChC,OAAO,CAAC,YAAY,EAAE,CAAC,CAyBzB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,YAAY,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,kFAAkF;AAClF,wBAAgB,SAAS,CAAC,MAAM,EAAE,YAAY,EAAE,EAAE,IAAI,GAAE,WAAgB,GAAG,UAAU,GAAG,IAAI,CAgC3F;AAED,MAAM,WAAW,aAAc,SAAQ,UAAU;IAC/C,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,SAAS,CAAC;CACpC;AAED;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,IAAI,GAAE;IAAE,IAAI,CAAC,EAAE,SAAS,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,GAC7F,OAAO,CAAC,aAAa,CAAC,CAiCxB;AAED,4EAA4E;AAC5E,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,CAoB/D"}
|
package/dist/models.js
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
// Model capability detection + selection.
|
|
2
|
+
//
|
|
3
|
+
// The router must run on ANY coding model — a 9B local coder, a 0.5B tool model,
|
|
4
|
+
// or a hosted frontier model. This module answers two questions deterministically:
|
|
5
|
+
// 1. what can a model actually do (tools / thinking / vision / context)?
|
|
6
|
+
// 2. which available model is the best fit for a task?
|
|
7
|
+
import { DEFAULT_MODELS } from "./providers.js";
|
|
8
|
+
const FAMILIES = [
|
|
9
|
+
"qwen",
|
|
10
|
+
"deepseek",
|
|
11
|
+
"llama",
|
|
12
|
+
"gemma",
|
|
13
|
+
"mistral",
|
|
14
|
+
"codellama",
|
|
15
|
+
"starcoder",
|
|
16
|
+
"granite",
|
|
17
|
+
"phi",
|
|
18
|
+
"ornith",
|
|
19
|
+
"claude",
|
|
20
|
+
"gpt",
|
|
21
|
+
"o1",
|
|
22
|
+
"o3",
|
|
23
|
+
];
|
|
24
|
+
const CODING_HINTS = /\b(coder|code|starcoder|codestral|deepseek-coder|granite-code|qwen-coder)\b/i;
|
|
25
|
+
const THINK_HINTS = /\b(think|thinking|reason|reasoning|r1|qwq|o1|o3|deepseek-v4)\b/i;
|
|
26
|
+
const VISION_HINTS = /\b(vl|vision|llava|multimodal)\b/i;
|
|
27
|
+
const EMBED_HINTS = /\b(embed|embedding|bge|nomic|gte)\b/i;
|
|
28
|
+
function familyOf(id) {
|
|
29
|
+
const base = id.toLowerCase().split(":")[0] ?? id.toLowerCase();
|
|
30
|
+
const found = FAMILIES.find((f) => base.includes(f));
|
|
31
|
+
if (found)
|
|
32
|
+
return found;
|
|
33
|
+
return base.split(/[-_.]/)[0] ?? base;
|
|
34
|
+
}
|
|
35
|
+
function paramsInBillions(parameterSize) {
|
|
36
|
+
if (!parameterSize)
|
|
37
|
+
return null;
|
|
38
|
+
const m = /([\d.]+)\s*([bBmM])/.exec(parameterSize);
|
|
39
|
+
if (!m)
|
|
40
|
+
return null;
|
|
41
|
+
const value = Number(m[1]);
|
|
42
|
+
if (!Number.isFinite(value))
|
|
43
|
+
return null;
|
|
44
|
+
return (m[2] ?? "B").toLowerCase() === "m" ? value / 1000 : value;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Infer a model profile from its id plus any provider-reported hints.
|
|
48
|
+
* Inference is deliberately explicit so `models pick` can explain itself.
|
|
49
|
+
*/
|
|
50
|
+
export function inferProfile(id, hints = {}) {
|
|
51
|
+
const lower = id.toLowerCase();
|
|
52
|
+
const family = familyOf(id);
|
|
53
|
+
const provider = hints.provider ?? "ollama";
|
|
54
|
+
const sizeB = paramsInBillions(hints.parameterSize);
|
|
55
|
+
const capabilities = {
|
|
56
|
+
tools: hints.capabilities?.tools ?? false,
|
|
57
|
+
thinking: hints.capabilities?.thinking ?? THINK_HINTS.test(lower),
|
|
58
|
+
vision: hints.capabilities?.vision ?? VISION_HINTS.test(lower),
|
|
59
|
+
embedding: hints.capabilities?.embedding ?? EMBED_HINTS.test(lower),
|
|
60
|
+
};
|
|
61
|
+
const signals = [];
|
|
62
|
+
let score = 0.3;
|
|
63
|
+
if (capabilities.embedding) {
|
|
64
|
+
return {
|
|
65
|
+
id,
|
|
66
|
+
provider,
|
|
67
|
+
family,
|
|
68
|
+
parameterSize: hints.parameterSize ?? null,
|
|
69
|
+
quantization: hints.quantization ?? null,
|
|
70
|
+
contextLength: hints.contextLength ?? null,
|
|
71
|
+
capabilities,
|
|
72
|
+
codingScore: 0,
|
|
73
|
+
reason: "embedding model — not usable for code generation",
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
if (CODING_HINTS.test(lower) || family === "starcoder") {
|
|
77
|
+
score += 0.35;
|
|
78
|
+
signals.push("purpose-built for code");
|
|
79
|
+
}
|
|
80
|
+
if (family === "qwen" || family === "deepseek") {
|
|
81
|
+
score += 0.1;
|
|
82
|
+
signals.push(`${family} family is strong on code`);
|
|
83
|
+
}
|
|
84
|
+
if (sizeB !== null) {
|
|
85
|
+
if (sizeB >= 7) {
|
|
86
|
+
score += 0.15;
|
|
87
|
+
signals.push(`${sizeB}B parameters`);
|
|
88
|
+
}
|
|
89
|
+
else if (sizeB >= 3) {
|
|
90
|
+
score += 0.08;
|
|
91
|
+
signals.push(`${sizeB}B parameters`);
|
|
92
|
+
}
|
|
93
|
+
else if (sizeB < 1) {
|
|
94
|
+
score -= 0.2;
|
|
95
|
+
signals.push("sub-1B model (smoke tests only)");
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if (capabilities.tools) {
|
|
99
|
+
score += 0.08;
|
|
100
|
+
signals.push("tool calling");
|
|
101
|
+
}
|
|
102
|
+
if (capabilities.thinking) {
|
|
103
|
+
score += 0.05;
|
|
104
|
+
signals.push("reasoning/thinking");
|
|
105
|
+
}
|
|
106
|
+
if ((hints.contextLength ?? 0) >= 65_536) {
|
|
107
|
+
score += 0.05;
|
|
108
|
+
signals.push("long context");
|
|
109
|
+
}
|
|
110
|
+
const codingScore = Math.max(0, Math.min(1, score));
|
|
111
|
+
return {
|
|
112
|
+
id,
|
|
113
|
+
provider,
|
|
114
|
+
family,
|
|
115
|
+
parameterSize: hints.parameterSize ?? null,
|
|
116
|
+
quantization: hints.quantization ?? null,
|
|
117
|
+
contextLength: hints.contextLength ?? null,
|
|
118
|
+
capabilities,
|
|
119
|
+
codingScore,
|
|
120
|
+
reason: signals.length > 0 ? signals.join(" · ") : "general-purpose model",
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
/** Enumerate models installed on a local Ollama server (GET /api/tags). */
|
|
124
|
+
export async function listOllamaModels(baseUrl = "http://localhost:11434", opts = {}) {
|
|
125
|
+
const res = await fetch(`${baseUrl.replace(/\/+$/, "")}/api/tags`, {
|
|
126
|
+
signal: AbortSignal.timeout(opts.timeoutMs ?? 2500),
|
|
127
|
+
});
|
|
128
|
+
if (!res.ok)
|
|
129
|
+
throw new Error(`Ollama /api/tags responded ${res.status}`);
|
|
130
|
+
const data = (await res.json());
|
|
131
|
+
return (data.models ?? [])
|
|
132
|
+
.map((m) => {
|
|
133
|
+
const id = m.name ?? m.model ?? "";
|
|
134
|
+
const caps = m.capabilities ?? [];
|
|
135
|
+
return inferProfile(id, {
|
|
136
|
+
provider: "ollama",
|
|
137
|
+
parameterSize: m.details?.parameter_size ?? null,
|
|
138
|
+
quantization: m.details?.quantization_level ?? null,
|
|
139
|
+
contextLength: m.details?.context_length ?? null,
|
|
140
|
+
capabilities: {
|
|
141
|
+
tools: caps.includes("tools"),
|
|
142
|
+
thinking: caps.includes("thinking"),
|
|
143
|
+
vision: caps.includes("vision"),
|
|
144
|
+
embedding: caps.includes("embedding"),
|
|
145
|
+
},
|
|
146
|
+
});
|
|
147
|
+
})
|
|
148
|
+
.filter((p) => p.id.length > 0)
|
|
149
|
+
.sort((a, b) => b.codingScore - a.codingScore || a.id.localeCompare(b.id));
|
|
150
|
+
}
|
|
151
|
+
/** Choose the best available model for a task, or null when nothing qualifies. */
|
|
152
|
+
export function pickModel(models, opts = {}) {
|
|
153
|
+
const task = opts.task ?? "coding";
|
|
154
|
+
const excluded = new Set(opts.exclude ?? []);
|
|
155
|
+
const candidates = models.filter((m) => {
|
|
156
|
+
if (excluded.has(m.id))
|
|
157
|
+
return false;
|
|
158
|
+
if (task === "embedding")
|
|
159
|
+
return m.capabilities.embedding;
|
|
160
|
+
if (m.capabilities.embedding)
|
|
161
|
+
return false; // never route code work to an embedder
|
|
162
|
+
if (opts.requireTools && !m.capabilities.tools)
|
|
163
|
+
return false;
|
|
164
|
+
if (opts.requireThinking && !m.capabilities.thinking)
|
|
165
|
+
return false;
|
|
166
|
+
if (opts.minContext !== undefined && (m.contextLength ?? 0) < opts.minContext)
|
|
167
|
+
return false;
|
|
168
|
+
return true;
|
|
169
|
+
});
|
|
170
|
+
if (candidates.length === 0)
|
|
171
|
+
return null;
|
|
172
|
+
const scored = candidates
|
|
173
|
+
.map((model) => ({
|
|
174
|
+
model,
|
|
175
|
+
score: task === "general" ? model.codingScore * 0.7 + 0.3 : model.codingScore,
|
|
176
|
+
}))
|
|
177
|
+
.sort((a, b) => b.score - a.score ||
|
|
178
|
+
(b.model.contextLength ?? 0) - (a.model.contextLength ?? 0) ||
|
|
179
|
+
a.model.id.localeCompare(b.model.id));
|
|
180
|
+
const best = scored[0];
|
|
181
|
+
return {
|
|
182
|
+
model: best.model,
|
|
183
|
+
score: best.score,
|
|
184
|
+
reason: `${best.model.reason} (task: ${task})`,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Resolve the model to run with: explicit env pin → best installed local model →
|
|
189
|
+
* documented default. Never throws — a missing Ollama server degrades to the default.
|
|
190
|
+
*/
|
|
191
|
+
export async function resolveModelForTask(opts = {}) {
|
|
192
|
+
const env = opts.env ?? process.env;
|
|
193
|
+
const task = opts.task ?? "coding";
|
|
194
|
+
const explicit = env["SKILL_ROUTER_MODEL"];
|
|
195
|
+
if (explicit) {
|
|
196
|
+
const model = inferProfile(explicit, { provider: "ollama" });
|
|
197
|
+
return {
|
|
198
|
+
model,
|
|
199
|
+
score: model.codingScore,
|
|
200
|
+
reason: "pinned by SKILL_ROUTER_MODEL",
|
|
201
|
+
source: "env",
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
try {
|
|
205
|
+
const models = await listOllamaModels(opts.baseUrl, { timeoutMs: opts.timeoutMs ?? 2500 });
|
|
206
|
+
const picked = pickModel(models, {
|
|
207
|
+
task,
|
|
208
|
+
requireTools: task === "coding" || task === "planning",
|
|
209
|
+
});
|
|
210
|
+
if (picked)
|
|
211
|
+
return { ...picked, source: "auto" };
|
|
212
|
+
}
|
|
213
|
+
catch {
|
|
214
|
+
// Local server not running — fall through to the default model.
|
|
215
|
+
}
|
|
216
|
+
const model = inferProfile(DEFAULT_MODELS.ollama, { provider: "ollama" });
|
|
217
|
+
return {
|
|
218
|
+
model,
|
|
219
|
+
score: model.codingScore,
|
|
220
|
+
reason: "no local models found — using the documented default",
|
|
221
|
+
source: "default",
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
/** Plain-text table so any model set can be inspected from the terminal. */
|
|
225
|
+
export function formatModelTable(models) {
|
|
226
|
+
const rows = models.map((m) => [
|
|
227
|
+
m.id,
|
|
228
|
+
m.parameterSize ?? "—",
|
|
229
|
+
m.contextLength ? `${Math.round(m.contextLength / 1024)}k` : "—",
|
|
230
|
+
[
|
|
231
|
+
m.capabilities.tools ? "tools" : "",
|
|
232
|
+
m.capabilities.thinking ? "think" : "",
|
|
233
|
+
m.capabilities.vision ? "vision" : "",
|
|
234
|
+
m.capabilities.embedding ? "embed" : "",
|
|
235
|
+
]
|
|
236
|
+
.filter(Boolean)
|
|
237
|
+
.join(",") || "—",
|
|
238
|
+
m.codingScore.toFixed(2),
|
|
239
|
+
]);
|
|
240
|
+
const header = ["MODEL", "PARAMS", "CTX", "CAPS", "CODE"];
|
|
241
|
+
const widths = header.map((h, i) => Math.max(h.length, ...rows.map((r) => (r[i] ?? "").length)));
|
|
242
|
+
const line = (cells) => cells.map((c, i) => (c ?? "").padEnd(widths[i] ?? 0)).join(" ");
|
|
243
|
+
return [line(header), ...rows.map(line)].join("\n");
|
|
244
|
+
}
|
|
245
|
+
//# sourceMappingURL=models.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"models.js","sourceRoot":"","sources":["../src/models.ts"],"names":[],"mappings":"AAAA,0CAA0C;AAC1C,EAAE;AACF,iFAAiF;AACjF,mFAAmF;AACnF,2EAA2E;AAC3E,yDAAyD;AACzD,OAAO,EAAE,cAAc,EAAqB,MAAM,gBAAgB,CAAC;AAkCnE,MAAM,QAAQ,GAAG;IACf,MAAM;IACN,UAAU;IACV,OAAO;IACP,OAAO;IACP,SAAS;IACT,WAAW;IACX,WAAW;IACX,SAAS;IACT,KAAK;IACL,QAAQ;IACR,QAAQ;IACR,KAAK;IACL,IAAI;IACJ,IAAI;CACL,CAAC;AAEF,MAAM,YAAY,GAAG,8EAA8E,CAAC;AACpG,MAAM,WAAW,GAAG,iEAAiE,CAAC;AACtF,MAAM,YAAY,GAAG,mCAAmC,CAAC;AACzD,MAAM,WAAW,GAAG,sCAAsC,CAAC;AAE3D,SAAS,QAAQ,CAAC,EAAU;IAC1B,MAAM,IAAI,GAAG,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAChE,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IACrD,IAAI,KAAK;QAAE,OAAO,KAAK,CAAC;IACxB,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;AACxC,CAAC;AAED,SAAS,gBAAgB,CAAC,aAAwC;IAChE,IAAI,CAAC,aAAa;QAAE,OAAO,IAAI,CAAC;IAChC,MAAM,CAAC,GAAG,qBAAqB,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IACpD,IAAI,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACpB,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACzC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,WAAW,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC;AACpE,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,EAAU,EAAE,QAAsB,EAAE;IAC/D,MAAM,KAAK,GAAG,EAAE,CAAC,WAAW,EAAE,CAAC;IAC/B,MAAM,MAAM,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAC;IAC5B,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,IAAI,QAAQ,CAAC;IAC5C,MAAM,KAAK,GAAG,gBAAgB,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;IAEpD,MAAM,YAAY,GAAsB;QACtC,KAAK,EAAE,KAAK,CAAC,YAAY,EAAE,KAAK,IAAI,KAAK;QACzC,QAAQ,EAAE,KAAK,CAAC,YAAY,EAAE,QAAQ,IAAI,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC;QACjE,MAAM,EAAE,KAAK,CAAC,YAAY,EAAE,MAAM,IAAI,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC;QAC9D,SAAS,EAAE,KAAK,CAAC,YAAY,EAAE,SAAS,IAAI,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC;KACpE,CAAC;IAEF,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,IAAI,KAAK,GAAG,GAAG,CAAC;IAEhB,IAAI,YAAY,CAAC,SAAS,EAAE,CAAC;QAC3B,OAAO;YACL,EAAE;YACF,QAAQ;YACR,MAAM;YACN,aAAa,EAAE,KAAK,CAAC,aAAa,IAAI,IAAI;YAC1C,YAAY,EAAE,KAAK,CAAC,YAAY,IAAI,IAAI;YACxC,aAAa,EAAE,KAAK,CAAC,aAAa,IAAI,IAAI;YAC1C,YAAY;YACZ,WAAW,EAAE,CAAC;YACd,MAAM,EAAE,kDAAkD;SAC3D,CAAC;IACJ,CAAC;IAED,IAAI,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,MAAM,KAAK,WAAW,EAAE,CAAC;QACvD,KAAK,IAAI,IAAI,CAAC;QACd,OAAO,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC;IACzC,CAAC;IACD,IAAI,MAAM,KAAK,MAAM,IAAI,MAAM,KAAK,UAAU,EAAE,CAAC;QAC/C,KAAK,IAAI,GAAG,CAAC;QACb,OAAO,CAAC,IAAI,CAAC,GAAG,MAAM,2BAA2B,CAAC,CAAC;IACrD,CAAC;IACD,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QACnB,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC;YACf,KAAK,IAAI,IAAI,CAAC;YACd,OAAO,CAAC,IAAI,CAAC,GAAG,KAAK,cAAc,CAAC,CAAC;QACvC,CAAC;aAAM,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC;YACtB,KAAK,IAAI,IAAI,CAAC;YACd,OAAO,CAAC,IAAI,CAAC,GAAG,KAAK,cAAc,CAAC,CAAC;QACvC,CAAC;aAAM,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;YACrB,KAAK,IAAI,GAAG,CAAC;YACb,OAAO,CAAC,IAAI,CAAC,iCAAiC,CAAC,CAAC;QAClD,CAAC;IACH,CAAC;IACD,IAAI,YAAY,CAAC,KAAK,EAAE,CAAC;QACvB,KAAK,IAAI,IAAI,CAAC;QACd,OAAO,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IAC/B,CAAC;IACD,IAAI,YAAY,CAAC,QAAQ,EAAE,CAAC;QAC1B,KAAK,IAAI,IAAI,CAAC;QACd,OAAO,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;IACrC,CAAC;IACD,IAAI,CAAC,KAAK,CAAC,aAAa,IAAI,CAAC,CAAC,IAAI,MAAM,EAAE,CAAC;QACzC,KAAK,IAAI,IAAI,CAAC;QACd,OAAO,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IAC/B,CAAC;IAED,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC;IACpD,OAAO;QACL,EAAE;QACF,QAAQ;QACR,MAAM;QACN,aAAa,EAAE,KAAK,CAAC,aAAa,IAAI,IAAI;QAC1C,YAAY,EAAE,KAAK,CAAC,YAAY,IAAI,IAAI;QACxC,aAAa,EAAE,KAAK,CAAC,aAAa,IAAI,IAAI;QAC1C,YAAY;QACZ,WAAW;QACX,MAAM,EAAE,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,uBAAuB;KAC3E,CAAC;AACJ,CAAC;AAaD,2EAA2E;AAC3E,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,OAAO,GAAG,wBAAwB,EAClC,OAA+B,EAAE;IAEjC,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,WAAW,EAAE;QACjE,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC;KACpD,CAAC,CAAC;IACH,IAAI,CAAC,GAAG,CAAC,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,8BAA8B,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;IACzE,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAA6B,CAAC;IAC5D,OAAO,CAAC,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC;SACvB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACT,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;QACnC,MAAM,IAAI,GAAG,CAAC,CAAC,YAAY,IAAI,EAAE,CAAC;QAClC,OAAO,YAAY,CAAC,EAAE,EAAE;YACtB,QAAQ,EAAE,QAAQ;YAClB,aAAa,EAAE,CAAC,CAAC,OAAO,EAAE,cAAc,IAAI,IAAI;YAChD,YAAY,EAAE,CAAC,CAAC,OAAO,EAAE,kBAAkB,IAAI,IAAI;YACnD,aAAa,EAAE,CAAC,CAAC,OAAO,EAAE,cAAc,IAAI,IAAI;YAChD,YAAY,EAAE;gBACZ,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC;gBAC7B,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC;gBACnC,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC;gBAC/B,SAAS,EAAE,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC;aACtC;SACF,CAAC,CAAC;IACL,CAAC,CAAC;SACD,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC;SAC9B,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,GAAG,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAC/E,CAAC;AAgBD,kFAAkF;AAClF,MAAM,UAAU,SAAS,CAAC,MAAsB,EAAE,OAAoB,EAAE;IACtE,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,QAAQ,CAAC;IACnC,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC;IAC7C,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE;QACrC,IAAI,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;YAAE,OAAO,KAAK,CAAC;QACrC,IAAI,IAAI,KAAK,WAAW;YAAE,OAAO,CAAC,CAAC,YAAY,CAAC,SAAS,CAAC;QAC1D,IAAI,CAAC,CAAC,YAAY,CAAC,SAAS;YAAE,OAAO,KAAK,CAAC,CAAC,uCAAuC;QACnF,IAAI,IAAI,CAAC,YAAY,IAAI,CAAC,CAAC,CAAC,YAAY,CAAC,KAAK;YAAE,OAAO,KAAK,CAAC;QAC7D,IAAI,IAAI,CAAC,eAAe,IAAI,CAAC,CAAC,CAAC,YAAY,CAAC,QAAQ;YAAE,OAAO,KAAK,CAAC;QACnE,IAAI,IAAI,CAAC,UAAU,KAAK,SAAS,IAAI,CAAC,CAAC,CAAC,aAAa,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,UAAU;YAAE,OAAO,KAAK,CAAC;QAC5F,OAAO,IAAI,CAAC;IACd,CAAC,CAAC,CAAC;IACH,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAEzC,MAAM,MAAM,GAAG,UAAU;SACtB,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QACf,KAAK;QACL,KAAK,EAAE,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW,GAAG,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW;KAC9E,CAAC,CAAC;SACF,IAAI,CACH,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CACP,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK;QACjB,CAAC,CAAC,CAAC,KAAK,CAAC,aAAa,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,aAAa,IAAI,CAAC,CAAC;QAC3D,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CACvC,CAAC;IAEJ,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,CAAE,CAAC;IACxB,OAAO;QACL,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,WAAW,IAAI,GAAG;KAC/C,CAAC;AACJ,CAAC;AAMD;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,OAA4F,EAAE;IAE9F,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC;IACpC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,QAAQ,CAAC;IAEnC,MAAM,QAAQ,GAAG,GAAG,CAAC,oBAAoB,CAAC,CAAC;IAC3C,IAAI,QAAQ,EAAE,CAAC;QACb,MAAM,KAAK,GAAG,YAAY,CAAC,QAAQ,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC;QAC7D,OAAO;YACL,KAAK;YACL,KAAK,EAAE,KAAK,CAAC,WAAW;YACxB,MAAM,EAAE,8BAA8B;YACtC,MAAM,EAAE,KAAK;SACd,CAAC;IACJ,CAAC;IAED,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,IAAI,IAAI,EAAE,CAAC,CAAC;QAC3F,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,EAAE;YAC/B,IAAI;YACJ,YAAY,EAAE,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,UAAU;SACvD,CAAC,CAAC;QACH,IAAI,MAAM;YAAE,OAAO,EAAE,GAAG,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;IACnD,CAAC;IAAC,MAAM,CAAC;QACP,gEAAgE;IAClE,CAAC;IAED,MAAM,KAAK,GAAG,YAAY,CAAC,cAAc,CAAC,MAAM,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC;IAC1E,OAAO;QACL,KAAK;QACL,KAAK,EAAE,KAAK,CAAC,WAAW;QACxB,MAAM,EAAE,sDAAsD;QAC9D,MAAM,EAAE,SAAS;KAClB,CAAC;AACJ,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,gBAAgB,CAAC,MAAsB;IACrD,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;QAC7B,CAAC,CAAC,EAAE;QACJ,CAAC,CAAC,aAAa,IAAI,GAAG;QACtB,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG;QAChE;YACE,CAAC,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE;YACnC,CAAC,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE;YACtC,CAAC,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE;YACrC,CAAC,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE;SACxC;aACE,MAAM,CAAC,OAAO,CAAC;aACf,IAAI,CAAC,GAAG,CAAC,IAAI,GAAG;QACnB,CAAC,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC;KACzB,CAAC,CAAC;IACH,MAAM,MAAM,GAAG,CAAC,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1D,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IACjG,MAAM,IAAI,GAAG,CAAC,KAAe,EAAE,EAAE,CAC/B,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACnE,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACtD,CAAC"}
|
package/dist/prompt.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../src/prompt.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,SAAS,EAAE,GAAG,MAAM,CAe5D"}
|
package/dist/prompt.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export function buildSystemPrompt(hints) {
|
|
2
|
+
if (hints.length === 0)
|
|
3
|
+
return "No services configured.";
|
|
4
|
+
const services = hints
|
|
5
|
+
.map((h) => `- ${h.name}: ${h.description}${h.constraints ? ` — ${h.constraints}` : ""}`)
|
|
6
|
+
.join("\n");
|
|
7
|
+
return [
|
|
8
|
+
"You are a single-shot assistant. Respond directly with the useful result — code, a plan, a summary, whatever the user asked for.",
|
|
9
|
+
"Do not restate the question. Do not add preamble. Put the answer first.",
|
|
10
|
+
"",
|
|
11
|
+
"Available services you may call or use in your answer:",
|
|
12
|
+
services,
|
|
13
|
+
"",
|
|
14
|
+
"If a service is useful, call it according to its constraints and cite it in your answer.",
|
|
15
|
+
"If no service is relevant, just answer from your own knowledge.",
|
|
16
|
+
].join("\n");
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=prompt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt.js","sourceRoot":"","sources":["../src/prompt.ts"],"names":[],"mappings":"AAIA,MAAM,UAAU,iBAAiB,CAAC,KAAkB;IAClD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,yBAAyB,CAAC;IACzD,MAAM,QAAQ,GAAG,KAAK;SACnB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,WAAW,GAAG,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;SACxF,IAAI,CAAC,IAAI,CAAC,CAAC;IACd,OAAO;QACL,kIAAkI;QAClI,yEAAyE;QACzE,EAAE;QACF,wDAAwD;QACxD,QAAQ;QACR,EAAE;QACF,0FAA0F;QAC1F,iEAAiE;KAClE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export type ProviderKind = "ollama" | "openai" | "anthropic";
|
|
2
|
+
export interface ProviderConfig {
|
|
3
|
+
kind: ProviderKind;
|
|
4
|
+
/** ollama: http://localhost:11434 · openai: .../v1 · anthropic: https://api.anthropic.com */
|
|
5
|
+
baseUrl: string;
|
|
6
|
+
model: string;
|
|
7
|
+
/** Human label surfaced in CLI output, e.g. "Ollama · qwen3.5-coder:latest (local)". */
|
|
8
|
+
label: string;
|
|
9
|
+
apiKey?: string;
|
|
10
|
+
temperature?: number;
|
|
11
|
+
maxTokens?: number;
|
|
12
|
+
/** Reasoning models: toggle chain-of-thought where the server supports it. */
|
|
13
|
+
think?: boolean;
|
|
14
|
+
/** Context window for local servers that accept it (Ollama `options.num_ctx`). */
|
|
15
|
+
numCtx?: number;
|
|
16
|
+
/** Per-request timeout in ms. Slow local models (e.g. 9B thinkers) need real room. */
|
|
17
|
+
timeoutMs?: number;
|
|
18
|
+
}
|
|
19
|
+
export interface Completion {
|
|
20
|
+
text: string;
|
|
21
|
+
thinking?: string;
|
|
22
|
+
inputTokens: number;
|
|
23
|
+
outputTokens: number;
|
|
24
|
+
}
|
|
25
|
+
export declare class ProviderError extends Error {
|
|
26
|
+
readonly status?: number | undefined;
|
|
27
|
+
constructor(message: string, status?: number | undefined);
|
|
28
|
+
}
|
|
29
|
+
/** Single entry point used by the executor. */
|
|
30
|
+
export declare function complete(config: ProviderConfig, system: string, user: string, opts?: {
|
|
31
|
+
maxTokens?: number;
|
|
32
|
+
temperature?: number;
|
|
33
|
+
json?: boolean;
|
|
34
|
+
}): Promise<Completion>;
|
|
35
|
+
/** Widely-available fallbacks per protocol; `models pick` can override these. */
|
|
36
|
+
export declare const DEFAULT_MODELS: Record<ProviderKind, string>;
|
|
37
|
+
export declare const DEFAULT_BASE_URLS: Record<ProviderKind, string>;
|
|
38
|
+
/**
|
|
39
|
+
* Resolve provider config. Local-first by default: if no API key and no explicit
|
|
40
|
+
* base URL are present, route to a local Ollama server (free, no key).
|
|
41
|
+
*/
|
|
42
|
+
export declare function resolveProvider(overrides?: Partial<ProviderConfig>, env?: NodeJS.ProcessEnv): ProviderConfig;
|
|
43
|
+
export declare function labelFor(kind: ProviderKind, baseUrl: string, model: string): string;
|
|
44
|
+
export declare function providerName(kind: ProviderKind, baseUrl: string): string;
|
|
45
|
+
//# sourceMappingURL=providers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"providers.d.ts","sourceRoot":"","sources":["../src/providers.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,QAAQ,GAAG,WAAW,CAAC;AAE7D,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,YAAY,CAAC;IACnB,6FAA6F;IAC7F,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,wFAAwF;IACxF,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,8EAA8E;IAC9E,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,kFAAkF;IAClF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sFAAsF;IACtF,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,qBAAa,aAAc,SAAQ,KAAK;IAGpC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM;gBADxB,OAAO,EAAE,MAAM,EACN,MAAM,CAAC,EAAE,MAAM,YAAA;CAK3B;AAED,+CAA+C;AAC/C,wBAAsB,QAAQ,CAC5B,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,CAAA;CAAO,GACtE,OAAO,CAAC,UAAU,CAAC,CASrB;AAwKD,iFAAiF;AACjF,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,YAAY,EAAE,MAAM,CAIvD,CAAC;AAEF,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,YAAY,EAAE,MAAM,CAI1D,CAAC;AAIF;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,SAAS,GAAE,OAAO,CAAC,cAAc,CAAM,EACvC,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,cAAc,CAsChB;AAaD,wBAAgB,QAAQ,CAAC,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAGnF;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CASxE"}
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
// Provider-agnostic completion. One code path per protocol so the router can run
|
|
2
|
+
// against ANY coding model: a local Ollama server, any OpenAI-compatible endpoint
|
|
3
|
+
// (OpenAI, OpenRouter, Groq, Together, LM Studio, vLLM), or Anthropic.
|
|
4
|
+
//
|
|
5
|
+
// No SDKs — plain fetch keeps the library dependency-light and portable.
|
|
6
|
+
export class ProviderError extends Error {
|
|
7
|
+
status;
|
|
8
|
+
constructor(message, status) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.status = status;
|
|
11
|
+
this.name = "ProviderError";
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
/** Single entry point used by the executor. */
|
|
15
|
+
export async function complete(config, system, user, opts = {}) {
|
|
16
|
+
switch (config.kind) {
|
|
17
|
+
case "ollama":
|
|
18
|
+
return completeOllama(config, system, user, opts);
|
|
19
|
+
case "anthropic":
|
|
20
|
+
return completeAnthropic(config, system, user, opts);
|
|
21
|
+
default:
|
|
22
|
+
return completeOpenAI(config, system, user, opts);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/** Local models can be slow — default to 5 minutes per request, override with SKILL_ROUTER_TIMEOUT_MS. */
|
|
26
|
+
const timeout = (ms) => AbortSignal.timeout(ms ?? 300_000);
|
|
27
|
+
async function safeBody(res) {
|
|
28
|
+
try {
|
|
29
|
+
return (await res.text()).slice(0, 300);
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return "<no body>";
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/** Ollama native /api/chat — supports think, json format, and context window control. */
|
|
36
|
+
async function completeOllama(config, system, user, opts) {
|
|
37
|
+
const options = {};
|
|
38
|
+
const temperature = opts.temperature ?? config.temperature;
|
|
39
|
+
const maxTokens = opts.maxTokens ?? config.maxTokens;
|
|
40
|
+
if (temperature !== undefined)
|
|
41
|
+
options["temperature"] = temperature;
|
|
42
|
+
if (config.numCtx !== undefined)
|
|
43
|
+
options["num_ctx"] = config.numCtx;
|
|
44
|
+
if (maxTokens !== undefined)
|
|
45
|
+
options["num_predict"] = maxTokens;
|
|
46
|
+
const body = {
|
|
47
|
+
model: config.model,
|
|
48
|
+
stream: false,
|
|
49
|
+
messages: [
|
|
50
|
+
{ role: "system", content: system },
|
|
51
|
+
{ role: "user", content: user },
|
|
52
|
+
],
|
|
53
|
+
};
|
|
54
|
+
if (config.think !== undefined)
|
|
55
|
+
body["think"] = config.think;
|
|
56
|
+
if (opts.json)
|
|
57
|
+
body["format"] = "json";
|
|
58
|
+
if (Object.keys(options).length > 0)
|
|
59
|
+
body["options"] = options;
|
|
60
|
+
const res = await fetch(`${config.baseUrl}/api/chat`, {
|
|
61
|
+
method: "POST",
|
|
62
|
+
headers: { "content-type": "application/json" },
|
|
63
|
+
body: JSON.stringify(body),
|
|
64
|
+
signal: timeout(config.timeoutMs),
|
|
65
|
+
});
|
|
66
|
+
if (!res.ok) {
|
|
67
|
+
throw new ProviderError(`${config.label} responded ${res.status}: ${await safeBody(res)}`, res.status);
|
|
68
|
+
}
|
|
69
|
+
const data = (await res.json());
|
|
70
|
+
const thinking = data.message?.thinking?.trim();
|
|
71
|
+
return {
|
|
72
|
+
text: data.message?.content?.trim() ?? "",
|
|
73
|
+
...(thinking ? { thinking } : {}),
|
|
74
|
+
inputTokens: data.prompt_eval_count ?? 0,
|
|
75
|
+
outputTokens: data.eval_count ?? 0,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
/** OpenAI-compatible /chat/completions — OpenAI, OpenRouter, Groq, Together, LM Studio, vLLM. */
|
|
79
|
+
async function completeOpenAI(config, system, user, opts) {
|
|
80
|
+
const headers = { "content-type": "application/json" };
|
|
81
|
+
if (config.apiKey)
|
|
82
|
+
headers["authorization"] = `Bearer ${config.apiKey}`;
|
|
83
|
+
const body = {
|
|
84
|
+
model: config.model,
|
|
85
|
+
messages: [
|
|
86
|
+
{ role: "system", content: system },
|
|
87
|
+
{ role: "user", content: user },
|
|
88
|
+
],
|
|
89
|
+
max_tokens: opts.maxTokens ?? config.maxTokens ?? 2048,
|
|
90
|
+
stream: false,
|
|
91
|
+
};
|
|
92
|
+
const temperature = opts.temperature ?? config.temperature;
|
|
93
|
+
if (temperature !== undefined)
|
|
94
|
+
body["temperature"] = temperature;
|
|
95
|
+
if (opts.json)
|
|
96
|
+
body["response_format"] = { type: "json_object" };
|
|
97
|
+
const res = await fetch(`${config.baseUrl}/chat/completions`, {
|
|
98
|
+
method: "POST",
|
|
99
|
+
headers,
|
|
100
|
+
body: JSON.stringify(body),
|
|
101
|
+
signal: timeout(config.timeoutMs),
|
|
102
|
+
});
|
|
103
|
+
if (!res.ok) {
|
|
104
|
+
throw new ProviderError(`${config.label} responded ${res.status}: ${await safeBody(res)}`, res.status);
|
|
105
|
+
}
|
|
106
|
+
const data = (await res.json());
|
|
107
|
+
const reasoning = data.choices?.[0]?.message?.reasoning_content?.trim();
|
|
108
|
+
return {
|
|
109
|
+
text: data.choices?.[0]?.message?.content?.trim() ?? "",
|
|
110
|
+
...(reasoning ? { thinking: reasoning } : {}),
|
|
111
|
+
inputTokens: data.usage?.prompt_tokens ?? 0,
|
|
112
|
+
outputTokens: data.usage?.completion_tokens ?? 0,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
/** Anthropic /v1/messages. */
|
|
116
|
+
async function completeAnthropic(config, system, user, opts) {
|
|
117
|
+
if (!config.apiKey) {
|
|
118
|
+
throw new ProviderError("Anthropic requires an API key — set SKILL_ROUTER_API_KEY or ANTHROPIC_API_KEY.");
|
|
119
|
+
}
|
|
120
|
+
const body = {
|
|
121
|
+
model: config.model,
|
|
122
|
+
max_tokens: opts.maxTokens ?? config.maxTokens ?? 4096,
|
|
123
|
+
system,
|
|
124
|
+
messages: [{ role: "user", content: user }],
|
|
125
|
+
};
|
|
126
|
+
const temperature = opts.temperature ?? config.temperature;
|
|
127
|
+
if (temperature !== undefined)
|
|
128
|
+
body["temperature"] = temperature;
|
|
129
|
+
const res = await fetch(`${config.baseUrl}/v1/messages`, {
|
|
130
|
+
method: "POST",
|
|
131
|
+
headers: {
|
|
132
|
+
"content-type": "application/json",
|
|
133
|
+
"x-api-key": config.apiKey,
|
|
134
|
+
"anthropic-version": "2023-06-01",
|
|
135
|
+
},
|
|
136
|
+
body: JSON.stringify(body),
|
|
137
|
+
signal: timeout(config.timeoutMs),
|
|
138
|
+
});
|
|
139
|
+
if (!res.ok) {
|
|
140
|
+
throw new ProviderError(`${config.label} responded ${res.status}: ${await safeBody(res)}`, res.status);
|
|
141
|
+
}
|
|
142
|
+
const data = (await res.json());
|
|
143
|
+
return {
|
|
144
|
+
text: (data.content ?? [])
|
|
145
|
+
.filter((b) => b.type === "text")
|
|
146
|
+
.map((b) => b.text ?? "")
|
|
147
|
+
.join("\n")
|
|
148
|
+
.trim(),
|
|
149
|
+
inputTokens: data.usage?.input_tokens ?? 0,
|
|
150
|
+
outputTokens: data.usage?.output_tokens ?? 0,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
/** Widely-available fallbacks per protocol; `models pick` can override these. */
|
|
154
|
+
export const DEFAULT_MODELS = {
|
|
155
|
+
ollama: "qwen2.5-coder:7b",
|
|
156
|
+
openai: "gpt-4o-mini",
|
|
157
|
+
anthropic: "claude-sonnet-4-5",
|
|
158
|
+
};
|
|
159
|
+
export const DEFAULT_BASE_URLS = {
|
|
160
|
+
ollama: "http://localhost:11434",
|
|
161
|
+
openai: "https://api.openai.com/v1",
|
|
162
|
+
anthropic: "https://api.anthropic.com",
|
|
163
|
+
};
|
|
164
|
+
const trimSlash = (url) => url.replace(/\/+$/, "");
|
|
165
|
+
/**
|
|
166
|
+
* Resolve provider config. Local-first by default: if no API key and no explicit
|
|
167
|
+
* base URL are present, route to a local Ollama server (free, no key).
|
|
168
|
+
*/
|
|
169
|
+
export function resolveProvider(overrides = {}, env = process.env) {
|
|
170
|
+
const declaredBase = overrides.baseUrl ?? env["SKILL_ROUTER_BASE_URL"];
|
|
171
|
+
const kind = overrides.kind ??
|
|
172
|
+
env["SKILL_ROUTER_PROVIDER"] ??
|
|
173
|
+
(declaredBase?.includes("11434") || declaredBase?.includes("localhost")
|
|
174
|
+
? "ollama"
|
|
175
|
+
: env["ANTHROPIC_API_KEY"] && !env["OPENAI_API_KEY"]
|
|
176
|
+
? "anthropic"
|
|
177
|
+
: env["OPENAI_API_KEY"]
|
|
178
|
+
? "openai"
|
|
179
|
+
: "ollama");
|
|
180
|
+
const apiKey = overrides.apiKey ??
|
|
181
|
+
env["SKILL_ROUTER_API_KEY"] ??
|
|
182
|
+
(kind === "anthropic" ? env["ANTHROPIC_API_KEY"] : env["OPENAI_API_KEY"]);
|
|
183
|
+
const baseUrl = trimSlash(overrides.baseUrl ?? declaredBase ?? DEFAULT_BASE_URLS[kind]);
|
|
184
|
+
const model = overrides.model ?? env["SKILL_ROUTER_MODEL"] ?? DEFAULT_MODELS[kind];
|
|
185
|
+
const temperature = overrides.temperature ?? numberOr(env["SKILL_ROUTER_TEMPERATURE"]);
|
|
186
|
+
const maxTokens = overrides.maxTokens ?? numberOr(env["SKILL_ROUTER_MAX_TOKENS"]);
|
|
187
|
+
const numCtx = overrides.numCtx ?? numberOr(env["SKILL_ROUTER_NUM_CTX"]);
|
|
188
|
+
const timeoutMs = overrides.timeoutMs ?? numberOr(env["SKILL_ROUTER_TIMEOUT_MS"]);
|
|
189
|
+
const think = overrides.think ?? booleanOr(env["SKILL_ROUTER_THINK"]);
|
|
190
|
+
return {
|
|
191
|
+
kind,
|
|
192
|
+
baseUrl,
|
|
193
|
+
model,
|
|
194
|
+
...(apiKey ? { apiKey } : {}),
|
|
195
|
+
...(temperature !== undefined ? { temperature } : {}),
|
|
196
|
+
...(maxTokens !== undefined ? { maxTokens } : {}),
|
|
197
|
+
...(numCtx !== undefined ? { numCtx } : {}),
|
|
198
|
+
...(timeoutMs !== undefined ? { timeoutMs } : {}),
|
|
199
|
+
...(think !== undefined ? { think } : {}),
|
|
200
|
+
label: labelFor(kind, baseUrl, model),
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
function numberOr(value) {
|
|
204
|
+
if (value === undefined || value.trim() === "")
|
|
205
|
+
return undefined;
|
|
206
|
+
const n = Number(value);
|
|
207
|
+
return Number.isFinite(n) ? n : undefined;
|
|
208
|
+
}
|
|
209
|
+
function booleanOr(value) {
|
|
210
|
+
if (value === undefined || value.trim() === "")
|
|
211
|
+
return undefined;
|
|
212
|
+
return value === "1" || value.toLowerCase() === "true";
|
|
213
|
+
}
|
|
214
|
+
export function labelFor(kind, baseUrl, model) {
|
|
215
|
+
const local = baseUrl.includes("localhost") || baseUrl.includes("127.0.0.1");
|
|
216
|
+
return `${providerName(kind, baseUrl)} · ${model}${local ? " (local)" : ""}`;
|
|
217
|
+
}
|
|
218
|
+
export function providerName(kind, baseUrl) {
|
|
219
|
+
if (kind === "anthropic")
|
|
220
|
+
return "Anthropic";
|
|
221
|
+
if (kind === "ollama")
|
|
222
|
+
return "Ollama";
|
|
223
|
+
if (baseUrl.includes("openrouter"))
|
|
224
|
+
return "OpenRouter";
|
|
225
|
+
if (baseUrl.includes("groq"))
|
|
226
|
+
return "Groq";
|
|
227
|
+
if (baseUrl.includes("together"))
|
|
228
|
+
return "Together";
|
|
229
|
+
if (baseUrl.includes("openai.com"))
|
|
230
|
+
return "OpenAI";
|
|
231
|
+
if (baseUrl.includes("1234"))
|
|
232
|
+
return "LM Studio";
|
|
233
|
+
return "OpenAI-compatible";
|
|
234
|
+
}
|
|
235
|
+
//# sourceMappingURL=providers.js.map
|