pi-openai-toolkit 0.3.0 → 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/README.md +25 -4
- package/README_zh.md +25 -4
- package/package.json +79 -79
- package/src/config.ts +283 -273
- package/src/debug.ts +18 -7
- package/src/extension-runtime.ts +92 -50
- package/src/native-fallback.ts +2 -21
- package/src/runtime.ts +445 -283
- package/src/supported-environment.ts +5 -0
- package/src/types.ts +18 -0
- package/tsconfig.check.json +36 -36
package/src/runtime.ts
CHANGED
|
@@ -1,283 +1,445 @@
|
|
|
1
|
-
import type { Api, Model, ProviderHeaders } from "@earendil-works/pi-ai";
|
|
2
|
-
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
3
|
-
import { RESPONSES_COMPACT_CAPABLE_APIS } from "./types";
|
|
4
|
-
|
|
5
|
-
const OPENAI_RESPONSES_PATH = "responses";
|
|
6
|
-
const CODEX_RESPONSES_PATH = "codex/responses";
|
|
7
|
-
const OPENAI_COMPACT_PATH = "responses/compact";
|
|
8
|
-
const CODEX_COMPACT_PATH = "codex/responses/compact";
|
|
9
|
-
|
|
10
|
-
type ResponsesCompactApi = (typeof RESPONSES_COMPACT_CAPABLE_APIS)[number];
|
|
11
|
-
|
|
12
|
-
type RuntimeModel = Model<Api>;
|
|
13
|
-
|
|
14
|
-
type
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
| "
|
|
21
|
-
| "
|
|
22
|
-
| "
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
export type
|
|
63
|
-
ok:
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
export
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
export function
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
return
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
};
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
if (!
|
|
208
|
-
return {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
}
|
|
1
|
+
import type { Api, Model, ProviderHeaders } from "@earendil-works/pi-ai";
|
|
2
|
+
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
3
|
+
import { RESPONSES_COMPACT_CAPABLE_APIS } from "./types";
|
|
4
|
+
|
|
5
|
+
const OPENAI_RESPONSES_PATH = "responses";
|
|
6
|
+
const CODEX_RESPONSES_PATH = "codex/responses";
|
|
7
|
+
const OPENAI_COMPACT_PATH = "responses/compact";
|
|
8
|
+
const CODEX_COMPACT_PATH = "codex/responses/compact";
|
|
9
|
+
|
|
10
|
+
type ResponsesCompactApi = (typeof RESPONSES_COMPACT_CAPABLE_APIS)[number];
|
|
11
|
+
|
|
12
|
+
type RuntimeModel = Model<Api>;
|
|
13
|
+
|
|
14
|
+
export type ParsedModelSpec = {
|
|
15
|
+
provider: string;
|
|
16
|
+
modelId: string;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export type NativeCompactionFailureReason =
|
|
20
|
+
| "disabled"
|
|
21
|
+
| "missing-model"
|
|
22
|
+
| "invalid-model-spec"
|
|
23
|
+
| "model-not-found"
|
|
24
|
+
| "unsupported-api"
|
|
25
|
+
| "missing-base-url"
|
|
26
|
+
| "missing-api-key"
|
|
27
|
+
| "auth-resolution-failed"
|
|
28
|
+
| "unsupported-payload"
|
|
29
|
+
| "payload-model-mismatch"
|
|
30
|
+
| "provider-mismatch"
|
|
31
|
+
| "api-mismatch"
|
|
32
|
+
| "base-url-mismatch";
|
|
33
|
+
|
|
34
|
+
export type NativeCompactionSupportOptions = {
|
|
35
|
+
enabled?: boolean;
|
|
36
|
+
/** Which Responses APIs should use the compact endpoint; defaults to all capable APIs. */
|
|
37
|
+
responsesApis?: readonly string[];
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export type ResponsesCompatibleRequestPayload = {
|
|
41
|
+
model: string;
|
|
42
|
+
input: unknown[];
|
|
43
|
+
instructions?: unknown;
|
|
44
|
+
[key: string]: unknown;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export type NativeCompactionRuntime = {
|
|
48
|
+
provider: string;
|
|
49
|
+
api: ResponsesCompactApi;
|
|
50
|
+
model: string;
|
|
51
|
+
baseUrl: string;
|
|
52
|
+
apiKey: string;
|
|
53
|
+
headers?: ProviderHeaders;
|
|
54
|
+
responsesPath: string;
|
|
55
|
+
responsesUrl: string;
|
|
56
|
+
compactPath: string;
|
|
57
|
+
compactUrl: string;
|
|
58
|
+
payload?: ResponsesCompatibleRequestPayload;
|
|
59
|
+
currentModel: RuntimeModel;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export type NativeCompactionEnvironmentFailure = {
|
|
63
|
+
ok: false;
|
|
64
|
+
reason: NativeCompactionFailureReason;
|
|
65
|
+
provider?: string;
|
|
66
|
+
api?: string;
|
|
67
|
+
model?: string;
|
|
68
|
+
baseUrl?: string;
|
|
69
|
+
modelSpec?: string;
|
|
70
|
+
errorMessage?: string;
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
export type NativeCompactionEnvironmentSuccess = {
|
|
74
|
+
ok: true;
|
|
75
|
+
runtime: NativeCompactionRuntime;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
export type NativeCompactionEnvironmentResolution =
|
|
79
|
+
| NativeCompactionEnvironmentFailure
|
|
80
|
+
| NativeCompactionEnvironmentSuccess;
|
|
81
|
+
|
|
82
|
+
export type RemoteCompactionExecution = {
|
|
83
|
+
/** Active session model. Its identity owns replay matching and is never mutated. */
|
|
84
|
+
consumer: NativeCompactionRuntime;
|
|
85
|
+
/** Model used only for the synthetic remote_compaction_v2 request. */
|
|
86
|
+
compactor: NativeCompactionRuntime;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
export type RemoteCompactionExecutionSuccess = {
|
|
90
|
+
ok: true;
|
|
91
|
+
execution: RemoteCompactionExecution;
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
export type RemoteCompactionExecutionResolution =
|
|
95
|
+
| NativeCompactionEnvironmentFailure
|
|
96
|
+
| RemoteCompactionExecutionSuccess;
|
|
97
|
+
|
|
98
|
+
type ResolvedRequestAuth =
|
|
99
|
+
| {
|
|
100
|
+
ok: true;
|
|
101
|
+
apiKey?: string;
|
|
102
|
+
headers?: ProviderHeaders;
|
|
103
|
+
baseUrl?: string;
|
|
104
|
+
}
|
|
105
|
+
| { ok: false; error: string };
|
|
106
|
+
|
|
107
|
+
function normalizeConfiguredApis(values: readonly string[] | undefined): Set<string> {
|
|
108
|
+
if (values === undefined) {
|
|
109
|
+
return new Set(RESPONSES_COMPACT_CAPABLE_APIS);
|
|
110
|
+
}
|
|
111
|
+
return new Set(values.map((value) => value.trim()).filter((value) => value.length > 0));
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** Parse "provider/model-id" (model ids may themselves contain slashes). */
|
|
115
|
+
export function parseModelSpec(spec: string): ParsedModelSpec | undefined {
|
|
116
|
+
const trimmed = spec.trim();
|
|
117
|
+
const separatorIndex = trimmed.indexOf("/");
|
|
118
|
+
if (separatorIndex <= 0 || separatorIndex >= trimmed.length - 1) {
|
|
119
|
+
return undefined;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const provider = trimmed.slice(0, separatorIndex).trim();
|
|
123
|
+
const modelId = trimmed.slice(separatorIndex + 1).trim();
|
|
124
|
+
if (!provider || !modelId) {
|
|
125
|
+
return undefined;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return { provider, modelId };
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export function normalizeBaseUrl(baseUrl: string | undefined | null): string | undefined {
|
|
132
|
+
const normalized = baseUrl?.trim().replace(/\/+$/, "");
|
|
133
|
+
return normalized ? normalized : undefined;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function buildOpenAIResponsesUrl(baseUrl: string): string {
|
|
137
|
+
const normalized = normalizeBaseUrl(baseUrl) ?? baseUrl;
|
|
138
|
+
return normalized.endsWith("/responses") ? normalized : `${normalized}/${OPENAI_RESPONSES_PATH}`;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function buildCodexResponsesUrl(baseUrl: string): string {
|
|
142
|
+
const normalized = normalizeBaseUrl(baseUrl) ?? baseUrl;
|
|
143
|
+
if (normalized.endsWith("/codex/responses")) {
|
|
144
|
+
return normalized;
|
|
145
|
+
}
|
|
146
|
+
if (normalized.endsWith("/codex")) {
|
|
147
|
+
return `${normalized}/responses`;
|
|
148
|
+
}
|
|
149
|
+
return `${normalized}/${CODEX_RESPONSES_PATH}`;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function buildOpenAICompactUrl(baseUrl: string): string {
|
|
153
|
+
return `${buildOpenAIResponsesUrl(baseUrl)}/compact`;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function buildCodexCompactUrl(baseUrl: string): string {
|
|
157
|
+
return `${buildCodexResponsesUrl(baseUrl)}/compact`;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export function buildResponsesUrl(baseUrl: string, api: ResponsesCompactApi): string {
|
|
161
|
+
return api === "openai-codex-responses" ? buildCodexResponsesUrl(baseUrl) : buildOpenAIResponsesUrl(baseUrl);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export function buildResponsesPath(api: ResponsesCompactApi): string {
|
|
165
|
+
return api === "openai-codex-responses" ? CODEX_RESPONSES_PATH : OPENAI_RESPONSES_PATH;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export function buildCompactUrl(baseUrl: string, api: ResponsesCompactApi): string {
|
|
169
|
+
return api === "openai-codex-responses" ? buildCodexCompactUrl(baseUrl) : buildOpenAICompactUrl(baseUrl);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export function buildCompactPath(api: ResponsesCompactApi): string {
|
|
173
|
+
return api === "openai-codex-responses" ? CODEX_COMPACT_PATH : OPENAI_COMPACT_PATH;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
async function resolveRequestAuth(ctx: ExtensionContext, model: RuntimeModel): Promise<ResolvedRequestAuth> {
|
|
177
|
+
const modelRegistry = ctx.modelRegistry as {
|
|
178
|
+
getApiKeyAndHeaders?: (currentModel: RuntimeModel) => Promise<ResolvedRequestAuth>;
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
if (typeof modelRegistry.getApiKeyAndHeaders !== "function") {
|
|
182
|
+
return { ok: true };
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return modelRegistry.getApiKeyAndHeaders(model);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export function isSupportedApi(api: string): api is ResponsesCompactApi {
|
|
189
|
+
return (RESPONSES_COMPACT_CAPABLE_APIS as readonly string[]).includes(api);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export function isResponsesCompatiblePayload(payload: unknown): payload is ResponsesCompatibleRequestPayload {
|
|
193
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const candidate = payload as Record<string, unknown>;
|
|
198
|
+
return typeof candidate.model === "string" && Array.isArray(candidate.input);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export function getRuntimeModelDescriptor(model: RuntimeModel | undefined): {
|
|
202
|
+
provider?: string;
|
|
203
|
+
api?: string;
|
|
204
|
+
model?: string;
|
|
205
|
+
baseUrl?: string;
|
|
206
|
+
} {
|
|
207
|
+
if (!model) {
|
|
208
|
+
return {};
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return {
|
|
212
|
+
provider: model.provider,
|
|
213
|
+
api: model.api,
|
|
214
|
+
model: model.id,
|
|
215
|
+
baseUrl: normalizeBaseUrl(model.baseUrl),
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
async function resolveNativeCompactionEnvironmentForModel(
|
|
220
|
+
ctx: ExtensionContext,
|
|
221
|
+
currentModel: RuntimeModel | undefined,
|
|
222
|
+
options: NativeCompactionSupportOptions,
|
|
223
|
+
payload?: unknown,
|
|
224
|
+
): Promise<NativeCompactionEnvironmentResolution> {
|
|
225
|
+
if (options.enabled === false) {
|
|
226
|
+
return {
|
|
227
|
+
ok: false,
|
|
228
|
+
reason: "disabled",
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const descriptor = getRuntimeModelDescriptor(currentModel);
|
|
233
|
+
if (!currentModel || !descriptor.provider || !descriptor.api || !descriptor.model) {
|
|
234
|
+
return {
|
|
235
|
+
ok: false,
|
|
236
|
+
reason: "missing-model",
|
|
237
|
+
...descriptor,
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// The compact endpoint is selected purely by API family: any provider speaking
|
|
242
|
+
// openai-responses/openai-codex-responses gets a native compact attempt and fails
|
|
243
|
+
// open (to the fallback model or pi's default) when the endpoint is missing.
|
|
244
|
+
const configuredApis = normalizeConfiguredApis(options.responsesApis);
|
|
245
|
+
if (!configuredApis.has(descriptor.api) || !isSupportedApi(descriptor.api)) {
|
|
246
|
+
return {
|
|
247
|
+
ok: false,
|
|
248
|
+
reason: "unsupported-api",
|
|
249
|
+
...descriptor,
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
let requestPayload: ResponsesCompatibleRequestPayload | undefined;
|
|
254
|
+
if (payload !== undefined) {
|
|
255
|
+
if (!isResponsesCompatiblePayload(payload)) {
|
|
256
|
+
return {
|
|
257
|
+
ok: false,
|
|
258
|
+
reason: "unsupported-payload",
|
|
259
|
+
...descriptor,
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if (payload.model !== descriptor.model) {
|
|
264
|
+
return {
|
|
265
|
+
ok: false,
|
|
266
|
+
reason: "payload-model-mismatch",
|
|
267
|
+
...descriptor,
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
requestPayload = payload;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
let auth: ResolvedRequestAuth;
|
|
275
|
+
try {
|
|
276
|
+
auth = await resolveRequestAuth(ctx, currentModel);
|
|
277
|
+
} catch (error) {
|
|
278
|
+
return {
|
|
279
|
+
ok: false,
|
|
280
|
+
reason: "auth-resolution-failed",
|
|
281
|
+
errorMessage: error instanceof Error ? error.message : String(error),
|
|
282
|
+
...descriptor,
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
if (!auth.ok) {
|
|
287
|
+
return {
|
|
288
|
+
ok: false,
|
|
289
|
+
reason: "auth-resolution-failed",
|
|
290
|
+
errorMessage: auth.error,
|
|
291
|
+
...descriptor,
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const baseUrl = normalizeBaseUrl(auth.baseUrl) ?? descriptor.baseUrl;
|
|
296
|
+
if (!baseUrl) {
|
|
297
|
+
return {
|
|
298
|
+
ok: false,
|
|
299
|
+
reason: "missing-base-url",
|
|
300
|
+
...descriptor,
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
if (!auth.apiKey) {
|
|
305
|
+
return {
|
|
306
|
+
ok: false,
|
|
307
|
+
reason: "missing-api-key",
|
|
308
|
+
...descriptor,
|
|
309
|
+
baseUrl,
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
return {
|
|
314
|
+
ok: true,
|
|
315
|
+
runtime: {
|
|
316
|
+
provider: descriptor.provider,
|
|
317
|
+
api: descriptor.api,
|
|
318
|
+
model: descriptor.model,
|
|
319
|
+
baseUrl,
|
|
320
|
+
apiKey: auth.apiKey,
|
|
321
|
+
headers: auth.headers,
|
|
322
|
+
responsesPath: buildResponsesPath(descriptor.api),
|
|
323
|
+
responsesUrl: buildResponsesUrl(baseUrl, descriptor.api),
|
|
324
|
+
compactPath: buildCompactPath(descriptor.api),
|
|
325
|
+
compactUrl: buildCompactUrl(baseUrl, descriptor.api),
|
|
326
|
+
payload: requestPayload,
|
|
327
|
+
currentModel,
|
|
328
|
+
},
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
export async function resolveNativeCompactionEnvironment(
|
|
333
|
+
ctx: ExtensionContext,
|
|
334
|
+
options: NativeCompactionSupportOptions = {},
|
|
335
|
+
payload?: unknown,
|
|
336
|
+
): Promise<NativeCompactionEnvironmentResolution> {
|
|
337
|
+
return resolveNativeCompactionEnvironmentForModel(ctx, ctx.model, options, payload);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
export async function resolveRemoteCompactionExecution(
|
|
341
|
+
ctx: ExtensionContext,
|
|
342
|
+
options: NativeCompactionSupportOptions = {},
|
|
343
|
+
remoteModelSpec?: string,
|
|
344
|
+
): Promise<RemoteCompactionExecutionResolution> {
|
|
345
|
+
const consumerResolution = await resolveNativeCompactionEnvironment(ctx, options);
|
|
346
|
+
if (!consumerResolution.ok) {
|
|
347
|
+
return consumerResolution;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
const spec = remoteModelSpec?.trim();
|
|
351
|
+
if (!spec) {
|
|
352
|
+
return {
|
|
353
|
+
ok: true,
|
|
354
|
+
execution: {
|
|
355
|
+
consumer: consumerResolution.runtime,
|
|
356
|
+
compactor: consumerResolution.runtime,
|
|
357
|
+
},
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
const parsed = parseModelSpec(spec);
|
|
362
|
+
if (!parsed) {
|
|
363
|
+
return {
|
|
364
|
+
ok: false,
|
|
365
|
+
reason: "invalid-model-spec",
|
|
366
|
+
modelSpec: spec,
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
const compactorModel = ctx.modelRegistry.find(parsed.provider, parsed.modelId);
|
|
371
|
+
if (!compactorModel) {
|
|
372
|
+
return {
|
|
373
|
+
ok: false,
|
|
374
|
+
reason: "model-not-found",
|
|
375
|
+
provider: parsed.provider,
|
|
376
|
+
model: parsed.modelId,
|
|
377
|
+
modelSpec: spec,
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
const compactorResolution = await resolveNativeCompactionEnvironmentForModel(
|
|
382
|
+
ctx,
|
|
383
|
+
compactorModel,
|
|
384
|
+
options,
|
|
385
|
+
);
|
|
386
|
+
if (!compactorResolution.ok) {
|
|
387
|
+
return {
|
|
388
|
+
...compactorResolution,
|
|
389
|
+
modelSpec: spec,
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
const consumer = consumerResolution.runtime;
|
|
394
|
+
const compactor = compactorResolution.runtime;
|
|
395
|
+
if (compactor.provider !== consumer.provider) {
|
|
396
|
+
return {
|
|
397
|
+
ok: false,
|
|
398
|
+
reason: "provider-mismatch",
|
|
399
|
+
provider: compactor.provider,
|
|
400
|
+
api: compactor.api,
|
|
401
|
+
model: compactor.model,
|
|
402
|
+
baseUrl: compactor.baseUrl,
|
|
403
|
+
modelSpec: spec,
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
if (compactor.api !== consumer.api) {
|
|
407
|
+
return {
|
|
408
|
+
ok: false,
|
|
409
|
+
reason: "api-mismatch",
|
|
410
|
+
provider: compactor.provider,
|
|
411
|
+
api: compactor.api,
|
|
412
|
+
model: compactor.model,
|
|
413
|
+
baseUrl: compactor.baseUrl,
|
|
414
|
+
modelSpec: spec,
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
if (compactor.baseUrl !== consumer.baseUrl) {
|
|
418
|
+
return {
|
|
419
|
+
ok: false,
|
|
420
|
+
reason: "base-url-mismatch",
|
|
421
|
+
provider: compactor.provider,
|
|
422
|
+
api: compactor.api,
|
|
423
|
+
model: compactor.model,
|
|
424
|
+
baseUrl: compactor.baseUrl,
|
|
425
|
+
modelSpec: spec,
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
return {
|
|
430
|
+
ok: true,
|
|
431
|
+
execution: {
|
|
432
|
+
consumer,
|
|
433
|
+
compactor,
|
|
434
|
+
},
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
export async function getNativeCompactionRuntime(
|
|
439
|
+
ctx: ExtensionContext,
|
|
440
|
+
options: NativeCompactionSupportOptions = {},
|
|
441
|
+
payload?: unknown,
|
|
442
|
+
): Promise<NativeCompactionRuntime | undefined> {
|
|
443
|
+
const resolution = await resolveNativeCompactionEnvironment(ctx, options, payload);
|
|
444
|
+
return resolution.ok ? resolution.runtime : undefined;
|
|
445
|
+
}
|