@capybara-hq/plugin-sdk 0.1.0-rc.3 → 0.1.0-rc.4
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/ctx.d.ts +62 -0
- package/dist/hooks.d.ts +62 -12
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/model.d.ts +60 -2
- package/dist/model.js +22 -0
- package/dist/permissions.d.ts +4 -1
- package/package.json +1 -1
package/dist/ctx.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Kysely } from "kysely";
|
|
2
2
|
import type { ExtensionPointRef } from "./extension-point.js";
|
|
3
3
|
import type { AvailableHook, HookHandler, HookOptions } from "./hooks.js";
|
|
4
|
+
import type { ModelEmbeddingInfo, ModelKind } from "./model.js";
|
|
4
5
|
import type { ObjectHead, PresignedUpload, PresignGetOptions } from "./storage.js";
|
|
5
6
|
import type { ToolHandler, ToolSpec } from "./tool.js";
|
|
6
7
|
import type { EmbedClaims, UiActionArgs, UiActionResult, UiRenderArgs, UiView } from "./ui.js";
|
|
@@ -58,6 +59,65 @@ export interface ExtensionStorage {
|
|
|
58
59
|
ttlSeconds: number;
|
|
59
60
|
}): Promise<PresignedUpload>;
|
|
60
61
|
}
|
|
62
|
+
/** A `ctx.models.embed` request: texts embedded for one org with the model mapped to a purpose. */
|
|
63
|
+
export interface ExtensionEmbedRequest {
|
|
64
|
+
orgId: string;
|
|
65
|
+
/** A purpose this extension declared in `permissions.models` with `kind: "embedding"`. */
|
|
66
|
+
purpose: string;
|
|
67
|
+
/** The texts, one vector each; split into batches by the model's `maxBatch` by the kernel. */
|
|
68
|
+
input: string[];
|
|
69
|
+
/** One of the model's `embedding.dimensions`; the model's default when absent. */
|
|
70
|
+
dimensions?: number;
|
|
71
|
+
inputType?: "query" | "document";
|
|
72
|
+
/**
|
|
73
|
+
* Aborts this call (e.g. the tool call's `signal`); combined with `ctx.signal`. Batches not yet
|
|
74
|
+
* sent are not sent; batches already answered are still recorded as usage.
|
|
75
|
+
*/
|
|
76
|
+
signal?: AbortSignal;
|
|
77
|
+
}
|
|
78
|
+
/** A `ctx.models.embed` result. */
|
|
79
|
+
export interface ExtensionEmbedResult {
|
|
80
|
+
/** One vector per input, in input order. */
|
|
81
|
+
vectors: number[][];
|
|
82
|
+
/** The model that made them (a changed model invalidates stored vectors). */
|
|
83
|
+
model: {
|
|
84
|
+
provider: string;
|
|
85
|
+
id: string;
|
|
86
|
+
};
|
|
87
|
+
dimensions: number;
|
|
88
|
+
}
|
|
89
|
+
/** The model a purpose maps to in an org (`ctx.models.describe`). */
|
|
90
|
+
export interface ExtensionModelTarget {
|
|
91
|
+
provider: string;
|
|
92
|
+
id: string;
|
|
93
|
+
kind: ModelKind;
|
|
94
|
+
/** Present for an embedding model. */
|
|
95
|
+
embedding?: ModelEmbeddingInfo;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Models by purpose (`permissions.models`): the purpose → model mapping is chosen by org admins
|
|
99
|
+
* (and the deployment default by the super admin), never by the extension. Only purposes this
|
|
100
|
+
* extension declared with `kind: "embedding"` are accepted (`model.purpose_unknown` otherwise), and
|
|
101
|
+
* only for an org that enabled the extension (`extension.not_enabled`). Usage is recorded by the
|
|
102
|
+
* kernel and reported through `usage.report` (purpose `embedding`).
|
|
103
|
+
*/
|
|
104
|
+
export interface ExtensionModels {
|
|
105
|
+
/**
|
|
106
|
+
* Embeds `input` with the org's effective model for `purpose`. Rejects with `model.unconfigured`
|
|
107
|
+
* when the purpose maps to no model the org can use, `validation_failed` for dimensions the model
|
|
108
|
+
* lacks, and a `ModelError` when the provider fails (a result of the wrong count or dimension is a
|
|
109
|
+
* `ModelError` of kind `server`). Not retried by the kernel. Each call fires the `model.request`
|
|
110
|
+
* hook first (`purpose: "embedding"`, every text of the call): when a policy hook denies it, the
|
|
111
|
+
* call rejects with an error whose `code` is `model.hook_denied` (HTTP 403 if it escapes an
|
|
112
|
+
* extension route), and nothing is sent or billed.
|
|
113
|
+
*/
|
|
114
|
+
embed(req: ExtensionEmbedRequest): Promise<ExtensionEmbedResult>;
|
|
115
|
+
/** The org's effective model for `purpose`, or null when it maps to no model the org can use. */
|
|
116
|
+
describe(req: {
|
|
117
|
+
orgId: string;
|
|
118
|
+
purpose: string;
|
|
119
|
+
}): Promise<ExtensionModelTarget | null>;
|
|
120
|
+
}
|
|
61
121
|
/** 传给插件 `setup` 的上下文。不提供定时器与进程内共享状态;kv 写入必须带 TTL。 */
|
|
62
122
|
export interface ExtensionContext {
|
|
63
123
|
kv: {
|
|
@@ -94,6 +154,8 @@ export interface ExtensionContext {
|
|
|
94
154
|
};
|
|
95
155
|
/** 对象存储(需 `permissions.storage`,见 {@link ExtensionStorage})。 */
|
|
96
156
|
storage: ExtensionStorage;
|
|
157
|
+
/** Models by purpose (requires `permissions.models`, see {@link ExtensionModels}). */
|
|
158
|
+
models: ExtensionModels;
|
|
97
159
|
/** 已切到本插件 schema(withSchema)的 Kysely 实例。 */
|
|
98
160
|
db: Kysely<any>;
|
|
99
161
|
http: {
|
package/dist/hooks.d.ts
CHANGED
|
@@ -39,8 +39,12 @@ export interface HookScope {
|
|
|
39
39
|
* `WebFetch` tool.
|
|
40
40
|
*/
|
|
41
41
|
export type ModelCallPurpose = "step" | "compaction" | "title" | "web_fetch";
|
|
42
|
-
/**
|
|
43
|
-
|
|
42
|
+
/**
|
|
43
|
+
* What a reported or requested model call was for: the {@link ModelCallPurpose} values, or
|
|
44
|
+
* `embedding` for an embedding call an extension made through `ctx.models` (`usage.report`, and
|
|
45
|
+
* `model.request` with {@link EmbeddingRequestHookPayload}; never `model.response`).
|
|
46
|
+
*/
|
|
47
|
+
export type UsagePurpose = ModelCallPurpose | "embedding";
|
|
44
48
|
/**
|
|
45
49
|
* An image or document of a `model.request` payload. Hook payloads never carry media bytes or
|
|
46
50
|
* attachment ids: each image or document block is replaced by this placeholder.
|
|
@@ -74,14 +78,15 @@ export type HookModelRequest = Omit<ModelRequest, "messages"> & {
|
|
|
74
78
|
messages: HookModelMessage[];
|
|
75
79
|
};
|
|
76
80
|
/**
|
|
77
|
-
* `model.request
|
|
78
|
-
* step (`purpose: "step"`), once for each compaction call (`purpose: "compaction"`, `attempt` 1)
|
|
81
|
+
* `model.request` of a chat model call (see {@link ModelRequestHookPayload}): once per attempt of a
|
|
82
|
+
* step (`purpose: "step"`), once for each compaction call (`purpose: "compaction"`, `attempt` 1),
|
|
79
83
|
* once for each conversation title call (`purpose: "title"`, `runId` "", `step` 0, `attempt` 1; a
|
|
80
|
-
* denial skips the title and is recorded only in the audit log)
|
|
84
|
+
* denial skips the title and is recorded only in the audit log) and once for each `WebFetch`
|
|
85
|
+
* extraction call (`purpose: "web_fetch"`).
|
|
81
86
|
* `request` is the request as sent, except that images and documents are placeholders
|
|
82
87
|
* ({@link HookMediaPlaceholder}): no media bytes, no attachment ids.
|
|
83
88
|
*/
|
|
84
|
-
export interface
|
|
89
|
+
export interface ModelCallRequestHookPayload extends HookScope {
|
|
85
90
|
purpose: ModelCallPurpose;
|
|
86
91
|
step: number;
|
|
87
92
|
attempt: number;
|
|
@@ -89,6 +94,45 @@ export interface ModelRequestHookPayload extends HookScope {
|
|
|
89
94
|
model: string;
|
|
90
95
|
request: Readonly<HookModelRequest>;
|
|
91
96
|
}
|
|
97
|
+
/**
|
|
98
|
+
* `model.request` of an embedding call an extension made through `ctx.models.embed`
|
|
99
|
+
* (`purpose: "embedding"`): fired once per `embed` call, before the first batch is sent, with every
|
|
100
|
+
* text of the call (the kernel splits them into batches afterwards). A call with no input sends
|
|
101
|
+
* nothing and fires nothing. There is no conversation: the conversation fields of a chat model call
|
|
102
|
+
* (`accountId`, `agentId`, `agentVersion`, `conversationId`, `runId`, `parent`, `step`, `attempt`,
|
|
103
|
+
* `request`) are absent. A policy denial makes `embed` reject (`model.hook_denied`); nothing is sent
|
|
104
|
+
* to the provider and no usage is recorded.
|
|
105
|
+
*/
|
|
106
|
+
export interface EmbeddingRequestHookPayload {
|
|
107
|
+
purpose: "embedding";
|
|
108
|
+
orgId: string;
|
|
109
|
+
/** The extension that called `ctx.models.embed`. */
|
|
110
|
+
extension: string;
|
|
111
|
+
/** The embedding purpose the extension called with (one it declared in `permissions.models`). */
|
|
112
|
+
embeddingPurpose: string;
|
|
113
|
+
provider: string;
|
|
114
|
+
model: string;
|
|
115
|
+
/** The texts of the call, in order (text only). */
|
|
116
|
+
input: readonly string[];
|
|
117
|
+
/** The dimensions the extension asked for, when it asked. */
|
|
118
|
+
dimensions?: number;
|
|
119
|
+
/** The input type the extension gave, when it gave one. */
|
|
120
|
+
inputType?: "query" | "document";
|
|
121
|
+
accountId?: undefined;
|
|
122
|
+
agentId?: undefined;
|
|
123
|
+
agentVersion?: undefined;
|
|
124
|
+
conversationId?: undefined;
|
|
125
|
+
runId?: undefined;
|
|
126
|
+
parent?: undefined;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* `model.request`: before a model call is sent. A policy hook may deny it. Either a chat model call
|
|
130
|
+
* ({@link ModelCallRequestHookPayload}: steps, compaction, titles, `WebFetch`) or an extension's
|
|
131
|
+
* embedding call ({@link EmbeddingRequestHookPayload}, `purpose: "embedding"`); tell them apart by
|
|
132
|
+
* `purpose`. Handlers must accept the absent conversation fields of an embedding call (a policy
|
|
133
|
+
* hook that throws on one denies the call). Payloads carry no media bytes.
|
|
134
|
+
*/
|
|
135
|
+
export type ModelRequestHookPayload = ModelCallRequestHookPayload | EmbeddingRequestHookPayload;
|
|
92
136
|
/**
|
|
93
137
|
* `model.response`: after a sent model call finished or failed, with its usage when known (step
|
|
94
138
|
* attempts, compaction and title calls, see `purpose`). Not fired for a call the user aborted (the run was
|
|
@@ -138,12 +182,18 @@ export interface UsageReportPayload {
|
|
|
138
182
|
*/
|
|
139
183
|
purpose: UsagePurpose;
|
|
140
184
|
orgId: string;
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
185
|
+
/** Absent for `embedding`. */
|
|
186
|
+
accountId?: string;
|
|
187
|
+
/** Absent for `embedding`. */
|
|
188
|
+
agentId?: string;
|
|
189
|
+
/** Absent for `embedding`. */
|
|
190
|
+
conversationId?: string;
|
|
191
|
+
/** A sub-agent's calls report `chat`; `parent` tells them apart. Absent for `embedding`. */
|
|
192
|
+
conversationKind?: "chat" | "eval";
|
|
193
|
+
/** Absent for `embedding`. */
|
|
194
|
+
runId?: string;
|
|
195
|
+
/** The calling extension; set for `embedding`. */
|
|
196
|
+
extension?: string;
|
|
147
197
|
/** Set when the call belongs to a sub-agent: where it was dispatched from. */
|
|
148
198
|
parent?: HookParentScope;
|
|
149
199
|
provider: string;
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
package/dist/model.d.ts
CHANGED
|
@@ -4,9 +4,32 @@
|
|
|
4
4
|
* 见 `model-params.ts`);旧契约 `model-provider@1`(`ModelProviderV1` 等)由内核经适配层继续支持。
|
|
5
5
|
*/
|
|
6
6
|
import type { Effort, ModelParameter, ParamValue } from "./model-params.js";
|
|
7
|
+
import type { LocalizedName } from "./plugin-package.js";
|
|
8
|
+
/** What a model does. Absent on a `ModelInfo` means `chat`. */
|
|
9
|
+
export type ModelKind = "chat" | "embedding" | "rerank" | "image_generation" | "video_generation" | "speech_to_text" | "text_to_speech";
|
|
10
|
+
/** Embedding facts of a model whose `kind` is `embedding`. */
|
|
11
|
+
export interface ModelEmbeddingInfo {
|
|
12
|
+
/** The output dimensions the model can produce (a single entry when not adjustable). */
|
|
13
|
+
dimensions: number[];
|
|
14
|
+
defaultDimensions: number;
|
|
15
|
+
/** Most tokens one input may hold. */
|
|
16
|
+
maxInputTokens: number;
|
|
17
|
+
/** Most inputs one `embed` call may carry. */
|
|
18
|
+
maxBatch: number;
|
|
19
|
+
}
|
|
7
20
|
export interface ModelInfo {
|
|
8
21
|
id: string;
|
|
9
22
|
displayName: string;
|
|
23
|
+
/** Defaults to `chat`. Chat-only fields below stay required for every kind. */
|
|
24
|
+
kind?: ModelKind;
|
|
25
|
+
/** Who made the model (display only). */
|
|
26
|
+
publisher?: string;
|
|
27
|
+
/** Display only. */
|
|
28
|
+
description?: LocalizedName | string;
|
|
29
|
+
/** ISO date after which the model is retired (display only). */
|
|
30
|
+
retiresAt?: string;
|
|
31
|
+
/** Set when `kind` is `embedding`. */
|
|
32
|
+
embedding?: ModelEmbeddingInfo;
|
|
10
33
|
contextWindow: number;
|
|
11
34
|
maxOutputTokens: number;
|
|
12
35
|
capabilities: {
|
|
@@ -206,14 +229,49 @@ export declare class ModelError extends Error {
|
|
|
206
229
|
/** rate_limited / overloaded / server / timeout / network 为 true。 */
|
|
207
230
|
get retryable(): boolean;
|
|
208
231
|
}
|
|
232
|
+
/**
|
|
233
|
+
* Options of a model call made for an organization.
|
|
234
|
+
* `config` is the extension's effective deployment config for this org: the platform's deployment
|
|
235
|
+
* config with the org's copy applied to the overridable fields (`orgAccess(…, "override")`), or the
|
|
236
|
+
* platform's config (equal to `ctx.config`) while the org follows it. Use it, not `ctx.config`, for
|
|
237
|
+
* work done for this org. Optional in the type so older providers compile; the kernel always passes
|
|
238
|
+
* both.
|
|
239
|
+
*/
|
|
240
|
+
export interface ModelCallOptions {
|
|
241
|
+
orgId?: string;
|
|
242
|
+
config?: unknown;
|
|
243
|
+
}
|
|
244
|
+
export interface EmbedRequest {
|
|
245
|
+
model: string;
|
|
246
|
+
input: string[];
|
|
247
|
+
/** One of the model's `embedding.dimensions`; the model's default when absent. */
|
|
248
|
+
dimensions?: number;
|
|
249
|
+
inputType?: "query" | "document";
|
|
250
|
+
}
|
|
251
|
+
export interface EmbedResult {
|
|
252
|
+
/** One vector per input, in input order. */
|
|
253
|
+
vectors: number[][];
|
|
254
|
+
usage: {
|
|
255
|
+
inputTokens: number;
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Checks an embedding result against its request and the model's embedding facts. Returns null when
|
|
260
|
+
* valid, else the reason: wrong vector count, wrong dimension, or a non-finite number.
|
|
261
|
+
*/
|
|
262
|
+
export declare function validateEmbedResult(req: EmbedRequest, info: ModelEmbeddingInfo, res: EmbedResult): string | null;
|
|
209
263
|
/** `model-provider@2` 的实现。 */
|
|
210
264
|
export interface ModelProvider {
|
|
211
265
|
/** 每个模型的 `parameters` 须通过 `assertValidDeclarations`,否则内核不使用该供应商。 */
|
|
212
|
-
listModels(): Promise<ModelInfo[]>;
|
|
266
|
+
listModels(opts?: ModelCallOptions): Promise<ModelInfo[]>;
|
|
213
267
|
/** 事件序列必须以且仅以一个 message_complete 结束;出错抛 ModelError。 */
|
|
214
268
|
stream(req: ModelRequest, opts: {
|
|
215
269
|
signal: AbortSignal;
|
|
216
|
-
}): AsyncIterable<ModelStreamEvent>;
|
|
270
|
+
} & ModelCallOptions): AsyncIterable<ModelStreamEvent>;
|
|
271
|
+
/** Present only on a provider that lists `embedding` models. */
|
|
272
|
+
embed?(req: EmbedRequest, opts: {
|
|
273
|
+
signal: AbortSignal;
|
|
274
|
+
} & ModelCallOptions): Promise<EmbedResult>;
|
|
217
275
|
}
|
|
218
276
|
/** `model-provider@1` 的实现(旧契约,内核经适配层接入:effort 由 `capabilities.effort` 推导)。 */
|
|
219
277
|
export interface ModelProviderV1 {
|
package/dist/model.js
CHANGED
|
@@ -148,5 +148,27 @@ export class ModelError extends Error {
|
|
|
148
148
|
return isRetryableKind(this.kind);
|
|
149
149
|
}
|
|
150
150
|
}
|
|
151
|
+
/**
|
|
152
|
+
* Checks an embedding result against its request and the model's embedding facts. Returns null when
|
|
153
|
+
* valid, else the reason: wrong vector count, wrong dimension, or a non-finite number.
|
|
154
|
+
*/
|
|
155
|
+
export function validateEmbedResult(req, info, res) {
|
|
156
|
+
if (res.vectors.length !== req.input.length) {
|
|
157
|
+
return `vector count ${res.vectors.length} does not match input count ${req.input.length}`;
|
|
158
|
+
}
|
|
159
|
+
const dims = req.dimensions ?? info.defaultDimensions;
|
|
160
|
+
for (let i = 0; i < res.vectors.length; i++) {
|
|
161
|
+
const v = res.vectors[i];
|
|
162
|
+
if (v.length !== dims) {
|
|
163
|
+
return `vector ${i} has dimension ${v.length}, expected ${dims}`;
|
|
164
|
+
}
|
|
165
|
+
for (const x of v) {
|
|
166
|
+
if (typeof x !== "number" || !Number.isFinite(x)) {
|
|
167
|
+
return `vector ${i} holds a non-finite number`;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
151
173
|
export const MODEL_PROVIDER_EXTENSION_POINT = "model-provider@1";
|
|
152
174
|
export const MODEL_PROVIDER_EXTENSION_POINT_V2 = "model-provider@2";
|
package/dist/permissions.d.ts
CHANGED
|
@@ -22,7 +22,10 @@ export interface ExtensionPermissions {
|
|
|
22
22
|
* on a replica, calls on these mount points fail closed there.
|
|
23
23
|
*/
|
|
24
24
|
policyHooks?: PolicyHook[];
|
|
25
|
-
/**
|
|
25
|
+
/**
|
|
26
|
+
* Model purposes (`ctx.models`): each key must be `<extension name>.<name>`; an org admin maps
|
|
27
|
+
* each to a model of its `kind`. `ctx.models.embed` / `describe` accept the `embedding` ones.
|
|
28
|
+
*/
|
|
26
29
|
models?: {
|
|
27
30
|
purpose: string;
|
|
28
31
|
kind: "chat" | "embedding";
|