create-windy 0.2.20 → 0.2.21
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 +1 -1
- package/dist/cli.js +84 -10
- package/package.json +1 -1
- package/template/.windy-template.json +2 -2
- package/template/README.md +1 -0
- package/template/apps/agent-server/Dockerfile +20 -0
- package/template/apps/agent-server/README.md +46 -0
- package/template/apps/agent-server/pyproject.toml +44 -0
- package/template/apps/agent-server/src/southwind_agent_server/__init__.py +5 -0
- package/template/apps/agent-server/src/southwind_agent_server/adk_adapter.py +39 -0
- package/template/apps/agent-server/src/southwind_agent_server/app.py +263 -0
- package/template/apps/agent-server/src/southwind_agent_server/config.py +141 -0
- package/template/apps/agent-server/src/southwind_agent_server/contracts.py +99 -0
- package/template/apps/agent-server/src/southwind_agent_server/deterministic.py +52 -0
- package/template/apps/agent-server/src/southwind_agent_server/engine.py +85 -0
- package/template/apps/agent-server/src/southwind_agent_server/openai_engine.py +197 -0
- package/template/apps/agent-server/src/southwind_agent_server/openai_provider.py +246 -0
- package/template/apps/agent-server/src/southwind_agent_server/service.py +180 -0
- package/template/apps/agent-server/src/southwind_agent_server/sqlite_store.py +317 -0
- package/template/apps/agent-server/src/southwind_agent_server/sse.py +41 -0
- package/template/apps/agent-server/src/southwind_agent_server/store.py +144 -0
- package/template/apps/agent-server/src/southwind_agent_server/tool_host.py +80 -0
- package/template/apps/agent-server/tests/test_api.py +207 -0
- package/template/apps/agent-server/tests/test_config.py +56 -0
- package/template/apps/agent-server/tests/test_openai_provider.py +224 -0
- package/template/apps/agent-server/tests/test_sqlite_store.py +93 -0
- package/template/apps/agent-server/uv.lock +925 -0
- package/template/apps/server/src/agent/execution-grants.ts +89 -0
- package/template/apps/server/src/agent/remote-runtime.ts +128 -0
- package/template/apps/server/src/agent/run-contracts.ts +43 -0
- package/template/apps/server/src/agent/run-facade.test.ts +257 -0
- package/template/apps/server/src/agent/run-facade.ts +190 -0
- package/template/apps/server/src/agent/run-routes.ts +222 -0
- package/template/apps/server/src/agent/runtime-bootstrap.ts +53 -0
- package/template/apps/server/src/agent/tool-host.test.ts +242 -0
- package/template/apps/server/src/agent/tool-host.ts +153 -0
- package/template/apps/server/src/index.ts +12 -16
- package/template/apps/server/src/module-composition.test.ts +9 -0
- package/template/apps/server/src/module-composition.ts +13 -0
- package/template/apps/server/src/module-host/initialize-contracts.ts +67 -0
- package/template/apps/server/src/module-host/initialize.test.ts +69 -0
- package/template/apps/server/src/module-host/initialize.ts +50 -53
- package/template/apps/server/src/module-host.ts +1 -0
- package/template/apps/server/src/module-runtime-validation.ts +40 -0
- package/template/docker-compose.yml +25 -0
- package/template/docs/architecture/ai-runtime.md +744 -0
- package/template/docs/platform/agent-runtime.md +128 -0
- package/template/docs/platform/agent-tools.md +8 -1
- package/template/package.json +1 -0
- package/template/packages/config/index.test.ts +43 -0
- package/template/packages/config/index.ts +1 -0
- package/template/packages/config/package.json +2 -1
- package/template/packages/config/src/ai-openai-compatible.ts +131 -0
- package/template/packages/server-sdk/index.ts +11 -0
- package/template/packages/server-sdk/package.json +4 -2
- package/template/packages/server-sdk/src/ai-operation-registration.ts +31 -0
- package/template/packages/server-sdk/src/module-host.ts +5 -0
- package/template/packages/server-sdk/src/tool-host-port.ts +22 -0
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
createSystemToolHandlers,
|
|
22
22
|
registerAgentRoutes,
|
|
23
23
|
} from "./agent/routes.js";
|
|
24
|
+
import { registerAgentPlatformRuntime } from "./agent/runtime-bootstrap.js";
|
|
24
25
|
// @windy-module system.agent end
|
|
25
26
|
import { registerAuthRoutes, registerAuthSessionRoute } from "./auth/routes.js";
|
|
26
27
|
import { createAuthRuntime } from "./auth/runtime.js";
|
|
@@ -55,7 +56,7 @@ import { registerNotificationRoutes } from "./notification/routes.js";
|
|
|
55
56
|
import { createStorageRuntime } from "./storage/runtime.js";
|
|
56
57
|
import { registerStorageRoutes } from "./storage/routes.js";
|
|
57
58
|
// @windy-module system.storage end
|
|
58
|
-
import {
|
|
59
|
+
import { validateInstalledRuntime } from "./module-runtime-validation.js";
|
|
59
60
|
import {
|
|
60
61
|
adaptModuleSearchProviders,
|
|
61
62
|
createModuleTaskDefinitions,
|
|
@@ -288,6 +289,14 @@ const toolHandlers = new Map([
|
|
|
288
289
|
...createModuleToolHandlerMap(moduleHosts),
|
|
289
290
|
]);
|
|
290
291
|
registerAgentRoutes(app, foundationModules, toolHandlers, runtime);
|
|
292
|
+
registerAgentPlatformRuntime({
|
|
293
|
+
app,
|
|
294
|
+
platformConfig,
|
|
295
|
+
modules: foundationModules,
|
|
296
|
+
hosts: moduleHosts,
|
|
297
|
+
runtime,
|
|
298
|
+
toolHandlers,
|
|
299
|
+
});
|
|
291
300
|
// @windy-module system.agent end
|
|
292
301
|
// @windy-module system.scheduler begin
|
|
293
302
|
registerSchedulerRoutes(app, scheduler, runtime, foundationSnapshot.features);
|
|
@@ -312,30 +321,17 @@ installModuleRoutes(moduleHosts, app, {
|
|
|
312
321
|
});
|
|
313
322
|
// @windy-module work-order end
|
|
314
323
|
|
|
315
|
-
|
|
324
|
+
validateInstalledRuntime({
|
|
316
325
|
modules: foundationModules,
|
|
317
326
|
routes: app.routes,
|
|
318
327
|
// @windy-module system.scheduler begin
|
|
319
328
|
tasks: scheduledTaskDefinitions,
|
|
320
329
|
// @windy-module system.scheduler end
|
|
321
|
-
// @windy-module system.agent begin
|
|
322
330
|
tools: toolHandlers,
|
|
323
|
-
// @windy-module system.agent end
|
|
324
331
|
// @windy-module system.search begin
|
|
325
332
|
providers: searchHost.providerKeys,
|
|
326
333
|
// @windy-module system.search end
|
|
327
|
-
|
|
328
|
-
routes: moduleHosts.routes.map(
|
|
329
|
-
({ definition }) => `${definition.method} ${definition.path}`,
|
|
330
|
-
),
|
|
331
|
-
tasks: moduleHosts.taskHandlers.map(
|
|
332
|
-
({ registration }) => registration.taskKey,
|
|
333
|
-
),
|
|
334
|
-
tools: moduleHosts.toolHandlers.map(
|
|
335
|
-
({ registration }) => registration.toolKey,
|
|
336
|
-
),
|
|
337
|
-
providers: moduleHosts.searchProviders.map(({ provider }) => provider.key),
|
|
338
|
-
},
|
|
334
|
+
hosts: moduleHosts,
|
|
339
335
|
});
|
|
340
336
|
|
|
341
337
|
const port = process.env.PORT ? Number(process.env.PORT) : 9_747;
|
|
@@ -73,6 +73,15 @@ describe("Server Module Composition", () => {
|
|
|
73
73
|
moduleRegistrations: { providers: ["system.unknown-search"] },
|
|
74
74
|
}),
|
|
75
75
|
).toThrow("搜索 Provider已注册但未在 Manifest 声明:system.unknown-search");
|
|
76
|
+
|
|
77
|
+
expect(() =>
|
|
78
|
+
validateServerModuleComposition({
|
|
79
|
+
...base,
|
|
80
|
+
moduleRegistrations: { aiOperations: ["system.unknown-operation"] },
|
|
81
|
+
}),
|
|
82
|
+
).toThrow(
|
|
83
|
+
"AI Operation Handler已注册但未在 Manifest 声明:system.unknown-operation",
|
|
84
|
+
);
|
|
76
85
|
});
|
|
77
86
|
});
|
|
78
87
|
|
|
@@ -16,6 +16,7 @@ export interface ModuleImplementationRegistrations {
|
|
|
16
16
|
routes?: readonly string[];
|
|
17
17
|
tasks?: readonly string[];
|
|
18
18
|
tools?: readonly string[];
|
|
19
|
+
aiOperations?: readonly string[];
|
|
19
20
|
providers?: readonly string[];
|
|
20
21
|
}
|
|
21
22
|
|
|
@@ -24,6 +25,7 @@ export interface ServerCompositionInput {
|
|
|
24
25
|
routes: readonly RegisteredHttpRoute[];
|
|
25
26
|
tasks?: readonly { key: string }[];
|
|
26
27
|
tools?: ReadonlyMap<string, unknown>;
|
|
28
|
+
aiOperations?: readonly string[];
|
|
27
29
|
providers?: readonly string[];
|
|
28
30
|
moduleRegistrations?: ModuleImplementationRegistrations;
|
|
29
31
|
}
|
|
@@ -35,6 +37,7 @@ export function validateServerModuleComposition(
|
|
|
35
37
|
apiRoutes: input.routes.map(apiRouteKey),
|
|
36
38
|
tasks: (input.tasks || []).map(({ key }) => key),
|
|
37
39
|
tools: [...(input.tools?.keys() || [])],
|
|
40
|
+
aiOperations: input.aiOperations,
|
|
38
41
|
providers: input.providers || [],
|
|
39
42
|
});
|
|
40
43
|
if (input.moduleRegistrations) {
|
|
@@ -55,6 +58,11 @@ function validateRegistrationConsistency(
|
|
|
55
58
|
const declaredTools = new Set(
|
|
56
59
|
modules.flatMap((module) => module.tools.map(({ key }) => key)),
|
|
57
60
|
);
|
|
61
|
+
const declaredAiOperations = new Set(
|
|
62
|
+
modules.flatMap((module) =>
|
|
63
|
+
(module.aiOperations ?? []).map(({ key }) => key),
|
|
64
|
+
),
|
|
65
|
+
);
|
|
58
66
|
const declaredProviders = new Set(
|
|
59
67
|
modules.flatMap((module) => module.providers.map(({ key }) => key)),
|
|
60
68
|
);
|
|
@@ -62,6 +70,11 @@ function validateRegistrationConsistency(
|
|
|
62
70
|
["模块路由", registrations.routes || [], declaredRoutes],
|
|
63
71
|
["定时任务 Handler", registrations.tasks || [], declaredTasks],
|
|
64
72
|
["Agent Tool Handler", registrations.tools || [], declaredTools],
|
|
73
|
+
[
|
|
74
|
+
"AI Operation Handler",
|
|
75
|
+
registrations.aiOperations || [],
|
|
76
|
+
declaredAiOperations,
|
|
77
|
+
],
|
|
65
78
|
["搜索 Provider", registrations.providers || [], declaredProviders],
|
|
66
79
|
] as const) {
|
|
67
80
|
for (const key of registered) {
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { DurableHandlerRegistry } from "@southwind-ai/jobs";
|
|
2
|
+
import type { ModuleManifest } from "@southwind-ai/modules";
|
|
3
|
+
import type {
|
|
4
|
+
InstallableServerModule,
|
|
5
|
+
ModuleAiOperationHandlerRegistration,
|
|
6
|
+
ModuleInitializer,
|
|
7
|
+
ModuleRolePreset,
|
|
8
|
+
ModuleRouteDefinition,
|
|
9
|
+
ModuleSearchProvider,
|
|
10
|
+
ModuleTaskHandlerRegistration,
|
|
11
|
+
ModuleToolHandlerRegistration,
|
|
12
|
+
ModuleUploadPolicy,
|
|
13
|
+
} from "@southwind-ai/server-sdk";
|
|
14
|
+
|
|
15
|
+
export interface RegisteredModuleRoute {
|
|
16
|
+
readonly moduleName: string;
|
|
17
|
+
readonly definition: ModuleRouteDefinition;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface RegisteredModuleTaskHandler {
|
|
21
|
+
readonly moduleName: string;
|
|
22
|
+
readonly registration: ModuleTaskHandlerRegistration;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface RegisteredModuleToolHandler {
|
|
26
|
+
readonly moduleName: string;
|
|
27
|
+
readonly registration: ModuleToolHandlerRegistration;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface RegisteredModuleAiOperationHandler {
|
|
31
|
+
readonly moduleName: string;
|
|
32
|
+
readonly registration: ModuleAiOperationHandlerRegistration;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface RegisteredModuleRolePreset {
|
|
36
|
+
readonly moduleName: string;
|
|
37
|
+
readonly preset: ModuleRolePreset;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface RegisteredModuleSearchProvider {
|
|
41
|
+
readonly moduleName: string;
|
|
42
|
+
readonly provider: ModuleSearchProvider;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface InitializeModuleHostsInput {
|
|
46
|
+
modules: readonly ModuleManifest[];
|
|
47
|
+
/** Manifest 与 setup 由同一业务模块安装单元贡献。 */
|
|
48
|
+
installations?: readonly InstallableServerModule<ModuleManifest>[];
|
|
49
|
+
/** @deprecated 仅供旧模块迁移;新模块必须使用 installations。 */
|
|
50
|
+
initializers?: readonly ModuleInitializer[];
|
|
51
|
+
baseUploadPolicies?: readonly ModuleUploadPolicy[];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface InitializedModuleHosts {
|
|
55
|
+
readonly manifests: readonly ModuleManifest[];
|
|
56
|
+
readonly uploadPolicies: readonly ModuleUploadPolicy[];
|
|
57
|
+
readonly routes: readonly RegisteredModuleRoute[];
|
|
58
|
+
readonly taskHandlers: readonly RegisteredModuleTaskHandler[];
|
|
59
|
+
readonly toolHandlers: readonly RegisteredModuleToolHandler[];
|
|
60
|
+
readonly aiOperationHandlers: readonly RegisteredModuleAiOperationHandler[];
|
|
61
|
+
readonly rolePresets: readonly RegisteredModuleRolePreset[];
|
|
62
|
+
readonly searchProviders: readonly RegisteredModuleSearchProvider[];
|
|
63
|
+
manifestOf(moduleName: string): ModuleManifest;
|
|
64
|
+
/** 该模块经 Host 注册的上传用途集合(不含平台基础用途)。 */
|
|
65
|
+
uploadPurposesOf(moduleName: string): ReadonlySet<string>;
|
|
66
|
+
installDurableHandlers(registry: DurableHandlerRegistry): void;
|
|
67
|
+
}
|
|
@@ -102,6 +102,75 @@ describe("模块 Host 初始化", () => {
|
|
|
102
102
|
}),
|
|
103
103
|
).rejects.toThrow("Server Module Manifest 与能力目录不是同一声明:known");
|
|
104
104
|
});
|
|
105
|
+
|
|
106
|
+
test("AI Operation Handler 只注册本模块 Manifest 声明", async () => {
|
|
107
|
+
const operation = {
|
|
108
|
+
key: "known.summarize",
|
|
109
|
+
kind: "agent" as const,
|
|
110
|
+
featureKey: "known.ai",
|
|
111
|
+
permissionKey: "known.ai.execute",
|
|
112
|
+
auditAction: "known.ai.execute",
|
|
113
|
+
inputSchemaKey: "known.input.v1",
|
|
114
|
+
outputSchemaKey: "known.output.v1",
|
|
115
|
+
requiredCapabilities: ["text-generation"],
|
|
116
|
+
allowedToolKeys: [],
|
|
117
|
+
dataClassification: "internal" as const,
|
|
118
|
+
execution: "streaming" as const,
|
|
119
|
+
publicNetworkAccess: "forbidden" as const,
|
|
120
|
+
limits: {
|
|
121
|
+
maxDurationSeconds: 60,
|
|
122
|
+
maxInputBytes: 4096,
|
|
123
|
+
maxOutputBytes: 4096,
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
const known = manifest("known", { aiOperations: [operation] });
|
|
127
|
+
const initialized = await initializeModuleHosts({
|
|
128
|
+
modules: [known],
|
|
129
|
+
installations: [
|
|
130
|
+
{
|
|
131
|
+
manifest: known,
|
|
132
|
+
setup(host) {
|
|
133
|
+
host.registerAiOperationHandlers([
|
|
134
|
+
{
|
|
135
|
+
operationKey: operation.key,
|
|
136
|
+
prepare: () => ({
|
|
137
|
+
instructions: "按要求回答。",
|
|
138
|
+
userPrompt: "你好",
|
|
139
|
+
}),
|
|
140
|
+
},
|
|
141
|
+
]);
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
],
|
|
145
|
+
});
|
|
146
|
+
expect(
|
|
147
|
+
initialized.aiOperationHandlers.map(
|
|
148
|
+
({ registration }) => registration.operationKey,
|
|
149
|
+
),
|
|
150
|
+
).toEqual(["known.summarize"]);
|
|
151
|
+
|
|
152
|
+
await expect(
|
|
153
|
+
initializeModuleHosts({
|
|
154
|
+
modules: [known],
|
|
155
|
+
installations: [
|
|
156
|
+
{
|
|
157
|
+
manifest: known,
|
|
158
|
+
setup(host) {
|
|
159
|
+
host.registerAiOperationHandlers([
|
|
160
|
+
{
|
|
161
|
+
operationKey: "known.unknown",
|
|
162
|
+
prepare: () => ({
|
|
163
|
+
instructions: "x",
|
|
164
|
+
userPrompt: "x",
|
|
165
|
+
}),
|
|
166
|
+
},
|
|
167
|
+
]);
|
|
168
|
+
},
|
|
169
|
+
},
|
|
170
|
+
],
|
|
171
|
+
}),
|
|
172
|
+
).rejects.toThrow("AI Operation Handler 未在 Manifest 声明");
|
|
173
|
+
});
|
|
105
174
|
});
|
|
106
175
|
|
|
107
176
|
function initializer(
|
|
@@ -5,64 +5,31 @@ import {
|
|
|
5
5
|
import type { ModuleManifest } from "@southwind-ai/modules";
|
|
6
6
|
import {
|
|
7
7
|
requireModuleNamespace,
|
|
8
|
-
type InstallableServerModule,
|
|
9
8
|
type ModuleHost,
|
|
10
|
-
type ModuleInitializer,
|
|
11
|
-
type ModuleRolePreset,
|
|
12
9
|
type ModuleRouteDefinition,
|
|
13
|
-
type ModuleSearchProvider,
|
|
14
|
-
type ModuleTaskHandlerRegistration,
|
|
15
|
-
type ModuleToolHandlerRegistration,
|
|
16
10
|
type ModuleUploadPolicy,
|
|
17
11
|
} from "@southwind-ai/server-sdk";
|
|
12
|
+
import type {
|
|
13
|
+
InitializedModuleHosts,
|
|
14
|
+
InitializeModuleHostsInput,
|
|
15
|
+
RegisteredModuleAiOperationHandler,
|
|
16
|
+
RegisteredModuleRolePreset,
|
|
17
|
+
RegisteredModuleRoute,
|
|
18
|
+
RegisteredModuleSearchProvider,
|
|
19
|
+
RegisteredModuleTaskHandler,
|
|
20
|
+
RegisteredModuleToolHandler,
|
|
21
|
+
} from "./initialize-contracts.js";
|
|
18
22
|
|
|
19
|
-
export
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
export interface RegisteredModuleToolHandler {
|
|
30
|
-
readonly moduleName: string;
|
|
31
|
-
readonly registration: ModuleToolHandlerRegistration;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export interface RegisteredModuleRolePreset {
|
|
35
|
-
readonly moduleName: string;
|
|
36
|
-
readonly preset: ModuleRolePreset;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export interface RegisteredModuleSearchProvider {
|
|
40
|
-
readonly moduleName: string;
|
|
41
|
-
readonly provider: ModuleSearchProvider;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export interface InitializeModuleHostsInput {
|
|
45
|
-
modules: readonly ModuleManifest[];
|
|
46
|
-
/** Manifest 与 setup 由同一业务模块安装单元贡献。 */
|
|
47
|
-
installations?: readonly InstallableServerModule<ModuleManifest>[];
|
|
48
|
-
/** @deprecated 仅供旧模块迁移;新模块必须使用 installations。 */
|
|
49
|
-
initializers?: readonly ModuleInitializer[];
|
|
50
|
-
baseUploadPolicies?: readonly ModuleUploadPolicy[];
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export interface InitializedModuleHosts {
|
|
54
|
-
readonly manifests: readonly ModuleManifest[];
|
|
55
|
-
readonly uploadPolicies: readonly ModuleUploadPolicy[];
|
|
56
|
-
readonly routes: readonly RegisteredModuleRoute[];
|
|
57
|
-
readonly taskHandlers: readonly RegisteredModuleTaskHandler[];
|
|
58
|
-
readonly toolHandlers: readonly RegisteredModuleToolHandler[];
|
|
59
|
-
readonly rolePresets: readonly RegisteredModuleRolePreset[];
|
|
60
|
-
readonly searchProviders: readonly RegisteredModuleSearchProvider[];
|
|
61
|
-
manifestOf(moduleName: string): ModuleManifest;
|
|
62
|
-
/** 该模块经 Host 注册的上传用途集合(不含平台基础用途)。 */
|
|
63
|
-
uploadPurposesOf(moduleName: string): ReadonlySet<string>;
|
|
64
|
-
installDurableHandlers(registry: DurableHandlerRegistry): void;
|
|
65
|
-
}
|
|
23
|
+
export type {
|
|
24
|
+
InitializedModuleHosts,
|
|
25
|
+
InitializeModuleHostsInput,
|
|
26
|
+
RegisteredModuleAiOperationHandler,
|
|
27
|
+
RegisteredModuleRolePreset,
|
|
28
|
+
RegisteredModuleRoute,
|
|
29
|
+
RegisteredModuleSearchProvider,
|
|
30
|
+
RegisteredModuleTaskHandler,
|
|
31
|
+
RegisteredModuleToolHandler,
|
|
32
|
+
} from "./initialize-contracts.js";
|
|
66
33
|
|
|
67
34
|
type HandlerInstaller = (registry: DurableHandlerRegistry) => void;
|
|
68
35
|
|
|
@@ -94,6 +61,8 @@ export async function initializeModuleHosts(
|
|
|
94
61
|
taskKeys: new Set<string>(),
|
|
95
62
|
toolHandlers: [],
|
|
96
63
|
toolKeys: new Set<string>(),
|
|
64
|
+
aiOperationHandlers: [],
|
|
65
|
+
aiOperationKeys: new Set<string>(),
|
|
97
66
|
rolePresets: [],
|
|
98
67
|
rolePresetKeys: new Set<string>(),
|
|
99
68
|
searchProviders: [],
|
|
@@ -142,6 +111,7 @@ export async function initializeModuleHosts(
|
|
|
142
111
|
routes: [...state.routes],
|
|
143
112
|
taskHandlers: [...state.taskHandlers],
|
|
144
113
|
toolHandlers: [...state.toolHandlers],
|
|
114
|
+
aiOperationHandlers: [...state.aiOperationHandlers],
|
|
145
115
|
rolePresets: [...state.rolePresets],
|
|
146
116
|
searchProviders: [...state.searchProviders],
|
|
147
117
|
manifestOf(moduleName) {
|
|
@@ -170,6 +140,8 @@ interface HostCollections {
|
|
|
170
140
|
taskKeys: Set<string>;
|
|
171
141
|
toolHandlers: RegisteredModuleToolHandler[];
|
|
172
142
|
toolKeys: Set<string>;
|
|
143
|
+
aiOperationHandlers: RegisteredModuleAiOperationHandler[];
|
|
144
|
+
aiOperationKeys: Set<string>;
|
|
173
145
|
rolePresets: RegisteredModuleRolePreset[];
|
|
174
146
|
rolePresetKeys: Set<string>;
|
|
175
147
|
searchProviders: RegisteredModuleSearchProvider[];
|
|
@@ -253,6 +225,31 @@ function createScopedHost(
|
|
|
253
225
|
state.toolHandlers.push({ moduleName, registration });
|
|
254
226
|
}
|
|
255
227
|
},
|
|
228
|
+
registerAiOperationHandlers(registrations) {
|
|
229
|
+
for (const registration of registrations) {
|
|
230
|
+
requireModuleNamespace(
|
|
231
|
+
"AI Operation",
|
|
232
|
+
registration.operationKey,
|
|
233
|
+
moduleName,
|
|
234
|
+
);
|
|
235
|
+
if (
|
|
236
|
+
!manifest.aiOperations?.some(
|
|
237
|
+
({ key }) => key === registration.operationKey,
|
|
238
|
+
)
|
|
239
|
+
) {
|
|
240
|
+
throw new Error(
|
|
241
|
+
`AI Operation Handler 未在 Manifest 声明:${registration.operationKey}`,
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
if (state.aiOperationKeys.has(registration.operationKey)) {
|
|
245
|
+
throw new Error(
|
|
246
|
+
`AI Operation Handler 重复注册:${registration.operationKey}`,
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
state.aiOperationKeys.add(registration.operationKey);
|
|
250
|
+
state.aiOperationHandlers.push({ moduleName, registration });
|
|
251
|
+
}
|
|
252
|
+
},
|
|
256
253
|
registerRolePresets(presets) {
|
|
257
254
|
for (const preset of presets) {
|
|
258
255
|
requireModuleNamespace("角色预设", preset.key, moduleName);
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { ModuleManifest } from "@southwind-ai/modules";
|
|
2
|
+
import type { ModuleToolHandlers } from "./agent/routes.js";
|
|
3
|
+
import { validateServerModuleComposition } from "./module-composition.js";
|
|
4
|
+
import type { InitializedModuleHosts } from "./module-host.js";
|
|
5
|
+
|
|
6
|
+
export function validateInstalledRuntime(input: {
|
|
7
|
+
modules: ModuleManifest[];
|
|
8
|
+
routes: readonly { method: string; path: string }[];
|
|
9
|
+
tasks: readonly { key: string }[];
|
|
10
|
+
tools: ModuleToolHandlers;
|
|
11
|
+
providers: readonly string[];
|
|
12
|
+
hosts: InitializedModuleHosts;
|
|
13
|
+
}): void {
|
|
14
|
+
const aiOperations = input.hosts.aiOperationHandlers.map(
|
|
15
|
+
({ registration }) => registration.operationKey,
|
|
16
|
+
);
|
|
17
|
+
validateServerModuleComposition({
|
|
18
|
+
modules: input.modules,
|
|
19
|
+
routes: input.routes,
|
|
20
|
+
tasks: input.tasks,
|
|
21
|
+
tools: input.tools,
|
|
22
|
+
aiOperations,
|
|
23
|
+
providers: input.providers,
|
|
24
|
+
moduleRegistrations: {
|
|
25
|
+
routes: input.hosts.routes.map(
|
|
26
|
+
({ definition }) => `${definition.method} ${definition.path}`,
|
|
27
|
+
),
|
|
28
|
+
tasks: input.hosts.taskHandlers.map(
|
|
29
|
+
({ registration }) => registration.taskKey,
|
|
30
|
+
),
|
|
31
|
+
tools: input.hosts.toolHandlers.map(
|
|
32
|
+
({ registration }) => registration.toolKey,
|
|
33
|
+
),
|
|
34
|
+
aiOperations,
|
|
35
|
+
providers: input.hosts.searchProviders.map(
|
|
36
|
+
({ provider }) => provider.key,
|
|
37
|
+
),
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
}
|
|
@@ -42,6 +42,10 @@ services:
|
|
|
42
42
|
WINDY_CONTAINERIZED: "1"
|
|
43
43
|
BUN_CONFIG_NO_CLEAR_TERMINAL_ON_RELOAD: "true"
|
|
44
44
|
WINDY_SYSTEM_USAGE_PATHS: /app/data/audit
|
|
45
|
+
PLATFORM_ENABLE_AI: ${PLATFORM_ENABLE_AI:-false}
|
|
46
|
+
AGENT_SERVER_URL: http://agent-server:8080
|
|
47
|
+
AGENT_TOOL_HOST_URL: http://server:9747
|
|
48
|
+
AGENT_SERVER_INTERNAL_TOKEN: ${AGENT_SERVER_INTERNAL_TOKEN:-}
|
|
45
49
|
ports:
|
|
46
50
|
- "9747:9747"
|
|
47
51
|
volumes:
|
|
@@ -52,6 +56,26 @@ services:
|
|
|
52
56
|
migrate:
|
|
53
57
|
condition: service_completed_successfully
|
|
54
58
|
|
|
59
|
+
agent-server:
|
|
60
|
+
profiles: ["agent"]
|
|
61
|
+
build:
|
|
62
|
+
context: .
|
|
63
|
+
dockerfile: apps/agent-server/Dockerfile
|
|
64
|
+
environment:
|
|
65
|
+
AGENT_SERVER_INTERNAL_TOKEN: ${AGENT_SERVER_INTERNAL_TOKEN:-}
|
|
66
|
+
AGENT_RUN_STORE_PATH: /app/data/agent/runs.sqlite3
|
|
67
|
+
QWEN_API_URL: ${QWEN_API_URL:-}
|
|
68
|
+
QWEN_API_KEY: ${QWEN_API_KEY:-}
|
|
69
|
+
QWEN_MODEL: ${QWEN_MODEL:-}
|
|
70
|
+
AI_PROVIDER_CONNECT_TIMEOUT_MS: ${AI_PROVIDER_CONNECT_TIMEOUT_MS:-5000}
|
|
71
|
+
AI_PROVIDER_REQUEST_TIMEOUT_MS: ${AI_PROVIDER_REQUEST_TIMEOUT_MS:-60000}
|
|
72
|
+
AI_PROVIDER_MAX_RESPONSE_BYTES: ${AI_PROVIDER_MAX_RESPONSE_BYTES:-2097152}
|
|
73
|
+
volumes:
|
|
74
|
+
- agent-run-data:/app/data/agent
|
|
75
|
+
depends_on:
|
|
76
|
+
server:
|
|
77
|
+
condition: service_started
|
|
78
|
+
|
|
55
79
|
web:
|
|
56
80
|
build:
|
|
57
81
|
context: .
|
|
@@ -85,3 +109,4 @@ services:
|
|
|
85
109
|
volumes:
|
|
86
110
|
postgres-data:
|
|
87
111
|
server-audit-data:
|
|
112
|
+
agent-run-data:
|