@cortexkit/opencode-magic-context 0.11.0 → 0.11.1
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/dist/hooks/magic-context/event-resolvers.d.ts.map +1 -1
- package/dist/hooks/magic-context/transform.d.ts.map +1 -1
- package/dist/index.js +43 -18
- package/dist/shared/models-dev-cache.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/shared/models-dev-cache.test.ts +114 -1
- package/src/shared/models-dev-cache.ts +34 -14
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"event-resolvers.d.ts","sourceRoot":"","sources":["../../../src/hooks/magic-context/event-resolvers.ts"],"names":[],"mappings":"AAIA,KAAK,cAAc,GAAG,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAEtD,wBAAgB,mBAAmB,CAC/B,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,OAAO,EAAE,MAAM,GAAG,SAAS,GAC5B,MAAM,CAiBR;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAiB9F;AAED,KAAK,sBAAsB,GAAG,MAAM,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"event-resolvers.d.ts","sourceRoot":"","sources":["../../../src/hooks/magic-context/event-resolvers.ts"],"names":[],"mappings":"AAIA,KAAK,cAAc,GAAG,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAEtD,wBAAgB,mBAAmB,CAC/B,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,OAAO,EAAE,MAAM,GAAG,SAAS,GAC5B,MAAM,CAiBR;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAiB9F;AAED,KAAK,sBAAsB,GAAG,MAAM,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,CAAC;AA+BvF,wBAAgB,uBAAuB,CACnC,MAAM,EAAE,sBAAsB,EAC9B,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,QAAQ,EAAE,MAAM,GACjB,MAAM,CAuBR;AAED,wBAAgB,eAAe,CAC3B,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,OAAO,EAAE,MAAM,GAAG,SAAS,GAC5B,MAAM,GAAG,SAAS,CAMpB;AAED,wBAAgB,gBAAgB,CAC5B,UAAU,EAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,GAC/D,MAAM,GAAG,SAAS,CAmBpB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transform.d.ts","sourceRoot":"","sources":["../../../src/hooks/magic-context/transform.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"transform.d.ts","sourceRoot":"","sources":["../../../src/hooks/magic-context/transform.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -150985,6 +150985,15 @@ function getOpencodeConfigPath() {
|
|
|
150985
150985
|
return json2;
|
|
150986
150986
|
return null;
|
|
150987
150987
|
}
|
|
150988
|
+
function resolveLimit(limit) {
|
|
150989
|
+
if (!limit)
|
|
150990
|
+
return;
|
|
150991
|
+
if (typeof limit.input === "number" && limit.input > 0)
|
|
150992
|
+
return limit.input;
|
|
150993
|
+
if (typeof limit.context === "number" && limit.context > 0)
|
|
150994
|
+
return limit.context;
|
|
150995
|
+
return;
|
|
150996
|
+
}
|
|
150988
150997
|
function loadModelsDevLimitsFromFile() {
|
|
150989
150998
|
const limits = new Map;
|
|
150990
150999
|
const modelsJsonPath = getModelsJsonPath();
|
|
@@ -150998,13 +151007,13 @@ function loadModelsDevLimitsFromFile() {
|
|
|
150998
151007
|
if (!provider2?.models || typeof provider2.models !== "object")
|
|
150999
151008
|
continue;
|
|
151000
151009
|
for (const [modelId, model] of Object.entries(provider2.models)) {
|
|
151001
|
-
const
|
|
151002
|
-
if (typeof
|
|
151003
|
-
limits.set(`${providerId}/${modelId}`,
|
|
151010
|
+
const effective = resolveLimit(model?.limit);
|
|
151011
|
+
if (typeof effective === "number" && effective > 0) {
|
|
151012
|
+
limits.set(`${providerId}/${modelId}`, effective);
|
|
151004
151013
|
const modes = model?.experimental?.modes;
|
|
151005
151014
|
if (modes && typeof modes === "object") {
|
|
151006
151015
|
for (const mode of Object.keys(modes)) {
|
|
151007
|
-
limits.set(`${providerId}/${modelId}-${mode}`,
|
|
151016
|
+
limits.set(`${providerId}/${modelId}-${mode}`, effective);
|
|
151008
151017
|
}
|
|
151009
151018
|
}
|
|
151010
151019
|
}
|
|
@@ -151025,9 +151034,9 @@ function loadModelsDevLimitsFromFile() {
|
|
|
151025
151034
|
if (!provider2?.models || typeof provider2.models !== "object")
|
|
151026
151035
|
continue;
|
|
151027
151036
|
for (const [modelId, model] of Object.entries(provider2.models)) {
|
|
151028
|
-
const
|
|
151029
|
-
if (typeof
|
|
151030
|
-
limits.set(`${providerId}/${modelId}`,
|
|
151037
|
+
const effective = resolveLimit(model?.limit);
|
|
151038
|
+
if (typeof effective === "number" && effective > 0) {
|
|
151039
|
+
limits.set(`${providerId}/${modelId}`, effective);
|
|
151031
151040
|
}
|
|
151032
151041
|
}
|
|
151033
151042
|
}
|
|
@@ -151054,9 +151063,9 @@ async function refreshModelLimitsFromApi(client) {
|
|
|
151054
151063
|
if (!p?.id || !p.models || typeof p.models !== "object")
|
|
151055
151064
|
continue;
|
|
151056
151065
|
for (const [modelId, model] of Object.entries(p.models)) {
|
|
151057
|
-
const
|
|
151058
|
-
if (typeof
|
|
151059
|
-
map2.set(`${p.id}/${modelId}`,
|
|
151066
|
+
const effective = resolveLimit(model?.limit);
|
|
151067
|
+
if (typeof effective === "number" && effective > 0) {
|
|
151068
|
+
map2.set(`${p.id}/${modelId}`, effective);
|
|
151060
151069
|
}
|
|
151061
151070
|
}
|
|
151062
151071
|
}
|
|
@@ -163053,20 +163062,34 @@ function resolveCacheTtl(cacheTtl, modelKey) {
|
|
|
163053
163062
|
}
|
|
163054
163063
|
return cacheTtl.default ?? "5m";
|
|
163055
163064
|
}
|
|
163065
|
+
function* modelKeyLookupOrder(modelKey) {
|
|
163066
|
+
const slash = modelKey.indexOf("/");
|
|
163067
|
+
const provider2 = slash >= 0 ? modelKey.slice(0, slash) : "";
|
|
163068
|
+
let modelId = slash >= 0 ? modelKey.slice(slash + 1) : modelKey;
|
|
163069
|
+
while (modelId.length > 0) {
|
|
163070
|
+
if (provider2)
|
|
163071
|
+
yield `${provider2}/${modelId}`;
|
|
163072
|
+
yield modelId;
|
|
163073
|
+
const lastDash = modelId.lastIndexOf("-");
|
|
163074
|
+
if (lastDash <= 0)
|
|
163075
|
+
break;
|
|
163076
|
+
modelId = modelId.slice(0, lastDash);
|
|
163077
|
+
}
|
|
163078
|
+
}
|
|
163056
163079
|
function resolveExecuteThreshold(config2, modelKey, fallback) {
|
|
163057
163080
|
const MAX_EXECUTE_THRESHOLD = 80;
|
|
163058
163081
|
let resolved;
|
|
163059
163082
|
if (typeof config2 === "number") {
|
|
163060
163083
|
resolved = config2;
|
|
163061
|
-
} else if (modelKey && typeof config2[modelKey] === "number") {
|
|
163062
|
-
resolved = config2[modelKey];
|
|
163063
163084
|
} else if (modelKey) {
|
|
163064
|
-
|
|
163065
|
-
|
|
163066
|
-
|
|
163067
|
-
|
|
163068
|
-
|
|
163085
|
+
let matched;
|
|
163086
|
+
for (const candidate of modelKeyLookupOrder(modelKey)) {
|
|
163087
|
+
if (typeof config2[candidate] === "number") {
|
|
163088
|
+
matched = config2[candidate];
|
|
163089
|
+
break;
|
|
163090
|
+
}
|
|
163069
163091
|
}
|
|
163092
|
+
resolved = matched ?? config2.default ?? fallback;
|
|
163070
163093
|
} else {
|
|
163071
163094
|
resolved = config2.default ?? fallback;
|
|
163072
163095
|
}
|
|
@@ -166020,7 +166043,9 @@ function createTransform(deps) {
|
|
|
166020
166043
|
const lastAssistantModel = findLastAssistantModel(messages);
|
|
166021
166044
|
if (lastAssistantModel) {
|
|
166022
166045
|
const knownModel = deps.liveModelBySession.get(sessionId);
|
|
166023
|
-
if (knownModel
|
|
166046
|
+
if (!knownModel) {
|
|
166047
|
+
deps.liveModelBySession.set(sessionId, lastAssistantModel);
|
|
166048
|
+
} else if (knownModel.providerID !== lastAssistantModel.providerID || knownModel.modelID !== lastAssistantModel.modelID) {
|
|
166024
166049
|
sessionLog(sessionId, `transform: model change detected (${knownModel.providerID}/${knownModel.modelID} -> ${lastAssistantModel.providerID}/${lastAssistantModel.modelID}), clearing stale context state`);
|
|
166025
166050
|
deps.liveModelBySession.set(sessionId, lastAssistantModel);
|
|
166026
166051
|
updateSessionMeta(db, sessionId, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"models-dev-cache.d.ts","sourceRoot":"","sources":["../../src/shared/models-dev-cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAMH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAG7D,KAAK,cAAc,GAAG,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"models-dev-cache.d.ts","sourceRoot":"","sources":["../../src/shared/models-dev-cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAMH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAG7D,KAAK,cAAc,GAAG,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAC;AA+K9D;;;;;;;;;;GAUG;AACH,wBAAsB,yBAAyB,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAmCrF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,wBAAwB,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAchG;AAED,4CAA4C;AAC5C,wBAAgB,mBAAmB,IAAI,IAAI,CAK1C;AAED,oDAAoD;AACpD,wBAAgB,sBAAsB,IAAI;IACtC,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACrB,CAOA"}
|
package/package.json
CHANGED
|
@@ -19,11 +19,17 @@ describe("models-dev-cache", () => {
|
|
|
19
19
|
OPENCODE_MODELS_PATH: process.env.OPENCODE_MODELS_PATH,
|
|
20
20
|
OPENCODE_MODELS_URL: process.env.OPENCODE_MODELS_URL,
|
|
21
21
|
XDG_CACHE_HOME: process.env.XDG_CACHE_HOME,
|
|
22
|
+
OPENCODE_CONFIG_DIR: process.env.OPENCODE_CONFIG_DIR,
|
|
22
23
|
};
|
|
23
|
-
// Isolate from user environment.
|
|
24
|
+
// Isolate from user environment — including user's ~/.config/opencode/opencode.jsonc
|
|
25
|
+
// which may have custom provider limits that would override models.json entries.
|
|
24
26
|
delete process.env.OPENCODE_MODELS_PATH;
|
|
25
27
|
delete process.env.OPENCODE_MODELS_URL;
|
|
26
28
|
process.env.XDG_CACHE_HOME = tempDir;
|
|
29
|
+
// Point at an empty directory so no opencode.json{c} is read unless the test writes one.
|
|
30
|
+
const emptyConfigDir = join(tempDir, "config", "opencode");
|
|
31
|
+
mkdirSync(emptyConfigDir, { recursive: true });
|
|
32
|
+
process.env.OPENCODE_CONFIG_DIR = emptyConfigDir;
|
|
27
33
|
clearModelsDevCache();
|
|
28
34
|
});
|
|
29
35
|
|
|
@@ -61,6 +67,113 @@ describe("models-dev-cache", () => {
|
|
|
61
67
|
expect(getModelsDevContextLimit("unknown", "unknown")).toBeUndefined();
|
|
62
68
|
});
|
|
63
69
|
|
|
70
|
+
test("prefers limit.input over limit.context when both are present", () => {
|
|
71
|
+
//#given — GitHub Copilot shape: input is max prompt, context is total window.
|
|
72
|
+
// Matches real-world github-copilot/gpt-5.3-codex which has
|
|
73
|
+
// limit.context = 400000 (total), limit.input = 272000 (max prompt).
|
|
74
|
+
// Our pressure math must use the input cap; sending a 400K prompt gets rejected.
|
|
75
|
+
// OpenCode's own session/overflow.ts follows the same rule.
|
|
76
|
+
const opencodeDir = join(tempDir, "opencode");
|
|
77
|
+
mkdirSync(opencodeDir, { recursive: true });
|
|
78
|
+
writeFileSync(
|
|
79
|
+
join(opencodeDir, "models.json"),
|
|
80
|
+
JSON.stringify({
|
|
81
|
+
"github-copilot": {
|
|
82
|
+
models: {
|
|
83
|
+
"gpt-5.3-codex": { limit: { context: 400000, input: 272000 } },
|
|
84
|
+
"claude-opus-4.6": { limit: { context: 144000, input: 128000 } },
|
|
85
|
+
// Context-only model (no input) falls back to context.
|
|
86
|
+
"legacy-only-context": { limit: { context: 100000 } },
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
}),
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
//#then
|
|
93
|
+
expect(getModelsDevContextLimit("github-copilot", "gpt-5.3-codex")).toBe(272000);
|
|
94
|
+
expect(getModelsDevContextLimit("github-copilot", "claude-opus-4.6")).toBe(128000);
|
|
95
|
+
expect(getModelsDevContextLimit("github-copilot", "legacy-only-context")).toBe(100000);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test("derived experimental.modes inherit the effective (input) limit", () => {
|
|
99
|
+
//#given — parent has input < context; derived modes should inherit input, not context
|
|
100
|
+
const opencodeDir = join(tempDir, "opencode");
|
|
101
|
+
mkdirSync(opencodeDir, { recursive: true });
|
|
102
|
+
writeFileSync(
|
|
103
|
+
join(opencodeDir, "models.json"),
|
|
104
|
+
JSON.stringify({
|
|
105
|
+
openai: {
|
|
106
|
+
models: {
|
|
107
|
+
"gpt-5.4": {
|
|
108
|
+
limit: { context: 1050000, input: 922000 },
|
|
109
|
+
experimental: { modes: { fast: {}, mini: {} } },
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
}),
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
//#then
|
|
117
|
+
expect(getModelsDevContextLimit("openai", "gpt-5.4")).toBe(922000);
|
|
118
|
+
expect(getModelsDevContextLimit("openai", "gpt-5.4-fast")).toBe(922000);
|
|
119
|
+
expect(getModelsDevContextLimit("openai", "gpt-5.4-mini")).toBe(922000);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
test("custom opencode.json provider overlay uses limit.input preferentially", () => {
|
|
123
|
+
//#given — user defines a proxy provider in opencode.json with input < context
|
|
124
|
+
const opencodeDir = join(tempDir, "opencode");
|
|
125
|
+
mkdirSync(opencodeDir, { recursive: true });
|
|
126
|
+
const configDir = join(tempDir, "config", "opencode");
|
|
127
|
+
mkdirSync(configDir, { recursive: true });
|
|
128
|
+
writeFileSync(
|
|
129
|
+
join(configDir, "opencode.json"),
|
|
130
|
+
JSON.stringify({
|
|
131
|
+
provider: {
|
|
132
|
+
"my-proxy": {
|
|
133
|
+
models: {
|
|
134
|
+
"split-model": { limit: { context: 400000, input: 200000 } },
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
}),
|
|
139
|
+
);
|
|
140
|
+
process.env.OPENCODE_CONFIG_DIR = configDir;
|
|
141
|
+
clearModelsDevCache();
|
|
142
|
+
|
|
143
|
+
//#then
|
|
144
|
+
expect(getModelsDevContextLimit("my-proxy", "split-model")).toBe(200000);
|
|
145
|
+
|
|
146
|
+
// Cleanup: restore env (afterEach also handles this, but we added a new var)
|
|
147
|
+
delete process.env.OPENCODE_CONFIG_DIR;
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
test("API cache uses limit.input preferentially", async () => {
|
|
151
|
+
//#given — API response shape mirrors file layer
|
|
152
|
+
const mockClient = {
|
|
153
|
+
config: {
|
|
154
|
+
providers: async () => ({
|
|
155
|
+
data: {
|
|
156
|
+
providers: [
|
|
157
|
+
{
|
|
158
|
+
id: "github-copilot",
|
|
159
|
+
models: {
|
|
160
|
+
"gpt-5.3-codex": {
|
|
161
|
+
limit: { context: 400000, input: 272000 },
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
],
|
|
166
|
+
},
|
|
167
|
+
}),
|
|
168
|
+
},
|
|
169
|
+
};
|
|
170
|
+
// @ts-expect-error mock narrow shape
|
|
171
|
+
await refreshModelLimitsFromApi(mockClient);
|
|
172
|
+
|
|
173
|
+
//#then
|
|
174
|
+
expect(getModelsDevContextLimit("github-copilot", "gpt-5.3-codex")).toBe(272000);
|
|
175
|
+
});
|
|
176
|
+
|
|
64
177
|
test("expands experimental.modes into derived model IDs with parent context", () => {
|
|
65
178
|
const opencodeDir = join(tempDir, "opencode");
|
|
66
179
|
mkdirSync(opencodeDir, { recursive: true });
|
|
@@ -86,6 +86,24 @@ function getOpencodeConfigPath(): string | null {
|
|
|
86
86
|
return null;
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
+
/**
|
|
90
|
+
* Resolve the effective pressure limit for a model's `limit` object.
|
|
91
|
+
*
|
|
92
|
+
* Prefers `limit.input` (max prompt tokens the provider will accept) over
|
|
93
|
+
* `limit.context` (total window including output). For GitHub Copilot and
|
|
94
|
+
* several proxy providers, `context` is the marketing number (input + output
|
|
95
|
+
* combined), and sending a prompt sized against `context` gets rejected.
|
|
96
|
+
* OpenCode's own `session/overflow.ts` uses `input ?? context` for the same
|
|
97
|
+
* reason — the denominator that drives overflow/pressure must be the number
|
|
98
|
+
* the provider actually enforces on input.
|
|
99
|
+
*/
|
|
100
|
+
function resolveLimit(limit: { context?: number; input?: number } | undefined): number | undefined {
|
|
101
|
+
if (!limit) return undefined;
|
|
102
|
+
if (typeof limit.input === "number" && limit.input > 0) return limit.input;
|
|
103
|
+
if (typeof limit.context === "number" && limit.context > 0) return limit.context;
|
|
104
|
+
return undefined;
|
|
105
|
+
}
|
|
106
|
+
|
|
89
107
|
function loadModelsDevLimitsFromFile(): Map<string, number> {
|
|
90
108
|
const limits = new Map<string, number>();
|
|
91
109
|
|
|
@@ -102,7 +120,7 @@ function loadModelsDevLimitsFromFile(): Map<string, number> {
|
|
|
102
120
|
models?: Record<
|
|
103
121
|
string,
|
|
104
122
|
{
|
|
105
|
-
limit?: { context?: number };
|
|
123
|
+
limit?: { context?: number; input?: number };
|
|
106
124
|
experimental?: { modes?: Record<string, unknown> };
|
|
107
125
|
}
|
|
108
126
|
>;
|
|
@@ -112,15 +130,15 @@ function loadModelsDevLimitsFromFile(): Map<string, number> {
|
|
|
112
130
|
for (const [providerId, provider] of Object.entries(data)) {
|
|
113
131
|
if (!provider?.models || typeof provider.models !== "object") continue;
|
|
114
132
|
for (const [modelId, model] of Object.entries(provider.models)) {
|
|
115
|
-
const
|
|
116
|
-
if (typeof
|
|
117
|
-
limits.set(`${providerId}/${modelId}`,
|
|
133
|
+
const effective = resolveLimit(model?.limit);
|
|
134
|
+
if (typeof effective === "number" && effective > 0) {
|
|
135
|
+
limits.set(`${providerId}/${modelId}`, effective);
|
|
118
136
|
// OpenCode creates derived model IDs from experimental.modes
|
|
119
137
|
// e.g. gpt-5.4 + modes.fast → gpt-5.4-fast (inherits parent limit).
|
|
120
138
|
const modes = model?.experimental?.modes;
|
|
121
139
|
if (modes && typeof modes === "object") {
|
|
122
140
|
for (const mode of Object.keys(modes)) {
|
|
123
|
-
limits.set(`${providerId}/${modelId}-${mode}`,
|
|
141
|
+
limits.set(`${providerId}/${modelId}-${mode}`, effective);
|
|
124
142
|
}
|
|
125
143
|
}
|
|
126
144
|
}
|
|
@@ -136,7 +154,7 @@ function loadModelsDevLimitsFromFile(): Map<string, number> {
|
|
|
136
154
|
}
|
|
137
155
|
|
|
138
156
|
// 2. Overlay custom provider models from OpenCode config (higher priority).
|
|
139
|
-
// Users define custom/proxy models via provider.<id>.models.<name>.limit.context
|
|
157
|
+
// Users define custom/proxy models via provider.<id>.models.<name>.limit.{input,context}
|
|
140
158
|
// in opencode.json(c). These override models.dev entries for the same key.
|
|
141
159
|
try {
|
|
142
160
|
const configPath = getOpencodeConfigPath();
|
|
@@ -149,7 +167,9 @@ function loadModelsDevLimitsFromFile(): Map<string, number> {
|
|
|
149
167
|
const config = JSON.parse(raw) as {
|
|
150
168
|
provider?: Record<
|
|
151
169
|
string,
|
|
152
|
-
{
|
|
170
|
+
{
|
|
171
|
+
models?: Record<string, { limit?: { context?: number; input?: number } }>;
|
|
172
|
+
}
|
|
153
173
|
>;
|
|
154
174
|
};
|
|
155
175
|
|
|
@@ -157,9 +177,9 @@ function loadModelsDevLimitsFromFile(): Map<string, number> {
|
|
|
157
177
|
for (const [providerId, provider] of Object.entries(config.provider)) {
|
|
158
178
|
if (!provider?.models || typeof provider.models !== "object") continue;
|
|
159
179
|
for (const [modelId, model] of Object.entries(provider.models)) {
|
|
160
|
-
const
|
|
161
|
-
if (typeof
|
|
162
|
-
limits.set(`${providerId}/${modelId}`,
|
|
180
|
+
const effective = resolveLimit(model?.limit);
|
|
181
|
+
if (typeof effective === "number" && effective > 0) {
|
|
182
|
+
limits.set(`${providerId}/${modelId}`, effective);
|
|
163
183
|
}
|
|
164
184
|
}
|
|
165
185
|
}
|
|
@@ -206,13 +226,13 @@ export async function refreshModelLimitsFromApi(client: OpencodeClient): Promise
|
|
|
206
226
|
for (const entry of providers) {
|
|
207
227
|
const p = entry as {
|
|
208
228
|
id?: string;
|
|
209
|
-
models?: Record<string, { limit?: { context?: number } }>;
|
|
229
|
+
models?: Record<string, { limit?: { context?: number; input?: number } }>;
|
|
210
230
|
};
|
|
211
231
|
if (!p?.id || !p.models || typeof p.models !== "object") continue;
|
|
212
232
|
for (const [modelId, model] of Object.entries(p.models)) {
|
|
213
|
-
const
|
|
214
|
-
if (typeof
|
|
215
|
-
map.set(`${p.id}/${modelId}`,
|
|
233
|
+
const effective = resolveLimit(model?.limit);
|
|
234
|
+
if (typeof effective === "number" && effective > 0) {
|
|
235
|
+
map.set(`${p.id}/${modelId}`, effective);
|
|
216
236
|
}
|
|
217
237
|
}
|
|
218
238
|
}
|