@maintainabilityai/research-runner 0.1.7 → 0.1.9
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.
|
@@ -23,8 +23,12 @@ async function callAnthropic(opts) {
|
|
|
23
23
|
throw new Error('ANTHROPIC_API_KEY missing — set the env var or pass apiKey directly');
|
|
24
24
|
}
|
|
25
25
|
const fetchImpl = opts.fetchImpl ?? globalThis.fetch;
|
|
26
|
-
//
|
|
27
|
-
|
|
26
|
+
// Sonnet at ~50–80 tok/s × 8K output = 100–160s, plus prompt-processing.
|
|
27
|
+
// 120s aborted real synth runs mid-stream; 240s gives headroom for
|
|
28
|
+
// slow days. (Real fix is streaming so the connection stays alive,
|
|
29
|
+
// but the cost/benefit isn't worth it yet — single-shot is fine
|
|
30
|
+
// until we hit 240s legitimately.)
|
|
31
|
+
const timeoutMs = opts.timeoutMs ?? 240_000;
|
|
28
32
|
const controller = new AbortController();
|
|
29
33
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
30
34
|
let response;
|
|
@@ -25,10 +25,13 @@ async function callGitHubModels(opts) {
|
|
|
25
25
|
}
|
|
26
26
|
const fetchImpl = opts.fetchImpl ?? globalThis.fetch;
|
|
27
27
|
const endpoint = opts.endpoint ?? DEFAULT_ENDPOINT;
|
|
28
|
-
// Synthesis prompts can produce 8K
|
|
29
|
-
//
|
|
30
|
-
//
|
|
31
|
-
|
|
28
|
+
// Synthesis prompts can produce 8K+ output tokens (and "custom"-tier
|
|
29
|
+
// models like gpt-5-chat can return much more), which routinely take
|
|
30
|
+
// 100–180s. Match the Anthropic client at 240s for headroom on slow
|
|
31
|
+
// days. (Real fix is streaming so the connection stays alive, but
|
|
32
|
+
// the cost/benefit isn't worth it yet — single-shot is fine until
|
|
33
|
+
// we hit 240s legitimately.)
|
|
34
|
+
const timeoutMs = opts.timeoutMs ?? 240_000;
|
|
32
35
|
const controller = new AbortController();
|
|
33
36
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
34
37
|
const messages = [];
|
package/dist/llm/llm-router.js
CHANGED
|
@@ -16,62 +16,96 @@ const MODEL_BY_TIER = {
|
|
|
16
16
|
// Models access through a Copilot subscription (GMT path).
|
|
17
17
|
synth: { anthropic: 'claude-sonnet-4-6', githubModels: 'openai/gpt-5-chat' },
|
|
18
18
|
};
|
|
19
|
+
/**
|
|
20
|
+
* Write progress to stderr so the GitHub Actions job shows the
|
|
21
|
+
* "tried X, falling back to Y" story when the primary provider fails.
|
|
22
|
+
* Silenced when RESEARCH_RUNNER_QUIET=1 (tests).
|
|
23
|
+
*/
|
|
24
|
+
function progress(msg) {
|
|
25
|
+
if (process.env.RESEARCH_RUNNER_QUIET === '1') {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const ts = new Date().toISOString().slice(11, 19);
|
|
29
|
+
process.stderr.write(`[research-runner ${ts}] ${msg}\n`);
|
|
30
|
+
}
|
|
31
|
+
async function callAnthropicTier(opts, tierModels) {
|
|
32
|
+
if (!opts.anthropicApiKey) {
|
|
33
|
+
throw new Error(`callLlm: provider=anthropic requires anthropicApiKey (set ANTHROPIC_API_KEY).`);
|
|
34
|
+
}
|
|
35
|
+
const r = await (0, anthropic_client_1.callAnthropic)({
|
|
36
|
+
apiKey: opts.anthropicApiKey,
|
|
37
|
+
model: tierModels.anthropic,
|
|
38
|
+
system: opts.system,
|
|
39
|
+
prompt: opts.prompt,
|
|
40
|
+
maxTokens: opts.maxTokens,
|
|
41
|
+
temperature: opts.temperature,
|
|
42
|
+
fetchImpl: opts.fetchImpl,
|
|
43
|
+
});
|
|
44
|
+
return {
|
|
45
|
+
provider: 'anthropic',
|
|
46
|
+
model: tierModels.anthropic,
|
|
47
|
+
text: r.text,
|
|
48
|
+
inputTokens: r.inputTokens,
|
|
49
|
+
outputTokens: r.outputTokens,
|
|
50
|
+
costUsd: r.costUsd,
|
|
51
|
+
httpStatus: r.httpStatus,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
async function callGitHubModelsTier(opts, tierModels) {
|
|
55
|
+
if (!opts.githubToken) {
|
|
56
|
+
throw new Error(`callLlm: provider=github-models requires githubToken (set GITHUB_TOKEN; workflow needs \`permissions: models: read\`).`);
|
|
57
|
+
}
|
|
58
|
+
const r = await (0, github_models_client_1.callGitHubModels)({
|
|
59
|
+
token: opts.githubToken,
|
|
60
|
+
model: tierModels.githubModels,
|
|
61
|
+
system: opts.system,
|
|
62
|
+
prompt: opts.prompt,
|
|
63
|
+
maxTokens: opts.maxTokens,
|
|
64
|
+
temperature: opts.temperature,
|
|
65
|
+
fetchImpl: opts.fetchImpl,
|
|
66
|
+
});
|
|
67
|
+
return {
|
|
68
|
+
provider: 'github-models',
|
|
69
|
+
model: tierModels.githubModels,
|
|
70
|
+
text: r.text,
|
|
71
|
+
inputTokens: r.inputTokens,
|
|
72
|
+
outputTokens: r.outputTokens,
|
|
73
|
+
costUsd: r.costUsd,
|
|
74
|
+
httpStatus: r.httpStatus,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
19
77
|
async function callLlm(opts) {
|
|
20
78
|
const tierModels = MODEL_BY_TIER[opts.tier];
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
//
|
|
24
|
-
//
|
|
25
|
-
//
|
|
26
|
-
//
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
79
|
+
// Try-then-fall-back routing for github-models.
|
|
80
|
+
//
|
|
81
|
+
// When the brief asks for github-models AND an Anthropic key is also
|
|
82
|
+
// available, we try GitHub Models FIRST (respects the user's stated
|
|
83
|
+
// provider preference — they're often paying for Copilot Pro). If it
|
|
84
|
+
// fails for ANY reason (8K cap 413, custom-tier 403, timeout, 5xx,
|
|
85
|
+
// network), fall back to Anthropic. The synth tier is where this
|
|
86
|
+
// matters most — plan-tier prompts are small and almost never fail.
|
|
87
|
+
//
|
|
88
|
+
// Why fall back on every error vs. only on 413: we'd rather have a
|
|
89
|
+
// working synth via Anthropic than a half-failed run. The progress
|
|
90
|
+
// log makes the "tried X, fell back to Y" story explicit so it's
|
|
91
|
+
// easy to debug.
|
|
92
|
+
//
|
|
93
|
+
// anthropic provider is pass-through (no fallback target).
|
|
94
|
+
if (opts.provider === 'github-models') {
|
|
95
|
+
try {
|
|
96
|
+
return await callGitHubModelsTier(opts, tierModels);
|
|
33
97
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
fetchImpl: opts.fetchImpl,
|
|
42
|
-
});
|
|
43
|
-
return {
|
|
44
|
-
provider: 'anthropic',
|
|
45
|
-
model: tierModels.anthropic,
|
|
46
|
-
text: r.text,
|
|
47
|
-
inputTokens: r.inputTokens,
|
|
48
|
-
outputTokens: r.outputTokens,
|
|
49
|
-
costUsd: r.costUsd,
|
|
50
|
-
httpStatus: r.httpStatus,
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
if (effectiveProvider === 'github-models') {
|
|
54
|
-
if (!opts.githubToken) {
|
|
55
|
-
throw new Error(`callLlm: provider=github-models requires githubToken (set GITHUB_TOKEN; workflow needs \`permissions: models: read\`).`);
|
|
98
|
+
catch (err) {
|
|
99
|
+
if (!opts.anthropicApiKey) {
|
|
100
|
+
throw err; // no fallback available
|
|
101
|
+
}
|
|
102
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
103
|
+
progress(`⚠ github-models failed on tier=${opts.tier} — falling back to Anthropic. Cause: ${msg.slice(0, 200)}`);
|
|
104
|
+
return await callAnthropicTier(opts, tierModels);
|
|
56
105
|
}
|
|
57
|
-
const r = await (0, github_models_client_1.callGitHubModels)({
|
|
58
|
-
token: opts.githubToken,
|
|
59
|
-
model: tierModels.githubModels,
|
|
60
|
-
system: opts.system,
|
|
61
|
-
prompt: opts.prompt,
|
|
62
|
-
maxTokens: opts.maxTokens,
|
|
63
|
-
temperature: opts.temperature,
|
|
64
|
-
fetchImpl: opts.fetchImpl,
|
|
65
|
-
});
|
|
66
|
-
return {
|
|
67
|
-
provider: 'github-models',
|
|
68
|
-
model: tierModels.githubModels,
|
|
69
|
-
text: r.text,
|
|
70
|
-
inputTokens: r.inputTokens,
|
|
71
|
-
outputTokens: r.outputTokens,
|
|
72
|
-
costUsd: r.costUsd,
|
|
73
|
-
httpStatus: r.httpStatus,
|
|
74
|
-
};
|
|
75
106
|
}
|
|
76
|
-
|
|
107
|
+
if (opts.provider === 'anthropic') {
|
|
108
|
+
return await callAnthropicTier(opts, tierModels);
|
|
109
|
+
}
|
|
110
|
+
throw new Error(`callLlm: provider "${opts.provider}" not yet implemented (phase 2c.1 ships anthropic + github-models; openai + azure-openai land later).`);
|
|
77
111
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maintainabilityai/research-runner",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"description": "Research + PRD agent runner — orchestrates the Archeologist and PRD pipelines for the MaintainabilityAI governance mesh",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "MaintainabilityAI",
|