ag-ai 0.4.2 → 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,8 +1,15 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  exports.opencodeStack = void 0;
7
+ const node_fs_1 = require("node:fs");
8
+ const node_path_1 = __importDefault(require("node:path"));
4
9
  const ag_rpi_mega_1 = require("ag-rpi-mega");
5
10
  const common_1 = require("./common");
11
+ /** The runtime's tier table is the single source of truth for which model and effort each tier runs. */
12
+ const MODEL_TIERS = JSON.parse((0, node_fs_1.readFileSync)(node_path_1.default.join(__dirname, '..', '..', 'runtime', 'model-tiers.json'), 'utf8'));
6
13
  /** The published OpenCode CLI is installed into a writable emptyDir at pod start. */
7
14
  const OPENCODE_PACKAGE = 'opencode-ai@1.18.31';
8
15
  const OPENCODE_PREFIX = '/opt/opencode';
@@ -49,19 +56,17 @@ const opencodeStack = (context) => {
49
56
  (0, common_1.env)('XDG_CACHE_HOME', '/data/opencode/cache'),
50
57
  // The unrestricted /provider response processes thousands of models from
51
58
  // unconfigured providers on every request, exceeding the runner's budget.
52
- // Each advertised model carries the reasoning effort its consumer asks
53
- // for: sol at high for draft copy, 5.5 at high for draft images, and luna
54
- // at max for the client's cluster fallback. OpenCode merges these entries
55
- // with its model catalogue; it does not replace it.
59
+ // Each advertised model carries the reasoning effort of the tier that
60
+ // selects it. OpenCode merges these entries with its model catalogue; it
61
+ // does not replace it.
56
62
  (0, common_1.env)('OPENCODE_CONFIG_CONTENT', JSON.stringify({
57
63
  enabled_providers: ['openai', 'google'],
58
64
  provider: {
59
65
  openai: {
60
- models: {
61
- 'gpt-5.6-sol': { options: { reasoningEffort: 'high' } },
62
- 'gpt-5.6-luna': { options: { reasoningEffort: 'max' } },
63
- 'gpt-5.5': { options: { reasoningEffort: 'high' } },
64
- },
66
+ models: Object.fromEntries(Object.values(MODEL_TIERS).map(({ model, reasoningEffort }) => [
67
+ model,
68
+ { options: { reasoningEffort } },
69
+ ])),
65
70
  },
66
71
  },
67
72
  })),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ag-ai",
3
- "version": "0.4.2",
3
+ "version": "0.4.3",
4
4
  "description": "Provider-neutral AI client and cluster runtime for the rpi-mega cluster.",
5
5
  "license": "ISC",
6
6
  "author": "admin@gec.dev",
@@ -0,0 +1,4 @@
1
+ {
2
+ "fast": { "model": "gpt-6-luna", "reasoningEffort": "medium" },
3
+ "quality": { "model": "gpt-6.1-sol", "reasoningEffort": "medium" }
4
+ }
@@ -3,6 +3,15 @@
3
3
  // differ.
4
4
 
5
5
  import { createHash } from 'node:crypto';
6
+ import { readFileSync } from 'node:fs';
7
+
8
+ /**
9
+ * The only place that names a model: each `prefer` tier maps to one model and
10
+ * effort. Callers send a tier; the model is resolved when the call is made.
11
+ */
12
+ const MODEL_TIERS = JSON.parse(
13
+ readFileSync(new URL('./model-tiers.json', import.meta.url), 'utf8'),
14
+ );
6
15
 
7
16
  const GENERATION_VERSION = 1;
8
17
  const MAX_TEXT_BYTES = 2 * 1024 * 1024;
@@ -257,21 +266,9 @@ const normalizeGenerationRequest = (value, models) => {
257
266
  if (prefer !== undefined && prefer !== 'fast' && prefer !== 'quality') {
258
267
  throw new GenerationProtocolError(400, 'invalid_request', 'prefer is invalid');
259
268
  }
260
- let requestedModel;
261
- if (request.model !== undefined) {
262
- requestedModel = boundedString(request.model, 160, 'model');
263
- } else if (prefer === 'fast') {
264
- requestedModel = catalog.find((candidate) => candidate.prefer.includes('fast'))?.id;
265
- requestedModel ??= catalog.find((candidate) => /(?:mini|luna)$/i.test(candidate.id))?.id;
266
- if (!requestedModel) throw unsupported('fast model preference');
267
- } else {
268
- requestedModel =
269
- catalog.find((candidate) => candidate.isDefault && candidate.prefer.includes('quality'))
270
- ?.id ??
271
- catalog.find((candidate) => candidate.isDefault)?.id ??
272
- catalog.find((candidate) => candidate.prefer.includes('quality'))?.id ??
273
- catalog[0].id;
274
- }
269
+ const tier = MODEL_TIERS[prefer ?? 'quality'];
270
+ const requestedModel =
271
+ request.model === undefined ? tier.model : boundedString(request.model, 160, 'model');
275
272
  const model =
276
273
  catalog.find((candidate) => candidate.id === requestedModel) ??
277
274
  (requestedModel.includes('/') ? undefined : uniqueSuffixModel(catalog, requestedModel));
@@ -290,7 +287,9 @@ const normalizeGenerationRequest = (value, models) => {
290
287
  const output = normalizeOutput(request.output, capabilities);
291
288
  const reasoningEffort =
292
289
  request.reasoningEffort === undefined
293
- ? (capabilities.defaultReasoningEffort ?? capabilities.supportedReasoningEfforts[0])
290
+ ? request.model === undefined
291
+ ? tier.reasoningEffort
292
+ : (capabilities.defaultReasoningEffort ?? capabilities.supportedReasoningEfforts[0])
294
293
  : boundedString(request.reasoningEffort, 32, 'reasoningEffort');
295
294
  if (!reasoningEffort) {
296
295
  throw new GenerationProtocolError(
@@ -358,6 +357,7 @@ const publicModelCatalog = (models, { ready = true } = {}) => ({
358
357
 
359
358
  export {
360
359
  GENERATION_VERSION,
360
+ MODEL_TIERS,
361
361
  MAX_INPUT_ATTACHMENT_BYTES,
362
362
  MAX_INPUT_TOTAL_BYTES,
363
363
  GenerationProtocolError,