@capybara-hq/kernel 0.1.0-rc.5 → 0.1.0-rc.6
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/admin/admin.controller.d.ts +13 -2
- package/dist/admin/admin.controller.js +29 -4
- package/dist/agents/agents.service.d.ts +3 -1
- package/dist/agents/agents.service.js +8 -6
- package/dist/agents/from-template.service.js +3 -3
- package/dist/agents/models.controller.d.ts +34 -5
- package/dist/agents/models.controller.js +50 -6
- package/dist/agents/readiness.controller.d.ts +5 -5
- package/dist/agents/readiness.controller.js +6 -6
- package/dist/catalog/marketplaces.service.d.ts +1 -1
- package/dist/config/schema.d.ts +4 -0
- package/dist/config/schema.js +24 -0
- package/dist/conversations/conversations.service.d.ts +4 -3
- package/dist/conversations/conversations.service.js +9 -8
- package/dist/ctx/create-ctx.d.ts +15 -1
- package/dist/ctx/create-ctx.js +23 -2
- package/dist/db/kernel-schema.d.ts +24 -0
- package/dist/errors.d.ts +3 -0
- package/dist/errors.js +3 -0
- package/dist/evals/run-replay.js +4 -2
- package/dist/evals/runs.service.d.ts +2 -1
- package/dist/evals/runs.service.js +13 -12
- package/dist/evals/step-replay.d.ts +2 -1
- package/dist/evals/step-replay.js +3 -2
- package/dist/extensions/config-hash.d.ts +10 -0
- package/dist/extensions/config-hash.js +38 -0
- package/dist/extensions/extensions.module.js +6 -12
- package/dist/extensions/host.d.ts +6 -10
- package/dist/extensions/host.js +22 -50
- package/dist/extensions/state.module.d.ts +9 -0
- package/dist/extensions/state.module.js +38 -0
- package/dist/http/api.module.js +2 -0
- package/dist/http/plugin-dispatch.d.ts +4 -1
- package/dist/http/plugin-dispatch.js +14 -2
- package/dist/infra/config/kernel-config.service.js +3 -0
- package/dist/infra/events/kernel-events.d.ts +8 -0
- package/dist/models/embeddings.d.ts +82 -0
- package/dist/models/embeddings.js +326 -0
- package/dist/models/enablement.controller.d.ts +40 -0
- package/dist/models/enablement.controller.js +74 -0
- package/dist/models/enablement.d.ts +99 -0
- package/dist/models/enablement.js +263 -0
- package/dist/models/models.module.d.ts +5 -1
- package/dist/models/models.module.js +16 -4
- package/dist/models/purposes.d.ts +18 -4
- package/dist/models/purposes.js +46 -9
- package/dist/platform/exclusions.js +18 -0
- package/dist/runtime/hook-calls.d.ts +8 -1
- package/dist/runtime/hook-calls.js +40 -9
- package/dist/runtime/model-call.d.ts +8 -3
- package/dist/runtime/model-call.js +28 -5
- package/dist/runtime/providers.d.ts +146 -32
- package/dist/runtime/providers.js +321 -67
- package/dist/runtime/run-worker.d.ts +1 -1
- package/dist/runtime/run-worker.js +4 -4
- package/dist/runtime/subagents/definition.js +3 -3
- package/dist/runtime/usage-report-jobs.d.ts +88 -0
- package/dist/runtime/usage-report-jobs.js +158 -0
- package/dist/runtime/usage-report.d.ts +33 -38
- package/dist/runtime/usage-report.js +152 -84
- package/dist/tools/pipeline.js +6 -0
- package/package.json +3 -3
- package/src/migrations/0089_model_kinds_usage.sql +38 -0
|
@@ -8,6 +8,8 @@ import { type Principal } from "../identity/principal.js";
|
|
|
8
8
|
import { SessionService } from "../identity/sessions.js";
|
|
9
9
|
import { type DeploymentSettings, SettingsService } from "../identity/settings.js";
|
|
10
10
|
import { KernelConfigService } from "../infra/config/kernel-config.service.js";
|
|
11
|
+
import { type ModelDisabledPatch, type ModelProviderParams } from "../models/enablement.controller.js";
|
|
12
|
+
import { type ModelDisableResult, ModelEnablement } from "../models/enablement.js";
|
|
11
13
|
import { type ModelPrice, type ModelPricePut, PricesService } from "../models/prices.js";
|
|
12
14
|
import { ModelPurposesService, type PurposeListing, type PurposePut } from "../models/purposes.js";
|
|
13
15
|
import { type Org, OrgsService } from "../orgs/orgs.service.js";
|
|
@@ -104,8 +106,9 @@ export declare class AdminController {
|
|
|
104
106
|
private readonly registry;
|
|
105
107
|
private readonly catalogRequests;
|
|
106
108
|
private readonly displayNames;
|
|
109
|
+
private readonly enablement;
|
|
107
110
|
private readonly config;
|
|
108
|
-
constructor(accounts: AccountsService, sessions: SessionService, log: Logger, orgs: OrgsService, settings: SettingsService, prices: PricesService, purposes: ModelPurposesService, plugins: ExtensionAdmin, registry: ProviderRegistry, catalogRequests: CatalogRequestsService, displayNames: ExtensionDisplayNames, config: KernelConfigService);
|
|
111
|
+
constructor(accounts: AccountsService, sessions: SessionService, log: Logger, orgs: OrgsService, settings: SettingsService, prices: PricesService, purposes: ModelPurposesService, plugins: ExtensionAdmin, registry: ProviderRegistry, catalogRequests: CatalogRequestsService, displayNames: ExtensionDisplayNames, enablement: ModelEnablement, config: KernelConfigService);
|
|
109
112
|
/** 后台概览的四个数字(只读计数,不含任何对话内容)。 */
|
|
110
113
|
overview(): Promise<AdminOverview>;
|
|
111
114
|
/**
|
|
@@ -139,7 +142,8 @@ export declare class AdminController {
|
|
|
139
142
|
/**
|
|
140
143
|
* 模型供应商与模型(后台「模型」页):每个供应商带状态,列模型失败的标 `error`,不影响其他供应商;
|
|
141
144
|
* 一并返回价格表与币种(改价仍走 `PUT /admin/model-prices`)。Each provider carries its extension's
|
|
142
|
-
* display name (null when it has none)
|
|
145
|
+
* display name (null when it has none); every model of the platform's list carries `disabled`
|
|
146
|
+
* (the platform's disabled list, spec B §5.3).
|
|
143
147
|
*/
|
|
144
148
|
getModels(): Promise<{
|
|
145
149
|
providers: (ProviderStatus & {
|
|
@@ -148,6 +152,13 @@ export declare class AdminController {
|
|
|
148
152
|
prices: ModelPrice[];
|
|
149
153
|
currency: string;
|
|
150
154
|
}>;
|
|
155
|
+
/**
|
|
156
|
+
* The platform's disabled models of one provider (spec B §5.3): disables and enables the ids the
|
|
157
|
+
* body names (others keep their state); such a model is invisible to every org. The response
|
|
158
|
+
* lists the agents and purposes, across orgs, that now point at a newly disabled model (the
|
|
159
|
+
* disable is not refused for them).
|
|
160
|
+
*/
|
|
161
|
+
patchModelsDisabled(principal: Principal, params: z.infer<typeof ModelProviderParams>, body: z.infer<typeof ModelDisabledPatch>): Promise<ModelDisableResult>;
|
|
151
162
|
/** 模型价格表(每百万 token)与币种(部署设置 `priceCurrency`)。 */
|
|
152
163
|
getModelPrices(): Promise<{
|
|
153
164
|
prices: ModelPrice[];
|
|
@@ -21,7 +21,9 @@ import { DEPLOYMENT_SETTINGS_PATCH, SettingsService, } from "../identity/setting
|
|
|
21
21
|
import { KernelConfigService } from "../infra/config/kernel-config.service.js";
|
|
22
22
|
import { KERNEL_LOGGER } from "../infra/tokens.js";
|
|
23
23
|
import { createZodDto } from "../infra/validation/create-zod-dto.js";
|
|
24
|
-
import { ZBody, ZQuery } from "../infra/validation/zod-params.js";
|
|
24
|
+
import { ZBody, ZParam, ZQuery } from "../infra/validation/zod-params.js";
|
|
25
|
+
import { ModelDisabledPatchDto, ModelProviderParamsDto, } from "../models/enablement.controller.js";
|
|
26
|
+
import { ModelEnablement } from "../models/enablement.js";
|
|
25
27
|
import { MODEL_PRICE_PUT, PricesService, } from "../models/prices.js";
|
|
26
28
|
import { purposeTargetOf } from "../models/purposes.controller.js";
|
|
27
29
|
import { ModelPurposesService, PurposePutDto, withDeclaredByNames, } from "../models/purposes.js";
|
|
@@ -73,8 +75,9 @@ let AdminController = class AdminController {
|
|
|
73
75
|
registry;
|
|
74
76
|
catalogRequests;
|
|
75
77
|
displayNames;
|
|
78
|
+
enablement;
|
|
76
79
|
config;
|
|
77
|
-
constructor(accounts, sessions, log, orgs, settings, prices, purposes, plugins, registry, catalogRequests, displayNames, config) {
|
|
80
|
+
constructor(accounts, sessions, log, orgs, settings, prices, purposes, plugins, registry, catalogRequests, displayNames, enablement, config) {
|
|
78
81
|
this.accounts = accounts;
|
|
79
82
|
this.sessions = sessions;
|
|
80
83
|
this.log = log;
|
|
@@ -86,6 +89,7 @@ let AdminController = class AdminController {
|
|
|
86
89
|
this.registry = registry;
|
|
87
90
|
this.catalogRequests = catalogRequests;
|
|
88
91
|
this.displayNames = displayNames;
|
|
92
|
+
this.enablement = enablement;
|
|
89
93
|
this.config = config;
|
|
90
94
|
}
|
|
91
95
|
/** 后台概览的四个数字(只读计数,不含任何对话内容)。 */
|
|
@@ -175,7 +179,8 @@ let AdminController = class AdminController {
|
|
|
175
179
|
/**
|
|
176
180
|
* 模型供应商与模型(后台「模型」页):每个供应商带状态,列模型失败的标 `error`,不影响其他供应商;
|
|
177
181
|
* 一并返回价格表与币种(改价仍走 `PUT /admin/model-prices`)。Each provider carries its extension's
|
|
178
|
-
* display name (null when it has none)
|
|
182
|
+
* display name (null when it has none); every model of the platform's list carries `disabled`
|
|
183
|
+
* (the platform's disabled list, spec B §5.3).
|
|
179
184
|
*/
|
|
180
185
|
async getModels() {
|
|
181
186
|
const [providers, prices] = await Promise.all([
|
|
@@ -188,6 +193,19 @@ let AdminController = class AdminController {
|
|
|
188
193
|
...prices,
|
|
189
194
|
};
|
|
190
195
|
}
|
|
196
|
+
/**
|
|
197
|
+
* The platform's disabled models of one provider (spec B §5.3): disables and enables the ids the
|
|
198
|
+
* body names (others keep their state); such a model is invisible to every org. The response
|
|
199
|
+
* lists the agents and purposes, across orgs, that now point at a newly disabled model (the
|
|
200
|
+
* disable is not refused for them).
|
|
201
|
+
*/
|
|
202
|
+
async patchModelsDisabled(principal, params, body) {
|
|
203
|
+
const actor = requireUser(principal);
|
|
204
|
+
return this.enablement.setPlatform(params.provider, body, {
|
|
205
|
+
accountId: actor.account.id,
|
|
206
|
+
tokenId: patTokenId(actor),
|
|
207
|
+
});
|
|
208
|
+
}
|
|
191
209
|
/** 模型价格表(每百万 token)与币种(部署设置 `priceCurrency`)。 */
|
|
192
210
|
async getModelPrices() {
|
|
193
211
|
const [prices, settings] = await Promise.all([this.prices.list(), this.settings.get()]);
|
|
@@ -244,6 +262,12 @@ __decorate([
|
|
|
244
262
|
__decorate([
|
|
245
263
|
Get("models")
|
|
246
264
|
], AdminController.prototype, "getModels", null);
|
|
265
|
+
__decorate([
|
|
266
|
+
Patch("models/:provider/disabled"),
|
|
267
|
+
__param(0, CurrentPrincipal()),
|
|
268
|
+
__param(1, ZParam(ModelProviderParamsDto)),
|
|
269
|
+
__param(2, ZBody(ModelDisabledPatchDto))
|
|
270
|
+
], AdminController.prototype, "patchModelsDisabled", null);
|
|
247
271
|
__decorate([
|
|
248
272
|
Get("model-prices")
|
|
249
273
|
], AdminController.prototype, "getModelPrices", null);
|
|
@@ -275,6 +299,7 @@ AdminController = __decorate([
|
|
|
275
299
|
__param(8, Inject(ProviderRegistry)),
|
|
276
300
|
__param(9, Inject(CatalogRequestsService)),
|
|
277
301
|
__param(10, Inject(ExtensionDisplayNames)),
|
|
278
|
-
__param(11, Inject(
|
|
302
|
+
__param(11, Inject(ModelEnablement)),
|
|
303
|
+
__param(12, Inject(KernelConfigService))
|
|
279
304
|
], AdminController);
|
|
280
305
|
export { AdminController };
|
|
@@ -105,7 +105,9 @@ export declare class AgentsService {
|
|
|
105
105
|
*/
|
|
106
106
|
private checkTools;
|
|
107
107
|
/**
|
|
108
|
-
*
|
|
108
|
+
* 校验主模型与兜底都可解析:enabled `chat` models in the org (`resolve`'s default kind; a model the
|
|
109
|
+
* platform or the org disabled, or an embedding model → 400 `agent.model_unavailable`, spec B
|
|
110
|
+
* §5.3),并用各自模型的参数声明严格校验 `modelParams` / `fallbackParams`
|
|
109
111
|
* (没有兜底时 `fallbackParams` 必须为空);不合法 → `validation_failed`,details 逐键点名。返回提示。
|
|
110
112
|
*/
|
|
111
113
|
private checkModels;
|
|
@@ -305,13 +305,15 @@ let AgentsService = class AgentsService {
|
|
|
305
305
|
return keys;
|
|
306
306
|
}
|
|
307
307
|
/**
|
|
308
|
-
*
|
|
308
|
+
* 校验主模型与兜底都可解析:enabled `chat` models in the org (`resolve`'s default kind; a model the
|
|
309
|
+
* platform or the org disabled, or an embedding model → 400 `agent.model_unavailable`, spec B
|
|
310
|
+
* §5.3),并用各自模型的参数声明严格校验 `modelParams` / `fallbackParams`
|
|
309
311
|
* (没有兜底时 `fallbackParams` 必须为空);不合法 → `validation_failed`,details 逐键点名。返回提示。
|
|
310
312
|
*/
|
|
311
|
-
async checkModels(def) {
|
|
313
|
+
async checkModels(orgId, def) {
|
|
312
314
|
const registry = this.providers;
|
|
313
|
-
const main = await registry.resolve(def.model);
|
|
314
|
-
const fallback = def.fallback === null ? null : await registry.resolve(def.fallback);
|
|
315
|
+
const main = await registry.resolve(orgId, def.model);
|
|
316
|
+
const fallback = def.fallback === null ? null : await registry.resolve(orgId, def.fallback);
|
|
315
317
|
const details = [
|
|
316
318
|
...paramIssues("modelParams", main.info.parameters, def.modelParams),
|
|
317
319
|
...paramIssues("fallbackParams", fallback?.info.parameters ?? [], def.fallbackParams),
|
|
@@ -369,7 +371,7 @@ let AgentsService = class AgentsService {
|
|
|
369
371
|
async createChecked(orgId, actorAccountId, request, template, actorTokenId, auditDetail) {
|
|
370
372
|
const input = { ...request, definition: deriveInstructions(request.definition, null) };
|
|
371
373
|
// 先解析模型(可能访问供应商),不在持有组织行锁时做
|
|
372
|
-
const warnings = await this.checkModels(input.definition);
|
|
374
|
+
const warnings = await this.checkModels(orgId, input.definition);
|
|
373
375
|
const toolKeys = await this.checkTools(orgId, input.definition);
|
|
374
376
|
if (input.visibility !== "members" && (input.memberIds?.length ?? 0) > 0) {
|
|
375
377
|
throw new ApiError("validation_failed", "memberIds requires visibility 'members'");
|
|
@@ -478,7 +480,7 @@ let AgentsService = class AgentsService {
|
|
|
478
480
|
*/
|
|
479
481
|
async update(orgId, agentId, actorAccountId, request, actorTokenId, auditDetail) {
|
|
480
482
|
const patch = request;
|
|
481
|
-
const warnings = patch.definition ? await this.checkModels(patch.definition) : [];
|
|
483
|
+
const warnings = patch.definition ? await this.checkModels(orgId, patch.definition) : [];
|
|
482
484
|
const toolKeys = patch.definition ? await this.checkTools(orgId, patch.definition) : null;
|
|
483
485
|
const agent = await this.updateIn(orgId, agentId, actorAccountId, patch, toolKeys, actorTokenId, auditDetail);
|
|
484
486
|
return { agent, warnings };
|
|
@@ -62,7 +62,7 @@ let AgentsFromTemplate = class AgentsFromTemplate {
|
|
|
62
62
|
enabledPlugins: await this.host.enabledPlugins(orgId),
|
|
63
63
|
resolveModel: async (ref) => {
|
|
64
64
|
try {
|
|
65
|
-
return (await this.providers.resolve(ref)).info.parameters;
|
|
65
|
+
return (await this.providers.resolve(orgId, ref)).info.parameters;
|
|
66
66
|
}
|
|
67
67
|
catch {
|
|
68
68
|
return null;
|
|
@@ -79,8 +79,8 @@ let AgentsFromTemplate = class AgentsFromTemplate {
|
|
|
79
79
|
}
|
|
80
80
|
},
|
|
81
81
|
defaultModel: async () => {
|
|
82
|
-
for (const p of await this.providers.list()) {
|
|
83
|
-
const m = p.models
|
|
82
|
+
for (const p of await this.providers.list(orgId)) {
|
|
83
|
+
const m = p.models.find((x) => x.kind === "chat");
|
|
84
84
|
if (m)
|
|
85
85
|
return { provider: p.provider, model: m.id };
|
|
86
86
|
}
|
|
@@ -1,19 +1,48 @@
|
|
|
1
|
-
import type { LocalizedName
|
|
1
|
+
import type { LocalizedName } from "@capybara-hq/plugin-sdk";
|
|
2
|
+
import { z } from "zod";
|
|
2
3
|
import { ExtensionDisplayNames } from "../extensions/display-name.js";
|
|
3
|
-
import {
|
|
4
|
+
import { type Principal } from "../identity/principal.js";
|
|
5
|
+
import { type OrgContext } from "../orgs/org.guard.js";
|
|
6
|
+
import { type KernelModelInfo, ProviderRegistry, type ProviderStatus } from "../runtime/providers.js";
|
|
7
|
+
declare const ModelsQuery: z.ZodObject<{
|
|
8
|
+
kind: z.ZodOptional<z.ZodEnum<{
|
|
9
|
+
chat: "chat";
|
|
10
|
+
embedding: "embedding";
|
|
11
|
+
image_generation: "image_generation";
|
|
12
|
+
other: "other";
|
|
13
|
+
rerank: "rerank";
|
|
14
|
+
speech_to_text: "speech_to_text";
|
|
15
|
+
text_to_speech: "text_to_speech";
|
|
16
|
+
video_generation: "video_generation";
|
|
17
|
+
}>>;
|
|
18
|
+
}, z.core.$strip>;
|
|
4
19
|
/**
|
|
5
20
|
* 可选模型(`/api/v1/orgs/:orgId/models`):已安装的模型供应商及其模型,列表缓存在 Redis。Each provider
|
|
6
|
-
* carries its extension's display name (null when it has none).
|
|
21
|
+
* carries its extension's display name (null when it has none). Listed for the org (its effective
|
|
22
|
+
* provider config: a provider needing configuration there is absent); only enabled models (neither
|
|
23
|
+
* the platform nor the org disabled them, spec B §5.3), optionally of one `?kind=`; every model
|
|
24
|
+
* carries its normalised `kind` (`other` for a kind this kernel does not know).
|
|
7
25
|
*/
|
|
8
26
|
export declare class ModelsController {
|
|
9
27
|
private readonly registry;
|
|
10
28
|
private readonly displayNames;
|
|
11
29
|
constructor(registry: ProviderRegistry, displayNames: ExtensionDisplayNames);
|
|
12
|
-
list(): Promise<{
|
|
30
|
+
list(org: OrgContext, q: z.infer<typeof ModelsQuery>): Promise<{
|
|
13
31
|
providers: {
|
|
14
32
|
provider: string;
|
|
15
33
|
displayName: LocalizedName | null;
|
|
16
|
-
models:
|
|
34
|
+
models: KernelModelInfo[];
|
|
17
35
|
}[];
|
|
18
36
|
}>;
|
|
37
|
+
/**
|
|
38
|
+
* The org's model catalog (the settings page; `org.models.manage`, people only): every provider
|
|
39
|
+
* with its status for the org and every model the platform left on, each with `disabled` for the
|
|
40
|
+
* org's own list.
|
|
41
|
+
*/
|
|
42
|
+
catalog(principal: Principal, org: OrgContext): Promise<{
|
|
43
|
+
providers: (ProviderStatus & {
|
|
44
|
+
displayName: LocalizedName | null;
|
|
45
|
+
})[];
|
|
46
|
+
}>;
|
|
19
47
|
}
|
|
48
|
+
export {};
|
|
@@ -8,12 +8,35 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
|
8
8
|
return function (target, key) { decorator(target, key, paramIndex); }
|
|
9
9
|
};
|
|
10
10
|
import { Controller, Get, Inject } from "@nestjs/common";
|
|
11
|
+
import { z } from "zod";
|
|
11
12
|
import { ExtensionDisplayNames } from "../extensions/display-name.js";
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
13
|
+
import { CurrentPrincipal, requireUser } from "../identity/principal.js";
|
|
14
|
+
import { createZodDto } from "../infra/validation/create-zod-dto.js";
|
|
15
|
+
import { ZQuery } from "../infra/validation/zod-params.js";
|
|
16
|
+
import { CurrentOrg, OrgAction } from "../orgs/org.guard.js";
|
|
17
|
+
import { ProviderRegistry, } from "../runtime/providers.js";
|
|
18
|
+
const ModelsQuery = z.object({
|
|
19
|
+
kind: z
|
|
20
|
+
.enum([
|
|
21
|
+
"chat",
|
|
22
|
+
"embedding",
|
|
23
|
+
"rerank",
|
|
24
|
+
"image_generation",
|
|
25
|
+
"video_generation",
|
|
26
|
+
"speech_to_text",
|
|
27
|
+
"text_to_speech",
|
|
28
|
+
"other",
|
|
29
|
+
])
|
|
30
|
+
.optional(),
|
|
31
|
+
});
|
|
32
|
+
class ModelsQueryDto extends createZodDto(ModelsQuery, "ModelsQuery") {
|
|
33
|
+
}
|
|
14
34
|
/**
|
|
15
35
|
* 可选模型(`/api/v1/orgs/:orgId/models`):已安装的模型供应商及其模型,列表缓存在 Redis。Each provider
|
|
16
|
-
* carries its extension's display name (null when it has none).
|
|
36
|
+
* carries its extension's display name (null when it has none). Listed for the org (its effective
|
|
37
|
+
* provider config: a provider needing configuration there is absent); only enabled models (neither
|
|
38
|
+
* the platform nor the org disabled them, spec B §5.3), optionally of one `?kind=`; every model
|
|
39
|
+
* carries its normalised `kind` (`other` for a kind this kernel does not know).
|
|
17
40
|
*/
|
|
18
41
|
let ModelsController = class ModelsController {
|
|
19
42
|
registry;
|
|
@@ -22,8 +45,8 @@ let ModelsController = class ModelsController {
|
|
|
22
45
|
this.registry = registry;
|
|
23
46
|
this.displayNames = displayNames;
|
|
24
47
|
}
|
|
25
|
-
async list() {
|
|
26
|
-
const providers = await this.registry.list();
|
|
48
|
+
async list(org, q) {
|
|
49
|
+
const providers = await this.registry.list(org.orgId, q.kind === undefined ? {} : { kind: q.kind });
|
|
27
50
|
const names = await this.displayNames.many(providers.map((p) => p.provider));
|
|
28
51
|
return {
|
|
29
52
|
providers: providers.map((p) => ({
|
|
@@ -33,11 +56,32 @@ let ModelsController = class ModelsController {
|
|
|
33
56
|
})),
|
|
34
57
|
};
|
|
35
58
|
}
|
|
59
|
+
/**
|
|
60
|
+
* The org's model catalog (the settings page; `org.models.manage`, people only): every provider
|
|
61
|
+
* with its status for the org and every model the platform left on, each with `disabled` for the
|
|
62
|
+
* org's own list.
|
|
63
|
+
*/
|
|
64
|
+
async catalog(principal, org) {
|
|
65
|
+
requireUser(principal);
|
|
66
|
+
const providers = await this.registry.catalog(org.orgId);
|
|
67
|
+
const names = await this.displayNames.many(providers.map((p) => p.plugin));
|
|
68
|
+
return {
|
|
69
|
+
providers: providers.map((p) => ({ ...p, displayName: names.get(p.plugin) ?? null })),
|
|
70
|
+
};
|
|
71
|
+
}
|
|
36
72
|
};
|
|
37
73
|
__decorate([
|
|
38
74
|
Get(),
|
|
39
|
-
OrgAction("agents.use")
|
|
75
|
+
OrgAction("agents.use"),
|
|
76
|
+
__param(0, CurrentOrg()),
|
|
77
|
+
__param(1, ZQuery(ModelsQueryDto))
|
|
40
78
|
], ModelsController.prototype, "list", null);
|
|
79
|
+
__decorate([
|
|
80
|
+
Get("catalog"),
|
|
81
|
+
OrgAction("org.models.manage"),
|
|
82
|
+
__param(0, CurrentPrincipal()),
|
|
83
|
+
__param(1, CurrentOrg())
|
|
84
|
+
], ModelsController.prototype, "catalog", null);
|
|
41
85
|
ModelsController = __decorate([
|
|
42
86
|
Controller("api/v1/orgs/:orgId/models"),
|
|
43
87
|
__param(0, Inject(ProviderRegistry)),
|
|
@@ -9,11 +9,11 @@ import { AgentsService } from "./agents.service.js";
|
|
|
9
9
|
* a vendor call; `agents` is the number of agents the caller can see (the agent list's
|
|
10
10
|
* visibility). Humans and service tokens (`org.read`).
|
|
11
11
|
*
|
|
12
|
-
* `modelProvider`
|
|
13
|
-
* (
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
12
|
+
* `modelProvider` counts the deployment's providers whose effective config is valid in this org
|
|
13
|
+
* (spec B §5.5: a provider the platform installed without a key needs each org's own). It is read
|
|
14
|
+
* from this replica's provider registry, which follows the per-replica extension reconcile: right
|
|
15
|
+
* after a provider is enabled, a replica that has not reconciled yet may still answer `none` for
|
|
16
|
+
* a moment.
|
|
17
17
|
*/
|
|
18
18
|
export declare class ReadinessController {
|
|
19
19
|
private readonly registry;
|
|
@@ -21,11 +21,11 @@ const viewerOf = (p) => (p.kind === "user" ? p.account.id : null);
|
|
|
21
21
|
* a vendor call; `agents` is the number of agents the caller can see (the agent list's
|
|
22
22
|
* visibility). Humans and service tokens (`org.read`).
|
|
23
23
|
*
|
|
24
|
-
* `modelProvider`
|
|
25
|
-
* (
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
24
|
+
* `modelProvider` counts the deployment's providers whose effective config is valid in this org
|
|
25
|
+
* (spec B §5.5: a provider the platform installed without a key needs each org's own). It is read
|
|
26
|
+
* from this replica's provider registry, which follows the per-replica extension reconcile: right
|
|
27
|
+
* after a provider is enabled, a replica that has not reconciled yet may still answer `none` for
|
|
28
|
+
* a moment.
|
|
29
29
|
*/
|
|
30
30
|
let ReadinessController = class ReadinessController {
|
|
31
31
|
registry;
|
|
@@ -37,7 +37,7 @@ let ReadinessController = class ReadinessController {
|
|
|
37
37
|
async get(principal, org) {
|
|
38
38
|
const visible = await this.agents.listVisible(org.orgId, viewerOf(principal));
|
|
39
39
|
return {
|
|
40
|
-
modelProvider: this.registry.hasProvider() ? "ready" : "none",
|
|
40
|
+
modelProvider: (await this.registry.hasProvider(org.orgId)) ? "ready" : "none",
|
|
41
41
|
agents: visible.length,
|
|
42
42
|
};
|
|
43
43
|
}
|
|
@@ -181,7 +181,7 @@ export declare function headsToResolve(entries: readonly MarketplaceEntry[], imp
|
|
|
181
181
|
/** The host versions (an entry's `minKernel` is judged against them). */
|
|
182
182
|
export declare const HOST_VERSIONS: {
|
|
183
183
|
readonly kernel: string;
|
|
184
|
-
readonly sdk: "0.1.0-rc.
|
|
184
|
+
readonly sdk: "0.1.0-rc.4";
|
|
185
185
|
};
|
|
186
186
|
/** Whether this deployment satisfies the entry's `minKernel`. */
|
|
187
187
|
export declare const entryCompatible: (e: Pick<MarketplaceEntry, "minKernel">) => boolean;
|
package/dist/config/schema.d.ts
CHANGED
|
@@ -232,6 +232,10 @@ export declare const KernelConfigSchema: z.ZodObject<{
|
|
|
232
232
|
judgeMaxOutputTokens: z.ZodDefault<z.ZodNumber>;
|
|
233
233
|
maxConsecutiveErrors: z.ZodDefault<z.ZodNumber>;
|
|
234
234
|
}, z.core.$strip>>;
|
|
235
|
+
models: z.ZodPrefault<z.ZodObject<{
|
|
236
|
+
listTimeoutMs: z.ZodDefault<z.ZodNumber>;
|
|
237
|
+
embedTimeoutMs: z.ZodDefault<z.ZodNumber>;
|
|
238
|
+
}, z.core.$strip>>;
|
|
235
239
|
hooks: z.ZodPrefault<z.ZodObject<{
|
|
236
240
|
timeoutMs: z.ZodDefault<z.ZodNumber>;
|
|
237
241
|
usageReportConcurrency: z.ZodDefault<z.ZodNumber>;
|
package/dist/config/schema.js
CHANGED
|
@@ -1252,6 +1252,30 @@ export const KernelConfigSchema = z.object({
|
|
|
1252
1252
|
path: ["defaultTokenCap"],
|
|
1253
1253
|
})
|
|
1254
1254
|
.prefault({}),
|
|
1255
|
+
models: z
|
|
1256
|
+
.object({
|
|
1257
|
+
listTimeoutMs: z
|
|
1258
|
+
.number()
|
|
1259
|
+
.int()
|
|
1260
|
+
.positive()
|
|
1261
|
+
.max(300_000)
|
|
1262
|
+
.default(10_000)
|
|
1263
|
+
.meta(meta({
|
|
1264
|
+
hot: true,
|
|
1265
|
+
description: "Time limit in milliseconds for one model provider's model list (listModels, which may page through a large platform catalog); a provider that exceeds it is treated as failing to list",
|
|
1266
|
+
})),
|
|
1267
|
+
embedTimeoutMs: z
|
|
1268
|
+
.number()
|
|
1269
|
+
.int()
|
|
1270
|
+
.positive()
|
|
1271
|
+
.max(600_000)
|
|
1272
|
+
.default(60_000)
|
|
1273
|
+
.meta(meta({
|
|
1274
|
+
hot: true,
|
|
1275
|
+
description: "Time limit in milliseconds for one embed call to a model provider (one batch of an extension's ctx.models.embed); the call is aborted and fails with a timeout model error",
|
|
1276
|
+
})),
|
|
1277
|
+
})
|
|
1278
|
+
.prefault({}),
|
|
1255
1279
|
hooks: z
|
|
1256
1280
|
.object({
|
|
1257
1281
|
timeoutMs: z
|
|
@@ -372,9 +372,10 @@ export declare class ConversationsService {
|
|
|
372
372
|
* The selection a message switches to, or null when it changes nothing. A model equal to the
|
|
373
373
|
* agent's means "back to the agent's model" (null); a model change without `effort` resets the
|
|
374
374
|
* effort. The target model must resolve (`agent.model_unavailable`) and declare the effort
|
|
375
|
-
* (`validation_failed`). Resolving is the agent editor's check
|
|
376
|
-
*
|
|
377
|
-
* worker's `fallBackIfUnavailable`
|
|
375
|
+
* (`validation_failed`). Resolving is the agent editor's check, for the org (its effective
|
|
376
|
+
* provider config, spec B §5.5) and its disabled lists: a model the platform or the org disabled,
|
|
377
|
+
* or a non-chat model, is refused (spec B §5.3), as the run worker's `fallBackIfUnavailable` falls
|
|
378
|
+
* back from it.
|
|
378
379
|
*/
|
|
379
380
|
private planSwitch;
|
|
380
381
|
/**
|
|
@@ -205,7 +205,7 @@ let ConversationsService = class ConversationsService {
|
|
|
205
205
|
if (prior)
|
|
206
206
|
return prior;
|
|
207
207
|
const environmentId = await this.environments.forNew(orgId, accountId, pick);
|
|
208
|
-
const next = await this.planSwitch(agent.definition, NO_SELECTION, input);
|
|
208
|
+
const next = await this.planSwitch(orgId, agent.definition, NO_SELECTION, input);
|
|
209
209
|
const extra = await this.resolveExtra(orgId, accountId, input);
|
|
210
210
|
const invoked = await this.prepareSkill(orgId, agentId, undefined, agent.definition, input);
|
|
211
211
|
const id = randomUUID();
|
|
@@ -701,7 +701,7 @@ let ConversationsService = class ConversationsService {
|
|
|
701
701
|
: undefined;
|
|
702
702
|
const next = agent === undefined || (input.model === undefined && input.effort === undefined)
|
|
703
703
|
? null
|
|
704
|
-
: await this.planSwitch(agent, conv.selection, input);
|
|
704
|
+
: await this.planSwitch(orgId, agent, conv.selection, input);
|
|
705
705
|
const extra = await this.resolveExtra(orgId, accountId, input);
|
|
706
706
|
const invoked = agent === undefined
|
|
707
707
|
? undefined
|
|
@@ -741,16 +741,17 @@ let ConversationsService = class ConversationsService {
|
|
|
741
741
|
* The selection a message switches to, or null when it changes nothing. A model equal to the
|
|
742
742
|
* agent's means "back to the agent's model" (null); a model change without `effort` resets the
|
|
743
743
|
* effort. The target model must resolve (`agent.model_unavailable`) and declare the effort
|
|
744
|
-
* (`validation_failed`). Resolving is the agent editor's check
|
|
745
|
-
*
|
|
746
|
-
* worker's `fallBackIfUnavailable`
|
|
744
|
+
* (`validation_failed`). Resolving is the agent editor's check, for the org (its effective
|
|
745
|
+
* provider config, spec B §5.5) and its disabled lists: a model the platform or the org disabled,
|
|
746
|
+
* or a non-chat model, is refused (spec B §5.3), as the run worker's `fallBackIfUnavailable` falls
|
|
747
|
+
* back from it.
|
|
747
748
|
*/
|
|
748
|
-
async planSwitch(agent, current, input) {
|
|
749
|
+
async planSwitch(orgId, agent, current, input) {
|
|
749
750
|
const next = planSelection(agent.model, current, input);
|
|
750
751
|
if (next === null)
|
|
751
752
|
return null;
|
|
752
753
|
const { model, effort } = next;
|
|
753
|
-
const { info } = await this.providers.resolve(model ?? agent.model);
|
|
754
|
+
const { info } = await this.providers.resolve(orgId, model ?? agent.model);
|
|
754
755
|
if (effort !== null) {
|
|
755
756
|
const knob = info.parameters.find((p) => p.key === "effort");
|
|
756
757
|
const values = knob?.schema?.enum ?? [];
|
|
@@ -771,7 +772,7 @@ let ConversationsService = class ConversationsService {
|
|
|
771
772
|
if (pick.model === undefined && pick.effort === undefined)
|
|
772
773
|
return;
|
|
773
774
|
const agent = await this.agents.get(orgId, conv.agentId, accountId);
|
|
774
|
-
await this.planSwitch(agent.definition, conv.selection, pick);
|
|
775
|
+
await this.planSwitch(orgId, agent.definition, conv.selection, pick);
|
|
775
776
|
}
|
|
776
777
|
/**
|
|
777
778
|
* Writes the message (preceded by its `model_switched` when it switches) inside `t` and updates
|
package/dist/ctx/create-ctx.d.ts
CHANGED
|
@@ -4,6 +4,8 @@ import { Kysely } from "kysely";
|
|
|
4
4
|
import { type RegisteredHook } from "../extensions/hooks.js";
|
|
5
5
|
import { RegistrationLedger } from "../extensions/ledger.js";
|
|
6
6
|
import type { LockService } from "../infra/lock/lock.service.js";
|
|
7
|
+
import type { ModelEmbeddings } from "../models/embeddings.js";
|
|
8
|
+
import type { HookRunner } from "../runtime/hook-calls.js";
|
|
7
9
|
import type { EmbedTokens } from "../ui/embed-token.js";
|
|
8
10
|
import { type UiActionHandler, type UiRender } from "../ui/registry.js";
|
|
9
11
|
import { type BusSubscriber } from "./bus.js";
|
|
@@ -22,6 +24,17 @@ export interface KernelDeps {
|
|
|
22
24
|
keyring: Keyring;
|
|
23
25
|
/** Verifies embed tokens (`ctx.ui` embed requests; the one `EmbedTokens` provider). */
|
|
24
26
|
embedTokens: Pick<EmbedTokens, "verify">;
|
|
27
|
+
/**
|
|
28
|
+
* The `ctx.models` broker (the one `ModelEmbeddings` provider). Absent (a ctx built alone in a
|
|
29
|
+
* test): `ctx.models` calls reject.
|
|
30
|
+
*/
|
|
31
|
+
models?: Pick<ModelEmbeddings, "embed" | "describe">;
|
|
32
|
+
/**
|
|
33
|
+
* The host's hook registry: `ctx.models.embed` fires `model.request` through it (spec B §5.4).
|
|
34
|
+
* A required key, so no builder leaves policy hooks out by omission (fail closed by
|
|
35
|
+
* construction); only a ctx built alone in a test passes `undefined` (no hooks fire).
|
|
36
|
+
*/
|
|
37
|
+
hooks: HookRunner | undefined;
|
|
25
38
|
runWorkers: boolean;
|
|
26
39
|
/** 内核 logger(`debug` 可选:插件 Logger 没有它,测试可不给)。 */
|
|
27
40
|
log: Logger & {
|
|
@@ -102,7 +115,8 @@ export declare class LiveConfig {
|
|
|
102
115
|
* `deploymentConfig` may be a {@link LiveConfig} (shared by the instance, replaceable in place);
|
|
103
116
|
* otherwise a fixed value is wrapped.
|
|
104
117
|
* - Granted automatically: `kv`, `lock`, `bus`, `queue`, `log`, `db`, `config`, `provide`;
|
|
105
|
-
* - `secrets` only with `permissions.secrets`, `storage` only with `permissions.storage
|
|
118
|
+
* - `secrets` only with `permissions.secrets`, `storage` only with `permissions.storage`, `models`
|
|
119
|
+
* only with `permissions.models` (and only its `embedding` purposes, through `deps.models`); `http.route`
|
|
106
120
|
* only the method + path declared in `permissions.http`; `tools.register` only names declared in
|
|
107
121
|
* `permissions.tools`; `ui.view` / `ui.action` only ids declared in `permissions.ui.views` (during
|
|
108
122
|
* setup only); `ui.invalidate` sends a refresh over the kernel bus (namespace `kernel`), across
|
package/dist/ctx/create-ctx.js
CHANGED
|
@@ -164,7 +164,8 @@ export class LiveConfig {
|
|
|
164
164
|
* `deploymentConfig` may be a {@link LiveConfig} (shared by the instance, replaceable in place);
|
|
165
165
|
* otherwise a fixed value is wrapped.
|
|
166
166
|
* - Granted automatically: `kv`, `lock`, `bus`, `queue`, `log`, `db`, `config`, `provide`;
|
|
167
|
-
* - `secrets` only with `permissions.secrets`, `storage` only with `permissions.storage
|
|
167
|
+
* - `secrets` only with `permissions.secrets`, `storage` only with `permissions.storage`, `models`
|
|
168
|
+
* only with `permissions.models` (and only its `embedding` purposes, through `deps.models`); `http.route`
|
|
168
169
|
* only the method + path declared in `permissions.http`; `tools.register` only names declared in
|
|
169
170
|
* `permissions.tools`; `ui.view` / `ui.action` only ids declared in `permissions.ui.views` (during
|
|
170
171
|
* setup only); `ui.invalidate` sends a refresh over the kernel bus (namespace `kernel`), across
|
|
@@ -218,6 +219,24 @@ export function createExtensionContext(deps, manifest, deploymentConfig, extensi
|
|
|
218
219
|
plugins: async () => [name],
|
|
219
220
|
}).forPlugin(name)
|
|
220
221
|
: undefined;
|
|
222
|
+
// ctx.models: only with permissions.models; the broker accepts only the purposes declared there
|
|
223
|
+
// with kind embedding (spec B §5.4), aborted by this instance's signal
|
|
224
|
+
// a frozen copy: the approved manifest, not an object the extension could change later
|
|
225
|
+
const declaredModels = Object.freeze(structuredClone(perms.models ?? []).map((m) => Object.freeze(m)));
|
|
226
|
+
const modelsApi = declaredModels.length > 0
|
|
227
|
+
? {
|
|
228
|
+
embed: async (req) => {
|
|
229
|
+
if (!deps.models)
|
|
230
|
+
throw new Error(`plugin ${name}: ctx.models is not available`);
|
|
231
|
+
return deps.models.embed(name, declaredModels, req, lifecycle.signal, deps.hooks);
|
|
232
|
+
},
|
|
233
|
+
describe: async (req) => {
|
|
234
|
+
if (!deps.models)
|
|
235
|
+
throw new Error(`plugin ${name}: ctx.models is not available`);
|
|
236
|
+
return deps.models.describe(name, declaredModels, req);
|
|
237
|
+
},
|
|
238
|
+
}
|
|
239
|
+
: undefined;
|
|
221
240
|
const declaredRoutes = new Set((perms.http ?? []).map((r) => `${r.method} ${r.path}`));
|
|
222
241
|
const declaredTools = new Set(perms.tools ?? []);
|
|
223
242
|
const declaredHooks = new Set(perms.hooks ?? []);
|
|
@@ -561,7 +580,8 @@ export function createExtensionContext(deps, manifest, deploymentConfig, extensi
|
|
|
561
580
|
return closing;
|
|
562
581
|
},
|
|
563
582
|
};
|
|
564
|
-
//
|
|
583
|
+
// secrets / storage / models: a plain property when declared; otherwise a non-enumerable getter
|
|
584
|
+
// that throws, so spreading or printing ctx triggers no denial and no audit
|
|
565
585
|
const gated = (key, api) => {
|
|
566
586
|
if (api) {
|
|
567
587
|
Object.assign(ctx, { [key]: api });
|
|
@@ -575,6 +595,7 @@ export function createExtensionContext(deps, manifest, deploymentConfig, extensi
|
|
|
575
595
|
};
|
|
576
596
|
gated("secrets", secretsApi);
|
|
577
597
|
gated("storage", storageApi);
|
|
598
|
+
gated("models", modelsApi);
|
|
578
599
|
return ctx;
|
|
579
600
|
}
|
|
580
601
|
/**
|
|
@@ -1012,6 +1012,28 @@ export interface UsageReportSweepTable {
|
|
|
1012
1012
|
watermark: Ts;
|
|
1013
1013
|
updated_at: ColumnType<Date, Date | string | undefined, Date | string>;
|
|
1014
1014
|
}
|
|
1015
|
+
/** A model disabled on the platform (`org_id` null) or in one org (0089, spec B §5.3). */
|
|
1016
|
+
export interface ModelDisabledTable {
|
|
1017
|
+
org_id: string | null;
|
|
1018
|
+
provider: string;
|
|
1019
|
+
model: string;
|
|
1020
|
+
disabled_by: string | null;
|
|
1021
|
+
disabled_at: Ts;
|
|
1022
|
+
}
|
|
1023
|
+
/** One model call outside a conversation, e.g. an extension's embed call (0089, spec B §5.4). */
|
|
1024
|
+
export interface ModelUsageTable {
|
|
1025
|
+
id: Generated<string>;
|
|
1026
|
+
org_id: string;
|
|
1027
|
+
extension: string;
|
|
1028
|
+
purpose: string;
|
|
1029
|
+
provider: string;
|
|
1030
|
+
model: string;
|
|
1031
|
+
input_tokens: number;
|
|
1032
|
+
occurred_at: Ts;
|
|
1033
|
+
reported_at: TsNullable;
|
|
1034
|
+
/** Set when the row's `usage.report` job gave up after its last attempt (the sweep skips it). */
|
|
1035
|
+
report_failed_at: TsNullable;
|
|
1036
|
+
}
|
|
1015
1037
|
export interface KernelDatabase {
|
|
1016
1038
|
accounts: AccountsTable;
|
|
1017
1039
|
identities: IdentitiesTable;
|
|
@@ -1071,6 +1093,8 @@ export interface KernelDatabase {
|
|
|
1071
1093
|
eval_runs: EvalRunsTable;
|
|
1072
1094
|
eval_results: EvalResultsTable;
|
|
1073
1095
|
model_purpose_mappings: ModelPurposeMappingsTable;
|
|
1096
|
+
model_disabled: ModelDisabledTable;
|
|
1097
|
+
model_usage: ModelUsageTable;
|
|
1074
1098
|
extension_packages: ExtensionPackagesTable;
|
|
1075
1099
|
extension_blobs: ExtensionBlobsTable;
|
|
1076
1100
|
extension_installs: ExtensionInstallsTable;
|
package/dist/errors.d.ts
CHANGED
|
@@ -280,6 +280,9 @@ export declare const ERROR_CODES: {
|
|
|
280
280
|
readonly "eval.candidate_invalid": 400;
|
|
281
281
|
readonly "model.unconfigured": 409;
|
|
282
282
|
readonly "model.purpose_unknown": 404;
|
|
283
|
+
readonly "model.provider_not_found": 404;
|
|
284
|
+
/** An extension's `ctx.models.embed` call was denied by a `model.request` policy hook. */
|
|
285
|
+
readonly "model.hook_denied": 403;
|
|
283
286
|
readonly "eval.result_not_found": 404;
|
|
284
287
|
};
|
|
285
288
|
export type ErrorCode = keyof typeof ERROR_CODES;
|